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 #include <inttypes.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include "stats/stats.h"
23 #include "ble_hs_priv.h"
24
25 static uint8_t ble_hs_pvcy_started;
26 static uint8_t ble_hs_pvcy_irk[16];
27
28 /** Use this as a default IRK if none gets set. */
29 const uint8_t ble_hs_pvcy_default_irk[16] = {
30 0xef, 0x8d, 0xe2, 0x16, 0x4f, 0xec, 0x43, 0x0d,
31 0xbf, 0x5b, 0xdd, 0x34, 0xc0, 0x53, 0x1e, 0xb8,
32 };
33
34 static int
ble_hs_pvcy_set_addr_timeout(uint16_t timeout)35 ble_hs_pvcy_set_addr_timeout(uint16_t timeout)
36 {
37 uint8_t buf[BLE_HCI_SET_RESOLV_PRIV_ADDR_TO_LEN];
38 int rc;
39
40 rc = ble_hs_hci_cmd_build_set_resolv_priv_addr_timeout(timeout, buf,
41 sizeof(buf));
42 if (rc != 0) {
43 return rc;
44 }
45
46 return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
47 BLE_HCI_OCF_LE_SET_RPA_TMO),
48 buf, sizeof(buf), NULL, 0, NULL);
49 }
50
51 static int
ble_hs_pvcy_set_resolve_enabled(int enable)52 ble_hs_pvcy_set_resolve_enabled(int enable)
53 {
54 uint8_t buf[BLE_HCI_SET_ADDR_RESOL_ENA_LEN];
55 int rc;
56
57 rc = ble_hs_hci_cmd_build_set_addr_res_en(enable, buf, sizeof(buf));
58 if (rc != 0) {
59 return rc;
60 }
61
62 rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
63 BLE_HCI_OCF_LE_SET_ADDR_RES_EN),
64 buf, sizeof(buf), NULL, 0, NULL);
65 if (rc != 0) {
66 return rc;
67 }
68
69 return 0;
70 }
71
72 int
ble_hs_pvcy_remove_entry(uint8_t addr_type,const uint8_t * addr)73 ble_hs_pvcy_remove_entry(uint8_t addr_type, const uint8_t *addr)
74 {
75 uint8_t buf[BLE_HCI_RMV_FROM_RESOLV_LIST_LEN];
76 int rc;
77
78 rc = ble_hs_hci_cmd_build_remove_from_resolv_list(addr_type, addr,
79 buf, sizeof(buf));
80 if (rc != 0) {
81 return rc;
82 }
83
84 rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
85 BLE_HCI_OCF_LE_RMV_RESOLV_LIST),
86 buf, sizeof(buf), NULL, 0, NULL);
87 if (rc != 0) {
88 return rc;
89 }
90
91 return 0;
92 }
93
94 static int
ble_hs_pvcy_clear_entries(void)95 ble_hs_pvcy_clear_entries(void)
96 {
97 int rc;
98
99 rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
100 BLE_HCI_OCF_LE_CLR_RESOLV_LIST),
101 NULL, 0, NULL, 0, NULL);
102 if (rc != 0) {
103 return rc;
104 }
105
106 return 0;
107 }
108
109 static int
ble_hs_pvcy_add_entry_hci(const uint8_t * addr,uint8_t addr_type,const uint8_t * irk)110 ble_hs_pvcy_add_entry_hci(const uint8_t *addr, uint8_t addr_type,
111 const uint8_t *irk)
112 {
113 struct hci_add_dev_to_resolving_list add;
114 uint8_t buf[BLE_HCI_ADD_TO_RESOLV_LIST_LEN];
115 ble_addr_t peer_addr;
116 int rc;
117
118 add.addr_type = addr_type;
119 memcpy(add.addr, addr, 6);
120 memcpy(add.local_irk, ble_hs_pvcy_irk, 16);
121 memcpy(add.peer_irk, irk, 16);
122
123 rc = ble_hs_hci_cmd_build_add_to_resolv_list(&add, buf, sizeof(buf));
124 if (rc != 0) {
125 return rc;
126 }
127
128 rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
129 BLE_HCI_OCF_LE_ADD_RESOLV_LIST),
130 buf, sizeof(buf), NULL, 0, NULL);
131 if (rc != 0) {
132 return rc;
133 }
134
135
136 /* FIXME Controller is BT5.0 and default privacy mode is network which
137 * can cause problems for apps which are not aware of it. We need to
138 * sort it out somehow. For now we set device mode for all of the peer
139 * devices and application should change it to network if needed
140 */
141 peer_addr.type = addr_type;
142 memcpy(peer_addr.val, addr, sizeof peer_addr.val);
143 rc = ble_hs_pvcy_set_mode(&peer_addr, BLE_GAP_PRIVATE_MODE_DEVICE);
144 if (rc != 0) {
145 return rc;
146 }
147
148 return 0;
149 }
150
151 int
ble_hs_pvcy_add_entry(const uint8_t * addr,uint8_t addr_type,const uint8_t * irk)152 ble_hs_pvcy_add_entry(const uint8_t *addr, uint8_t addr_type,
153 const uint8_t *irk)
154 {
155 int rc;
156
157 STATS_INC(ble_hs_stats, pvcy_add_entry);
158
159 /* No GAP procedures can be active when adding an entry to the resolving
160 * list (Vol 2, Part E, 7.8.38). Stop all GAP procedures and temporarily
161 * prevent any new ones from being started.
162 */
163 ble_gap_preempt();
164
165 /* Try to add the entry now that GAP is halted. */
166 rc = ble_hs_pvcy_add_entry_hci(addr, addr_type, irk);
167
168 /* Allow GAP procedures to be started again. */
169 ble_gap_preempt_done();
170
171 if (rc != 0) {
172 STATS_INC(ble_hs_stats, pvcy_add_entry_fail);
173 }
174
175 return rc;
176 }
177
178 int
ble_hs_pvcy_ensure_started(void)179 ble_hs_pvcy_ensure_started(void)
180 {
181 int rc;
182
183 if (ble_hs_pvcy_started) {
184 return 0;
185 }
186
187 /* Set up the periodic change of our RPA. */
188 rc = ble_hs_pvcy_set_addr_timeout(MYNEWT_VAL(BLE_RPA_TIMEOUT));
189 if (rc != 0) {
190 return rc;
191 }
192
193 ble_hs_pvcy_started = 1;
194
195 return 0;
196 }
197
198 int
ble_hs_pvcy_set_our_irk(const uint8_t * irk)199 ble_hs_pvcy_set_our_irk(const uint8_t *irk)
200 {
201 uint8_t tmp_addr[6];
202 uint8_t new_irk[16];
203 int rc;
204
205 if (irk != NULL) {
206 memcpy(new_irk, irk, 16);
207 } else {
208 memcpy(new_irk, ble_hs_pvcy_default_irk, 16);
209 }
210
211 /* Clear the resolving list if this is a new IRK. */
212 if (memcmp(ble_hs_pvcy_irk, new_irk, 16) != 0) {
213 memcpy(ble_hs_pvcy_irk, new_irk, 16);
214
215 rc = ble_hs_pvcy_set_resolve_enabled(0);
216 if (rc != 0) {
217 return rc;
218 }
219
220 rc = ble_hs_pvcy_clear_entries();
221 if (rc != 0) {
222 return rc;
223 }
224
225 rc = ble_hs_pvcy_set_resolve_enabled(1);
226 if (rc != 0) {
227 return rc;
228 }
229
230 /*
231 * Add local IRK entry with 00:00:00:00:00:00 address. This entry will
232 * be used to generate RPA for non-directed advertising if own_addr_type
233 * is set to rpa_pub since we use all-zero address as peer addres in
234 * such case. Peer IRK should be left all-zero since this is not for an
235 * actual peer.
236 */
237 memset(tmp_addr, 0, 6);
238 memset(new_irk, 0, 16);
239 rc = ble_hs_pvcy_add_entry(tmp_addr, 0, new_irk);
240 if (rc != 0) {
241 return rc;
242 }
243 }
244
245 return 0;
246 }
247
248 int
ble_hs_pvcy_our_irk(const uint8_t ** out_irk)249 ble_hs_pvcy_our_irk(const uint8_t **out_irk)
250 {
251 /* XXX: Return error if privacy not supported. */
252
253 *out_irk = ble_hs_pvcy_irk;
254 return 0;
255 }
256
257 int
ble_hs_pvcy_set_mode(const ble_addr_t * addr,uint8_t priv_mode)258 ble_hs_pvcy_set_mode(const ble_addr_t *addr, uint8_t priv_mode)
259 {
260 uint8_t buf[BLE_HCI_LE_SET_PRIVACY_MODE_LEN];
261 int rc;
262
263 rc = ble_hs_hci_cmd_build_le_set_priv_mode(addr->val, addr->type, priv_mode,
264 buf, sizeof(buf));
265 if (rc != 0) {
266 return rc;
267 }
268
269 return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
270 BLE_HCI_OCF_LE_SET_PRIVACY_MODE),
271 buf, sizeof(buf), NULL, 0, NULL);
272 }
273