From 46371708005b402e32132d8e9e0adf113fce6855 Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Mon, 23 Mar 2026 14:39:33 +0530 Subject: [PATCH 1/2] fix(esp_wifi): Optimize crypto operations for SAE - Montgomery multiplication fast path for P-256 mulmod - Jacobi symbol for legendre (replacing exp_mod) - Software Jacobian point multiplication for MPI-only chips - ECC hardware acceleration for supported chips - ECDH fast path for P-256 --- components/esp_wifi/Kconfig | 12 + .../src/crypto/crypto_mbedtls-bignum.c | 328 ++++- .../src/crypto/crypto_mbedtls-ec.c | 1170 +++++++++++++++-- .../esp_supplicant/src/crypto/p256_common.h | 236 ++++ 4 files changed, 1620 insertions(+), 126 deletions(-) create mode 100644 components/wpa_supplicant/esp_supplicant/src/crypto/p256_common.h diff --git a/components/esp_wifi/Kconfig b/components/esp_wifi/Kconfig index 9bd93843c15..a45524403a4 100644 --- a/components/esp_wifi/Kconfig +++ b/components/esp_wifi/Kconfig @@ -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 y + 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 diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-bignum.c b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-bignum.c index 61f8840729c..41c81b7f76b 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-bignum.c +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-bignum.c @@ -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, diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c index b7c9fab6972..8c0453b9b62 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c @@ -1,13 +1,18 @@ /* - * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #ifdef ESP_PLATFORM #include "esp_system.h" +#include "esp_random.h" +#include "soc/soc_caps.h" #include "mbedtls/bignum.h" #include "mbedtls/esp_mbedtls_random.h" +#if CONFIG_MBEDTLS_HARDWARE_ECC +#include "ecc_impl.h" +#endif #endif #include "utils/includes.h" @@ -24,6 +29,8 @@ #include "mbedtls/asn1write.h" #include "mbedtls/error.h" #include "mbedtls/oid.h" +#include "mbedtls/platform_util.h" +#include "p256_common.h" #define ECP_PRV_DER_MAX_BYTES ( 29 + 3 * MBEDTLS_ECP_MAX_BYTES ) #define ECP_PUB_DER_MAX_BYTES ( 30 + 2 * MBEDTLS_ECP_MAX_BYTES ) @@ -34,26 +41,131 @@ #define ACCESS_ECDH(S, var) S->MBEDTLS_PRIVATE(ctx).MBEDTLS_PRIVATE(mbed_ecdh).MBEDTLS_PRIVATE(var) #endif +#define ESP_WIFI_P256_SOFT_ACCEL \ + (CONFIG_ESP_WIFI_P256_ACCEL && CONFIG_MBEDTLS_HARDWARE_MPI) + +#define ESP_WIFI_P256_SOFT_ACCEL_PREFERRED \ + (ESP_WIFI_P256_SOFT_ACCEL && !CONFIG_MBEDTLS_HARDWARE_ECC) + +#if CONFIG_MBEDTLS_HARDWARE_ECC +static bool crypto_ec_point_mul_curve_supported(const mbedtls_ecp_group *grp) +{ + switch (grp->id) { + case MBEDTLS_ECP_DP_SECP192R1: + case MBEDTLS_ECP_DP_SECP256R1: +#if SOC_ECC_SUPPORT_CURVE_P384 + case MBEDTLS_ECP_DP_SECP384R1: +#endif + return true; + default: + return false; + } +} + +static int crypto_ec_point_mul_ecc_hw(const mbedtls_ecp_group *grp, + const mbedtls_ecp_point *p, + const mbedtls_mpi *k, + mbedtls_ecp_point *res) +{ + int ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + ecc_point_t p_hw = { 0 }; + ecc_point_t r_hw = { 0 }; + unsigned char scalar_le[MAX_SIZE] = { 0 }; + size_t curve_len = grp->pbits / 8; + + if (!crypto_ec_point_mul_curve_supported(grp)) { + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + } + + if (curve_len != P192_LEN && curve_len != P256_LEN +#if SOC_ECC_SUPPORT_CURVE_P384 + && curve_len != P384_LEN +#endif + ) { + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + } + + /* Preserve mbedTLS input validation semantics for this fast path. */ + MBEDTLS_MPI_CHK(mbedtls_ecp_check_privkey(grp, k)); + MBEDTLS_MPI_CHK(mbedtls_ecp_check_pubkey(grp, p)); + + p_hw.len = curve_len; + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary_le(&p->MBEDTLS_PRIVATE(X), p_hw.x, MAX_SIZE)); + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary_le(&p->MBEDTLS_PRIVATE(Y), p_hw.y, MAX_SIZE)); + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary_le(k, scalar_le, MAX_SIZE)); + + if (esp_ecc_point_multiply(&p_hw, scalar_le, &r_hw, false) != 0) { + ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + goto cleanup; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary_le(&res->MBEDTLS_PRIVATE(X), r_hw.x, curve_len)); + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary_le(&res->MBEDTLS_PRIVATE(Y), r_hw.y, curve_len)); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&res->MBEDTLS_PRIVATE(Z), 1)); + +cleanup: + mbedtls_platform_zeroize(scalar_le, sizeof(scalar_le)); + mbedtls_platform_zeroize(&p_hw, sizeof(p_hw)); + mbedtls_platform_zeroize(&r_hw, sizeof(r_hw)); + return ret; +} +#endif + +static mbedtls_ecp_group_id crypto_ec_get_iana_to_mbedtls_group_id(int group) +{ + switch (group) { + case IANA_SECP256R1: + return MBEDTLS_ECP_DP_SECP256R1; + case IANA_SECP384R1: + return MBEDTLS_ECP_DP_SECP384R1; + case IANA_SECP521R1: + return MBEDTLS_ECP_DP_SECP521R1; + case 28: + return MBEDTLS_ECP_DP_BP256R1; + case 29: + return MBEDTLS_ECP_DP_BP384R1; + case 30: + return MBEDTLS_ECP_DP_BP512R1; + default: + return MBEDTLS_ECP_DP_NONE; + } +} + +static unsigned int crypto_ec_get_mbedtls_to_iana_group_id(mbedtls_ecp_group_id id) +{ + switch (id) { + case MBEDTLS_ECP_DP_SECP256R1: + return IANA_SECP256R1; + case MBEDTLS_ECP_DP_SECP384R1: + return IANA_SECP384R1; + case MBEDTLS_ECP_DP_SECP521R1: + return IANA_SECP521R1; + case MBEDTLS_ECP_DP_BP256R1: + return 28; + case MBEDTLS_ECP_DP_BP384R1: + return 29; + case MBEDTLS_ECP_DP_BP512R1: + return 30; + default: + return 0; + } +} #ifdef CONFIG_ECC +static int crypto_ec_is_p256_group(const mbedtls_ecp_group *grp) +{ + return grp && grp->id == MBEDTLS_ECP_DP_SECP256R1; +} + struct crypto_ec *crypto_ec_init(int group) { mbedtls_ecp_group *e; + mbedtls_ecp_group_id grp_id = crypto_ec_get_iana_to_mbedtls_group_id(group); - mbedtls_ecp_group_id grp_id; - - /* IANA registry to mbedtls internal mapping*/ - switch (group) { - case IANA_SECP256R1: - /* For now just support NIST-P256. - * This is of type "short Weierstrass". - */ - grp_id = MBEDTLS_ECP_DP_SECP256R1; - break; - default: + if (grp_id == MBEDTLS_ECP_DP_NONE) { return NULL; - } + e = os_zalloc(sizeof(*e)); if (!e) { return NULL; @@ -267,6 +379,672 @@ cleanup: return NULL; } +#if CONFIG_ESP_WIFI_P256_ACCEL && CONFIG_MBEDTLS_HARDWARE_MPI && !CONFIG_MBEDTLS_HARDWARE_ECC +typedef struct { + u32 X[P256_WORDS]; + u32 Y[P256_WORDS]; + u32 Z[P256_WORDS]; +} p256_fast_jac_point; + +static int p256_words_is_one(const u32 *a) +{ + size_t i; + + if (a[0] != 1U) { + return 0; + } + + for (i = 1; i < P256_WORDS; i++) { + if (a[i] != 0U) { + return 0; + } + } + + return 1; +} + +static int p256_words_cmp(const u32 *x, const u32 *y) +{ + int i; + + for (i = P256_WORDS - 1; i >= 0; i--) { + if (x[i] > y[i]) { + return 1; + } + if (x[i] < y[i]) { + return -1; + } + } + + return 0; +} + +static u32 p256_fast_words_add_carry(u32 *z, const u32 *x, const u32 *y) +{ + size_t i; + u64 carry = 0; + + for (i = 0; i < P256_WORDS; i++) { + u64 sum = (u64) x[i] + y[i] + carry; + + z[i] = (u32) sum; + carry = sum >> 32; + } + + return (u32) carry; +} + +static void p256_fast_words_add_mod(u32 *z, const u32 *x, const u32 *y) +{ + u32 reduced[P256_WORDS]; + u32 carry = p256_fast_words_add_carry(z, x, y); + u32 borrow = p256_words_sub_borrow(reduced, z, p256_p_le); + u32 use_sub = carry | (1U - borrow); + + p256_words_cmov(z, reduced, use_sub); +} + +static void p256_fast_words_sub_mod(u32 *z, const u32 *x, const u32 *y) +{ + u32 tmp[P256_WORDS]; + + if (p256_words_sub_borrow(z, x, y) != 0) { + (void) p256_fast_words_add_carry(tmp, z, p256_p_le); + os_memcpy(z, tmp, sizeof(tmp)); + } +} + +static void p256_words_rshift1_with_carry(u32 *z, u32 carry) +{ + int i; + + for (i = P256_WORDS - 1; i >= 0; i--) { + u32 next = z[i] & 1U; + + z[i] = (z[i] >> 1) | (carry << 31); + carry = next; + } +} + +static void p256_fast_half_mod(u32 *z) +{ + if (z[0] & 1U) { + u32 tmp[P256_WORDS]; + u32 carry = p256_fast_words_add_carry(tmp, z, p256_p_le); + + p256_words_rshift1_with_carry(tmp, carry); + os_memcpy(z, tmp, sizeof(tmp)); + return; + } + + p256_words_rshift1_with_carry(z, 0); +} + +static int p256_fast_inv_std(u32 out[P256_WORDS], const u32 in[P256_WORDS]) +{ + u32 u[P256_WORDS], v[P256_WORDS], r[P256_WORDS], s[P256_WORDS]; + + if (p256_words_is_zero(in)) { + return -1; + } + + os_memcpy(u, in, sizeof(u)); + os_memcpy(v, p256_p_le, sizeof(v)); + os_memset(r, 0, sizeof(r)); + os_memset(s, 0, sizeof(s)); + r[0] = 1U; + + while (!p256_words_is_one(u) && !p256_words_is_one(v)) { + while ((u[0] & 1U) == 0U) { + p256_words_rshift1_with_carry(u, 0); + p256_fast_half_mod(r); + } + + while ((v[0] & 1U) == 0U) { + p256_words_rshift1_with_carry(v, 0); + p256_fast_half_mod(s); + } + + if (p256_words_cmp(u, v) >= 0) { + (void) p256_words_sub_borrow(u, u, v); + p256_fast_words_sub_mod(r, r, s); + } else { + (void) p256_words_sub_borrow(v, v, u); + p256_fast_words_sub_mod(s, s, r); + } + } + + os_memcpy(out, p256_words_is_one(u) ? r : s, sizeof(u)); + return 0; +} + +static void p256_fast_to_mont(u32 out[P256_WORDS], + const u32 in[P256_WORDS]) +{ + p256_mont_mul(out, in, p256_r2_le); +} + +static void p256_fast_from_mont(u32 out[P256_WORDS], + const u32 in[P256_WORDS]) +{ + static const u32 one[P256_WORDS] = {1}; + + p256_mont_mul(out, in, one); +} + +static const u32 p256_base_comb4_x[16][P256_WORDS] = { + {0}, + {0x18a9143cU, 0x79e730d4U, 0x5fedb601U, 0x75ba95fcU, 0x77622510U, 0x79fb732bU, 0xa53755c6U, 0x18905f76U}, + {0x16a0d2bbU, 0x4f922fc5U, 0x1a623499U, 0x0d5cc16cU, 0x57c62c8bU, 0x9241cf3aU, 0xfd1b667fU, 0x2f5e6961U}, + {0xe137bbbcU, 0x9e566847U, 0x8a6a0becU, 0xe434469eU, 0x79d73463U, 0xb1c42761U, 0x133d0015U, 0x5abe0285U}, + {0xbfe20925U, 0x62a8c244U, 0x8fdce867U, 0x91c19ac3U, 0xdd387063U, 0x5a96a5d5U, 0x21d324f6U, 0x61d587d4U}, + {0x2cb19ffdU, 0x1c891f2bU, 0xb1923c23U, 0x01ba8d5bU, 0x8ac5ca8eU, 0xb6d03d67U, 0x1f13bedcU, 0x586eb04cU}, + {0xd2b533d5U, 0x62577734U, 0xa1bdddc0U, 0x673b8af6U, 0xa79ec293U, 0x577e7c9aU, 0xc3b266b1U, 0xbb6de651U}, + {0x1ae5aa1cU, 0xbd6a38e1U, 0x49e73658U, 0xb8b7652bU, 0xee5f87edU, 0x0b130014U, 0xaeebffcdU, 0x9d0f27b2U}, + {0xf4f8b16aU, 0x56f8410eU, 0xc47b266aU, 0x97241afeU, 0x6d9c87c1U, 0x0a406b8eU, 0xcd42ab1bU, 0x803f3e02U}, + {0xc379ab34U, 0x846a56f2U, 0x841df8d1U, 0xa8ee068bU, 0x176c68efU, 0x20314459U, 0x915f1f30U, 0xf1af32d5U}, + {0xd5be5a2bU, 0xed93e225U, 0x5934f3c6U, 0x6fe79983U, 0x22626ffcU, 0x43140926U, 0x7990216aU, 0x50bbb4d9U}, + {0x9b391593U, 0xfc68b5c5U, 0x598270fcU, 0xc385f5a2U, 0xd19adcbbU, 0x7144f3aaU, 0x83fbae0cU, 0xdd558999U}, + {0x80ec21feU, 0x5fe14bfeU, 0xc255be82U, 0xf6ce116aU, 0x2f4a5d67U, 0x98bc5a07U, 0xdb7e63afU, 0xfad27148U}, + {0xa56c0dd7U, 0x1e9ecc49U, 0x46086c74U, 0xa5cffcd8U, 0xf505aeceU, 0x8f7a1408U, 0xbef0c47eU, 0xb37b85c0U}, + {0x95c8f8beU, 0x0a1c7294U, 0x3bf362bfU, 0x2961c480U, 0xdf63d4acU, 0x9e418403U, 0x91ece900U, 0xc109f9cbU}, + {0x42913074U, 0x0d5ae356U, 0x48a542b1U, 0x55491b27U, 0xb310732aU, 0x469ca665U, 0x5f1a4cc1U, 0x29591d52U}, +}; + +static const u32 p256_base_comb4_y[16][P256_WORDS] = { + {0}, + {0xce95560aU, 0xddf25357U, 0xba19e45cU, 0x8b4ab8e4U, 0xdd21f325U, 0xd2e88688U, 0x25885d85U, 0x8571ff18U}, + {0xf5a01797U, 0x5c15c70bU, 0x60956192U, 0x3d20b44dU, 0x071fdb52U, 0x04911b37U, 0x8d6f0f7bU, 0xf648f916U}, + {0xc04c7dabU, 0x92aa837cU, 0x43260c07U, 0x573d9f4cU, 0x78e6cc37U, 0x0c931562U, 0x6b6f7383U, 0x94bb725bU}, + {0xa37173eaU, 0xe87673a2U, 0x53778b65U, 0x23848008U, 0x05bab43eU, 0x10f8441eU, 0x4621efbeU, 0xfa11fe12U}, + {0x27e8ed09U, 0x0c35c6e5U, 0x1819ede2U, 0x1e81a33cU, 0x56c652faU, 0x278fd6c0U, 0x70864f11U, 0x19d5ac08U}, + {0xb65259b3U, 0xe7e9303aU, 0xd03a7480U, 0xd6a0afd3U, 0x9b3cfc27U, 0xc5ac83d1U, 0x5d18b99bU, 0x60b4619aU}, + {0x7a730a55U, 0xca924631U, 0xddbbc83aU, 0x9c955b2fU, 0xac019a71U, 0x07c1dfe0U, 0x356ec48dU, 0x244a566dU}, + {0x04dbec69U, 0x7f0309a8U, 0x3bbad05fU, 0xa83b85f7U, 0xad8e197fU, 0xc6097273U, 0x5067adc1U, 0xc097440eU}, + {0x5d75bd50U, 0x99c37531U, 0xf72f67bcU, 0x837cffbaU, 0x48d7723fU, 0x0613a418U, 0xe2d41c8bU, 0x23d0f130U}, + {0xe57ec63eU, 0x378191c6U, 0x181dcdb2U, 0x65422c40U, 0x0236e0f6U, 0x41a8099bU, 0x01fe49c3U, 0x2b100118U}, + {0x74b82ff4U, 0x93b88b8eU, 0x71e734c9U, 0xd2e03c40U, 0x43c0322aU, 0x9a7a9eafU, 0x149d6041U, 0xe6e4c551U}, + {0x29ab05b3U, 0x90c0b6acU, 0x4e251ae6U, 0x37a9a83cU, 0xc2aade7dU, 0x0a7dc875U, 0x9f0e1a84U, 0x77387de3U}, + {0xcc0e6a8fU, 0x3596b6e4U, 0x6b388f23U, 0xfd6d4bbfU, 0xc39cef4eU, 0xaba453faU, 0xf9f628d5U, 0x9c135ac8U}, + {0x58945705U, 0xc2d095d0U, 0xddeb85c0U, 0xb9083d96U, 0x7a40449bU, 0x84692b8dU, 0x2eee1ee1U, 0x9bc3344fU}, + {0xb84f983fU, 0xe76f5b6bU, 0x9f5f84e1U, 0xbe7eef41U, 0x80baa189U, 0x1200d496U, 0x18ef332cU, 0x6376551fU}, +}; + +static void p256_fast_sqr(u32 out[P256_WORDS], const u32 in[P256_WORDS]) +{ + p256_mont_mul(out, in, in); +} + +static void p256_fast_mul(u32 out[P256_WORDS], + const u32 a[P256_WORDS], + const u32 b[P256_WORDS]) +{ + p256_mont_mul(out, a, b); +} + +static void p256_fast_dbl(u32 out[P256_WORDS], const u32 in[P256_WORDS]) +{ + p256_fast_words_add_mod(out, in, in); +} + +static void p256_fast_triple(u32 out[P256_WORDS], const u32 in[P256_WORDS]) +{ + u32 tmp[P256_WORDS]; + + p256_fast_dbl(tmp, in); + p256_fast_words_add_mod(out, tmp, in); +} + +static void p256_fast_eight(u32 out[P256_WORDS], const u32 in[P256_WORDS]) +{ + u32 tmp[P256_WORDS]; + + p256_fast_dbl(tmp, in); + p256_fast_dbl(tmp, tmp); + p256_fast_dbl(out, tmp); +} + +static void p256_fast_point_set_zero(p256_fast_jac_point *p) +{ + os_memset(p, 0, sizeof(*p)); +} + +static inline u32 p256_words_get_bit(const u32 *scalar, unsigned bit) +{ + return (scalar[bit / 32] >> (bit % 32)) & 1U; +} + +static inline u32 p256_words_get_window(const u32 *scalar, + unsigned bit, + unsigned width) +{ + u32 value = scalar[bit / 32] >> (bit % 32); + + if ((bit % 32) + width > 32 && (bit / 32) + 1 < P256_WORDS) { + value |= scalar[(bit / 32) + 1] << (32 - (bit % 32)); + } + + return value & ((1U << width) - 1U); +} + +static void p256_fast_point_from_affine(p256_fast_jac_point *p, + const u32 x[P256_WORDS], + const u32 y[P256_WORDS], + const u32 one_mont[P256_WORDS]) +{ + os_memcpy(p->X, x, sizeof(p->X)); + os_memcpy(p->Y, y, sizeof(p->Y)); + os_memcpy(p->Z, one_mont, sizeof(p->Z)); +} + +static void p256_fast_point_double(p256_fast_jac_point *r) +{ + u32 z2[P256_WORDS], y2[P256_WORDS], y4[P256_WORDS]; + u32 s[P256_WORDS], m[P256_WORDS], x3[P256_WORDS]; + u32 y3[P256_WORDS], z3[P256_WORDS], tmp1[P256_WORDS]; + u32 tmp2[P256_WORDS]; + + if (p256_words_is_zero(r->Z) || p256_words_is_zero(r->Y)) { + p256_fast_point_set_zero(r); + return; + } + + p256_fast_sqr(z2, r->Z); + p256_fast_sqr(y2, r->Y); + p256_fast_sqr(y4, y2); + + p256_fast_mul(s, r->X, y2); + p256_fast_dbl(s, s); + p256_fast_dbl(s, s); + + p256_fast_words_add_mod(tmp1, r->X, z2); + p256_fast_words_sub_mod(tmp2, r->X, z2); + p256_fast_mul(m, tmp1, tmp2); + p256_fast_triple(m, m); + + p256_fast_sqr(x3, m); + p256_fast_words_sub_mod(x3, x3, s); + p256_fast_words_sub_mod(x3, x3, s); + + p256_fast_words_sub_mod(tmp1, s, x3); + p256_fast_mul(y3, m, tmp1); + p256_fast_eight(tmp2, y4); + p256_fast_words_sub_mod(y3, y3, tmp2); + + p256_fast_mul(z3, r->Y, r->Z); + p256_fast_dbl(z3, z3); + + os_memcpy(r->X, x3, sizeof(r->X)); + os_memcpy(r->Y, y3, sizeof(r->Y)); + os_memcpy(r->Z, z3, sizeof(r->Z)); +} + +static void p256_fast_point_add_mixed(p256_fast_jac_point *r, + const u32 qx[P256_WORDS], + const u32 qy[P256_WORDS], + const u32 one_mont[P256_WORDS]) +{ + u32 z1z1[P256_WORDS], z1z1z1[P256_WORDS]; + u32 u2[P256_WORDS], s2[P256_WORDS], h[P256_WORDS]; + u32 rr[P256_WORDS], hh[P256_WORDS], hhh[P256_WORDS]; + u32 v[P256_WORDS], x3[P256_WORDS], y3[P256_WORDS]; + u32 z3[P256_WORDS], tmp[P256_WORDS], y1[P256_WORDS]; + + if (p256_words_is_zero(r->Z)) { + p256_fast_point_from_affine(r, qx, qy, one_mont); + return; + } + + os_memcpy(y1, r->Y, sizeof(y1)); + + p256_fast_sqr(z1z1, r->Z); + p256_fast_mul(z1z1z1, z1z1, r->Z); + p256_fast_mul(u2, qx, z1z1); + p256_fast_mul(s2, qy, z1z1z1); + p256_fast_words_sub_mod(h, u2, r->X); + p256_fast_words_sub_mod(rr, s2, y1); + + if (p256_words_is_zero(h)) { + if (p256_words_is_zero(rr)) { + p256_fast_point_double(r); + } else { + p256_fast_point_set_zero(r); + } + return; + } + + p256_fast_sqr(hh, h); + p256_fast_mul(hhh, hh, h); + p256_fast_mul(v, r->X, hh); + + p256_fast_sqr(x3, rr); + p256_fast_words_sub_mod(x3, x3, hhh); + p256_fast_words_sub_mod(x3, x3, v); + p256_fast_words_sub_mod(x3, x3, v); + + p256_fast_words_sub_mod(tmp, v, x3); + p256_fast_mul(y3, rr, tmp); + p256_fast_mul(tmp, y1, hhh); + p256_fast_words_sub_mod(y3, y3, tmp); + + p256_fast_mul(z3, r->Z, h); + + os_memcpy(r->X, x3, sizeof(r->X)); + os_memcpy(r->Y, y3, sizeof(r->Y)); + os_memcpy(r->Z, z3, sizeof(r->Z)); +} + +static int p256_fast_point_normalize(const mbedtls_ecp_group *grp, + const p256_fast_jac_point *p, + mbedtls_ecp_point *res) +{ + u32 z_std[P256_WORDS], inv_std[P256_WORDS], inv_mont[P256_WORDS]; + u32 x_std[P256_WORDS], y_std[P256_WORDS], tmp[P256_WORDS]; + int ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + + if (p256_words_is_zero(p->Z)) { + return mbedtls_ecp_set_zero(res); + } + + p256_fast_from_mont(z_std, p->Z); + if (p256_fast_inv_std(inv_std, z_std) != 0) { + ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + goto cleanup; + } + + p256_fast_to_mont(inv_mont, inv_std); + p256_fast_mul(y_std, p->Y, inv_mont); + p256_fast_sqr(tmp, inv_mont); + p256_fast_mul(x_std, p->X, tmp); + p256_fast_mul(y_std, y_std, tmp); + p256_fast_from_mont(x_std, x_std); + p256_fast_from_mont(y_std, y_std); + + MBEDTLS_MPI_CHK(p256_words_to_mpi(x_std, &res->MBEDTLS_PRIVATE(X))); + MBEDTLS_MPI_CHK(p256_words_to_mpi(y_std, &res->MBEDTLS_PRIVATE(Y))); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&res->MBEDTLS_PRIVATE(Z), 1)); + +cleanup: + return ret; +} + +static int p256_fast_points_batch_to_affine_mont( + const p256_fast_jac_point *points, size_t num, + u32(*xs)[P256_WORDS], u32(*ys)[P256_WORDS]) +{ + u32 prefix[14][P256_WORDS]; + u32 prod_std[P256_WORDS], inv_std[P256_WORDS]; + u32 running_inv[P256_WORDS], inv_z[P256_WORDS]; + u32 tmp[P256_WORDS]; + size_t i; + + if (!points || !xs || !ys) { + return -1; + } + + if (num == 0) { + return 0; + } + + if (num > ARRAY_SIZE(prefix)) { + return -1; + } + + os_memcpy(prefix[0], points[0].Z, sizeof(prefix[0])); + for (i = 1; i < num; i++) { + p256_fast_mul(prefix[i], prefix[i - 1], points[i].Z); + } + + p256_fast_from_mont(prod_std, prefix[num - 1]); + if (p256_fast_inv_std(inv_std, prod_std) != 0) { + return -1; + } + + p256_fast_to_mont(running_inv, inv_std); + + for (i = num; i-- > 0;) { + if (i == 0) { + os_memcpy(inv_z, running_inv, sizeof(inv_z)); + } else { + p256_fast_mul(inv_z, running_inv, prefix[i - 1]); + } + + p256_fast_sqr(tmp, inv_z); + p256_fast_mul(xs[i], points[i].X, tmp); + p256_fast_mul(tmp, tmp, inv_z); + p256_fast_mul(ys[i], points[i].Y, tmp); + + p256_fast_mul(tmp, running_inv, points[i].Z); + os_memcpy(running_inv, tmp, sizeof(running_inv)); + } + + return 0; +} + +struct p256_window4_scratch { + p256_fast_jac_point precomp[15]; + u32 table_x[16][P256_WORDS]; + u32 table_y[16][P256_WORDS]; +}; + +static int crypto_ec_point_mul_p256_window4_core(const mbedtls_ecp_group *grp, + const mbedtls_ecp_point *p, + const mbedtls_mpi *k, + p256_fast_jac_point *r, + bool validate_inputs) +{ + struct p256_window4_scratch *scratch = NULL; + u32 scalar[P256_WORDS], x_std[P256_WORDS], y_std[P256_WORDS]; + u32 x_mont[P256_WORDS], y_mont[P256_WORDS], one_mont[P256_WORDS]; + static const u32 one_std[P256_WORDS] = {1}; + int window; + int ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + bool started = false; + + if (!grp || grp->id != MBEDTLS_ECP_DP_SECP256R1 || + mbedtls_mpi_cmp_int(&p->MBEDTLS_PRIVATE(Z), 1) != 0) { + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + } + + if (validate_inputs) { + MBEDTLS_MPI_CHK(mbedtls_ecp_check_privkey(grp, k)); + MBEDTLS_MPI_CHK(mbedtls_ecp_check_pubkey(grp, p)); + } + + if (p256_words_from_mpi(k, scalar) != 0 || + p256_words_from_mpi_reduced(&p->MBEDTLS_PRIVATE(X), x_std) != 0 || + p256_words_from_mpi_reduced(&p->MBEDTLS_PRIVATE(Y), y_std) != 0) { + ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + goto cleanup; + } + + if (p256_words_bitlen(scalar) == 0) { + p256_fast_point_set_zero(r); + ret = 0; + goto cleanup; + } + + scratch = os_malloc(sizeof(*scratch)); + if (!scratch) { + ret = MBEDTLS_ERR_MPI_ALLOC_FAILED; + goto cleanup; + } + + p256_fast_to_mont(x_mont, x_std); + p256_fast_to_mont(y_mont, y_std); + p256_fast_to_mont(one_mont, one_std); + os_memcpy(scratch->table_x[1], x_mont, sizeof(scratch->table_x[1])); + os_memcpy(scratch->table_y[1], y_mont, sizeof(scratch->table_y[1])); + + p256_fast_point_from_affine(&scratch->precomp[0], x_mont, y_mont, one_mont); + os_memcpy(&scratch->precomp[1], &scratch->precomp[0], sizeof(scratch->precomp[1])); + p256_fast_point_double(&scratch->precomp[1]); + for (window = 2; window < 15; window++) { + os_memcpy(&scratch->precomp[window], &scratch->precomp[window - 1], + sizeof(scratch->precomp[window])); + p256_fast_point_add_mixed(&scratch->precomp[window], x_mont, y_mont, + one_mont); + } + + if (p256_fast_points_batch_to_affine_mont(&scratch->precomp[1], 14, + &scratch->table_x[2], + &scratch->table_y[2]) != 0) { + ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + goto cleanup; + } + + p256_fast_point_set_zero(r); + + for (window = 63; window >= 0; window--) { + u32 idx = p256_words_get_window(scalar, (unsigned) window * 4, 4); + + if (started) { + p256_fast_point_double(r); + p256_fast_point_double(r); + p256_fast_point_double(r); + p256_fast_point_double(r); + } + + if (idx == 0U) { + continue; + } + + if (!started) { + p256_fast_point_from_affine(r, scratch->table_x[idx], + scratch->table_y[idx], one_mont); + started = true; + } else { + p256_fast_point_add_mixed(r, scratch->table_x[idx], + scratch->table_y[idx], one_mont); + } + } + + ret = 0; + +cleanup: + forced_memzero(scalar, sizeof(scalar)); + if (scratch) { + forced_memzero(scratch, sizeof(*scratch)); + } + os_free(scratch); + return ret; +} + +static int crypto_ec_point_mul_p256_jacobian_fast_internal(const mbedtls_ecp_group *grp, + const mbedtls_ecp_point *p, + const mbedtls_mpi *k, + mbedtls_ecp_point *res, + bool validate_inputs) +{ + p256_fast_jac_point r; + int ret; + + ret = crypto_ec_point_mul_p256_window4_core(grp, p, k, &r, validate_inputs); + if (ret != 0) { + return ret; + } + + return p256_fast_point_normalize(grp, &r, res); +} + +static int crypto_ec_point_mul_p256_generator_comb_fast(const mbedtls_ecp_group *grp, + const mbedtls_mpi *k, + mbedtls_ecp_point *res, + bool validate_inputs) +{ + p256_fast_jac_point r; + u32 scalar[P256_WORDS]; + u32 one_mont[P256_WORDS]; + static const u32 one_std[P256_WORDS] = {1}; + int col; + int ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + + if (!grp || grp->id != MBEDTLS_ECP_DP_SECP256R1) { + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + } + + if (validate_inputs) { + MBEDTLS_MPI_CHK(mbedtls_ecp_check_privkey(grp, k)); + } + + if (p256_words_from_mpi(k, scalar) != 0) { + ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + goto cleanup; + } + + if (p256_words_bitlen(scalar) == 0) { + ret = mbedtls_ecp_set_zero(res); + goto cleanup; + } + + p256_fast_to_mont(one_mont, one_std); + p256_fast_point_set_zero(&r); + + for (col = 63; col >= 0; col--) { + u32 idx = p256_words_get_bit(scalar, (unsigned) col) | + (p256_words_get_bit(scalar, (unsigned)(col + 64)) << 1) | + (p256_words_get_bit(scalar, (unsigned)(col + 128)) << 2) | + (p256_words_get_bit(scalar, (unsigned)(col + 192)) << 3); + + p256_fast_point_double(&r); + if (idx != 0U) { + p256_fast_point_add_mixed(&r, + p256_base_comb4_x[idx], + p256_base_comb4_y[idx], + one_mont); + } + } + + ret = p256_fast_point_normalize(grp, &r, res); + +cleanup: + mbedtls_platform_zeroize(scalar, sizeof(scalar)); + mbedtls_platform_zeroize(&r, sizeof(r)); + return ret; +} + +static int crypto_ec_point_mul_p256_jacobian_fast(const mbedtls_ecp_group *grp, + const mbedtls_ecp_point *p, + const mbedtls_mpi *k, + mbedtls_ecp_point *res) +{ + return crypto_ec_point_mul_p256_jacobian_fast_internal(grp, p, k, res, + true); +} +#endif + +static int crypto_ec_point_mul_generic(const mbedtls_ecp_group *grp, + const mbedtls_ecp_point *p, + const mbedtls_mpi *k, + mbedtls_ecp_point *res) +{ + return mbedtls_ecp_mul((mbedtls_ecp_group *) grp, res, k, p, + mbedtls_esp_random, NULL); +} + +static int crypto_ec_point_mul_fast(const mbedtls_ecp_group *grp, + const mbedtls_ecp_point *p, + const mbedtls_mpi *k, + mbedtls_ecp_point *res) +{ +#if CONFIG_MBEDTLS_HARDWARE_ECC + int ret; + + ret = crypto_ec_point_mul_ecc_hw(grp, p, k, res); + if (ret != MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) { + return ret; + } +#endif + +#if ESP_WIFI_P256_SOFT_ACCEL + if (crypto_ec_is_p256_group(grp)) { + return crypto_ec_point_mul_p256_jacobian_fast(grp, p, k, res); + } +#endif + + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; +} + int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, const struct crypto_ec_point *b, struct crypto_ec_point *c) @@ -289,14 +1067,18 @@ int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, struct crypto_ec_point *res) { int ret; - MBEDTLS_MPI_CHK(mbedtls_ecp_mul((mbedtls_ecp_group *)e, - (mbedtls_ecp_point *) res, - (const mbedtls_mpi *)b, - (const mbedtls_ecp_point *)p, - mbedtls_esp_random, - NULL)); -cleanup: + ret = crypto_ec_point_mul_fast((mbedtls_ecp_group *) e, + (const mbedtls_ecp_point *) p, + (const mbedtls_mpi *) b, + (mbedtls_ecp_point *) res); + if (ret == MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) { + ret = crypto_ec_point_mul_generic((mbedtls_ecp_group *) e, + (const mbedtls_ecp_point *) p, + (const mbedtls_mpi *) b, + (mbedtls_ecp_point *) res); + } + return ret ? -1 : 0; } @@ -382,6 +1164,7 @@ struct crypto_bignum *crypto_ec_point_compute_y_sqr(struct crypto_ec *e, const struct crypto_bignum *x) { mbedtls_mpi temp, temp2, num; + mbedtls_ecp_group *grp = (mbedtls_ecp_group *) e; int ret = 0; mbedtls_mpi *y_sqr = os_zalloc(sizeof(mbedtls_mpi)); @@ -395,30 +1178,54 @@ struct crypto_bignum *crypto_ec_point_compute_y_sqr(struct crypto_ec *e, mbedtls_mpi_init(y_sqr); /* y^2 = x^3 + ax + b mod P */ - /* X*X*X is faster on esp32 whereas X^3 is faster on other chips */ -#if CONFIG_IDF_TARGET_ESP32 - /* Calculate x*x*x mod P*/ - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&temp, (const mbedtls_mpi *) x, (const mbedtls_mpi *) x)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&temp, &temp, (const mbedtls_mpi *) x)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&temp, &temp, &((mbedtls_ecp_group *)e)->P)); + MBEDTLS_MPI_CHK(crypto_bignum_mulmod(x, x, + (const struct crypto_bignum *) &grp->P, + (struct crypto_bignum *) &temp)); + MBEDTLS_MPI_CHK(crypto_bignum_mulmod((const struct crypto_bignum *) &temp, x, + (const struct crypto_bignum *) &grp->P, + (struct crypto_bignum *) &temp)); + +#if CONFIG_ESP_WIFI_P256_ACCEL + if (mbedtls_ecp_group_a_is_minus_3(grp)) { + /* + * For NIST P-curves used in SAE, a == -3. Compute (-3x + b) mod p + * with additions/subtractions instead of a generic multiply+mod path. + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&temp2, (const mbedtls_mpi *) x, + (const mbedtls_mpi *) x)); + if (mbedtls_mpi_cmp_mpi(&temp2, &grp->P) >= 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&temp2, &temp2, &grp->P)); + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&temp2, &temp2, + (const mbedtls_mpi *) x)); + while (mbedtls_mpi_cmp_mpi(&temp2, &grp->P) >= 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&temp2, &temp2, &grp->P)); + } + + if (mbedtls_mpi_cmp_int(&temp2, 0) != 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&temp2, &grp->P, &temp2)); + } + } else { + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&temp2, (const mbedtls_mpi *) x, + &grp->A)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&temp2, &temp2, &grp->P)); + } #else - /* Calculate x^3 mod P*/ - MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&num, 3)); - MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&temp, (const mbedtls_mpi *) x, &num, &((mbedtls_ecp_group *)e)->P, NULL)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&temp2, (const mbedtls_mpi *) x, + &grp->A)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&temp2, &temp2, &grp->P)); #endif - /* Calculate ax mod P*/ - MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&num, -3)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&temp2, (const mbedtls_mpi *) x, &num)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&temp2, &temp2, &((mbedtls_ecp_group *)e)->P)); + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&temp2, &temp2, &grp->B)); + while (mbedtls_mpi_cmp_mpi(&temp2, &grp->P) >= 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&temp2, &temp2, &grp->P)); + } - /* Calculate ax + b mod P. Note that b is already < P*/ - MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&temp2, &temp2, &((mbedtls_ecp_group *)e)->B)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&temp2, &temp2, &((mbedtls_ecp_group *)e)->P)); - - /* Calculate x^3 + ax + b mod P*/ - MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&temp2, &temp2, &temp)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(y_sqr, &temp2, &((mbedtls_ecp_group *)e)->P)); + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(y_sqr, &temp2, &temp)); + while (mbedtls_mpi_cmp_mpi(y_sqr, &grp->P) >= 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(y_sqr, y_sqr, &grp->P)); + } cleanup: mbedtls_mpi_free(&temp); @@ -439,8 +1246,9 @@ int crypto_ec_point_is_at_infinity(struct crypto_ec *e, return mbedtls_ecp_is_zero((mbedtls_ecp_point *) p); } -int crypto_ec_point_is_on_curve(struct crypto_ec *e, - const struct crypto_ec_point *p) +#if !CONFIG_MBEDTLS_HARDWARE_ECC +static int crypto_ec_point_is_on_curve_mpi(struct crypto_ec *e, + const struct crypto_ec_point *p) { mbedtls_mpi y_sqr_lhs, *y_sqr_rhs = NULL, two; int ret = 0, on_curve = 0; @@ -448,11 +1256,15 @@ int crypto_ec_point_is_on_curve(struct crypto_ec *e, mbedtls_mpi_init(&y_sqr_lhs); mbedtls_mpi_init(&two); - /* Calculate y^2 mod P*/ + /* Calculate y^2 mod P */ MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&two, 2)); - MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&y_sqr_lhs, &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y), &two, &((mbedtls_ecp_group *)e)->P, NULL)); + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&y_sqr_lhs, + &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y), + &two, &((mbedtls_ecp_group *)e)->P, NULL)); - y_sqr_rhs = (mbedtls_mpi *) crypto_ec_point_compute_y_sqr(e, (const struct crypto_bignum *) & ((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X)); + y_sqr_rhs = (mbedtls_mpi *) crypto_ec_point_compute_y_sqr( + e, (const struct crypto_bignum *) + & ((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X)); if (y_sqr_rhs && (mbedtls_mpi_cmp_mpi(y_sqr_rhs, &y_sqr_lhs) == 0)) { on_curve = 1; @@ -465,6 +1277,20 @@ cleanup: os_free(y_sqr_rhs); return (ret == 0) && (on_curve == 1); } +#endif + +int crypto_ec_point_is_on_curve(struct crypto_ec *e, + const struct crypto_ec_point *p) +{ +#if CONFIG_MBEDTLS_HARDWARE_ECC + /* ECC HW verify path via mbedTLS alt hooks. */ + return mbedtls_ecp_check_pubkey((const mbedtls_ecp_group *)e, + (const mbedtls_ecp_point *)p) == 0; +#else + /* MPI implementation. */ + return crypto_ec_point_is_on_curve_mpi(e, p); +#endif +} int crypto_ec_point_cmp(const struct crypto_ec *e, const struct crypto_ec_point *a, @@ -625,24 +1451,35 @@ struct crypto_bignum *crypto_ec_key_get_private_key(struct crypto_ec_key *key) int crypto_ec_get_publickey_buf(struct crypto_ec_key *key, u8 *key_buf, int len) { mbedtls_pk_context *pkey = (mbedtls_pk_context *)key; - unsigned char buf[MBEDTLS_MPI_MAX_SIZE + 10]; /* tag, length + MPI */ - unsigned char *c = buf + sizeof(buf); - int pk_len = 0; + mbedtls_ecp_keypair *ec = NULL; + size_t pk_len = 0; - memset(buf, 0, sizeof(buf)); - pk_len = mbedtls_pk_write_pubkey(&c, buf, pkey); - - if (pk_len < 0) { + if (!pkey || !mbedtls_pk_can_do(pkey, MBEDTLS_PK_ECKEY)) { return -1; } - if (len == 0) { - return pk_len; + ec = mbedtls_pk_ec(*pkey); + if (!ec) { + return -1; } - os_memcpy(key_buf, buf + MBEDTLS_MPI_MAX_SIZE + 10 - pk_len, pk_len); + pk_len = 1 + (2 * mbedtls_mpi_size(&ec->MBEDTLS_PRIVATE(grp).P)); + if (len == 0) { + return (int) pk_len; + } - return pk_len; + if (len < 0 || (size_t) len < pk_len) { + return -1; + } + + if (mbedtls_ecp_point_write_binary(&ec->MBEDTLS_PRIVATE(grp), + &ec->MBEDTLS_PRIVATE(Q), + MBEDTLS_ECP_PF_UNCOMPRESSED, + &pk_len, key_buf, len) != 0) { + return -1; + } + + return (int) pk_len; } int crypto_write_pubkey_der(struct crypto_ec_key *key, unsigned char **key_buf) @@ -696,31 +1533,7 @@ fail: unsigned int crypto_ec_get_mbedtls_to_nist_group_id(int id) { - unsigned int nist_grpid = 0; - switch (id) { - case MBEDTLS_ECP_DP_SECP256R1: - nist_grpid = 19; - break; - case MBEDTLS_ECP_DP_SECP384R1: - nist_grpid = 20; - break; - case MBEDTLS_ECP_DP_SECP521R1: - nist_grpid = 21; - break; - case MBEDTLS_ECP_DP_BP256R1: - nist_grpid = 28; - break; - case MBEDTLS_ECP_DP_BP384R1: - nist_grpid = 29; - break; - case MBEDTLS_ECP_DP_BP512R1: - nist_grpid = 30; - break; - default: - break; - } - - return nist_grpid; + return crypto_ec_get_mbedtls_to_iana_group_id((mbedtls_ecp_group_id) id); } int crypto_ec_get_curve_id(const struct crypto_ec_group *group) @@ -729,12 +1542,69 @@ int crypto_ec_get_curve_id(const struct crypto_ec_group *group) return (crypto_ec_get_mbedtls_to_nist_group_id(grp->id)); } -int crypto_ecdh(struct crypto_ec_key *key_own, struct crypto_ec_key *key_peer, - u8 *secret, size_t *secret_len) +static int crypto_ecdh_fast_p256(struct crypto_ec_key *key_own, + struct crypto_ec_key *key_peer, + u8 *secret, size_t *secret_len) { - mbedtls_ecdh_context *ctx = NULL; mbedtls_pk_context *own = (mbedtls_pk_context *)key_own; mbedtls_pk_context *peer = (mbedtls_pk_context *)key_peer; + mbedtls_ecp_keypair *own_ec; + mbedtls_ecp_keypair *peer_ec; + mbedtls_ecp_point shared_point; + size_t coord_len; + int ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + + if (!own || !peer || !mbedtls_pk_can_do(own, MBEDTLS_PK_ECKEY) || + !mbedtls_pk_can_do(peer, MBEDTLS_PK_ECKEY)) { + return ret; + } + + own_ec = mbedtls_pk_ec(*own); + peer_ec = mbedtls_pk_ec(*peer); + if (!own_ec || !peer_ec || + !crypto_ec_is_p256_group(&own_ec->MBEDTLS_PRIVATE(grp)) || + !crypto_ec_is_p256_group(&peer_ec->MBEDTLS_PRIVATE(grp))) { + return ret; + } + + ret = mbedtls_ecp_check_privkey(&own_ec->MBEDTLS_PRIVATE(grp), + &own_ec->MBEDTLS_PRIVATE(d)); + if (ret != 0) { + return ret; + } + + ret = mbedtls_ecp_check_pubkey(&peer_ec->MBEDTLS_PRIVATE(grp), + &peer_ec->MBEDTLS_PRIVATE(Q)); + if (ret != 0) { + return ret; + } + + coord_len = mbedtls_mpi_size(&own_ec->MBEDTLS_PRIVATE(grp).P); + mbedtls_ecp_point_init(&shared_point); + + ret = crypto_ec_point_mul_fast(&own_ec->MBEDTLS_PRIVATE(grp), + &peer_ec->MBEDTLS_PRIVATE(Q), + &own_ec->MBEDTLS_PRIVATE(d), + &shared_point); + if (ret == 0 && !mbedtls_ecp_is_zero(&shared_point) && + mbedtls_mpi_write_binary(&shared_point.MBEDTLS_PRIVATE(X), + secret, coord_len) == 0) { + *secret_len = coord_len; + } else if (ret == 0) { + ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + mbedtls_ecp_point_free(&shared_point); + return ret; +} + +static int crypto_ecdh_generic(struct crypto_ec_key *key_own, + struct crypto_ec_key *key_peer, + u8 *secret, size_t *secret_len) +{ + mbedtls_pk_context *own = (mbedtls_pk_context *)key_own; + mbedtls_pk_context *peer = (mbedtls_pk_context *)key_peer; + mbedtls_ecdh_context *ctx = NULL; int ret = -1; *secret_len = 0; @@ -746,10 +1616,9 @@ int crypto_ecdh(struct crypto_ec_key *key_own, struct crypto_ec_key *key_peer, } mbedtls_ecdh_init(ctx); - /* No need to setup, done through mbedtls_ecdh_get_params */ - /* set params from our key */ - if (mbedtls_ecdh_get_params(ctx, mbedtls_pk_ec(*own), MBEDTLS_ECDH_OURS) < 0) { + if (mbedtls_ecdh_get_params(ctx, mbedtls_pk_ec(*own), + MBEDTLS_ECDH_OURS) < 0) { wpa_printf(MSG_ERROR, "failed to set our ecdh params"); goto fail; } @@ -757,13 +1626,14 @@ int crypto_ecdh(struct crypto_ec_key *key_own, struct crypto_ec_key *key_peer, #ifndef DPP_MAX_SHARED_SECRET_LEN #define DPP_MAX_SHARED_SECRET_LEN 66 #endif - /* set params from peers key */ - if (mbedtls_ecdh_get_params(ctx, mbedtls_pk_ec(*peer), MBEDTLS_ECDH_THEIRS) < 0) { + if (mbedtls_ecdh_get_params(ctx, mbedtls_pk_ec(*peer), + MBEDTLS_ECDH_THEIRS) < 0) { wpa_printf(MSG_ERROR, "failed to set peer's ecdh params"); goto fail; } - if (mbedtls_ecdh_calc_secret(ctx, secret_len, secret, DPP_MAX_SHARED_SECRET_LEN, + if (mbedtls_ecdh_calc_secret(ctx, secret_len, secret, + DPP_MAX_SHARED_SECRET_LEN, mbedtls_esp_random, NULL) < 0) { wpa_printf(MSG_ERROR, "failed to calculate secret"); goto fail; @@ -784,6 +1654,18 @@ fail: return ret; } +int crypto_ecdh(struct crypto_ec_key *key_own, struct crypto_ec_key *key_peer, + u8 *secret, size_t *secret_len) +{ + int ret = crypto_ecdh_fast_p256(key_own, key_peer, secret, secret_len); + + if (ret == 0) { + return 0; + } + + return crypto_ecdh_generic(key_own, key_peer, secret, secret_len); +} + int crypto_ecdsa_get_sign(unsigned char *hash, const struct crypto_bignum *r, const struct crypto_bignum *s, struct crypto_ec_key *csign, int hash_len) { @@ -817,22 +1699,31 @@ int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *csign, { /* (mbedtls_ecdsa_context *) */ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)csign); + int ret; + mbedtls_ecp_group *ecp_kp_grp; + mbedtls_ecp_point *ecp_kp_q; + struct crypto_bignum *rb = NULL, *sb = NULL; + if (!ecp_kp) { return -1; } - struct crypto_bignum *rb = NULL, *sb = NULL; rb = crypto_bignum_init_set(r, r_len); sb = crypto_bignum_init_set(s, s_len); - - mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp); - mbedtls_ecp_point *ecp_kp_q = &ecp_kp->MBEDTLS_PRIVATE(Q); - int ret = mbedtls_ecdsa_verify(ecp_kp_grp, hash, hlen, - ecp_kp_q, (mbedtls_mpi *)rb, (mbedtls_mpi *)sb); - if (ret != 0) { - wpa_printf(MSG_ERROR, "ecdsa verification failed"); + if (!rb || !sb) { crypto_bignum_deinit(rb, 0); crypto_bignum_deinit(sb, 0); + return -1; + } + + ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp); + ecp_kp_q = &ecp_kp->MBEDTLS_PRIVATE(Q); + ret = mbedtls_ecdsa_verify(ecp_kp_grp, hash, hlen, + ecp_kp_q, (mbedtls_mpi *)rb, (mbedtls_mpi *)sb); + crypto_bignum_deinit(rb, 0); + crypto_bignum_deinit(sb, 0); + if (ret != 0) { + wpa_printf(MSG_ERROR, "ecdsa verification failed"); return ret; } @@ -885,9 +1776,10 @@ int crypto_is_ec_key(struct crypto_ec_key *key) return ret; } -struct crypto_ec_key * crypto_ec_key_gen(u16 ike_group) +static struct crypto_ec_key *crypto_ec_key_gen_fast_p256(void) { mbedtls_pk_context *kctx = (mbedtls_pk_context *)crypto_alloc_key(); + int ret; if (!kctx) { wpa_printf(MSG_ERROR, "%s: memory allocation failed", __func__); @@ -899,8 +1791,32 @@ struct crypto_ec_key * crypto_ec_key_gen(u16 ike_group) goto fail; } - mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(*kctx), //get this from argument - mbedtls_esp_random, NULL); + if ((ret = mbedtls_ecp_group_load(&mbedtls_pk_ec(*kctx)->MBEDTLS_PRIVATE(grp), + MBEDTLS_ECP_DP_SECP256R1)) != 0) { + goto fail; + } + + ret = mbedtls_ecp_gen_privkey(&mbedtls_pk_ec(*kctx)->MBEDTLS_PRIVATE(grp), + &mbedtls_pk_ec(*kctx)->MBEDTLS_PRIVATE(d), + mbedtls_esp_random, NULL); + if (ret != 0) { + goto fail; + } + +#if ESP_WIFI_P256_SOFT_ACCEL_PREFERRED + ret = crypto_ec_point_mul_p256_generator_comb_fast( + &mbedtls_pk_ec(*kctx)->MBEDTLS_PRIVATE(grp), + &mbedtls_pk_ec(*kctx)->MBEDTLS_PRIVATE(d), + &mbedtls_pk_ec(*kctx)->MBEDTLS_PRIVATE(Q), false); +#else + ret = crypto_ec_point_mul_fast(&mbedtls_pk_ec(*kctx)->MBEDTLS_PRIVATE(grp), + &mbedtls_pk_ec(*kctx)->MBEDTLS_PRIVATE(grp).G, + &mbedtls_pk_ec(*kctx)->MBEDTLS_PRIVATE(d), + &mbedtls_pk_ec(*kctx)->MBEDTLS_PRIVATE(Q)); +#endif + if (ret != 0) { + goto fail; + } return (struct crypto_ec_key *)kctx; fail: @@ -909,6 +1825,47 @@ fail: return NULL; } +static struct crypto_ec_key *crypto_ec_key_gen_generic(u16 ike_group) +{ + mbedtls_ecp_group_id grp_id = crypto_ec_get_iana_to_mbedtls_group_id(ike_group); + mbedtls_pk_context *kctx = (mbedtls_pk_context *)crypto_alloc_key(); + + if (grp_id == MBEDTLS_ECP_DP_NONE || !kctx) { + os_free(kctx); + return NULL; + } + + if (mbedtls_pk_setup(kctx, + mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)) != 0) { + goto fail; + } + + if (mbedtls_ecp_gen_key(grp_id, mbedtls_pk_ec(*kctx), + mbedtls_esp_random, NULL) != 0) { + goto fail; + } + + return (struct crypto_ec_key *) kctx; + +fail: + mbedtls_pk_free(kctx); + os_free(kctx); + return NULL; +} + +struct crypto_ec_key * crypto_ec_key_gen(u16 ike_group) +{ + if (ike_group == IANA_SECP256R1) { + struct crypto_ec_key *key = crypto_ec_key_gen_fast_p256(); + + if (key) { + return key; + } + } + + return crypto_ec_key_gen_generic(ike_group); +} + /* * ECParameters ::= CHOICE { * namedCurve OBJECT IDENTIFIER @@ -1060,16 +2017,7 @@ struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key) int crypto_mbedtls_get_grp_id(int group) { - switch (group) { - case IANA_SECP256R1: - return MBEDTLS_ECP_DP_SECP256R1; - case IANA_SECP384R1: - return MBEDTLS_ECP_DP_SECP384R1; - case IANA_SECP521R1: - return MBEDTLS_ECP_DP_SECP521R1; - default: - return MBEDTLS_ECP_DP_NONE; - } + return (int) crypto_ec_get_iana_to_mbedtls_group_id(group); } void crypto_ecdh_deinit(struct crypto_ecdh *ecdh) diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/p256_common.h b/components/wpa_supplicant/esp_supplicant/src/crypto/p256_common.h new file mode 100644 index 00000000000..b2fb6da6ea4 --- /dev/null +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/p256_common.h @@ -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); + } +} From cbd9468904f526aa25b60acb200f1fd85cf7a07f Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Fri, 3 Apr 2026 16:35:47 +0530 Subject: [PATCH 2/2] ci(wpa_supplicant): Add UT for supplicant crypto --- components/esp_wifi/Kconfig | 2 +- .../src/crypto/crypto_mbedtls-ec.c | 44 +- components/wpa_supplicant/src/common/dpp.c | 61 ++ components/wpa_supplicant/src/common/dpp.h | 11 + .../test_apps/main/CMakeLists.txt | 6 +- .../test_apps/main/test_crypto.c | 693 +++++++++++++++++- .../wpa_supplicant/test_apps/main/test_dpp.c | 167 ++++- .../wpa_supplicant/test_apps/main/test_sae.c | 60 +- .../test_apps/main/test_wpa_supplicant_main.c | 2 +- .../test_apps/sdkconfig.defaults | 2 + 10 files changed, 1020 insertions(+), 28 deletions(-) diff --git a/components/esp_wifi/Kconfig b/components/esp_wifi/Kconfig index a45524403a4..8d6e5175984 100644 --- a/components/esp_wifi/Kconfig +++ b/components/esp_wifi/Kconfig @@ -724,7 +724,7 @@ menu "Wi-Fi" config ESP_WIFI_P256_ACCEL bool "Enable P-256 crypto acceleration" depends on ESP_WIFI_MBEDTLS_CRYPTO - default y + default n help Enable Espressif-specific P-256 acceleration in the WPA supplicant crypto layer. This reduces SAE and DPP latency on supported targets diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c index 8c0453b9b62..4c74e4946e2 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c @@ -637,6 +637,12 @@ static void p256_fast_point_from_affine(p256_fast_jac_point *p, os_memcpy(p->Z, one_mont, sizeof(p->Z)); } +#define P256_WINDOW_BITS 4U +#define P256_WINDOW_ENTRY_COUNT (1U << P256_WINDOW_BITS) +#define P256_WINDOW_PRECOMP_COUNT (P256_WINDOW_ENTRY_COUNT - 1U) +#define P256_WINDOW_BATCH_COUNT (P256_WINDOW_PRECOMP_COUNT - 1U) +#define P256_SCALAR_WINDOW_COUNT ((P256_WORDS * 32U) / P256_WINDOW_BITS) + static void p256_fast_point_double(p256_fast_jac_point *r) { u32 z2[P256_WORDS], y2[P256_WORDS], y4[P256_WORDS]; @@ -772,7 +778,7 @@ static int p256_fast_points_batch_to_affine_mont( const p256_fast_jac_point *points, size_t num, u32(*xs)[P256_WORDS], u32(*ys)[P256_WORDS]) { - u32 prefix[14][P256_WORDS]; + u32 prefix[P256_WINDOW_BATCH_COUNT][P256_WORDS]; u32 prod_std[P256_WORDS], inv_std[P256_WORDS]; u32 running_inv[P256_WORDS], inv_z[P256_WORDS]; u32 tmp[P256_WORDS]; @@ -822,9 +828,9 @@ static int p256_fast_points_batch_to_affine_mont( } struct p256_window4_scratch { - p256_fast_jac_point precomp[15]; - u32 table_x[16][P256_WORDS]; - u32 table_y[16][P256_WORDS]; + p256_fast_jac_point precomp[P256_WINDOW_PRECOMP_COUNT]; + u32 table_x[P256_WINDOW_ENTRY_COUNT][P256_WORDS]; + u32 table_y[P256_WINDOW_ENTRY_COUNT][P256_WORDS]; }; static int crypto_ec_point_mul_p256_window4_core(const mbedtls_ecp_group *grp, @@ -879,14 +885,15 @@ static int crypto_ec_point_mul_p256_window4_core(const mbedtls_ecp_group *grp, p256_fast_point_from_affine(&scratch->precomp[0], x_mont, y_mont, one_mont); os_memcpy(&scratch->precomp[1], &scratch->precomp[0], sizeof(scratch->precomp[1])); p256_fast_point_double(&scratch->precomp[1]); - for (window = 2; window < 15; window++) { + for (window = 2; window < P256_WINDOW_PRECOMP_COUNT; window++) { os_memcpy(&scratch->precomp[window], &scratch->precomp[window - 1], sizeof(scratch->precomp[window])); p256_fast_point_add_mixed(&scratch->precomp[window], x_mont, y_mont, one_mont); } - if (p256_fast_points_batch_to_affine_mont(&scratch->precomp[1], 14, + if (p256_fast_points_batch_to_affine_mont(&scratch->precomp[1], + P256_WINDOW_BATCH_COUNT, &scratch->table_x[2], &scratch->table_y[2]) != 0) { ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; @@ -895,14 +902,17 @@ static int crypto_ec_point_mul_p256_window4_core(const mbedtls_ecp_group *grp, p256_fast_point_set_zero(r); - for (window = 63; window >= 0; window--) { - u32 idx = p256_words_get_window(scalar, (unsigned) window * 4, 4); + for (window = P256_SCALAR_WINDOW_COUNT - 1; window >= 0; window--) { + u32 idx = p256_words_get_window(scalar, + (unsigned) window * P256_WINDOW_BITS, + P256_WINDOW_BITS); if (started) { - p256_fast_point_double(r); - p256_fast_point_double(r); - p256_fast_point_double(r); - p256_fast_point_double(r); + unsigned int dbl; + + for (dbl = 0; dbl < P256_WINDOW_BITS; dbl++) { + p256_fast_point_double(r); + } } if (idx == 0U) { @@ -1034,9 +1044,7 @@ static int crypto_ec_point_mul_fast(const mbedtls_ecp_group *grp, if (ret != MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) { return ret; } -#endif - -#if ESP_WIFI_P256_SOFT_ACCEL +#elif ESP_WIFI_P256_SOFT_ACCEL if (crypto_ec_is_p256_group(grp)) { return crypto_ec_point_mul_p256_jacobian_fast(grp, p, k, res); } @@ -1185,7 +1193,6 @@ struct crypto_bignum *crypto_ec_point_compute_y_sqr(struct crypto_ec *e, (const struct crypto_bignum *) &grp->P, (struct crypto_bignum *) &temp)); -#if CONFIG_ESP_WIFI_P256_ACCEL if (mbedtls_ecp_group_a_is_minus_3(grp)) { /* * For NIST P-curves used in SAE, a == -3. Compute (-3x + b) mod p @@ -1211,11 +1218,6 @@ struct crypto_bignum *crypto_ec_point_compute_y_sqr(struct crypto_ec *e, &grp->A)); MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&temp2, &temp2, &grp->P)); } -#else - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&temp2, (const mbedtls_mpi *) x, - &grp->A)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&temp2, &temp2, &grp->P)); -#endif MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&temp2, &temp2, &grp->B)); while (mbedtls_mpi_cmp_mpi(&temp2, &grp->P) >= 0) { diff --git a/components/wpa_supplicant/src/common/dpp.c b/components/wpa_supplicant/src/common/dpp.c index fd7a1f9848a..eb1fad5f029 100644 --- a/components/wpa_supplicant/src/common/dpp.c +++ b/components/wpa_supplicant/src/common/dpp.c @@ -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; diff --git a/components/wpa_supplicant/src/common/dpp.h b/components/wpa_supplicant/src/common/dpp.h index b4a5fce4328..84f6aade08e 100644 --- a/components/wpa_supplicant/src/common/dpp.h +++ b/components/wpa_supplicant/src/common/dpp.h @@ -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; diff --git a/components/wpa_supplicant/test_apps/main/CMakeLists.txt b/components/wpa_supplicant/test_apps/main/CMakeLists.txt index d9528030ae4..50a680655cf 100644 --- a/components/wpa_supplicant/test_apps/main/CMakeLists.txt +++ b/components/wpa_supplicant/test_apps/main/CMakeLists.txt @@ -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() diff --git a/components/wpa_supplicant/test_apps/main/test_crypto.c b/components/wpa_supplicant/test_apps/main/test_crypto.c index 23f14b8da52..dfcd3cfef5c 100644 --- a/components/wpa_supplicant/test_apps/main/test_crypto.c +++ b/components/wpa_supplicant/test_apps/main/test_crypto.c @@ -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); +} diff --git a/components/wpa_supplicant/test_apps/main/test_dpp.c b/components/wpa_supplicant/test_apps/main/test_dpp.c index 69b5176fe82..4040084c55f 100644 --- a/components/wpa_supplicant/test_apps/main/test_dpp.c +++ b/components/wpa_supplicant/test_apps/main/test_dpp.c @@ -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 diff --git a/components/wpa_supplicant/test_apps/main/test_sae.c b/components/wpa_supplicant/test_apps/main/test_sae.c index bb1e36728dd..6b558dab1fb 100644 --- a/components/wpa_supplicant/test_apps/main/test_sae.c +++ b/components/wpa_supplicant/test_apps/main/test_sae.c @@ -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 ============"); diff --git a/components/wpa_supplicant/test_apps/main/test_wpa_supplicant_main.c b/components/wpa_supplicant/test_apps/main/test_wpa_supplicant_main.c index c206b4bf1d1..af15bcdd0e5 100644 --- a/components/wpa_supplicant/test_apps/main/test_wpa_supplicant_main.c +++ b/components/wpa_supplicant/test_apps/main/test_wpa_supplicant_main.c @@ -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 diff --git a/components/wpa_supplicant/test_apps/sdkconfig.defaults b/components/wpa_supplicant/test_apps/sdkconfig.defaults index 2e630cda563..1a63caec1ea 100644 --- a/components/wpa_supplicant/test_apps/sdkconfig.defaults +++ b/components/wpa_supplicant/test_apps/sdkconfig.defaults @@ -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