From e731ff3598e8094a438dd6556f165938a9c26c6d Mon Sep 17 00:00:00 2001 From: Nachiket Kukade Date: Tue, 19 May 2026 18:00:43 +0800 Subject: [PATCH] feat(wpa_supplicant): Add PASN Support to for NAN Pairing - Create pasn module from upstream. Changes till 1a791e9c - Add ecdh prime len api to MbedTLS port - Integrate nan and pasn modules for PIN code method - Fix KCK length and add auth timeout - Add NAN Pairing PASN support --- components/esp_wifi/Kconfig | 9 + .../esp_wifi/include/esp_private/wifi.h | 1 + components/esp_wifi/remote/Kconfig.wifi.in | 9 + .../esp_wifi/remote/Kconfig.wifi_is_remote.in | 7 + .../include/injected/esp_wifi_types_generic.h | 4 + .../include/apps_private/wifi_apps_private.h | 10 +- .../esp_wifi/wifi_apps/nan_app/src/nan_app.c | 21 + .../esp_wifi/wifi_apps/nan_app/src/nan_i.h | 6 + components/wpa_supplicant/CMakeLists.txt | 20 +- .../esp_supplicant/include/nan_pasn.h | 143 ++ .../src/crypto/crypto_mbedtls-ec.c | 90 + .../esp_supplicant/src/esp_wifi_driver.h | 1 + .../esp_supplicant/src/nan_pasn.c | 1401 +++++++++++++++ components/wpa_supplicant/src/ap/hostapd.h | 1 - .../wpa_supplicant/src/ap/pmksa_cache_auth.c | 3 +- components/wpa_supplicant/src/common/defs.h | 3 + .../src/common/ieee802_11_common.c | 66 + .../src/common/ieee802_11_common.h | 14 + .../src/common/ieee802_11_defs.h | 36 + .../wpa_supplicant/src/common/wpa_common.c | 973 ++++++++++- .../wpa_supplicant/src/common/wpa_common.h | 115 +- components/wpa_supplicant/src/crypto/crypto.h | 1 + .../wpa_supplicant/src/pasn/pasn_common.c | 352 ++++ .../wpa_supplicant/src/pasn/pasn_common.h | 272 +++ .../wpa_supplicant/src/pasn/pasn_initiator.c | 1555 +++++++++++++++++ .../wpa_supplicant/src/pasn/pasn_responder.c | 1168 +++++++++++++ .../wpa_supplicant/src/rsn_supp/pmksa_cache.c | 30 + .../wpa_supplicant/src/rsn_supp/pmksa_cache.h | 8 + 28 files changed, 6309 insertions(+), 10 deletions(-) create mode 100644 components/wpa_supplicant/esp_supplicant/include/nan_pasn.h create mode 100644 components/wpa_supplicant/esp_supplicant/src/nan_pasn.c create mode 100644 components/wpa_supplicant/src/pasn/pasn_common.c create mode 100644 components/wpa_supplicant/src/pasn/pasn_common.h create mode 100644 components/wpa_supplicant/src/pasn/pasn_initiator.c create mode 100644 components/wpa_supplicant/src/pasn/pasn_responder.c diff --git a/components/esp_wifi/Kconfig b/components/esp_wifi/Kconfig index 28495c0ccae..c6885320dfa 100644 --- a/components/esp_wifi/Kconfig +++ b/components/esp_wifi/Kconfig @@ -354,6 +354,15 @@ menu "Wi-Fi" help Select this option to allow the device to enable OWE Only mode for softap. + config ESP_WIFI_PASN_SUPPORT + bool "Enable PASN support" + depends on ESP_WIFI_NAN_PAIRING + default y if ESP_WIFI_NAN_PAIRING + help + Enable PASN for Wi-Fi NAN; the supplicant exposes CONFIG_PASN when this is on. + This option is only available when "NAN-Sync pairing bootstrapping" + (ESP_WIFI_NAN_PAIRING) is enabled, because NAN PASN builds on that path. + config ESP_WIFI_SLP_IRAM_OPT bool "WiFi SLP IRAM speed optimization" select PM_SLP_DEFAULT_PARAMS_OPT diff --git a/components/esp_wifi/include/esp_private/wifi.h b/components/esp_wifi/include/esp_private/wifi.h index fd359f69a6c..26490246ff7 100644 --- a/components/esp_wifi/include/esp_private/wifi.h +++ b/components/esp_wifi/include/esp_private/wifi.h @@ -175,6 +175,7 @@ struct nan_sync_callbacks { void (* ndp_response_indication)(struct ndp_cb_peer_info *peer_info); void (* pairing_indication)(uint8_t peer_svc_id, uint8_t pub_id, uint8_t peer_nmi[6], uint16_t selected_method); void (* pairing_confirm)(uint8_t status, uint8_t peer_svc_id, uint8_t sub_id, uint8_t peer_nmi[6], uint16_t matched_method, uint8_t reason_code); + void (* receive_pasn)(uint8_t *buf, size_t len, uint16_t trans_seq, uint16_t status); }; /* Host helpers for NAN encrypted-datapath, registered via diff --git a/components/esp_wifi/remote/Kconfig.wifi.in b/components/esp_wifi/remote/Kconfig.wifi.in index 475bfcdccfa..ffbde47764c 100644 --- a/components/esp_wifi/remote/Kconfig.wifi.in +++ b/components/esp_wifi/remote/Kconfig.wifi.in @@ -333,6 +333,15 @@ config WIFI_RMT_ENABLE_WPA3_OWE_SOFTAP help Select this option to allow the device to enable OWE Only mode for softap. +config WIFI_RMT_PASN_SUPPORT + bool "Enable PASN support" + depends on WIFI_RMT_NAN_PAIRING + default y if WIFI_RMT_NAN_PAIRING + help + Enable PASN for Wi-Fi NAN; the supplicant exposes CONFIG_PASN when this is on. + This option is only available when "NAN-Sync pairing bootstrapping" + (WIFI_RMT_NAN_PAIRING) is enabled, because NAN PASN builds on that path. + config WIFI_RMT_SLP_IRAM_OPT bool "WiFi SLP IRAM speed optimization" select PM_SLP_DEFAULT_PARAMS_OPT diff --git a/components/esp_wifi/remote/Kconfig.wifi_is_remote.in b/components/esp_wifi/remote/Kconfig.wifi_is_remote.in index 202aae7568f..f19740e6f8b 100644 --- a/components/esp_wifi/remote/Kconfig.wifi_is_remote.in +++ b/components/esp_wifi/remote/Kconfig.wifi_is_remote.in @@ -149,6 +149,13 @@ if WIFI_RMT_ENABLE_WPA3_OWE_SOFTAP default WIFI_RMT_ENABLE_WPA3_OWE_SOFTAP endif +if WIFI_RMT_PASN_SUPPORT + config ESP_WIFI_PASN_SUPPORT # ignore: multiple-definition + bool + depends on WIFI_RMT_NAN_PAIRING + default WIFI_RMT_PASN_SUPPORT +endif + if WIFI_RMT_SLP_IRAM_OPT config ESP_WIFI_SLP_IRAM_OPT # ignore: multiple-definition bool diff --git a/components/esp_wifi/remote/include/injected/esp_wifi_types_generic.h b/components/esp_wifi/remote/include/injected/esp_wifi_types_generic.h index ab2aa00a0a1..12523ae3542 100644 --- a/components/esp_wifi/remote/include/injected/esp_wifi_types_generic.h +++ b/components/esp_wifi/remote/include/injected/esp_wifi_types_generic.h @@ -924,6 +924,8 @@ typedef enum { WIFI_NAN_CSID_NCS_SK_256 = 2, /**< NCS-SK-256 (PSK/Passphrase). Reserved: not supported right now. */ WIFI_NAN_CSID_NCS_PK_2WDH_128 = 3, /**< NCS-PK-2WDH-128. Reserved: not supported right now. */ WIFI_NAN_CSID_NCS_PK_2WDH_256 = 4, /**< NCS-PK-2WDH-256. Reserved: not supported right now. */ + WIFI_NAN_CSID_NCS_GTK_CCM_128 = 5, + WIFI_NAN_CSID_NCS_GTK_GCM_256 = 6, WIFI_NAN_CSID_NCS_PK_PASN_128 = 7, /**< NCS-PK-PASN-128. Reserved: not supported right now. */ WIFI_NAN_CSID_NCS_PK_PASN_256 = 8, /**< NCS-PK-PASN-256. Reserved: not supported right now. */ } wifi_nan_cipher_suite_id_t; @@ -1049,6 +1051,7 @@ typedef struct { The driver makes a private copy during esp_wifi_nan_publish_service(); the caller may free this immediately after the call returns. */ nan_vendor_ie_t *vendor_ie; /**< Vendor specific IE to be added in publish frames */ + wifi_nan_pairing_cfg_t pairing; /**< Pairing configuration parameters */ } wifi_nan_publish_cfg_t; /** @@ -1075,6 +1078,7 @@ typedef struct { The driver makes a private copy during esp_wifi_nan_subscribe_service(); the caller may free this immediately after the call returns. */ nan_vendor_ie_t *vendor_ie; /**< Vendor specific IE to be added in subscribe frames */ + wifi_nan_pairing_cfg_t pairing; /**< Pairing configuration parameters */ } wifi_nan_subscribe_cfg_t; /** diff --git a/components/esp_wifi/wifi_apps/include/apps_private/wifi_apps_private.h b/components/esp_wifi/wifi_apps/include/apps_private/wifi_apps_private.h index df2b9883cf9..cd726f58b1c 100644 --- a/components/esp_wifi/wifi_apps/include/apps_private/wifi_apps_private.h +++ b/components/esp_wifi/wifi_apps/include/apps_private/wifi_apps_private.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -51,6 +51,14 @@ void esp_nan_action_stop(void); #endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */ +#if defined(CONFIG_ESP_WIFI_PASN_SUPPORT) +struct nan_pasn_data; +struct nan_pasn_data *esp_nan_app_get_pasn_data(void); +void esp_nan_app_set_pasn_data(struct nan_pasn_data *pd); +//void esp_nan_app_post_pasn_pairing_indication(const wifi_event_nan_pasn_pairing_indication_t *evt); +//void esp_nan_app_post_pasn_pairing_confirm(const wifi_event_nan_pasn_pairing_confirm_t *evt); +#endif /* CONFIG_ESP_WIFI_PASN_SUPPORT */ + #ifdef __cplusplus } #endif diff --git a/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c b/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c index ea598a93def..02db1385262 100644 --- a/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c +++ b/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c @@ -22,6 +22,10 @@ #ifdef CONFIG_ESP_WIFI_NAN_USD_ENABLE #include "esp_private/esp_nan_usd.h" #endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */ +#if defined(CONFIG_ESP_WIFI_NAN_SYNC_ENABLE) && defined(CONFIG_ESP_WIFI_PASN_SUPPORT) +#include "nan_pasn.h" +#include "apps_private/wifi_apps_private.h" +#endif /* NAN States */ #define NAN_STARTED_BIT BIT0 @@ -70,6 +74,18 @@ static const uint8_t s_wfa_oui[3] = {0x50, 0x6f, 0x9a}; /* Definition of nan_ctx_t storage shared via nan_i.h. */ nan_ctx_t s_nan_ctx; +#if defined(CONFIG_ESP_WIFI_PASN_SUPPORT) +struct nan_pasn_data *esp_nan_app_get_pasn_data(void) +{ + return s_nan_ctx.nan_pasn_data; +} + +void esp_nan_app_set_pasn_data(struct nan_pasn_data *pd) +{ + s_nan_ctx.nan_pasn_data = pd; +} +#endif + void esp_wifi_nan_get_ipv6_linklocal_from_mac(ip6_addr_t *ip6, uint8_t *mac_addr) { if (ip6 == NULL || mac_addr == NULL) { @@ -1269,6 +1285,11 @@ void esp_nan_action_start(esp_netif_t *nan_netif) #ifdef CONFIG_ESP_WIFI_NAN_PAIRING .pairing_indication = nan_app_pairing_indication_cb, .pairing_confirm = nan_app_pairing_confirm_cb, +#endif +#if defined(CONFIG_ESP_WIFI_NAN_SYNC_ENABLE) && defined(CONFIG_ESP_WIFI_PASN_SUPPORT) + .receive_pasn = handle_auth_pasn, +#else + .receive_pasn = NULL, #endif }; esp_nan_internal_register_callbacks(&nan_cb); diff --git a/components/esp_wifi/wifi_apps/nan_app/src/nan_i.h b/components/esp_wifi/wifi_apps/nan_app/src/nan_i.h index e5d0762a997..0f2c595bda0 100644 --- a/components/esp_wifi/wifi_apps/nan_app/src/nan_i.h +++ b/components/esp_wifi/wifi_apps/nan_app/src/nan_i.h @@ -231,6 +231,9 @@ struct ndl_info { #endif }; +/* Forward decl for PASN data, used opaquely when CONFIG_ESP_WIFI_PASN_SUPPORT */ +struct nan_pasn_data; + /* NAN context shared between files */ typedef struct { uint8_t state; @@ -238,6 +241,9 @@ typedef struct { struct ndl_info ndl[ESP_WIFI_NAN_DATAPATH_MAX_PEERS]; struct own_svc_info own_svc[ESP_WIFI_NAN_MAX_SVC_SUPPORTED]; esp_netif_t *nan_netif; +#ifdef CONFIG_ESP_WIFI_PASN_SUPPORT + struct nan_pasn_data *nan_pasn_data; +#endif } nan_ctx_t; extern nan_ctx_t s_nan_ctx; diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index 61a95b95987..9a92fdf0088 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -85,7 +85,8 @@ set(esp_srcs "esp_supplicant/src/esp_eap_client.c" "esp_supplicant/src/esp_common.c" "esp_supplicant/src/esp_wps.c" "esp_supplicant/src/esp_wpa3.c" - "esp_supplicant/src/esp_owe.c") + "esp_supplicant/src/esp_owe.c" + "esp_supplicant/src/nan_pasn.c") if(CONFIG_ESP_WIFI_SOFTAP_SUPPORT) set(esp_srcs ${esp_srcs} "esp_supplicant/src/esp_hostap.c") endif() @@ -250,12 +251,22 @@ else() set(usd_src "") endif() +if(CONFIG_ESP_WIFI_PASN_SUPPORT) + set(pasn_src + "src/pasn/pasn_common.c" + "src/pasn/pasn_initiator.c" + "src/pasn/pasn_responder.c") +else() + set(pasn_src "") +endif() + idf_component_register(SRCS "${srcs}" "${esp_srcs}" "${tls_src}" "${roaming_src}" - "${crypto_src}" "${mbo_src}" "${dpp_src}" "${wps_registrar_src}" "${usd_src}" + "${crypto_src}" "${mbo_src}" "${dpp_src}" "${wps_registrar_src}" "${usd_src}" "${pasn_src}" INCLUDE_DIRS include port/include esp_supplicant/include - PRIV_INCLUDE_DIRS src src/utils esp_supplicant/src src/crypto + PRIV_INCLUDE_DIRS src src/utils src/common esp_supplicant/src src/crypto ../esp_wifi/wifi_apps/roaming_app/include ../esp_wifi/wifi_apps/roaming_app/src + ../esp_wifi/wifi_apps/include/apps_private esp_supplicant/include/esp_private LDFRAGMENTS ${linker_fragments} PRIV_REQUIRES mbedtls esp_timer esp_wifi) @@ -362,6 +373,9 @@ endif() if(CONFIG_ESP_WIFI_NAN_USD_ENABLE) target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_NAN_USD) endif() +if(CONFIG_ESP_WIFI_PASN_SUPPORT) + target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_PASN) +endif() if(CONFIG_ESP_WIFI_ENABLE_WPA3_OWE_SOFTAP) target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_OWE_SOFTAP) endif() diff --git a/components/wpa_supplicant/esp_supplicant/include/nan_pasn.h b/components/wpa_supplicant/esp_supplicant/include/nan_pasn.h new file mode 100644 index 00000000000..2ed5e83e5e3 --- /dev/null +++ b/components/wpa_supplicant/esp_supplicant/include/nan_pasn.h @@ -0,0 +1,143 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + * + * NAN PASN — public API and types for NSD / Wi-Fi Aware examples (e.g. esp-nsd udp_client). + * When CONFIG_ESP_WIFI_PASN_SUPPORT is off, nan_pasn.c still links stubs (no-op / -1) + * for this API so apps can call e.g. pasn_responder_init_eloop without a link error. + */ + +#ifndef _NAN_PASN_H_ +#define _NAN_PASN_H_ + +#include +#include +#include + +/* Self-contained: public consumers only get esp_supplicant/include. */ +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif +#ifndef PMKID_LEN +#define PMKID_LEN 16 +#endif +#ifndef NAN_PASN_GLOBAL_PTK_BLOB_MAX +#define NAN_PASN_GLOBAL_PTK_BLOB_MAX 128 +#endif +#ifndef NAN_PASN_KEY_PMK_MAX +#define NAN_PASN_KEY_PMK_MAX 64 +#endif + +struct wpabuf; +struct pasn_data; +struct rsn_pmksa_cache; + +enum nan_role { + NAN_ROLE_IDLE = 0, + NAN_ROLE_PAIRING_INITIATOR = 1, + NAN_ROLE_PAIRING_RESPONDER = 2, +}; + +struct nan_config { + uint8_t dev_addr[ETH_ALEN]; + uint8_t pasn_type; + void *cb_ctx; + int (*set_pmksa)(void *ctx, const uint8_t *peer_addr, const uint8_t *pmkid); + int (*pasn_send_mgmt)(void *ctx, const uint8_t *data, size_t data_len, int noack, + unsigned int freq, unsigned int wait_ms); + int (*prepare_data_element)(void *ctx, const uint8_t *peer_addr); + int (*parse_data_element)(void *ctx, const uint8_t *data, size_t len); + int (*pasn_validate_pmkid)(void *ctx, const uint8_t *addr, const uint8_t *pmkid); +}; + +struct nan_pasn_data { + enum nan_role dev_role; + /** Last known unicast peer; used when @c addr is broadcast so PASN Auth1 DA is not ff:ff:ff:ff:ff:ff. */ + uint8_t pasn_unicast_peer[ETH_ALEN]; + /** + * NUL-terminated decimal PIN ('0'–'9' only). + * Same buffer is SAE password material (ASCII per digit) and @c pasn->password. + */ + char dev_sae_pin[64]; + size_t dev_sae_pin_len; + struct nan_config *cfg; + struct rsn_pmksa_cache *initiator_pmksa; + struct rsn_pmksa_cache *responder_pmksa; + uint8_t pasn_ptk[128]; + size_t pasn_ptk_len; + struct pasn_data *pasn; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int nan_initiate_pasn_verify(struct nan_pasn_data *pd, const uint8_t *peer_addr, + int freq, int role, const uint8_t *bssid, + const uint8_t *ssid, size_t ssid_len); + +int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const uint8_t *addr, int freq); + +struct nan_pasn_data *nan_pasn_data_init(void); +void nan_pasn_data_deinit(struct nan_pasn_data *pd); +int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr, int freq); +int nan_pasn_auth(struct nan_pasn_data **pd_out, const uint8_t *peer_addr, int freq); +/** + * Defer PASN auth initiation on eloop (same delayed pattern as @ref pasn_responder_init_eloop). + * Replaces the global NAN PASN object: deinits any existing @c esp_nan_app_get_pasn_data(), + * @ref nan_pasn_data_init, optional PIN override, then @ref nan_pasn_auth_initiate. + * Operating frequency is chosen internally (current NAN channel, or 2412 MHz fallback). + * @param pincode 6-digit PIN value (0..999999), e.g. @c 0 for @c "000000". @c UINT32_MAX to keep the default PIN from @ref nan_pasn_data_init. + */ +int nan_pasn_auth_eloop(const uint8_t *peer_addr, uint32_t pincode); + +/** + * Schedule @ref nan_initiate_pasn_verify on wpa_supplicant eloop after @a secs / @a usecs. + * Looks up @c struct nan_pasn_data via @ref esp_nan_app_get_pasn_data in the callback. + * @a bssid may be NULL to use @a peer_addr as BSSID. @a ssid may be NULL if @a ssid_len is 0. + */ +int nan_pasn_verify_eloop(unsigned int secs, unsigned int usecs, + const uint8_t *peer_addr, int freq, int role, + const uint8_t *bssid, + const uint8_t *ssid, size_t ssid_len); + +/** + * NAN PASN responder setup (nan_pasn_data_init, esp_nan_app_set_pasn_data, PIN, nan_pasn_initialize). + * @param pincode 6-digit PIN value (e.g. @c wa_pairing_cred_t.pincode, 0..999999). Use @c UINT32_MAX to keep the default PIN from @ref nan_pasn_data_init (no override). + * Frequency is chosen internally (current NAN channel, or 2412 MHz fallback). + */ +int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode); +/** + * Schedule @ref pasn_responder_init on wpa_supplicant eloop (delay 0). + * @a peer_addr may be NULL (broadcast placeholder). + * @a pincode same as @ref pasn_responder_init (value is stored in eloop context). + */ +int pasn_responder_init_eloop(const uint8_t *peer_addr, uint32_t pincode); + +void handle_auth_pasn(uint8_t *buf, size_t len, uint16_t trans_seq, uint16_t status); + +/** + * Last PASN key material after successful pairing (PMK + flattened PTK KCK|KEK|TK|KDK). + * Written before @c pasn PTK is cleared; initiator session is torn down on Auth3 TX status. + */ +struct nan_pasn_key_material { + uint8_t valid; + uint8_t peer_addr[ETH_ALEN]; + enum nan_role role; + int akmp; + int cipher; + size_t pmk_len; + uint8_t pmk[NAN_PASN_KEY_PMK_MAX]; + size_t ptk_blob_len; + uint8_t ptk_blob[NAN_PASN_GLOBAL_PTK_BLOB_MAX]; +}; + +const struct nan_pasn_key_material *nan_pasn_get_saved_keys(void); +void nan_pasn_clear_saved_keys(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _NAN_PASN_H_ */ diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c index 9f8362aa126..4cccdeed481 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c @@ -3036,6 +3036,96 @@ struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y, return sh_secret; } +mbedtls_ecp_group_id ecc_group_from_psa(psa_ecc_family_t family, + size_t bits) +{ + switch (family) { + case PSA_ECC_FAMILY_SECP_R1: + switch (bits) { + case 192: + return MBEDTLS_ECP_DP_SECP192R1; + case 256: + return MBEDTLS_ECP_DP_SECP256R1; + case 384: + return MBEDTLS_ECP_DP_SECP384R1; + case 521: + return MBEDTLS_ECP_DP_SECP521R1; + } + break; + + case PSA_ECC_FAMILY_BRAINPOOL_P_R1: + switch (bits) { + case 256: + return MBEDTLS_ECP_DP_BP256R1; + case 384: + return MBEDTLS_ECP_DP_BP384R1; + case 512: + return MBEDTLS_ECP_DP_BP512R1; + } + break; + + case PSA_ECC_FAMILY_MONTGOMERY: + switch (bits) { + case 255: + return MBEDTLS_ECP_DP_CURVE25519; + case 448: + return MBEDTLS_ECP_DP_CURVE448; + } + break; + + case PSA_ECC_FAMILY_SECP_K1: + switch (bits) { + case 192: + return MBEDTLS_ECP_DP_SECP192K1; + case 256: + return MBEDTLS_ECP_DP_SECP256K1; + } + break; + } + + return MBEDTLS_ECP_DP_NONE; +} + +size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh) +{ + mbedtls_ecp_group e; + mbedtls_ecp_group_id grp_id; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_ecc_family_t curve; + size_t bits; + size_t prime_len = 0; + int ret; + + if (!ecdh) { + return 0; + } + + psa_key_id_t key_id = *(psa_key_id_t *)ecdh; + psa_status_t status = psa_get_key_attributes(key_id, &attributes); + if (status != PSA_SUCCESS) { + psa_reset_key_attributes(&attributes); + return 0; + } + + curve = PSA_KEY_TYPE_ECC_GET_FAMILY(psa_get_key_type(&attributes)); + bits = psa_get_key_bits(&attributes); + psa_reset_key_attributes(&attributes); + + grp_id = ecc_group_from_psa(curve, bits); + if (grp_id == MBEDTLS_ECP_DP_NONE) { + return 0; + } + + mbedtls_ecp_group_init(&e); + ret = mbedtls_ecp_group_load(&e, grp_id); + if (ret == 0) { + prime_len = mbedtls_mpi_size(&e.P); + } + mbedtls_ecp_group_free(&e); + + return prime_len; +} + struct crypto_ec_key *crypto_ec_key_parse_pub(const u8 *der, size_t der_len) { int ret; diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h b/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h index 6760a9c204f..6811351cf55 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h @@ -327,6 +327,7 @@ void esp_wifi_set_sigma_internal(bool flag); void esp_wifi_ap_set_group_mgmt_cipher_internal(wifi_cipher_type_t cipher); uint8_t esp_wifi_op_class_supported_internal(uint8_t op_class, uint8_t min_chan, uint8_t max_chan, uint8_t inc, uint8_t bw, channel_bitmap_t *non_pref_channels); bool esp_wifi_is_wpa3_compatible_mode_enabled(uint8_t if_index); +esp_err_t esp_wifi_nan_get_pasn_attr(uint8_t *buf, size_t buf_len, size_t *actual_len); uint8_t esp_wifi_ap_get_owe_config_internal(void); #endif /* _ESP_WIFI_DRIVER_H_ */ diff --git a/components/wpa_supplicant/esp_supplicant/src/nan_pasn.c b/components/wpa_supplicant/esp_supplicant/src/nan_pasn.c new file mode 100644 index 00000000000..8a96f541545 --- /dev/null +++ b/components/wpa_supplicant/esp_supplicant/src/nan_pasn.c @@ -0,0 +1,1401 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + * + * NAN PASN: PASN pairing and authentication for Wi-Fi NAN. + */ + +#include "sdkconfig.h" +#include "nan_pasn.h" + +#if CONFIG_ESP_WIFI_PASN_SUPPORT + +#include "utils/includes.h" +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/nan.h" +#include "esp_wifi_driver.h" +#include "crypto/crypto.h" + +#include "pasn/pasn_common.h" +#include "common/wpa_common.h" +#include "apps_private/wifi_apps_private.h" + +#include "includes.h" +#include "wpa_supplicant_i.h" +#include "utils/eloop.h" +#include "esp_err.h" +#include "esp_wifi.h" +#include "esp_private/wifi.h" + +#define IEEE80211_MGMT_HDRLEN 24 + +/* Default NAN pairing PIN used when caller does not configure one. */ +#define NAN_DEFAULT_PAIRING_PIN "000000" +#define NAN_PASN_AUTH_TIMEOUT_SECS 10 + +/* Match nan_app pairwise key install for NAN data path (esp_wifi_set_nan_key_internal). */ +#define NAN_PASN_WIFI_ALG_CCMP 3 +#define NAN_PASN_KEY_FLAG_RX BIT(2) +#define NAN_PASN_KEY_FLAG_TX BIT(3) +#define NAN_PASN_KEY_FLAG_PAIRWISE BIT(5) + +static struct nan_pasn_key_material g_nan_pasn_saved_keys; + +/* Key index for esp_wifi_set_nan_key_internal (NAN PASN pairwise TK). */ +int temp = 1; + +/** Same layout as @ref nan_pasn_store_ptk (KCK|KEK|TK|KDK). */ +static int nan_pasn_flatten_ptk_blob(struct wpa_ptk *ptk, u8 *dst, size_t dst_sz, + size_t *out_len) +{ + u8 *pos; + + if (!ptk || !dst || !out_len) { + return -1; + } + if (ptk->ptk_len > dst_sz) { + return -1; + } + pos = dst; + *out_len = ptk->ptk_len; + if (ptk->kck_len) { + os_memcpy(pos, ptk->kck, ptk->kck_len); + pos += ptk->kck_len; + } + if (ptk->kek_len) { + os_memcpy(pos, ptk->kek, ptk->kek_len); + pos += ptk->kek_len; + } + if (ptk->tk_len) { + os_memcpy(pos, ptk->tk, ptk->tk_len); + pos += ptk->tk_len; + } + if (ptk->kdk_len) { + os_memcpy(pos, ptk->kdk, ptk->kdk_len); + pos += ptk->kdk_len; + } + return 0; +} + +/** + * Install PASN pairwise TK into the NAN interface key table (firmware). + * Call while @a pasn still holds a valid PTK (before forced_memzero). + */ +static void nan_pasn_install_nan_pairwise_tk(struct pasn_data *pasn) +{ + struct wpa_ptk *ptk; + uint8_t key_rsc[8] = {0}; + uint8_t peer[ETH_ALEN]; + int kret; + + if (!pasn) { + return; + } + + if (pasn->cipher != WPA_CIPHER_CCMP) { + wpa_printf(MSG_INFO, "NAN PASN: skip NAN TK install (cipher=%d)", pasn->cipher); + return; + } + + ptk = pasn_get_ptk(pasn); + if (!ptk || !ptk->tk_len || ptk->tk_len > sizeof(ptk->tk)) { + return; + } + + os_memcpy(peer, pasn->peer_addr, ETH_ALEN); + wpa_hexdump_key(MSG_INFO, "NAN PASN: TK before esp_wifi_set_nan_key_internal", + ptk->tk, ptk->tk_len); + kret = esp_wifi_set_nan_key_internal( + NAN_PASN_WIFI_ALG_CCMP, peer, temp, 1, key_rsc, sizeof(key_rsc), + ptk->tk, ptk->tk_len, + NAN_PASN_KEY_FLAG_PAIRWISE | NAN_PASN_KEY_FLAG_RX | NAN_PASN_KEY_FLAG_TX); + if (kret != 0) { + wpa_printf(MSG_WARNING, "NAN PASN: esp_wifi_set_nan_key_internal failed (%d)", + kret); + } +} + +/** + * Common path for @ref esp_nan_app_post_pasn_pairing_indication (initiator and responder). + * + * @param initiator_nmi Initiator NMI (6 octets). + * @param responder_nmi Responder NMI (6 octets). + */ +static void nan_pasn_post_pasn_pairing_indication_evt(struct nan_pasn_data *nan, + struct pasn_data *pasn, + const u8 initiator_nmi[ETH_ALEN], + const u8 responder_nmi[ETH_ALEN]) +{ +#if 0 + wifi_event_nan_pasn_pairing_indication_t ind; + + if (!nan || !pasn) { + return; + } + + os_memset(&ind, 0, sizeof(ind)); + ind.type = WIFI_NAN_PASN_PAIRING_IND_TYPE_SETUP; + ind.self_handle = 0; + os_memcpy(ind.initiator_nan_address, initiator_nmi, ETH_ALEN); + os_memcpy(ind.responder_nan_address, responder_nmi, ETH_ALEN); + ind.paired_peer_handle_valid = 0; + ind.auth_password = nan->dev_sae_pin_len > 0 ? 1 : 0; + ind.auth_opportunistic = + (pasn->akmp == WPA_KEY_MGMT_PASN && nan->dev_sae_pin_len == 0) ? 1 : 0; + ind.npk_nik_caching = 0; + esp_nan_app_post_pasn_pairing_indication(&ind); +#endif +} + +/** + * Common path for @ref esp_nan_app_post_pasn_pairing_confirm (initiator and responder). + * + * @param auth_frame_status_code IEEE 802.11 Authentication @c status_code from the RX frame (host endian). + * @param initiator_nmi Initiator NMI (6 octets). + * @param responder_nmi Responder NMI (6 octets). + * @param require_pasn_internal_success If true, post only when @c pasn->status is @c WLAN_STATUS_SUCCESS + * (initiator after Auth2). Responder uses false. + */ +static void nan_pasn_post_pasn_pairing_confirm_evt(struct nan_pasn_data *nan, + struct pasn_data *pasn, + u16 auth_frame_status_code, + const u8 initiator_nmi[ETH_ALEN], + const u8 responder_nmi[ETH_ALEN], + bool require_pasn_internal_success) +{ +#if 0 + wifi_event_nan_pasn_pairing_confirm_t conf; + + if (!nan || !pasn) { + return; + } + if (require_pasn_internal_success && pasn->status != WLAN_STATUS_SUCCESS) { + return; + } + + os_memset(&conf, 0, sizeof(conf)); + conf.type = WIFI_NAN_PASN_PAIRING_IND_TYPE_SETUP; + conf.status = WIFI_NAN_PASN_PAIRING_CONFIRM_STATUS_ACCEPTED; + conf.self_handle = 0; + conf.reason_code = + (auth_frame_status_code == WLAN_STATUS_SUCCESS) ? 0 + : (uint8_t)auth_frame_status_code; + os_memcpy(conf.initiator_nan_address, initiator_nmi, ETH_ALEN); + os_memcpy(conf.responder_nan_address, responder_nmi, ETH_ALEN); + conf.paired_peer_handle_valid = 0; + conf.auth_password = nan->dev_sae_pin_len > 0 ? 1 : 0; + conf.auth_opportunistic = + (pasn->akmp == WPA_KEY_MGMT_PASN && nan->dev_sae_pin_len == 0) ? 1 : 0; + conf.npk_nik_caching = 0; + esp_nan_app_post_pasn_pairing_confirm(&conf); +#endif +} + +static void nan_pasn_copy_keys_from_pasn(struct nan_pasn_data *nan, struct pasn_data *pasn) +{ + struct wpa_ptk *ptk; + size_t pmk_len; + const u8 *pmk_ptr; + + if (!nan || !pasn) { + return; + } + + forced_memzero(&g_nan_pasn_saved_keys, sizeof(g_nan_pasn_saved_keys)); + + ptk = pasn_get_ptk(pasn); + if (!ptk) { + return; + } + + os_memcpy(g_nan_pasn_saved_keys.peer_addr, pasn->peer_addr, ETH_ALEN); + g_nan_pasn_saved_keys.role = nan->dev_role; + g_nan_pasn_saved_keys.akmp = pasn->akmp; + g_nan_pasn_saved_keys.cipher = pasn->cipher; + + pmk_len = pasn_get_pmk_len(pasn); + pmk_ptr = pasn_get_pmk(pasn); + if (pmk_len && pmk_ptr && pmk_len <= sizeof(g_nan_pasn_saved_keys.pmk)) { + os_memcpy(g_nan_pasn_saved_keys.pmk, pmk_ptr, pmk_len); + g_nan_pasn_saved_keys.pmk_len = pmk_len; + } + + if (nan_pasn_flatten_ptk_blob(ptk, g_nan_pasn_saved_keys.ptk_blob, + sizeof(g_nan_pasn_saved_keys.ptk_blob), + &g_nan_pasn_saved_keys.ptk_blob_len) != 0) { + forced_memzero(&g_nan_pasn_saved_keys, sizeof(g_nan_pasn_saved_keys)); + return; + } + + g_nan_pasn_saved_keys.valid = 1; +} + +const struct nan_pasn_key_material *nan_pasn_get_saved_keys(void) +{ + return g_nan_pasn_saved_keys.valid ? &g_nan_pasn_saved_keys : NULL; +} + +void nan_pasn_clear_saved_keys(void) +{ + forced_memzero(&g_nan_pasn_saved_keys, sizeof(g_nan_pasn_saved_keys)); +} + +static int nan_chan_to_freq_mhz(uint8_t chan) +{ + if (chan >= 1 && chan <= 13) { + return 2407 + (int)chan * 5; + } + if (chan == 14) { + return 2484; + } + return 0; +} + +static int nan_pasn_get_current_freq_mhz(void) +{ + uint8_t primary = 0; + wifi_second_chan_t second = WIFI_SECOND_CHAN_NONE; + + if (esp_wifi_get_channel(&primary, &second) != ESP_OK || primary == 0) { + return 0; + } + return nan_chan_to_freq_mhz(primary); +} + +static const char * __attribute__((unused)) +nan_pasn_role_to_str(enum nan_role role) +{ + switch (role) { + case NAN_ROLE_PAIRING_INITIATOR: + return "initiator"; + case NAN_ROLE_PAIRING_RESPONDER: + return "responder"; + case NAN_ROLE_IDLE: + default: + return "idle"; + } +} + +static void nan_pasn_auth_timeout_cancel(struct nan_pasn_data *nan); + +static void nan_pasn_auth_timeout_cb(void *eloop_ctx, void *user_data) +{ + struct nan_pasn_data *nan = eloop_ctx; + + if (!nan) { + return; + } + + wpa_printf(MSG_INFO, + "NAN PASN: %s auth timed out after %u seconds", + nan_pasn_role_to_str((enum nan_role)(uintptr_t)user_data), + NAN_PASN_AUTH_TIMEOUT_SECS); + nan_pasn_data_deinit(nan); +} + +static int nan_pasn_auth_timeout_arm(struct nan_pasn_data *nan, enum nan_role role) +{ + if (!nan) { + return -1; + } + + nan_pasn_auth_timeout_cancel(nan); + if (eloop_register_timeout(NAN_PASN_AUTH_TIMEOUT_SECS, 0, + nan_pasn_auth_timeout_cb, nan, + (void *)(uintptr_t)role) != 0) { + return -1; + } + + return 0; +} + +static void nan_pasn_auth_timeout_cancel(struct nan_pasn_data *nan) +{ + if (!nan) { + return; + } + + eloop_cancel_timeout(nan_pasn_auth_timeout_cb, nan, ELOOP_ALL_CTX); +} + +/* Store decimal PIN digits in @a nan->dev_sae_pin (NUL-terminated ASCII) for SAE. */ +static int nan_set_dev_sae_pin(struct nan_pasn_data *nan, const char *digits) +{ + size_t i; + size_t len; + + if (!nan || !digits) { + return -1; + } + + os_memset(nan->dev_sae_pin, 0, sizeof(nan->dev_sae_pin)); + nan->dev_sae_pin_len = 0; + + len = os_strlen(digits); + if (len == 0 || len >= sizeof(nan->dev_sae_pin)) { + return -1; + } + + for (i = 0; i < len; i++) { + if (digits[i] < '0' || digits[i] > '9') { + return -1; + } + nan->dev_sae_pin[i] = digits[i]; + } + nan->dev_sae_pin[len] = '\0'; + nan->dev_sae_pin_len = len; + + return 0; +} + +/* + * Build Wi-Fi Alliance NAN vendor IE (EID 221) with DCEA / BPBA / CSIA for PASN + * and pass to @a pasn via pasn_set_extra_ies() so wpa_pasn_add_extra_ies() appends + * it after PASN Parameters on Auth 1/3 (and after prepare_data_element on Auth 2). + */ +static int nan_prepare_pasn_extra_ie(struct nan_pasn_data *nan, struct pasn_data *pasn, + const struct wpabuf *frame, bool add_dira) +{ + static const u8 nan_pasn_attr_payload[] = { + NAN_ATTR_DCEA, 0x02, 0x00, 0x00, 0x03, + NAN_ATTR_BPBA, 0x05, 0x00, 0x90, 0x02, 0x00, 0x02, 0x00, + NAN_ATTR_CSIA, 0x03, 0x00, 0x00, 0x07, 0x05, + }; + u8 fixed[2 + 3 + 1 + sizeof(nan_pasn_attr_payload)]; + u8 *buf = NULL; + size_t fr_len = 0; + size_t total_len; + int ret; + + (void)nan; + (void)add_dira; + + fixed[0] = WLAN_EID_VENDOR_SPECIFIC; + fixed[1] = 3 + 1 + sizeof(nan_pasn_attr_payload); + WPA_PUT_BE24(&fixed[2], OUI_WFA); + fixed[5] = NAN_OUI_TYPE; + os_memcpy(&fixed[6], nan_pasn_attr_payload, sizeof(nan_pasn_attr_payload)); + + if (frame) { + fr_len = wpabuf_len(frame); + } + + if (!fr_len) { + return pasn_set_extra_ies(pasn, fixed, sizeof(fixed)); + } + + total_len = sizeof(fixed) + fr_len; + buf = os_malloc(total_len); + if (!buf) { + return -1; + } + os_memcpy(buf, fixed, sizeof(fixed)); + os_memcpy(buf + sizeof(fixed), wpabuf_head_u8(frame), fr_len); + ret = pasn_set_extra_ies(pasn, buf, total_len); + os_free(buf); + return ret; +} + +static struct wpabuf * nan_pairing_generate_rsnxe(struct nan_pasn_data *nan, + int akmp) +{ + u32 capab; + size_t flen = 0; + struct wpabuf *buf; + + capab = BIT(WLAN_RSNX_CAPAB_KEK_IN_PASN); + + if (wpa_key_mgmt_sae(akmp)) { + capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); + } + + while (capab >> flen * 8) { + flen++; + } + if (!flen) { + flen = 1; + } + + buf = wpabuf_alloc(2 + 1); + if (!buf) { + return NULL; + } + + if (wpabuf_tailroom(buf) < 2 + 1) { + wpa_printf(MSG_INFO, "wpabuf tail room too small"); + wpabuf_free(buf); + return NULL; + } + capab |= flen - 1; /* merge full-IE length code for cap bits */ + capab &= ~0x0000000fU; /* RSNX Length (bits 0-3): n-1 = 0 */ + + wpa_printf(MSG_INFO, "RSNXE capabilities: %04x", capab); + wpabuf_put_u8(buf, WLAN_EID_RSNX); + /* PASN/NAN: only first RSNX octet on the wire; omit 2nd + reserved tail */ + wpabuf_put_u8(buf, 1); + wpabuf_put_u8(buf, (u8)(capab & 0xff)); + return buf; +} + +/* Fixed NAN pairing SSID (salt for SAE HKDF / H2E), not the user PIN. */ +#define NAN_PAIRING_SSID "516F9A010000" + +static void nan_pairing_apply_sae_pin(struct pasn_data *pasn, u8 pasn_type, + struct nan_pasn_data *nan) +{ + int pasn_groups[4] = { 0 }; + size_t ssid_len = os_strlen(NAN_PAIRING_SSID); + + if (!pasn || !nan || nan->dev_sae_pin_len == 0) { + return; + } + + if (pasn_type & 0xc && pasn_type & 0x3) { + pasn_groups[0] = 20; + pasn_groups[1] = 19; + } else if (pasn_type & 0xc) { + pasn_groups[0] = 20; + } else { + pasn_groups[0] = 19; + } + + pasn->pt = sae_derive_pt(pasn_groups, (const u8 *) NAN_PAIRING_SSID, + ssid_len, (const u8 *) nan->dev_sae_pin, + nan->dev_sae_pin_len, NULL); + pasn->password = nan->dev_sae_pin; +} + +void nan_pasn_initialize(struct nan_pasn_data *nan, const u8 *addr, int freq, bool verify, bool derive_kek) +{ + struct pasn_data *pasn; + struct wpabuf *rsnxe; + + if (!nan) { + return; + } + + if (nan->pasn) { + wpa_pasn_reset(nan->pasn); + } else { + nan->pasn = pasn_data_init(); + if (!nan->pasn) { + return; + } + } + + pasn = nan->pasn; + + os_memcpy(pasn->own_addr, nan->cfg->dev_addr, ETH_ALEN); + + /* PASN Auth1 DA = pasn->peer_addr (see wpa_pasn_build_auth_header). Avoid broadcast DA. */ + if (addr) { + const u8 *peer = addr; + + if (is_broadcast_ether_addr(peer)) { + /* Responder may not know peer MAC until Auth1; allow broadcast placeholder. */ + if (nan->dev_role == NAN_ROLE_PAIRING_RESPONDER) { + os_memcpy(pasn->peer_addr, peer, ETH_ALEN); + } else if (is_zero_ether_addr(nan->pasn_unicast_peer) || + is_broadcast_ether_addr(nan->pasn_unicast_peer)) { + wpa_printf(MSG_INFO, + "NAN PASN: peer is broadcast and no cached unicast peer"); + return; + } else { + peer = nan->pasn_unicast_peer; + wpa_printf(MSG_DEBUG, + "NAN PASN: using cached unicast peer for Auth1 DA"); + } + } else { + os_memcpy(nan->pasn_unicast_peer, peer, ETH_ALEN); + } + os_memcpy(pasn->peer_addr, peer, ETH_ALEN); + } + + os_memcpy(pasn->bssid, nan->dev_role == NAN_ROLE_PAIRING_INITIATOR ? + pasn->peer_addr : pasn->own_addr, ETH_ALEN); + + pasn->group = 19; + pasn->cipher = WPA_CIPHER_CCMP; + pasn_enable_kdk_derivation(pasn); + + if (!derive_kek) { + pasn->derive_kek = false; + pasn->kek_len = 0; + } + + if (nan->dev_sae_pin_len > 0) { + pasn->akmp = WPA_KEY_MGMT_SAE; + nan_pairing_apply_sae_pin(pasn, nan->cfg->pasn_type, nan); + } else if (!verify) { + pasn->akmp = WPA_KEY_MGMT_PASN; + } + + pasn->rsn_pairwise = pasn->cipher; + pasn->wpa_key_mgmt = pasn->akmp; + + rsnxe = nan_pairing_generate_rsnxe(nan, pasn->akmp); + if (rsnxe) { + os_free(pasn->rsnxe_ie); + pasn->rsnxe_ie = os_memdup(wpabuf_head_u8(rsnxe), + wpabuf_len(rsnxe)); + if (!pasn->rsnxe_ie) { + wpabuf_free(rsnxe); + return; + } + wpabuf_free(rsnxe); + } + + if (nan->dev_role == NAN_ROLE_PAIRING_INITIATOR) { + pasn->pmksa = nan->initiator_pmksa; + } else { + pasn->pmksa = nan->responder_pmksa; + } + + pasn->cb_ctx = nan->cfg->cb_ctx; + pasn->send_mgmt = nan->cfg->pasn_send_mgmt; + pasn->prepare_data_element = nan->cfg->prepare_data_element; + pasn->parse_data_element = nan->cfg->parse_data_element; + pasn->validate_custom_pmkid = nan->cfg->pasn_validate_pmkid; + + pasn->freq = freq; + +} + +int nan_initiate_pasn_verify(struct nan_pasn_data *pd, const u8 *peer_addr, + int freq, int role, + const u8 *bssid, const u8 *ssid, size_t ssid_len) +{ + struct nan_pasn_data *nan; + struct pasn_data *pasn; + int ret = 0; + + (void)role; + (void)bssid; + (void)ssid; + (void)ssid_len; + + if (!pd) { + return -1; + } + nan = pd; + + if (!peer_addr) { + wpa_printf(MSG_INFO, "Peer address NULL"); + return -1; + } + + nan->dev_role = NAN_ROLE_PAIRING_INITIATOR; + nan_pasn_initialize(nan, peer_addr, freq, true, true); + pasn = nan->pasn; + + if (nan_prepare_pasn_extra_ie(nan, pasn, NULL, false) != 0) { + wpa_printf(MSG_INFO, "NAN PASN: extra IE failed"); + return -1; + } + + if (wpa_pasn_verify(pasn, pasn->own_addr, pasn->peer_addr, pasn->bssid, + pasn->akmp, pasn->cipher, pasn->group, pasn->freq, + NULL, 0, NULL, 0, NULL)) { + wpa_printf(MSG_INFO, "PASN verify failed"); + ret = -1; + } + if (pasn->extra_ies) { + os_free((u8 *) pasn->extra_ies); + pasn->extra_ies = NULL; + pasn->extra_ies_len = 0; + } + return ret; +} + +int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const u8 *addr, int freq) +{ + struct nan_pasn_data *nan; + struct pasn_data *pasn; + int ret = 0; + + if (!pd) { + return -1; + } + nan = pd; + + if (!addr) { + wpa_printf(MSG_INFO, "Peer address NULL"); + return -1; + } + + nan->dev_role = NAN_ROLE_PAIRING_INITIATOR; + + nan_pasn_initialize(nan, addr, freq, false, true); + pasn = nan->pasn; + + pasn_initiator_pmksa_cache_remove(pasn->pmksa, (u8 *)addr); + + if (nan_prepare_pasn_extra_ie(nan, pasn, NULL, false) != 0) { + wpa_printf(MSG_INFO, "NAN PASN: extra IE failed"); + return -1; + } + + if (wpas_pasn_start(pasn, pasn->own_addr, pasn->peer_addr, pasn->bssid, + pasn->akmp, pasn->cipher, pasn->group, pasn->freq, + NULL, 0, NULL, 0, NULL)) { + wpa_printf(MSG_INFO, "Failed to start PASN"); + ret = -1; + } else { + nan_pasn_post_pasn_pairing_indication_evt(nan, pasn, pasn->own_addr, + pasn->peer_addr); + if (nan_pasn_auth_timeout_arm(nan, NAN_ROLE_PAIRING_INITIATOR) != 0) { + wpa_printf(MSG_INFO, "Failed to arm initiator PASN auth timeout"); + ret = -1; + } + } + if (pasn->extra_ies) { + os_free((u8 *) pasn->extra_ies); + pasn->extra_ies = NULL; + pasn->extra_ies_len = 0; + } + return ret; +} + +int * int_array_dup(const int *a) +{ + if (!a) { + return NULL; + } + return os_memdup(a, (int_array_len(a) + 1) * sizeof(int)); +} + +static int nan_handle_pasn_auth(struct nan_pasn_data *nan, + const struct ieee80211_auth *mgmt, size_t len, + int freq) +{ + struct pasn_data *pasn; + u8 pasn_type; + int pasn_groups[4] = { 0 }; + u16 auth_alg, auth_transaction, status_code; + + (void)freq; + if (!nan || !nan->pasn) { + return -1; + } + + if (os_memcmp(mgmt->da, nan->cfg->dev_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_INFO, "PASN Responder: Not our frame"); + return -1; + } + + if (len < offsetof(struct ieee80211_auth, auth.variable)) { + return -1; + } + + pasn = nan->pasn; + auth_alg = le_to_host16(mgmt->auth.auth_alg); + status_code = le_to_host16(mgmt->auth.status_code); + auth_transaction = le_to_host16(mgmt->auth.auth_transaction); + + if (status_code != WLAN_STATUS_SUCCESS && + status_code != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) { + wpa_printf(MSG_INFO, "PASN: Authentication rejected - status=%u", + status_code); + return -1; + } + + if (auth_alg != WLAN_AUTH_PASN || + auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH2) { + wpa_printf(MSG_INFO, + "PASN Responder: Not a PASN frame or unexpected Authentication frame, auth_alg=%d", + auth_alg); + return -1; + } + if (auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH1) { + if (nan_pasn_auth_timeout_arm(nan, NAN_ROLE_PAIRING_RESPONDER) != 0) { + wpa_printf(MSG_INFO, "PASN Responder: Failed to arm auth timeout"); + return -1; + } + pasn_type = nan->cfg->pasn_type; + if (pasn_type & 0xc && pasn_type & 0x3) { + pasn_groups[0] = 20; + pasn_groups[1] = 19; + } else if (pasn_type & 0xc) { + pasn_groups[0] = 20; + } else { + pasn_groups[0] = 19; + } + os_free(pasn->pasn_groups); + pasn->pasn_groups = int_array_dup(pasn_groups); + + if (handle_auth_pasn_1(pasn, nan->cfg->dev_addr, mgmt->sa, mgmt, + len, false) < 0) { + nan_pasn_auth_timeout_cancel(nan); + wpa_printf(MSG_INFO, + "PASN Responder: Handle Auth 1 failed"); + return -1; + } + nan_pasn_post_pasn_pairing_indication_evt(nan, pasn, mgmt->sa, + nan->cfg->dev_addr); + } else if (auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH3) { + if (handle_auth_pasn_3(pasn, nan->cfg->dev_addr, mgmt->sa, mgmt, + len) < 0) { + wpa_printf(MSG_INFO, + "PASN Responder: Handle Auth 3 failed"); + return -1; + } + nan_pasn_auth_timeout_cancel(nan); + nan_pasn_post_pasn_pairing_confirm_evt( + nan, pasn, le_to_host16(mgmt->auth.status_code), + mgmt->sa, nan->cfg->dev_addr, false); +#ifdef CONFIG_TESTING_OPTIONS + nan_pasn_store_ptk(nan, &pasn->ptk); +#endif /* CONFIG_TESTING_OPTIONS */ + nan_pasn_copy_keys_from_pasn(nan, pasn); + nan_pasn_install_nan_pairwise_tk(pasn); + forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk)); + nan_pasn_data_deinit(nan); + } + return 0; +} + +int nan_pasn_auth_rx(struct nan_pasn_data *nan, const struct ieee80211_auth *mgmt, + size_t len, int freq) +{ + int ret = 0; + u16 auth_transaction; + struct pasn_data *pasn; + struct wpa_pasn_params_data pasn_data; + + if (!nan || !mgmt) { + return -1; + } + if (!nan->pasn) { + nan->pasn = pasn_data_init(); + if (!nan->pasn) { + wpa_printf(MSG_INFO, "PASN: Uninitialized"); + return -1; + } + } + + pasn = nan->pasn; + + wpabuf_free(pasn->frame); + pasn->frame = NULL; + pasn_register_callbacks(pasn, nan->cfg->cb_ctx, + nan->cfg->pasn_send_mgmt, + nan->cfg->pasn_validate_pmkid); + auth_transaction = le_to_host16(mgmt->auth.auth_transaction); + + if (nan->dev_role == NAN_ROLE_PAIRING_INITIATOR && + auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH2) { + ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, &pasn_data); + if (ret < 0) { + wpa_printf(MSG_INFO, "PASN: wpa_pasn_auth_rx() failed"); + nan->dev_role = NAN_ROLE_IDLE; + } else { + nan_pasn_post_pasn_pairing_confirm_evt( + nan, pasn, le_to_host16(mgmt->auth.status_code), + pasn->own_addr, pasn->peer_addr, true); + nan_pasn_copy_keys_from_pasn(nan, pasn); + nan_pasn_install_nan_pairwise_tk(pasn); + } +#ifdef CONFIG_TESTING_OPTIONS + nan_pasn_store_ptk(nan, &pasn->ptk); +#endif /* CONFIG_TESTING_OPTIONS */ + forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk)); + } else { + ret = nan_handle_pasn_auth(nan, mgmt, len, freq); + } + return ret; +} + +void handle_auth_pasn(uint8_t *buf, size_t len, uint16_t trans_seq, uint16_t status) +{ + const struct ieee80211_auth *mgmt; + struct nan_pasn_data *nan; + int rx_freq; + + (void)trans_seq; + (void)status; + + if (!buf || len < IEEE80211_MGMT_HDRLEN + 6) { + return; + } + mgmt = (const struct ieee80211_auth *)buf; + nan = esp_nan_app_get_pasn_data(); + rx_freq = nan_pasn_get_current_freq_mhz(); + if (rx_freq <= 0) { + rx_freq = 2412; + } + if (!nan) { + wpa_printf(MSG_DEBUG, "NAN PASN: receive_pasn: no context"); + return; + } + nan_pasn_auth_rx(nan, mgmt, len, rx_freq); +} + +void nan_pasn_pmksa_set_pmk(struct nan_pasn_data *nan, const u8 *src, const u8 *dst, + const u8 *pmk, size_t pmk_len, const u8 *pmkid) +{ + if (!nan || !pmk || !pmk_len) { + return; + } + + wpa_hexdump_key(MSG_DEBUG, "NAN PASN: PMK", pmk, pmk_len); + if (pmkid) { + wpa_hexdump(MSG_DEBUG, "NAN PASN: PMKID", pmkid, PMKID_LEN); + } + + pasn_initiator_pmksa_cache_add(nan->initiator_pmksa, src, dst, pmk, + pmk_len, pmkid); + pasn_responder_pmksa_cache_add(nan->responder_pmksa, src, dst, pmk, + pmk_len, pmkid); +} + +#ifdef CONFIG_TESTING_OPTIONS + +void nan_pasn_store_ptk(struct nan_pasn_data *nan, struct wpa_ptk *ptk) +{ + u8 *pos; + + if (ptk->ptk_len > sizeof(nan->pasn_ptk)) { + wpa_printf(MSG_INFO, "NAN PASN PTK exceeds: (len=%ld)", ptk->ptk_len); + return; + } + + pos = nan->pasn_ptk; + nan->pasn_ptk_len = ptk->ptk_len; + if (ptk->kck_len) { + os_memcpy(pos, ptk->kck, ptk->kck_len); + pos += ptk->kck_len; + } + if (ptk->kek_len) { + os_memcpy(pos, ptk->kek, ptk->kek_len); + pos += ptk->kek_len; + } + if (ptk->tk_len) { + os_memcpy(pos, ptk->tk, ptk->tk_len); + pos += ptk->tk_len; + } + if (ptk->kdk_len) { + os_memcpy(pos, ptk->kdk, ptk->kdk_len); + pos += ptk->kdk_len; + } + + wpa_hexdump_key(MSG_DEBUG, "NAN PASN: stored PTK blob (KCK|KEK|TK|KDK)", + nan->pasn_ptk, nan->pasn_ptk_len); +} + +int nan_pasn_get_ptk(struct nan_pasn_data *nan, const u8 **buf, size_t *buf_len) +{ + if (!nan || !nan->pasn_ptk_len) { + return -1; + } + + *buf_len = nan->pasn_ptk_len; + *buf = nan->pasn_ptk; + return 0; +} + +#endif /* CONFIG_TESTING_OPTIONS */ + +static int nan_pasn_esp_send_mgmt(void *ctx, const u8 *data, size_t data_len, int noack, + unsigned int freq, unsigned int wait_ms) +{ + wifi_mgmt_frm_req_t *req; + size_t body_len; + const u8 *body; + const u8 *da = data + 4; + struct nan_pasn_data *nan = ctx; + int ret; + + (void)noack; + (void)freq; + (void)wait_ms; + + if (!data || data_len <= IEEE80211_MGMT_HDRLEN) { + return -1; + } + + /* If stack built Auth1 with broadcast DA, use cached unicast peer for the driver. */ + if (nan && is_broadcast_ether_addr(da) && + !is_zero_ether_addr(nan->pasn_unicast_peer) && + !is_broadcast_ether_addr(nan->pasn_unicast_peer)) { + da = nan->pasn_unicast_peer; + wpa_printf(MSG_DEBUG, + "NAN PASN: overriding broadcast DA with cached unicast peer"); + } + + body = data + IEEE80211_MGMT_HDRLEN; + body_len = data_len - IEEE80211_MGMT_HDRLEN; + + req = os_zalloc(sizeof(*req) + body_len); + if (!req) { + return -1; + } + + req->ifx = WIFI_IF_NAN; + req->subtype = (WLAN_FC_STYPE_AUTH << 4); + req->data_len = body_len; + os_memcpy(req->da, da, ETH_ALEN); + os_memcpy(req->data, body, body_len); + + ret = esp_wifi_send_mgmt_frm_internal(req); + + os_free(req); + + return (ret == 0) ? 0 : -1; +} + +struct nan_pasn_data *nan_pasn_data_init(void) +{ + struct nan_pasn_data *pd; + struct nan_config *cfg; + + pd = os_zalloc(sizeof(*pd)); + if (!pd) { + return NULL; + } + + cfg = os_zalloc(sizeof(*cfg)); + if (!cfg) { + os_free(pd); + return NULL; + } + + esp_wifi_get_mac(WIFI_IF_NAN, cfg->dev_addr); + + cfg->pasn_type = 0x03; + cfg->cb_ctx = pd; + cfg->set_pmksa = NULL; + cfg->pasn_send_mgmt = nan_pasn_esp_send_mgmt; + cfg->prepare_data_element = NULL; + cfg->parse_data_element = NULL; + cfg->pasn_validate_pmkid = NULL; + + pd->cfg = cfg; + pd->initiator_pmksa = pasn_initiator_pmksa_cache_init(); + pd->responder_pmksa = pasn_responder_pmksa_cache_init(); + pd->pasn = NULL; + + /* Keep a deterministic decimal PIN unless caller overrides it later. */ + if (nan_set_dev_sae_pin(pd, NAN_DEFAULT_PAIRING_PIN) != 0) { + os_free(cfg); + os_free(pd); + return NULL; + } + + return pd; +} + +void nan_pasn_data_deinit(struct nan_pasn_data *pd) +{ + if (!pd) { + return; + } + + nan_pasn_auth_timeout_cancel(pd); + + forced_memzero(pd->dev_sae_pin, sizeof(pd->dev_sae_pin)); + pd->dev_sae_pin_len = 0; + + if (pd == esp_nan_app_get_pasn_data()) { + esp_nan_app_set_pasn_data(NULL); + } + + if (pd->pasn) { + /* Reset must run before pasn_data_deinit: deinit frees pasn (UAF if reversed). */ + wpa_pasn_reset(pd->pasn); + pasn_data_deinit(pd->pasn); + pd->pasn = NULL; + } + if (pd->initiator_pmksa) { + pasn_initiator_pmksa_cache_deinit(pd->initiator_pmksa); + pd->initiator_pmksa = NULL; + } + if (pd->responder_pmksa) { + pasn_responder_pmksa_cache_deinit(pd->responder_pmksa); + pd->responder_pmksa = NULL; + } + os_free(pd->cfg); + pd->cfg = NULL; + os_free(pd); +} + +int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr, int freq) +{ + if (!pd || !peer_addr) { + return -1; + } + return nan_initiate_pasn_auth(pd, peer_addr, freq); +} + +#define NAN_PASN_VERIFY_ELOOP_SSID_MAX 32 + +struct nan_pasn_eloop_ctx { + uint8_t peer_addr[ETH_ALEN]; + uint32_t pincode; +}; + +static void nan_pasn_auth_eloop_cb(void *eloop_ctx, void *user_data) +{ + struct nan_pasn_eloop_ctx *ctx = user_data; + struct nan_pasn_data *old; + struct nan_pasn_data *pd; + char pin_digits[16]; + int n; + const char *pin_to_apply = NULL; + + (void)eloop_ctx; + if (!ctx) { + return; + } + + old = esp_nan_app_get_pasn_data(); + if (old) { + nan_pasn_data_deinit(old); + } + + pd = nan_pasn_data_init(); + if (!pd) { + os_free(ctx); + return; + } + + esp_nan_app_set_pasn_data(pd); + + if (ctx->pincode != UINT32_MAX) { + n = os_snprintf(pin_digits, sizeof(pin_digits), "%06u", + (unsigned)(ctx->pincode % 1000000U)); + if (os_snprintf_error(sizeof(pin_digits), n)) { + nan_pasn_data_deinit(pd); + os_free(ctx); + return; + } + pin_to_apply = pin_digits; + } + + if (pin_to_apply && nan_set_dev_sae_pin(pd, pin_to_apply) != 0) { + nan_pasn_data_deinit(pd); + os_free(ctx); + return; + } + + { + int freq = nan_pasn_get_current_freq_mhz(); + + if (freq <= 0) { + freq = 2412; + } + nan_pasn_auth_initiate(pd, ctx->peer_addr, freq); + } + os_free(ctx); +} + +int nan_pasn_auth_eloop(const uint8_t *peer_addr, uint32_t pincode) +{ + struct nan_pasn_eloop_ctx *ctx; + + if (!peer_addr) { + return -1; + } + + ctx = os_zalloc(sizeof(*ctx)); + if (!ctx) { + return -1; + } + + os_memcpy(ctx->peer_addr, peer_addr, ETH_ALEN); + ctx->pincode = pincode; + + if (eloop_register_timeout(0, 0, nan_pasn_auth_eloop_cb, NULL, ctx) != 0) { + os_free(ctx); + return -1; + } + + return 0; +} + +struct nan_pasn_verify_eloop_ctx { + uint8_t peer_addr[ETH_ALEN]; + int freq; + int role; + uint8_t bssid[ETH_ALEN]; + uint8_t ssid[NAN_PASN_VERIFY_ELOOP_SSID_MAX]; + size_t ssid_len; +}; + +static void nan_pasn_verify_eloop_cb(void *eloop_ctx, void *user_data) +{ + struct nan_pasn_verify_eloop_ctx *ctx = user_data; + struct nan_pasn_data *pd; + const uint8_t *ssid_arg; + + (void)eloop_ctx; + if (!ctx) { + return; + } + + pd = esp_nan_app_get_pasn_data(); + if (!pd) { + os_free(ctx); + return; + } + + ssid_arg = ctx->ssid_len ? ctx->ssid : NULL; + nan_initiate_pasn_verify(pd, ctx->peer_addr, ctx->freq, ctx->role, + ctx->bssid, ssid_arg, ctx->ssid_len); + os_free(ctx); +} + +int nan_pasn_verify_eloop(unsigned int secs, unsigned int usecs, + const uint8_t *peer_addr, int freq, int role, + const uint8_t *bssid, + const uint8_t *ssid, size_t ssid_len) +{ + struct nan_pasn_verify_eloop_ctx *ctx; + + if (!peer_addr) { + return -1; + } + if (ssid_len > NAN_PASN_VERIFY_ELOOP_SSID_MAX) { + return -1; + } + + ctx = os_zalloc(sizeof(*ctx)); + if (!ctx) { + return -1; + } + + os_memcpy(ctx->peer_addr, peer_addr, ETH_ALEN); + ctx->freq = freq; + ctx->role = role; + if (bssid) { + os_memcpy(ctx->bssid, bssid, ETH_ALEN); + } else { + os_memcpy(ctx->bssid, peer_addr, ETH_ALEN); + } + if (ssid && ssid_len) { + os_memcpy(ctx->ssid, ssid, ssid_len); + ctx->ssid_len = ssid_len; + } + + if (eloop_register_timeout(secs, usecs, nan_pasn_verify_eloop_cb, NULL, ctx) != 0) { + os_free(ctx); + return -1; + } + + return 0; +} + +/** + * pasn_responder_init - Set up NAN PASN responder state for incoming Auth frames. + * + * Calls nan_pasn_data_init(), esp_nan_app_set_pasn_data(), optional PIN override, + * role @c NAN_ROLE_PAIRING_RESPONDER, and nan_pasn_initialize(). + * + * @param peer_addr Peer NAN address, or NULL to use a broadcast placeholder until Auth1. + * @param pincode 6-digit value 0..999999, or @c UINT32_MAX to keep default PIN from @ref nan_pasn_data_init. + * Returns 0 on success, -1 on failure. + */ +int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode) +{ + struct nan_pasn_data *pd; + struct nan_pasn_data *old; + char pin_digits[16]; + static const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + const u8 *peer = peer_addr ? peer_addr : bcast; + const char *pin_to_apply = NULL; + int n; + int freq; + + os_memset(pin_digits, 0, sizeof(pin_digits)); + + old = esp_nan_app_get_pasn_data(); + if (old) { + nan_pasn_data_deinit(old); + } + + pd = nan_pasn_data_init(); + if (!pd) { + return -1; + } + + esp_nan_app_set_pasn_data(pd); + + if (pincode != UINT32_MAX) { + n = os_snprintf(pin_digits, sizeof(pin_digits), "%06u", + (unsigned)(pincode % 1000000U)); + if (os_snprintf_error(sizeof(pin_digits), n)) { + goto fail; + } + pin_to_apply = pin_digits; + } + + if (pin_to_apply && nan_set_dev_sae_pin(pd, pin_to_apply) != 0) { + goto fail; + } + + freq = nan_pasn_get_current_freq_mhz(); + if (freq <= 0) { + freq = 2412; + } + + pd->dev_role = NAN_ROLE_PAIRING_RESPONDER; + nan_pasn_initialize(pd, peer, freq, false, true); + + if (!pd->pasn || nan_prepare_pasn_extra_ie(pd, pd->pasn, NULL, false) != 0) { + goto fail; + } + + return 0; + +fail: + nan_pasn_data_deinit(pd); + return -1; +} + +struct pasn_responder_eloop_ctx { + uint8_t peer_addr[ETH_ALEN]; + unsigned int has_peer; + uint32_t pincode; +}; + +static void pasn_responder_init_eloop_cb(void *eloop_ctx, void *user_data) +{ + struct pasn_responder_eloop_ctx *ctx = user_data; + + (void)eloop_ctx; + if (!ctx) { + return; + } + pasn_responder_init(ctx->has_peer ? ctx->peer_addr : NULL, ctx->pincode); + os_free(ctx); +} + +int pasn_responder_init_eloop(const uint8_t *peer_addr, uint32_t pincode) +{ + struct pasn_responder_eloop_ctx *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (!ctx) { + return -1; + } + + ctx->pincode = pincode; + if (peer_addr) { + ctx->has_peer = 1; + os_memcpy(ctx->peer_addr, peer_addr, ETH_ALEN); + } + + if (eloop_register_timeout(0, 0, pasn_responder_init_eloop_cb, NULL, ctx) != 0) { + os_free(ctx); + return -1; + } + + return 0; +} + +#else /* !CONFIG_ESP_WIFI_PASN_SUPPORT */ + +void handle_auth_pasn(uint8_t *buf, size_t len, uint16_t trans_seq, uint16_t status) +{ + (void)buf; + (void)len; + (void)trans_seq; + (void)status; +} + +int nan_initiate_pasn_verify(struct nan_pasn_data *pd, const uint8_t *peer_addr, + int freq, int role, const uint8_t *bssid, + const uint8_t *ssid, size_t ssid_len) +{ + (void)pd; + (void)peer_addr; + (void)freq; + (void)role; + (void)bssid; + (void)ssid; + (void)ssid_len; + return -1; +} + +int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const uint8_t *addr, int freq) +{ + (void)pd; + (void)addr; + (void)freq; + return -1; +} + +struct nan_pasn_data *nan_pasn_data_init(void) +{ + return NULL; +} + +void nan_pasn_data_deinit(struct nan_pasn_data *pd) +{ + (void)pd; +} + +int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr, int freq) +{ + (void)pd; + (void)peer_addr; + (void)freq; + return -1; +} + +int nan_pasn_auth(struct nan_pasn_data **pd_out, const uint8_t *peer_addr, int freq) +{ + (void)pd_out; + (void)peer_addr; + (void)freq; + return -1; +} + +int nan_pasn_auth_eloop(const uint8_t *peer_addr, uint32_t pincode) +{ + (void)peer_addr; + (void)pincode; + return -1; +} + +int nan_pasn_verify_eloop(unsigned int secs, unsigned int usecs, + const uint8_t *peer_addr, int freq, int role, + const uint8_t *bssid, + const uint8_t *ssid, size_t ssid_len) +{ + (void)secs; + (void)usecs; + (void)peer_addr; + (void)freq; + (void)role; + (void)bssid; + (void)ssid; + (void)ssid_len; + return -1; +} + +int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode) +{ + (void)peer_addr; + (void)pincode; + return -1; +} + +int pasn_responder_init_eloop(const uint8_t *peer_addr, uint32_t pincode) +{ + (void)peer_addr; + (void)pincode; + return -1; +} + +const struct nan_pasn_key_material *nan_pasn_get_saved_keys(void) +{ + return NULL; +} + +void nan_pasn_clear_saved_keys(void) +{ +} + +#endif /* CONFIG_ESP_WIFI_PASN_SUPPORT */ diff --git a/components/wpa_supplicant/src/ap/hostapd.h b/components/wpa_supplicant/src/ap/hostapd.h index ec8a02fb6fa..d51e2bad588 100644 --- a/components/wpa_supplicant/src/ap/hostapd.h +++ b/components/wpa_supplicant/src/ap/hostapd.h @@ -147,7 +147,6 @@ struct hostapd_data { #ifdef CONFIG_SAE #define COMEBACK_KEY_SIZE 8 -#define COMEBACK_PENDING_IDX_SIZE 256 /** Key used for generating SAE anti-clogging tokens */ u8 comeback_key[COMEBACK_KEY_SIZE]; diff --git a/components/wpa_supplicant/src/ap/pmksa_cache_auth.c b/components/wpa_supplicant/src/ap/pmksa_cache_auth.c index f16ced9981b..3376174e80a 100644 --- a/components/wpa_supplicant/src/ap/pmksa_cache_auth.c +++ b/components/wpa_supplicant/src/ap/pmksa_cache_auth.c @@ -337,7 +337,8 @@ void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa) pmksa->pmksa = NULL; for (i = 0; i < PMKID_HASH_SIZE; i++) pmksa->pmkid[i] = NULL; - os_free(pmksa); + if(pmksa) + os_free(pmksa); } diff --git a/components/wpa_supplicant/src/common/defs.h b/components/wpa_supplicant/src/common/defs.h index 543f1bbd6b5..a9174922bfa 100644 --- a/components/wpa_supplicant/src/common/defs.h +++ b/components/wpa_supplicant/src/common/defs.h @@ -28,6 +28,7 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; #define WPA_CIPHER_GCMP_256 BIT(12) #define WPA_CIPHER_BIP_GMAC_128 BIT(13) #define WPA_CIPHER_BIP_GMAC_256 BIT(14) +#define WPA_CIPHER_GTK_NOT_USED BIT(15) #define WPA_KEY_MGMT_IEEE8021X BIT(0) #define WPA_KEY_MGMT_PSK BIT(1) @@ -50,6 +51,8 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; #define WPA_KEY_MGMT_OWE BIT(22) #define WPA_KEY_MGMT_SAE_EXT_KEY BIT(26) #define WPA_KEY_MGMT_DPP BIT(23) +#define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24) +#define WPA_KEY_MGMT_PASN BIT(25) static inline int wpa_key_mgmt_wpa_ieee8021x(int akm) { diff --git a/components/wpa_supplicant/src/common/ieee802_11_common.c b/components/wpa_supplicant/src/common/ieee802_11_common.c index 6098dd469ee..9b134c4d4a8 100644 --- a/components/wpa_supplicant/src/common/ieee802_11_common.c +++ b/components/wpa_supplicant/src/common/ieee802_11_common.c @@ -270,6 +270,20 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, elems->fils_pk = pos; elems->fils_pk_len = elen; break; +#ifdef CONFIG_PASN + case WLAN_EID_EXT_PASN_PARAMS: + elems->pasn_params = pos; + elems->pasn_params_len = elen; + break; + case WLAN_EID_EXT_PASN_ENCRYPTED_DATA: + elems->pasn_encrypted_data = pos; + elems->pasn_encrypted_data_len = elen; + break; +#endif /* CONFIG_PASN */ + case WLAN_EID_EXT_WRAPPED_DATA: + elems->wrapped_data = pos; + elems->wrapped_data_len = elen; + break; default: wpa_printf(MSG_EXCESSIVE, "IEEE 802.11 element parsing ignored unknown element extension (ext_id=%u elen=%u)", @@ -326,6 +340,18 @@ static ParseRes __ieee802_11_parse_elems(const u8 *start, size_t len, elems->ext_capab_len = elen; break; #endif + case WLAN_EID_RSN: + elems->rsn_ie = pos; + elems->rsn_ie_len = elen; + break; + case WLAN_EID_RSNX: + elems->rsnxe = pos; + elems->rsnxe_len = elen; + break; + case WLAN_EID_MIC: + elems->mic = pos; + elems->mic_len = elen; + break; default: break; @@ -504,3 +530,43 @@ u8 get_operating_class(u8 chan, int sec_channel) return op_class; } + +struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem) +{ + struct wpabuf *buf; + const u8 *pos, *end; + size_t min_defrag_len = ext_elem ? 255 : 256; + + if (!data || !len) + return NULL; + + if (len < min_defrag_len) + return wpabuf_alloc_copy(data, len); + + buf = wpabuf_alloc_copy(data, min_defrag_len - 1); + if (!buf) + return NULL; + + pos = &data[min_defrag_len - 1]; + end = data + len; + len -= min_defrag_len - 1; + while (len > 2 && pos[0] == WLAN_EID_FRAGMENT && pos[1]) { + int ret; + size_t elen = 2 + pos[1]; + + if (elen > (size_t) (end - pos) || elen > len) + break; + ret = wpabuf_resize(&buf, pos[1]); + if (ret < 0) { + wpabuf_free(buf); + return NULL; + } + + /* Copy only the fragment data (without the EID and length) */ + wpabuf_put_data(buf, &pos[2], pos[1]); + pos += elen; + len -= elen; + } + + return buf; +} diff --git a/components/wpa_supplicant/src/common/ieee802_11_common.h b/components/wpa_supplicant/src/common/ieee802_11_common.h index 43e5a0b43b7..f9a772fcc2d 100644 --- a/components/wpa_supplicant/src/common/ieee802_11_common.h +++ b/components/wpa_supplicant/src/common/ieee802_11_common.h @@ -70,6 +70,16 @@ struct ieee802_11_elems { const u8 *sae_pk; u8 sae_pk_len; #endif +#ifdef CONFIG_PASN + const u8 *pasn_encrypted_data; + const u8 *pasn_params; + u8 pasn_encrypted_data_len; + u8 pasn_params_len; +#endif + const u8 *wrapped_data; + size_t wrapped_data_len; + const u8 *mic; + u8 mic_len; }; typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; @@ -87,4 +97,8 @@ u8 get_operating_class(u8 chan, int sec_channel); int ieee802_11_ie_count(const u8 *ies, size_t ies_len); struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, u32 oui_type); +bool ieee802_11_rsnx_capab_len(const u8 *rsnxe, size_t rsnxe_len, + unsigned int capab); +bool ieee802_11_rsnx_capab(const u8 *rsnxe, unsigned int capab); +struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem); #endif /* IEEE802_11_COMMON_H */ diff --git a/components/wpa_supplicant/src/common/ieee802_11_defs.h b/components/wpa_supplicant/src/common/ieee802_11_defs.h index 4242084961f..ede713ebe30 100644 --- a/components/wpa_supplicant/src/common/ieee802_11_defs.h +++ b/components/wpa_supplicant/src/common/ieee802_11_defs.h @@ -74,9 +74,18 @@ #define WLAN_AUTH_FT 2 #define WLAN_AUTH_SAE 3 #define WLAN_AUTH_LEAP 128 +#define WLAN_AUTH_PASN 7 #define WLAN_AUTH_CHALLENGE_LEN 128 +/* Authentication transaction sequence number */ +#define WLAN_AUTH_TR_SEQ_SAE_COMMIT 1 +#define WLAN_AUTH_TR_SEQ_SAE_CONFIRM 2 + +#define WLAN_AUTH_TR_SEQ_PASN_AUTH1 1 +#define WLAN_AUTH_TR_SEQ_PASN_AUTH2 2 +#define WLAN_AUTH_TR_SEQ_PASN_AUTH3 3 + #define WLAN_CAPABILITY_ESS BIT(0) #define WLAN_CAPABILITY_IBSS BIT(1) #define WLAN_CAPABILITY_CF_POLLABLE BIT(2) @@ -160,6 +169,8 @@ #define WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER 123 #define WLAN_STATUS_SAE_HASH_TO_ELEMENT 126 #define WLAN_STATUS_SAE_PK 127 +#define WLAN_STATUS_INVALID_PUBLIC_KEY 136 +#define WLAN_STATUS_PASN_BASE_AKMP_FAILED 137 /* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */ #define WLAN_REASON_UNSPECIFIED 1 @@ -227,6 +238,7 @@ #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74 #define WLAN_EID_MMIE 76 #define WLAN_EID_EXT_CAPAB 127 +#define WLAN_EID_MIC 140 #define WLAN_EID_VENDOR_SPECIFIC 221 #define WLAN_EID_CAG_NUMBER 237 #define WLAN_EID_AP_CSN 239 @@ -258,11 +270,16 @@ #define WLAN_EID_EXT_REJECTED_GROUPS 92 #define WLAN_EID_EXT_ANTI_CLOGGING_TOKEN 93 #define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114 +#define WLAN_EID_EXT_PASN_ENCRYPTED_DATA 140 +#define WLAN_EID_EXT_PASN_PARAMS 100 +#define WLAN_EID_EXT_PASN_ENCRYPTED_DATA 140 /* Extended RSN Capabilities */ /* bits 0-3: Field length (n-1) */ #define WLAN_RSNX_CAPAB_SAE_H2E 5 #define WLAN_RSNX_CAPAB_SAE_PK 6 +#define WLAN_RSNX_CAPAB_SECURE_LTF 8 +#define WLAN_RSNX_CAPAB_KEK_IN_PASN 18 #define WLAN_EXT_CAPAB_20_40_COEX 0 #define WLAN_EXT_CAPAB_BSS_TRANSITION 19 @@ -407,6 +424,25 @@ struct ieee80211_action { struct ieee80211_public_action public_action; } u; } STRUCT_PACKED; + +/* Authentication frame (management frame, type AUTH). Layout is the same as + * the auth variant of struct ieee80211_mgmt (u.auth) for PASN and other + * auth frame handling. */ +struct ieee80211_auth { + le16 frame_control; + le16 duration; + u8 da[6]; + u8 sa[6]; + u8 bssid[6]; + le16 seq_ctrl; + struct { + le16 auth_alg; + le16 auth_transaction; + le16 status_code; + /* possibly followed by Challenge text */ + u8 variable[]; + } STRUCT_PACKED auth; +}; #endif /* ESP_SUPPLICANT */ #define IEEE80211_MAX_MMPDU_SIZE 2304 diff --git a/components/wpa_supplicant/src/common/wpa_common.c b/components/wpa_supplicant/src/common/wpa_common.c index 2b83b5801ca..e5b983d8284 100644 --- a/components/wpa_supplicant/src/common/wpa_common.c +++ b/components/wpa_supplicant/src/common/wpa_common.c @@ -320,7 +320,12 @@ static int rsn_selector_to_bitfield(const u8 *s) return WPA_CIPHER_BIP_GMAC_256; #endif #endif /* CONFIG_IEEE80211W */ + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED){ + printf("### function = %s line = %di ###\n",__func__,__LINE__); + return WPA_CIPHER_GTK_NOT_USED; + } + printf("### function = %s line = %di ###\n",__func__,__LINE__); return 0; } @@ -399,8 +404,9 @@ static int wpa_key_mgmt_to_bitfield(const u8 *s) int wpa_cipher_valid_mgmt_group(int cipher) { - return cipher == WPA_CIPHER_AES_128_CMAC || - cipher == WPA_CIPHER_BIP_GMAC_128 || + return cipher == WPA_CIPHER_GTK_NOT_USED || + cipher == WPA_CIPHER_AES_128_CMAC || + cipher == WPA_CIPHER_BIP_GMAC_128 || cipher == WPA_CIPHER_BIP_GMAC_256; } @@ -489,6 +495,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, if (left >= RSN_SELECTOR_LEN) { data->group_cipher = rsn_selector_to_bitfield(pos); + data->has_group = 1; pos += RSN_SELECTOR_LEN; left -= RSN_SELECTOR_LEN; } else if (left > 0) { @@ -507,6 +514,8 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, "count %u left %u", __func__, count, left); return -4; } + if (count) + data->has_pairwise = 1; for (i = 0; i < count; i++) { data->pairwise_cipher |= rsn_selector_to_bitfield(pos); pos += RSN_SELECTOR_LEN; @@ -1773,6 +1782,495 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) return ret; } +#ifdef CONFIG_PASN + +/* + * wpa_pasn_build_auth_header - Add the MAC header and initialize Authentication + * frame for PASN + * + * @buf: Buffer in which the header will be added + * @bssid: The BSSID of the AP + * @src: Source address + * @dst: Destination address + * @trans_seq: Authentication transaction sequence number + * @status: Authentication status + */ +void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid, + const u8 *src, const u8 *dst, + u8 trans_seq, u16 status) +{ + struct ieee80211_auth *auth; + + wpa_printf(MSG_DEBUG, "PASN: Add authentication header. trans_seq=%u", + trans_seq); + + auth = wpabuf_put(buf, offsetof(struct ieee80211_auth, auth.variable)); + + auth->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_AUTH << 4)); + + os_memcpy(auth->da, dst, ETH_ALEN); + os_memcpy(auth->sa, src, ETH_ALEN); + os_memcpy(auth->bssid, bssid, ETH_ALEN); + auth->seq_ctrl = 0; + + auth->auth.auth_alg = host_to_le16(WLAN_AUTH_PASN); + auth->auth.auth_transaction = host_to_le16(trans_seq); + auth->auth.status_code = host_to_le16(status); +} + + +/* + * wpa_pasn_add_rsne - Add an RSNE for PASN authentication + * @buf: Buffer in which the IE will be added + * @pmkid: Optional PMKID. Can be NULL. + * @akmp: Authentication and key management protocol + * @cipher: The cipher suite + */ +int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher) +{ + struct rsn_ie_hdr *hdr; + u32 suite; + u16 capab; + u8 *pos; + u8 rsne_len; + + wpa_printf(MSG_DEBUG, "PASN: Add RSNE"); + + rsne_len = sizeof(*hdr) + RSN_SELECTOR_LEN + + 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + + 2 + RSN_SELECTOR_LEN + 2 + (pmkid ? PMKID_LEN : 0); + + if (wpabuf_tailroom(buf) < rsne_len) + return -1; + hdr = wpabuf_put(buf, rsne_len); + hdr->elem_id = WLAN_EID_RSN; + hdr->len = rsne_len - 2; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + + /* Group addressed data is not allowed */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + pos += RSN_SELECTOR_LEN; + + /* Add the pairwise cipher */ + WPA_PUT_LE16(pos, 1); + pos += 2; + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, cipher); + RSN_SELECTOR_PUT(pos, suite); + pos += RSN_SELECTOR_LEN; + + /* Add the AKM suite */ + WPA_PUT_LE16(pos, 1); + pos += 2; + + switch (akmp) { + case WPA_KEY_MGMT_PASN: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PASN); + break; +#if defined(CONFIG_SAE) || defined(CONFIG_WPA3_SAE) + case WPA_KEY_MGMT_SAE: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); + break; + case WPA_KEY_MGMT_SAE_EXT_KEY: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE_EXT_KEY); + break; +#endif /* CONFIG_SAE || CONFIG_WPA3_SAE */ +#ifdef CONFIG_FILS + case WPA_KEY_MGMT_FILS_SHA256: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256); + break; + case WPA_KEY_MGMT_FILS_SHA384: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384); + break; +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R + case WPA_KEY_MGMT_FT_PSK: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); + break; + case WPA_KEY_MGMT_FT_IEEE8021X: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); + break; + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384); + break; +#endif /* CONFIG_IEEE80211R */ + default: + wpa_printf(MSG_ERROR, "PASN: Invalid AKMP=0x%x", akmp); + return -1; + } + pos += RSN_SELECTOR_LEN; + + /* RSN Capabilities: PASN mandates both MFP capable and required */ + capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR; + capab |= WPA_CAPABILITY_OCVC; /* OCVC: testing — Operating Channel Validation */ + WPA_PUT_LE16(pos, capab); + pos += 2; + + if (pmkid) { + wpa_printf(MSG_DEBUG, "PASN: Adding PMKID"); + + WPA_PUT_LE16(pos, 1); + pos += 2; + os_memcpy(pos, pmkid, PMKID_LEN); + pos += PMKID_LEN; + } else { + WPA_PUT_LE16(pos, 0); + pos += 2; + } + + /* Group addressed management is not allowed */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + + return 0; +} + + +/* + * wpa_pasn_add_parameter_ie - Add PASN Parameters IE for PASN authentication + * @buf: Buffer in which the IE will be added + * @pasn_group: Finite Cyclic Group ID for PASN authentication + * @wrapped_data_format: Format of the data in the Wrapped Data IE + * @pubkey: A buffer holding the local public key. Can be NULL + * @compressed: In case pubkey is included, indicates if the public key is + * compressed (only x coordinate is included) or not (both x and y + * coordinates are included) + * @comeback: A buffer holding the comeback token. Can be NULL + * @after: If comeback is set, defined the comeback time in seconds. -1 to not + * include the Comeback After field (frames from non-AP STA). + */ +void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group, + u8 wrapped_data_format, + const struct wpabuf *pubkey, bool compressed, + const struct wpabuf *comeback, int after) +{ + struct pasn_parameter_ie *params; + + wpa_printf(MSG_DEBUG, "PASN: Add PASN Parameters element"); + + params = wpabuf_put(buf, sizeof(*params)); + + params->id = WLAN_EID_EXTENSION; + params->len = sizeof(*params) - 2; + params->id_ext = WLAN_EID_EXT_PASN_PARAMS; + params->control = 0; + params->wrapped_data_format = wrapped_data_format; + + if (comeback) { + wpa_printf(MSG_DEBUG, "PASN: Adding comeback data"); + + /* + * 2 octets for the 'after' field + 1 octet for the length + + * actual cookie data + */ + if (after >= 0) + params->len += 2; + params->len += 1 + wpabuf_len(comeback); + params->control |= WPA_PASN_CTRL_COMEBACK_INFO_PRESENT; + + if (after >= 0) + wpabuf_put_le16(buf, after); + wpabuf_put_u8(buf, wpabuf_len(comeback)); + wpabuf_put_buf(buf, comeback); + } + + if (pubkey) { + wpa_printf(MSG_DEBUG, + "PASN: Adding public key and group ID %u", + pasn_group); + + /* + * 2 octets for the finite cyclic group + 2 octets public key + * length + 1 octet for the compressed/uncompressed indication + + * the actual key. + */ + params->len += 2 + 1 + 1 + wpabuf_len(pubkey); + params->control |= WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT; + + wpabuf_put_le16(buf, pasn_group); + + /* + * The first octet indicates whether the public key is + * compressed, as defined in RFC 5480 section 2.2. + */ + wpabuf_put_u8(buf, wpabuf_len(pubkey) + 1); + wpabuf_put_u8(buf, compressed ? WPA_PASN_PUBKEY_COMPRESSED_0 : + WPA_PASN_PUBKEY_UNCOMPRESSED); + + wpabuf_put_buf(buf, pubkey); + } +} + +/* + * wpa_pasn_add_wrapped_data - Add a Wrapped Data IE to PASN Authentication + * frame. If needed, the Wrapped Data IE would be fragmented. + * + * @buf: Buffer in which the IE will be added + * @wrapped_data_buf: Buffer holding the wrapped data + */ +int wpa_pasn_add_wrapped_data(struct wpabuf *buf, + struct wpabuf *wrapped_data_buf) +{ + const u8 *data; + size_t data_len; + u8 len; + + if (!wrapped_data_buf) + return 0; + + wpa_printf(MSG_DEBUG, "PASN: Add wrapped data"); + + data = wpabuf_head_u8(wrapped_data_buf); + data_len = wpabuf_len(wrapped_data_buf); + + /* nothing to add */ + if (!data_len) + return 0; + + if (data_len <= 254) + len = 1 + data_len; + else + len = 255; + + if (wpabuf_tailroom(buf) < 3 + data_len) + return -1; + + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, len); + wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA); + wpabuf_put_data(buf, data, len - 1); + + data += len - 1; + data_len -= len - 1; + + while (data_len) { + if (wpabuf_tailroom(buf) < 2 + data_len) + return -1; + wpabuf_put_u8(buf, WLAN_EID_FRAGMENT); + len = data_len > 255 ? 255 : data_len; + wpabuf_put_u8(buf, len); + wpabuf_put_data(buf, data, len); + data += len; + data_len -= len; + } + + return 0; +} + + +/* + * wpa_pasn_validate_rsne - Validate PASN specific data of RSNE + * @data: Parsed representation of an RSNE + * Returns -1 for invalid data; otherwise 0 + */ +int wpa_pasn_validate_rsne(const struct wpa_ie_data *data) +{ + u16 capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR; + + if (data->proto != WPA_PROTO_RSN) + return -1; + + if ((data->capabilities & capab) != capab) { + wpa_printf(MSG_DEBUG, "PASN: Invalid RSNE capabilities"); + return -1; + } + + if ((!data->has_group) || (data->group_cipher != WPA_CIPHER_GTK_NOT_USED)) { + wpa_printf(MSG_DEBUG, "PASN: Invalid group data cipher"); + return -1; + } + printf("### function = %s line = %d %d %d ###\n",__func__,__LINE__,data->has_pairwise,data->pairwise_cipher); + + if (!data->has_pairwise || !data->pairwise_cipher || + (data->pairwise_cipher & (data->pairwise_cipher - 1))) { + wpa_printf(MSG_DEBUG, "PASN: No valid pairwise suite"); + return -1; + } + + switch (data->key_mgmt) { +#ifdef CONFIG_SAE + case WPA_KEY_MGMT_SAE: + case WPA_KEY_MGMT_SAE_EXT_KEY: + /* fall through */ +#endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FILS_SHA384: + /* fall through */ +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R + case WPA_KEY_MGMT_FT_PSK: + case WPA_KEY_MGMT_FT_IEEE8021X: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + /* fall through */ +#endif /* CONFIG_IEEE80211R */ + case WPA_KEY_MGMT_PASN: + break; + default: + wpa_printf(MSG_ERROR, "PASN: invalid key_mgmt: 0x%0x", + data->key_mgmt); + return -1; + } + + if (data->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED) { + wpa_printf(MSG_DEBUG, "PASN: Invalid group mgmt cipher"); + return -1; + } + + if (data->num_pmkid > 1) { + wpa_printf(MSG_DEBUG, "PASN: Invalid number of PMKIDs"); + return -1; + } + + return 0; +} + + +/* + * wpa_pasn_parse_parameter_ie - Validates PASN Parameters IE + * @data: Pointer to the PASN Parameters IE (starting with the EID). + * @len: Length of the data in the PASN Parameters IE + * @from_ap: Whether this was received from an AP + * @pasn_params: On successful return would hold the parsed PASN parameters. + * Returns: -1 for invalid data; otherwise 0 + * + * Note: On successful return, the pointers in &pasn_params point to the data in + * the IE and are not locally allocated (so they should not be freed etc.). + */ +int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, bool from_ap, + struct wpa_pasn_params_data *pasn_params) +{ + struct pasn_parameter_ie *params = (struct pasn_parameter_ie *) data; + const u8 *pos = (const u8 *) (params + 1); + + if (!pasn_params) { + wpa_printf(MSG_DEBUG, "PASN: Invalid params"); + return -1; + } + + if (!params || ((size_t) (params->len + 2) < sizeof(*params)) || + len < sizeof(*params) || params->len + 2 != len) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid parameters IE. len=(%u, %u)", + params ? params->len : 0, len); + return -1; + } + + os_memset(pasn_params, 0, sizeof(*pasn_params)); + + switch (params->wrapped_data_format) { + case WPA_PASN_WRAPPED_DATA_NO: + case WPA_PASN_WRAPPED_DATA_SAE: + case WPA_PASN_WRAPPED_DATA_FILS_SK: + case WPA_PASN_WRAPPED_DATA_FT: + break; + default: + wpa_printf(MSG_DEBUG, "PASN: Invalid wrapped data format"); + return -1; + } + + pasn_params->wrapped_data_format = params->wrapped_data_format; + + len -= sizeof(*params); + + if (params->control & WPA_PASN_CTRL_COMEBACK_INFO_PRESENT) { + if (from_ap) { + if (len < 2) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid Parameters IE: Truncated Comeback After"); + return -1; + } + pasn_params->after = WPA_GET_LE16(pos); + pos += 2; + len -= 2; + } + + if (len < 1 || len < 1 + *pos) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid Parameters IE: comeback len"); + return -1; + } + + pasn_params->comeback_len = *pos++; + len--; + pasn_params->comeback = pos; + len -= pasn_params->comeback_len; + pos += pasn_params->comeback_len; + } + + if (params->control & WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT) { + if (len < 3 || len < 3 + pos[2]) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid Parameters IE: group and key"); + return -1; + } + + pasn_params->group = WPA_GET_LE16(pos); + pos += 2; + len -= 2; + pasn_params->pubkey_len = *pos++; + len--; + pasn_params->pubkey = pos; + len -= pasn_params->pubkey_len; + pos += pasn_params->pubkey_len; + } + + if (len) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid Parameters IE. Bytes left=%u", len); + return -1; + } + + return 0; +} + + +void wpa_pasn_add_rsnxe(struct wpabuf *buf, u16 capab) +{ + size_t flen; + + flen = (capab & 0xff00) ? 2 : 1; + if (!capab) + return; /* no supported extended RSN capabilities */ + if (wpabuf_tailroom(buf) < 2 + flen) + return; + capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */ + + wpabuf_put_u8(buf, WLAN_EID_RSNX); + wpabuf_put_u8(buf, flen); + wpabuf_put_u8(buf, capab & 0x00ff); + capab >>= 8; + if (capab) + wpabuf_put_u8(buf, capab); +} + + +/* + * wpa_pasn_add_extra_ies - Add protocol specific IEs in Authentication + * frame for PASN. + * + * @buf: Buffer in which the elements will be added + * @extra_ies: Protocol specific elements to add + * @len: Length of the elements + * Returns: 0 on success, -1 on failure + */ + +int wpa_pasn_add_extra_ies(struct wpabuf *buf, const u8 *extra_ies, size_t len) +{ + if (!buf) + return -1; + if (!len || !extra_ies) + return 0; + + if (wpabuf_tailroom(buf) < len) + return -1; + + wpabuf_put_data(buf, extra_ies, len); + return 0; +} + +#endif /* CONFIG_PASN */ + void rsn_set_snonce_cookie(u8 *snonce) { u8 *pos; @@ -1792,4 +2290,475 @@ bool rsn_is_snonce_cookie(const u8 *snonce) return WPA_GET_BE24(pos) == OUI_WFA && WPA_GET_BE24(pos + 3) == 0x000029; } + +#ifdef CONFIG_PASN + +/* + * pasn_use_sha384 - Should SHA384 be used or SHA256 + * + * @akmp: Authentication and key management protocol + * @cipher: The cipher suite + * + * According to IEEE Std 802.11-2024, 12.13.8 (PTKSA derivation with PASN + * authentication), the hash algorithm to use is the + * hash algorithm defined for the Base AKM (see Table 9-190 (AKM suite + * selectors)). When there is no Base AKM, the hash algorithm is selected based + * on the pairwise cipher suite provided in the RSNE by the AP in the second + * PASN frame. SHA-256 is used as the hash algorithm, except for the ciphers + * 00-0F-AC:9 and 00-0F-AC:10 for which SHA-384 is used. + */ +static bool pasn_use_sha384(int akmp, int cipher) +{ + return (akmp == WPA_KEY_MGMT_PASN && (cipher == WPA_CIPHER_GCMP_256)) || + wpa_key_mgmt_sha384(akmp); +} + + +/** + * pasn_select_hash_alg - Select hash algorithm for PTK derivation + * @akmp: Authentication and key management protocol + * @cipher: The cipher suite + * @pmk_len: PMK length in octets + * + * According to IEEE Std 802.11-2024, Table 9-190 (AKM suite selectors), AKMs + * 00-0F-AC:24 and 00-0F-AC:25 have the length of the PMK, the length + * of the SAE key confirmation key, SAE-KCK, and PTK-KCK, and the length of + * PTK-KEK depending on the hash algorithm specified in 12.4.2 (see 12.7.1.3 + * and 12.7.3), i.e, the hash algorithm depends on the prime length associated + * with the selected group per Table 12-1 (Hash algorithm based on length of + * prime). + */ +static enum rsn_hash_alg pasn_select_hash_alg(int akmp, int cipher, + size_t pmk_len) +{ +#ifdef CONFIG_SAE + if (wpa_key_mgmt_sae_ext_key(akmp)) { + if (pmk_len == 48) + return RSN_HASH_SHA384; + } +#endif /* CONFIG_SAE */ + + if (pasn_use_sha384(akmp, cipher)) + return RSN_HASH_SHA384; + + return RSN_HASH_SHA256; +} + +struct pasn_nd_pmk_store pasn_nd_pmk_global; + +void pasn_nd_pmk_global_clear(void) +{ + os_memset(&pasn_nd_pmk_global, 0, sizeof(pasn_nd_pmk_global)); +} + + +/** + * pasn_pmk_to_ptk - Calculate PASN PTK from PMK, addresses, etc. + * @pmk: Pairwise master key + * @pmk_len: Length of PMK + * @spa: Suppplicant address + * @bssid: AP BSSID + * @dhss: Is the shared secret (DHss) derived from the PASN ephemeral key + * exchange encoded as an octet string + * @dhss_len: The length of dhss in octets + * @ptk: Buffer for pairwise transient key + * @akmp: Negotiated AKM + * @cipher: Negotiated pairwise cipher + * @kdk_len: the length in octets that should be derived for HTLK. Can be zero. + * @kek_len: The length in octets that should be derived for KEK. Can be zero. + * @alg: Output variable for indicating the selected hash algorithm + * Returns: 0 on success, -1 on failure + */ +int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, + const u8 *spa, const u8 *bssid, + const u8 *dhss, size_t dhss_len, + struct wpa_ptk *ptk, int akmp, int cipher, + size_t kdk_len, size_t kek_len, enum rsn_hash_alg *alg) +{ + u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN + + WPA_KDK_MAX_LEN]; + u8 kek_buf[WPA_KEK_MAX_LEN]; + u8 nd_pmk_buf[PMK_LEN]; + const u8 *pos; + u8 *data; + size_t data_len, ptk_len; + size_t first_prf_len; + const size_t nan_mgmt_kek_len = 16; + int ret = -1; + const char *label = "PASN PTK Derivation"; + const char *kek_label = "NAN Management KEK Derivation"; + const char *nd_pmk_label = "NDP PMK Derivation"; + + (void) kek_len; + + if (!pmk || !pmk_len) { + wpa_printf(MSG_ERROR, "PASN: No PMK set for PTK derivation"); + return -1; + } + + if (!dhss || !dhss_len) { + wpa_printf(MSG_ERROR, "PASN: No DHss set for PTK derivation"); + return -1; + } + + pasn_nd_pmk_global_clear(); + + /* + * PASN-PTK = KDF(PMK, “PASN PTK Derivation”, SPA || BSSID || DHss) + * + * KCK = L(PASN-PTK, 0, 256) + * TK = L(PASN-PTK, 256, TK_bits) + * KDK = L(PASN-PTK, 256 + TK_bits, kdk_len * 8) + */ + data_len = 2 * ETH_ALEN + dhss_len; + data = os_zalloc(data_len); + if (!data) + return -1; + + os_memcpy(data, spa, ETH_ALEN); + os_memcpy(data + ETH_ALEN, bssid, ETH_ALEN); + os_memcpy(data + 2 * ETH_ALEN, dhss, dhss_len); + + /* KEK is not taken from the first PASN-PTK layout; optional NAN KEK below. */ + ptk->kck_len = WPA_PASN_KCK_LEN; + ptk->tk_len = wpa_cipher_key_len(cipher); + ptk->kdk_len = kdk_len; + ptk->kek_len = 0; + ptk->kek2_len = 0; + ptk->kck2_len = 0; + + if (ptk->tk_len == 0) { + wpa_printf(MSG_ERROR, + "PASN: Unsupported cipher (0x%x) used in PTK derivation", + cipher); + goto err; + } + + first_prf_len = ptk->kck_len + ptk->tk_len + ptk->kdk_len; + if (first_prf_len > sizeof(tmp)) + goto err; + ptk_len = first_prf_len; + + *alg = pasn_select_hash_alg(akmp, cipher, pmk_len); + + switch (*alg) { + case RSN_HASH_SHA384: +#ifdef CONFIG_SHA384 + wpa_printf(MSG_DEBUG, "PASN: PTK derivation using SHA384"); + + if (sha384_prf(pmk, pmk_len, label, data, data_len, tmp, + ptk_len) < 0) + goto err; + break; +#endif + case RSN_HASH_SHA256: + wpa_printf(MSG_DEBUG, "PASN: PTK derivation using SHA256"); + + if (sha256_prf(pmk, pmk_len, label, data, data_len, tmp, + ptk_len) < 0) + goto err; + break; + default: + wpa_printf(MSG_DEBUG, "PASN: Unsupported hash algorithm %d", + *alg); + goto err; + } + + wpa_printf(MSG_DEBUG, + "PASN: PTK derivation: SPA=" MACSTR " BSSID=" MACSTR, + MAC2STR(spa), MAC2STR(bssid)); + + wpa_hexdump_key(MSG_DEBUG, "PASN: DHss", dhss, dhss_len); + wpa_hexdump_key(MSG_DEBUG, "PASN: PMK", pmk, pmk_len); + wpa_hexdump_key(MSG_DEBUG, "PASN: PASN-PTK", tmp, ptk_len); + + os_memcpy(ptk->kck, tmp, WPA_PASN_KCK_LEN); + wpa_hexdump_key(MSG_DEBUG, "PASN: KCK:", ptk->kck, WPA_PASN_KCK_LEN); + pos = &tmp[WPA_PASN_KCK_LEN]; + + os_memcpy(ptk->tk, pos, ptk->tk_len); + wpa_hexdump_key(MSG_DEBUG, "PASN: TK:", ptk->tk, ptk->tk_len); + pos += ptk->tk_len; + + if (kdk_len) { + os_memcpy(ptk->kdk, pos, ptk->kdk_len); + wpa_hexdump_key(MSG_DEBUG, "PASN: KDK:", + ptk->kdk, ptk->kdk_len); + + /* + * NAN Management KEK = KDF(KDK, "NAN Management KEK Derivation", + * SPA || BSSID || DHss) + */ + switch (*alg) { + case RSN_HASH_SHA384: +#ifdef CONFIG_SHA384 + wpa_printf(MSG_DEBUG, "PASN: KEK derivation using SHA384"); + + if (sha384_prf(ptk->kdk, ptk->kdk_len, kek_label, data, + data_len, kek_buf, nan_mgmt_kek_len) < 0) + goto err; + break; +#endif + case RSN_HASH_SHA256: + wpa_printf(MSG_DEBUG, "PASN: KEK derivation using SHA256"); + + if (sha256_prf(ptk->kdk, ptk->kdk_len, kek_label, data, + data_len, kek_buf, nan_mgmt_kek_len) < 0) + goto err; + break; + default: + wpa_printf(MSG_DEBUG, "PASN: Unsupported hash algorithm %d", + *alg); + goto err; + } + + os_memcpy(ptk->kek, kek_buf, nan_mgmt_kek_len); + ptk->kek_len = nan_mgmt_kek_len; + wpa_hexdump_key(MSG_DEBUG, "PASN: KEK (NAN management):", + ptk->kek, ptk->kek_len); + + /* + * ND-PMK = KDF(KDK, "NDP PMK Derivation", SPA || BSSID || DHss) + */ + switch (*alg) { + case RSN_HASH_SHA384: +#ifdef CONFIG_SHA384 + wpa_printf(MSG_DEBUG, "PASN: ND-PMK derivation using SHA384"); + + if (sha384_prf(ptk->kdk, ptk->kdk_len, nd_pmk_label, data, + data_len, nd_pmk_buf, PMK_LEN) < 0) + goto err; + break; +#endif + case RSN_HASH_SHA256: + wpa_printf(MSG_DEBUG, "PASN: ND-PMK derivation using SHA256"); + + if (sha256_prf(ptk->kdk, ptk->kdk_len, nd_pmk_label, data, + data_len, nd_pmk_buf, PMK_LEN) < 0) + goto err; + break; + default: + wpa_printf(MSG_DEBUG, "PASN: Unsupported hash algorithm %d", + *alg); + goto err; + } + + os_memcpy(pasn_nd_pmk_global.nd_pmk, nd_pmk_buf, PMK_LEN); + pasn_nd_pmk_global.valid = 1; + wpa_hexdump_key(MSG_DEBUG, "PASN: ND-PMK (global):", + pasn_nd_pmk_global.nd_pmk, PMK_LEN); + forced_memzero(nd_pmk_buf, sizeof(nd_pmk_buf)); + } + + ptk->ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len + ptk->kdk_len; + forced_memzero(tmp, sizeof(tmp)); + ret = 0; +err: + forced_memzero(kek_buf, sizeof(kek_buf)); + forced_memzero(nd_pmk_buf, sizeof(nd_pmk_buf)); + bin_clear_free(data, data_len); + return ret; +} + + +/* + * pasn_mic_len - Returns the MIC length for PASN authentication + * @alg: Selected hash algorithm from pasn_pmk_to_ptk() + */ +size_t pasn_mic_len(enum rsn_hash_alg alg) +{ + switch (alg) { + case RSN_HASH_SHA384: + return 24; + case RSN_HASH_SHA256: + default: + return 16; + } +} + + +/** + * wpa_ltf_keyseed - Compute LTF keyseed from KDK + * @ptk: Buffer that holds pairwise transient key + * @akmp: Negotiated AKM + * @cipher: Negotiated pairwise cipher + * Returns: 0 on success, -1 on failure + */ +int wpa_ltf_keyseed(struct wpa_ptk *ptk, int akmp, int cipher) +{ + u8 *buf; + size_t buf_len; + u8 hash[SHA384_MAC_LEN]; + const u8 *kdk = ptk->kdk; + size_t kdk_len = ptk->kdk_len; + const char *label = "Secure LTF key seed"; + + if (!kdk || !kdk_len) { + wpa_printf(MSG_ERROR, "WPA: No KDK for LTF keyseed generation"); + return -1; + } + + buf = (u8 *)label; + buf_len = os_strlen(label); + + if (pasn_use_sha384(akmp, cipher)) { + wpa_printf(MSG_DEBUG, + "WPA: Secure LTF keyseed using HMAC-SHA384"); + + if (hmac_sha384(kdk, kdk_len, buf, buf_len, hash)) { + wpa_printf(MSG_ERROR, + "WPA: HMAC-SHA384 compute failed"); + return -1; + } + os_memcpy(ptk->ltf_keyseed, hash, SHA384_MAC_LEN); + ptk->ltf_keyseed_len = SHA384_MAC_LEN; + wpa_hexdump_key(MSG_DEBUG, "WPA: Secure LTF keyseed: ", + ptk->ltf_keyseed, ptk->ltf_keyseed_len); + + } else { + wpa_printf(MSG_DEBUG, "WPA: LTF keyseed using HMAC-SHA256"); + + if (hmac_sha256(kdk, kdk_len, buf, buf_len, hash)) { + wpa_printf(MSG_ERROR, + "WPA: HMAC-SHA256 compute failed"); + return -1; + } + os_memcpy(ptk->ltf_keyseed, hash, SHA256_MAC_LEN); + ptk->ltf_keyseed_len = SHA256_MAC_LEN; + wpa_hexdump_key(MSG_DEBUG, "WPA: Secure LTF keyseed: ", + ptk->ltf_keyseed, ptk->ltf_keyseed_len); + } + + return 0; +} + + +/** + * pasn_mic - Calculate PASN MIC + * @alg: Selected hash algorithm from pasn_pmk_to_ptk() + * @kck: The key confirmation key for the PASN PTKSA + * @kck_len: KCK length in octets + * @addr1: For the 2nd PASN frame supplicant address; for the 3rd frame the + * BSSID + * @addr2: For the 2nd PASN frame the BSSID; for the 3rd frame the supplicant + * address + * @data: For the 2nd PASN frame, RSNE concatenated with RSNXE (each full IE) + * as present in that Authentication frame, in order. For the 3rd PASN + * frame, this should hold the hash of the body of the PASN 1st frame. + * @data_len: The length of data + * @frame: The body of the PASN frame including the MIC element with the octets + * in the MIC field of the MIC element set to 0. + * @frame_len: The length of frame + * @mic: Buffer to hold the MIC on success. Should be big enough to handle the + * maximal MIC length + * Returns: 0 on success, -1 on failure + */ +int pasn_mic(enum rsn_hash_alg alg, const u8 *kck, size_t kck_len, + const u8 *addr1, const u8 *addr2, + const u8 *data, size_t data_len, + const u8 *frame, size_t frame_len, u8 *mic) +{ + u8 *buf; + u8 hash[SHA512_MAC_LEN]; + size_t buf_len = 2 * ETH_ALEN + data_len + frame_len; + int ret = -1; + size_t mic_len; + + if (!kck) { + wpa_printf(MSG_ERROR, "PASN: No KCK for MIC calculation"); + return -1; + } + + if (kck_len != WPA_PASN_KCK_LEN) { + wpa_printf(MSG_ERROR, + "PASN: Unexpected KCK length %zu for MIC calculation", + kck_len); + return -1; + } + + if (!data || !data_len) { + wpa_printf(MSG_ERROR, "PASN: invalid data for MIC calculation"); + return -1; + } + + if (!frame || !frame_len) { + wpa_printf(MSG_ERROR, "PASN: invalid data for MIC calculation"); + return -1; + } + + buf = os_zalloc(buf_len); + if (!buf) + return -1; + + os_memcpy(buf, addr1, ETH_ALEN); + os_memcpy(buf + ETH_ALEN, addr2, ETH_ALEN); + + wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: data", data, data_len); + os_memcpy(buf + 2 * ETH_ALEN, data, data_len); + + wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: frame", frame, frame_len); + os_memcpy(buf + 2 * ETH_ALEN + data_len, frame, frame_len); + + wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: KCK", kck, kck_len); + wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: buf", buf, buf_len); + + mic_len = pasn_mic_len(alg); + + switch (alg) { +#ifdef CONFIG_SHA384 + case RSN_HASH_SHA384: + wpa_printf(MSG_DEBUG, "PASN: MIC using HMAC-SHA384"); + if (hmac_sha384(kck, kck_len, buf, buf_len, hash)) + goto err; + break; +#endif /* CONFIG_SHA384 */ + case RSN_HASH_SHA256: + wpa_printf(MSG_DEBUG, "PASN: MIC using HMAC-SHA256"); + if (hmac_sha256(kck, kck_len, buf, buf_len, hash)) + goto err; + break; + default: + wpa_printf(MSG_ERROR, + "PASN: Unsupported alg=%d for MIC calculation", alg); + goto err; + } + + os_memcpy(mic, hash, mic_len); + wpa_hexdump_key(MSG_DEBUG, "PASN: MIC", mic, mic_len); + + ret = 0; +err: + bin_clear_free(buf, buf_len); + return ret; +} + + +/** + * pasn_auth_frame_hash - Computes a hash of an Authentication frame body + * @alg: Selected hash algorithm from pasn_pmk_to_ptk() + * @data: Pointer to the Authentication frame body + * @len: Length of the Authentication frame body + * @hash: On return would hold the computed hash. Should be big enough to handle + * SHA512. + * Returns: 0 on success, -1 on failure + */ +int pasn_auth_frame_hash(enum rsn_hash_alg alg, const u8 *data, size_t len, + u8 *hash) +{ + switch (alg) { +#ifdef CONFIG_SHA384 + case RSN_HASH_SHA384: + wpa_printf(MSG_DEBUG, "PASN: Frame hash using SHA-384"); + return sha384_vector(1, &data, &len, hash); +#endif /* CONFIG_SHA384 */ + case RSN_HASH_SHA256: + wpa_printf(MSG_DEBUG, "PASN: Frame hash using SHA-256"); + return sha256_vector(1, &data, &len, hash); + default: + wpa_printf(MSG_ERROR, "PASN: Unsupported alg=%d", alg); + return -1; + } +} + +#endif /* CONFIG_PASN */ #endif // ESP_SUPPLICANT diff --git a/components/wpa_supplicant/src/common/wpa_common.h b/components/wpa_supplicant/src/common/wpa_common.h index c035c2051ba..aafb0128efa 100644 --- a/components/wpa_supplicant/src/common/wpa_common.h +++ b/components/wpa_supplicant/src/common/wpa_common.h @@ -24,6 +24,29 @@ #define WPA_GMK_LEN 32 #define WPA_GTK_MAX_LEN 32 #define WPA_MAX_RSNXE_LEN 4 +#define WPA_PASN_PMK_LEN 32 +#define WPA_PASN_MAX_MIC_LEN 32 + +/** + * NDP PMK (32 octets) from KDK in pasn_pmk_to_ptk (label "NDP PMK Derivation"). + * @valid: nonzero after successful derivation in the current session. + */ +struct pasn_nd_pmk_store { + u8 nd_pmk[PMK_LEN]; + int valid; +}; + +extern struct pasn_nd_pmk_store pasn_nd_pmk_global; + +void pasn_nd_pmk_global_clear(void); + +#define COMEBACK_PENDING_IDX_SIZE 256 + +enum rsn_hash_alg { + RSN_HASH_SHA256, + RSN_HASH_SHA384, + RSN_HASH_SHA512, +}; #define WPA_SELECTOR_LEN 4 #define WPA_VERSION 1 @@ -59,6 +82,9 @@ #define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3) #define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4) #endif /* CONFIG_IEEE80211R */ + +#define RSN_AUTH_KEY_MGMT_PASN RSN_SELECTOR(0x00, 0x0f, 0xac, 21) + #define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #define RSN_AUTH_KEY_MGMT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 8) @@ -129,6 +155,7 @@ RSN_SELECTOR(0x00, 0x0f, 0xac, 13) #define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9) #define WPA_CAPABILITY_SPP_CAPABLE BIT(10) #define WPA_CAPABILITY_SPP_REQUIRED BIT(11) +#define WPA_CAPABILITY_OCVC BIT(14) /* Operating Channel Validation Capable */ /* IEEE 802.11r */ @@ -201,10 +228,12 @@ struct wpa_eapol_key_192 { } STRUCT_PACKED; #define WPA_EAPOL_KEY_MIC_MAX_LEN 24 -#define WPA_KCK_MAX_LEN 24 +#define WPA_KCK_MAX_LEN 32 #define WPA_KEK_MAX_LEN 32 - +#define WPA_KDK_MAX_LEN 32 #define WPA_TK_MAX_LEN 32 +#define WPA_PASN_KCK_LEN 32 +#define WPA_LTF_KEYSEED_MAX_LEN 48 /** * struct wpa_ptk - WPA Pairwise Transient Key @@ -214,9 +243,18 @@ struct wpa_ptk { u8 kck[WPA_KCK_MAX_LEN]; /* EAPOL-Key Key Confirmation Key (KCK) */ u8 kek[WPA_KEK_MAX_LEN]; /* EAPOL-Key Key Encryption Key (KEK) */ u8 tk[WPA_TK_MAX_LEN]; /* Temporal Key (TK) */ + u8 kck2[WPA_KCK_MAX_LEN]; /* FT reasoc Key Confirmation Key (KCK2) */ + u8 kek2[WPA_KEK_MAX_LEN]; /* FT reassoc Key Encryption Key (KEK2) */ + u8 kdk[WPA_KDK_MAX_LEN]; /* Key Derivation Key */ + u8 ltf_keyseed[WPA_LTF_KEYSEED_MAX_LEN]; /* LTF Key seed */ size_t kck_len; size_t kek_len; size_t tk_len; + size_t kck2_len; + size_t kek2_len; + size_t kdk_len; + size_t ptk_len; + size_t ltf_keyseed_len; int installed; /* 1 if key has already been installed to driver */ }; @@ -375,6 +413,8 @@ struct wpa_ie_data { const u8 *pmkid; int mgmt_group_cipher; uint8_t rsnxe_capa; + int has_group; + int has_pairwise; }; struct rsn_sppamsdu_sup { @@ -441,6 +481,38 @@ struct wpa_ft_ies { size_t rsnxe_len; }; +/* IEEE Std 802.11-2024 - 9.4.2.305 PASN Parameters element */ +#define WPA_PASN_CTRL_COMEBACK_INFO_PRESENT BIT(0) +#define WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT BIT(1) + +#define WPA_PASN_WRAPPED_DATA_NO 0 +#define WPA_PASN_WRAPPED_DATA_FT 1 +#define WPA_PASN_WRAPPED_DATA_FILS_SK 2 +#define WPA_PASN_WRAPPED_DATA_SAE 3 + +struct pasn_parameter_ie { + u8 id; + u8 len; + u8 id_ext; + u8 control; /* WPA_PASN_CTRL_* */ + u8 wrapped_data_format; /* WPA_PASN_WRAPPED_DATA_* */ +} STRUCT_PACKED; + +struct wpa_pasn_params_data { + u8 wrapped_data_format; + u16 after; + u8 comeback_len; + const u8 *comeback; + u16 group; + u8 pubkey_len; + const u8 *pubkey; +}; + +/* See RFC 5480 section 2.2 */ +#define WPA_PASN_PUBKEY_COMPRESSED_0 0x02 +#define WPA_PASN_PUBKEY_COMPRESSED_1 0x03 +#define WPA_PASN_PUBKEY_UNCOMPRESSED 0x04 + /* WPA3 specification - RSN Selection element */ enum rsn_selection_variant { RSN_SELECTION_RSNE = 0, @@ -522,4 +594,43 @@ int wpa_use_aes_key_wrap(int akmp); void rsn_set_snonce_cookie(u8 *snonce); bool rsn_is_snonce_cookie(const u8 *snonce); +int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, + const u8 *spa, const u8 *bssid, + const u8 *dhss, size_t dhss_len, + struct wpa_ptk *ptk, int akmp, int cipher, + size_t kdk_len, size_t kek_len, enum rsn_hash_alg *alg); + +size_t pasn_mic_len(enum rsn_hash_alg alg); + +int pasn_mic(enum rsn_hash_alg alg, const u8 *kck, size_t kck_len, + const u8 *addr1, const u8 *addr2, + const u8 *data, size_t data_len, + const u8 *frame, size_t frame_len, u8 *mic); + +int wpa_ltf_keyseed(struct wpa_ptk *ptk, int akmp, int cipher); + +int pasn_auth_frame_hash(enum rsn_hash_alg alg, const u8 *data, size_t len, + u8 *hash); + +void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid, + const u8 *src, const u8 *dst, + u8 trans_seq, u16 status); + +int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, + int akmp, int cipher); + +void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group, + u8 wrapped_data_format, + const struct wpabuf *pubkey, bool compressed, + const struct wpabuf *comeback, int after); + +int wpa_pasn_add_wrapped_data(struct wpabuf *buf, + struct wpabuf *wrapped_data_buf); + +int wpa_pasn_validate_rsne(const struct wpa_ie_data *data); +int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, bool from_ap, + struct wpa_pasn_params_data *pasn_params); + +void wpa_pasn_add_rsnxe(struct wpabuf *buf, u16 capab); +int wpa_pasn_add_extra_ies(struct wpabuf *buf, const u8 *extra_ies, size_t len); #endif /* WPA_COMMON_H */ diff --git a/components/wpa_supplicant/src/crypto/crypto.h b/components/wpa_supplicant/src/crypto/crypto.h index 7db0c1d4849..e661de49fcc 100644 --- a/components/wpa_supplicant/src/crypto/crypto.h +++ b/components/wpa_supplicant/src/crypto/crypto.h @@ -1163,6 +1163,7 @@ struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh,int y); struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y, const u8 *key, size_t len); +size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh); /** * crypto_ec_key_parse_pub - Initialize EC key pair from SubjectPublicKeyInfo ASN.1 diff --git a/components/wpa_supplicant/src/pasn/pasn_common.c b/components/wpa_supplicant/src/pasn/pasn_common.c new file mode 100644 index 00000000000..9bf4c8830d0 --- /dev/null +++ b/components/wpa_supplicant/src/pasn/pasn_common.c @@ -0,0 +1,352 @@ +/* + * PASN common processing + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/wpa_common.h" +#include "common/sae.h" +#include "crypto/sha384.h" +#include "crypto/crypto.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "crypto/aes_wrap.h" +#include "pasn_common.h" + + +struct pasn_data * pasn_data_init(void) +{ + struct pasn_data *pasn = os_zalloc(sizeof(struct pasn_data)); + + return pasn; +} + + +void pasn_data_deinit(struct pasn_data *pasn) +{ + if (!pasn) + return; + os_free(pasn->rsnxe_ie); + wpabuf_free(pasn->frame); + os_free(pasn->pasn_groups); + wpabuf_free(pasn->auth1); + bin_clear_free(pasn, sizeof(struct pasn_data)); +} + + +void pasn_register_callbacks(struct pasn_data *pasn, void *cb_ctx, + int (*send_mgmt)(void *ctx, const u8 *data, + size_t data_len, int noack, + unsigned int freq, + unsigned int wait), + int (*validate_custom_pmkid)(void *ctx, + const u8 *addr, + const u8 *pmkid)) +{ + if (!pasn) + return; + + pasn->cb_ctx = cb_ctx; + pasn->send_mgmt = send_mgmt; + pasn->validate_custom_pmkid = validate_custom_pmkid; +} + + +void pasn_enable_kdk_derivation(struct pasn_data *pasn) +{ + if (!pasn) + return; + pasn->derive_kdk = true; + pasn->kdk_len = WPA_KDK_MAX_LEN; +} + + +void pasn_disable_kdk_derivation(struct pasn_data *pasn) +{ + if (!pasn) + return; + pasn->derive_kdk = false; + pasn->kdk_len = 0; +} + + +void pasn_set_akmp(struct pasn_data *pasn, int akmp) +{ + if (!pasn) + return; + pasn->akmp = akmp; +} + + +void pasn_set_cipher(struct pasn_data *pasn, int cipher) +{ + if (!pasn) + return; + pasn->cipher = cipher; +} + + +void pasn_set_own_addr(struct pasn_data *pasn, const u8 *addr) +{ + if (!pasn || !addr) + return; + os_memcpy(pasn->own_addr, addr, ETH_ALEN); +} + + +void pasn_set_peer_addr(struct pasn_data *pasn, const u8 *addr) +{ + if (!pasn || !addr) + return; + os_memcpy(pasn->peer_addr, addr, ETH_ALEN); +} + + +void pasn_set_bssid(struct pasn_data *pasn, const u8 *addr) +{ + if (!pasn || !addr) + return; + os_memcpy(pasn->bssid, addr, ETH_ALEN); +} + + +int pasn_set_pt(struct pasn_data *pasn, struct sae_pt *pt) +{ + if (!pasn) + return -1; +#ifdef CONFIG_SAE + pasn->pt = pt; + return 0; +#else /* CONFIG_SAE */ + return -1; +#endif /* CONFIG_SAE */ +} + + +void pasn_set_password(struct pasn_data *pasn, const char *password) +{ + if (!pasn) + return; + pasn->password = password; +} + + +void pasn_set_wpa_key_mgmt(struct pasn_data *pasn, int key_mgmt) +{ + if (!pasn) + return; + pasn->wpa_key_mgmt = key_mgmt; +} + + +void pasn_set_rsn_pairwise(struct pasn_data *pasn, int rsn_pairwise) +{ + if (!pasn) + return; + pasn->rsn_pairwise = rsn_pairwise; +} + + +void pasn_set_rsnxe_caps(struct pasn_data *pasn, u16 rsnxe_capab) +{ + if (!pasn) + return; + pasn->rsnxe_capab = rsnxe_capab; +} + + +void pasn_set_rsnxe_ie(struct pasn_data *pasn, const u8 *rsnxe_ie) +{ + if (!pasn || !rsnxe_ie) + return; + pasn->rsnxe_ie = os_memdup(rsnxe_ie, 2 + rsnxe_ie[1]); +} + + +void pasn_set_custom_pmkid(struct pasn_data *pasn, const u8 *pmkid) +{ + if (!pasn || !pmkid) + return; + os_memcpy(pasn->custom_pmkid, pmkid, PMKID_LEN); + pasn->custom_pmkid_valid = true; +} + + +int pasn_set_extra_ies(struct pasn_data *pasn, const u8 *extra_ies, + size_t extra_ies_len) +{ + if (!pasn || !extra_ies_len || !extra_ies) + return -1; + + if (pasn->extra_ies) { + os_free((u8 *) pasn->extra_ies); + pasn->extra_ies_len = 0; + } + + pasn->extra_ies = os_memdup(extra_ies, extra_ies_len); + if (!pasn->extra_ies) { + wpa_printf(MSG_ERROR, + "PASN: Extra IEs memory allocation failed"); + return -1; + } + pasn->extra_ies_len = extra_ies_len; + return 0; +} + + +void pasn_set_noauth(struct pasn_data *pasn, bool noauth) +{ + if (!pasn) + return; + pasn->noauth = noauth; +} + + +int pasn_get_akmp(struct pasn_data *pasn) +{ + if (!pasn) + return 0; + return pasn->akmp; +} + + +int pasn_get_cipher(struct pasn_data *pasn) +{ + if (!pasn) + return 0; + return pasn->cipher; +} + + +size_t pasn_get_pmk_len(struct pasn_data *pasn) +{ + if (!pasn) + return 0; + return pasn->pmk_len; +} + + +u8 * pasn_get_pmk(struct pasn_data *pasn) +{ + if (!pasn) + return NULL; + return pasn->pmk; +} + + +struct wpa_ptk * pasn_get_ptk(struct pasn_data *pasn) +{ + if (!pasn) + return NULL; + return &pasn->ptk; +} + + +int pasn_add_encrypted_data(struct pasn_data *pasn, struct wpabuf *buf, + const u8 *data, size_t data_len) +{ + int ret; + u8 *encrypted_data, *padded_data = NULL; + u8 *len; + size_t pad_len = 0; + + if (!pasn->ptk.kek_len) { + wpa_printf(MSG_DEBUG, "PASN: KEK not available"); + return -2; + } + + pad_len = data_len % 8; + if (pad_len) { + pad_len = 8 - pad_len; + padded_data = os_zalloc(data_len + pad_len); + if (!padded_data) + return -1; + os_memcpy(padded_data, data, data_len); + data = padded_data; + padded_data[data_len] = 0xdd; + } + data_len += pad_len + 8; + + encrypted_data = os_malloc(data_len); + if (!encrypted_data) { + os_free(padded_data); + return -1; + } + + ret = aes_wrap(pasn->ptk.kek, pasn->ptk.kek_len, + (data_len - 8) / 8, data, encrypted_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: AES wrap failed, ret=%d", ret); + goto out; + } + + if (wpabuf_tailroom(buf) < 1 + 1 + 1 + data_len) { + wpa_printf(MSG_DEBUG, + "PASN: Not enough room in the buffer for PASN Encrypred Data element"); + ret = -1; + goto out; + } + + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + len = wpabuf_put(buf, 1); + + wpabuf_put_u8(buf, WLAN_EID_EXT_PASN_ENCRYPTED_DATA); + + wpabuf_put_data(buf, encrypted_data, data_len); + *len = (u8 *) wpabuf_put(buf, 0) - len - 1; + +out: + os_free(padded_data); + os_free(encrypted_data); + return ret; +} + + +int pasn_parse_encrypted_data(struct pasn_data *pasn, const u8 *data, + size_t len) +{ + int ret = -1; + u8 *buf; + u16 buf_len; + struct ieee802_11_elems elems; + const struct ieee80211_auth *mgmt = + (const struct ieee80211_auth *) data; + + if (len < 24 + 6 || + ieee802_11_parse_elems(mgmt->auth.variable, + len - offsetof(struct ieee80211_auth, + auth.variable), + &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "PASN: Failed parsing Authentication frame"); + return -1; + } + + if (!elems.pasn_encrypted_data || elems.pasn_encrypted_data_len < 8 || + elems.pasn_encrypted_data_len % 8) { + wpa_printf(MSG_DEBUG, "PASN: No encrypted elements"); + return 0; + } + + buf_len = elems.pasn_encrypted_data_len - 8; + + buf = os_malloc(buf_len); + if (!buf) + return -1; + + ret = aes_unwrap(pasn->ptk.kek, pasn->ptk.kek_len, buf_len / 8, + elems.pasn_encrypted_data, buf); + if (ret) + wpa_printf(MSG_DEBUG, "PASN: AES unwrap failed, ret=%d", ret); + else if (pasn->parse_data_element && pasn->cb_ctx) + ret = pasn->parse_data_element(pasn->cb_ctx, buf, buf_len); + + os_free(buf); + return ret; +} diff --git a/components/wpa_supplicant/src/pasn/pasn_common.h b/components/wpa_supplicant/src/pasn/pasn_common.h new file mode 100644 index 00000000000..6cd0f7a1e52 --- /dev/null +++ b/components/wpa_supplicant/src/pasn/pasn_common.h @@ -0,0 +1,272 @@ +/* + * PASN info for initiator and responder + * + * Copyright (C) 2019, Intel Corporation + * Copyright (c) 2022, Jouni Malinen + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PASN_COMMON_H +#define PASN_COMMON_H + +#include "common/wpa_common.h" +#if defined(CONFIG_SAE) || defined(CONFIG_WPA3_SAE) +#include "common/sae.h" +#endif /* CONFIG_SAE || CONFIG_WPA3_SAE */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ESP_SUPPLICANT +#ifdef CONFIG_FILS +enum pasn_fils_state { + PASN_FILS_STATE_NONE = 0, + PASN_FILS_STATE_PENDING_AS, + PASN_FILS_STATE_COMPLETE +}; + +struct pasn_fils { + u8 state; + u8 nonce[FILS_NONCE_LEN]; + u8 anonce[FILS_NONCE_LEN]; + u8 session[FILS_SESSION_LEN]; + u8 erp_pmkid[PMKID_LEN]; + bool completed; + struct wpabuf *erp_resp; +}; +#endif /* CONFIG_FILS */ +#endif /* ESP_SUPPLICANT */ + +struct pasn_data { + /* External modules access below variables using setter and getter + * functions */ + int akmp; + int cipher; + u8 own_addr[ETH_ALEN]; + u8 peer_addr[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + struct rsn_pmksa_cache *pmksa; + bool derive_kdk; + size_t kdk_len; + void *cb_ctx; + + struct sae_pt *pt; + + /* Responder */ + const char *password; + int wpa_key_mgmt; + int rsn_pairwise; + u16 rsnxe_capab; + u8 *rsnxe_ie; + bool custom_pmkid_valid; + u8 custom_pmkid[PMKID_LEN]; + + /* + * Extra elements to add into Authentication frames. These can be used, + * e.g., for Wi-Fi Aware use cases. + */ + const u8 *extra_ies; + size_t extra_ies_len; + + /* External modules do not access below variables */ + bool derive_kek; + size_t kek_len; + u16 group; + bool secure_ltf; + int freq; + + u8 trans_seq; + u8 status; + + size_t pmk_len; + u8 pmk[PMK_LEN_MAX]; + bool using_pmksa; + enum rsn_hash_alg hash_alg; + + struct wpabuf *auth1; + + struct wpa_ptk ptk; + struct crypto_ecdh *ecdh; + + struct wpabuf *comeback; + u16 comeback_after; + +#if defined(CONFIG_SAE) || defined(CONFIG_WPA3_SAE) + struct sae_data sae; +#endif /* CONFIG_SAE || CONFIG_WPA3_SAE */ + +#ifndef ESP_SUPPLICANT +#ifdef CONFIG_FILS + bool fils_eapol; + bool fils_wd_valid; + struct pasn_fils fils; +#endif /* CONFIG_FILS */ +#endif /* ESP_SUPPLICANT */ + +#ifdef CONFIG_IEEE80211R + u8 pmk_r1[PMK_LEN_MAX]; + size_t pmk_r1_len; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; +#endif /* CONFIG_IEEE80211R */ + /* Note that these pointers to RSN PMKSA cache are defined differently + * for the PASN initiator (RSN supplicant) and PASN responder (RSN + * authenticator). Functions cannot be mixed between those cases. */ + struct rsn_pmksa_cache_entry *pmksa_entry; + struct eapol_sm *eapol; + int fast_reauth; +#ifdef CONFIG_TESTING_OPTIONS + int corrupt_mic; +#endif /* CONFIG_TESTING_OPTIONS */ + int network_id; + + u8 wrapped_data_format; + struct wpabuf *secret; + + /* Responder */ + bool noauth; /* Whether PASN without mutual authentication is enabled */ + int disable_pmksa_caching; + int *pasn_groups; + int use_anti_clogging; + const u8 *rsn_ie; + size_t rsn_ie_len; + + u8 *comeback_key; + struct os_reltime last_comeback_key_update; + u16 comeback_idx; + u16 *comeback_pending_idx; + struct wpabuf *frame; + + /** + * send_mgmt - Function handler to transmit a Management frame + * @ctx: Callback context from cb_ctx + * @frame_buf : Frame to transmit + * @frame_len: Length of frame to transmit + * @freq: Frequency in MHz for the channel on which to transmit + * @wait_dur: How many milliseconds to wait for a response frame + * Returns: 0 on success, -1 on failure + */ + int (*send_mgmt)(void *ctx, const u8 *data, size_t data_len, int noack, + unsigned int freq, unsigned int wait); + /** + * validate_custom_pmkid - Handler to validate vendor specific PMKID + * @ctx: Callback context from cb_ctx + * @addr : MAC address of the peer + * @pmkid: Custom PMKID + * Returns: 0 on success (valid PMKID), -1 on failure + */ + int (*validate_custom_pmkid)(void *ctx, const u8 *addr, + const u8 *pmkid); + + int (*prepare_data_element)(void *ctx, const u8 *peer_addr); + + int (*parse_data_element)(void *ctx, const u8 *data, size_t len); +}; + +/* Initiator */ +void wpa_pasn_reset(struct pasn_data *pasn); +int wpas_pasn_start(struct pasn_data *pasn, const u8 *own_addr, + const u8 *peer_addr, const u8 *bssid, + int akmp, int cipher, u16 group, + int freq, const u8 *beacon_rsne, u8 beacon_rsne_len, + const u8 *beacon_rsnxe, u8 beacon_rsnxe_len, + const struct wpabuf *comeback); +int wpa_pasn_verify(struct pasn_data *pasn, const u8 *own_addr, + const u8 *peer_addr, const u8 *bssid, + int akmp, int cipher, u16 group, + int freq, const u8 *beacon_rsne, u8 beacon_rsne_len, + const u8 *beacon_rsnxe, u8 beacon_rsnxe_len, + const struct wpabuf *comeback); +int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, + struct wpa_pasn_params_data *pasn_params); +int wpa_pasn_auth_tx_status(struct pasn_data *pasn, + const u8 *data, size_t data_len, u8 acked); + +/* Responder */ +int handle_auth_pasn_1(struct pasn_data *pasn, + const u8 *own_addr, const u8 *peer_addr, + const struct ieee80211_auth *mgmt, size_t len, + bool reject); +int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr, + const u8 *peer_addr, + const struct ieee80211_auth *mgmt, size_t len); +int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr, + const u8 *peer_addr, + struct rsn_pmksa_cache_entry *pmksa, u16 status); + +struct pasn_data * pasn_data_init(void); +void pasn_data_deinit(struct pasn_data *pasn); +void pasn_register_callbacks(struct pasn_data *pasn, void *cb_ctx, + int (*send_mgmt)(void *ctx, const u8 *data, + size_t data_len, int noack, + unsigned int freq, + unsigned int wait), + int (*validate_custom_pmkid)(void *ctx, + const u8 *addr, + const u8 *pmkid)); +void pasn_enable_kdk_derivation(struct pasn_data *pasn); +void pasn_disable_kdk_derivation(struct pasn_data *pasn); + +void pasn_set_akmp(struct pasn_data *pasn, int akmp); +void pasn_set_cipher(struct pasn_data *pasn, int cipher); +void pasn_set_own_addr(struct pasn_data *pasn, const u8 *addr); +void pasn_set_peer_addr(struct pasn_data *pasn, const u8 *addr); +void pasn_set_bssid(struct pasn_data *pasn, const u8 *addr); +void pasn_set_initiator_pmksa(struct pasn_data *pasn, + struct rsn_pmksa_cache *pmksa); +void pasn_set_responder_pmksa(struct pasn_data *pasn, + struct rsn_pmksa_cache *pmksa); +int pasn_set_pt(struct pasn_data *pasn, struct sae_pt *pt); +struct rsn_pmksa_cache * pasn_initiator_pmksa_cache_init(void); +void pasn_initiator_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); +int pasn_initiator_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, + const u8 *own_addr, const u8 *bssid, + const u8 *pmk, size_t pmk_len, + const u8 *pmkid); +int pasn_initiator_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *bssid, u8 *pmkid, u8 *pmk, + size_t *pmk_len); +void pasn_initiator_pmksa_cache_remove(struct rsn_pmksa_cache *pmksa, + const u8 *bssid); +void pasn_initiator_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa); + +/* Responder */ +void pasn_set_noauth(struct pasn_data *pasn, bool noauth); +void pasn_set_password(struct pasn_data *pasn, const char *password); +void pasn_set_wpa_key_mgmt(struct pasn_data *pasn, int key_mgmt); +void pasn_set_rsn_pairwise(struct pasn_data *pasn, int rsn_pairwise); +void pasn_set_rsnxe_caps(struct pasn_data *pasn, u16 rsnxe_capab); +void pasn_set_rsnxe_ie(struct pasn_data *pasn, const u8 *rsnxe_ie); +void pasn_set_custom_pmkid(struct pasn_data *pasn, const u8 *pmkid); +int pasn_set_extra_ies(struct pasn_data *pasn, const u8 *extra_ies, + size_t extra_ies_len); +struct rsn_pmksa_cache * pasn_responder_pmksa_cache_init(void); +void pasn_responder_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); +int pasn_responder_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, + const u8 *own_addr, const u8 *bssid, + const u8 *pmk, size_t pmk_len, + const u8 *pmkid); +int pasn_responder_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *bssid, u8 *pmkid, u8 *pmk, + size_t *pmk_len); +void pasn_responder_pmksa_cache_remove(struct rsn_pmksa_cache *pmksa, + const u8 *bssid); +void pasn_responder_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa); + +int pasn_get_akmp(struct pasn_data *pasn); +int pasn_get_cipher(struct pasn_data *pasn); +size_t pasn_get_pmk_len(struct pasn_data *pasn); +u8 * pasn_get_pmk(struct pasn_data *pasn); +struct wpa_ptk * pasn_get_ptk(struct pasn_data *pasn); +int pasn_add_encrypted_data(struct pasn_data *pasn, struct wpabuf *buf, + const u8 *data, size_t data_len); +int pasn_parse_encrypted_data(struct pasn_data *pasn, const u8 *data, + size_t len); + +#ifdef __cplusplus +} +#endif +#endif /* PASN_COMMON_H */ diff --git a/components/wpa_supplicant/src/pasn/pasn_initiator.c b/components/wpa_supplicant/src/pasn/pasn_initiator.c new file mode 100644 index 00000000000..c712ff636ac --- /dev/null +++ b/components/wpa_supplicant/src/pasn/pasn_initiator.c @@ -0,0 +1,1555 @@ +/* + * PASN initiator processing + * + * Copyright (C) 2019, Intel Corporation + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/wpa_common.h" +#include "common/sae.h" +#include "common/ieee802_11_common.h" +#include "common/ieee802_11_defs.h" +#include "common/dragonfly.h" +#include "crypto/sha384.h" +#include "crypto/crypto.h" +#include "crypto/random.h" +#include "eap_peer/eap_defs.h" +#include "rsn_supp/wpa.h" +#include "rsn_supp/pmksa_cache.h" +#include "pasn_common.h" + + +struct rsn_pmksa_cache * pasn_initiator_pmksa_cache_init(void) +{ + return pmksa_cache_init(NULL, NULL, NULL); +} + + +void pasn_initiator_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) +{ + return pmksa_cache_deinit(pmksa); +} + + +int pasn_initiator_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, + const u8 *own_addr, const u8 *bssid, + const u8 *pmk, + size_t pmk_len, const u8 *pmkid) +{ + if (pmksa_cache_add(pmksa, pmk, pmk_len, pmkid, NULL, 0, bssid, + own_addr, NULL, WPA_KEY_MGMT_SAE)) + return 0; + return -1; +} + + +void pasn_initiator_pmksa_cache_remove(struct rsn_pmksa_cache *pmksa, + const u8 *bssid) +{ + struct rsn_pmksa_cache_entry *entry; + + entry = pmksa_cache_get(pmksa, bssid, NULL, NULL); + if (!entry) + return; + + pmksa_cache_remove(pmksa, entry); +} + + +int pasn_initiator_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *bssid, u8 *pmkid, u8 *pmk, + size_t *pmk_len) +{ + struct rsn_pmksa_cache_entry *entry; + + entry = pmksa_cache_get(pmksa, bssid, NULL, NULL); + if (entry) { + os_memcpy(pmkid, entry->pmkid, PMKID_LEN); + os_memcpy(pmk, entry->pmk, entry->pmk_len); + *pmk_len = entry->pmk_len; + return 0; + } + return -1; +} + + +void pasn_initiator_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa) +{ + return pmksa_cache_flush(pmksa, NULL, NULL, 0); +} + + +void pasn_set_initiator_pmksa(struct pasn_data *pasn, + struct rsn_pmksa_cache *pmksa) +{ + if (pasn) + pasn->pmksa = pmksa; +} + + +#if defined(CONFIG_SAE) || defined(CONFIG_WPA3_SAE) + +static struct wpabuf * wpas_pasn_wd_sae_commit(struct pasn_data *pasn) +{ + struct wpabuf *buf = NULL; + int ret; + + ret = sae_set_group(&pasn->sae, pasn->group); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group"); + return NULL; + } + + pasn->sae.akmp = pasn->akmp; + ret = sae_prepare_commit_pt(&pasn->sae, pasn->pt, + pasn->own_addr, pasn->peer_addr, + NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit"); + return NULL; + } + + /* Need to add the entire Authentication frame body */ + buf = wpabuf_alloc(6 + SAE_COMMIT_MAX_LEN); + if (!buf) { + wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer"); + return NULL; + } + + wpabuf_put_le16(buf, WLAN_AUTH_SAE); + wpabuf_put_le16(buf, 1); + wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT); + + sae_write_commit(&pasn->sae, buf, NULL, NULL); + pasn->sae.state = SAE_COMMITTED; + + return buf; +} + + +static int wpas_pasn_wd_sae_rx(struct pasn_data *pasn, struct wpabuf *wd) +{ + const u8 *data; + size_t buf_len; + u16 len, res, alg, seq, status; + int groups[] = { pasn->group, 0 }; + int ret; + + if (!wd) + return -1; + + data = wpabuf_head_u8(wd); + buf_len = wpabuf_len(wd); + + /* first handle the commit message */ + if (buf_len < 2) { + wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (commit)"); + return -1; + } + + len = WPA_GET_LE16(data); + if (len < 6 || buf_len - 2 < len) { + wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for commit"); + return -1; + } + + buf_len -= 2; + data += 2; + + alg = WPA_GET_LE16(data); + seq = WPA_GET_LE16(data + 2); + status = WPA_GET_LE16(data + 4); + + wpa_printf(MSG_DEBUG, "PASN: SAE: commit: alg=%u, seq=%u, status=%u", + alg, seq, status); + + if (alg != WLAN_AUTH_SAE || seq != 1 || + status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) { + wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer commit"); + return -1; + } + + res = sae_parse_commit(&pasn->sae, data + 6, len - 6, NULL, NULL, + groups, 1); + if (res != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: SAE failed parsing commit"); + return -1; + } + + /* Process the commit message and derive the PMK */ + ret = sae_process_commit(&pasn->sae); + if (ret) { + wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit"); + return -1; + } + + buf_len -= len; + data += len; + + /* Handle the confirm message */ + if (buf_len < 2) { + wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (confirm)"); + return -1; + } + + len = WPA_GET_LE16(data); + if (len < 6 || buf_len - 2 < len) { + wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for confirm"); + return -1; + } + + buf_len -= 2; + data += 2; + + alg = WPA_GET_LE16(data); + seq = WPA_GET_LE16(data + 2); + status = WPA_GET_LE16(data + 4); + + wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u", + alg, seq, status); + + if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm"); + return -1; + } + + res = sae_check_confirm(&pasn->sae, data + 6, len - 6); + if (res != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm"); + return -1; + } + + wpa_printf(MSG_DEBUG, "PASN: SAE completed successfully"); + pasn->sae.state = SAE_ACCEPTED; + + return 0; +} + + +static struct wpabuf * wpas_pasn_wd_sae_confirm(struct pasn_data *pasn) +{ + struct wpabuf *buf = NULL; + + /* Need to add the entire authentication frame body */ + buf = wpabuf_alloc(6 + SAE_CONFIRM_MAX_LEN); + if (!buf) { + wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer"); + return NULL; + } + + wpabuf_put_le16(buf, WLAN_AUTH_SAE); + wpabuf_put_le16(buf, 2); + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + + sae_write_confirm(&pasn->sae, buf); + pasn->sae.state = SAE_CONFIRMED; + + return buf; +} + +#endif /* CONFIG_SAE || CONFIG_WPA3_SAE */ + + +#ifndef ESP_SUPPLICANT +#ifdef CONFIG_FILS + +static struct wpabuf * wpas_pasn_fils_build_auth(struct pasn_data *pasn) +{ + struct wpabuf *buf = NULL; + struct wpabuf *erp_msg; + int ret; + + erp_msg = eapol_sm_build_erp_reauth_start(pasn->eapol); + if (!erp_msg) { + wpa_printf(MSG_DEBUG, + "PASN: FILS: ERP EAP-Initiate/Re-auth unavailable"); + return NULL; + } + + if (random_get_bytes(pasn->fils.nonce, FILS_NONCE_LEN) < 0 || + random_get_bytes(pasn->fils.session, FILS_SESSION_LEN) < 0) + goto fail; + + wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", pasn->fils.nonce, + FILS_NONCE_LEN); + + wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", pasn->fils.session, + FILS_SESSION_LEN); + + buf = wpabuf_alloc(1500); + if (!buf) + goto fail; + + /* Add the authentication algorithm */ + wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK); + + /* Authentication Transaction seq# */ + wpabuf_put_le16(buf, WLAN_AUTH_TR_SEQ_PASN_AUTH1); + + /* Status Code */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + + /* Own RSNE */ + wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher); + + /* FILS Nonce */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE); + wpabuf_put_data(buf, pasn->fils.nonce, FILS_NONCE_LEN); + + /* FILS Session */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION); + wpabuf_put_data(buf, pasn->fils.session, FILS_SESSION_LEN); + + /* Wrapped Data (ERP) */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg)); + wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA); + wpabuf_put_buf(buf, erp_msg); + + /* + * Calculate pending PMKID here so that we do not need to maintain a + * copy of the EAP-Initiate/Reauth message. + */ + ret = fils_pmkid_erp(pasn->akmp, wpabuf_head(erp_msg), + wpabuf_len(erp_msg), + pasn->fils.erp_pmkid); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ERP PMKID"); + goto fail; + } + + wpabuf_free(erp_msg); + erp_msg = NULL; + + wpa_hexdump_buf(MSG_DEBUG, "PASN: FILS: Authentication frame", buf); + return buf; +fail: + wpabuf_free(erp_msg); + wpabuf_free(buf); + return NULL; +} + + +static struct wpabuf * wpas_pasn_wd_fils_auth(struct pasn_data *pasn) +{ + wpa_printf(MSG_DEBUG, "PASN: FILS: wrapped data - completed=%u", + pasn->fils.completed); + + /* Nothing to add as we are done */ + if (pasn->fils.completed) + return NULL; + + if (!pasn->fils_eapol) { + wpa_printf(MSG_DEBUG, + "PASN: FILS: Missing Indication IE or PFS"); + return NULL; + } + + return wpas_pasn_fils_build_auth(pasn); +} + + +static int wpas_pasn_wd_fils_rx(struct pasn_data *pasn, struct wpabuf *wd) +{ + struct ieee802_11_elems elems; + struct wpa_ie_data rsne_data; + u8 rmsk[ERP_MAX_KEY_LEN]; + size_t rmsk_len; + u8 anonce[FILS_NONCE_LEN]; + const u8 *data; + size_t buf_len; + struct wpabuf *fils_wd = NULL; + u16 alg, seq, status; + int ret; + + if (!wd) + return -1; + + data = wpabuf_head(wd); + buf_len = wpabuf_len(wd); + + wpa_hexdump(MSG_DEBUG, "PASN: FILS: Authentication frame len=%zu", + data, buf_len); + + /* first handle the header */ + if (buf_len < 6) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short"); + return -1; + } + + alg = WPA_GET_LE16(data); + seq = WPA_GET_LE16(data + 2); + status = WPA_GET_LE16(data + 4); + + wpa_printf(MSG_DEBUG, "PASN: FILS: commit: alg=%u, seq=%u, status=%u", + alg, seq, status); + + if (alg != WLAN_AUTH_FILS_SK || seq != 2 || + status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, + "PASN: FILS: Dropping peer authentication"); + return -1; + } + + data += 6; + buf_len -= 6; + + if (ieee802_11_parse_elems(data, buf_len, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Could not parse elements"); + return -1; + } + + if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce || + !elems.wrapped_data) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs"); + return -1; + } + + ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsne_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RSNE"); + return -1; + } + + ret = wpa_pasn_validate_rsne(&rsne_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE"); + return -1; + } + + if (rsne_data.num_pmkid) { + wpa_printf(MSG_DEBUG, + "PASN: FILS: Not expecting PMKID in RSNE"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "PASN: FILS: ANonce", elems.fils_nonce, + FILS_NONCE_LEN); + os_memcpy(anonce, elems.fils_nonce, FILS_NONCE_LEN); + + wpa_hexdump(MSG_DEBUG, "PASN: FILS: FILS Session", elems.fils_session, + FILS_SESSION_LEN); + + if (os_memcmp(pasn->fils.session, elems.fils_session, + FILS_SESSION_LEN)) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Session mismatch"); + return -1; + } + + fils_wd = ieee802_11_defrag(elems.wrapped_data, elems.wrapped_data_len, + true); + + if (!fils_wd) { + wpa_printf(MSG_DEBUG, + "PASN: FILS: Failed getting wrapped data"); + return -1; + } + + eapol_sm_process_erp_finish(pasn->eapol, wpabuf_head(fils_wd), + wpabuf_len(fils_wd)); + + wpabuf_free(fils_wd); + fils_wd = NULL; + + if (eapol_sm_failed(pasn->eapol)) { + wpa_printf(MSG_DEBUG, "PASN: FILS: ERP finish failed"); + return -1; + } + + rmsk_len = ERP_MAX_KEY_LEN; + ret = eapol_sm_get_key(pasn->eapol, rmsk, rmsk_len); + + if (ret == PMK_LEN) { + rmsk_len = PMK_LEN; + ret = eapol_sm_get_key(pasn->eapol, rmsk, rmsk_len); + } + + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Failed getting RMSK"); + return -1; + } + + ret = fils_rmsk_to_pmk(pasn->akmp, rmsk, rmsk_len, + pasn->fils.nonce, anonce, NULL, 0, + pasn->pmk, &pasn->pmk_len); + + forced_memzero(rmsk, sizeof(rmsk)); + + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PMK"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "PASN: FILS: PMKID", pasn->fils.erp_pmkid, + PMKID_LEN); + + wpa_printf(MSG_DEBUG, "PASN: FILS: ERP processing succeeded"); + + pasn->pmksa_entry = pmksa_cache_add(pasn->pmksa, pasn->pmk, + pasn->pmk_len, pasn->fils.erp_pmkid, + NULL, 0, pasn->peer_addr, + pasn->own_addr, NULL, + pasn->akmp); + + pasn->fils.completed = true; + return 0; +} + +#endif /* CONFIG_FILS */ +#endif /* ESP_SUPPLICANT */ + + +static struct wpabuf * wpas_pasn_get_wrapped_data(struct pasn_data *pasn) +{ + if (pasn->using_pmksa) + return NULL; + + switch (pasn->akmp) { + case WPA_KEY_MGMT_PASN: + /* no wrapped data */ + return NULL; + case WPA_KEY_MGMT_SAE: + case WPA_KEY_MGMT_SAE_EXT_KEY: +#if defined(CONFIG_SAE) || defined(CONFIG_WPA3_SAE) + if (pasn->trans_seq == 0) + return wpas_pasn_wd_sae_commit(pasn); + if (pasn->trans_seq == WLAN_AUTH_TR_SEQ_SAE_CONFIRM) + return wpas_pasn_wd_sae_confirm(pasn); +#endif /* CONFIG_SAE || CONFIG_WPA3_SAE */ + wpa_printf(MSG_ERROR, + "PASN: SAE: Cannot derive wrapped data"); + return NULL; +#ifndef ESP_SUPPLICANT + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FILS_SHA384: +#ifdef CONFIG_FILS + return wpas_pasn_wd_fils_auth(pasn); +#endif /* CONFIG_FILS */ +#endif /* ESP_SUPPLICANT */ + case WPA_KEY_MGMT_FT_PSK: + case WPA_KEY_MGMT_FT_IEEE8021X: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + /* + * Wrapped data with these AKMs is optional and is only needed + * for further validation of FT security parameters. For now do + * not use them. + */ + return NULL; + default: + wpa_printf(MSG_ERROR, + "PASN: TODO: Wrapped data for akmp=0x%x", + pasn->akmp); + return NULL; + } +} + + +static u8 wpas_pasn_get_wrapped_data_format(struct pasn_data *pasn) +{ + if (pasn->using_pmksa) + return WPA_PASN_WRAPPED_DATA_NO; + + /* Note: Valid AKMP is expected to already be validated */ + switch (pasn->akmp) { + case WPA_KEY_MGMT_SAE: + case WPA_KEY_MGMT_SAE_EXT_KEY: + return WPA_PASN_WRAPPED_DATA_SAE; +#ifndef ESP_SUPPLICANT + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FILS_SHA384: + return WPA_PASN_WRAPPED_DATA_FILS_SK; +#endif /* ESP_SUPPLICANT */ + case WPA_KEY_MGMT_FT_PSK: + case WPA_KEY_MGMT_FT_IEEE8021X: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + /* + * Wrapped data with these AKMs is optional and is only needed + * for further validation of FT security parameters. For now do + * not use them. + */ + return WPA_PASN_WRAPPED_DATA_NO; + case WPA_KEY_MGMT_PASN: + default: + return WPA_PASN_WRAPPED_DATA_NO; + } +} + + +static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn, + const struct wpabuf *comeback, + bool verify) +{ + struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL; + const u8 *pmkid; + u8 wrapped_data; + + wpa_printf(MSG_DEBUG, "PASN: Building frame 1"); + + if (pasn->trans_seq) + return NULL; + + buf = wpabuf_alloc(1500); + if (!buf) + goto fail; + + /* Get public key */ + pubkey = crypto_ecdh_get_pubkey(pasn->ecdh, 0); + pubkey = wpabuf_zeropad(pubkey, crypto_ecdh_prime_len(pasn->ecdh)); + if (!pubkey) { + wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey"); + goto fail; + } + + wrapped_data = wpas_pasn_get_wrapped_data_format(pasn); + + wpa_pasn_build_auth_header(buf, pasn->bssid, + pasn->own_addr, pasn->peer_addr, + pasn->trans_seq + 1, WLAN_STATUS_SUCCESS); + + pmkid = NULL; + if (wpa_key_mgmt_ft(pasn->akmp)) { +#ifdef CONFIG_IEEE80211R + pmkid = pasn->pmk_r1_name; +#else /* CONFIG_IEEE80211R */ + goto fail; +#endif /* CONFIG_IEEE80211R */ + } else if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) { + struct rsn_pmksa_cache_entry *pmksa; + + pmksa = pmksa_cache_get(pasn->pmksa, pasn->peer_addr, NULL, NULL); + if (pmksa && pasn->custom_pmkid_valid) + pmkid = pasn->custom_pmkid; + else if (pmksa) + pmkid = pmksa->pmkid; + + /* + * Note: Even when PMKSA is available, also add wrapped data as + * it is possible that the PMKID is no longer valid at the AP. + */ + if (!verify) + wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn); + } + + if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0) + goto fail; + + if (!wrapped_data_buf) + wrapped_data = WPA_PASN_WRAPPED_DATA_NO; + + if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0) + goto fail; + + if (pasn->rsnxe_ie){ + wpabuf_put_data(buf, pasn->rsnxe_ie, 2 + pasn->rsnxe_ie[1]); + } + else { + wpa_pasn_add_rsnxe(buf, pasn->rsnxe_capab); + } + + wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data, + pubkey, true, comeback, -1); + + wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len); + + wpabuf_free(pasn->auth1); + pasn->auth1 = wpabuf_alloc_copy(wpabuf_head_u8(buf) + IEEE80211_HDRLEN, + wpabuf_len(buf) - IEEE80211_HDRLEN); + if (!pasn->auth1) { + wpa_printf(MSG_DEBUG, "PASN: Failed to store a copy of Auth1"); + goto fail; + } + + pasn->trans_seq++; + + wpabuf_free(wrapped_data_buf); + wpabuf_free(pubkey); + + wpa_printf(MSG_DEBUG, "PASN: Frame 1: Success"); + return buf; +fail: + pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpabuf_free(wrapped_data_buf); + wpabuf_free(pubkey); + wpabuf_free(buf); + return NULL; +} + + +static struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn) +{ + struct wpabuf *buf, *wrapped_data_buf = NULL; + u8 mic[WPA_PASN_MAX_MIC_LEN]; + u8 mic_len; + size_t data_len; + const u8 *data; + u8 *ptr; + u8 wrapped_data; + int ret; + u8 hash[SHA512_MAC_LEN]; + + wpa_printf(MSG_DEBUG, "PASN: Building frame 3"); + + if (pasn->trans_seq != WLAN_AUTH_TR_SEQ_PASN_AUTH2) + return NULL; + + buf = wpabuf_alloc(1500); + if (!buf) + goto fail; + + wrapped_data = wpas_pasn_get_wrapped_data_format(pasn); + + wpa_pasn_build_auth_header(buf, pasn->bssid, + pasn->own_addr, pasn->peer_addr, + WLAN_AUTH_TR_SEQ_PASN_AUTH3, + WLAN_STATUS_SUCCESS); + + wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn); + + if (!wrapped_data_buf) + wrapped_data = WPA_PASN_WRAPPED_DATA_NO; + + if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0) + goto fail; + wpabuf_free(wrapped_data_buf); + wrapped_data_buf = NULL; + + wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data, + NULL, false, NULL, -1); + + if (pasn->prepare_data_element && pasn->cb_ctx) + pasn->prepare_data_element(pasn->cb_ctx, pasn->peer_addr); + + wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len); + + /* Add the MIC */ + mic_len = pasn_mic_len(pasn->hash_alg); + wpabuf_put_u8(buf, WLAN_EID_MIC); + wpabuf_put_u8(buf, mic_len); + ptr = wpabuf_put(buf, mic_len); + + os_memset(ptr, 0, mic_len); + + data = wpabuf_head_u8(buf) + IEEE80211_HDRLEN; + data_len = wpabuf_len(buf) - IEEE80211_HDRLEN; + + if (!pasn->auth1 || + pasn_auth_frame_hash(pasn->hash_alg, wpabuf_head(pasn->auth1), + wpabuf_len(pasn->auth1), hash)) { + wpa_printf(MSG_INFO, "PASN: Failed to calculate Auth1 hash"); + goto fail; + } + + ret = pasn_mic(pasn->hash_alg, pasn->ptk.kck, pasn->ptk.kck_len, + pasn->own_addr, pasn->peer_addr, + hash, mic_len * 2, data, data_len, mic); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: frame 3: Failed MIC calculation"); + goto fail; + } + +#ifdef CONFIG_TESTING_OPTIONS + if (pasn->corrupt_mic) { + wpa_printf(MSG_DEBUG, "PASN: frame 3: Corrupt MIC"); + mic[0] = ~mic[0]; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + os_memcpy(ptr, mic, mic_len); + + pasn->trans_seq++; + + wpa_printf(MSG_DEBUG, "PASN: frame 3: Success"); + return buf; +fail: + pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpabuf_free(wrapped_data_buf); + wpabuf_free(buf); + return NULL; +} + + +void wpa_pasn_reset(struct pasn_data *pasn) +{ + wpa_printf(MSG_DEBUG, "PASN: Reset"); + + crypto_ecdh_deinit(pasn->ecdh); + pasn->ecdh = NULL; + + + pasn->akmp = 0; + pasn->cipher = 0; + pasn->group = 0; + pasn->trans_seq = 0; + pasn->pmk_len = 0; + pasn->using_pmksa = false; + + forced_memzero(pasn->pmk, sizeof(pasn->pmk)); + forced_memzero(&pasn->ptk, sizeof(pasn->ptk)); + + wpabuf_free(pasn->auth1); + pasn->auth1 = NULL; + + wpabuf_free(pasn->comeback); + pasn->comeback = NULL; + pasn->comeback_after = 0; + +#if defined(CONFIG_SAE) || defined(CONFIG_WPA3_SAE) + sae_clear_data(&pasn->sae); + if (pasn->pt) { + sae_deinit_pt(pasn->pt); + pasn->pt = NULL; + } +#endif /* CONFIG_SAE || CONFIG_WPA3_SAE */ + +#ifndef ESP_SUPPLICANT +#ifdef CONFIG_FILS + pasn->fils_eapol = false; + os_memset(&pasn->fils, 0, sizeof(pasn->fils)); +#endif /* CONFIG_FILS*/ +#endif /* ESP_SUPPLICANT */ + +#ifdef CONFIG_IEEE80211R + forced_memzero(pasn->pmk_r1, sizeof(pasn->pmk_r1)); + pasn->pmk_r1_len = 0; + os_memset(pasn->pmk_r1_name, 0, sizeof(pasn->pmk_r1_name)); +#endif /* CONFIG_IEEE80211R */ + pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + pasn->pmksa_entry = NULL; +#ifdef CONFIG_TESTING_OPTIONS + pasn->corrupt_mic = 0; +#endif /* CONFIG_TESTING_OPTIONS */ + pasn->network_id = 0; + pasn->derive_kdk = false; + pasn->rsn_ie = NULL; + pasn->rsn_ie_len = 0; + os_free(pasn->rsnxe_ie); + pasn->rsnxe_ie = NULL; + pasn->custom_pmkid_valid = false; + + if (pasn->extra_ies) { + os_free((u8 *) pasn->extra_ies); + pasn->extra_ies = NULL; + } + + wpabuf_free(pasn->frame); + pasn->frame = NULL; + + wpabuf_free(pasn->auth1); + pasn->auth1 = NULL; +} + + +static int wpas_pasn_set_pmk(struct pasn_data *pasn, + struct wpa_ie_data *rsn_data, + struct wpa_pasn_params_data *pasn_data, + struct wpabuf *wrapped_data) +{ + static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'}; + + os_memset(pasn->pmk, 0, sizeof(pasn->pmk)); + pasn->pmk_len = 0; + + if (pasn->akmp == WPA_KEY_MGMT_PASN) { + wpa_printf(MSG_DEBUG, "PASN: Using default PMK"); + + pasn->pmk_len = WPA_PASN_PMK_LEN; + os_memcpy(pasn->pmk, pasn_default_pmk, + sizeof(pasn_default_pmk)); + return 0; + } + + if (wpa_key_mgmt_ft(pasn->akmp)) { +#ifdef CONFIG_IEEE80211R + wpa_printf(MSG_DEBUG, "PASN: FT: Using PMK-R1"); + pasn->pmk_len = pasn->pmk_r1_len; + os_memcpy(pasn->pmk, pasn->pmk_r1, pasn->pmk_r1_len); + pasn->using_pmksa = true; + return 0; +#else /* CONFIG_IEEE80211R */ + wpa_printf(MSG_DEBUG, "PASN: FT: Not supported"); + return -1; +#endif /* CONFIG_IEEE80211R */ + } + + if (rsn_data->num_pmkid) { + int ret; + struct rsn_pmksa_cache_entry *pmksa; + const u8 *pmkid = NULL; + + if (pasn->custom_pmkid_valid) { + ret = pasn->validate_custom_pmkid(pasn->cb_ctx, + pasn->peer_addr, + rsn_data->pmkid); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed custom PMKID validation"); + return -1; + } + } else { + pmkid = rsn_data->pmkid; + } + + pmksa = pmksa_cache_get(pasn->pmksa, pasn->peer_addr, + pmkid, NULL); + if (pmksa) { + wpa_printf(MSG_DEBUG, "PASN: Using PMKSA"); + + pasn->pmk_len = pmksa->pmk_len; + os_memcpy(pasn->pmk, pmksa->pmk, pmksa->pmk_len); + pasn->using_pmksa = true; + + return 0; + } + } + +#if defined(CONFIG_SAE) || defined(CONFIG_WPA3_SAE) + if (pasn->akmp == WPA_KEY_MGMT_SAE || + pasn->akmp == WPA_KEY_MGMT_SAE_EXT_KEY) { + int ret; + + ret = wpas_pasn_wd_sae_rx(pasn, wrapped_data); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed processing SAE wrapped data"); + pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + return -1; + } + + wpa_printf(MSG_DEBUG, "PASN: Success deriving PMK with SAE"); + pasn->pmk_len = pasn->sae.pmk_len; + os_memcpy(pasn->pmk, pasn->sae.pmk, pasn->pmk_len); + + pasn->pmksa_entry = pmksa_cache_add(pasn->pmksa, pasn->pmk, + pasn->pmk_len, + pasn->sae.pmkid, + NULL, 0, pasn->peer_addr, + pasn->own_addr, NULL, + pasn->akmp); + return 0; + } +#endif /* CONFIG_SAE || CONFIG_WPA3_SAE */ + +#ifndef ESP_SUPPLICANT +#ifdef CONFIG_FILS + if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 || + pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) { + int ret; + + ret = wpas_pasn_wd_fils_rx(pasn, wrapped_data); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed processing FILS wrapped data"); + pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + return -1; + } + + return 0; + } +#endif /* CONFIG_FILS */ +#endif /* ESP_SUPPLICANT */ + + /* TODO: Derive PMK based on wrapped data */ + wpa_printf(MSG_DEBUG, "PASN: Missing implementation to derive PMK"); + pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + return -1; +} + + +static int wpas_pasn_send_auth_1(struct pasn_data *pasn, const u8 *own_addr, + const u8 *peer_addr, const u8 *bssid, int akmp, + int cipher, u16 group, int freq, + const u8 *beacon_rsne, u8 beacon_rsne_len, + const u8 *beacon_rsnxe, u8 beacon_rsnxe_len, + const struct wpabuf *comeback, bool verify) +{ + struct wpabuf *frame; + int ret; + + (void) beacon_rsne; + (void) beacon_rsne_len; + (void) beacon_rsnxe; + (void) beacon_rsnxe_len; + + pasn->ecdh = crypto_ecdh_init(group); + if (!pasn->ecdh) { + wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH"); + goto fail; + } + + pasn->akmp = akmp; + pasn->cipher = cipher; + pasn->group = group; + pasn->freq = freq; + + os_memcpy(pasn->own_addr, own_addr, ETH_ALEN); + os_memcpy(pasn->peer_addr, peer_addr, ETH_ALEN); + os_memcpy(pasn->bssid, bssid, ETH_ALEN); + + wpa_printf(MSG_DEBUG, + "PASN: Init%s: " MACSTR " akmp=0x%x, cipher=0x%x, group=%u", + verify ? " (verify)" : "", + MAC2STR(pasn->peer_addr), pasn->akmp, pasn->cipher, + pasn->group); + + frame = wpas_pasn_build_auth_1(pasn, comeback, verify); + if (!frame) { + wpa_printf(MSG_DEBUG, "PASN: Failed building 1st auth frame"); + goto fail; + } + + wpabuf_free(pasn->frame); + pasn->frame = NULL; + + ret = pasn->send_mgmt(pasn->cb_ctx, + wpabuf_head(frame), wpabuf_len(frame), 0, + pasn->freq, 1000); + + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed sending 1st auth frame"); + wpabuf_free(frame); + goto fail; + } + + pasn->frame = frame; + return 0; + +fail: + return -1; +} + + +int wpas_pasn_start(struct pasn_data *pasn, const u8 *own_addr, + const u8 *peer_addr, const u8 *bssid, + int akmp, int cipher, u16 group, + int freq, const u8 *beacon_rsne, u8 beacon_rsne_len, + const u8 *beacon_rsnxe, u8 beacon_rsnxe_len, + const struct wpabuf *comeback) +{ + /* TODO: Currently support only ECC groups */ + if (!dragonfly_suitable_group(group, 1)) { + wpa_printf(MSG_DEBUG, + "PASN: Reject unsuitable group %u", group); + return -1; + } + + switch (akmp) { + case WPA_KEY_MGMT_PASN: + break; +#if defined(CONFIG_SAE) || defined(CONFIG_WPA3_SAE) + case WPA_KEY_MGMT_SAE: + case WPA_KEY_MGMT_SAE_EXT_KEY: + if (beacon_rsnxe && + !ieee802_11_rsnx_capab(beacon_rsnxe, + WLAN_RSNX_CAPAB_SAE_H2E)) { + wpa_printf(MSG_DEBUG, + "PASN: AP does not support SAE H2E"); + return -1; + } + pasn->sae.state = SAE_NOTHING; + pasn->sae.send_confirm = 0; + break; +#endif /* CONFIG_SAE || CONFIG_WPA3_SAE */ +#ifndef ESP_SUPPLICANT +#ifdef CONFIG_FILS + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FILS_SHA384: + break; +#endif /* CONFIG_FILS */ +#endif /* ESP_SUPPLICANT */ +#ifdef CONFIG_IEEE80211R + case WPA_KEY_MGMT_FT_PSK: + case WPA_KEY_MGMT_FT_IEEE8021X: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + break; +#endif /* CONFIG_IEEE80211R */ + default: + wpa_printf(MSG_ERROR, "PASN: Unsupported AKMP=0x%x", akmp); + return -1; + } + + return wpas_pasn_send_auth_1(pasn, own_addr, peer_addr, bssid, akmp, + cipher, group, + freq, beacon_rsne, beacon_rsne_len, + beacon_rsnxe, beacon_rsnxe_len, comeback, + false); +} + +/* + * Wi-Fi Aware uses PASN handshake to authenticate peer devices. + * Devices can simply verify each other for subsequent sessions using + * pairing verification procedure. + * + * In pairing verification, Wi-Fi aware devices use PASN authentication + * frames with a custom PMKID and Wi-Fi Aware R4 specific verification IEs. + * It does not use wrapped data in the Authentication frames. This function + * provides support to construct PASN Authentication frames for pairing + * verification. + */ +int wpa_pasn_verify(struct pasn_data *pasn, const u8 *own_addr, + const u8 *peer_addr, const u8 *bssid, + int akmp, int cipher, u16 group, + int freq, const u8 *beacon_rsne, u8 beacon_rsne_len, + const u8 *beacon_rsnxe, u8 beacon_rsnxe_len, + const struct wpabuf *comeback) +{ + return wpas_pasn_send_auth_1(pasn, own_addr, peer_addr, bssid, akmp, + cipher, group, freq, beacon_rsne, + beacon_rsne_len, beacon_rsnxe, + beacon_rsnxe_len, comeback, true); +} + + +static bool is_pasn_auth_frame(struct pasn_data *pasn, + const struct ieee80211_auth *mgmt, + size_t len, bool rx) +{ + u16 fc; + size_t min_len = offsetof(struct ieee80211_auth, auth.variable); + + if (!mgmt || len < min_len) { + wpa_printf(MSG_DEBUG, + "PASN: is_pasn_auth_frame: reject len=%zu (min %zu) mgmt=%p", + len, min_len, mgmt); + return false; + } + + /* Not an Authentication frame; do nothing */ + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_AUTH) { + wpa_printf(MSG_DEBUG, + "PASN: is_pasn_auth_frame: reject fc=0x%04x type=%u stype=%u (want MGMT AUTH)", + fc, WLAN_FC_GET_TYPE(fc), WLAN_FC_GET_STYPE(fc)); + return false; + } + + /* Not our frame; do nothing */ + /* if (!ether_addr_equal(mgmt->bssid, pasn->bssid)) { */ + /* wpa_printf(MSG_DEBUG, */ + /* "PASN: is_pasn_auth_frame: reject BSSID mismatch frame=" MACSTR */ + /* " pasn->bssid=" MACSTR, */ + /* MAC2STR(mgmt->bssid), MAC2STR(pasn->bssid)); */ + /* return false; */ + /* } */ + + if (rx && (!ether_addr_equal(mgmt->da, pasn->own_addr) || + !ether_addr_equal(mgmt->sa, pasn->peer_addr))) { + wpa_printf(MSG_DEBUG, + "PASN: is_pasn_auth_frame: reject RX addr DA=" MACSTR + " SA=" MACSTR " (expect DA=" MACSTR " SA=" MACSTR ")", + MAC2STR(mgmt->da), MAC2STR(mgmt->sa), + MAC2STR(pasn->own_addr), MAC2STR(pasn->peer_addr)); + return false; + } + + if (!rx && (!ether_addr_equal(mgmt->sa, pasn->own_addr) || + !ether_addr_equal(mgmt->da, pasn->peer_addr))) { + wpa_printf(MSG_DEBUG, + "PASN: is_pasn_auth_frame: reject TX-status addr SA=" MACSTR + " DA=" MACSTR " (expect SA=" MACSTR " DA=" MACSTR ")", + MAC2STR(mgmt->sa), MAC2STR(mgmt->da), + MAC2STR(pasn->own_addr), MAC2STR(pasn->peer_addr)); + return false; + } + + /* Not PASN; do nothing */ + if (mgmt->auth.auth_alg != host_to_le16(WLAN_AUTH_PASN)) { + wpa_printf(MSG_DEBUG, + "PASN: is_pasn_auth_frame: reject auth_alg=%u (want PASN %u)", + le_to_host16(mgmt->auth.auth_alg), WLAN_AUTH_PASN); + return false; + } + + wpa_printf(MSG_DEBUG, + "PASN: is_pasn_auth_frame: OK rx=%d trans=%u status=%u", + rx, le_to_host16(mgmt->auth.auth_transaction), + le_to_host16(mgmt->auth.status_code)); + return true; +} + + +int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, + struct wpa_pasn_params_data *pasn_params) + +{ + struct ieee802_11_elems elems; + struct wpa_ie_data rsn_data; + const struct ieee80211_auth *mgmt = + (const struct ieee80211_auth *) data; + struct wpabuf *wrapped_data = NULL, *secret = NULL, *frame = NULL; + u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN]; + u8 mic_len; + u16 status; + int ret, inc_y; + u8 *copy = NULL; + size_t mic_offset, copy_len; + + if (!pasn) { + wpa_printf(MSG_DEBUG, + "PASN: RX: wpa_pasn_auth_rx pasn is NULL len=%zu pasn_params=%p", + len, pasn_params); + return -2; + } + + wpa_printf(MSG_DEBUG, + "PASN: RX: wpa_pasn_auth_rx enter len=%zu pasn_params=%p trans_seq=%u " + "own=" MACSTR " peer=" MACSTR " bssid=" MACSTR, + len, pasn_params, pasn->trans_seq, + MAC2STR(pasn->own_addr), MAC2STR(pasn->peer_addr), + MAC2STR(pasn->bssid)); + + if (!is_pasn_auth_frame(pasn, mgmt, len, true)) { + wpa_printf(MSG_DEBUG, + "PASN: RX: wpa_pasn_auth_rx not a PASN auth frame (is_pasn_auth_frame)"); + return -2; + } + + wpa_printf(MSG_DEBUG, + "PASN: RX: wpa_pasn_auth_rx frame accepted, expect trans=%u", + pasn->trans_seq + 1); + + if (mgmt->auth.auth_transaction != + host_to_le16(pasn->trans_seq + 1)) { + wpa_printf(MSG_DEBUG, + "PASN: RX: wpa_pasn_auth_rx invalid trans got=%u expect=%u -> -3", + le_to_host16(mgmt->auth.auth_transaction), + pasn->trans_seq + 1); + return -3; + } + + status = le_to_host16(mgmt->auth.status_code); + + if (status != WLAN_STATUS_SUCCESS && + status != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) { + wpa_printf(MSG_DEBUG, + "PASN: Authentication rejected - status=%u", status); + goto fail; + } + + if (ieee802_11_parse_elems(mgmt->auth.variable, + len - offsetof(struct ieee80211_auth, + auth.variable), + &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "PASN: Failed parsing Authentication frame"); + goto fail; + } + + if (!elems.pasn_params || !elems.pasn_params_len) { + wpa_printf(MSG_DEBUG, + "PASN: Missing PASN Parameters IE"); + goto fail; + } + + if (!pasn_params) { + wpa_printf(MSG_DEBUG, "PASN: pasn_params == NULL"); + goto fail; + } + + ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3, + elems.pasn_params_len + 3, + true, pasn_params); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed validation PASN of Parameters IE"); + goto fail; + } + + if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) { + wpa_printf(MSG_DEBUG, + "PASN: Authentication temporarily rejected"); + + if (pasn_params->comeback && pasn_params->comeback_len) { + wpa_printf(MSG_DEBUG, + "PASN: Comeback token available. After=%u", + pasn_params->after); + + if (!pasn_params->after) { + wpa_printf(MSG_DEBUG, + "PASN: RX: wpa_pasn_auth_rx comeback immediate -> 1"); + return 1; + } + + pasn->comeback = wpabuf_alloc_copy( + pasn_params->comeback, + pasn_params->comeback_len); + if (pasn->comeback) + pasn->comeback_after = pasn_params->after; + } + + pasn->status = status; + goto fail; + } + + if (!elems.rsn_ie) { + wpa_printf(MSG_DEBUG, "PASN: Missing RSNE"); + goto fail; + } + + ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsn_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE"); + goto fail; + } + + ret = wpa_pasn_validate_rsne(&rsn_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE"); + goto fail; + } + + if (pasn->akmp != rsn_data.key_mgmt || + pasn->cipher != rsn_data.pairwise_cipher) { + wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher"); + goto fail; + } + + if (pasn->group != pasn_params->group) { + wpa_printf(MSG_DEBUG, "PASN: Mismatch in group"); + goto fail; + } + + if (!pasn_params->pubkey || !pasn_params->pubkey_len) { + wpa_printf(MSG_DEBUG, "PASN: Invalid public key"); + goto fail; + } + + if (pasn_params->pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) { + inc_y = 1; + } else if (pasn_params->pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 || + pasn_params->pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) { + inc_y = 0; + } else { + wpa_printf(MSG_DEBUG, + "PASN: Invalid first octet in pubkey=0x%x", + pasn_params->pubkey[0]); + goto fail; + } + + secret = crypto_ecdh_set_peerkey(pasn->ecdh, inc_y, + pasn_params->pubkey + 1, + pasn_params->pubkey_len - 1); + + if (!secret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret"); + goto fail; + } + + if (pasn_params->wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) { + wrapped_data = ieee802_11_defrag(elems.wrapped_data, + elems.wrapped_data_len, + true); + + if (!wrapped_data) { + wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data"); + goto fail; + } + } + + ret = wpas_pasn_set_pmk(pasn, &rsn_data, pasn_params, wrapped_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to set PMK"); + goto fail; + } + + ret = pasn_pmk_to_ptk(pasn->pmk, pasn->pmk_len, + pasn->own_addr, pasn->peer_addr, + wpabuf_head(secret), wpabuf_len(secret), + &pasn->ptk, pasn->akmp, pasn->cipher, + pasn->kdk_len, pasn->kek_len, &pasn->hash_alg); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK"); + goto fail; + } + + if (pasn->secure_ltf) { + ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, pasn->cipher); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed to derive LTF keyseed"); + goto fail; + } + } + + wpabuf_free(wrapped_data); + wrapped_data = NULL; + wpabuf_free(secret); + secret = NULL; + + /* Check that the MIC IE exists. Save it and zero out the memory */ + mic_len = pasn_mic_len(pasn->hash_alg); + if (status == WLAN_STATUS_SUCCESS) { + if (!elems.mic || elems.mic_len != mic_len) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid MIC. Expecting len=%u %p %d", + mic_len,elems.mic,elems.mic_len); + goto fail; + } + os_memcpy(mic, elems.mic, mic_len); + } + + /* Use a copy of the message since we need to clear the MIC field */ + if (!elems.mic) + goto fail; + mic_offset = elems.mic - (const u8 *) &mgmt->auth; + copy_len = len - offsetof(struct ieee80211_auth, auth); + if (mic_offset + mic_len > copy_len) + goto fail; + copy = os_memdup(&mgmt->auth, copy_len); + if (!copy) + goto fail; + os_memset(copy + mic_offset, 0, mic_len); + + { + u8 *rsne_rsnxe; + size_t rsne_rsnxe_len = 0; + size_t off = 0; + + /* MIC uses RSNE and RSNXE as present in this Authentication frame */ + if (!elems.rsn_ie || !elems.rsn_ie_len) + goto fail; + rsne_rsnxe_len = elems.rsn_ie_len + 2; + if (elems.rsnxe && elems.rsnxe_len) + rsne_rsnxe_len += elems.rsnxe_len + 2; + + rsne_rsnxe = os_malloc(rsne_rsnxe_len); + if (!rsne_rsnxe) + goto fail; + + os_memcpy(rsne_rsnxe, elems.rsn_ie - 2, elems.rsn_ie_len + 2); + off = elems.rsn_ie_len + 2; + if (elems.rsnxe && elems.rsnxe_len) + os_memcpy(rsne_rsnxe + off, elems.rsnxe - 2, + elems.rsnxe_len + 2); + + wpa_hexdump_key(MSG_DEBUG, "PASN: RSN + RSNXE buf", + rsne_rsnxe, rsne_rsnxe_len); + + ret = pasn_mic(pasn->hash_alg, pasn->ptk.kck, pasn->ptk.kck_len, + pasn->peer_addr, pasn->own_addr, + rsne_rsnxe, rsne_rsnxe_len, + copy, copy_len, out_mic); + + os_free(rsne_rsnxe); + } + os_free(copy); + copy = NULL; + + wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len); + if (ret || os_memcmp(mic, out_mic, mic_len) != 0) { + wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification"); + goto fail; + } + + pasn->trans_seq++; + + wpa_printf(MSG_DEBUG, "PASN: Success verifying Authentication frame"); + + if (pasn_parse_encrypted_data(pasn, data, len) < 0) { + wpa_printf(MSG_DEBUG, "PASN: Encrypted data processing failed"); + goto fail; + } + + frame = wpas_pasn_build_auth_3(pasn); + if (!frame) { + wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame"); + goto fail; + } + + wpabuf_free(pasn->frame); + pasn->frame = NULL; + + ret = pasn->send_mgmt(pasn->cb_ctx, + wpabuf_head(frame), wpabuf_len(frame), 0, + pasn->freq, 100); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed sending 3st auth frame"); + wpabuf_free(frame); + goto fail; + } + + pasn->frame = frame; + wpa_printf(MSG_DEBUG, "PASN: Success sending last frame. Store PTK"); + + pasn->status = WLAN_STATUS_SUCCESS; + + wpa_printf(MSG_DEBUG, + "PASN: RX: wpa_pasn_auth_rx complete trans_seq=%u -> 0", + pasn->trans_seq); + return 0; +fail: + wpa_printf(MSG_DEBUG, + "PASN: RX: wpa_pasn_auth_rx fail status=%u -> -1", + status); + wpa_printf(MSG_DEBUG, "PASN: Failed RX processing - terminating"); + wpabuf_free(wrapped_data); + wpabuf_free(secret); + os_free(copy); + + /* + * TODO: In case of an error the standard allows to silently drop + * the frame and terminate the authentication exchange. However, better + * reply to the AP with an error status. + */ + if (status == WLAN_STATUS_SUCCESS) + pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + else + pasn->status = status; + + return -1; +} + + +int wpa_pasn_auth_tx_status(struct pasn_data *pasn, + const u8 *data, size_t data_len, u8 acked) + +{ + const struct ieee80211_auth *mgmt = + (const struct ieee80211_auth *) data; + + wpa_printf(MSG_DEBUG, "PASN: auth_tx_status: acked=%u", acked); + + if (!is_pasn_auth_frame(pasn, mgmt, data_len, false)) + return -1; + + if (mgmt->auth.auth_transaction != host_to_le16(pasn->trans_seq)) { + wpa_printf(MSG_ERROR, + "PASN: Invalid transaction sequence: (%u != %u)", + pasn->trans_seq, + le_to_host16(mgmt->auth.auth_transaction)); + return 0; + } + + wpa_printf(MSG_ERROR, + "PASN: auth with trans_seq=%u, acked=%u", pasn->trans_seq, + acked); + + /* + * Even if the frame was not acked, do not treat this is an error, and + * try to complete the flow, relying on the PASN timeout callback to + * clean up. + */ + if (pasn->trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH3) { + wpa_printf(MSG_DEBUG, "PASN: auth complete with: " MACSTR, + MAC2STR(pasn->peer_addr)); + /* + * Either frame was not ACKed or it was ACKed but the trans_seq + * != 1, i.e., not expecting an RX frame, so we are done. + */ + return 1; + } + + return 0; +} diff --git a/components/wpa_supplicant/src/pasn/pasn_responder.c b/components/wpa_supplicant/src/pasn/pasn_responder.c new file mode 100644 index 00000000000..97e5491dc16 --- /dev/null +++ b/components/wpa_supplicant/src/pasn/pasn_responder.c @@ -0,0 +1,1168 @@ +/* + * PASN responder processing + * + * Copyright (C) 2019, Intel Corporation + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/wpa_common.h" +#include "common/sae.h" +#include "common/ieee802_11_common.h" +#include "common/ieee802_11_defs.h" +#include "crypto/sha384.h" +#include "crypto/sha256.h" +#include "crypto/random.h" +#include "crypto/crypto.h" +#include "ap/hostapd.h" +#include "ap/comeback_token.h" +#include "ap/ieee802_1x.h" +#include "ap/pmksa_cache_auth.h" +#include "pasn_common.h" + + +struct rsn_pmksa_cache * pasn_responder_pmksa_cache_init(void) +{ + return pmksa_cache_auth_init(NULL, NULL); +} + + +void pasn_responder_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) +{ + return pmksa_cache_auth_deinit(pmksa); +} + + +int pasn_responder_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, + const u8 *own_addr, const u8 *bssid, + const u8 *pmk, size_t pmk_len, + const u8 *pmkid) +{ + if (pmksa_cache_auth_add(pmksa, pmk, pmk_len, pmkid, NULL, 0, own_addr, + bssid, 0, NULL, WPA_KEY_MGMT_SAE)) + return 0; + return -1; +} + + +int pasn_responder_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *bssid, u8 *pmkid, u8 *pmk, + size_t *pmk_len) +{ + struct rsn_pmksa_cache_entry *entry; + + entry = pmksa_cache_auth_get(pmksa, bssid, NULL); + if (entry) { + os_memcpy(pmkid, entry->pmkid, PMKID_LEN); + os_memcpy(pmk, entry->pmk, entry->pmk_len); + *pmk_len = entry->pmk_len; + return 0; + } + return -1; +} + + +void pasn_responder_pmksa_cache_remove(struct rsn_pmksa_cache *pmksa, + const u8 *bssid) +{ + struct rsn_pmksa_cache_entry *entry; + + entry = pmksa_cache_auth_get(pmksa, bssid, NULL); + if (!entry) + return; + + pmksa_cache_free_entry(pmksa, entry); +} + + +void pasn_responder_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa) +{ + return pmksa_cache_auth_flush(pmksa); +} + + +void pasn_set_responder_pmksa(struct pasn_data *pasn, + struct rsn_pmksa_cache *pmksa) +{ + if (pasn) + pasn->pmksa = pmksa; +} + + +#ifdef CONFIG_PASN +#ifdef CONFIG_SAE + +static int pasn_wd_handle_sae_commit(struct pasn_data *pasn, + const u8 *own_addr, const u8 *peer_addr, + struct wpabuf *wd) +{ + const u8 *data; + size_t buf_len; + u16 res, alg, seq, status; + int groups[] = { pasn->group, 0 }; + int ret; + + if (!wd) + return -1; + + data = wpabuf_head_u8(wd); + buf_len = wpabuf_len(wd); + + if (buf_len < 6) { + wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu", + buf_len); + return -1; + } + + alg = WPA_GET_LE16(data); + seq = WPA_GET_LE16(data + 2); + status = WPA_GET_LE16(data + 4); + + wpa_printf(MSG_DEBUG, "PASN: SAE commit: alg=%u, seq=%u, status=%u", + alg, seq, status); + + if (alg != WLAN_AUTH_SAE || seq != 1 || + status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) { + wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE commit"); + return -1; + } + + sae_clear_data(&pasn->sae); + pasn->sae.state = SAE_NOTHING; + + ret = sae_set_group(&pasn->sae, pasn->group); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group"); + return -1; + } + + pasn->sae.akmp = pasn->akmp; + if (!pasn->password || !pasn->pt) { + wpa_printf(MSG_DEBUG, "PASN: No SAE PT found"); + return -1; + } + + ret = sae_prepare_commit_pt(&pasn->sae, pasn->pt, own_addr, peer_addr, + NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit"); + return -1; + } + + res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, NULL, + groups, 1); + if (res != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: Failed parsing SAE commit"); + return -1; + } + + /* Process the commit message and derive the PMK */ + ret = sae_process_commit(&pasn->sae); + if (ret) { + wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit"); + return -1; + } + + pasn->sae.state = SAE_COMMITTED; + + return 0; +} + + +static int pasn_wd_handle_sae_confirm(struct pasn_data *pasn, + const u8 *peer_addr, struct wpabuf *wd) +{ + const u8 *data; + size_t buf_len; + u16 res, alg, seq, status; + + if (!wd) + return -1; + + data = wpabuf_head_u8(wd); + buf_len = wpabuf_len(wd); + + if (buf_len < 6) { + wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu", + buf_len); + return -1; + } + + alg = WPA_GET_LE16(data); + seq = WPA_GET_LE16(data + 2); + status = WPA_GET_LE16(data + 4); + + wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u", + alg, seq, status); + + if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm"); + return -1; + } + + res = sae_check_confirm(&pasn->sae, data + 6, buf_len - 6); + if (res != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm"); + return -1; + } + + pasn->sae.state = SAE_ACCEPTED; + + /* + * TODO: Based on on IEEE P802.11az/D2.6, the PMKSA derived with + * PASN/SAE should only be allowed with future PASN only. For now do not + * restrict this only for PASN. + */ + if (pasn->disable_pmksa_caching) + return 0; + + wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", + pasn->sae.pmk, pasn->sae.pmk_len); + if (!pasn->sae.akmp) + pasn->sae.akmp = WPA_KEY_MGMT_SAE; + + pmksa_cache_auth_add(pasn->pmksa, pasn->sae.pmk, pasn->sae.pmk_len, + pasn->sae.pmkid, NULL, 0, pasn->own_addr, + peer_addr, 0, NULL, pasn->sae.akmp); + return 0; +} + + +static struct wpabuf * pasn_get_sae_wd(struct pasn_data *pasn) +{ + struct wpabuf *buf = NULL; + u8 *len_ptr; + size_t len; + + /* Need to add the entire Authentication frame body */ + buf = wpabuf_alloc(8 + SAE_COMMIT_MAX_LEN + 8 + SAE_CONFIRM_MAX_LEN); + if (!buf) { + wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer"); + return NULL; + } + + /* Need to add the entire authentication frame body for the commit */ + len_ptr = wpabuf_put(buf, 2); + wpabuf_put_le16(buf, WLAN_AUTH_SAE); + wpabuf_put_le16(buf, 1); + wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT); + + /* Write the actual commit and update the length accordingly */ + sae_write_commit(&pasn->sae, buf, NULL, NULL); + len = wpabuf_len(buf); + WPA_PUT_LE16(len_ptr, len - 2); + + /* Need to add the entire Authentication frame body for the confirm */ + len_ptr = wpabuf_put(buf, 2); + wpabuf_put_le16(buf, WLAN_AUTH_SAE); + wpabuf_put_le16(buf, 2); + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + + sae_write_confirm(&pasn->sae, buf); + WPA_PUT_LE16(len_ptr, wpabuf_len(buf) - len - 2); + + pasn->sae.state = SAE_CONFIRMED; + + return buf; +} + +#endif /* CONFIG_SAE */ + + +#ifndef ESP_SUPPLICANT +#ifdef CONFIG_FILS + +static struct wpabuf * pasn_get_fils_wd(struct pasn_data *pasn) +{ + struct pasn_fils *fils = &pasn->fils; + struct wpabuf *buf = NULL; + + if (!fils->erp_resp) { + wpa_printf(MSG_DEBUG, "PASN: FILS: Missing erp_resp"); + return NULL; + } + + buf = wpabuf_alloc(1500); + if (!buf) + return NULL; + + /* Add the authentication algorithm */ + wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK); + + /* Authentication Transaction seq# */ + wpabuf_put_le16(buf, WLAN_AUTH_TR_SEQ_PASN_AUTH2); + + /* Status Code */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + + /* Own RSNE */ + wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher); + + /* FILS Nonce */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE); + wpabuf_put_data(buf, fils->anonce, FILS_NONCE_LEN); + + /* FILS Session */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION); + wpabuf_put_data(buf, fils->session, FILS_SESSION_LEN); + + /* Wrapped Data */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + wpabuf_len(fils->erp_resp)); + wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA); + wpabuf_put_buf(buf, fils->erp_resp); + + return buf; +} + +#endif /* CONFIG_FILS */ +#endif /* ESP_SUPPLICANT */ + +static struct wpabuf * pasn_get_wrapped_data(struct pasn_data *pasn) +{ + switch (pasn->akmp) { + case WPA_KEY_MGMT_PASN: + /* no wrapped data */ + return NULL; + case WPA_KEY_MGMT_SAE: + case WPA_KEY_MGMT_SAE_EXT_KEY: +#ifdef CONFIG_SAE + return pasn_get_sae_wd(pasn); +#else /* CONFIG_SAE */ + wpa_printf(MSG_ERROR, + "PASN: SAE: Cannot derive wrapped data"); + return NULL; +#endif /* CONFIG_SAE */ +#ifndef ESP_SUPPLICANT + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FILS_SHA384: +#ifdef CONFIG_FILS + return pasn_get_fils_wd(pasn); +#endif /* CONFIG_FILS */ + /* fall through */ +#endif /* ESP_SUPPLICANT */ + case WPA_KEY_MGMT_FT_PSK: + case WPA_KEY_MGMT_FT_IEEE8021X: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + default: + wpa_printf(MSG_ERROR, + "PASN: TODO: Wrapped data for akmp=0x%x", + pasn->akmp); + return NULL; + } +} + + +static int +pasn_derive_keys(struct pasn_data *pasn, + const u8 *own_addr, const u8 *peer_addr, + const u8 *cached_pmk, size_t cached_pmk_len, + struct wpa_pasn_params_data *pasn_data, + struct wpabuf *wrapped_data, + struct wpabuf *secret) +{ + static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'}; + u8 pmk[PMK_LEN_MAX]; + u8 pmk_len; + int ret; + + os_memset(pmk, 0, sizeof(pmk)); + pmk_len = 0; + + if (!cached_pmk || !cached_pmk_len) + wpa_printf(MSG_DEBUG, "PASN: No valid PMKSA entry"); + + if (pasn->akmp == WPA_KEY_MGMT_PASN) { + wpa_printf(MSG_DEBUG, "PASN: Using default PMK"); + + pmk_len = WPA_PASN_PMK_LEN; + os_memcpy(pmk, pasn_default_pmk, sizeof(pasn_default_pmk)); + } else if (cached_pmk && cached_pmk_len) { + wpa_printf(MSG_DEBUG, "PASN: Using PMKSA entry"); + + pmk_len = cached_pmk_len; + os_memcpy(pmk, cached_pmk, cached_pmk_len); + } else { + switch (pasn->akmp) { +#ifdef CONFIG_SAE + case WPA_KEY_MGMT_SAE: + case WPA_KEY_MGMT_SAE_EXT_KEY: + if (pasn->sae.state == SAE_COMMITTED) { + pmk_len = pasn->sae.pmk_len; + os_memcpy(pmk, pasn->sae.pmk, pmk_len); + break; + } +#endif /* CONFIG_SAE */ + /* fall through */ + default: + /* TODO: Derive PMK based on wrapped data */ + wpa_printf(MSG_DEBUG, + "PASN: Missing PMK derivation"); + return -1; + } + } + + pasn->pmk_len = pmk_len; + os_memcpy(pasn->pmk, pmk, pmk_len); + ret = pasn_pmk_to_ptk(pmk, pmk_len, peer_addr, own_addr, + wpabuf_head(secret), wpabuf_len(secret), + &pasn->ptk, pasn->akmp, + pasn->cipher, pasn->kdk_len, pasn->kek_len, + &pasn->hash_alg); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK"); + return -1; + } + + if (pasn->secure_ltf) { + ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, + pasn->cipher); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed to derive LTF keyseed"); + return -1; + } + } + + wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived"); + return 0; +} + + +static void handle_auth_pasn_comeback(struct pasn_data *pasn, + const u8 *own_addr, const u8 *peer_addr, + u16 group) +{ + struct wpabuf *buf, *comeback; + int ret; + + wpa_printf(MSG_DEBUG, + "PASN: Building comeback frame 2. Comeback after=%u", + pasn->comeback_after); + + buf = wpabuf_alloc(1500); + if (!buf) + return; + + wpa_pasn_build_auth_header(buf, pasn->bssid, own_addr, peer_addr, 2, + WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY); + + /* + * Do not include the group as a part of the token since it is not going + * to be used. + */ + comeback = auth_build_token_req(&pasn->last_comeback_key_update, + pasn->comeback_key, pasn->comeback_idx, + pasn->comeback_pending_idx, + sizeof(u16) * COMEBACK_PENDING_IDX_SIZE, + 0, peer_addr, 0); + if (!comeback) { + wpa_printf(MSG_DEBUG, + "PASN: Failed sending auth with comeback"); + wpabuf_free(buf); + return; + } + + wpa_pasn_add_parameter_ie(buf, group, + WPA_PASN_WRAPPED_DATA_NO, + NULL, 0, comeback, + pasn->comeback_after); + wpabuf_free(comeback); + + wpa_printf(MSG_DEBUG, + "PASN: comeback: STA=" MACSTR, MAC2STR(peer_addr)); + + ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf), + wpabuf_len(buf), 0, pasn->freq, 0); + if (ret) + wpa_printf(MSG_INFO, "PASN: Failed to send comeback frame 2"); + + wpabuf_free(buf); +} + + +/* + * PASN frame-2 MIC: RSNE and RSNXE must be the octets as transmitted in this + * Authentication frame (IEEE 802.11), in order, before the MIC element. + */ +static int pasn_mic_rsne_rsnxe_from_auth_body(const u8 *auth_body, + size_t auth_body_len, + u8 **out, size_t *out_len) +{ + const u8 *pos, *end; + const u8 *rsne = NULL, *rsnx = NULL; + size_t rsne_len = 0, rsnx_len = 0; + + if (!auth_body || auth_body_len < 6) + return -1; + + pos = auth_body + 6; + end = auth_body + auth_body_len; + + while (pos + 2 <= end) { + u8 id = pos[0]; + u8 elen = pos[1]; + + if ((size_t) 2 + elen > (size_t) (end - pos)) + break; + + if (id == WLAN_EID_MIC) + break; + + if (id == WLAN_EID_RSN && !rsne) { + rsne = pos; + rsne_len = 2 + elen; + } else if (id == WLAN_EID_RSNX && !rsnx) { + rsnx = pos; + rsnx_len = 2 + elen; + } + pos += 2 + elen; + } + + if (!rsne || rsne_len < 4) + return -1; + + if (rsnx) { + *out_len = rsne_len + rsnx_len; + *out = os_malloc(*out_len); + if (!*out) + return -1; + os_memcpy(*out, rsne, rsne_len); + os_memcpy(*out + rsne_len, rsnx, rsnx_len); + } else { + *out_len = rsne_len; + *out = os_malloc(*out_len); + if (!*out) + return -1; + os_memcpy(*out, rsne, rsne_len); + } + return 0; +} + + +int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr, + const u8 *peer_addr, + struct rsn_pmksa_cache_entry *pmksa, u16 status) +{ + struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL; + u8 mic[WPA_PASN_MAX_MIC_LEN]; + u8 mic_len; + u8 *ptr; + const u8 *frame, *rsnxe_ie; + u8 *data_buf = NULL; + size_t frame_len, data_len; + int ret; + const u8 *pmkid = NULL; + + wpa_printf(MSG_DEBUG, "PASN: Building frame 2: status=%u", status); + + buf = wpabuf_alloc(1500); + if (!buf) + goto fail; + + wpa_pasn_build_auth_header(buf, pasn->bssid, own_addr, peer_addr, 2, + status); + + if (status != WLAN_STATUS_SUCCESS) + goto done; + + if (pmksa && pasn->custom_pmkid_valid) + pmkid = pasn->custom_pmkid; + else if (pmksa) { + pmkid = pmksa->pmkid; +#ifdef CONFIG_SAE + } else if (pasn->akmp == WPA_KEY_MGMT_SAE || + pasn->akmp == WPA_KEY_MGMT_SAE_EXT_KEY) { + wpa_printf(MSG_DEBUG, "PASN: Use SAE PMKID"); + pmkid = pasn->sae.pmkid; +#endif /* CONFIG_SAE */ +#ifndef ESP_SUPPLICANT +#ifdef CONFIG_FILS + } else if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 || + pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) { + wpa_printf(MSG_DEBUG, "PASN: Use FILS ERP PMKID"); + pmkid = pasn->fils.erp_pmkid; +#endif /* CONFIG_FILS */ +#endif /* ESP_SUPPLICANT */ + } + + if (wpa_pasn_add_rsne(buf, pmkid, + pasn->akmp, pasn->cipher) < 0) + goto fail; + + rsnxe_ie = pasn->rsnxe_ie; + + /* No need to derive PMK if PMKSA is given */ + if (!pmksa) + wrapped_data_buf = pasn_get_wrapped_data(pasn); + else + pasn->wrapped_data_format = WPA_PASN_WRAPPED_DATA_NO; + + /* Get public key */ + pubkey = crypto_ecdh_get_pubkey(pasn->ecdh, 0); + pubkey = wpabuf_zeropad(pubkey, + crypto_ecdh_prime_len(pasn->ecdh)); + if (!pubkey) { + wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey"); + goto fail; + } + + if (rsnxe_ie) + wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]); + + if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0) + goto fail; + + wpabuf_free(wrapped_data_buf); + wrapped_data_buf = NULL; + + wpa_pasn_add_parameter_ie(buf, pasn->group, + pasn->wrapped_data_format, + pubkey, true, NULL, 0); + + wpabuf_free(pubkey); + pubkey = NULL; + + if (pasn->prepare_data_element && pasn->cb_ctx) + pasn->prepare_data_element(pasn->cb_ctx, peer_addr); + + wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len); + + /* Add the mic */ + mic_len = pasn_mic_len(pasn->hash_alg); + wpabuf_put_u8(buf, WLAN_EID_MIC); + wpabuf_put_u8(buf, mic_len); + ptr = wpabuf_put(buf, mic_len); + + os_memset(ptr, 0, mic_len); + + frame = wpabuf_head_u8(buf) + IEEE80211_HDRLEN; + frame_len = wpabuf_len(buf) - IEEE80211_HDRLEN; + + if (pasn_mic_rsne_rsnxe_from_auth_body(frame, frame_len, &data_buf, + &data_len) < 0) { + wpa_printf(MSG_DEBUG, + "PASN: Frame 2: Failed RSNE/RSNXE extraction for MIC"); + goto fail; + } + + ret = pasn_mic(pasn->hash_alg, pasn->ptk.kck, pasn->ptk.kck_len, + own_addr, peer_addr, data_buf, data_len, + frame, frame_len, mic); + os_free(data_buf); + data_buf = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Frame 2: Failed MIC calculation"); + goto fail; + } + +#ifdef CONFIG_TESTING_OPTIONS + if (pasn->corrupt_mic) { + wpa_printf(MSG_DEBUG, "PASN: frame 2: Corrupt MIC"); + mic[0] = ~mic[0]; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + os_memcpy(ptr, mic, mic_len); + +done: + wpa_printf(MSG_DEBUG, + "PASN: Building frame 2: success; resp STA=" MACSTR, + MAC2STR(peer_addr)); + wpabuf_free(pasn->frame); + pasn->frame = NULL; + + ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf), + wpabuf_len(buf), 0, pasn->freq, 0); + if (ret) + wpa_printf(MSG_INFO, "send_auth_reply: Send failed"); + + pasn->frame = buf; + return ret; +fail: + os_free(data_buf); + wpabuf_free(wrapped_data_buf); + wpabuf_free(pubkey); + wpabuf_free(buf); + return -1; +} + + +int handle_auth_pasn_1(struct pasn_data *pasn, + const u8 *own_addr, const u8 *peer_addr, + const struct ieee80211_auth *mgmt, size_t len, + bool reject) +{ + struct ieee802_11_elems elems; + struct wpa_ie_data rsn_data; + struct wpa_pasn_params_data pasn_params; + struct rsn_pmksa_cache_entry *pmksa = NULL; + const u8 *cached_pmk = NULL; + size_t cached_pmk_len = 0; + struct wpabuf *wrapped_data = NULL, *secret = NULL; + const int *groups = pasn->pasn_groups; + static const int default_groups[] = { 19, 0 }; + u16 status = WLAN_STATUS_SUCCESS; + int ret, inc_y; + bool derive_keys; + u32 i; + + if (!groups) + groups = default_groups; + + if (reject) { + wpa_printf(MSG_DEBUG, "PASN: Received Rejection"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + if (ieee802_11_parse_elems(mgmt->auth.variable, + len - offsetof(struct ieee80211_auth, + auth.variable), + &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "PASN: Failed parsing Authentication frame"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + if (!elems.rsn_ie) { + wpa_printf(MSG_DEBUG, "PASN: No RSNE"); + status = WLAN_STATUS_INVALID_RSNIE; + goto send_resp; + } + + ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsn_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE"); + status = WLAN_STATUS_INVALID_RSNIE; + goto send_resp; + } + + ret = wpa_pasn_validate_rsne(&rsn_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE"); + status = WLAN_STATUS_INVALID_RSNIE; + goto send_resp; + } + + if (!(rsn_data.key_mgmt & pasn->wpa_key_mgmt) || + !(rsn_data.pairwise_cipher & pasn->rsn_pairwise)) { + status = WLAN_STATUS_INVALID_RSNIE; + goto send_resp; + } + + pasn->akmp = rsn_data.key_mgmt; + pasn->cipher = rsn_data.pairwise_cipher; + + if (pasn->derive_kdk && + ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len, + WLAN_RSNX_CAPAB_SECURE_LTF)) + pasn->secure_ltf = true; + + if (pasn->derive_kdk) + pasn->kdk_len = WPA_KDK_MAX_LEN; + else + pasn->kdk_len = 0; + + wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len); + + pasn->derive_kek = false; + if (!ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len, + WLAN_RSNX_CAPAB_KEK_IN_PASN)) { + pasn->kek_len = 0; + pasn->derive_kek = false; + } + + wpa_printf(MSG_DEBUG, "PASN: kek_len=%zu", pasn->kek_len); + + if (!elems.pasn_params || !elems.pasn_params_len) { + wpa_printf(MSG_DEBUG, + "PASN: No PASN Parameters element found"); + status = WLAN_STATUS_INVALID_PARAMETERS; + goto send_resp; + } + + ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3, + elems.pasn_params_len + 3, + false, &pasn_params); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed validation of PASN Parameters IE"); + status = WLAN_STATUS_INVALID_PARAMETERS; + goto send_resp; + } + + for (i = 0; groups[i] > 0 && groups[i] != pasn_params.group; i++) + ; + + if (!pasn_params.group || groups[i] != pasn_params.group) { + wpa_printf(MSG_DEBUG, "PASN: Requested group=%hu not allowed", + pasn_params.group); + status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + goto send_resp; + } + + if (!pasn_params.pubkey || !pasn_params.pubkey_len) { + wpa_printf(MSG_DEBUG, "PASN: Invalid public key"); + status = WLAN_STATUS_INVALID_PARAMETERS; + goto send_resp; + } + + if (pasn_params.comeback) { + wpa_printf(MSG_DEBUG, "PASN: Checking peer comeback token"); + + ret = check_comeback_token(pasn->comeback_key, + pasn->comeback_pending_idx, + peer_addr, + pasn_params.comeback, + pasn_params.comeback_len); + + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Invalid comeback token"); + status = WLAN_STATUS_INVALID_PARAMETERS; + goto send_resp; + } + } else if (pasn->use_anti_clogging) { + wpa_printf(MSG_DEBUG, "PASN: Respond with comeback"); + handle_auth_pasn_comeback(pasn, own_addr, peer_addr, + pasn_params.group); + return -1; + } + + if (pasn->ecdh) { + crypto_ecdh_deinit(pasn->ecdh); + pasn->ecdh = NULL; + } + pasn->ecdh = crypto_ecdh_init(pasn_params.group); + if (!pasn->ecdh) { + wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + pasn->group = pasn_params.group; + + if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) { + inc_y = 1; + } else if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 || + pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) { + inc_y = 0; + } else { + wpa_printf(MSG_DEBUG, + "PASN: Invalid first octet in pubkey=0x%x", + pasn_params.pubkey[0]); + status = WLAN_STATUS_INVALID_PUBLIC_KEY; + goto send_resp; + } + + secret = crypto_ecdh_set_peerkey(pasn->ecdh, inc_y, + pasn_params.pubkey + 1, + pasn_params.pubkey_len - 1); + if (!secret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + if (!pasn->noauth && pasn->akmp == WPA_KEY_MGMT_PASN) { + wpa_printf(MSG_DEBUG, "PASN: Refuse PASN-UNAUTH"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + derive_keys = true; + if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) { + wrapped_data = ieee802_11_defrag(elems.wrapped_data, + elems.wrapped_data_len, true); + if (!wrapped_data) { + wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + +#ifdef CONFIG_SAE + if (pasn->akmp == WPA_KEY_MGMT_SAE || + pasn->akmp == WPA_KEY_MGMT_SAE_EXT_KEY) { + ret = pasn_wd_handle_sae_commit(pasn, own_addr, + peer_addr, + wrapped_data); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed processing SAE commit"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + } +#endif /* CONFIG_SAE */ +/* #ifndef ESP_SUPPLICANT */ +/* #ifdef CONFIG_FILS */ +/* if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 || */ +/* pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) { */ +/* if (!pasn->fils_wd_valid) { */ +/* wpa_printf(MSG_DEBUG, */ +/* "PASN: Invalid FILS wrapped data"); */ +/* status = WLAN_STATUS_UNSPECIFIED_FAILURE; */ +/* goto send_resp; */ +/* } */ + +/* wpa_printf(MSG_DEBUG, */ +/* "PASN: FILS: Pending AS response"); */ + +/* * With PASN/FILS, keys can be derived only after a */ +/* * response from the AS is processed. */ +/* *1/ */ +/* derive_keys = false; */ +/* } */ +/* #endif /1* CONFIG_FILS *1/ */ +/* #endif /1* ESP_SUPPLICANT *1/ */ + } + + pasn->wrapped_data_format = pasn_params.wrapped_data_format; + + wpabuf_free(pasn->auth1); + pasn->auth1 = wpabuf_alloc_copy(((const u8 *) mgmt) + IEEE80211_HDRLEN, + len - IEEE80211_HDRLEN); + if (!pasn->auth1) { + wpa_printf(MSG_DEBUG, "PASN: Failed to store a copy of Auth1"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + + if (!derive_keys) { + wpa_printf(MSG_DEBUG, "PASN: Storing secret"); + pasn->secret = secret; + wpabuf_free(wrapped_data); + return 0; + } + + if (rsn_data.num_pmkid) { + if (wpa_key_mgmt_ft(pasn->akmp)) { +#ifdef CONFIG_IEEE80211R_AP + wpa_printf(MSG_DEBUG, "PASN: FT: Fetch PMK-R1"); + + if (!pasn->pmk_r1_len) { + wpa_printf(MSG_DEBUG, + "PASN: FT: Failed getting PMK-R1"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + cached_pmk = pasn->pmk_r1; + cached_pmk_len = pasn->pmk_r1_len; +#else /* CONFIG_IEEE80211R_AP */ + wpa_printf(MSG_DEBUG, "PASN: FT: Not supported"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; +#endif /* CONFIG_IEEE80211R_AP */ + } else { + wpa_printf(MSG_DEBUG, "PASN: Try to find PMKSA entry"); + + if (pasn->pmksa) { + const u8 *pmkid = NULL; + + if (pasn->custom_pmkid_valid) { + ret = pasn->validate_custom_pmkid( + pasn->cb_ctx, peer_addr, + rsn_data.pmkid); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed custom PMKID validation"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + } else { + pmkid = rsn_data.pmkid; + } + + pmksa = pmksa_cache_auth_get(pasn->pmksa, + peer_addr, + pmkid); + if (pmksa) { + cached_pmk = pmksa->pmk; + cached_pmk_len = pmksa->pmk_len; + } + } + } + } else { + wpa_printf(MSG_DEBUG, "PASN: No PMKID specified"); + } + + ret = pasn_derive_keys(pasn, own_addr, peer_addr, + cached_pmk, cached_pmk_len, + &pasn_params, wrapped_data, secret); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to derive keys"); + status = WLAN_STATUS_PASN_BASE_AKMP_FAILED; + goto send_resp; + } + + wpabuf_free(pasn->auth1); + pasn->auth1 = wpabuf_alloc_copy(((const u8 *) mgmt) + IEEE80211_HDRLEN, + len - IEEE80211_HDRLEN); + if (!pasn->auth1) { + wpa_printf(MSG_DEBUG, "PASN: Failed to store a copy of Auth1"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + } + +send_resp: + ret = handle_auth_pasn_resp(pasn, own_addr, peer_addr, pmksa, status); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: Failed to send response"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + } else { + wpa_printf(MSG_DEBUG, + "PASN: Success handling transaction == 1"); + } + + wpabuf_free(secret); + wpabuf_free(wrapped_data); + + if (status != WLAN_STATUS_SUCCESS) + return -1; + + return 0; +} + + +int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr, + const u8 *peer_addr, + const struct ieee80211_auth *mgmt, size_t len) +{ + struct ieee802_11_elems elems; + struct wpa_pasn_params_data pasn_params; + struct wpabuf *wrapped_data = NULL; + u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN]; + u8 mic_len; + int ret; + u8 *copy = NULL; + size_t copy_len, mic_offset; + u8 hash[SHA512_MAC_LEN]; + + if (ieee802_11_parse_elems(mgmt->auth.variable, + len - offsetof(struct ieee80211_auth, + auth.variable), + &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "PASN: Failed parsing Authentication frame"); + goto fail; + } + + /* Check that the MIC IE exists. Save it and zero out the memory. */ + mic_len = pasn_mic_len(pasn->hash_alg); + if (!elems.mic || elems.mic_len != mic_len) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid MIC. Expecting len=%u", mic_len); + goto fail; + } + os_memcpy(mic, elems.mic, mic_len); + + if (!elems.pasn_params || !elems.pasn_params_len) { + wpa_printf(MSG_DEBUG, + "PASN: No PASN Parameters element found"); + goto fail; + } + + ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3, + elems.pasn_params_len + 3, + false, &pasn_params); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed validation of PASN Parameters IE"); + goto fail; + } + + if (pasn_params.pubkey || pasn_params.pubkey_len) { + wpa_printf(MSG_DEBUG, + "PASN: Public key should not be included"); + goto fail; + } + + /* Verify the MIC */ + copy_len = len - offsetof(struct ieee80211_auth, auth); + mic_offset = elems.mic - (const u8 *) &mgmt->auth; + if (mic_offset + mic_len > copy_len) + goto fail; + copy = os_memdup(&mgmt->auth, copy_len); + if (!copy) + goto fail; + os_memset(copy + mic_offset, 0, mic_len); + if (!pasn->auth1 || + pasn_auth_frame_hash(pasn->hash_alg, wpabuf_head(pasn->auth1), + wpabuf_len(pasn->auth1), hash)) { + wpa_printf(MSG_INFO, "PASN: Failed to calculate Auth1 hash"); + goto fail; + } + ret = pasn_mic(pasn->hash_alg, pasn->ptk.kck, pasn->ptk.kck_len, + peer_addr, own_addr, hash, mic_len * 2, + copy, copy_len, out_mic); + os_free(copy); + copy = NULL; + + wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len); + if (ret || os_memcmp(mic, out_mic, mic_len) != 0) { + wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification"); + goto fail; + } + + if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) { + wrapped_data = ieee802_11_defrag(elems.wrapped_data, + elems.wrapped_data_len, + true); + + if (!wrapped_data) { + wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data"); + goto fail; + } + +#ifdef CONFIG_SAE + if (pasn->akmp == WPA_KEY_MGMT_SAE || + pasn->akmp == WPA_KEY_MGMT_SAE_EXT_KEY) { + ret = pasn_wd_handle_sae_confirm(pasn, peer_addr, + wrapped_data); + if (ret) { + wpa_printf(MSG_DEBUG, + "PASN: Failed processing SAE confirm"); + wpabuf_free(wrapped_data); + goto fail; + } + } +#endif /* CONFIG_SAE */ +#ifndef ESP_SUPPLICANT +#ifdef CONFIG_FILS + if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 || + pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) { + if (wrapped_data) { + wpa_printf(MSG_DEBUG, + "PASN: FILS: Ignore wrapped data"); + } + } +#endif /* CONFIG_FILS */ +#endif /* ESP_SUPPLICANT */ + wpabuf_free(wrapped_data); + } + + if (pasn_parse_encrypted_data(pasn, (const u8 *) mgmt, len) < 0) { + wpa_printf(MSG_DEBUG, "PASN: Encrypted data processing failed"); + goto fail; + } + + wpa_printf(MSG_INFO, + "PASN: Success handling transaction == 3. Store PTK"); + return 0; + +fail: + os_free(copy); + return -1; +} + +#endif /* CONFIG_PASN */ diff --git a/components/wpa_supplicant/src/rsn_supp/pmksa_cache.c b/components/wpa_supplicant/src/rsn_supp/pmksa_cache.c index 87f8b6871da..54707f6bcbf 100644 --- a/components/wpa_supplicant/src/rsn_supp/pmksa_cache.c +++ b/components/wpa_supplicant/src/rsn_supp/pmksa_cache.c @@ -51,6 +51,34 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, } +void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + struct rsn_pmksa_cache_entry *e; + + e = pmksa->pmksa; + while (e) { + if (e == entry) { + pmksa->pmksa = entry->next; + break; + } + if (e->next == entry) { + e->next = entry->next; + break; + } + } + + if (!e) { + wpa_printf(MSG_DEBUG, + "RSN: Could not remove PMKSA cache entry %p since it is not in the list", + entry); + return; + } + + pmksa_cache_free_entry(pmksa, entry, PMKSA_FREE); +} + + static void pmksa_cache_expire(void *eloop_ctx, void *user_data) { struct rsn_pmksa_cache *pmksa = eloop_ctx; @@ -314,6 +342,8 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, const void *network_ctx) { + if(!pmksa) + return NULL; struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; while (entry) { if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) && diff --git a/components/wpa_supplicant/src/rsn_supp/pmksa_cache.h b/components/wpa_supplicant/src/rsn_supp/pmksa_cache.h index 2bdf3d4d156..8923e27bd5d 100644 --- a/components/wpa_supplicant/src/rsn_supp/pmksa_cache.h +++ b/components/wpa_supplicant/src/rsn_supp/pmksa_cache.h @@ -72,6 +72,8 @@ pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *aa); void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *pmk, size_t pmk_len); +void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry); #else /* IEEE8021X_EAPOL */ @@ -132,6 +134,12 @@ static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, { } +void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ +} + + #endif /* IEEE8021X_EAPOL */ #endif /* PMKSA_CACHE_H */