| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include <stddef.h> |
| #include <errno.h> |
| #include <string.h> |
| #include "testutil/testutil.h" |
| #include "host/ble_hs_test.h" |
| #include "ble_hs_test_util.h" |
| |
| #define BLE_HS_PVCY_TEST_MAX_GAP_EVENTS 256 |
| static struct ble_gap_event |
| ble_hs_pvcy_test_gap_events[BLE_HS_PVCY_TEST_MAX_GAP_EVENTS]; |
| static int ble_hs_pvcy_test_num_gap_events; |
| |
| static void |
| ble_hs_pvcy_test_util_init(void) |
| { |
| ble_hs_test_util_init(); |
| ble_hs_pvcy_test_num_gap_events = 0; |
| } |
| |
| static int |
| ble_hs_pvcy_test_util_gap_event(struct ble_gap_event *event, void *arg) |
| { |
| TEST_ASSERT_FATAL(ble_hs_pvcy_test_num_gap_events < |
| BLE_HS_PVCY_TEST_MAX_GAP_EVENTS); |
| ble_hs_pvcy_test_gap_events[ble_hs_pvcy_test_num_gap_events++] = *event; |
| |
| return 0; |
| } |
| |
| static void |
| ble_hs_pvcy_test_util_all_gap_procs(int adv_status, |
| int conn_status, |
| int disc_status) |
| { |
| struct ble_gap_disc_params disc_params; |
| ble_addr_t peer_addr; |
| int rc; |
| |
| /* Advertise. */ |
| rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC, |
| NULL, &ble_hs_test_util_adv_params, |
| BLE_HS_FOREVER, |
| ble_hs_pvcy_test_util_gap_event, |
| NULL, 0, 0); |
| TEST_ASSERT_FATAL(rc == adv_status); |
| |
| if (rc == 0) { |
| rc = ble_hs_test_util_adv_stop(0); |
| TEST_ASSERT_FATAL(rc == 0); |
| } |
| |
| /* Connect. */ |
| peer_addr = (ble_addr_t){ BLE_ADDR_PUBLIC, {1,2,3,4,5,6} }; |
| rc = ble_hs_test_util_connect(BLE_ADDR_PUBLIC, &peer_addr, |
| BLE_HS_FOREVER, NULL, |
| ble_hs_pvcy_test_util_gap_event, NULL, 0); |
| TEST_ASSERT_FATAL(rc == conn_status); |
| |
| if (rc == 0) { |
| ble_hs_test_util_conn_cancel_full(); |
| } |
| |
| /* Discover. */ |
| disc_params = (struct ble_gap_disc_params){ 0 }; |
| rc = ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER, |
| &disc_params, ble_hs_pvcy_test_util_gap_event, |
| NULL, -1, 0); |
| TEST_ASSERT_FATAL(rc == disc_status); |
| |
| if (rc == 0) { |
| rc = ble_hs_test_util_disc_cancel(0); |
| TEST_ASSERT_FATAL(rc == 0); |
| } |
| } |
| |
| static void |
| ble_hs_pvcy_test_util_add_irk_set_acks(void) |
| { |
| ble_hs_test_util_hci_ack_append( |
| BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_ADD_RESOLV_LIST), 0); |
| ble_hs_test_util_hci_ack_append( |
| BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_PRIVACY_MODE), 0); |
| } |
| |
| static void |
| ble_hs_pvcy_test_util_start_host(int num_expected_irks) |
| { |
| int rc; |
| int i; |
| |
| /* Clear our IRK. This ensures the full startup sequence, including |
| * setting the default IRK, takes place. We need this so that we can plan |
| * which HCI acks to fake. |
| */ |
| rc = ble_hs_test_util_set_our_irk((uint8_t[16]){0}, -1, 0); |
| TEST_ASSERT_FATAL(rc == 0); |
| ble_hs_test_util_hci_out_clear(); |
| |
| ble_hs_test_util_hci_ack_set_startup(); |
| |
| for (i = 0; i < num_expected_irks; i++) { |
| ble_hs_pvcy_test_util_add_irk_set_acks(); |
| } |
| |
| rc = ble_hs_start(); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| /* Discard startup HCI commands. */ |
| ble_hs_test_util_hci_out_adj(ble_hs_test_util_hci_startup_seq_cnt()); |
| } |
| |
| static void |
| ble_hs_pvcy_test_util_add_irk_verify_tx(const ble_addr_t *peer_addr, |
| const uint8_t *peer_irk, |
| const uint8_t *local_irk) |
| { |
| ble_hs_test_util_hci_verify_tx_add_irk(peer_addr->type, |
| peer_addr->val, |
| peer_irk, |
| local_irk); |
| |
| ble_hs_test_util_hci_verify_tx_set_priv_mode(peer_addr->type, |
| peer_addr->val, |
| BLE_GAP_PRIVATE_MODE_DEVICE); |
| } |
| |
| static void |
| ble_hs_pvcy_test_util_add_irk(const ble_addr_t *peer_addr, |
| const uint8_t *peer_irk, |
| const uint8_t *local_irk) |
| { |
| int rc; |
| |
| ble_hs_pvcy_test_util_add_irk_set_acks(); |
| |
| rc = ble_hs_pvcy_add_entry(peer_addr->val, peer_addr->type, peer_irk); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| ble_hs_test_util_hci_out_adj(-2); |
| ble_hs_pvcy_test_util_add_irk_verify_tx(peer_addr, peer_irk, local_irk); |
| } |
| |
| static void |
| ble_hs_pvcy_test_util_add_arbitrary_irk(void) |
| { |
| ble_addr_t peer_addr; |
| |
| peer_addr = (ble_addr_t) { |
| .type = BLE_ADDR_PUBLIC, |
| .val = {1,2,3,4,5,6}, |
| }; |
| ble_hs_pvcy_test_util_add_irk( |
| &peer_addr, |
| (uint8_t[16]){1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}, |
| ble_hs_pvcy_default_irk); |
| } |
| |
| static void |
| ble_hs_pvcy_test_util_restore_irk(const struct ble_store_value_sec *value_sec) |
| { |
| int rc; |
| |
| ble_hs_pvcy_test_util_add_irk_set_acks(); |
| |
| rc = ble_store_write_peer_sec(value_sec); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| ble_hs_pvcy_test_util_add_irk_verify_tx(&value_sec->peer_addr, |
| value_sec->irk, |
| ble_hs_pvcy_default_irk); |
| } |
| |
| TEST_CASE(ble_hs_pvcy_test_case_restore_irks) |
| { |
| struct ble_store_value_sec value_sec1; |
| struct ble_store_value_sec value_sec2; |
| |
| ble_hs_pvcy_test_util_init(); |
| |
| /*** No persisted IRKs. */ |
| ble_hs_pvcy_test_util_start_host(0); |
| |
| /*** One persisted IRK. */ |
| |
| /* Persist IRK; ensure it automatically gets added to the list. */ |
| value_sec1 = (struct ble_store_value_sec) { |
| .peer_addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 } }, |
| .key_size = 16, |
| .ediv = 1, |
| .rand_num = 2, |
| .irk = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, |
| .irk_present = 1, |
| }; |
| ble_hs_pvcy_test_util_restore_irk(&value_sec1); |
| |
| /* Ensure it gets added to list on startup. */ |
| ble_hs_pvcy_test_util_start_host(1); |
| ble_hs_pvcy_test_util_add_irk_verify_tx(&value_sec1.peer_addr, |
| value_sec1.irk, |
| ble_hs_pvcy_default_irk); |
| |
| /* Two persisted IRKs. */ |
| value_sec2 = (struct ble_store_value_sec) { |
| .peer_addr = { BLE_ADDR_PUBLIC, { 2, 3, 4, 5, 6, 7 } }, |
| .key_size = 16, |
| .ediv = 12, |
| .rand_num = 20, |
| .irk = { 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 9, 9, 9, 9, 9, 10 }, |
| .irk_present = 1, |
| }; |
| ble_hs_pvcy_test_util_restore_irk(&value_sec2); |
| |
| /* Ensure both get added to list on startup. */ |
| ble_hs_pvcy_test_util_start_host(2); |
| ble_hs_pvcy_test_util_add_irk_verify_tx(&value_sec1.peer_addr, |
| value_sec1.irk, |
| ble_hs_pvcy_default_irk); |
| ble_hs_pvcy_test_util_add_irk_verify_tx(&value_sec2.peer_addr, |
| value_sec2.irk, |
| ble_hs_pvcy_default_irk); |
| } |
| |
| /** No active GAP procedures. */ |
| TEST_CASE(ble_hs_pvcy_test_case_add_irk_idle) |
| { |
| ble_hs_pvcy_test_util_init(); |
| |
| ble_hs_pvcy_test_util_add_arbitrary_irk(); |
| TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 0); |
| } |
| |
| /*** Advertising active. */ |
| TEST_CASE(ble_hs_pvcy_test_case_add_irk_adv) |
| { |
| int rc; |
| |
| ble_hs_pvcy_test_util_init(); |
| |
| /* Start an advertising procedure. */ |
| rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC, |
| NULL, &ble_hs_test_util_adv_params, |
| BLE_HS_FOREVER, |
| ble_hs_pvcy_test_util_gap_event, |
| NULL, 0, 0); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| ble_hs_test_util_hci_ack_set( |
| BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_ENABLE), |
| 0); |
| ble_hs_pvcy_test_util_add_arbitrary_irk(); |
| |
| TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 1); |
| TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type == |
| BLE_GAP_EVENT_ADV_COMPLETE); |
| TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].adv_complete.reason == |
| BLE_HS_EPREEMPTED); |
| |
| /* Ensure GAP procedures are no longer preempted. */ |
| ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0); |
| } |
| |
| /*** Discovery active. */ |
| TEST_CASE(ble_hs_pvcy_test_case_add_irk_disc) |
| { |
| struct ble_gap_disc_params disc_params; |
| int rc; |
| |
| ble_hs_pvcy_test_util_init(); |
| |
| /* Start an advertising procedure. */ |
| disc_params = (struct ble_gap_disc_params){ 0 }; |
| rc = ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER, |
| &disc_params, ble_hs_pvcy_test_util_gap_event, |
| NULL, -1, 0); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| ble_hs_test_util_hci_ack_set( |
| BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_SCAN_ENABLE), |
| 0); |
| ble_hs_pvcy_test_util_add_arbitrary_irk(); |
| |
| TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 1); |
| TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type == |
| BLE_GAP_EVENT_DISC_COMPLETE); |
| TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].disc_complete.reason == |
| BLE_HS_EPREEMPTED); |
| |
| /* Ensure GAP procedures are no longer preempted. */ |
| ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0); |
| } |
| |
| /*** Connect active. */ |
| TEST_CASE(ble_hs_pvcy_test_case_add_irk_conn) |
| { |
| ble_addr_t peer_addr; |
| int rc; |
| |
| ble_hs_pvcy_test_util_init(); |
| |
| /* Start a connect procedure. */ |
| peer_addr = (ble_addr_t){ BLE_ADDR_PUBLIC, {1,2,3,4,5,6} }; |
| rc = ble_hs_test_util_connect(BLE_ADDR_PUBLIC, &peer_addr, |
| BLE_HS_FOREVER, NULL, |
| ble_hs_pvcy_test_util_gap_event, NULL, 0); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| ble_hs_test_util_hci_ack_set( |
| BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_CREATE_CONN_CANCEL), |
| 0); |
| ble_hs_pvcy_test_util_add_arbitrary_irk(); |
| |
| /* Cancel is now in progress. */ |
| TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 0); |
| |
| /* Ensure no GAP procedures are allowed. */ |
| ble_hs_pvcy_test_util_all_gap_procs(BLE_HS_EPREEMPTED, |
| BLE_HS_EALREADY, |
| BLE_HS_EBUSY); |
| |
| /* Receive cancel event. */ |
| ble_hs_test_util_rx_conn_cancel_evt(); |
| |
| TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 1); |
| TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type == |
| BLE_GAP_EVENT_CONNECT); |
| TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].connect.status == |
| BLE_HS_EPREEMPTED); |
| |
| /* Ensure GAP procedures are no longer preempted. */ |
| ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0); |
| } |
| |
| /*** Advertising and discovery active. */ |
| TEST_CASE(ble_hs_pvcy_test_case_add_irk_adv_disc) |
| { |
| struct ble_gap_disc_params disc_params; |
| int rc; |
| |
| ble_hs_pvcy_test_util_init(); |
| |
| /* Start an advertising procedure. */ |
| rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC, |
| NULL, &ble_hs_test_util_adv_params, |
| BLE_HS_FOREVER, |
| ble_hs_pvcy_test_util_gap_event, |
| NULL, 0, 0); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| /* Start a discovery procedure. */ |
| disc_params = (struct ble_gap_disc_params){ 0 }; |
| rc = ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER, |
| &disc_params, ble_hs_pvcy_test_util_gap_event, |
| NULL, -1, 0); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| ble_hs_test_util_hci_ack_set_seq((struct ble_hs_test_util_hci_ack[]) { |
| { BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_ENABLE), 0 }, |
| { BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_SCAN_ENABLE), 0 }, |
| { 0 }, |
| }); |
| |
| ble_hs_pvcy_test_util_add_arbitrary_irk(); |
| |
| TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 2); |
| TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type == |
| BLE_GAP_EVENT_ADV_COMPLETE); |
| TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].adv_complete.reason == |
| BLE_HS_EPREEMPTED); |
| TEST_ASSERT(ble_hs_pvcy_test_gap_events[1].type == |
| BLE_GAP_EVENT_DISC_COMPLETE); |
| TEST_ASSERT(ble_hs_pvcy_test_gap_events[1].disc_complete.reason == |
| BLE_HS_EPREEMPTED); |
| |
| /* Ensure GAP procedures are no longer preempted. */ |
| ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0); |
| } |
| |
| /*** Advertising and connecting active. */ |
| TEST_CASE(ble_hs_pvcy_test_case_add_irk_adv_conn) |
| { |
| ble_addr_t peer_addr; |
| int rc; |
| |
| ble_hs_pvcy_test_util_init(); |
| |
| /* Start an advertising procedure. */ |
| rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC, |
| NULL, &ble_hs_test_util_adv_params, |
| BLE_HS_FOREVER, |
| ble_hs_pvcy_test_util_gap_event, |
| NULL, 0, 0); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| /* Start a connect procedure. */ |
| peer_addr = (ble_addr_t){ BLE_ADDR_PUBLIC, {1,2,3,4,5,6} }; |
| rc = ble_hs_test_util_connect(BLE_ADDR_PUBLIC, &peer_addr, |
| BLE_HS_FOREVER, NULL, |
| ble_hs_pvcy_test_util_gap_event, NULL, 0); |
| TEST_ASSERT_FATAL(rc == 0); |
| |
| ble_hs_test_util_hci_ack_set_seq((struct ble_hs_test_util_hci_ack[]) { |
| { BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_ENABLE), 0 }, |
| { BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_CREATE_CONN_CANCEL), 0 }, |
| { 0 }, |
| }); |
| |
| ble_hs_pvcy_test_util_add_arbitrary_irk(); |
| |
| /* Cancel is now in progress. */ |
| TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 1); |
| TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type == |
| BLE_GAP_EVENT_ADV_COMPLETE); |
| TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].adv_complete.reason == |
| BLE_HS_EPREEMPTED); |
| |
| /* Ensure no GAP procedures are allowed. */ |
| ble_hs_pvcy_test_util_all_gap_procs(BLE_HS_EPREEMPTED, |
| BLE_HS_EALREADY, |
| BLE_HS_EBUSY); |
| |
| /* Receive cancel event. */ |
| ble_hs_test_util_rx_conn_cancel_evt(); |
| |
| TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 2); |
| TEST_ASSERT(ble_hs_pvcy_test_gap_events[1].type == |
| BLE_GAP_EVENT_CONNECT); |
| TEST_ASSERT(ble_hs_pvcy_test_gap_events[1].connect.status == |
| BLE_HS_EPREEMPTED); |
| |
| /* Ensure GAP procedures are no longer preempted. */ |
| ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0); |
| } |
| |
| TEST_SUITE(ble_hs_pvcy_test_suite_irk) |
| { |
| tu_suite_set_post_test_cb(ble_hs_test_util_post_test, NULL); |
| |
| ble_hs_pvcy_test_case_restore_irks(); |
| ble_hs_pvcy_test_case_add_irk_idle(); |
| ble_hs_pvcy_test_case_add_irk_adv(); |
| ble_hs_pvcy_test_case_add_irk_disc(); |
| ble_hs_pvcy_test_case_add_irk_conn(); |
| ble_hs_pvcy_test_case_add_irk_adv_disc(); |
| ble_hs_pvcy_test_case_add_irk_adv_conn(); |
| } |
| |
| int |
| ble_hs_pvcy_test_all(void) |
| { |
| ble_hs_pvcy_test_suite_irk(); |
| |
| return tu_any_failed; |
| } |