Bluetooth: Move Mesh CCM into a separate module

Moves the Mesh AES-CCM module out into a separate module, to make it
accessible from other subsystems. Adds the new CCM API in
include/bluetooth/crypto.h along with the bt_encrypt functions.

Signed-off-by: Trond Einar Snekvik <Trond.Einar.Snekvik@nordicsemi.no>
This commit is contained in:
Trond Einar Snekvik 2020-01-29 13:03:23 +01:00 committed by Johan Hedberg
commit 4ea59711d2
6 changed files with 301 additions and 229 deletions

View file

@ -3,7 +3,7 @@
*/
/*
* Copyright (c) 2017 Nordic Semiconductor ASA
* Copyright (c) 2017-2020 Nordic Semiconductor ASA
* Copyright (c) 2015-2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
@ -67,6 +67,55 @@ int bt_encrypt_le(const u8_t key[16], const u8_t plaintext[16],
int bt_encrypt_be(const u8_t key[16], const u8_t plaintext[16],
u8_t enc_data[16]);
/** @brief Decrypt big-endian data with AES-CCM.
*
* Decrypts and authorizes @c enc_data with AES-CCM, as described in
* https://tools.ietf.org/html/rfc3610.
*
* Assumes that the MIC follows directly after the encrypted data.
*
* @param key 128 bit MS byte first key
* @param nonce 13 byte MS byte first nonce
* @param enc_data Encrypted data
* @param len Length of the encrypted data
* @param aad Additional input data
* @param aad_len Additional input data length
* @param plaintext Plaintext buffer to place result in
* @param mic_size Size of the trailing MIC (in bytes)
*
* @retval 0 Successfully decrypted the data.
* @retval -EINVAL Invalid parameters.
* @retval -EBADMSG Authentication failed.
*/
int bt_ccm_decrypt(const u8_t key[16], u8_t nonce[13], const u8_t *enc_data,
size_t len, const u8_t *aad, size_t aad_len,
u8_t *plaintext, size_t mic_size);
/** @brief Encrypt big-endian data with AES-CCM.
*
* Encrypts and generates a MIC from @c plaintext with AES-CCM, as described in
* https://tools.ietf.org/html/rfc3610.
*
* Places the MIC directly after the encrypted data.
*
* @param key 128 bit MS byte first key
* @param nonce 13 byte MS byte first nonce
* @param enc_data Buffer to place encrypted data in
* @param len Length of the encrypted data
* @param aad Additional input data
* @param aad_len Additional input data length
* @param plaintext Plaintext buffer to encrypt
* @param mic_size Size of the trailing MIC (in bytes)
*
* @retval 0 Successfully encrypted the data.
* @retval -EINVAL Invalid parameters.
*/
int bt_ccm_encrypt(const u8_t key[16], u8_t nonce[13], const u8_t *enc_data,
size_t len, const u8_t *aad, size_t aad_len,
u8_t *plaintext, size_t mic_size);
#ifdef __cplusplus
}
#endif

View file

