mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/sae_pk_station' into 'master'
esp-wifi: SAE-PK (Public Key) authentication support for station See merge request espressif/esp-idf!20417
This commit is contained in:
commit
e44c7fb048
@ -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
|
||||
|
@ -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 @@
|
||||
Subproject commit 096f13551ec3b88eb190018f2689734ef0dbf21f
|
||||
Subproject commit 1ae8090158192107c0dfbc2de20f1e6dcc0f60e2
|
@ -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()
|
||||
|
@ -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(¶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 */
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
components/wpa_supplicant/src/common/sae_pk.c
Normal file
724
components/wpa_supplicant/src/common/sae_pk.c
Normal file
@ -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 */
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user