Skip to content

Commit

Permalink
nimble/host: Support database hash
Browse files Browse the repository at this point in the history
Supports database hash characteristic.
Computes database hash and stores it on first pairing with device.
Computes current hash and compares it with stored hash on reconnection.

See BLE Core spec 5.2 section 7.2
  • Loading branch information
Marceau Fillon authored and pi-anl committed Nov 25, 2021
1 parent 58bdadb commit 3f01350
Show file tree
Hide file tree
Showing 9 changed files with 311 additions and 4 deletions.
1 change: 1 addition & 0 deletions nimble/host/include/host/ble_att.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct os_mbuf;
#define BLE_ATT_UUID_SECONDARY_SERVICE 0x2801
#define BLE_ATT_UUID_INCLUDE 0x2802
#define BLE_ATT_UUID_CHARACTERISTIC 0x2803
#define BLE_ATT_UUID_DESCRIPTOR 0x2902

#define BLE_ATT_ERR_INVALID_HANDLE 0x01
#define BLE_ATT_ERR_READ_NOT_PERMITTED 0x02
Expand Down
2 changes: 2 additions & 0 deletions nimble/host/include/host/ble_gatt.h
Original file line number Diff line number Diff line change
Expand Up @@ -894,3 +894,5 @@ int ble_gatts_start(void);
*/

#endif

int ble_compute_db_hash(uint8_t db_hash[16]);
35 changes: 35 additions & 0 deletions nimble/host/include/host/ble_store.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ extern "C" {
#define BLE_STORE_OBJ_TYPE_OUR_SEC 1
#define BLE_STORE_OBJ_TYPE_PEER_SEC 2
#define BLE_STORE_OBJ_TYPE_CCCD 3
#define BLE_STORE_OBJ_TYPE_HASH 4

/** Failed to persist record; insufficient storage capacity. */
#define BLE_STORE_EVENT_OVERFLOW 1
Expand Down Expand Up @@ -120,13 +121,38 @@ struct ble_store_value_cccd {
unsigned value_changed:1;
};

/**
* Used as a key for lookups of stored database hashes.
* This struct corresponds to the BLE_STORE_OBJ_TYPE_HASH store object type.
*/
struct ble_store_key_hash {
/**
* Key by peer identity address;
* peer_addr=BLE_ADDR_NONE means don't key off peer.
*/
ble_addr_t peer_addr;

/** Number of results to skip; 0 means retrieve the first match. */
uint8_t idx;
};

/**
* Represents the last stored server database hash for a paired client device.
* This struct corresponds to the BLE_STORE_OBJ_TYPE_HASH store object type.
*/
struct ble_store_value_hash{
ble_addr_t peer_addr;
uint8_t db_hash[16];
};

/**
* Used as a key for store lookups. This union must be accompanied by an
* object type code to indicate which field is valid.
*/
union ble_store_key {
struct ble_store_key_sec sec;
struct ble_store_key_cccd cccd;
struct ble_store_key_hash hash;
};

/**
Expand All @@ -136,6 +162,7 @@ union ble_store_key {
union ble_store_value {
struct ble_store_value_sec sec;
struct ble_store_value_cccd cccd;
struct ble_store_value_hash hash;
};

struct ble_store_status_event {
Expand Down Expand Up @@ -266,10 +293,18 @@ int ble_store_read_cccd(const struct ble_store_key_cccd *key,
int ble_store_write_cccd(const struct ble_store_value_cccd *value);
int ble_store_delete_cccd(const struct ble_store_key_cccd *key);

int ble_store_read_hash(uint16_t conn_handle,
const struct ble_store_key_hash *key,
struct ble_store_value_hash *out_value);
int ble_store_write_hash(const struct ble_store_value_hash *value);
int ble_store_delete_hash(const struct ble_store_key_hash *key);

void ble_store_key_from_value_sec(struct ble_store_key_sec *out_key,
const struct ble_store_value_sec *value);
void ble_store_key_from_value_cccd(struct ble_store_key_cccd *out_key,
const struct ble_store_value_cccd *value);
void ble_store_key_from_value_hash(struct ble_store_key_hash *out_key,
const struct ble_store_value_hash *value);

void ble_store_key_from_value(int obj_type,
union ble_store_key *out_key,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ extern "C" {
struct ble_hs_cfg;

#define BLE_SVC_GATT_CHR_SERVICE_CHANGED_UUID16 0x2a05
#define BLE_SVC_GATT_CHR_DATABASE_HASH_UUID16 0x2B2A

void ble_svc_gatt_changed(uint16_t start_handle, uint16_t end_handle);
void ble_svc_conn_gatt_changed(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle);
void ble_svc_gatt_init(void);

#ifdef __cplusplus
Expand Down
56 changes: 53 additions & 3 deletions nimble/host/services/gatt/src/ble_svc_gatt.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,21 @@
#include "sysinit/sysinit.h"
#include "host/ble_hs.h"
#include "services/gatt/ble_svc_gatt.h"
#include "host/ble_gatt.h"

static uint16_t ble_svc_gatt_changed_val_handle;
static uint16_t ble_svc_gatt_hash_val_handle;
static uint16_t ble_svc_gatt_start_handle;
static uint16_t ble_svc_gatt_end_handle;

static int
ble_svc_gatt_access(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);

static int
ble_svc_gatt_db_hash_access(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);

static const struct ble_gatt_svc_def ble_svc_gatt_defs[] = {
{
/*** Service: GATT */
Expand All @@ -41,7 +47,14 @@ static const struct ble_gatt_svc_def ble_svc_gatt_defs[] = {
.access_cb = ble_svc_gatt_access,
.val_handle = &ble_svc_gatt_changed_val_handle,
.flags = BLE_GATT_CHR_F_INDICATE,
}, {
},
{
.uuid = BLE_UUID16_DECLARE(BLE_SVC_GATT_CHR_DATABASE_HASH_UUID16),
.access_cb = ble_svc_gatt_db_hash_access,
.val_handle = &ble_svc_gatt_hash_val_handle,
.flags = BLE_GATT_CHR_F_READ,
},
{
0, /* No more characteristics in this service. */
} },
},
Expand All @@ -51,6 +64,28 @@ static const struct ble_gatt_svc_def ble_svc_gatt_defs[] = {
},
};

