Merge branch 'feat/dpp_multi_config' into 'master'

Feature: Add dpp multiconfig support

Closes WIFIBUG-1873, WIFIBUG-1852, WIFIBUG-762, and WIFIBUG-1810

See merge request espressif/esp-idf!48191
This commit is contained in:
Jiang Jiang Jian
2026-05-25 15:40:54 +08:00
19 changed files with 2783 additions and 648 deletions

View File

@@ -771,6 +771,17 @@ menu "Wi-Fi"
help
Select this option to enable WiFi Easy Connect Support.
config ESP_WIFI_DPP_MAX_CONF_OBJ
int "Maximum number of DPP configuration objects"
depends on ESP_WIFI_DPP_SUPPORT
range 1 10
default 3
help
Maximum number of configuration objects that can be received and stored
during a DPP configuration exchange. DPP v2 configurators may send
multiple configuration objects (e.g., for different SSIDs or AKMs).
Increasing this value will increase the memory usage for DPP events.
config ESP_WIFI_11R_SUPPORT
bool "Enable 802.11R (Fast Transition) Support"
default n

View File

@@ -47,11 +47,13 @@ typedef enum {
typedef enum {
WIFI_OFFCHAN_TX_CANCEL, /**< Cancel off-channel transmission */
WIFI_OFFCHAN_TX_REQ, /**< Request off-channel transmission */
WIFI_OFFCHAN_TX_CONNECTING_REQ, /**< Off-channel Tx request during connecting state; not recommended for use by public APIs */
} wifi_action_tx_t;
typedef enum {
WIFI_ROC_CANCEL, /**< Cancel remain on channel */
WIFI_ROC_REQ, /**< Request remain on channel */
WIFI_ROC_CONNECTING_REQ, /**< Remain-on-channel request during connecting state; not recommended for use by public APIs */
} wifi_roc_t;
/**
* @brief Wi-Fi country policy
@@ -1300,7 +1302,7 @@ typedef enum {
WIFI_EVENT_STA_BEACON_OFFSET_UNSTABLE, /**< Station sampled beacon offset unstable */
WIFI_EVENT_DPP_URI_READY, /**< DPP URI is ready through Bootstrapping */
WIFI_EVENT_DPP_CFG_RECVD, /**< Config received via DPP Authentication */
WIFI_EVENT_DPP_CFG_RECVD, /**< DPP Configuration Response; payload is wifi_event_dpp_config_received_t */
WIFI_EVENT_DPP_FAILED, /**< DPP failed */
WIFI_EVENT_NAN_BOOTSTRAP_INDICATION, /**< Received NAN Pairing Bootstrapping Request from a Peer */
WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED, /**< NAN Pairing Bootstrapping completed (success/failure) */
@@ -1756,14 +1758,75 @@ typedef struct {
char uri[]; /**< URI data */
} wifi_event_dpp_uri_ready_t;
/** Argument structure for WIFI_EVENT_DPP_CFG_RECVD event */
#define ESP_DPP_MAX_CONNECTOR_LEN 512
#define ESP_DPP_MAX_KEY_LEN 128
/**
* @brief One provisioned network from a DPP Configuration Response
*
* Filled by the stack for WIFI_EVENT_DPP_CFG_RECVD. A single network row may contain hybrid
* credentials (e.g., both a DPP Connector and a legacy PSK/SAE password). The application
* should pass this structure to esp_supp_dpp_set_config() to load any DPP credentials, and
* also extract the SSID/password for esp_wifi_set_config() to configure the driver for
* legacy connection and fallback.
*
* @note Memory footprint: this structure uses fixed-size buffers for the connector and keys
* (dominated by ESP_DPP_MAX_CONNECTOR_LEN and ESP_DPP_MAX_KEY_LEN). While this avoids
* dynamic allocation and keeps the structure application-friendly, it results in a
* size of nearly 1KB per entry.
*/
typedef struct {
wifi_config_t wifi_cfg; /**< Received WIFI config in DPP */
uint8_t ssid[MAX_SSID_LEN]; /**< SSID octets; valid length is @ref ssid_len */
uint8_t ssid_len; /**< SSID length in octets, 0 .. MAX_SSID_LEN */
uint8_t password[MAX_PASSPHRASE_LEN]; /**< Legacy AKM: passphrase or hex PSK octets; length @ref password_len */
uint8_t password_len; /**< Password length in octets, 0 .. MAX_PASSPHRASE_LEN; 0 if unused */
char connector[ESP_DPP_MAX_CONNECTOR_LEN]; /**< DPP Connector when akm includes DPP; NUL-terminated when @ref connector_len > 0 */
uint16_t connector_len; /**< Connector length in octets, excluding NUL; 0 if absent */
uint8_t net_access_key[ESP_DPP_MAX_KEY_LEN]; /**< Network access key when akm includes DPP */
uint16_t net_access_key_len; /**< Used length of net_access_key */
uint8_t c_sign_key[ESP_DPP_MAX_KEY_LEN]; /**< C-sign key when akm includes DPP */
uint16_t c_sign_key_len; /**< Used length of c_sign_key */
uint64_t net_access_key_expiry; /**< Optional expiry hint for net_access_key from configurator */
uint8_t curr_chan; /**< Channel from the DPP exchange for this row; used for off-channel network introduction */
uint8_t akm; /**< AKM for this row; values are esp_dpp_akm_t (stored as uint8_t) */
} esp_dpp_config_data_t;
/**
* @brief Argument structure for WIFI_EVENT_DPP_CFG_RECVD event
*
* Contains STA configurations received from the DPP Configurator. wifi_cfg is
* populated from the first configuration object for backward compatibility.
* Application should iterate through configs[] if the first connection attempt fails.
* The event data is only valid within the callback handler; applications must
* copy the configurations if they need to use them after the handler returns.
* If the first configuration object from the Configurator uses a DPP AKM and includes a connector, the supplicant
* automatically loads it into the internal DPP store for Network Introduction for backward
* compatibility. esp_supp_dpp_set_config() replaces that store when the application selects a
* row explicitly.
*
* Only the first CONFIG_ESP_WIFI_DPP_MAX_CONF_OBJ objects from the Configurator are forwarded; additional objects in the
* DPP response are ignored.
*
* Variable-length payload: base struct plus total_conf elements of configs[].
*
* @note Memory footprint: total event payload is roughly
* sizeof(wifi_event_dpp_config_received_t) + total_conf * sizeof(esp_dpp_config_data_t),
* i.e. ~1KB per forwarded configuration object. esp_event_post() deep-copies
* this payload into the event queue, so transient peak heap usage during the post is
* roughly twice the payload. Applications must copy these configurations if they
* need to use them after the event handler returns, as they will no longer be available.
* Memory-constrained applications can cap the payload by lowering
* CONFIG_ESP_WIFI_DPP_MAX_CONF_OBJ (default 3).
*/
typedef struct {
wifi_config_t wifi_cfg; /**< STA summary from the first configuration object */
uint8_t total_conf; /**< Number of esp_dpp_config_data_t entries in configs */
esp_dpp_config_data_t configs[]; /**< One row per provisioned network, configurator order */
} wifi_event_dpp_config_received_t;
/** Argument structure for WIFI_EVENT_DPP_FAIL event */
/** Argument structure for WIFI_EVENT_DPP_FAILED event */
typedef struct {
int failure_reason; /**< Failure reason */
int failure_reason; /**< esp_err_t code (e.g. ESP_ERR_DPP_FAILURE) */
} wifi_event_dpp_failed_t;
#ifdef __cplusplus

View File

@@ -750,6 +750,17 @@ config WIFI_RMT_DPP_SUPPORT
help
Select this option to enable WiFi Easy Connect Support.
config WIFI_RMT_DPP_MAX_CONF_OBJ
int "Maximum number of DPP configuration objects"
depends on WIFI_RMT_DPP_SUPPORT
range 1 10
default 3
help
Maximum number of configuration objects that can be received and stored
during a DPP configuration exchange. DPP v2 configurators may send
multiple configuration objects (e.g., for different SSIDs or AKMs).
Increasing this value will increase the memory usage for DPP events.
config WIFI_RMT_11R_SUPPORT
bool "Enable 802.11R (Fast Transition) Support"
default n

View File

@@ -396,6 +396,11 @@ if WIFI_RMT_DPP_SUPPORT
default WIFI_RMT_DPP_SUPPORT
endif
config ESP_WIFI_DPP_MAX_CONF_OBJ # ignore: multiple-definition
int
depends on WIFI_RMT_DPP_SUPPORT
default WIFI_RMT_DPP_MAX_CONF_OBJ
if WIFI_RMT_11R_SUPPORT
config ESP_WIFI_11R_SUPPORT # ignore: multiple-definition
bool

View File

@@ -47,11 +47,13 @@ typedef enum {
typedef enum {
WIFI_OFFCHAN_TX_CANCEL, /**< Cancel off-channel transmission */
WIFI_OFFCHAN_TX_REQ, /**< Request off-channel transmission */
WIFI_OFFCHAN_TX_CONNECTING_REQ, /**< Off-channel Tx request during connecting state; not recommended for use by public APIs */
} wifi_action_tx_t;
typedef enum {
WIFI_ROC_CANCEL, /**< Cancel remain on channel */
WIFI_ROC_REQ, /**< Request remain on channel */
WIFI_ROC_CONNECTING_REQ, /**< Remain-on-channel request during connecting state; not recommended for use by public APIs */
} wifi_roc_t;
/**
* @brief Wi-Fi country policy
@@ -1300,7 +1302,7 @@ typedef enum {
WIFI_EVENT_STA_BEACON_OFFSET_UNSTABLE, /**< Station sampled beacon offset unstable */
WIFI_EVENT_DPP_URI_READY, /**< DPP URI is ready through Bootstrapping */
WIFI_EVENT_DPP_CFG_RECVD, /**< Config received via DPP Authentication */
WIFI_EVENT_DPP_CFG_RECVD, /**< DPP Configuration Response; payload is wifi_event_dpp_config_received_t */
WIFI_EVENT_DPP_FAILED, /**< DPP failed */
WIFI_EVENT_NAN_BOOTSTRAP_INDICATION, /**< Received NAN Pairing Bootstrapping Request from a Peer */
WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED, /**< NAN Pairing Bootstrapping completed (success/failure) */
@@ -1756,14 +1758,75 @@ typedef struct {
char uri[]; /**< URI data */
} wifi_event_dpp_uri_ready_t;
/** Argument structure for WIFI_EVENT_DPP_CFG_RECVD event */
#define ESP_DPP_MAX_CONNECTOR_LEN 512
#define ESP_DPP_MAX_KEY_LEN 128
/**
* @brief One provisioned network from a DPP Configuration Response
*
* Filled by the stack for WIFI_EVENT_DPP_CFG_RECVD. A single network row may contain hybrid
* credentials (e.g., both a DPP Connector and a legacy PSK/SAE password). The application
* should pass this structure to esp_supp_dpp_set_config() to load any DPP credentials, and
* also extract the SSID/password for esp_wifi_set_config() to configure the driver for
* legacy connection and fallback.
*
* @note Memory footprint: this structure uses fixed-size buffers for the connector and keys
* (dominated by ESP_DPP_MAX_CONNECTOR_LEN and ESP_DPP_MAX_KEY_LEN). While this avoids
* dynamic allocation and keeps the structure application-friendly, it results in a
* size of nearly 1KB per entry.
*/
typedef struct {
wifi_config_t wifi_cfg; /**< Received WIFI config in DPP */
uint8_t ssid[MAX_SSID_LEN]; /**< SSID octets; valid length is @ref ssid_len */
uint8_t ssid_len; /**< SSID length in octets, 0 .. MAX_SSID_LEN */
uint8_t password[MAX_PASSPHRASE_LEN]; /**< Legacy AKM: passphrase or hex PSK octets; length @ref password_len */
uint8_t password_len; /**< Password length in octets, 0 .. MAX_PASSPHRASE_LEN; 0 if unused */
char connector[ESP_DPP_MAX_CONNECTOR_LEN]; /**< DPP Connector when akm includes DPP; NUL-terminated when @ref connector_len > 0 */
uint16_t connector_len; /**< Connector length in octets, excluding NUL; 0 if absent */
uint8_t net_access_key[ESP_DPP_MAX_KEY_LEN]; /**< Network access key when akm includes DPP */
uint16_t net_access_key_len; /**< Used length of net_access_key */
uint8_t c_sign_key[ESP_DPP_MAX_KEY_LEN]; /**< C-sign key when akm includes DPP */
uint16_t c_sign_key_len; /**< Used length of c_sign_key */
uint64_t net_access_key_expiry; /**< Optional expiry hint for net_access_key from configurator */
uint8_t curr_chan; /**< Channel from the DPP exchange for this row; used for off-channel network introduction */
uint8_t akm; /**< AKM for this row; values are esp_dpp_akm_t (stored as uint8_t) */
} esp_dpp_config_data_t;
/**
* @brief Argument structure for WIFI_EVENT_DPP_CFG_RECVD event
*
* Contains STA configurations received from the DPP Configurator. wifi_cfg is
* populated from the first configuration object for backward compatibility.
* Application should iterate through configs[] if the first connection attempt fails.
* The event data is only valid within the callback handler; applications must
* copy the configurations if they need to use them after the handler returns.
* If the first configuration object from the Configurator uses a DPP AKM and includes a connector, the supplicant
* automatically loads it into the internal DPP store for Network Introduction for backward
* compatibility. esp_supp_dpp_set_config() replaces that store when the application selects a
* row explicitly.
*
* Only the first CONFIG_WIFI_RMT_DPP_MAX_CONF_OBJ objects from the Configurator are forwarded; additional objects in the
* DPP response are ignored.
*
* Variable-length payload: base struct plus total_conf elements of configs[].
*
* @note Memory footprint: total event payload is roughly
* sizeof(wifi_event_dpp_config_received_t) + total_conf * sizeof(esp_dpp_config_data_t),
* i.e. ~1KB per forwarded configuration object. esp_event_post() deep-copies
* this payload into the event queue, so transient peak heap usage during the post is
* roughly twice the payload. Applications must copy these configurations if they
* need to use them after the event handler returns, as they will no longer be available.
* Memory-constrained applications can cap the payload by lowering
* CONFIG_WIFI_RMT_DPP_MAX_CONF_OBJ (default 3).
*/
typedef struct {
wifi_config_t wifi_cfg; /**< STA summary from the first configuration object */
uint8_t total_conf; /**< Number of esp_dpp_config_data_t entries in configs */
esp_dpp_config_data_t configs[]; /**< One row per provisioned network, configurator order */
} wifi_event_dpp_config_received_t;
/** Argument structure for WIFI_EVENT_DPP_FAIL event */
/** Argument structure for WIFI_EVENT_DPP_FAILED event */
typedef struct {
int failure_reason; /**< Failure reason */
int failure_reason; /**< esp_err_t code (e.g. ESP_ERR_DPP_FAILURE) */
} wifi_event_dpp_failed_t;
#ifdef __cplusplus

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -18,6 +18,14 @@ extern "C" {
#define ESP_DPP_MAX_CHAN_COUNT 5
#ifndef ESP_DPP_MAX_CONFIG_COUNT
#ifdef CONFIG_ESP_WIFI_DPP_MAX_CONF_OBJ
#define ESP_DPP_MAX_CONFIG_COUNT CONFIG_ESP_WIFI_DPP_MAX_CONF_OBJ
#else
#define ESP_DPP_MAX_CONFIG_COUNT 3
#endif
#endif
#define ESP_ERR_DPP_FAILURE (ESP_ERR_WIFI_BASE + 151) /*!< Generic failure during DPP Operation */
#define ESP_ERR_DPP_TX_FAILURE (ESP_ERR_WIFI_BASE + 152) /*!< DPP Frame Tx failed OR not Acked */
#define ESP_ERR_DPP_INVALID_ATTR (ESP_ERR_WIFI_BASE + 153) /*!< Encountered invalid DPP Attribute */
@@ -25,6 +33,22 @@ extern "C" {
#define ESP_ERR_DPP_INVALID_LIST (ESP_ERR_WIFI_BASE + 155) /*!< Channel list given in esp_supp_dpp_bootstrap_gen() is not valid or too big */
#define ESP_ERR_DPP_CONF_TIMEOUT (ESP_ERR_WIFI_BASE + 156) /*!< DPP Configuration was not received in time */
/**
* @brief AKM values for one DPP configuration row (which credentials apply and how to connect).
*
* Use this with fields in esp_dpp_config_data_t: legacy modes use password,
* DPP modes use connector and network access key via esp_supp_dpp_set_config().
*/
typedef enum {
ESP_DPP_AKM_UNKNOWN = 0, /**< Not set or unrecognized */
ESP_DPP_AKM_DPP = 1, /**< DPP-only: connector and network access key */
ESP_DPP_AKM_PSK = 2, /**< WPA2-PSK: passphrase in password */
ESP_DPP_AKM_SAE = 3, /**< WPA3-SAE: passphrase in password */
ESP_DPP_AKM_PSK_SAE = 4, /**< WPA2/WPA3 transition: passphrase in password */
ESP_DPP_AKM_SAE_DPP = 5, /**< SAE plus DPP: passphrase and DPP credentials */
ESP_DPP_AKM_PSK_SAE_DPP = 6, /**< WPA2/WPA3 plus DPP: passphrase and DPP credentials */
} esp_dpp_akm_t;
/** @brief Types of Bootstrap Methods for DPP. */
typedef enum dpp_bootstrap_type {
DPP_BOOTSTRAP_QR_CODE, /**< QR Code Method */
@@ -59,13 +83,14 @@ esp_err_t esp_supp_dpp_deinit(void);
* Generates Out Of Band Bootstrap information as an Enrollee which can be
* used by a DPP Configurator to provision the Enrollee.
*
* @param chan_list List of channels device will be available on for listening
* @param chan_list List of channels device will be available on for listening (must not be NULL)
* @param type Bootstrap method type, only QR Code method is supported for now.
* @param key (Optional) 32 byte Raw Private Key for generating a Bootstrapping Public Key
* @param info (Optional) Ancillary Device Information like Serial Number
*
* @return
* - ESP_OK: Success
* - ESP_ERR_INVALID_ARG: chan_list is NULL
* - ESP_ERR_DPP_INVALID_LIST: Channel list not valid
* - ESP_FAIL: Failure
*/
@@ -97,6 +122,30 @@ esp_err_t esp_supp_dpp_start_listen(void);
*/
esp_err_t esp_supp_dpp_stop_listen(void);
/**
* @brief Install or clear DPP AKM connector material in the supplicant.
*
* This function copies the given DPP AKM row (connector and related keys) into the
* supplicant; only one such row is retained, and a new row replaces the previous one.
* Pass NULL to clear the stored row. (If the first configuration object in the
* Configuration Response uses a DPP AKM with a connector, the stack may load it into the
* same store before the event is delivered; NULL clears that selection too.)
*
* Connection retry/fallback policy is application-owned. Typical sequence per selected
* row is: pick one row from WIFI_EVENT_DPP_CFG_RECVD, call esp_wifi_set_config(),
* call esp_supp_dpp_set_config() using connector values received from
* WIFI_EVENT_DPP_CFG_RECVD, call esp_wifi_connect().
*
* @param config Pointer to one DPP AKM row from WIFI_EVENT_DPP_CFG_RECVD, or NULL to clear.
*
* @return
* - ESP_OK: Success
* - ESP_ERR_INVALID_ARG: @a config is not a DPP AKM row, or key lengths are invalid
* - ESP_ERR_INVALID_STATE: DPP supplicant not initialized
* - ESP_ERR_NO_MEM: Allocation failed while storing the row
*/
esp_err_t esp_supp_dpp_set_config(const esp_dpp_config_data_t *config);
#ifdef __cplusplus
}
#endif

View File

@@ -1529,6 +1529,56 @@ static psa_ecc_family_t group_id_to_psa(mbedtls_ecp_group_id grp_id, size_t *bit
}
}
static mbedtls_ecp_group_id psa_to_group_id(psa_ecc_family_t family, size_t bits)
{
switch (family) {
case PSA_ECC_FAMILY_SECP_R1:
switch (bits) {
case 256:
return MBEDTLS_ECP_DP_SECP256R1;
case 384:
return MBEDTLS_ECP_DP_SECP384R1;
case 521:
return MBEDTLS_ECP_DP_SECP521R1;
default:
break;
}
break;
case PSA_ECC_FAMILY_BRAINPOOL_P_R1:
switch (bits) {
case 256:
return MBEDTLS_ECP_DP_BP256R1;
case 384:
return MBEDTLS_ECP_DP_BP384R1;
case 512:
return MBEDTLS_ECP_DP_BP512R1;
default:
break;
}
break;
case PSA_ECC_FAMILY_MONTGOMERY:
if (bits == 255) {
return MBEDTLS_ECP_DP_CURVE25519;
}
if (bits == 448) {
return MBEDTLS_ECP_DP_CURVE448;
}
break;
case PSA_ECC_FAMILY_SECP_K1:
switch (bits) {
case 256:
return MBEDTLS_ECP_DP_SECP256K1;
default:
break;
}
break;
default:
break;
}
return MBEDTLS_ECP_DP_NONE;
}
static size_t crypto_ecdh_output_size(const crypto_ec_key_wrapper_t *wrapper)
{
size_t key_bits = 0;
@@ -1546,46 +1596,6 @@ static size_t crypto_ecdh_output_size(const crypto_ec_key_wrapper_t *wrapper)
return PSA_BITS_TO_BYTES(key_bits);
}
/* Reverse mapping: PSA ECC family + bit size to mbedtls group ID */
static mbedtls_ecp_group_id psa_to_group_id(psa_ecc_family_t family, size_t bits)
{
switch (family) {
case PSA_ECC_FAMILY_SECP_R1:
if (bits == 256) {
return MBEDTLS_ECP_DP_SECP256R1;
} else if (bits == 384) {
return MBEDTLS_ECP_DP_SECP384R1;
} else if (bits == 521) {
return MBEDTLS_ECP_DP_SECP521R1;
}
break;
case PSA_ECC_FAMILY_BRAINPOOL_P_R1:
if (bits == 256) {
return MBEDTLS_ECP_DP_BP256R1;
} else if (bits == 384) {
return MBEDTLS_ECP_DP_BP384R1;
} else if (bits == 512) {
return MBEDTLS_ECP_DP_BP512R1;
}
break;
case PSA_ECC_FAMILY_MONTGOMERY:
if (bits == 255) {
return MBEDTLS_ECP_DP_CURVE25519;
} else if (bits == 448) {
return MBEDTLS_ECP_DP_CURVE448;
}
break;
case PSA_ECC_FAMILY_SECP_K1:
if (bits == 256) {
return MBEDTLS_ECP_DP_SECP256K1;
}
break;
default:
break;
}
return MBEDTLS_ECP_DP_NONE;
}
struct crypto_ec_key * crypto_ec_key_set_pub(const struct crypto_ec_group *group,
const u8 *buf, size_t len)
{
@@ -1640,13 +1650,7 @@ struct crypto_ec_key * crypto_ec_key_set_pub(const struct crypto_ec_group *group
os_memcpy(key_buf, buf, len);
key_len = len;
// For uncompressed: key_bits = (len - 1) * 4
// For compressed: key_bits = (len - 1) * 8
if (buf[0] == 0x04) {
key_bits = (len - 1) * 4;
} else {
key_bits = (len - 1) * 8;
}
key_bits = bits;
} else if ((len & 1) == 0) {
// Raw X||Y format (even length, no prefix) - prepend 0x04
key_buf = os_calloc(1, len + 1);
@@ -1659,8 +1663,7 @@ struct crypto_ec_key * crypto_ec_key_set_pub(const struct crypto_ec_group *group
key_buf[0] = 0x04;
os_memcpy(key_buf + 1, buf, len);
key_len = len + 1;
// key_bits = len * 4 (since len = 2 * coordinate_size)
key_bits = len * 4;
key_bits = bits;
} else {
// Odd length without format prefix - invalid format
wpa_printf(MSG_ERROR, "Invalid public key format: odd length without prefix");
@@ -1677,11 +1680,16 @@ struct crypto_ec_key * crypto_ec_key_set_pub(const struct crypto_ec_group *group
}
os_memcpy(key_buf, buf, len);
key_len = len;
key_bits = len * 8;
key_bits = bits;
}
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY_HASH | PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_DERIVE);
psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
if (ecc_family == PSA_ECC_FAMILY_MONTGOMERY) {
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_DERIVE);
psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDH);
} else {
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY_HASH | PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_DERIVE);
psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
}
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_ECC_PUBLIC_KEY(ecc_family));
psa_set_key_bits(&key_attributes, key_bits);
@@ -2399,6 +2407,9 @@ int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *csign,
const u8 *s, size_t s_len)
{
crypto_ec_key_wrapper_t *wrapper = (crypto_ec_key_wrapper_t *)csign;
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_algorithm_t policy_alg;
psa_algorithm_t verify_alg;
if (!wrapper) {
return -1;
}
@@ -2411,7 +2422,70 @@ int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *csign,
os_memcpy(sig, r, r_len);
os_memcpy(sig + r_len, s, s_len);
psa_status_t status = psa_verify_hash(wrapper->key_id, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), hash, hlen, sig, r_len + s_len);
if (psa_get_key_attributes(wrapper->key_id, &key_attributes) != PSA_SUCCESS) {
psa_reset_key_attributes(&key_attributes);
wpa_printf(MSG_ERROR, "psa_get_key_attributes failed for verify key");
os_free(sig);
return -1;
}
policy_alg = psa_get_key_algorithm(&key_attributes);
psa_reset_key_attributes(&key_attributes);
if (policy_alg == PSA_ALG_ECDSA_ANY) {
verify_alg = PSA_ALG_ECDSA_ANY;
} else {
psa_algorithm_t verify_hash_alg;
psa_algorithm_t policy_hash_alg;
bool policy_deterministic;
switch (hlen) {
case 20:
verify_hash_alg = PSA_ALG_SHA_1;
break;
case 28:
verify_hash_alg = PSA_ALG_SHA_224;
break;
case 32:
verify_hash_alg = PSA_ALG_SHA_256;
break;
case 48:
verify_hash_alg = PSA_ALG_SHA_384;
break;
case 64:
verify_hash_alg = PSA_ALG_SHA_512;
break;
default:
wpa_printf(MSG_ERROR, "Unsupported ECDSA hash length: %d", hlen);
os_free(sig);
return -1;
}
if (!PSA_ALG_IS_RANDOMIZED_ECDSA(policy_alg) &&
!PSA_ALG_IS_DETERMINISTIC_ECDSA(policy_alg)) {
wpa_printf(MSG_ERROR, "Unsupported ECDSA verify policy alg: 0x%x",
(unsigned int) policy_alg);
os_free(sig);
return -1;
}
policy_hash_alg = PSA_ALG_SIGN_GET_HASH(policy_alg);
if (policy_hash_alg != 0 && policy_hash_alg != PSA_ALG_ANY_HASH &&
policy_hash_alg != verify_hash_alg) {
wpa_printf(MSG_ERROR, "ECDSA hash mismatch policy=0x%x verify=0x%x",
(unsigned int) policy_hash_alg, (unsigned int) verify_hash_alg);
os_free(sig);
return -1;
}
policy_deterministic = PSA_ALG_IS_DETERMINISTIC_ECDSA(policy_alg);
verify_alg = policy_deterministic ?
PSA_ALG_DETERMINISTIC_ECDSA(verify_hash_alg) :
PSA_ALG_ECDSA(verify_hash_alg);
}
psa_status_t status = psa_verify_hash(wrapper->key_id,
verify_alg,
hash, hlen, sig, r_len + s_len);
if (status != PSA_SUCCESS) {
wpa_printf(MSG_ERROR, "psa_verify_hash failed with %d", (int) status);
os_free(sig);
@@ -2518,6 +2592,7 @@ struct crypto_ec_key *crypto_ec_parse_subpub_key(const unsigned char *p, size_t
// Configure attributes for import
psa_set_key_usage_flags(&attributes,
PSA_KEY_USAGE_VERIFY_HASH | PSA_KEY_USAGE_VERIFY_MESSAGE | PSA_KEY_USAGE_EXPORT);
/* C-sign public keys: ECDSA-SHA256 policy for DPP signature verify */
psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
// Import directly from PK context into PSA
@@ -3253,21 +3328,53 @@ int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
}
size_t key_bits = psa_get_key_bits(&key_attributes);
psa_algorithm_t policy_alg = psa_get_key_algorithm(&key_attributes);
psa_reset_key_attributes(&key_attributes);
/* Determine hash algorithm from data length */
/* Determine hash algorithm from data length and enforce key policy. */
psa_algorithm_t verify_alg;
if (len == 32) {
verify_alg = PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256);
} else if (len == 48) {
verify_alg = PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_384);
} else if (len == 64) {
verify_alg = PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_512);
} else if (len == 20) {
verify_alg = PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_1);
if (policy_alg == PSA_ALG_ECDSA_ANY) {
verify_alg = PSA_ALG_ECDSA_ANY;
} else {
wpa_printf(MSG_ERROR, "crypto_ec_key_verify_signature: Unsupported hash length %d", (int)len);
return -1;
psa_algorithm_t verify_hash_alg;
psa_algorithm_t policy_hash_alg;
bool policy_deterministic;
if (len == 32) {
verify_hash_alg = PSA_ALG_SHA_256;
} else if (len == 28) {
verify_hash_alg = PSA_ALG_SHA_224;
} else if (len == 48) {
verify_hash_alg = PSA_ALG_SHA_384;
} else if (len == 64) {
verify_hash_alg = PSA_ALG_SHA_512;
} else if (len == 20) {
verify_hash_alg = PSA_ALG_SHA_1;
} else {
wpa_printf(MSG_ERROR, "crypto_ec_key_verify_signature: Unsupported hash length %d", (int)len);
return -1;
}
if (!PSA_ALG_IS_RANDOMIZED_ECDSA(policy_alg) &&
!PSA_ALG_IS_DETERMINISTIC_ECDSA(policy_alg)) {
wpa_printf(MSG_ERROR, "crypto_ec_key_verify_signature: Unsupported policy alg 0x%x",
(unsigned int)policy_alg);
return -1;
}
policy_hash_alg = PSA_ALG_SIGN_GET_HASH(policy_alg);
if (policy_hash_alg != 0 && policy_hash_alg != PSA_ALG_ANY_HASH &&
policy_hash_alg != verify_hash_alg) {
wpa_printf(MSG_ERROR, "crypto_ec_key_verify_signature: hash mismatch policy=0x%x verify=0x%x",
(unsigned int)policy_hash_alg, (unsigned int)verify_hash_alg);
return -1;
}
policy_deterministic = PSA_ALG_IS_DETERMINISTIC_ECDSA(policy_alg);
verify_alg = policy_deterministic ?
PSA_ALG_DETERMINISTIC_ECDSA(verify_hash_alg) :
PSA_ALG_ECDSA(verify_hash_alg);
}
/* Convert DER-encoded signature to raw format (r||s) for PSA */

