]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
Support Key Data field decryption for EAPOL-Key msg 2/4 and 4/4
authorJouni Malinen <quic_jouni@quicinc.com>
Tue, 16 Jan 2024 19:05:13 +0000 (21:05 +0200)
committerJouni Malinen <j@w1.fi>
Tue, 16 Jan 2024 19:05:13 +0000 (21:05 +0200)
Extend RSN authenticator to be able to process EAPOL-Key msg 2/4 and 4/4
messages in cases where the Key Data field is encrypted using AES key
wrapping (i.e., non-AEAD cipher). While there is not yet any defined
case where such encryption would be used in IEEE Std 802.11-2020,
extensions are considered to be added to use such constructions (e.g.,
in IEEE P802.11bh). As such, it is good to extend the parsing and
processing rules in the authenticator to be ready for such
functionality.

Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
hostapd/Android.mk
hostapd/Makefile
src/ap/wpa_auth.c

index 3a243d13b43965c871d1cb8bb59909527693ee17..3cc2b51b890b163a8c66534cc3c0e18dd3abf350 100644 (file)
@@ -237,6 +237,8 @@ L_CFLAGS += -DCONFIG_OCV
 OBJS += src/common/ocv.c
 endif
 
+NEED_AES_UNWRAP=y
+
 ifdef CONFIG_IEEE80211R
 L_CFLAGS += -DCONFIG_IEEE80211R -DCONFIG_IEEE80211R_AP
 OBJS += src/ap/wpa_auth_ft.c
index 500098c8bae494f08261b9023f149ec5febbb3c3..bfafe73b751488a3cf40bc0bef0b85580aed5b79 100644 (file)
@@ -276,6 +276,8 @@ CFLAGS += -DCONFIG_OCV
 OBJS += ../src/common/ocv.o
 endif
 
+NEED_AES_UNWRAP=y
+
 ifdef CONFIG_IEEE80211R
 CFLAGS += -DCONFIG_IEEE80211R -DCONFIG_IEEE80211R_AP
 OBJS += ../src/ap/wpa_auth_ft.o
index 13e3b2e654370023de57f5f49961b75f54cccc53..e6fe8e9bbc0250b9bb77fd28335ce7cf9da43a48 100644 (file)
@@ -1100,7 +1100,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
 {
        struct ieee802_1x_hdr *hdr;
        struct wpa_eapol_key *key;
-       u16 key_info, key_data_length;
+       u16 key_info, ver, key_data_length;
        enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg;
        char *msgtxt;
        struct wpa_eapol_ie_parse kde;
@@ -1108,6 +1108,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
        size_t keyhdrlen, mic_len;
        u8 *mic;
        bool is_mld = false;
+       u8 *key_data_buf = NULL;
+       size_t key_data_buf_len = 0;
 
        if (!wpa_auth || !wpa_auth->conf.wpa || !sm)
                return;
@@ -1185,6 +1187,30 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                return;
        }
 
