515 lines
16 KiB
C
515 lines
16 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2017 Intel Corporation
|
|
* SPDX-FileCopyrightText: 2023 Nordic Semiconductor ASA
|
|
* SPDX-FileContributor: 2026 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_ */
|