Merge branch 'feature/pbkdf2_fast_implementation' into 'master'

esp_wifi: Port fast_pbkdf2 implementation to calculate PMK

See merge request espressif/esp-idf!24287
This commit is contained in:
Jiang Jiang Jian 2023-09-12 14:06:02 +08:00
commit 62720ffa8c
9 changed files with 478 additions and 2 deletions

View File

@ -1398,6 +1398,7 @@ UT_S2_SDSPI:
UT_C2:
extends: .unit_test_esp32c2_template
parallel: 2
tags:
- ESP32C2_IDF
- UT_T1_1
@ -1419,6 +1420,7 @@ UT_C3_SDSPI:
UT_C6:
extends: .unit_test_esp32c6_template
parallel: 2
tags:
- ESP32C6_IDF
- UT_T1_1

View File

@ -50,6 +50,24 @@ typedef struct {
esp_mbedtls_sha1_mode mode;
} mbedtls_sha1_context;
/**
* \brief Set the SHA-1 mode for a mbedtls_sha1_context.
*
* \param ctx The SHA-1 context structure.
* \param mode The SHA-1 mode to be set. It can be one of the following:
* - ESP_MBEDTLS_SHA1_UNUSED: Indicates that the first block hasn't been processed yet.
* - ESP_MBEDTLS_SHA1_HARDWARE: Specifies the use of hardware SHA engine for SHA-1 calculations.
* - ESP_MBEDTLS_SHA1_SOFTWARE: Specifies the use of software-based SHA-1 calculations.
*
* \return None.
*/
static inline void esp_mbedtls_set_sha1_mode(mbedtls_sha1_context *ctx, esp_mbedtls_sha1_mode mode)
{
if (ctx) {
ctx->mode = mode;
}
}
#elif SOC_SHA_SUPPORT_DMA || SOC_SHA_SUPPORT_RESUME
typedef enum {

View File

@ -96,6 +96,7 @@ endif()
if(CONFIG_ESP_WIFI_MBEDTLS_CRYPTO)
set(crypto_src
"esp_supplicant/src/crypto/fastpbkdf2.c"
"esp_supplicant/src/crypto/crypto_mbedtls.c"
"esp_supplicant/src/crypto/crypto_mbedtls-bignum.c"
"esp_supplicant/src/crypto/crypto_mbedtls-rsa.c"
@ -229,6 +230,7 @@ target_compile_definitions(${COMPONENT_LIB} PRIVATE
CONFIG_IEEE80211W
CONFIG_SHA256
CONFIG_NO_RADIUS
CONFIG_FAST_PBKDF2
)
if(CONFIG_ESP_WIFI_ENABLE_WPA3_SAE)

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -36,6 +36,10 @@
#include "crypto.h"
#include "mbedtls/esp_config.h"
#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)
{
@ -745,9 +749,14 @@ 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
fastpbkdf2_hmac_sha1((const u8 *) passphrase, os_strlen(passphrase),
ssid, ssid_len, iterations, buf, buflen);
return 0;
#else
int ret = mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1, (const u8 *) passphrase,
os_strlen(passphrase) , ssid,
ssid_len, iterations, 32, buf);
ssid_len, iterations, buflen, buf);
if (ret != 0) {
ret = -1;
goto cleanup;
@ -755,6 +764,7 @@ int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
cleanup:
return ret;
#endif
}
#ifdef MBEDTLS_DES_C

View File

@ -0,0 +1,325 @@
/*
* SPDX-FileCopyrightText: 2015 Joseph Birr-Pixton <jpixton@gmail.com>
*
* SPDX-License-Identifier: CC0-1.0
*/
/*
* fast-pbkdf2 - Optimal PBKDF2-HMAC calculation
* Written in 2015 by Joseph Birr-Pixton <jpixton@gmail.com>
*
* To the extent possible under law, the author(s) have dedicated all
* copyright and related and neighboring rights to this software to the
* public domain worldwide. This software is distributed without any
* warranty.
*
* You should have received a copy of the CC0 Public Domain Dedication
* along with this software. If not, see
* <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#include "utils/common.h"
#include "fastpbkdf2.h"
#include <assert.h>
#include <string.h>
#if defined(__GNUC__)
#include <endian.h>
#endif
#include <mbedtls/sha1.h>
#include "mbedtls/esp_config.h"
#include "utils/wpa_debug.h"
/* --- MSVC doesn't support C99 --- */
#ifdef _MSC_VER
#define restrict
#define _Pragma __pragma
#endif
/* --- Common useful things --- */
#ifndef MIN
#define MIN(a, b) ((a) > (b)) ? (b) : (a)
#endif
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
}
/* Prepare block (of blocksz bytes) to contain md padding denoting a msg-size
* message (in bytes). block has a prefix of used bytes.
*
* Message length is expressed in 32 bits (so suitable for sha1, sha256, sha512). */
static inline void md_pad(uint8_t *block, size_t blocksz, size_t used, size_t msg)
{
memset(block + used, 0, blocksz - used - 4);
block[used] = 0x80;
block += blocksz - 4;
write32_be((uint32_t) (msg * 8), block);
}
/* Internal function/type names for hash-specific things. */
#define HMAC_CTX(_name) HMAC_ ## _name ## _ctx
#define HMAC_INIT(_name) HMAC_ ## _name ## _init
#define HMAC_UPDATE(_name) HMAC_ ## _name ## _update
#define HMAC_FINAL(_name) HMAC_ ## _name ## _final
#define PBKDF2_F(_name) pbkdf2_f_ ## _name
#define PBKDF2(_name) pbkdf2_ ## _name
/* This macro expands to decls for the whole implementation for a given
* hash function. Arguments are:
*
* _name like 'sha1', added to symbol names
* _blocksz block size, in bytes
* _hashsz digest output, in bytes
* _ctx hash context type
* _init hash context initialisation function
* args: (_ctx *c)
* _update hash context update function
* args: (_ctx *c, const void *data, size_t ndata)
* _final hash context finish function
* args: (_ctx *c, void *out)
* _xform hash context raw block update function
* args: (_ctx *c, const void *data)
* _xcpy hash context raw copy function (only need copy hash state)
* args: (_ctx * restrict out, const _ctx *restrict in)
* _xtract hash context state extraction
* args: args (_ctx *restrict c, uint8_t *restrict out)
* _xxor hash context xor function (only need xor hash state)
* args: (_ctx *restrict out, const _ctx *restrict in)
*
* The resulting function is named PBKDF2(_name).
*/
#define DECL_PBKDF2(_name, _blocksz, _hashsz, _ctx, \
_init, _update, _xform, _final, _xcpy, _xtract, _xxor) \
typedef struct { \
_ctx inner; \
_ctx outer; \
} HMAC_CTX(_name); \
\
static inline void HMAC_INIT(_name)(HMAC_CTX(_name) *ctx, \
const uint8_t *key, size_t nkey) \
{ \
/* Prepare key: */ \
uint8_t k[_blocksz]; \
\
/* Shorten long keys. */ \
if (nkey > _blocksz) \
{ \
_init(&ctx->inner); \
_update(&ctx->inner, key, nkey); \
_final(&ctx->inner, k); \
\
key = k; \
nkey = _hashsz; \
} \
\
/* Standard doesn't cover case where blocksz < hashsz. */ \
assert(nkey <= _blocksz); \
\
/* Right zero-pad short keys. */ \
if (k != key) \
memcpy(k, key, nkey); \
if (_blocksz > nkey) \
memset(k + nkey, 0, _blocksz - nkey); \
\
/* Start inner hash computation */ \
uint8_t blk_inner[_blocksz]; \
uint8_t blk_outer[_blocksz]; \
\
for (size_t i = 0; i < _blocksz; i++) \
{ \
blk_inner[i] = 0x36 ^ k[i]; \
blk_outer[i] = 0x5c ^ k[i]; \
} \
\
_init(&ctx->inner); \
_update(&ctx->inner, blk_inner, sizeof blk_inner); \
\
/* And outer. */ \
_init(&ctx->outer); \
_update(&ctx->outer, blk_outer, sizeof blk_outer); \
} \
\
static inline void HMAC_UPDATE(_name)(HMAC_CTX(_name) *ctx, \
const void *data, size_t ndata) \
{ \
_update(&ctx->inner, data, ndata); \
} \
\
static inline void HMAC_FINAL(_name)(HMAC_CTX(_name) *ctx, \
uint8_t out[_hashsz]) \
{ \
_final(&ctx->inner, out); \
_update(&ctx->outer, out, _hashsz); \
_final(&ctx->outer, out); \
} \
\
\
/* --- PBKDF2 --- */ \
static inline void PBKDF2_F(_name)(const HMAC_CTX(_name) *startctx, \
uint32_t counter, \
const uint8_t *salt, size_t nsalt, \
uint32_t iterations, \
uint8_t *out) \
{ \
uint8_t countbuf[4]; \
write32_be(counter, countbuf); \
\
/* Prepare loop-invariant padding block. */ \
uint8_t Ublock[_blocksz]; \
md_pad(Ublock, _blocksz, _hashsz, _blocksz + _hashsz); \
\
/* First iteration: \
* U_1 = PRF(P, S || INT_32_BE(i)) \
*/ \
HMAC_CTX(_name) ctx = *startctx; \
HMAC_UPDATE(_name)(&ctx, salt, nsalt); \
HMAC_UPDATE(_name)(&ctx, countbuf, sizeof countbuf); \
HMAC_FINAL(_name)(&ctx, Ublock); \
_ctx result = ctx.outer; \
\
/* Subsequent iterations: \
* U_c = PRF(P, U_{c-1}) \
*/ \
for (uint32_t i = 1; i < iterations; i++) \
{ \
/* Complete inner hash with previous U */ \
_xcpy(&ctx.inner, &startctx->inner); \
_xform(&ctx.inner, Ublock); \
_xtract(&ctx.inner, Ublock); \
/* Complete outer hash with inner output */ \
_xcpy(&ctx.outer, &startctx->outer); \
_xform(&ctx.outer, Ublock); \
_xtract(&ctx.outer, Ublock); \
_xxor(&result, &ctx.outer); \
} \
\
/* Reform result into output buffer. */ \
_xtract(&result, out); \
} \
\
static inline void PBKDF2(_name)(const uint8_t *pw, size_t npw, \
const uint8_t *salt, size_t nsalt, \
uint32_t iterations, \
uint8_t *out, size_t nout) \
{ \
assert(iterations); \
assert(out && nout); \
\
/* Starting point for inner loop. */ \
HMAC_CTX(_name) ctx; \
HMAC_INIT(_name)(&ctx, pw, npw); \
\
/* How many blocks do we need? */ \
uint32_t blocks_needed = (uint32_t)(nout + _hashsz - 1) / _hashsz; \
\
for (uint32_t counter = 1; counter <= blocks_needed; counter++) \
{ \
uint8_t block[_hashsz]; \
PBKDF2_F(_name)(&ctx, counter, salt, nsalt, iterations, block); \
\
size_t offset = (counter - 1) * _hashsz; \
size_t taken = MIN(nout - offset, _hashsz); \
memcpy(out + offset, block, taken); \
} \
}
static inline void sha1_extract(mbedtls_sha1_context *restrict ctx, uint8_t *restrict out)
{
#if defined(MBEDTLS_SHA1_ALT)
#if CONFIG_IDF_TARGET_ESP32
/* ESP32 stores internal SHA state in BE format similar to software */
write32_be(ctx->state[0], out);
write32_be(ctx->state[1], out + 4);
write32_be(ctx->state[2], out + 8);
write32_be(ctx->state[3], out + 12);
write32_be(ctx->state[4], out + 16);
#else
*(uint32_t *)(out) = ctx->state[0];
*(uint32_t *)(out + 4) = ctx->state[1];
*(uint32_t *)(out + 8) = ctx->state[2];
*(uint32_t *)(out + 12) = ctx->state[3];
*(uint32_t *)(out + 16) = ctx->state[4];
#endif
#else
write32_be(ctx->MBEDTLS_PRIVATE(state)[0], out);
write32_be(ctx->MBEDTLS_PRIVATE(state)[1], out + 4);
write32_be(ctx->MBEDTLS_PRIVATE(state)[2], out + 8);
write32_be(ctx->MBEDTLS_PRIVATE(state)[3], out + 12);
write32_be(ctx->MBEDTLS_PRIVATE(state)[4], out + 16);
#endif
}
static inline void sha1_cpy(mbedtls_sha1_context *restrict out, const mbedtls_sha1_context *restrict in)
{
#if defined(MBEDTLS_SHA1_ALT)
out->state[0] = in->state[0];
out->state[1] = in->state[1];
out->state[2] = in->state[2];
out->state[3] = in->state[3];
out->state[4] = in->state[4];
#else
out->MBEDTLS_PRIVATE(state)[0] = in->MBEDTLS_PRIVATE(state)[0];
out->MBEDTLS_PRIVATE(state)[1] = in->MBEDTLS_PRIVATE(state)[1];
out->MBEDTLS_PRIVATE(state)[2] = in->MBEDTLS_PRIVATE(state)[2];
out->MBEDTLS_PRIVATE(state)[3] = in->MBEDTLS_PRIVATE(state)[3];
out->MBEDTLS_PRIVATE(state)[4] = in->MBEDTLS_PRIVATE(state)[4];
#endif
}
static inline void sha1_xor(mbedtls_sha1_context *restrict out, const mbedtls_sha1_context *restrict in)
{
#if defined(MBEDTLS_SHA1_ALT)
out->state[0] ^= in->state[0];
out->state[1] ^= in->state[1];
out->state[2] ^= in->state[2];
out->state[3] ^= in->state[3];
out->state[4] ^= in->state[4];
#else
out->MBEDTLS_PRIVATE(state)[0] ^= in->MBEDTLS_PRIVATE(state)[0];
out->MBEDTLS_PRIVATE(state)[1] ^= in->MBEDTLS_PRIVATE(state)[1];
out->MBEDTLS_PRIVATE(state)[2] ^= in->MBEDTLS_PRIVATE(state)[2];
out->MBEDTLS_PRIVATE(state)[3] ^= in->MBEDTLS_PRIVATE(state)[3];
out->MBEDTLS_PRIVATE(state)[4] ^= in->MBEDTLS_PRIVATE(state)[4];
#endif
}
static int mbedtls_sha1_init_start(mbedtls_sha1_context *ctx)
{
mbedtls_sha1_init(ctx);
mbedtls_sha1_starts(ctx);
#if defined(CONFIG_IDF_TARGET_ESP32) && defined(MBEDTLS_SHA1_ALT)
/* Use software mode for esp32 since hardware can't give output more than 20 */
esp_mbedtls_set_sha1_mode(ctx, ESP_MBEDTLS_SHA1_SOFTWARE);
#endif
return 0;
}
DECL_PBKDF2(sha1, // _name
64, // _blocksz
20, // _hashsz
mbedtls_sha1_context, // _ctx
mbedtls_sha1_init_start, // _init
mbedtls_sha1_update, // _update
mbedtls_internal_sha1_process, // _xform
mbedtls_sha1_finish, // _final
sha1_cpy, // _xcpy
sha1_extract, // _xtract
sha1_xor) // _xxor
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)
{
PBKDF2(sha1)(pw, npw, salt, nsalt, iterations, out, nout);
}

