sm: add sm_cmac_general_start for aes_cmac calculation in secure connection functions

This commit is contained in:
Matthias Ringwald 2016-05-27 16:40:37 +02:00
parent 74c92e7149
commit 514d35fc5d
3 changed files with 152 additions and 41 deletions

View File

@ -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){

View File

@ -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

View File

@ -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);
}