mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
Merge branch 'feat/ble_mesh_unify_crypto_v6.0' into 'release/v6.0'
Feat/ble mesh unify crypto v6.0 See merge request espressif/esp-idf!44818
This commit is contained in:
@@ -556,8 +556,21 @@ if(CONFIG_BT_ENABLED)
|
||||
"esp_ble_mesh/common/mutex.c"
|
||||
"esp_ble_mesh/common/queue.c"
|
||||
"esp_ble_mesh/common/timer.c"
|
||||
"esp_ble_mesh/common/utils.c"
|
||||
"esp_ble_mesh/core/storage/settings_nvs.c"
|
||||
"esp_ble_mesh/common/utils.c")
|
||||
|
||||
# Select crypto implementation based on config
|
||||
if(CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT OR
|
||||
CONFIG_BT_SMP_CRYPTO_STACK_NATIVE)
|
||||
list(APPEND srcs "esp_ble_mesh/common/crypto_tc.c")
|
||||
elseif(CONFIG_BT_SMP_CRYPTO_STACK_MBEDTLS)
|
||||
if(CONFIG_MBEDTLS_VER_4_X_SUPPORT)
|
||||
list(APPEND srcs "esp_ble_mesh/common/crypto_psa.c")
|
||||
else()
|
||||
list(APPEND srcs "esp_ble_mesh/common/crypto_mbedtls.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
list(APPEND srcs "esp_ble_mesh/core/storage/settings_nvs.c"
|
||||
"esp_ble_mesh/core/storage/settings_uid.c"
|
||||
"esp_ble_mesh/core/storage/settings.c"
|
||||
"esp_ble_mesh/core/access.c"
|
||||
@@ -599,7 +612,7 @@ if(CONFIG_BT_ENABLED)
|
||||
"esp_ble_mesh/models/server/state_transition.c"
|
||||
"esp_ble_mesh/models/server/time_scene_server.c")
|
||||
|
||||
if(NOT CONFIG_BLE_MESH_USE_UNIFIED_CRYPTO)
|
||||
if(CONFIG_BT_SMP_CRYPTO_STACK_NATIVE)
|
||||
list(APPEND include_dirs ${ble_mesh_tinycrypt_dirs})
|
||||
list(APPEND srcs
|
||||
"esp_ble_mesh/common/tinycrypt/src/aes_decrypt.c"
|
||||
|
||||
@@ -9,7 +9,7 @@ config BT_ALARM_MAX_NUM
|
||||
choice BT_SMP_CRYPTO_STACK
|
||||
prompt "SMP cryptographic stack"
|
||||
depends on (BT_BLE_SMP_ENABLE || BT_SMP_ENABLE || BT_NIMBLE_SECURITY_ENABLE)
|
||||
default BT_SMP_CRYPTO_STACK_NATIVE
|
||||
default BT_SMP_CRYPTO_STACK_TINYCRYPT
|
||||
help
|
||||
Select the cryptographic library to use for SMP operations (AES, AES-CMAC, ECDH P-256).
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ if BLE_MESH
|
||||
config BLE_MESH_USE_UNIFIED_CRYPTO
|
||||
bool "Use the unified BLE tinycrypt implementation"
|
||||
depends on !BT_LE_CRYPTO_STACK_MBEDTLS && !BT_NIMBLE_CRYPTO_STACK_MBEDTLS
|
||||
default y if BT_SMP_CRYPTO_STACK_TINYCRYPT
|
||||
default n
|
||||
help
|
||||
Enable this option to use the unified BLE tinycrypt solution
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -67,9 +67,9 @@ esp_err_t esp_ble_mesh_node_prov_disable(esp_ble_mesh_prov_bearer_t bearers);
|
||||
* So as an unprovisioned device, it should use this function to input
|
||||
* the Public Key exchanged through the out-of-band mechanism.
|
||||
*
|
||||
* @param[in] pub_key_x: Unprovisioned device's Public Key X
|
||||
* @param[in] pub_key_y: Unprovisioned device's Public Key Y
|
||||
* @param[in] private_key: Unprovisioned device's Private Key
|
||||
* @param[in] pub_key_x: Unprovisioned device's Public Key X(Little Endian)
|
||||
* @param[in] pub_key_y: Unprovisioned device's Public Key Y(Little Endian)
|
||||
* @param[in] private_key: Unprovisioned device's Private Key(Little Endian)
|
||||
*
|
||||
* @return ESP_OK on success or error code otherwise.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,543 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 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;
|
||||
}
|
||||
@@ -0,0 +1,713 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Nordic Semiconductor ASA
|
||||
* SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "esp_random.h"
|
||||
#include "psa/crypto.h"
|
||||
|
||||
#include "mesh/common.h"
|
||||
#include "mesh/crypto.h"
|
||||
#include "mesh/trace.h"
|
||||
#include "mesh/config.h"
|
||||
|
||||
/* PSA Key ID range for BLE Mesh */
|
||||
#define BT_MESH_PSA_KEY_ID_MIN 0x0001A000
|
||||
#define BT_MESH_PSA_KEY_ID_RANGE_SIZE \
|
||||
(2 * CONFIG_BLE_MESH_SUBNET_COUNT + 2 * CONFIG_BLE_MESH_APP_KEY_COUNT + 2)
|
||||
|
||||
/* Internal DH key pair storage
|
||||
*
|
||||
* PSA functions expect BIG-ENDIAN format.
|
||||
*/
|
||||
static struct {
|
||||
bool is_ready;
|
||||
psa_key_id_t priv_key_id;
|
||||
uint8_t public_key[PUB_KEY_SIZE + 1]; /* Big-endian, PSA format: 0x04 + X + Y */
|
||||
} dh_pair;
|
||||
|
||||
/* Bitmap for tracking allocated key IDs */
|
||||
static uint32_t pst_keys[(BT_MESH_PSA_KEY_ID_RANGE_SIZE + 31) / 32];
|
||||
|
||||
static psa_key_id_t keyid_alloc(void)
|
||||
{
|
||||
for (int i = 0; i < BT_MESH_PSA_KEY_ID_RANGE_SIZE; i++) {
|
||||
int word = i / 32;
|
||||
int bit = i % 32;
|
||||
if (!(pst_keys[word] & (1U << bit))) {
|
||||
pst_keys[word] |= (1U << bit);
|
||||
return BT_MESH_PSA_KEY_ID_MIN + i;
|
||||
}
|
||||
}
|
||||
return PSA_KEY_ID_NULL;
|
||||
}
|
||||
|
||||
static int keyid_free(psa_key_id_t key_id)
|
||||
{
|
||||
if (key_id >= BT_MESH_PSA_KEY_ID_MIN &&
|
||||
key_id < BT_MESH_PSA_KEY_ID_MIN + BT_MESH_PSA_KEY_ID_RANGE_SIZE) {
|
||||
int idx = key_id - BT_MESH_PSA_KEY_ID_MIN;
|
||||
int word = idx / 32;
|
||||
int bit = idx % 32;
|
||||
pst_keys[word] &= ~(1U << bit);
|
||||
return 0;
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void keyid_assign(psa_key_id_t key_id)
|
||||
{
|
||||
if (key_id >= BT_MESH_PSA_KEY_ID_MIN &&
|
||||
key_id < BT_MESH_PSA_KEY_ID_MIN + BT_MESH_PSA_KEY_ID_RANGE_SIZE) {
|
||||
int idx = key_id - BT_MESH_PSA_KEY_ID_MIN;
|
||||
int word = idx / 32;
|
||||
int bit = idx % 32;
|
||||
pst_keys[word] |= (1U << bit);
|
||||
}
|
||||
}
|
||||
|
||||
int bt_mesh_crypto_init(void)
|
||||
{
|
||||
psa_status_t status = psa_crypto_init();
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("PSA crypto init failed: %d", status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dh_pair.is_ready = false;
|
||||
dh_pair.priv_key_id = PSA_KEY_ID_NULL;
|
||||
memset(pst_keys, 0, sizeof(pst_keys));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_mesh_encrypt(const struct bt_mesh_key *key, const uint8_t plaintext[16],
|
||||
uint8_t enc_data[16])
|
||||
{
|
||||
size_t output_len;
|
||||
psa_status_t status;
|
||||
|
||||
status = psa_cipher_encrypt(key->key, PSA_ALG_ECB_NO_PADDING,
|
||||
plaintext, 16, enc_data, 16, &output_len);
|
||||
|
||||
if (status != PSA_SUCCESS || output_len != 16) {
|
||||
BT_ERR("PSA cipher encrypt failed: %d", status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
size_t output_len;
|
||||
psa_status_t status;
|
||||
psa_algorithm_t alg = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, mic_size);
|
||||
|
||||
status = psa_aead_encrypt(key->key, alg, nonce, 13, aad, aad_len,
|
||||
plaintext, len, enc_data, len + mic_size, &output_len);
|
||||
|
||||
if (status != PSA_SUCCESS || output_len != len + mic_size) {
|
||||
BT_ERR("PSA AEAD encrypt failed: %d", status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
size_t output_len;
|
||||
psa_status_t status;
|
||||
psa_algorithm_t alg = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, mic_size);
|
||||
|
||||
status = psa_aead_decrypt(key->key, alg, nonce, 13, aad, aad_len,
|
||||
enc_data, len + mic_size, plaintext, len, &output_len);
|
||||
|
||||
if (status != PSA_SUCCESS || output_len != len) {
|
||||
BT_ERR("PSA AEAD decrypt failed: %d", status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
int err;
|
||||
|
||||
err = bt_mesh_key_import(BT_MESH_KEY_TYPE_CCM, key, &mesh_key);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = bt_mesh_ccm_encrypt(&mesh_key, nonce, plaintext, len, aad, aad_len,
|
||||
enc_data, mic_size);
|
||||
|
||||
psa_destroy_key(mesh_key.key);
|
||||
return err;
|
||||
}
|
||||
|
||||
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;
|
||||
int err;
|
||||
|
||||
err = bt_mesh_key_import(BT_MESH_KEY_TYPE_CCM, key, &mesh_key);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = bt_mesh_ccm_decrypt(&mesh_key, nonce, enc_data, len, aad, aad_len,
|
||||
plaintext, mic_size);
|
||||
|
||||
psa_destroy_key(mesh_key.key);
|
||||
return err;
|
||||
}
|
||||
|
||||
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])
|
||||
{
|
||||
psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT;
|
||||
psa_status_t status;
|
||||
size_t mac_len;
|
||||
|
||||
status = psa_mac_sign_setup(&operation, key->key, PSA_ALG_CMAC);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("PSA MAC setup failed: %d", status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (; sg_len; sg_len--, sg++) {
|
||||
status = psa_mac_update(&operation, sg->data, sg->len);
|
||||
if (status != PSA_SUCCESS) {
|
||||
psa_mac_abort(&operation);
|
||||
BT_ERR("PSA MAC update failed: %d", status);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
status = psa_mac_sign_finish(&operation, mac, 16, &mac_len);
|
||||
if (status != PSA_SUCCESS || mac_len != 16) {
|
||||
BT_ERR("PSA MAC finish failed: %d", status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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])
|
||||
{
|
||||
struct bt_mesh_key key_id;
|
||||
int err;
|
||||
|
||||
err = bt_mesh_key_import(BT_MESH_KEY_TYPE_CMAC, key, &key_id);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = bt_mesh_aes_cmac_mesh_key(&key_id, sg, sg_len, mac);
|
||||
|
||||
psa_destroy_key(key_id.key);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
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])
|
||||
{
|
||||
psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT;
|
||||
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_key_id_t key_id;
|
||||
psa_status_t status;
|
||||
size_t mac_len;
|
||||
int err = 0;
|
||||
|
||||
/* Import HMAC key */
|
||||
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE);
|
||||
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE);
|
||||
psa_set_key_algorithm(&attributes, PSA_ALG_HMAC(PSA_ALG_SHA_256));
|
||||
psa_set_key_type(&attributes, PSA_KEY_TYPE_HMAC);
|
||||
psa_set_key_bits(&attributes, 256);
|
||||
|
||||
status = psa_import_key(&attributes, key, 32, &key_id);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("PSA import HMAC key failed: %d", status);
|
||||
return -EIO;
|
||||
}
|
||||
psa_reset_key_attributes(&attributes);
|
||||
|
||||
status = psa_mac_sign_setup(&operation, key_id, PSA_ALG_HMAC(PSA_ALG_SHA_256));
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("PSA HMAC setup failed: %d", status);
|
||||
err = -EIO;
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (; sg_len; sg_len--, sg++) {
|
||||
status = psa_mac_update(&operation, sg->data, sg->len);
|
||||
if (status != PSA_SUCCESS) {
|
||||
psa_mac_abort(&operation);
|
||||
BT_ERR("PSA HMAC update failed: %d", status);
|
||||
err = -EIO;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
status = psa_mac_sign_finish(&operation, mac, 32, &mac_len);
|
||||
if (status != PSA_SUCCESS || mac_len != 32) {
|
||||
BT_ERR("PSA HMAC finish failed: %d", status);
|
||||
err = -EIO;
|
||||
}
|
||||
|
||||
end:
|
||||
psa_destroy_key(key_id);
|
||||
return err;
|
||||
}
|
||||
|
||||
int bt_mesh_pub_key_gen(void)
|
||||
{
|
||||
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_status_t status;
|
||||
uint8_t private_key[PRIV_KEY_SIZE];
|
||||
size_t key_len;
|
||||
int err;
|
||||
|
||||
/* Destroy any existing key */
|
||||
if (dh_pair.priv_key_id != PSA_KEY_ID_NULL) {
|
||||
psa_destroy_key(dh_pair.priv_key_id);
|
||||
dh_pair.priv_key_id = PSA_KEY_ID_NULL;
|
||||
}
|
||||
dh_pair.is_ready = false;
|
||||
|
||||
/* Generate a random private key (in little-endian format for storage) */
|
||||
do {
|
||||
err = bt_mesh_rand(private_key, sizeof(private_key));
|
||||
if (err) {
|
||||
BT_ERR("Failed to generate random private key");
|
||||
return err;
|
||||
}
|
||||
/* Ensure the private key is valid (non-zero first bytes in BE) */
|
||||
} while (private_key[0] == 0 && private_key[1] == 0);
|
||||
|
||||
/* Configure key attributes for ECDH with P-256 */
|
||||
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_DERIVE);
|
||||
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE);
|
||||
psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDH);
|
||||
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
|
||||
psa_set_key_bits(&key_attributes, 256);
|
||||
|
||||
/* Import the private key */
|
||||
status = psa_import_key(&key_attributes, private_key, sizeof(private_key),
|
||||
&dh_pair.priv_key_id);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("PSA import private key failed: %d", status);
|
||||
psa_reset_key_attributes(&key_attributes);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Export public key (PSA computes it from the private key) */
|
||||
status = psa_export_public_key(dh_pair.priv_key_id, dh_pair.public_key,
|
||||
sizeof(dh_pair.public_key), &key_len);
|
||||
if (status != PSA_SUCCESS || key_len != PUB_KEY_SIZE + 1) {
|
||||
BT_ERR("PSA export public key failed: %d", status);
|
||||
psa_destroy_key(dh_pair.priv_key_id);
|
||||
dh_pair.priv_key_id = PSA_KEY_ID_NULL;
|
||||
psa_reset_key_attributes(&key_attributes);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dh_pair.is_ready = true;
|
||||
psa_reset_key_attributes(&key_attributes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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[1];
|
||||
}
|
||||
|
||||
void bt_mesh_set_private_key_raw(const uint8_t pri_key[32])
|
||||
{
|
||||
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_status_t status;
|
||||
size_t key_len;
|
||||
|
||||
/* Destroy any existing key */
|
||||
if (dh_pair.priv_key_id != PSA_KEY_ID_NULL) {
|
||||
psa_destroy_key(dh_pair.priv_key_id);
|
||||
dh_pair.priv_key_id = PSA_KEY_ID_NULL;
|
||||
}
|
||||
dh_pair.is_ready = false;
|
||||
|
||||
/* Import the provided private key */
|
||||
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE);
|
||||
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE);
|
||||
psa_set_key_algorithm(&attributes, PSA_ALG_ECDH);
|
||||
psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
|
||||
psa_set_key_bits(&attributes, 256);
|
||||
|
||||
status = psa_import_key(&attributes, pri_key, PRIV_KEY_SIZE, &dh_pair.priv_key_id);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("PSA import private key failed: %d", status);
|
||||
psa_reset_key_attributes(&attributes);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Export public key (PSA computes it from the private key) */
|
||||
status = psa_export_public_key(dh_pair.priv_key_id, dh_pair.public_key,
|
||||
sizeof(dh_pair.public_key), &key_len);
|
||||
if (status != PSA_SUCCESS || key_len != PUB_KEY_SIZE + 1) {
|
||||
BT_ERR("PSA export public key failed: %d", status);
|
||||
psa_destroy_key(dh_pair.priv_key_id);
|
||||
dh_pair.priv_key_id = PSA_KEY_ID_NULL;
|
||||
psa_reset_key_attributes(&attributes);
|
||||
return;
|
||||
}
|
||||
|
||||
BT_DBG("Pubkey:%s", bt_hex(&dh_pair.public_key[1], PUB_KEY_SIZE));
|
||||
BT_DBG("Privkey:%s", bt_hex(pri_key, PRIV_KEY_SIZE));
|
||||
dh_pair.is_ready = true;
|
||||
psa_reset_key_attributes(&attributes);
|
||||
}
|
||||
|
||||
bool bt_mesh_check_public_key_raw(const uint8_t key[64])
|
||||
{
|
||||
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_key_id_t key_id;
|
||||
psa_status_t status;
|
||||
uint8_t pub_be[PUB_KEY_SIZE + 1];
|
||||
|
||||
/* PSA requires 0x04 prefix for uncompressed point */
|
||||
pub_be[0] = 0x04;
|
||||
/* Convert from little-endian to big-endian */
|
||||
memcpy(&pub_be[1], key, 32);
|
||||
memcpy(&pub_be[33], key + 32, 32);
|
||||
|
||||
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_VERIFY_HASH);
|
||||
psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
|
||||
psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1));
|
||||
psa_set_key_bits(&attributes, 256);
|
||||
|
||||
status = psa_import_key(&attributes, pub_be, sizeof(pub_be), &key_id);
|
||||
psa_reset_key_attributes(&attributes);
|
||||
|
||||
if (status != PSA_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
psa_destroy_key(key_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
int bt_mesh_dhkey_gen_raw(const uint8_t *pub_key, const uint8_t *priv_key,
|
||||
uint8_t *dhkey)
|
||||
{
|
||||
psa_key_id_t priv_key_id = PSA_KEY_ID_NULL;
|
||||
uint8_t public_key_be[PUB_KEY_SIZE + 1];
|
||||
psa_status_t status;
|
||||
size_t dh_key_len;
|
||||
int err = 0;
|
||||
|
||||
if (priv_key) {
|
||||
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
|
||||
/* Import custom private key */
|
||||
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE);
|
||||
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE);
|
||||
psa_set_key_algorithm(&attributes, PSA_ALG_ECDH);
|
||||
psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
|
||||
psa_set_key_bits(&attributes, 256);
|
||||
|
||||
status = psa_import_key(&attributes, priv_key, PRIV_KEY_SIZE, &priv_key_id);
|
||||
psa_reset_key_attributes(&attributes);
|
||||
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("PSA import private key failed: %d", status);
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
priv_key_id = dh_pair.priv_key_id;
|
||||
}
|
||||
|
||||
/* Prepare public key with 0x04 prefix in big-endian format */
|
||||
public_key_be[0] = 0x04;
|
||||
/* Convert from little-endian to big-endian */
|
||||
memcpy(public_key_be + 1, pub_key, 32);
|
||||
memcpy(public_key_be + 33, pub_key + 32, 32);
|
||||
|
||||
/* Calculate shared secret */
|
||||
status = psa_raw_key_agreement(PSA_ALG_ECDH, priv_key_id, public_key_be,
|
||||
PUB_KEY_SIZE + 1, dhkey, DH_KEY_SIZE, &dh_key_len);
|
||||
|
||||
if (status != PSA_SUCCESS || dh_key_len != DH_KEY_SIZE) {
|
||||
BT_ERR("PSA ECDH failed: %d", status);
|
||||
err = -EIO;
|
||||
}
|
||||
|
||||
if (priv_key && priv_key_id != PSA_KEY_ID_NULL) {
|
||||
psa_destroy_key(priv_key_id);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int bt_mesh_key_import(enum bt_mesh_key_type type, const uint8_t in[16],
|
||||
struct bt_mesh_key *out)
|
||||
{
|
||||
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_status_t status;
|
||||
psa_key_id_t key_id = PSA_KEY_ID_NULL;
|
||||
|
||||
switch (type) {
|
||||
case BT_MESH_KEY_TYPE_ECB:
|
||||
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE);
|
||||
psa_set_key_usage_flags(&key_attributes,
|
||||
PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
|
||||
psa_set_key_algorithm(&key_attributes, PSA_ALG_ECB_NO_PADDING);
|
||||
break;
|
||||
case BT_MESH_KEY_TYPE_CCM:
|
||||
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE);
|
||||
psa_set_key_usage_flags(&key_attributes,
|
||||
PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
|
||||
psa_set_key_algorithm(&key_attributes,
|
||||
PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_CCM, 4));
|
||||
break;
|
||||
case BT_MESH_KEY_TYPE_CMAC:
|
||||
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE);
|
||||
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_SIGN_MESSAGE);
|
||||
psa_set_key_algorithm(&key_attributes, PSA_ALG_CMAC);
|
||||
break;
|
||||
case BT_MESH_KEY_TYPE_NET:
|
||||
#if CONFIG_BT_SETTINGS
|
||||
key_id = keyid_alloc();
|
||||
if (key_id == PSA_KEY_ID_NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_PERSISTENT);
|
||||
psa_set_key_id(&key_attributes, key_id);
|
||||
#else
|
||||
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE);
|
||||
#endif
|
||||
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_EXPORT);
|
||||
break;
|
||||
case BT_MESH_KEY_TYPE_APP:
|
||||
case BT_MESH_KEY_TYPE_DEV:
|
||||
#if CONFIG_BT_SETTINGS
|
||||
key_id = keyid_alloc();
|
||||
if (key_id == PSA_KEY_ID_NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_PERSISTENT);
|
||||
psa_set_key_id(&key_attributes, key_id);
|
||||
#else
|
||||
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE);
|
||||
#endif
|
||||
psa_set_key_usage_flags(&key_attributes,
|
||||
PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT | PSA_KEY_USAGE_EXPORT);
|
||||
psa_set_key_algorithm(&key_attributes,
|
||||
PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_CCM, 4));
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_AES);
|
||||
psa_set_key_bits(&key_attributes, 128);
|
||||
|
||||
status = psa_import_key(&key_attributes, in, 16, &out->key);
|
||||
if (status == PSA_ERROR_ALREADY_EXISTS) {
|
||||
BT_WARN("Key 0x%04x already exists, destroying and reimporting", key_id);
|
||||
psa_destroy_key(key_id);
|
||||
status = psa_import_key(&key_attributes, in, 16, &out->key);
|
||||
}
|
||||
|
||||
psa_reset_key_attributes(&key_attributes);
|
||||
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("PSA import key failed: %d", status);
|
||||
if (key_id != PSA_KEY_ID_NULL) {
|
||||
keyid_free(key_id);
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_mesh_key_export(uint8_t out[16], const struct bt_mesh_key *in)
|
||||
{
|
||||
size_t data_length;
|
||||
psa_status_t status;
|
||||
|
||||
status = psa_export_key(in->key, out, 16, &data_length);
|
||||
if (status != PSA_SUCCESS || data_length != 16) {
|
||||
BT_ERR("PSA export key failed: %d", status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
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));
|
||||
#if CONFIG_BT_SETTINGS
|
||||
keyid_assign(dst->key);
|
||||
#endif
|
||||
}
|
||||
|
||||
int bt_mesh_key_destroy(const struct bt_mesh_key *key)
|
||||
{
|
||||
psa_status_t status;
|
||||
|
||||
status = psa_destroy_key(key->key);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("PSA destroy key failed: %d", status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#if CONFIG_BT_SETTINGS
|
||||
return keyid_free(key->key);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int bt_mesh_key_compare(const uint8_t raw_key[16], const struct bt_mesh_key *key)
|
||||
{
|
||||
uint8_t out[16];
|
||||
int err;
|
||||
|
||||
err = bt_mesh_key_export(out, key);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return memcmp(out, raw_key, 16);
|
||||
}
|
||||
|
||||
int bt_mesh_encrypt_le(const uint8_t key[16], const uint8_t plaintext[16],
|
||||
uint8_t enc_data[16])
|
||||
{
|
||||
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_key_id_t key_id;
|
||||
psa_status_t status;
|
||||
uint8_t tmp[16];
|
||||
size_t output_len;
|
||||
|
||||
BT_DBG("key %s plaintext %s", bt_hex(key, 16), bt_hex(plaintext, 16));
|
||||
|
||||
/* Swap key bytes (LE to BE) */
|
||||
sys_memcpy_swap(tmp, key, 16);
|
||||
|
||||
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT);
|
||||
psa_set_key_algorithm(&attributes, PSA_ALG_ECB_NO_PADDING);
|
||||
psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
|
||||
psa_set_key_bits(&attributes, 128);
|
||||
|
||||
status = psa_import_key(&attributes, tmp, 16, &key_id);
|
||||
psa_reset_key_attributes(&attributes);
|
||||
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("PSA import key failed: %d", status);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Swap plaintext bytes and encrypt */
|
||||
sys_memcpy_swap(tmp, plaintext, 16);
|
||||
|
||||
status = psa_cipher_encrypt(key_id, PSA_ALG_ECB_NO_PADDING,
|
||||
tmp, 16, enc_data, 16, &output_len);
|
||||
|
||||
psa_destroy_key(key_id);
|
||||
|
||||
if (status != PSA_SUCCESS || output_len != 16) {
|
||||
BT_ERR("PSA encrypt failed: %d", status);
|
||||
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])
|
||||
{
|
||||
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_key_id_t key_id;
|
||||
psa_status_t status;
|
||||
size_t output_len;
|
||||
|
||||
BT_DBG("key %s plaintext %s", bt_hex(key, 16), bt_hex(plaintext, 16));
|
||||
|
||||
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT);
|
||||
psa_set_key_algorithm(&attributes, PSA_ALG_ECB_NO_PADDING);
|
||||
psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
|
||||
psa_set_key_bits(&attributes, 128);
|
||||
|
||||
status = psa_import_key(&attributes, key, 16, &key_id);
|
||||
psa_reset_key_attributes(&attributes);
|
||||
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("PSA import key failed: %d", status);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
status = psa_cipher_encrypt(key_id, PSA_ALG_ECB_NO_PADDING,
|
||||
plaintext, 16, enc_data, 16, &output_len);
|
||||
|
||||
psa_destroy_key(key_id);
|
||||
|
||||
if (status != PSA_SUCCESS || output_len != 16) {
|
||||
BT_ERR("PSA encrypt failed: %d", status);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
BT_DBG("enc_data %s", bt_hex(enc_data, 16));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
int bt_mesh_rand(void *buf, size_t len)
|
||||
{
|
||||
psa_status_t status;
|
||||
|
||||
if (buf == NULL || len == 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
status = psa_generate_random(buf, len);
|
||||
return status == PSA_SUCCESS ? 0 : -EIO;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2023 Nordic Semiconductor ASA
|
||||
* SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "esp_random.h"
|
||||
|
||||
#include <tinycrypt/aes.h>
|
||||
#include <tinycrypt/constants.h>
|
||||
#include <tinycrypt/cmac_mode.h>
|
||||
#include <tinycrypt/ccm_mode.h>
|
||||
#include <tinycrypt/ecc.h>
|
||||
#include <tinycrypt/ecc_dh.h>
|
||||
#include <tinycrypt/hmac.h>
|
||||
|
||||
#include "mesh/common.h"
|
||||
#include "mesh/crypto.h"
|
||||
#include "mesh/trace.h"
|
||||
|
||||
/* Internal DH key pair storage
|
||||
*
|
||||
* TinyCrypt's uECC 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])
|
||||
{
|
||||
struct tc_aes_key_sched_struct sched;
|
||||
|
||||
if (tc_aes128_set_encrypt_key(&sched, key->key) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (tc_aes_encrypt(enc_data, plaintext, &sched) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct tc_ccm_mode_struct ccm;
|
||||
struct tc_aes_key_sched_struct sched;
|
||||
|
||||
if (tc_aes128_set_encrypt_key(&sched, key->key) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (tc_ccm_config(&ccm, &sched, nonce, 13, mic_size) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (tc_ccm_generation_encryption(enc_data, len + mic_size, aad, aad_len,
|
||||
plaintext, len, &ccm) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct tc_ccm_mode_struct ccm;
|
||||
struct tc_aes_key_sched_struct sched;
|
||||
|
||||
if (tc_aes128_set_encrypt_key(&sched, key->key) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (tc_ccm_config(&ccm, &sched, nonce, 13, mic_size) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (tc_ccm_decryption_verification(plaintext, len, aad, aad_len,
|
||||
enc_data, len + mic_size, &ccm) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_raw_key(const uint8_t key[16], struct bt_mesh_sg *sg,
|
||||
size_t sg_len, uint8_t mac[16])
|
||||
{
|
||||
struct tc_aes_key_sched_struct sched;
|
||||
struct tc_cmac_struct state;
|
||||
|
||||
if (tc_cmac_setup(&state, key, &sched) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (; sg_len; sg_len--, sg++) {
|
||||
if (tc_cmac_update(&state, sg->data, sg->len) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (tc_cmac_final(mac, &state) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_sha256_hmac_raw_key(const uint8_t key[32], struct bt_mesh_sg *sg,
|
||||
size_t sg_len, uint8_t mac[32])
|
||||
{
|
||||
struct tc_hmac_state_struct h;
|
||||
|
||||
if (tc_hmac_set_key(&h, key, 32) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (tc_hmac_init(&h) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (; sg_len; sg_len--, sg++) {
|
||||
if (tc_hmac_update(&h, sg->data, sg->len) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (tc_hmac_final(mac, 32, &h) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_mesh_pub_key_gen(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Generate a random private key */
|
||||
do {
|
||||
rc = bt_mesh_rand(dh_pair.private_key, sizeof(dh_pair.private_key));
|
||||
if (rc) {
|
||||
BT_ERR("Failed to generate random private key");
|
||||
return rc;
|
||||
}
|
||||
/* Ensure the private key is valid (first bytes in BE shall be non-zero) */
|
||||
} while (dh_pair.private_key[0] == 0 &&
|
||||
dh_pair.private_key[1] == 0);
|
||||
|
||||
/* Compute the public key from the private key */
|
||||
rc = uECC_compute_public_key(dh_pair.private_key, dh_pair.public_key, uECC_secp256r1());
|
||||
if (rc != TC_CRYPTO_SUCCESS) {
|
||||
dh_pair.is_ready = false;
|
||||
BT_ERR("Failed to compute public key");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dh_pair.is_ready = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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])
|
||||
{
|
||||
int rc;
|
||||
|
||||
memcpy(dh_pair.private_key, pri_key, PRIV_KEY_SIZE);
|
||||
|
||||
/* Compute the public key from the provided private key */
|
||||
rc = uECC_compute_public_key(dh_pair.private_key, dh_pair.public_key, uECC_secp256r1());
|
||||
if (rc != TC_CRYPTO_SUCCESS) {
|
||||
dh_pair.is_ready = false;
|
||||
BT_ERR("Failed to compute public key from private key");
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool bt_mesh_check_public_key_raw(const uint8_t key[64])
|
||||
{
|
||||
return uECC_valid_public_key(key, uECC_secp256r1()) == 0;
|
||||
}
|
||||
|
||||
int bt_mesh_dhkey_gen_raw(const uint8_t *pub_key, const uint8_t *priv_key,
|
||||
uint8_t *dhkey)
|
||||
{
|
||||
if (uECC_valid_public_key(pub_key, uECC_secp256r1()) != 0) {
|
||||
BT_ERR("Public key is not valid");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (uECC_shared_secret(pub_key, priv_key ? priv_key : dh_pair.private_key,
|
||||
dhkey, uECC_secp256r1()) != TC_CRYPTO_SUCCESS) {
|
||||
BT_ERR("DHKey generation failed");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_mesh_key_import(enum bt_mesh_key_type type, const uint8_t in[16],
|
||||
struct bt_mesh_key *out)
|
||||
{
|
||||
(void)type; /* Type is not used for TinyCrypt, just store raw key */
|
||||
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->key, src->key, 16);
|
||||
}
|
||||
|
||||
int bt_mesh_key_destroy(const struct bt_mesh_key *key)
|
||||
{
|
||||
(void)key; /* Nothing to destroy for TinyCrypt */
|
||||
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])
|
||||
{
|
||||
struct tc_aes_key_sched_struct sched;
|
||||
uint8_t tmp[16];
|
||||
|
||||
BT_DBG("key %s plaintext %s", bt_hex(key, 16), bt_hex(plaintext, 16));
|
||||
|
||||
/* Swap key bytes (LE to BE) */
|
||||
sys_memcpy_swap(tmp, key, 16);
|
||||
|
||||
if (tc_aes128_set_encrypt_key(&sched, tmp) == TC_CRYPTO_FAIL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Swap plaintext bytes (LE to BE) */
|
||||
sys_memcpy_swap(tmp, plaintext, 16);
|
||||
|
||||
if (tc_aes_encrypt(enc_data, tmp, &sched) == TC_CRYPTO_FAIL) {
|
||||
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])
|
||||
{
|
||||
struct tc_aes_key_sched_struct sched;
|
||||
|
||||
BT_DBG("key %s plaintext %s", bt_hex(key, 16), bt_hex(plaintext, 16));
|
||||
|
||||
if (tc_aes128_set_encrypt_key(&sched, key) == TC_CRYPTO_FAIL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (tc_aes_encrypt(enc_data, plaintext, &sched) == TC_CRYPTO_FAIL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
BT_DBG("enc_data %s", bt_hex(enc_data, 16));
|
||||
return 0;
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "mesh/trace.h"
|
||||
#include "mesh/mutex.h"
|
||||
#include "mesh/access.h"
|
||||
#include "mesh/crypto.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
@@ -0,0 +1,514 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2023 Nordic Semiconductor ASA
|
||||
* SPDX-FileContributor: 2018-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _BLE_MESH_CRYPTO_H_
|
||||
#define _BLE_MESH_CRYPTO_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "mesh/common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Key sizes */
|
||||
#define PRIV_KEY_SIZE 32
|
||||
#define PUB_KEY_SIZE 64
|
||||
#define DH_KEY_SIZE 32
|
||||
|
||||
/**
|
||||
* @brief Crypto module endianness definitions
|
||||
*
|
||||
* BLE Mesh protocol uses big-endian format for ECC operations.
|
||||
* These macros define whether the underlying crypto implementation
|
||||
* expects big-endian or little-endian input.
|
||||
*
|
||||
* - BT_MESH_CRYPTO_ENDIAN_BIG: Module expects big-endian (TinyCrypt, MbedTLS, PSA)
|
||||
* - BT_MESH_CRYPTO_ENDIAN_LITTLE: Module expects little-endian (future implementations)
|
||||
*/
|
||||
#define BT_MESH_CRYPTO_ENDIAN_BIG 0
|
||||
#define BT_MESH_CRYPTO_ENDIAN_LITTLE 1
|
||||
|
||||
/**
|
||||
* @brief Current crypto module endianness
|
||||
*
|
||||
* This macro should be defined by the crypto implementation.
|
||||
* Default is big-endian (matching TinyCrypt, MbedTLS, PSA).
|
||||
*/
|
||||
#ifndef BT_MESH_CRYPTO_ENDIAN
|
||||
#define BT_MESH_CRYPTO_ENDIAN BT_MESH_CRYPTO_ENDIAN_BIG
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Parameter conversion macros for endianness handling
|
||||
*
|
||||
* These macros automatically handle byte order conversion between
|
||||
* BLE Mesh's big-endian format and the crypto module's expected format.
|
||||
*
|
||||
* For public keys (64 bytes = X + Y), the X and Y coordinates must be
|
||||
* swapped separately, not as a single 64-byte block.
|
||||
*/
|
||||
#if (BT_MESH_CRYPTO_ENDIAN == BT_MESH_CRYPTO_ENDIAN_LITTLE)
|
||||
|
||||
/* Little-endian mode: need byte order conversion */
|
||||
#define CRYPTO_PARAM_DECLARE(name, size) \
|
||||
uint8_t _##name[size]
|
||||
|
||||
#define CRYPTO_PARAM_IN(name, size) \
|
||||
sys_memcpy_swap(_##name, name, size)
|
||||
|
||||
#define CRYPTO_PARAM_OUT(name, size) \
|
||||
sys_memcpy_swap((uint8_t *)(name), _##name, size)
|
||||
|
||||
#define CRYPTO_PARAM(name) (_##name)
|
||||
|
||||
/* Public key (64 bytes) needs special handling: swap X and Y separately */
|
||||
#define CRYPTO_PARAM_PUBKEY_IN(name) \
|
||||
do { \
|
||||
sys_memcpy_swap(_##name, name, 32); \
|
||||
sys_memcpy_swap(_##name + 32, (name) + 32, 32); \
|
||||
} while (0)
|
||||
|
||||
#define CRYPTO_PARAM_PUBKEY_OUT(name) \
|
||||
do { \
|
||||
sys_memcpy_swap((uint8_t *)(name), _##name, 32); \
|
||||
sys_memcpy_swap((uint8_t *)(name) + 32, _##name + 32, 32); \
|
||||
} while (0)
|
||||
|
||||
#else /* BT_MESH_CRYPTO_ENDIAN_BIG */
|
||||
|
||||
/* Big-endian mode: no conversion needed, use original parameters directly */
|
||||
#define CRYPTO_PARAM_DECLARE(name, size) \
|
||||
(void)0
|
||||
|
||||
#define CRYPTO_PARAM_IN(name, size) \
|
||||
(void)0
|
||||
|
||||
#define CRYPTO_PARAM_OUT(name, size) \
|
||||
(void)0
|
||||
|
||||
#define CRYPTO_PARAM(name) (name)
|
||||
|
||||
#define CRYPTO_PARAM_PUBKEY_IN(name) \
|
||||
(void)0
|
||||
|
||||
#define CRYPTO_PARAM_PUBKEY_OUT(name) \
|
||||
(void)0
|
||||
|
||||
#endif /* BT_MESH_CRYPTO_ENDIAN */
|
||||
|
||||
/**
|
||||
* @brief Scatter-Gather data structure for cryptographic operations
|
||||
*/
|
||||
struct bt_mesh_sg {
|
||||
const void *data;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Mesh key type enumeration
|
||||
*/
|
||||
enum bt_mesh_key_type {
|
||||
BT_MESH_KEY_TYPE_ECB, /**< AES-ECB encryption/decryption */
|
||||
BT_MESH_KEY_TYPE_CCM, /**< AES-CCM AEAD */
|
||||
BT_MESH_KEY_TYPE_CMAC, /**< AES-CMAC */
|
||||
BT_MESH_KEY_TYPE_NET, /**< Network key */
|
||||
BT_MESH_KEY_TYPE_APP, /**< Application key */
|
||||
BT_MESH_KEY_TYPE_DEV, /**< Device key */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Mesh key structure
|
||||
*
|
||||
* For TinyCrypt/MbedTLS: stores raw key bytes
|
||||
* For PSA: stores PSA key ID
|
||||
*/
|
||||
struct bt_mesh_key {
|
||||
#if CONFIG_BT_SMP_CRYPTO_STACK_MBEDTLS && CONFIG_MBEDTLS_VER_4_X_SUPPORT
|
||||
uint32_t key; /* PSA key ID */
|
||||
#else
|
||||
uint8_t key[16]; /* Raw key bytes */
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Initialize the cryptographic subsystem
|
||||
*
|
||||
* @return 0 on success, negative error code otherwise
|
||||
*/
|
||||
int bt_mesh_crypto_init(void);
|
||||
|
||||
/**
|
||||
* @brief AES-ECB encryption (big-endian)
|
||||
*
|
||||
* @param key Pointer to mesh key structure
|
||||
* @param plaintext 16-byte plaintext
|
||||
* @param enc_data 16-byte output buffer for encrypted data
|
||||
*
|
||||
* @return 0 on success, negative error code otherwise
|
||||
*/
|
||||
int bt_mesh_encrypt(const struct bt_mesh_key *key, const uint8_t plaintext[16],
|
||||
uint8_t enc_data[16]);
|
||||
|
||||
/**
|
||||
* @brief AES-CCM encryption
|
||||
*
|
||||
* @param key Pointer to mesh key structure
|
||||
* @param nonce 13-byte nonce
|
||||
* @param plaintext Input plaintext
|
||||
* @param len Length of plaintext
|
||||
* @param aad Additional authenticated data
|
||||
* @param aad_len Length of AAD
|
||||
* @param enc_data Output buffer (must be len + mic_size bytes)
|
||||
* @param mic_size Size of MIC (4, 8, or 16 bytes)
|
||||
*
|
||||
* @return 0 on success, negative error code otherwise
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief AES-CCM decryption
|
||||
*
|
||||
* @param key Pointer to mesh key structure
|
||||
* @param nonce 13-byte nonce
|
||||
* @param enc_data Encrypted data with MIC
|
||||
* @param len Length of plaintext (without MIC)
|
||||
* @param aad Additional authenticated data
|
||||
* @param aad_len Length of AAD
|
||||
* @param plaintext Output buffer for plaintext
|
||||
* @param mic_size Size of MIC (4, 8, or 16 bytes)
|
||||
*
|
||||
* @return 0 on success, negative error code otherwise
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief AES-CCM encryption using raw key bytes
|
||||
*
|
||||
* @param key 16-byte raw key
|
||||
* @param nonce 13-byte nonce
|
||||
* @param plaintext Input plaintext
|
||||
* @param len Length of plaintext
|
||||
* @param aad Additional authenticated data
|
||||
* @param aad_len Length of AAD
|
||||
* @param enc_data Output buffer (must be len + mic_size bytes)
|
||||
* @param mic_size Size of MIC (4, 8, or 16 bytes)
|
||||
*
|
||||
* @return 0 on success, negative error code otherwise
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief AES-CCM decryption using raw key bytes
|
||||
*
|
||||
* @param key 16-byte raw key
|
||||
* @param nonce 13-byte nonce
|
||||
* @param enc_data Encrypted data with MIC
|
||||
* @param len Length of plaintext (without MIC)
|
||||
* @param aad Additional authenticated data
|
||||
* @param aad_len Length of AAD
|
||||
* @param plaintext Output buffer for plaintext
|
||||
* @param mic_size Size of MIC (4, 8, or 16 bytes)
|
||||
*
|
||||
* @return 0 on success, negative error code otherwise
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief AES-CMAC using mesh key structure
|
||||
*
|
||||
* @param key Pointer to mesh key structure
|
||||
* @param sg Scatter-gather list of input data
|
||||
* @param sg_len Number of scatter-gather entries
|
||||
* @param mac 16-byte output buffer for MAC
|
||||
*
|
||||
* @return 0 on success, negative error code otherwise
|
||||
*/
|
||||
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]);
|
||||
|
||||
/**
|
||||
* @brief AES-CMAC using raw key bytes
|
||||
*
|
||||
* @param key 16-byte raw key
|
||||
* @param sg Scatter-gather list of input data
|
||||
* @param sg_len Number of scatter-gather entries
|
||||
* @param mac 16-byte output buffer for MAC
|
||||
*
|
||||
* @return 0 on success, negative error code otherwise
|
||||
*/
|
||||
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]);
|
||||
|
||||
/**
|
||||
* @brief AES-CMAC with single data input (convenience wrapper)
|
||||
*/
|
||||
static inline int bt_mesh_aes_cmac_one(const uint8_t key[16], const void *m,
|
||||
size_t len, uint8_t mac[16])
|
||||
{
|
||||
struct bt_mesh_sg sg = { m, len };
|
||||
return bt_mesh_aes_cmac_raw_key(key, &sg, 1, mac);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HMAC-SHA256 using raw key bytes
|
||||
*
|
||||
* @param key 32-byte raw key
|
||||
* @param sg Scatter-gather list of input data
|
||||
* @param sg_len Number of scatter-gather entries
|
||||
* @param mac 32-byte output buffer for MAC
|
||||
*
|
||||
* @return 0 on success, negative error code otherwise
|
||||
*/
|
||||
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]);
|
||||
|
||||
/**
|
||||
* @brief HMAC-SHA256 with single data input (convenience wrapper)
|
||||
*/
|
||||
static inline int bt_mesh_sha256_hmac_one(const uint8_t key[32], const void *m,
|
||||
size_t len, uint8_t mac[32])
|
||||
{
|
||||
struct bt_mesh_sg sg = { m, len };
|
||||
return bt_mesh_sha256_hmac_raw_key(key, &sg, 1, mac);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* Internal Low-Level ECC APIs
|
||||
* ============================================================================
|
||||
* These functions operate in the crypto module's native byte order.
|
||||
* Do NOT call these directly - use the public wrapper functions below.
|
||||
*/
|
||||
const uint8_t *bt_mesh_pub_key_get_raw(void);
|
||||
void bt_mesh_set_private_key_raw(const uint8_t pri_key[32]);
|
||||
bool bt_mesh_check_public_key_raw(const uint8_t key[64]);
|
||||
int bt_mesh_dhkey_gen_raw(const uint8_t *pub_key, const uint8_t *priv_key,
|
||||
uint8_t *dhkey);
|
||||
|
||||
/* ============================================================================
|
||||
* Public ECC APIs (with automatic endianness conversion)
|
||||
* ============================================================================
|
||||
* All public key and DHKey parameters use big-endian format (BLE Mesh protocol).
|
||||
* For little-endian crypto modules, conversion is handled automatically.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Generate a new ECC public key pair
|
||||
*
|
||||
* Generates a new P-256 key pair for ECDH operations.
|
||||
* The private key is stored internally.
|
||||
*
|
||||
* @return 0 on success, negative error code otherwise
|
||||
*/
|
||||
int bt_mesh_pub_key_gen(void);
|
||||
|
||||
/**
|
||||
* @brief Get the current public key (raw pointer)
|
||||
*
|
||||
* Returns pointer to the internal public key storage.
|
||||
* Note: The byte order depends on crypto module's native format.
|
||||
* Use bt_mesh_pub_key_copy() to get big-endian format.
|
||||
*
|
||||
* @return Pointer to 64-byte public key, or NULL if not ready
|
||||
*/
|
||||
#define bt_mesh_pub_key_get() bt_mesh_pub_key_get_raw()
|
||||
|
||||
/**
|
||||
* @brief Copy current public key to user buffer (big-endian output)
|
||||
*
|
||||
* Copies the public key to the provided buffer, converting to big-endian
|
||||
* format (BLE Mesh protocol format) if necessary.
|
||||
*
|
||||
* @param buf 64-byte output buffer for public key (big-endian output)
|
||||
*
|
||||
* @return 0 on success, -EAGAIN if public key not ready
|
||||
*/
|
||||
static inline int bt_mesh_pub_key_copy(uint8_t *buf)
|
||||
{
|
||||
const uint8_t *key = bt_mesh_pub_key_get_raw();
|
||||
|
||||
if (key == NULL || buf == NULL) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
#if (BT_MESH_CRYPTO_ENDIAN == BT_MESH_CRYPTO_ENDIAN_LITTLE)
|
||||
/* Swap X and Y coordinates separately to big-endian */
|
||||
sys_memcpy_swap(buf, key, 32);
|
||||
sys_memcpy_swap(buf + 32, key + 32, 32);
|
||||
#else
|
||||
memcpy(buf, key, PUB_KEY_SIZE);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set a custom private key
|
||||
*
|
||||
* @param pri_key 32-byte private key (big-endian, BLE Mesh format)
|
||||
*/
|
||||
static inline void bt_mesh_set_private_key(const uint8_t pri_key[32])
|
||||
{
|
||||
CRYPTO_PARAM_DECLARE(pri_key, PRIV_KEY_SIZE);
|
||||
CRYPTO_PARAM_IN(pri_key, PRIV_KEY_SIZE);
|
||||
bt_mesh_set_private_key_raw(CRYPTO_PARAM(pri_key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if a public key is valid
|
||||
*
|
||||
* Verifies that the given public key is a valid point on the P-256 curve.
|
||||
*
|
||||
* @param key 64-byte public key (X || Y, big-endian, BLE Mesh format)
|
||||
*
|
||||
* @return true if valid, false otherwise
|
||||
*/
|
||||
static inline bool bt_mesh_check_public_key(const uint8_t key[64])
|
||||
{
|
||||
CRYPTO_PARAM_DECLARE(key, PUB_KEY_SIZE);
|
||||
CRYPTO_PARAM_PUBKEY_IN(key);
|
||||
return bt_mesh_check_public_key_raw(CRYPTO_PARAM(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate ECDH shared secret (DHKey)
|
||||
*
|
||||
* @param pub_key 64-byte remote public key (X || Y, big-endian, BLE Mesh format)
|
||||
* @param priv_key 32-byte private key (NULL to use internal key)
|
||||
* @param dhkey 32-byte output buffer for shared secret (big-endian output)
|
||||
*
|
||||
* @return 0 on success, negative error code otherwise
|
||||
*/
|
||||
static inline int bt_mesh_dhkey_gen(const uint8_t *pub_key, const uint8_t *priv_key,
|
||||
uint8_t *dhkey)
|
||||
{
|
||||
int rc;
|
||||
CRYPTO_PARAM_DECLARE(pub_key, PUB_KEY_SIZE);
|
||||
CRYPTO_PARAM_DECLARE(priv_key, PRIV_KEY_SIZE);
|
||||
CRYPTO_PARAM_DECLARE(dhkey, DH_KEY_SIZE);
|
||||
|
||||
/* Public key: swap X and Y coordinates separately */
|
||||
CRYPTO_PARAM_PUBKEY_IN(pub_key);
|
||||
if (priv_key != NULL) {
|
||||
CRYPTO_PARAM_IN(priv_key, PRIV_KEY_SIZE);
|
||||
}
|
||||
|
||||
rc = bt_mesh_dhkey_gen_raw(CRYPTO_PARAM(pub_key),
|
||||
priv_key ? CRYPTO_PARAM(priv_key) : NULL,
|
||||
CRYPTO_PARAM(dhkey));
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
CRYPTO_PARAM_OUT(dhkey, DH_KEY_SIZE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define bt_mesh_dh_key_gen(pubkey, dhkey) bt_mesh_dhkey_gen(pubkey, NULL, dhkey)
|
||||
|
||||
/**
|
||||
* @brief Import a raw key into mesh key structure
|
||||
*
|
||||
* @param type Key type (determines key usage)
|
||||
* @param in 16-byte raw key
|
||||
* @param out Output mesh key structure
|
||||
*
|
||||
* @return 0 on success, negative error code otherwise
|
||||
*/
|
||||
int bt_mesh_key_import(enum bt_mesh_key_type type, const uint8_t in[16],
|
||||
struct bt_mesh_key *out);
|
||||
|
||||
/**
|
||||
* @brief Export raw key bytes from mesh key structure
|
||||
*
|
||||
* @param out 16-byte output buffer
|
||||
* @param in Input mesh key structure
|
||||
*
|
||||
* @return 0 on success, negative error code otherwise
|
||||
*/
|
||||
int bt_mesh_key_export(uint8_t out[16], const struct bt_mesh_key *in);
|
||||
|
||||
/**
|
||||
* @brief Assign (copy) a mesh key
|
||||
*
|
||||
* @param dst Destination mesh key
|
||||
* @param src Source mesh key
|
||||
*/
|
||||
void bt_mesh_key_assign(struct bt_mesh_key *dst, const struct bt_mesh_key *src);
|
||||
|
||||
/**
|
||||
* @brief Destroy a mesh key
|
||||
*
|
||||
* Releases any resources associated with the key.
|
||||
*
|
||||
* @param key Mesh key to destroy
|
||||
*
|
||||
* @return 0 on success, negative error code otherwise
|
||||
*/
|
||||
int bt_mesh_key_destroy(const struct bt_mesh_key *key);
|
||||
|
||||
/**
|
||||
* @brief Compare a raw key with a mesh key
|
||||
*
|
||||
* @param raw_key 16-byte raw key
|
||||
* @param key Mesh key structure to compare
|
||||
*
|
||||
* @return 0 if equal, non-zero otherwise
|
||||
*/
|
||||
int bt_mesh_key_compare(const uint8_t raw_key[16], const struct bt_mesh_key *key);
|
||||
|
||||
/**
|
||||
* @brief AES encryption (little-endian byte order)
|
||||
*
|
||||
* The key and plaintext are byte-swapped before encryption,
|
||||
* and the result is byte-swapped after encryption.
|
||||
*
|
||||
* @param key 16-byte key
|
||||
* @param plaintext 16-byte plaintext
|
||||
* @param enc_data 16-byte output buffer
|
||||
*
|
||||
* @return 0 on success, negative error code otherwise
|
||||
*/
|
||||
int bt_mesh_encrypt_le(const uint8_t key[16], const uint8_t plaintext[16],
|
||||
uint8_t enc_data[16]);
|
||||
|
||||
/**
|
||||
* @brief AES encryption (big-endian byte order)
|
||||
*
|
||||
* Standard AES-ECB encryption without byte swapping.
|
||||
*
|
||||
* @param key 16-byte key
|
||||
* @param plaintext 16-byte plaintext
|
||||
* @param enc_data 16-byte output buffer
|
||||
*
|
||||
* @return 0 on success, negative error code otherwise
|
||||
*/
|
||||
int bt_mesh_encrypt_be(const uint8_t key[16], const uint8_t plaintext[16],
|
||||
uint8_t enc_data[16]);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _BLE_MESH_CRYPTO_H_ */
|
||||
@@ -17,14 +17,6 @@
|
||||
#include "p_256_ecc_pp.h"
|
||||
#include "osi/future.h"
|
||||
#include "device/controller.h"
|
||||
|
||||
#if CONFIG_MBEDTLS_HARDWARE_AES
|
||||
#include "psa/crypto.h"
|
||||
#endif
|
||||
|
||||
#include <tinycrypt/aes.h>
|
||||
#include <tinycrypt/constants.h>
|
||||
|
||||
#include "mesh/hci.h"
|
||||
#include "mesh/adapter.h"
|
||||
#include "mesh/common.h"
|
||||
@@ -56,10 +48,6 @@ struct bt_mesh_dev bt_mesh_dev;
|
||||
*/
|
||||
#define BLE_MESH_DEV 0
|
||||
|
||||
/* P-256 Variables */
|
||||
static uint8_t bt_mesh_public_key[64];
|
||||
static uint8_t bt_mesh_private_key[32];
|
||||
|
||||
/* Scan related functions */
|
||||
static bt_mesh_scan_cb_t *bt_mesh_scan_dev_found_cb;
|
||||
|
||||
@@ -2481,228 +2469,9 @@ void bt_mesh_gatt_deinit(void)
|
||||
|
||||
void bt_mesh_adapt_init(void)
|
||||
{
|
||||
/* initialization of P-256 parameters */
|
||||
p_256_init_curve(KEY_LENGTH_DWORDS_P256);
|
||||
|
||||
/* Set "bt_mesh_dev.flags" to 0 (only the "BLE_MESH_DEV_HAS_PUB_KEY"
|
||||
* flag is used) here, because we need to make sure each time after
|
||||
* the private key is initialized, a corresponding public key must
|
||||
* be generated.
|
||||
*/
|
||||
/* Use unified crypto module initialization */
|
||||
bt_mesh_crypto_init();
|
||||
bt_mesh_atomic_set(bt_mesh_dev.flags, 0);
|
||||
bt_mesh_rand(bt_mesh_private_key, sizeof(bt_mesh_private_key));
|
||||
}
|
||||
|
||||
void bt_mesh_set_private_key(const uint8_t pri_key[32])
|
||||
{
|
||||
memcpy(bt_mesh_private_key, pri_key, 32);
|
||||
}
|
||||
|
||||
const uint8_t *bt_mesh_pub_key_get(void)
|
||||
{
|
||||
uint8_t private_key[32] = {0};
|
||||
Point public_key = {0};
|
||||
|
||||
if (bt_mesh_atomic_test_bit(bt_mesh_dev.flags, BLE_MESH_DEV_HAS_PUB_KEY)) {
|
||||
return bt_mesh_public_key;
|
||||
}
|
||||
|
||||
/* BLE Mesh BQB test case MESH/NODE/PROV/UPD/BV-12-C requires
|
||||
* different public key for each provisioning procedure.
|
||||
* Note: if enabled, when Provisioner provision multiple devices
|
||||
* at the same time, this may cause invalid confirmation value.
|
||||
*
|
||||
* Use the following code for generating different private key
|
||||
* for each provisioning procedure.
|
||||
*
|
||||
* if (bt_mesh_rand(bt_mesh_private_key, BT_OCTET32_LEN)) {
|
||||
* BT_ERR("%s, Unable to generate bt_mesh_private_key", __func__);
|
||||
* return NULL;
|
||||
* }
|
||||
*/
|
||||
|
||||
memcpy(private_key, bt_mesh_private_key, BT_OCTET32_LEN);
|
||||
ECC_PointMult(&public_key, &(curve_p256.G), (DWORD *)private_key, KEY_LENGTH_DWORDS_P256);
|
||||
|
||||
memcpy(bt_mesh_public_key, public_key.x, BT_OCTET32_LEN);
|
||||
memcpy(bt_mesh_public_key + BT_OCTET32_LEN, public_key.y, BT_OCTET32_LEN);
|
||||
|
||||
bt_mesh_atomic_set_bit(bt_mesh_dev.flags, BLE_MESH_DEV_HAS_PUB_KEY);
|
||||
|
||||
BT_DBG("Public Key %s", bt_hex(bt_mesh_public_key, sizeof(bt_mesh_public_key)));
|
||||
|
||||
return bt_mesh_public_key;
|
||||
}
|
||||
|
||||
bool bt_mesh_check_public_key(const uint8_t key[64])
|
||||
{
|
||||
struct p256_pub_key {
|
||||
uint8_t x[32];
|
||||
uint8_t y[32];
|
||||
} check = {0};
|
||||
|
||||
sys_memcpy_swap(check.x, key, 32);
|
||||
sys_memcpy_swap(check.y, key + 32, 32);
|
||||
|
||||
return ECC_CheckPointIsInElliCur_P256((Point *)&check);
|
||||
}
|
||||
|
||||
int bt_mesh_dh_key_gen(const uint8_t remote_pub_key[64], uint8_t dhkey[32])
|
||||
{
|
||||
uint8_t private_key[32] = {0};
|
||||
Point peer_pub_key = {0};
|
||||
Point new_pub_key = {0};
|
||||
|
||||
BT_DBG("private key = %s", bt_hex(bt_mesh_private_key, BT_OCTET32_LEN));
|
||||
|
||||
memcpy(private_key, bt_mesh_private_key, BT_OCTET32_LEN);
|
||||
memcpy(peer_pub_key.x, remote_pub_key, BT_OCTET32_LEN);
|
||||
memcpy(peer_pub_key.y, &remote_pub_key[BT_OCTET32_LEN], BT_OCTET32_LEN);
|
||||
|
||||
BT_DBG("remote public key x = %s", bt_hex(peer_pub_key.x, BT_OCTET32_LEN));
|
||||
BT_DBG("remote public key y = %s", bt_hex(peer_pub_key.y, BT_OCTET32_LEN));
|
||||
|
||||
ECC_PointMult(&new_pub_key, &peer_pub_key, (DWORD *)private_key, KEY_LENGTH_DWORDS_P256);
|
||||
|
||||
BT_DBG("new public key x = %s", bt_hex(new_pub_key.x, 32));
|
||||
BT_DBG("new public key y = %s", bt_hex(new_pub_key.y, 32));
|
||||
|
||||
memcpy(dhkey, new_pub_key.x, 32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_mesh_encrypt_le(const uint8_t key[16], const uint8_t plaintext[16],
|
||||
uint8_t enc_data[16])
|
||||
{
|
||||
uint8_t tmp[16] = {0};
|
||||
|
||||
BT_DBG("key %s plaintext %s", bt_hex(key, 16), bt_hex(plaintext, 16));
|
||||
|
||||
#if CONFIG_MBEDTLS_HARDWARE_AES
|
||||
sys_memcpy_swap(tmp, key, 16);
|
||||
psa_status_t status;
|
||||
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_key_id_t key_id = 0;
|
||||
psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT;
|
||||
psa_algorithm_t alg = PSA_ALG_ECB_NO_PADDING;
|
||||
psa_set_key_algorithm(&attributes, alg);
|
||||
psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
|
||||
psa_set_key_bits(&attributes, 128);
|
||||
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT);
|
||||
|
||||
status = psa_import_key(&attributes, tmp, 16, &key_id);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("psa_import_key failed with status %d", status);
|
||||
return -EINVAL;
|
||||
}
|
||||
psa_reset_key_attributes(&attributes);
|
||||
|
||||
status = psa_cipher_encrypt_setup(&operation, key_id, alg);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("psa_cipher_encrypt_setup failed with status %d", status);
|
||||
psa_destroy_key(key_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
size_t output_length = 0;
|
||||
status = psa_cipher_update(&operation, plaintext, 16, enc_data, 16, &output_length);
|
||||
if (status != PSA_SUCCESS || output_length != 16) {
|
||||
BT_ERR("psa_cipher_update failed with status %d", status);
|
||||
psa_cipher_abort(&operation);
|
||||
psa_destroy_key(key_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
status = psa_cipher_finish(&operation, enc_data + output_length, 16 - output_length, &output_length);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("psa_cipher_finish failed with status %d", status);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
psa_destroy_key(key_id);
|
||||
|
||||
#else /* CONFIG_MBEDTLS_HARDWARE_AES */
|
||||
struct tc_aes_key_sched_struct s = {0};
|
||||
|
||||
sys_memcpy_swap(tmp, key, 16);
|
||||
|
||||
if (tc_aes128_set_encrypt_key(&s, tmp) == TC_CRYPTO_FAIL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sys_memcpy_swap(tmp, plaintext, 16);
|
||||
|
||||
if (tc_aes_encrypt(enc_data, tmp, &s) == TC_CRYPTO_FAIL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif /* CONFIG_MBEDTLS_HARDWARE_AES */
|
||||
|
||||
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])
|
||||
{
|
||||
BT_DBG("key %s plaintext %s", bt_hex(key, 16), bt_hex(plaintext, 16));
|
||||
|
||||
#if CONFIG_MBEDTLS_HARDWARE_AES
|
||||
psa_status_t status;
|
||||
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_key_id_t key_id = 0;
|
||||
psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT;
|
||||
psa_algorithm_t alg = PSA_ALG_ECB_NO_PADDING;
|
||||
psa_set_key_algorithm(&attributes, alg);
|
||||
psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
|
||||
psa_set_key_bits(&attributes, 128);
|
||||
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT);
|
||||
|
||||
status = psa_import_key(&attributes, key, 16, &key_id);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("psa_import_key failed with status %d", status);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
status = psa_cipher_encrypt_setup(&operation, key_id, alg);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("psa_cipher_encrypt_setup failed with status %d", status);
|
||||
psa_destroy_key(key_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
size_t output_length = 0;
|
||||
status = psa_cipher_update(&operation, plaintext, 16, enc_data, 16, &output_length);
|
||||
if (status != PSA_SUCCESS || output_length != 16) {
|
||||
BT_ERR("psa_cipher_update failed with status %d", status);
|
||||
psa_cipher_abort(&operation);
|
||||
psa_destroy_key(key_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
status = psa_cipher_finish(&operation, enc_data + output_length, 16 - output_length, &output_length);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("psa_cipher_finish failed with status %d", status);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
psa_destroy_key(key_id);
|
||||
#else /* CONFIG_MBEDTLS_HARDWARE_AES */
|
||||
struct tc_aes_key_sched_struct s = {0};
|
||||
|
||||
if (tc_aes128_set_encrypt_key(&s, key) == TC_CRYPTO_FAIL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (tc_aes_encrypt(enc_data, plaintext, &s) == TC_CRYPTO_FAIL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif /* CONFIG_MBEDTLS_HARDWARE_AES */
|
||||
|
||||
BT_DBG("enc_data %s", bt_hex(enc_data, 16));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_BLE_MESH_USE_DUPLICATE_SCAN
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017 Intel Corporation
|
||||
* SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileContributor: 2018-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -11,12 +11,6 @@
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <tinycrypt/aes.h>
|
||||
#include <tinycrypt/constants.h>
|
||||
#include <tinycrypt/cmac_mode.h>
|
||||
#include <tinycrypt/hmac.h>
|
||||
#include <tinycrypt/sha256.h>
|
||||
|
||||
#include "crypto.h"
|
||||
#include "mesh/config.h"
|
||||
#include "mesh/common.h"
|
||||
@@ -29,29 +23,7 @@
|
||||
#define NET_MIC_LEN(pdu) (((pdu)[1] & 0x80) ? 8 : 4)
|
||||
#define APP_MIC_LEN(aszmic) ((aszmic) ? 8 : 4)
|
||||
|
||||
int bt_mesh_aes_cmac(const uint8_t key[16], struct bt_mesh_sg *sg,
|
||||
size_t sg_len, uint8_t mac[16])
|
||||
{
|
||||
struct tc_aes_key_sched_struct sched = {0};
|
||||
struct tc_cmac_struct state = {0};
|
||||
|
||||
if (tc_cmac_setup(&state, key, &sched) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (; sg_len; sg_len--, sg++) {
|
||||
if (tc_cmac_update(&state, sg->data,
|
||||
sg->len) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (tc_cmac_final(mac, &state) == TC_CRYPTO_FAIL) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* bt_mesh_aes_cmac is now mapped to bt_mesh_aes_cmac_raw_key from common crypto module */
|
||||
|
||||
int bt_mesh_k1(const uint8_t *ikm, size_t ikm_len, const uint8_t salt[16],
|
||||
const char *info, uint8_t okm[16])
|
||||
@@ -199,326 +171,9 @@ int bt_mesh_id128(const uint8_t n[16], const char *s, uint8_t out[16])
|
||||
return bt_mesh_k1(n, 16, salt, id128, out);
|
||||
}
|
||||
|
||||
static int bt_mesh_ccm_decrypt(const uint8_t key[16], uint8_t nonce[13],
|
||||
const uint8_t *enc_msg, size_t msg_len,
|
||||
const uint8_t *aad, size_t aad_len,
|
||||
uint8_t *out_msg, size_t mic_size)
|
||||
{
|
||||
uint8_t msg[16] = {0}, pmsg[16] = {0}, cmic[16] = {0},
|
||||
cmsg[16] = {0}, Xn[16] = {0}, mic[16] = {0};
|
||||
uint16_t last_blk = 0U, blk_cnt = 0U;
|
||||
size_t i = 0U, j = 0U;
|
||||
int err = 0;
|
||||
|
||||
if (msg_len < 1 || aad_len >= 0xff00) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */
|
||||
pmsg[0] = 0x01;
|
||||
memcpy(pmsg + 1, nonce, 13);
|
||||
sys_put_be16(0x0000, pmsg + 14);
|
||||
|
||||
err = bt_mesh_encrypt_be(key, pmsg, cmic);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* X_0 = e(AppKey, 0x09 || nonce || length) */
|
||||
if (mic_size == sizeof(uint64_t)) {
|
||||
pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00);
|
||||
} else {
|
||||
pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00);
|
||||
}
|
||||
|
||||
memcpy(pmsg + 1, nonce, 13);
|
||||
sys_put_be16(msg_len, pmsg + 14);
|
||||
|
||||
err = bt_mesh_encrypt_be(key, pmsg, Xn);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* If AAD is being used to authenticate, include it here */
|
||||
if (aad_len) {
|
||||
sys_put_be16(aad_len, pmsg);
|
||||
|
||||
for (i = 0; i < sizeof(uint16_t); i++) {
|
||||
pmsg[i] = Xn[i] ^ pmsg[i];
|
||||
}
|
||||
|
||||
j = 0;
|
||||
aad_len += sizeof(uint16_t);
|
||||
while (aad_len > 16) {
|
||||
do {
|
||||
pmsg[i] = Xn[i] ^ aad[j];
|
||||
i++, j++;
|
||||
} while (i < 16);
|
||||
|
||||
aad_len -= 16;
|
||||
i = 0;
|
||||
|
||||
err = bt_mesh_encrypt_be(key, pmsg, Xn);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < aad_len; i++, j++) {
|
||||
pmsg[i] = Xn[i] ^ aad[j];
|
||||
}
|
||||
|
||||
for (i = aad_len; i < 16; i++) {
|
||||
pmsg[i] = Xn[i];
|
||||
}
|
||||
|
||||
err = bt_mesh_encrypt_be(key, pmsg, Xn);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
last_blk = msg_len % 16;
|
||||
blk_cnt = (msg_len + 15) / 16;
|
||||
if (!last_blk) {
|
||||
last_blk = 16U;
|
||||
}
|
||||
|
||||
for (j = 0; j < blk_cnt; j++) {
|
||||
if (j + 1 == blk_cnt) {
|
||||
/* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
|
||||
pmsg[0] = 0x01;
|
||||
memcpy(pmsg + 1, nonce, 13);
|
||||
sys_put_be16(j + 1, pmsg + 14);
|
||||
|
||||
err = bt_mesh_encrypt_be(key, pmsg, cmsg);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Encrypted = Payload[0-15] ^ C_1 */
|
||||
for (i = 0; i < last_blk; i++) {
|
||||
msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i];
|
||||
}
|
||||
|
||||
memcpy(out_msg + (j * 16), msg, last_blk);
|
||||
|
||||
/* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
|
||||
for (i = 0; i < last_blk; i++) {
|
||||
pmsg[i] = Xn[i] ^ msg[i];
|
||||
}
|
||||
|
||||
for (i = last_blk; i < 16; i++) {
|
||||
pmsg[i] = Xn[i] ^ 0x00;
|
||||
}
|
||||
|
||||
err = bt_mesh_encrypt_be(key, pmsg, Xn);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* MIC = C_mic ^ X_1 */
|
||||
for (i = 0; i < sizeof(mic); i++) {
|
||||
mic[i] = cmic[i] ^ Xn[i];
|
||||
}
|
||||
} else {
|
||||
/* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
|
||||
pmsg[0] = 0x01;
|
||||
memcpy(pmsg + 1, nonce, 13);
|
||||
sys_put_be16(j + 1, pmsg + 14);
|
||||
|
||||
err = bt_mesh_encrypt_be(key, pmsg, cmsg);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Encrypted = Payload[0-15] ^ C_1 */
|
||||
for (i = 0; i < 16; i++) {
|
||||
msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i];
|
||||
}
|
||||
|
||||
memcpy(out_msg + (j * 16), msg, 16);
|
||||
|
||||
/* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
|
||||
for (i = 0; i < 16; i++) {
|
||||
pmsg[i] = Xn[i] ^ msg[i];
|
||||
}
|
||||
|
||||
err = bt_mesh_encrypt_be(key, pmsg, Xn);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (memcmp(mic, enc_msg + msg_len, mic_size)) {
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bt_mesh_ccm_encrypt(const uint8_t key[16], uint8_t nonce[13],
|
||||
const uint8_t *msg, size_t msg_len,
|
||||
const uint8_t *aad, size_t aad_len,
|
||||
uint8_t *out_msg, size_t mic_size)
|
||||
{
|
||||
uint8_t pmsg[16] = {0}, cmic[16] = {0}, cmsg[16] = {0},
|
||||
mic[16] = {0}, Xn[16] = {0};
|
||||
uint16_t blk_cnt = 0U, last_blk = 0U;
|
||||
size_t i = 0U, j = 0U;
|
||||
int err = 0;
|
||||
|
||||
BT_DBG("CCMEncrypt");
|
||||
BT_DBG("Key %s", bt_hex(key, 16));
|
||||
BT_DBG("Nonce %s", bt_hex(nonce, 13));
|
||||
BT_DBG("Len %u: %s", msg_len, bt_hex(msg, msg_len));
|
||||
BT_DBG("AADLen %u MicSize %u", aad_len, mic_size);
|
||||
|
||||
/* Unsupported AAD size */
|
||||
if (aad_len >= 0xff00) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */
|
||||
pmsg[0] = 0x01;
|
||||
memcpy(pmsg + 1, nonce, 13);
|
||||
sys_put_be16(0x0000, pmsg + 14);
|
||||
|
||||
err = bt_mesh_encrypt_be(key, pmsg, cmic);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* X_0 = e(AppKey, 0x09 || nonce || length) */
|
||||
if (mic_size == sizeof(uint64_t)) {
|
||||
pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00);
|
||||
} else {
|
||||
pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00);
|
||||
}
|
||||
|
||||
memcpy(pmsg + 1, nonce, 13);
|
||||
sys_put_be16(msg_len, pmsg + 14);
|
||||
|
||||
err = bt_mesh_encrypt_be(key, pmsg, Xn);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* If AAD is being used to authenticate, include it here */
|
||||
if (aad_len) {
|
||||
sys_put_be16(aad_len, pmsg);
|
||||
|
||||
for (i = 0; i < sizeof(uint16_t); i++) {
|
||||
pmsg[i] = Xn[i] ^ pmsg[i];
|
||||
}
|
||||
|
||||
j = 0;
|
||||
aad_len += sizeof(uint16_t);
|
||||
while (aad_len > 16) {
|
||||
do {
|
||||
pmsg[i] = Xn[i] ^ aad[j];
|
||||
i++, j++;
|
||||
} while (i < 16);
|
||||
|
||||
aad_len -= 16;
|
||||
i = 0;
|
||||
|
||||
err = bt_mesh_encrypt_be(key, pmsg, Xn);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < aad_len; i++, j++) {
|
||||
pmsg[i] = Xn[i] ^ aad[j];
|
||||
}
|
||||
|
||||
for (i = aad_len; i < 16; i++) {
|
||||
pmsg[i] = Xn[i];
|
||||
}
|
||||
|
||||
err = bt_mesh_encrypt_be(key, pmsg, Xn);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
last_blk = msg_len % 16;
|
||||
blk_cnt = (msg_len + 15) / 16;
|
||||
if (!last_blk) {
|
||||
last_blk = 16U;
|
||||
}
|
||||
|
||||
for (j = 0; j < blk_cnt; j++) {
|
||||
if (j + 1 == blk_cnt) {
|
||||
/* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
|
||||
for (i = 0; i < last_blk; i++) {
|
||||
pmsg[i] = Xn[i] ^ msg[(j * 16) + i];
|
||||
}
|
||||
for (i = last_blk; i < 16; i++) {
|
||||
pmsg[i] = Xn[i] ^ 0x00;
|
||||
}
|
||||
|
||||
err = bt_mesh_encrypt_be(key, pmsg, Xn);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* MIC = C_mic ^ X_1 */
|
||||
for (i = 0; i < sizeof(mic); i++) {
|
||||
mic[i] = cmic[i] ^ Xn[i];
|
||||
}
|
||||
|
||||
/* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
|
||||
pmsg[0] = 0x01;
|
||||
memcpy(pmsg + 1, nonce, 13);
|
||||
sys_put_be16(j + 1, pmsg + 14);
|
||||
|
||||
err = bt_mesh_encrypt_be(key, pmsg, cmsg);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Encrypted = Payload[0-15] ^ C_1 */
|
||||
for (i = 0; i < last_blk; i++) {
|
||||
out_msg[(j * 16) + i] =
|
||||
msg[(j * 16) + i] ^ cmsg[i];
|
||||
}
|
||||
} else {
|
||||
/* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
|
||||
for (i = 0; i < 16; i++) {
|
||||
pmsg[i] = Xn[i] ^ msg[(j * 16) + i];
|
||||
}
|
||||
|
||||
err = bt_mesh_encrypt_be(key, pmsg, Xn);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
|
||||
pmsg[0] = 0x01;
|
||||
memcpy(pmsg + 1, nonce, 13);
|
||||
sys_put_be16(j + 1, pmsg + 14);
|
||||
|
||||
err = bt_mesh_encrypt_be(key, pmsg, cmsg);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Encrypted = Payload[0-15] ^ C_N */
|
||||
for (i = 0; i < 16; i++) {
|
||||
out_msg[(j * 16) + i] =
|
||||
msg[(j * 16) + i] ^ cmsg[i];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(out_msg + msg_len, mic, mic_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* Use the unified crypto module for CCM operations */
|
||||
#define bt_mesh_ccm_decrypt bt_mesh_ccm_decrypt_raw_key
|
||||
#define bt_mesh_ccm_encrypt bt_mesh_ccm_encrypt_raw_key
|
||||
|
||||
#if CONFIG_BLE_MESH_PROXY
|
||||
static void create_proxy_nonce(uint8_t nonce[13], const uint8_t *pdu,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017 Intel Corporation
|
||||
* SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileContributor: 2018-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -12,26 +12,17 @@
|
||||
|
||||
#include <string.h>
|
||||
#include "mesh/buf.h"
|
||||
#include "mesh/crypto.h" /* Include common crypto API */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct bt_mesh_sg {
|
||||
const void *data;
|
||||
size_t len;
|
||||
};
|
||||
/* Map bt_mesh_aes_cmac to bt_mesh_aes_cmac_raw_key from common crypto module.
|
||||
* struct bt_mesh_sg is defined in mesh/crypto.h */
|
||||
#define bt_mesh_aes_cmac bt_mesh_aes_cmac_raw_key
|
||||
|
||||
int bt_mesh_aes_cmac(const uint8_t key[16], struct bt_mesh_sg *sg,
|
||||
size_t sg_len, uint8_t mac[16]);
|
||||
|
||||
static inline int bt_mesh_aes_cmac_one(const uint8_t key[16], const void *m,
|
||||
size_t len, uint8_t mac[16])
|
||||
{
|
||||
struct bt_mesh_sg sg = { m, len };
|
||||
|
||||
return bt_mesh_aes_cmac(key, &sg, 1, mac);
|
||||
}
|
||||
/* bt_mesh_aes_cmac_one is defined as inline in mesh/crypto.h */
|
||||
|
||||
static inline bool bt_mesh_s1(const char *m, uint8_t salt[16])
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "mesh/utils.h"
|
||||
#include "mesh/uuid.h"
|
||||
#include "mesh/buf.h"
|
||||
#include "mesh/crypto.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -932,20 +933,6 @@ void bt_mesh_gatt_deinit(void);
|
||||
|
||||
void bt_mesh_adapt_init(void);
|
||||
|
||||
void bt_mesh_set_private_key(const uint8_t pri_key[32]);
|
||||
|
||||
const uint8_t *bt_mesh_pub_key_get(void);
|
||||
|
||||
bool bt_mesh_check_public_key(const uint8_t key[64]);
|
||||
|
||||
int bt_mesh_dh_key_gen(const uint8_t remote_pub_key[64], uint8_t dhkey[32]);
|
||||
|
||||
int bt_mesh_encrypt_le(const uint8_t key[16], const uint8_t plaintext[16],
|
||||
uint8_t enc_data[16]);
|
||||
|
||||
int bt_mesh_encrypt_be(const uint8_t key[16], const uint8_t plaintext[16],
|
||||
uint8_t enc_data[16]);
|
||||
|
||||
enum {
|
||||
BLE_MESH_EXCEP_LIST_SUB_CODE_ADD = 0,
|
||||
BLE_MESH_EXCEP_LIST_SUB_CODE_REMOVE,
|
||||
|
||||
@@ -10,19 +10,12 @@
|
||||
|
||||
#include "btc/btc_task.h"
|
||||
#include "osi/alarm.h"
|
||||
|
||||
#include "psa/crypto.h"
|
||||
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/ble_uuid.h"
|
||||
#include "host/ble_att.h"
|
||||
#include "host/ble_gatt.h"
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "services/gatt/ble_svc_gatt.h"
|
||||
|
||||
#include <tinycrypt/aes.h>
|
||||
#include <tinycrypt/constants.h>
|
||||
|
||||
#include "mesh/hci.h"
|
||||
#include "mesh/common.h"
|
||||
#include "prov_pvnr.h"
|
||||
@@ -64,10 +57,6 @@
|
||||
static uint16_t proxy_svc_start_handle, prov_svc_start_handle;
|
||||
struct bt_mesh_dev bt_mesh_dev;
|
||||
|
||||
/* P-256 Variables */
|
||||
static uint8_t bt_mesh_public_key[64];
|
||||
static uint8_t bt_mesh_private_key[32];
|
||||
|
||||
/* Scan related functions */
|
||||
static bt_mesh_scan_cb_t *bt_mesh_scan_dev_found_cb;
|
||||
#if CONFIG_BLE_MESH_NODE
|
||||
@@ -2605,232 +2594,11 @@ void bt_mesh_gatt_deinit(void)
|
||||
}
|
||||
#endif /* CONFIG_BLE_MESH_DEINIT */
|
||||
|
||||
void ble_sm_alg_ecc_init(void);
|
||||
|
||||
void bt_mesh_adapt_init(void)
|
||||
{
|
||||
/* initialization of P-256 parameters */
|
||||
ble_sm_alg_ecc_init();
|
||||
|
||||
/* Set "bt_mesh_dev.flags" to 0 (only the "BLE_MESH_DEV_HAS_PUB_KEY"
|
||||
* flag is used) here, because we need to make sure each time after
|
||||
* the private key is initialized, a corresponding public key must
|
||||
* be generated.
|
||||
*/
|
||||
/* Use unified crypto module initialization */
|
||||
bt_mesh_crypto_init();
|
||||
bt_mesh_atomic_set(bt_mesh_dev.flags, 0);
|
||||
bt_mesh_rand(bt_mesh_private_key, sizeof(bt_mesh_private_key));
|
||||
}
|
||||
|
||||
void bt_mesh_set_private_key(const uint8_t pri_key[32])
|
||||
{
|
||||
memcpy(bt_mesh_private_key, pri_key, 32);
|
||||
}
|
||||
|
||||
int ble_sm_alg_gen_key_pair(uint8_t *pub, uint8_t *priv);
|
||||
|
||||
const uint8_t *bt_mesh_pub_key_get(void)
|
||||
{
|
||||
uint8_t pri_key[32] = {0};
|
||||
|
||||
#if 1
|
||||
if (bt_mesh_atomic_test_bit(bt_mesh_dev.flags, BLE_MESH_DEV_HAS_PUB_KEY)) {
|
||||
return bt_mesh_public_key;
|
||||
}
|
||||
#else
|
||||
/* BLE Mesh BQB test case MESH/NODE/PROV/UPD/BV-12-C requires
|
||||
* different public key for each provisioning procedure.
|
||||
* Note: if enabled, when Provisioner provision multiple devices
|
||||
* at the same time, this may cause invalid confirmation value.
|
||||
*/
|
||||
if (bt_mesh_rand(bt_mesh_private_key, 32)) {
|
||||
BT_ERR("%s, Unable to generate bt_mesh_private_key", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
int rc = ble_sm_alg_gen_key_pair(bt_mesh_public_key, pri_key);
|
||||
if (rc != 0) {
|
||||
BT_ERR("Failed to generate the key pair");
|
||||
return NULL;
|
||||
}
|
||||
memcpy(bt_mesh_private_key, pri_key, 32);
|
||||
|
||||
bt_mesh_atomic_set_bit(bt_mesh_dev.flags, BLE_MESH_DEV_HAS_PUB_KEY);
|
||||
|
||||
BT_DBG("Public Key %s", bt_hex(bt_mesh_public_key, sizeof(bt_mesh_public_key)));
|
||||
|
||||
return bt_mesh_public_key;
|
||||
}
|
||||
|
||||
bool bt_mesh_check_public_key(const uint8_t key[64])
|
||||
{
|
||||
psa_status_t status = PSA_SUCCESS;
|
||||
|
||||
uint8_t pub[65] = {0};
|
||||
/* Hardcoded first byte of pub key for MBEDTLS_ECP_PF_UNCOMPRESSED */
|
||||
pub[0] = 0x04;
|
||||
memcpy(&pub[1], key, 64);
|
||||
|
||||
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_key_id_t key_id = 0;
|
||||
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_VERIFY_HASH);
|
||||
psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
|
||||
psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1));
|
||||
psa_set_key_bits(&attributes, 256);
|
||||
|
||||
status = psa_import_key(&attributes, pub, sizeof(pub), &key_id);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("Failed to import public key, status: %d", status);
|
||||
return false;
|
||||
}
|
||||
psa_reset_key_attributes(&attributes);
|
||||
psa_destroy_key(key_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ble_sm_alg_gen_dhkey(uint8_t *peer_pub_key_x, uint8_t *peer_pub_key_y,
|
||||
uint8_t *our_priv_key, uint8_t *out_dhkey);
|
||||
|
||||
int bt_mesh_dh_key_gen(const uint8_t remote_pub_key[64], uint8_t dhkey[32])
|
||||
{
|
||||
BT_DBG("private key = %s", bt_hex(bt_mesh_private_key, 32));
|
||||
|
||||
return ble_sm_alg_gen_dhkey((uint8_t *)&remote_pub_key[0], (uint8_t *)&remote_pub_key[32],
|
||||
bt_mesh_private_key, dhkey);
|
||||
}
|
||||
|
||||
int bt_mesh_encrypt_le(const uint8_t key[16], const uint8_t plaintext[16],
|
||||
uint8_t enc_data[16])
|
||||
{
|
||||
uint8_t tmp[16] = {0};
|
||||
|
||||
BT_DBG("key %s plaintext %s", bt_hex(key, 16), bt_hex(plaintext, 16));
|
||||
|
||||
#if CONFIG_MBEDTLS_HARDWARE_AES
|
||||
sys_memcpy_swap(tmp, key, 16);
|
||||
psa_status_t status;
|
||||
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_key_id_t key_id = 0;
|
||||
psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT;
|
||||
psa_algorithm_t alg = PSA_ALG_ECB_NO_PADDING;
|
||||
psa_set_key_algorithm(&attributes, alg);
|
||||
psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
|
||||
psa_set_key_bits(&attributes, 128);
|
||||
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT);
|
||||
|
||||
status = psa_import_key(&attributes, tmp, 16, &key_id);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("psa_import_key failed with status %d", status);
|
||||
return -EINVAL;
|
||||
}
|
||||
psa_reset_key_attributes(&attributes);
|
||||
|
||||
status = psa_cipher_encrypt_setup(&operation, key_id, alg);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("psa_cipher_encrypt_setup failed with status %d", status);
|
||||
psa_destroy_key(key_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
size_t output_length = 0;
|
||||
status = psa_cipher_update(&operation, plaintext, 16, enc_data, 16, &output_length);
|
||||
if (status != PSA_SUCCESS || output_length != 16) {
|
||||
BT_ERR("psa_cipher_update failed with status %d", status);
|
||||
psa_cipher_abort(&operation);
|
||||
psa_destroy_key(key_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
status = psa_cipher_finish(&operation, enc_data + output_length, 16 - output_length, &output_length);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("psa_cipher_finish failed with status %d", status);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
psa_destroy_key(key_id);
|
||||
#else /* CONFIG_MBEDTLS_HARDWARE_AES */
|
||||
struct tc_aes_key_sched_struct s = {0};
|
||||
|
||||
sys_memcpy_swap(tmp, key, 16);
|
||||
|
||||
if (tc_aes128_set_encrypt_key(&s, tmp) == TC_CRYPTO_FAIL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sys_memcpy_swap(tmp, plaintext, 16);
|
||||
|
||||
if (tc_aes_encrypt(enc_data, tmp, &s) == TC_CRYPTO_FAIL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif /* CONFIG_MBEDTLS_HARDWARE_AES */
|
||||
|
||||
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])
|
||||
{
|
||||
BT_DBG("key %s plaintext %s", bt_hex(key, 16), bt_hex(plaintext, 16));
|
||||
|
||||
#if CONFIG_MBEDTLS_HARDWARE_AES
|
||||
psa_status_t status;
|
||||
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_key_id_t key_id = 0;
|
||||
psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT;
|
||||
psa_algorithm_t alg = PSA_ALG_ECB_NO_PADDING;
|
||||
psa_set_key_algorithm(&attributes, alg);
|
||||
psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
|
||||
psa_set_key_bits(&attributes, 128);
|
||||
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT);
|
||||
|
||||
status = psa_import_key(&attributes, key, 16, &key_id);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("psa_import_key failed with status %d", status);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
status = psa_cipher_encrypt_setup(&operation, key_id, alg);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("psa_cipher_encrypt_setup failed with status %d", status);
|
||||
psa_destroy_key(key_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
size_t output_length = 0;
|
||||
status = psa_cipher_update(&operation, plaintext, 16, enc_data, 16, &output_length);
|
||||
if (status != PSA_SUCCESS || output_length != 16) {
|
||||
BT_ERR("psa_cipher_update failed with status %d", status);
|
||||
psa_cipher_abort(&operation);
|
||||
psa_destroy_key(key_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
status = psa_cipher_finish(&operation, enc_data + output_length, 16 - output_length, &output_length);
|
||||
if (status != PSA_SUCCESS) {
|
||||
BT_ERR("psa_cipher_finish failed with status %d", status);
|
||||
psa_destroy_key(key_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
psa_destroy_key(key_id);
|
||||
#else /* CONFIG_MBEDTLS_HARDWARE_AES */
|
||||
struct tc_aes_key_sched_struct s = {0};
|
||||
|
||||
if (tc_aes128_set_encrypt_key(&s, key) == TC_CRYPTO_FAIL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (tc_aes_encrypt(enc_data, plaintext, &s) == TC_CRYPTO_FAIL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif /* CONFIG_MBEDTLS_HARDWARE_AES */
|
||||
|
||||
BT_DBG("enc_data %s", bt_hex(enc_data, 16));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_BLE_MESH_USE_DUPLICATE_SCAN
|
||||
|
||||
@@ -616,7 +616,7 @@ int bt_mesh_input_string(const char *str)
|
||||
|
||||
static void send_pub_key(void)
|
||||
{
|
||||
const uint8_t *key = NULL;
|
||||
uint8_t pub_key[64] = {0};
|
||||
uint8_t dhkey[32] = {0};
|
||||
PROV_BUF(buf, 65);
|
||||
|
||||
@@ -625,8 +625,8 @@ static void send_pub_key(void)
|
||||
* buffer as a temporary storage location. The validating
|
||||
* of the remote public key is finished when it is received.
|
||||
*/
|
||||
sys_memcpy_swap(buf.data, &prov_link.conf_inputs[17], 32);
|
||||
sys_memcpy_swap(&buf.data[32], &prov_link.conf_inputs[49], 32);
|
||||
memcpy(buf.data, &prov_link.conf_inputs[17], 32);
|
||||
memcpy(&buf.data[32], &prov_link.conf_inputs[49], 32);
|
||||
|
||||
if (bt_mesh_dh_key_gen(buf.data, dhkey)) {
|
||||
BT_ERR("Unable to generate DHKey");
|
||||
@@ -634,26 +634,25 @@ static void send_pub_key(void)
|
||||
return;
|
||||
}
|
||||
|
||||
sys_memcpy_swap(prov_link.dhkey, dhkey, 32);
|
||||
memcpy(prov_link.dhkey, dhkey, 32);
|
||||
|
||||
BT_DBG("DHkey: %s", bt_hex(prov_link.dhkey, 32));
|
||||
|
||||
bt_mesh_atomic_set_bit(prov_link.flags, HAVE_DHKEY);
|
||||
|
||||
key = bt_mesh_pub_key_get();
|
||||
if (!key) {
|
||||
if (bt_mesh_pub_key_copy(pub_key)) {
|
||||
BT_ERR("No public key available");
|
||||
close_link(PROV_ERR_UNEXP_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
BT_DBG("Local Public Key: %s", bt_hex(key, 64));
|
||||
BT_DBG("Local Public Key: %s", bt_hex(pub_key, 64));
|
||||
|
||||
bt_mesh_prov_buf_init(&buf, PROV_PUB_KEY);
|
||||
|
||||
/* Swap X and Y halves independently to big-endian */
|
||||
sys_memcpy_swap(net_buf_simple_add(&buf, 32), key, 32);
|
||||
sys_memcpy_swap(net_buf_simple_add(&buf, 32), &key[32], 32);
|
||||
/* Public key is already in big-endian format from bt_mesh_pub_key_copy() */
|
||||
memcpy(net_buf_simple_add(&buf, 32), pub_key, 32);
|
||||
memcpy(net_buf_simple_add(&buf, 32), &pub_key[32], 32);
|
||||
|
||||
memcpy(&prov_link.conf_inputs[81], &buf.data[1], 64);
|
||||
|
||||
@@ -673,8 +672,8 @@ static int bt_mesh_calc_dh_key(void)
|
||||
/* Copy remote key in little-endian for generating DHKey.
|
||||
* X and Y halves are swapped independently.
|
||||
*/
|
||||
sys_memcpy_swap(&pub_key[0], &prov_link.conf_inputs[17], 32);
|
||||
sys_memcpy_swap(&pub_key[32], &prov_link.conf_inputs[49], 32);
|
||||
memcpy(&pub_key[0], &prov_link.conf_inputs[17], 32);
|
||||
memcpy(&pub_key[32], &prov_link.conf_inputs[49], 32);
|
||||
|
||||
if (bt_mesh_dh_key_gen(pub_key, dhkey)) {
|
||||
BT_ERR("Unable to generate DHKey");
|
||||
@@ -682,7 +681,7 @@ static int bt_mesh_calc_dh_key(void)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
sys_memcpy_swap(prov_link.dhkey, dhkey, 32);
|
||||
memcpy(prov_link.dhkey, dhkey, 32);
|
||||
|
||||
BT_DBG("DHkey: %s", bt_hex(prov_link.dhkey, 32));
|
||||
|
||||
@@ -704,6 +703,8 @@ int bt_mesh_set_oob_pub_key(const uint8_t pub_key_x[32],
|
||||
const uint8_t pub_key_y[32],
|
||||
const uint8_t pri_key[32])
|
||||
{
|
||||
uint8_t privkey[32] = {0};
|
||||
|
||||
if (!pub_key_x || !pub_key_y || !pri_key) {
|
||||
BT_ERR("%s, Invalid parameter", __func__);
|
||||
return -EINVAL;
|
||||
@@ -715,7 +716,8 @@ int bt_mesh_set_oob_pub_key(const uint8_t pub_key_x[32],
|
||||
*/
|
||||
sys_memcpy_swap(&prov_link.conf_inputs[81], pub_key_x, 32);
|
||||
sys_memcpy_swap(&prov_link.conf_inputs[81] + 32, pub_key_y, 32);
|
||||
bt_mesh_set_private_key(pri_key);
|
||||
sys_memcpy_swap(privkey, pri_key, 32);
|
||||
bt_mesh_set_private_key(privkey);
|
||||
|
||||
bt_mesh_atomic_set_bit(prov_link.flags, OOB_PUB_KEY);
|
||||
|
||||
@@ -1558,8 +1560,6 @@ static void protocol_timeout(struct k_work *work)
|
||||
|
||||
int bt_mesh_prov_init(void)
|
||||
{
|
||||
const uint8_t *key = NULL;
|
||||
|
||||
if (bt_mesh_prov_get() == NULL) {
|
||||
BT_ERR("No provisioning context provided");
|
||||
return -EINVAL;
|
||||
@@ -1574,8 +1574,7 @@ int bt_mesh_prov_init(void)
|
||||
|
||||
__ASSERT(bt_mesh_prov_get()->uuid, "Device UUID not initialized");
|
||||
|
||||
key = bt_mesh_pub_key_get();
|
||||
if (!key) {
|
||||
if (bt_mesh_pub_key_gen()) {
|
||||
BT_ERR("Failed to generate public key");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@@ -1764,8 +1764,8 @@ static void prov_gen_dh_key(struct bt_mesh_prov_link *link)
|
||||
/* Copy public key in little-endian for generating DHKey.
|
||||
* X and Y halves are swapped independently.
|
||||
*/
|
||||
sys_memcpy_swap(&pub_key[0], &link->conf_inputs[81], 32);
|
||||
sys_memcpy_swap(&pub_key[32], &link->conf_inputs[113], 32);
|
||||
memcpy(&pub_key[0], &link->conf_inputs[81], 32);
|
||||
memcpy(&pub_key[32], &link->conf_inputs[113], 32);
|
||||
|
||||
if (bt_mesh_dh_key_gen(pub_key, dhkey)) {
|
||||
BT_ERR("Failed to generate DHKey");
|
||||
@@ -1773,7 +1773,7 @@ static void prov_gen_dh_key(struct bt_mesh_prov_link *link)
|
||||
return;
|
||||
}
|
||||
|
||||
sys_memcpy_swap(link->dhkey, dhkey, 32);
|
||||
memcpy(link->dhkey, dhkey, 32);
|
||||
|
||||
BT_DBG("DHKey: %s", bt_hex(link->dhkey, 32));
|
||||
|
||||
@@ -1847,23 +1847,22 @@ static void prov_gen_dh_key(struct bt_mesh_prov_link *link)
|
||||
|
||||
static void send_pub_key(struct bt_mesh_prov_link *link)
|
||||
{
|
||||
const uint8_t *key = NULL;
|
||||
uint8_t pub_key[64] = {0};
|
||||
PROV_BUF(buf, 65);
|
||||
|
||||
key = bt_mesh_pub_key_get();
|
||||
if (!key) {
|
||||
if (bt_mesh_pub_key_copy(pub_key)) {
|
||||
BT_ERR("No public key available");
|
||||
close_link(link, CLOSE_REASON_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
BT_DBG("Local Public Key: %s", bt_hex(key, 64));
|
||||
BT_DBG("Local Public Key: %s", bt_hex(pub_key, 64));
|
||||
|
||||
bt_mesh_prov_buf_init(&buf, PROV_PUB_KEY);
|
||||
|
||||
/* Swap X and Y halves independently to big-endian */
|
||||
sys_memcpy_swap(net_buf_simple_add(&buf, 32), key, 32);
|
||||
sys_memcpy_swap(net_buf_simple_add(&buf, 32), &key[32], 32);
|
||||
/* Public key is already in big-endian format from bt_mesh_pub_key_copy() */
|
||||
memcpy(net_buf_simple_add(&buf, 32), pub_key, 32);
|
||||
memcpy(net_buf_simple_add(&buf, 32), &pub_key[32], 32);
|
||||
|
||||
/* Store provisioner public key value in conf_inputs */
|
||||
memcpy(&link->conf_inputs[17], &buf.data[1], 64);
|
||||
@@ -2819,7 +2818,6 @@ static void protocol_timeout(struct k_work *work)
|
||||
|
||||
int bt_mesh_provisioner_prov_init(void)
|
||||
{
|
||||
const uint8_t *key = NULL;
|
||||
int i;
|
||||
|
||||
if (bt_mesh_prov_get() == NULL) {
|
||||
@@ -2827,8 +2825,7 @@ int bt_mesh_provisioner_prov_init(void)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
key = bt_mesh_pub_key_get();
|
||||
if (!key) {
|
||||
if (bt_mesh_pub_key_gen()) {
|
||||
BT_ERR("Failed to generate Public Key");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@@ -35,8 +35,6 @@
|
||||
#include "lpn.h"
|
||||
#include "rpl.h"
|
||||
#include "foundation.h"
|
||||
#include <tinycrypt/hmac.h>
|
||||
#include <tinycrypt/sha256.h>
|
||||
#include "mesh/buf.h"
|
||||
#include "mesh/slist.h"
|
||||
#include "mesh/config.h"
|
||||
@@ -562,24 +560,9 @@ int bt_mesh_ext_net_decrypt(const uint8_t key[16], struct net_buf_simple *buf,
|
||||
return bt_mesh_net_decrypt(key, buf, iv_index, proxy, proxy_solic);
|
||||
}
|
||||
|
||||
int bt_mesh_ext_tc_hmac_set_key(void *ctx, const uint8_t *key, unsigned int key_size)
|
||||
int bt_mesh_ext_hmac_sha_256(const uint8_t key[32], struct bt_mesh_sg *sg, size_t sg_len, uint8_t mac[32])
|
||||
{
|
||||
return tc_hmac_set_key(ctx, key, key_size);
|
||||
}
|
||||
|
||||
int bt_mesh_ext_tc_hmac_init(void *ctx)
|
||||
{
|
||||
return tc_hmac_init(ctx);
|
||||
}
|
||||
|
||||
int bt_mesh_ext_tc_hmac_update(void *ctx, const void *data, unsigned int data_length)
|
||||
{
|
||||
return tc_hmac_update(ctx, data, data_length);
|
||||
}
|
||||
|
||||
int bt_mesh_ext_tc_hmac_final(uint8_t *tag, unsigned int taglen, void *ctx)
|
||||
{
|
||||
return tc_hmac_final(tag, taglen, ctx);
|
||||
return bt_mesh_sha256_hmac_raw_key(key, sg, sg_len, mac);
|
||||
}
|
||||
|
||||
/* Mutex */
|
||||
@@ -4274,12 +4257,6 @@ static const bt_mesh_ext_config_t bt_mesh_ext_cfg = {
|
||||
.struct_addr_off_val = offsetof(bt_mesh_addr_t, val),
|
||||
.struct_sg_size = sizeof(struct bt_mesh_sg),
|
||||
.struct_sg_off_len = offsetof(struct bt_mesh_sg, len),
|
||||
.struct_tc_sha256_state = sizeof(struct tc_sha256_state_struct),
|
||||
.struct_tc_sha256_off_bits_hashed = offsetof(struct tc_sha256_state_struct, bits_hashed),
|
||||
.struct_tc_sha256_off_leftover = offsetof(struct tc_sha256_state_struct, leftover),
|
||||
.struct_tc_sha256_off_leftover_offset = offsetof(struct tc_sha256_state_struct, leftover_offset),
|
||||
.struct_tc_hmac_state_size = sizeof(struct tc_hmac_state_struct),
|
||||
.struct_tc_hmac_state_off_key = offsetof(struct tc_hmac_state_struct, key),
|
||||
|
||||
.btc_ble_mesh_evt_agg_client_send_timeout = BTC_BLE_MESH_EVT_AGG_CLIENT_SEND_TIMEOUT,
|
||||
.btc_ble_mesh_evt_agg_client_recv_rsp = BTC_BLE_MESH_EVT_AGG_CLIENT_RECV_RSP,
|
||||
|
||||
Submodule components/bt/esp_ble_mesh/lib/lib updated: 85e64e61f8...e44762384e
@@ -4,3 +4,10 @@ idf_component_register(SRCS "test_bt_main.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES unity bt
|
||||
WHOLE_ARCHIVE)
|
||||
|
||||
# Add private include directories from bt component to access internal headers
|
||||
idf_component_get_property(bt_dir bt COMPONENT_DIR)
|
||||
target_include_directories(${COMPONENT_LIB} PRIVATE ${bt_dir}/host/bluedroid/stack/smp/include)
|
||||
target_include_directories(${COMPONENT_LIB} PRIVATE ${bt_dir}/host/bluedroid/common/include)
|
||||
target_include_directories(${COMPONENT_LIB} PRIVATE ${bt_dir}/host/bluedroid/stack/include)
|
||||
target_include_directories(${COMPONENT_LIB} PRIVATE ${bt_dir}/common/include)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -18,42 +18,14 @@
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_bt_device.h"
|
||||
#include "esp_gap_ble_api.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if defined(CONFIG_BT_SMP_CRYPTO_STACK_NATIVE) && CONFIG_BT_SMP_CRYPTO_STACK_NATIVE
|
||||
/* Native Bluedroid crypto implementation tests */
|
||||
#include "p_256_ecc_pp.h"
|
||||
|
||||
#define KEY_LENGTH_DWORDS_P256 8
|
||||
|
||||
typedef unsigned long DWORD;
|
||||
typedef uint32_t UINT32;
|
||||
|
||||
typedef struct {
|
||||
DWORD x[KEY_LENGTH_DWORDS_P256];
|
||||
DWORD y[KEY_LENGTH_DWORDS_P256];
|
||||
DWORD z[KEY_LENGTH_DWORDS_P256];
|
||||
} Point;
|
||||
|
||||
typedef struct {
|
||||
// curve's coefficients
|
||||
DWORD a[KEY_LENGTH_DWORDS_P256];
|
||||
DWORD b[KEY_LENGTH_DWORDS_P256];
|
||||
|
||||
//whether a is -3
|
||||
int a_minus3;
|
||||
|
||||
// prime modulus
|
||||
DWORD p[KEY_LENGTH_DWORDS_P256];
|
||||
|
||||
// Omega, p = 2^m -omega
|
||||
DWORD omega[KEY_LENGTH_DWORDS_P256];
|
||||
|
||||
// base point, a point on E of order r
|
||||
Point G;
|
||||
|
||||
} elliptic_curve_t;
|
||||
|
||||
extern void ECC_PointMult_Bin_NAF(Point *q, Point *p, DWORD *n, uint32_t keyLength);
|
||||
extern bool ECC_CheckPointIsInElliCur_P256(Point *p);
|
||||
extern void p_256_init_curve(UINT32 keyLength);
|
||||
extern elliptic_curve_t curve_p256;
|
||||
|
||||
static void bt_rand(void *buf, size_t len)
|
||||
{
|
||||
if (!len) {
|
||||
@@ -70,7 +42,17 @@ static void bt_rand(void *buf, size_t len)
|
||||
return;
|
||||
}
|
||||
|
||||
TEST_CASE("ble_smp_public_key_check", "[ble_smp]")
|
||||
/**
|
||||
* @brief Test ECC public key validation for native Bluedroid crypto implementation
|
||||
*
|
||||
* This test is only compiled when SMP_CRYPTO_STACK_NATIVE is enabled.
|
||||
* It tests the native Bluedroid ECC implementation including:
|
||||
* - Public key generation from private key
|
||||
* - Public key validation on elliptic curve
|
||||
* - Attack simulation (invalid public key detection)
|
||||
* - Random key generation and validation
|
||||
*/
|
||||
TEST_CASE("ble_smp_public_key_check", "[ble_smp][native_crypto]")
|
||||
{
|
||||
/* We wait init finish 200ms here */
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||
@@ -96,7 +78,14 @@ TEST_CASE("ble_smp_public_key_check", "[ble_smp]")
|
||||
TEST_ASSERT(ECC_CheckPointIsInElliCur_P256(&public_key));
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BT_SMP_CRYPTO_STACK_NATIVE */
|
||||
|
||||
/**
|
||||
* @brief Test static passkey set/clear functionality
|
||||
*
|
||||
* This test is applicable to all crypto stack implementations (Native, TinyCrypt, mbedTLS).
|
||||
* It tests the SMP security parameter API for setting and clearing static passkeys.
|
||||
*/
|
||||
TEST_CASE("ble_smp_set_clear_static_passkey", "[ble_smp]")
|
||||
{
|
||||
/* We wait init finish 200ms here */
|
||||
|
||||
Reference in New Issue
Block a user