253 lines
6.3 KiB
C
253 lines
6.3 KiB
C
/*
|
|
* Copyright (c) 2020 Bose Corporation
|
|
* Copyright (c) 2021-2022 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* The static functions in this file operate on Big Endian (BE) as the
|
|
* underlying encryption library is BE as well. Furthermore, the sample data
|
|
* in the CSIS spec is also provided as BE, and logging values as BE will make
|
|
* it easier to compare.
|
|
*/
|
|
#include "csip_crypto.h"
|
|
#include <zephyr/bluetooth/crypto.h>
|
|
#include <tinycrypt/constants.h>
|
|
#include <tinycrypt/utils.h>
|
|
#include <tinycrypt/aes.h>
|
|
#include <tinycrypt/cmac_mode.h>
|
|
#include <tinycrypt/ccm_mode.h>
|
|
#include <zephyr/sys/byteorder.h>
|
|
|
|
#include "common/bt_str.h"
|
|
|
|
#include <zephyr/logging/log.h>
|
|
|
|
LOG_MODULE_REGISTER(bt_csip_crypto, CONFIG_BT_CSIP_SET_MEMBER_CRYPTO_LOG_LEVEL);
|
|
|
|
#define BT_CSIP_CRYPTO_PADDING_SIZE 13
|
|
#define BT_CSIP_PADDED_RAND_SIZE (BT_CSIP_CRYPTO_PADDING_SIZE + BT_CSIP_CRYPTO_PRAND_SIZE)
|
|
#define BT_CSIP_R_MASK BIT_MASK(24) /* r is 24 bit / 3 octet */
|
|
|
|
static int aes_cmac(const uint8_t key[BT_CSIP_CRYPTO_KEY_SIZE],
|
|
const uint8_t *in, size_t in_len, uint8_t *out)
|
|
{
|
|
struct tc_aes_key_sched_struct sched;
|
|
struct tc_cmac_struct state;
|
|
|
|
/* TODO: Copy of the aes_cmac from smp.c: Can we merge them? */
|
|
|
|
if (tc_cmac_setup(&state, key, &sched) == TC_CRYPTO_FAIL) {
|
|
return -EIO;
|
|
}
|
|
|
|
if (tc_cmac_update(&state, in, in_len) == TC_CRYPTO_FAIL) {
|
|
return -EIO;
|
|
}
|
|
|
|
if (tc_cmac_final(out, &state) == TC_CRYPTO_FAIL) {
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void xor_128(const uint8_t a[16], const uint8_t b[16], uint8_t out[16])
|
|
{
|
|
size_t len = 16;
|
|
/* TODO: Identical to the xor_128 from smp.c: Move to util */
|
|
|
|
while (len--) {
|
|
*out++ = *a++ ^ *b++;
|
|
}
|
|
}
|
|
|
|
int bt_csip_sih(const uint8_t sirk[BT_CSIP_SET_SIRK_SIZE], uint8_t r[BT_CSIP_CRYPTO_PRAND_SIZE],
|
|
uint8_t out[BT_CSIP_CRYPTO_HASH_SIZE])
|
|
{
|
|
uint8_t res[BT_CSIP_PADDED_RAND_SIZE]; /* need to store 128 bit */
|
|
int err;
|
|
|
|
if ((r[BT_CSIP_CRYPTO_PRAND_SIZE - 1] & BIT(7)) ||
|
|
((r[BT_CSIP_CRYPTO_PRAND_SIZE - 1] & BIT(6)) == 0)) {
|
|
LOG_DBG("Invalid r %s", bt_hex(r, BT_CSIP_CRYPTO_PRAND_SIZE));
|
|
}
|
|
|
|
LOG_DBG("SIRK %s", bt_hex(sirk, BT_CSIP_SET_SIRK_SIZE));
|
|
LOG_DBG("r %s", bt_hex(r, BT_CSIP_CRYPTO_PRAND_SIZE));
|
|
|
|
/* r' = padding || r */
|
|
(void)memset(res + BT_CSIP_CRYPTO_PRAND_SIZE, 0, BT_CSIP_CRYPTO_PADDING_SIZE);
|
|
memcpy(res, r, BT_CSIP_CRYPTO_PRAND_SIZE);
|
|
|
|
LOG_DBG("r' %s", bt_hex(res, sizeof(res)));
|
|
|
|
err = bt_encrypt_le(sirk, res, res);
|
|
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
/* The output of the function sih is:
|
|
* sih(k, r) = e(k, r') mod 2^24
|
|
* The output of the security function e is then truncated to 24 bits
|
|
* by taking the least significant 24 bits of the output of e as the
|
|
* result of sih.
|
|
*/
|
|
|
|
LOG_DBG("res %s", bt_hex(res, sizeof(res)));
|
|
|
|
/* Result is the lowest 3 bytes */
|
|
memcpy(out, res, BT_CSIP_CRYPTO_HASH_SIZE);
|
|
|
|
LOG_DBG("sih %s", bt_hex(out, BT_CSIP_CRYPTO_HASH_SIZE));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief k1 derivation function
|
|
*
|
|
* The key derivation function k1 is used to derive a key. The derived key is
|
|
* used to encrypt and decrypt the value of the Set Identity Resolving Key
|
|
* characteristic.
|
|
*
|
|
* @param n n is 0 or more bytes.
|
|
* @param n_size Number of bytes in @p n.
|
|
* @param salt A 16-byte salt.
|
|
* @param p p is 0 or more bytes.
|
|
* @param p_size Number of bytes in @p p.
|
|
* @param out A 16-byte output buffer.
|
|
* @return int 0 on success, any other value indicates a failure.
|
|
*/
|
|
static int k1(const uint8_t *n, size_t n_size,
|
|
const uint8_t salt[BT_CSIP_CRYPTO_SALT_SIZE],
|
|
const uint8_t *p, size_t p_size, uint8_t out[16])
|
|
{
|
|
/* TODO: This is basically a duplicate of bt_mesh_k1 - Perhaps they can
|
|
* be merged
|
|
*/
|
|
uint8_t t[16];
|
|
int err;
|
|
|
|
/*
|
|
* T = AES_CMAC_SALT(N)
|
|
*
|
|
* k1(N, SALT, P) = AES-CMAC_T(P)
|
|
*/
|
|
|
|
LOG_DBG("BE: n %s", bt_hex(n, n_size));
|
|
LOG_DBG("BE: salt %s", bt_hex(salt, BT_CSIP_CRYPTO_SALT_SIZE));
|
|
LOG_DBG("BE: p %s", bt_hex(p, p_size));
|
|
|
|
err = aes_cmac(salt, n, n_size, t);
|
|
|
|
LOG_DBG("BE: t %s", bt_hex(t, sizeof(t)));
|
|
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
err = aes_cmac(t, p, p_size, out);
|
|
|
|
LOG_DBG("BE: out %s", bt_hex(out, 16));
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* @brief s1 SALT generation function
|
|
*
|
|
* @param m A non-zero length octet array or ASCII encoded string
|
|
* @param m_size Size of @p m.
|
|
* @param out 16-byte output buffer.
|
|
* @return int 0 on success, any other value indicates a failure.
|
|
*/
|
|
static int s1(const uint8_t *m, size_t m_size,
|
|
uint8_t out[BT_CSIP_CRYPTO_SALT_SIZE])
|
|
{
|
|
uint8_t zero[16];
|
|
int err;
|
|
|
|
/*
|
|
* s1(M) = AES-CMAC_zero(M)
|
|
*/
|
|
|
|
LOG_DBG("BE: m %s", bt_hex(m, m_size));
|
|
|
|
memset(zero, 0, sizeof(zero));
|
|
|
|
err = aes_cmac(zero, m, m_size, out);
|
|
|
|
LOG_DBG("BE: out %s", bt_hex(out, 16));
|
|
|
|
return err;
|
|
}
|
|
|
|
int bt_csip_sef(const uint8_t k[BT_CSIP_CRYPTO_KEY_SIZE],
|
|
const uint8_t sirk[BT_CSIP_SET_SIRK_SIZE],
|
|
uint8_t out_sirk[BT_CSIP_SET_SIRK_SIZE])
|
|
{
|
|
const uint8_t m[] = {'S', 'I', 'R', 'K', 'e', 'n', 'c'};
|
|
const uint8_t p[] = {'c', 's', 'i', 's'};
|
|
uint8_t s1_out[BT_CSIP_CRYPTO_SALT_SIZE];
|
|
uint8_t k1_out[BT_CSIP_CRYPTO_KEY_SIZE];
|
|
uint8_t k1_tmp[BT_CSIP_CRYPTO_KEY_SIZE];
|
|
int err;
|
|
|
|
/*
|
|
* sef(K, SIRK) = k1(K, s1("SIRKenc"), "csis") ^ SIRK
|
|
*/
|
|
|
|
LOG_DBG("SIRK %s", bt_hex(sirk, BT_CSIP_SET_SIRK_SIZE));
|
|
|
|
if (IS_ENABLED(CONFIG_LITTLE_ENDIAN)) {
|
|
/* Swap because aes_cmac is big endian
|
|
* and we are little endian
|
|
*/
|
|
sys_memcpy_swap(k1_tmp, k, sizeof(k1_tmp));
|
|
} else {
|
|
(void)memcpy(k1_tmp, k, sizeof(k1_tmp));
|
|
}
|
|
LOG_DBG("BE: k %s", bt_hex(k1_tmp, sizeof(k1_tmp)));
|
|
|
|
err = s1(m, sizeof(m), s1_out);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
LOG_DBG("BE: s1 result %s", bt_hex(s1_out, sizeof(s1_out)));
|
|
|
|
err = k1(k1_tmp, sizeof(k1_tmp), s1_out, p, sizeof(p), k1_out);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
LOG_DBG("BE: k1 result %s", bt_hex(k1_out, sizeof(k1_out)));
|
|
|
|
if (IS_ENABLED(CONFIG_LITTLE_ENDIAN)) {
|
|
/* Swap result back to little endian */
|
|
sys_mem_swap(k1_out, sizeof(k1_out));
|
|
}
|
|
|
|
xor_128(k1_out, sirk, out_sirk);
|
|
LOG_DBG("out %s", bt_hex(out_sirk, BT_CSIP_SET_SIRK_SIZE));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_csip_sdf(const uint8_t k[BT_CSIP_CRYPTO_KEY_SIZE],
|
|
const uint8_t enc_sirk[BT_CSIP_SET_SIRK_SIZE],
|
|
uint8_t out_sirk[BT_CSIP_SET_SIRK_SIZE])
|
|
{
|
|
/* SIRK encryption is currently symmetric, which means that we can
|
|
* simply apply the sef function to decrypt it.
|
|
*/
|
|
|
|
/*
|
|
* sdf(K, EncSIRK) = k1(K, s1("SIRKenc"), "csis") ^ EncSIRK
|
|
*/
|
|
|
|
LOG_DBG("Running SDF as SEF");
|
|
return bt_csip_sef(k, enc_sirk, out_sirk);
|
|
}
|