Files
esp-idf/components/bt/esp_ble_mesh/common/crypto_mbedtls.c
T

544 lines
14 KiB
C

/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <errno.h>
#include "esp_random.h"
#include "mbedtls/aes.h"
#include "mbedtls/cmac.h"
#include "mbedtls/md.h"
#include "mbedtls/ecp.h"
#include "mbedtls/ecdh.h"
#include "mbedtls/ccm.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mesh/common.h"
#include "mesh/crypto.h"
#include "mesh/trace.h"
#include "mesh/config.h"
/* ECC key storage
*
* MbedTLS functions expect BIG-ENDIAN format.
*/
static struct {
bool is_ready;
uint8_t private_key[PRIV_KEY_SIZE]; /* Big-endian */
uint8_t public_key[PUB_KEY_SIZE]; /* Big-endian */
} dh_pair;
int bt_mesh_crypto_init(void)
{
memset(&dh_pair, 0, sizeof(dh_pair));
dh_pair.is_ready = false;
return 0;
}
int bt_mesh_encrypt(const struct bt_mesh_key *key, const uint8_t plaintext[16],
uint8_t enc_data[16])
{
mbedtls_aes_context ctx;
int ret;
mbedtls_aes_init(&ctx);
ret = mbedtls_aes_setkey_enc(&ctx, key->key, 128);
if (ret != 0) {
mbedtls_aes_free(&ctx);
return -EIO;
}
ret = mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, plaintext, enc_data);
mbedtls_aes_free(&ctx);
return ret == 0 ? 0 : -EIO;
}
int bt_mesh_ccm_encrypt(const struct bt_mesh_key *key, uint8_t nonce[13],
const uint8_t *plaintext, size_t len,
const uint8_t *aad, size_t aad_len,
uint8_t *enc_data, size_t mic_size)
{
mbedtls_ccm_context ctx;
int ret;
mbedtls_ccm_init(&ctx);
ret = mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, key->key, 128);
if (ret != 0) {
mbedtls_ccm_free(&ctx);
return -EIO;
}
ret = mbedtls_ccm_encrypt_and_tag(&ctx, len, nonce, 13, aad, aad_len,
plaintext, enc_data, enc_data + len, mic_size);
mbedtls_ccm_free(&ctx);
return ret == 0 ? 0 : -EIO;
}
int bt_mesh_ccm_decrypt(const struct bt_mesh_key *key, uint8_t nonce[13],
const uint8_t *enc_data, size_t len,
const uint8_t *aad, size_t aad_len,
uint8_t *plaintext, size_t mic_size)
{
mbedtls_ccm_context ctx;
int ret;
mbedtls_ccm_init(&ctx);
ret = mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, key->key, 128);
if (ret != 0) {
mbedtls_ccm_free(&ctx);
return -EIO;
}
ret = mbedtls_ccm_auth_decrypt(&ctx, len, nonce, 13, aad, aad_len,
enc_data, plaintext, enc_data + len, mic_size);
mbedtls_ccm_free(&ctx);
return ret == 0 ? 0 : -EIO;
}
int bt_mesh_ccm_encrypt_raw_key(const uint8_t key[16], uint8_t nonce[13],
const uint8_t *plaintext, size_t len,
const uint8_t *aad, size_t aad_len,
uint8_t *enc_data, size_t mic_size)
{
struct bt_mesh_key mesh_key;
memcpy(mesh_key.key, key, 16);
return bt_mesh_ccm_encrypt(&mesh_key, nonce, plaintext, len, aad, aad_len,
enc_data, mic_size);
}
int bt_mesh_ccm_decrypt_raw_key(const uint8_t key[16], uint8_t nonce[13],
const uint8_t *enc_data, size_t len,
const uint8_t *aad, size_t aad_len,
uint8_t *plaintext, size_t mic_size)
{
struct bt_mesh_key mesh_key;
memcpy(mesh_key.key, key, 16);
return bt_mesh_ccm_decrypt(&mesh_key, nonce, enc_data, len, aad, aad_len,
plaintext, mic_size);
}
int bt_mesh_aes_cmac_mesh_key(const struct bt_mesh_key *key,
struct bt_mesh_sg *sg, size_t sg_len,
uint8_t mac[16])
{
return bt_mesh_aes_cmac_raw_key(key->key, sg, sg_len, mac);
}
int bt_mesh_aes_cmac_raw_key(const uint8_t key[16], struct bt_mesh_sg *sg,
size_t sg_len, uint8_t mac[16])
{
mbedtls_cipher_context_t ctx;
const mbedtls_cipher_info_t *info;
int ret;
info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB);
if (info == NULL) {
return -EIO;
}
mbedtls_cipher_init(&ctx);
ret = mbedtls_cipher_setup(&ctx, info);
if (ret != 0) {
mbedtls_cipher_free(&ctx);
return -EIO;
}
ret = mbedtls_cipher_cmac_starts(&ctx, key, 128);
if (ret != 0) {
mbedtls_cipher_free(&ctx);
return -EIO;
}
for (; sg_len; sg_len--, sg++) {
ret = mbedtls_cipher_cmac_update(&ctx, sg->data, sg->len);
if (ret != 0) {
mbedtls_cipher_free(&ctx);
return -EIO;
}
}
ret = mbedtls_cipher_cmac_finish(&ctx, mac);
mbedtls_cipher_free(&ctx);
return ret == 0 ? 0 : -EIO;
}
int bt_mesh_sha256_hmac_raw_key(const uint8_t key[32], struct bt_mesh_sg *sg,
size_t sg_len, uint8_t mac[32])
{
mbedtls_md_context_t ctx;
const mbedtls_md_info_t *info;
int ret;
info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
if (info == NULL) {
return -EIO;
}
mbedtls_md_init(&ctx);
ret = mbedtls_md_setup(&ctx, info, 1); /* 1 = HMAC mode */
if (ret != 0) {
mbedtls_md_free(&ctx);
return -EIO;
}
ret = mbedtls_md_hmac_starts(&ctx, key, 32);
if (ret != 0) {
mbedtls_md_free(&ctx);
return -EIO;
}
for (; sg_len; sg_len--, sg++) {
ret = mbedtls_md_hmac_update(&ctx, sg->data, sg->len);
if (ret != 0) {
mbedtls_md_free(&ctx);
return -EIO;
}
}
ret = mbedtls_md_hmac_finish(&ctx, mac);
mbedtls_md_free(&ctx);
return ret == 0 ? 0 : -EIO;
}
int bt_mesh_pub_key_gen(void)
{
mbedtls_ecp_group grp;
mbedtls_mpi d;
mbedtls_ecp_point Q;
uint8_t private_key_be[PRIV_KEY_SIZE];
uint8_t public_key_be[PUB_KEY_SIZE];
int ret;
int err;
dh_pair.is_ready = false;
do {
err = bt_mesh_rand(dh_pair.private_key, sizeof(dh_pair.private_key));
if (err) {
BT_ERR("Failed to generate random private key");
return err;
}
/* Ensure the private key is valid (non-zero last bytes in LE) */
} while (dh_pair.private_key[0] == 0 &&
dh_pair.private_key[1] == 0);
mbedtls_ecp_group_init(&grp);
mbedtls_mpi_init(&d);
mbedtls_ecp_point_init(&Q);
ret = mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1);
if (ret != 0) {
goto cleanup;
}
/* Read private key into MPI (MbedTLS expects big-endian) */
ret = mbedtls_mpi_read_binary(&d, dh_pair.private_key, PRIV_KEY_SIZE);
if (ret != 0) {
goto cleanup;
}
/* Compute public key: Q = d * G */
ret = mbedtls_ecp_mul(&grp, &Q, &d, &grp.G, NULL, NULL);
if (ret != 0) {
goto cleanup;
}
/* Export public key point in big-endian */
ret = mbedtls_mpi_write_binary(&Q.MBEDTLS_PRIVATE(X), dh_pair.public_key, 32);
if (ret != 0) {
goto cleanup;
}
ret = mbedtls_mpi_write_binary(&Q.MBEDTLS_PRIVATE(Y), dh_pair.public_key + 32, 32);
if (ret != 0) {
goto cleanup;
}
dh_pair.is_ready = true;
cleanup:
mbedtls_ecp_group_free(&grp);
mbedtls_mpi_free(&d);
mbedtls_ecp_point_free(&Q);
return ret == 0 ? 0 : -EIO;
}
const uint8_t *bt_mesh_pub_key_get_raw(void)
{
if (!dh_pair.is_ready) {
if (bt_mesh_pub_key_gen() != 0) {
return NULL;
}
}
return dh_pair.public_key;
}
void bt_mesh_set_private_key_raw(const uint8_t pri_key[32])
{
mbedtls_ecp_group grp;
mbedtls_mpi d;
mbedtls_ecp_point Q;
int ret;
dh_pair.is_ready = false;
memcpy(dh_pair.private_key, pri_key, PRIV_KEY_SIZE);
/* Compute public key from private key */
mbedtls_ecp_group_init(&grp);
mbedtls_mpi_init(&d);
mbedtls_ecp_point_init(&Q);
ret = mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1);
if (ret != 0) {
goto cleanup;
}
ret = mbedtls_mpi_read_binary(&d, pri_key, PRIV_KEY_SIZE);
if (ret != 0) {
goto cleanup;
}
/* Compute public key: Q = d * G (no RNG needed for deterministic computation) */
ret = mbedtls_ecp_mul(&grp, &Q, &d, &grp.G, NULL, NULL);
if (ret != 0) {
goto cleanup;
}
/* Export public key point in big-endian */
ret = mbedtls_mpi_write_binary(&Q.MBEDTLS_PRIVATE(X), dh_pair.public_key, 32);
if (ret != 0) {
goto cleanup;
}
ret = mbedtls_mpi_write_binary(&Q.MBEDTLS_PRIVATE(Y), dh_pair.public_key + 32, 32);
if (ret != 0) {
goto cleanup;
}
BT_DBG("Pubkey:%s", bt_hex(dh_pair.public_key, PUB_KEY_SIZE));
BT_DBG("Privkey:%s", bt_hex(dh_pair.private_key, PRIV_KEY_SIZE));
dh_pair.is_ready = true;
cleanup:
mbedtls_ecp_group_free(&grp);
mbedtls_mpi_free(&d);
mbedtls_ecp_point_free(&Q);
}
bool bt_mesh_check_public_key_raw(const uint8_t key[64])
{
mbedtls_ecp_group grp;
mbedtls_ecp_point Q;
uint8_t pub_key_be[PUB_KEY_SIZE];
int ret;
mbedtls_ecp_group_init(&grp);
mbedtls_ecp_point_init(&Q);
ret = mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1);
if (ret != 0) {
goto cleanup;
}
ret = mbedtls_mpi_read_binary(&Q.MBEDTLS_PRIVATE(X), dh_pair.public_key, 32);
if (ret != 0) {
goto cleanup;
}
ret = mbedtls_mpi_read_binary(&Q.MBEDTLS_PRIVATE(Y), dh_pair.public_key + 32, 32);
if (ret != 0) {
goto cleanup;
}
ret = mbedtls_mpi_lset(&Q.MBEDTLS_PRIVATE(Z), 1);
if (ret != 0) {
goto cleanup;
}
ret = mbedtls_ecp_check_pubkey(&grp, &Q);
cleanup:
mbedtls_ecp_group_free(&grp);
mbedtls_ecp_point_free(&Q);
return ret == 0;
}
int bt_mesh_dhkey_gen_raw(const uint8_t *pub_key, const uint8_t *priv_key,
uint8_t *dhkey)
{
mbedtls_ecp_group grp;
mbedtls_mpi d;
mbedtls_ecp_point Q;
mbedtls_ecp_point result;
int ret;
mbedtls_ecp_group_init(&grp);
mbedtls_mpi_init(&d);
mbedtls_ecp_point_init(&Q);
mbedtls_ecp_point_init(&result);
ret = mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1);
if (ret != 0) {
goto cleanup;
}
ret = mbedtls_mpi_read_binary(&d, priv_key ? priv_key : dh_pair.private_key, PRIV_KEY_SIZE);
if (ret != 0) {
goto cleanup;
}
/* Load public key point */
ret = mbedtls_mpi_read_binary(&Q.MBEDTLS_PRIVATE(X), dh_pair.public_key, 32);
if (ret != 0) {
goto cleanup;
}
ret = mbedtls_mpi_read_binary(&Q.MBEDTLS_PRIVATE(Y), dh_pair.public_key + 32, 32);
if (ret != 0) {
goto cleanup;
}
ret = mbedtls_mpi_lset(&Q.MBEDTLS_PRIVATE(Z), 1);
if (ret != 0) {
goto cleanup;
}
/* Check the peer's public key */
ret = mbedtls_ecp_check_pubkey(&grp, &Q);
if (ret != 0) {
goto cleanup;
}
/* Calculate shared secret: result = d * Q (no RNG needed for deterministic computation) */
ret = mbedtls_ecp_mul(&grp, &result, &d, &Q, NULL, NULL);
if (ret != 0) {
goto cleanup;
}
/* Export X coordinate as DH key (big-endian) */
ret = mbedtls_mpi_write_binary(&result.MBEDTLS_PRIVATE(X), dhkey, DH_KEY_SIZE);
cleanup:
mbedtls_ecp_group_free(&grp);
mbedtls_mpi_free(&d);
mbedtls_ecp_point_free(&Q);
mbedtls_ecp_point_free(&result);
return ret == 0 ? 0 : -EIO;
}
int bt_mesh_key_import(enum bt_mesh_key_type type, const uint8_t in[16],
struct bt_mesh_key *out)
{
(void)type; /* Not used for non-PSA implementation */
memcpy(out->key, in, 16);
return 0;
}
int bt_mesh_key_export(uint8_t out[16], const struct bt_mesh_key *in)
{
memcpy(out, in->key, 16);
return 0;
}
void bt_mesh_key_assign(struct bt_mesh_key *dst, const struct bt_mesh_key *src)
{
memcpy(dst, src, sizeof(struct bt_mesh_key));
}
int bt_mesh_key_destroy(const struct bt_mesh_key *key)
{
(void)key; /* No cleanup needed for non-PSA implementation */
return 0;
}
int bt_mesh_key_compare(const uint8_t raw_key[16], const struct bt_mesh_key *key)
{
return memcmp(raw_key, key->key, 16);
}
int bt_mesh_encrypt_le(const uint8_t key[16], const uint8_t plaintext[16],
uint8_t enc_data[16])
{
mbedtls_aes_context ctx;
uint8_t tmp[16];
int ret;
BT_DBG("key %s plaintext %s", bt_hex(key, 16), bt_hex(plaintext, 16));
mbedtls_aes_init(&ctx);
/* Swap key bytes (LE to BE) */
sys_memcpy_swap(tmp, key, 16);
ret = mbedtls_aes_setkey_enc(&ctx, tmp, 128);
if (ret != 0) {
mbedtls_aes_free(&ctx);
return -EINVAL;
}
/* Swap plaintext bytes and encrypt */
sys_memcpy_swap(tmp, plaintext, 16);
ret = mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, tmp, enc_data);
mbedtls_aes_free(&ctx);
if (ret != 0) {
return -EINVAL;
}
/* Swap result bytes (BE to LE) */
sys_mem_swap(enc_data, 16);
BT_DBG("enc_data %s", bt_hex(enc_data, 16));
return 0;
}
int bt_mesh_encrypt_be(const uint8_t key[16], const uint8_t plaintext[16],
uint8_t enc_data[16])
{
mbedtls_aes_context ctx;
int ret;
BT_DBG("key %s plaintext %s", bt_hex(key, 16), bt_hex(plaintext, 16));
mbedtls_aes_init(&ctx);
ret = mbedtls_aes_setkey_enc(&ctx, key, 128);
if (ret != 0) {
mbedtls_aes_free(&ctx);
return -EINVAL;
}
ret = mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, plaintext, enc_data);
mbedtls_aes_free(&ctx);
if (ret != 0) {
return -EINVAL;
}
BT_DBG("enc_data %s", bt_hex(enc_data, 16));
return 0;
}