From acae6a50938382db104596a37d9c9ec141b9d22d Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 14 Aug 2015 16:00:07 +0200 Subject: [PATCH 1/7] sm: use public address for le device db lookup --- ble/sm.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/ble/sm.c b/ble/sm.c index 1506e1d62..43cdeffdd 100644 --- a/ble/sm.c +++ b/ble/sm.c @@ -798,7 +798,8 @@ static int sm_key_distribution_all_received(sm_connection_t * sm_conn){ } else { // master / initiator recv_flags = sm_key_distribution_flags_for_set(setup->sm_s_pres.responder_key_distribution); - } + } + log_debug("sm_key_distribution_all_received: received 0x%02x, expecting 0x%02x", setup->sm_key_distribution_received_set, recv_flags); return recv_flags == setup->sm_key_distribution_received_set; } @@ -823,6 +824,8 @@ static void sm_init_setup(sm_connection_t * sm_conn){ // fill in sm setup sm_reset_tk(); + setup->sm_peer_addr_type = sm_conn->sm_peer_addr_type; + memcpy(setup->sm_peer_address, sm_conn->sm_peer_address, 6); // query client for OOB data int have_oob_data = 0; @@ -2119,6 +2122,27 @@ static void sm_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *pac } } + // if no IRK available, lookup via public address if possible + log_info("sm peer addr type %u, peer addres %s", setup->sm_peer_addr_type, bd_addr_to_str(setup->sm_peer_address)); + if (sm_conn->sm_le_db_index < 0 && setup->sm_peer_addr_type == BD_ADDR_TYPE_LE_PUBLIC){ + int i; + for (i=0; i < le_device_db_count(); i++){ + bd_addr_t address; + int address_type; + le_device_db_info(i, &address_type, address, NULL); + log_info("device %u, sm peer addr type %u, peer addres %s", i, address_type, bd_addr_to_str(address)); + if (address_type == BD_ADDR_TYPE_LE_PUBLIC && memcmp(address, setup->sm_peer_address, 6) == 0){ + log_info("sm: device found for public address, updating"); + sm_conn->sm_le_db_index = i; + break; + } + } + // if not found, add to db + if (sm_conn->sm_le_db_index < 0) { + sm_conn->sm_le_db_index = le_device_db_add(setup->sm_peer_addr_type, setup->sm_peer_address, setup->sm_peer_irk); + } + } + if (sm_conn->sm_le_db_index >= 0){ le_device_db_local_counter_set(sm_conn->sm_le_db_index, 0); From 5b1061f9e80d26ab1b342ff2e88f8c4c422811aa Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 14 Aug 2015 18:33:14 +0200 Subject: [PATCH 2/7] fix bug retrieving encryption info --- ble/le_device_db_memory.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ble/le_device_db_memory.c b/ble/le_device_db_memory.c index 57db80b1e..a57cdfcb5 100644 --- a/ble/le_device_db_memory.c +++ b/ble/le_device_db_memory.c @@ -126,7 +126,8 @@ void le_device_db_info(int index, int * addr_type, bd_addr_t addr, sm_key_t irk) } void le_device_db_encryption_set(int index, uint16_t ediv, uint8_t rand[8], sm_key_t ltk, int key_size, int authenticated, int authorized){ - log_info("Central Device DB set encryption for %u, ediv x%04x, key size %u, authenticated %u, authorized %u", index, ediv, key_size, authenticated, authorized); + log_info("Central Device DB set encryption for %u, ediv x%04x, key size %u, authenticated %u, authorized %u", + index, ediv, key_size, authenticated, authorized); le_device_memory_db_t * device = &le_devices[index]; device->ediv = ediv; if (rand) memcpy(device->rand, rand, 8); @@ -138,13 +139,14 @@ void le_device_db_encryption_set(int index, uint16_t ediv, uint8_t rand[8], sm_k void le_device_db_encryption_get(int index, uint16_t * ediv, uint8_t rand[8], sm_key_t ltk, int * key_size, int * authenticated, int * authorized){ le_device_memory_db_t * device = &le_devices[index]; - log_info("Central Device DB encryption for %u, ediv x%04x", index, device->ediv); + log_info("Central Device DB encryption for %u, ediv x%04x, keysize %u, authenticated %u, authorized %u", + index, device->ediv, device->key_size, device->authenticated, device->authorized); if (ediv) *ediv = device->ediv; if (rand) memcpy(rand, device->rand, 8); if (ltk) memcpy(ltk, device->ltk, 16); if (key_size) *key_size = device->key_size; - if (authenticated) *key_size = device->authenticated; - if (authorized) *key_size = device->authorized; + if (authenticated) *authenticated = device->authenticated; + if (authorized) *authorized = device->authorized; } // get signature key From 78b8321e18ed036bb046fada1fa590812e1e23cc Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 14 Aug 2015 18:36:44 +0200 Subject: [PATCH 3/7] sm: central uses stored ltk only when requested by client or peripheral --- ble/sm.c | 107 +++++++++++++++++++++++++++++++++--------------------- src/hci.h | 3 +- 2 files changed, 67 insertions(+), 43 deletions(-) diff --git a/ble/sm.c b/ble/sm.c index 43cdeffdd..2a5cc4a05 100644 --- a/ble/sm.c +++ b/ble/sm.c @@ -907,39 +907,35 @@ static void sm_address_resolution_handle_event(address_resolution_event_t event) sm_address_resolution_test = -1; sm_connection_t * sm_connection; + uint16_t ediv; switch (mode){ case ADDRESS_RESOLUTION_GENERAL: - switch (event){ - case ADDRESS_RESOLUTION_SUCEEDED: - // sm_address_resolution_succeeded_for_advertisement(); - break; - case ADDRESS_RESOLUTION_FAILED: - // sm_address_resolution_succeeded_for_advertisement(); - break; - } break; case ADDRESS_RESOLUTION_FOR_CONNECTION: sm_connection = (sm_connection_t *) context; switch (event){ case ADDRESS_RESOLUTION_SUCEEDED: - // use stored LTK/EDIV/RAND if we're master and have a valid ediv/random/ltk combination - sm_connection->sm_le_db_index = matched_device_id; - if (sm_connection->sm_role == 0){ - uint16_t ediv; - le_device_db_encryption_get(sm_connection->sm_le_db_index, &ediv, NULL, NULL, NULL, NULL, NULL); - if (ediv){ - log_info("sm: Setting up previous ltk/ediv/rand for device index %u", sm_connection->sm_le_db_index); - sm_connection->sm_engine_state = SM_INITIATOR_PH0_HAS_LTK; - } - } sm_connection->sm_csrk_lookup_state = CSRK_LOOKUP_SUCCEEDED; + sm_connection->sm_le_db_index = matched_device_id; + log_info("ADDRESS_RESOLUTION_SUCEEDED, index %d", sm_connection->sm_le_db_index); + if (sm_connection->sm_role) break; + if (!sm_connection->sm_bonding_requested && !sm_connection->sm_security_request_received) break; + sm_connection->sm_security_request_received = 0; + sm_connection->sm_bonding_requested = 0; + le_device_db_encryption_get(sm_connection->sm_le_db_index, &ediv, NULL, NULL, NULL, NULL, NULL); + if (ediv){ + sm_connection->sm_engine_state = SM_INITIATOR_PH0_HAS_LTK; + } else { + sm_connection->sm_engine_state = SM_INITIATOR_PH1_W2_SEND_PAIRING_REQUEST; + } break; case ADDRESS_RESOLUTION_FAILED: sm_connection->sm_csrk_lookup_state = CSRK_LOOKUP_FAILED; - if (sm_connection->sm_security_request_received) { - sm_connection->sm_security_request_received = 0; - sm_connection->sm_engine_state = SM_INITIATOR_PH1_W2_SEND_PAIRING_REQUEST; - } + if (sm_connection->sm_role) break; + if (!sm_connection->sm_bonding_requested && !sm_connection->sm_security_request_received) break; + sm_connection->sm_security_request_received = 0; + sm_connection->sm_bonding_requested = 0; + sm_connection->sm_engine_state = SM_INITIATOR_PH1_W2_SEND_PAIRING_REQUEST; break; } break; @@ -1152,6 +1148,7 @@ static void sm_run(void){ // fetch data from device db - incl. authenticated/authorized/key size. Note all sm_connection_X require encryption enabled le_device_db_encryption_get(sm_connection->sm_le_db_index, &setup->sm_peer_ediv, setup->sm_peer_rand, setup->sm_peer_ltk, &encryption_key_size, &authenticated, &authorized); + log_info("db index %u, key size %u, authenticated %u, authorized %u", sm_connection->sm_le_db_index, encryption_key_size, authenticated, authorized); sm_connection->sm_actual_encryption_key_size = encryption_key_size; sm_connection->sm_connection_authenticated = authenticated; sm_connection->sm_connection_authorization_state = authorized ? AUTHORIZATION_GRANTED : AUTHORIZATION_UNKNOWN; @@ -1956,11 +1953,21 @@ static void sm_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *pac sm_pdu_received_in_wrong_state(sm_conn); break; } - // if csrk over, but we didn't have LTK, start pairing - if (sm_conn->sm_csrk_lookup_state == CSRK_LOOKUP_FAILED || sm_conn->sm_csrk_lookup_state == CSRK_LOOKUP_SUCCEEDED){ + if (sm_conn->sm_csrk_lookup_state == CSRK_LOOKUP_FAILED){ sm_conn->sm_engine_state = SM_INITIATOR_PH1_W2_SEND_PAIRING_REQUEST; break; } + if (sm_conn->sm_csrk_lookup_state == CSRK_LOOKUP_SUCCEEDED){ + uint16_t ediv; + le_device_db_encryption_get(sm_conn->sm_le_db_index, &ediv, NULL, NULL, NULL, NULL, NULL); + if (ediv){ + log_info("sm: Setting up previous ltk/ediv/rand for device index %u", sm_conn->sm_le_db_index); + sm_conn->sm_engine_state = SM_INITIATOR_PH0_HAS_LTK; + } else { + sm_conn->sm_engine_state = SM_INITIATOR_PH1_W2_SEND_PAIRING_REQUEST; + } + break; + } // otherwise, store security request sm_conn->sm_security_request_received = 1; break; @@ -2100,8 +2107,7 @@ static void sm_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *pac } // done with key distribution? if (sm_key_distribution_all_received(sm_conn)){ - - sm_conn->sm_le_db_index = -1; + int le_db_index = -1; if (setup->sm_key_distribution_received_set & SM_KEYDIST_FLAG_IDENTITY_INFORMATION){ // lookup device based on IRK int i; @@ -2112,19 +2118,19 @@ static void sm_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *pac le_device_db_info(i, &address_type, address, irk); if (memcmp(irk, setup->sm_peer_irk, 16) == 0){ log_info("sm: device found for IRK, updating"); - sm_conn->sm_le_db_index = i; + le_db_index = i; break; } } // if not found, add to db - if (sm_conn->sm_le_db_index < 0) { - sm_conn->sm_le_db_index = le_device_db_add(setup->sm_peer_addr_type, setup->sm_peer_address, setup->sm_peer_irk); + if (le_db_index < 0) { + le_db_index = le_device_db_add(setup->sm_peer_addr_type, setup->sm_peer_address, setup->sm_peer_irk); } } // if no IRK available, lookup via public address if possible log_info("sm peer addr type %u, peer addres %s", setup->sm_peer_addr_type, bd_addr_to_str(setup->sm_peer_address)); - if (sm_conn->sm_le_db_index < 0 && setup->sm_peer_addr_type == BD_ADDR_TYPE_LE_PUBLIC){ + if (le_db_index < 0 && setup->sm_peer_addr_type == BD_ADDR_TYPE_LE_PUBLIC){ int i; for (i=0; i < le_device_db_count(); i++){ bd_addr_t address; @@ -2133,31 +2139,31 @@ static void sm_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *pac log_info("device %u, sm peer addr type %u, peer addres %s", i, address_type, bd_addr_to_str(address)); if (address_type == BD_ADDR_TYPE_LE_PUBLIC && memcmp(address, setup->sm_peer_address, 6) == 0){ log_info("sm: device found for public address, updating"); - sm_conn->sm_le_db_index = i; + le_db_index = i; break; } } // if not found, add to db - if (sm_conn->sm_le_db_index < 0) { - sm_conn->sm_le_db_index = le_device_db_add(setup->sm_peer_addr_type, setup->sm_peer_address, setup->sm_peer_irk); + if (le_db_index < 0) { + le_db_index = le_device_db_add(setup->sm_peer_addr_type, setup->sm_peer_address, setup->sm_peer_irk); } } - if (sm_conn->sm_le_db_index >= 0){ - le_device_db_local_counter_set(sm_conn->sm_le_db_index, 0); + if (le_db_index >= 0){ + le_device_db_local_counter_set(le_db_index, 0); // store CSRK if (setup->sm_key_distribution_received_set & SM_KEYDIST_FLAG_SIGNING_IDENTIFICATION){ log_info("sm: set csrk"); - le_device_db_csrk_set(sm_conn->sm_le_db_index, setup->sm_peer_csrk); - le_device_db_remote_counter_set(sm_conn->sm_le_db_index, 0); + le_device_db_csrk_set(le_db_index, setup->sm_peer_csrk); + le_device_db_remote_counter_set(le_db_index, 0); } // store encryption information if (setup->sm_key_distribution_received_set & SM_KEYDIST_FLAG_ENCRYPTION_INFORMATION && setup->sm_key_distribution_received_set & SM_KEYDIST_FLAG_MASTER_IDENTIFICATION){ - log_info("sm: set encryption information"); - le_device_db_encryption_set(sm_conn->sm_le_db_index, setup->sm_peer_ediv, setup->sm_peer_rand, setup->sm_peer_ltk, + log_info("sm: set encryption information (key size %u, authenticatd %u)", sm_conn->sm_actual_encryption_key_size, sm_conn->sm_connection_authenticated); + le_device_db_encryption_set(le_db_index, setup->sm_peer_ediv, setup->sm_peer_rand, setup->sm_peer_ltk, sm_conn->sm_actual_encryption_key_size, sm_conn->sm_connection_authenticated, sm_conn->sm_connection_authorization_state == AUTHORIZATION_GRANTED); } } @@ -2318,13 +2324,30 @@ void sm_request_authorization(uint8_t addr_type, bd_addr_t address){ log_info("sm_request_authorization in role %u, state %u", sm_conn->sm_role, sm_conn->sm_engine_state); if (sm_conn->sm_role){ - // code has no effect so far + // code has no effect so far - what's the difference between sm_send_security_request and this in slave role sm_conn->sm_connection_authorization_state = AUTHORIZATION_PENDING; sm_notify_client(SM_AUTHORIZATION_REQUEST, sm_conn->sm_peer_addr_type, sm_conn->sm_peer_address, 0, 0); } else { // used as a trigger to start central/master/initiator security procedures - if (sm_conn->sm_engine_state == SM_INITIATOR_CONNECTED){ - sm_conn->sm_engine_state = SM_INITIATOR_PH1_W2_SEND_PAIRING_REQUEST; + uint16_t ediv; + if (sm_conn->sm_engine_state == SM_INITIATOR_CONNECTED){ + switch (sm_conn->sm_csrk_lookup_state){ + case CSRK_LOOKUP_FAILED: + sm_conn->sm_engine_state = SM_INITIATOR_PH1_W2_SEND_PAIRING_REQUEST; + break; + case CSRK_LOOKUP_SUCCEEDED: + le_device_db_encryption_get(sm_conn->sm_le_db_index, &ediv, NULL, NULL, NULL, NULL, NULL); + if (ediv){ + log_info("sm: Setting up previous ltk/ediv/rand for device index %u", sm_conn->sm_le_db_index); + sm_conn->sm_engine_state = SM_INITIATOR_PH0_HAS_LTK; + } else { + sm_conn->sm_engine_state = SM_INITIATOR_PH1_W2_SEND_PAIRING_REQUEST; + } + break; + default: + sm_conn->sm_bonding_requested = 1; + break; + } } } sm_run(); diff --git a/src/hci.h b/src/hci.h index db571a7b2..df27f6e6f 100644 --- a/src/hci.h +++ b/src/hci.h @@ -465,8 +465,9 @@ typedef struct sm_connection { uint16_t sm_handle; uint8_t sm_role; // 0 - IamMaster, 1 = IamSlave uint8_t sm_security_request_received; - bd_addr_t sm_peer_address; + uint8_t sm_bonding_requested; uint8_t sm_peer_addr_type; + bd_addr_t sm_peer_address; security_manager_state_t sm_engine_state; csrk_lookup_state_t sm_csrk_lookup_state; uint8_t sm_connection_encrypted; From 5fcb62a185268cc001041208b13e72524fdbfd4b Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Wed, 19 Aug 2015 12:57:38 +0200 Subject: [PATCH 4/7] reset state after CMAC calculation --- ble/sm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ble/sm.c b/ble/sm.c index 2a5cc4a05..b33d1fed3 100644 --- a/ble/sm.c +++ b/ble/sm.c @@ -746,6 +746,7 @@ static void sm_cmac_handle_encryption_result(sm_key_t data){ // done log_key("CMAC", data); sm_cmac_done_handler(data); + sm_cmac_state = CMAC_IDLE; break; default: log_info("sm_cmac_handle_encryption_result called in state %u", sm_cmac_state); From 4a2d13bbabcee040792c8d477ab597c852385890 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Wed, 19 Aug 2015 12:58:04 +0200 Subject: [PATCH 5/7] log valid cmac signature check, too --- ble/att_server.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ble/att_server.c b/ble/att_server.c index 78368b031..aa06a1d5e 100644 --- a/ble/att_server.c +++ b/ble/att_server.c @@ -216,6 +216,7 @@ static void att_signed_write_handle_cmac_result(uint8_t hash[8]){ att_server_state = ATT_SERVER_IDLE; return; } + log_info("ATT Signed Write, valid signature"); // update sequence number uint32_t counter_packet = READ_BT_32(att_request_buffer, att_request_size-12); From 291a8b9a5858e5bea2a876db301673bf2aed1b8c Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Wed, 19 Aug 2015 12:58:33 +0200 Subject: [PATCH 6/7] mark characteristic for signed writes --- test/pts/profile.gatt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pts/profile.gatt b/test/pts/profile.gatt index 783a43993..73901e9d3 100644 --- a/test/pts/profile.gatt +++ b/test/pts/profile.gatt @@ -6,4 +6,4 @@ PRIMARY_SERVICE, GATT_SERVICE CHARACTERISTIC, GATT_SERVICE_CHANGED, READ, PRIMARY_SERVICE, FFF0 -CHARACTERISTIC, FFF1, READ | WRITE | AUTHENTICATION_REQUIRED, "A" +CHARACTERISTIC, FFF1, READ | WRITE | AUTHENTICATED_SIGNED_WRITE | AUTHENTICATION_REQUIRED, "A" From ad78015fbd9ab7ab1467909305d4cb6e3a99afe2 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Wed, 19 Aug 2015 12:58:44 +0200 Subject: [PATCH 7/7] allow for signed writes --- test/pts/ble_central_test.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/test/pts/ble_central_test.c b/test/pts/ble_central_test.c index 76ac8a781..b15ac065c 100644 --- a/test/pts/ble_central_test.c +++ b/test/pts/ble_central_test.c @@ -358,13 +358,24 @@ void handle_gatt_client_event(le_event_t * event){ break; case CENTRAL_W4_PERIPHERAL_PRIVACY_FLAG_QUERY_COMPLETE: central_state = CENTRAL_IDLE; - gatt_client_write_value_of_characteristic(gc_id, handle, gap_peripheral_privacy_flag_characteristic.value_handle, 1, &pts_privacy_flag); - if (pts_privacy_flag){ - printf("Peripheral Privacy Flag set to TRUE\n"); - } else { - use_public_pts_address(); - printf("Peripheral Privacy Flag set to FALSE, connecting to public PTS address again\n"); - } + switch (pts_privacy_flag){ + case 0: + use_public_pts_address(); + printf("Peripheral Privacy Flag set to FALSE, connecting to public PTS address again\n"); + gatt_client_write_value_of_characteristic(gc_id, handle, gap_peripheral_privacy_flag_characteristic.value_handle, 1, &pts_privacy_flag); + break; + case 1: + printf("Peripheral Privacy Flag set to TRUE\n"); + gatt_client_write_value_of_characteristic(gc_id, handle, gap_peripheral_privacy_flag_characteristic.value_handle, 1, &pts_privacy_flag); + break; + case 2: + printf("Signed write on Peripheral Privacy Flag to TRUE\n"); + pts_privacy_flag = 1; + gatt_client_signed_write_without_response(gc_id, handle, gap_peripheral_privacy_flag_characteristic.value_handle, 1, &pts_privacy_flag); + break; + default: + break; + } break; default: break; @@ -429,6 +440,7 @@ void show_usage(void){ printf("t - terminate connection, stop connecting\n"); printf("p - auto connect to PTS\n"); printf("P - direct connect to PTS\n"); + printf("w - signed write on Privacy Flag\n"); printf("z - Update L2CAP Connection Parameters\n"); printf("---\n"); printf("4 - IO_CAPABILITY_DISPLAY_ONLY\n"); @@ -602,6 +614,11 @@ int stdin_process(struct data_source *ds){ gap_auto_connection_stop_all(); le_central_connect_cancel(); break; + case 'w': + pts_privacy_flag = 2; + central_state = CENTRAL_W4_PERIPHERAL_PRIVACY_FLAG_QUERY_COMPLETE; + gatt_client_discover_characteristics_for_handle_range_by_uuid16(gc_id, handle, 1, 0xffff, GAP_PERIPHERAL_PRIVACY_FLAG); + break; case 'z': printf("Updating l2cap connection parameters\n"); gap_update_connection_parameters(handle, 50, 120, 0, 550);