aboutsummaryrefslogtreecommitdiffstats
path: root/wpa_supplicant/mesh_rsn.c
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2016-06-18 11:11:23 (GMT)
committerJouni Malinen <j@w1.fi>2016-06-19 17:18:09 (GMT)
commitf868d5607d67d9e99b7ca3e7eed3383283830c64 (patch)
tree976a987a90395d20cb58bb83cf9787ecd88b2384 /wpa_supplicant/mesh_rsn.c
parent4367eec4394002046e1ad99b2ecf878d925415f0 (diff)
downloadhostap-f868d5607d67d9e99b7ca3e7eed3383283830c64.zip
hostap-f868d5607d67d9e99b7ca3e7eed3383283830c64.tar.gz
hostap-f868d5607d67d9e99b7ca3e7eed3383283830c64.tar.bz2
mesh: Clean up AMPE element encoding and parsing
The AMPE element includes number of optional and variable length fields and those cannot really be represented by a fixed struct ieee80211_ampe_ie. Remove the optional fields from the struct and build/parse these fields separately. This is also adding support for IGTKdata that was completely missing from the previous implementation. In addition, Key RSC for MGTK is now filled in and used when configuring the RX MGTK for a peer. Signed-off-by: Jouni Malinen <j@w1.fi>
Diffstat (limited to 'wpa_supplicant/mesh_rsn.c')
-rw-r--r--wpa_supplicant/mesh_rsn.c151
1 files changed, 116 insertions, 35 deletions
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index db2a608..6ca3837 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -176,17 +176,20 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
rsn->mgtk_len = wpa_cipher_key_len(WPA_CIPHER_CCMP);
if (random_get_bytes(rsn->mgtk, rsn->mgtk_len) < 0)
return -1;
+ rsn->mgtk_key_id = 1;
#ifdef CONFIG_IEEE80211W
if (ieee80211w != NO_MGMT_FRAME_PROTECTION) {
- if (random_get_bytes(rsn->igtk, 16) < 0)
+ rsn->igtk_len = wpa_cipher_key_len(WPA_CIPHER_AES_128_CMAC);
+ if (random_get_bytes(rsn->igtk, rsn->igtk_len) < 0)
return -1;
- rsn->igtk_len = 16;
+ rsn->igtk_key_id = 4;
/* group mgmt */
wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX IGTK",
rsn->igtk, rsn->igtk_len);
- wpa_drv_set_key(rsn->wpa_s, WPA_ALG_IGTK, NULL, 4, 1,
+ wpa_drv_set_key(rsn->wpa_s, WPA_ALG_IGTK, NULL,
+ rsn->igtk_key_id, 1,
seq, sizeof(seq), rsn->igtk, rsn->igtk_len);
}
#endif /* CONFIG_IEEE80211W */
@@ -194,7 +197,7 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
/* group privacy / data frames */
wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX MGTK",
rsn->mgtk, rsn->mgtk_len);
- wpa_drv_set_key(rsn->wpa_s, WPA_ALG_CCMP, NULL, 1, 1,
+ wpa_drv_set_key(rsn->wpa_s, WPA_ALG_CCMP, NULL, rsn->mgtk_key_id, 1,
seq, sizeof(seq), rsn->mgtk, rsn->mgtk_len);
return 0;
@@ -491,65 +494,89 @@ int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta,
{
struct ieee80211_ampe_ie *ampe;
u8 const *ie = wpabuf_head_u8(buf) + wpabuf_len(buf);
- u8 *ampe_ie = NULL, *mic_ie = NULL, *mic_payload;
+ u8 *ampe_ie, *pos, *mic_payload;
const u8 *aad[] = { rsn->wpa_s->own_addr, sta->addr, cat };
const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, ie - cat };
int ret = 0;
+ size_t len;
- if (AES_BLOCK_SIZE + 2 + sizeof(*ampe) + 2 > wpabuf_tailroom(buf)) {
+ len = sizeof(*ampe) + rsn->mgtk_len + WPA_KEY_RSC_LEN + 4;
+#ifdef CONFIG_IEEE80211W
+ if (rsn->igtk_len)
+ len += 2 + 6 + rsn->igtk_len;
+#endif /* CONFIG_IEEE80211W */
+
+ if (2 + AES_BLOCK_SIZE + 2 + len > wpabuf_tailroom(buf)) {
wpa_printf(MSG_ERROR, "protect frame: buffer too small");
return -EINVAL;
}
- ampe_ie = os_zalloc(2 + sizeof(*ampe));
+ ampe_ie = os_zalloc(2 + len);
if (!ampe_ie) {
wpa_printf(MSG_ERROR, "protect frame: out of memory");
return -ENOMEM;
}
- mic_ie = os_zalloc(2 + AES_BLOCK_SIZE);
- if (!mic_ie) {
- wpa_printf(MSG_ERROR, "protect frame: out of memory");
- ret = -ENOMEM;
- goto free;
- }
-
/* IE: AMPE */
ampe_ie[0] = WLAN_EID_AMPE;
- ampe_ie[1] = sizeof(*ampe);
+ ampe_ie[1] = len;
ampe = (struct ieee80211_ampe_ie *) (ampe_ie + 2);
RSN_SELECTOR_PUT(ampe->selected_pairwise_suite,
- wpa_cipher_to_suite(WPA_PROTO_RSN, WPA_CIPHER_CCMP));
+ RSN_CIPHER_SUITE_CCMP);
os_memcpy(ampe->local_nonce, sta->my_nonce, WPA_NONCE_LEN);
os_memcpy(ampe->peer_nonce, sta->peer_nonce, WPA_NONCE_LEN);
- /* incomplete: see 13.5.4 */
+
+ pos = (u8 *) (ampe + 1);
+
+ /* TODO: Key Replay Counter[8] optionally for
+ * Mesh Group Key Inform/Acknowledge frames */
+
/* TODO: static mgtk for now since we don't support rekeying! */
- os_memcpy(ampe->mgtk, rsn->mgtk, 16);
- /* TODO: Populate Key RSC */
- /* expire in 13 decades or so */
- os_memset(ampe->key_expiration, 0xff, 4);
+ /*
+ * GTKdata[variable]:
+ * MGTK[variable] || Key RSC[8] || GTKExpirationTime[4]
+ */
+ os_memcpy(pos, rsn->mgtk, rsn->mgtk_len);
+ pos += rsn->mgtk_len;
+ wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->mgtk_key_id, pos);
+ pos += WPA_KEY_RSC_LEN;
+ /* Use fixed GTKExpirationTime for now */
+ WPA_PUT_LE32(pos, 0xffffffff);
+ pos += 4;
+
+#ifdef CONFIG_IEEE80211W
+ /*
+ * IGTKdata[variable]:
+ * Key ID[2], IPN[6], IGTK[variable]
+ */
+ if (rsn->igtk_len) {
+ WPA_PUT_LE16(pos, rsn->igtk_key_id);
+ pos += 2;
+ wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->igtk_key_id, pos);
+ pos += 6;
+ os_memcpy(pos, rsn->igtk, rsn->igtk_len);
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ wpa_hexdump_key(MSG_DEBUG, "mesh: Plaintext AMPE element",
+ ampe_ie, 2 + len);
/* IE: MIC */
- mic_ie[0] = WLAN_EID_MIC;
- mic_ie[1] = AES_BLOCK_SIZE;
- wpabuf_put_data(buf, mic_ie, 2);
+ wpabuf_put_u8(buf, WLAN_EID_MIC);
+ wpabuf_put_u8(buf, AES_BLOCK_SIZE);
/* MIC field is output ciphertext */
/* encrypt after MIC */
- mic_payload = (u8 *) wpabuf_put(buf, 2 + sizeof(*ampe) +
- AES_BLOCK_SIZE);
+ mic_payload = wpabuf_put(buf, 2 + len + AES_BLOCK_SIZE);
- if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + sizeof(*ampe), 3,
+ if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + len, 3,
aad, aad_len, mic_payload)) {
wpa_printf(MSG_ERROR, "protect frame: failed to encrypt");
ret = -ENOMEM;
- goto free;
}
-free:
os_free(ampe_ie);
- os_free(mic_ie);
return ret;
}
@@ -565,11 +592,12 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
u8 null_nonce[WPA_NONCE_LEN] = {};
u8 ampe_eid;
u8 ampe_ie_len;
- u8 *ampe_buf, *crypt = NULL;
+ u8 *ampe_buf, *crypt = NULL, *pos, *end;
size_t crypt_len;
const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat };
const size_t aad_len[] = { ETH_ALEN, ETH_ALEN,
(elems->mic - 2) - cat };
+ size_t key_len;
if (!sta->sae) {
struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
@@ -598,7 +626,7 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
return -1;
crypt_len = elems_len - (elems->mic - start);
- if (crypt_len < 2) {
+ if (crypt_len < 2 + AES_BLOCK_SIZE) {
wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing ampe ie");
return -1;
}
@@ -620,10 +648,15 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
goto free;
}
+ crypt_len -= AES_BLOCK_SIZE;
+ wpa_hexdump_key(MSG_DEBUG, "mesh: Decrypted AMPE element",
+ ampe_buf, crypt_len);
+
ampe_eid = *ampe_buf++;
ampe_ie_len = *ampe_buf++;
if (ampe_eid != WLAN_EID_AMPE ||
+ (size_t) 2 + ampe_ie_len > crypt_len ||
ampe_ie_len < sizeof(struct ieee80211_ampe_ie)) {
wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid ampe ie");
ret = -1;
@@ -631,6 +664,8 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
}
ampe = (struct ieee80211_ampe_ie *) ampe_buf;
+ pos = (u8 *) (ampe + 1);
+ end = ampe_buf + ampe_ie_len;
if (os_memcmp(ampe->peer_nonce, null_nonce, WPA_NONCE_LEN) != 0 &&
os_memcmp(ampe->peer_nonce, sta->my_nonce, WPA_NONCE_LEN) != 0) {
wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid peer nonce");
@@ -639,10 +674,56 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
}
os_memcpy(sta->peer_nonce, ampe->local_nonce,
sizeof(ampe->local_nonce));
- os_memcpy(sta->mgtk, ampe->mgtk, sizeof(ampe->mgtk));
- sta->mgtk_len = sizeof(ampe->mgtk);
- /* todo parse mgtk expiration */
+ /* TODO: Key Replay Counter[8] in Mesh Group Key Inform/Acknowledge
+ * frames */
+
+ /*
+ * GTKdata[variable]:
+ * MGTK[variable] || Key RSC[8] || GTKExpirationTime[4]
+ */
+ key_len = wpa_cipher_key_len(WPA_CIPHER_CCMP);
+ if ((int) key_len + WPA_KEY_RSC_LEN + 4 > end - pos) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "mesh: Truncated AMPE element");
+ ret = -1;
+ goto free;
+ }
+ sta->mgtk_len = key_len;
+ os_memcpy(sta->mgtk, pos, sta->mgtk_len);
+ wpa_hexdump_key(MSG_DEBUG, "mesh: GTKdata - MGTK",
+ sta->mgtk, sta->mgtk_len);
+ pos += sta->mgtk_len;
+ wpa_hexdump(MSG_DEBUG, "mesh: GTKdata - MGTK - Key RSC",
+ pos, WPA_KEY_RSC_LEN);
+ os_memcpy(sta->mgtk_rsc, pos, sizeof(sta->mgtk_rsc));
+ pos += WPA_KEY_RSC_LEN;
+ wpa_printf(MSG_DEBUG,
+ "mesh: GTKdata - MGTK - GTKExpirationTime: %u seconds",
+ WPA_GET_LE32(pos));
+ pos += 4;
+
+#ifdef CONFIG_IEEE80211W
+ /*
+ * IGTKdata[variable]:
+ * Key ID[2], IPN[6], IGTK[variable]
+ */
+ key_len = wpa_cipher_key_len(WPA_CIPHER_AES_128_CMAC);
+ if (end - pos >= (int) (2 + 6 + key_len)) {
+ sta->igtk_key_id = WPA_GET_LE16(pos);
+ wpa_printf(MSG_DEBUG, "mesh: IGTKdata - Key ID %u",
+ sta->igtk_key_id);
+ pos += 2;
+ os_memcpy(sta->igtk_rsc, pos, sizeof(sta->igtk_rsc));
+ wpa_hexdump(MSG_DEBUG, "mesh: IGTKdata - IPN",
+ sta->igtk_rsc, sizeof(sta->igtk_rsc));
+ pos += 6;
+ os_memcpy(sta->igtk, pos, key_len);
+ sta->igtk_len = key_len;
+ wpa_hexdump_key(MSG_DEBUG, "mesh: IGTKdata - IGTK",
+ sta->igtk, sta->igtk_len);
+ }
+#endif /* CONFIG_IEEE80211W */
+
free:
os_free(crypt);
return ret;