static int
ble_svc_gatt_db_hash_access(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
int rc;
assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
assert(ctxt->chr == &ble_svc_gatt_defs[0].characteristics[1]);
uint8_t db_hash[16];
uint16_t len_data = sizeof(db_hash);
int res = ble_compute_db_hash(db_hash);
if(res != 0){
return BLE_ATT_ERR_UNLIKELY;
}

rc = os_mbuf_append(ctxt->om, db_hash, len_data);
if(rc !=0){
return BLE_ATT_ERR_UNLIKELY;
}

return 0;
}

static int
ble_svc_gatt_access(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
Expand All @@ -73,7 +108,7 @@ ble_svc_gatt_access(uint16_t conn_handle, uint16_t attr_handle,

put_le16(u8p + 0, ble_svc_gatt_start_handle);
put_le16(u8p + 2, ble_svc_gatt_end_handle);

return 0;
}

Expand All @@ -89,7 +124,22 @@ ble_svc_gatt_changed(uint16_t start_handle, uint16_t end_handle)
{
ble_svc_gatt_start_handle = start_handle;
ble_svc_gatt_end_handle = end_handle;
ble_gatts_chr_updated(ble_svc_gatt_changed_val_handle);
ble_gatts_chr_updated(ble_svc_gatt_changed_val_handle);
}

/**
* Indicates a change in attribute assignment to specified peer.
*
* @param conn_handle The connection to indicate.
* @param start_handle The start of the affected handle range.
* @param end_handle The end of the affected handle range.
*/
void
ble_svc_conn_gatt_changed(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle)
{
ble_svc_gatt_start_handle = start_handle;
ble_svc_gatt_end_handle = end_handle;
ble_gattc_indicate(conn_handle, ble_svc_gatt_changed_val_handle);
}

void
Expand Down
124 changes: 124 additions & 0 deletions nimble/host/src/ble_gatts.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@
#define BLE_GATTS_INCLUDE_SZ 6
#define BLE_GATTS_CHR_MAX_SZ 19

#if NIMBLE_BLE_SM
#include "tinycrypt/aes.h"
#include "tinycrypt/constants.h"
#include "tinycrypt/utils.h"

#if MYNEWT_VAL(BLE_SM_SC)
#include "tinycrypt/cmac_mode.h"
#include "tinycrypt/ecc_dh.h"
#endif
#endif

static const ble_uuid_t *uuid_pri =
BLE_UUID16_DECLARE(BLE_ATT_UUID_PRIMARY_SERVICE);
static const ble_uuid_t *uuid_sec =
Expand Down Expand Up @@ -1719,6 +1730,8 @@ ble_gatts_bonding_restored(uint16_t conn_handle)
{
struct ble_store_value_cccd cccd_value;
struct ble_store_key_cccd cccd_key;
struct ble_store_value_hash hash_value;
struct ble_store_key_hash hash_key;
struct ble_gatts_clt_cfg *clt_cfg;
struct ble_hs_conn *conn;
uint8_t att_op;
Expand Down Expand Up @@ -1799,6 +1812,13 @@ ble_gatts_bonding_restored(uint16_t conn_handle)

cccd_key.idx++;
}

hash_key.peer_addr = conn->bhc_peer_addr;
hash_key.peer_addr.type =
ble_hs_misc_peer_addr_type_to_id(conn->bhc_peer_addr.type);
hash_key.idx = 0;

rc = ble_store_read_hash(conn_handle, &hash_key, &hash_value); // read peer hash, read the database hash characteristic and compare, if rc != 0 => ble_svc_conn_gatt_changed(0, 0xffff)
}

static struct ble_gatts_svc_entry *
Expand Down Expand Up @@ -2182,3 +2202,107 @@ ble_gatts_init(void)
return 0;

}

