diff --git a/epicardium/ble/bondings.c b/epicardium/ble/bondings.c index 54a774b7c26bd2c03de260e11f7ef20b4d8a2cec..3f43ebde45b48fdcdf7039ca0fe0495e2219b70f 100644 --- a/epicardium/ble/bondings.c +++ b/epicardium/ble/bondings.c @@ -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 **************************************************************************************************/ @@ -88,6 +117,165 @@ 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 = 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) +{ + 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); +} + /*************************************************************************************************/ /*! * \brief Initialize the device database. @@ -97,28 +285,27 @@ static appDbRec_t *pAppDbNewRec = records; /*************************************************************************************************/ void AppDbInit(void) { - int fd = epic_file_open("pairings.bin", "r"); - - if(fd >= 0) { - if(epic_file_read(fd, records, sizeof(records)) != sizeof(records)) { - memset(records, 0, sizeof(records)); - } - epic_file_close(fd); - } -} - -static void AppDbStore(void) -{ - LOG_INFO("bondings", "writing to persistent storage"); - - int fd = epic_file_open("pairings.bin", "w"); - if(fd >= 0) { - if(epic_file_write(fd, records, sizeof(records)) != sizeof(records)) { - } - 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. @@ -229,6 +416,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); } /*************************************************************************************************/ @@ -246,7 +436,11 @@ void AppDbValidateRecord(appDbHdl_t hdl, uint8_t keyMask) { ((appDbRec_t *) hdl)->valid = TRUE; ((appDbRec_t *) hdl)->keyValidMask = keyMask; - AppDbStore(); + char filename[32]; + record_to_filename((appDbRec_t *) hdl, filename, sizeof(filename)); + /* Directory might exist already. Call will fail silently in that case. */ + epic_file_mkdir("pairings"); + write_bond_to_file((appDbRec_t *) hdl, filename); } /*************************************************************************************************/ @@ -514,6 +708,10 @@ void AppDbSetCccTblValue(appDbHdl_t hdl, uint16_t idx, uint16_t value) WSF_ASSERT(idx < APP_DB_NUM_CCCD); ((appDbRec_t *) hdl)->cccTbl[idx] = value; + + char filename[32]; + record_to_filename((appDbRec_t *) hdl, filename, sizeof(filename)); + write_bond_to_file((appDbRec_t *) hdl, filename); } /*************************************************************************************************/