mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-04-16 08:42:28 +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 random_address_update_t rau_state;
|
||||||
static bd_addr_t sm_random_address;
|
static bd_addr_t sm_random_address;
|
||||||
|
|
||||||
// CMAC calculation
|
// CMAC Calculation: General
|
||||||
static cmac_state_t sm_cmac_state;
|
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 uint16_t sm_cmac_message_len;
|
||||||
static uint8_t * sm_cmac_message;
|
static sm_key_t sm_cmac_k;
|
||||||
static uint8_t sm_cmac_sign_counter[4];
|
|
||||||
static sm_key_t sm_cmac_m_last;
|
|
||||||
static sm_key_t sm_cmac_x;
|
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_current;
|
||||||
static uint8_t sm_cmac_block_count;
|
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
|
// resolvable private address lookup / CSRK calculation
|
||||||
static int sm_address_resolution_test;
|
static int sm_address_resolution_test;
|
||||||
@ -912,14 +920,19 @@ static inline void dkg_next_state(void){
|
|||||||
static inline void rau_next_state(void){
|
static inline void rau_next_state(void){
|
||||||
rau_state = (random_address_update_t) (((int)rau_state) + 1);
|
rau_state = (random_address_update_t) (((int)rau_state) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CMAC calculation using AES Engine
|
||||||
|
|
||||||
static inline void sm_cmac_next_state(void){
|
static inline void sm_cmac_next_state(void){
|
||||||
sm_cmac_state = (cmac_state_t) (((int)sm_cmac_state) + 1);
|
sm_cmac_state = (cmac_state_t) (((int)sm_cmac_state) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sm_cmac_last_block_complete(void){
|
static int sm_cmac_last_block_complete(void){
|
||||||
if (sm_cmac_message_len == 0) return 0;
|
if (sm_cmac_message_len == 0) return 0;
|
||||||
return (sm_cmac_message_len & 0x0f) == 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) {
|
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);
|
log_error("sm_cmac_message_get_byte. out of bounds, access %u, len %u", offset, sm_cmac_message_len);
|
||||||
return 0;
|
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];
|
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])){
|
// generic cmac calculation
|
||||||
memcpy(sm_cmac_k, k, 16);
|
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])){
|
||||||
sm_cmac_header[0] = opcode;
|
// Generalized CMAC
|
||||||
little_endian_store_16(sm_cmac_header, 1, con_handle);
|
memcpy(sm_cmac_k, key, 16);
|
||||||
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;
|
|
||||||
memset(sm_cmac_x, 0, 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);
|
// step 2: n := ceil(len/const_Bsize);
|
||||||
sm_cmac_block_count = (sm_cmac_message_len + 15) / 16;
|
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;
|
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
|
// first, we need to compute l for k1, k2, and m_last
|
||||||
sm_cmac_state = CMAC_CALC_SUBKEYS;
|
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();
|
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){
|
int sm_cmac_ready(void){
|
||||||
return sm_cmac_state == CMAC_IDLE;
|
return sm_cmac_state == CMAC_IDLE;
|
||||||
}
|
}
|
||||||
@ -983,7 +1006,7 @@ static void sm_cmac_handle_aes_engine_ready(void){
|
|||||||
int j;
|
int j;
|
||||||
sm_key_t y;
|
sm_key_t y;
|
||||||
for (j=0;j<16;j++){
|
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_block_current++;
|
||||||
sm_cmac_next_state();
|
sm_cmac_next_state();
|
||||||
@ -1032,13 +1055,13 @@ static void sm_cmac_handle_encryption_result(sm_key_t data){
|
|||||||
int i;
|
int i;
|
||||||
if (sm_cmac_last_block_complete()){
|
if (sm_cmac_last_block_complete()){
|
||||||
for (i=0;i<16;i++){
|
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 {
|
} else {
|
||||||
int valid_octets_in_last_block = sm_cmac_message_len & 0x0f;
|
int valid_octets_in_last_block = sm_cmac_message_len & 0x0f;
|
||||||
for (i=0;i<16;i++){
|
for (i=0;i<16;i++){
|
||||||
if (i < valid_octets_in_last_block){
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
if (i == valid_octets_in_last_block){
|
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.
|
* @brief Support for signed writes, used by att_server.
|
||||||
* @note Message is in little endian to allows passing in ATT PDU without flipping.
|
* @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);
|
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
|
* @brief Match address against bonded devices
|
||||||
|
@ -121,7 +121,6 @@ uint8_t * mock_packet_buffer(void);
|
|||||||
uint16_t mock_packet_buffer_len(void);
|
uint16_t mock_packet_buffer_len(void);
|
||||||
void mock_clear_packet_buffer(void);
|
void mock_clear_packet_buffer(void);
|
||||||
|
|
||||||
|
|
||||||
void app_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
void app_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||||
uint16_t aHandle;
|
uint16_t aHandle;
|
||||||
bd_addr_t event_address;
|
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 const char * key_string = "2b7e1516 28aed2a6 abf71588 09cf4f3c";
|
||||||
static uint8_t cmac_hash[8];
|
|
||||||
static void cmac_done(uint8_t hash[8]){
|
static const char * m0_string = "";
|
||||||
memcpy(cmac_hash, hash, 8);
|
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("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){
|
TEST_GROUP(SecurityManager){
|
||||||
void setup(void){
|
void setup(void){
|
||||||
btstack_memory_init();
|
static int first = 1;
|
||||||
btstack_run_loop_init(btstack_run_loop_posix_get_instance());
|
if (first){
|
||||||
|
first = 0;
|
||||||
|
btstack_memory_init();
|
||||||
|
btstack_run_loop_init(btstack_run_loop_posix_get_instance());
|
||||||
|
}
|
||||||
sm_init();
|
sm_init();
|
||||||
sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
|
sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
|
||||||
sm_set_authentication_requirements( SM_AUTHREQ_BONDING );
|
sm_set_authentication_requirements( SM_AUTHREQ_BONDING );
|
||||||
@ -208,6 +253,42 @@ TEST_GROUP(SecurityManager){
|
|||||||
sm_add_event_handler(&sm_event_callback_registration); }
|
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){
|
TEST(SecurityManager, MainTest){
|
||||||
|
|
||||||
mock_init();
|
mock_init();
|
||||||
@ -356,19 +437,9 @@ TEST(SecurityManager, MainTest){
|
|||||||
|
|
||||||
// expect send LE SMP Code Signing Information Command
|
// expect send LE SMP Code Signing Information Command
|
||||||
CHECK_ACL_PACKET(test_acl_packet_22);
|
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[]){
|
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);
|
return CommandLineTestRunner::RunAllTests(argc, argv);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user