View File

@@ -18,6 +18,7 @@
#include "esp_scan_i.h"
#include "esp_common_i.h"
#include "common/ieee802_11_common.h"
#include "esp_dpp_i.h"
#include "esp_rrm.h"
#include "esp_wnm.h"
#include "rsn_supp/wpa_i.h"
@@ -297,6 +298,13 @@ int esp_supplicant_common_init(struct wpa_funcs *wpa_cb)
dl_list_init(&wpa_s->bss_tmp_disallowed);
#endif /* CONFIG_MBO */
#endif /* defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) */
#ifdef CONFIG_DPP
if (esp_supp_dpp_common_init() != ESP_OK) {
ret = -1;
goto err;
}
#endif
return 0;
err:
esp_supplicant_common_deinit();

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -12,11 +12,11 @@
#include "utils/common.h"
#include "common/dpp.h"
#include "esp_dpp.h"
#include "esp_wifi_driver.h"
#define ESP_DPP_AUTH_TIMEOUT_SECS 2
#define ESP_GAS_TIMEOUT_SECS 2
#define ESP_DPP_PMK_CACHE_DEFAULT_TIMEOUT 86400 * 7 /*!< 7 days */
#define ESP_DPP_GAS_REASSEMBLY_MAX_LEN 4096
#define ESP_DPP_PMK_CACHE_DEFAULT_TIMEOUT (86400 * 7) /*!< 7 days */
#define BOOTSTRAP_ROC_WAIT_TIME 500
#define OFFCHAN_TX_WAIT_TIME 600
@@ -26,19 +26,40 @@ struct dpp_bootstrap_params_t {
uint8_t chan_list[14];
uint8_t num_chan;
uint8_t mac[6];
uint32_t info_len;
char *info;
};
enum dpp_tx_frame_type {
DPP_TX_INVALID = 0,
DPP_TX_AUTHENTICATION_RESP,
DPP_TX_AUTHENTICATION_CONF,
DPP_TX_PEER_DISCOVERY_REQ,
DPP_TX_GAS_CONFIG_REQ,
DPP_TX_GAS_COMEBACK,
};
struct esp_dpp_context_t {
struct dpp_bootstrap_params_t bootstrap_params;
struct dpp_authentication *dpp_auth;
int gas_dialog_token;
struct dpp_global *dpp_global;
wifi_config_t wifi_cfg;
int id;
bool dpp_init_done;
bool dpp_deinit_pending; /* True while deinit is queued so init is rejected until it finishes */
bool bootstrap_done;
bool dpp_listen_ongoing;
struct dpp_config_store *dpp_config_store;
/* Fragmented GAS Configuration Response */
struct wpabuf *gas_resp_buf; /* Reassembled query response data */
uint8_t gas_frag_id; /* Expected next fragment id */
bool gas_wait_comeback; /* Waiting for GAS Comeback Response */
/* Retry counter for GAS query and peer discovery TX */
unsigned int gas_query_tries;
/* Last off-channel TX for status matching */
struct {
uint8_t op_id;
enum dpp_tx_frame_type type;
} pending_tx_op;
bool pending_tx_op_in_progress;
unsigned int listen_chan_idx;
};
#ifdef CONFIG_TESTING_OPTIONS
@@ -46,12 +67,17 @@ int dpp_test_gen_invalid_key(struct wpabuf *msg, const struct dpp_curve_params *
char * dpp_corrupt_connector_signature(const char *connector);
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_ESP_WIFI_DPP_SUPPORT
#ifdef CONFIG_DPP
bool is_dpp_enabled(void);
esp_err_t esp_supp_dpp_common_init(void);
#else
static inline bool is_dpp_enabled(void)
{
return false;
}
static inline esp_err_t esp_supp_dpp_common_init(void)
{
return ESP_OK;
}
#endif
#endif /* ESP_DPP_I_H */

View File

@@ -220,7 +220,7 @@ int dpp_connect(uint8_t *bssid, bool pdr_done)
int res = 0;
if (!pdr_done) {
if (esp_wifi_sta_get_prof_authmode_internal() == WPA3_AUTH_DPP) {
esp_dpp_start_net_intro_protocol(bssid);
res = esp_dpp_start_net_intro_protocol(bssid);
}
} else {
res = wpa_config_bss(bssid);
@@ -555,6 +555,11 @@ int esp_supplicant_init(void)
ret = esp_supplicant_common_init(wpa_cb);
if (ret != 0) {
/* esp_wifi_init() propagates this error to wifi_deinit_internal() which calls
* esp_supplicant_deinit(); that path runs eloop_destroy() for eloop cleanup.
*/
os_free(wpa_cb);
wpa_cb = NULL;
return ret;
}
@@ -572,6 +577,7 @@ int esp_supplicant_deinit(void)
esp_supplicant_common_deinit();
esp_supplicant_unset_all_appie();
eloop_destroy();
/* wpa_cb is freed by esp_wifi_unregister_wpa_cb_internal() */
wpa_cb = NULL;
#if CONFIG_ESP_WIFI_WAPI_PSK
esp_wifi_internal_wapi_deinit();

View File

@@ -353,7 +353,7 @@ extern const wifi_osi_funcs_t *wifi_funcs;
#define os_mutex_lock(a) wifi_funcs->_mutex_lock((a))
#define os_mutex_unlock(a) wifi_funcs->_mutex_unlock((a))
#define os_recursive_mutex_create() wifi_funcs->_recursive_mutex_create()
#define os_mutex_create() wifi_funcs->_mutex_create();
#define os_mutex_create() wifi_funcs->_mutex_create()
#define os_mutex_delete(a) wifi_funcs->_mutex_delete(a)
#define os_queue_create(a, b) wifi_funcs->_queue_create((a), (b))

View File

@@ -98,17 +98,9 @@ struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size)
size);
}
void dpp_debug_print_point(const char *title, struct crypto_ec *e,
const struct crypto_ec_point *point)
struct wpabuf * gas_build_comeback_req(u8 dialog_token)
{
u8 x[64], y[64];
if (crypto_ec_point_to_bin(e, point, x, y) < 0) {
wpa_printf(MSG_ERROR, "Failed to get coordinates");
return;
}
wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x, y);
return gas_build_req(WLAN_PA_GAS_COMEBACK_REQ, dialog_token, 0);
}
static void dpp_auth_fail(struct dpp_authentication *auth, const char *txt)
@@ -495,7 +487,7 @@ static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
/* Build DPP Authentication Request frame attributes */
attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) +
4 + sizeof(wrapped_data);
4 + sizeof(wrapped_data) + 5;
if (neg_freq > 0)
attr_len += 4 + 2;
#ifdef CONFIG_TESTING_OPTIONS
@@ -532,6 +524,11 @@ static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
wpabuf_put_u8(msg, channel);
}
/* Protocol version: advertise v2 */
wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
wpabuf_put_le16(msg, 1);
wpabuf_put_u8(msg, 2);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
@@ -658,6 +655,9 @@ static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
/* Build DPP Authentication Response frame attributes */
attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data);
/* Protocol Version attribute is added below when peer_version >= 2 */
if (auth->peer_version >= 2)
attr_len += 5;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
attr_len += 5;
@@ -685,6 +685,13 @@ static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
wpabuf_put_buf(msg, pr);
}
/* Protocol version for v2 peer */
if (auth->peer_version >= 2) {
wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
wpabuf_put_le16(msg, 1);
wpabuf_put_u8(msg, 2);
}
attr_end = wpabuf_put(msg, 0);
#ifdef CONFIG_TESTING_OPTIONS
@@ -803,30 +810,97 @@ skip_wrapped_data:
return msg;
}
struct wpabuf * dpp_build_peer_disc_req(struct dpp_authentication *auth, struct dpp_config_obj *conf)
struct json_token * dpp_parse_own_connector(const char *own_connector);
static u8 dpp_get_connector_version(const char *connector)
{
struct json_token *root, *token;
u8 version = 1;
root = dpp_parse_own_connector(connector);
if (!root)
return 1;
token = json_get_member(root, "version");
if (!token)
token = json_get_member(root, "v");
if (token && token->type == JSON_NUMBER)
version = token->number;
json_free(root);
return version;
}
void dpp_clear_confs(struct dpp_conf *conf)
{
if (!conf)
return;
if (conf->connector)
bin_clear_free(conf->connector, os_strlen(conf->connector));
wpabuf_clear_free(conf->c_sign_key);
wpabuf_clear_free(conf->net_access_key);
bin_clear_free(conf, sizeof(*conf));
}
void dpp_config_store_deinit(struct dpp_config_store *dc)
{
if (!dc)
return;
dpp_clear_confs(dc->conf);
dc->conf = NULL;
bin_clear_free(dc, sizeof(*dc));
}
struct dpp_config_store * dpp_config_store_init(void)
{
struct dpp_config_store *dc;
dc = os_zalloc(sizeof(*dc));
if (!dc)
return NULL;
return dc;
}
struct wpabuf * dpp_build_peer_disc_req(struct dpp_config_store *dc, struct dpp_conf *conf)
{
struct wpabuf *msg;
size_t len;
struct os_time now;
int version;
if (!conf || !conf->connector || !auth || !auth->net_access_key || !conf->c_sign_key) {
wpa_printf(MSG_ERROR, "missing %s", !conf->connector ? "Connector" : !auth->net_access_key ? "netAccessKey" : "C-sign-key");
if (!dc) {
wpa_printf(MSG_ERROR, "missing DPP config store");
return NULL;
}
if (!conf) {
wpa_printf(MSG_ERROR, "missing DPP config");
return NULL;
}
if (!conf->connector || !conf->net_access_key || !conf->c_sign_key) {
wpa_printf(MSG_ERROR, "missing %s",
!conf->connector ? "Connector" :
!conf->net_access_key ? "netAccessKey" : "C-sign-key");
return NULL;
}
os_get_time(&now);
if (auth->net_access_key_expiry &&
(os_time_t) auth->net_access_key_expiry < now.sec) {
if (conf->net_access_key_expiry &&
(os_time_t) conf->net_access_key_expiry < now.sec) {
wpa_printf(MSG_ERROR, "netAccessKey expired");
return NULL;
}
version = dpp_get_connector_version(conf->connector);
wpa_printf(MSG_DEBUG,
"DPP: Starting network introduction protocol to derive PMKSA for "
MACSTR, MAC2STR(auth->peer_mac_addr));
MACSTR, MAC2STR(dc->peer_mac_addr));
len = TRANSACTION_ID_ATTR_SET_LEN + CONNECTOR_ATTR_SET_LEN + os_strlen(conf->connector);
if (version >= 2)
len += 5;
msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_REQ, len);
if (!msg) {
return NULL;
@@ -852,6 +926,15 @@ struct wpabuf * dpp_build_peer_disc_req(struct dpp_authentication *auth, struct
#ifdef CONFIG_TESTING_OPTIONS
skip_trans_id:
#endif /* CONFIG_TESTING_OPTIONS */
if (version >= 2) {
wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
wpabuf_put_le16(msg, 1);
wpabuf_put_u8(msg, version);
}
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_CONNECTOR_PEER_DISC_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Connector");
goto skip_connector;
@@ -1110,7 +1193,7 @@ struct dpp_authentication * dpp_auth_init(void *msg_ctx,
{
struct dpp_authentication *auth;
size_t nonce_len;
size_t secret_len;
size_t secret_len = 0;
struct wpabuf *pi = NULL;
const u8 *r_pubkey_hash, *i_pubkey_hash;
#ifdef CONFIG_TESTING_OPTIONS
@@ -1473,7 +1556,7 @@ static void dpp_auth_success(struct dpp_authentication *auth)
static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
{
size_t nonce_len;
size_t secret_len;
size_t secret_len = 0;
struct wpabuf *msg, *pr = NULL;
u8 r_auth[4 + DPP_MAX_HASH_LEN];
u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth;
@@ -1538,6 +1621,7 @@ static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
if (dpp_ecdh(auth->own_protocol_key, auth->peer_protocol_key,
auth->Nx, &secret_len) < 0)
goto fail;
auth->secret_len = secret_len;
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
auth->Nx, auth->secret_len);
@@ -1725,7 +1809,7 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
size_t attr_len)
{
struct crypto_ec_key *pi = NULL;
size_t secret_len;
size_t secret_len = 0;
const u8 *addr[2];
size_t len[2];
u8 *unwrapped = NULL;
@@ -1735,11 +1819,13 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
const u8 *i_nonce;
const u8 *i_capab;
const u8 *i_bootstrap;
const u8 *version;
u16 wrapped_data_len;
u16 i_proto_len;
u16 i_nonce_len;
u16 i_capab_len;
u16 i_bootstrap_len;
u16 version_len;
struct dpp_authentication *auth = NULL;
#ifdef CONFIG_TESTING_OPTIONS
u64 start_us = dpp_time_us();
@@ -1776,6 +1862,13 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
auth->curr_chan = curr_chan;
auth->peer_version = 1; /* default to the first version */
version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
&version_len);
if (version && version_len >= 1 && version[0] >= 2) {
auth->peer_version = version[0];
wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
auth->peer_version);
}
#if 0
channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
@@ -2292,17 +2385,17 @@ struct wpabuf *
dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
const u8 *attr_start, size_t attr_len)
{
struct crypto_ec_key *pr;
size_t secret_len;
struct crypto_ec_key *pr = NULL;
size_t secret_len = 0;
const u8 *addr[2];
size_t len[2];
u8 *unwrapped = NULL, *unwrapped2 = NULL;
size_t unwrapped_len = 0, unwrapped2_len = 0;
const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto,
*r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth;
*r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth, *version;
u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
wrapped2_len, r_auth_len;
wrapped2_len, r_auth_len, version_len;
u8 r_auth2[DPP_MAX_HASH_LEN];
u8 role;
@@ -2380,6 +2473,13 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
}
auth->peer_version = 1; /* default to the first version */
version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
&version_len);
if (version && version_len >= 1 && version[0] >= 2) {
auth->peer_version = version[0];
wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
auth->peer_version);
}
status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
&status_len);
@@ -2428,6 +2528,7 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
goto fail;
}
auth->secret_len = secret_len;
crypto_ec_key_deinit(auth->peer_protocol_key);
auth->peer_protocol_key = pr;
pr = NULL;
@@ -3068,7 +3169,7 @@ static int dpp_configuration_parse(struct dpp_authentication *auth,
goto fail;
os_memcpy(tmp, cmd, len);
tmp[len] = '\0';
res = dpp_configuration_parse_helper(auth, cmd, 0);
res = dpp_configuration_parse_helper(auth, tmp, 0);
str_clear_free(tmp);
if (res)
goto fail;
@@ -3078,9 +3179,13 @@ static int dpp_configuration_parse(struct dpp_authentication *auth,
return 0;
fail:
dpp_configuration_free(auth->conf_sta);
auth->conf_sta = NULL;
dpp_configuration_free(auth->conf2_sta);
auth->conf2_sta = NULL;
dpp_configuration_free(auth->conf_ap);
auth->conf_ap = NULL;
dpp_configuration_free(auth->conf2_ap);
auth->conf2_ap = NULL;
return -1;
}
@@ -3158,13 +3263,19 @@ void dpp_auth_deinit(struct dpp_authentication *auth)
wpabuf_free(auth->resp_msg);
wpabuf_free(auth->conf_req);
for (i = 0; i < auth->num_conf_obj; i++) {
struct dpp_config_obj *conf = &auth->conf_obj[i];
struct dpp_config_obj *conf = &auth->conf_obj[i];
os_free(conf->connector);
wpabuf_free(conf->c_sign_key);
if (conf->connector) {
bin_clear_free(conf->connector, os_strlen(conf->connector));
conf->connector = NULL;
}
wpabuf_clear_free(conf->c_sign_key);
conf->c_sign_key = NULL;
}
wpabuf_free(auth->net_access_key);
wpabuf_clear_free(auth->net_access_key);
auth->net_access_key = NULL;
dpp_bootstrap_info_free(auth->tmp_own_bi);
#ifdef CONFIG_TESTING_OPTIONS
os_free(auth->config_obj_override);
os_free(auth->discovery_override);
@@ -3428,7 +3539,7 @@ skip_groups:
if (!hash) {
goto fail;
}
if (dpp_get_config_obj_hash(signed1, signed1_len, signed2, signed1_len, hash, curve->hash_len) < 0)
if (dpp_get_config_obj_hash(signed1, signed1_len, signed2, signed2_len, hash, curve->hash_len) < 0)
goto fail;
r = crypto_bignum_init();
@@ -4807,6 +4918,8 @@ static int dpp_connector_match_groups(struct json_token *own_root,
}
}
wpa_printf(MSG_DEBUG, "DPP: No compatible group found in peer connector");
return 0;
}
@@ -4857,7 +4970,7 @@ dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
struct wpabuf *own_key_pub = NULL;
const struct dpp_curve_params *curve, *own_curve;
struct dpp_signed_connector_info info;
size_t Nx_len;
size_t Nx_len = 0;
u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
os_memset(intro, 0, sizeof(*intro));

