xref: /openbmc/linux/drivers/net/wireless/silabs/wfx/key.c (revision 2c33360b)
14a5fb1bbSJérôme Pouiller // SPDX-License-Identifier: GPL-2.0-only
24a5fb1bbSJérôme Pouiller /*
34a5fb1bbSJérôme Pouiller  * Key management related functions.
44a5fb1bbSJérôme Pouiller  *
54a5fb1bbSJérôme Pouiller  * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
64a5fb1bbSJérôme Pouiller  * Copyright (c) 2010, ST-Ericsson
74a5fb1bbSJérôme Pouiller  */
84a5fb1bbSJérôme Pouiller #include <linux/etherdevice.h>
94a5fb1bbSJérôme Pouiller #include <net/mac80211.h>
104a5fb1bbSJérôme Pouiller 
114a5fb1bbSJérôme Pouiller #include "key.h"
124a5fb1bbSJérôme Pouiller #include "wfx.h"
134a5fb1bbSJérôme Pouiller #include "hif_tx_mib.h"
144a5fb1bbSJérôme Pouiller 
wfx_alloc_key(struct wfx_dev * wdev)154a5fb1bbSJérôme Pouiller static int wfx_alloc_key(struct wfx_dev *wdev)
164a5fb1bbSJérôme Pouiller {
174a5fb1bbSJérôme Pouiller 	int idx;
184a5fb1bbSJérôme Pouiller 
194a5fb1bbSJérôme Pouiller 	idx = ffs(~wdev->key_map) - 1;
204a5fb1bbSJérôme Pouiller 	if (idx < 0 || idx >= MAX_KEY_ENTRIES)
214a5fb1bbSJérôme Pouiller 		return -1;
224a5fb1bbSJérôme Pouiller 
234a5fb1bbSJérôme Pouiller 	wdev->key_map |= BIT(idx);
244a5fb1bbSJérôme Pouiller 	return idx;
254a5fb1bbSJérôme Pouiller }
264a5fb1bbSJérôme Pouiller 
wfx_free_key(struct wfx_dev * wdev,int idx)274a5fb1bbSJérôme Pouiller static void wfx_free_key(struct wfx_dev *wdev, int idx)
284a5fb1bbSJérôme Pouiller {
294a5fb1bbSJérôme Pouiller 	WARN(!(wdev->key_map & BIT(idx)), "inconsistent key allocation");
304a5fb1bbSJérôme Pouiller 	wdev->key_map &= ~BIT(idx);
314a5fb1bbSJérôme Pouiller }
324a5fb1bbSJérôme Pouiller 
fill_wep_pair(struct wfx_hif_wep_pairwise_key * msg,struct ieee80211_key_conf * key,u8 * peer_addr)334a5fb1bbSJérôme Pouiller static u8 fill_wep_pair(struct wfx_hif_wep_pairwise_key *msg,
344a5fb1bbSJérôme Pouiller 			struct ieee80211_key_conf *key, u8 *peer_addr)
354a5fb1bbSJérôme Pouiller {
364a5fb1bbSJérôme Pouiller 	WARN(key->keylen > sizeof(msg->key_data), "inconsistent data");
374a5fb1bbSJérôme Pouiller 	msg->key_length = key->keylen;
384a5fb1bbSJérôme Pouiller 	memcpy(msg->key_data, key->key, key->keylen);
394a5fb1bbSJérôme Pouiller 	ether_addr_copy(msg->peer_address, peer_addr);
404a5fb1bbSJérôme Pouiller 	return HIF_KEY_TYPE_WEP_PAIRWISE;
414a5fb1bbSJérôme Pouiller }
424a5fb1bbSJérôme Pouiller 
fill_wep_group(struct wfx_hif_wep_group_key * msg,struct ieee80211_key_conf * key)434a5fb1bbSJérôme Pouiller static u8 fill_wep_group(struct wfx_hif_wep_group_key *msg,
444a5fb1bbSJérôme Pouiller 			 struct ieee80211_key_conf *key)
454a5fb1bbSJérôme Pouiller {
464a5fb1bbSJérôme Pouiller 	WARN(key->keylen > sizeof(msg->key_data), "inconsistent data");
474a5fb1bbSJérôme Pouiller 	msg->key_id = key->keyidx;
484a5fb1bbSJérôme Pouiller 	msg->key_length = key->keylen;
494a5fb1bbSJérôme Pouiller 	memcpy(msg->key_data, key->key, key->keylen);
504a5fb1bbSJérôme Pouiller 	return HIF_KEY_TYPE_WEP_DEFAULT;
514a5fb1bbSJérôme Pouiller }
524a5fb1bbSJérôme Pouiller 
fill_tkip_pair(struct wfx_hif_tkip_pairwise_key * msg,struct ieee80211_key_conf * key,u8 * peer_addr)534a5fb1bbSJérôme Pouiller static u8 fill_tkip_pair(struct wfx_hif_tkip_pairwise_key *msg,
544a5fb1bbSJérôme Pouiller 			 struct ieee80211_key_conf *key, u8 *peer_addr)
554a5fb1bbSJérôme Pouiller {
564a5fb1bbSJérôme Pouiller 	u8 *keybuf = key->key;
574a5fb1bbSJérôme Pouiller 
584a5fb1bbSJérôme Pouiller 	WARN(key->keylen != sizeof(msg->tkip_key_data) + sizeof(msg->tx_mic_key) +
594a5fb1bbSJérôme Pouiller 			    sizeof(msg->rx_mic_key), "inconsistent data");
604a5fb1bbSJérôme Pouiller 	memcpy(msg->tkip_key_data, keybuf, sizeof(msg->tkip_key_data));
614a5fb1bbSJérôme Pouiller 	keybuf += sizeof(msg->tkip_key_data);
624a5fb1bbSJérôme Pouiller 	memcpy(msg->tx_mic_key, keybuf, sizeof(msg->tx_mic_key));
634a5fb1bbSJérôme Pouiller 	keybuf += sizeof(msg->tx_mic_key);
644a5fb1bbSJérôme Pouiller 	memcpy(msg->rx_mic_key, keybuf, sizeof(msg->rx_mic_key));
654a5fb1bbSJérôme Pouiller 	ether_addr_copy(msg->peer_address, peer_addr);
664a5fb1bbSJérôme Pouiller 	return HIF_KEY_TYPE_TKIP_PAIRWISE;
674a5fb1bbSJérôme Pouiller }
684a5fb1bbSJérôme Pouiller 
fill_tkip_group(struct wfx_hif_tkip_group_key * msg,struct ieee80211_key_conf * key,struct ieee80211_key_seq * seq,enum nl80211_iftype iftype)694a5fb1bbSJérôme Pouiller static u8 fill_tkip_group(struct wfx_hif_tkip_group_key *msg, struct ieee80211_key_conf *key,
704a5fb1bbSJérôme Pouiller 			  struct ieee80211_key_seq *seq, enum nl80211_iftype iftype)
714a5fb1bbSJérôme Pouiller {
724a5fb1bbSJérôme Pouiller 	u8 *keybuf = key->key;
734a5fb1bbSJérôme Pouiller 
744a5fb1bbSJérôme Pouiller 	WARN(key->keylen != sizeof(msg->tkip_key_data) + 2 * sizeof(msg->rx_mic_key),
754a5fb1bbSJérôme Pouiller 	     "inconsistent data");
764a5fb1bbSJérôme Pouiller 	msg->key_id = key->keyidx;
774a5fb1bbSJérôme Pouiller 	memcpy(msg->rx_sequence_counter, &seq->tkip.iv16, sizeof(seq->tkip.iv16));
784a5fb1bbSJérôme Pouiller 	memcpy(msg->rx_sequence_counter + sizeof(u16), &seq->tkip.iv32, sizeof(seq->tkip.iv32));
794a5fb1bbSJérôme Pouiller 	memcpy(msg->tkip_key_data, keybuf, sizeof(msg->tkip_key_data));
804a5fb1bbSJérôme Pouiller 	keybuf += sizeof(msg->tkip_key_data);
814a5fb1bbSJérôme Pouiller 	if (iftype == NL80211_IFTYPE_AP)
824a5fb1bbSJérôme Pouiller 		/* Use Tx MIC Key */
834a5fb1bbSJérôme Pouiller 		memcpy(msg->rx_mic_key, keybuf + 0, sizeof(msg->rx_mic_key));
844a5fb1bbSJérôme Pouiller 	else
854a5fb1bbSJérôme Pouiller 		/* Use Rx MIC Key */
864a5fb1bbSJérôme Pouiller 		memcpy(msg->rx_mic_key, keybuf + 8, sizeof(msg->rx_mic_key));
874a5fb1bbSJérôme Pouiller 	return HIF_KEY_TYPE_TKIP_GROUP;
884a5fb1bbSJérôme Pouiller }
894a5fb1bbSJérôme Pouiller 
fill_ccmp_pair(struct wfx_hif_aes_pairwise_key * msg,struct ieee80211_key_conf * key,u8 * peer_addr)904a5fb1bbSJérôme Pouiller static u8 fill_ccmp_pair(struct wfx_hif_aes_pairwise_key *msg,
914a5fb1bbSJérôme Pouiller 			 struct ieee80211_key_conf *key, u8 *peer_addr)
924a5fb1bbSJérôme Pouiller {
934a5fb1bbSJérôme Pouiller 	WARN(key->keylen != sizeof(msg->aes_key_data), "inconsistent data");
944a5fb1bbSJérôme Pouiller 	ether_addr_copy(msg->peer_address, peer_addr);
954a5fb1bbSJérôme Pouiller 	memcpy(msg->aes_key_data, key->key, key->keylen);
964a5fb1bbSJérôme Pouiller 	return HIF_KEY_TYPE_AES_PAIRWISE;
974a5fb1bbSJérôme Pouiller }
984a5fb1bbSJérôme Pouiller 
fill_ccmp_group(struct wfx_hif_aes_group_key * msg,struct ieee80211_key_conf * key,struct ieee80211_key_seq * seq)994a5fb1bbSJérôme Pouiller static u8 fill_ccmp_group(struct wfx_hif_aes_group_key *msg,
1004a5fb1bbSJérôme Pouiller 			  struct ieee80211_key_conf *key, struct ieee80211_key_seq *seq)
1014a5fb1bbSJérôme Pouiller {
1024a5fb1bbSJérôme Pouiller 	WARN(key->keylen != sizeof(msg->aes_key_data), "inconsistent data");
1034a5fb1bbSJérôme Pouiller 	memcpy(msg->aes_key_data, key->key, key->keylen);
1044a5fb1bbSJérôme Pouiller 	memcpy(msg->rx_sequence_counter, seq->ccmp.pn, sizeof(seq->ccmp.pn));
1054a5fb1bbSJérôme Pouiller 	memreverse(msg->rx_sequence_counter, sizeof(seq->ccmp.pn));
1064a5fb1bbSJérôme Pouiller 	msg->key_id = key->keyidx;
1074a5fb1bbSJérôme Pouiller 	return HIF_KEY_TYPE_AES_GROUP;
1084a5fb1bbSJérôme Pouiller }
1094a5fb1bbSJérôme Pouiller 
fill_sms4_pair(struct wfx_hif_wapi_pairwise_key * msg,struct ieee80211_key_conf * key,u8 * peer_addr)1104a5fb1bbSJérôme Pouiller static u8 fill_sms4_pair(struct wfx_hif_wapi_pairwise_key *msg,
1114a5fb1bbSJérôme Pouiller 			 struct ieee80211_key_conf *key, u8 *peer_addr)
1124a5fb1bbSJérôme Pouiller {
1134a5fb1bbSJérôme Pouiller 	u8 *keybuf = key->key;
1144a5fb1bbSJérôme Pouiller 
1154a5fb1bbSJérôme Pouiller 	WARN(key->keylen != sizeof(msg->wapi_key_data) + sizeof(msg->mic_key_data),
1164a5fb1bbSJérôme Pouiller 	     "inconsistent data");
1174a5fb1bbSJérôme Pouiller 	ether_addr_copy(msg->peer_address, peer_addr);
1184a5fb1bbSJérôme Pouiller 	memcpy(msg->wapi_key_data, keybuf, sizeof(msg->wapi_key_data));
1194a5fb1bbSJérôme Pouiller 	keybuf += sizeof(msg->wapi_key_data);
1204a5fb1bbSJérôme Pouiller 	memcpy(msg->mic_key_data, keybuf, sizeof(msg->mic_key_data));
1214a5fb1bbSJérôme Pouiller 	msg->key_id = key->keyidx;
1224a5fb1bbSJérôme Pouiller 	return HIF_KEY_TYPE_WAPI_PAIRWISE;
1234a5fb1bbSJérôme Pouiller }
1244a5fb1bbSJérôme Pouiller 
fill_sms4_group(struct wfx_hif_wapi_group_key * msg,struct ieee80211_key_conf * key)1254a5fb1bbSJérôme Pouiller static u8 fill_sms4_group(struct wfx_hif_wapi_group_key *msg,
1264a5fb1bbSJérôme Pouiller 			  struct ieee80211_key_conf *key)
1274a5fb1bbSJérôme Pouiller {
1284a5fb1bbSJérôme Pouiller 	u8 *keybuf = key->key;
1294a5fb1bbSJérôme Pouiller 
1304a5fb1bbSJérôme Pouiller 	WARN(key->keylen != sizeof(msg->wapi_key_data) + sizeof(msg->mic_key_data),
1314a5fb1bbSJérôme Pouiller 	     "inconsistent data");
1324a5fb1bbSJérôme Pouiller 	memcpy(msg->wapi_key_data, keybuf, sizeof(msg->wapi_key_data));
1334a5fb1bbSJérôme Pouiller 	keybuf += sizeof(msg->wapi_key_data);
1344a5fb1bbSJérôme Pouiller 	memcpy(msg->mic_key_data, keybuf, sizeof(msg->mic_key_data));
1354a5fb1bbSJérôme Pouiller 	msg->key_id = key->keyidx;
1364a5fb1bbSJérôme Pouiller 	return HIF_KEY_TYPE_WAPI_GROUP;
1374a5fb1bbSJérôme Pouiller }
1384a5fb1bbSJérôme Pouiller 
fill_aes_cmac_group(struct wfx_hif_igtk_group_key * msg,struct ieee80211_key_conf * key,struct ieee80211_key_seq * seq)1394a5fb1bbSJérôme Pouiller static u8 fill_aes_cmac_group(struct wfx_hif_igtk_group_key *msg,
1404a5fb1bbSJérôme Pouiller 			      struct ieee80211_key_conf *key, struct ieee80211_key_seq *seq)
1414a5fb1bbSJérôme Pouiller {
1424a5fb1bbSJérôme Pouiller 	WARN(key->keylen != sizeof(msg->igtk_key_data), "inconsistent data");
1434a5fb1bbSJérôme Pouiller 	memcpy(msg->igtk_key_data, key->key, key->keylen);
1444a5fb1bbSJérôme Pouiller 	memcpy(msg->ipn, seq->aes_cmac.pn, sizeof(seq->aes_cmac.pn));
1454a5fb1bbSJérôme Pouiller 	memreverse(msg->ipn, sizeof(seq->aes_cmac.pn));
1464a5fb1bbSJérôme Pouiller 	msg->key_id = key->keyidx;
1474a5fb1bbSJérôme Pouiller 	return HIF_KEY_TYPE_IGTK_GROUP;
1484a5fb1bbSJérôme Pouiller }
1494a5fb1bbSJérôme Pouiller 
wfx_add_key(struct wfx_vif * wvif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key)1504a5fb1bbSJérôme Pouiller static int wfx_add_key(struct wfx_vif *wvif, struct ieee80211_sta *sta,
1514a5fb1bbSJérôme Pouiller 		       struct ieee80211_key_conf *key)
1524a5fb1bbSJérôme Pouiller {
1534a5fb1bbSJérôme Pouiller 	int ret;
1544a5fb1bbSJérôme Pouiller 	struct wfx_hif_req_add_key k = { };
1554a5fb1bbSJérôme Pouiller 	struct ieee80211_key_seq seq;
1564a5fb1bbSJérôme Pouiller 	struct wfx_dev *wdev = wvif->wdev;
1574a5fb1bbSJérôme Pouiller 	int idx = wfx_alloc_key(wvif->wdev);
1584a5fb1bbSJérôme Pouiller 	bool pairwise = key->flags & IEEE80211_KEY_FLAG_PAIRWISE;
159*2c33360bSJaehee Park 	struct ieee80211_vif *vif = wvif_to_vif(wvif);
1604a5fb1bbSJérôme Pouiller 
1614a5fb1bbSJérôme Pouiller 	WARN(key->flags & IEEE80211_KEY_FLAG_PAIRWISE && !sta, "inconsistent data");
1624a5fb1bbSJérôme Pouiller 	ieee80211_get_key_rx_seq(key, 0, &seq);
1634a5fb1bbSJérôme Pouiller 	if (idx < 0)
1644a5fb1bbSJérôme Pouiller 		return -EINVAL;
1654a5fb1bbSJérôme Pouiller 	k.int_id = wvif->id;
1664a5fb1bbSJérôme Pouiller 	k.entry_index = idx;
1674a5fb1bbSJérôme Pouiller 	if (key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
1684a5fb1bbSJérôme Pouiller 	    key->cipher == WLAN_CIPHER_SUITE_WEP104) {
1694a5fb1bbSJérôme Pouiller 		if (pairwise)
1704a5fb1bbSJérôme Pouiller 			k.type = fill_wep_pair(&k.key.wep_pairwise_key, key, sta->addr);
1714a5fb1bbSJérôme Pouiller 		else
1724a5fb1bbSJérôme Pouiller 			k.type = fill_wep_group(&k.key.wep_group_key, key);
1734a5fb1bbSJérôme Pouiller 	} else if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
1744a5fb1bbSJérôme Pouiller 		if (pairwise)
1754a5fb1bbSJérôme Pouiller 			k.type = fill_tkip_pair(&k.key.tkip_pairwise_key, key, sta->addr);
1764a5fb1bbSJérôme Pouiller 		else
1774a5fb1bbSJérôme Pouiller 			k.type = fill_tkip_group(&k.key.tkip_group_key, key, &seq,
178*2c33360bSJaehee Park 						 vif->type);
1794a5fb1bbSJérôme Pouiller 	} else if (key->cipher == WLAN_CIPHER_SUITE_CCMP) {
1804a5fb1bbSJérôme Pouiller 		if (pairwise)
1814a5fb1bbSJérôme Pouiller 			k.type = fill_ccmp_pair(&k.key.aes_pairwise_key, key, sta->addr);
1824a5fb1bbSJérôme Pouiller 		else
1834a5fb1bbSJérôme Pouiller 			k.type = fill_ccmp_group(&k.key.aes_group_key, key, &seq);
1844a5fb1bbSJérôme Pouiller 	} else if (key->cipher == WLAN_CIPHER_SUITE_SMS4) {
1854a5fb1bbSJérôme Pouiller 		if (pairwise)
1864a5fb1bbSJérôme Pouiller 			k.type = fill_sms4_pair(&k.key.wapi_pairwise_key, key, sta->addr);
1874a5fb1bbSJérôme Pouiller 		else
1884a5fb1bbSJérôme Pouiller 			k.type = fill_sms4_group(&k.key.wapi_group_key, key);
1894a5fb1bbSJérôme Pouiller 	} else if (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC) {
1904a5fb1bbSJérôme Pouiller 		k.type = fill_aes_cmac_group(&k.key.igtk_group_key, key, &seq);
1914a5fb1bbSJérôme Pouiller 		key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
1924a5fb1bbSJérôme Pouiller 	} else {
1934a5fb1bbSJérôme Pouiller 		dev_warn(wdev->dev, "unsupported key type %d\n", key->cipher);
1944a5fb1bbSJérôme Pouiller 		wfx_free_key(wdev, idx);
1954a5fb1bbSJérôme Pouiller 		return -EOPNOTSUPP;
1964a5fb1bbSJérôme Pouiller 	}
1974a5fb1bbSJérôme Pouiller 	ret = wfx_hif_add_key(wdev, &k);
1984a5fb1bbSJérôme Pouiller 	if (ret) {
1994a5fb1bbSJérôme Pouiller 		wfx_free_key(wdev, idx);
2004a5fb1bbSJérôme Pouiller 		return -EOPNOTSUPP;
2014a5fb1bbSJérôme Pouiller 	}
2024a5fb1bbSJérôme Pouiller 	key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE | IEEE80211_KEY_FLAG_RESERVE_TAILROOM;
2034a5fb1bbSJérôme Pouiller 	key->hw_key_idx = idx;
2044a5fb1bbSJérôme Pouiller 	return 0;
2054a5fb1bbSJérôme Pouiller }
2064a5fb1bbSJérôme Pouiller 
wfx_remove_key(struct wfx_vif * wvif,struct ieee80211_key_conf * key)2074a5fb1bbSJérôme Pouiller static int wfx_remove_key(struct wfx_vif *wvif, struct ieee80211_key_conf *key)
2084a5fb1bbSJérôme Pouiller {
2094a5fb1bbSJérôme Pouiller 	WARN(key->hw_key_idx >= MAX_KEY_ENTRIES, "corrupted hw_key_idx");
2104a5fb1bbSJérôme Pouiller 	wfx_free_key(wvif->wdev, key->hw_key_idx);
2114a5fb1bbSJérôme Pouiller 	return wfx_hif_remove_key(wvif->wdev, key->hw_key_idx);
2124a5fb1bbSJérôme Pouiller }
2134a5fb1bbSJérôme Pouiller 
wfx_set_key(struct ieee80211_hw * hw,enum set_key_cmd cmd,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key)2144a5fb1bbSJérôme Pouiller int wfx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif,
2154a5fb1bbSJérôme Pouiller 		struct ieee80211_sta *sta, struct ieee80211_key_conf *key)
2164a5fb1bbSJérôme Pouiller {
2174a5fb1bbSJérôme Pouiller 	int ret = -EOPNOTSUPP;
2184a5fb1bbSJérôme Pouiller 	struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv;
2194a5fb1bbSJérôme Pouiller 
2204a5fb1bbSJérôme Pouiller 	mutex_lock(&wvif->wdev->conf_mutex);
2214a5fb1bbSJérôme Pouiller 	if (cmd == SET_KEY)
2224a5fb1bbSJérôme Pouiller 		ret = wfx_add_key(wvif, sta, key);
2234a5fb1bbSJérôme Pouiller 	if (cmd == DISABLE_KEY)
2244a5fb1bbSJérôme Pouiller 		ret = wfx_remove_key(wvif, key);
2254a5fb1bbSJérôme Pouiller 	mutex_unlock(&wvif->wdev->conf_mutex);
2264a5fb1bbSJérôme Pouiller 	return ret;
2274a5fb1bbSJérôme Pouiller }
228