From 7943d41d9cf65fa9d2db5d274b51f96152b2b6e2 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 | 2 + nimble/host/src/ble_sm.c | 104 ++++++++++++++++++++++ nimble/host/src/ble_sm_alg.c | 133 +++++++++++++++++++++++++++++ nimble/host/src/ble_sm_priv.h | 13 +++ nimble/host/test/src/ble_sm_test.c | 98 +++++++++++++++++++++ 5 files changed, 350 insertions(+) diff --git a/nimble/host/include/host/ble_sm.h b/nimble/host/include/host/ble_sm.h index ff381cf25b..899ab158dd 100644 --- a/nimble/host/include/host/ble_sm.h +++ b/nimble/host/include/host/ble_sm.h @@ -111,6 +111,8 @@ struct ble_sm_io { }; int ble_sm_sc_oob_generate_data(struct ble_sm_sc_oob_data *oob_data); +int ble_sm_csis_resolve_rsi(ble_addr_t peer_addr, uint8_t *rsi, + uint8_t *sirk, bool is_encrypted); #if NIMBLE_BLE_SM int ble_sm_inject_io(uint16_t conn_handle, struct ble_sm_io *pkey); diff --git a/nimble/host/src/ble_sm.c b/nimble/host/src/ble_sm.c index 831cf0fcb2..9b68118147 100644 --- a/nimble/host/src/ble_sm.c +++ b/nimble/host/src/ble_sm.c @@ -2910,4 +2910,108 @@ ble_sm_create_chan(uint16_t conn_handle) return chan; } +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(ble_addr_t peer_addr, uint8_t *rsi, + uint8_t *sirk, bool is_encrypted) +{ + 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; + + memset(&key_sec, 0, sizeof key_sec); + key_sec.peer_addr.type = peer_addr.type; + memcpy(key_sec.peer_addr.val, peer_addr.val, 6); + + 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; + } + + memcpy(hash, rsi, 3); + memcpy(prand, rsi + 3, 3); + + if (is_encrypted) { + rc = ble_sm_csis_decrypt_sirk(value_sec.ltk, sirk, plaintext_sirk); + swap_buf(plaintext_sirk, plaintext_sirk, 16); + 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 0; + } else { + return BLE_HS_EAUTHEN; + } +} + +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] &= ~(0x80); + 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 diff --git a/nimble/host/src/ble_sm_alg.c b/nimble/host/src/ble_sm_alg.c index 282a2b13f6..040ee03b75 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}; + + /* TODO: 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_EUNKNOWN; + } + + 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}; + + /* TODO: 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_EUNKNOWN; + } + + /* 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..d475a1b1bc 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/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();