From 929a2ea5df7a56c83a84cdea96a56c9be220329d Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 16 Nov 2014 15:40:02 +0200 Subject: [PATCH] Suite B: Select EAPOL-Key integrity and key-wrap algorithms based on AKM This adds support for AKM 00-0F-AC:11 to specify the integrity and key-wrap algorithms for EAPOL-Key frames using the new design where descriptor version is set to 0 and algorithms are determined based on AKM. Signed-off-by: Jouni Malinen --- src/ap/wpa_auth.c | 38 ++++++++++++++++++++++++++++---------- src/common/wpa_common.c | 25 +++++++++++++++++++------ src/common/wpa_common.h | 4 ++-- src/rsn_supp/peerkey.c | 6 +++--- src/rsn_supp/wpa.c | 30 ++++++++++++++++++++++-------- wlantest/rx_eapol.c | 25 ++++++++++++++++--------- 6 files changed, 90 insertions(+), 38 deletions(-) diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 562801a59..0d2a311c5 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -33,7 +33,8 @@ static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); static int wpa_sm_step(struct wpa_state_machine *sm); -static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len); +static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, + size_t data_len); static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, struct wpa_group *group); @@ -884,6 +885,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, sm->pairwise == WPA_CIPHER_GCMP) { if (wpa_use_aes_cmac(sm) && sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN && + !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, @@ -902,6 +904,13 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, return; } } + + if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && + ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, + "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases"); + return; + } } if (key_info & WPA_KEY_INFO_REQUEST) { @@ -1123,7 +1132,8 @@ continue_processing: sm->MICVerified = FALSE; if (sm->PTK_valid && !sm->update_snonce) { - if (wpa_verify_key_mic(&sm->PTK, data, data_len)) { + if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data, + data_len)) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key with invalid MIC"); return; @@ -1295,7 +1305,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, if (force_version) version = force_version; - else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) + else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) version = WPA_KEY_INFO_TYPE_AKM_DEFINED; else if (wpa_use_aes_cmac(sm)) version = WPA_KEY_INFO_TYPE_AES_128_CMAC; @@ -1320,6 +1331,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) { pad_len = key_data_len % 8; if (pad_len) @@ -1389,6 +1401,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, buf, key_data_len); if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { if (aes_wrap(sm->PTK.kek, 16, (key_data_len - 8) / 8, buf, @@ -1420,8 +1433,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, os_free(hdr); return; } - wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len, - key->key_mic); + wpa_eapol_key_mic(sm->PTK.kck, sm->wpa_key_mgmt, version, + (u8 *) hdr, len, key->key_mic); #ifdef CONFIG_TESTING_OPTIONS if (!pairwise && wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 && @@ -1473,7 +1486,8 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, } -static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) +static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, + size_t data_len) { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; @@ -1489,7 +1503,7 @@ static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) key_info = WPA_GET_BE16(key->key_info); os_memcpy(mic, key->key_mic, 16); os_memset(key->key_mic, 0, 16); - if (wpa_eapol_key_mic(PTK->kck, key_info & WPA_KEY_INFO_TYPE_MASK, + if (wpa_eapol_key_mic(PTK->kck, akmp, key_info & WPA_KEY_INFO_TYPE_MASK, data, data_len, key->key_mic) || os_memcmp_const(mic, key->key_mic, 16) != 0) ret = -1; @@ -1795,10 +1809,13 @@ SM_STATE(WPA_PTK, PTKSTART) pmkid[0] = WLAN_EID_VENDOR_SPECIFIC; pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID); - if (sm->pmksa) + if (sm->pmksa) { os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], sm->pmksa->pmkid, PMKID_LEN); - else { + } else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) { + /* No KCK available to derive PMKID */ + pmkid = NULL; + } else { /* * Calculate PMKID since no PMKSA cache entry was * available with pre-calculated PMKID. @@ -1856,7 +1873,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) wpa_derive_ptk(sm, pmk, &PTK); - if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key, + if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, + sm->last_rx_eapol_key, sm->last_rx_eapol_key_len) == 0) { ok = 1; break; diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index f87e7910e..2970d0f0f 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -22,6 +22,7 @@ /** * wpa_eapol_key_mic - Calculate EAPOL-Key MIC * @key: EAPOL-Key Key Confirmation Key (KCK) + * @akmp: WPA_KEY_MGMT_* used in key derivation * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*) * @buf: Pointer to the beginning of the EAPOL header (version field) * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame) @@ -37,10 +38,10 @@ * happened during final editing of the standard and the correct behavior is * defined in the last draft (IEEE 802.11i/D10). */ -int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, - u8 *mic) +int wpa_eapol_key_mic(const u8 *key, int akmp, int ver, const u8 *buf, + size_t len, u8 *mic) { - u8 hash[SHA1_MAC_LEN]; + u8 hash[SHA256_MAC_LEN]; switch (ver) { #ifndef CONFIG_FIPS @@ -56,11 +57,23 @@ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, case WPA_KEY_INFO_TYPE_AES_128_CMAC: return omac1_aes_128(key, buf, len, mic); #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ -#ifdef CONFIG_HS20 case WPA_KEY_INFO_TYPE_AKM_DEFINED: - /* FIX: This should be based on negotiated AKM */ - return omac1_aes_128(key, buf, len, mic); + switch (akmp) { +#ifdef CONFIG_HS20 + case WPA_KEY_MGMT_OSEN: + return omac1_aes_128(key, buf, len, mic); #endif /* CONFIG_HS20 */ +#ifdef CONFIG_SUITEB + case WPA_KEY_MGMT_IEEE8021X_SUITE_B: + if (hmac_sha256(key, 16, buf, len, hash)) + return -1; + os_memcpy(mic, hash, MD5_MAC_LEN); + break; +#endif /* CONFIG_SUITEB */ + default: + return -1; + } + break; default: return -1; } diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 3bacbdcea..17bed34a5 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -327,8 +327,8 @@ struct rsn_rdie { #endif /* _MSC_VER */ -int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, - u8 *mic); +int wpa_eapol_key_mic(const u8 *key, int akmp, int ver, const u8 *buf, + size_t len, u8 *mic); void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, const u8 *nonce1, const u8 *nonce2, diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c index 48b6f13bd..aca8f540c 100644 --- a/src/rsn_supp/peerkey.c +++ b/src/rsn_supp/peerkey.c @@ -928,8 +928,8 @@ int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, os_memcpy(mic, key->key_mic, 16); if (peerkey->tstk_set) { os_memset(key->key_mic, 0, 16); - wpa_eapol_key_mic(peerkey->tstk.kck, ver, buf, len, - key->key_mic); + wpa_eapol_key_mic(peerkey->tstk.kck, sm->key_mgmt, ver, buf, + len, key->key_mic); if (os_memcmp_const(mic, key->key_mic, 16) != 0) { wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " "when using TSTK - ignoring TSTK"); @@ -944,7 +944,7 @@ int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, if (!ok && peerkey->stk_set) { os_memset(key->key_mic, 0, 16); - wpa_eapol_key_mic(peerkey->stk.kck, ver, buf, len, + wpa_eapol_key_mic(peerkey->stk.kck, sm->key_mgmt, ver, buf, len, key->key_mic); if (os_memcmp_const(mic, key->key_mic, 16) != 0) { wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 9c840c698..1d38ba508 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -56,10 +56,10 @@ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, } } if (key_mic && - wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic)) { + wpa_eapol_key_mic(kck, sm->key_mgmt, ver, msg, msg_len, key_mic)) { wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, - "WPA: Failed to generate EAPOL-Key " - "version %d MIC", ver); + "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC", + ver, sm->key_mgmt); goto out; } wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, 16); @@ -89,7 +89,8 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) int key_info, ver; u8 bssid[ETH_ALEN], *rbuf; - if (sm->key_mgmt == WPA_KEY_MGMT_OSEN) + if (sm->key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->key_mgmt)) ver = WPA_KEY_INFO_TYPE_AKM_DEFINED; else if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt)) @@ -1451,7 +1452,7 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, os_memcpy(mic, key->key_mic, 16); if (sm->tptk_set) { os_memset(key->key_mic, 0, 16); - wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len, + wpa_eapol_key_mic(sm->tptk.kck, sm->key_mgmt, ver, buf, len, key->key_mic); if (os_memcmp_const(mic, key->key_mic, 16) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -1468,7 +1469,7 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, if (!ok && sm->ptk_set) { os_memset(key->key_mic, 0, 16); - wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len, + wpa_eapol_key_mic(sm->ptk.kck, sm->key_mgmt, ver, buf, len, key->key_mic); if (os_memcmp_const(mic, key->key_mic, 16) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -1522,7 +1523,8 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, os_memset(ek, 0, sizeof(ek)); } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || ver == WPA_KEY_INFO_TYPE_AES_128_CMAC || - sm->key_mgmt == WPA_KEY_MGMT_OSEN) { + sm->key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->key_mgmt)) { u8 *buf; if (*key_data_len < 8 || *key_data_len % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -1720,6 +1722,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor version %d", @@ -1735,6 +1738,14 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, goto out; } + if (wpa_key_mgmt_suite_b(sm->key_mgmt) && + ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)", + ver); + goto out; + } + #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */ @@ -1748,7 +1759,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(sm->key_mgmt)) { if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && - sm->key_mgmt != WPA_KEY_MGMT_OSEN) { + sm->key_mgmt != WPA_KEY_MGMT_OSEN && + !wpa_key_mgmt_suite_b(sm->key_mgmt)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: AP did not use the " "negotiated AES-128-CMAC"); @@ -1757,6 +1769,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, } else #endif /* CONFIG_IEEE80211W */ if (sm->pairwise_cipher == WPA_CIPHER_CCMP && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: CCMP is used, but EAPOL-Key " @@ -1776,6 +1789,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, } else goto out; } else if (sm->pairwise_cipher == WPA_CIPHER_GCMP && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: GCMP is used, but EAPOL-Key " diff --git a/wlantest/rx_eapol.c b/wlantest/rx_eapol.c index a1a9b057b..13510ef7a 100644 --- a/wlantest/rx_eapol.c +++ b/wlantest/rx_eapol.c @@ -31,7 +31,8 @@ static int is_zero(const u8 *buf, size_t len) } -static int check_mic(const u8 *kck, int ver, const u8 *data, size_t len) +static int check_mic(const u8 *kck, int akmp, int ver, const u8 *data, + size_t len) { u8 *buf; int ret = -1; @@ -49,7 +50,7 @@ static int check_mic(const u8 *kck, int ver, const u8 *data, size_t len) os_memcpy(rx_mic, key->key_mic, 16); os_memset(key->key_mic, 0, 16); - if (wpa_eapol_key_mic(kck, ver, buf, len, key->key_mic) == 0 && + if (wpa_eapol_key_mic(kck, akmp, ver, buf, len, key->key_mic) == 0 && os_memcmp(rx_mic, key->key_mic, 16) == 0) ret = 0; @@ -105,7 +106,7 @@ static int try_pmk(struct wlantest *wt, struct wlantest_bss *bss, bss->bssid, sta->addr, sta->anonce, sta->snonce, (u8 *) &ptk, ptk_len, wpa_key_mgmt_sha256(sta->key_mgmt)); - if (check_mic(ptk.kck, ver, data, len) < 0) + if (check_mic(ptk.kck, sta->key_mgmt, ver, data, len) < 0) return -1; sta->tk_len = wpa_cipher_key_len(sta->pairwise_cipher); @@ -168,7 +169,8 @@ static void derive_ptk(struct wlantest *wt, struct wlantest_bss *bss, wpa_debug_level = MSG_WARNING; dl_list_for_each(ptk, &wt->ptk, struct wlantest_ptk, list) { - if (check_mic(ptk->ptk.kck, ver, data, len) < 0) + if (check_mic(ptk->ptk.kck, sta->key_mgmt, ver, data, + len) < 0) continue; wpa_printf(MSG_INFO, "Pre-set PTK matches for STA " MACSTR " BSSID " MACSTR, @@ -241,7 +243,8 @@ static void rx_data_eapol_key_2_of_4(struct wlantest *wt, const u8 *dst, "Use TPTK for validation EAPOL-Key MIC"); kck = sta->tptk.kck; } - if (check_mic(kck, key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) { + if (check_mic(kck, sta->key_mgmt, key_info & WPA_KEY_INFO_TYPE_MASK, + data, len) < 0) { add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 2/4 MIC"); return; } @@ -532,7 +535,8 @@ static void rx_data_eapol_key_3_of_4(struct wlantest *wt, const u8 *dst, kck = sta->tptk.kck; kek = sta->tptk.kek; } - if (check_mic(kck, key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) { + if (check_mic(kck, sta->key_mgmt, key_info & WPA_KEY_INFO_TYPE_MASK, + data, len) < 0) { add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 3/4 MIC"); return; } @@ -677,7 +681,8 @@ static void rx_data_eapol_key_4_of_4(struct wlantest *wt, const u8 *dst, "Use TPTK for validation EAPOL-Key MIC"); kck = sta->tptk.kck; } - if (check_mic(kck, key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) { + if (check_mic(kck, sta->key_mgmt, key_info & WPA_KEY_INFO_TYPE_MASK, + data, len) < 0) { add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 4/4 MIC"); return; } @@ -724,7 +729,8 @@ static void rx_data_eapol_key_1_of_2(struct wlantest *wt, const u8 *dst, } if (sta->ptk_set && - check_mic(sta->ptk.kck, key_info & WPA_KEY_INFO_TYPE_MASK, + check_mic(sta->ptk.kck, sta->key_mgmt, + key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) { add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 1/2 MIC"); return; @@ -848,7 +854,8 @@ static void rx_data_eapol_key_2_of_2(struct wlantest *wt, const u8 *dst, } if (sta->ptk_set && - check_mic(sta->ptk.kck, key_info & WPA_KEY_INFO_TYPE_MASK, + check_mic(sta->ptk.kck, sta->key_mgmt, + key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) { add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 2/2 MIC"); return;