From f4b33574b8b8a983cf5ba955722d7d498d69564f Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Wed, 16 May 2018 12:34:33 +0200 Subject: [PATCH] gatt_client: if ENABLE_GATT_CLIENT_PAIRING, GATT Client starts pairing and retry operation on security error --- CHANGELOG.md | 1 + doc/manual/docs/how_to.md | 1 + src/ble/gatt_client.c | 148 +++++++++++++++++++++++++++++++++++--- src/ble/gatt_client.h | 12 +++- 4 files changed, 152 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 796011576..0a44271b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - ATT Server: wait until re-encryption is complete - GATT Client: wait until re-encryption is complete - ATT Server: added att_server_request_to_send_notification and att_server_request_to_send_indication +- GATT Client: if ENABLE_GATT_CLIENT_PAIRING, GATT Client starts pairing and retry operation on security error ### Changed - ATT Server: att_server_register_can_send_now_callback is deprecated, use att_server_request_to_send_notification/indication instead diff --git a/doc/manual/docs/how_to.md b/doc/manual/docs/how_to.md index 55ec2b13e..88d57e772 100644 --- a/doc/manual/docs/how_to.md +++ b/doc/manual/docs/how_to.md @@ -80,6 +80,7 @@ ENABLE_HFP_WIDE_BAND_SPEECH | Enable support for mSBC codec used in HFP pro ENBALE_LE_PERIPHERAL | Enable support for LE Peripheral Role in HCI and Security Manager ENBALE_LE_CENTRAL | Enable support for LE Central Role in HCI and Security Manager ENABLE_LE_SECURE_CONNECTIONS | Enable LE Secure Connections +ENABLE_GATT_CLIENT_PAIRING | Enable GATT Client to start pairing and retry operation on security error ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS | Use [micro-ecc library](https://github.com/kmackay/micro-ecc) for ECC operations ENABLE_LE_DATA_CHANNELS | Enable LE Data Channels in credit-based flow control mode ENABLE_LE_DATA_LENGTH_EXTENSION | Enable LE Data Length Extension support diff --git a/src/ble/gatt_client.c b/src/ble/gatt_client.c index 33de09d33..2b51c5189 100644 --- a/src/ble/gatt_client.c +++ b/src/ble/gatt_client.c @@ -66,10 +66,14 @@ static btstack_linked_list_t gatt_client_connections; static btstack_linked_list_t gatt_client_value_listeners; static btstack_packet_callback_registration_t hci_event_callback_registration; +#ifdef ENABLE_GATT_CLIENT_PAIRING +static btstack_packet_callback_registration_t sm_event_callback_registration; +#endif + static uint8_t mtu_exchange_enabled; static void gatt_client_att_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *packet, uint16_t size); -static void gatt_client_hci_event_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); +static void gatt_client_event_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); static void gatt_client_report_error_if_pending(gatt_client_t *peripheral, uint8_t error_code); #ifdef ENABLE_LE_SIGNED_WRITE @@ -87,10 +91,17 @@ static uint16_t peripheral_mtu(gatt_client_t *peripheral){ void gatt_client_init(void){ gatt_client_connections = NULL; mtu_exchange_enabled = 1; + // regsister for HCI Events - hci_event_callback_registration.callback = &gatt_client_hci_event_packet_handler; + hci_event_callback_registration.callback = &gatt_client_event_packet_handler; hci_add_event_handler(&hci_event_callback_registration); +#ifdef ENABLE_GATT_CLIENT_PAIRING + // register for SM Events + sm_event_callback_registration.callback = &gatt_client_event_packet_handler; + sm_add_event_handler(&sm_event_callback_registration); +#endif + // and ATT Client PDUs att_dispatch_register_client(gatt_client_att_packet_handler); } @@ -825,6 +836,11 @@ static int gatt_client_run_for_peripheral( gatt_client_t * peripheral){ // wait until re-encryption as central is complete if (gap_reconnect_security_setup_active(peripheral->con_handle)) return 0; +#ifdef ENABLE_GATT_CLIENT_PAIRING + // wait until pairing complete + if (peripheral->wait_for_pairing_complete) return 0; +#endif + switch (peripheral->mtu_state) { case SEND_MTU_EXCHANGE: peripheral->mtu_state = SENT_MTU_EXCHANGE; @@ -1052,25 +1068,46 @@ static void gatt_client_report_error_if_pending(gatt_client_t *peripheral, uint8 emit_gatt_complete_event(peripheral, error_code); } -static void gatt_client_hci_event_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ +static void gatt_client_event_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ UNUSED(channel); // ok: handling own l2cap events UNUSED(size); // ok: there is no channel if (packet_type != HCI_EVENT_PACKET) return; + hci_con_handle_t con_handle; + gatt_client_t * peripheral; switch (hci_event_packet_get_type(packet)) { case HCI_EVENT_DISCONNECTION_COMPLETE: - { log_info("GATT Client: HCI_EVENT_DISCONNECTION_COMPLETE"); - hci_con_handle_t con_handle = little_endian_read_16(packet,3); - gatt_client_t * peripheral = get_gatt_client_context_for_handle(con_handle); + con_handle = little_endian_read_16(packet,3); + peripheral = get_gatt_client_context_for_handle(con_handle); if (!peripheral) break; gatt_client_report_error_if_pending(peripheral, ATT_ERROR_HCI_DISCONNECT_RECEIVED); btstack_linked_list_remove(&gatt_client_connections, (btstack_linked_item_t *) peripheral); btstack_memory_gatt_client_free(peripheral); break; - } + +#ifdef ENABLE_GATT_CLIENT_PAIRING + // Pairing complete (with/without bonding=storing of pairing information) + case SM_EVENT_PAIRING_COMPLETE: + con_handle = sm_event_pairing_complete_get_handle(packet); + peripheral = get_gatt_client_context_for_handle(con_handle); + if (!peripheral) break; + + if (peripheral->wait_for_pairing_complete){ + peripheral->wait_for_pairing_complete = 0; + if (sm_event_pairing_complete_get_status(packet)){ + log_info("pairing failed, report previous error 0x%x", peripheral->pending_error_code); + gatt_client_handle_transaction_complete(peripheral); + emit_gatt_complete_event(peripheral, peripheral->pending_error_code); + } else { + log_info("pairing success, retry operation"); + } + } + break; +#endif + default: break; } @@ -1401,7 +1438,102 @@ static void gatt_client_att_packet_handler(uint8_t packet_type, uint16_t handle, } break; } - default: + +#ifdef ENABLE_GATT_CLIENT_PAIRING + + case ATT_ERROR_INSUFFICIENT_AUTHENTICATION: + case ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE: + case ATT_ERROR_INSUFFICIENT_ENCRYPTION: + // security too low + if (peripheral->security_counter > 0) { + gatt_client_report_error_if_pending(peripheral, packet[4]); + break; + } + // start security + peripheral->security_counter++; + + // setup action + int retry = 1; + switch (peripheral->gatt_client_state){ + case P_W4_READ_CHARACTERISTIC_VALUE_RESULT: + peripheral->gatt_client_state = P_W2_SEND_READ_CHARACTERISTIC_VALUE_QUERY ; + break; + case P_W4_READ_BLOB_RESULT: + peripheral->gatt_client_state = P_W2_SEND_READ_BLOB_QUERY; + break; + case P_W4_READ_BY_TYPE_RESPONSE: + peripheral->gatt_client_state = P_W2_SEND_READ_BY_TYPE_REQUEST; + break; + case P_W4_READ_MULTIPLE_RESPONSE: + peripheral->gatt_client_state = P_W2_SEND_READ_MULTIPLE_REQUEST; + break; + case P_W4_WRITE_CHARACTERISTIC_VALUE_RESULT: + peripheral->gatt_client_state = P_W2_SEND_WRITE_CHARACTERISTIC_VALUE; + break; + case P_W4_PREPARE_WRITE_RESULT: + peripheral->gatt_client_state = P_W2_PREPARE_WRITE; + break; + case P_W4_PREPARE_WRITE_SINGLE_RESULT: + peripheral->gatt_client_state = P_W2_PREPARE_WRITE_SINGLE; + break; + case P_W4_PREPARE_RELIABLE_WRITE_RESULT: + peripheral->gatt_client_state = P_W2_PREPARE_RELIABLE_WRITE; + break; + case P_W4_EXECUTE_PREPARED_WRITE_RESULT: + peripheral->gatt_client_state = P_W2_EXECUTE_PREPARED_WRITE; + break; + case P_W4_CANCEL_PREPARED_WRITE_RESULT: + peripheral->gatt_client_state = P_W2_CANCEL_PREPARED_WRITE; + break; + case P_W4_CANCEL_PREPARED_WRITE_DATA_MISMATCH_RESULT: + peripheral->gatt_client_state = P_W2_CANCEL_PREPARED_WRITE_DATA_MISMATCH; + break; + case P_W4_READ_CHARACTERISTIC_DESCRIPTOR_RESULT: + peripheral->gatt_client_state = P_W2_SEND_READ_CHARACTERISTIC_DESCRIPTOR_QUERY; + break; + case P_W4_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_RESULT: + peripheral->gatt_client_state = P_W2_SEND_READ_BLOB_CHARACTERISTIC_DESCRIPTOR_QUERY; + break; + case P_W4_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT: + peripheral->gatt_client_state = P_W2_SEND_WRITE_CHARACTERISTIC_DESCRIPTOR; + break; + case P_W4_CLIENT_CHARACTERISTIC_CONFIGURATION_RESULT: + peripheral->gatt_client_state = P_W2_WRITE_CLIENT_CHARACTERISTIC_CONFIGURATION; + break; + case P_W4_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT: + peripheral->gatt_client_state = P_W2_PREPARE_WRITE_CHARACTERISTIC_DESCRIPTOR; + break; + case P_W4_EXECUTE_PREPARED_WRITE_CHARACTERISTIC_DESCRIPTOR_RESULT: + peripheral->gatt_client_state = P_W2_EXECUTE_PREPARED_WRITE_CHARACTERISTIC_DESCRIPTOR; + break; +#ifdef ENABLE_LE_SIGNED_WRITE + case P_W4_SEND_SINGED_WRITE_DONE: + peripheral->gatt_client_state = P_W2_SEND_SIGNED_WRITE; + break; +#endif + default: + log_info("retry not supported for state %x", peripheral->gatt_client_state); + retry = 0; + break; + } + + if (!retry) { + gatt_client_report_error_if_pending(peripheral, packet[4]); + break; + } + + log_info("security error, start pairing"); + + // requrest pairing + peripheral->wait_for_pairing_complete = 1; + peripheral->pending_error_code = packet[4]; + sm_request_pairing(peripheral->con_handle); + break; +#endif + + // nothing we can do about that + case ATT_ERROR_INSUFFICIENT_AUTHORIZATION: + default: gatt_client_report_error_if_pending(peripheral, packet[4]); break; } diff --git a/src/ble/gatt_client.h b/src/ble/gatt_client.h index ff201699b..ab621bb18 100644 --- a/src/ble/gatt_client.h +++ b/src/ble/gatt_client.h @@ -145,7 +145,8 @@ typedef struct gatt_client{ uint8_t address_type; bd_addr_t address; - uint16_t mtu; + + uint16_t mtu; gatt_client_mtu_t mtu_state; uint16_t uuid16; @@ -174,11 +175,18 @@ typedef struct gatt_client{ uint8_t filter_with_uuid; uint8_t send_confirmation; - + int le_device_index; uint8_t cmac[8]; btstack_timer_source_t gc_timeout; + +#ifdef ENABLE_GATT_CLIENT_PAIRING + uint8_t security_counter; + uint8_t wait_for_pairing_complete; + uint8_t pending_error_code; +#endif + } gatt_client_t; typedef struct gatt_client_notification {