فهرست منبع

esp-wifi: add SAE-PK (Public Key) authentication support for station

Sarvesh Bodakhe 3 سال پیش
والد
کامیت
d2f6a3dacc

+ 7 - 0
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

+ 8 - 0
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. */

+ 1 - 1
components/esp_wifi/lib

@@ -1 +1 @@
-Subproject commit 096f13551ec3b88eb190018f2689734ef0dbf21f
+Subproject commit 1ae8090158192107c0dfbc2de20f1e6dcc0f60e2

+ 16 - 4
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()

+ 324 - 0
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 @@ cleanup:
 	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(&params, 0, sizeof(mbedtls_asn1_buf));
+	if (mbedtls_asn1_get_alg((unsigned char **)&p, end, &alg_oid, &params) != 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(&params, 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(&params, 0, sizeof(mbedtls_asn1_buf));
+	if ((ret = mbedtls_asn1_get_alg((unsigned char **)&p, end, &alg_oid, &params)) != 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(&params, 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 */

+ 8 - 8
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);

+ 7 - 5
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;
 }

+ 2 - 0
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_ */

+ 50 - 2
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;

+ 85 - 2
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;
 }
 

+ 4 - 0
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)

+ 45 - 1
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;
 }
 

+ 54 - 1
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 */

+ 724 - 0
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 */

+ 15 - 0
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;

+ 38 - 0
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 */

+ 11 - 0
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;
 }
 

+ 2 - 0
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 */

+ 6 - 1
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;