@ -11,6 +11,7 @@ zephyr_library_sources_ifdef(CONFIG_BT_AVDTP avdtp.c)
zephyr_library_sources_ifdef(CONFIG_BT_RFCOMM rfcomm.c)
zephyr_library_sources_ifdef(CONFIG_BT_TESTING testing.c)
zephyr_library_sources_ifdef(CONFIG_BT_SETTINGS settings.c)
zephyr_library_sources_ifdef(CONFIG_BT_HOST_CCM aes_ccm.c)
zephyr_library_sources_ifdef(
CONFIG_BT_BREDR

View file

@ -1,6 +1,6 @@
# Bluetooth LE stack configuration options
# Copyright (c) 2016-2017 Nordic Semiconductor ASA
# Copyright (c) 2016-2020 Nordic Semiconductor ASA
# Copyright (c) 2015-2016 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
@ -541,6 +541,13 @@ config BT_TINYCRYPT_ECC
to enabled for a combined build with Zephyr's own controller, since it
does not have any special ECC support itself (at least not currently).
config BT_HOST_CCM
bool "Enable host side AES-CCM module"
help
Enables the software based AES-CCM engine in the host. Will use the
controller's AES encryption functions if available, or BT_HOST_CRYPTO
otherwise.
if BT_DEBUG
config BT_DEBUG_SETTINGS
bool "Bluetooth storage debug"

View file

@ -0,0 +1,231 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr.h>
#include <sys/byteorder.h>
#include <bluetooth/crypto.h>
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE)
#define LOG_MODULE_NAME bt_aes_ccm
#include "common/log.h"
static inline void xor16(u8_t *dst, const u8_t *a, const u8_t *b)
{
dst[0] = a[0] ^ b[0];
dst[1] = a[1] ^ b[1];
dst[2] = a[2] ^ b[2];
dst[3] = a[3] ^ b[3];
dst[4] = a[4] ^ b[4];
dst[5] = a[5] ^ b[5];
dst[6] = a[6] ^ b[6];
dst[7] = a[7] ^ b[7];
dst[8] = a[8] ^ b[8];
dst[9] = a[9] ^ b[9];
dst[10] = a[10] ^ b[10];
dst[11] = a[11] ^ b[11];
dst[12] = a[12] ^ b[12];
dst[13] = a[13] ^ b[13];
dst[14] = a[14] ^ b[14];
dst[15] = a[15] ^ b[15];
}
/* pmsg is assumed to have the nonce already present in bytes 1-13 */
static int ccm_calculate_X0(const u8_t key[16], const u8_t *aad, u8_t aad_len,
size_t mic_size, u8_t msg_len, u8_t b[16],
u8_t X0[16])
{
int i, j, err;
/* X_0 = e(AppKey, flags || nonce || length) */
b[0] = (((mic_size - 2) / 2) << 3) | ((!!aad_len) << 6) | 0x01;
sys_put_be16(msg_len, b + 14);
err = bt_encrypt_be(key, b, X0);
if (err) {
return err;
}
/* If AAD is being used to authenticate, include it here */
if (aad_len) {
sys_put_be16(aad_len, b);
for (i = 0; i < sizeof(u16_t); i++) {
b[i] = X0[i] ^ b[i];
}
j = 0;
aad_len += sizeof(u16_t);
while (aad_len > 16) {
do {
b[i] = X0[i] ^ aad[j];
i++, j++;
} while (i < 16);
aad_len -= 16;
i = 0;
err = bt_encrypt_be(key, b, X0);
if (err) {
return err;
}
}
for (; i < aad_len; i++, j++) {
b[i] = X0[i] ^ aad[j];
}
for (i = aad_len; i < 16; i++) {
b[i] = X0[i];
}
err = bt_encrypt_be(key, b, X0);
if (err) {
return err;
}
}
return 0;
}
static int ccm_auth(const u8_t key[16], u8_t nonce[13],
const u8_t *cleartext_msg, size_t msg_len, const u8_t *aad,
size_t aad_len, u8_t *mic, size_t mic_size)
{
u8_t b[16], Xn[16], s0[16];
u16_t blk_cnt, last_blk;
int err, j, i;
last_blk = msg_len % 16;
blk_cnt = (msg_len + 15) / 16;
if (!last_blk) {
last_blk = 16U;
}
b[0] = 0x01;
memcpy(b + 1, nonce, 13);
/* S[0] = e(AppKey, 0x01 || nonce || 0x0000) */
sys_put_be16(0x0000, &b[14]);
err = bt_encrypt_be(key, b, s0);
if (err) {
return err;
}
ccm_calculate_X0(key, aad, aad_len, mic_size, msg_len, b, Xn);
for (j = 0; j < blk_cnt; j++) {
/* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
if (j + 1 == blk_cnt) {
for (i = 0; i < last_blk; i++) {
b[i] = Xn[i] ^ cleartext_msg[(j * 16) + i];
}
memcpy(&b[i], &Xn[i], 16 - i);
} else {
xor16(b, Xn, &cleartext_msg[j * 16]);
}
err = bt_encrypt_be(key, b, Xn);
if (err) {
return err;
}
}
/* MIC = C_mic ^ X_1 */
for (i = 0; i < mic_size; i++) {
mic[i] = s0[i] ^ Xn[i];
}
return 0;
}
static int ccm_crypt(const u8_t key[16], const u8_t nonce[13],
const u8_t *in_msg, u8_t *out_msg, size_t msg_len)
{
u8_t a_i[16], s_i[16];
u16_t last_blk, blk_cnt;
size_t i, j;
int err;
last_blk = msg_len % 16;
blk_cnt = (msg_len + 15) / 16;
if (!last_blk) {
last_blk = 16U;
}
a_i[0] = 0x01;
memcpy(&a_i[1], nonce, 13);
for (j = 0; j < blk_cnt; j++) {
/* S_1 = e(AppKey, 0x01 || nonce || 0x0001) */
sys_put_be16(j + 1, &a_i[14]);
err = bt_encrypt_be(key, a_i, s_i);
if (err) {
return err;
}
/* Encrypted = Payload[0-15] ^ C_1 */
if (j < blk_cnt - 1) {
xor16(&out_msg[j * 16], s_i, &in_msg[j * 16]);
} else {
for (i = 0; i < last_blk; i++) {
out_msg[(j * 16) + i] =
in_msg[(j * 16) + i] ^ s_i[i];
}
}
}
return 0;
}
int bt_ccm_decrypt(const u8_t key[16], u8_t nonce[13], const u8_t *enc_msg,
size_t msg_len, const u8_t *aad, size_t aad_len,
u8_t *out_msg, size_t mic_size)
{
u8_t mic[16];
if (aad_len >= 0xff00 || mic_size > sizeof(mic)) {
return -EINVAL;
}
ccm_crypt(key, nonce, enc_msg, out_msg, msg_len);
ccm_auth(key, nonce, out_msg, msg_len, aad, aad_len, mic, mic_size);
if (memcmp(mic, enc_msg + msg_len, mic_size)) {
return -EBADMSG;
}
return 0;
}
int bt_ccm_encrypt(const u8_t key[16], u8_t nonce[13], const u8_t *msg,
size_t msg_len, const u8_t *aad, size_t aad_len,
u8_t *out_msg, size_t mic_size)
{
u8_t *mic = out_msg + msg_len;
BT_DBG("key %s", bt_hex(key, 16));
BT_DBG("nonce %s", bt_hex(nonce, 13));
BT_DBG("msg (len %zu) %s", msg_len, bt_hex(msg, msg_len));
BT_DBG("aad_len %zu mic_size %zu", aad_len, mic_size);
/* Unsupported AAD size */
if (aad_len >= 0xff00 || mic_size > 16) {
return -EINVAL;
}
ccm_auth(key, nonce, out_msg, msg_len, aad, aad_len, mic, mic_size);
ccm_crypt(key, nonce, msg, out_msg, msg_len);
return 0;
}

View file

@ -8,6 +8,7 @@ menuconfig BT_MESH
select TINYCRYPT
select TINYCRYPT_AES
select TINYCRYPT_AES_CMAC
select BT_HOST_CCM
depends on BT_OBSERVER && BT_BROADCASTER
help
This option enables Bluetooth Mesh support. The specific

View file

@ -203,223 +203,6 @@ int bt_mesh_id128(const u8_t n[16], const char *s, u8_t out[16])
return bt_mesh_k1(n, 16, salt, id128, out);
}
static inline void xor16(u8_t *dst, const u8_t *a, const u8_t *b)
{
dst[0] = a[0] ^ b[0];
dst[1] = a[1] ^ b[1];
dst[2] = a[2] ^ b[2];
dst[3] = a[3] ^ b[3];
dst[4] = a[4] ^ b[4];
dst[5] = a[5] ^ b[5];
dst[6] = a[6] ^ b[6];
dst[7] = a[7] ^ b[7];
dst[8] = a[8] ^ b[8];
dst[9] = a[9] ^ b[9];
dst[10] = a[10] ^ b[10];
dst[11] = a[11] ^ b[11];
dst[12] = a[12] ^ b[12];
dst[13] = a[13] ^ b[13];
dst[14] = a[14] ^ b[14];
dst[15] = a[15] ^ b[15];
}
/* pmsg is assumed to have the nonce already present in bytes 1-13 */
static int ccm_calculate_X0(const u8_t key[16], const u8_t *aad, u8_t aad_len,
size_t mic_size, u8_t msg_len, u8_t b[16],
u8_t X0[16])
{
int i, j, err;
/* X_0 = e(AppKey, flags || nonce || length) */
b[0] = (((mic_size - 2) / 2) << 3) | ((!!aad_len) << 6) | 0x01;
sys_put_be16(msg_len, b + 14);
err = bt_encrypt_be(key, b, X0);
if (err) {
return err;
}
/* If AAD is being used to authenticate, include it here */
if (aad_len) {
sys_put_be16(aad_len, b);
for (i = 0; i < sizeof(u16_t); i++) {
b[i] = X0[i] ^ b[i];
}
j = 0;
aad_len += sizeof(u16_t);
while (aad_len > 16) {
do {
b[i] = X0[i] ^ aad[j];
i++, j++;
} while (i < 16);
aad_len -= 16;
i = 0;
err = bt_encrypt_be(key, b, X0);
if (err) {
return err;
}
}
for (; i < aad_len; i++, j++) {
b[i] = X0[i] ^ aad[j];
}
for (i = aad_len; i < 16; i++) {
b[i] = X0[i];
}
err = bt_encrypt_be(key, b, X0);
if (err) {
return err;
}
}
return 0;
}
static int ccm_auth(const u8_t key[16], u8_t nonce[13],
const u8_t *cleartext_msg, size_t msg_len, const u8_t *aad,
size_t aad_len, u8_t *mic, size_t mic_size)
{
u8_t b[16], Xn[16], s0[16];
u16_t blk_cnt, last_blk;
int err, j, i;
last_blk = msg_len % 16;
blk_cnt = (msg_len + 15) / 16;
if (!last_blk) {
last_blk = 16U;
}
b[0] = 0x01;
memcpy(b + 1, nonce, 13);
/* S[0] = e(AppKey, 0x01 || nonce || 0x0000) */
sys_put_be16(0x0000, &b[14]);
err = bt_encrypt_be(key, b, s0);
if (err) {
return err;
}
ccm_calculate_X0(key, aad, aad_len, mic_size, msg_len, b, Xn);
for (j = 0; j < blk_cnt; j++) {
/* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
if (j + 1 == blk_cnt) {
for (i = 0; i < last_blk; i++) {
b[i] = Xn[i] ^ cleartext_msg[(j * 16) + i];
}
memcpy(&b[i], &Xn[i], 16 - i);
} else {
xor16(b, Xn, &cleartext_msg[j * 16]);
}
err = bt_encrypt_be(key, b, Xn);
if (err) {
return err;
}
}
/* MIC = C_mic ^ X_1 */
for (i = 0; i < mic_size; i++) {
mic[i] = s0[i] ^ Xn[i];
}
return 0;
}
static int ccm_crypt(const u8_t key[16], const u8_t nonce[13],
const u8_t *in_msg, u8_t *out_msg, size_t msg_len)
{
u8_t a_i[16], s_i[16];
u16_t last_blk, blk_cnt;
size_t i, j;
int err;
last_blk = msg_len % 16;
blk_cnt = (msg_len + 15) / 16;
if (!last_blk) {
last_blk = 16U;
}
a_i[0] = 0x01;
memcpy(&a_i[1], nonce, 13);
for (j = 0; j < blk_cnt; j++) {
/* S_1 = e(AppKey, 0x01 || nonce || 0x0001) */
sys_put_be16(j + 1, &a_i[14]);
err = bt_encrypt_be(key, a_i, s_i);
if (err) {
return err;
}
/* Encrypted = Payload[0-15] ^ C_1 */
if (j < blk_cnt - 1) {
xor16(&out_msg[j * 16], s_i, &in_msg[j * 16]);
} else {
for (i = 0; i < last_blk; i++) {
out_msg[(j * 16) + i] =
in_msg[(j * 16) + i] ^ s_i[i];
}
}
}
return 0;
}
static int bt_mesh_ccm_decrypt(const u8_t key[16], u8_t nonce[13],
const u8_t *enc_msg, size_t msg_len,
const u8_t *aad, size_t aad_len,
u8_t *out_msg, size_t mic_size)
{
u8_t mic[16];
if (msg_len == 0 || aad_len >= 0xff00) {
return -EINVAL;
}
ccm_crypt(key, nonce, enc_msg, out_msg, msg_len);
ccm_auth(key, nonce, out_msg, msg_len, aad, aad_len, mic, mic_size);
if (memcmp(mic, enc_msg + msg_len, mic_size)) {
return -EBADMSG;
}
return 0;
}
static int bt_mesh_ccm_encrypt(const u8_t key[16], u8_t nonce[13],
const u8_t *msg, size_t msg_len,
const u8_t *aad, size_t aad_len,
u8_t *out_msg, size_t mic_size)
{
u8_t *mic = out_msg + msg_len;
BT_DBG("key %s", bt_hex(key, 16));
BT_DBG("nonce %s", bt_hex(nonce, 13));
BT_DBG("msg (len %zu) %s", msg_len, bt_hex(msg, msg_len));
BT_DBG("aad_len %zu mic_size %zu", aad_len, mic_size);
/* Unsupported AAD size */
if (aad_len >= 0xff00) {
return -EINVAL;
}
ccm_auth(key, nonce, out_msg, msg_len, aad, aad_len, mic, mic_size);
ccm_crypt(key, nonce, msg, out_msg, msg_len);
return 0;
}
static void create_proxy_nonce(u8_t nonce[13], const u8_t *pdu,
u32_t iv_index)
{
@ -517,8 +300,8 @@ int bt_mesh_net_encrypt(const u8_t key[16], struct net_buf_simple *buf,
BT_DBG("Nonce %s", bt_hex(nonce, 13));
err = bt_mesh_ccm_encrypt(key, nonce, &buf->data[7], buf->len - 7,
NULL, 0, &buf->data[7], mic_len);
err = bt_ccm_encrypt(key, nonce, &buf->data[7], buf->len - 7, NULL, 0,
&buf->data[7], mic_len);
if (!err) {
net_buf_simple_add(buf, mic_len);
}
@ -546,8 +329,8 @@ int bt_mesh_net_decrypt(const u8_t key[16], struct net_buf_simple *buf,
buf->len -= mic_len;
return bt_mesh_ccm_decrypt(key, nonce, &buf->data[7], buf->len - 7,
NULL, 0, &buf->data[7], mic_len);
return bt_ccm_decrypt(key, nonce, &buf->data[7], buf->len - 7, NULL, 0,
&buf->data[7], mic_len);
}
static void create_app_nonce(u8_t nonce[13], bool dev_key, u8_t aszmic,
@ -584,8 +367,8 @@ int bt_mesh_app_encrypt(const u8_t key[16], bool dev_key, u8_t aszmic,
BT_DBG("Nonce %s", bt_hex(nonce, 13));
err = bt_mesh_ccm_encrypt(key, nonce, buf->data, buf->len, ad,
ad ? 16 : 0, buf->data, APP_MIC_LEN(aszmic));
err = bt_ccm_encrypt(key, nonce, buf->data, buf->len, ad, ad ? 16 : 0,
buf->data, APP_MIC_LEN(aszmic));
if (!err) {
net_buf_simple_add(buf, APP_MIC_LEN(aszmic));
BT_DBG("Encr: %s", bt_hex(buf->data, buf->len));
@ -609,8 +392,8 @@ int bt_mesh_app_decrypt(const u8_t key[16], bool dev_key, u8_t aszmic,
BT_DBG("AppKey %s", bt_hex(key, 16));
BT_DBG("Nonce %s", bt_hex(nonce, 13));
err = bt_mesh_ccm_decrypt(key, nonce, buf->data, buf->len, ad,
ad ? 16 : 0, out->data, APP_MIC_LEN(aszmic));
err = bt_ccm_decrypt(key, nonce, buf->data, buf->len, ad, ad ? 16 : 0,
out->data, APP_MIC_LEN(aszmic));
if (!err) {
net_buf_simple_add(out, buf->len);
}
@ -736,13 +519,13 @@ int bt_mesh_prov_conf(const u8_t conf_key[16], const u8_t rand[16],
int bt_mesh_prov_decrypt(const u8_t key[16], u8_t nonce[13],
const u8_t data[25 + 8], u8_t out[25])
{
return bt_mesh_ccm_decrypt(key, nonce, data, 25, NULL, 0, out, 8);
return bt_ccm_decrypt(key, nonce, data, 25, NULL, 0, out, 8);
}
int bt_mesh_prov_encrypt(const u8_t key[16], u8_t nonce[13],
const u8_t data[25], u8_t out[25 + 8])
{
return bt_mesh_ccm_encrypt(key, nonce, data, 25, NULL, 0, out, 8);
return bt_ccm_encrypt(key, nonce, data, 25, NULL, 0, out, 8);
}
int bt_mesh_beacon_auth(const u8_t beacon_key[16], u8_t flags,