Merge branch 'feat/sae_calculation_optimizations_v5.5' into 'release/v5.5'

fix(esp_wifi): Optimize crypto operations for supplicant (v5.5)

See merge request espressif/esp-idf!47233
This commit is contained in:
Jiang Jiang Jian
2026-04-28 11:05:07 +08:00
12 changed files with 2619 additions and 133 deletions
+12
View File
@@ -721,6 +721,18 @@ menu "Wi-Fi"
help
Select this option to enable WiFi Easy Connect Support.
config ESP_WIFI_P256_ACCEL
bool "Enable P-256 crypto acceleration"
depends on ESP_WIFI_MBEDTLS_CRYPTO
default n
help
Enable Espressif-specific P-256 acceleration in the WPA supplicant
crypto layer. This reduces SAE and DPP latency on supported targets
at the cost of additional code size.
If disabled, the supplicant falls back to the generic Mbed TLS
implementation.
config ESP_WIFI_11R_SUPPORT
bool "Enable 802.11R (Fast Transition) Support"
default n
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -16,6 +16,253 @@
#include "random.h"
#include "sha256.h"
#include "mbedtls/pk.h"
#include "p256_common.h"
#if CONFIG_ESP_WIFI_P256_ACCEL
static int mpi_is_secp256r1_prime(const mbedtls_mpi *p)
{
u8 p_be[P256_LEN_BYTES];
if (!p || mbedtls_mpi_size(p) != P256_LEN_BYTES) {
return 0;
}
if (mbedtls_mpi_write_binary(p, p_be, sizeof(p_be)) != 0) {
return 0;
}
return os_memcmp(p_be, p256_p_be, sizeof(p_be)) == 0;
}
static int p256_words_is_one(const u32 *a)
{
size_t i;
if (a[0] != 1) {
return 0;
}
for (i = 1; i < P256_WORDS; i++) {
if (a[i] != 0) {
return 0;
}
}
return 1;
}
static int p256_words_cmp(const u32 *a, const u32 *b)
{
int i;
for (i = P256_WORDS - 1; i >= 0; i--) {
if (a[i] < b[i]) {
return -1;
}
if (a[i] > b[i]) {
return 1;
}
}
return 0;
}
static size_t p256_words_ctz(const u32 *a)
{
size_t i;
for (i = 0; i < P256_WORDS; i++) {
if (a[i] != 0) {
return i * 32 + __builtin_ctz(a[i]);
}
}
return P256_WORDS * 32;
}
static void p256_words_rshift(u32 *a, size_t count)
{
size_t word_shift = count / 32;
size_t bit_shift = count % 32;
size_t i;
if (word_shift >= P256_WORDS) {
os_memset(a, 0, sizeof(u32) * P256_WORDS);
return;
}
if (word_shift > 0) {
for (i = 0; i + word_shift < P256_WORDS; i++) {
a[i] = a[i + word_shift];
}
for (; i < P256_WORDS; i++) {
a[i] = 0;
}
}
if (bit_shift > 0) {
for (i = 0; i < P256_WORDS - 1; i++) {
a[i] = (a[i] >> bit_shift) |
(a[i + 1] << (32 - bit_shift));
}
a[P256_WORDS - 1] >>= bit_shift;
}
}
static void p256_words_lshift(const u32 *in, size_t count, u32 *out)
{
size_t word_shift = count / 32;
size_t bit_shift = count % 32;
size_t i;
os_memset(out, 0, sizeof(u32) * P256_WORDS);
if (word_shift >= P256_WORDS) {
return;
}
for (i = 0; i < P256_WORDS; i++) {
u64 val;
size_t dst;
if (in[i] == 0) {
continue;
}
dst = i + word_shift;
if (dst >= P256_WORDS) {
break;
}
val = (u64) in[i] << bit_shift;
out[dst] |= (u32) val;
if (bit_shift > 0 && dst + 1 < P256_WORDS) {
out[dst + 1] |= (u32)(val >> 32);
}
}
}
static void p256_words_sub(u32 *a, const u32 *b)
{
size_t i;
u64 borrow = 0;
for (i = 0; i < P256_WORDS; i++) {
u64 ai = a[i];
u64 bi = b[i];
u64 res = ai - bi - borrow;
a[i] = (u32) res;
borrow = (ai < bi + borrow) ? 1 : 0;
}
}
static void p256_words_swap(u32 *a, u32 *b)
{
u32 tmp[P256_WORDS];
os_memcpy(tmp, a, sizeof(tmp));
os_memcpy(a, b, sizeof(tmp));
os_memcpy(b, tmp, sizeof(tmp));
}
static void p256_words_mod(u32 *a, const u32 *n)
{
u32 tmp[P256_WORDS];
while (p256_words_cmp(a, n) >= 0) {
size_t a_bits = p256_words_bitlen(a);
size_t n_bits = p256_words_bitlen(n);
size_t shift = a_bits - n_bits;
p256_words_lshift(n, shift, tmp);
if (p256_words_cmp(a, tmp) < 0) {
shift--;
p256_words_lshift(n, shift, tmp);
}
p256_words_sub(a, tmp);
}
}
static int crypto_bignum_mulmod_secp256r1(const mbedtls_mpi *a,
const mbedtls_mpi *b,
const mbedtls_mpi *mod,
mbedtls_mpi *out)
{
u32 a_words[P256_WORDS];
u32 b_words[P256_WORDS];
u32 b_mont[P256_WORDS];
u32 result[P256_WORDS];
if (!mpi_is_secp256r1_prime(mod) ||
p256_words_from_mpi_reduced(a, a_words) != 0 ||
p256_words_from_mpi_reduced(b, b_words) != 0) {
return -2;
}
p256_mont_mul(b_mont, b_words, p256_r2_le);
p256_mont_mul(result, a_words, b_mont);
return p256_words_to_mpi(result, out) == 0 ? 0 : -1;
}
static int crypto_bignum_legendre_secp256r1(const mbedtls_mpi *a,
const mbedtls_mpi *p)
{
u32 A[P256_WORDS];
u32 N[P256_WORDS];
unsigned int n_mod8;
unsigned int a_mod4;
unsigned int n_mod4;
size_t two_power;
int sign = 1;
if (!mpi_is_secp256r1_prime(p) ||
p256_words_from_mpi_reduced(a, A) != 0) {
return -2;
}
os_memcpy(N, p256_p_le, sizeof(N));
if (p256_words_is_zero(A)) {
return 0;
}
while (!p256_words_is_zero(A)) {
if (p256_words_is_one(A)) {
return sign;
}
n_mod8 = N[0] & 0x7;
two_power = p256_words_ctz(A);
if (two_power > 0) {
p256_words_rshift(A, two_power);
if ((n_mod8 == 3 || n_mod8 == 5) && (two_power & 1U)) {
sign = -sign;
}
}
p256_words_swap(A, N);
a_mod4 = A[0] & 0x3;
n_mod4 = N[0] & 0x3;
if (a_mod4 == 3 && n_mod4 == 3) {
sign = -sign;
}
if (p256_words_cmp(A, N) >= 0) {
p256_words_mod(A, N);
}
if (p256_words_is_one(N)) {
return sign;
}
}
return p256_words_is_one(N) ? sign : 0;
}
#endif
struct crypto_bignum *crypto_bignum_init(void)
{
@@ -119,7 +366,43 @@ int crypto_bignum_exptmod(const struct crypto_bignum *a,
const struct crypto_bignum *c,
struct crypto_bignum *d)
{
return mbedtls_mpi_exp_mod((mbedtls_mpi *) d, (const mbedtls_mpi *) a, (const mbedtls_mpi *) b, (const mbedtls_mpi *) c, NULL) ? -1 : 0;
int ret;
/* Fast path for small public exponents frequently used in SAE math. */
if (mbedtls_mpi_cmp_int((const mbedtls_mpi *) b, 0) >= 0 &&
mbedtls_mpi_cmp_int((const mbedtls_mpi *) b, 3) <= 0) {
if (mbedtls_mpi_cmp_int((const mbedtls_mpi *) b, 0) == 0) {
ret = mbedtls_mpi_lset((mbedtls_mpi *) d, 1) ||
mbedtls_mpi_mod_mpi((mbedtls_mpi *) d, (mbedtls_mpi *) d,
(const mbedtls_mpi *) c);
return ret ? -1 : 0;
}
if (mbedtls_mpi_cmp_int((const mbedtls_mpi *) b, 1) == 0) {
ret = mbedtls_mpi_copy((mbedtls_mpi *) d, (const mbedtls_mpi *) a) ||
mbedtls_mpi_mod_mpi((mbedtls_mpi *) d, (mbedtls_mpi *) d,
(const mbedtls_mpi *) c);
return ret ? -1 : 0;
}
if (mbedtls_mpi_cmp_int((const mbedtls_mpi *) b, 2) == 0) {
return crypto_bignum_mulmod(a, a, c, d);
} else {
mbedtls_mpi tmp;
mbedtls_mpi_init(&tmp);
ret = crypto_bignum_mulmod(a, a, c, (struct crypto_bignum *) &tmp);
if (ret == 0) {
ret = crypto_bignum_mulmod((struct crypto_bignum *) &tmp,
a, c, d);
}
mbedtls_mpi_free(&tmp);
return ret ? -1 : 0;
}
}
return mbedtls_mpi_exp_mod((mbedtls_mpi *) d, (const mbedtls_mpi *) a,
(const mbedtls_mpi *) b,
(const mbedtls_mpi *) c, NULL) ? -1 : 0;
}
@@ -152,6 +435,15 @@ int crypto_bignum_mulmod(const struct crypto_bignum *a,
const struct crypto_bignum *c,
struct crypto_bignum *d)
{
#if CONFIG_ESP_WIFI_P256_ACCEL
int fast_ret = crypto_bignum_mulmod_secp256r1((const mbedtls_mpi *) a,
(const mbedtls_mpi *) b,
(const mbedtls_mpi *) c,
(mbedtls_mpi *) d);
if (fast_ret != -2) {
return fast_ret;
}
#endif
return mbedtls_mpi_mul_mpi((mbedtls_mpi *)d, (const mbedtls_mpi *)a, (const mbedtls_mpi *)b) ||
mbedtls_mpi_mod_mpi((mbedtls_mpi *)d, (mbedtls_mpi *)d, (const mbedtls_mpi *)c) ? -1 : 0;
}
@@ -160,17 +452,7 @@ int crypto_bignum_sqrmod(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *c)
{
int res;
struct crypto_bignum *tmp = crypto_bignum_init();
if (!tmp) {
return -1;
}
res = mbedtls_mpi_copy((mbedtls_mpi *) tmp, (const mbedtls_mpi *) a);
res = crypto_bignum_mulmod(a, tmp, b, c);
crypto_bignum_deinit(tmp, 0);
return res ? -1 : 0;
return crypto_bignum_mulmod(a, a, b, c);
}
int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
@@ -219,8 +501,8 @@ int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
mbedtls_esp_random, NULL) != 0) ? -1 : 0);
}
int crypto_bignum_legendre(const struct crypto_bignum *a,
const struct crypto_bignum *p)
static int mbedtls_bignum_legendre(const struct crypto_bignum *a,
const struct crypto_bignum *p)
{
mbedtls_mpi exp, tmp;
int res = -2, ret;
@@ -252,6 +534,22 @@ cleanup:
return res;
}
int crypto_bignum_legendre(const struct crypto_bignum *a,
const struct crypto_bignum *p)
{
#if CONFIG_ESP_WIFI_P256_ACCEL
int legendre_res;
legendre_res = crypto_bignum_legendre_secp256r1((const mbedtls_mpi *) a,
(const mbedtls_mpi *) p);
if (legendre_res != -2) {
return legendre_res;
}
#endif
return mbedtls_bignum_legendre(a, p);
}
int crypto_bignum_addmod(const struct crypto_bignum *a,
const struct crypto_bignum *b,
const struct crypto_bignum *c,
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,236 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Shared P-256 (secp256r1) word-level and Montgomery arithmetic used by
* both the bignum and EC fast paths. All helpers are static inline so
* each translation unit gets its own copy without linkage issues.
*
* Prerequisites: the including .c file must already provide u8/u32/u64
* typedefs, os_memcmp/os_memset/os_memcpy (via utils/common.h), and
* mbedtls/bignum.h.
*/
#pragma once
#define P256_WORDS 8
#define P256_LEN_BYTES 32
static const u8 p256_p_be[P256_LEN_BYTES] = {
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
static const u32 p256_p_le[P256_WORDS] = {
0xffffffffU, 0xffffffffU, 0xffffffffU, 0x00000000U,
0x00000000U, 0x00000000U, 0x00000001U, 0xffffffffU
};
/* R^2 mod p in little-endian word order, for Montgomery domain entry. */
static const u32 p256_r2_le[P256_WORDS] = {
0x00000003U, 0x00000000U, 0xffffffffU, 0xfffffffbU,
0xfffffffeU, 0xffffffffU, 0xfffffffdU, 0x00000004U
};
static inline int p256_words_is_zero(const u32 *a)
{
size_t i;
for (i = 0; i < P256_WORDS; i++) {
if (a[i] != 0) {
return 0;
}
}
return 1;
}
static inline size_t p256_words_bitlen(const u32 *a)
{
int i;
for (i = P256_WORDS - 1; i >= 0; i--) {
if (a[i] != 0) {
return (size_t) i * 32 + 32 - __builtin_clz(a[i]);
}
}
return 0;
}
static inline int p256_words_from_mpi(const mbedtls_mpi *in, u32 *out)
{
u8 in_be[P256_LEN_BYTES];
size_t i;
if (!in || in->MBEDTLS_PRIVATE(s) < 0 ||
mbedtls_mpi_size(in) > P256_LEN_BYTES ||
mbedtls_mpi_write_binary(in, in_be, sizeof(in_be)) != 0) {
return -1;
}
for (i = 0; i < P256_WORDS; i++) {
size_t off = P256_LEN_BYTES - (i + 1) * 4;
out[i] = ((u32) in_be[off] << 24) |
((u32) in_be[off + 1] << 16) |
((u32) in_be[off + 2] << 8) |
(u32) in_be[off + 3];
}
return 0;
}
static inline int p256_words_from_mpi_reduced(const mbedtls_mpi *in, u32 *out)
{
u8 in_be[P256_LEN_BYTES];
size_t i;
size_t in_size;
if (!in || in->MBEDTLS_PRIVATE(s) < 0) {
return -1;
}
in_size = mbedtls_mpi_size(in);
if (in_size > P256_LEN_BYTES) {
return -1;
}
if (mbedtls_mpi_write_binary(in, in_be, sizeof(in_be)) != 0) {
return -1;
}
if (in_size == P256_LEN_BYTES &&
os_memcmp(in_be, p256_p_be, sizeof(in_be)) >= 0) {
return -1;
}
for (i = 0; i < P256_WORDS; i++) {
size_t off = P256_LEN_BYTES - (i + 1) * 4;
out[i] = ((u32) in_be[off] << 24) |
((u32) in_be[off + 1] << 16) |
((u32) in_be[off + 2] << 8) |
(u32) in_be[off + 3];
}
return 0;
}
static inline int p256_words_to_mpi(const u32 *in, mbedtls_mpi *out)
{
u8 out_be[P256_LEN_BYTES];
size_t i;
for (i = 0; i < P256_WORDS; i++) {
size_t off = P256_LEN_BYTES - (i + 1) * 4;
out_be[off] = (u8)(in[i] >> 24);
out_be[off + 1] = (u8)(in[i] >> 16);
out_be[off + 2] = (u8)(in[i] >> 8);
out_be[off + 3] = (u8) in[i];
}
return mbedtls_mpi_read_binary(out, out_be, sizeof(out_be));
}
static inline u32 p256_words_sub_borrow(u32 *z, const u32 *x, const u32 *y)
{
size_t i;
u32 borrow = 0;
for (i = 0; i < P256_WORDS; i++) {
u64 diff = (u64) x[i] - y[i] - borrow;
z[i] = (u32) diff;
borrow = -(u32)(diff >> 32);
}
return borrow;
}
static inline void p256_words_cmov(u32 *z, const u32 *x, u32 c)
{
size_t i;
u32 mask = (u32) - (int) c;
for (i = 0; i < P256_WORDS; i++) {
z[i] = (z[i] & ~mask) | (x[i] & mask);
}
}
static inline u64 p256_u32_muladd64(u32 x, u32 y, u32 z, u32 t)
{
return (u64) x * y + z + t;
}
static inline u32 p256_u288_muladd(u32 z[P256_WORDS + 1], u32 x,
const u32 y[P256_WORDS])
{
size_t i;
u32 carry = 0;
for (i = 0; i < P256_WORDS; i++) {
u64 prod = p256_u32_muladd64(x, y[i], z[i], carry);
z[i] = (u32) prod;
carry = (u32)(prod >> 32);
}
{
u64 sum = (u64) z[P256_WORDS] + carry;
z[P256_WORDS] = (u32) sum;
carry = (u32)(sum >> 32);
}
return carry;
}
static inline void p256_u288_rshift32(u32 z[P256_WORDS + 1], u32 c)
{
size_t i;
for (i = 0; i < P256_WORDS; i++) {
z[i] = z[i + 1];
}
z[P256_WORDS] = c;
}
/*
* CIOS Montgomery multiplication for secp256r1.
*
* The Montgomery constant mu = -p^{-1} mod 2^{32} equals 1 for this prime
* because p[0] = 0xFFFFFFFF, i.e. p ≡ -1 (mod 2^{32}). That simplifies
* the reduction factor to u = new_a[0] * 1 = new_a[0], which we compute
* early as a[0] + x[i]*y[0] (the low word of the partial accumulator after
* the multiply step) to break the data dependency.
*/
static inline void p256_mont_mul(u32 z[P256_WORDS],
const u32 x[P256_WORDS],
const u32 y[P256_WORDS])
{
u32 a[P256_WORDS + 1] = {0};
u32 reduced[P256_WORDS];
size_t i;
for (i = 0; i < P256_WORDS; i++) {
u32 u = a[0] + x[i] * y[0];
u32 c = p256_u288_muladd(a, x[i], y);
c += p256_u288_muladd(a, u, p256_p_le);
p256_u288_rshift32(a, c);
}
{
u32 carry_add = a[P256_WORDS];
u32 carry_sub = p256_words_sub_borrow(reduced, a, p256_p_le);
u32 use_sub = carry_add | (1U - carry_sub);
os_memcpy(z, a, sizeof(u32) * P256_WORDS);
p256_words_cmov(z, reduced, use_sub);
}
}
@@ -44,6 +44,42 @@ struct dpp_global {
extern struct dpp_curve_params dpp_curves[];
#ifdef CONFIG_TESTING_OPTIONS
u64 dpp_last_auth_req_parse_us;
u64 dpp_last_auth_resp_form_us;
u64 dpp_last_auth_req_total_us;
static u64 dpp_time_us(void)
{
struct os_reltime now;
if (os_get_reltime(&now) < 0)
return 0;
return ((u64) now.sec * 1000000) + now.usec;
}
static void dpp_auth_req_set_timing(struct dpp_authentication *auth,
u64 start_us, u64 parse_done_us)
{
u64 end_us;
if (!auth || !start_us || !parse_done_us || parse_done_us < start_us)
return;
end_us = dpp_time_us();
if (!end_us || end_us < parse_done_us)
return;
auth->auth_req_parse_us = parse_done_us - start_us;
auth->auth_resp_form_us = end_us - parse_done_us;
auth->auth_req_total_us = end_us - start_us;
dpp_last_auth_req_parse_us = auth->auth_req_parse_us;
dpp_last_auth_resp_form_us = auth->auth_resp_form_us;
dpp_last_auth_req_total_us = auth->auth_req_total_us;
}
#endif
#define TRANSACTION_ID_ATTR_SET_LEN 5
#define CONNECTOR_ATTR_SET_LEN 4
@@ -1707,6 +1743,13 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
u16 i_capab_len;
u16 i_bootstrap_len;
struct dpp_authentication *auth = NULL;
#ifdef CONFIG_TESTING_OPTIONS
u64 start_us = dpp_time_us();
u64 parse_done_us = 0;
dpp_last_auth_req_parse_us = 0;
dpp_last_auth_resp_form_us = 0;
dpp_last_auth_req_total_us = 0;
#endif
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) {
@@ -1892,9 +1935,15 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
wpa_printf(MSG_DEBUG,
"DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time");
#ifdef CONFIG_TESTING_OPTIONS
parse_done_us = dpp_time_us();
#endif
if (dpp_auth_build_resp_status(auth,
DPP_STATUS_RESPONSE_PENDING) < 0)
goto fail;
#ifdef CONFIG_TESTING_OPTIONS
dpp_auth_req_set_timing(auth, start_us, parse_done_us);
#endif
i_bootstrap = dpp_get_attr(attr_start, attr_len,
DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
&i_bootstrap_len);
@@ -1912,8 +1961,14 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
"%s", hex);
return auth;
}
#ifdef CONFIG_TESTING_OPTIONS
parse_done_us = dpp_time_us();
#endif
if (dpp_auth_build_resp_ok(auth) < 0)
goto fail;
#ifdef CONFIG_TESTING_OPTIONS
dpp_auth_req_set_timing(auth, start_us, parse_done_us);
#endif
return auth;
@@ -1926,8 +1981,14 @@ not_compatible:
auth->configurator = 0;
auth->peer_protocol_key = pi;
pi = NULL;
#ifdef CONFIG_TESTING_OPTIONS
parse_done_us = dpp_time_us();
#endif
if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0)
goto fail;
#ifdef CONFIG_TESTING_OPTIONS
dpp_auth_req_set_timing(auth, start_us, parse_done_us);
#endif
auth->remove_on_tx_status = 1;
return auth;
@@ -317,8 +317,19 @@ struct dpp_authentication {
char *groups_override;
unsigned int ignore_netaccesskey_mismatch:1;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_TESTING_OPTIONS
u64 auth_req_parse_us;
u64 auth_resp_form_us;
u64 auth_req_total_us;
#endif
};
#ifdef CONFIG_TESTING_OPTIONS
extern u64 dpp_last_auth_req_parse_us;
extern u64 dpp_last_auth_resp_form_us;
extern u64 dpp_last_auth_req_total_us;
#endif
struct dpp_configurator {
struct dl_list list;
unsigned int id;
@@ -9,7 +9,7 @@ idf_component_register(SRCS
"test_wpa_supplicant_main.c"
"test_wifi_external_bss.c"
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES wpa_supplicant mbedtls esp_wifi esp_event unity esp_psram
PRIV_REQUIRES wpa_supplicant mbedtls esp_wifi esp_event unity esp_psram esp_timer
WHOLE_ARCHIVE)
idf_component_get_property(esp_supplicant_dir wpa_supplicant COMPONENT_DIR)
@@ -25,3 +25,7 @@ target_include_directories(${COMPONENT_LIB} PRIVATE ${esp_supplicant_dir}/src)
add_definitions(-DWIFI_SUPPLICANT_MD5=\"${WIFI_SUPPLICANT_MD5}\")
add_definitions(-DCONFIG_WPA3_SAE)
add_definitions(-DCONFIG_DPP)
if(CONFIG_ESP_WIFI_TESTING_OPTIONS)
target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_TESTING_OPTIONS)
endif()
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -14,12 +14,211 @@
#include "utils/includes.h"
#include "crypto/crypto.h"
#include "esp_timer.h"
#include "mbedtls/ecdh.h"
#include "mbedtls/ecp.h"
#include "mbedtls/pk.h"
#include "test_utils.h"
#include "test_wpa_supplicant_common.h"
typedef struct crypto_bignum crypto_bignum;
static const uint8_t test_secp256r1_prime[32] = {
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
static const uint8_t test_p256_bignum_vals[][32] = {
{
0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef,
0xca, 0xfe, 0xba, 0xbe, 0x88, 0x99, 0xaa, 0xbb,
0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe,
0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0
},
{
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21,
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10
},
{
0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0x5a,
0x5a, 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0x5a, 0xa5,
0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78,
0x89, 0x9a, 0xab, 0xbc, 0xcd, 0xde, 0xef, 0xf0
}
};
static const uint8_t test_p256_scalar_seeds[][32] = {
{
0xff, 0xff, 0xff, 0xff, 0xde, 0xad, 0xbe, 0xef,
0xca, 0xfe, 0xba, 0xbe, 0x88, 0x99, 0xaa, 0xbb,
0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe,
0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0
},
{
0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0x5a,
0x5a, 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0x5a, 0xa5,
0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78,
0x89, 0x9a, 0xab, 0xbc, 0xcd, 0xde, 0xef, 0xf0
},
{
0x0f, 0x1e, 0x2d, 0x3c, 0x4b, 0x5a, 0x69, 0x78,
0x87, 0x96, 0xa5, 0xb4, 0xc3, 0xd2, 0xe1, 0xf0,
0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87,
0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f
}
};
static const unsigned int test_p256_point_multipliers[] = { 7, 13 };
static const unsigned int test_small_exponents[] = { 0, 1, 2, 3 };
static int test_mbedtls_rng(void *ctx, unsigned char *buf, size_t len)
{
(void) ctx;
return os_get_random(buf, len) == 0 ? 0 : MBEDTLS_ERR_ECP_RANDOM_FAILED;
}
static void test_load_valid_p256_scalar(const mbedtls_ecp_group *grp,
const uint8_t *seed, size_t seed_len,
mbedtls_mpi *scalar)
{
mbedtls_mpi range;
mbedtls_mpi_init(&range);
TEST_ASSERT_EQUAL(0, mbedtls_mpi_sub_int(&range, &grp->N, 1));
TEST_ASSERT_EQUAL(0, mbedtls_mpi_read_binary(scalar, seed, seed_len));
TEST_ASSERT_EQUAL(0, mbedtls_mpi_mod_mpi(scalar, scalar, &range));
TEST_ASSERT_EQUAL(0, mbedtls_mpi_add_int(scalar, scalar, 1));
mbedtls_mpi_free(&range);
}
static void test_make_p256_affine_point(mbedtls_ecp_group *grp,
unsigned int multiplier,
mbedtls_ecp_point *point)
{
mbedtls_mpi k;
mbedtls_mpi_init(&k);
TEST_ASSERT_EQUAL(0, mbedtls_mpi_lset(&k, multiplier));
TEST_ASSERT_EQUAL(0, mbedtls_ecp_mul(grp, point, &k, &grp->G,
test_mbedtls_rng, NULL));
TEST_ASSERT_EQUAL(0, mbedtls_mpi_cmp_int(&point->MBEDTLS_PRIVATE(Z), 1));
mbedtls_mpi_free(&k);
}
static int test_legendre_reference(const mbedtls_mpi *a, const mbedtls_mpi *p)
{
mbedtls_mpi a_mod, exp, res, one, pm1;
int legendre = -2;
mbedtls_mpi_init(&a_mod);
mbedtls_mpi_init(&exp);
mbedtls_mpi_init(&res);
mbedtls_mpi_init(&one);
mbedtls_mpi_init(&pm1);
TEST_ASSERT_EQUAL(0, mbedtls_mpi_mod_mpi(&a_mod, a, p));
if (mbedtls_mpi_cmp_int(&a_mod, 0) == 0) {
legendre = 0;
goto cleanup;
}
TEST_ASSERT_EQUAL(0, mbedtls_mpi_copy(&exp, p));
TEST_ASSERT_EQUAL(0, mbedtls_mpi_sub_int(&exp, &exp, 1));
TEST_ASSERT_EQUAL(0, mbedtls_mpi_shift_r(&exp, 1));
TEST_ASSERT_EQUAL(0, mbedtls_mpi_exp_mod(&res, &a_mod, &exp, p, NULL));
TEST_ASSERT_EQUAL(0, mbedtls_mpi_lset(&one, 1));
if (mbedtls_mpi_cmp_mpi(&res, &one) == 0) {
legendre = 1;
goto cleanup;
}
TEST_ASSERT_EQUAL(0, mbedtls_mpi_copy(&pm1, p));
TEST_ASSERT_EQUAL(0, mbedtls_mpi_sub_int(&pm1, &pm1, 1));
if (mbedtls_mpi_cmp_mpi(&res, &pm1) == 0) {
legendre = -1;
goto cleanup;
}
TEST_FAIL_MESSAGE("Unexpected Legendre reference result");
cleanup:
mbedtls_mpi_free(&a_mod);
mbedtls_mpi_free(&exp);
mbedtls_mpi_free(&res);
mbedtls_mpi_free(&one);
mbedtls_mpi_free(&pm1);
return legendre;
}
static void test_print_crypto_timing(const char *label,
int64_t generic_total_us, size_t generic_ops,
int64_t api_total_us, size_t api_ops)
{
long long generic_avg = generic_ops ? (long long)(generic_total_us / (int64_t) generic_ops) : 0;
long long api_avg = api_ops ? (long long)(api_total_us / (int64_t) api_ops) : 0;
printf("%s timing(us): generic_avg=%lld api_avg=%lld generic_total=%lld api_total=%lld ops=%u\n",
label, generic_avg, api_avg,
(long long) generic_total_us, (long long) api_total_us,
(unsigned int) api_ops);
}
static int test_mbedtls_ecdh(const struct crypto_ec_key *key_own,
const struct crypto_ec_key *key_peer,
u8 *secret, size_t *secret_len)
{
mbedtls_ecdh_context ctx;
mbedtls_pk_context *own = (mbedtls_pk_context *) key_own;
mbedtls_pk_context *peer = (mbedtls_pk_context *) key_peer;
int ret = -1;
mbedtls_ecdh_init(&ctx);
if (mbedtls_ecdh_get_params(&ctx, mbedtls_pk_ec(*own),
MBEDTLS_ECDH_OURS) != 0) {
goto out;
}
if (mbedtls_ecdh_get_params(&ctx, mbedtls_pk_ec(*peer),
MBEDTLS_ECDH_THEIRS) != 0) {
goto out;
}
if (mbedtls_ecdh_calc_secret(&ctx, secret_len, secret, 66,
test_mbedtls_rng, NULL) != 0) {
goto out;
}
ret = 0;
out:
mbedtls_ecdh_free(&ctx);
return ret;
}
static int test_mbedtls_key_gen_p256(mbedtls_pk_context *kctx)
{
mbedtls_pk_init(kctx);
if (mbedtls_pk_setup(kctx,
mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)) != 0) {
return -1;
}
if (mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(*kctx),
test_mbedtls_rng, NULL) != 0) {
mbedtls_pk_free(kctx);
return -1;
}
return 0;
}
TEST_CASE("Test crypto lib bignum apis", "[wpa_crypto]")
{
set_leak_threshold(300);
@@ -203,6 +402,38 @@ TEST_CASE("Test crypto lib bignum apis", "[wpa_crypto]")
}
{ /** BN mul mod on secp256r1 prime */
uint8_t val[32];
uint8_t one[32] = {0};
crypto_bignum *bn1, *bn2, *bn3, *mulmod;
one[0] = 1;
os_memcpy(val, test_secp256r1_prime, sizeof(val));
val[31]--;
mulmod = crypto_bignum_init();
TEST_ASSERT_NOT_NULL(mulmod);
bn1 = crypto_bignum_init_set(val, sizeof(val));
TEST_ASSERT_NOT_NULL(bn1);
bn2 = crypto_bignum_init_set(val, sizeof(val));
TEST_ASSERT_NOT_NULL(bn2);
bn3 = crypto_bignum_init_set(test_secp256r1_prime,
sizeof(test_secp256r1_prime));
TEST_ASSERT_NOT_NULL(bn3);
TEST_ASSERT(crypto_bignum_mulmod(bn1, bn2, bn3, mulmod) == 0);
TEST_ASSERT(crypto_bignum_to_bin(mulmod, val, sizeof(val), 0) == 1);
TEST_ASSERT_EQUAL_UINT8_ARRAY(one, val, 1);
crypto_bignum_deinit(bn1, 1);
crypto_bignum_deinit(bn2, 1);
crypto_bignum_deinit(bn3, 1);
crypto_bignum_deinit(mulmod, 1);
}
{ /** BN exp mod*/
uint8_t buf1[32], buf2[32], buf3[32], buf4[32], buf5[32];
@@ -273,6 +504,84 @@ TEST_CASE("Test crypto lib bignum apis", "[wpa_crypto]")
crypto_bignum_deinit(bn2, 1);
}
{ /** BN Legendre symbol test on secp256r1 prime */
uint8_t val[32] = {0};
crypto_bignum *bn_val, *bn_p;
bn_p = crypto_bignum_init_set(test_secp256r1_prime,
sizeof(test_secp256r1_prime));
TEST_ASSERT_NOT_NULL(bn_p);
val[31] = 1;
bn_val = crypto_bignum_init_set(val, sizeof(val));
TEST_ASSERT_NOT_NULL(bn_val);
TEST_ASSERT(crypto_bignum_legendre(bn_val, bn_p) == 1);
crypto_bignum_deinit(bn_val, 1);
os_memset(val, 0, sizeof(val));
val[31] = 3;
bn_val = crypto_bignum_init_set(val, sizeof(val));
TEST_ASSERT_NOT_NULL(bn_val);
TEST_ASSERT(crypto_bignum_legendre(bn_val, bn_p) == -1);
crypto_bignum_deinit(bn_val, 1);
os_memset(val, 0, sizeof(val));
bn_val = crypto_bignum_init_set(val, sizeof(val));
TEST_ASSERT_NOT_NULL(bn_val);
TEST_ASSERT(crypto_bignum_legendre(bn_val, bn_p) == 0);
crypto_bignum_deinit(bn_val, 1);
crypto_bignum_deinit(bn_p, 1);
}
}
TEST_CASE("Test secp256r1 fast bignum paths against mbedtls reference", "[wpa_crypto]")
{
crypto_bignum *bn_p;
int i;
set_leak_threshold(620);
bn_p = crypto_bignum_init_set(test_secp256r1_prime,
sizeof(test_secp256r1_prime));
TEST_ASSERT_NOT_NULL(bn_p);
for (i = 0; i < ARRAY_SIZE(test_p256_bignum_vals); i++) {
crypto_bignum *bn_a = crypto_bignum_init_set(test_p256_bignum_vals[i], sizeof(test_p256_bignum_vals[i]));
crypto_bignum *bn_b = crypto_bignum_init_set(
test_p256_bignum_vals[(i + 1) % ARRAY_SIZE(test_p256_bignum_vals)],
sizeof(test_p256_bignum_vals[0]));
crypto_bignum *bn_mul = crypto_bignum_init();
mbedtls_mpi ref_mul;
int ref_legendre;
TEST_ASSERT_NOT_NULL(bn_a);
TEST_ASSERT_NOT_NULL(bn_b);
TEST_ASSERT_NOT_NULL(bn_mul);
mbedtls_mpi_init(&ref_mul);
TEST_ASSERT_EQUAL(0, mbedtls_mpi_mul_mpi(&ref_mul,
(const mbedtls_mpi *) bn_a,
(const mbedtls_mpi *) bn_b));
TEST_ASSERT_EQUAL(0, mbedtls_mpi_mod_mpi(&ref_mul, &ref_mul,
(const mbedtls_mpi *) bn_p));
TEST_ASSERT_EQUAL(0, crypto_bignum_mulmod(bn_a, bn_b, bn_p, bn_mul));
TEST_ASSERT_EQUAL(0, mbedtls_mpi_cmp_mpi((const mbedtls_mpi *) bn_mul,
&ref_mul));
ref_legendre = test_legendre_reference((const mbedtls_mpi *) bn_a,
(const mbedtls_mpi *) bn_p);
TEST_ASSERT_EQUAL(ref_legendre, crypto_bignum_legendre(bn_a, bn_p));
mbedtls_mpi_free(&ref_mul);
crypto_bignum_deinit(bn_a, 1);
crypto_bignum_deinit(bn_b, 1);
crypto_bignum_deinit(bn_mul, 1);
}
crypto_bignum_deinit(bn_p, 1);
}
/*
@@ -536,3 +845,385 @@ TEST_CASE("Test crypto lib ECC apis", "[wpa_crypto]")
}
}
TEST_CASE("Test secp256r1 point multiply against mbedtls reference", "[wpa_crypto]")
{
struct crypto_ec *e;
struct crypto_ec_point *p = NULL;
struct crypto_ec_point *res = NULL;
mbedtls_ecp_point ref;
int i, j;
set_leak_threshold(620);
e = crypto_ec_init(19);
TEST_ASSERT_NOT_NULL(e);
p = crypto_ec_point_init(e);
TEST_ASSERT_NOT_NULL(p);
res = crypto_ec_point_init(e);
TEST_ASSERT_NOT_NULL(res);
mbedtls_ecp_point_init(&ref);
for (i = 0; i < ARRAY_SIZE(test_p256_point_multipliers); i++) {
test_make_p256_affine_point((mbedtls_ecp_group *) e,
test_p256_point_multipliers[i],
(mbedtls_ecp_point *) p);
for (j = 0; j < ARRAY_SIZE(test_p256_scalar_seeds); j++) {
mbedtls_mpi scalar;
mbedtls_mpi_init(&scalar);
test_load_valid_p256_scalar((const mbedtls_ecp_group *) e,
test_p256_scalar_seeds[j],
sizeof(test_p256_scalar_seeds[j]),
&scalar);
TEST_ASSERT_EQUAL(0, crypto_ec_point_mul(e, p,
(struct crypto_bignum *) &scalar,
res));
TEST_ASSERT_EQUAL(0, mbedtls_ecp_mul((mbedtls_ecp_group *) e,
&ref, &scalar,
(const mbedtls_ecp_point *) p,
test_mbedtls_rng, NULL));
TEST_ASSERT_EQUAL(0, crypto_ec_point_cmp(e, res,
(const struct crypto_ec_point *) &ref));
mbedtls_mpi_free(&scalar);
}
}
mbedtls_ecp_point_free(&ref);
crypto_ec_point_deinit(p, 1);
crypto_ec_point_deinit(res, 1);
crypto_ec_deinit(e);
}
TEST_CASE("Measure secp256r1 bignum API timings against mbedtls reference", "[wpa_crypto]")
{
const unsigned int loops = 64;
crypto_bignum *bn_p;
int64_t generic_total_us = 0;
int64_t api_total_us = 0;
size_t ops = 0;
int i, loop;
set_leak_threshold(700);
bn_p = crypto_bignum_init_set(test_secp256r1_prime,
sizeof(test_secp256r1_prime));
TEST_ASSERT_NOT_NULL(bn_p);
for (i = 0; i < ARRAY_SIZE(test_p256_bignum_vals); i++) {
crypto_bignum *bn_a = crypto_bignum_init_set(test_p256_bignum_vals[i],
sizeof(test_p256_bignum_vals[i]));
crypto_bignum *bn_b = crypto_bignum_init_set(
test_p256_bignum_vals[(i + 1) % ARRAY_SIZE(test_p256_bignum_vals)],
sizeof(test_p256_bignum_vals[0]));
crypto_bignum *bn_mul = crypto_bignum_init();
mbedtls_mpi ref_mul;
int ref_legendre;
TEST_ASSERT_NOT_NULL(bn_a);
TEST_ASSERT_NOT_NULL(bn_b);
TEST_ASSERT_NOT_NULL(bn_mul);
mbedtls_mpi_init(&ref_mul);
TEST_ASSERT_EQUAL(0, mbedtls_mpi_mul_mpi(&ref_mul,
(const mbedtls_mpi *) bn_a,
(const mbedtls_mpi *) bn_b));
TEST_ASSERT_EQUAL(0, mbedtls_mpi_mod_mpi(&ref_mul, &ref_mul,
(const mbedtls_mpi *) bn_p));
TEST_ASSERT_EQUAL(0, crypto_bignum_mulmod(bn_a, bn_b, bn_p, bn_mul));
TEST_ASSERT_EQUAL(0, mbedtls_mpi_cmp_mpi((const mbedtls_mpi *) bn_mul,
&ref_mul));
ref_legendre = test_legendre_reference((const mbedtls_mpi *) bn_a,
(const mbedtls_mpi *) bn_p);
TEST_ASSERT_EQUAL(ref_legendre, crypto_bignum_legendre(bn_a, bn_p));
for (loop = 0; loop < loops; loop++) {
int64_t start_us = esp_timer_get_time();
TEST_ASSERT_EQUAL(0, mbedtls_mpi_mul_mpi(&ref_mul,
(const mbedtls_mpi *) bn_a,
(const mbedtls_mpi *) bn_b));
TEST_ASSERT_EQUAL(0, mbedtls_mpi_mod_mpi(&ref_mul, &ref_mul,
(const mbedtls_mpi *) bn_p));
generic_total_us += esp_timer_get_time() - start_us;
}
for (loop = 0; loop < loops; loop++) {
int64_t start_us = esp_timer_get_time();
TEST_ASSERT_EQUAL(0, crypto_bignum_mulmod(bn_a, bn_b, bn_p, bn_mul));
api_total_us += esp_timer_get_time() - start_us;
}
ops += loops;
mbedtls_mpi_free(&ref_mul);
crypto_bignum_deinit(bn_a, 1);
crypto_bignum_deinit(bn_b, 1);
crypto_bignum_deinit(bn_mul, 1);
}
test_print_crypto_timing("secp256r1 mulmod", generic_total_us, ops,
api_total_us, ops);
generic_total_us = 0;
api_total_us = 0;
ops = 0;
for (i = 0; i < ARRAY_SIZE(test_small_exponents); i++) {
crypto_bignum *bn_exp = crypto_bignum_init_uint(test_small_exponents[i]);
char label[48];
TEST_ASSERT_NOT_NULL(bn_exp);
generic_total_us = 0;
api_total_us = 0;
ops = 0;
for (loop = 0; loop < ARRAY_SIZE(test_p256_bignum_vals); loop++) {
crypto_bignum *bn_a = crypto_bignum_init_set(test_p256_bignum_vals[loop],
sizeof(test_p256_bignum_vals[loop]));
crypto_bignum *bn_res = crypto_bignum_init();
mbedtls_mpi ref_res;
int iter;
TEST_ASSERT_NOT_NULL(bn_a);
TEST_ASSERT_NOT_NULL(bn_res);
mbedtls_mpi_init(&ref_res);
TEST_ASSERT_EQUAL(0, mbedtls_mpi_exp_mod(&ref_res,
(const mbedtls_mpi *) bn_a,
(const mbedtls_mpi *) bn_exp,
(const mbedtls_mpi *) bn_p,
NULL));
TEST_ASSERT_EQUAL(0, crypto_bignum_exptmod(bn_a, bn_exp, bn_p,
bn_res));
TEST_ASSERT_EQUAL(0, mbedtls_mpi_cmp_mpi((const mbedtls_mpi *) bn_res,
&ref_res));
for (iter = 0; iter < loops; iter++) {
int64_t start_us = esp_timer_get_time();
TEST_ASSERT_EQUAL(0, mbedtls_mpi_exp_mod(&ref_res,
(const mbedtls_mpi *) bn_a,
(const mbedtls_mpi *) bn_exp,
(const mbedtls_mpi *) bn_p,
NULL));
generic_total_us += esp_timer_get_time() - start_us;
}
for (iter = 0; iter < loops; iter++) {
int64_t start_us = esp_timer_get_time();
TEST_ASSERT_EQUAL(0, crypto_bignum_exptmod(bn_a, bn_exp, bn_p,
bn_res));
api_total_us += esp_timer_get_time() - start_us;
}
ops += loops;
mbedtls_mpi_free(&ref_res);
crypto_bignum_deinit(bn_a, 1);
crypto_bignum_deinit(bn_res, 1);
}
snprintf(label, sizeof(label), "secp256r1 exptmod e=%u",
test_small_exponents[i]);
test_print_crypto_timing(label, generic_total_us, ops,
api_total_us, ops);
crypto_bignum_deinit(bn_exp, 1);
}
generic_total_us = 0;
api_total_us = 0;
ops = 0;
for (i = 0; i < ARRAY_SIZE(test_p256_bignum_vals); i++) {
crypto_bignum *bn_a = crypto_bignum_init_set(test_p256_bignum_vals[i],
sizeof(test_p256_bignum_vals[i]));
TEST_ASSERT_NOT_NULL(bn_a);
TEST_ASSERT_EQUAL(test_legendre_reference((const mbedtls_mpi *) bn_a,
(const mbedtls_mpi *) bn_p),
crypto_bignum_legendre(bn_a, bn_p));
for (loop = 0; loop < loops; loop++) {
int64_t start_us = esp_timer_get_time();
(void) test_legendre_reference((const mbedtls_mpi *) bn_a,
(const mbedtls_mpi *) bn_p);
generic_total_us += esp_timer_get_time() - start_us;
}
for (loop = 0; loop < loops; loop++) {
int64_t start_us = esp_timer_get_time();
(void) crypto_bignum_legendre(bn_a, bn_p);
api_total_us += esp_timer_get_time() - start_us;
}
ops += loops;
crypto_bignum_deinit(bn_a, 1);
}
test_print_crypto_timing("secp256r1 legendre", generic_total_us, ops,
api_total_us, ops);
crypto_bignum_deinit(bn_p, 1);
}
TEST_CASE("Measure secp256r1 EC API timings against mbedtls reference", "[wpa_crypto]")
{
const unsigned int point_mul_loops = 4;
const unsigned int key_gen_loops = 4;
const unsigned int ecdh_loops = 4;
struct crypto_ec *e;
struct crypto_ec_point *p = NULL;
struct crypto_ec_point *res = NULL;
mbedtls_ecp_point ref;
int64_t generic_total_us = 0;
int64_t api_total_us = 0;
size_t ops = 0;
int i, j, loop;
set_leak_threshold(900);
e = crypto_ec_init(19);
TEST_ASSERT_NOT_NULL(e);
p = crypto_ec_point_init(e);
TEST_ASSERT_NOT_NULL(p);
res = crypto_ec_point_init(e);
TEST_ASSERT_NOT_NULL(res);
mbedtls_ecp_point_init(&ref);
for (i = 0; i < ARRAY_SIZE(test_p256_point_multipliers); i++) {
test_make_p256_affine_point((mbedtls_ecp_group *) e,
test_p256_point_multipliers[i],
(mbedtls_ecp_point *) p);
for (j = 0; j < ARRAY_SIZE(test_p256_scalar_seeds); j++) {
mbedtls_mpi scalar;
mbedtls_mpi_init(&scalar);
test_load_valid_p256_scalar((const mbedtls_ecp_group *) e,
test_p256_scalar_seeds[j],
sizeof(test_p256_scalar_seeds[j]),
&scalar);
TEST_ASSERT_EQUAL(0, crypto_ec_point_mul(e, p,
(struct crypto_bignum *) &scalar,
res));
TEST_ASSERT_EQUAL(0, mbedtls_ecp_mul((mbedtls_ecp_group *) e,
&ref, &scalar,
(const mbedtls_ecp_point *) p,
test_mbedtls_rng, NULL));
TEST_ASSERT_EQUAL(0, crypto_ec_point_cmp(e, res,
(const struct crypto_ec_point *) &ref));
for (loop = 0; loop < point_mul_loops; loop++) {
int64_t start_us = esp_timer_get_time();
TEST_ASSERT_EQUAL(0, mbedtls_ecp_mul((mbedtls_ecp_group *) e,
&ref, &scalar,
(const mbedtls_ecp_point *) p,
test_mbedtls_rng, NULL));
generic_total_us += esp_timer_get_time() - start_us;
}
for (loop = 0; loop < point_mul_loops; loop++) {
int64_t start_us = esp_timer_get_time();
TEST_ASSERT_EQUAL(0, crypto_ec_point_mul(e, p,
(struct crypto_bignum *) &scalar,
res));
api_total_us += esp_timer_get_time() - start_us;
}
ops += point_mul_loops;
mbedtls_mpi_free(&scalar);
}
}
test_print_crypto_timing("secp256r1 point_mul", generic_total_us, ops,
api_total_us, ops);
generic_total_us = 0;
api_total_us = 0;
for (loop = 0; loop < key_gen_loops; loop++) {
mbedtls_pk_context kctx;
int64_t start_us = esp_timer_get_time();
TEST_ASSERT_EQUAL(0, test_mbedtls_key_gen_p256(&kctx));
generic_total_us += esp_timer_get_time() - start_us;
mbedtls_pk_free(&kctx);
}
for (loop = 0; loop < key_gen_loops; loop++) {
struct crypto_ec_key *key;
int64_t start_us = esp_timer_get_time();
key = crypto_ec_key_gen(19);
TEST_ASSERT_NOT_NULL(key);
api_total_us += esp_timer_get_time() - start_us;
crypto_ec_key_deinit(key);
}
test_print_crypto_timing("secp256r1 key_gen", generic_total_us, key_gen_loops,
api_total_us, key_gen_loops);
{
struct crypto_ec_key *key_own = crypto_ec_key_gen(19);
struct crypto_ec_key *key_peer = crypto_ec_key_gen(19);
u8 secret_generic[66];
u8 secret_api[66];
size_t secret_generic_len = 0;
size_t secret_api_len = 0;
TEST_ASSERT_NOT_NULL(key_own);
TEST_ASSERT_NOT_NULL(key_peer);
TEST_ASSERT_EQUAL(0, test_mbedtls_ecdh(key_own, key_peer,
secret_generic,
&secret_generic_len));
TEST_ASSERT_EQUAL(0, crypto_ecdh(key_own, key_peer,
secret_api, &secret_api_len));
TEST_ASSERT_EQUAL(secret_generic_len, secret_api_len);
TEST_ASSERT_EQUAL_MEMORY(secret_generic, secret_api, secret_api_len);
generic_total_us = 0;
api_total_us = 0;
for (loop = 0; loop < ecdh_loops; loop++) {
int64_t start_us = esp_timer_get_time();
size_t secret_len = 0;
TEST_ASSERT_EQUAL(0, test_mbedtls_ecdh(key_own, key_peer,
secret_generic,
&secret_len));
generic_total_us += esp_timer_get_time() - start_us;
}
for (loop = 0; loop < ecdh_loops; loop++) {
int64_t start_us = esp_timer_get_time();
size_t secret_len = 0;
TEST_ASSERT_EQUAL(0, crypto_ecdh(key_own, key_peer,
secret_api, &secret_len));
api_total_us += esp_timer_get_time() - start_us;
}
test_print_crypto_timing("secp256r1 ecdh", generic_total_us, ecdh_loops,
api_total_us, ecdh_loops);
crypto_ec_key_deinit(key_own);
crypto_ec_key_deinit(key_peer);
}
mbedtls_ecp_point_free(&ref);
crypto_ec_point_deinit(p, 1);
crypto_ec_point_deinit(res, 1);
crypto_ec_deinit(e);
}
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -18,8 +18,16 @@
#include "common/dpp.h"
#include "sdkconfig.h"
#include "test_wpa_supplicant_common.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#ifdef CONFIG_ESP_WIFI_TESTING_OPTIONS
static unsigned int dpp_test_task_stack_high_watermark_bytes(void)
{
return (unsigned int)(uxTaskGetStackHighWaterMark(NULL) *
sizeof(StackType_t));
}
struct dpp_global {
void *msg_ctx;
struct dl_list bootstrap; /* struct dpp_bootstrap_info */
@@ -32,9 +40,46 @@ extern u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
extern size_t dpp_nonce_override_len;
#define MAX_FRAME_SIZE 1200
static void dpp_test_clear_overrides(void)
{
dpp_protocol_key_override_len = 0;
dpp_nonce_override_len = 0;
os_memset(dpp_protocol_key_override, 0, sizeof(dpp_protocol_key_override));
os_memset(dpp_nonce_override, 0, sizeof(dpp_nonce_override));
}
static u32 dpp_test_prod_limit_us(void)
{
#if CONFIG_MBEDTLS_HARDWARE_ECC
return 200000;
#else
return 425000;
#endif
}
static int dpp_test_leak_threshold(void)
{
return 800;
}
static void dpp_test_log_auth_timing(const char *label,
const struct dpp_authentication *auth)
{
TEST_ASSERT_NOT_NULL(auth);
ESP_LOGI("DPP Test",
"%s timing(us): parse=%llu response_form=%llu total=%llu",
label,
(unsigned long long) auth->auth_req_parse_us,
(unsigned long long) auth->auth_resp_form_us,
(unsigned long long) auth->auth_req_total_us);
ESP_LOGI("DPP Test", "%s task stack high watermark(bytes): %u",
label, dpp_test_task_stack_high_watermark_bytes());
}
TEST_CASE("Test vectors DPP responder p256", "[wpa_dpp]")
{
set_leak_threshold(130);
set_leak_threshold(dpp_test_leak_threshold());
/* Global variables */
char command[1200] = {0};
const u8 *frame;
@@ -66,6 +111,10 @@ TEST_CASE("Test vectors DPP responder p256", "[wpa_dpp]")
sprintf(command, "type=qrcode key=%s", key);
id = dpp_bootstrap_gen(dpp, command);
uri = dpp_bootstrap_get_uri(dpp, id);
if (uri == NULL) {
ESP_LOGE("DPP Test", "Failed to get URI from bootstrap id");
TEST_ASSERT(0);
}
printf("uri is =%s\n", uri);
printf("is be =%s\n", bootstrap_info);
TEST_ASSERT((strcmp(uri, bootstrap_info) == 0));
@@ -129,6 +178,9 @@ TEST_CASE("Test vectors DPP responder p256", "[wpa_dpp]")
len -= 26;
auth_instance = dpp_auth_req_rx(NULL, 1, 0, NULL,
dpp_bootstrap_get_id(dpp, id), 2412, frame, frame + 6, len - 6);
TEST_ASSERT_NOT_NULL(auth_instance);
TEST_ASSERT_NOT_NULL(auth_instance->resp_msg);
dpp_test_log_auth_timing("Vector responder", auth_instance);
/* auth response u8 */
hex_len = os_strlen(auth_resp);
@@ -172,7 +224,118 @@ TEST_CASE("Test vectors DPP responder p256", "[wpa_dpp]")
{
dpp_auth_deinit(auth_instance);
dpp_global_deinit(dpp);
dpp_test_clear_overrides();
}
ESP_LOGI("DPP Test", "Test case passed");
}
TEST_CASE("Test DPP responder p256 production timing", "[wpa_dpp][performance]")
{
struct dpp_global_config dpp_conf;
struct dpp_global *dpp = NULL;
struct dpp_bootstrap_info *responder_bi = NULL;
struct dpp_bootstrap_info *initiator_bi = NULL;
struct dpp_authentication *initiator_auth = NULL;
struct dpp_authentication *responder_auth = NULL;
struct wpabuf *conf = NULL;
const u8 *frame;
size_t len;
int responder_id;
int initiator_id;
u32 limit_us = dpp_test_prod_limit_us();
u64 total_us = 0;
const char *failure = NULL;
set_leak_threshold(dpp_test_leak_threshold());
os_memset(&dpp_conf, 0, sizeof(dpp_conf));
dpp = dpp_global_init(&dpp_conf);
if (!dpp) {
TEST_FAIL_MESSAGE("Failed to initialize DPP global context");
}
responder_id = dpp_bootstrap_gen(dpp, "type=qrcode curve=P-256");
if (responder_id <= 0) {
failure = "Failed to generate responder bootstrap";
goto cleanup;
}
initiator_id = dpp_bootstrap_gen(dpp, "type=qrcode curve=P-256");
if (initiator_id <= 0) {
failure = "Failed to generate initiator bootstrap";
goto cleanup;
}
responder_bi = dpp_bootstrap_get_id(dpp, responder_id);
initiator_bi = dpp_bootstrap_get_id(dpp, initiator_id);
if (!responder_bi || !initiator_bi) {
failure = "Failed to resolve bootstrap info";
goto cleanup;
}
dpp_test_clear_overrides();
initiator_auth = dpp_auth_init(NULL, responder_bi, initiator_bi,
DPP_CAPAB_CONFIGURATOR, 2412, NULL, 0);
if (!initiator_auth || !initiator_auth->req_msg) {
failure = "Failed to initialize DPP initiator authentication";
goto cleanup;
}
frame = wpabuf_head_u8(initiator_auth->req_msg) + 2;
len = wpabuf_len(initiator_auth->req_msg) - 2;
responder_auth = dpp_auth_req_rx(NULL, DPP_CAPAB_ENROLLEE, 0,
NULL, responder_bi, 2412,
frame, frame + DPP_HDR_LEN,
len - DPP_HDR_LEN);
if (!responder_auth || !responder_auth->resp_msg) {
failure = "Failed to process DPP authentication request";
goto cleanup;
}
dpp_test_log_auth_timing("Production responder", responder_auth);
total_us = responder_auth->auth_req_total_us;
if (limit_us) {
ESP_LOGI("DPP Test",
"Production responder timing gate(us): total=%llu limit=%u",
(unsigned long long) total_us,
limit_us);
}
frame = wpabuf_head_u8(responder_auth->resp_msg) + 2;
len = wpabuf_len(responder_auth->resp_msg) - 2;
conf = dpp_auth_resp_rx(initiator_auth, frame, frame + DPP_HDR_LEN,
len - DPP_HDR_LEN);
if (!conf) {
failure = "Failed to process DPP authentication response";
goto cleanup;
}
if (initiator_auth->auth_success != 1) {
failure = "Initiator authentication did not complete successfully";
goto cleanup;
}
frame = wpabuf_head_u8(conf) + 2;
len = wpabuf_len(conf) - 2;
if (dpp_auth_conf_rx(responder_auth, frame, frame + DPP_HDR_LEN,
len - DPP_HDR_LEN) != 0) {
failure = "Failed to process DPP authentication confirmation";
goto cleanup;
}
if (responder_auth->auth_success != 1) {
failure = "Responder authentication did not complete successfully";
goto cleanup;
}
cleanup:
wpabuf_free(conf);
dpp_auth_deinit(responder_auth);
dpp_auth_deinit(initiator_auth);
dpp_global_deinit(dpp);
dpp_test_clear_overrides();
if (failure) {
TEST_FAIL_MESSAGE(failure);
}
if (limit_us) {
TEST_ASSERT_MESSAGE(total_us <= limit_us,
"DPP responder production timing regression");
}
}
#endif
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -20,9 +20,41 @@
#include "utils/wpabuf.h"
#include "test_utils.h"
#include "test_wpa_supplicant_common.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
typedef struct crypto_bignum crypto_bignum;
static unsigned int test_task_stack_high_watermark_bytes(void)
{
return (unsigned int)(uxTaskGetStackHighWaterMark(NULL) *
sizeof(StackType_t));
}
static int sae_commit_parse_limit_us(void)
{
#if CONFIG_IDF_TARGET_ESP32
return 400000;
#elif CONFIG_IDF_TARGET_ESP32S3
return 300000;
#elif CONFIG_IDF_TARGET_ESP32S2
return 380000;
#elif CONFIG_IDF_TARGET_ESP32C3
return 340000;
#elif CONFIG_IDF_TARGET_ESP32C5
return 130000;
#elif CONFIG_IDF_TARGET_ESP32C6
return 180000;
#elif CONFIG_IDF_TARGET_ESP32C61
return 200000;
#elif CONFIG_IDF_TARGET_ESP32C2
return 230000;
#else
return 230000;
#endif
}
static struct wpabuf *wpabuf_alloc2(size_t len)
{
struct wpabuf *buf = (struct wpabuf *)os_zalloc(sizeof(struct wpabuf) + len);
@@ -233,26 +265,52 @@ TEST_CASE("Test SAE functionality with ECC group", "[wpa3_sae]")
u8 pwd[] = "ESP32-WPA3";
struct wpabuf *buf;
int default_groups[] = { IANA_SECP256R1, 0 };
int64_t start_us;
int64_t total_start_us;
int64_t total_us;
int64_t prepare_us;
int64_t write_us;
int64_t parse_us;
int64_t formation_us;
int limit_us = sae_commit_parse_limit_us();
memset(&sae, 0, sizeof(sae));
total_start_us = esp_timer_get_time();
TEST_ASSERT(sae_set_group(&sae, IANA_SECP256R1) == 0);
start_us = esp_timer_get_time();
TEST_ASSERT(sae_prepare_commit(addr1, addr2, pwd, strlen((const char *)pwd), &sae) == 0);
prepare_us = esp_timer_get_time() - start_us;
buf = wpabuf_alloc2(SAE_COMMIT_MAX_LEN);
TEST_ASSERT(buf != NULL);
start_us = esp_timer_get_time();
sae_write_commit(&sae, buf, NULL, NULL);// No anti-clogging token
write_us = esp_timer_get_time() - start_us;
formation_us = prepare_us + write_us;
/* Parsing commit created by self will be detected as reflection attack*/
start_us = esp_timer_get_time();
TEST_ASSERT(sae_parse_commit(&sae,
wpabuf_mhead(buf), buf->used, NULL, 0, default_groups, 0) == SAE_SILENTLY_DISCARD);
parse_us = esp_timer_get_time() - start_us;
wpabuf_free2(buf);
sae_clear_temp_data(&sae);
sae_clear_data(&sae);
total_us = esp_timer_get_time() - total_start_us;
ESP_LOGI("SAE Test",
"Commit/parse timing(us): prepare=%lld write=%lld formation=%lld parse=%lld total=%lld limit=%d",
(long long) prepare_us, (long long) write_us,
(long long) formation_us, (long long) parse_us,
(long long) total_us, limit_us);
ESP_LOGI("SAE Test", "Task stack high watermark(bytes): %u",
test_task_stack_high_watermark_bytes());
TEST_ASSERT_MESSAGE(total_us <= limit_us, "SAE commit/parse timing regression");
}
ESP_LOGI("SAE Test", "=========== Complete ============");
@@ -30,7 +30,7 @@ static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d, threshold %d)\n", type, before_free, after_free, delta, leak_threshold);
TEST_ASSERT_MESSAGE(delta > leak_threshold, "memory leak");
TEST_ASSERT_MESSAGE(delta >= leak_threshold, "memory leak");
}
#if SOC_SHA_SUPPORT_SHA512
@@ -1,5 +1,7 @@
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_ESP_WIFI_TESTING_OPTIONS=y
CONFIG_ESP_WIFI_DEBUG_PRINT=y
CONFIG_ESP_WIFI_DPP_SUPPORT=y
CONFIG_ESP_WIFI_ENABLE_WPA3_SAE=y
CONFIG_ESP_WIFI_P256_ACCEL=y