mirror of
https://github.com/espressif/esp-idf.git
synced 2026-08-15 13:22:48 +03:00
Merge branch 'bugfix/fastpsk_optimization_v5.4' into 'release/v5.4'
fast psk optimization (v5.4) See merge request espressif/esp-idf!47957
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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) */
|
||||
|
||||
|
||||
@@ -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 <string.h>
|
||||
#include <sha/sha_dma.h>
|
||||
#include <hal/sha_hal.h>
|
||||
#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 */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user