Commit d788c494 authored by Rahix's avatar Rahix
Browse files

Merge 'New pairing database'

See merge request card10/firmware!402
parents 23aa643b 747803cc
......@@ -43,5 +43,9 @@ Option name Type Description
------------------ ---------- -----------
``default_app`` String Full path to the exectutable file of the default application. If this option is not set,``apps/analog_clock/__init__.py`` is used.
------------------ ---------- -----------
``ble_enable`` Boolean Activate the BLE interface. Turn off for more privacy or to conserve energy.
------------------ ---------- -----------
``ble_mac`` Boolean MAC address used for BLE. Format: ``ca:4d:10:xx:xx:xx``.
------------------ ---------- -----------
``ble_log_enable`` Boolean Activate HCI level logging of BLE data. Creates a new btsnoop compatible log file named ``ble.log`` in the ``logs`` folder after each boot if BLE is activated. Keeps the last 10 files.
================== ========== ===========
......@@ -180,11 +180,11 @@ static void setAddress(void)
uint8_t bdAddr[6] = { 0xCA, 0x4D, 0x10, 0x00, 0x00, 0x00 };
char buf[32];
int result = fs_read_text_file("mac.txt", buf, sizeof(buf));
int result = epic_config_get_string("ble_mac", buf, sizeof(buf));
if (result < 0) {
APP_TRACE_INFO0("mac.txt not found, generating random MAC");
epic_trng_read(bdAddr + 3, 3);
APP_TRACE_INFO0("ble_mac not set. Generating random MAC");
epic_csprng_read(bdAddr + 3, 3);
sprintf(buf,
"%02x:%02x:%02x:%02x:%02x:%02x\n",
bdAddr[0],
......@@ -193,9 +193,9 @@ static void setAddress(void)
bdAddr[3],
bdAddr[4],
bdAddr[5]);
fs_write_file("mac.txt", buf, strlen(buf));
epic_config_set_string("ble_mac", buf);
} else {
APP_TRACE_INFO1("mac file contents: %s", buf);
APP_TRACE_INFO1("ble_mac: %s", buf);
}
int a, b, c, d, e, f;
......@@ -261,35 +261,17 @@ void RSV11_IRQHandler(void)
notify();
}
/*************************************************************************************************/
#define BLEMAXCFGBYTES 100
bool ble_shall_start(void)
{
int bleConfigFile = epic_file_open("ble.txt", "r");
if (bleConfigFile < 0) {
LOG_INFO("ble", "can not open ble.txt -> BLE is not started");
epic_file_close(bleConfigFile);
return false;
}
char cfgBuf[BLEMAXCFGBYTES + 1];
int readNum = epic_file_read(bleConfigFile, cfgBuf, BLEMAXCFGBYTES);
epic_file_close(bleConfigFile);
if (readNum < 0) {
LOG_WARN("ble", "can not read ble.txt -> BLE is not started");
return false;
}
cfgBuf[readNum] = '\0';
bool ble_enabled = config_get_boolean_with_default("ble_enable", false);
char bleActiveStr[] = "active=true";
cfgBuf[sizeof(bleActiveStr) - 1] = '\0';
if (strcmp(cfgBuf, "active=true") != 0) {
LOG_INFO("ble", "BLE is disabled.");
return false;
} else {
if (ble_enabled) {
LOG_INFO("ble", "BLE is enabled.");
return true;
} else {
LOG_INFO("ble", "BLE is disabled.");
}
return ble_enabled;
}
/*************************************************************************************************/
static void scheduleTimer(void)
......
......@@ -394,6 +394,7 @@ static void bleCccCback(attsCccEvt_t *pEvt)
{
/* store value in device database */
AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value);
AppDbNvmStoreCccTbl(dbHdl);
}
if ((pMsg = WsfMsgAlloc(sizeof(attsCccEvt_t))) != NULL)
......@@ -463,7 +464,8 @@ static void bleSetup(bleMsg_t *pMsg)
char buf[32];
char a, b, c, d, e, f, K;
if (fs_read_text_file("mac.txt", buf, sizeof(buf)) > 0)
int result = epic_config_get_string("ble_mac", buf, sizeof(buf));
if (result == 0)
{
if (sscanf(buf, "%c%c:%c%c:%c%c:%c%c:%c%c:%c%c", &K,&K,&K,&K,&K,&K, &a, &b, &c, &d, &e, &f) == 12)
{
......@@ -761,6 +763,10 @@ static void bleProcMsg(bleMsg_t *pMsg)
case DM_SEC_PAIR_CMPL_IND:
LOG_INFO("ble", "Secure pairing successful, auth: 0x%02X",
pMsg->dm.pairCmpl.auth);
DmSecGenerateEccKeyReq();
AppDbNvmStoreBond(AppDbGetHdl((dmConnId_t) pMsg->hdr.param));
pair_connId = DM_CONN_ID_NONE;
trigger_event(BLE_EVENT_PAIRING_COMPLETE);
/* After a successful pairing, bonding is disabled again.
......@@ -783,6 +789,9 @@ static void bleProcMsg(bleMsg_t *pMsg)
pMsg->hdr.status);
break;
}
DmSecGenerateEccKeyReq();
pair_connId = DM_CONN_ID_NONE;
trigger_event(BLE_EVENT_PAIRING_FAILED);
break;
......
......@@ -20,7 +20,7 @@
/* card10:
* copied from: lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/apps/app/common/app_db.c
*
* Reason: we need to implement persistent storage for pairings
* Reason: we need to implement persistent storage for bondings
*/
/* clang-format off */
/* clang-formet turned off for easier diffing against orginal file */
......@@ -41,6 +41,35 @@
#include <string.h>
#include <stdio.h>
/**************************************************************************************************
Macros
**************************************************************************************************/
/* App DB NVM record parameter indicies from upstream:
* https://github.com/packetcraft-inc/stacks/blob/master/ble-profiles/sources/af/common/app_db.c#L46
*/
#define APP_DB_NVM_IN_USE_ID 0
#define APP_DB_NVM_PEER_ADDR_ID 1
#define APP_DB_NVM_ADDR_TYPE_ID 2
#define APP_DB_NVM_PEER_IRK_ID 3
#define APP_DB_NVM_PEER_CSRK_ID 4
#define APP_DB_NVM_KV_MASK_ID 5
#define APP_DB_NVM_VALID_ID 6
#define APP_DB_NVM_PEER_RAPO_ID 7
#define APP_DB_NVM_LOCAL_LTK_ID 8
#define APP_DB_NVM_LOCAL_SEC_LVL_ID 9
#define APP_DB_NVM_PEER_ADDR_RES_ID 10
#define APP_DB_NVM_PEER_LTK_ID 11
#define APP_DB_NVM_PEER_SEC_LVL_ID 12
#define APP_DB_NVM_CCC_TBL_ID 13
#define APP_DB_NVM_PEER_SIGN_CTR_ID 14
#define APP_DB_NVM_CAS_ID 15
#define APP_DB_NVM_CSF_ID 16
#define APP_DB_NVM_CACHE_HASH_ID 17
#define APP_DB_NVM_HASH_ID 18
#define APP_DB_NVM_HDL_LIST_ID 19
#define APP_DB_NVM_DISC_STATUS_ID 20
/**************************************************************************************************
Data Types
**************************************************************************************************/
......@@ -77,23 +106,179 @@ typedef struct
uint8_t discStatus; /*! Service discovery and configuration status */
} appDbRec_t;
/*! Database type */
typedef struct
{
appDbRec_t rec[APP_DB_NUM_RECS]; /*! Device database records */
char devName[ATT_DEFAULT_PAYLOAD_LEN]; /*! Device name */
uint8_t devNameLen; /*! Device name length */
} appDb_t;
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! Database */
static appDb_t appDb;
static appDbRec_t records[APP_DB_NUM_RECS];
/*! When all records are allocated use this index to determine which to overwrite */
static appDbRec_t *pAppDbNewRec = appDb.rec;
static appDbRec_t *pAppDbNewRec = records;
/* clang-format on */
/* Translate a pointer to a record into the filename to be used for it. */
static int record_to_filename(appDbRec_t *record, char *buf, size_t buf_size)
{
int id = record - records;
int ret = snprintf(buf, buf_size, "pairings/pairing%d.bin", id + 1);
if (ret >= (int)buf_size) {
ret = -1;
}
return ret;
}
/* Write a TLV to a file. */
static int write_tlv(int fd, uint32_t t, uint32_t l, void *v)
{
int ret;
ret = epic_file_write(fd, &t, sizeof(t));
if (ret != sizeof(t))
return ret;
ret = epic_file_write(fd, &l, sizeof(l));
if (ret != sizeof(l))
return ret;
ret = epic_file_write(fd, v, l);
if (ret != (int)l)
return ret;
return 0;
}
/* Read a TLV from a file.
*
* Super naive implementation assuming that the next TLV is
* the expected one. */
static int read_tlv(int fd, uint32_t t, uint32_t l, void *v)
{
int ret;
uint32_t t_r;
ret = epic_file_read(fd, &t_r, sizeof(t_r));
if (ret != sizeof(t))
return ret;
if (t != t_r)
return -ENOENT;
uint32_t l_r;
ret = epic_file_read(fd, &l_r, sizeof(l_r));
if (ret != sizeof(l_r))
return ret;
if (l_r > l)
return -EINVAL;
memset(v, 0, l);
ret = epic_file_read(fd, v, l_r);
if (ret != (int)l_r)
return ret;
return 0;
}
static int write_bond_to_file(appDbRec_t *r, char *filename)
{
if (!r->inUse) {
return -EINVAL;
}
int fd = epic_file_open(filename, "w");
int ret;
if (fd < 0) {
return fd;
}
static const uint8_t version = 1;
ret = epic_file_write(fd, &version, sizeof(version));
if (ret != sizeof(version))
goto out;
#define write_element(t, x) \
if ((ret = write_tlv(fd, t, sizeof(r->x), &r->x))) \
goto out;
write_element(APP_DB_NVM_PEER_ADDR_ID, peerAddr);
write_element(APP_DB_NVM_ADDR_TYPE_ID, addrType);
write_element(APP_DB_NVM_PEER_IRK_ID, peerIrk);
write_element(APP_DB_NVM_PEER_CSRK_ID, peerCsrk);
write_element(APP_DB_NVM_KV_MASK_ID, keyValidMask);
/* peerAddedToRl not persisted by upstream */
/* write_element(, peerAddedToRl); */
write_element(APP_DB_NVM_PEER_RAPO_ID, peerRpao);
write_element(APP_DB_NVM_LOCAL_LTK_ID, localLtk);
write_element(APP_DB_NVM_LOCAL_SEC_LVL_ID, localLtkSecLevel);
write_element(APP_DB_NVM_PEER_ADDR_RES_ID, peerAddrRes);
write_element(APP_DB_NVM_PEER_LTK_ID, peerLtk);
write_element(APP_DB_NVM_PEER_SEC_LVL_ID, peerLtkSecLevel);
write_element(APP_DB_NVM_CCC_TBL_ID, cccTbl);
write_element(APP_DB_NVM_PEER_SIGN_CTR_ID, peerSignCounter);
write_element(APP_DB_NVM_HDL_LIST_ID, hdlList);
write_element(APP_DB_NVM_DISC_STATUS_ID, discStatus);
write_element(APP_DB_NVM_VALID_ID, valid);
#undef write_element
out:
epic_file_close(fd);
return ret;
}
static int read_bond_from_file(appDbRec_t *r, char *filename)
{
int fd = epic_file_open(filename, "r");
if (fd < 0) {
return fd;
}
uint8_t version;
int ret = epic_file_read(fd, &version, sizeof(version));
if (ret != sizeof(version)) {
goto out;
}
if (version != 1) {
ret = -EINVAL;
goto out;
}
#define read_element(t, x) \
if ((ret = read_tlv(fd, t, sizeof(r->x), &r->x))) \
goto out;
read_element(APP_DB_NVM_PEER_ADDR_ID, peerAddr);
read_element(APP_DB_NVM_ADDR_TYPE_ID, addrType);
read_element(APP_DB_NVM_PEER_IRK_ID, peerIrk);
read_element(APP_DB_NVM_PEER_CSRK_ID, peerCsrk);
read_element(APP_DB_NVM_KV_MASK_ID, keyValidMask);
/* peerAddedToRl not persisted by upstream */
/* read_element(, peerAddedToRl); */
read_element(APP_DB_NVM_PEER_RAPO_ID, peerRpao);
read_element(APP_DB_NVM_LOCAL_LTK_ID, localLtk);
read_element(APP_DB_NVM_LOCAL_SEC_LVL_ID, localLtkSecLevel);
read_element(APP_DB_NVM_PEER_ADDR_RES_ID, peerAddrRes);
read_element(APP_DB_NVM_PEER_LTK_ID, peerLtk);
read_element(APP_DB_NVM_PEER_SEC_LVL_ID, peerLtkSecLevel);
read_element(APP_DB_NVM_CCC_TBL_ID, cccTbl);
read_element(APP_DB_NVM_PEER_SIGN_CTR_ID, peerSignCounter);
read_element(APP_DB_NVM_HDL_LIST_ID, hdlList);
read_element(APP_DB_NVM_DISC_STATUS_ID, discStatus);
read_element(APP_DB_NVM_VALID_ID, valid);
#undef read_element
r->inUse = true;
out:
epic_file_close(fd);
return ret;
}
static int delete_bond(char *filename)
{
return epic_file_unlink(filename);
}
/*************************************************************************************************/
/*!
......@@ -104,28 +289,27 @@ static appDbRec_t *pAppDbNewRec = appDb.rec;
/*************************************************************************************************/
void AppDbInit(void)
{
int fd = epic_file_open("pairings.bin", "r");
if(fd >= 0) {
if(epic_file_read(fd, &appDb, sizeof(appDb)) != sizeof(appDb)) {
memset(&appDb, 0, sizeof(appDb));
}
epic_file_close(fd);
}
}
static void AppDbStore(void)
{
LOG_INFO("appDb", "writing to persistent storage");
int fd = epic_file_open("pairings.bin", "w");
if(fd >= 0) {
if(epic_file_write(fd, &appDb, sizeof(appDb)) != sizeof(appDb)) {
}
epic_file_close(fd);
}
memset(&records, 0, sizeof(records));
char filename[32];
for (int i = 0; i < APP_DB_NUM_RECS; i++) {
record_to_filename(&records[i], filename, sizeof(filename));
int ret = read_bond_from_file(&records[i], filename);
if (ret < 0) {
if (ret != -ENOENT) {
LOG_WARN(
"bondings",
"Reading pairing '%s' failed: %d",
filename,
ret
);
}
memset(&records[i], 0, sizeof(records[i]));
}
}
}
/* clang-format off */
/*************************************************************************************************/
/*!
* \brief Create a new device database record.
......@@ -138,7 +322,7 @@ static void AppDbStore(void)
/*************************************************************************************************/
appDbHdl_t AppDbNewRecord(uint8_t addrType, uint8_t *pAddr)
{
appDbRec_t *pRec = appDb.rec;
appDbRec_t *pRec = records;
uint8_t i;
/* find a free record */
......@@ -158,9 +342,9 @@ appDbHdl_t AppDbNewRecord(uint8_t addrType, uint8_t *pAddr)
/* get next record to overwrite */
pAppDbNewRec++;
if (pAppDbNewRec == &appDb.rec[APP_DB_NUM_RECS])
if (pAppDbNewRec == &records[APP_DB_NUM_RECS])
{
pAppDbNewRec = appDb.rec;
pAppDbNewRec = records;
}
}
......@@ -192,7 +376,7 @@ appDbHdl_t AppDbGetNextRecord(appDbHdl_t hdl)
/* if first record is requested */
if (hdl == APP_DB_HDL_NONE)
{
pRec = appDb.rec;
pRec = records;
}
/* if valid record passed in */
else if (AppDbRecordInUse(hdl))
......@@ -207,7 +391,7 @@ appDbHdl_t AppDbGetNextRecord(appDbHdl_t hdl)
}
/* look for next valid record */
while (pRec < &appDb.rec[APP_DB_NUM_RECS])
while (pRec < &records[APP_DB_NUM_RECS])
{
/* if record is in use */
if (pRec->inUse && pRec->valid)
......@@ -236,6 +420,9 @@ appDbHdl_t AppDbGetNextRecord(appDbHdl_t hdl)
void AppDbDeleteRecord(appDbHdl_t hdl)
{
((appDbRec_t *) hdl)->inUse = FALSE;
char filename[32];
record_to_filename((appDbRec_t *) hdl, filename, sizeof(filename));
delete_bond(filename);
}
/*************************************************************************************************/
......@@ -253,7 +440,6 @@ void AppDbValidateRecord(appDbHdl_t hdl, uint8_t keyMask)
{
((appDbRec_t *) hdl)->valid = TRUE;
((appDbRec_t *) hdl)->keyValidMask = keyMask;
AppDbStore();
}
/*************************************************************************************************/
......@@ -285,7 +471,7 @@ void AppDbCheckValidRecord(appDbHdl_t hdl)
/*************************************************************************************************/
bool_t AppDbRecordInUse(appDbHdl_t hdl)
{
appDbRec_t *pRec = appDb.rec;
appDbRec_t *pRec = records;
uint8_t i;
/* see if record is in database record list */
......@@ -311,7 +497,7 @@ bool_t AppDbRecordInUse(appDbHdl_t hdl)
/*************************************************************************************************/
bool_t AppDbCheckBonded(void)
{
appDbRec_t *pRec = appDb.rec;
appDbRec_t *pRec = records;
uint8_t i;
/* find a record */
......@@ -335,7 +521,7 @@ bool_t AppDbCheckBonded(void)
/*************************************************************************************************/
void AppDbDeleteAllRecords(void)
{
appDbRec_t *pRec = appDb.rec;
appDbRec_t *pRec = records;
uint8_t i;
/* set in use to false for all records */
......@@ -357,7 +543,7 @@ void AppDbDeleteAllRecords(void)
/*************************************************************************************************/
appDbHdl_t AppDbFindByAddr(uint8_t addrType, uint8_t *pAddr)
{
appDbRec_t *pRec = appDb.rec;
appDbRec_t *pRec = records;
uint8_t peerAddrType = DmHostAddrType(addrType);
uint8_t i;
......@@ -385,7 +571,7 @@ appDbHdl_t AppDbFindByAddr(uint8_t addrType, uint8_t *pAddr)
/*************************************************************************************************/
appDbHdl_t AppDbFindByLtkReq(uint16_t encDiversifier, uint8_t *pRandNum)
{
appDbRec_t *pRec = appDb.rec;
appDbRec_t *pRec = records;
uint8_t i;
/* find matching record */
......@@ -581,48 +767,6 @@ void AppDbSetHdlList(appDbHdl_t hdl, uint16_t *pHdlList)
memcpy(((appDbRec_t *) hdl)->hdlList, pHdlList, sizeof(((appDbRec_t *) hdl)->hdlList));
}
/*************************************************************************************************/
/*!
* \brief Get the device name.
*
* \param pLen Returned device name length.
*
* \return Pointer to UTF-8 string containing device name or NULL if not set.
*/
/*************************************************************************************************/
char *AppDbGetDevName(uint8_t *pLen)
{
/* if first character of name is NULL assume it is uninitialized */
if (appDb.devName[0] == 0)
{
*pLen = 0;
return NULL;
}
else
{
*pLen = appDb.devNameLen;
return appDb.devName;
}
}
/*************************************************************************************************/
/*!
* \brief Set the device name.
*
* \param len Device name length.
* \param pStr UTF-8 string containing device name.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbSetDevName(uint8_t len, char *pStr)
{
/* check for maximum device length */
len = (len <= sizeof(appDb.devName)) ? len : sizeof(appDb.devName);
memcpy(appDb.devName, pStr, len);
}
/*************************************************************************************************/
/*!
* \brief Get address resolution attribute value read from a peer device.
......@@ -740,4 +884,49 @@ void AppDbSetPeerRpao(appDbHdl_t hdl, bool_t peerRpao)
{
((appDbRec_t *)hdl)->peerRpao = peerRpao;
}
/*************************************************************************************************/
/*!
* \brief Store the client characteristic configuration table for a device record in NVM.
*
* \param hdl Database record handle.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbNvmStoreCccTbl(appDbHdl_t hdl)
{
/* We take a short cut and simply write the whole file again. */
AppDbNvmStoreBond(hdl);
}
/*************************************************************************************************/
/*!
* \brief Store bonding information for device record in NVM.
*
* \param hdl Database record handle.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbNvmStoreBond(appDbHdl_t hdl)
{
appDbRec_t *pRec = (appDbRec_t *) hdl;
if (pRec->inUse && pRec->valid) {
char filename[32];
record_to_filename(pRec, filename, sizeof(filename));
/* Directory might exist already. Call will fail silently in that case. */
epic_file_mkdir("pairings");
int ret = write_bond_to_file(pRec, filename);
if(ret < 0) {
LOG_WARN(
"bondings",
"Writing pairing '%s' failed: %d",
filename,
ret
);
}
}
}
/* clang-format on */
......@@ -4,7 +4,7 @@ ble_sources = files(
'ble_main.c',
'svc_dis.c',
'svc_core.c',
'app/common/app_db.c',
'bondings.c',
'uart.c',
'card10.c',
'filetransfer.c',
......
......@@ -395,6 +395,28 @@ bool_t AppDbGetPeerRpao(appDbHdl_t hdl);
/*************************************************************************************************/
void AppDbSetPeerRpao(appDbHdl_t hdl, bool_t peerRpao);
/*************************************************************************************************/
/*!
* \brief Store the client characteristic configuration table for a device record in NVM.
*
* \param hdl Database record handle.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbNvmStoreCccTbl(appDbHdl_t hdl);
/*************************************************************************************************/
/*!
* \brief Store bonding information for device record in NVM.
*
* \param hdl Database record handle.