diff --git a/src/ble/sm.c b/src/ble/sm.c index 1f4fc7119..499813b01 100644 --- a/src/ble/sm.c +++ b/src/ble/sm.c @@ -171,18 +171,26 @@ static derived_key_generation_t dkg_state; static random_address_update_t rau_state; static bd_addr_t sm_random_address; -// CMAC calculation +// CMAC Calculation: General static cmac_state_t sm_cmac_state; -static sm_key_t sm_cmac_k; -static uint8_t sm_cmac_header[3]; static uint16_t sm_cmac_message_len; -static uint8_t * sm_cmac_message; -static uint8_t sm_cmac_sign_counter[4]; -static sm_key_t sm_cmac_m_last; +static sm_key_t sm_cmac_k; static sm_key_t sm_cmac_x; +static sm_key_t sm_cmac_m_last; static uint8_t sm_cmac_block_current; static uint8_t sm_cmac_block_count; -static void (*sm_cmac_done_handler)(uint8_t hash[8]); +static uint8_t (*sm_cmac_get_byte)(uint16_t offset); +static void (*sm_cmac_done_handler)(uint8_t * hash); + +// CMAC for ATT Signed Writes +static uint8_t sm_cmac_header[3]; +static uint8_t * sm_cmac_message; +static uint8_t sm_cmac_sign_counter[4]; + +// CMAC for Secure Connection functions +#ifdef ENABLE_LE_SECURE_CONNECTIONS +static uint8_t sm_cmac_sc_buffer[80]; +#endif // resolvable private address lookup / CSRK calculation static int sm_address_resolution_test; @@ -912,14 +920,19 @@ static inline void dkg_next_state(void){ static inline void rau_next_state(void){ rau_state = (random_address_update_t) (((int)rau_state) + 1); } + +// CMAC calculation using AES Engine + static inline void sm_cmac_next_state(void){ sm_cmac_state = (cmac_state_t) (((int)sm_cmac_state) + 1); } + static int sm_cmac_last_block_complete(void){ if (sm_cmac_message_len == 0) return 0; return (sm_cmac_message_len & 0x0f) == 0; } -static inline uint8_t sm_cmac_message_get_byte(int offset){ + +static inline uint8_t sm_cmac_message_get_byte(uint16_t offset){ if (offset >= sm_cmac_message_len) { log_error("sm_cmac_message_get_byte. out of bounds, access %u, len %u", offset, sm_cmac_message_len); return 0; @@ -938,16 +951,15 @@ static inline uint8_t sm_cmac_message_get_byte(int offset){ return sm_cmac_sign_counter[offset - actual_message_len_incl_header]; } -void sm_cmac_start(sm_key_t k, uint8_t opcode, hci_con_handle_t con_handle, uint16_t message_len, uint8_t * message, uint32_t sign_counter, void (*done_handler)(uint8_t hash[8])){ - memcpy(sm_cmac_k, k, 16); - sm_cmac_header[0] = opcode; - little_endian_store_16(sm_cmac_header, 1, con_handle); - little_endian_store_32(sm_cmac_sign_counter, 0, sign_counter); - sm_cmac_message_len = 3 + message_len + 4; // incl. virtually prepended att opcode, handle and appended sign_counter in LE - sm_cmac_message = message; - sm_cmac_done_handler = done_handler; - sm_cmac_block_current = 0; +// generic cmac calculation +void sm_cmac_general_start(sm_key_t key, uint16_t message_len, uint8_t (*get_byte_callback)(uint16_t offset), void (*done_callback)(uint8_t hash[8])){ + // Generalized CMAC + memcpy(sm_cmac_k, key, 16); memset(sm_cmac_x, 0, 16); + sm_cmac_block_current = 0; + sm_cmac_message_len = message_len; + sm_cmac_done_handler = done_callback; + sm_cmac_get_byte = get_byte_callback; // step 2: n := ceil(len/const_Bsize); sm_cmac_block_count = (sm_cmac_message_len + 15) / 16; @@ -957,7 +969,7 @@ void sm_cmac_start(sm_key_t k, uint8_t opcode, hci_con_handle_t con_handle, uint sm_cmac_block_count = 1; } - log_info("sm_cmac_start: len %u, block count %u", sm_cmac_message_len, sm_cmac_block_count); + log_info("sm_cmac_general_start: len %u, block count %u", sm_cmac_message_len, sm_cmac_block_count); // first, we need to compute l for k1, k2, and m_last sm_cmac_state = CMAC_CALC_SUBKEYS; @@ -966,6 +978,17 @@ void sm_cmac_start(sm_key_t k, uint8_t opcode, hci_con_handle_t con_handle, uint sm_run(); } +// cmac for ATT Message signing +void sm_cmac_start(sm_key_t k, uint8_t opcode, hci_con_handle_t con_handle, uint16_t message_len, uint8_t * message, uint32_t sign_counter, void (*done_handler)(uint8_t * hash)){ + // ATT Message Signing + sm_cmac_header[0] = opcode; + little_endian_store_16(sm_cmac_header, 1, con_handle); + little_endian_store_32(sm_cmac_sign_counter, 0, sign_counter); + uint16_t total_message_len = 3 + message_len + 4; // incl. virtually prepended att opcode, handle and appended sign_counter in LE + sm_cmac_message = message; + sm_cmac_general_start(k, total_message_len, &sm_cmac_message_get_byte, done_handler); +} + int sm_cmac_ready(void){ return sm_cmac_state == CMAC_IDLE; } @@ -983,7 +1006,7 @@ static void sm_cmac_handle_aes_engine_ready(void){ int j; sm_key_t y; for (j=0;j<16;j++){ - y[j] = sm_cmac_x[j] ^ sm_cmac_message_get_byte(sm_cmac_block_current*16 + j); + y[j] = sm_cmac_x[j] ^ sm_cmac_get_byte(sm_cmac_block_current*16 + j); } sm_cmac_block_current++; sm_cmac_next_state(); @@ -1032,13 +1055,13 @@ static void sm_cmac_handle_encryption_result(sm_key_t data){ int i; if (sm_cmac_last_block_complete()){ for (i=0;i<16;i++){ - sm_cmac_m_last[i] = sm_cmac_message_get_byte(sm_cmac_message_len - 16 + i) ^ k1[i]; + sm_cmac_m_last[i] = sm_cmac_get_byte(sm_cmac_message_len - 16 + i) ^ k1[i]; } } else { int valid_octets_in_last_block = sm_cmac_message_len & 0x0f; for (i=0;i<16;i++){ if (i < valid_octets_in_last_block){ - sm_cmac_m_last[i] = sm_cmac_message_get_byte((sm_cmac_message_len & 0xfff0) + i) ^ k2[i]; + sm_cmac_m_last[i] = sm_cmac_get_byte((sm_cmac_message_len & 0xfff0) + i) ^ k2[i]; continue; } if (i == valid_octets_in_last_block){ diff --git a/src/ble/sm.h b/src/ble/sm.h index 0579f923f..6b687dd25 100644 --- a/src/ble/sm.h +++ b/src/ble/sm.h @@ -232,10 +232,27 @@ void sm_authorization_grant(hci_con_handle_t con_handle); /** * @brief Support for signed writes, used by att_server. * @note Message is in little endian to allows passing in ATT PDU without flipping. - * @note calculated hash in done_callback is big endian + * @note signing data: [opcode, attribute_handle, message, sign_counter] + * @note calculated hash in done_callback is big endian and has 16 byte. + * @param key + * @param opcde + * @param attribute_handle + * @param message_len + * @param message + * @param sign_counter */ int sm_cmac_ready(void); -void sm_cmac_start(sm_key_t k, uint8_t opcode, uint16_t attribute_handle, uint16_t message_len, uint8_t * message, uint32_t sign_counter, void (*done_handler)(uint8_t hash[8])); +void sm_cmac_start(sm_key_t key, uint8_t opcode, uint16_t attribute_handle, uint16_t message_len, uint8_t * message, uint32_t sign_counter, void (*done_callback)(uint8_t * hash)); + +/* + * @brief Generic CMAC AES + * @param key + * @param message_len + * @param get_byte_callback + * @param done_callback + * @note hash is 16 bytes in big endian + */ +void sm_cmac_general_start(sm_key_t key, uint16_t message_len, uint8_t (*get_byte_callback)(uint16_t offset), void (*done_callback)(uint8_t * hash)); /* * @brief Match address against bonded devices diff --git a/test/security_manager/security_manager.c b/test/security_manager/security_manager.c index d71720747..1d82016c1 100644 --- a/test/security_manager/security_manager.c +++ b/test/security_manager/security_manager.c @@ -121,7 +121,6 @@ uint8_t * mock_packet_buffer(void); uint16_t mock_packet_buffer_len(void); void mock_clear_packet_buffer(void); - void app_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ uint16_t aHandle; bd_addr_t event_address; @@ -190,17 +189,63 @@ static int parse_hex(uint8_t * buffer, const char * hex_string){ } static const char * key_string = "2b7e1516 28aed2a6 abf71588 09cf4f3c"; -static uint8_t cmac_hash[8]; -static void cmac_done(uint8_t hash[8]){ - memcpy(cmac_hash, hash, 8); + +static const char * m0_string = ""; +static const char * cmac_m0_string = "bb1d6929 e9593728 7fa37d12 9b756746"; +static const char * m16_string = "6bc1bee2 2e409f96 e93d7e11 7393172a"; +static const char * cmac_m16_string = "070a16b4 6b4d4144 f79bdd9d d04a287c"; +static const char * m40_string = "6bc1bee2 2e409f96 e93d7e11 7393172a ae2d8a57 1e03ac9c 9eb76fac 45af8e51 30c81c46 a35ce411"; +static const char * cmac_m40_string = "dfa66747 de9ae630 30ca3261 1497c827"; +static const char * m64_string = "6bc1bee2 2e409f96 e93d7e11 7393172a ae2d8a57 1e03ac9c 9eb76fac 45af8e51 30c81c46 a35ce411 e5fbc119 1a0a52ef f69f2445 df4f9b17 ad2b417b e66c3710"; +static const char * cmac_m64_string = "51f0bebf 7e3b9d92 fc497417 79363cfe"; + +static uint8_t cmac_hash[16]; +static int cmac_hash_received; +static void cmac_done(uint8_t * hash){ + memcpy(cmac_hash, hash, 16); printf("cmac hash: "); - printf_hexdump(hash, 8); + printf_hexdump(hash, 16); + cmac_hash_received = 1; } +static uint8_t m[128]; +static uint8_t get_byte(uint16_t offset){ + // printf ("get byte %02u -> %02x\n", offset, m[offset]); + return m[offset]; +} +static void validate_message(const char * name, const char * message_string, const char * cmac_string){ + + mock_clear_packet_buffer(); + int len = parse_hex(m, message_string); + + // expected result + sm_key_t cmac; + parse_hex(cmac, cmac_string); + + printf("-- verify key %s message %s, len %u:\nm: %s\ncmac: %s\n", key_string, name, len, message_string, cmac_string); + + sm_key_t key; + parse_hex(key, key_string); + // printf_hexdump(key, 16); + + cmac_hash_received = 0; + sm_cmac_general_start(key, len, &get_byte, &cmac_done); + while (!cmac_hash_received){ + aes128_report_result(); + } + CHECK_EQUAL_ARRAY(cmac, cmac_hash, 16); +} + +#define VALIDATE_MESSAGE(NAME) validate_message(#NAME, NAME##_string, cmac_##NAME##_string) + TEST_GROUP(SecurityManager){ void setup(void){ - btstack_memory_init(); - btstack_run_loop_init(btstack_run_loop_posix_get_instance()); + static int first = 1; + if (first){ + first = 0; + btstack_memory_init(); + btstack_run_loop_init(btstack_run_loop_posix_get_instance()); + } sm_init(); sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT); sm_set_authentication_requirements( SM_AUTHREQ_BONDING ); @@ -208,6 +253,42 @@ TEST_GROUP(SecurityManager){ sm_add_event_handler(&sm_event_callback_registration); } }; +TEST(SecurityManager, CMACTest){ + + mock_init(); + mock_simulate_hci_state_working(); + + // expect le encrypt commmand + CHECK_HCI_COMMAND(test_command_packet_01); + + aes128_report_result(); + + // expect le encrypt commmand + CHECK_HCI_COMMAND(test_command_packet_02); + + aes128_report_result(); + mock_clear_packet_buffer(); + + // additional test: cmac signing + // aes cmac tests + sm_key_t key; + parse_hex(key, key_string); + uint8_t message [] = "hallo"; + cmac_hash_received = 0; + sm_cmac_start(key, 0x11, 0x1234, sizeof(message), message, 1, &cmac_done); + while (!cmac_hash_received){ + aes128_report_result(); + } + uint8_t expected_hash[] = { 0x40, 0x4E, 0xDC, 0x0F, 0x6E, 0x0F, 0xF9, 0x5C}; + CHECK_EQUAL_ARRAY(expected_hash, cmac_hash, 8); + + // generic aes cmac tests + VALIDATE_MESSAGE(m0); + VALIDATE_MESSAGE(m16); + VALIDATE_MESSAGE(m40); + VALIDATE_MESSAGE(m64); +} + TEST(SecurityManager, MainTest){ mock_init(); @@ -356,19 +437,9 @@ TEST(SecurityManager, MainTest){ // expect send LE SMP Code Signing Information Command CHECK_ACL_PACKET(test_acl_packet_22); - - // additional test: cmac signing - sm_key_t key; - parse_hex(key, key_string); - uint8_t message [] = "hallo"; - sm_cmac_start(key, 0x11, 0x1234, sizeof(message), message, 1, &cmac_done); - aes128_report_result(); - aes128_report_result(); - uint8_t expected_hash[] = { 0x5F, 0xE6, 0x86, 0x3E, 0xF3, 0x45, 0xD8, 0x43}; - CHECK_EQUAL_ARRAY(expected_hash, cmac_hash, 8); } int main (int argc, const char * argv[]){ - hci_dump_open("hci_dump.pklg", HCI_DUMP_PACKETLOGGER); + // hci_dump_open("hci_dump.pklg", HCI_DUMP_PACKETLOGGER); return CommandLineTestRunner::RunAllTests(argc, argv); }