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