le_device_db_tlv: le_device_db.h implementation on top of btstack_tlv.h interface

This commit is contained in:
Matthias Ringwald 2017-08-11 16:34:10 +02:00
parent 5389404447
commit 1f5ff4332f
6 changed files with 642 additions and 13 deletions

View File

@ -47,11 +47,11 @@ extern "C" {
/**
LE Device DB for pure LE Peripherals is only required for signed writes
Note: LE Device DB for pure LE Peripherals is not required if only LE Legacy Pairing without signed writes is used
Per bonded device, it can store
Per bonded device:
- it stores the Identity Resolving Key (IRK) and its address to resolve private addresses
- it stores the LTK + EDIV, RAND. EDIV + RAND allow a LE Perihperal to reconstruct the LTK
- it stores the LTK + EDIV, RAND. EDIV + RAND allows a LE Peripheral to reconstruct the LTK
- it stores the Connection Signature Resolving Key (CSRK) and the last used counter.
The CSRK is used to generate the signatur on the remote device and is needed to verify the signature itself
The Counter is necessary to prevent reply attacks

439
src/ble/le_device_db_tlv.c Normal file
View File

@ -0,0 +1,439 @@
/*
* Copyright (C) 2017 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#define __BTSTACK_FILE__ "le_device_db_tlv.c"
#include "ble/le_device_db.h"
#include "ble/le_device_db_tlv.h"
#include "ble/core.h"
#include <stdio.h>
#include <string.h>
#include "btstack_debug.h"
// LE Device DB Implementation storing entries in btstack_tlv
// Local cache is used to keep track of deleted entries in TLV
#define INVALID_ENTRY_ADDR_TYPE 0xff
// Single stored entry
typedef struct le_device_db_entry_t {
// Identification
int addr_type;
bd_addr_t addr;
sm_key_t irk;
// Stored pairing information allows to re-establish an enncrypted connection
// with a peripheral that doesn't have any persistent memory
sm_key_t ltk;
uint16_t ediv;
uint8_t rand[8];
uint8_t key_size;
uint8_t authenticated;
uint8_t authorized;
#ifdef ENABLE_LE_SIGNED_WRITE
// Signed Writes by remote
sm_key_t remote_csrk;
uint32_t remote_counter;
// Signed Writes by us
sm_key_t local_csrk;
uint32_t local_counter;
#endif
} le_device_db_entry_t;
#ifndef MAX_NR_LE_DEVICE_DB_ENTRIES
#error "MAX_NR_LE_DEVICE_DB_ENTRIES not defined, please define in btstack_config.h"
#endif
#if MAX_NR_LE_DEVICE_DB_ENTRIES == 0
#error "MAX_NR_LE_DEVICE_DB_ENTRIES must not be 0, please update in btstack_config.h"
#endif
static uint8_t entry_map[MAX_NR_LE_DEVICE_DB_ENTRIES];
static uint32_t num_valid_entries;
static const btstack_tlv_t * le_device_db_tlv_btstack_tlv_impl;
static void * le_device_db_tlv_btstack_tlv_context;
static const char tag_0 = 'B';
static const char tag_1 = 'T';
static const char tag_2 = 'D';
static uint32_t le_device_db_tlv_tag_for_index(uint8_t index){
return (tag_0 << 24) | (tag_1 << 16) | (tag_2 << 8) | index;
}
// @returns success
// @param index = entry_pos
static int le_device_db_tlv_fetch(int index, le_device_db_entry_t * entry){
if (index < 0 || index >= MAX_NR_LE_DEVICE_DB_ENTRIES){
log_error("le_device_db_tlv_fetch called with invalid index %d", index);
return 0;
}
uint32_t tag = le_device_db_tlv_tag_for_index(index);
int size = le_device_db_tlv_btstack_tlv_impl->get_tag(le_device_db_tlv_btstack_tlv_context, tag, (uint8_t*) entry, sizeof(le_device_db_entry_t));
return size != 0;
}
// @returns success
// @param index = entry_pos
static int le_device_db_tlv_store(int index, le_device_db_entry_t * entry){
if (index < 0 || index >= MAX_NR_LE_DEVICE_DB_ENTRIES){
log_error("le_device_db_tlv_store called with invalid index %d", index);
return 0;
}
uint32_t tag = le_device_db_tlv_tag_for_index(index);
le_device_db_tlv_btstack_tlv_impl->store_tag(le_device_db_tlv_btstack_tlv_context, tag, (uint8_t*) entry, sizeof(le_device_db_entry_t));
return 1;
}
// @param index = entry_pos
static int le_device_db_tlv_delete(int index){
if (index < 0 || index >= MAX_NR_LE_DEVICE_DB_ENTRIES){
log_error("le_device_db_tlv_delete called with invalid index %d", index);
return 0;
}
uint32_t tag = le_device_db_tlv_tag_for_index(index);
le_device_db_tlv_btstack_tlv_impl->delete_tag(le_device_db_tlv_btstack_tlv_context, tag);
return 1;
}
static int le_device_db_tlv_fetch_mapped(int index, le_device_db_entry_t * entry){
if (index < 0 || index >= num_valid_entries){
log_error("le_device_db_tlv_fetch_mapped called with invalid index %d", index);
return 0;
}
return le_device_db_tlv_fetch(entry_map[index], entry);
}
static int le_device_db_tlv_store_mapped(int index, le_device_db_entry_t * entry){
if (index < 0 || index >= num_valid_entries){
log_error("le_device_tlv_store_mapped called with invalid index %d", index);
return 0;
}
return le_device_db_tlv_store_mapped(entry_map[index], entry);
}
void le_device_db_init(void){
int i;
num_valid_entries = 0;
memset(entry_map, 0, sizeof(entry_map));
for (i=0;i<MAX_NR_LE_DEVICE_DB_ENTRIES;i++){
// lookup entry
le_device_db_entry_t entry;
int ok = le_device_db_tlv_fetch(i, &entry);
if (!ok) continue;
// if valid, store entry_pos in entry_map
entry_map[num_valid_entries++] = i;
}
log_info("num valid le device entries %u", num_valid_entries);
}
// not used
void le_device_db_set_local_bd_addr(bd_addr_t bd_addr){
(void)bd_addr;
}
// @returns number of device in db
int le_device_db_count(void){
return num_valid_entries;
}
void le_device_db_remove(int index){
// delete entry in TLV
int entry_pos = entry_map[index];
le_device_db_tlv_delete(entry_pos);
// shift all entries down by one
int i;
for (i=index;i<MAX_NR_LE_DEVICE_DB_ENTRIES - 1;i++){
entry_map[i] = entry_map[i+1];
}
entry_map[MAX_NR_LE_DEVICE_DB_ENTRIES-1] = 0;
// keep track
num_valid_entries--;
}
int le_device_db_add(int addr_type, bd_addr_t addr, sm_key_t irk){
// find unused entry in the used list
int i;
int index = -1;
int expected_index = 0;
int new_index = -1;
for (i=0;i<num_valid_entries;i++){
if (entry_map[i] == expected_index){
expected_index++;
continue;
}
index = i;
new_index = expected_index;
break;
}
if (i == num_valid_entries && num_valid_entries < MAX_NR_LE_DEVICE_DB_ENTRIES){
index = num_valid_entries;
new_index = num_valid_entries;
}
// no free entry found
if (index < 0) return -1;
log_info("new entry pos %u used for index %u", index, new_index);
// shift all entries up by one
for (i = MAX_NR_LE_DEVICE_DB_ENTRIES - 1; i > index; i--){
entry_map[i] = entry_map[i-1];
}
// set in entry_mape
entry_map[index] = new_index;
// store entry at entry_pos = index
le_device_db_entry_t entry;
log_info("LE Device DB adding type %u - %s", addr_type, bd_addr_to_str(addr));
log_info_key("irk", irk);
entry.addr_type = addr_type;
memcpy(entry.addr, addr, 6);
memcpy(entry.irk, irk, 16);
#ifdef ENABLE_LE_SIGNED_WRITE
entry.remote_counter = 0;
#endif
// store
le_device_db_tlv_store(index, &entry);
// keep track
num_valid_entries++;
return index;
}
// get device information: addr type and address
void le_device_db_info(int index, int * addr_type, bd_addr_t addr, sm_key_t irk){
// fetch entry
le_device_db_entry_t entry;
int ok = le_device_db_tlv_fetch_mapped(index, &entry);
if (!ok) return;
if (addr_type) *addr_type = entry.addr_type;
if (addr) memcpy(addr, entry.addr, 6);
if (irk) memcpy(irk, entry.irk, 16);
}
void le_device_db_encryption_set(int index, uint16_t ediv, uint8_t rand[8], sm_key_t ltk, int key_size, int authenticated, int authorized){
// fetch entry
le_device_db_entry_t entry;
int ok = le_device_db_tlv_fetch_mapped(index, &entry);
if (!ok) return;
// update
log_info("LE Device DB set encryption for %u, ediv x%04x, key size %u, authenticated %u, authorized %u",
index, ediv, key_size, authenticated, authorized);
entry.ediv = ediv;
if (rand) memcpy(entry.rand, rand, 8);
if (ltk) memcpy(entry.ltk, ltk, 16);
entry.key_size = key_size;
entry.authenticated = authenticated;
entry.authorized = authorized;
// store
le_device_db_tlv_store_mapped(index, &entry);
}
void le_device_db_encryption_get(int index, uint16_t * ediv, uint8_t rand[8], sm_key_t ltk, int * key_size, int * authenticated, int * authorized){
// fetch entry
le_device_db_entry_t entry;
int ok = le_device_db_tlv_fetch_mapped(index, &entry);
if (!ok) return;
// update user fields
log_info("LE Device DB encryption for %u, ediv x%04x, keysize %u, authenticated %u, authorized %u",
index, entry.ediv, entry.key_size, entry.authenticated, entry.authorized);
if (ediv) *ediv = entry.ediv;
if (rand) memcpy(rand, entry.rand, 8);
if (ltk) memcpy(ltk, entry.ltk, 16);
if (key_size) *key_size = entry.key_size;
if (authenticated) *authenticated = entry.authenticated;
if (authorized) *authorized = entry.authorized;
}
#ifdef ENABLE_LE_SIGNED_WRITE
// get signature key
void le_device_db_remote_csrk_get(int index, sm_key_t csrk){
// fetch entry
le_device_db_entry_t entry;
int ok = le_device_db_tlv_fetch_mapped(index, &entry);
if (!ok) return;
if (csrk) memcpy(csrk, entry.remote_csrk, 16);
}
void le_device_db_remote_csrk_set(int index, sm_key_t csrk){
// fetch entry
le_device_db_entry_t entry;
int ok = le_device_db_tlv_fetch_mapped(index, &entry);
if (!ok) return;
if (!csrk) return;
// update
memcpy(entry.remote_csrk, csrk, 16);
// store
le_device_db_tlv_store_mapped(index, &entry);
}
void le_device_db_local_csrk_get(int index, sm_key_t csrk){
// fetch entry
le_device_db_entry_t entry;
int ok = le_device_db_tlv_fetch_mapped(index, &entry);
if (!ok) return;
if (!csrk) return;
// fill
memcpy(csrk, entry.local_csrk, 16);
}
void le_device_db_local_csrk_set(int index, sm_key_t csrk){
// fetch entry
le_device_db_entry_t entry;
int ok = le_device_db_tlv_fetch_mapped(index, &entry);
if (!ok) return;
if (!csrk) return;
// update
memcpy(entry.local_csrk, csrk, 16);
// store
le_device_db_tlv_store_mapped(index, &entry);
}
// query last used/seen signing counter
uint32_t le_device_db_remote_counter_get(int index){
// fetch entry
le_device_db_entry_t entry;
int ok = le_device_db_tlv_fetch_mapped(index, &entry);
if (!ok) return 0;
return entry.remote_counter;
}
// update signing counter
void le_device_db_remote_counter_set(int index, uint32_t counter){
// fetch entry
le_device_db_entry_t entry;
int ok = le_device_db_tlv_fetch_mapped(index, &entry);
if (!ok) return;
entry.remote_counter = counter;
// store
le_device_db_tlv_store_mapped(index, &entry);
}
// query last used/seen signing counter
uint32_t le_device_db_local_counter_get(int index){
// fetch entry
le_device_db_entry_t entry;
int ok = le_device_db_tlv_fetch_mapped(index, &entry);
if (!ok) return 0;
return entry.local_counter;
}
// update signing counter
void le_device_db_local_counter_set(int index, uint32_t counter){
// fetch entry
le_device_db_entry_t entry;
int ok = le_device_db_tlv_fetch_mapped(index, &entry);
if (!ok) return;
// update
entry.local_counter = counter;
// store
le_device_db_tlv_store_mapped(index, &entry);
}
#endif
void le_device_db_dump(void){
log_info("LE Device DB dump, devices: %d", le_device_db_count());
int i;
for (i=0;i<num_valid_entries;i++){
// fetch entry
le_device_db_entry_t entry;
le_device_db_tlv_fetch_mapped(i, &entry);
log_info("%u: %u %s", i, entry.addr_type, bd_addr_to_str(entry.addr));
log_info_key("irk", entry.irk);
#ifdef ENABLE_LE_SIGNED_WRITE
log_info_key("local csrk", entry.local_csrk);
log_info_key("remote csrk", entry.remote_csrk);
#endif
}
}
void le_device_db_tlv_configure(const btstack_tlv_t * btstack_tlv_impl, void * btstack_tlv_context){
le_device_db_tlv_btstack_tlv_impl = btstack_tlv_impl;
le_device_db_tlv_btstack_tlv_context = btstack_tlv_context;
}

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#ifndef __LE_DEVICE_DB_TLV_H
#define __LE_DEVICE_DB_TLV_H
#include "btstack_util.h"
#include "btstack_tlv.h"
#if defined __cplusplus
extern "C" {
#endif
/* API_START */
/**
* @brief configure le device db for use with btstack tlv instance
* @param btstack_tlv_impl to use
* @param btstack_tlv_context
*/
void le_device_db_tlv_configure(const btstack_tlv_t * btstack_tlv_impl, void * btstack_tlv_context);
/* API_END */
#if defined __cplusplus
}
#endif
#endif // __LE_DEVICE_DB_TLV_H