static void ble_db_hash_message_append_u16(uint16_t val, uint8_t *buf, uint8_t *len) {
val = htole16(val);
memcpy(buf + *len, &val, sizeof(uint16_t));
*len += sizeof(uint16_t);
}

static void ble_db_hash_message_append_u32(uint32_t val, uint8_t *buf, uint8_t *len) {
val = htole32(val);
memcpy(buf + *len, &val, sizeof(uint32_t));
*len += sizeof(uint32_t);
}

static void ble_db_hash_message_append_uuid(const ble_uuid_t *uuid, uint8_t *buf, uint8_t *len) {
switch (uuid->type) {
case BLE_UUID_TYPE_16: {
ble_db_hash_message_append_u16( BLE_UUID16(uuid)->value, buf, len);
break;
}
case BLE_UUID_TYPE_32: {
ble_db_hash_message_append_u32(BLE_UUID32(uuid)->value, buf, len);
break;
}
case BLE_UUID_TYPE_128: {
memcpy(buf + *len, BLE_UUID128(uuid)->value, 16);
*len += 16;
break;
}
default:
break;
}
}

/**
* Called when the database cmac hash needs to be computed using the information
* from registered services, characteristics and descriptors. This
* function:
* o Sets up the cmac generator with a key of 0s
* o Parse services, characteristics and descriptors and adds information
* to the message to code.
* o Computes the cmac coded message.
*/
int ble_compute_db_hash(uint8_t db_hash[16])
{
uint8_t buf[24];
uint8_t buf_len;

struct tc_aes_key_sched_struct sched;
struct tc_cmac_struct state;
const uint8_t key[16] = {0};

if (tc_cmac_setup(&state, key, &sched) == TC_CRYPTO_FAIL) {
return BLE_HS_EUNKNOWN;
}

for (int i = 0; i < ble_gatts_num_svc_entries; i++) {
struct ble_gatts_svc_entry *entry = &ble_gatts_svc_entries[i];
buf_len = 0;
ble_db_hash_message_append_u16(entry->handle, buf, &buf_len);
ble_db_hash_message_append_u16(BLE_ATT_UUID_PRIMARY_SERVICE, buf, &buf_len);
ble_db_hash_message_append_uuid(entry->svc->uuid, buf, &buf_len);

if (tc_cmac_update(&state, buf, buf_len) == TC_CRYPTO_FAIL) {
return BLE_HS_EUNKNOWN;
}
buf_len = 0;

if (entry->svc->characteristics != NULL) {
for (const struct ble_gatt_chr_def *chr = entry->svc->characteristics; chr && chr->uuid; chr++) {
if (chr->val_handle == NULL) {
continue;
}
ble_db_hash_message_append_u16(*chr->val_handle - 1, buf, &buf_len);
ble_db_hash_message_append_u16(BLE_ATT_UUID_CHARACTERISTIC, buf, &buf_len);
ble_db_hash_message_append_u16(chr->flags, buf, &buf_len);
ble_db_hash_message_append_u16(*chr->val_handle, buf, &buf_len);
ble_db_hash_message_append_uuid(chr->uuid, buf, &buf_len);

if (tc_cmac_update(&state, buf, buf_len) == TC_CRYPTO_FAIL) {
return BLE_HS_EUNKNOWN;
}
buf_len = 0;

if (chr->descriptors != NULL) {
for (struct ble_gatt_dsc_def *dsc = chr->descriptors; dsc && dsc->uuid; dsc++) {
ble_db_hash_message_append_u16(*chr->val_handle + 1, buf, &buf_len);
ble_db_hash_message_append_u16(BLE_ATT_UUID_DESCRIPTOR, buf, &buf_len);

if (tc_cmac_update(&state, buf, buf_len) == TC_CRYPTO_FAIL) {
return BLE_HS_EUNKNOWN;
}
buf_len = 0;
}
}
}
}
}

if (tc_cmac_final(db_hash, &state) == TC_CRYPTO_FAIL) {
return BLE_HS_EUNKNOWN;
}

return 0;
}
10 changes: 10 additions & 0 deletions nimble/host/src/ble_sm.c
Original file line number Diff line number Diff line change
Expand Up @@ -516,11 +516,13 @@ static void
ble_sm_persist_keys(struct ble_sm_proc *proc)
{
struct ble_store_value_sec value_sec;
struct ble_store_value_hash value_hash;
struct ble_hs_conn *conn;
ble_addr_t peer_addr;
int authenticated;
int identity_ev = 0;
int sc;
int rc;

ble_hs_lock();

Expand Down Expand Up @@ -577,6 +579,14 @@ ble_sm_persist_keys(struct ble_sm_proc *proc)
ble_sm_fill_store_value(&peer_addr, authenticated, sc, &proc->peer_keys,
&value_sec);
ble_store_write_peer_sec(&value_sec);


// Compute current database hash and store it
value_hash.peer_addr = peer_addr;
rc = ble_compute_db_hash(value_hash.db_hash);
if (rc == 0) {
rc = ble_store_write_hash(&value_hash);
}
}

static int
Expand Down
Loading

0 comments on commit 3f01350

Please sign in to comment.