Skip to content

Commit a3bca71

Browse files
Vudentzjhedberg
authored andcommitted
Bluetooth: GATT: Implement Database Hash
This implements the Database Hash characteristic which generates a hash with the contents of certain attributes. The generation of hash is usually offloaded to the systemwq using a delayed work so that when application register multiple services only one hash needs to be calculated. Signed-off-by: Luiz Augusto von Dentz <[email protected]>
1 parent ad9846b commit a3bca71

File tree

2 files changed

+150
-0
lines changed

2 files changed

+150
-0
lines changed

subsys/bluetooth/host/gatt.c

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717

1818
#include <settings/settings.h>
1919

20+
#include <tinycrypt/constants.h>
21+
#include <tinycrypt/utils.h>
22+
#include <tinycrypt/aes.h>
23+
#include <tinycrypt/cmac_mode.h>
24+
#include <tinycrypt/ccm_mode.h>
25+
2026
#include <bluetooth/hci.h>
2127
#include <bluetooth/bluetooth.h>
2228
#include <bluetooth/conn.h>
@@ -40,6 +46,8 @@
4046
#define SC_TIMEOUT K_MSEC(10)
4147
#define CCC_STORE_DELAY K_SECONDS(1)
4248

49+
#define DB_HASH_TIMEOUT K_MSEC(10)
50+
4351
/* Persistent storage format for GATT CCC */
4452
struct ccc_store {
4553
u16_t handle;
@@ -267,6 +275,130 @@ static ssize_t cf_write(struct bt_conn *conn, const struct bt_gatt_attr *attr,
267275

268276
return len;
269277
}
278+
279+
static u8_t db_hash[16];
280+
struct k_delayed_work db_hash_work;
281+
282+
struct gen_hash_state {
283+
struct tc_cmac_struct state;
284+
int err;
285+
};
286+
287+
static u8_t gen_hash_m(const struct bt_gatt_attr *attr, void *user_data)
288+
{
289+
struct gen_hash_state *state = user_data;
290+
struct bt_uuid_16 *u16;
291+
u8_t data[16];
292+
size_t len;
293+
u16_t value;
294+
295+
if (attr->uuid->type != BT_UUID_TYPE_16)
296+
return BT_GATT_ITER_CONTINUE;
297+
298+
u16 = (struct bt_uuid_16 *)attr->uuid;
299+
300+
switch (u16->val) {
301+
/* Attributes to hash: handle + UUID + value */
302+
case 0x2800: /* GATT Primary Service */
303+
case 0x2801: /* GATT Secondary Service */
304+
case 0x2802: /* GATT Include Service */
305+
case 0x2803: /* GATT Characteristic */
306+
case 0x2900: /* GATT Characteristic Extended Properties */
307+
value = sys_cpu_to_le16(attr->handle);
308+
if (tc_cmac_update(&state->state, (uint8_t *)&value,
309+
sizeof(attr->handle)) == TC_CRYPTO_FAIL) {
310+
state->err = -EINVAL;
311+
return BT_GATT_ITER_STOP;
312+
}
313+
314+
value = sys_cpu_to_le16(u16->val);
315+
if (tc_cmac_update(&state->state, (uint8_t *)&value,
316+
sizeof(u16->val)) == TC_CRYPTO_FAIL) {
317+
state->err = -EINVAL;
318+
return BT_GATT_ITER_STOP;
319+
}
320+
321+
len = attr->read(NULL, attr, data, sizeof(data), 0);
322+
if (len < 0) {
323+
state->err = len;
324+
return BT_GATT_ITER_STOP;
325+
}
326+
327+
if (tc_cmac_update(&state->state, data, len) ==
328+
TC_CRYPTO_FAIL) {
329+
state->err = -EINVAL;
330+
return BT_GATT_ITER_STOP;
331+
}
332+
333+
break;
334+
/* Attributes to hash: handle + UUID */
335+
case 0x2901: /* GATT Characteristic User Descriptor */
336+
case 0x2902: /* GATT Client Characteristic Configuration */
337+
case 0x2903: /* GATT Server Characteristic Configuration */
338+
case 0x2904: /* GATT Characteristic Presentation Format */
339+
case 0x2905: /* GATT Characteristic Aggregated Format */
340+
value = sys_cpu_to_le16(attr->handle);
341+
if (tc_cmac_update(&state->state, (uint8_t *)&value,
342+
sizeof(attr->handle)) == TC_CRYPTO_FAIL) {
343+
state->err = -EINVAL;
344+
return BT_GATT_ITER_STOP;
345+
}
346+
347+
value = sys_cpu_to_le16(u16->val);
348+
if (tc_cmac_update(&state->state, (uint8_t *)&value,
349+
sizeof(u16->val)) == TC_CRYPTO_FAIL) {
350+
state->err = -EINVAL;
351+
return BT_GATT_ITER_STOP;
352+
}
353+
break;
354+
default:
355+
return BT_GATT_ITER_CONTINUE;
356+
}
357+
358+
return BT_GATT_ITER_CONTINUE;
359+
}
360+
361+
static void db_hash_gen(void)
362+
{
363+
u8_t key[16] = {};
364+
struct tc_aes_key_sched_struct sched;
365+
struct gen_hash_state state;
366+
367+
if (tc_cmac_setup(&state.state, key, &sched) == TC_CRYPTO_FAIL) {
368+
BT_ERR("Unable to setup AES CMAC");
369+
return;
370+
}
371+
372+
bt_gatt_foreach_attr(0x0001, 0xffff, gen_hash_m, &state);
373+
374+
if (tc_cmac_final(db_hash, &state.state) == TC_CRYPTO_FAIL) {
375+
BT_ERR("Unable to calculate hash");
376+
return;
377+
}
378+
379+
BT_HEXDUMP_DBG(db_hash, sizeof(db_hash), "Hash: ");
380+
}
381+
382+
static void db_hash_process(struct k_work *work)
383+
{
384+
db_hash_gen();
385+
}
386+
387+
static ssize_t db_hash_read(struct bt_conn *conn,
388+
const struct bt_gatt_attr *attr,
389+
void *buf, u16_t len, u16_t offset)
390+
{
391+
/* Check if db_hash is already pending in which case it shall be
392+
* generated immediately instead of waiting the work to complete.
393+
*/
394+
if (k_delayed_work_remaining_get(&db_hash_work)) {
395+
k_delayed_work_cancel(&db_hash_work);
396+
db_hash_gen();
397+
}
398+
399+
return bt_gatt_attr_read(conn, attr, buf, len, offset, db_hash,
400+
sizeof(db_hash));
401+
}
270402
#endif /* COFNIG_BT_GATT_CACHING */
271403

272404
static struct bt_gatt_attr gatt_attrs[] = {
@@ -284,7 +416,11 @@ static struct bt_gatt_attr gatt_attrs[] = {
284416
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
285417
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
286418
cf_read, cf_write, NULL),
419+
BT_GATT_CHARACTERISTIC(BT_UUID_GATT_DB_HASH,
420+
BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
421+
db_hash_read, NULL, NULL),
287422
#endif /* COFNIG_BT_GATT_CACHING */
423+
288424
};
289425

290426
static struct bt_gatt_service gatt_svc = BT_GATT_SERVICE(gatt_attrs);
@@ -453,6 +589,11 @@ void bt_gatt_init(void)
453589
gatt_register(&gatt_svc);
454590
gatt_register(&gap_svc);
455591

592+
#if defined(CONFIG_BT_GATT_CACHING)
593+
k_delayed_work_init(&db_hash_work, db_hash_process);
594+
db_hash_gen();
595+
#endif /* COFNIG_BT_GATT_CACHING */
596+
456597
k_delayed_work_init(&gatt_sc.work, sc_process);
457598
#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE)
458599
k_delayed_work_init(&gatt_ccc_store.work, ccc_delayed_store);
@@ -529,6 +670,10 @@ int bt_gatt_service_register(struct bt_gatt_service *svc)
529670
sc_indicate(&gatt_sc, svc->attrs[0].handle,
530671
svc->attrs[svc->attr_count - 1].handle);
531672

673+
#if defined(CONFIG_BT_GATT_CACHING)
674+
k_delayed_work_submit(&db_hash_work, DB_HASH_TIMEOUT);
675+
#endif /* COFNIG_BT_GATT_CACHING */
676+
532677
return 0;
533678
}
534679

@@ -543,6 +688,10 @@ int bt_gatt_service_unregister(struct bt_gatt_service *svc)
543688
sc_indicate(&gatt_sc, svc->attrs[0].handle,
544689
svc->attrs[svc->attr_count - 1].handle);
545690

691+
#if defined(CONFIG_BT_GATT_CACHING)
692+
k_delayed_work_submit(&db_hash_work, DB_HASH_TIMEOUT);
693+
#endif /* COFNIG_BT_GATT_CACHING */
694+
546695
return 0;
547696
}
548697

tests/bluetooth/mesh_shell/prj.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ CONFIG_BT_CENTRAL=y
2222
CONFIG_BT_PERIPHERAL=y
2323
CONFIG_BT_TINYCRYPT_ECC=y
2424
CONFIG_BT_DEVICE_NAME="Zephyr Mesh"
25+
CONFIG_BT_GATT_CACHING=n
2526

2627
CONFIG_BT_CTLR_DUP_FILTER_LEN=0
2728
CONFIG_BT_CTLR_LE_ENC=n

0 commit comments

Comments
 (0)