From: Jérôme Pouiller <jerome.pouiller@xxxxxxxxxx> Chip support encryption of the link between host and chip. This feature is called "secure link". Driver code on github[1] support it. However, it relies on mbedtls for cryptographic functions. So, I decided to not import this feature in current patch. However, in order to keep code synchronized between github and kernel, I imported all code related to this feature, even if most of it is just no-op. [1]: https://github.com/SiliconLabs/wfx-linux-driver/ Signed-off-by: Jérôme Pouiller <jerome.pouiller@xxxxxxxxxx> --- drivers/staging/wfx/bh.c | 31 +++++++++++++++++++-- drivers/staging/wfx/debug.c | 17 ++++++++++++ drivers/staging/wfx/hif_rx.c | 17 ++++++++++++ drivers/staging/wfx/hif_tx.c | 6 ++++ drivers/staging/wfx/hif_tx.h | 1 + drivers/staging/wfx/main.c | 36 ++++++++++++++++++++++++ drivers/staging/wfx/main.h | 2 ++ drivers/staging/wfx/secure_link.h | 46 +++++++++++++++++++++++++++++++ drivers/staging/wfx/wfx.h | 2 ++ 9 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 drivers/staging/wfx/secure_link.h diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c index c94c9c401a69..d321fd312d55 100644 --- a/drivers/staging/wfx/bh.c +++ b/drivers/staging/wfx/bh.c @@ -12,6 +12,7 @@ #include "wfx.h" #include "hwio.h" #include "traces.h" +#include "secure_link.h" #include "hif_rx.h" #include "hif_api_cmd.h" @@ -74,7 +75,18 @@ static int rx_helper(struct wfx_dev *wdev, size_t read_len, int *is_cnf) hif = (struct hif_msg *) skb->data; WARN(hif->encrypted & 0x1, "unsupported encryption type"); if (hif->encrypted == 0x2) { - BUG(); // Not yet implemented + if (wfx_sl_decode(wdev, (void *) hif)) { + dev_kfree_skb(skb); + // If frame was a confirmation, expect trouble in next + // exchange. However, it is harmless to fail to decode + // an indication frame, so try to continue. Anyway, + // piggyback is probably correct. + return piggyback; + } + le16_to_cpus(hif->len); + computed_len = round_up(hif->len - sizeof(hif->len), 16) + + sizeof(struct hif_sl_msg) + + sizeof(struct hif_sl_tag); } else { le16_to_cpus(hif->len); computed_len = round_up(hif->len, 2); @@ -166,7 +178,22 @@ static void tx_helper(struct wfx_dev *wdev, struct hif_msg *hif) hif->seqnum = wdev->hif.tx_seqnum; wdev->hif.tx_seqnum = (wdev->hif.tx_seqnum + 1) % (HIF_COUNTER_MAX + 1); - data = hif; + if (wfx_is_secure_command(wdev, hif->id)) { + len = round_up(len - sizeof(hif->len), 16) + sizeof(hif->len) + + sizeof(struct hif_sl_msg_hdr) + sizeof(struct hif_sl_tag); + // AES support encryption in-place. However, mac80211 access to + // 802.11 header after frame was sent (to get MAC addresses). + // So, keep origin buffer clear. + data = kmalloc(len, GFP_KERNEL); + if (!data) + goto end; + is_encrypted = true; + ret = wfx_sl_encode(wdev, hif, data); + if (ret) + goto end; + } else { + data = hif; + } WARN(len > wdev->hw_caps.size_inp_ch_buf, "%s: request exceed WFx capability: %zu > %d\n", __func__, len, wdev->hw_caps.size_inp_ch_buf); diff --git a/drivers/staging/wfx/debug.c b/drivers/staging/wfx/debug.c index 0a328c96eaa0..f79693a4be7f 100644 --- a/drivers/staging/wfx/debug.c +++ b/drivers/staging/wfx/debug.c @@ -6,6 +6,7 @@ * Copyright (c) 2010, ST-Ericsson */ #include <linux/debugfs.h> +#include <linux/crc32.h> #include "debug.h" #include "wfx.h" @@ -53,6 +54,21 @@ const char *get_reg_name(unsigned long id) return get_symbol(id, wfx_reg_print_map); } +static ssize_t wfx_burn_slk_key_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wfx_dev *wdev = file->private_data; + + dev_info(wdev->dev, "this driver does not support secure link\n"); + return -EINVAL; +} + +static const struct file_operations wfx_burn_slk_key_fops = { + .open = simple_open, + .write = wfx_burn_slk_key_write, +}; + struct dbgfs_hif_msg { struct wfx_dev *wdev; struct completion complete; @@ -146,6 +162,7 @@ int wfx_debug_init(struct wfx_dev *wdev) struct dentry *d; d = debugfs_create_dir("wfx", wdev->hw->wiphy->debugfsdir); + debugfs_create_file("burn_slk_key", 0200, d, wdev, &wfx_burn_slk_key_fops); debugfs_create_file("send_hif_msg", 0600, d, wdev, &wfx_send_hif_msg_fops); return 0; diff --git a/drivers/staging/wfx/hif_rx.c b/drivers/staging/wfx/hif_rx.c index ba8ea4f3c91b..dd5f1dea4e85 100644 --- a/drivers/staging/wfx/hif_rx.c +++ b/drivers/staging/wfx/hif_rx.c @@ -11,6 +11,7 @@ #include "hif_rx.h" #include "wfx.h" +#include "secure_link.h" #include "hif_api_cmd.h" static int hif_generic_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) @@ -46,6 +47,8 @@ static int hif_generic_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void * } else { wdev->hif_cmd.buf_send = NULL; mutex_unlock(&wdev->hif_cmd.lock); + if (cmd != HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS) + mutex_unlock(&wdev->hif_cmd.key_renew_lock); } return status; } @@ -68,11 +71,25 @@ static int hif_startup_indication(struct wfx_dev *wdev, struct hif_msg *hif, voi return 0; } +static int hif_keys_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct hif_ind_sl_exchange_pub_keys *body = buf; + + // Compatibility with legacy secure link + if (body->status == SL_PUB_KEY_EXCHANGE_STATUS_SUCCESS) + body->status = 0; + if (body->status) + dev_warn(wdev->dev, "secure link negociation error\n"); + wfx_sl_check_pubkey(wdev, body->ncp_pub_key, body->ncp_pub_key_mac); + return 0; +} + static const struct { int msg_id; int (*handler)(struct wfx_dev *wdev, struct hif_msg *hif, void *buf); } hif_handlers[] = { { HIF_IND_ID_STARTUP, hif_startup_indication }, + { HIF_IND_ID_SL_EXCHANGE_PUB_KEYS, hif_keys_indication }, }; void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb) diff --git a/drivers/staging/wfx/hif_tx.c b/drivers/staging/wfx/hif_tx.c index 781a6e28dbad..f8ab871aa188 100644 --- a/drivers/staging/wfx/hif_tx.c +++ b/drivers/staging/wfx/hif_tx.c @@ -20,6 +20,7 @@ void wfx_init_hif_cmd(struct wfx_hif_cmd *hif_cmd) init_completion(&hif_cmd->ready); init_completion(&hif_cmd->done); mutex_init(&hif_cmd->lock); + mutex_init(&hif_cmd->key_renew_lock); } static void wfx_fill_header(struct hif_msg *hif, int if_id, unsigned int cmd, size_t size) @@ -59,6 +60,9 @@ int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, void *reply, siz if (wdev->chip_frozen) return -ETIMEDOUT; + if (cmd != HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS) + mutex_lock(&wdev->hif_cmd.key_renew_lock); + mutex_lock(&wdev->hif_cmd.lock); WARN(wdev->hif_cmd.buf_send, "data locking error"); @@ -107,6 +111,8 @@ int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, void *reply, siz "WSM request %s%s%s (%#.2x) on vif %d returned status %d\n", get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret); + if (cmd != HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS) + mutex_unlock(&wdev->hif_cmd.key_renew_lock); return ret; } diff --git a/drivers/staging/wfx/hif_tx.h b/drivers/staging/wfx/hif_tx.h index 31f2a02c8466..6f2ea2f3a77d 100644 --- a/drivers/staging/wfx/hif_tx.h +++ b/drivers/staging/wfx/hif_tx.h @@ -23,6 +23,7 @@ struct wfx_scan_params { struct wfx_hif_cmd { struct mutex lock; + struct mutex key_renew_lock; struct completion ready; struct completion done; bool async; diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index 8973eeb60eb8..0cfd6b2ec8d1 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -27,6 +27,7 @@ #include "bh.h" #include "sta.h" #include "debug.h" +#include "secure_link.h" #include "hif_api_cmd.h" #include "wfx_version.h" @@ -39,6 +40,10 @@ static int gpio_wakeup = -2; module_param(gpio_wakeup, int, 0644); MODULE_PARM_DESC(gpio_wakeup, "gpio number for wakeup. -1 for none."); +static char *slk_key; +module_param(slk_key, charp, 0600); +MODULE_PARM_DESC(slk_key, "secret key for secure link (expect 64 hexdecimal digits)."); + static const struct ieee80211_ops wfx_ops = { .start = wfx_start, .stop = wfx_stop, @@ -84,6 +89,29 @@ struct gpio_desc *wfx_get_gpio(struct device *dev, int override, const char *lab return ret; } +static void wfx_fill_sl_key(struct device *dev, struct wfx_platform_data *pdata) +{ + const char *ascii_key = NULL; + int ret = 0; + + if (slk_key) + ascii_key = slk_key; + if (!ascii_key) + ret = of_property_read_string(dev->of_node, "slk_key", &ascii_key); + if (ret == -EILSEQ || ret == -ENODATA) + dev_err(dev, "ignoring malformatted key from DT\n"); + if (!ascii_key) + return; + + ret = hex2bin(pdata->slk_key, ascii_key, sizeof(pdata->slk_key)); + if (ret) { + dev_err(dev, "ignoring malformatted key: %s\n", ascii_key); + memset(pdata->slk_key, 0, sizeof(pdata->slk_key)); + return; + } + dev_err(dev, "secure link is not supported by this driver, ignoring provided key\n"); +} + struct wfx_dev *wfx_init_common(struct device *dev, const struct wfx_platform_data *pdata, const struct hwbus_ops *hwbus_ops, @@ -113,6 +141,7 @@ struct wfx_dev *wfx_init_common(struct device *dev, wdev->hwbus_ops = hwbus_ops; wdev->hwbus_priv = hwbus_priv; memcpy(&wdev->pdata, pdata, sizeof(*pdata)); + wfx_fill_sl_key(dev, &wdev->pdata); init_completion(&wdev->firmware_ready); wfx_init_hif_cmd(&wdev->hif_cmd); @@ -167,6 +196,12 @@ int wfx_probe(struct wfx_dev *wdev) goto err1; } + err = wfx_sl_init(wdev); + if (err && wdev->hw_caps.capabilities.link_mode == SEC_LINK_ENFORCED) { + dev_err(wdev->dev, "chip require secure_link, but can't negociate it\n"); + goto err1; + } + for (i = 0; i < ARRAY_SIZE(wdev->addresses); i++) { eth_zero_addr(wdev->addresses[i].addr); macaddr = of_get_mac_address(wdev->dev->of_node); @@ -198,6 +233,7 @@ int wfx_probe(struct wfx_dev *wdev) void wfx_release(struct wfx_dev *wdev) { wfx_bh_unregister(wdev); + wfx_sl_deinit(wdev); } static int __init wfx_core_init(void) diff --git a/drivers/staging/wfx/main.h b/drivers/staging/wfx/main.h index f7c65999a493..2c9c215455ce 100644 --- a/drivers/staging/wfx/main.h +++ b/drivers/staging/wfx/main.h @@ -14,12 +14,14 @@ #include <linux/gpio/consumer.h> #include "bus.h" +#include "hif_api_general.h" struct wfx_dev; struct wfx_platform_data { /* Keyset and ".sec" extention will appended to this string */ const char *file_fw; + unsigned char slk_key[API_KEY_VALUE_SIZE]; struct gpio_desc *gpio_wakeup; /* * if true HIF D_out is sampled on the rising edge of the clock diff --git a/drivers/staging/wfx/secure_link.h b/drivers/staging/wfx/secure_link.h new file mode 100644 index 000000000000..e2da1c73c760 --- /dev/null +++ b/drivers/staging/wfx/secure_link.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2019, Silicon Laboratories, Inc. + */ +#ifndef WFX_SECURE_LINK_H +#define WFX_SECURE_LINK_H + +#include "hif_api_general.h" + +struct wfx_dev; + + +struct sl_context { +}; + +static inline bool wfx_is_secure_command(struct wfx_dev *wdev, int cmd_id) +{ + return false; +} + +static inline int wfx_sl_decode(struct wfx_dev *wdev, struct hif_sl_msg *m) +{ + return -EIO; +} + +static inline int wfx_sl_encode(struct wfx_dev *wdev, struct hif_msg *input, struct hif_sl_msg *output) +{ + return -EIO; +} + +static inline int wfx_sl_check_pubkey(struct wfx_dev *wdev, uint8_t *ncp_pubkey, uint8_t *ncp_pubmac) +{ + return -EIO; +} + +static inline int wfx_sl_init(struct wfx_dev *wdev) +{ + return -EIO; +} + +static inline void wfx_sl_deinit(struct wfx_dev *wdev) +{ +} + + +#endif diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h index e23e86d4d7f0..2537fc97af27 100644 --- a/drivers/staging/wfx/wfx.h +++ b/drivers/staging/wfx/wfx.h @@ -16,6 +16,7 @@ #include "bh.h" #include "main.h" +#include "secure_link.h" #include "hif_tx.h" #include "hif_api_general.h" @@ -39,6 +40,7 @@ struct wfx_dev { struct completion firmware_ready; struct hif_ind_startup hw_caps; struct wfx_hif hif; + struct sl_context sl; int chip_frozen; struct wfx_hif_cmd hif_cmd; -- 2.20.1