| /* |
| * 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 <inttypes.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include "stats/stats.h" |
| #include "ble_hs_priv.h" |
| |
| static uint8_t ble_hs_pvcy_started; |
| static uint8_t ble_hs_pvcy_irk[16]; |
| |
| /** Use this as a default IRK if none gets set. */ |
| const uint8_t ble_hs_pvcy_default_irk[16] = { |
| 0xef, 0x8d, 0xe2, 0x16, 0x4f, 0xec, 0x43, 0x0d, |
| 0xbf, 0x5b, 0xdd, 0x34, 0xc0, 0x53, 0x1e, 0xb8, |
| }; |
| |
| static int |
| ble_hs_pvcy_set_addr_timeout(uint16_t timeout) |
| { |
| struct ble_hci_le_set_rpa_tmo_cp cmd; |
| |
| if (timeout == 0 || timeout > 0xA1B8) { |
| return BLE_ERR_INV_HCI_CMD_PARMS; |
| } |
| |
| cmd.rpa_timeout = htole16(timeout); |
| |
| return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_SET_RPA_TMO), |
| &cmd, sizeof(cmd), NULL, 0); |
| } |
| |
| static int |
| ble_hs_pvcy_set_resolve_enabled(int enable) |
| { |
| struct ble_hci_le_set_addr_res_en_cp cmd; |
| |
| cmd.enable = enable; |
| |
| return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_SET_ADDR_RES_EN), |
| &cmd, sizeof(cmd), NULL, 0); |
| } |
| |
| int |
| ble_hs_pvcy_remove_entry(uint8_t addr_type, const uint8_t *addr) |
| { |
| struct ble_hci_le_rmv_resolve_list_cp cmd; |
| |
| if (addr_type > BLE_ADDR_RANDOM) { |
| addr_type = addr_type % 2; |
| } |
| |
| cmd.peer_addr_type = addr_type; |
| memcpy(cmd.peer_id_addr, addr, BLE_DEV_ADDR_LEN); |
| |
| return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_RMV_RESOLV_LIST), |
| &cmd, sizeof(cmd), NULL, 0); |
| } |
| |
| static int |
| ble_hs_pvcy_clear_entries(void) |
| { |
| return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_CLR_RESOLV_LIST), |
| NULL, 0, NULL, 0); |
| } |
| |
| static int |
| ble_hs_pvcy_add_entry_hci(const uint8_t *addr, uint8_t addr_type, |
| const uint8_t *irk) |
| { |
| struct ble_hci_le_add_resolv_list_cp cmd; |
| ble_addr_t peer_addr; |
| int rc; |
| |
| if (addr_type > BLE_ADDR_RANDOM) { |
| return BLE_ERR_INV_HCI_CMD_PARMS; |
| } |
| |
| cmd.peer_addr_type = addr_type; |
| memcpy(cmd.peer_id_addr, addr, 6); |
| memcpy(cmd.local_irk, ble_hs_pvcy_irk, 16); |
| memcpy(cmd.peer_irk, irk, 16); |
| |
| rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_ADD_RESOLV_LIST), |
| &cmd, sizeof(cmd), NULL, 0); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| /* FIXME Controller is BT5.0 and default privacy mode is network which |
| * can cause problems for apps which are not aware of it. We need to |
| * sort it out somehow. For now we set device mode for all of the peer |
| * devices and application should change it to network if needed |
| */ |
| peer_addr.type = addr_type; |
| memcpy(peer_addr.val, addr, sizeof peer_addr.val); |
| rc = ble_hs_pvcy_set_mode(&peer_addr, BLE_GAP_PRIVATE_MODE_DEVICE); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| int |
| ble_hs_pvcy_add_entry(const uint8_t *addr, uint8_t addr_type, |
| const uint8_t *irk) |
| { |
| int rc; |
| |
| STATS_INC(ble_hs_stats, pvcy_add_entry); |
| |
| /* No GAP procedures can be active when adding an entry to the resolving |
| * list (Vol 2, Part E, 7.8.38). Stop all GAP procedures and temporarily |
| * prevent any new ones from being started. |
| */ |
| ble_gap_preempt(); |
| |
| /* Try to add the entry now that GAP is halted. */ |
| rc = ble_hs_pvcy_add_entry_hci(addr, addr_type, irk); |
| |
| /* Allow GAP procedures to be started again. */ |
| ble_gap_preempt_done(); |
| |
| if (rc != 0) { |
| STATS_INC(ble_hs_stats, pvcy_add_entry_fail); |
| } |
| |
| return rc; |
| } |
| |
| int |
| ble_hs_pvcy_ensure_started(void) |
| { |
| int rc; |
| |
| if (ble_hs_pvcy_started) { |
| return 0; |
| } |
| |
| /* Set up the periodic change of our RPA. */ |
| rc = ble_hs_pvcy_set_addr_timeout(MYNEWT_VAL(BLE_RPA_TIMEOUT)); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| ble_hs_pvcy_started = 1; |
| |
| return 0; |
| } |
| |
| int |
| ble_hs_pvcy_set_our_irk(const uint8_t *irk) |
| { |
| uint8_t tmp_addr[6]; |
| uint8_t new_irk[16]; |
| int rc; |
| |
| if (irk != NULL) { |
| memcpy(new_irk, irk, 16); |
| } else { |
| memcpy(new_irk, ble_hs_pvcy_default_irk, 16); |
| } |
| |
| /* Clear the resolving list if this is a new IRK. */ |
| if (memcmp(ble_hs_pvcy_irk, new_irk, 16) != 0) { |
| memcpy(ble_hs_pvcy_irk, new_irk, 16); |
| |
| rc = ble_hs_pvcy_set_resolve_enabled(0); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| rc = ble_hs_pvcy_clear_entries(); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| rc = ble_hs_pvcy_set_resolve_enabled(1); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| /* |
| * Add local IRK entry with 00:00:00:00:00:00 address. This entry will |
| * be used to generate RPA for non-directed advertising if own_addr_type |
| * is set to rpa_pub since we use all-zero address as peer addres in |
| * such case. Peer IRK should be left all-zero since this is not for an |
| * actual peer. |
| */ |
| memset(tmp_addr, 0, 6); |
| memset(new_irk, 0, 16); |
| rc = ble_hs_pvcy_add_entry(tmp_addr, 0, new_irk); |
| if (rc != 0) { |
| return rc; |
| } |
| } |
| |
| return 0; |
| } |
| |
| int |
| ble_hs_pvcy_our_irk(const uint8_t **out_irk) |
| { |
| /* XXX: Return error if privacy not supported. */ |
| |
| *out_irk = ble_hs_pvcy_irk; |
| return 0; |
| } |
| |
| int |
| ble_hs_pvcy_set_mode(const ble_addr_t *addr, uint8_t priv_mode) |
| { |
| struct ble_hci_le_set_privacy_mode_cp cmd; |
| |
| if (addr->type > BLE_ADDR_RANDOM) { |
| return BLE_ERR_INV_HCI_CMD_PARMS; |
| } |
| |
| cmd.mode = priv_mode; |
| cmd.peer_id_addr_type = addr->type; |
| memcpy(cmd.peer_id_addr, addr->val, BLE_DEV_ADDR_LEN); |
| |
| return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_SET_PRIVACY_MODE), |
| &cmd, sizeof(cmd), NULL, 0); |
| } |