From 6ee663fbddf82ea84e51cc71b176a308ce6ac4d9 Mon Sep 17 00:00:00 2001 From: Michal Gorecki Date: Thu, 5 Oct 2023 10:44:23 +0200 Subject: [PATCH] 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. --- nimble/host/include/host/ble_sm.h | 19 +++++ nimble/host/src/ble_sm.c | 103 ++++++++++++++++++++++ nimble/host/src/ble_sm_alg.c | 133 +++++++++++++++++++++++++++++ nimble/host/src/ble_sm_priv.h | 13 +++ nimble/host/syscfg.yml | 5 ++ nimble/host/test/src/ble_sm_test.c | 98 +++++++++++++++++++++ nimble/host/test/syscfg.yml | 1 + 7 files changed, 372 insertions(+) diff --git a/nimble/host/include/host/ble_sm.h b/nimble/host/include/host/ble_sm.h index ff381cf25b..51e745f36c 100644 --- a/nimble/host/include/host/ble_sm.h +++ b/nimble/host/include/host/ble_sm.h @@ -22,6 +22,8 @@ #include #include "syscfg/syscfg.h" +#include +#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 diff --git a/nimble/host/src/ble_sm.c b/nimble/host/src/ble_sm.c index 831cf0fcb2..0b7c28db59 100644 --- a/nimble/host/src/ble_sm.c +++ b/nimble/host/src/ble_sm.c @@ -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 diff --git a/nimble/host/src/ble_sm_alg.c b/nimble/host/src/ble_sm_alg.c index 282a2b13f6..bf81f21ffb 100644 --- a/nimble/host/src/ble_sm_alg.c +++ b/nimble/host/src/ble_sm_alg.c @@ -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) diff --git a/nimble/host/src/ble_sm_priv.h b/nimble/host/src/ble_sm_priv.h index 5e761c1e1a..d8e1f0358e 100644 --- a/nimble/host/src/ble_sm_priv.h +++ b/nimble/host/src/ble_sm_priv.h @@ -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); diff --git a/nimble/host/syscfg.yml b/nimble/host/syscfg.yml index 8218b9e769..f667eee985 100644 --- a/nimble/host/syscfg.yml +++ b/nimble/host/syscfg.yml @@ -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: diff --git a/nimble/host/test/src/ble_sm_test.c b/nimble/host/test/src/ble_sm_test.c index be4285fdce..2ba1678d7b 100644 --- a/nimble/host/test/src/ble_sm_test.c +++ b/nimble/host/test/src/ble_sm_test.c @@ -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(); diff --git a/nimble/host/test/syscfg.yml b/nimble/host/test/syscfg.yml index cfb3a8178c..dfafb8e76b 100644 --- a/nimble/host/test/syscfg.yml +++ b/nimble/host/test/syscfg.yml @@ -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