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 * Copyright (c) 2015-2017 Intel Corporation
* *
* SPDX-License-Identifier: Apache-2.0 * 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], int bt_encrypt_be(const u8_t key[16], const u8_t plaintext[16],
u8_t enc_data[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 #ifdef __cplusplus
} }
#endif #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_RFCOMM rfcomm.c)
zephyr_library_sources_ifdef(CONFIG_BT_TESTING testing.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_SETTINGS settings.c)
zephyr_library_sources_ifdef(CONFIG_BT_HOST_CCM aes_ccm.c)
zephyr_library_sources_ifdef( zephyr_library_sources_ifdef(
CONFIG_BT_BREDR CONFIG_BT_BREDR

View file

@ -1,6 +1,6 @@
# Bluetooth LE stack configuration options # 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 # Copyright (c) 2015-2016 Intel Corporation
# SPDX-License-Identifier: Apache-2.0 # 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 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). 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 if BT_DEBUG
config BT_DEBUG_SETTINGS config BT_DEBUG_SETTINGS
bool "Bluetooth storage debug" 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
select TINYCRYPT_AES select TINYCRYPT_AES
select TINYCRYPT_AES_CMAC select TINYCRYPT_AES_CMAC
select BT_HOST_CCM
depends on BT_OBSERVER && BT_BROADCASTER depends on BT_OBSERVER && BT_BROADCASTER
help help
This option enables Bluetooth Mesh support. The specific 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); 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, static void create_proxy_nonce(u8_t nonce[13], const u8_t *pdu,
u32_t iv_index) 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)); BT_DBG("Nonce %s", bt_hex(nonce, 13));
err = bt_mesh_ccm_encrypt(key, nonce, &buf->data[7], buf->len - 7, err = bt_ccm_encrypt(key, nonce, &buf->data[7], buf->len - 7, NULL, 0,
NULL, 0, &buf->data[7], mic_len); &buf->data[7], mic_len);
if (!err) { if (!err) {
net_buf_simple_add(buf, mic_len); 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; buf->len -= mic_len;
return bt_mesh_ccm_decrypt(key, nonce, &buf->data[7], buf->len - 7, return bt_ccm_decrypt(key, nonce, &buf->data[7], buf->len - 7, NULL, 0,
NULL, 0, &buf->data[7], mic_len); &buf->data[7], mic_len);
} }
static void create_app_nonce(u8_t nonce[13], bool dev_key, u8_t aszmic, 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)); BT_DBG("Nonce %s", bt_hex(nonce, 13));
err = bt_mesh_ccm_encrypt(key, nonce, buf->data, buf->len, ad, err = bt_ccm_encrypt(key, nonce, buf->data, buf->len, ad, ad ? 16 : 0,
ad ? 16 : 0, buf->data, APP_MIC_LEN(aszmic)); buf->data, APP_MIC_LEN(aszmic));
if (!err) { if (!err) {
net_buf_simple_add(buf, APP_MIC_LEN(aszmic)); net_buf_simple_add(buf, APP_MIC_LEN(aszmic));
BT_DBG("Encr: %s", bt_hex(buf->data, buf->len)); 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("AppKey %s", bt_hex(key, 16));
BT_DBG("Nonce %s", bt_hex(nonce, 13)); BT_DBG("Nonce %s", bt_hex(nonce, 13));
err = bt_mesh_ccm_decrypt(key, nonce, buf->data, buf->len, ad, err = bt_ccm_decrypt(key, nonce, buf->data, buf->len, ad, ad ? 16 : 0,
ad ? 16 : 0, out->data, APP_MIC_LEN(aszmic)); out->data, APP_MIC_LEN(aszmic));
if (!err) { if (!err) {
net_buf_simple_add(out, buf->len); 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], int bt_mesh_prov_decrypt(const u8_t key[16], u8_t nonce[13],
const u8_t data[25 + 8], u8_t out[25]) 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], int bt_mesh_prov_encrypt(const u8_t key[16], u8_t nonce[13],
const u8_t data[25], u8_t out[25 + 8]) 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, int bt_mesh_beacon_auth(const u8_t beacon_key[16], u8_t flags,