mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-04-15 23:42:52 +00:00
sm: add sm_cmac_general_start for aes_cmac calculation in secure connection functions
This commit is contained in:
parent
74c92e7149
commit
514d35fc5d
65
src/ble/sm.c
65
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){
|
||||
|
21
src/ble/sm.h
21
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
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user