bt-host: add option to use PSA APIs instead of TinyCrypt

By enabling CONFIG_BT_USE_PSA_API the user can specify to use
PSA APIs instead of TinyCrypt for crypto operations in bluetooth
host module.

This commit also extends tests/bluetooth/gatt in order to
add a PSA test.

Signed-off-by: Valerio Setti <vsetti@baylibre.com>
This commit is contained in:
Valerio Setti 2024-06-04 13:47:34 +02:00 committed by Alberto Escolar
commit 814b2ed457
7 changed files with 293 additions and 28 deletions

View file

@ -31,10 +31,13 @@ if(CONFIG_BT_HCI_HOST)
CONFIG_BT_OBSERVER CONFIG_BT_OBSERVER
scan.c scan.c
) )
zephyr_library_sources_ifdef(
CONFIG_BT_HOST_CRYPTO if(CONFIG_BT_USE_PSA_API)
crypto.c zephyr_library_sources_ifdef(CONFIG_BT_HOST_CRYPTO crypto_psa.c)
) else()
zephyr_library_sources_ifdef(CONFIG_BT_HOST_CRYPTO crypto_tc.c)
endif()
zephyr_library_sources_ifdef( zephyr_library_sources_ifdef(
CONFIG_BT_ECC CONFIG_BT_ECC
ecc.c ecc.c
@ -108,6 +111,13 @@ if(CONFIG_BT_CONN_DISABLE_SECURITY)
) )
endif() endif()
if(CONFIG_BT_USE_PSA_API)
zephyr_library_link_libraries_ifdef(CONFIG_MBEDTLS mbedTLS)
zephyr_library_include_directories_ifdef(CONFIG_BUILD_WITH_TFM
$<TARGET_PROPERTY:tfm,TFM_BINARY_DIR>/api_ns/interface/include
)
endif()
# Bluetooth Mesh has test dependencies in the host. # Bluetooth Mesh has test dependencies in the host.
# In order to compile Bsim tests with these test features # In order to compile Bsim tests with these test features
# and PSA enabled, the libraries must be linked. # and PSA enabled, the libraries must be linked.

View file

@ -139,8 +139,9 @@ rsource "../audio/Kconfig"
config BT_HOST_CRYPTO config BT_HOST_CRYPTO
bool "Use crypto functionality implemented in the Bluetooth host" bool "Use crypto functionality implemented in the Bluetooth host"
default y if !BT_CTLR_CRYPTO default y if !BT_CTLR_CRYPTO
select TINYCRYPT select TINYCRYPT if !BT_USE_PSA_API
select TINYCRYPT_AES select TINYCRYPT_AES if !BT_USE_PSA_API
select PSA_WANT_KEY_TYPE_AES if BT_USE_PSA_API
help help
The option adds the AES encryption support using TinyCrypt The option adds the AES encryption support using TinyCrypt
library if this is not provided by the controller implementation. library if this is not provided by the controller implementation.
@ -148,9 +149,9 @@ config BT_HOST_CRYPTO
config BT_HOST_CRYPTO_PRNG config BT_HOST_CRYPTO_PRNG
bool "Use Tinycrypt library for random number generation" bool "Use Tinycrypt library for random number generation"
default y default y
select TINYCRYPT_SHA256 select TINYCRYPT_SHA256 if !BT_USE_PSA_API
select TINYCRYPT_SHA256_HMAC select TINYCRYPT_SHA256_HMAC if !BT_USE_PSA_API
select TINYCRYPT_SHA256_HMAC_PRNG select TINYCRYPT_SHA256_HMAC_PRNG if !BT_USE_PSA_API
depends on BT_HOST_CRYPTO depends on BT_HOST_CRYPTO
help help
When selected, will use tinycrypt library for random number generation. When selected, will use tinycrypt library for random number generation.

View file

@ -107,9 +107,11 @@ config BT_GATT_CACHING
bool "GATT Caching support" bool "GATT Caching support"
default y default y
depends on BT_GATT_SERVICE_CHANGED depends on BT_GATT_SERVICE_CHANGED
select TINYCRYPT select TINYCRYPT if !BT_USE_PSA_API
select TINYCRYPT_AES select TINYCRYPT_AES if !BT_USE_PSA_API
select TINYCRYPT_AES_CMAC select TINYCRYPT_AES_CMAC if !BT_USE_PSA_API
select PSA_WANT_KEY_TYPE_AES if BT_USE_PSA_API
select PSA_WANT_ALG_CMAC if BT_USE_PSA_API
help help
This option enables support for GATT Caching. When enabled the stack This option enables support for GATT Caching. When enabled the stack
will register Client Supported Features and Database Hash will register Client Supported Features and Database Hash

