Files
esp-idf/components/bt/esp_ble_mesh/common/include/mesh/crypto.h
T

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_ */