View File

@ -1,2 +1,4 @@
tlv_test
*.pklg
tlv_le_test
tlv_le_test.pklg

View File

@ -3,18 +3,16 @@ CC=g++
BTSTACK_ROOT = ../..
CPPUTEST_HOME = ${BTSTACK_ROOT}/test/cpputest
COMMON = \
btstack_link_key_db_tlv.c \
btstack_tlv_flash_sector.c \
btstack_util.c \
hal_flash_sector_memory.c \
hci_dump.c \
COMMON_OBJ = $(COMMON:.c=.o)
COMMON_OBJ = \
btstack_tlv_flash_sector.o \
btstack_util.o \
hal_flash_sector_memory.o \
hci_dump.o \
VPATH = \
${BTSTACK_ROOT}/src \
${BTSTACK_ROOT}/src/classic \
${BTSTACK_ROOT}/src/ble \
${BTSTACK_ROOT}/platform/embedded \
CFLAGS = \
@ -30,14 +28,17 @@ CFLAGS = \
LDFLAGS += -lCppUTest -lCppUTestExt
TESTS = tlv_test
TESTS = tlv_test tlv_le_test
all: ${TESTS}
clean:
rm -rf *.o $(TESTS) *.dSYM *.pklg
tlv_test: ${COMMON_OBJ} tlv_test.o
tlv_test: ${COMMON_OBJ} btstack_link_key_db_tlv.o tlv_test.o
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
tlv_le_test: ${COMMON_OBJ} le_device_db_tlv.o tlv_le_test.o
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
test: all

