diff --git a/src/ble/gatt-service/bond_management_service_server.c b/src/ble/gatt-service/bond_management_service_server.c index 01047e6cc..cb9b85bb6 100644 --- a/src/ble/gatt-service/bond_management_service_server.c +++ b/src/ble/gatt-service/bond_management_service_server.c @@ -40,6 +40,7 @@ #include "bluetooth.h" #include "btstack_defines.h" #include "hci.h" +#include "gap.h" #include "btstack_util.h" #include "btstack_debug.h" @@ -50,21 +51,6 @@ #include "ble/gatt-service/bond_management_service_server.h" -#define BOND_MANAGEMENT_CONTROL_POINT_OPCODE_NOT_SUPPORTED 0x80 -#define BOND_MANAGEMENT_OPERATION_FAILED 0x81 - -typedef enum { - BOND_MANAGEMENT_CMD_DELETE_ACTIVE_BOND_CLASSIC_AND_LE = 0x01, - BOND_MANAGEMENT_CMD_DELETE_ACTIVE_BOND_CLASSIC, - BOND_MANAGEMENT_CMD_DELETE_ACTIVE_BOND_LE, - BOND_MANAGEMENT_CMD_DELETE_ALL_BONDS_CLASSIC_AND_LE, - BOND_MANAGEMENT_CMD_DELETE_ALL_BONDS_CLASSIC, - BOND_MANAGEMENT_CMD_DELETE_ALL_BONDS_LE, - BOND_MANAGEMENT_CMD_DELETE_ALL_BUT_ACTIVE_BOND_CLASSIC_AND_LE, - BOND_MANAGEMENT_CMD_DELETE_ALL_BUT_ACTIVE_BOND_CLASSIC, - BOND_MANAGEMENT_CMD_DELETE_ALL_BUT_ACTIVE_BOND_LE -} bond_management_cmd_t; - // characteristic: Control Point static uint16_t bm_control_point_value_handle; @@ -118,11 +104,11 @@ static void bond_management_delete_bonding_information_le(hci_connection_t * con if ((entry_address_type == (int) device_address_type) && (memcmp(entry_address, connection->address, 6) == 0)){ if (delete_own_bonding){ - gap_delete_bonding(entry_address_type, entry_address); + gap_delete_bonding((bd_addr_type_t)entry_address_type, entry_address); } } else { if (delete_all_bonding_but_active){ - gap_delete_bonding(entry_address_type, entry_address); + gap_delete_bonding((bd_addr_type_t)entry_address_type, entry_address); } } } @@ -175,25 +161,28 @@ static int bond_management_service_write_callback(hci_con_handle_t con_handle, u if (cmd > BOND_MANAGEMENT_CMD_DELETE_ALL_BUT_ACTIVE_BOND_LE) { return BOND_MANAGEMENT_CONTROL_POINT_OPCODE_NOT_SUPPORTED; } - uint16_t authorisation_code_size = buffer_size - 1; if (authorisation_code_size > 511){ return BOND_MANAGEMENT_OPERATION_FAILED; } - uint8_t auth_provided = authorisation_code_size > 0 ? 1 : 0; + uint8_t auth_provided = authorisation_code_size > 0 ? 1 : 0; uint32_t requested_feature_mask = 1UL << (2*(cmd-1) + auth_provided); + if ((bm_supported_features & requested_feature_mask) == 0){ // abort, feature not allowed return BOND_MANAGEMENT_CONTROL_POINT_OPCODE_NOT_SUPPORTED; } if (auth_provided == 1){ + if (!bm_authorization_string){ + return ATT_ERROR_INSUFFICIENT_AUTHORIZATION; + } if (strlen(bm_authorization_string) != authorisation_code_size){ - return BOND_MANAGEMENT_OPERATION_FAILED; + return ATT_ERROR_INSUFFICIENT_AUTHORIZATION; } if (memcmp(bm_authorization_string, (const char *)&buffer[1], authorisation_code_size) != 0){ - return BOND_MANAGEMENT_OPERATION_FAILED; + return ATT_ERROR_INSUFFICIENT_AUTHORIZATION; } } diff --git a/src/ble/gatt-service/bond_management_service_server.h b/src/ble/gatt-service/bond_management_service_server.h index 46c539f9b..d2c4b0a68 100644 --- a/src/ble/gatt-service/bond_management_service_server.h +++ b/src/ble/gatt-service/bond_management_service_server.h @@ -59,6 +59,8 @@ extern "C" { */ /* API_START */ +#define BOND_MANAGEMENT_CONTROL_POINT_OPCODE_NOT_SUPPORTED 0x80 +#define BOND_MANAGEMENT_OPERATION_FAILED 0x81 #define BMF_DELETE_ACTIVE_BOND_CLASSIC_AND_LE 0x00001 #define BMF_DELETE_ACTIVE_BOND_CLASSIC_AND_LE_WITH_AUTH 0x00002 @@ -79,6 +81,18 @@ extern "C" { #define BMF_DELETE_ALL_BUT_ACTIVE_BOND_LE 0x10000 #define BMF_DELETE_ALL_BUT_ACTIVE_BOND_LE_WITH_AUT 0x20000 +typedef enum { + BOND_MANAGEMENT_CMD_DELETE_ACTIVE_BOND_CLASSIC_AND_LE = 0x01, + BOND_MANAGEMENT_CMD_DELETE_ACTIVE_BOND_CLASSIC, + BOND_MANAGEMENT_CMD_DELETE_ACTIVE_BOND_LE, + BOND_MANAGEMENT_CMD_DELETE_ALL_BONDS_CLASSIC_AND_LE, + BOND_MANAGEMENT_CMD_DELETE_ALL_BONDS_CLASSIC, + BOND_MANAGEMENT_CMD_DELETE_ALL_BONDS_LE, + BOND_MANAGEMENT_CMD_DELETE_ALL_BUT_ACTIVE_BOND_CLASSIC_AND_LE, + BOND_MANAGEMENT_CMD_DELETE_ALL_BUT_ACTIVE_BOND_CLASSIC, + BOND_MANAGEMENT_CMD_DELETE_ALL_BUT_ACTIVE_BOND_LE +} bond_management_cmd_t; + /** * @brief Init Bond Management Service with ATT DB * @param supported_features diff --git a/test/gatt_service/Makefile b/test/gatt_service/Makefile index ddefb0f92..cfe04ddce 100644 --- a/test/gatt_service/Makefile +++ b/test/gatt_service/Makefile @@ -52,6 +52,7 @@ COMMON_OBJ_ASAN = $(addprefix build-asan/, $(COMMON:.c=.o)) all: build-coverage/battery_service_test build-asan/battery_service_test \ build-coverage/device_information_service_test build-asan/device_information_service_test \ build-coverage/tx_power_service_test build-asan/tx_power_service_test \ + build-coverage/bond_management_service_test build-asan/bond_management_service_test \ build-%: mkdir -p $@ @@ -65,6 +66,9 @@ build-%/device_information_service_profile.h: device_information_service.gatt| b build-%/tx_power_service_profile.h: tx_power_service.gatt| build-% python3 ${BTSTACK_ROOT}/tool/compile_gatt.py $< $@ +build-%/bond_management_service_profile.h: bond_management_service.gatt| build-% + python3 ${BTSTACK_ROOT}/tool/compile_gatt.py $< $@ + build-coverage/%.o: %.c | build-coverage ${CC} -c $(CFLAGS_COVERAGE) $< -o $@ @@ -89,16 +93,24 @@ build-coverage/tx_power_service_test: build-coverage/tx_power_service_profile.h build-asan/tx_power_service_test: build-asan/tx_power_service_profile.h ${COMMON_OBJ_ASAN} build-asan/tx_power_service_test.o | build-asan ${CC} $(filter-out build-asan/tx_power_service_profile.h,$^) ${LDFLAGS_ASAN} -o $@ +build-coverage/bond_management_service_test: build-coverage/bond_management_service_profile.h ${COMMON_OBJ_COVERAGE} build-coverage/bond_management_service_server.o build-coverage/bond_management_service_test.o | build-coverage + ${CC} $(filter-out build-coverage/bond_management_service_profile.h,$^) ${LDFLAGS_COVERAGE} -o $@ + +build-asan/bond_management_service_test: build-asan/bond_management_service_profile.h ${COMMON_OBJ_ASAN} build-asan/bond_management_service_server.o build-asan/bond_management_service_test.o | build-asan + ${CC} $(filter-out build-asan/bond_management_service_profile.h,$^) ${LDFLAGS_ASAN} -o $@ + test: all build-asan/battery_service_test build-asan/device_information_service_test build-asan/tx_power_service_test + build-asan/bond_management_service_test coverage: all rm -f build-coverage/*.gcda build-coverage/battery_service_test build-coverage/device_information_service_test build-asan/tx_power_service_test + build-asan/bond_management_service_test clean: rm -rf build-coverage build-asan diff --git a/test/gatt_service/bond_management_service_profile.gatt b/test/gatt_service/bond_management_service_profile.gatt new file mode 100644 index 000000000..77be59fd7 --- /dev/null +++ b/test/gatt_service/bond_management_service_profile.gatt @@ -0,0 +1,2 @@ +// add Bond Management Service +#import \ No newline at end of file diff --git a/test/gatt_service/bond_management_service_test.c b/test/gatt_service/bond_management_service_test.c new file mode 100644 index 000000000..e67d3d600 --- /dev/null +++ b/test/gatt_service/bond_management_service_test.c @@ -0,0 +1,254 @@ + +// ***************************************************************************** +// +// test battery service +// +// ***************************************************************************** + + +#include +#include +#include +#include + +#include "CppUTest/TestHarness.h" +#include "CppUTest/CommandLineTestRunner.h" +#include "CppUTestExt/MockSupport.h" + +#include "hci.h" +#include "gap.h" +#include "btstack_util.h" +#include "bluetooth.h" +#include "bluetooth_gatt.h" +#include "ble/le_device_db.h" +#include "ble/gatt-service/bond_management_service_server.h" +#include "bond_management_service_profile.h" +#include "mock_att_server.h" + +static uint32_t bond_management_features = 0xFFFFFF; +static uint8_t request[550]; + +// mocks +void gap_delete_bonding(bd_addr_type_t address_type, bd_addr_t address){ + // traack call if needed +} +void gap_drop_link_key_for_bd_addr(bd_addr_t addr){ + // traack call if needed +} +int gap_link_key_iterator_init(btstack_link_key_iterator_t * it){ + return 1; +} +int gap_link_key_iterator_get_next(btstack_link_key_iterator_t * it, bd_addr_t bd_addr, link_key_t link_key, link_key_type_t * type){ + // link keys!! + return 0; +} +void gap_link_key_iterator_done(btstack_link_key_iterator_t * it){ +} +static hci_connection_t test_connection; +hci_connection_t * hci_connection_for_handle(hci_con_handle_t con_handle){ + memset(test_connection.address, 0x33, 6); + test_connection.address_type = BD_ADDR_TYPE_ACL; + return &test_connection; +} +int le_device_db_max_count(void){ + return 0; +} +void le_device_db_info(int index, int * addr_type, bd_addr_t addr, sm_key_t irk){ +} +// + +TEST_GROUP(BOND_MANAGEMENT_SERVICE_SERVER){ + att_service_handler_t * service; + uint16_t con_handle; + uint16_t bm_features_value_handle; + uint16_t bm_control_point_value_handle; + + void setup(void){ + // setup database + att_set_db(profile_data); + bm_features_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(0, 0xffff, ORG_BLUETOOTH_CHARACTERISTIC_BOND_MANAGEMENT_FEATURE); + bm_control_point_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(0, 0xffff, ORG_BLUETOOTH_CHARACTERISTIC_BOND_MANAGEMENT_CONTROL_POINT); + // setup tx power service + con_handle = 0x00; + } + + void validate_write_control_point(uint32_t local_flag, const char * local_auth_str, uint8_t remote_cmd, const char * remote_auth_str, uint16_t expected_outcome){ + bond_management_service_server_init(local_flag); + service = mock_att_server_get_service(); + bond_management_service_server_set_authorisation_string(local_auth_str); + + request[0] = remote_cmd; + uint16_t remote_auth_str_len = 0; + if (remote_auth_str != NULL) { + remote_auth_str_len = strlen(remote_auth_str); + } + memcpy(&request[1], remote_auth_str, remote_auth_str_len); + uint16_t request_len = 1 + remote_auth_str_len; + int response = mock_att_service_write_callback(con_handle, bm_control_point_value_handle, ATT_TRANSACTION_MODE_NONE, 0, request, request_len); + CHECK_EQUAL(expected_outcome, response); + } + + void teardown(){ + mock_deinit(); + } +}; + +TEST(BOND_MANAGEMENT_SERVICE_SERVER, lookup_attribute_handles){ + bond_management_service_server_init(bond_management_features); + service = mock_att_server_get_service(); + + CHECK(bm_features_value_handle != 0); + CHECK(bm_control_point_value_handle != 0); +} + +TEST(BOND_MANAGEMENT_SERVICE_SERVER, read_bm_features_value0){ + uint8_t response[10]; + uint16_t response_len; + + bond_management_service_server_init(0x00); + service = mock_att_server_get_service(); + + // invalid attribute handle + response_len = mock_att_service_read_callback(con_handle, 0xffff, 0xffff, response, sizeof(response)); + CHECK_EQUAL(0, response_len); + + response_len = mock_att_service_read_callback(con_handle, bm_features_value_handle, 0, response, sizeof(response)); + CHECK_EQUAL(0, response_len); +} + + +TEST(BOND_MANAGEMENT_SERVICE_SERVER, read_bm_features_value1){ + uint8_t response[10]; + uint16_t response_len; + + bond_management_service_server_init(0xFF); + service = mock_att_server_get_service(); + + response_len = mock_att_service_read_callback(con_handle, bm_features_value_handle, 0, response, sizeof(response)); + CHECK_EQUAL(1, response_len); +} + +TEST(BOND_MANAGEMENT_SERVICE_SERVER, read_bm_features_value2){ + uint8_t response[10]; + uint16_t response_len; + + bond_management_service_server_init(0xFFFF); + service = mock_att_server_get_service(); + + response_len = mock_att_service_read_callback(con_handle, bm_features_value_handle, 0, response, sizeof(response)); + CHECK_EQUAL(2, response_len); +} + +TEST(BOND_MANAGEMENT_SERVICE_SERVER, read_bm_features_value3){ + uint8_t response[10]; + uint16_t response_len; + + // invalid attribute handle + bond_management_service_server_init(0xFFFFFF); + service = mock_att_server_get_service(); + + response_len = mock_att_service_read_callback(con_handle, bm_features_value_handle, 0, response, sizeof(response)); + CHECK_EQUAL(3, response_len); +} + + +TEST(BOND_MANAGEMENT_SERVICE_SERVER, write_bm_control_point_0){ + uint8_t request[520]; + uint16_t request_len = sizeof(request); + int response; + + // invalid attribute handle + bond_management_service_server_init(0xFFFFFF); + service = mock_att_server_get_service(); + + // wrong handle + response = mock_att_service_write_callback(con_handle, 0xffff, ATT_TRANSACTION_MODE_NONE, 0, request, request_len); + CHECK_EQUAL(response, 0); + + // buffer size 0 + response = mock_att_service_write_callback(con_handle, bm_control_point_value_handle, ATT_TRANSACTION_MODE_NONE, 0, NULL, 0); + CHECK_EQUAL(response, BOND_MANAGEMENT_CONTROL_POINT_OPCODE_NOT_SUPPORTED); + + // wrong cmd + request[0] = 20; + response = mock_att_service_write_callback(con_handle, bm_control_point_value_handle, ATT_TRANSACTION_MODE_NONE, 0, request, request_len); + CHECK_EQUAL(response, BOND_MANAGEMENT_CONTROL_POINT_OPCODE_NOT_SUPPORTED); + + // wrong authorisation code len: request_len > 512 + request[0] = BMF_DELETE_ACTIVE_BOND_CLASSIC_AND_LE; + response = mock_att_service_write_callback(con_handle, bm_control_point_value_handle, ATT_TRANSACTION_MODE_NONE, 0, request, request_len); + CHECK_EQUAL(response, BOND_MANAGEMENT_OPERATION_FAILED); +} + + +// locally no (string empty), remote no (string empty) -> OK +TEST(BOND_MANAGEMENT_SERVICE_SERVER, write_control_point_ln_empty_rn_empty){ + validate_write_control_point(BMF_DELETE_ACTIVE_BOND_CLASSIC_AND_LE, NULL, + BOND_MANAGEMENT_CMD_DELETE_ACTIVE_BOND_CLASSIC_AND_LE, NULL, 0); +} + +// locally no (string exists), remote no (string empty) -> OK +TEST(BOND_MANAGEMENT_SERVICE_SERVER, write_control_point_ln_exists_rn_empty){ + validate_write_control_point(BMF_DELETE_ACTIVE_BOND_CLASSIC_AND_LE, "local_auth_str", + BOND_MANAGEMENT_CMD_DELETE_ACTIVE_BOND_CLASSIC_AND_LE, NULL, 0); +} + +// locally no (string empty), remote yes (string exists) -> BOND_MANAGEMENT_CONTROL_POINT_OPCODE_NOT_SUPPORTED +TEST(BOND_MANAGEMENT_SERVICE_SERVER, write_control_point_ln_empty_ry_exists){ + validate_write_control_point(BMF_DELETE_ACTIVE_BOND_CLASSIC_AND_LE, "", + BOND_MANAGEMENT_CMD_DELETE_ACTIVE_BOND_CLASSIC_AND_LE, "remote", + BOND_MANAGEMENT_CONTROL_POINT_OPCODE_NOT_SUPPORTED); +} + +// locally no (string exists), remote yes (string exists) -> BOND_MANAGEMENT_CONTROL_POINT_OPCODE_NOT_SUPPORTED +TEST(BOND_MANAGEMENT_SERVICE_SERVER, write_control_point_ln_exists_ry_exists){ + validate_write_control_point(BMF_DELETE_ACTIVE_BOND_CLASSIC_AND_LE, "local_auth_str", + BOND_MANAGEMENT_CMD_DELETE_ACTIVE_BOND_CLASSIC_AND_LE, "remote_auth_str", + BOND_MANAGEMENT_CONTROL_POINT_OPCODE_NOT_SUPPORTED); +} + +// locally no (string exists), remote yes, (string exact) -> BOND_MANAGEMENT_CONTROL_POINT_OPCODE_NOT_SUPPORTED +TEST(BOND_MANAGEMENT_SERVICE_SERVER, write_control_point_ln_exists_ry_exact){ + validate_write_control_point(BMF_DELETE_ACTIVE_BOND_CLASSIC_AND_LE, "auth_str", + BOND_MANAGEMENT_CMD_DELETE_ACTIVE_BOND_CLASSIC_AND_LE, "auth_str", + BOND_MANAGEMENT_CONTROL_POINT_OPCODE_NOT_SUPPORTED); +} + +// locally yes (string empty), remote no (string empty) -> BOND_MANAGEMENT_CONTROL_POINT_OPCODE_NOT_SUPPORTED +TEST(BOND_MANAGEMENT_SERVICE_SERVER, write_control_point_ly_empty_ry_empty){ + validate_write_control_point(BMF_DELETE_ACTIVE_BOND_CLASSIC_AND_LE_WITH_AUTH, NULL, + BOND_MANAGEMENT_CMD_DELETE_ACTIVE_BOND_CLASSIC_AND_LE, NULL, + BOND_MANAGEMENT_CONTROL_POINT_OPCODE_NOT_SUPPORTED); +} + +// locally yes (string exists), remote no (string empty) -> BOND_MANAGEMENT_CONTROL_POINT_OPCODE_NOT_SUPPORTED +TEST(BOND_MANAGEMENT_SERVICE_SERVER, write_control_point_ly_exists_rn_empty){ + validate_write_control_point(BMF_DELETE_ACTIVE_BOND_CLASSIC_AND_LE_WITH_AUTH, "local_auth_str", + BOND_MANAGEMENT_CMD_DELETE_ACTIVE_BOND_CLASSIC_AND_LE, NULL, + BOND_MANAGEMENT_CONTROL_POINT_OPCODE_NOT_SUPPORTED); +} + +// locally yes (string exists), remote yes (string exists) -> ATT_ERROR_INSUFFICIENT_AUTHORIZATION +TEST(BOND_MANAGEMENT_SERVICE_SERVER, write_control_point_ly_exists_ry_exists){ + validate_write_control_point(BMF_DELETE_ACTIVE_BOND_CLASSIC_AND_LE_WITH_AUTH, "local_auth_str", + BOND_MANAGEMENT_CMD_DELETE_ACTIVE_BOND_CLASSIC_AND_LE, "remote_auth_str", + ATT_ERROR_INSUFFICIENT_AUTHORIZATION); +} + +// locally yes (string exists), remote yes, (string exact) -> OK +TEST(BOND_MANAGEMENT_SERVICE_SERVER, write_control_point_ly_exists_ry_exact){ + validate_write_control_point(BMF_DELETE_ACTIVE_BOND_CLASSIC_AND_LE_WITH_AUTH, "auth_str", + BOND_MANAGEMENT_CMD_DELETE_ACTIVE_BOND_CLASSIC_AND_LE, "auth_str", + 0); +} + +// locally yes (string empty), remote yes (string exists) -> ATT_ERROR_INSUFFICIENT_AUTHORIZATION +TEST(BOND_MANAGEMENT_SERVICE_SERVER, write_control_point_ly_empty_ry_exists){ + validate_write_control_point(BMF_DELETE_ACTIVE_BOND_CLASSIC_AND_LE_WITH_AUTH, NULL, + BOND_MANAGEMENT_CMD_DELETE_ACTIVE_BOND_CLASSIC_AND_LE, "remote", + ATT_ERROR_INSUFFICIENT_AUTHORIZATION); +} + +int main (int argc, const char * argv[]){ + return CommandLineTestRunner::RunAllTests(argc, argv); +}