+       ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+       if (mic_len > 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
+           sm->PTK_valid &&
+           (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+            ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
+            wpa_use_aes_key_wrap(sm->wpa_key_mgmt)) &&
+           key_data_length >= 8 && key_data_length % 8 == 0) {
+               key_data_length -= 8; /* AES-WRAP adds 8 bytes */
+               key_data_buf = os_malloc(key_data_length);
+               if (!key_data_buf)
+                       goto out;
+               key_data_buf_len = key_data_length;
+               if (aes_unwrap(sm->PTK.kek, sm->PTK.kek_len,
+                              key_data_length / 8, key_data, key_data_buf)) {
+                       bin_clear_free(key_data_buf, key_data_buf_len);
+                       wpa_printf(MSG_INFO,
+                                  "RSN: AES unwrap failed - could not decrypt EAPOL-Key key data");
+                       goto out;
+               }
+               key_data = key_data_buf;
+               wpa_hexdump_key(MSG_DEBUG, "RSN: Decrypted EAPOL-Key Key Data",
+                               key_data, key_data_length);
+       }
+
        /* TODO: Make this more robust for distinguising EAPOL-Key msg 2/4 from
         * 4/4. Secure=1 is used in msg 2/4 when doing PTK rekeying, so the
         * MLD mechanism here does not work without the somewhat undesired check
@@ -1198,7 +1224,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                msgtxt = "2/2 Group";
        } else if (key_data_length == 0 ||
                   (sm->wpa == WPA_VERSION_WPA2 &&
-                   !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
+                   (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA) ||
+                    key_data_buf) &&
                    (key_info & WPA_KEY_INFO_SECURE) &&
                    !get_ie(key_data, key_data_length, WLAN_EID_RSN)) ||
                   (mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
@@ -1214,7 +1241,6 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
 
        if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
            msg == GROUP_2) {
-               u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
                if (sm->pairwise == WPA_CIPHER_CCMP ||
                    sm->pairwise == WPA_CIPHER_GCMP) {
                        if (wpa_use_cmac(sm->wpa_key_mgmt) &&
@@ -1223,7 +1249,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                                wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
                                                LOGGER_WARNING,
                                                "advertised support for AES-128-CMAC, but did not use it");
-                               return;
+                               goto out;
                        }
 
                        if (!wpa_use_cmac(sm->wpa_key_mgmt) &&
@@ -1232,7 +1258,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                                wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
                                                LOGGER_WARNING,
                                                "did not use HMAC-SHA1-AES with CCMP/GCMP");
-                               return;
+                               goto out;
                        }
                }
 
@@ -1241,7 +1267,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                        wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
                                        LOGGER_WARNING,
                                        "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
-                       return;
+                       goto out;
                }
        }
 
@@ -1252,7 +1278,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                        wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
                                        LOGGER_WARNING,
                                        "received EAPOL-Key request with replayed counter");
-                       return;
+                       goto out;
                }
        }
 
@@ -1323,7 +1349,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                }
                wpa_hexdump(MSG_DEBUG, "received replay counter",
                            key->replay_counter, WPA_REPLAY_COUNTER_LEN);
-               return;
+               goto out;
        }
 
 continue_processing:
@@ -1332,7 +1358,7 @@ continue_processing:
            !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
                wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
                                 "WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame");
-               return;
+               goto out;
        }
 #endif /* CONFIG_FILS */
 
@@ -1346,7 +1372,7 @@ continue_processing:
                                         LOGGER_INFO,
                                         "received EAPOL-Key msg 2/4 in invalid state (%d) - dropped",
                                         sm->wpa_ptk_state);
-                       return;
+                       goto out;
                }
                random_add_randomness(key->key_nonce, WPA_NONCE_LEN);
                if (sm->group->reject_4way_hs_for_entropy) {
@@ -1364,7 +1390,7 @@ continue_processing:
                        random_mark_pool_ready();
                        wpa_sta_disconnect(wpa_auth, sm->addr,
                                           WLAN_REASON_PREV_AUTH_NOT_VALID);
-                       return;
+                       goto out;
                }
                break;
        case PAIRWISE_4:
@@ -1374,7 +1400,7 @@ continue_processing:
                                         LOGGER_INFO,
                                         "received EAPOL-Key msg 4/4 in invalid state (%d) - dropped",
                                         sm->wpa_ptk_state);
-                       return;
+                       goto out;
                }
                break;
        case GROUP_2:
@@ -1384,7 +1410,7 @@ continue_processing:
                                         LOGGER_INFO,
                                         "received EAPOL-Key msg 2/2 in invalid state (%d) - dropped",
                                         sm->wpa_ptk_group_state);
-                       return;
+                       goto out;
                }
                break;
        case REQUEST:
@@ -1397,14 +1423,14 @@ continue_processing:
        if (key_info & WPA_KEY_INFO_ACK) {
                wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
                                "received invalid EAPOL-Key: Key Ack set");
-               return;
+               goto out;
        }
 
        if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
            !(key_info & WPA_KEY_INFO_MIC)) {
                wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
                                "received invalid EAPOL-Key: Key MIC not set");
-               return;
+               goto out;
        }
 
 #ifdef CONFIG_FILS
@@ -1412,7 +1438,7 @@ continue_processing:
            (key_info & WPA_KEY_INFO_MIC)) {
                wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
                                "received invalid EAPOL-Key: Key MIC set");
-               return;
+               goto out;
        }
 #endif /* CONFIG_FILS */
 
@@ -1431,7 +1457,7 @@ continue_processing:
                                   "TEST: Ignore Key MIC failure for fuzz testing");
                        goto continue_fuzz;
 #endif /* TEST_FUZZ */
