544 lines
14 KiB
C
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;
|
|
}
|