From 4bc801ab42eb9308e82bc9ac35f82c13d497c80e Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 16 Mar 2018 13:04:15 +0200 Subject: [PATCH] SAE: Fix EAPOL-Key integrity and key-wrap algorithm selection The SAE AKM 00-0F-AC:8 is supposed to use EAPOL-Key Key Descriptor Version 0 (AKM-defined) with AES-128-CMAC and NIST AES Key Wrap. However, the previous implementation ended up using Key Descriptor Version 2 (HMAC-SHA-1-128 and NIST AES Key Wrap). Fix this by using the appropriate Key Descriptor Version and integrity algorithm. Use helper functions to keep the selection clearer and more consistent between wpa_supplicant and hostapd uses. Note: This change is not backwards compatible. Both the AP and station side implementations will need to be updated at the same time to maintain functionality. Signed-off-by: Jouni Malinen --- src/ap/wpa_auth.c | 53 ++++++----------------------------- src/common/wpa_common.c | 61 +++++++++++++++++++++++++++++++++++++++++ src/common/wpa_common.h | 3 ++ src/rsn_supp/wpa.c | 41 ++++++--------------------- 4 files changed, 82 insertions(+), 76 deletions(-) diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index f3d1bbdc4..203329eeb 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -238,23 +238,6 @@ static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, } -static int wpa_use_aes_cmac(struct wpa_state_machine *sm) -{ - int ret = 0; -#ifdef CONFIG_IEEE80211R_AP - if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) - ret = 1; -#endif /* CONFIG_IEEE80211R_AP */ -#ifdef CONFIG_IEEE80211W - if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt)) - ret = 1; -#endif /* CONFIG_IEEE80211W */ - if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) - ret = 1; - return ret; -} - - static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; @@ -1010,10 +993,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK; if (sm->pairwise == WPA_CIPHER_CCMP || 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) && - !wpa_key_mgmt_fils(sm->wpa_key_mgmt) && + if (wpa_use_cmac(sm->wpa_key_mgmt) && + !wpa_use_akm_defined(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, @@ -1023,11 +1004,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, return; } - if (!wpa_use_aes_cmac(sm) && - !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && - !wpa_key_mgmt_fils(sm->wpa_key_mgmt) && - sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE && - sm->wpa_key_mgmt != WPA_KEY_MGMT_DPP && + if (!wpa_use_cmac(sm->wpa_key_mgmt) && + !wpa_use_akm_defined(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, @@ -1037,10 +1015,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } } - if ((wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || - wpa_key_mgmt_fils(sm->wpa_key_mgmt) || - sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP || - sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) && + if (wpa_use_akm_defined(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"); @@ -1401,13 +1376,9 @@ 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 || - sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE || - sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP || - wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || - wpa_key_mgmt_fils(sm->wpa_key_mgmt)) + else if (wpa_use_akm_defined(sm->wpa_key_mgmt)) version = WPA_KEY_INFO_TYPE_AKM_DEFINED; - else if (wpa_use_aes_cmac(sm)) + else if (wpa_use_cmac(sm->wpa_key_mgmt)) version = WPA_KEY_INFO_TYPE_AES_128_CMAC; else if (sm->pairwise != WPA_CIPHER_TKIP) version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; @@ -1429,10 +1400,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, key_data_len = kde_len; if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || - sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE || - sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP || - sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || - wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || + wpa_use_aes_key_wrap(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) { pad_len = key_data_len % 8; if (pad_len) @@ -1531,10 +1499,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", buf, key_data_len); if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || - sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE || - sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP || - sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || - wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || + wpa_use_aes_key_wrap(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_printf(MSG_DEBUG, "WPA: Encrypt Key Data using AES-WRAP (KEK length %u)", diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 853594ad9..13ae42272 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -81,6 +81,60 @@ unsigned int wpa_mic_len(int akmp, size_t pmk_len) } +/** + * wpa_use_akm_defined - Is AKM-defined Key Descriptor Version used + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: 1 if AKM-defined Key Descriptor Version is used; 0 otherwise + */ +int wpa_use_akm_defined(int akmp) +{ + return akmp == WPA_KEY_MGMT_OSEN || + akmp == WPA_KEY_MGMT_OWE || + akmp == WPA_KEY_MGMT_DPP || + wpa_key_mgmt_sae(akmp) || + wpa_key_mgmt_suite_b(akmp) || + wpa_key_mgmt_fils(akmp); +} + + +/** + * wpa_use_cmac - Is CMAC integrity algorithm used for EAPOL-Key MIC + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: 1 if CMAC is used; 0 otherwise + */ +int wpa_use_cmac(int akmp) +{ + return akmp == WPA_KEY_MGMT_OSEN || + akmp == WPA_KEY_MGMT_OWE || + akmp == WPA_KEY_MGMT_DPP || + wpa_key_mgmt_ft(akmp) || + wpa_key_mgmt_sha256(akmp) || + wpa_key_mgmt_sae(akmp) || + wpa_key_mgmt_suite_b(akmp); +} + + +/** + * wpa_use_aes_key_wrap - Is AES Keywrap algorithm used for EAPOL-Key Key Data + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: 1 if AES Keywrap is used; 0 otherwise + * + * Note: AKM 00-0F-AC:1 and 00-0F-AC:2 have special rules for selecting whether + * to use AES Keywrap based on the negotiated pairwise cipher. This function + * does not cover those special cases. + */ +int wpa_use_aes_key_wrap(int akmp) +{ + return akmp == WPA_KEY_MGMT_OSEN || + akmp == WPA_KEY_MGMT_OWE || + akmp == WPA_KEY_MGMT_DPP || + wpa_key_mgmt_ft(akmp) || + wpa_key_mgmt_sha256(akmp) || + wpa_key_mgmt_sae(akmp) || + wpa_key_mgmt_suite_b(akmp); +} + + /** * wpa_eapol_key_mic - Calculate EAPOL-Key MIC * @key: EAPOL-Key Key Confirmation Key (KCK) @@ -131,6 +185,13 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ case WPA_KEY_INFO_TYPE_AKM_DEFINED: switch (akmp) { +#ifdef CONFIG_SAE + case WPA_KEY_MGMT_SAE: + case WPA_KEY_MGMT_FT_SAE: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - SAE)"); + return omac1_aes_128(key, buf, len, mic); +#endif /* CONFIG_SAE */ #ifdef CONFIG_HS20 case WPA_KEY_MGMT_OSEN: wpa_printf(MSG_DEBUG, diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 3b8c1fb93..5c918a4e6 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -461,6 +461,9 @@ int wpa_parse_cipher(const char *value); int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim); int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise); unsigned int wpa_mic_len(int akmp, size_t pmk_len); +int wpa_use_akm_defined(int akmp); +int wpa_use_cmac(int akmp); +int wpa_use_aes_key_wrap(int akmp); int fils_domain_name_hash(const char *domain, u8 *hash); #endif /* WPA_COMMON_H */ diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 07fad4517..ea54f327e 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -181,8 +181,7 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) int key_info, ver; u8 bssid[ETH_ALEN], *rbuf, *key_mic, *mic; - if (sm->key_mgmt == WPA_KEY_MGMT_OSEN || - wpa_key_mgmt_suite_b(sm->key_mgmt)) + if (wpa_use_akm_defined(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)) @@ -1814,10 +1813,7 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, #endif /* CONFIG_NO_RC4 */ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || ver == WPA_KEY_INFO_TYPE_AES_128_CMAC || - sm->key_mgmt == WPA_KEY_MGMT_OWE || - sm->key_mgmt == WPA_KEY_MGMT_DPP || - sm->key_mgmt == WPA_KEY_MGMT_OSEN || - wpa_key_mgmt_suite_b(sm->key_mgmt)) { + wpa_use_aes_key_wrap(sm->key_mgmt)) { u8 *buf; wpa_printf(MSG_DEBUG, @@ -2094,29 +2090,14 @@ 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) && - !wpa_key_mgmt_fils(sm->key_mgmt) && - sm->key_mgmt != WPA_KEY_MGMT_OWE && - sm->key_mgmt != WPA_KEY_MGMT_DPP && - sm->key_mgmt != WPA_KEY_MGMT_OSEN) { + !wpa_use_akm_defined(sm->key_mgmt)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor version %d", ver); goto out; } - if (sm->key_mgmt == WPA_KEY_MGMT_OSEN && - ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { - wpa_msg(sm->ctx->msg_ctx, MSG_INFO, - "OSEN: Unsupported EAPOL-Key descriptor version %d", - ver); - goto out; - } - - if ((wpa_key_mgmt_suite_b(sm->key_mgmt) || - wpa_key_mgmt_fils(sm->key_mgmt) || - sm->key_mgmt == WPA_KEY_MGMT_DPP || - sm->key_mgmt == WPA_KEY_MGMT_OWE) && + if (wpa_use_akm_defined(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)", @@ -2127,7 +2108,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */ - if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { + if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && + !wpa_use_akm_defined(sm->key_mgmt)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "FT: AP did not use AES-128-CMAC"); goto out; @@ -2137,9 +2119,7 @@ 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 && - !wpa_key_mgmt_fils(sm->key_mgmt) && - !wpa_key_mgmt_suite_b(sm->key_mgmt)) { + !wpa_use_akm_defined(sm->key_mgmt)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: AP did not use the " "negotiated AES-128-CMAC"); @@ -2148,10 +2128,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) && - !wpa_key_mgmt_fils(sm->key_mgmt) && - sm->key_mgmt != WPA_KEY_MGMT_OWE && - sm->key_mgmt != WPA_KEY_MGMT_DPP && + !wpa_use_akm_defined(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 " @@ -2171,7 +2148,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) && + !wpa_use_akm_defined(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 "