View file

@ -0,0 +1,151 @@
/*
* Copyright (c) 2017 Nordic Semiconductor ASA
* Copyright (c) 2015-2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <errno.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/check.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/crypto.h>
#include "psa/crypto.h"
#include "common/bt_str.h"
#include "hci_core.h"
#define LOG_LEVEL CONFIG_BT_HCI_CORE_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_host_crypto);
int prng_init(void)
{
if (psa_crypto_init() != PSA_SUCCESS) {
return -EIO;
}
return 0;
}
#if defined(CONFIG_BT_HOST_CRYPTO_PRNG)
int bt_rand(void *buf, size_t len)
{
if (psa_generate_random(buf, len) == PSA_SUCCESS) {
return 0;
}
return -EIO;
}
#else /* !CONFIG_BT_HOST_CRYPTO_PRNG */
int bt_rand(void *buf, size_t len)
{
CHECKIF(buf == NULL || len == 0) {
return -EINVAL;
}
return bt_hci_le_rand(buf, len);
}
#endif /* CONFIG_BT_HOST_CRYPTO_PRNG */
int bt_encrypt_le(const uint8_t key[16], const uint8_t plaintext[16],
uint8_t enc_data[16])
{
psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT;
psa_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT;
psa_status_t status, destroy_status;
size_t out_len;
uint8_t tmp[16];
CHECKIF(key == NULL || plaintext == NULL || enc_data == NULL) {
return -EINVAL;
}
LOG_DBG("key %s", bt_hex(key, 16));
LOG_DBG("plaintext %s", bt_hex(plaintext, 16));
sys_memcpy_swap(tmp, key, 16);
psa_set_key_type(&attr, PSA_KEY_TYPE_AES);
psa_set_key_bits(&attr, 128);
psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_ENCRYPT);
psa_set_key_algorithm(&attr, PSA_ALG_ECB_NO_PADDING);
if (psa_import_key(&attr, tmp, 16, &key_id) != PSA_SUCCESS) {
LOG_ERR("Failed to import AES key");
return -EINVAL;
}
sys_memcpy_swap(tmp, plaintext, 16);
status = psa_cipher_encrypt(key_id, PSA_ALG_ECB_NO_PADDING, tmp, 16,
enc_data, 16, &out_len);
if (status != PSA_SUCCESS) {
LOG_ERR("AES encryption failed");
}
destroy_status = psa_destroy_key(key_id);
if (destroy_status != PSA_SUCCESS) {
LOG_ERR("Failed to destroy AES key");
}
if ((status != PSA_SUCCESS) || (destroy_status != PSA_SUCCESS)) {
return -EIO;
}
sys_mem_swap(enc_data, 16);
LOG_DBG("enc_data %s", bt_hex(enc_data, 16));
return 0;
}
int bt_encrypt_be(const uint8_t key[16], const uint8_t plaintext[16],
uint8_t enc_data[16])
{
psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT;
psa_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT;
psa_status_t status, destroy_status;
size_t out_len;
CHECKIF(key == NULL || plaintext == NULL || enc_data == NULL) {
return -EINVAL;
}
LOG_DBG("key %s", bt_hex(key, 16));
LOG_DBG("plaintext %s", bt_hex(plaintext, 16));
psa_set_key_type(&attr, PSA_KEY_TYPE_AES);
psa_set_key_bits(&attr, 128);
psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_ENCRYPT);
psa_set_key_algorithm(&attr, PSA_ALG_ECB_NO_PADDING);
if (psa_import_key(&attr, key, 16, &key_id) != PSA_SUCCESS) {
LOG_ERR("Failed to import AES key");
return -EINVAL;
}
status = psa_cipher_encrypt(key_id, PSA_ALG_ECB_NO_PADDING,
plaintext, 16, enc_data, 16, &out_len);
if (status != PSA_SUCCESS) {
LOG_ERR("AES encryption failed");
}
destroy_status = psa_destroy_key(key_id);
if (destroy_status != PSA_SUCCESS) {
LOG_ERR("Failed to destroy AES key");
}
if ((status != PSA_SUCCESS) || (destroy_status != PSA_SUCCESS)) {
return -EIO;
}
LOG_DBG("enc_data %s", bt_hex(enc_data, 16));
return 0;
}

View file

