From d2f6a3dacc1cbec8ebc0deb64cc7211f7c7750c5 Mon Sep 17 00:00:00 2001 From: Sarvesh Bodakhe Date: Thu, 16 Feb 2023 13:49:03 +0530 Subject: [PATCH] esp-wifi: add SAE-PK (Public Key) authentication support for station --- components/esp_wifi/Kconfig | 7 + components/esp_wifi/include/esp_wifi_types.h | 8 + components/esp_wifi/lib | 2 +- components/wpa_supplicant/CMakeLists.txt | 20 +- .../src/crypto/crypto_mbedtls-ec.c | 324 ++++++++ .../esp_supplicant/src/esp_common.c | 16 +- .../esp_supplicant/src/esp_scan.c | 12 +- .../esp_supplicant/src/esp_wifi_driver.h | 2 + .../esp_supplicant/src/esp_wpa3.c | 52 +- .../src/common/ieee802_11_common.c | 87 ++- .../src/common/ieee802_11_defs.h | 4 + components/wpa_supplicant/src/common/sae.c | 46 +- components/wpa_supplicant/src/common/sae.h | 55 +- components/wpa_supplicant/src/common/sae_pk.c | 724 ++++++++++++++++++ .../src/common/wpa_supplicant_i.h | 15 + components/wpa_supplicant/src/crypto/crypto.h | 38 + components/wpa_supplicant/src/rsn_supp/wpa.c | 11 + .../wpa_supplicant/src/rsn_supp/wpa_i.h | 2 + .../wpa_supplicant/src/rsn_supp/wpa_ie.c | 7 +- 19 files changed, 1407 insertions(+), 25 deletions(-) create mode 100644 components/wpa_supplicant/src/common/sae_pk.c diff --git a/components/esp_wifi/Kconfig b/components/esp_wifi/Kconfig index e10917f6bde..0c234bea7e6 100644 --- a/components/esp_wifi/Kconfig +++ b/components/esp_wifi/Kconfig @@ -255,6 +255,13 @@ menu "Wi-Fi" PMF (Protected Management Frames) is a prerequisite feature for a WPA3 connection, it needs to be explicitly configured before attempting connection. Please refer to the Wi-Fi Driver API Guide for details. + config ESP_WIFI_ENABLE_SAE_PK + bool "Enable SAE-PK" + default y + depends on ESP_WIFI_ENABLE_WPA3_SAE + help + Select this option to enable SAE-PK + config ESP_WIFI_ENABLE_WPA3_OWE_STA bool "Enable OWE STA" default y diff --git a/components/esp_wifi/include/esp_wifi_types.h b/components/esp_wifi/include/esp_wifi_types.h index 1af4e352508..88def44e4e6 100644 --- a/components/esp_wifi/include/esp_wifi_types.h +++ b/components/esp_wifi/include/esp_wifi_types.h @@ -267,6 +267,13 @@ typedef enum { WPA3_SAE_PWE_BOTH, } wifi_sae_pwe_method_t; +/** Configuration for SAE-PK */ +typedef enum { + WPA3_SAE_PK_MODE_AUTOMATIC = 0, + WPA3_SAE_PK_MODE_ONLY = 1, + WPA3_SAE_PK_MODE_DISABLED = 2, +} wifi_sae_pk_mode_t; + /** @brief Soft-AP configuration settings for the device */ typedef struct { uint8_t ssid[32]; /**< SSID of soft-AP. If ssid_len field is 0, this must be a Null terminated string. Otherwise, length is set according to ssid_len. */ @@ -304,6 +311,7 @@ typedef struct { uint32_t phymode:6; /**< Operation phy mode, BIT[5]: indicate whether LR enabled, BIT[0-4]: wifi_phy_mode_t. */ uint32_t reserved:8; /**< Reserved for future feature set */ wifi_sae_pwe_method_t sae_pwe_h2e; /**< Whether SAE hash to element is enabled */ + wifi_sae_pk_mode_t sae_pk_mode; /**< SAE-PK mode */ uint8_t failure_retry_cnt; /**< Number of connection retries station will do before moving to next AP. scan_method should be set as WIFI_ALL_CHANNEL_SCAN to use this config. Note: Enabling this may cause connection time to increase incase best AP doesn't behave properly. */ uint32_t he_dcm_set:1; /**< Whether DCM max.constellation for transmission and reception is set. */ diff --git a/components/esp_wifi/lib b/components/esp_wifi/lib index 096f13551ec..1ae80901581 160000 --- a/components/esp_wifi/lib +++ b/components/esp_wifi/lib @@ -1 +1 @@ -Subproject commit 096f13551ec3b88eb190018f2689734ef0dbf21f +Subproject commit 1ae8090158192107c0dfbc2de20f1e6dcc0f60e2 diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index 0b431d9a98f..8726d689de7 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -151,10 +151,7 @@ endif() if(CONFIG_ESP_WIFI_11KV_SUPPORT OR CONFIG_ESP_WIFI_11R_SUPPORT) set(roaming_src - "src/common/bss.c" - "src/common/scan.c" - "src/common/ieee802_11_common.c" - "esp_supplicant/src/esp_scan.c") + "src/common/ieee802_11_common.c") if(CONFIG_ESP_WIFI_11KV_SUPPORT) set(roaming_src ${roaming_src} "src/common/rrm.c" "src/common/wnm_sta.c") endif() @@ -165,6 +162,18 @@ else() set(roaming_src "") endif() +if(CONFIG_ESP_WIFI_ENABLE_SAE_PK) + set(srcs ${srcs} + "src/common/sae_pk.c") +endif() + +if(CONFIG_ESP_WIFI_11KV_SUPPORT OR CONFIG_ESP_WIFI_11R_SUPPORT OR CONFIG_ESP_WIFI_ENABLE_SAE_PK) + set(srcs ${srcs} + "src/common/bss.c" + "src/common/scan.c" + "esp_supplicant/src/esp_scan.c") +endif() + if(CONFIG_ESP_WIFI_MBO_SUPPORT) set(mbo_src "src/common/mbo.c") else() @@ -222,6 +231,9 @@ target_compile_definitions(${COMPONENT_LIB} PRIVATE if(CONFIG_ESP_WIFI_ENABLE_WPA3_SAE) target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_WPA3_SAE) endif() +if(CONFIG_ESP_WIFI_ENABLE_SAE_PK) + target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_SAE_PK) +endif() if(CONFIG_ESP_WIFI_WPS_STRICT) target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_WPS_STRICT) endif() 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 32733fae038..815c2ecad70 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 @@ -601,6 +601,14 @@ struct crypto_ec_group *crypto_ec_get_group_from_key(struct crypto_key *key) return (struct crypto_ec_group *)&(mbedtls_pk_ec(*pkey)->MBEDTLS_PRIVATE(grp)); } +int crypto_ec_key_group(struct crypto_ec_key *key) +{ + mbedtls_pk_context *pkey = (mbedtls_pk_context *)key; + + int iana_group = (int)crypto_ec_get_mbedtls_to_nist_group_id(mbedtls_pk_ec(*pkey)->MBEDTLS_PRIVATE(grp).id); + return iana_group; +} + struct crypto_bignum *crypto_ec_get_private_key(struct crypto_key *key) { mbedtls_pk_context *pkey = (mbedtls_pk_context *)key; @@ -1214,4 +1222,320 @@ struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y, return sh_secret; } +static int crypto_ec_key_load_group_from_der_params(mbedtls_asn1_buf *params, mbedtls_ecp_group *group) +{ + if (params == NULL) { + return -1; + } + + mbedtls_ecp_group_id grp_id; + if (params->tag == MBEDTLS_ASN1_OID) { + if (mbedtls_oid_get_ec_grp(params, &grp_id) != 0) { + return -1; + } + } else { + return -1; + } + + if (group->id != MBEDTLS_ECP_DP_NONE && group->id != grp_id) { + return -1; + } + if (mbedtls_ecp_group_load(group, grp_id) != 0) { + return -1; + } + return 0; +} + +static bool crypto_ec_key_is_compressed(const u8 *der, size_t der_len) +{ + size_t len; + size_t prime_len; + const unsigned char *end = der + der_len; + const unsigned char *p; + *(const unsigned char **)&p = der; + + + if (mbedtls_asn1_get_tag((unsigned char **)&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) { + return false; + } + + end = p + len; + mbedtls_asn1_buf alg_oid; + mbedtls_asn1_buf params; + memset(¶ms, 0, sizeof(mbedtls_asn1_buf)); + if (mbedtls_asn1_get_alg((unsigned char **)&p, end, &alg_oid, ¶ms) != 0) { + return false; + } + + + if (mbedtls_asn1_get_bitstring_null((unsigned char **)&p, end, &len) != 0) { + return false; + } + + mbedtls_ecp_group *group = os_malloc(sizeof(mbedtls_ecp_group)); + if (!group) { + return false; + } + mbedtls_ecp_group_init(group); + if (crypto_ec_key_load_group_from_der_params(¶ms, group) != 0) { + goto _err; + } + + prime_len = mbedtls_mpi_size(&group->P); + + if (mbedtls_ecp_get_type(group) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS + && (end - p) == 1+prime_len + && (*p == 0x02 || *p == 0x03)) { + mbedtls_ecp_group_free(group); + os_free(group); + return true; + } + +_err: + mbedtls_ecp_group_free(group); + os_free(group); + return false; +} + +/* See https://github.com/Mbed-TLS/mbedtls/pull/6282 and https://github.com/mwarning/mbedtls_ecp_compression for more details*/ +static struct crypto_ec_key* crypto_ec_key_parse_compressed_pub(const u8 *der, size_t der_len) +{ + + mbedtls_pk_type_t pk_alg = MBEDTLS_PK_NONE; + const mbedtls_pk_info_t *pk_info; + mbedtls_pk_context *pkey = NULL; + int ret; + size_t len; + size_t prime_len; + const unsigned char *end = der + der_len; + const unsigned char *p; + *(const unsigned char **)&p = der; + int parity_bit; + + + if ((ret = mbedtls_asn1_get_tag((unsigned char **)&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return NULL; + } + + end = p + len; + mbedtls_asn1_buf alg_oid; + mbedtls_asn1_buf params; + memset(¶ms, 0, sizeof(mbedtls_asn1_buf)); + if ((ret = mbedtls_asn1_get_alg((unsigned char **)&p, end, &alg_oid, ¶ms)) != 0) { + return NULL; + } + + if (mbedtls_oid_get_pk_alg(&alg_oid, &pk_alg) != 0) { + return NULL; + } + + if ((ret = mbedtls_asn1_get_bitstring_null((unsigned char **)&p, end, &len)) != 0) { + return NULL; + } + + if (p + len != end) { + return NULL; + } + + if ((pk_info = mbedtls_pk_info_from_type(pk_alg)) == NULL) { + return NULL; + } + + if (!(pk_alg == MBEDTLS_PK_ECKEY || pk_alg == MBEDTLS_PK_ECKEY_DH)) { + return NULL; + } + + mbedtls_ecp_group *group = os_malloc(sizeof(mbedtls_ecp_group)); + if (!group) { + return NULL; + } + mbedtls_ecp_group_init(group); + + if ((ret = crypto_ec_key_load_group_from_der_params(¶ms, group)) != 0) { + mbedtls_ecp_group_free(group); + os_free(group); + return NULL; + } + + /* Check prerequisite p = 3 mod 4 */ + if (mbedtls_mpi_get_bit(&group->P, 0) != 1 || + mbedtls_mpi_get_bit(&group->P, 1) != 1) { + mbedtls_ecp_group_free(group); + os_free(group); + return NULL; + } + + prime_len = mbedtls_mpi_size(&group->P); + + unsigned char *new_der = os_malloc(sizeof(unsigned char)*(der_len+prime_len)); + if (!new_der) { + mbedtls_ecp_group_free(group); + os_free(group); + return NULL; + } + os_memcpy(new_der, der, der_len); + int offset = p - (unsigned char *)der ; + unsigned char *key_start = (unsigned char *)new_der + offset; + unsigned int new_der_len = der_len + prime_len; + + mbedtls_mpi y; + mbedtls_mpi x; + mbedtls_mpi n; + + + mbedtls_mpi_init(&y); + mbedtls_mpi_init(&x); + mbedtls_mpi_init(&n); + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&x, key_start + 1, prime_len)); + parity_bit = key_start[0] & 1; + + /* w = y^2 = x^3 + ax + b + * y = sqrt(w) = w^((p+1)/4) mod p (for prime p where p = 3 mod 4) + * use y to store intermediate results*/ + + /* y = x^2 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&y, &x, &x)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&y, &y, &group->P)); + + /* y = x^2 + a */ + if (group->A.MBEDTLS_PRIVATE(p) == NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&n, -3)); + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&y, &y, &n)); + } else { + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&y, &y, &group->A)); + } + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&y, &y, &group->P)); + + /* y = x^3 + ax */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&y, &y, &x)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&y, &y, &group->P)); + + /* y = x^3 + ax + b */ + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&y, &y, &group->B)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&y, &y, &group->P)); + + /* n = P + 1 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&n, &group->P, 1)); + + /* n = (P + 1) / 4 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&n, 2)); + + /* y ^ ((P + 1) / 4) (mod p) */ + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&y, &y, &n, &group->P, NULL)); + + /* check parity bit match or else invert Y */ + /* This quick inversion implementation is valid because Y != 0 for all + * Short Weierstrass curves supported by mbedtls, as each supported curve + * has an order that is a large prime, so each supported curve does not + * have any point of order 2, and a point with Y == 0 would be of order 2 */ + + if (mbedtls_mpi_get_bit(&y, 0) != parity_bit) { + /* y = p - y */ + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&y, &group->P, &y)); + } + + /* y => output */ + ret = mbedtls_mpi_write_binary(&y, key_start + 1 + prime_len, prime_len); + + key_start[0] = 0x04; + + pkey = os_zalloc(sizeof(*pkey)); + if (!pkey) { + goto cleanup; + } + mbedtls_pk_init(pkey); + mbedtls_pk_setup(pkey, pk_info); + + mbedtls_ecp_group_copy(&mbedtls_pk_ec(*pkey)->MBEDTLS_PRIVATE(grp), (const mbedtls_ecp_group *)group); + + if ((ret = mbedtls_ecp_point_read_binary(group, &mbedtls_pk_ec(*pkey)->MBEDTLS_PRIVATE(Q), + (const unsigned char *)key_start, (new_der + new_der_len - key_start))) == 0) { + ret = mbedtls_ecp_check_pubkey(&mbedtls_pk_ec(*pkey)->MBEDTLS_PRIVATE(grp), &mbedtls_pk_ec(*pkey)->MBEDTLS_PRIVATE(Q)); + } + + if (ret < 0) { + wpa_printf(MSG_ERROR, "failed to parse ec public key"); + mbedtls_ecp_group_free(group); + os_free(group); + os_free(new_der); + mbedtls_pk_free(pkey); + os_free(pkey); + return NULL; + } + +cleanup: + mbedtls_mpi_free(&y); + mbedtls_mpi_free(&x); + mbedtls_mpi_free(&n); + mbedtls_ecp_group_free(group); + os_free(group); + os_free(new_der); + + return (struct crypto_ec_key *)pkey; +} + +struct crypto_ec_key *crypto_ec_key_parse_pub(const u8 *der, size_t der_len) +{ + mbedtls_pk_context *pkey = NULL; + + if (crypto_ec_key_is_compressed(der, der_len)) { + pkey = (mbedtls_pk_context *)crypto_ec_key_parse_compressed_pub(der, der_len); + if (!pkey) { + wpa_printf(MSG_ERROR, "failed to parse ec public key"); + return NULL; + } + } else { + wpa_printf(MSG_ERROR, "failed to parse ec public key. expected compressed format"); + return NULL; + } + return (struct crypto_ec_key *)pkey; +} + + +void crypto_ec_key_deinit(struct crypto_ec_key *key) +{ + mbedtls_pk_free((mbedtls_pk_context *)key); + os_free(key); +} + +int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data, + size_t len, const u8 *sig, size_t sig_len) +{ + int ret = 0; + + mbedtls_ecdsa_context *ctx_verify = os_malloc(sizeof(mbedtls_ecdsa_context)); + if (ctx_verify == NULL) { + return -1; + } + + mbedtls_ecdsa_init(ctx_verify); + + mbedtls_ecp_keypair *ec_key = mbedtls_pk_ec(*((mbedtls_pk_context *)key)); + mbedtls_ecp_group *grp = &ec_key->MBEDTLS_PRIVATE(grp); + + if ((ret = mbedtls_ecp_group_copy(&ctx_verify->MBEDTLS_PRIVATE(grp),grp)) != 0) { + goto cleanup; + } + + if ((ret = mbedtls_ecp_copy(&ctx_verify->MBEDTLS_PRIVATE(Q), &ec_key->MBEDTLS_PRIVATE(Q))) != 0) { + goto cleanup; + } + + if ((ret = mbedtls_ecdsa_read_signature(ctx_verify, + data, len, + sig, sig_len)) != 0) { + goto cleanup; + } + ret = 1; + +cleanup: + mbedtls_ecdsa_free(ctx_verify); + os_free(ctx_verify); + return ret; +} + + #endif /* CONFIG_ECC */ diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_common.c b/components/wpa_supplicant/esp_supplicant/src/esp_common.c index 0d8ed30b6dc..974870dcedf 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_common.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_common.c @@ -24,7 +24,7 @@ #include "rsn_supp/wpa.h" struct wpa_supplicant g_wpa_supp; -#if defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) +#if defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) || defined(CONFIG_SAE_PK) #ifdef CONFIG_SUPPLICANT_TASK static void *s_supplicant_task_hdl = NULL; @@ -278,7 +278,7 @@ static int handle_assoc_frame(u8 *frame, size_t len, return 0; } #endif /* CONFIG_IEEE80211R */ -#endif /* defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) */ +#endif /* defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) || defined(CONFIG_SAE_PK)*/ static int ieee80211_handle_rx_frm(u8 type, u8 *frame, size_t len, u8 *sender, u32 rssi, u8 channel, u64 current_tsf) @@ -286,12 +286,12 @@ static int ieee80211_handle_rx_frm(u8 type, u8 *frame, size_t len, u8 *sender, int ret = 0; switch (type) { -#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211KV) +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211KV) || defined(CONFIG_SAE_PK) case WLAN_FC_STYPE_BEACON: case WLAN_FC_STYPE_PROBE_RESP: ret = esp_handle_beacon_probe(type, frame, len, sender, rssi, channel, current_tsf); break; -#endif /* defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) */ +#endif /* defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) || defined(CONFIG_SAE_PK)*/ #ifdef CONFIG_IEEE80211R case WLAN_FC_STYPE_AUTH: ret = handle_auth_frame(frame, len, sender, rssi, channel); @@ -354,7 +354,7 @@ int esp_supplicant_common_init(struct wpa_funcs *wpa_cb) struct wpa_supplicant *wpa_s = &g_wpa_supp; int ret = 0; -#if defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) +#if defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) || defined(CONFIG_SAE_PK) #ifdef CONFIG_SUPPLICANT_TASK s_supplicant_api_lock = os_recursive_mutex_create(); if (!s_supplicant_api_lock) { @@ -388,7 +388,7 @@ int esp_supplicant_common_init(struct wpa_funcs *wpa_cb) esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &supplicant_sta_disconn_handler, NULL); -#endif /* defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) */ +#endif /* defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) || defined(CONFIG_SAE_PK)*/ wpa_s->type = 0; wpa_s->subtype = 0; wpa_s->type |= (1 << WLAN_FC_STYPE_ASSOC_RESP) | (1 << WLAN_FC_STYPE_REASSOC_RESP) | (1 << WLAN_FC_STYPE_AUTH); @@ -413,7 +413,7 @@ void esp_supplicant_common_deinit(void) { struct wpa_supplicant *wpa_s = &g_wpa_supp; -#if defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) +#if defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) || defined(CONFIG_SAE_PK) esp_scan_deinit(wpa_s); #ifdef CONFIG_IEEE80211KV wpas_rrm_reset(wpa_s); @@ -423,7 +423,7 @@ void esp_supplicant_common_deinit(void) &supplicant_sta_conn_handler); esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &supplicant_sta_disconn_handler); -#endif /* defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) */ +#endif /* defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) || defined(CONFIG_SAE_PK)*/ if (wpa_s->type) { wpa_s->type = 0; esp_wifi_register_mgmt_frame_internal(wpa_s->type, wpa_s->subtype); diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_scan.c b/components/wpa_supplicant/esp_supplicant/src/esp_scan.c index eac8cf228b3..21f9b1e05e6 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_scan.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_scan.c @@ -238,11 +238,13 @@ static int issue_scan(struct wpa_supplicant *wpa_s, wpa_printf(MSG_INFO, "scan issued at time=%llu", wpa_s->scan_start_tsf); cleanup: - if (params->ssid) - os_free(params->ssid); - if (params->bssid) - os_free(params->bssid); - os_free(params); + if (params) { + if (params->ssid) + os_free(params->ssid); + if (params->bssid) + os_free(params->bssid); + os_free(params); + } return 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 e0b2b5de22b..40a304dec42 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h @@ -281,6 +281,8 @@ void esp_wifi_get_pmf_config_internal(wifi_pmf_config_t *pmf_cfg, uint8_t ifx); bool esp_wifi_is_ft_enabled_internal(uint8_t if_index); uint8_t esp_wifi_sta_get_config_sae_pwe_h2e_internal(void); uint8_t esp_wifi_sta_get_use_h2e_internal(void); +uint8_t esp_wifi_sta_get_config_sae_pk_internal(void); +void esp_wifi_sta_disable_sae_pk_internal(void); void esp_wifi_sta_disable_wpa2_authmode_internal(void); #endif /* _ESP_WIFI_DRIVER_H_ */ diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wpa3.c b/components/wpa_supplicant/esp_supplicant/src/esp_wpa3.c index 62fc38e5616..39802caa530 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wpa3.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wpa3.c @@ -11,6 +11,11 @@ #include "esp_wifi_driver.h" #include "rsn_supp/wpa.h" +#ifdef CONFIG_SAE_PK +#include "common/bss.h" +extern struct wpa_supplicant g_wpa_supp; +#endif + static struct sae_pt *g_sae_pt; static struct sae_data g_sae_data; static struct wpabuf *g_sae_token = NULL; @@ -59,9 +64,43 @@ static esp_err_t wpa3_build_sae_commit(u8 *bssid, size_t *sae_msg_len) return ESP_FAIL; } +#ifdef CONFIG_SAE_PK + bool use_pk = false; + uint8_t sae_pk_mode = esp_wifi_sta_get_config_sae_pk_internal(); + u8 rsnxe_capa = 0; + struct wpa_bss *bss = wpa_bss_get_bssid(&g_wpa_supp, (uint8_t *)bssid); + if (!bss) { + wpa_printf(MSG_ERROR, + "SAE: BSS not available, update scan result to get BSS"); + // TODO: should we trigger scan again. + return ESP_FAIL; + } + if (bss) { + const u8 *rsnxe; + + rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX); + if (rsnxe && rsnxe[1] >= 1) { + rsnxe_capa = rsnxe[2]; + } + } + + if (use_pt && (rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) && + sae_pk_mode != WPA3_SAE_PK_MODE_DISABLED && + ((pw && sae_pk_valid_password((const char*)pw)))) { + use_pt = 1; + use_pk = true; + } + + if (sae_pk_mode == WPA3_SAE_PK_MODE_ONLY && !use_pk) { + wpa_printf(MSG_DEBUG, + "SAE: Cannot use PK with the selected AP"); + return ESP_FAIL; + } +#endif /* CONFIG_SAE_PK */ + if (use_pt && sae_prepare_commit_pt(&g_sae_data, g_sae_pt, - own_addr, bssid, NULL) < 0) { + own_addr, bssid, NULL, NULL) < 0) { wpa_printf(MSG_ERROR, "wpa3: failed to prepare SAE commit!"); return ESP_FAIL; } @@ -73,6 +112,15 @@ static esp_err_t wpa3_build_sae_commit(u8 *bssid, size_t *sae_msg_len) return ESP_FAIL; } +#ifdef CONFIG_SAE_PK + if (g_sae_data.tmp && use_pt && use_pk) { + g_sae_data.pk = 1; + os_memcpy(g_sae_data.tmp->own_addr,own_addr, ETH_ALEN ); + os_memcpy(g_sae_data.tmp->peer_addr, bssid, ETH_ALEN); + sae_pk_set_password(&g_sae_data,(const char*) pw); + } +#endif + reuse_data: len += SAE_COMMIT_MAX_LEN; g_sae_commit = wpabuf_alloc(len); @@ -199,7 +247,7 @@ static int wpa3_parse_sae_commit(u8 *buf, u32 len, u16 status) } ret = sae_parse_commit(&g_sae_data, buf, len, NULL, 0, g_allowed_groups, - status == WLAN_STATUS_SAE_HASH_TO_ELEMENT); + (status == WLAN_STATUS_SAE_HASH_TO_ELEMENT || status == WLAN_STATUS_SAE_PK)); if (ret) { wpa_printf(MSG_ERROR, "wpa3: could not parse commit(%d)", ret); return ret; diff --git a/components/wpa_supplicant/src/common/ieee802_11_common.c b/components/wpa_supplicant/src/common/ieee802_11_common.c index ea67cff9678..ed44d3532ef 100644 --- a/components/wpa_supplicant/src/common/ieee802_11_common.c +++ b/components/wpa_supplicant/src/common/ieee802_11_common.c @@ -191,6 +191,69 @@ int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, return nei_pos - nei_rep; } +#ifdef CONFIG_SAE_PK +static int ieee802_11_parse_vendor_specific(struct wpa_supplicant *wpa_s, const struct element *elem, const u8* pos) +{ + u32 oui; + oui = WPA_GET_BE24(pos); + switch (oui) { + case OUI_WFA: + switch (pos[3]) { + case SAE_PK_OUI_TYPE: + wpa_s->sae_pk_elems.sae_pk_len = elem->datalen - 4; + wpa_s->sae_pk_elems.sae_pk = (u8*)os_zalloc(sizeof(u8)*(elem->datalen-4)); + os_memcpy(wpa_s->sae_pk_elems.sae_pk, pos+4, elem->datalen-4); + break; + default: + wpa_printf(MSG_EXCESSIVE, "Unknown WFA " + "information element ignored " + "(type=%d len=%lu)", + pos[3], (unsigned long) elem->datalen); + return -1; + } + break; + default: + wpa_printf(MSG_EXCESSIVE, "unknown vendor specific " + "information element ignored (vendor OUI " + "%02x:%02x:%02x len=%lu)", + pos[0], pos[1], pos[2], (unsigned long) elem->datalen); + return -1; + } + + return 0; +} + +static int ieee802_11_parse_extension(struct wpa_supplicant *wpa_s, const struct element *elem, const u8* pos){ + // do not consider extension_id element len in datalen + if (elem->datalen < 1) { + wpa_printf(MSG_DEBUG, + "short information element (Ext)"); + return -1; + } + u8 ext_id; + ext_id = *pos++; + switch (ext_id) { + case WLAN_EID_EXT_FILS_KEY_CONFIRM: + wpa_s->sae_pk_elems.fils_key_confirm_len = elem->datalen - 1; + wpa_s->sae_pk_elems.fils_key_confirm = (u8*)os_zalloc(sizeof(u8)*(elem->datalen - 1)); + os_memcpy(wpa_s->sae_pk_elems.fils_key_confirm, pos, elem->datalen - 1); + break; + case WLAN_EID_EXT_FILS_PUBLIC_KEY: + wpa_s->sae_pk_elems.fils_pk_len = elem->datalen - 1; + wpa_s->sae_pk_elems.fils_pk = (u8*)os_zalloc(sizeof(u8)*(elem->datalen - 1)); + os_memcpy(wpa_s->sae_pk_elems.fils_pk, pos, elem->datalen - 1); + break; + default: + wpa_printf(MSG_EXCESSIVE, + "IEEE 802.11 element parsing ignored unknown element extension (ext_id=%u elen=%u)", + ext_id, (unsigned int) elem->datalen-1); + return -1; + } + + return 0; +} +#endif /* CONFIG_SAE_PK */ + /** * ieee802_11_parse_elems - Parse information elements in management frames * @start: Pointer to the start of IEs @@ -201,8 +264,9 @@ int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, */ int ieee802_11_parse_elems(struct wpa_supplicant *wpa_s, const u8 *start, size_t len) { -#ifdef CONFIG_RRM +#if defined(CONFIG_RRM) || defined(CONFIG_SAE_PK) const struct element *elem; + u8 unknown = 0; if (!start) return 0; @@ -211,20 +275,39 @@ int ieee802_11_parse_elems(struct wpa_supplicant *wpa_s, const u8 *start, size_t u8 id = elem->id; const u8 *pos = elem->data; switch (id) { +#ifdef CONFIG_RRM case WLAN_EID_RRM_ENABLED_CAPABILITIES: os_memcpy(wpa_s->rrm_ie, pos, 5); wpa_s->rrm.rrm_used = true; break; +#endif +#ifdef CONFIG_SAE_PK + case WLAN_EID_EXTENSION: + if(ieee802_11_parse_extension(wpa_s, elem, pos) != 0){ + unknown++; + } + break; + case WLAN_EID_VENDOR_SPECIFIC: + if(ieee802_11_parse_vendor_specific(wpa_s, elem, pos) != 0){ + unknown++; + } + break; +#endif /*CONFIG_SAE_PK*/ +#ifdef CONFIG_RRM case WLAN_EID_EXT_CAPAB: /* extended caps can go beyond 8 octacts but we aren't using them now */ os_memcpy(wpa_s->extend_caps, pos, 5); break; +#endif default: break; } } -#endif + if (unknown) + return -1; + +#endif /* defined(CONFIG_RRM) || defined(CONFIG_SAE_PK) */ return 0; } diff --git a/components/wpa_supplicant/src/common/ieee802_11_defs.h b/components/wpa_supplicant/src/common/ieee802_11_defs.h index 1742eed0c54..4cc35c7a0c4 100644 --- a/components/wpa_supplicant/src/common/ieee802_11_defs.h +++ b/components/wpa_supplicant/src/common/ieee802_11_defs.h @@ -158,6 +158,7 @@ #define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104 #define WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER 123 #define WLAN_STATUS_SAE_HASH_TO_ELEMENT 126 +#define WLAN_STATUS_SAE_PK 127 /* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */ #define WLAN_REASON_UNSPECIFIED 1 @@ -257,6 +258,7 @@ /* 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_EXT_CAPAB_BSS_TRANSITION 19 @@ -555,6 +557,8 @@ struct ieee80211_ht_operation { #define MBO_IE_VENDOR_TYPE 0x506f9a16 #define OSEN_IE_VENDOR_TYPE 0x506f9a12 +#define SAE_PK_IE_VENDOR_TYPE 0x506f9a1f +#define SAE_PK_OUI_TYPE 0x1f #define MBO_OUI_TYPE 22 #define OCE_STA BIT(0) #define OCE_STA_CFON BIT(1) diff --git a/components/wpa_supplicant/src/common/sae.c b/components/wpa_supplicant/src/common/sae.c index 3d04995327f..c9c0775473e 100644 --- a/components/wpa_supplicant/src/common/sae.c +++ b/components/wpa_supplicant/src/common/sae.c @@ -19,6 +19,7 @@ #include "sae.h" #include "dragonfly.h" #include "esp_wifi_crypto_types.h" +#include "common/defs.h" int sae_set_group(struct sae_data *sae, int group) { @@ -1045,6 +1046,10 @@ sae_derive_pt_group(int group, const u8 *ssid, size_t ssid_len, pt = os_zalloc(sizeof(*pt)); if (!pt) return NULL; +#ifdef CONFIG_SAE_PK + os_memcpy(pt->ssid, ssid, ssid_len); + pt->ssid_len = ssid_len; +#endif /* CONFIG_SAE_PK */ pt->group = group; pt->ec = crypto_ec_init(group); @@ -1368,7 +1373,7 @@ int sae_prepare_commit(const u8 *addr1, const u8 *addr2, int sae_prepare_commit_pt(struct sae_data *sae, struct sae_pt *pt, const u8 *addr1, const u8 *addr2, - int *rejected_groups) + int *rejected_groups, const struct sae_pk *pk) { if (!sae->tmp) return -1; @@ -1383,6 +1388,11 @@ int sae_prepare_commit_pt(struct sae_data *sae, struct sae_pt *pt, sae->group); return -1; } +#ifdef CONFIG_SAE_PK + os_memcpy(sae->tmp->ssid, pt->ssid, pt->ssid_len); + sae->tmp->ssid_len = pt->ssid_len; + sae->tmp->ap_pk = pk; +#endif /* CONFIG_SAE_PK */ sae->tmp->own_addr_higher = os_memcmp(addr1, addr2, ETH_ALEN) > 0; wpabuf_free(sae->tmp->own_rejected_groups); @@ -1523,6 +1533,9 @@ static int sae_derive_keys(struct sae_data *sae, const u8 *k) * KCK || PMK = KDF-Hash-Length(keyseed, "SAE KCK and PMK", * (commit-scalar + peer-commit-scalar) modulo r) * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128) + * + * When SAE_PK is used, + * KCK || PMK || KEK = KDF-Hash-Length(keyseed, "SAE-PK keys", context) */ if (!sae->h2e) hash_len = SHA256_MAC_LEN; @@ -1530,6 +1543,7 @@ static int sae_derive_keys(struct sae_data *sae, const u8 *k) hash_len = sae_ffc_prime_len_2_hash_len(prime_len); else hash_len = sae_ecc_prime_len_2_hash_len(prime_len); + if (sae->h2e && (sae->tmp->own_rejected_groups || sae->tmp->peer_rejected_groups)) { struct wpabuf *own, *peer; @@ -1583,14 +1597,38 @@ static int sae_derive_keys(struct sae_data *sae, const u8 *k) crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->order_len); wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); +#ifdef CONFIG_SAE_PK + if (sae->pk) { + if (sae_kdf_hash(hash_len, keyseed, "SAE-PK keys", + val, sae->tmp->order_len, + keys, 2 * hash_len + SAE_PMK_LEN) < 0) + goto fail; + } else { + if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK", + val, sae->tmp->order_len, + keys, hash_len + SAE_PMK_LEN) < 0) + goto fail; + } +#else /* CONFIG_SAE_PK */ if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK", val, sae->tmp->order_len, keys, hash_len + SAE_PMK_LEN) < 0) goto fail; +#endif /* !CONFIG_SAE_PK */ + forced_memzero(keyseed, sizeof(keyseed)); os_memcpy(sae->tmp->kck, keys, hash_len); os_memcpy(sae->pmk, keys + hash_len, SAE_PMK_LEN); os_memcpy(sae->pmkid, val, SAE_PMKID_LEN); + +#ifdef CONFIG_SAE_PK + if (sae->pk) { + os_memcpy(sae->tmp->kek, keys + hash_len + SAE_PMK_LEN, hash_len); + sae->tmp->kek_len = hash_len; + wpa_hexdump_key(MSG_DEBUG, "SAE: KEK for SAE-PK", + sae->tmp->kek, sae->tmp->kek_len); + } +#endif /* CONFIG_SAE_PK */ forced_memzero(keys, sizeof(keys)); wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN); @@ -2293,6 +2331,12 @@ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) return ESP_FAIL; } +#ifdef CONFIG_SAE_PK + if (sae_check_confirm_pk(sae, data + 2 + hash_len, + len - 2 - hash_len) != ESP_OK) + return ESP_FAIL; +#endif /* CONFIG_SAE_PK */ + return ESP_OK; } diff --git a/components/wpa_supplicant/src/common/sae.h b/components/wpa_supplicant/src/common/sae.h index f71a6ad1f4f..2e31ad9223d 100644 --- a/components/wpa_supplicant/src/common/sae.h +++ b/components/wpa_supplicant/src/common/sae.h @@ -23,11 +23,23 @@ #define SAE_MAX_ECC_PRIME_LEN 66 #define SAE_MAX_HASH_LEN 64 #define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN + 255) +#ifdef CONFIG_SAE_PK +#define SAE_CONFIRM_MAX_LEN ((2 + SAE_MAX_HASH_LEN) + 1500) +#else /* CONFIG_SAE_PK */ #define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_HASH_LEN) +#endif /* CONFIG_SAE_PK */ +#define SAE_PK_M_LEN 16 /* Special value returned by sae_parse_commit() */ #define SAE_SILENTLY_DISCARD 65535 +struct sae_pk { + struct wpabuf *m; + struct crypto_ec_key *key; + int group; + struct wpabuf *pubkey; /* DER encoded subjectPublicKey */ +}; + struct sae_temporary_data { u8 kck[SAE_KCK_LEN]; struct crypto_bignum *own_commit_scalar; @@ -50,6 +62,21 @@ struct sae_temporary_data { struct wpabuf *own_rejected_groups; struct wpabuf *peer_rejected_groups; unsigned int own_addr_higher:1; + +#ifdef CONFIG_SAE_PK + u8 kek[SAE_MAX_HASH_LEN]; + size_t kek_len; + const struct sae_pk *ap_pk; + u8 own_addr[ETH_ALEN]; + u8 peer_addr[ETH_ALEN]; + u8 fingerprint[SAE_MAX_HASH_LEN]; + size_t fingerprint_bytes; + size_t fingerprint_bits; + size_t lambda; + unsigned int sec; + u8 ssid[32]; + size_t ssid_len; +#endif }; struct sae_pt { @@ -60,6 +87,10 @@ struct sae_pt { const struct dh_group *dh; struct crypto_bignum *ffc_pt; +#ifdef CONFIG_SAE_PK + u8 ssid[32]; + size_t ssid_len; +#endif /* CONFIG_SAE_PK */ }; enum { @@ -76,6 +107,7 @@ struct sae_data { u16 send_confirm; u8 pmk[SAE_PMK_LEN]; u8 pmkid[SAE_PMKID_LEN]; + struct crypto_bignum *peer_commit_scalar; int group; unsigned int sync; /* protocol instance variable: Sync */ @@ -83,6 +115,7 @@ struct sae_data { struct sae_temporary_data *tmp; struct crypto_bignum *peer_commit_scalar_accepted; unsigned int h2e:1; + unsigned int pk:1; }; int sae_set_group(struct sae_data *sae, int group); @@ -94,7 +127,7 @@ int sae_prepare_commit(const u8 *addr1, const u8 *addr2, struct sae_data *sae); int sae_prepare_commit_pt(struct sae_data *sae, struct sae_pt *pt, const u8 *addr1, const u8 *addr2, - int *rejected_groups); + int *rejected_groups, const struct sae_pk *pk); int sae_process_commit(struct sae_data *sae); int sae_write_commit(struct sae_data *sae, struct wpabuf *buf, const struct wpabuf *token, const char *identifier); @@ -118,5 +151,25 @@ sae_derive_pwe_from_pt_ffc(const struct sae_pt *pt, const u8 *addr1, const u8 *addr2); void sae_deinit_pt(struct sae_pt *pt); +/* sae_pk.c */ +#ifdef CONFIG_SAE_PK +bool sae_pk_valid_password(const char *pw); +#else /* CONFIG_SAE_PK */ +static inline bool sae_pk_valid_password(const char *pw) +{ + return false; +} +#endif /* CONFIG_SAE_PK */ +char * sae_pk_base32_encode(const u8 *src, size_t len_bits); +u8 * sae_pk_base32_decode(const char *src, size_t len, size_t *out_len); +int sae_pk_set_password(struct sae_data *sae, const char *password); +void sae_deinit_pk(struct sae_pk *pk); +struct sae_pk * sae_parse_pk(const char *val); +int sae_write_confirm_pk(struct sae_data *sae, struct wpabuf *buf); +int sae_check_confirm_pk(struct sae_data *sae, const u8 *ies, size_t ies_len); +int sae_hash(size_t hash_len, const u8 *data, size_t len, u8 *hash); +u32 sae_pk_get_be19(const u8 *buf); +void sae_pk_buf_shift_left_19(u8 *buf, size_t len); + #endif /* SAE_H */ #endif /* CONFIG_WPA3_SAE */ diff --git a/components/wpa_supplicant/src/common/sae_pk.c b/components/wpa_supplicant/src/common/sae_pk.c new file mode 100644 index 00000000000..472ef03d2c2 --- /dev/null +++ b/components/wpa_supplicant/src/common/sae_pk.c @@ -0,0 +1,724 @@ +/* + * SAE-PK + * Copyright (c) 2020, The Linux Foundation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifdef CONFIG_SAE_PK + +#include "utils/includes.h" +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_supplicant_i.h" +#include "common/ieee802_11_common.h" +#include "esp_common_i.h" +#include "sae.h" +#include "utils/base64.h" +#include "crypto/crypto.h" +#include "crypto/aes.h" +#include "crypto/aes_siv.h" + + +extern struct wpa_supplicant g_wpa_supp; + +/* RFC 4648 base 32 alphabet with lowercase characters */ +static const char *sae_pk_base32_table = "abcdefghijklmnopqrstuvwxyz234567"; + + +static const u8 d_mult_table[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, + 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, + 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, + 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, + 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, + 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, + 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, + 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, + 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, + 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, + 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, + 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, + 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, + 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, + 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, + 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, + 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, + 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, + 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, + 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, + 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, + 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, + 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, + 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, + 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, + 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +}; + + +static const u8 d_perm_table[] = { + 7, 2, 1, 30, 16, 20, 27, 11, 31, 6, 8, 13, 29, 5, 10, 21, + 22, 3, 24, 0, 23, 25, 12, 9, 28, 14, 4, 15, 17, 18, 19, 26 +}; + + +static u8 d_permute(u8 val, unsigned int iter) +{ + if (iter == 0) { + return val; + } + return d_permute(d_perm_table[val], iter - 1); +} + + +static u8 d_invert(u8 val) +{ + if (val > 0 && val < 16) { + return 16 - val; + } + return val; +} + + +static char d_check_char(const char *str, size_t len) +{ + size_t i; + u8 val = 0; + u8 dtable[256]; + unsigned int iter = 1; + int j; + + os_memset(dtable, 0x80, 256); + for (i = 0; sae_pk_base32_table[i]; i++) { + dtable[(u8) sae_pk_base32_table[i]] = i; + } + + for (j = len - 1; j >= 0; j--) { + u8 c, p; + + c = dtable[(u8) str[j]]; + if (c == 0x80) { + continue; + } + p = d_permute(c, iter); + iter++; + val = d_mult_table[val * 32 + p]; + } + + return sae_pk_base32_table[d_invert(val)]; +} + + +bool sae_pk_valid_password(const char *pw) +{ + int pos; + size_t i, pw_len = os_strlen(pw); + u8 sec_1b; + u8 dtable[256]; + + os_memset(dtable, 0x80, 256); + for (i = 0; sae_pk_base32_table[i]; i++) { + dtable[(u8) sae_pk_base32_table[i]] = i; + } + + /* SAE-PK password has at least three four character components + * separated by hyphens. */ + if (pw_len < 14 || pw_len % 5 != 4) { + wpa_printf(MSG_DEBUG, "SAE-PK: Not a valid password (length)"); + return false; + } + + for (pos = 0; pw[pos]; pos++) { + if (pos && pos % 5 == 4) { + if (pw[pos] != '-') { + wpa_printf(MSG_DEBUG, + "SAE-PK: Not a valid password (separator)"); + return false; + } + continue; + } + if (dtable[(u8) pw[pos]] == 0x80) { + wpa_printf(MSG_DEBUG, + "SAE-PK: Not a valid password (character)"); + return false; + } + } + + /* Verify that the checksum character is valid */ + if (pw[pw_len - 1] != d_check_char(pw, pw_len - 1)) { + wpa_printf(MSG_DEBUG, + "SAE-PK: Not a valid password (checksum)"); + return false; + } + + /* Verify that Sec_1b bits match */ + sec_1b = dtable[(u8) pw[0]] & BIT(4); + for (i = 5; i < pw_len; i += 5) { + if (sec_1b != (dtable[(u8) pw[i]] & BIT(4))) { + wpa_printf(MSG_DEBUG, + "SAE-PK: Not a valid password (Sec_1b)"); + return false; + } + } + return true; +} + + +static char *add_char(const char *start, char *pos, u8 idx, size_t *bits) +{ + if (*bits == 0) { + return pos; + } + if (*bits > 5) { + *bits -= 5; + } else { + *bits = 0; + } + + if ((pos - start) % 5 == 4) { + *pos++ = '-'; + } + *pos++ = sae_pk_base32_table[idx]; + return pos; +} + + +/* Base32 encode a password and add hyper separators and checksum */ +char *sae_pk_base32_encode(const u8 *src, size_t len_bits) +{ + char *out, *pos; + size_t olen, extra_pad, i; + u64 block = 0; + u8 val; + size_t len = (len_bits + 7) / 8; + size_t left = len_bits; + int j; + + if (len == 0 || len >= SIZE_MAX / 8) { + return NULL; + } + olen = len * 8 / 5 + 1; + olen += olen / 4; /* hyphen separators */ + pos = out = os_zalloc(olen + 2); /* include room for ChkSum and nul */ + if (!out) { + return NULL; + } + + extra_pad = (5 - len % 5) % 5; + for (i = 0; i < len + extra_pad; i++) { + val = i < len ? src[i] : 0; + block <<= 8; + block |= val; + if (i % 5 == 4) { + for (j = 7; j >= 0; j--) + pos = add_char(out, pos, + (block >> j * 5) & 0x1f, &left); + block = 0; + } + } + + *pos = d_check_char(out, os_strlen(out)); + + return out; +} + + +u8 *sae_pk_base32_decode(const char *src, size_t len, size_t *out_len) +{ + u8 dtable[256], *out, *pos, tmp; + u64 block = 0; + size_t i, count, olen; + int pad = 0; + size_t extra_pad; + + os_memset(dtable, 0x80, 256); + for (i = 0; sae_pk_base32_table[i]; i++) { + dtable[(u8) sae_pk_base32_table[i]] = i; + } + dtable['='] = 0; + + count = 0; + for (i = 0; i < len; i++) { + if (dtable[(u8) src[i]] != 0x80) { + count++; + } + } + + if (count == 0) { + return NULL; + } + extra_pad = (8 - count % 8) % 8; + + olen = (count + extra_pad) / 8 * 5; + pos = out = os_malloc(olen); + if (!out) { + return NULL; + } + + count = 0; + for (i = 0; i < len + extra_pad; i++) { + u8 val; + + if (i >= len) { + val = '='; + } else { + val = src[i]; + } + tmp = dtable[val]; + if (tmp == 0x80) { + continue; + } + + if (val == '=') { + pad++; + } + block <<= 5; + block |= tmp; + count++; + if (count == 8) { + *pos++ = (block >> 32) & 0xff; + *pos++ = (block >> 24) & 0xff; + *pos++ = (block >> 16) & 0xff; + *pos++ = (block >> 8) & 0xff; + *pos++ = block & 0xff; + count = 0; + block = 0; + if (pad) { + /* Leave in all the available bits with zero + * padding to full octets from right. */ + pos -= pad * 5 / 8; + break; + } + } + } + + *out_len = pos - out; + return out; +} + + +u32 sae_pk_get_be19(const u8 *buf) +{ + return (buf[0] << 11) | (buf[1] << 3) | (buf[2] >> 5); +} + + +/* shift left by two octets and three bits; fill in zeros from right; + * len must be at least three */ +void sae_pk_buf_shift_left_19(u8 *buf, size_t len) +{ + u8 *dst, *src, *end; + + dst = buf; + src = buf + 2; + end = buf + len; + + while (src + 1 < end) { + *dst++ = (src[0] << 3) | (src[1] >> 5); + src++; + } + *dst++ = *src << 3; + *dst++ = 0; + *dst++ = 0; +} + + +static void sae_pk_buf_shift_left_1(u8 *buf, size_t len) +{ + u8 *dst, *src, *end; + + dst = buf; + src = buf; + end = buf + len; + + while (src + 1 < end) { + *dst++ = (src[0] << 1) | (src[1] >> 7); + src++; + } + *dst++ = *src << 1; +} + + +int sae_pk_set_password(struct sae_data *sae, const char *password) +{ + struct sae_temporary_data *tmp = sae->tmp; + size_t len, pw_len; + u8 *pw, *pos; + int bits; + u32 val = 0, val19; + unsigned int val_bits = 0; + + if (!tmp) { + return -1; + } + + os_memset(tmp->fingerprint, 0, sizeof(tmp->fingerprint)); + tmp->fingerprint_bytes = tmp->fingerprint_bits = 0; + + len = os_strlen(password); + if (len < 1 || !sae_pk_valid_password(password)) { + return -1; + } + + pw = sae_pk_base32_decode(password, len, &pw_len); + if (!pw) { + return -1; + } + + tmp->sec = (pw[0] & BIT(7)) ? 3 : 5; + tmp->lambda = len - len / 5; + tmp->fingerprint_bits = 8 * tmp->sec + 19 * tmp->lambda / 4 - 5; + wpa_printf(MSG_DEBUG, "SAE-PK: Sec=%u Lambda=%zu fingerprint_bits=%zu", + tmp->sec, tmp->lambda, tmp->fingerprint_bits); + + /* Construct Fingerprint from PasswordBase by prefixing with Sec zero + * octets and skipping the Sec_1b bits */ + pos = &tmp->fingerprint[tmp->sec]; + bits = tmp->fingerprint_bits - 8 * tmp->sec; + wpa_hexdump_key(MSG_DEBUG, "SAE-PK: PasswordBase", pw, pw_len); + while (bits > 0) { + if (val_bits < 8) { + sae_pk_buf_shift_left_1(pw, pw_len); /* Sec_1b */ + val19 = sae_pk_get_be19(pw); + sae_pk_buf_shift_left_19(pw, pw_len); + val = (val << 19) | val19; + val_bits += 19; + } + if (val_bits >= 8) { + if (bits < 8) { + break; + } + *pos++ = (val >> (val_bits - 8)) & 0xff; + val_bits -= 8; + bits -= 8; + } + } + if (bits > 0) { + val >>= val_bits - bits; + *pos++ = val << (8 - bits); + } + tmp->fingerprint_bytes = pos - tmp->fingerprint; + wpa_hexdump_key(MSG_DEBUG, "SAE-PK: Fingerprint", + tmp->fingerprint, tmp->fingerprint_bytes); + bin_clear_free(pw, pw_len); + return 0; +} + + +static size_t sae_group_2_hash_len(int group) +{ + switch (group) { + case 19: + return 32; + case 20: + return 48; + case 21: + return 64; + } + + return 0; +} + + +int sae_hash(size_t hash_len, const u8 *data, size_t len, u8 *hash) +{ + if (hash_len == 32) { + return sha256_vector(1, &data, &len, hash); + } +#ifdef CONFIG_SHA384 + if (hash_len == 48) { + return sha384_vector(1, &data, &len, hash); + } +#endif /* CONFIG_SHA384 */ +#ifdef CONFIG_SHA512 + if (hash_len == 64) { + return sha512_vector(1, &data, &len, hash); + } +#endif /* CONFIG_SHA512 */ + return -1; +} + + +static int sae_pk_hash_sig_data(struct sae_data *sae, size_t hash_len, + bool ap, const u8 *m, size_t m_len, + const u8 *pubkey, size_t pubkey_len, u8 *hash) +{ + struct sae_temporary_data *tmp = sae->tmp; + struct wpabuf *sig_data; + u8 *pos; + int ret = -1; + + /* Signed data for KeyAuth: eleAP || eleSTA || scaAP || scaSTA || + * M || K_AP || AP-BSSID || STA-MAC */ + sig_data = wpabuf_alloc(tmp->prime_len * 6 + m_len + pubkey_len + + 2 * ETH_ALEN); + if (!sig_data) { + goto fail; + } + pos = wpabuf_put(sig_data, 2 * tmp->prime_len); + if (crypto_ec_point_to_bin(tmp->ec, ap ? tmp->own_commit_element_ecc : + tmp->peer_commit_element_ecc, + pos, pos + tmp->prime_len) < 0) { + goto fail; + } + pos = wpabuf_put(sig_data, 2 * tmp->prime_len); + if (crypto_ec_point_to_bin(tmp->ec, ap ? tmp->peer_commit_element_ecc : + tmp->own_commit_element_ecc, + pos, pos + tmp->prime_len) < 0) { + goto fail; + } + if (crypto_bignum_to_bin(ap ? tmp->own_commit_scalar : + sae->peer_commit_scalar, + wpabuf_put(sig_data, tmp->prime_len), + tmp->prime_len, tmp->prime_len) < 0 || + crypto_bignum_to_bin(ap ? sae->peer_commit_scalar : + tmp->own_commit_scalar, + wpabuf_put(sig_data, tmp->prime_len), + tmp->prime_len, tmp->prime_len) < 0) { + goto fail; + } + wpabuf_put_data(sig_data, m, m_len); + wpabuf_put_data(sig_data, pubkey, pubkey_len); + wpabuf_put_data(sig_data, ap ? tmp->own_addr : tmp->peer_addr, + ETH_ALEN); + wpabuf_put_data(sig_data, ap ? tmp->peer_addr : tmp->own_addr, + ETH_ALEN); + wpa_hexdump_buf_key(MSG_DEBUG, "SAE-PK: Data to be signed for KeyAuth", + sig_data); + if (sae_hash(hash_len, wpabuf_head(sig_data), wpabuf_len(sig_data), + hash) < 0) { + goto fail; + } + wpa_hexdump(MSG_DEBUG, "SAE-PK: hash(data to be signed)", + hash, hash_len); + ret = 0; +fail: + wpabuf_free(sig_data); + return ret; +} + + +static bool sae_pk_valid_fingerprint(struct sae_data *sae, + const u8 *m, size_t m_len, + const u8 *k_ap, size_t k_ap_len, int group) +{ + struct sae_temporary_data *tmp = sae->tmp; + u8 *hash_data, *pos; + size_t hash_len, hash_data_len; + u8 hash[SAE_MAX_HASH_LEN]; + int res; + + if (!tmp->fingerprint_bytes) { + wpa_printf(MSG_DEBUG, + "SAE-PK: No PW available for K_AP fingerprint check"); + return false; + } + + /* Fingerprint = L(Hash(SSID || M || K_AP), 0, 8*Sec + 19*Lambda/4 - 5) + */ + + hash_len = sae_group_2_hash_len(group); + hash_data_len = tmp->ssid_len + m_len + k_ap_len; + hash_data = os_malloc(hash_data_len); + if (!hash_data) { + return false; + } + pos = hash_data; + os_memcpy(pos, tmp->ssid, tmp->ssid_len); + pos += tmp->ssid_len; + os_memcpy(pos, m, m_len); + pos += m_len; + os_memcpy(pos, k_ap, k_ap_len); + + wpa_hexdump_key(MSG_DEBUG, "SAE-PK: SSID || M || K_AP", + hash_data, hash_data_len); + res = sae_hash(hash_len, hash_data, hash_data_len, hash); + bin_clear_free(hash_data, hash_data_len); + if (res < 0) { + return false; + } + wpa_hexdump(MSG_DEBUG, "SAE-PK: Hash(SSID || M || K_AP)", + hash, hash_len); + + if (tmp->fingerprint_bits > hash_len * 8) { + wpa_printf(MSG_INFO, + "SAE-PK: Not enough hash output bits for the fingerprint"); + return false; + } + if (tmp->fingerprint_bits % 8) { + size_t extra; + + /* Zero out the extra bits in the last octet */ + extra = 8 - tmp->fingerprint_bits % 8; + pos = &hash[tmp->fingerprint_bits / 8]; + *pos = (*pos >> extra) << extra; + } + wpa_hexdump(MSG_DEBUG, "SAE-PK: Fingerprint", hash, + tmp->fingerprint_bytes); + res = os_memcmp_const(hash, tmp->fingerprint, tmp->fingerprint_bytes); + if (res) { + wpa_printf(MSG_DEBUG, "SAE-PK: K_AP fingerprint mismatch"); + wpa_hexdump(MSG_DEBUG, "SAE-PK: Expected fingerprint", + tmp->fingerprint, tmp->fingerprint_bytes); + return false; + } + + wpa_printf(MSG_DEBUG, "SAE-PK: Valid K_AP fingerprint"); + return true; +} + + +int sae_check_confirm_pk(struct sae_data *sae, const u8 *ies, size_t ies_len) +{ + struct sae_temporary_data *tmp = sae->tmp; + const u8 *k_ap; + u8 m[SAE_PK_M_LEN]; + size_t k_ap_len; + struct crypto_ec_key *key; + int res; + u8 hash[SAE_MAX_HASH_LEN]; + size_t hash_len; + int group; + struct wpa_supplicant *wpa_s = &g_wpa_supp; + struct sae_pk_elems elems; + + if (!tmp) { + return -1; + } + if (!sae->pk || tmp->ap_pk) { + return 0; + } + + if (tmp->kek_len != 32 && tmp->kek_len != 48 && tmp->kek_len != 64) { + wpa_printf(MSG_INFO, + "SAE-PK: No KEK available for checking confirm"); + return -1; + } + + if (!tmp->ec) { + /* Only ECC groups are supported for SAE-PK in the current + * implementation. */ + wpa_printf(MSG_INFO, + "SAE-PK: SAE commit did not use an ECC group"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "SAE-PK: Received confirm IEs", ies, ies_len); + ieee802_11_parse_elems(wpa_s, ies, ies_len); + + elems = wpa_s->sae_pk_elems; + + if (!elems.fils_pk || !elems.fils_key_confirm || !elems.sae_pk) { + wpa_printf(MSG_INFO, + "SAE-PK: Not all mandatory IEs included in confirm"); + return -1; + } + + /* TODO: Fragment reassembly */ + + if (elems.sae_pk_len < SAE_PK_M_LEN + AES_BLOCK_SIZE) { + wpa_printf(MSG_INFO, + "SAE-PK: No room for EncryptedModifier in SAE-PK element"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "SAE-PK: EncryptedModifier", + elems.sae_pk, SAE_PK_M_LEN + AES_BLOCK_SIZE); + + if (aes_siv_decrypt(tmp->kek, tmp->kek_len, + elems.sae_pk, SAE_PK_M_LEN + AES_BLOCK_SIZE, + 0, NULL, NULL, m) < 0) { + wpa_printf(MSG_INFO, + "SAE-PK: Failed to decrypt EncryptedModifier"); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "SAE-PK: Modifier M", m, SAE_PK_M_LEN); + + if (elems.fils_pk[0] != 2) { + wpa_printf(MSG_INFO, "SAE-PK: Unsupported public key type %u", + elems.fils_pk[0]); + return -1; + } + k_ap_len = elems.fils_pk_len - 1; + k_ap = elems.fils_pk + 1; + wpa_hexdump(MSG_DEBUG, "SAE-PK: Received K_AP", k_ap, k_ap_len); + /* TODO: Check against the public key, if one is stored in the network + * profile */ + key = crypto_ec_key_parse_pub(k_ap, k_ap_len); + if (!key) { + wpa_printf(MSG_INFO, "SAE-PK: Failed to parse K_AP"); + return -1; + } + group = crypto_ec_key_group(key); + if (!sae_pk_valid_fingerprint(sae, m, SAE_PK_M_LEN, k_ap, k_ap_len, + group)) { + crypto_ec_key_deinit(key); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "SAE-PK: Received KeyAuth", + elems.fils_key_confirm, elems.fils_key_confirm_len); + + hash_len = sae_group_2_hash_len(group); + if (sae_pk_hash_sig_data(sae, hash_len, false, m, SAE_PK_M_LEN, + k_ap, k_ap_len, hash) < 0) { + crypto_ec_key_deinit(key); + return -1; + } + + res = crypto_ec_key_verify_signature(key, hash, hash_len, + elems.fils_key_confirm, + elems.fils_key_confirm_len); + crypto_ec_key_deinit(key); + + if (res != 1) { + wpa_printf(MSG_INFO, + "SAE-PK: Invalid or incorrect signature in KeyAuth"); + return -1; + } + + wpa_printf(MSG_DEBUG, "SAE-PK: Valid KeyAuth signature received"); + + /* TODO: Store validated public key into network profile */ + return 0; +} +#endif /* CONFIG_SAE_PK */ diff --git a/components/wpa_supplicant/src/common/wpa_supplicant_i.h b/components/wpa_supplicant/src/common/wpa_supplicant_i.h index 77907842fb6..581d1c14344 100644 --- a/components/wpa_supplicant/src/common/wpa_supplicant_i.h +++ b/components/wpa_supplicant/src/common/wpa_supplicant_i.h @@ -67,6 +67,17 @@ enum scan_trigger_reason { REASON_WNM_BSS_TRANS_REQ, }; +#ifdef CONFIG_SAE_PK +struct sae_pk_elems { + u8 *fils_pk; + u8 fils_pk_len; + u8 *fils_key_confirm; + u8 fils_key_confirm_len; + u8 *sae_pk; + u8 sae_pk_len; +}; +#endif + struct wpa_supplicant { int scanning; @@ -138,6 +149,10 @@ struct wpa_supplicant { struct beacon_rep_data beacon_rep_data; struct os_reltime beacon_rep_scan; #endif +#ifdef CONFIG_SAE_PK + struct sae_pk_elems sae_pk_elems; +#endif + }; struct non_pref_chan_s; diff --git a/components/wpa_supplicant/src/crypto/crypto.h b/components/wpa_supplicant/src/crypto/crypto.h index cae569284a3..7d7e4580208 100644 --- a/components/wpa_supplicant/src/crypto/crypto.h +++ b/components/wpa_supplicant/src/crypto/crypto.h @@ -1147,4 +1147,42 @@ 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); + +struct crypto_ec_key; + + +/** + * crypto_ec_key_parse_pub - Initialize EC key pair from SubjectPublicKeyInfo ASN.1 + * @der: DER encoding of ASN.1 SubjectPublicKeyInfo + * @der_len: Length of @der buffer + * Returns: EC key or %NULL on failure + */ +struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len); + + +/** + * crypto_ec_key_group - Get IANA group identifier for an EC key + * @key: EC key from crypto_ec_key_parse/set_pub/priv() or crypto_ec_key_gen() + * Returns: IANA group identifier and -1 on failure + */ +int crypto_ec_key_group(struct crypto_ec_key *key); + +/** + * crypto_ec_key_deinit - Free EC key + * @key: EC key from crypto_ec_key_parse_pub/priv() or crypto_ec_key_gen() + */ +void crypto_ec_key_deinit(struct crypto_ec_key *key); + +/** + * crypto_ec_key_verify_signature - Verify ECDSA signature + * @key: EC key from crypto_ec_key_parse/set_pub() or crypto_ec_key_gen() + * @data: Data to be signed + * @len: Length of @data buffer + * @sig: DER encoding of ASN.1 Ecdsa-Sig-Value + * @sig_len: Length of @sig buffer + * Returns: 1 if signature is valid, 0 if signature is invalid and -1 on failure + */ +int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data, + size_t len, const u8 *sig, size_t sig_len); + #endif /* CRYPTO_H */ diff --git a/components/wpa_supplicant/src/rsn_supp/wpa.c b/components/wpa_supplicant/src/rsn_supp/wpa.c index 878e63d706c..923276a6a1b 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa.c +++ b/components/wpa_supplicant/src/rsn_supp/wpa.c @@ -34,6 +34,7 @@ #include "common/bss.h" #include "esp_common_i.h" #include "esp_owe_i.h" +#include "common/sae.h" /** * eapol_sm_notify_eap_success - Notification of external EAP success trigger @@ -2424,6 +2425,9 @@ int wpa_set_bss(char *macddr, char * bssid, u8 pairwise_cipher, u8 group_cipher, return -1; #endif +#ifndef CONFIG_SAE_PK + esp_wifi_sta_disable_sae_pk_internal(); +#endif /* CONFIG_SAE_PK */ return 0; } @@ -2687,6 +2691,13 @@ int wpa_sm_set_ap_rsnxe(const u8 *ie, size_t len) } sm->sae_pwe = esp_wifi_sta_get_config_sae_pwe_h2e_internal(); +#ifdef CONFIG_SAE_PK + const u8 *pw = (const u8 *)esp_wifi_sta_get_prof_password_internal(); + if (esp_wifi_sta_get_config_sae_pk_internal() != WPA3_SAE_PK_MODE_DISABLED && + sae_pk_valid_password((const char*)pw)) { + sm->sae_pk = true; + } +#endif /* CONFIG_SAE_PK */ return 0; } diff --git a/components/wpa_supplicant/src/rsn_supp/wpa_i.h b/components/wpa_supplicant/src/rsn_supp/wpa_i.h index eccf1d79faf..271d8a593f0 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa_i.h +++ b/components/wpa_supplicant/src/rsn_supp/wpa_i.h @@ -55,6 +55,8 @@ struct wpa_sm { int rsn_enabled; /* Whether RSN is enabled in configuration */ int sae_pwe; /* SAE PWE generation options */ + bool sae_pk; /* whether SAE-PK is used */ + int countermeasures; /*TKIP countermeasures state flag, 1:in countermeasures state*/ u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */ diff --git a/components/wpa_supplicant/src/rsn_supp/wpa_ie.c b/components/wpa_supplicant/src/rsn_supp/wpa_ie.c index c8c6aeb624a..cb9368bef3b 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa_ie.c +++ b/components/wpa_supplicant/src/rsn_supp/wpa_ie.c @@ -302,8 +302,13 @@ int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len) size_t flen; if (wpa_key_mgmt_sae(sm->key_mgmt) && - (sm->sae_pwe == 1 || sm->sae_pwe == 2)) { + (sm->sae_pwe == 1 || sm->sae_pwe == 2 || sm->sae_pk)) { capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); +#ifdef CONFIG_SAE_PK + if (sm->sae_pk) { + capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK); + } +#endif /* CONFIG_SAE_PK */ } flen = (capab & 0xff00) ? 2 : 1;