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>
This commit is contained in:
Jouni Malinen 2016-06-18 14:11:23 +03:00
parent 4367eec439
commit f868d5607d
6 changed files with 146 additions and 41 deletions

View file

@ -87,10 +87,13 @@ struct sta_info {
u8 aek[32]; /* SHA256 digest length */
u8 mtk[WPA_TK_MAX_LEN];
size_t mtk_len;
u8 mgtk_rsc[6];
u8 mgtk[WPA_TK_MAX_LEN];
size_t mgtk_len;
u8 igtk_rsc[6];
u8 igtk[WPA_TK_MAX_LEN];
size_t igtk_len;
u16 igtk_key_id;
u8 sae_auth_retry;
#endif /* CONFIG_MESH */

View file

@ -757,9 +757,14 @@ struct ieee80211_ampe_ie {
u8 selected_pairwise_suite[4];
u8 local_nonce[32];
u8 peer_nonce[32];
u8 mgtk[16];
u8 key_rsc[8];
u8 key_expiration[4];
/* Followed by
* Key Replay Counter[8] (optional)
* (only in Mesh Group Key Inform/Acknowledge frames)
* GTKdata[variable] (optional)
* (MGTK[variable] || Key RSC[8] || GTKExpirationTime[4])
* IGTKdata[variable] (optional)
* (Key ID[2], IPN[6], IGTK[variable] in IGTK KDE format)
*/
} STRUCT_PACKED;
#ifdef _MSC_VER

View file

@ -158,6 +158,15 @@ static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s,
return -1;
}
static inline int wpa_drv_get_seqnum(struct wpa_supplicant *wpa_s,
const u8 *addr, int idx, u8 *seq)
{
if (wpa_s->driver->get_seqnum)
return wpa_s->driver->get_seqnum(wpa_s->ifname, wpa_s->drv_priv,
addr, idx, seq);
return -1;
}
static inline int wpa_drv_sta_deauth(struct wpa_supplicant *wpa_s,
const u8 *addr, int reason_code)
{

View file

@ -798,20 +798,25 @@ static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s,
wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 0, 0,
seq, sizeof(seq), sta->mtk, sta->mtk_len);
wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK Key RSC",
sta->mgtk_rsc, sizeof(sta->mgtk_rsc));
wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK",
sta->mgtk, sta->mgtk_len);
/* TODO: support for other ciphers */
/* FIX: key index.. */
wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 1, 0,
seq, sizeof(seq),
sta->mgtk_rsc, sizeof(sta->mgtk_rsc),
sta->mgtk, sta->mgtk_len);
if (sta->igtk_len) {
wpa_hexdump_key(MSG_DEBUG, "mesh: RX IGTK Key RSC",
sta->igtk_rsc, sizeof(sta->igtk_rsc));
wpa_hexdump_key(MSG_DEBUG, "RX IGTK",
sta->igtk, sta->igtk_len);
/* FIX: key index.. */
wpa_drv_set_key(wpa_s, WPA_ALG_IGTK, sta->addr, 4, 0,
seq, sizeof(seq),
wpa_drv_set_key(wpa_s, WPA_ALG_IGTK, sta->addr,
sta->igtk_key_id, 0,
sta->igtk_rsc, sizeof(sta->igtk_rsc),
sta->igtk, sta->igtk_len);
}
}

View file

@ -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;

View file

@ -14,6 +14,8 @@ struct mesh_rsn {
struct wpa_authenticator *auth;
u8 mgtk[WPA_TK_MAX_LEN];
size_t mgtk_len;
u8 mgtk_key_id;
u8 igtk_key_id;
u8 igtk[WPA_TK_MAX_LEN];
size_t igtk_len;
#ifdef CONFIG_SAE