Merge branch 'feature/mbedtls_psa_migration_v6.0' into 'release/v6.0'

feat(mbedtls): PSA Migration to release v6.0

See merge request espressif/esp-idf!43323
This commit is contained in:
Mahavir Jain
2025-12-21 13:52:48 +05:30
234 changed files with 14728 additions and 9796 deletions
+245 -71
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -9,9 +9,10 @@
#include "esp_log.h"
#include "esp_err.h"
#include <mbedtls/sha512.h>
#include "psa/crypto.h"
#include "esp_srp_mpi.h"
#include "esp_srp.h"
#include "esp_check.h"
#define SHA512_HASH_SZ 64
@@ -178,33 +179,63 @@ void esp_srp_free(esp_srp_handle_t *hd)
static esp_mpi_t *calculate_x(char *bytes_salt, int salt_len, const char *username, int username_len, const char *pass, int pass_len)
{
unsigned char digest[SHA512_HASH_SZ];
mbedtls_sha512_context ctx;
ESP_LOGD(TAG, "Username: %s | Passphrase: %s | Passphrase length: %d", username, pass, pass_len);
mbedtls_sha512_init(&ctx);
mbedtls_sha512_starts(&ctx, 0);
mbedtls_sha512_update(&ctx, (unsigned char *)username, username_len);
mbedtls_sha512_update(&ctx, (unsigned char *)":", 1);
mbedtls_sha512_update(&ctx, (unsigned char *)pass, pass_len);
mbedtls_sha512_finish(&ctx, digest);
// ret is unused here as it is required by the esp_check macros, suppressing the unused variable warning
__attribute__((unused)) esp_err_t ret = ESP_FAIL;
mbedtls_sha512_init(&ctx);
mbedtls_sha512_starts(&ctx, 0);
mbedtls_sha512_update(&ctx, (unsigned char *)bytes_salt, salt_len);
mbedtls_sha512_update(&ctx, digest, sizeof(digest));
mbedtls_sha512_finish(&ctx, digest);
mbedtls_sha512_free(&ctx);
unsigned char digest[SHA512_HASH_SZ];
psa_hash_operation_t hash_op = PSA_HASH_OPERATION_INIT;
psa_status_t status;
/* Add validation for input parameters */
if (!bytes_salt || !username || !pass || salt_len <= 0 || username_len <= 0 || pass_len <= 0) {
ESP_LOGE(TAG, "Invalid parameters: salt=%p, username=%p, pass=%p, salt_len=%d, username_len=%d, pass_len=%d",
bytes_salt, username, pass, salt_len, username_len, pass_len);
return NULL;
}
ESP_LOGD(TAG, "Username: %s | Passphrase: %s | Passphrase length: %d", username, pass, pass_len);
status = psa_hash_setup(&hash_op, PSA_ALG_SHA_512);
ESP_RETURN_ON_FALSE(status == PSA_SUCCESS, NULL, TAG, "Failed to setup hash operation: %d", status);
psa_hash_update(&hash_op, (unsigned char *)username, username_len);
psa_hash_update(&hash_op, (unsigned char *)":", 1);
psa_hash_update(&hash_op, (unsigned char *)pass, pass_len);
size_t hash_len = 0;
status = psa_hash_finish(&hash_op, digest, sizeof(digest), &hash_len);
ESP_GOTO_ON_FALSE(status == PSA_SUCCESS && hash_len == SHA512_HASH_SZ, ESP_FAIL, error, TAG,
"Hash operation failed: status=%d, hash_len=%d", status, hash_len);
status = psa_hash_setup(&hash_op, PSA_ALG_SHA_512);
ESP_RETURN_ON_FALSE(status == PSA_SUCCESS, NULL, TAG, "Failed to setup hash operation: %d", status);
psa_hash_update(&hash_op, (unsigned char *)bytes_salt, salt_len);
psa_hash_update(&hash_op, digest, sizeof(digest));
status = psa_hash_finish(&hash_op, digest, sizeof(digest), &hash_len);
ESP_GOTO_ON_FALSE(status == PSA_SUCCESS && hash_len == SHA512_HASH_SZ, ESP_FAIL, error, TAG,
"Hash operation failed: status=%d, hash_len=%d", status, hash_len);
return esp_mpi_new_from_bin((char *)digest, sizeof(digest));
error:
psa_hash_abort(&hash_op);
return NULL;
}
static esp_mpi_t *calculate_padded_hash(esp_srp_handle_t *hd, const char *a, int len_a, const char *b, int len_b)
{
unsigned char digest[SHA512_HASH_SZ];
mbedtls_sha512_context ctx;
psa_hash_operation_t hash_op = PSA_HASH_OPERATION_INIT;
psa_status_t status;
int pad_len;
size_t hash_len = 0;
char *s = NULL;
/* Add validation for input parameters */
if (!hd || !a || !b || len_a <= 0 || len_b <= 0) {
ESP_LOGE(TAG, "Invalid parameters: hd=%p, a=%p, b=%p, len_a=%d, len_b=%d",
hd, a, b, len_a, len_b);
return NULL;
}
if (len_a > len_b) {
pad_len = hd->len_n - len_b;
} else {
@@ -218,28 +249,37 @@ static esp_mpi_t *calculate_padded_hash(esp_srp_handle_t *hd, const char *a, int
}
}
mbedtls_sha512_init(&ctx);
mbedtls_sha512_starts(&ctx, 0);
/* PAD (a) */
if (s && (len_a != hd->len_n)) {
mbedtls_sha512_update(&ctx, (unsigned char *)s, hd->len_n - len_a);
status = psa_hash_setup(&hash_op, PSA_ALG_SHA_512);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "Failed to setup hash operation: %d", status);
if (s) {
free(s);
}
return NULL;
}
mbedtls_sha512_update(&ctx, (unsigned char *)a, len_a);
/* PAD (a) */
if (s && (len_a != hd->len_n)) {
psa_hash_update(&hash_op, (unsigned char *)s, hd->len_n - len_a);
}
psa_hash_update(&hash_op, (unsigned char *)a, len_a);
/* PAD (b) */
if (s && (len_b != hd->len_n)) {
mbedtls_sha512_update(&ctx, (unsigned char *)s, hd->len_n - len_b);
psa_hash_update(&hash_op, (unsigned char *)s, hd->len_n - len_b);
}
mbedtls_sha512_update(&ctx, (unsigned char *)b, len_b);
mbedtls_sha512_finish(&ctx, digest);
mbedtls_sha512_free(&ctx);
psa_hash_update(&hash_op, (unsigned char *)b, len_b);
status = psa_hash_finish(&hash_op, digest, sizeof(digest), &hash_len);
if (s) {
free(s);
}
if (status != PSA_SUCCESS || hash_len != SHA512_HASH_SZ) {
psa_hash_abort(&hash_op);
ESP_LOGE(TAG, "Hash operation failed: status=%d, hash_len=%d", status, hash_len);
return NULL;
}
return esp_mpi_new_from_bin((char *)digest, sizeof(digest));
}
@@ -250,24 +290,53 @@ static esp_mpi_t *calculate_padded_hash(esp_srp_handle_t *hd, const char *a, int
*/
static esp_mpi_t *calculate_k(esp_srp_handle_t *hd)
{
if (!hd) {
ESP_LOGE(TAG, "Invalid parameter: hd=%p", hd);
return NULL;
}
return calculate_padded_hash(hd, hd->bytes_n, hd->len_n, hd->bytes_g, hd->len_g);
}
static esp_mpi_t *calculate_u(esp_srp_handle_t *hd, char *A, int len_A)
{
if (!hd || !A || len_A <= 0) {
ESP_LOGE(TAG, "Invalid parameters: hd=%p, A=%p, len_A=%d", hd, A, len_A);
return NULL;
}
return calculate_padded_hash(hd, A, len_A, hd->bytes_B, hd->len_B);
}
static esp_err_t __esp_srp_srv_pubkey(esp_srp_handle_t *hd, char **bytes_B, int *len_B)
{
esp_mpi_t *k = calculate_k(hd);
esp_mpi_t *k = NULL;
esp_mpi_t *kv = NULL;
esp_mpi_t *gb = NULL;
/* Add validation for input parameters */
if (!hd || !bytes_B || !len_B) {
ESP_LOGE(TAG, "Invalid parameters: hd=%p, bytes_B=%p, len_B=%p", hd, bytes_B, len_B);
return ESP_ERR_INVALID_ARG;
}
if (!hd->v) {
ESP_LOGE(TAG, "Verifier must be set before generating server public key");
return ESP_ERR_INVALID_STATE;
}
k = calculate_k(hd);
if (!k) {
goto error;
}
hexdump_mpi("k", k);
// At this point hd->b, hd->B must be NULL
// If it is not NULL, then free it.
if (hd->b || hd->B) {
esp_mpi_free(hd->b);
hd->b = NULL;
esp_mpi_free(hd->B);
hd->B = NULL;
}
hd->b = esp_mpi_new();
if (!hd->b) {
goto error;
@@ -317,6 +386,18 @@ error:
static esp_err_t _esp_srp_gen_salt_verifier(esp_srp_handle_t *hd, const char *username, int username_len,
const char *pass, int pass_len, int salt_len)
{
/* Add validation for input parameters */
if (!hd || !username || !pass) {
ESP_LOGE(TAG, "Invalid parameters: hd=%p, username=%p, pass=%p", hd, username, pass);
return ESP_ERR_INVALID_ARG;
}
if (username_len <= 0 || pass_len <= 0 || salt_len <= 0) {
ESP_LOGE(TAG, "Invalid length parameters: username_len=%d, pass_len=%d, salt_len=%d",
username_len, pass_len, salt_len);
return ESP_ERR_INVALID_ARG;
}
/* Get Salt */
int str_salt_len;
esp_mpi_t *x = NULL;
@@ -382,9 +463,16 @@ esp_err_t esp_srp_srv_pubkey(esp_srp_handle_t *hd, const char *username, int use
const char *pass, int pass_len, int salt_len,
char **bytes_B, int *len_B, char **bytes_salt)
{
if (!hd || !username || !pass) {
if (!hd || !username || !pass || !bytes_B || !len_B || !bytes_salt) {
return ESP_ERR_INVALID_ARG;
}
if (username_len <= 0 || pass_len <= 0 || salt_len <= 0) {
ESP_LOGE(TAG, "Invalid length parameters: username_len=%d, pass_len=%d, salt_len=%d",
username_len, pass_len, salt_len);
return ESP_ERR_INVALID_ARG;
}
if (ESP_OK != _esp_srp_gen_salt_verifier(hd, username, username_len, pass, pass_len, salt_len)) {
goto error;
}
@@ -429,6 +517,19 @@ esp_err_t esp_srp_gen_salt_verifier(const char *username, int username_len,
{
esp_err_t ret = ESP_FAIL;
/* Add validation for input parameters */
if (!username || !pass || !bytes_salt || !verifier || !verifier_len) {
ESP_LOGE(TAG, "Invalid parameters: username=%p, pass=%p, bytes_salt=%p, verifier=%p, verifier_len=%p",
username, pass, bytes_salt, verifier, verifier_len);
return ESP_ERR_INVALID_ARG;
}
if (username_len <= 0 || pass_len <= 0 || salt_len <= 0) {
ESP_LOGE(TAG, "Invalid length parameters: username_len=%d, pass_len=%d, salt_len=%d",
username_len, pass_len, salt_len);
return ESP_ERR_INVALID_ARG;
}
/* allocate and init temporary SRP handle */
esp_srp_handle_t *srp_hd = esp_srp_init(ESP_NG_3072);
if (!srp_hd) {
@@ -461,6 +562,16 @@ cleanup:
esp_err_t esp_srp_set_salt_verifier(esp_srp_handle_t *hd, const char *salt, int salt_len,
const char *verifier, int verifier_len)
{
if (!hd || !salt || !verifier) {
return ESP_ERR_INVALID_ARG;
}
if (salt_len <= 0 || verifier_len <= 0) {
ESP_LOGE(TAG, "Invalid length parameters: salt_len=%d, verifier_len=%d",
salt_len, verifier_len);
return ESP_ERR_INVALID_ARG;
}
hd->bytes_s = malloc(salt_len);
if (!hd->bytes_s) {
goto error;
@@ -498,6 +609,26 @@ error:
esp_err_t esp_srp_get_session_key(esp_srp_handle_t *hd, char *bytes_A, int len_A, char **bytes_key, uint16_t *len_key)
{
esp_err_t ret = ESP_FAIL;
/* Add validation for input parameters */
if (!hd || !bytes_A || !bytes_key || !len_key) {
ESP_LOGE(TAG, "Invalid parameters: hd=%p, bytes_A=%p, bytes_key=%p, len_key=%p",
hd, bytes_A, bytes_key, len_key);
return ESP_ERR_INVALID_ARG;
}
if (len_A <= 0) {
ESP_LOGE(TAG, "Invalid length parameter: len_A=%d", len_A);
return ESP_ERR_INVALID_ARG;
}
/* Check if the necessary SRP parameters are initialized */
if (!hd->b || !hd->v || !hd->n) {
ESP_LOGE(TAG, "SRP parameters not properly initialized");
return ESP_ERR_INVALID_STATE;
}
esp_mpi_t *u = NULL;
esp_mpi_t *vu = NULL;
esp_mpi_t *avu = NULL;
@@ -545,9 +676,16 @@ esp_err_t esp_srp_get_session_key(esp_srp_handle_t *hd, char *bytes_A, int len_A
goto error;
}
mbedtls_sha512((unsigned char *)bytes_S, len_S, (unsigned char *)hd->session_key, 0);
psa_hash_operation_t hash_op = PSA_HASH_OPERATION_INIT;
psa_status_t status = psa_hash_setup(&hash_op, PSA_ALG_SHA_512);
ESP_GOTO_ON_FALSE(status == PSA_SUCCESS, ESP_FAIL, error, TAG, "Failed to setup hash operation: %d", status);
psa_hash_update(&hash_op, (unsigned char *)bytes_S, len_S);
size_t hash_len = 0;
status = psa_hash_finish(&hash_op, (unsigned char *)hd->session_key, SHA512_HASH_SZ, &hash_len);
ESP_GOTO_ON_FALSE(status == PSA_SUCCESS && hash_len == SHA512_HASH_SZ, ESP_FAIL, error, TAG,
"Hash operation failed: status=%d, hash_len=%d", status, hash_len);
*len_key = hash_len;
*bytes_key = hd->session_key;
*len_key = SHA512_HASH_SZ;
free(bytes_S);
esp_mpi_free(vu);
@@ -583,73 +721,109 @@ error:
free(hd->bytes_A);
hd->bytes_A = NULL;
}
return ESP_FAIL;
psa_hash_abort(&hash_op);
return ret;
}
esp_err_t esp_srp_exchange_proofs(esp_srp_handle_t *hd, char *username, uint16_t username_len, char *bytes_user_proof, char *bytes_host_proof)
{
esp_err_t ret = ESP_FAIL;
/* Add validation for input parameters */
if (!hd || !username || !bytes_user_proof || !bytes_host_proof) {
ESP_LOGE(TAG, "Invalid parameters: hd=%p, username=%p, bytes_user_proof=%p, bytes_host_proof=%p",
hd, username, bytes_user_proof, bytes_host_proof);
return ESP_ERR_INVALID_ARG;
}
if (username_len <= 0) {
ESP_LOGE(TAG, "Invalid username length: %d", username_len);
return ESP_ERR_INVALID_ARG;
}
/* Check if the necessary SRP parameters and session key are initialized */
if (!hd->bytes_A || !hd->bytes_B || !hd->bytes_s || !hd->session_key) {
ESP_LOGE(TAG, "SRP exchange not properly initialized: A=%p, B=%p, s=%p, key=%p",
hd->bytes_A, hd->bytes_B, hd->bytes_s, hd->session_key);
return ESP_ERR_INVALID_STATE;
}
/* First calculate M */
unsigned char hash_n[SHA512_HASH_SZ];
unsigned char hash_g[SHA512_HASH_SZ];
unsigned char hash_n_xor_g[SHA512_HASH_SZ];
int i;
char *s = NULL;
unsigned char hash_I[SHA512_HASH_SZ];
mbedtls_sha512((unsigned char *)username, username_len, (unsigned char *)hash_I, 0);
mbedtls_sha512((unsigned char *)hd->bytes_n, hd->len_n, (unsigned char *)hash_n, 0);
size_t hash_len = 0;
psa_hash_operation_t hash_op = PSA_HASH_OPERATION_INIT;
psa_status_t status = psa_hash_setup(&hash_op, PSA_ALG_SHA_512);
ESP_RETURN_ON_FALSE(status == PSA_SUCCESS, ESP_FAIL, TAG, "Failed to setup hash operation: %d", status);
psa_hash_update(&hash_op, (unsigned char *)username, username_len);
status = psa_hash_finish(&hash_op, (unsigned char *)hash_I, SHA512_HASH_SZ, &hash_len);
ESP_GOTO_ON_FALSE(status == PSA_SUCCESS && hash_len == SHA512_HASH_SZ, ESP_FAIL, error, TAG,
"Hash operation failed: status=%d, hash_len=%d", status, hash_len);
status = psa_hash_setup(&hash_op, PSA_ALG_SHA_512);
ESP_RETURN_ON_FALSE(status == PSA_SUCCESS, ESP_FAIL, TAG, "Failed to setup hash operation: %d", status);
psa_hash_update(&hash_op, (unsigned char *)hd->bytes_n, hd->len_n);
status = psa_hash_finish(&hash_op, (unsigned char *)hash_n, SHA512_HASH_SZ, &hash_len);
ESP_GOTO_ON_FALSE(status == PSA_SUCCESS && hash_len == SHA512_HASH_SZ, ESP_FAIL, error, TAG,
"Hash operation failed: status=%d, hash_len=%d", status, hash_len);
int pad_len = hd->len_n - hd->len_g;
char *s = calloc(pad_len, sizeof(char));
if (!s) {
return ESP_ERR_NO_MEM;
}
s = calloc(pad_len, sizeof(char));
ESP_RETURN_ON_FALSE(s, ESP_ERR_NO_MEM, TAG, "Failed to allocate memory");
mbedtls_sha512_context ctx;
mbedtls_sha512_init(&ctx);
mbedtls_sha512_starts(&ctx, 0);
mbedtls_sha512_update(&ctx, (unsigned char *)s, pad_len);
mbedtls_sha512_update(&ctx, (unsigned char *)hd->bytes_g, hd->len_g);
mbedtls_sha512_finish(&ctx, hash_g);
mbedtls_sha512_free(&ctx);
status = psa_hash_setup(&hash_op, PSA_ALG_SHA_512);
ESP_RETURN_ON_FALSE(status == PSA_SUCCESS, ESP_FAIL, TAG, "Failed to setup hash operation: %d", status);
psa_hash_update(&hash_op, (unsigned char *)s, pad_len);
psa_hash_update(&hash_op, (unsigned char *)hd->bytes_g, hd->len_g);
status = psa_hash_finish(&hash_op, (unsigned char *)hash_g, SHA512_HASH_SZ, &hash_len);
ESP_GOTO_ON_FALSE(status == PSA_SUCCESS && hash_len == SHA512_HASH_SZ, ESP_FAIL, error, TAG,
"Hash operation failed: status=%d, hash_len=%d", status, hash_len);
for (i = 0; i < SHA512_HASH_SZ; i++) {
hash_n_xor_g[i] = hash_n[i] ^ hash_g[i];
}
unsigned char digest[SHA512_HASH_SZ];
mbedtls_sha512_init(&ctx);
mbedtls_sha512_starts(&ctx, 0);
mbedtls_sha512_update(&ctx, hash_n_xor_g, SHA512_HASH_SZ);
mbedtls_sha512_update(&ctx, hash_I, SHA512_HASH_SZ);
mbedtls_sha512_update(&ctx, (unsigned char *)hd->bytes_s, hd->len_s);
mbedtls_sha512_update(&ctx, (unsigned char *)hd->bytes_A, hd->len_A);
mbedtls_sha512_update(&ctx, (unsigned char *)hd->bytes_B, hd->len_B);
mbedtls_sha512_update(&ctx, (unsigned char *)hd->session_key, SHA512_HASH_SZ);
mbedtls_sha512_finish(&ctx, digest);
mbedtls_sha512_free(&ctx);
status = psa_hash_setup(&hash_op, PSA_ALG_SHA_512);
ESP_RETURN_ON_FALSE(status == PSA_SUCCESS, ESP_FAIL, TAG, "Failed to setup hash operation: %d", status);
psa_hash_update(&hash_op, hash_n_xor_g, SHA512_HASH_SZ);
psa_hash_update(&hash_op, hash_I, SHA512_HASH_SZ);
psa_hash_update(&hash_op, (unsigned char *)hd->bytes_s, hd->len_s);
psa_hash_update(&hash_op, (unsigned char *)hd->bytes_A, hd->len_A);
psa_hash_update(&hash_op, (unsigned char *)hd->bytes_B, hd->len_B);
psa_hash_update(&hash_op, (unsigned char *)hd->session_key, SHA512_HASH_SZ);
status = psa_hash_finish(&hash_op, digest, SHA512_HASH_SZ, &hash_len);
ESP_GOTO_ON_FALSE(status == PSA_SUCCESS && hash_len == SHA512_HASH_SZ, ESP_FAIL, error, TAG,
"Hash operation failed: status=%d, hash_len=%d", status, hash_len);
ESP_LOGD(TAG, "M ->");
ESP_LOG_BUFFER_HEX_LEVEL(TAG, (char *)digest, sizeof(digest), ESP_LOG_DEBUG);
if (memcmp(bytes_user_proof, digest, SHA512_HASH_SZ) != 0) {
free(s);
return ESP_FAIL;
}
ESP_GOTO_ON_FALSE(memcmp(bytes_user_proof, digest, SHA512_HASH_SZ) == 0, ESP_FAIL, error, TAG, "Failed to validate user proof");
/* M is now validated, let's proceed to H(AMK) */
mbedtls_sha512_init(&ctx);
mbedtls_sha512_starts(&ctx, 0);
mbedtls_sha512_update(&ctx, (unsigned char *)hd->bytes_A, hd->len_A);
mbedtls_sha512_update(&ctx, digest, SHA512_HASH_SZ);
mbedtls_sha512_update(&ctx, (unsigned char *)hd->session_key, SHA512_HASH_SZ);
mbedtls_sha512_finish(&ctx, (unsigned char *)bytes_host_proof);
mbedtls_sha512_free(&ctx);
status = psa_hash_setup(&hash_op, PSA_ALG_SHA_512);
ESP_RETURN_ON_FALSE(status == PSA_SUCCESS, ESP_FAIL, TAG, "Failed to setup hash operation: %d", status);
psa_hash_update(&hash_op, (unsigned char *)hd->bytes_A, hd->len_A);
psa_hash_update(&hash_op, digest, SHA512_HASH_SZ);
psa_hash_update(&hash_op, (unsigned char *)hd->session_key, SHA512_HASH_SZ);
status = psa_hash_finish(&hash_op, (unsigned char *)bytes_host_proof, SHA512_HASH_SZ, &hash_len);
ESP_GOTO_ON_FALSE(status == PSA_SUCCESS && hash_len == SHA512_HASH_SZ, ESP_FAIL, error, TAG,
"Hash operation failed: status=%d, hash_len=%d", status, hash_len);
ESP_LOGD(TAG, "AMK ->");
ESP_LOG_BUFFER_HEX_LEVEL(TAG, (char *)bytes_host_proof, SHA512_HASH_SZ, ESP_LOG_DEBUG);
ret = ESP_OK;
error:
psa_hash_abort(&hash_op);
if (s) {
free(s);
}
return ESP_OK;
return ret;
}
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#define MBEDTLS_DECLARE_PRIVATE_IDENTIFIERS
#include "esp_srp_mpi.h"
esp_mpi_t *esp_mpi_new(void)
@@ -9,9 +9,7 @@
#include "string.h"
#include "stdio.h"
#include "mbedtls/bignum.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/private/bignum.h"
#include "esp_random.h"
#ifdef __cplusplus
+132 -141
View File
@@ -25,13 +25,9 @@
#define ACCESS_ECDH(S, var) S->MBEDTLS_PRIVATE(ctx).MBEDTLS_PRIVATE(mbed_ecdh).MBEDTLS_PRIVATE(var)
#endif
#include <mbedtls/aes.h>
#include <mbedtls/sha256.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/ecdh.h>
#include <mbedtls/error.h>
#include <mbedtls/constant_time.h>
#include "psa/crypto.h"
#include <protocomm_security.h>
#include <protocomm_security1.h>
@@ -65,26 +61,21 @@ typedef struct session {
uint8_t sym_key[PUBLIC_KEY_LEN];
uint8_t rand[SZ_RANDOM];
/* Operation counter for CTR mode nonce */
uint32_t op_counter;
/* mbedtls context data for AES */
mbedtls_aes_context ctx_aes;
psa_cipher_operation_t ctx_aes;
psa_key_id_t key_id;
psa_key_id_t key_id_sym;
unsigned char stb[16];
size_t nc_off;
} session_t;
static void flip_endian(uint8_t *data, size_t len)
{
uint8_t swp_buf;
for (int i = 0; i < len/2; i++) {
swp_buf = data[i];
data[i] = data[len - i - 1];
data[len - i - 1] = swp_buf;
}
}
static void hexdump(const char *msg, uint8_t *buf, int len)
{
ESP_LOGD(TAG, "%s:", msg);
ESP_LOG_BUFFER_HEX_LEVEL(TAG, buf, len, ESP_LOG_DEBUG);
ESP_LOG_BUFFER_HEX_LEVEL(TAG, buf, len, ESP_LOG_INFO);
}
static esp_err_t handle_session_command1(session_t *cur_session,
@@ -93,8 +84,6 @@ static esp_err_t handle_session_command1(session_t *cur_session,
{
ESP_LOGD(TAG, "Request to handle setup1_command");
Sec1Payload *in = (Sec1Payload *) req->sec1;
uint8_t check_buf[PUBLIC_KEY_LEN];
int mbed_err;
if (cur_session->state != SESSION_STATE_CMD1) {
ESP_LOGE(TAG, "Invalid state of session %d (expected %d)", SESSION_STATE_CMD1, cur_session->state);
@@ -117,28 +106,55 @@ static esp_err_t handle_session_command1(session_t *cur_session,
}
/* Initialize crypto context */
mbedtls_aes_init(&cur_session->ctx_aes);
memset(cur_session->stb, 0, sizeof(cur_session->stb));
cur_session->nc_off = 0;
cur_session->op_counter = 0;
hexdump("Client verifier", in->sc1->client_verify_data.data,
hexdump("Data to decrypt", in->sc1->client_verify_data.data,
in->sc1->client_verify_data.len);
mbed_err = mbedtls_aes_setkey_enc(&cur_session->ctx_aes, cur_session->sym_key,
sizeof(cur_session->sym_key)*8);
if (mbed_err != 0) {
ESP_LOGE(TAG, "Failure at mbedtls_aes_setkey_enc with error code : -0x%x", -mbed_err);
mbedtls_aes_free(&cur_session->ctx_aes);
psa_status_t status;
psa_key_id_t key_id = 0;
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_algorithm_t alg = PSA_ALG_CTR;
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_DECRYPT | PSA_KEY_USAGE_ENCRYPT);
psa_set_key_algorithm(&key_attributes, alg);
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_AES);
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE);
psa_set_key_bits(&key_attributes, sizeof(cur_session->sym_key) * 8);
status = psa_import_key(&key_attributes, cur_session->sym_key, sizeof(cur_session->sym_key), &key_id);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_import_key failed with status=%d", status);
return ESP_FAIL;
}
cur_session->key_id_sym = key_id;
psa_reset_key_attributes(&key_attributes);
size_t output_len = 0;
size_t cipher_size = PSA_CIPHER_DECRYPT_OUTPUT_SIZE(PSA_KEY_TYPE_AES, alg, in->sc1->client_verify_data.len);
uint8_t check_buf[cipher_size];
cur_session->ctx_aes = psa_cipher_operation_init();
status = psa_cipher_encrypt_setup(&cur_session->ctx_aes, key_id, alg);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_cipher_encrypt_setup failed with status=%d", status);
psa_cipher_abort(&cur_session->ctx_aes);
psa_destroy_key(key_id);
return ESP_FAIL;
}
status = psa_cipher_set_iv(&cur_session->ctx_aes, cur_session->rand, sizeof(cur_session->rand));
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_cipher_set_iv failed with status=%d", status);
psa_cipher_abort(&cur_session->ctx_aes);
psa_destroy_key(key_id);
return ESP_FAIL;
}
mbed_err = mbedtls_aes_crypt_ctr(&cur_session->ctx_aes,
PUBLIC_KEY_LEN, &cur_session->nc_off,
cur_session->rand, cur_session->stb,
in->sc1->client_verify_data.data, check_buf);
if (mbed_err != 0) {
ESP_LOGE(TAG, "Failure at mbedtls_aes_crypt_ctr with error code : -0x%x", -mbed_err);
mbedtls_aes_free(&cur_session->ctx_aes);
status = psa_cipher_update(&cur_session->ctx_aes, in->sc1->client_verify_data.data,
in->sc1->client_verify_data.len, check_buf, sizeof(check_buf), &output_len);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_cipher_update failed with status=%d", status);
psa_cipher_abort(&cur_session->ctx_aes);
psa_destroy_key(key_id);
return ESP_FAIL;
}
@@ -148,7 +164,7 @@ static esp_err_t handle_session_command1(session_t *cur_session,
if (mbedtls_ct_memcmp(check_buf, cur_session->device_pubkey,
sizeof(cur_session->device_pubkey)) != 0) {
ESP_LOGE(TAG, "Key mismatch. Close connection");
mbedtls_aes_free(&cur_session->ctx_aes);
psa_destroy_key(key_id);
if (esp_event_post(PROTOCOMM_SECURITY_SESSION_EVENT, PROTOCOMM_SECURITY_SESSION_CREDENTIALS_MISMATCH, NULL, 0, portMAX_DELAY) != ESP_OK) {
ESP_LOGE(TAG, "Failed to post credential mismatch event");
}
@@ -161,7 +177,6 @@ static esp_err_t handle_session_command1(session_t *cur_session,
ESP_LOGE(TAG, "Error allocating memory for response1");
free(out);
free(out_resp);
mbedtls_aes_free(&cur_session->ctx_aes);
return ESP_ERR_NO_MEM;
}
@@ -174,20 +189,14 @@ static esp_err_t handle_session_command1(session_t *cur_session,
ESP_LOGE(TAG, "Error allocating ciphertext buffer");
free(out);
free(out_resp);
mbedtls_aes_free(&cur_session->ctx_aes);
return ESP_ERR_NO_MEM;
}
mbed_err = mbedtls_aes_crypt_ctr(&cur_session->ctx_aes,
PUBLIC_KEY_LEN, &cur_session->nc_off,
cur_session->rand, cur_session->stb,
cur_session->client_pubkey, outbuf);
if (mbed_err != 0) {
ESP_LOGE(TAG, "Failure at mbedtls_aes_crypt_ctr with error code : -0x%x", -mbed_err);
size_t outlen = 0;
status = psa_cipher_update(&cur_session->ctx_aes, cur_session->client_pubkey, sizeof(cur_session->client_pubkey), outbuf, PUBLIC_KEY_LEN, &outlen);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "Failed at psa_cipher_update with error code : %d", status);
free(outbuf);
free(out);
free(out_resp);
mbedtls_aes_free(&cur_session->ctx_aes);
return ESP_FAIL;
}
@@ -221,7 +230,6 @@ static esp_err_t handle_session_command0(session_t *cur_session,
ESP_LOGD(TAG, "Request to handle setup0_command");
Sec1Payload *in = (Sec1Payload *) req->sec1;
esp_err_t ret;
int mbed_err;
if (cur_session->state != SESSION_STATE_CMD0) {
ESP_LOGW(TAG, "Invalid state of session %d (expected %d). Restarting session.",
@@ -237,55 +245,30 @@ static esp_err_t handle_session_command0(session_t *cur_session,
return ESP_ERR_INVALID_ARG;
}
mbedtls_ecdh_context *ctx_server = malloc(sizeof(mbedtls_ecdh_context));
mbedtls_entropy_context *entropy = malloc(sizeof(mbedtls_entropy_context));
mbedtls_ctr_drbg_context *ctr_drbg = malloc(sizeof(mbedtls_ctr_drbg_context));
if (!ctx_server || !entropy || !ctr_drbg) {
ESP_LOGE(TAG, "Failed to allocate memory for mbedtls context");
free(ctx_server);
free(entropy);
free(ctr_drbg);
return ESP_ERR_NO_MEM;
psa_status_t status;
psa_key_id_t key_id = 0;
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_MONTGOMERY));
psa_set_key_bits(&key_attributes, 255);
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE);
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_EXPORT);
psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDH);
status = psa_generate_key(&key_attributes, &key_id);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_generate_key failed with status=%d", status);
psa_reset_key_attributes(&key_attributes);
return ESP_FAIL;
}
psa_reset_key_attributes(&key_attributes);
size_t olen = 0;
mbedtls_ecdh_init(ctx_server);
mbedtls_ecdh_setup(ctx_server, MBEDTLS_ECP_DP_CURVE25519);
mbedtls_ctr_drbg_init(ctr_drbg);
mbedtls_entropy_init(entropy);
mbed_err = mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func,
entropy, NULL, 0);
if (mbed_err != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_ctr_drbg_seed with error code : -0x%x", -mbed_err);
ret = ESP_FAIL;
goto exit_cmd0;
status = psa_export_public_key(key_id, cur_session->device_pubkey, PUBLIC_KEY_LEN, &olen);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_export_public_key failed with status=%d", status);
psa_reset_key_attributes(&key_attributes);
return ESP_FAIL;
}
mbed_err = mbedtls_ecp_group_load(ACCESS_ECDH(&ctx_server, grp), MBEDTLS_ECP_DP_CURVE25519);
if (mbed_err != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_ecp_group_load with error code : -0x%x", -mbed_err);
ret = ESP_FAIL;
goto exit_cmd0;
}
mbed_err = mbedtls_ecdh_gen_public(ACCESS_ECDH(&ctx_server, grp), ACCESS_ECDH(&ctx_server, d), ACCESS_ECDH(&ctx_server, Q),
mbedtls_ctr_drbg_random, ctr_drbg);
if (mbed_err != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_ecdh_gen_public with error code : -0x%x", -mbed_err);
ret = ESP_FAIL;
goto exit_cmd0;
}
mbed_err = mbedtls_mpi_write_binary(ACCESS_ECDH(&ctx_server, Q).MBEDTLS_PRIVATE(X),
cur_session->device_pubkey,
PUBLIC_KEY_LEN);
if (mbed_err != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_mpi_write_binary with error code : -0x%x", -mbed_err);
ret = ESP_FAIL;
goto exit_cmd0;
}
flip_endian(cur_session->device_pubkey, PUBLIC_KEY_LEN);
memcpy(cur_session->client_pubkey, in->sc0->client_pubkey.data, PUBLIC_KEY_LEN);
uint8_t *dev_pubkey = cur_session->device_pubkey;
@@ -293,49 +276,48 @@ static esp_err_t handle_session_command0(session_t *cur_session,
hexdump("Device pubkey", dev_pubkey, PUBLIC_KEY_LEN);
hexdump("Client pubkey", cli_pubkey, PUBLIC_KEY_LEN);
mbed_err = mbedtls_mpi_lset(ACCESS_ECDH(&ctx_server, Qp).MBEDTLS_PRIVATE(Z), 1);
if (mbed_err != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_mpi_lset with error code : -0x%x", -mbed_err);
status = psa_raw_key_agreement(PSA_ALG_ECDH, key_id, cur_session->client_pubkey, PUBLIC_KEY_LEN,
cur_session->sym_key, sizeof(cur_session->sym_key), &olen);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_raw_key_agreement failed with status=%d", status);
ret = ESP_FAIL;
goto exit_cmd0;
}
flip_endian(cur_session->client_pubkey, PUBLIC_KEY_LEN);
mbed_err = mbedtls_mpi_read_binary(ACCESS_ECDH(&ctx_server, Qp).MBEDTLS_PRIVATE(X), cli_pubkey, PUBLIC_KEY_LEN);
flip_endian(cur_session->client_pubkey, PUBLIC_KEY_LEN);
if (mbed_err != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_mpi_read_binary with error code : -0x%x", -mbed_err);
if (olen != sizeof(cur_session->sym_key)) {
ESP_LOGE(TAG, "psa_raw_key_agreement output length mismatch: expected %zu, got %zu",
sizeof(cur_session->sym_key), olen);
ret = ESP_FAIL;
goto exit_cmd0;
}
mbed_err = mbedtls_ecdh_compute_shared(ACCESS_ECDH(&ctx_server, grp), ACCESS_ECDH(&ctx_server, z), ACCESS_ECDH(&ctx_server, Qp),
ACCESS_ECDH(&ctx_server, d), mbedtls_ctr_drbg_random, ctr_drbg);
if (mbed_err != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_ecdh_compute_shared with error code : -0x%x", -mbed_err);
ret = ESP_FAIL;
goto exit_cmd0;
}
mbed_err = mbedtls_mpi_write_binary(ACCESS_ECDH(&ctx_server, z), cur_session->sym_key, PUBLIC_KEY_LEN);
if (mbed_err != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_mpi_write_binary with error code : -0x%x", -mbed_err);
ret = ESP_FAIL;
goto exit_cmd0;
}
flip_endian(cur_session->sym_key, PUBLIC_KEY_LEN);
cur_session->key_id = key_id;
if (pop != NULL && pop->data != NULL && pop->len != 0) {
ESP_LOGD(TAG, "Adding proof of possession");
uint8_t sha_out[PUBLIC_KEY_LEN];
mbed_err = mbedtls_sha256((const unsigned char *) pop->data, pop->len, sha_out, 0);
if (mbed_err != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_sha256_ret with error code : -0x%x", -mbed_err);
psa_hash_operation_t hash_operation = PSA_HASH_OPERATION_INIT;
status = psa_hash_setup(&hash_operation, PSA_ALG_SHA_256);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_hash_setup failed with status=%d", status);
ret = ESP_FAIL;
goto exit_cmd0;
}
status = psa_hash_update(&hash_operation, pop->data, pop->len);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_hash_update failed with status=%d", status);
psa_hash_abort(&hash_operation);
ret = ESP_FAIL;
goto exit_cmd0;
}
status = psa_hash_finish(&hash_operation, sha_out, sizeof(sha_out), &olen);
if (status != PSA_SUCCESS || olen != sizeof(sha_out)) {
ESP_LOGE(TAG, "psa_hash_finish failed with status=%d", status);
psa_hash_abort(&hash_operation);
ret = ESP_FAIL;
goto exit_cmd0;
}
for (int i = 0; i < PUBLIC_KEY_LEN; i++) {
cur_session->sym_key[i] ^= sha_out[i];
}
@@ -343,9 +325,9 @@ static esp_err_t handle_session_command0(session_t *cur_session,
hexdump("Shared key", cur_session->sym_key, PUBLIC_KEY_LEN);
mbed_err = mbedtls_ctr_drbg_random(ctr_drbg, cur_session->rand, SZ_RANDOM);
if (mbed_err != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_ctr_drbg_random with error code : -0x%x", -mbed_err);
status = psa_generate_random(cur_session->rand, SZ_RANDOM);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_generate_random failed with status=%d", status);
ret = ESP_FAIL;
goto exit_cmd0;
}
@@ -386,15 +368,11 @@ static esp_err_t handle_session_command0(session_t *cur_session,
ret = ESP_OK;
exit_cmd0:
mbedtls_ecdh_free(ctx_server);
free(ctx_server);
mbedtls_ctr_drbg_free(ctr_drbg);
free(ctr_drbg);
mbedtls_entropy_free(entropy);
free(entropy);
// Clean up the key_id if it wasn't stored in the session
// This happens when key agreement fails before cur_session->key_id is assigned
if (ret != ESP_OK && key_id != 0 && cur_session->key_id != key_id) {
psa_destroy_key(key_id);
}
return ret;
}
@@ -473,9 +451,21 @@ static esp_err_t sec1_close_session(protocomm_security_handle_t handle, uint32_t
return ESP_ERR_INVALID_STATE;
}
if (cur_session->state == SESSION_STATE_DONE) {
/* Free AES context data */
mbedtls_aes_free(&cur_session->ctx_aes);
if (cur_session->key_id != 0) {
psa_status_t status = psa_destroy_key(cur_session->key_id);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_destroy_key failed with status=%d", status);
}
}
if (cur_session->key_id_sym != 0) {
psa_status_t status = psa_destroy_key(cur_session->key_id_sym);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_destroy_key failed with status=%d", status);
}
}
psa_status_t status = psa_cipher_abort(&cur_session->ctx_aes);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_cipher_abort failed with status=%d", status);
}
memset(cur_session, 0, sizeof(session_t));
@@ -525,7 +515,7 @@ static esp_err_t sec1_cleanup(protocomm_security_handle_t handle)
return ESP_OK;
}
static esp_err_t sec1_decrypt(protocomm_security_handle_t handle,
static esp_err_t sec1_crypt(protocomm_security_handle_t handle,
uint32_t session_id,
const uint8_t *inbuf, ssize_t inlen,
uint8_t **outbuf, ssize_t *outlen)
@@ -552,10 +542,11 @@ static esp_err_t sec1_decrypt(protocomm_security_handle_t handle,
return ESP_ERR_NO_MEM;
}
int ret = mbedtls_aes_crypt_ctr(&cur_session->ctx_aes, inlen, &cur_session->nc_off,
cur_session->rand, cur_session->stb, inbuf, *outbuf);
if (ret != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_aes_crypt_ctr with error code : %d", ret);
size_t out_len = 0;
psa_status_t status = psa_cipher_update(&cur_session->ctx_aes, inbuf, inlen, *outbuf, *outlen, &out_len);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_cipher_update failed with status=%d", status);
free(*outbuf);
return ESP_FAIL;
}
return ESP_OK;
@@ -624,6 +615,6 @@ const protocomm_security_t protocomm_security1 = {
.new_transport_session = sec1_new_session,
.close_transport_session = sec1_close_session,
.security_req_handler = sec1_req_handler,
.encrypt = sec1_decrypt, /* Encrypt == decrypt for AES-CTR */
.decrypt = sec1_decrypt,
.encrypt = sec1_crypt, /* Encrypt == decrypt for AES-CTR */
.decrypt = sec1_crypt,
};
+66 -44
View File
@@ -12,10 +12,8 @@
#include <esp_check.h>
#include <inttypes.h>
#include <mbedtls/gcm.h>
#include <mbedtls/error.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include "psa/crypto.h"
#include <protocomm_security.h>
#include <protocomm_security2.h>
@@ -64,8 +62,8 @@ typedef struct session {
char *session_key;
uint16_t session_key_len;
uint8_t iv[AES_GCM_IV_SIZE];
/* mbedtls context data for AES-GCM */
mbedtls_gcm_context ctx_gcm;
/* PSA key for AES-GCM */
psa_key_id_t key_id;
esp_srp_handle_t *srp_hd;
} session_t;
@@ -215,7 +213,6 @@ static esp_err_t handle_session_command1(session_t *cur_session,
{
ESP_LOGD(TAG, "Request to handle setup1_command");
Sec2Payload *in = (Sec2Payload *) req->sec2;
int mbed_err = -0x0001;
if (cur_session->state != SESSION_STATE_CMD1) {
ESP_LOGE(TAG, "Invalid state of session %d (expected %d)", SESSION_STATE_CMD1, cur_session->state);
@@ -242,24 +239,11 @@ static esp_err_t handle_session_command1(session_t *cur_session,
}
hexdump("Device proof", device_proof, CLIENT_PROOF_LEN);
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&ctr_drbg);
int ret;
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to seed random number generator");
free(device_proof);
return ESP_FAIL;
}
aes_gcm_iv_t *iv = (aes_gcm_iv_t *) cur_session->iv;
ret = mbedtls_ctr_drbg_random(&ctr_drbg, iv->session_id, SESSION_ID_LEN);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to generate random number");
psa_status_t status;
status = psa_generate_random(iv->session_id, SESSION_ID_LEN);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_generate_random failed with status=%d", status);
free(device_proof);
return ESP_FAIL;
}
@@ -268,16 +252,28 @@ static esp_err_t handle_session_command1(session_t *cur_session,
hexdump("Initialization vector", (char *)cur_session->iv, AES_GCM_IV_SIZE);
/* Initialize crypto context */
mbedtls_gcm_init(&cur_session->ctx_gcm);
mbed_err = mbedtls_gcm_setkey(&cur_session->ctx_gcm, MBEDTLS_CIPHER_ID_AES, (unsigned char *)cur_session->session_key, AES_GCM_KEY_LEN);
if (mbed_err != 0) {
ESP_LOGE(TAG, "Failure at mbedtls_gcm_setkey_enc with error code : -0x%x", -mbed_err);
/* Initialize AES-GCM key */
psa_algorithm_t alg = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, AES_GCM_TAG_LEN);
psa_key_id_t key_id = 0;
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_AES);
psa_set_key_bits(&key_attributes, AES_GCM_KEY_LEN);
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
psa_set_key_algorithm(&key_attributes, alg);
/* Use first 32 bytes (256 bits) of the session key for AES-GCM */
size_t aes_key_bytes = AES_GCM_KEY_LEN / 8;
if (cur_session->session_key_len < aes_key_bytes) {
ESP_LOGE(TAG, "Session key too short: %d bytes (need at least %zu bytes)", cur_session->session_key_len, aes_key_bytes);
free(device_proof);
mbedtls_gcm_free(&cur_session->ctx_gcm);
return ESP_FAIL;
}
status = psa_import_key(&key_attributes, (uint8_t *)cur_session->session_key, aes_key_bytes, &key_id);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_import_key failed with status=%d", status);
free(device_proof);
return ESP_FAIL;
}
cur_session->key_id = key_id;
Sec2Payload *out = (Sec2Payload *) malloc(sizeof(Sec2Payload));
S2SessionResp1 *out_resp = (S2SessionResp1 *) malloc(sizeof(S2SessionResp1));
@@ -286,7 +282,7 @@ static esp_err_t handle_session_command1(session_t *cur_session,
free(device_proof);
free(out);
free(out_resp);
mbedtls_gcm_free(&cur_session->ctx_gcm);
psa_destroy_key(key_id);
return ESP_ERR_NO_MEM;
}
@@ -390,8 +386,9 @@ static esp_err_t sec2_close_session(protocomm_security_handle_t handle, uint32_t
}
if (cur_session->state == SESSION_STATE_DONE) {
/* Free GCM context data */
mbedtls_gcm_free(&cur_session->ctx_gcm);
/* Destroy the AES-GCM key */
psa_destroy_key(cur_session->key_id);
cur_session->key_id = 0;
}
free(cur_session->username);
@@ -480,16 +477,27 @@ static esp_err_t sec2_encrypt(protocomm_security_handle_t handle,
ESP_LOGE(TAG, "Failed to allocate encrypt buf len %d", *outlen);
return ESP_ERR_NO_MEM;
}
uint8_t gcm_tag[AES_GCM_TAG_LEN];
int ret = mbedtls_gcm_crypt_and_tag(&cur_session->ctx_gcm, MBEDTLS_GCM_ENCRYPT, inlen, cur_session->iv,
AES_GCM_IV_SIZE, NULL, 0, inbuf,
*outbuf, AES_GCM_TAG_LEN, gcm_tag);
if (ret != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_gcm_crypt_and_tag with error code : %d", ret);
psa_status_t status;
psa_algorithm_t alg = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, AES_GCM_TAG_LEN);
size_t out_len = 0;
status = psa_aead_encrypt(cur_session->key_id, alg,
cur_session->iv, AES_GCM_IV_SIZE,
NULL, 0, /* No additional data */
inbuf, inlen,
*outbuf, *outlen, &out_len);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_aead_encrypt failed with status=%d", status);
free(*outbuf);
return ESP_FAIL;
}
if (out_len != *outlen) {
ESP_LOGE(TAG, "psa_aead_encrypt output length mismatch: expected %zd, got %zu", *outlen, out_len);
free(*outbuf);
return ESP_FAIL;
}
memcpy(*outbuf + inlen, gcm_tag, AES_GCM_TAG_LEN);
/* Increment counter value for next operation */
sec2_gcm_iv_counter_increment(cur_session->iv);
@@ -531,10 +539,24 @@ static esp_err_t sec2_decrypt(protocomm_security_handle_t handle,
return ESP_ERR_NO_MEM;
}
int ret = mbedtls_gcm_auth_decrypt(&cur_session->ctx_gcm, inlen - AES_GCM_TAG_LEN, cur_session->iv,
AES_GCM_IV_SIZE, NULL, 0, inbuf + (inlen - AES_GCM_TAG_LEN), AES_GCM_TAG_LEN, inbuf, *outbuf);
if (ret != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_gcm_auth_decrypt : %d", ret);
psa_status_t status;
psa_algorithm_t alg = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, AES_GCM_TAG_LEN);
size_t out_len = 0;
status = psa_aead_decrypt(cur_session->key_id, alg,
cur_session->iv, AES_GCM_IV_SIZE,
NULL, 0, /* No additional data */
inbuf, inlen,
*outbuf, *outlen, &out_len);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_aead_decrypt failed with status=%d", status);
free(*outbuf);
return ESP_FAIL;
}
if (out_len != *outlen) {
ESP_LOGE(TAG, "psa_aead_decrypt output length mismatch: expected %zd, got %zu", *outlen, out_len);
free(*outbuf);
return ESP_FAIL;
}
@@ -1,3 +1,4 @@
idf_component_register(SRC_DIRS "."
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES cmock mbedtls protocomm protobuf-c test_utils unity)
PRIV_REQUIRES cmock mbedtls protocomm protobuf-c test_utils unity
WHOLE_ARCHIVE)
@@ -0,0 +1,109 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
#include "test_utils.h"
#include "memory_checks.h"
#include "esp_newlib.h"
#include "psa/crypto.h"
// #include "mbedtls/aes.h"
#if SOC_SHA_SUPPORT_PARALLEL_ENG
#include "sha/sha_parallel_engine.h"
#else
#include "sha/sha_core.h"
#endif
#include "bignum_impl.h"
/* setUp runs before every test */
void setUp(void)
{
#if CONFIG_MBEDTLS_HARDWARE_SHA
// Execute esp_sha operation to allocate internal SHA semaphore (in case of ESP32)
// and initial DMA setup memory which is considered as leaked otherwise
const uint8_t input_buffer[64] = {0};
uint8_t output_buffer[64];
#if SOC_SHA_SUPPORT_SHA1
esp_sha(SHA1, input_buffer, sizeof(input_buffer), output_buffer);
#endif // SOC_SHA_SUPPORT_SHA1
#if SOC_SHA_SUPPORT_SHA256
esp_sha(SHA2_256, input_buffer, sizeof(input_buffer), output_buffer);
#endif // SOC_SHA_SUPPORT_SHA256
#if SOC_SHA_SUPPORT_SHA512
esp_sha(SHA2_512, input_buffer, sizeof(input_buffer), output_buffer);
#endif // SOC_SHA_SUPPORT_SHA512
#endif // CONFIG_MBEDTLS_HARDWARE_SHA
#if defined(CONFIG_MBEDTLS_HARDWARE_MPI)
esp_mpi_enable_hardware_hw_op();
esp_mpi_disable_hardware_hw_op();
#endif // CONFIG_MBEDTLS_HARDWARE_MPI
// #if SOC_AES_SUPPORTED
// Execute mbedtls_aes_init operation to allocate AES interrupt
// allocation memory which is considered as leak otherwise
const uint8_t plaintext[16] = {0};
uint8_t ciphertext[32];
const uint8_t key[16] = { 0 };
psa_status_t status;
psa_key_id_t key_id = 0;
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT);
psa_set_key_algorithm(&attributes, PSA_ALG_CBC_NO_PADDING);
psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
psa_set_key_bits(&attributes, 128);
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE);
status = psa_import_key(&attributes, key, sizeof(key), &key_id);
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
size_t output_len = 0;
status = psa_cipher_encrypt(key_id, PSA_ALG_CBC_NO_PADDING, plaintext, sizeof(plaintext), ciphertext, sizeof(ciphertext), &output_len);
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
const uint8_t plaintext_long[256] = {0};
uint8_t ciphertext_long[272];
output_len = 0;
status = psa_cipher_encrypt(key_id, PSA_ALG_CBC_NO_PADDING, plaintext_long, sizeof(plaintext_long), ciphertext_long, sizeof(ciphertext_long), &output_len);
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
psa_destroy_key(key_id);
// Destroying the key again to get rid of nvs flash memory leak
// If the key doesn't exist, PSA looks for it in nvs and that
// allocates some memory which is considered as leak otherwise
psa_destroy_key(key_id);
// #endif // SOC_AES_SUPPORTED
test_utils_record_free_mem();
TEST_ESP_OK(test_utils_set_leak_level(50, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL));
TEST_ESP_OK(test_utils_set_leak_level(50, ESP_LEAK_TYPE_WARNING, ESP_COMP_LEAK_GENERAL));
}
/* tearDown runs after every test */
void tearDown(void)
{
/* some FreeRTOS stuff is cleaned up by idle task */
vTaskDelay(5);
/* clean up some of the newlib's lazy allocations */
esp_reent_cleanup();
// mbedtls_psa_crypto_free();
/* check if unit test has caused heap corruption in any heap */
TEST_ASSERT_MESSAGE( heap_caps_check_integrity(MALLOC_CAP_INVALID, true), "The test has corrupted the heap");
test_utils_finish_and_evaluate_leaks(test_utils_get_leak_level(ESP_LEAK_TYPE_WARNING, ESP_COMP_LEAK_ALL),
test_utils_get_leak_level(ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_ALL));
}
static void test_task(void *pvParameters)
{
vTaskDelay(2); /* Delay a bit to let the main task be deleted */
unity_run_menu();
}
void app_main(void)
{
xTaskCreatePinnedToCore(test_task, "testTask", CONFIG_UNITY_FREERTOS_STACK_SIZE, NULL, CONFIG_UNITY_FREERTOS_PRIORITY, NULL, CONFIG_UNITY_FREERTOS_CPU);
}
@@ -1,9 +1,11 @@
/*
* SPDX-FileCopyrightText: 2018-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "psa/crypto_struct.h"
#include "psa/crypto_types.h"
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
@@ -28,13 +30,9 @@
#define ACCESS_ECDH(S, var) S.MBEDTLS_PRIVATE(ctx).MBEDTLS_PRIVATE(mbed_ecdh).MBEDTLS_PRIVATE(var)
#endif
#include <mbedtls/aes.h>
#include <mbedtls/sha256.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/ecdh.h>
#define MBEDTLS_DECLARE_PRIVATE_IDENTIFIERS
#include <mbedtls/error.h>
#include "psa/crypto.h"
#include <protocomm.h>
#include <protocomm_security.h>
#include <protocomm_security0.h>
@@ -62,15 +60,15 @@ typedef struct {
uint8_t sym_key[PUBLIC_KEY_LEN];
uint8_t rand[SZ_RANDOM];
/* mbedtls context data for Curve25519 */
mbedtls_ecdh_context ctx_client;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
/* mbedtls context data for AES */
mbedtls_aes_context ctx_aes;
psa_cipher_operation_t ctx_aes;
psa_key_id_t client_key_id;
psa_key_id_t key_id;
unsigned char stb[16];
size_t nc_off;
/* Operation counter for CTR mode nonce */
uint32_t op_counter;
} session_t;
static const char *TAG = "protocomm_test";
@@ -80,16 +78,6 @@ static const protocomm_security_t *test_sec = NULL;
protocomm_security_handle_t sec_inst = NULL;
static uint32_t test_priv_data = 1234;
static void flip_endian(uint8_t *data, size_t len)
{
uint8_t swp_buf;
for (int i = 0; i < len/2; i++) {
swp_buf = data[i];
data[i] = data[len - i - 1];
data[len - i - 1] = swp_buf;
}
}
static void hexdump(const char *msg, uint8_t *buf, int len)
{
ESP_LOGI(TAG, "%s:", msg);
@@ -142,7 +130,6 @@ static esp_err_t verify_response0(session_t *session, SessionData *resp)
return ESP_ERR_INVALID_ARG;
}
int ret;
Sec1Payload *in = (Sec1Payload *) resp->sec1;
if (in->sr0->device_pubkey.len != PUBLIC_KEY_LEN) {
@@ -159,50 +146,39 @@ static esp_err_t verify_response0(session_t *session, SessionData *resp)
uint8_t *dev_pubkey = session->device_pubkey;
memcpy(session->device_pubkey, in->sr0->device_pubkey.data, in->sr0->device_pubkey.len);
hexdump("Device pubkey", dev_pubkey, PUBLIC_KEY_LEN);
hexdump("Client pubkey", cli_pubkey, PUBLIC_KEY_LEN);
hexdump("Device pubkey0", dev_pubkey, PUBLIC_KEY_LEN);
hexdump("Client pubkey0", cli_pubkey, PUBLIC_KEY_LEN);
ret = mbedtls_mpi_lset(ACCESS_ECDH(&session->ctx_client, Qp).MBEDTLS_PRIVATE(Z), 1);
if (ret != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_mpi_lset with error code : %d", ret);
size_t olen = 0;
psa_status_t status = psa_raw_key_agreement(
PSA_ALG_ECDH, session->client_key_id, dev_pubkey,
PUBLIC_KEY_LEN, session->sym_key, sizeof(session->sym_key), &olen);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "Failed at psa_raw_key_agreement with error code : %d", status);
return ESP_FAIL;
}
flip_endian(session->device_pubkey, PUBLIC_KEY_LEN);
ret = mbedtls_mpi_read_binary(ACCESS_ECDH(&session->ctx_client, Qp).MBEDTLS_PRIVATE(X), dev_pubkey, PUBLIC_KEY_LEN);
flip_endian(session->device_pubkey, PUBLIC_KEY_LEN);
if (ret != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_mpi_read_binary with error code : %d", ret);
return ESP_FAIL;
}
ret = mbedtls_ecdh_compute_shared(ACCESS_ECDH(&session->ctx_client, grp),
ACCESS_ECDH(&session->ctx_client, z),
ACCESS_ECDH(&session->ctx_client, Qp),
ACCESS_ECDH(&session->ctx_client, d),
mbedtls_ctr_drbg_random,
&session->ctr_drbg);
if (ret != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_ecdh_compute_shared with error code : %d", ret);
return ESP_FAIL;
}
ret = mbedtls_mpi_write_binary(ACCESS_ECDH(&session->ctx_client, z), session->sym_key, PUBLIC_KEY_LEN);
if (ret != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_mpi_write_binary with error code : %d", ret);
return ESP_FAIL;
}
flip_endian(session->sym_key, PUBLIC_KEY_LEN);
const protocomm_security1_params_t *pop = session->pop;
if (pop != NULL && pop->data != NULL && pop->len != 0) {
ESP_LOGD(TAG, "Adding proof of possession");
uint8_t sha_out[PUBLIC_KEY_LEN];
ret = mbedtls_sha256((const unsigned char *) pop->data, pop->len, sha_out, 0);
if (ret != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_sha256_ret with error code : %d", ret);
return ESP_FAIL;
psa_hash_operation_t hash_operation = PSA_HASH_OPERATION_INIT;
status = psa_hash_setup(&hash_operation, PSA_ALG_SHA_256);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_hash_setup failed with status=%d", status);
}
status = psa_hash_update(&hash_operation, pop->data, pop->len);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_hash_update failed with status=%d", status);
psa_hash_abort(&hash_operation);
}
status = psa_hash_finish(&hash_operation, sha_out, sizeof(sha_out), &olen);
if (status != PSA_SUCCESS || olen != sizeof(sha_out)) {
ESP_LOGE(TAG, "psa_hash_finish failed with status=%d", status);
psa_hash_abort(&hash_operation);
}
for (int i = 0; i < PUBLIC_KEY_LEN; i++) {
@@ -219,7 +195,6 @@ static esp_err_t verify_response0(session_t *session, SessionData *resp)
static esp_err_t prepare_command1(session_t *session, SessionData *req)
{
int ret;
uint8_t *outbuf = (uint8_t *) malloc(PUBLIC_KEY_LEN);
if (!outbuf) {
ESP_LOGE(TAG, "Error allocating ciphertext buffer");
@@ -227,26 +202,44 @@ static esp_err_t prepare_command1(session_t *session, SessionData *req)
}
/* Initialise crypto context */
mbedtls_aes_init(&session->ctx_aes);
memset(session->stb, 0, sizeof(session->stb));
session->nc_off = 0;
ret = mbedtls_aes_setkey_enc(&session->ctx_aes, session->sym_key,
sizeof(session->sym_key)*8);
if (ret != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_aes_setkey_enc with error code : %d", ret);
psa_status_t status;
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_key_id_t key_id;
psa_algorithm_t alg = PSA_ALG_CTR;
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_AES);
psa_set_key_bits(&key_attributes, 256);
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE);
psa_set_key_algorithm(&key_attributes, alg);
status = psa_import_key(&key_attributes, session->sym_key, sizeof(session->sym_key), &key_id);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "Failed at psa_import_key with error code : %d", status);
free(outbuf);
return ESP_FAIL;
}
psa_reset_key_attributes(&key_attributes);
session->ctx_aes = psa_cipher_operation_init();
status = psa_cipher_encrypt_setup(&session->ctx_aes, key_id, alg);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "Failed at psa_cipher_encrypt_setup with error code : %d", status);
free(outbuf);
return ESP_FAIL;
}
status = psa_cipher_set_iv(&session->ctx_aes, session->rand, sizeof(session->rand));
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "Failed at psa_cipher_set_iv with error code : %d", status);
free(outbuf);
return ESP_FAIL;
}
size_t outlen = 0;
status = psa_cipher_update(&session->ctx_aes, session->device_pubkey, sizeof(session->device_pubkey), outbuf, PUBLIC_KEY_LEN, &outlen);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "Failed at psa_cipher_update with error code : %d", status);
free(outbuf);
return ESP_FAIL;
}
ret = mbedtls_aes_crypt_ctr(&session->ctx_aes, PUBLIC_KEY_LEN,
&session->nc_off, session->rand,
session->stb, session->device_pubkey, outbuf);
if (ret != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_aes_crypt_ctr with error code : %d", ret);
free(outbuf);
return ESP_FAIL;
}
session->key_id = key_id;
Sec1Payload *out = (Sec1Payload *) malloc(sizeof(Sec1Payload));
if (!out) {
@@ -292,8 +285,8 @@ static esp_err_t verify_response1(session_t *session, SessionData *resp)
uint8_t *cli_pubkey = session->client_pubkey;
uint8_t *dev_pubkey = session->device_pubkey;
hexdump("Device pubkey", dev_pubkey, PUBLIC_KEY_LEN);
hexdump("Client pubkey", cli_pubkey, PUBLIC_KEY_LEN);
hexdump("Device pubkey1", dev_pubkey, PUBLIC_KEY_LEN);
hexdump("Client pubkey1", cli_pubkey, PUBLIC_KEY_LEN);
if ((resp->proto_case != SESSION_DATA__PROTO_SEC1) ||
(resp->sec1->msg != SEC1_MSG_TYPE__Session_Response1)) {
@@ -304,13 +297,17 @@ static esp_err_t verify_response1(session_t *session, SessionData *resp)
uint8_t check_buf[PUBLIC_KEY_LEN];
Sec1Payload *in = (Sec1Payload *) resp->sec1;
int ret = mbedtls_aes_crypt_ctr(&session->ctx_aes, PUBLIC_KEY_LEN,
&session->nc_off, session->rand, session->stb,
in->sr1->device_verify_data.data, check_buf);
if (ret != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_aes_crypt_ctr with error code : %d", ret);
hexdump("Device verify data", in->sr1->device_verify_data.data, in->sr1->device_verify_data.len);
hexdump("Rand: ", session->rand, sizeof(session->rand));
size_t out_len = 0;
psa_status_t status = psa_cipher_update(&session->ctx_aes, in->sr1->device_verify_data.data, in->sr1->device_verify_data.len, check_buf, sizeof(check_buf), &out_len);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "Failed at psa_cipher_update with error code : %d", status);
return ESP_FAIL;
}
hexdump("Dec Device verifier", check_buf, sizeof(check_buf));
if (memcmp(check_buf, session->client_pubkey, sizeof(session->client_pubkey)) != 0) {
@@ -318,6 +315,9 @@ static esp_err_t verify_response1(session_t *session, SessionData *resp)
return ESP_FAIL;
}
/* Initialize operation counter after successful handshake */
session->op_counter = 0;
return ESP_OK;
}
@@ -358,6 +358,14 @@ static esp_err_t test_delete_session(session_t *session)
if (test_sec->cleanup && (test_sec->cleanup(sec_inst) != ESP_OK)) {
return ESP_FAIL;
}
psa_destroy_key(session->client_key_id);
session->client_key_id = 0;
psa_destroy_key(session->key_id);
session->key_id = 0;
psa_cipher_abort(&session->ctx_aes);
return ESP_OK;
}
@@ -377,52 +385,43 @@ static esp_err_t test_sec_endpoint(session_t *session)
ssize_t outlen = 0;
uint8_t *outbuf = NULL;
mbedtls_ecdh_init(&session->ctx_client);
mbedtls_ecdh_setup(&session->ctx_client, MBEDTLS_ECP_DP_CURVE25519);
mbedtls_ctr_drbg_init(&session->ctr_drbg);
psa_status_t status;
mbedtls_entropy_init(&session->entropy);
ret = mbedtls_ctr_drbg_seed(&session->ctr_drbg, mbedtls_entropy_func,
&session->entropy, NULL, 0);
if (ret != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_ctr_drbg_seed with error code : %d", ret);
goto abort_test_sec_endpoint;
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
if (session->client_key_id != 0) {
psa_destroy_key(session->client_key_id);
session->client_key_id = 0;
}
ret = mbedtls_ecp_group_load(ACCESS_ECDH(&session->ctx_client, grp), MBEDTLS_ECP_DP_CURVE25519);
if (ret != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_ecp_group_load with error code : %d", ret);
goto abort_test_sec_endpoint;
}
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_MONTGOMERY));
psa_set_key_bits(&key_attributes, 255);
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE);
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_DERIVE);
psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDH);
ret = mbedtls_ecdh_gen_public(ACCESS_ECDH(&session->ctx_client, grp),
ACCESS_ECDH(&session->ctx_client, d),
ACCESS_ECDH(&session->ctx_client, Q),
mbedtls_ctr_drbg_random,
&session->ctr_drbg);
if (ret != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_ecdh_gen_public with error code : %d", ret);
goto abort_test_sec_endpoint;
status = psa_generate_key(&key_attributes, &session->client_key_id);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_generate_key failed with status=%d", status);
psa_reset_key_attributes(&key_attributes);
return ESP_FAIL;
}
psa_reset_key_attributes(&key_attributes);
size_t olen = 0;
if (session->weak) {
/* Read zero client public key */
ret = mbedtls_mpi_read_binary(ACCESS_ECDH(&session->ctx_client, Q).MBEDTLS_PRIVATE(X),
session->client_pubkey,
PUBLIC_KEY_LEN);
if (ret != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_mpi_read_binary with error code : %d", ret);
goto abort_test_sec_endpoint;
// If weak key is request, just set the session->client_pubkey to be 0
memset(session->client_pubkey, 0, PUBLIC_KEY_LEN);
} else {
status = psa_export_public_key(session->client_key_id, session->client_pubkey, PUBLIC_KEY_LEN, &olen);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_export_public_key failed with status=%d", status);
psa_reset_key_attributes(&key_attributes);
return ESP_FAIL;
}
}
ret = mbedtls_mpi_write_binary(ACCESS_ECDH(&session->ctx_client, Q).MBEDTLS_PRIVATE(X),
session->client_pubkey,
PUBLIC_KEY_LEN);
if (ret != 0) {
ESP_LOGE(TAG, "Failed at mbedtls_mpi_write_binary with error code : %d", ret);
goto abort_test_sec_endpoint;
}
flip_endian(session->client_pubkey, PUBLIC_KEY_LEN);
hexdump("Client public key", session->client_pubkey, PUBLIC_KEY_LEN);
/*********** Transaction0 = SessionCmd0 + SessionResp0 ****************/
session_data__init(&req);
@@ -511,17 +510,14 @@ static esp_err_t test_sec_endpoint(session_t *session)
}
session_data__free_unpacked(resp, NULL);
mbedtls_ecdh_free(&session->ctx_client);
mbedtls_ctr_drbg_free(&session->ctr_drbg);
mbedtls_entropy_free(&session->entropy);
psa_destroy_key(session->client_key_id);
session->client_key_id = 0;
return ESP_OK;
abort_test_sec_endpoint:
mbedtls_ecdh_free(&session->ctx_client);
mbedtls_ctr_drbg_free(&session->ctr_drbg);
mbedtls_entropy_free(&session->entropy);
psa_destroy_key(session->client_key_id);
session->client_key_id = 0;
return ESP_FAIL;
}
@@ -564,11 +560,17 @@ static esp_err_t test_req_endpoint(session_t *session)
// Check if the AES key is correctly set before calling the software encryption
// API. Without this check, the code will crash, resulting in a test case failure.
// For hardware AES, portability layer takes care of this.
if (session->ctx_aes.MBEDTLS_PRIVATE(nr) > 0) {
// if (session->ctx_aes.MBEDTLS_PRIVATE(nr) > 0) {
if (session->ctx_aes.MBEDTLS_PRIVATE(id) > 0) {
#endif
mbedtls_aes_crypt_ctr(&session->ctx_aes, sizeof(rand_test_data), &session->nc_off,
session->rand, session->stb, rand_test_data, enc_test_data);
size_t out_len = 0;
psa_status_t status = psa_cipher_update(&session->ctx_aes, rand_test_data, sizeof(rand_test_data), enc_test_data, sizeof(enc_test_data), &out_len);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "Error updating cipher, status: %d", status);
return ESP_FAIL;
}
#if !CONFIG_MBEDTLS_HARDWARE_AES
}
#endif
@@ -597,8 +599,14 @@ static esp_err_t test_req_endpoint(session_t *session)
memcpy(verify_data, enc_verify_data, verify_data_len);
}
else if (session->sec_ver == 1) {
mbedtls_aes_crypt_ctr(&session->ctx_aes, verify_data_len, &session->nc_off,
session->rand, session->stb, enc_verify_data, verify_data);
size_t out_len = 0;
psa_status_t status = psa_cipher_update(&session->ctx_aes, enc_verify_data, verify_data_len, verify_data, verify_data_len, &out_len);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "psa_cipher_update failed");
free(verify_data);
free(enc_verify_data);
return ESP_FAIL;
}
}
free(enc_verify_data);
@@ -715,7 +723,7 @@ static esp_err_t test_security1_no_encryption (void)
return ESP_ERR_INVALID_STATE;
}
// Intialise protocomm session with zero public keys
// Initialise protocomm session with zero public keys
if (test_new_session(session) != ESP_OK) {
ESP_LOGE(TAG, "Error creating new session");
stop_test_service();
@@ -794,7 +802,7 @@ static esp_err_t test_security1_session_overflow (void)
return ESP_FAIL;
}
// Intialise protocomm session with zero public keys
// Initialise protocomm session with zero public keys
if (test_new_session(session1) != ESP_OK) {
ESP_LOGE(TAG, "Error creating new session");
stop_test_service();
@@ -860,7 +868,7 @@ static esp_err_t test_security1_wrong_pop (void)
return ESP_FAIL;
}
// Intialise protocomm session with zero public keys
// Initialise protocomm session with zero public keys
if (test_new_session(session) != ESP_OK) {
ESP_LOGE(TAG, "Error creating new session");
stop_test_service();
@@ -975,7 +983,7 @@ static esp_err_t test_security1_weak_session (void)
return ESP_FAIL;
}
// Intialise protocomm session with zero public keys
// Initialise protocomm session with zero public keys
if (test_new_session(session) != ESP_OK) {
ESP_LOGE(TAG, "Error creating new session");
stop_test_service();
@@ -1028,7 +1036,7 @@ static esp_err_t test_protocomm (session_t *session)
return ESP_FAIL;
}
// Intialise protocomm session with zero public keys
// Initialise protocomm session with zero public keys
if (test_new_session(session) != ESP_OK) {
ESP_LOGE(TAG, "Error creating new session");
stop_test_service();
@@ -1136,7 +1144,6 @@ TEST_CASE("leak test", "[PROTOCOMM]")
/* Run all tests passively. Any leaks due
* to protocomm should show up now */
unsigned pre_start_mem = esp_get_free_heap_size();
test_security0();
test_security1();
test_security1_no_encryption();
@@ -1190,8 +1197,3 @@ TEST_CASE("security 1 weak session test", "[PROTOCOMM]")
{
TEST_ASSERT(test_security1_weak_session() == ESP_OK);
}
void app_main(void)
{
unity_run_menu();
}
@@ -0,0 +1,325 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include <unity.h>
#include "esp_srp.h"
#include "esp_log.h"
#include "test_utils.h"
#include "esp_rom_crc.h"
static const char *TAG = "srp_test";
// Example username and password
static const char *username = "wifiprov";
static const char *password = "abcd1234";
static const char sec2_salt[] = {
0x03, 0x6e, 0xe0, 0xc7, 0xbc, 0xb9, 0xed, 0xa8, 0x4c, 0x9e, 0xac, 0x97, 0xd9, 0x3d, 0xec, 0xf4
};
static const char sec2_verifier[] = {
0x7c, 0x7c, 0x85, 0x47, 0x65, 0x08, 0x94, 0x6d, 0xd6, 0x36, 0xaf, 0x37, 0xd7, 0xe8, 0x91, 0x43,
0x78, 0xcf, 0xfd, 0x61, 0x6c, 0x59, 0xd2, 0xf8, 0x39, 0x08, 0x12, 0x72, 0x38, 0xde, 0x9e, 0x24,
0xa4, 0x70, 0x26, 0x1c, 0xdf, 0xa9, 0x03, 0xc2, 0xb2, 0x70, 0xe7, 0xb1, 0x32, 0x24, 0xda, 0x11,
0x1d, 0x97, 0x18, 0xdc, 0x60, 0x72, 0x08, 0xcc, 0x9a, 0xc9, 0x0c, 0x48, 0x27, 0xe2, 0xae, 0x89,
0xaa, 0x16, 0x25, 0xb8, 0x04, 0xd2, 0x1a, 0x9b, 0x3a, 0x8f, 0x37, 0xf6, 0xe4, 0x3a, 0x71, 0x2e,
0xe1, 0x27, 0x86, 0x6e, 0xad, 0xce, 0x28, 0xff, 0x54, 0x46, 0x60, 0x1f, 0xb9, 0x96, 0x87, 0xdc,
0x57, 0x40, 0xa7, 0xd4, 0x6c, 0xc9, 0x77, 0x54, 0xdc, 0x16, 0x82, 0xf0, 0xed, 0x35, 0x6a, 0xc4,
0x70, 0xad, 0x3d, 0x90, 0xb5, 0x81, 0x94, 0x70, 0xd7, 0xbc, 0x65, 0xb2, 0xd5, 0x18, 0xe0, 0x2e,
0xc3, 0xa5, 0xf9, 0x68, 0xdd, 0x64, 0x7b, 0xb8, 0xb7, 0x3c, 0x9c, 0xfc, 0x00, 0xd8, 0x71, 0x7e,
0xb7, 0x9a, 0x7c, 0xb1, 0xb7, 0xc2, 0xc3, 0x18, 0x34, 0x29, 0x32, 0x43, 0x3e, 0x00, 0x99, 0xe9,
0x82, 0x94, 0xe3, 0xd8, 0x2a, 0xb0, 0x96, 0x29, 0xb7, 0xdf, 0x0e, 0x5f, 0x08, 0x33, 0x40, 0x76,
0x52, 0x91, 0x32, 0x00, 0x9f, 0x97, 0x2c, 0x89, 0x6c, 0x39, 0x1e, 0xc8, 0x28, 0x05, 0x44, 0x17,
0x3f, 0x68, 0x02, 0x8a, 0x9f, 0x44, 0x61, 0xd1, 0xf5, 0xa1, 0x7e, 0x5a, 0x70, 0xd2, 0xc7, 0x23,
0x81, 0xcb, 0x38, 0x68, 0xe4, 0x2c, 0x20, 0xbc, 0x40, 0x57, 0x76, 0x17, 0xbd, 0x08, 0xb8, 0x96,
0xbc, 0x26, 0xeb, 0x32, 0x46, 0x69, 0x35, 0x05, 0x8c, 0x15, 0x70, 0xd9, 0x1b, 0xe9, 0xbe, 0xcc,
0xa9, 0x38, 0xa6, 0x67, 0xf0, 0xad, 0x50, 0x13, 0x19, 0x72, 0x64, 0xbf, 0x52, 0xc2, 0x34, 0xe2,
0x1b, 0x11, 0x79, 0x74, 0x72, 0xbd, 0x34, 0x5b, 0xb1, 0xe2, 0xfd, 0x66, 0x73, 0xfe, 0x71, 0x64,
0x74, 0xd0, 0x4e, 0xbc, 0x51, 0x24, 0x19, 0x40, 0x87, 0x0e, 0x92, 0x40, 0xe6, 0x21, 0xe7, 0x2d,
0x4e, 0x37, 0x76, 0x2f, 0x2e, 0xe2, 0x68, 0xc7, 0x89, 0xe8, 0x32, 0x13, 0x42, 0x06, 0x84, 0x84,
0x53, 0x4a, 0xb3, 0x0c, 0x1b, 0x4c, 0x8d, 0x1c, 0x51, 0x97, 0x19, 0xab, 0xae, 0x77, 0xff, 0xdb,
0xec, 0xf0, 0x10, 0x95, 0x34, 0x33, 0x6b, 0xcb, 0x3e, 0x84, 0x0f, 0xb9, 0xd8, 0x5f, 0xb8, 0xa0,
0xb8, 0x55, 0x53, 0x3e, 0x70, 0xf7, 0x18, 0xf5, 0xce, 0x7b, 0x4e, 0xbf, 0x27, 0xce, 0xce, 0xa8,
0xb3, 0xbe, 0x40, 0xc5, 0xc5, 0x32, 0x29, 0x3e, 0x71, 0x64, 0x9e, 0xde, 0x8c, 0xf6, 0x75, 0xa1,
0xe6, 0xf6, 0x53, 0xc8, 0x31, 0xa8, 0x78, 0xde, 0x50, 0x40, 0xf7, 0x62, 0xde, 0x36, 0xb2, 0xba
};
static void test_srp_init_and_free(void) {
esp_srp_handle_t *handle = esp_srp_init(ESP_NG_3072);
TEST_ASSERT_NOT_NULL(handle);
esp_srp_free(handle);
}
static void test_srp_gen_salt_verifier(void) {
char *bytes_salt = NULL;
char *verifier = NULL;
int verifier_len = 0;
esp_err_t err = esp_srp_gen_salt_verifier(username, strlen(username),
password, strlen(password),
&bytes_salt, 16, &verifier, &verifier_len);
TEST_ASSERT_EQUAL(ESP_OK, err);
TEST_ASSERT_NOT_NULL(bytes_salt);
TEST_ASSERT_NOT_NULL(verifier);
// Verify salt length is as requested
TEST_ASSERT_EQUAL(16, 16);
// Verify verifier length is correct for 3072-bit SRP
TEST_ASSERT_GREATER_THAN(0, verifier_len);
// Log the generated salt and verifier for debugging
ESP_LOG_BUFFER_HEXDUMP("Generated Salt", bytes_salt, 16, ESP_LOG_INFO);
ESP_LOG_BUFFER_HEXDUMP("Generated Verifier", verifier, verifier_len, ESP_LOG_INFO);
free(bytes_salt);
free(verifier);
}
static void test_srp_set_salt_verifier(void) {
esp_srp_handle_t *handle = esp_srp_init(ESP_NG_3072);
TEST_ASSERT_NOT_NULL(handle);
esp_err_t err = esp_srp_set_salt_verifier(handle, sec2_salt, sizeof(sec2_salt),
sec2_verifier, sizeof(sec2_verifier));
TEST_ASSERT_EQUAL(ESP_OK, err);
char *bytes_B = NULL;
int len_B = 0;
err = esp_srp_srv_pubkey_from_salt_verifier(handle, &bytes_B, &len_B);
TEST_ASSERT_EQUAL(ESP_OK, err);
// Verify B length is correct for 3072-bit SRP (384 bytes)
TEST_ASSERT_EQUAL(384, len_B);
esp_srp_free(handle);
}
static void test_srp_srv_pubkey(void) {
esp_srp_handle_t *handle = esp_srp_init(ESP_NG_3072);
TEST_ASSERT_NOT_NULL(handle);
char *bytes_B = NULL;
int len_B = 0;
char *bytes_salt = NULL;
esp_err_t err = esp_srp_srv_pubkey(handle, username, strlen(username),
password, strlen(password), 16,
&bytes_B, &len_B, &bytes_salt);
TEST_ASSERT_EQUAL(ESP_OK, err);
TEST_ASSERT_NOT_NULL(bytes_B);
TEST_ASSERT_NOT_NULL(bytes_salt);
// Verify salt and B length
TEST_ASSERT_EQUAL(16, 16);
TEST_ASSERT_EQUAL(384, len_B);
// Log for debugging
ESP_LOG_BUFFER_HEXDUMP("Generated Salt", bytes_salt, 16, ESP_LOG_INFO);
ESP_LOG_BUFFER_HEXDUMP("Generated Server Public Key B", bytes_B, len_B, ESP_LOG_INFO);
esp_srp_free(handle);
}
static void test_srp_get_session_key(void) {
esp_srp_handle_t *handle = esp_srp_init(ESP_NG_3072);
TEST_ASSERT_NOT_NULL(handle);
char *bytes_B = NULL;
int len_B = 0;
char *bytes_salt = NULL;
esp_err_t err = esp_srp_srv_pubkey(handle, username, strlen(username),
password, strlen(password), 16,
&bytes_B, &len_B, &bytes_salt);
TEST_ASSERT_EQUAL(ESP_OK, err);
// In a real scenario, bytes_A would be from client
// For testing purposes, we use bytes_B as a convenient value (server talks to itself)
char *bytes_key = NULL;
uint16_t len_key = 0;
err = esp_srp_get_session_key(handle, bytes_B, len_B, &bytes_key, &len_key);
TEST_ASSERT_EQUAL(ESP_OK, err);
TEST_ASSERT_NOT_NULL(bytes_key);
// Verify session key length (SHA512 hash)
TEST_ASSERT_EQUAL(64, len_key);
// Log session key for debugging
ESP_LOG_BUFFER_HEXDUMP("Session Key", bytes_key, len_key, ESP_LOG_INFO);
esp_srp_free(handle);
}
static void test_srp_exchange_proofs(void) {
esp_srp_handle_t *handle = esp_srp_init(ESP_NG_3072);
TEST_ASSERT_NOT_NULL(handle);
char *bytes_B = NULL;
int len_B = 0;
char *bytes_salt = NULL;
esp_err_t err = esp_srp_srv_pubkey(handle, username, strlen(username),
password, strlen(password), 16,
&bytes_B, &len_B, &bytes_salt);
TEST_ASSERT_EQUAL(ESP_OK, err);
char *bytes_key = NULL;
uint16_t len_key = 0;
err = esp_srp_get_session_key(handle, bytes_B, len_B, &bytes_key, &len_key);
TEST_ASSERT_EQUAL(ESP_OK, err);
// In a real environment, bytes_user_proof would be calculated by the client
// For our test, we'll generate zeros - this simulates an authentication failure scenario
char bytes_user_proof[64] = {0}; // Example proof
char bytes_host_proof[64] = {0};
// This should fail since user proof is zeros and doesn't match expected value
err = esp_srp_exchange_proofs(handle, (char *)username, strlen(username),
bytes_user_proof, bytes_host_proof);
TEST_ASSERT_EQUAL(ESP_FAIL, err);
esp_srp_free(handle);
}
// Add test for error handling with invalid parameters
static void test_srp_error_handling(void) {
esp_srp_handle_t *handle = esp_srp_init(ESP_NG_3072);
TEST_ASSERT_NOT_NULL(handle);
// Test with NULL salt
esp_err_t err = esp_srp_set_salt_verifier(handle, NULL, sizeof(sec2_salt),
sec2_verifier, sizeof(sec2_verifier));
TEST_ASSERT_NOT_EQUAL(ESP_OK, err);
// Test with zero salt length
err = esp_srp_set_salt_verifier(handle, sec2_salt, 0,
sec2_verifier, sizeof(sec2_verifier));
TEST_ASSERT_NOT_EQUAL(ESP_OK, err);
// Test with NULL verifier
err = esp_srp_set_salt_verifier(handle, sec2_salt, sizeof(sec2_salt),
NULL, sizeof(sec2_verifier));
TEST_ASSERT_NOT_EQUAL(ESP_OK, err);
// Test with zero verifier length
err = esp_srp_set_salt_verifier(handle, sec2_salt, sizeof(sec2_salt),
sec2_verifier, 0);
TEST_ASSERT_NOT_EQUAL(ESP_OK, err);
esp_srp_free(handle);
}
// Test verifier calculation consistency
static void test_srp_verifier_consistency(void) {
char *bytes_salt1 = NULL;
char *verifier1 = NULL;
int verifier_len1 = 0;
// Generate first salt/verifier pair
esp_err_t err = esp_srp_gen_salt_verifier(username, strlen(username),
password, strlen(password),
&bytes_salt1, 16, &verifier1, &verifier_len1);
TEST_ASSERT_EQUAL(ESP_OK, err);
// Generate second salt/verifier pair
char *bytes_salt2 = NULL;
char *verifier2 = NULL;
int verifier_len2 = 0;
err = esp_srp_gen_salt_verifier(username, strlen(username),
password, strlen(password),
&bytes_salt2, 16, &verifier2, &verifier_len2);
TEST_ASSERT_EQUAL(ESP_OK, err);
// Salts should be different (randomly generated)
TEST_ASSERT_NOT_EQUAL(0, memcmp(bytes_salt1, bytes_salt2, 16));
// Verifiers should also be different since they depend on the salt
TEST_ASSERT_NOT_EQUAL(0, memcmp(verifier1, verifier2, verifier_len1));
free(bytes_salt1);
free(verifier1);
free(bytes_salt2);
free(verifier2);
}
static void test_srp_pubkey_randomness(void) {
esp_srp_handle_t *handle = esp_srp_init(ESP_NG_3072);
TEST_ASSERT_NOT_NULL(handle);
esp_err_t err = esp_srp_set_salt_verifier(handle, sec2_salt, sizeof(sec2_salt),
sec2_verifier, sizeof(sec2_verifier));
TEST_ASSERT_EQUAL(ESP_OK, err);
// Generate first public key
char *bytes_B1 = NULL;
int len_B1 = 0;
err = esp_srp_srv_pubkey_from_salt_verifier(handle, &bytes_B1, &len_B1);
TEST_ASSERT_EQUAL(ESP_OK, err);
TEST_ASSERT_NOT_NULL(bytes_B1);
TEST_ASSERT_EQUAL(384, len_B1);
// Generate second public key with same salt/verifier
char *bytes_B2 = NULL;
int len_B2 = 0;
err = esp_srp_srv_pubkey_from_salt_verifier(handle, &bytes_B2, &len_B2);
TEST_ASSERT_EQUAL(ESP_OK, err);
TEST_ASSERT_NOT_NULL(bytes_B2);
TEST_ASSERT_EQUAL(384, len_B2);
// Keys should be different due to random b generation
TEST_ASSERT_NOT_EQUAL(0, memcmp(bytes_B1, bytes_B2, len_B1));
// Calculate CRCs for logging
uint32_t crc1 = esp_rom_crc32_le(0, (uint8_t*)bytes_B1, len_B1);
uint32_t crc2 = esp_rom_crc32_le(0, (uint8_t*)bytes_B2, len_B2);
ESP_LOGI(TAG, "Public key CRCs: %u, %u (should be different)", crc1, crc2);
free(bytes_B1);
bytes_B1 = NULL;
esp_srp_free(handle);
}
TEST_CASE("SRP init and free test", "[SRP]")
{
test_srp_init_and_free();
}
TEST_CASE("SRP generate salt and verifier test", "[SRP]")
{
test_srp_gen_salt_verifier();
}
TEST_CASE("SRP set salt and verifier test", "[SRP]")
{
test_srp_set_salt_verifier();
}
TEST_CASE("SRP server public key test", "[SRP]")
{
test_srp_srv_pubkey();
}
TEST_CASE("SRP get session key test", "[SRP]")
{
test_srp_get_session_key();
}
TEST_CASE("SRP exchange proofs test", "[SRP]")
{
test_srp_exchange_proofs();
}
TEST_CASE("SRP error handling test", "[SRP]")
{
test_srp_error_handling();
}
TEST_CASE("SRP verifier consistency test", "[SRP]")
{
test_srp_verifier_consistency();
}
TEST_CASE("SRP public key randomness test", "[SRP]")
{
test_srp_pubkey_randomness();
}