From: Luiz Augusto von Dentz <luiz.von.dentz@xxxxxxxxx> This use bt_crypto_hash (AES-CMAC) to generate a database hash using the content of the attribute: In ascending order of attribute handles, starting with the first handle, concatenate the fields Attribute Handle, Attribute Type, and Attribute Value if the attribute has one of the following types: «Primary Service», «Secondary Service», «Included Service», «Characteristic», or «Characteristic Extended Properties», concatenate the fields Attribute Handle and Attribute Type if the attribute has one of the following types: «Characteristic User Description», «Client Characteristic Configuration», «Server Characteristic Configuration», «Characteristic Format», or «Characteristic Aggregate Format», and ignore the attribute if it has any other type (such attributes are not part of the concatenation). --- src/shared/gatt-db.c | 68 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c index d97dca124..7dc93268b 100644 --- a/src/shared/gatt-db.c +++ b/src/shared/gatt-db.c @@ -35,6 +35,7 @@ #include "src/shared/timeout.h" #include "src/shared/att.h" #include "src/shared/gatt-db.h" +#include "src/shared/crypto.h" #ifndef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) @@ -58,6 +59,7 @@ static const bt_uuid_t ext_desc_uuid = { .type = BT_UUID16, struct gatt_db { int ref_count; + struct bt_crypto *crypto; uint8_t hash[16]; unsigned int hash_id; uint16_t next_handle; @@ -225,6 +227,7 @@ struct gatt_db *gatt_db_new(void) struct gatt_db *db; db = new0(struct gatt_db, 1); + db->crypto = bt_crypto_new(); db->services = queue_new(); db->notify_list = queue_new(); db->next_handle = 0x0001; @@ -266,12 +269,71 @@ static void handle_notify(void *data, void *user_data) notify->service_removed(notify_data->attr, notify->user_data); } +static void gen_hash_m(struct gatt_db_attribute *attr, void *user_data) +{ + struct iovec *iov = user_data; + uint8_t *data; + size_t len; + + if (bt_uuid_len(&attr->uuid) != 2) + return; + + switch (attr->uuid.value.u16) { + case GATT_PRIM_SVC_UUID: + case GATT_SND_SVC_UUID: + case GATT_INCLUDE_UUID: + case GATT_CHARAC_UUID: + /* Allocate space for handle + type + value */ + len = 2 + 2 + attr->value_len; + data = malloc(2 + 2 + attr->value_len); + put_le16(attr->handle, data); + bt_uuid_to_le(&attr->uuid, data + 2); + memcpy(data + 4, attr->value, attr->value_len); + break; + case GATT_CHARAC_USER_DESC_UUID: + case GATT_CLIENT_CHARAC_CFG_UUID: + case GATT_SERVER_CHARAC_CFG_UUID: + case GATT_CHARAC_FMT_UUID: + case GATT_CHARAC_AGREG_FMT_UUID: + /* Allocate space for handle + type */ + len = 2 + 2; + data = malloc(2 + 2 + attr->value_len); + put_le16(attr->handle, data); + bt_uuid_to_le(&attr->uuid, data + 2); + break; + default: + return; + } + + iov[attr->handle].iov_base = data; + iov[attr->handle].iov_len = len; + + return; +} + +static void service_gen_hash_m(struct gatt_db_attribute *attr, void *user_data) +{ + gatt_db_service_foreach(attr, NULL, gen_hash_m, user_data); +} + static bool db_hash_update(void *user_data) { struct gatt_db *db = user_data; + struct iovec *iov; + uint16_t i; db->hash_id = 0; + iov = new0(struct iovec, db->next_handle); + + gatt_db_foreach_service(db, NULL, service_gen_hash_m, iov); + bt_crypto_gatt_hash(db->crypto, iov, db->next_handle, db->hash); + + for (i = 0; i < db->next_handle; i++) + free(iov[i].iov_base); + + free(iov); + return false; } @@ -292,7 +354,7 @@ static void notify_service_changed(struct gatt_db *db, queue_foreach(db->notify_list, handle_notify, &data); /* Tigger hash update */ - if (!db->hash_id) + if (!db->hash_id && db->crypto) db->hash_id = timeout_add(HASH_UPDATE_TIMEOUT, db_hash_update, db, NULL); @@ -319,6 +381,8 @@ static void gatt_db_destroy(struct gatt_db *db) if (!db) return; + bt_crypto_unref(db->crypto); + /* * Clear the notify list before clearing the services to prevent the * latter from sending service_removed events. @@ -506,7 +570,7 @@ uint8_t *gatt_db_get_hash(struct gatt_db *db) { uint8_t hash[16] = {}; - if (!db) + if (!db || !db->crypto) return NULL; /* Generate hash if if has not been generated yet */ -- 2.17.2