From db025d7015b2d6008191fafae79051e32b28c47b Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Sat, 25 Apr 2026 12:45:31 +0530 Subject: [PATCH 1/2] fix(wpa_supplicant): Add optimizations in fastpsk --- components/wpa_supplicant/CMakeLists.txt | 18 ++- .../src/crypto/crypto_mbedtls.c | 34 +++--- .../esp_supplicant/src/crypto/fastpsk.c | 106 ++++++++++++++---- 3 files changed, 114 insertions(+), 44 deletions(-) diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index 4039ab4345a..12137f922a2 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -118,8 +118,12 @@ if(CONFIG_ESP_WIFI_MBEDTLS_CRYPTO) "esp_supplicant/src/crypto/crypto_mbedtls-bignum.c" "esp_supplicant/src/crypto/crypto_mbedtls-rsa.c" "esp_supplicant/src/crypto/crypto_mbedtls-ec.c") - if(NOT CONFIG_IDF_TARGET_ESP32) - list(APPEND crypto_src "esp_supplicant/src/crypto/fastpsk.c") + if(CONFIG_MBEDTLS_HARDWARE_SHA) + if(NOT CONFIG_SOC_SHA_SUPPORT_PARALLEL_ENG) + list(APPEND crypto_src "esp_supplicant/src/crypto/fastpsk.c") + else() + list(APPEND crypto_src "esp_supplicant/src/crypto/fastpbkdf2.c") + endif() endif() # Add internal RC4 as RC4 has been removed from mbedtls set(crypto_src ${crypto_src} "src/crypto/rc4.c") @@ -130,8 +134,6 @@ if(CONFIG_ESP_WIFI_MBEDTLS_CRYPTO) set(crypto_src ${crypto_src} "src/crypto/sha1-pbkdf2.c" ${crypto_src} "src/crypto/sha1.c" ${crypto_src} "src/crypto/sha1-internal.c") - else() - set(crypto_src ${crypto_src} "esp_supplicant/src/crypto/fastpbkdf2.c") endif() if(NOT CONFIG_MBEDTLS_SHA1_C AND CONFIG_MBEDTLS_HARDWARE_SHA) set(crypto_src ${crypto_src} "src/crypto/sha1.c") @@ -272,8 +274,12 @@ target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_NO_RADIUS ) -if(CONFIG_MBEDTLS_SHA1_C OR CONFIG_MBEDTLS_HARDWARE_SHA) - target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_FAST_PBKDF2) +if(CONFIG_MBEDTLS_HARDWARE_SHA) + if(NOT CONFIG_SOC_SHA_SUPPORT_PARALLEL_ENG) + target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_FAST_PSK) + else() + target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_FAST_PBKDF2) + endif() endif() if(CONFIG_ESP_WIFI_ENABLE_WPA3_SAE) diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls.c b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls.c index 9c65c304f8b..9b462a6c13a 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls.c +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -36,9 +36,11 @@ #include "mbedtls/esp_config.h" #include "mbedtls/sha1.h" +#ifdef CONFIG_FAST_PSK +#include "fastpsk.h" +#endif #ifdef CONFIG_FAST_PBKDF2 #include "fastpbkdf2.h" -#include "fastpsk.h" #endif static int digest_vector(mbedtls_md_type_t md_type, size_t num_elem, @@ -777,23 +779,19 @@ cleanup: int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen) { -#ifdef CONFIG_FAST_PBKDF2 - /* For ESP32: Using pbkdf2_hmac_sha1() because esp_fast_psk() utilizes hardware, - * but for ESP32, the SHA1 hardware implementation is slower than the software implementation. - */ -#if defined(CONFIG_IDF_TARGET_ESP32) || !defined(CONFIG_SOC_SHA_SUPPORTED) - fastpbkdf2_hmac_sha1((const u8 *) passphrase, os_strlen(passphrase), - ssid, ssid_len, iterations, buf, buflen); - return 0; -#else - return esp_fast_psk(passphrase, os_strlen(passphrase), ssid, ssid_len, iterations, buf, buflen); -#endif -#else - int ret = mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1, (const u8 *) passphrase, - os_strlen(passphrase), ssid, - ssid_len, iterations, buflen, buf); - return ret == 0 ? 0 : -1; + if (ssid_len <= 32 && os_strlen(passphrase) <= 63 && + iterations == 4096 && buflen == 32) { +#if defined(CONFIG_FAST_PSK) + return esp_fast_psk(passphrase, os_strlen(passphrase), ssid, ssid_len, iterations, buf, buflen); +#elif defined(CONFIG_FAST_PBKDF2) + fastpbkdf2_hmac_sha1((const u8 *) passphrase, os_strlen(passphrase), + ssid, ssid_len, iterations, buf, buflen); + return 0; #endif + } + return mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1, (const u8 *) passphrase, + os_strlen(passphrase), ssid, + ssid_len, iterations, buflen, buf) == 0 ? 0 : -1; } #endif /* defined(CONFIG_MBEDTLS_SHA1_C) || defined(CONFIG_MBEDTLS_HARDWARE_SHA) */ diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.c b/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.c index 3ec8a6a9cc1..a800112656c 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.c +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -46,8 +46,12 @@ * - Subsequent `U` values are derived using SHA1 on the previous `U` value. * 4. All intermediate values are XORed together to produce the final segment of the key. * 5. The `esp_fast_psk` function combines two invocations of `fast_psk_f` to produce the complete 32-byte key. - * - The first invocation computes the first 16 bytes. - * - The second invocation computes the second 16 bytes. + * - The first invocation computes the first 20 bytes (SHA1_OUTPUT_SZ). + * - The second invocation computes the remaining 12 bytes. + * + * On non-parallel-engine targets (all except ESP32), the ipad/opad SHA states are + * computed once and restored via esp_sha_write_digest_state() each iteration, + * halving the SHA block operations per iteration from 4 to 2. * * - The code uses the ESP SHA1 hardware accelerator for faster computation. */ @@ -140,6 +144,7 @@ static void pad_blocks(union hmac_block *ctx, size_t len) PUT_UINT32_BE(bits, bytes, FAST_PSK_SHA1_BLOCKS_BUF_BYTES - 4); } +#if SOC_SHA_SUPPORT_PARALLEL_ENG /* * Performs SHA1 hash operation on two consecutive blocks. * Input: blocks array (two blocks of 64 bytes each), output (20-byte digest). @@ -158,7 +163,7 @@ static inline void write32_be(uint32_t n, uint8_t out[4]) } #endif /* CONFIG_IDF_TARGET_ESP32 */ -void sha1_op(uint32_t blocks[FAST_PSK_SHA1_BLOCKS_BUF_WORDS], uint32_t output[SHA1_OUTPUT_SZ_WORDS]) +static void sha1_op(uint32_t blocks[FAST_PSK_SHA1_BLOCKS_BUF_WORDS], uint32_t output[SHA1_OUTPUT_SZ_WORDS]) { esp_sha_set_mode(SHA1); /* First block */ @@ -174,6 +179,7 @@ void sha1_op(uint32_t blocks[FAST_PSK_SHA1_BLOCKS_BUF_WORDS], uint32_t output[SH } #endif /* CONFIG_IDF_TARGET_ESP32 */ } +#endif /* SOC_SHA_SUPPORT_PARALLEL_ENG */ /* * Implements the PBKDF2-HMAC-SHA1 function for WPA key derivation. @@ -184,11 +190,16 @@ void sha1_op(uint32_t blocks[FAST_PSK_SHA1_BLOCKS_BUF_WORDS], uint32_t output[SH * - count: The iteration counter. * - digest: Output buffer for the resulting digest (20 bytes). */ -void fast_psk_f(const char *password, size_t password_len, const uint8_t *ssid, size_t ssid_len, uint32_t count, uint8_t digest[SHA1_OUTPUT_SZ]) +static void fast_psk_f(const char *password, size_t password_len, const uint8_t *ssid, size_t ssid_len, uint32_t count, uint8_t digest[SHA1_OUTPUT_SZ]) { struct fast_psk_context ctx_, *ctx = &ctx_; size_t i; +#if !SOC_SHA_SUPPORT_PARALLEL_ENG + uint32_t ipad_state[SHA1_OUTPUT_SZ_WORDS]; + uint32_t opad_state[SHA1_OUTPUT_SZ_WORDS]; +#endif + /* Clear the context */ memset(ctx, 0, sizeof(*ctx)); @@ -212,19 +223,23 @@ void fast_psk_f(const char *password, size_t password_len, const uint8_t *ssid, sha1_setup(); - uint32_t *pi, *po; - pi = ctx->inner.whole_words; - po = ctx->outer.whole_words; - - // T1 = SHA1(K ^ ipad, S || i) - sha1_op(pi, ctx->outer.block[1].words); - - // U1 = SHA1(K ^ opad, T1) - pad_blocks(&ctx->outer, SHA1_BLOCK_SZ + SHA1_OUTPUT_SZ); uint32_t *inner_blk1 = ctx->inner.block[1].words; uint32_t *outer_blk1 = ctx->outer.block[1].words; uint32_t *sum = ctx->sum; +#if SOC_SHA_SUPPORT_PARALLEL_ENG + /* + * Parallel engine (ESP32): no state save/restore available. + * Each sha1_op processes 2 blocks (ipad/opad + data) = 4 block ops per iteration. + */ + uint32_t *pi = ctx->inner.whole_words; + uint32_t *po = ctx->outer.whole_words; + + // T1 = SHA1(K ^ ipad, S || i) + sha1_op(pi, outer_blk1); + + // U1 = SHA1(K ^ opad, T1) + pad_blocks(&ctx->outer, SHA1_BLOCK_SZ + SHA1_OUTPUT_SZ); sha1_op(po, inner_blk1); /* Copy result to the sum */ memcpy(sum, inner_blk1, SHA1_OUTPUT_SZ); @@ -244,13 +259,61 @@ void fast_psk_f(const char *password, size_t password_len, const uint8_t *ssid, sum[j] ^= inner_blk1[j]; } } +#else + /* + * Core engine: save ipad/opad SHA states once, restore each iteration. + * Each iteration processes only 1 data block per HMAC = 2 block ops total, + * half of the parallel engine path. + */ + esp_sha_set_mode(SHA1); + + /* Process ipad/opad blocks once, save intermediate SHA1 states */ + esp_sha_block(SHA1, ctx->inner.block[0].words, true); + esp_sha_read_digest_state(SHA1, ipad_state); + esp_sha_block(SHA1, ctx->outer.block[0].words, true); + esp_sha_read_digest_state(SHA1, opad_state); + + // T1 = SHA1(ipad_state, S || i) + esp_sha_write_digest_state(SHA1, ipad_state); + esp_sha_block(SHA1, inner_blk1, false); + esp_sha_read_digest_state(SHA1, outer_blk1); + + // U1 = SHA1(opad_state, T1) + pad_blocks(&ctx->outer, SHA1_BLOCK_SZ + SHA1_OUTPUT_SZ); + esp_sha_write_digest_state(SHA1, opad_state); + esp_sha_block(SHA1, outer_blk1, false); + esp_sha_read_digest_state(SHA1, inner_blk1); + memcpy(sum, inner_blk1, SHA1_OUTPUT_SZ); + pad_blocks(&ctx->inner, SHA1_BLOCK_SZ + SHA1_OUTPUT_SZ); + + /* + * Iterations 2..4096. + * esp_sha_read_digest_state writes exactly 20 bytes (5 words) for SHA1, + * so the padding at bytes [20..63] in each block stays intact. + */ + for (i = 1; i < 4096; ++i) { + // Tn = SHA1(ipad_state, Un-1) + esp_sha_write_digest_state(SHA1, ipad_state); + esp_sha_block(SHA1, inner_blk1, false); + esp_sha_read_digest_state(SHA1, outer_blk1); + + // Un = SHA1(opad_state, Tn) + esp_sha_write_digest_state(SHA1, opad_state); + esp_sha_block(SHA1, outer_blk1, false); + esp_sha_read_digest_state(SHA1, inner_blk1); + + for (size_t j = 0; j < SHA1_OUTPUT_SZ_WORDS; ++j) { + sum[j] ^= inner_blk1[j]; + } + } +#endif /* SOC_SHA_SUPPORT_PARALLEL_ENG */ sha1_teardown(); /* Copy the final result to the output digest */ memcpy(digest, sum, SHA1_OUTPUT_SZ); - /* Clear sensitive data */ + /* Clear context */ memset(ctx, 0, sizeof(*ctx)); } @@ -260,13 +323,16 @@ int esp_fast_psk(const char *password, size_t password_len, const uint8_t *ssid, return -1; /* Invalid input parameters */ } - /* Compute the first 16 bytes of the PSK */ + /* + * PBKDF2 with dkLen=32 needs two 20-byte F() blocks: + * output[0..19] = F(password, ssid, 4096, 1) + * output[20..31] = F(password, ssid, 4096, 2)[0..11] + * + * Compute block 2 first so its first 12 bytes can be placed at + * output[20..31], then block 1 overwrites output[0..19]. + */ fast_psk_f(password, password_len, ssid, ssid_len, 2, output); - - /* Replicate the first 16 bytes to form the second half temporarily */ memcpy(output + SHA1_OUTPUT_SZ, output, 32 - SHA1_OUTPUT_SZ); - - /* Compute the second 16 bytes of the PSK */ fast_psk_f(password, password_len, ssid, ssid_len, 1, output); return 0; /* Success */ From 5d469203822578c88e3469285ab419d51070fc32 Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Sat, 25 Apr 2026 12:46:01 +0530 Subject: [PATCH 2/2] ci(esp_wifi): skip timing test if hw not available --- .../test_apps/main/test_fast_pbkdf2.c | 69 +++++++++++++------ 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/components/wpa_supplicant/test_apps/main/test_fast_pbkdf2.c b/components/wpa_supplicant/test_apps/main/test_fast_pbkdf2.c index 0b6674cf69e..abdede5b746 100644 --- a/components/wpa_supplicant/test_apps/main/test_fast_pbkdf2.c +++ b/components/wpa_supplicant/test_apps/main/test_fast_pbkdf2.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -12,13 +12,20 @@ #include "test_wpa_supplicant_common.h" #define PMK_LEN 32 -#define NUM_ITERATIONS 5 +#define NUM_ITERATIONS 2 #define MIN_PASSPHARSE_LEN 8 +#ifdef CONFIG_FAST_PBKDF2 void fastpbkdf2_hmac_sha1(const uint8_t *pw, size_t npw, const uint8_t *salt, size_t nsalt, uint32_t iterations, uint8_t *out, size_t nout); +#endif + +#ifdef CONFIG_FAST_PSK +int esp_fast_psk(const char *password, size_t password_len, const uint8_t *ssid, + size_t ssid_len, size_t iterations, uint8_t *output, size_t output_len); +#endif int64_t esp_timer_get_time(void); @@ -50,9 +57,14 @@ TEST_CASE("Test pbkdf2", "[crypto-pbkdf2]") strlen("espressif2"), 4096, PMK_LEN, expected_pmk); TEST_ASSERT(memcmp(PMK, expected_pmk, PMK_LEN) == 0); - int64_t total_time_pbkdf2 = 0; // Variable to store total time for pbkdf2_sha1 + int64_t total_time_pbkdf2 = 0; int64_t total_time_mbedtls = 0; +#ifdef CONFIG_FAST_PBKDF2 int64_t total_time_fast_pbkdf2 = 0; +#endif +#ifdef CONFIG_FAST_PSK + int64_t total_time_fast_psk = 0; +#endif int i; for (i = 0; i < NUM_ITERATIONS; i++) { /* Calculate PMK using random ssid and passphrase and compare */ @@ -70,17 +82,21 @@ TEST_CASE("Test pbkdf2", "[crypto-pbkdf2]") } os_get_random(passphrase, passphrase_len); + + /* Reference: mbedtls PBKDF2 */ int64_t start_time = esp_timer_get_time(); - pbkdf2_sha1((char *)passphrase, ssid, ssid_len, 4096, PMK, PMK_LEN); - int64_t end_time = esp_timer_get_time(); - total_time_pbkdf2 += (end_time - start_time); - start_time = esp_timer_get_time(); mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1, (const unsigned char *) passphrase, strlen((char *)passphrase), (const unsigned char *)ssid, ssid_len, 4096, PMK_LEN, expected_pmk); - end_time = esp_timer_get_time(); + int64_t end_time = esp_timer_get_time(); total_time_mbedtls += (end_time - start_time); - /* Dump values if fails */ + + /* pbkdf2_sha1 (dispatches to fastpsk or fastpbkdf2 per chip) */ + start_time = esp_timer_get_time(); + pbkdf2_sha1((char *)passphrase, ssid, ssid_len, 4096, PMK, PMK_LEN); + end_time = esp_timer_get_time(); + total_time_pbkdf2 += (end_time - start_time); + if (memcmp(PMK, expected_pmk, PMK_LEN) != 0) { ESP_LOG_BUFFER_HEXDUMP("passphrase", passphrase, passphrase_len, ESP_LOG_INFO); ESP_LOG_BUFFER_HEXDUMP("ssid", ssid, ssid_len, ESP_LOG_INFO); @@ -89,23 +105,34 @@ TEST_CASE("Test pbkdf2", "[crypto-pbkdf2]") } TEST_ASSERT(memcmp(PMK, expected_pmk, PMK_LEN) == 0); -#if 0 +#ifdef CONFIG_FAST_PSK + /* esp_fast_psk: HW SHA accelerated (fastpsk.c) */ start_time = esp_timer_get_time(); - fastpbkdf2_hmac_sha1((const u8 *)passphrase, os_strlen((char *)passphrase), ssid, ssid_len, 4096, PMK, PMK_LEN); + esp_fast_psk((char *)passphrase, os_strlen((char *)passphrase), + ssid, ssid_len, 4096, PMK, PMK_LEN); + end_time = esp_timer_get_time(); + total_time_fast_psk += (end_time - start_time); + TEST_ASSERT(memcmp(PMK, expected_pmk, PMK_LEN) == 0); +#endif + +#ifdef CONFIG_FAST_PBKDF2 + /* fastpbkdf2: SW SHA optimized (fastpbkdf2.c) */ + start_time = esp_timer_get_time(); + fastpbkdf2_hmac_sha1((const u8 *)passphrase, os_strlen((char *)passphrase), + ssid, ssid_len, 4096, PMK, PMK_LEN); end_time = esp_timer_get_time(); total_time_fast_pbkdf2 += (end_time - start_time); + TEST_ASSERT(memcmp(PMK, expected_pmk, PMK_LEN) == 0); #endif } - // Calculate average time for pbkdf2_sha1 - int64_t avg_time_pbkdf2 = total_time_pbkdf2 / NUM_ITERATIONS; - // Calculate average time for mbedtls_pkcs5_pbkdf2_hmac_ext - int64_t avg_time_mbedtls = total_time_mbedtls / NUM_ITERATIONS; - int64_t avg_time_fast = total_time_fast_pbkdf2 / NUM_ITERATIONS; - - // Log average times - ESP_LOGI("Timing", "Average time for pbkdf2_sha1: %lld microseconds", avg_time_pbkdf2); - ESP_LOGI("Timing", "Average time for fast_pbkdf2_sha1: %lld microseconds", avg_time_fast); - ESP_LOGI("Timing", "Average time for mbedtls_pkcs5_pbkdf2_hmac_ext: %lld microseconds", avg_time_mbedtls); + ESP_LOGI("Timing", "Average time for pbkdf2_sha1: %lld us", total_time_pbkdf2 / NUM_ITERATIONS); +#ifdef CONFIG_FAST_PSK + ESP_LOGI("Timing", "Average time for esp_fast_psk (HW): %lld us", total_time_fast_psk / NUM_ITERATIONS); +#endif +#ifdef CONFIG_FAST_PBKDF2 + ESP_LOGI("Timing", "Average time for fastpbkdf2 (SW): %lld us", total_time_fast_pbkdf2 / NUM_ITERATIONS); +#endif + ESP_LOGI("Timing", "Average time for mbedtls_pbkdf2: %lld us", total_time_mbedtls / NUM_ITERATIONS); } #endif