View File

@@ -16,6 +16,7 @@
#include "crypto/sha256.h"
#include "utils/includes.h"
#include "utils/common.h"
#include "ieee802_11_defs.h"
#include "esp_err.h"
#include "esp_dpp.h"
#include "crypto/crypto.h"
@@ -64,17 +65,6 @@ static const u8 TRANSACTION_ID = 1;
#define DPP_EVENT_INTRO "DPP-INTRO "
#define DPP_EVENT_CONF_REQ_RX "DPP-CONF-REQ-RX "
#define WLAN_ACTION_PUBLIC 4
#define WLAN_PA_VENDOR_SPECIFIC 9
#define OUI_WFA 0x506f9a
#define DPP_OUI_TYPE 0x1A
#define WLAN_EID_ADV_PROTO 108
#define WLAN_EID_VENDOR_SPECIFIC 221
#define WLAN_PA_GAS_INITIAL_REQ 10
enum dpp_public_action_frame_type {
DPP_PA_AUTHENTICATION_REQ = 0,
DPP_PA_AUTHENTICATION_RESP = 1,
@@ -234,7 +224,23 @@ struct dpp_configuration {
int psk_set;
};
#define DPP_MAX_CONF_OBJ 10
#define DPP_MAX_CONF_OBJ ESP_DPP_MAX_CONFIG_COUNT
struct dpp_conf {
char *connector;
struct wpabuf *c_sign_key;
size_t dpp_csign_len;
struct wpabuf *net_access_key;
os_time_t net_access_key_expiry;
uint8_t curr_chan;
enum dpp_akm akm;
};
struct dpp_config_store {
/* Single active DPP config used for Network Introduction; applications own multi-config selection. */
struct dpp_conf *conf;
u8 peer_mac_addr[ETH_ALEN];
};
struct dpp_authentication {
void *msg_ctx;
@@ -511,9 +517,10 @@ struct wpabuf * dpp_build_conn_status_result(struct dpp_authentication *auth,
enum dpp_status_error result,
const u8 *ssid, size_t ssid_len,
const char *channel_list);
struct wpabuf * dpp_build_peer_disc_req(struct dpp_authentication *auth, struct dpp_config_obj *conf);
struct wpabuf * dpp_build_peer_disc_req(struct dpp_config_store *dc, struct dpp_conf *conf);
struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
size_t len);
struct wpabuf * gas_build_comeback_req(u8 dialog_token);
const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len);
int dpp_check_attrs(const u8 *buf, size_t len);
int dpp_key_expired(const char *timestamp, os_time_t *expiry);
@@ -593,6 +600,9 @@ struct dpp_global_config {
int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
};
struct dpp_config_store * dpp_config_store_init(void);
void dpp_clear_confs(struct dpp_conf *conf);
void dpp_config_store_deinit(struct dpp_config_store *dc);
struct dpp_global * dpp_global_init(struct dpp_global_config *config);
void dpp_global_clear(struct dpp_global *dpp);
void dpp_global_deinit(struct dpp_global *dpp);