View File

@ -0,0 +1,48 @@
/*
* SPDX-FileCopyrightText: 2015 Joseph Birr-Pixton <jpixton@gmail.com>
*
* SPDX-License-Identifier: CC0-1.0
*/
/*
* fastpbkdf2 - Faster PBKDF2-HMAC calculation
* Written in 2015 by Joseph Birr-Pixton <jpixton@gmail.com>
*
* To the extent possible under law, the author(s) have dedicated all
* copyright and related and neighboring rights to this software to the
* public domain worldwide. This software is distributed without any
* warranty.
*
* You should have received a copy of the CC0 Public Domain Dedication
* along with this software. If not, see
* <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#ifndef FASTPBKDF2_H
#define FASTPBKDF2_H
#include <stdlib.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Calculates PBKDF2-HMAC-SHA1.
*
* @p npw bytes at @p pw are the password input.
* @p nsalt bytes at @p salt are the salt input.
* @p iterations is the PBKDF2 iteration count and must be non-zero.
* @p nout bytes of output are written to @p out. @p nout must be non-zero.
*
* This function cannot fail; it does not report errors.
*/
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);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,68 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "string.h"
#include <inttypes.h>
#include "unity.h"
#include "utils/common.h"
#include "mbedtls/pkcs5.h"
#include "crypto/sha1.h"
#if SOC_WIFI_SUPPORTED
#define PMK_LEN 32
TEST_CASE("Test pbkdf2", "[crypto-pbkdf2]")
{
uint8_t PMK[PMK_LEN];
uint8_t ssid_len;
uint8_t passphrase_len;
uint8_t ssid[MAX_SSID_LEN];
uint8_t passphrase[MAX_PASSPHRASE_LEN];
uint8_t expected_pmk1[PMK_LEN] =
{0xe7, 0x90, 0xd0, 0x65, 0x67, 0xf0, 0xbf, 0xca, 0xca, 0x10, 0x88, 0x0b, 0x85, 0xb2, 0x33, 0xe5,
0xe1, 0xd5, 0xe5, 0xb8, 0xd0, 0xfd, 0x94, 0x60, 0x56, 0x95, 0x5e, 0x41, 0x5a, 0x7f, 0xfa, 0xfa};
uint8_t expected_pmk[PMK_LEN];
/* Compare Fast PBKDF output with expected output*/
pbkdf2_sha1("espressif", (uint8_t *)"espressif", strlen("espressif"), 4096, PMK, PMK_LEN);
TEST_ASSERT(memcmp(PMK, expected_pmk1, PMK_LEN) == 0);
/* Compare fast PBKDF output with mbedtls pbkdf2 function's output */
pbkdf2_sha1("espressif2", (uint8_t *)"espressif2", strlen("espressif2"), 4096, PMK, PMK_LEN);
mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1, (const unsigned char *) "espressif2",
strlen("espressif2") , (const unsigned char *)"espressif2",
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;
os_get_random(ssid, ssid_len);
passphrase_len = os_random();
passphrase_len %= MAX_PASSPHRASE_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);
/* 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);
}
#endif /* SOC_WIFI_SUPPORTED */

View File

@ -27,6 +27,8 @@ These third party libraries can be included into the application (firmware) prod
* `wpa_supplicant`_ Copyright (c) 2003-2022 Jouni Malinen <j@w1.fi> and contributors and licensed under the BSD license.
* :component_file:`Fast PBKDF2 <wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls.c>` Copyright (c) 2015 Joseph Birr-Pixton and licensed under CC0 Public Domain Dedication license.
* `FreeBSD net80211`_ Copyright (c) 2004-2008 Sam Leffler, Errno Consulting and licensed under the BSD license.
* `argtable3`_ argument parsing library Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann and licensed under 3-clause BSD license. argtable3 also includes the following software components. For details, please see argtable3 :component_file:`LICENSE file<console/argtable3/LICENSE>`.

View File

@ -195,6 +195,7 @@ ignore:
- components/http_parser/
- components/wpa_supplicant/src/
- '!components/wpa_supplicant/esp_supplicant/'
- components/wpa_supplicant/esp_supplicant/src/crypto/fastpbkdf2*
- components/bt/host/bluedroid/
- '!components/bt/host/bluedroid/api/'
- '!components/bt/host/bluedroid/btc/'