View File

@ -0,0 +1,123 @@
#include "CppUTest/TestHarness.h"
#include "CppUTest/CommandLineTestRunner.h"
#include "hal_flash_sector.h"
#include "hal_flash_sector_memory.h"
#include "btstack_tlv.h"
#include "btstack_tlv_flash_sector.h"
#include "hci_dump.h"
#include "ble/le_device_db.h"
#include "ble/le_device_db_tlv.h"
#include "btstack_util.h"
#include "btstack_config.h"
#include "btstack_debug.h"
#define HAL_FLASH_SECTOR_MEMORY_STORAGE_SIZE 512
static uint8_t hal_flash_sector_memory_storage[HAL_FLASH_SECTOR_MEMORY_STORAGE_SIZE];
static void CHECK_EQUAL_ARRAY(uint8_t * expected, uint8_t * actual, int size){
int i;
for (i=0; i<size; i++){
if (expected[i] != actual[i]) {
printf("offset %u wrong\n", i);
printf("expected: "); printf_hexdump(expected, size);
printf("actual: "); printf_hexdump(actual, size);
}
BYTES_EQUAL(expected[i], actual[i]);
}
}
//
TEST_GROUP(LE_DEVICE_DB){
const hal_flash_sector_t * hal_flash_sector_impl;
hal_flash_sector_memory_t hal_flash_sector_context;
const btstack_tlv_t * btstack_tlv_impl;
btstack_tlv_flash_sector_t btstack_tlv_context;
bd_addr_t addr_aa, addr_bb, addr_cc;
sm_key_t sm_key_aa, sm_key_bb, sm_key_cc;
void setup(void){
// hal_flash_sector
hal_flash_sector_impl = hal_flash_sector_memory_init_instance(&hal_flash_sector_context, hal_flash_sector_memory_storage, HAL_FLASH_SECTOR_MEMORY_STORAGE_SIZE);
hal_flash_sector_impl->erase(&hal_flash_sector_context, 0);
hal_flash_sector_impl->erase(&hal_flash_sector_context, 1);
// btstack_tlv
btstack_tlv_impl = btstack_tlv_flash_sector_init_instance(&btstack_tlv_context, hal_flash_sector_impl, &hal_flash_sector_context);
// le_device_db_tlv
le_device_db_tlv_configure(btstack_tlv_impl, &btstack_tlv_context);
le_device_db_init();
bd_addr_t addr_1 = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa };
bd_addr_t addr_2 = { 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb };
bd_addr_t addr_3 = { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc };
bd_addr_copy(addr_aa, addr_1);
bd_addr_copy(addr_bb, addr_2);
bd_addr_copy(addr_cc, addr_3);
memset(sm_key_aa, 0xaa, 16);
memset(sm_key_bb, 0xbb, 16);
memset(sm_key_cc, 0xcc, 16);
}
};
TEST(LE_DEVICE_DB, Empty){
CHECK_EQUAL(0, le_device_db_count());
}
TEST(LE_DEVICE_DB, AddOne){
le_device_db_add(BD_ADDR_TYPE_LE_PUBLIC, addr_aa, sm_key_aa);
CHECK_EQUAL(1, le_device_db_count());
}
TEST(LE_DEVICE_DB, RetrieveOne){
le_device_db_add(BD_ADDR_TYPE_LE_PUBLIC, addr_aa, sm_key_aa);
CHECK_EQUAL(1, le_device_db_count());
bd_addr_t addr;
sm_key_t sm_key;
int addr_type;
le_device_db_info(0, &addr_type, addr, sm_key);
CHECK_EQUAL_ARRAY(sm_key_aa, sm_key, 16);
CHECK_EQUAL_ARRAY(addr_aa, addr, 6);
}
TEST(LE_DEVICE_DB, AddOTwo){
le_device_db_add(BD_ADDR_TYPE_LE_PUBLIC, addr_aa, sm_key_aa);
le_device_db_add(BD_ADDR_TYPE_LE_PUBLIC, addr_bb, sm_key_bb);
CHECK_EQUAL(2, le_device_db_count());
}
TEST(LE_DEVICE_DB, AddOTwoRemoveOne){
le_device_db_add(BD_ADDR_TYPE_LE_PUBLIC, addr_aa, sm_key_aa);
le_device_db_add(BD_ADDR_TYPE_LE_PUBLIC, addr_bb, sm_key_bb);
le_device_db_remove(0);
CHECK_EQUAL(1, le_device_db_count());
bd_addr_t addr;
sm_key_t sm_key;
int addr_type;
le_device_db_info(0, &addr_type, addr, sm_key);
CHECK_EQUAL_ARRAY(sm_key_bb, sm_key, 16);
CHECK_EQUAL_ARRAY(addr_bb, addr, 6);
}
TEST(LE_DEVICE_DB, AddOTwoRemoveOneAddOne){
le_device_db_add(BD_ADDR_TYPE_LE_PUBLIC, addr_aa, sm_key_aa);
le_device_db_add(BD_ADDR_TYPE_LE_PUBLIC, addr_bb, sm_key_bb);
le_device_db_remove(0);
le_device_db_add(BD_ADDR_TYPE_LE_PUBLIC, addr_cc, sm_key_cc);
CHECK_EQUAL(2, le_device_db_count());
bd_addr_t addr;
sm_key_t sm_key;
int addr_type;
le_device_db_info(0, &addr_type, addr, sm_key);
CHECK_EQUAL_ARRAY(sm_key_cc, sm_key, 16);
CHECK_EQUAL_ARRAY(addr_cc, addr, 6);
}
int main (int argc, const char * argv[]){
hci_dump_open("tlv_le_test.pklg", HCI_DUMP_PACKETLOGGER);
return CommandLineTestRunner::RunAllTests(argc, argv);
}