From f2dcdc1b9cbcd3b48d2653671b1bf7555355bf11 Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Sat, 25 Apr 2026 14:53:52 +0530 Subject: [PATCH 1/2] fix(wpa_supplicant): Add optimizations in fastpsk --- components/wpa_supplicant/CMakeLists.txt | 18 ++- .../src/crypto/crypto_mbedtls.c | 37 +++--- .../esp_supplicant/src/crypto/fastpsk.c | 109 +++++++++--------- 3 files changed, 84 insertions(+), 80 deletions(-) diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index 0c680e383b5..b0242ac302f 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..8fe4a870cfc 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,11 +36,12 @@ #include "mbedtls/esp_config.h" #include "mbedtls/sha1.h" -#ifdef CONFIG_FAST_PBKDF2 -#include "fastpbkdf2.h" +#ifdef CONFIG_FAST_PSK #include "fastpsk.h" #endif - +#ifdef CONFIG_FAST_PBKDF2 +#include "fastpbkdf2.h" +#endif static int digest_vector(mbedtls_md_type_t md_type, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { @@ -777,23 +778,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 2e033f4f7d9..e27130a227a 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. + * + * The ipad/opad SHA states are + * computed once and restored via sha_hal_write_digest() each iteration, + * halving the SHA block operations per iteration from 4 to 2. * * - The code uses the ESP SHA1 hardware accelerator for faster computation. */ @@ -55,8 +59,8 @@ #include "fastpsk.h" #include -#include -#include +#include "sha/sha_dma.h" +#include "hal/sha_hal.h" #ifndef PUT_UINT32_BE #define PUT_UINT32_BE(n, b, i) \ @@ -126,33 +130,12 @@ static void pad_blocks(union hmac_block *ctx, size_t len) PUT_UINT32_BE(bits, bytes, FAST_PSK_SHA1_BLOCKS_BUF_BYTES - 4); } -/* - * Performs SHA1 hash operation on two consecutive blocks. - * Input: blocks array (two blocks of 64 bytes each), output (20-byte digest). - */ -void sha1_op(uint32_t blocks[FAST_PSK_SHA1_BLOCKS_BUF_WORDS], uint32_t output[SHA1_OUTPUT_SZ_WORDS]) -{ - /* First block */ - sha_hal_hash_block(SHA1, blocks, SHA1_BLOCK_SZ_WORDS, true); - /* Second block */ - sha_hal_hash_block(SHA1, &blocks[SHA1_BLOCK_SZ_WORDS], SHA1_BLOCK_SZ_WORDS, false); - /* Read the final digest */ - sha_hal_read_digest(SHA1, output); -} - -/* - * Implements the PBKDF2-HMAC-SHA1 function for WPA key derivation. - * - password: The passphrase (up to 63 bytes). - * - password_len: Length of the passphrase. - * - ssid: The SSID (up to 32 bytes). - * - ssid_len: Length of the SSID. - * - 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; + uint32_t ipad_state[SHA1_OUTPUT_SZ_WORDS]; + uint32_t opad_state[SHA1_OUTPUT_SZ_WORDS]; /* Clear the context */ memset(ctx, 0, sizeof(*ctx)); @@ -177,34 +160,49 @@ 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; - sha1_op(po, inner_blk1); - /* Copy result to the sum */ + /* + * Save ipad/opad SHA states once, restore each iteration. + * Each iteration processes only 1 data block per HMAC = 2 block ops total. + */ + /* Process ipad/opad blocks once, save intermediate SHA1 states */ + sha_hal_hash_block(SHA1, ctx->inner.block[0].words, SHA1_BLOCK_SZ_WORDS, true); + sha_hal_read_digest(SHA1, ipad_state); + sha_hal_hash_block(SHA1, ctx->outer.block[0].words, SHA1_BLOCK_SZ_WORDS, true); + sha_hal_read_digest(SHA1, opad_state); + + // T1 = SHA1(ipad_state, S || i) + sha_hal_write_digest(SHA1, ipad_state); + sha_hal_hash_block(SHA1, inner_blk1, SHA1_BLOCK_SZ_WORDS, false); + sha_hal_read_digest(SHA1, outer_blk1); + + // U1 = SHA1(opad_state, T1) + pad_blocks(&ctx->outer, SHA1_BLOCK_SZ + SHA1_OUTPUT_SZ); + sha_hal_write_digest(SHA1, opad_state); + sha_hal_hash_block(SHA1, outer_blk1, SHA1_BLOCK_SZ_WORDS, false); + sha_hal_read_digest(SHA1, inner_blk1); memcpy(sum, inner_blk1, SHA1_OUTPUT_SZ); pad_blocks(&ctx->inner, SHA1_BLOCK_SZ + SHA1_OUTPUT_SZ); - /* Iterate for remaining 4096 - 1 times */ + /* + * Iterations 2..4096. + * sha_hal_read_digest() 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) { - /* Compute Tn and Un */ - // Tn = SHA1(K ^ ipad, Un-1) - sha1_op(pi, outer_blk1); - // Un = SHA1(K ^ opad, Tn) - sha1_op(po, inner_blk1); + // Tn = SHA1(ipad_state, Un-1) + sha_hal_write_digest(SHA1, ipad_state); + sha_hal_hash_block(SHA1, inner_blk1, SHA1_BLOCK_SZ_WORDS, false); + sha_hal_read_digest(SHA1, outer_blk1); + + // Un = SHA1(opad_state, Tn) + sha_hal_write_digest(SHA1, opad_state); + sha_hal_hash_block(SHA1, outer_blk1, SHA1_BLOCK_SZ_WORDS, false); + sha_hal_read_digest(SHA1, inner_blk1); - /* XOR the results to accumulate into F */ - // F = U1 ^ U2 ^ ... Un for (size_t j = 0; j < SHA1_OUTPUT_SZ_WORDS; ++j) { sum[j] ^= inner_blk1[j]; } @@ -215,7 +213,7 @@ void fast_psk_f(const char *password, size_t password_len, const uint8_t *ssid, /* Copy the final result to the output digest */ memcpy(digest, sum, SHA1_OUTPUT_SZ); - /* Clear sensitive data */ + /* Clear context */ memset(ctx, 0, sizeof(*ctx)); } @@ -225,13 +223,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 65d4abc309535d4e5374b2ca32830e9785b00d77 Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Sat, 25 Apr 2026 14:54:52 +0530 Subject: [PATCH 2/2] ci(esp_wifi): skip timing test if hw not available --- .../test_apps/main/CMakeLists.txt | 7 ++++ .../test_apps/main/test_fast_pbkdf2.c | 37 +++++++++++++++---- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/components/wpa_supplicant/test_apps/main/CMakeLists.txt b/components/wpa_supplicant/test_apps/main/CMakeLists.txt index 5dcdf7b250e..90040b3cf89 100644 --- a/components/wpa_supplicant/test_apps/main/CMakeLists.txt +++ b/components/wpa_supplicant/test_apps/main/CMakeLists.txt @@ -19,6 +19,13 @@ string(SUBSTRING "${WIFI_SUPPLICANT_MD5}" 0 7 WIFI_SUPPLICANT_MD5) # Steal some private include directories from wpa_supplicant target_include_directories(${COMPONENT_LIB} PRIVATE ${esp_supplicant_dir}/esp_supplicant/src) target_include_directories(${COMPONENT_LIB} PRIVATE ${esp_supplicant_dir}/src) +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() add_definitions(-DWIFI_SUPPLICANT_MD5=\"${WIFI_SUPPLICANT_MD5}\") add_definitions(-DCONFIG_WPA3_SAE) 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..46753117758 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 */ @@ -15,6 +15,11 @@ #define NUM_ITERATIONS 5 #define MIN_PASSPHARSE_LEN 8 +#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 + void fastpbkdf2_hmac_sha1(const uint8_t *pw, size_t npw, const uint8_t *salt, size_t nsalt, uint32_t iterations, @@ -50,9 +55,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_PSK + int64_t total_time_fast_psk = 0; +#endif +#ifdef CONFIG_FAST_PBKDF2 int64_t total_time_fast_pbkdf2 = 0; +#endif int i; for (i = 0; i < NUM_ITERATIONS; i++) { /* Calculate PMK using random ssid and passphrase and compare */ @@ -89,23 +99,34 @@ TEST_CASE("Test pbkdf2", "[crypto-pbkdf2]") } TEST_ASSERT(memcmp(PMK, expected_pmk, PMK_LEN) == 0); -#if 0 +#ifdef CONFIG_FAST_PSK + start_time = esp_timer_get_time(); + 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 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); +#ifdef CONFIG_FAST_PSK + ESP_LOGI("Timing", "Average time for esp_fast_psk: %lld microseconds", total_time_fast_psk / NUM_ITERATIONS); +#endif +#ifdef CONFIG_FAST_PBKDF2 + ESP_LOGI("Timing", "Average time for fastpbkdf2_hmac_sha1: %lld microseconds", total_time_fast_pbkdf2 / NUM_ITERATIONS); +#endif ESP_LOGI("Timing", "Average time for mbedtls_pkcs5_pbkdf2_hmac_ext: %lld microseconds", avg_time_mbedtls); } #endif