-                       return;
+                       goto out;
                }
 #ifdef CONFIG_FILS
                if (!mic_len &&
@@ -1445,7 +1471,7 @@ continue_processing:
                                   "TEST: Ignore Key MIC failure for fuzz testing");
                        goto continue_fuzz;
 #endif /* TEST_FUZZ */
-                       return;
+                       goto out;
                }
 #endif /* CONFIG_FILS */
 #ifdef TEST_FUZZ
@@ -1465,7 +1491,7 @@ continue_processing:
                        wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
                                        LOGGER_INFO,
                                        "received EAPOL-Key request with invalid MIC");
-                       return;
+                       goto out;
                }
 
                /*
@@ -1477,7 +1503,7 @@ continue_processing:
                        if (wpa_receive_error_report(
                                    wpa_auth, sm,
                                    !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
-                               return; /* STA entry was removed */
+                               goto out; /* STA entry was removed */
                } else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
                        wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
                                        LOGGER_INFO,
@@ -1528,7 +1554,7 @@ continue_processing:
        os_free(sm->last_rx_eapol_key);
        sm->last_rx_eapol_key = os_memdup(data, data_len);
        if (!sm->last_rx_eapol_key)
-               return;
+               goto out;
        sm->last_rx_eapol_key_len = data_len;
 
        sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
@@ -1537,6 +1563,9 @@ continue_processing:
        sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST);
        os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN);
        wpa_sm_step(sm);
+
+out:
+       bin_clear_free(key_data_buf, key_data_buf_len);
 }
 
 
@@ -3283,7 +3312,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
        size_t pmk_len;
        int ft;
        const u8 *eapol_key_ie, *key_data, *mic;
-       u16 key_data_length;
+       u16 key_info, ver, key_data_length;
        size_t mic_len, eapol_key_ie_len;
        struct ieee802_1x_hdr *hdr;
        struct wpa_eapol_key *key;
@@ -3293,6 +3322,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
        u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
        u8 pmk_r1[PMK_LEN_MAX];
        size_t key_len;
+       u8 *key_data_buf = NULL;
+       size_t key_data_buf_len = 0;
 
        SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
        sm->EAPOLKeyReceived = false;
@@ -3400,12 +3431,46 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
        hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
        key = (struct wpa_eapol_key *) (hdr + 1);
        mic = (u8 *) (key + 1);
+       key_info = WPA_GET_BE16(key->key_info);
        key_data = mic + mic_len + 2;
        key_data_length = WPA_GET_BE16(mic + mic_len);
        if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
            sizeof(*key) - mic_len - 2)
                goto out;
 
+       ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+       if (mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+               if (ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
+                   ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+                   !wpa_use_aes_key_wrap(sm->wpa_key_mgmt)) {
+                       wpa_printf(MSG_INFO,
+                                  "Unsupported EAPOL-Key Key Data field encryption");
+                       goto out;
+               }
+
+               if (key_data_length < 8 || key_data_length % 8) {
+                       wpa_printf(MSG_INFO,
+                                  "RSN: Unsupported AES-WRAP len %u",
+                                  key_data_length);
+                       goto out;
+               }
+               key_data_length -= 8; /* AES-WRAP adds 8 bytes */
+               key_data_buf = os_malloc(key_data_length);
+               if (!key_data_buf)
+                       goto out;
+               key_data_buf_len = key_data_length;
+               if (aes_unwrap(PTK.kek, PTK.kek_len, key_data_length / 8,
+                              key_data, key_data_buf)) {
+                       bin_clear_free(key_data_buf, key_data_buf_len);
+                       wpa_printf(MSG_INFO,
+                                  "RSN: AES unwrap failed - could not decrypt EAPOL-Key key data");
+                       goto out;
+               }
+               key_data = key_data_buf;
+               wpa_hexdump_key(MSG_DEBUG, "RSN: Decrypted EAPOL-Key Key Data",
+                               key_data, key_data_length);
+       }
+
        if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
                wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
                                 "received EAPOL-Key msg 2/4 with invalid Key Data contents");
@@ -3605,6 +3670,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
 out:
        forced_memzero(pmk_r0, sizeof(pmk_r0));
        forced_memzero(pmk_r1, sizeof(pmk_r1));
+       bin_clear_free(key_data_buf, key_data_buf_len);
 }