View File

@@ -237,6 +237,7 @@
#define WLAN_EID_20_40_BSS_INTOLERANT 73
#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
#define WLAN_EID_MMIE 76
#define WLAN_EID_ADV_PROTO 108
#define WLAN_EID_EXT_CAPAB 127
#define WLAN_EID_MIC 140
#define WLAN_EID_VENDOR_SPECIFIC 221

View File

@@ -189,7 +189,7 @@ pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
PMKID_LEN) == 0) {
wpa_printf(MSG_DEBUG, "WPA: reusing previous "
"PMKSA entry");
os_free(entry);
bin_clear_free(entry, sizeof(*entry));
return pos;
}
if (prev == NULL)

View File

@@ -2667,6 +2667,10 @@ int wpa_set_bss(uint8_t *macddr, uint8_t *bssid, uint8_t pairwise_cipher, uint8_
use_pmk_cache = false;
}
if (sm->key_mgmt == WPA_KEY_MGMT_DPP) {
use_pmk_cache = true;
}
if (os_memcmp(sm->ssid, ssid, ssid_len) == 0) {
wpa_printf(MSG_DEBUG, "reassoc same ess and okc is %d", sm->okc);
if (sm->okc == 1) {
@@ -2882,7 +2886,7 @@ void wpa_set_passphrase(char * passphrase, u8 *ssid, size_t ssid_len)
/* This is really SLOW, so just re cacl while reset param */
if (esp_wifi_sta_get_reset_nvs_pmk_internal() != 0) {
// check it's psk
if (strlen((char *)esp_wifi_sta_get_prof_password_internal()) == 64) {
if (os_strlen((char *)esp_wifi_sta_get_prof_password_internal()) == 64) {
if (hexstr2bin((char *)esp_wifi_sta_get_prof_password_internal(),
esp_wifi_sta_get_ap_info_prof_pmk_internal(), PMK_LEN) != 0)
return;

View File

@@ -39,17 +39,165 @@
#define CURVE_SEC256R1_PKEY_HEX_DIGITS 64
static const char *TAG = "wifi dpp-enrollee";
wifi_config_t s_dpp_wifi_config;
static int s_retry_num = 0;
/* Multi-config state */
static esp_dpp_config_data_t *s_dpp_configs = NULL;
static int s_config_count = 0;
static int s_active_config_index = 0;
static int s_last_attempted_config_index = -1;
static int s_connect_retry_count = 0;
#define WIFI_MAX_RETRY_NUM 3
#define DPP_AUTH_RETRY_MAX 3
#define IS_DPP_AKM(akm) \
((akm) == ESP_DPP_AKM_DPP || (akm) == ESP_DPP_AKM_SAE_DPP || (akm) == ESP_DPP_AKM_PSK_SAE_DPP)
/* Flag to track if bootstrap URI generation was successful. */
static bool s_bootstrap_ready = false;
/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_dpp_event_group;
#define DPP_CONNECTED_BIT BIT0
#define DPP_CONNECT_FAIL_BIT BIT1
#define DPP_AUTH_FAIL_BIT BIT2
#define WIFI_MAX_RETRY_NUM 3
#define DPP_CONNECTED_BIT BIT0
#define DPP_CONNECT_FAIL_BIT BIT1
#define DPP_AUTH_FAIL_BIT BIT2
static void cleanup_dpp_configs(void)
{
if (s_dpp_configs) {
free(s_dpp_configs);
s_dpp_configs = NULL;
}
s_config_count = 0;
s_active_config_index = 0;
s_last_attempted_config_index = -1;
s_connect_retry_count = 0;
}
static void dpp_enrollee_retry_dpp_auth_or_fail(void)
{
esp_err_t err;
if (s_connect_retry_count >= DPP_AUTH_RETRY_MAX) {
xEventGroupSetBits(s_dpp_event_group, DPP_AUTH_FAIL_BIT);
return;
}
ESP_LOGI(TAG, "Retrying DPP Auth/Listen... (%d/%d)",
s_connect_retry_count + 1, DPP_AUTH_RETRY_MAX);
err = esp_supp_dpp_start_listen();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to restart DPP listen: %s", esp_err_to_name(err));
xEventGroupSetBits(s_dpp_event_group, DPP_AUTH_FAIL_BIT);
return;
}
s_connect_retry_count++;
}
static esp_err_t apply_config_and_connect(esp_dpp_config_data_t *config_row)
{
wifi_config_t wifi_cfg = {0};
esp_err_t err;
if (!config_row) {
return ESP_ERR_INVALID_ARG;
}
s_last_attempted_config_index = s_active_config_index;
size_t ssid_len = config_row->ssid_len;
if (ssid_len > MAX_SSID_LEN) {
ESP_LOGW(TAG, "ssid_len %u exceeds MAX_SSID_LEN, clamping",
(unsigned)config_row->ssid_len);
ssid_len = MAX_SSID_LEN;
}
if (ssid_len > sizeof(wifi_cfg.sta.ssid)) {
ssid_len = sizeof(wifi_cfg.sta.ssid);
}
if (ssid_len) {
memcpy(wifi_cfg.sta.ssid, config_row->ssid, ssid_len);
}
/* Auth threshold so a weaker same-SSID AP does not win the default RSSI sort */
switch ((esp_dpp_akm_t)config_row->akm) {
case ESP_DPP_AKM_DPP:
wifi_cfg.sta.threshold.authmode = WIFI_AUTH_DPP;
break;
case ESP_DPP_AKM_SAE_DPP:
case ESP_DPP_AKM_SAE:
wifi_cfg.sta.threshold.authmode = WIFI_AUTH_WPA3_PSK;
break;
case ESP_DPP_AKM_PSK_SAE_DPP:
case ESP_DPP_AKM_PSK_SAE:
wifi_cfg.sta.threshold.authmode = WIFI_AUTH_WPA2_WPA3_PSK;
break;
case ESP_DPP_AKM_PSK:
default:
wifi_cfg.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
break;
}
if (IS_DPP_AKM(config_row->akm)) {
err = esp_supp_dpp_set_config(config_row);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_supp_dpp_set_config failed: %s", esp_err_to_name(err));
return err;
}
} else {
err = esp_supp_dpp_set_config(NULL);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_supp_dpp_set_config(NULL) failed: %s", esp_err_to_name(err));
return err;
}
}
size_t pass_len = config_row->password_len;
if (pass_len > MAX_PASSPHRASE_LEN) {
ESP_LOGW(TAG, "password_len %u exceeds MAX_PASSPHRASE_LEN, clamping",
(unsigned)config_row->password_len);
pass_len = MAX_PASSPHRASE_LEN;
}
if (pass_len > sizeof(wifi_cfg.sta.password)) {
pass_len = sizeof(wifi_cfg.sta.password);
}
if (pass_len) {
memcpy(wifi_cfg.sta.password, config_row->password, pass_len);
}
err = esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_wifi_set_config failed: %s", esp_err_to_name(err));
return err;
}
err = esp_wifi_connect();
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_wifi_connect failed: %s", esp_err_to_name(err));
}
return err;
}
static void advance_to_next_config_or_fail(void)
{
s_active_config_index++;
if (s_active_config_index < s_config_count) {
esp_dpp_config_data_t *config_row = &s_dpp_configs[s_active_config_index];
s_connect_retry_count = 0;
ESP_LOGI(TAG, "Next config %d/%d - SSID len=%u: %.*s",
s_active_config_index + 1, s_config_count,
(unsigned)config_row->ssid_len,
(int)config_row->ssid_len, (const char *)config_row->ssid);
if (apply_config_and_connect(config_row) != ESP_OK) {
advance_to_next_config_or_fail();
}
} else {
ESP_LOGE(TAG, "No more configs left");
xEventGroupSetBits(s_dpp_event_group, DPP_CONNECT_FAIL_BIT);
}
}
static void event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
@@ -57,56 +205,101 @@ static void event_handler(void *arg, esp_event_base_t event_base,
if (event_base == WIFI_EVENT) {
switch (event_id) {
case WIFI_EVENT_STA_START:
cleanup_dpp_configs();
ESP_ERROR_CHECK(esp_supp_dpp_start_listen());
ESP_LOGI(TAG, "Started listening for DPP Authentication");
break;
case WIFI_EVENT_STA_DISCONNECTED:
if (s_retry_num < WIFI_MAX_RETRY_NUM) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "Disconnect event, retry to connect to the AP");
} else {
xEventGroupSetBits(s_dpp_event_group, DPP_CONNECT_FAIL_BIT);
case WIFI_EVENT_STA_DISCONNECTED: {
if (s_config_count > 0) {
if (s_connect_retry_count < WIFI_MAX_RETRY_NUM) {
ESP_LOGI(TAG, "Disconnect event, retry %d/%d to connect to the AP",
s_connect_retry_count + 1, WIFI_MAX_RETRY_NUM);
if (esp_wifi_connect() == ESP_OK) {
s_connect_retry_count++;
} else {
advance_to_next_config_or_fail();
}
} else {
ESP_LOGD(TAG, "Retries exhausted on current config");
advance_to_next_config_or_fail();
}
}
break;
}
case WIFI_EVENT_STA_CONNECTED:
ESP_LOGI(TAG, "Successfully connected to the AP ssid : %s ", s_dpp_wifi_config.sta.ssid);
ESP_LOGI(TAG, "Successfully connected to the AP");
break;
case WIFI_EVENT_DPP_URI_READY:
case WIFI_EVENT_DPP_URI_READY: {
wifi_event_dpp_uri_ready_t *uri_data = event_data;
if (uri_data != NULL) {
esp_qrcode_config_t cfg = ESP_QRCODE_CONFIG_DEFAULT();
ESP_LOGI(TAG, "Scan below QR Code to configure the enrollee:");
esp_qrcode_generate(&cfg, (const char *)uri_data->uri);
s_bootstrap_ready = true;
}
break;
case WIFI_EVENT_DPP_CFG_RECVD:
wifi_event_dpp_config_received_t *config = event_data;
memcpy(&s_dpp_wifi_config, &config->wifi_cfg, sizeof(s_dpp_wifi_config));
s_retry_num = 0;
esp_wifi_set_config(WIFI_IF_STA, &s_dpp_wifi_config);
esp_wifi_connect();
break;
case WIFI_EVENT_DPP_FAILED:
wifi_event_dpp_failed_t *dpp_failure = event_data;
if (s_retry_num < 5) {
ESP_LOGI(TAG, "DPP Auth failed (Reason: %s), retry...", esp_err_to_name((int)dpp_failure->failure_reason));
ESP_ERROR_CHECK(esp_supp_dpp_start_listen());
s_retry_num++;
} else {
xEventGroupSetBits(s_dpp_event_group, DPP_AUTH_FAIL_BIT);
}
}
case WIFI_EVENT_DPP_CFG_RECVD: {
wifi_event_dpp_config_received_t *ev = event_data;
cleanup_dpp_configs();
if (ev->total_conf > 0) {
s_config_count = ev->total_conf;
s_dpp_configs = malloc(s_config_count * sizeof(esp_dpp_config_data_t));
if (s_dpp_configs) {
memcpy(s_dpp_configs, ev->configs, s_config_count * sizeof(esp_dpp_config_data_t));
s_active_config_index = 0;
s_connect_retry_count = 0;
ESP_LOGI(TAG, "DPP Config Received: %d objects", s_config_count);
if (apply_config_and_connect(&s_dpp_configs[0]) != ESP_OK) {
/* First config failed at the application stage; try the
* remaining ones (consistent with the STA_DISCONNECTED
* retry-exhausted path) before declaring failure. */
advance_to_next_config_or_fail();
}
} else {
ESP_LOGE(TAG, "Failed to allocate memory for %d DPP configs", s_config_count);
s_config_count = 0;
xEventGroupSetBits(s_dpp_event_group, DPP_CONNECT_FAIL_BIT);
}
}
break;
}
case WIFI_EVENT_DPP_FAILED: {
if (s_config_count == 0) {
wifi_event_dpp_failed_t *dpp_failure = event_data;
esp_err_t reason = (dpp_failure != NULL) ? (esp_err_t)dpp_failure->failure_reason : ESP_ERR_DPP_FAILURE;
if (s_bootstrap_ready) {
ESP_LOGI(TAG, "DPP Authentication failed (%s), retrying...", esp_err_to_name(reason));
dpp_enrollee_retry_dpp_auth_or_fail();
} else {
ESP_LOGE(TAG, "DPP Bootstrap generation failed (%s)", esp_err_to_name(reason));
xEventGroupSetBits(s_dpp_event_group, DPP_AUTH_FAIL_BIT);
}
} else {
/*
* DPP failure occurred after we already have a configuration list.
* This is typically a Network Introduction protocol failure.
* Trigger a disconnect to drive the retry/fallback state machine
* in the STA_DISCONNECTED handler.
*/
ESP_LOGE(TAG, "DPP post-provisioning failure; triggering retry");
esp_wifi_disconnect();
}
break;
}
default:
break;
}
}
if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
s_connect_retry_count = 0;
xEventGroupSetBits(s_dpp_event_group, DPP_CONNECTED_BIT);
}
}
@@ -176,16 +369,52 @@ void dpp_enrollee_init(void)
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
* happened. */
if (bits & DPP_CONNECTED_BIT) {
ESP_LOGI(TAG, "DPP Provisioning and Connection successful");
} else if (bits & DPP_CONNECT_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
s_dpp_wifi_config.sta.ssid, s_dpp_wifi_config.sta.password);
ESP_LOGI(TAG, "Failed to connect after exhausting all configurations");
} else if (bits & DPP_AUTH_FAIL_BIT) {
ESP_LOGI(TAG, "DPP Authentication failed after %d retries", s_retry_num);
ESP_LOGI(TAG, "DPP Authentication failed");
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
uint8_t diag_ssid_len = 0;
uint8_t diag_ssid[32]; // MAX_SSID_LEN
bool diag_password_set = false;
bool diag_have_last_config = false;
if (bits & DPP_CONNECT_FAIL_BIT) {
int last_idx = s_last_attempted_config_index;
if (last_idx < 0 && s_config_count > 0) {
last_idx = s_active_config_index >= s_config_count ? s_config_count - 1 : s_active_config_index;
}
if (s_dpp_configs != NULL && last_idx >= 0 && last_idx < s_config_count) {
const esp_dpp_config_data_t *row = &s_dpp_configs[last_idx];
diag_ssid_len = row->ssid_len;
if (diag_ssid_len > 32) {
diag_ssid_len = 32;
}
memcpy(diag_ssid, row->ssid, diag_ssid_len);
diag_password_set = row->password_len > 0;
diag_have_last_config = true;
}
}
if (bits & DPP_CONNECT_FAIL_BIT && diag_have_last_config) {
ESP_LOGI(TAG, "Last attempted config -> SSID len=%u: %.*s, password: %s",
(unsigned)diag_ssid_len,
(int)diag_ssid_len,
(const char *)diag_ssid,
diag_password_set ? "(set)" : "(not set)");
}
esp_supp_dpp_deinit();
s_bootstrap_ready = false;
cleanup_dpp_configs();
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler));
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler));
vEventGroupDelete(s_dpp_event_group);