| /* |
| * 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 <string.h> |
| |
| #include "sysinit/sysinit.h" |
| #include "syscfg/syscfg.h" |
| #include "host/ble_hs.h" |
| #include "config/config.h" |
| #include "base64/base64.h" |
| #include "store/config/ble_store_config.h" |
| #include "ble_store_config_priv.h" |
| |
| struct ble_store_value_sec |
| ble_store_config_our_secs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)]; |
| int ble_store_config_num_our_secs; |
| |
| struct ble_store_value_sec |
| ble_store_config_peer_secs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)]; |
| int ble_store_config_num_peer_secs; |
| |
| struct ble_store_value_cccd |
| ble_store_config_cccds[MYNEWT_VAL(BLE_STORE_MAX_CCCDS)]; |
| int ble_store_config_num_cccds; |
| |
| /***************************************************************************** |
| * $sec * |
| *****************************************************************************/ |
| |
| static void |
| ble_store_config_print_value_sec(const struct ble_store_value_sec *sec) |
| { |
| if (sec->ltk_present) { |
| BLE_HS_LOG(DEBUG, "ediv=%u rand=%llu authenticated=%d ltk=", |
| sec->ediv, sec->rand_num, sec->authenticated); |
| ble_hs_log_flat_buf(sec->ltk, 16); |
| BLE_HS_LOG(DEBUG, " "); |
| } |
| if (sec->irk_present) { |
| BLE_HS_LOG(DEBUG, "irk="); |
| ble_hs_log_flat_buf(sec->irk, 16); |
| BLE_HS_LOG(DEBUG, " "); |
| } |
| if (sec->csrk_present) { |
| BLE_HS_LOG(DEBUG, "csrk="); |
| ble_hs_log_flat_buf(sec->csrk, 16); |
| BLE_HS_LOG(DEBUG, " "); |
| } |
| |
| BLE_HS_LOG(DEBUG, "\n"); |
| } |
| |
| static void |
| ble_store_config_print_key_sec(const struct ble_store_key_sec *key_sec) |
| { |
| if (ble_addr_cmp(&key_sec->peer_addr, BLE_ADDR_ANY)) { |
| BLE_HS_LOG(DEBUG, "peer_addr_type=%d peer_addr=", |
| key_sec->peer_addr.type); |
| ble_hs_log_flat_buf(key_sec->peer_addr.val, 6); |
| BLE_HS_LOG(DEBUG, " "); |
| } |
| if (key_sec->ediv_rand_present) { |
| BLE_HS_LOG(DEBUG, "ediv=0x%02x rand=0x%llx ", |
| key_sec->ediv, key_sec->rand_num); |
| } |
| } |
| |
| static int |
| ble_store_config_find_sec(const struct ble_store_key_sec *key_sec, |
| const struct ble_store_value_sec *value_secs, |
| int num_value_secs) |
| { |
| const struct ble_store_value_sec *cur; |
| int skipped; |
| int i; |
| |
| skipped = 0; |
| |
| for (i = 0; i < num_value_secs; i++) { |
| cur = value_secs + i; |
| |
| if (ble_addr_cmp(&key_sec->peer_addr, BLE_ADDR_ANY)) { |
| if (ble_addr_cmp(&cur->peer_addr, &key_sec->peer_addr)) { |
| continue; |
| } |
| } |
| |
| if (key_sec->ediv_rand_present) { |
| if (cur->ediv != key_sec->ediv) { |
| continue; |
| } |
| |
| if (cur->rand_num != key_sec->rand_num) { |
| continue; |
| } |
| } |
| |
| if (key_sec->idx > skipped) { |
| skipped++; |
| continue; |
| } |
| |
| return i; |
| } |
| |
| return -1; |
| } |
| |
| static int |
| ble_store_config_read_our_sec(const struct ble_store_key_sec *key_sec, |
| struct ble_store_value_sec *value_sec) |
| { |
| int idx; |
| |
| idx = ble_store_config_find_sec(key_sec, ble_store_config_our_secs, |
| ble_store_config_num_our_secs); |
| if (idx == -1) { |
| return BLE_HS_ENOENT; |
| } |
| |
| *value_sec = ble_store_config_our_secs[idx]; |
| return 0; |
| } |
| |
| |
| static int |
| ble_store_config_write_our_sec(const struct ble_store_value_sec *value_sec) |
| { |
| struct ble_store_key_sec key_sec; |
| int idx; |
| int rc; |
| |
| BLE_HS_LOG(DEBUG, "persisting our sec; "); |
| ble_store_config_print_value_sec(value_sec); |
| |
| ble_store_key_from_value_sec(&key_sec, value_sec); |
| idx = ble_store_config_find_sec(&key_sec, ble_store_config_our_secs, |
| ble_store_config_num_our_secs); |
| if (idx == -1) { |
| if (ble_store_config_num_our_secs >= MYNEWT_VAL(BLE_STORE_MAX_BONDS)) { |
| BLE_HS_LOG(DEBUG, "error persisting our sec; too many entries " |
| "(%d)\n", ble_store_config_num_our_secs); |
| return BLE_HS_ESTORE_CAP; |
| } |
| |
| idx = ble_store_config_num_our_secs; |
| ble_store_config_num_our_secs++; |
| } |
| |
| ble_store_config_our_secs[idx] = *value_sec; |
| |
| rc = ble_store_config_persist_our_secs(); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ble_store_config_delete_obj(void *values, int value_size, int idx, |
| int *num_values) |
| { |
| uint8_t *dst; |
| uint8_t *src; |
| int move_count; |
| |
| (*num_values)--; |
| if (idx < *num_values) { |
| dst = values; |
| dst += idx * value_size; |
| src = dst + value_size; |
| |
| move_count = *num_values - idx; |
| memmove(dst, src, move_count * value_size); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ble_store_config_delete_sec(const struct ble_store_key_sec *key_sec, |
| struct ble_store_value_sec *value_secs, |
| int *num_value_secs) |
| { |
| int idx; |
| int rc; |
| |
| idx = ble_store_config_find_sec(key_sec, value_secs, *num_value_secs); |
| if (idx == -1) { |
| return BLE_HS_ENOENT; |
| } |
| |
| rc = ble_store_config_delete_obj(value_secs, sizeof *value_secs, idx, |
| num_value_secs); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ble_store_config_delete_our_sec(const struct ble_store_key_sec *key_sec) |
| { |
| int rc; |
| |
| rc = ble_store_config_delete_sec(key_sec, ble_store_config_our_secs, |
| &ble_store_config_num_our_secs); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| rc = ble_store_config_persist_our_secs(); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ble_store_config_delete_peer_sec(const struct ble_store_key_sec *key_sec) |
| { |
| int rc; |
| |
| rc = ble_store_config_delete_sec(key_sec, ble_store_config_peer_secs, |
| &ble_store_config_num_peer_secs); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| rc = ble_store_config_persist_peer_secs(); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ble_store_config_read_peer_sec(const struct ble_store_key_sec *key_sec, |
| struct ble_store_value_sec *value_sec) |
| { |
| int idx; |
| |
| idx = ble_store_config_find_sec(key_sec, ble_store_config_peer_secs, |
| ble_store_config_num_peer_secs); |
| if (idx == -1) { |
| return BLE_HS_ENOENT; |
| } |
| |
| *value_sec = ble_store_config_peer_secs[idx]; |
| return 0; |
| } |
| |
| static int |
| ble_store_config_write_peer_sec(const struct ble_store_value_sec *value_sec) |
| { |
| struct ble_store_key_sec key_sec; |
| int idx; |
| int rc; |
| |
| BLE_HS_LOG(DEBUG, "persisting peer sec; "); |
| ble_store_config_print_value_sec(value_sec); |
| |
| ble_store_key_from_value_sec(&key_sec, value_sec); |
| idx = ble_store_config_find_sec(&key_sec, ble_store_config_peer_secs, |
| ble_store_config_num_peer_secs); |
| if (idx == -1) { |
| if (ble_store_config_num_peer_secs >= MYNEWT_VAL(BLE_STORE_MAX_BONDS)) { |
| BLE_HS_LOG(DEBUG, "error persisting peer sec; too many entries " |
| "(%d)\n", ble_store_config_num_peer_secs); |
| return BLE_HS_ESTORE_CAP; |
| } |
| |
| idx = ble_store_config_num_peer_secs; |
| ble_store_config_num_peer_secs++; |
| } |
| |
| ble_store_config_peer_secs[idx] = *value_sec; |
| |
| rc = ble_store_config_persist_peer_secs(); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * $cccd * |
| *****************************************************************************/ |
| |
| static int |
| ble_store_config_find_cccd(const struct ble_store_key_cccd *key) |
| { |
| struct ble_store_value_cccd *cccd; |
| int skipped; |
| int i; |
| |
| skipped = 0; |
| for (i = 0; i < ble_store_config_num_cccds; i++) { |
| cccd = ble_store_config_cccds + i; |
| |
| if (ble_addr_cmp(&key->peer_addr, BLE_ADDR_ANY)) { |
| if (ble_addr_cmp(&cccd->peer_addr, &key->peer_addr)) { |
| continue; |
| } |
| } |
| |
| if (key->chr_val_handle != 0) { |
| if (cccd->chr_val_handle != key->chr_val_handle) { |
| continue; |
| } |
| } |
| |
| if (key->idx > skipped) { |
| skipped++; |
| continue; |
| } |
| |
| return i; |
| } |
| |
| return -1; |
| } |
| |
| static int |
| ble_store_config_delete_cccd(const struct ble_store_key_cccd *key_cccd) |
| { |
| int idx; |
| int rc; |
| |
| idx = ble_store_config_find_cccd(key_cccd); |
| if (idx == -1) { |
| return BLE_HS_ENOENT; |
| } |
| |
| rc = ble_store_config_delete_obj(ble_store_config_cccds, |
| sizeof *ble_store_config_cccds, |
| idx, |
| &ble_store_config_num_cccds); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| rc = ble_store_config_persist_cccds(); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ble_store_config_read_cccd(const struct ble_store_key_cccd *key_cccd, |
| struct ble_store_value_cccd *value_cccd) |
| { |
| int idx; |
| |
| idx = ble_store_config_find_cccd(key_cccd); |
| if (idx == -1) { |
| return BLE_HS_ENOENT; |
| } |
| |
| *value_cccd = ble_store_config_cccds[idx]; |
| return 0; |
| } |
| |
| static int |
| ble_store_config_write_cccd(const struct ble_store_value_cccd *value_cccd) |
| { |
| struct ble_store_key_cccd key_cccd; |
| int idx; |
| int rc; |
| |
| ble_store_key_from_value_cccd(&key_cccd, value_cccd); |
| idx = ble_store_config_find_cccd(&key_cccd); |
| if (idx == -1) { |
| if (ble_store_config_num_cccds >= MYNEWT_VAL(BLE_STORE_MAX_CCCDS)) { |
| BLE_HS_LOG(DEBUG, "error persisting cccd; too many entries (%d)\n", |
| ble_store_config_num_cccds); |
| return BLE_HS_ESTORE_CAP; |
| } |
| |
| idx = ble_store_config_num_cccds; |
| ble_store_config_num_cccds++; |
| } |
| |
| ble_store_config_cccds[idx] = *value_cccd; |
| |
| rc = ble_store_config_persist_cccds(); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * $api * |
| *****************************************************************************/ |
| |
| /** |
| * Searches the database for an object matching the specified criteria. |
| * |
| * @return 0 if a key was found; else BLE_HS_ENOENT. |
| */ |
| int |
| ble_store_config_read(int obj_type, const union ble_store_key *key, |
| union ble_store_value *value) |
| { |
| int rc; |
| |
| switch (obj_type) { |
| case BLE_STORE_OBJ_TYPE_PEER_SEC: |
| /* An encryption procedure (bonding) is being attempted. The nimble |
| * stack is asking us to look in our key database for a long-term key |
| * corresponding to the specified ediv and random number. |
| * |
| * Perform a key lookup and populate the context object with the |
| * result. The nimble stack will use this key if this function returns |
| * success. |
| */ |
| BLE_HS_LOG(DEBUG, "looking up peer sec; "); |
| ble_store_config_print_key_sec(&key->sec); |
| BLE_HS_LOG(DEBUG, "\n"); |
| rc = ble_store_config_read_peer_sec(&key->sec, &value->sec); |
| return rc; |
| |
| case BLE_STORE_OBJ_TYPE_OUR_SEC: |
| BLE_HS_LOG(DEBUG, "looking up our sec; "); |
| ble_store_config_print_key_sec(&key->sec); |
| BLE_HS_LOG(DEBUG, "\n"); |
| rc = ble_store_config_read_our_sec(&key->sec, &value->sec); |
| return rc; |
| |
| case BLE_STORE_OBJ_TYPE_CCCD: |
| rc = ble_store_config_read_cccd(&key->cccd, &value->cccd); |
| return rc; |
| |
| default: |
| return BLE_HS_ENOTSUP; |
| } |
| } |
| |
| /** |
| * Adds the specified object to the database. |
| * |
| * @return 0 on success; |
| * BLE_HS_ESTORE_CAP if the database is full. |
| */ |
| int |
| ble_store_config_write(int obj_type, const union ble_store_value *val) |
| { |
| int rc; |
| |
| switch (obj_type) { |
| case BLE_STORE_OBJ_TYPE_PEER_SEC: |
| rc = ble_store_config_write_peer_sec(&val->sec); |
| return rc; |
| |
| case BLE_STORE_OBJ_TYPE_OUR_SEC: |
| rc = ble_store_config_write_our_sec(&val->sec); |
| return rc; |
| |
| case BLE_STORE_OBJ_TYPE_CCCD: |
| rc = ble_store_config_write_cccd(&val->cccd); |
| return rc; |
| |
| default: |
| return BLE_HS_ENOTSUP; |
| } |
| } |
| |
| int |
| ble_store_config_delete(int obj_type, const union ble_store_key *key) |
| { |
| int rc; |
| |
| switch (obj_type) { |
| case BLE_STORE_OBJ_TYPE_PEER_SEC: |
| rc = ble_store_config_delete_peer_sec(&key->sec); |
| return rc; |
| |
| case BLE_STORE_OBJ_TYPE_OUR_SEC: |
| rc = ble_store_config_delete_our_sec(&key->sec); |
| return rc; |
| |
| case BLE_STORE_OBJ_TYPE_CCCD: |
| rc = ble_store_config_delete_cccd(&key->cccd); |
| return rc; |
| |
| default: |
| return BLE_HS_ENOTSUP; |
| } |
| } |
| |
| void |
| ble_store_config_init(void) |
| { |
| /* Ensure this function only gets called by sysinit. */ |
| SYSINIT_ASSERT_ACTIVE(); |
| |
| ble_hs_cfg.store_read_cb = ble_store_config_read; |
| ble_hs_cfg.store_write_cb = ble_store_config_write; |
| ble_hs_cfg.store_delete_cb = ble_store_config_delete; |
| |
| /* Re-initialize BSS values in case of unit tests. */ |
| ble_store_config_num_our_secs = 0; |
| ble_store_config_num_peer_secs = 0; |
| ble_store_config_num_cccds = 0; |
| |
| ble_store_config_conf_init(); |
| } |