@ -21,11 +21,15 @@
#include <zephyr/settings/settings.h> #include <zephyr/settings/settings.h>
#if defined(CONFIG_BT_GATT_CACHING) #if defined(CONFIG_BT_GATT_CACHING)
#if defined(CONFIG_BT_USE_PSA_API)
#include "psa/crypto.h"
#else /* CONFIG_BT_USE_PSA_API */
#include <tinycrypt/constants.h> #include <tinycrypt/constants.h>
#include <tinycrypt/utils.h> #include <tinycrypt/utils.h>
#include <tinycrypt/aes.h> #include <tinycrypt/aes.h>
#include <tinycrypt/cmac_mode.h> #include <tinycrypt/cmac_mode.h>
#include <tinycrypt/ccm_mode.h> #include <tinycrypt/ccm_mode.h>
#endif /* CONFIG_BT_USE_PSA_API */
#endif /* CONFIG_BT_GATT_CACHING */ #endif /* CONFIG_BT_GATT_CACHING */
#include <zephyr/bluetooth/hci.h> #include <zephyr/bluetooth/hci.h>
@ -693,11 +697,93 @@ static ssize_t cf_write(struct bt_conn *conn, const struct bt_gatt_attr *attr,
return len; return len;
} }
#if defined(CONFIG_BT_USE_PSA_API)
struct gen_hash_state { struct gen_hash_state {
struct tc_cmac_struct state; psa_mac_operation_t operation;
psa_key_id_t key;
int err; int err;
}; };
static int db_hash_setup(struct gen_hash_state *state, uint8_t *key)
{
psa_key_attributes_t key_attr = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_type(&key_attr, PSA_KEY_TYPE_AES);
psa_set_key_bits(&key_attr, 128);
psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_MESSAGE);
psa_set_key_algorithm(&key_attr, PSA_ALG_CMAC);
if (psa_import_key(&key_attr, key, 16, &(state->key)) != PSA_SUCCESS) {
LOG_ERR("Unable to import the key for AES CMAC");
return -EIO;
}
state->operation = psa_mac_operation_init();
if (psa_mac_sign_setup(&(state->operation), state->key,
PSA_ALG_CMAC) != PSA_SUCCESS) {
LOG_ERR("CMAC operation init failed");
return -EIO;
}
return 0;
}
static int db_hash_update(struct gen_hash_state *state, uint8_t *data, size_t len)
{
if (psa_mac_update(&(state->operation), data, len) != PSA_SUCCESS) {
LOG_ERR("CMAC update failed");
return -EIO;
}
return 0;
}
static int db_hash_finish(struct gen_hash_state *state)
{
size_t mac_length;
if (psa_mac_sign_finish(&(state->operation), db_hash.hash, 16,
&mac_length) != PSA_SUCCESS) {
LOG_ERR("CMAC finish failed");
return -EIO;
}
return 0;
}
#else /* CONFIG_BT_USE_PSA_API */
struct gen_hash_state {
struct tc_cmac_struct state;
struct tc_aes_key_sched_struct sched;
int err;
};
static int db_hash_setup(struct gen_hash_state *state, uint8_t *key)
{
if (tc_cmac_setup(&(state->state), key, &(state->sched)) == TC_CRYPTO_FAIL) {
LOG_ERR("CMAC setup failed");
return -EIO;
}
return 0;
}
static int db_hash_update(struct gen_hash_state *state, uint8_t *data, size_t len)
{
if (tc_cmac_update(&state->state, data, len) == TC_CRYPTO_FAIL) {
LOG_ERR("CMAC update failed");
return -EIO;
}
return 0;
}
static int db_hash_finish(struct gen_hash_state *state)
{
if (tc_cmac_final(db_hash.hash, &(state->state)) == TC_CRYPTO_FAIL) {
LOG_ERR("CMAC finish failed");
return -EIO;
}
return 0;
}
#endif /* CONFIG_BT_USE_PSA_API */
union hash_attr_value { union hash_attr_value {
/* Bluetooth Core Specification Version 5.3 | Vol 3, Part G /* Bluetooth Core Specification Version 5.3 | Vol 3, Part G
* Table 3.1: Service declaration * Table 3.1: Service declaration
@ -755,15 +841,15 @@ static uint8_t gen_hash_m(const struct bt_gatt_attr *attr, uint16_t handle,
case BT_UUID_GATT_CHRC_VAL: case BT_UUID_GATT_CHRC_VAL:
case BT_UUID_GATT_CEP_VAL: case BT_UUID_GATT_CEP_VAL:
value = sys_cpu_to_le16(handle); value = sys_cpu_to_le16(handle);
if (tc_cmac_update(&state->state, (uint8_t *)&value, if (db_hash_update(state, (uint8_t *)&value,
sizeof(handle)) == TC_CRYPTO_FAIL) { sizeof(handle)) != 0) {
state->err = -EINVAL; state->err = -EINVAL;
return BT_GATT_ITER_STOP; return BT_GATT_ITER_STOP;
} }
value = sys_cpu_to_le16(u16->val); value = sys_cpu_to_le16(u16->val);
if (tc_cmac_update(&state->state, (uint8_t *)&value, if (db_hash_update(state, (uint8_t *)&value,
sizeof(u16->val)) == TC_CRYPTO_FAIL) { sizeof(u16->val)) != 0) {
state->err = -EINVAL; state->err = -EINVAL;
return BT_GATT_ITER_STOP; return BT_GATT_ITER_STOP;
} }
@ -774,8 +860,7 @@ static uint8_t gen_hash_m(const struct bt_gatt_attr *attr, uint16_t handle,
return BT_GATT_ITER_STOP; return BT_GATT_ITER_STOP;
} }
if (tc_cmac_update(&state->state, data, len) == if (db_hash_update(state, data, len) != 0) {
TC_CRYPTO_FAIL) {
state->err = -EINVAL; state->err = -EINVAL;
return BT_GATT_ITER_STOP; return BT_GATT_ITER_STOP;
} }
@ -788,18 +873,19 @@ static uint8_t gen_hash_m(const struct bt_gatt_attr *attr, uint16_t handle,
case BT_UUID_GATT_CPF_VAL: case BT_UUID_GATT_CPF_VAL:
case BT_UUID_GATT_CAF_VAL: case BT_UUID_GATT_CAF_VAL:
value = sys_cpu_to_le16(handle); value = sys_cpu_to_le16(handle);
if (tc_cmac_update(&state->state, (uint8_t *)&value, if (db_hash_update(state, (uint8_t *)&value,
sizeof(handle)) == TC_CRYPTO_FAIL) { sizeof(handle)) != 0) {
state->err = -EINVAL; state->err = -EINVAL;
return BT_GATT_ITER_STOP; return BT_GATT_ITER_STOP;
} }
value = sys_cpu_to_le16(u16->val); value = sys_cpu_to_le16(u16->val);
if (tc_cmac_update(&state->state, (uint8_t *)&value, if (db_hash_update(state, (uint8_t *)&value,
sizeof(u16->val)) == TC_CRYPTO_FAIL) { sizeof(u16->val)) != 0) {
state->err = -EINVAL; state->err = -EINVAL;
return BT_GATT_ITER_STOP; return BT_GATT_ITER_STOP;
} }
break; break;
default: default:
return BT_GATT_ITER_CONTINUE; return BT_GATT_ITER_CONTINUE;
@ -825,18 +911,15 @@ static void db_hash_store(void)
static void db_hash_gen(void) static void db_hash_gen(void)
{ {
uint8_t key[16] = {}; uint8_t key[16] = {};
struct tc_aes_key_sched_struct sched;
struct gen_hash_state state; struct gen_hash_state state;
if (tc_cmac_setup(&state.state, key, &sched) == TC_CRYPTO_FAIL) { if (db_hash_setup(&state, key) != 0) {
LOG_ERR("Unable to setup AES CMAC");
return; return;
} }
bt_gatt_foreach_attr(0x0001, 0xffff, gen_hash_m, &state); bt_gatt_foreach_attr(0x0001, 0xffff, gen_hash_m, &state);
if (tc_cmac_final(db_hash.hash, &state.state) == TC_CRYPTO_FAIL) { if (db_hash_finish(&state) != 0) {
LOG_ERR("Unable to calculate hash");
return; return;
} }

View file

@ -14,3 +14,21 @@ tests:
tags: tags:
- bluetooth - bluetooth
- gatt - gatt
bluetooth.gatt.psa:
filter: CONFIG_PSA_CRYPTO_CLIENT
extra_args:
- EXTRA_DTC_OVERLAY_FILE="test.overlay"
platform_allow:
- native_posix
- native_posix/native/64
- native_sim
- native_sim/native/64
- qemu_x86
- qemu_cortex_m3
- nrf5340dk/nrf5340/cpuapp/ns
- nrf52840dk/nrf52840
integration_platforms:
- native_sim
tags:
- bluetooth
- gatt