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:
Jiang Jiang Jian 2023-02-16 23:59:39 +08:00
commit e44c7fb048
19 changed files with 1407 additions and 25 deletions

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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 */

View File

@ -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);

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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;

View File

@ -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;
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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 */

View 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 */

View File

@ -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;

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;