From 03311c6867e77cf303752959542c38db12eed29a Mon Sep 17 00:00:00 2001 From: Chien Wong Date: Thu, 12 Dec 2024 22:27:23 +0800 Subject: [PATCH 1/4] feat(wpa_supplicant): Add optimized PSK impl Signed-off-by: Chien Wong --- components/wpa_supplicant/CMakeLists.txt | 3 + .../src/crypto/crypto_mbedtls.c | 5 + .../esp_supplicant/src/crypto/fastpsk.c | 154 ++++++++++++++++++ .../esp_supplicant/src/crypto/fastpsk.h | 32 ++++ 4 files changed, 194 insertions(+) create mode 100644 components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.c create mode 100644 components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.h diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index d812d3272d..fc07b09cfb 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -119,6 +119,9 @@ 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") + endif() # Add internal RC4 as RC4 has been removed from mbedtls set(crypto_src ${crypto_src} "src/crypto/rc4.c") if(NOT CONFIG_MBEDTLS_DES_C) 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 b2679a04ea..09a94a2ee0 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls.c +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls.c @@ -38,6 +38,7 @@ #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, @@ -751,9 +752,13 @@ int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen) { #ifdef CONFIG_FAST_PBKDF2 +# if CONFIG_IDF_TARGET_ESP32 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, diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.c b/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.c new file mode 100644 index 0000000000..ed92b2c910 --- /dev/null +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.c @@ -0,0 +1,154 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Specialized and optimized PBKDF2-SHA1 implementation for Wi-Fi PSK + * + * Initially authored by Chien Wong(m@xv97.com). + */ + +#include "fastpsk.h" + +#include + +#include +#include + +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +#define FAST_PSK_SHA1_BLOCKS 2 +#define SHA1_BLOCK_SZ 64 +#define SHA1_BLOCK_SZ_WORDS 16 +#define SHA1_OUTPUT_SZ 20 +#define SHA1_OUTPUT_SZ_WORDS 5 +#define FAST_PSK_SHA1_BLOCKS_BUF_BYTES (FAST_PSK_SHA1_BLOCKS * SHA1_BLOCK_SZ) +#define FAST_PSK_SHA1_BLOCKS_BUF_WORDS (FAST_PSK_SHA1_BLOCKS * SHA1_BLOCK_SZ / 4) + +union hmac_block { + union { + uint32_t words[SHA1_BLOCK_SZ / 4]; + uint8_t bytes[SHA1_BLOCK_SZ]; + } block[FAST_PSK_SHA1_BLOCKS]; + uint8_t whole_bytes[FAST_PSK_SHA1_BLOCKS_BUF_BYTES]; + uint32_t whole_words[FAST_PSK_SHA1_BLOCKS_BUF_WORDS]; +}; +_Static_assert(sizeof(union hmac_block) == 128, "Incorrect layout of hmac_block"); + +struct fast_psk_context { + union hmac_block inner, outer; + uint32_t sum[SHA1_OUTPUT_SZ_WORDS]; +}; + +static inline void sha1_setup(void) +{ + esp_sha_acquire_hardware(); +} + +static inline void sha1_teardown(void) +{ + esp_sha_release_hardware(); +} + +static void pad_blocks(union hmac_block *ctx, size_t len) +{ + size_t bits = len << 3; + uint8_t *bytes = ctx->whole_bytes; + bytes[len] = 0x80; + // Set all remaining bytes to 0 + memset(&bytes[len + 1], 0, FAST_PSK_SHA1_BLOCKS_BUF_BYTES - (len + 1)); + + /* + * Simplified PUT_UINT64_BE(bits, bytes, FAST_PSK_SHA1_BLOCKS_BUF_BYTES - 8). + * Since len < 128 => bits < 1024, we only need to update the two least significant + * bytes, actually. + */ + PUT_UINT32_BE(bits, bytes, FAST_PSK_SHA1_BLOCKS_BUF_BYTES - 4); +} + +void sha1_op(uint32_t blocks[FAST_PSK_SHA1_BLOCKS_BUF_WORDS], uint32_t output[SHA1_OUTPUT_SZ_WORDS]) +{ + sha_hal_hash_block(SHA1, blocks, SHA1_BLOCK_SZ_WORDS, true); + sha_hal_hash_block(SHA1, &blocks[SHA1_BLOCK_SZ_WORDS], SHA1_BLOCK_SZ_WORDS, false); + sha_hal_read_digest(SHA1, output); +} + +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; + + memset(ctx, 0, sizeof(*ctx)); + + memset(ctx->outer.block[0].bytes, 0x5c, SHA1_BLOCK_SZ); + memset(ctx->inner.block[0].bytes, 0x36, SHA1_BLOCK_SZ); + + for (i = 0; i < password_len; ++i) { + ctx->outer.block[0].bytes[i] ^= password[i]; + ctx->inner.block[0].bytes[i] ^= password[i]; + } + + // U1 = PRF(P, S || i) + memcpy(ctx->inner.block[1].bytes, ssid, ssid_len); + PUT_UINT32_BE(count, ctx->inner.block[1].bytes, ssid_len); + pad_blocks(&ctx->inner, SHA1_BLOCK_SZ + ssid_len + 4); + + 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); + memcpy(sum, inner_blk1, SHA1_OUTPUT_SZ); + pad_blocks(&ctx->inner, SHA1_BLOCK_SZ + SHA1_OUTPUT_SZ); + + for (i = 1; i < 4096; ++i) { + // Tn = SHA1(K ^ ipad, Un-1) + sha1_op(pi, outer_blk1); + + // Un = SHA1(K ^ opad, Tn) + sha1_op(po, inner_blk1); + + // F = U1 ^ U2 ^ ... Un + for (size_t j = 0; j < SHA1_OUTPUT_SZ_WORDS; ++j) { + sum[j] ^= inner_blk1[j]; + } + } + + sha1_teardown(); + + memcpy(digest, sum, SHA1_OUTPUT_SZ); + + memset(ctx, 0, sizeof(*ctx)); +} + +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) +{ + if (!(ssid_len <= 32 && password_len <= 63 && iterations == 4096 && output_len == 32)) { + return -1; + } + fast_psk_f(password, password_len, ssid, ssid_len, 2, output); + memcpy(output + SHA1_OUTPUT_SZ, output, 32 - SHA1_OUTPUT_SZ); + fast_psk_f(password, password_len, ssid, ssid_len, 1, output); + + return 0; +} diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.h b/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.h new file mode 100644 index 0000000000..7b0ef58e8c --- /dev/null +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.h @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Calculate PSK + * + * @param password Password + * @param password_len Length of password, it must be <= 63 + * @param ssid SSID + * @param ssid_len Length of SSID, it must be <= 32 + * @param iterations Iterations of the PBKDF2-SHA1, this is a dummy param and it must be 4096 + * @param output Buffer for calculated PSK, it must be at least 32 bytes + * @param output_len Length of output to return, this is a dummy param and it must be 32 + * @return 0 on success, non-zero on failure + */ +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); + +#ifdef __cplusplus +} +#endif From e02e6fe5cad0a21d13b4d8b23a74f6de2b777d10 Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Wed, 8 Jan 2025 11:37:58 +0530 Subject: [PATCH 2/4] fix(esp_wifi): Add some comments in github PR 15073 Closes https://github.com/espressif/esp-idf/pull/15073 --- .../src/crypto/crypto_mbedtls.c | 8 +- .../esp_supplicant/src/crypto/fastpsk.c | 122 +++++++++++++++--- .../esp_supplicant/src/crypto/fastpsk.h | 2 +- 3 files changed, 108 insertions(+), 24 deletions(-) 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 09a94a2ee0..9837b8a60d 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-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -752,13 +752,13 @@ int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen) { #ifdef CONFIG_FAST_PBKDF2 -# if CONFIG_IDF_TARGET_ESP32 +#if CONFIG_IDF_TARGET_ESP32 fastpbkdf2_hmac_sha1((const u8 *) passphrase, os_strlen(passphrase), ssid, ssid_len, iterations, buf, buflen); return 0; -# else +#else return esp_fast_psk(passphrase, os_strlen(passphrase), ssid, ssid_len, iterations, buf, buflen); -# endif +#endif #else int ret = mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1, (const u8 *) passphrase, os_strlen(passphrase), ssid, diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.c b/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.c index ed92b2c910..2e033f4f7d 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: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,20 +10,61 @@ * Initially authored by Chien Wong(m@xv97.com). */ +/* + * This implementation derives a Pre-Shared Key (PSK) for WPA/WPA2 using a custom PBKDF2-like approach. + * + * PBKDF2 (Password-Based Key Derivation Function 2) is a standard algorithm used to derive cryptographic keys + * from a password and salt. It relies on iteratively applying a pseudorandom function, such as HMAC, to the input. + * The derived key is designed to be computationally expensive to generate, making brute-force attacks more difficult. + * + * In standard PBKDF2, the process is as follows: + * 1. Combine the password and salt (SSID in WPA). + * 2. Compute HMAC for this combination, iteratively applying the previous HMAC output as input for the next iteration. + * 3. XOR all intermediate results to produce the final derived key. + * + * This implementation adapts PBKDF2 for WPA/WPA2 by leveraging the SHA1 hashing algorithm and fixed parameters: + * - The password is up to 63 characters long. + * - The SSID (salt) is up to 32 bytes. + * - The iteration count is fixed at 4096, as required by WPA. + * - The output key length is 32 bytes, suitable for WPA. + * + * Key Differences from Standard PBKDF2: + * - Instead of a general-purpose pseudorandom function, this implementation uses a fixed combination of SHA1 blocks. + * - The logic for handling HMAC is explicitly implemented to optimize for this specific use case. + * - Padding and block alignment are carefully managed to fit within hardware constraints (e.g., the ESP32 SHA1 hardware). + * + * How This Implementation Works: + * 1. The `fast_psk_f` function computes one segment of the derived key. It takes as input: + * - The password. + * - The SSID. + * - A counter value (`count`) that varies for each segment. + * 2. HMAC-SHA1 is implemented explicitly using: + * - An inner padding block (`ipad`) initialized with 0x36 XORed with the password. + * - An outer padding block (`opad`) initialized with 0x5C XORed with the password. + * 3. Intermediate hashes (`U1`, `U2`, ..., `Un`) are computed iteratively. Each `U` value depends on the previous one. + * - `U1` is derived from the password, SSID, and counter. + * - 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 code uses the ESP SHA1 hardware accelerator for faster computation. + */ + #include "fastpsk.h" #include - #include #include #ifndef PUT_UINT32_BE -#define PUT_UINT32_BE(n,b,i) \ +#define PUT_UINT32_BE(n, b, i) \ { \ - (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ - (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ - (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ - (b)[(i) + 3] = (unsigned char) ( (n) ); \ + (b)[(i)] = (unsigned char)((n) >> 24); \ + (b)[(i) + 1] = (unsigned char)((n) >> 16); \ + (b)[(i) + 2] = (unsigned char)((n) >> 8); \ + (b)[(i) + 3] = (unsigned char)((n)); \ } #endif @@ -35,36 +76,45 @@ #define FAST_PSK_SHA1_BLOCKS_BUF_BYTES (FAST_PSK_SHA1_BLOCKS * SHA1_BLOCK_SZ) #define FAST_PSK_SHA1_BLOCKS_BUF_WORDS (FAST_PSK_SHA1_BLOCKS * SHA1_BLOCK_SZ / 4) +/* Union to represent SHA1 HMAC blocks */ union hmac_block { union { - uint32_t words[SHA1_BLOCK_SZ / 4]; - uint8_t bytes[SHA1_BLOCK_SZ]; + uint32_t words[SHA1_BLOCK_SZ / 4]; /* SHA1 block in words */ + uint8_t bytes[SHA1_BLOCK_SZ]; /* SHA1 block in bytes */ } block[FAST_PSK_SHA1_BLOCKS]; - uint8_t whole_bytes[FAST_PSK_SHA1_BLOCKS_BUF_BYTES]; - uint32_t whole_words[FAST_PSK_SHA1_BLOCKS_BUF_WORDS]; + uint8_t whole_bytes[FAST_PSK_SHA1_BLOCKS_BUF_BYTES]; /* Complete block as bytes */ + uint32_t whole_words[FAST_PSK_SHA1_BLOCKS_BUF_WORDS]; /* Complete block as words */ }; _Static_assert(sizeof(union hmac_block) == 128, "Incorrect layout of hmac_block"); +/* Structure to hold HMAC context */ struct fast_psk_context { - union hmac_block inner, outer; - uint32_t sum[SHA1_OUTPUT_SZ_WORDS]; + union hmac_block inner, outer; /* Inner and outer padding */ + uint32_t sum[SHA1_OUTPUT_SZ_WORDS]; /* Intermediate hash result */ }; +/* Acquire SHA1 hardware for exclusive use */ static inline void sha1_setup(void) { esp_sha_acquire_hardware(); } +/* Release SHA1 hardware */ static inline void sha1_teardown(void) { esp_sha_release_hardware(); } +/* + * Pads the given HMAC block context with the appropriate SHA1 padding. + * Length is the number of bytes of actual data in the block. + */ static void pad_blocks(union hmac_block *ctx, size_t len) { - size_t bits = len << 3; + size_t bits = len << 3; /* Convert length to bits */ uint8_t *bytes = ctx->whole_bytes; - bytes[len] = 0x80; + bytes[len] = 0x80; /* Append 0x80 as per SHA1 padding rules */ + // Set all remaining bytes to 0 memset(&bytes[len + 1], 0, FAST_PSK_SHA1_BLOCKS_BUF_BYTES - (len + 1)); @@ -76,31 +126,53 @@ 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]) { struct fast_psk_context ctx_, *ctx = &ctx_; size_t i; + /* Clear the context */ memset(ctx, 0, sizeof(*ctx)); + /* Initialize inner and outer padding */ memset(ctx->outer.block[0].bytes, 0x5c, SHA1_BLOCK_SZ); memset(ctx->inner.block[0].bytes, 0x36, SHA1_BLOCK_SZ); + /* XOR the password into the padding */ for (i = 0; i < password_len; ++i) { ctx->outer.block[0].bytes[i] ^= password[i]; ctx->inner.block[0].bytes[i] ^= password[i]; } - // U1 = PRF(P, S || i) + /* Prepare the first input block for HMAC (S || i) */ + /* Copy SSID */ memcpy(ctx->inner.block[1].bytes, ssid, ssid_len); + /* Append the counter */ PUT_UINT32_BE(count, ctx->inner.block[1].bytes, ssid_len); + /* Pad the block */ pad_blocks(&ctx->inner, SHA1_BLOCK_SZ + ssid_len + 4); sha1_setup(); @@ -108,6 +180,7 @@ void fast_psk_f(const char *password, size_t password_len, const uint8_t *ssid, 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); @@ -118,16 +191,19 @@ void fast_psk_f(const char *password, size_t password_len, const uint8_t *ssid, uint32_t *sum = ctx->sum; sha1_op(po, inner_blk1); + /* Copy result to the sum */ memcpy(sum, inner_blk1, SHA1_OUTPUT_SZ); pad_blocks(&ctx->inner, SHA1_BLOCK_SZ + SHA1_OUTPUT_SZ); + /* Iterate for remaining 4096 - 1 times */ 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); + /* 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]; @@ -136,19 +212,27 @@ void fast_psk_f(const char *password, size_t password_len, const uint8_t *ssid, sha1_teardown(); + /* Copy the final result to the output digest */ memcpy(digest, sum, SHA1_OUTPUT_SZ); + /* Clear sensitive data */ memset(ctx, 0, sizeof(*ctx)); } 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) { if (!(ssid_len <= 32 && password_len <= 63 && iterations == 4096 && output_len == 32)) { - return -1; + return -1; /* Invalid input parameters */ } + + /* Compute the first 16 bytes of the PSK */ 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; + return 0; /* Success */ } diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.h b/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.h index 7b0ef58e8c..27de90bd5b 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.h +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ From 700f62e2066dbb9f70412942a89d6b0466ad5576 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Wed, 29 Jan 2025 17:08:05 +0530 Subject: [PATCH 3/4] feat(wpa_supplicant): Support all targets generically in fastpsk --- .../esp_supplicant/src/crypto/fastpsk.c | 44 ++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.c b/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.c index 2e033f4f7d..72bfeb43b0 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.c +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/fastpsk.c @@ -55,8 +55,14 @@ #include "fastpsk.h" #include -#include -#include +#include "soc/soc_caps.h" + +#if SOC_SHA_SUPPORT_PARALLEL_ENG +#include "sha/sha_parallel_engine.h" +#else +#include "sha/sha_core.h" +#endif +#include "esp_log.h" #ifndef PUT_UINT32_BE #define PUT_UINT32_BE(n, b, i) \ @@ -96,13 +102,21 @@ struct fast_psk_context { /* Acquire SHA1 hardware for exclusive use */ static inline void sha1_setup(void) { +#if SOC_SHA_SUPPORT_PARALLEL_ENG + esp_sha_lock_engine(SHA1); +#else esp_sha_acquire_hardware(); +#endif } /* Release SHA1 hardware */ static inline void sha1_teardown(void) { +#if SOC_SHA_SUPPORT_PARALLEL_ENG + esp_sha_unlock_engine(SHA1); +#else esp_sha_release_hardware(); +#endif } /* @@ -130,14 +144,34 @@ static void pad_blocks(union hmac_block *ctx, size_t len) * Performs SHA1 hash operation on two consecutive blocks. * Input: blocks array (two blocks of 64 bytes each), output (20-byte digest). */ +#if CONFIG_IDF_TARGET_ESP32 +static inline void write32_be(uint32_t n, uint8_t out[4]) +{ +#if defined(__GNUC__) && __GNUC__ >= 4 && __BYTE_ORDER == __LITTLE_ENDIAN + *(uint32_t *)(out) = __builtin_bswap32(n); +#else + out[0] = (n >> 24) & 0xff; + out[1] = (n >> 16) & 0xff; + out[2] = (n >> 8) & 0xff; + out[3] = n & 0xff; +#endif +} +#endif /* CONFIG_IDF_TARGET_ESP32 */ + 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); + esp_sha_block(SHA1, blocks, true); /* Second block */ - sha_hal_hash_block(SHA1, &blocks[SHA1_BLOCK_SZ_WORDS], SHA1_BLOCK_SZ_WORDS, false); + esp_sha_block(SHA1, &blocks[SHA1_BLOCK_SZ_WORDS], false); /* Read the final digest */ - sha_hal_read_digest(SHA1, output); + esp_sha_read_digest_state(SHA1, output); + +#if CONFIG_IDF_TARGET_ESP32 + for (int i = 0; i < SHA1_OUTPUT_SZ_WORDS; i++) { + write32_be(output[i], ((uint8_t*) output) + i * 4); + } +#endif /* CONFIG_IDF_TARGET_ESP32 */ } /* From a8e5fd4b86ef4bd70d2e764744390b114a5eb0f3 Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Wed, 8 Jan 2025 12:12:43 +0530 Subject: [PATCH 4/4] fix(ci): Update UT to verify fast psk calculations Also update some comments --- .../src/crypto/crypto_mbedtls.c | 3 + .../test_apps/main/test_fast_pbkdf2.c | 81 ++++++++++++++----- 2 files changed, 63 insertions(+), 21 deletions(-) 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 9837b8a60d..32abe2a522 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls.c +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls.c @@ -752,6 +752,9 @@ 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 CONFIG_IDF_TARGET_ESP32 fastpbkdf2_hmac_sha1((const u8 *) passphrase, os_strlen(passphrase), ssid, ssid_len, iterations, buf, buflen); 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 5e74a7b76d..14a2f45333 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 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -12,6 +12,15 @@ #include "test_wpa_supplicant_common.h" #define PMK_LEN 32 +#define NUM_ITERATIONS 15 +#define MIN_PASSPHARSE_LEN 8 + +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); + +int64_t esp_timer_get_time(void); TEST_CASE("Test pbkdf2", "[crypto-pbkdf2]") { @@ -40,29 +49,59 @@ TEST_CASE("Test pbkdf2", "[crypto-pbkdf2]") strlen("espressif2"), 4096, PMK_LEN, expected_pmk); TEST_ASSERT(memcmp(PMK, expected_pmk, PMK_LEN) == 0); - /* Calculate PMK using random ssid and passphrase and compare */ - os_memset(ssid, 0, MAX_SSID_LEN); - os_memset(passphrase, 0, MAX_PASSPHRASE_LEN); - ssid_len = os_random(); - ssid_len %= MAX_SSID_LEN; + int64_t total_time_pbkdf2 = 0; // Variable to store total time for pbkdf2_sha1 + int64_t total_time_mbedtls = 0; + int64_t total_time_fast_pbkdf2 = 0; + int i; + for (i = 0; i < NUM_ITERATIONS; i++) { + /* Calculate PMK using random ssid and passphrase and compare */ + os_memset(ssid, 0, MAX_SSID_LEN); + os_memset(passphrase, 0, MAX_PASSPHRASE_LEN); + ssid_len = os_random(); + ssid_len %= MAX_SSID_LEN; - os_get_random(ssid, ssid_len); + os_get_random(ssid, ssid_len); - passphrase_len = os_random(); - passphrase_len %= MAX_PASSPHRASE_LEN; + passphrase_len = os_random(); + passphrase_len %= MAX_PASSPHRASE_LEN; + if (passphrase_len < MIN_PASSPHARSE_LEN) { + passphrase_len += MIN_PASSPHARSE_LEN; + } - os_get_random(passphrase, passphrase_len); - pbkdf2_sha1((char *)passphrase, ssid, ssid_len, 4096, PMK, PMK_LEN); - 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); + os_get_random(passphrase, passphrase_len); + 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(); + total_time_mbedtls += (end_time - start_time); + /* Dump values if fails */ + 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); + ESP_LOG_BUFFER_HEXDUMP("PMK", PMK, PMK_LEN, ESP_LOG_INFO); + ESP_LOG_BUFFER_HEXDUMP("expected_pmk", expected_pmk, PMK_LEN, ESP_LOG_INFO); + } + TEST_ASSERT(memcmp(PMK, expected_pmk, PMK_LEN) == 0); - /* Dump values if fails */ - 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); - ESP_LOG_BUFFER_HEXDUMP("PMK", PMK, PMK_LEN, ESP_LOG_INFO); - ESP_LOG_BUFFER_HEXDUMP("expected_pmk", expected_pmk, PMK_LEN, ESP_LOG_INFO); + 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); + + // 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); }