mirror of
https://github.com/espressif/esp-nimble.git
synced 2026-11-12 07:20:33 +00:00
host/sm: Add CSIS SIRK encryption
This adds functions and algorithms to encrypt and decrypt SIRK from Coordinated Set Identification Service. This also adds API to resolve RSI. Application can use it to find devices that are part of Coordinated Set.
This commit is contained in:
committed by
Andrzej Kaczmarek
parent
861877636c
commit
f2cb1ec98e
@@ -22,6 +22,8 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "syscfg/syscfg.h"
|
||||
#include <stdbool.h>
|
||||
#include "nimble/ble.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -112,6 +114,23 @@ struct ble_sm_io {
|
||||
|
||||
int ble_sm_sc_oob_generate_data(struct ble_sm_sc_oob_data *oob_data);
|
||||
|
||||
#if MYNEWT_VAL(BLE_SM_CSIS_SIRK)
|
||||
/**
|
||||
* Resolves CSIS RSI to check if advertising device is part of the same Coordinated Set,
|
||||
* as the device with specified SIRK
|
||||
*
|
||||
* @param rsi RSI value from Advertising Data
|
||||
* @param sirk SIRK
|
||||
* @param ltk_peer_addr If SIRK is in plaintext form this should be set to NULL,
|
||||
* otherwise peer address should be passed here to get
|
||||
* LTK and decrypt SIRK
|
||||
*
|
||||
* @return 0 if RSI was resolved succesfully; nonzero on failure.
|
||||
*/
|
||||
int ble_sm_csis_resolve_rsi(const uint8_t *rsi, const uint8_t *sirk,
|
||||
const ble_addr_t *ltk_peer_addr);
|
||||
#endif
|
||||
|
||||
#if NIMBLE_BLE_SM
|
||||
int ble_sm_inject_io(uint16_t conn_handle, struct ble_sm_io *pkey);
|
||||
#else
|
||||
|
||||
@@ -2910,4 +2910,107 @@ ble_sm_create_chan(uint16_t conn_handle)
|
||||
return chan;
|
||||
}
|
||||
|
||||
#if MYNEWT_VAL(BLE_SM_CSIS_SIRK)
|
||||
int
|
||||
ble_sm_csis_decrypt_sirk(const uint8_t *ltk, const uint8_t *enc_sirk, uint8_t *out)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Decrypt SIRK with sdf(K, EncSIRK) */
|
||||
rc = ble_sm_alg_csis_sdf(ltk, enc_sirk, out);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
ble_sm_csis_resolve_rsi(const uint8_t *rsi, const uint8_t *sirk,
|
||||
const ble_addr_t *ltk_peer_addr)
|
||||
{
|
||||
struct ble_store_key_sec key_sec;
|
||||
struct ble_store_value_sec value_sec;
|
||||
uint8_t plaintext_sirk[16] = {0};
|
||||
uint8_t local_hash[3] = {0};
|
||||
uint8_t prand[3] = {0};
|
||||
uint8_t hash[3] = {0};
|
||||
int rc;
|
||||
|
||||
memcpy(hash, rsi, 3);
|
||||
memcpy(prand, rsi + 3, 3);
|
||||
|
||||
if (ltk_peer_addr) {
|
||||
memset(&key_sec, 0, sizeof(key_sec));
|
||||
key_sec.peer_addr = *ltk_peer_addr;
|
||||
|
||||
rc = ble_store_read_peer_sec(&key_sec, &value_sec);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
} else if (!value_sec.ltk_present) {
|
||||
return BLE_HS_ENOENT;
|
||||
}
|
||||
|
||||
rc = ble_sm_csis_decrypt_sirk(value_sec.ltk, sirk, plaintext_sirk);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
memcpy(plaintext_sirk, sirk, 16);
|
||||
}
|
||||
|
||||
rc = ble_sm_alg_csis_sih(plaintext_sirk, prand, local_hash);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (memcmp(local_hash, hash, 3)) {
|
||||
return BLE_HS_EAUTHEN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ble_sm_csis_encrypt_sirk(const uint8_t *ltk, const uint8_t *plaintext_sirk, uint8_t *out)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Encrypt SIRK with sef(K, SIRK) */
|
||||
rc = ble_sm_alg_csis_sef(ltk, plaintext_sirk, out);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
ble_sm_csis_generate_rsi(const uint8_t *sirk, uint8_t *out)
|
||||
{
|
||||
const uint8_t prand_check_all_set[3] = {0xff, 0xff, 0xef};
|
||||
const uint8_t prand_check_all_reset[3] = {0x0, 0x0, 0x40};
|
||||
uint8_t prand[3] = {0};
|
||||
uint8_t hash[3] = {0};
|
||||
int rc;
|
||||
|
||||
do {
|
||||
rc = ble_hs_hci_rand(prand, 3);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
/* Two MSBs of prand shall be equal to 0 and 1 */
|
||||
prand[2] &= ~0xc0;
|
||||
prand[2] |= 0x40;
|
||||
|
||||
/* prand's random part shall not be all 0s nor all 1s */
|
||||
} while (memcmp(prand, prand_check_all_set, 3) ||
|
||||
memcmp(prand, prand_check_all_reset, 3));
|
||||
|
||||
rc = ble_sm_alg_csis_sih(sirk, prand, hash);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
memcpy(out, hash, 3);
|
||||
memcpy(out + 3, prand, 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -418,6 +418,139 @@ ble_sm_alg_g2(const uint8_t *u, const uint8_t *v, const uint8_t *x,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ble_sm_alg_csis_k1(const uint8_t *n, size_t n_len, const uint8_t *salt,
|
||||
const uint8_t *p, size_t p_len, uint8_t *out)
|
||||
{
|
||||
int rc;
|
||||
uint8_t t[16] = {0};
|
||||
uint8_t salt_be[16] = {0};
|
||||
uint8_t n_be[16] = {0};
|
||||
|
||||
/* XXX: Spec does not specify the maximum N and P parameters length.
|
||||
* We assume that 16 bytes is enough and return error if passed len value is greater
|
||||
* than that */
|
||||
if ((n_len > 16) || (p_len > 16)) {
|
||||
return BLE_HS_EINVAL;
|
||||
}
|
||||
|
||||
swap_buf(salt_be, salt, 16);
|
||||
swap_buf(n_be, n, n_len);
|
||||
|
||||
/* T = AES-CMAC_SALT (N) */
|
||||
rc = ble_sm_alg_aes_cmac(salt_be, n_be, n_len, t);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* AES-CMAC_T (P) */
|
||||
rc = ble_sm_alg_aes_cmac(t, p, p_len, out);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
swap_in_place(out, 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ble_sm_alg_csis_s1(const uint8_t *m, size_t m_len, uint8_t *out)
|
||||
{
|
||||
int rc;
|
||||
uint8_t k_zero[16] = {0};
|
||||
|
||||
/* XXX: Spec does not specify the maximum M parameter length.
|
||||
* We assume that 16 bytes is enough and return error if passed len value is greater
|
||||
* than that */
|
||||
if (m_len > 16) {
|
||||
return BLE_HS_EINVAL;
|
||||
}
|
||||
|
||||
/* AES-CMAC_zero (M) */
|
||||
rc = ble_sm_alg_aes_cmac(k_zero, m, m_len, out);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
swap_in_place(out, 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ble_sm_alg_csis_sef(const uint8_t *k, const uint8_t *plaintext_sirk, uint8_t *out)
|
||||
{
|
||||
uint8_t salt[16];
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
/* s1("SIRKenc") */
|
||||
rc = ble_sm_alg_csis_s1((const uint8_t *) "SIRKenc", 7, salt);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* k1(K, s1("SIRKenc"), "csis") */
|
||||
rc = ble_sm_alg_csis_k1(k, 16, salt, (const uint8_t *) "csis", 4, out);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* k1(K, s1("SIRKenc"), "csis") ^ SIRK */
|
||||
for (i = 0; i < 16; i++) {
|
||||
out[i] ^= plaintext_sirk[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ble_sm_alg_csis_sdf(const uint8_t *k, const uint8_t *enc_sirk, uint8_t *out)
|
||||
{
|
||||
uint8_t salt[16];
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
/* s1("SIRKenc") */
|
||||
rc = ble_sm_alg_csis_s1((const uint8_t *) "SIRKenc", 7, salt);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* k1(K, s1("SIRKenc"), "csis") */
|
||||
rc = ble_sm_alg_csis_k1(k, 16, salt, (const uint8_t *) "csis", 4, out);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* k1(K, s1("SIRKenc"), "csis") ^ EncSIRK */
|
||||
for (i = 0; i < 16; i++) {
|
||||
out[i] ^= enc_sirk[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ble_sm_alg_csis_sih(const uint8_t *k, const uint8_t *r, uint8_t *out)
|
||||
{
|
||||
uint8_t r1[16];
|
||||
int rc;
|
||||
|
||||
memcpy(r1, r, 3);
|
||||
memset(r1 + 3, 0, 13);
|
||||
|
||||
rc = ble_sm_alg_encrypt(k, r1, r1);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
memcpy(out, r1, 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ble_sm_alg_gen_dhkey(const uint8_t *peer_pub_key_x, const uint8_t *peer_pub_key_y,
|
||||
const uint8_t *our_priv_key, uint8_t *out_dhkey)
|
||||
|
||||
@@ -314,12 +314,25 @@ int ble_sm_alg_f6(const uint8_t *w, const uint8_t *n1, const uint8_t *n2,
|
||||
const uint8_t *r, const uint8_t *iocap, uint8_t a1t,
|
||||
const uint8_t *a1, uint8_t a2t, const uint8_t *a2,
|
||||
uint8_t *check);
|
||||
int ble_sm_alg_csis_k1(const uint8_t *n, size_t n_len, const uint8_t *salt,
|
||||
const uint8_t *p, size_t p_len, uint8_t *out);
|
||||
int ble_sm_alg_csis_s1(const uint8_t *m, size_t m_len, uint8_t *out);
|
||||
int ble_sm_alg_csis_sef(const uint8_t *k, const uint8_t *plaintext_sirk,
|
||||
uint8_t *out);
|
||||
int ble_sm_alg_csis_sdf(const uint8_t *k, const uint8_t *enc_sirk,
|
||||
uint8_t *out);
|
||||
int ble_sm_alg_csis_sih(const uint8_t *k, const uint8_t *r, uint8_t *out);
|
||||
int ble_sm_alg_gen_dhkey(const uint8_t *peer_pub_key_x,
|
||||
const uint8_t *peer_pub_key_y,
|
||||
const uint8_t *our_priv_key, uint8_t *out_dhkey);
|
||||
int ble_sm_alg_gen_key_pair(uint8_t *pub, uint8_t *priv);
|
||||
void ble_sm_alg_ecc_init(void);
|
||||
|
||||
int ble_sm_csis_generate_rsi(const uint8_t *sirk, uint8_t *out);
|
||||
int ble_sm_csis_encrypt_sirk(const uint8_t *ltk, const uint8_t *plaintext_sirk,
|
||||
uint8_t *out);
|
||||
int ble_sm_csis_decrypt_sirk(const uint8_t *ltk, const uint8_t *enc_sirk, uint8_t *out);
|
||||
|
||||
void ble_sm_enc_change_rx(const struct ble_hci_ev_enrypt_chg *ev);
|
||||
void ble_sm_enc_key_refresh_rx(const struct ble_hci_ev_enc_key_refresh *ev);
|
||||
int ble_sm_ltk_req_rx(const struct ble_hci_ev_le_subev_lt_key_req *ev);
|
||||
|
||||
@@ -188,6 +188,11 @@ syscfg.defs:
|
||||
allows to decrypt air traffic easily and thus should be only used
|
||||
for debugging.
|
||||
value: 0
|
||||
BLE_SM_CSIS_SIRK:
|
||||
description: >
|
||||
Enable LE Audio CSIS SIRK Encryption and Decryption API.
|
||||
value: 0
|
||||
experimental: 1
|
||||
|
||||
# GAP options.
|
||||
BLE_GAP_MAX_PENDING_CONN_PARAM_UPDATE:
|
||||
|
||||
@@ -142,6 +142,99 @@ TEST_CASE_SELF(ble_sm_test_case_g2)
|
||||
ble_hs_test_util_assert_mbufs_freed(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE_SELF(ble_sm_test_case_csis_sih)
|
||||
{
|
||||
uint8_t sirk[16] = { 0xcd, 0xcc, 0x72, 0xdd, 0x86, 0x8c, 0xcd, 0xce,
|
||||
0x22, 0xfd, 0xa1, 0x21, 0x09, 0x7d, 0x7d, 0x45 };
|
||||
uint8_t prand[3] = { 0x63, 0xf5, 0x69 };
|
||||
const uint8_t ah_expected[3] = { 0xda, 0x48, 0x19 };
|
||||
uint8_t ah_out[3];
|
||||
int err;
|
||||
|
||||
err = ble_sm_alg_csis_sih(sirk, prand, ah_out);
|
||||
TEST_ASSERT_FATAL(err == 0);
|
||||
TEST_ASSERT(memcmp(ah_out, ah_expected, 3) == 0);
|
||||
|
||||
ble_hs_test_util_assert_mbufs_freed(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE_SELF(ble_sm_test_case_csis_sef)
|
||||
{
|
||||
uint8_t sirk[16] = { 0xcd, 0xcc, 0x72, 0xdd, 0x86, 0x8c, 0xcd, 0xce,
|
||||
0x22, 0xfd, 0xa1, 0x21, 0x09, 0x7d, 0x7d, 0x45 };
|
||||
uint8_t k[16] = { 0xd9, 0xce, 0xe5, 0x3c, 0x22, 0xc6, 0x1e, 0x06,
|
||||
0x6f, 0x69, 0x48, 0xd4, 0x9b, 0x1b, 0x6e, 0x67 };
|
||||
const uint8_t sef_expected[16] = { 0x46, 0xd3, 0x5f, 0xf2, 0xd5, 0x62, 0x25, 0x7e,
|
||||
0xa0, 0x24, 0x35, 0xe1, 0x35, 0x38, 0x0a, 0x17 };
|
||||
uint8_t sef_out[16];
|
||||
int err;
|
||||
|
||||
err = ble_sm_alg_csis_sef(k, sirk, sef_out);
|
||||
TEST_ASSERT_FATAL(err == 0);
|
||||
TEST_ASSERT(memcmp(sef_out, sef_expected, 16) == 0);
|
||||
|
||||
ble_hs_test_util_assert_mbufs_freed(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE_SELF(ble_sm_test_case_csis_s1_sirkenc)
|
||||
{
|
||||
const uint8_t s1_expected[16] = { 0x72, 0x45, 0x77, 0x7d, 0x3a, 0x13, 0x7d, 0x3c,
|
||||
0x82, 0x9e, 0x14, 0x18, 0x3f, 0x98, 0x01, 0x69 };
|
||||
uint8_t s1_out[16];
|
||||
int err;
|
||||
|
||||
err = ble_sm_alg_csis_s1((const uint8_t *) "SIRKenc", 7, s1_out);
|
||||
|
||||
TEST_ASSERT_FATAL(err == 0);
|
||||
TEST_ASSERT(memcmp(s1_out, s1_expected, 16) == 0);
|
||||
|
||||
ble_hs_test_util_assert_mbufs_freed(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE_SELF(ble_sm_test_case_csis_k1_csis)
|
||||
{
|
||||
uint8_t k[16] = { 0xd9, 0xce, 0xe5, 0x3c, 0x22, 0xc6, 0x1e, 0x06,
|
||||
0x6f, 0x69, 0x48, 0xd4, 0x9b, 0x1b, 0x6e, 0x67 };
|
||||
uint8_t k1_expected[16] = { 0x8b, 0x1f, 0x2d, 0x2f, 0x53, 0xee, 0xe8, 0xb0,
|
||||
0x82, 0xd9, 0x94, 0xc0, 0x3c, 0x45, 0x77, 0x52 };
|
||||
uint8_t k1_out[16];
|
||||
uint8_t s1_out[16];
|
||||
int err;
|
||||
|
||||
err = ble_sm_alg_csis_s1((const uint8_t *) "SIRKenc", 7, s1_out);
|
||||
TEST_ASSERT_FATAL(err == 0);
|
||||
|
||||
err = ble_sm_alg_csis_k1(k, 16, s1_out, (const uint8_t *) "csis", 4, k1_out);
|
||||
|
||||
TEST_ASSERT_FATAL(err == 0);
|
||||
TEST_ASSERT(memcmp(k1_out, k1_expected, 16) == 0);
|
||||
|
||||
ble_hs_test_util_assert_mbufs_freed(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE_SELF(ble_sm_test_case_csis_enc_dec_sirk)
|
||||
{
|
||||
uint8_t plaintext_sirk[16] = { 0xcd, 0xcc, 0x72, 0xdd, 0x86, 0x8c, 0xcd, 0xce,
|
||||
0x22, 0xfd, 0xa1, 0x21, 0x09, 0x7d, 0x7d, 0x45 };
|
||||
uint8_t k[16] = { 0xd9, 0xce, 0xe5, 0x3c, 0x22, 0xc6, 0x1e, 0x06,
|
||||
0x6f, 0x69, 0x48, 0xd4, 0x9b, 0x1b, 0x6e, 0x67 };
|
||||
uint8_t enc_sirk[16] = {0};
|
||||
uint8_t dec_sirk[16] = {0};
|
||||
uint8_t rsi[6] = {0x0, 0x0, 0x0, 0xab, 0x34, 0xef};
|
||||
int err;
|
||||
|
||||
/* Generate only hash part of rsi, as random part is already hard-coded */
|
||||
err = ble_sm_alg_csis_sih(plaintext_sirk, rsi + 3, rsi);
|
||||
TEST_ASSERT_FATAL(err == 0);
|
||||
|
||||
ble_sm_csis_encrypt_sirk(k, plaintext_sirk, enc_sirk);
|
||||
TEST_ASSERT_FATAL(err == 0);
|
||||
|
||||
ble_sm_csis_decrypt_sirk(k, enc_sirk, dec_sirk);
|
||||
TEST_ASSERT_FATAL(err == 0);
|
||||
TEST_ASSERT(memcmp(dec_sirk, plaintext_sirk, 16) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE_SELF(ble_sm_test_case_conn_broken)
|
||||
{
|
||||
struct ble_hci_ev_disconn_cmp disconn_evt;
|
||||
@@ -403,6 +496,11 @@ TEST_SUITE(ble_sm_gen_test_suite)
|
||||
ble_sm_test_case_f5();
|
||||
ble_sm_test_case_f6();
|
||||
ble_sm_test_case_g2();
|
||||
ble_sm_test_case_csis_sih();
|
||||
ble_sm_test_case_csis_sef();
|
||||
ble_sm_test_case_csis_s1_sirkenc();
|
||||
ble_sm_test_case_csis_k1_csis();
|
||||
ble_sm_test_case_csis_enc_dec_sirk();
|
||||
|
||||
ble_sm_test_case_peer_fail_inval();
|
||||
ble_sm_test_case_peer_lgcy_fail_confirm();
|
||||
|
||||
@@ -24,6 +24,7 @@ syscfg.vals:
|
||||
BLE_GATT_MAX_PROCS: 16
|
||||
BLE_SM: 1
|
||||
BLE_SM_SC: 1
|
||||
BLE_SM_CSIS_SIRK: 1
|
||||
MSYS_1_BLOCK_COUNT: 100
|
||||
BLE_L2CAP_COC_MAX_NUM: 2
|
||||
CONFIG_FCB: 1
|
||||
|
||||
Reference in New Issue
Block a user