1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <stddef.h>
21 #include <errno.h>
22 #include <string.h>
23 #include "testutil/testutil.h"
24 #include "host/ble_hs_test.h"
25 #include "ble_hs_test_util.h"
26
27 #define BLE_HS_PVCY_TEST_MAX_GAP_EVENTS 256
28 static struct ble_gap_event
29 ble_hs_pvcy_test_gap_events[BLE_HS_PVCY_TEST_MAX_GAP_EVENTS];
30 static int ble_hs_pvcy_test_num_gap_events;
31
32 static void
ble_hs_pvcy_test_util_init(void)33 ble_hs_pvcy_test_util_init(void)
34 {
35 ble_hs_test_util_init();
36 ble_hs_pvcy_test_num_gap_events = 0;
37 }
38
39 static int
ble_hs_pvcy_test_util_gap_event(struct ble_gap_event * event,void * arg)40 ble_hs_pvcy_test_util_gap_event(struct ble_gap_event *event, void *arg)
41 {
42 TEST_ASSERT_FATAL(ble_hs_pvcy_test_num_gap_events <
43 BLE_HS_PVCY_TEST_MAX_GAP_EVENTS);
44 ble_hs_pvcy_test_gap_events[ble_hs_pvcy_test_num_gap_events++] = *event;
45
46 return 0;
47 }
48
49 static void
ble_hs_pvcy_test_util_all_gap_procs(int adv_status,int conn_status,int disc_status)50 ble_hs_pvcy_test_util_all_gap_procs(int adv_status,
51 int conn_status,
52 int disc_status)
53 {
54 struct ble_gap_disc_params disc_params;
55 ble_addr_t peer_addr;
56 int rc;
57
58 /* Advertise. */
59 rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC,
60 NULL, &ble_hs_test_util_adv_params,
61 BLE_HS_FOREVER,
62 ble_hs_pvcy_test_util_gap_event,
63 NULL, 0, 0);
64 TEST_ASSERT_FATAL(rc == adv_status);
65
66 if (rc == 0) {
67 rc = ble_hs_test_util_adv_stop(0);
68 TEST_ASSERT_FATAL(rc == 0);
69 }
70
71 /* Connect. */
72 peer_addr = (ble_addr_t){ BLE_ADDR_PUBLIC, {1,2,3,4,5,6} };
73 rc = ble_hs_test_util_connect(BLE_ADDR_PUBLIC, &peer_addr,
74 BLE_HS_FOREVER, NULL,
75 ble_hs_pvcy_test_util_gap_event, NULL, 0);
76 TEST_ASSERT_FATAL(rc == conn_status);
77
78 if (rc == 0) {
79 ble_hs_test_util_conn_cancel_full();
80 }
81
82 /* Discover. */
83 disc_params = (struct ble_gap_disc_params){ 0 };
84 rc = ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER,
85 &disc_params, ble_hs_pvcy_test_util_gap_event,
86 NULL, -1, 0);
87 TEST_ASSERT_FATAL(rc == disc_status);
88
89 if (rc == 0) {
90 rc = ble_hs_test_util_disc_cancel(0);
91 TEST_ASSERT_FATAL(rc == 0);
92 }
93 }
94
95 static void
ble_hs_pvcy_test_util_add_irk_set_acks(bool scanning,bool connecting)96 ble_hs_pvcy_test_util_add_irk_set_acks(bool scanning, bool connecting)
97 {
98 ble_hs_test_util_hci_ack_append(
99 BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_ADV_ENABLE), 0);
100
101 if (connecting) {
102 ble_hs_test_util_hci_ack_append(
103 BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_CREATE_CONN_CANCEL),
104 0);
105 }
106
107 if (scanning) {
108 ble_hs_test_util_hci_ack_append(
109 BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_SCAN_ENABLE),
110 0);
111 }
112
113 ble_hs_test_util_hci_ack_append(
114 BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_ADD_RESOLV_LIST), 0);
115 ble_hs_test_util_hci_ack_append(
116 BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_PRIVACY_MODE), 0);
117 }
118
119 static void
ble_hs_pvcy_test_util_start_host(int num_expected_irks)120 ble_hs_pvcy_test_util_start_host(int num_expected_irks)
121 {
122 int rc;
123 int i;
124
125 /* Clear our IRK. This ensures the full startup sequence, including
126 * setting the default IRK, takes place. We need this so that we can plan
127 * which HCI acks to fake.
128 */
129 rc = ble_hs_test_util_set_our_irk((uint8_t[16]){0}, -1, 0);
130 TEST_ASSERT_FATAL(rc == 0);
131 ble_hs_test_util_hci_out_clear();
132
133 ble_hs_test_util_hci_ack_set_startup();
134
135 for (i = 0; i < num_expected_irks; i++) {
136 ble_hs_pvcy_test_util_add_irk_set_acks(false, false);
137 }
138
139 ble_hs_enabled_state = BLE_HS_ENABLED_STATE_OFF;
140 rc = ble_hs_start();
141 TEST_ASSERT_FATAL(rc == 0);
142
143 /* Discard startup HCI commands. */
144 ble_hs_test_util_hci_out_adj(ble_hs_test_util_hci_startup_seq_cnt());
145 }
146
147 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,bool scanning,bool connecting)148 ble_hs_pvcy_test_util_add_irk_verify_tx(const ble_addr_t *peer_addr,
149 const uint8_t *peer_irk,
150 const uint8_t *local_irk,
151 bool scanning,
152 bool connecting)
153 {
154 ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE,
155 BLE_HCI_OCF_LE_SET_ADV_ENABLE,
156 NULL);
157
158 if (connecting) {
159 ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE,
160 BLE_HCI_OCF_LE_CREATE_CONN_CANCEL,
161 NULL);
162 }
163
164 if (scanning) {
165 ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE,
166 BLE_HCI_OCF_LE_SET_SCAN_ENABLE,
167 NULL);
168 }
169
170 ble_hs_test_util_hci_verify_tx_add_irk(peer_addr->type,
171 peer_addr->val,
172 peer_irk,
173 local_irk);
174
175 ble_hs_test_util_hci_verify_tx_set_priv_mode(peer_addr->type,
176 peer_addr->val,
177 BLE_GAP_PRIVATE_MODE_DEVICE);
178 }
179
180 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,bool scanning,bool connecting)181 ble_hs_pvcy_test_util_add_irk(const ble_addr_t *peer_addr,
182 const uint8_t *peer_irk,
183 const uint8_t *local_irk,
184 bool scanning,
185 bool connecting)
186 {
187 int num_acks;
188 int rc;
189
190 ble_hs_pvcy_test_util_add_irk_set_acks(scanning, connecting);
191
192 rc = ble_hs_pvcy_add_entry(peer_addr->val, peer_addr->type, peer_irk);
193 TEST_ASSERT_FATAL(rc == 0);
194
195 num_acks = 3;
196 if (scanning) {
197 num_acks++;
198 }
199 if (connecting) {
200 num_acks++;
201 }
202 ble_hs_test_util_hci_out_adj(-num_acks);
203 ble_hs_pvcy_test_util_add_irk_verify_tx(peer_addr, peer_irk, local_irk,
204 scanning, connecting);
205 }
206
207 static void
ble_hs_pvcy_test_util_add_arbitrary_irk(bool scanning,bool connecting)208 ble_hs_pvcy_test_util_add_arbitrary_irk(bool scanning, bool connecting)
209 {
210 ble_addr_t peer_addr;
211
212 peer_addr = (ble_addr_t) {
213 .type = BLE_ADDR_PUBLIC,
214 .val = {1,2,3,4,5,6},
215 };
216 ble_hs_pvcy_test_util_add_irk(
217 &peer_addr,
218 (uint8_t[16]){1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16},
219 ble_hs_pvcy_default_irk,
220 scanning,
221 connecting);
222 }
223
224 static void
ble_hs_pvcy_test_util_restore_irk(const struct ble_store_value_sec * value_sec,bool scanning,bool connecting)225 ble_hs_pvcy_test_util_restore_irk(const struct ble_store_value_sec *value_sec,
226 bool scanning,
227 bool connecting)
228 {
229 int rc;
230
231 ble_hs_pvcy_test_util_add_irk_set_acks(scanning, connecting);
232
233 rc = ble_store_write_peer_sec(value_sec);
234 TEST_ASSERT_FATAL(rc == 0);
235
236 ble_hs_pvcy_test_util_add_irk_verify_tx(&value_sec->peer_addr,
237 value_sec->irk,
238 ble_hs_pvcy_default_irk,
239 scanning,
240 connecting);
241 }
242
TEST_CASE(ble_hs_pvcy_test_case_restore_irks)243 TEST_CASE(ble_hs_pvcy_test_case_restore_irks)
244 {
245 struct ble_store_value_sec value_sec1;
246 struct ble_store_value_sec value_sec2;
247
248 ble_hs_pvcy_test_util_init();
249
250 /*** No persisted IRKs. */
251 ble_hs_pvcy_test_util_start_host(0);
252
253 /*** One persisted IRK. */
254
255 /* Persist IRK; ensure it automatically gets added to the list. */
256 value_sec1 = (struct ble_store_value_sec) {
257 .peer_addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 } },
258 .key_size = 16,
259 .ediv = 1,
260 .rand_num = 2,
261 .irk = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 },
262 .irk_present = 1,
263 };
264 ble_hs_pvcy_test_util_restore_irk(&value_sec1, false, false);
265
266 /* Ensure it gets added to list on startup. */
267 ble_hs_pvcy_test_util_start_host(1);
268 ble_hs_pvcy_test_util_add_irk_verify_tx(&value_sec1.peer_addr,
269 value_sec1.irk,
270 ble_hs_pvcy_default_irk,
271 false, false);
272
273 /* Two persisted IRKs. */
274 value_sec2 = (struct ble_store_value_sec) {
275 .peer_addr = { BLE_ADDR_PUBLIC, { 2, 3, 4, 5, 6, 7 } },
276 .key_size = 16,
277 .ediv = 12,
278 .rand_num = 20,
279 .irk = { 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 9, 9, 9, 9, 9, 10 },
280 .irk_present = 1,
281 };
282 ble_hs_pvcy_test_util_restore_irk(&value_sec2, false, false);
283
284 /* Ensure both get added to list on startup. */
285 ble_hs_pvcy_test_util_start_host(2);
286 ble_hs_pvcy_test_util_add_irk_verify_tx(&value_sec1.peer_addr,
287 value_sec1.irk,
288 ble_hs_pvcy_default_irk,
289 false, false);
290 ble_hs_pvcy_test_util_add_irk_verify_tx(&value_sec2.peer_addr,
291 value_sec2.irk,
292 ble_hs_pvcy_default_irk,
293 false, false);
294 }
295
296 /** No active GAP procedures. */
TEST_CASE(ble_hs_pvcy_test_case_add_irk_idle)297 TEST_CASE(ble_hs_pvcy_test_case_add_irk_idle)
298 {
299 ble_hs_pvcy_test_util_init();
300
301 ble_hs_pvcy_test_util_add_arbitrary_irk(false, false);
302 TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 0);
303 }
304
305 /*** Advertising active. */
TEST_CASE(ble_hs_pvcy_test_case_add_irk_adv)306 TEST_CASE(ble_hs_pvcy_test_case_add_irk_adv)
307 {
308 int rc;
309
310 ble_hs_pvcy_test_util_init();
311
312 /* Start an advertising procedure. */
313 rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC,
314 NULL, &ble_hs_test_util_adv_params,
315 BLE_HS_FOREVER,
316 ble_hs_pvcy_test_util_gap_event,
317 NULL, 0, 0);
318 TEST_ASSERT_FATAL(rc == 0);
319
320 ble_hs_pvcy_test_util_add_arbitrary_irk(false, false);
321
322 TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 1);
323 TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type ==
324 BLE_GAP_EVENT_ADV_COMPLETE);
325 TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].adv_complete.reason ==
326 BLE_HS_EPREEMPTED);
327
328 /* Ensure GAP procedures are no longer preempted. */
329 ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0);
330 }
331
332 /*** Discovery active. */
TEST_CASE(ble_hs_pvcy_test_case_add_irk_disc)333 TEST_CASE(ble_hs_pvcy_test_case_add_irk_disc)
334 {
335 struct ble_gap_disc_params disc_params;
336 int rc;
337
338 ble_hs_pvcy_test_util_init();
339
340 /* Start an advertising procedure. */
341 disc_params = (struct ble_gap_disc_params){ 0 };
342 rc = ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER,
343 &disc_params, ble_hs_pvcy_test_util_gap_event,
344 NULL, -1, 0);
345 TEST_ASSERT_FATAL(rc == 0);
346
347 ble_hs_pvcy_test_util_add_arbitrary_irk(true, false);
348
349 TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 1);
350 TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type ==
351 BLE_GAP_EVENT_DISC_COMPLETE);
352 TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].disc_complete.reason ==
353 BLE_HS_EPREEMPTED);
354
355 /* Ensure GAP procedures are no longer preempted. */
356 ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0);
357 }
358
359 /*** Connect active. */
TEST_CASE(ble_hs_pvcy_test_case_add_irk_conn)360 TEST_CASE(ble_hs_pvcy_test_case_add_irk_conn)
361 {
362 ble_addr_t peer_addr;
363 int rc;
364
365 ble_hs_pvcy_test_util_init();
366
367 /* Start a connect procedure. */
368 peer_addr = (ble_addr_t){ BLE_ADDR_PUBLIC, {1,2,3,4,5,6} };
369 rc = ble_hs_test_util_connect(BLE_ADDR_PUBLIC, &peer_addr,
370 BLE_HS_FOREVER, NULL,
371 ble_hs_pvcy_test_util_gap_event, NULL, 0);
372 TEST_ASSERT_FATAL(rc == 0);
373
374 ble_hs_pvcy_test_util_add_arbitrary_irk(false, true);
375
376 /* Cancel is now in progress. */
377 TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 0);
378
379 /* Ensure no GAP procedures are allowed. */
380 ble_hs_pvcy_test_util_all_gap_procs(BLE_HS_EPREEMPTED,
381 BLE_HS_EALREADY,
382 BLE_HS_EBUSY);
383
384 /* Receive cancel event. */
385 ble_hs_test_util_rx_conn_cancel_evt();
386
387 TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 1);
388 TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type ==
389 BLE_GAP_EVENT_CONNECT);
390 TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].connect.status ==
391 BLE_HS_EPREEMPTED);
392
393 /* Ensure GAP procedures are no longer preempted. */
394 ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0);
395 }
396
397 /*** Advertising and discovery active. */
TEST_CASE(ble_hs_pvcy_test_case_add_irk_adv_disc)398 TEST_CASE(ble_hs_pvcy_test_case_add_irk_adv_disc)
399 {
400 struct ble_gap_disc_params disc_params;
401 int rc;
402
403 ble_hs_pvcy_test_util_init();
404
405 /* Start an advertising procedure. */
406 rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC,
407 NULL, &ble_hs_test_util_adv_params,
408 BLE_HS_FOREVER,
409 ble_hs_pvcy_test_util_gap_event,
410 NULL, 0, 0);
411 TEST_ASSERT_FATAL(rc == 0);
412
413 /* Start a discovery procedure. */
414 disc_params = (struct ble_gap_disc_params){ 0 };
415 rc = ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER,
416 &disc_params, ble_hs_pvcy_test_util_gap_event,
417 NULL, -1, 0);
418 TEST_ASSERT_FATAL(rc == 0);
419
420 ble_hs_pvcy_test_util_add_arbitrary_irk(true, false);
421
422 TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 2);
423 TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type ==
424 BLE_GAP_EVENT_ADV_COMPLETE);
425 TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].adv_complete.reason ==
426 BLE_HS_EPREEMPTED);
427 TEST_ASSERT(ble_hs_pvcy_test_gap_events[1].type ==
428 BLE_GAP_EVENT_DISC_COMPLETE);
429 TEST_ASSERT(ble_hs_pvcy_test_gap_events[1].disc_complete.reason ==
430 BLE_HS_EPREEMPTED);
431
432 /* Ensure GAP procedures are no longer preempted. */
433 ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0);
434 }
435
436 /*** Advertising and connecting active. */
TEST_CASE(ble_hs_pvcy_test_case_add_irk_adv_conn)437 TEST_CASE(ble_hs_pvcy_test_case_add_irk_adv_conn)
438 {
439 ble_addr_t peer_addr;
440 int rc;
441
442 ble_hs_pvcy_test_util_init();
443
444 /* Start an advertising procedure. */
445 rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC,
446 NULL, &ble_hs_test_util_adv_params,
447 BLE_HS_FOREVER,
448 ble_hs_pvcy_test_util_gap_event,
449 NULL, 0, 0);
450 TEST_ASSERT_FATAL(rc == 0);
451
452 /* Start a connect procedure. */
453 peer_addr = (ble_addr_t){ BLE_ADDR_PUBLIC, {1,2,3,4,5,6} };
454 rc = ble_hs_test_util_connect(BLE_ADDR_PUBLIC, &peer_addr,
455 BLE_HS_FOREVER, NULL,
456 ble_hs_pvcy_test_util_gap_event, NULL, 0);
457 TEST_ASSERT_FATAL(rc == 0);
458
459 ble_hs_pvcy_test_util_add_arbitrary_irk(false, true);
460
461 /* Cancel is now in progress. */
462 TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 1);
463 TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type ==
464 BLE_GAP_EVENT_ADV_COMPLETE);
465 TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].adv_complete.reason ==
466 BLE_HS_EPREEMPTED);
467
468 /* Ensure no GAP procedures are allowed. */
469 ble_hs_pvcy_test_util_all_gap_procs(BLE_HS_EPREEMPTED,
470 BLE_HS_EALREADY,
471 BLE_HS_EBUSY);
472
473 /* Receive cancel event. */
474 ble_hs_test_util_rx_conn_cancel_evt();
475
476 TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 2);
477 TEST_ASSERT(ble_hs_pvcy_test_gap_events[1].type ==
478 BLE_GAP_EVENT_CONNECT);
479 TEST_ASSERT(ble_hs_pvcy_test_gap_events[1].connect.status ==
480 BLE_HS_EPREEMPTED);
481
482 /* Ensure GAP procedures are no longer preempted. */
483 ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0);
484 }
485
TEST_SUITE(ble_hs_pvcy_test_suite_irk)486 TEST_SUITE(ble_hs_pvcy_test_suite_irk)
487 {
488 tu_suite_set_post_test_cb(ble_hs_test_util_post_test, NULL);
489
490 ble_hs_pvcy_test_case_restore_irks();
491 ble_hs_pvcy_test_case_add_irk_idle();
492 ble_hs_pvcy_test_case_add_irk_adv();
493 ble_hs_pvcy_test_case_add_irk_disc();
494 ble_hs_pvcy_test_case_add_irk_conn();
495 ble_hs_pvcy_test_case_add_irk_adv_disc();
496 ble_hs_pvcy_test_case_add_irk_adv_conn();
497 }
498
499 int
ble_hs_pvcy_test_all(void)500 ble_hs_pvcy_test_all(void)
501 {
502 ble_hs_pvcy_test_suite_irk();
503
504 return tu_any_failed;
505 }
506