Linux-Bluetooth Archive on lore.kernel.org
 help / Atom feed
* [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash
@ 2019-01-29 13:26 Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 02/15] unit/test-crypto: Add test for bt_crypto_gatt_hash Luiz Augusto von Dentz
                   ` (14 more replies)
  0 siblings, 15 replies; 16+ messages in thread
From: Luiz Augusto von Dentz @ 2019-01-29 13:26 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

The bt_crypto_gatt_hash uses AES-CCM to create a hash with the contents
stored in the iovec.

Note: The spec disregards the byte order so both the key, msg and res
are not swapped.
---
 src/shared/crypto.c | 31 +++++++++++++++++++++++++++++++
 src/shared/crypto.h |  3 +++
 2 files changed, 34 insertions(+)

diff --git a/src/shared/crypto.c b/src/shared/crypto.c
index ce0dcd638..5c5e1217d 100644
--- a/src/shared/crypto.c
+++ b/src/shared/crypto.c
@@ -688,3 +688,34 @@ bool bt_crypto_h6(struct bt_crypto *crypto, const uint8_t w[16],
 
 	return true;
 }
+
+bool bt_crypto_gatt_hash(struct bt_crypto *crypto, struct iovec *iov,
+				size_t iov_len, uint8_t res[16])
+{
+	const uint8_t key[16] = {};
+	ssize_t len;
+	int fd;
+
+	if (!crypto)
+		return false;
+
+	fd = alg_new(crypto->cmac_aes, key, 16);
+	if (fd < 0)
+		return false;
+
+	len = writev(fd, iov, iov_len);
+	if (len < 0) {
+		close(fd);
+		return false;
+	}
+
+	len = read(fd, res, 16);
+	if (len < 0) {
+		close(fd);
+		return false;
+	}
+
+	close(fd);
+
+	return true;
+}
diff --git a/src/shared/crypto.h b/src/shared/crypto.h
index 1e1b4836d..c58d2e104 100644
--- a/src/shared/crypto.h
+++ b/src/shared/crypto.h
@@ -23,6 +23,7 @@
 
 #include <stdbool.h>
 #include <stdint.h>
+#include <sys/uio.h>
 
 struct bt_crypto;
 
@@ -61,3 +62,5 @@ bool bt_crypto_h6(struct bt_crypto *crypto, const uint8_t w[16],
 bool bt_crypto_sign_att(struct bt_crypto *crypto, const uint8_t key[16],
 				const uint8_t *m, uint16_t m_len,
 				uint32_t sign_cnt, uint8_t signature[12]);
+bool bt_crypto_gatt_hash(struct bt_crypto *crypto, struct iovec *iov,
+				size_t iov_len, uint8_t res[16]);
-- 
2.17.2


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH BlueZ 02/15] unit/test-crypto: Add test for bt_crypto_gatt_hash
  2019-01-29 13:26 [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
@ 2019-01-29 13:26 ` Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 03/15] lib/uuid: Introduce definition for GATT caching attributes Luiz Augusto von Dentz
                   ` (13 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Luiz Augusto von Dentz @ 2019-01-29 13:26 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds test case base on the example given in the spec.
---
 unit/test-crypto.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/unit/test-crypto.c b/unit/test-crypto.c
index bc37abb51..5a938c09e 100644
--- a/unit/test-crypto.c
+++ b/unit/test-crypto.c
@@ -208,6 +208,54 @@ static void test_sign(gconstpointer data)
 	tester_test_passed();
 }
 
+static void test_gatt_hash(gconstpointer data)
+{
+	struct iovec iov;
+	const uint8_t m[] = {
+			0x01, 0x00, 0x00, 0x28, 0x00, 0x18, 0x02, 0x00,
+			0x03, 0x28, 0x0A, 0x03, 0x00, 0x00, 0x2A, 0x04,
+			0x00, 0x03, 0x28, 0x02, 0x05, 0x00, 0x01, 0x2A,
+			0x06, 0x00, 0x00, 0x28, 0x01, 0x18, 0x07, 0x00,
+			0x03, 0x28, 0x20, 0x08, 0x00, 0x05, 0x2A, 0x09,
+			0x00, 0x02, 0x29, 0x0A, 0x00, 0x03, 0x28, 0x02,
+			0x0B, 0x00, 0xFF, 0xFF, 0x0C, 0x00, 0x03, 0x28,
+			0x02, 0x0D, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0x00,
+			0x28, 0x08, 0x18, 0x0F, 0x00, 0x02, 0x28, 0x14,
+			0x00, 0x16, 0x00, 0x0F, 0x18, 0x10, 0x00, 0x03,
+			0x28, 0xA2, 0x11, 0x00, 0x18, 0x2A, 0x12, 0x00,
+			0x02, 0x29, 0x13, 0x00, 0x00, 0x29, 0x00, 0x00,
+			0x14, 0x00, 0x01, 0x28, 0x0F, 0x18, 0x15, 0x00,
+			0x03, 0x28, 0x02, 0x16, 0x00, 0x19, 0x2A };
+	const uint8_t exp[16] = {
+			0x22, 0x7C, 0xE3, 0x34, 0x22, 0xBA, 0xA5, 0xDD,
+			0x6F, 0xA0, 0x82, 0x2A, 0xC1, 0xAD, 0x94, 0x19 };
+	uint8_t res[16];
+
+	tester_debug("M:");
+	util_hexdump(' ', m, sizeof(m), print_debug, NULL);
+
+	iov.iov_base = (void *) m;
+	iov.iov_len = sizeof(m);
+
+	if (!bt_crypto_gatt_hash(crypto, &iov, 1, res)) {
+		tester_test_failed();
+		return;
+	}
+
+	tester_debug("Expected:");
+	util_hexdump(' ', exp, 16, print_debug, NULL);
+
+	tester_debug("Result:");
+	util_hexdump(' ', res, 16, print_debug, NULL);
+
+	if (memcmp(res, exp, 16)) {
+		tester_test_failed();
+		return;
+	}
+
+	tester_test_passed();
+}
+
 int main(int argc, char *argv[])
 {
 	int exit_status;
@@ -226,6 +274,8 @@ int main(int argc, char *argv[])
 	tester_add("/crypto/sign_att_4", &test_data_4, NULL, test_sign, NULL);
 	tester_add("/crypto/sign_att_5", &test_data_5, NULL, test_sign, NULL);
 
+	tester_add("/crypto/gatt_hash", NULL, NULL, test_gatt_hash, NULL);
+
 	exit_status = tester_run();
 
 	bt_crypto_unref(crypto);
-- 
2.17.2


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH BlueZ 03/15] lib/uuid: Introduce definition for GATT caching attributes
  2019-01-29 13:26 [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 02/15] unit/test-crypto: Add test for bt_crypto_gatt_hash Luiz Augusto von Dentz
@ 2019-01-29 13:26 ` Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 04/15] shared/util: Add decoding support " Luiz Augusto von Dentz
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Luiz Augusto von Dentz @ 2019-01-29 13:26 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This introduces UUIDs for Client Features and Database Hash
characteristics.
---
 lib/uuid.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/lib/uuid.h b/lib/uuid.h
index 7e13e0fda..fbc08f51e 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -150,6 +150,10 @@ extern "C" {
 #define MESH_PROXY_DATA_IN				0x2ADD
 #define MESH_PROXY_DATA_OUT				0x2ADE
 
+/* GATT Caching attributes */
+#define GATT_CHARAC_CLI_FEAT				0x2B29
+#define GATT_CHARAC_DB_HASH				0x2B2A
+
 typedef struct {
 	enum {
 		BT_UUID_UNSPEC = 0,
-- 
2.17.2


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH BlueZ 04/15] shared/util: Add decoding support for GATT caching attributes
  2019-01-29 13:26 [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 02/15] unit/test-crypto: Add test for bt_crypto_gatt_hash Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 03/15] lib/uuid: Introduce definition for GATT caching attributes Luiz Augusto von Dentz
@ 2019-01-29 13:26 ` " Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 05/15] shared/att-types: Add errors introduced by GATT caching Luiz Augusto von Dentz
                   ` (11 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Luiz Augusto von Dentz @ 2019-01-29 13:26 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds support to decode Client Supported Features and Database
Hash.
---
 src/shared/util.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/shared/util.c b/src/shared/util.c
index 145bb2a3a..330a0722a 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -526,6 +526,8 @@ static const struct {
 	{ 0x2adc, "Mesh Provisioning Data Out"			},
 	{ 0x2add, "Mesh Proxy Data In"				},
 	{ 0x2ade, "Mesh Proxy Data Out"				},
+	{ 0x2b29, "Client Supported Features"			},
+	{ 0x2b2A, "Database Hash"				},
 	/* vendor defined */
 	{ 0xfeff, "GN Netcom"					},
 	{ 0xfefe, "GN ReSound A/S"				},
-- 
2.17.2


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH BlueZ 05/15] shared/att-types: Add errors introduced by GATT caching
  2019-01-29 13:26 [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
                   ` (2 preceding siblings ...)
  2019-01-29 13:26 ` [PATCH BlueZ 04/15] shared/util: Add decoding support " Luiz Augusto von Dentz
@ 2019-01-29 13:26 ` Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 06/15] monitor: Decode GATT Caching errors Luiz Augusto von Dentz
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Luiz Augusto von Dentz @ 2019-01-29 13:26 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds Database Out of Sync and Value Not Allowed errors.
---
 src/shared/att-types.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/shared/att-types.h b/src/shared/att-types.h
index 51922d174..8a2658de3 100644
--- a/src/shared/att-types.h
+++ b/src/shared/att-types.h
@@ -100,6 +100,8 @@ struct bt_att_pdu_error_rsp {
 #define BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION		0x0F
 #define BT_ATT_ERROR_UNSUPPORTED_GROUP_TYPE		0x10
 #define BT_ATT_ERROR_INSUFFICIENT_RESOURCES		0x11
+#define BT_ATT_ERROR_DB_OUT_OF_SYNC			0x12
+#define BT_ATT_ERROR_VALUE_NOT_ALLOWED			0x13
 
 /*
  * Common Profile and Service Error Code descriptions (see Supplement to the
@@ -154,3 +156,6 @@ struct bt_att_pdu_error_rsp {
 #define BT_GATT_CHRC_EXT_PROP_AUTH_WRITE		0x20
 #define BT_GATT_CHRC_EXT_PROP_AUTH	(BT_GATT_CHRC_EXT_PROP_AUTH_READ | \
 					BT_GATT_CHRC_EXT_PROP_AUTH_WRITE)
+
+/* GATT Characteristic Client Features Bitfield values */
+#define BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING		0x01
-- 
2.17.2


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH BlueZ 06/15] monitor: Decode GATT Caching errors
  2019-01-29 13:26 [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
                   ` (3 preceding siblings ...)
  2019-01-29 13:26 ` [PATCH BlueZ 05/15] shared/att-types: Add errors introduced by GATT caching Luiz Augusto von Dentz
@ 2019-01-29 13:26 ` Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 07/15] shared/gatt-db: Introduce Database Hash Luiz Augusto von Dentz
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Luiz Augusto von Dentz @ 2019-01-29 13:26 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This enables decoding of Database Out of Sync and Value Not Allowed
ATT errors introduced with GATT Caching improvements.
---
 monitor/l2cap.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/monitor/l2cap.c b/monitor/l2cap.c
index 1e6ca3492..26719ac5e 100644
--- a/monitor/l2cap.c
+++ b/monitor/l2cap.c
@@ -2132,6 +2132,12 @@ static void att_error_response(const struct l2cap_frame *frame)
 	case 0x11:
 		str = "Insufficient Resources";
 		break;
+	case 0x12:
+		str = "Database Out of Sync";
+		break;
+	case 0x13:
+		str = "Value Not Allowed";
+		break;
 	case 0xfd:
 		str = "CCC Improperly Configured";
 		break;
-- 
2.17.2


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH BlueZ 07/15] shared/gatt-db: Introduce Database Hash
  2019-01-29 13:26 [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
                   ` (4 preceding siblings ...)
  2019-01-29 13:26 ` [PATCH BlueZ 06/15] monitor: Decode GATT Caching errors Luiz Augusto von Dentz
@ 2019-01-29 13:26 ` Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 08/15] shared/gatt-db: Generate database hash Luiz Augusto von Dentz
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Luiz Augusto von Dentz @ 2019-01-29 13:26 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This introduces a database hash which is generate whenever the database
changes.
---
 src/shared/gatt-db.c | 36 ++++++++++++++++++++++++++++++++++++
 src/shared/gatt-db.h |  1 +
 2 files changed, 37 insertions(+)

diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index d28301ac4..d97dca124 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -43,6 +43,7 @@
 #define MAX_CHAR_DECL_VALUE_LEN 19
 #define MAX_INCLUDED_VALUE_LEN 6
 #define ATTRIBUTE_TIMEOUT 5000
+#define HASH_UPDATE_TIMEOUT 100
 
 static const bt_uuid_t primary_service_uuid = { .type = BT_UUID16,
 					.value.u16 = GATT_PRIM_SVC_UUID };
@@ -57,6 +58,8 @@ static const bt_uuid_t ext_desc_uuid = { .type = BT_UUID16,
 
 struct gatt_db {
 	int ref_count;
+	uint8_t hash[16];
+	unsigned int hash_id;
 	uint16_t next_handle;
 	struct queue *services;
 
@@ -263,6 +266,15 @@ static void handle_notify(void *data, void *user_data)
 		notify->service_removed(notify_data->attr, notify->user_data);
 }
 
+static bool db_hash_update(void *user_data)
+{
+	struct gatt_db *db = user_data;
+
+	db->hash_id = 0;
+
+	return false;
+}
+
 static void notify_service_changed(struct gatt_db *db,
 						struct gatt_db_service *service,
 						bool added)
@@ -279,6 +291,11 @@ static void notify_service_changed(struct gatt_db *db,
 
 	queue_foreach(db->notify_list, handle_notify, &data);
 
+	/* Tigger hash update */
+	if (!db->hash_id)
+		db->hash_id = timeout_add(HASH_UPDATE_TIMEOUT, db_hash_update,
+								db, NULL);
+
 	gatt_db_unref(db);
 }
 
@@ -309,6 +326,9 @@ static void gatt_db_destroy(struct gatt_db *db)
 	queue_destroy(db->notify_list, notify_destroy);
 	db->notify_list = NULL;
 
+	if (db->hash_id)
+		timeout_remove(db->hash_id);
+
 	queue_destroy(db->services, gatt_db_service_destroy);
 	free(db);
 }
@@ -482,6 +502,22 @@ done:
 	return true;
 }
 
+uint8_t *gatt_db_get_hash(struct gatt_db *db)
+{
+	uint8_t hash[16] = {};
+
+	if (!db)
+		return NULL;
+
+	/* Generate hash if if has not been generated yet */
+	if (db->hash_id || !memcmp(db->hash, hash, 16)) {
+		timeout_remove(db->hash_id);
+		db_hash_update(db);
+	}
+
+	return db->hash;
+}
+
 static struct gatt_db_service *find_insert_loc(struct gatt_db *db,
 						uint16_t start, uint16_t end,
 						struct gatt_db_service **after)
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index e2ac645f3..f3ab803d1 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -41,6 +41,7 @@ bool gatt_db_remove_service(struct gatt_db *db,
 bool gatt_db_clear(struct gatt_db *db);
 bool gatt_db_clear_range(struct gatt_db *db, uint16_t start_handle,
 							uint16_t end_handle);
+uint8_t *gatt_db_get_hash(struct gatt_db *db);
 
 struct gatt_db_attribute *gatt_db_insert_service(struct gatt_db *db,
 							uint16_t handle,
-- 
2.17.2


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH BlueZ 08/15] shared/gatt-db: Generate database hash
  2019-01-29 13:26 [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
                   ` (5 preceding siblings ...)
  2019-01-29 13:26 ` [PATCH BlueZ 07/15] shared/gatt-db: Introduce Database Hash Luiz Augusto von Dentz
@ 2019-01-29 13:26 ` Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 09/15] gatt: Add caching support for server Luiz Augusto von Dentz
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Luiz Augusto von Dentz @ 2019-01-29 13:26 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

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


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH BlueZ 09/15] gatt: Add caching support for server
  2019-01-29 13:26 [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
                   ` (6 preceding siblings ...)
  2019-01-29 13:26 ` [PATCH BlueZ 08/15] shared/gatt-db: Generate database hash Luiz Augusto von Dentz
@ 2019-01-29 13:26 ` Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 10/15] shared/gatt-db: Add gatt_db_set_authorize Luiz Augusto von Dentz
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Luiz Augusto von Dentz @ 2019-01-29 13:26 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This registers both Client Features and Database Hash Characteristics
which are mandatory for the server.
---
 src/gatt-database.c | 103 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 102 insertions(+), 1 deletion(-)

diff --git a/src/gatt-database.c b/src/gatt-database.c
index 2c2f035b1..d72f2c781 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -86,6 +86,8 @@ struct btd_gatt_database {
 	struct queue *ccc_callbacks;
 	struct gatt_db_attribute *svc_chngd;
 	struct gatt_db_attribute *svc_chngd_ccc;
+	struct gatt_db_attribute *cli_feat;
+	struct gatt_db_attribute *db_hash;
 	struct queue *apps;
 	struct queue *profiles;
 };
@@ -177,6 +179,7 @@ struct device_state {
 	bdaddr_t bdaddr;
 	uint8_t bdaddr_type;
 	unsigned int disc_id;
+	uint8_t cli_feat[1];
 	struct queue *ccc_states;
 	struct notify *pending;
 };
@@ -1055,6 +1058,90 @@ service_add_ccc(struct gatt_db_attribute *service,
 	return ccc;
 }
 
+static void cli_feat_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct btd_gatt_database *database = user_data;
+	struct device_state *state;
+	uint8_t ecode = 0;
+	const uint8_t *value = NULL;
+	size_t len = 0;
+
+	DBG("Client Features read");
+
+	state = get_device_state(database, att);
+	if (!state) {
+		ecode = BT_ATT_ERROR_UNLIKELY;
+		goto done;
+	}
+
+	len = sizeof(state->cli_feat) - offset;
+	value = len ? &state->cli_feat[offset] : NULL;
+
+done:
+	gatt_db_attribute_read_result(attrib, id, ecode, value, len);
+}
+
+static void cli_feat_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct btd_gatt_database *database = user_data;
+	struct device_state *state;
+	uint8_t ecode = 0;
+
+	DBG("Client Features write");
+
+	state = get_device_state(database, att);
+	if (!state) {
+		ecode = BT_ATT_ERROR_UNLIKELY;
+		goto done;
+	}
+
+	if (!value || !len) {
+		ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+		goto done;
+	}
+
+	/* A client shall never clear a bit it has set.
+	 * TODO: make it generic to any bits.
+	 */
+	if (state->cli_feat[0] & BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING &&
+			!(value[0] & BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING)) {
+		ecode = BT_ATT_ERROR_VALUE_NOT_ALLOWED;
+		goto done;
+	}
+
+	/* Shall we reallocate the feat array if bigger? */
+	len = MIN(sizeof(state->cli_feat), len);
+	while (len) {
+		state->cli_feat[len - 1] |= value[len -1];
+		len--;
+	}
+
+done:
+	gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static void db_hash_read_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct btd_gatt_database *database = user_data;
+	const uint8_t *hash;
+
+	DBG("Database Hash read");
+
+	hash = gatt_db_get_hash(database->db);
+
+	gatt_db_attribute_read_result(attrib, id, 0, hash, 16);
+}
+
 static void populate_gatt_service(struct btd_gatt_database *database)
 {
 	bt_uuid_t uuid;
@@ -1062,7 +1149,7 @@ static void populate_gatt_service(struct btd_gatt_database *database)
 
 	/* Add the GATT service */
 	bt_uuid16_create(&uuid, UUID_GATT);
-	service = gatt_db_add_service(database->db, &uuid, true, 4);
+	service = gatt_db_add_service(database->db, &uuid, true, 8);
 
 	bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
 	database->svc_chngd = gatt_db_service_add_characteristic(service, &uuid,
@@ -1072,11 +1159,25 @@ static void populate_gatt_service(struct btd_gatt_database *database)
 	database->svc_chngd_ccc = service_add_ccc(service, database, NULL, NULL,
 									NULL);
 
+	bt_uuid16_create(&uuid, GATT_CHARAC_CLI_FEAT);
+	database->cli_feat = gatt_db_service_add_characteristic(service,
+				&uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+				BT_GATT_CHRC_PROP_READ |
+				BT_GATT_CHRC_PROP_WRITE,
+				cli_feat_read_cb, cli_feat_write_cb,
+				database);
+
+	bt_uuid16_create(&uuid, GATT_CHARAC_DB_HASH);
+	database->db_hash = gatt_db_service_add_characteristic(service,
+				&uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
+				db_hash_read_cb, NULL, database);
+
 	gatt_db_service_set_active(service, true);
 
 	database_add_record(database, service);
 }
 
+
 static void register_core_services(struct btd_gatt_database *database)
 {
 	populate_gap_service(database);
-- 
2.17.2


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH BlueZ 10/15] shared/gatt-db: Add gatt_db_set_authorize
  2019-01-29 13:26 [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
                   ` (7 preceding siblings ...)
  2019-01-29 13:26 ` [PATCH BlueZ 09/15] gatt: Add caching support for server Luiz Augusto von Dentz
@ 2019-01-29 13:26 ` Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 11/15] shared/gatt-server: Add bt_gatt_server_set_authorize Luiz Augusto von Dentz
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Luiz Augusto von Dentz @ 2019-01-29 13:26 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

gatt_db_set_authorize can be used to set the callback to authorize
attribute operations.
---
 src/shared/gatt-db.c | 41 +++++++++++++++++++++++++++++++++++++++++
 src/shared/gatt-db.h |  6 ++++++
 2 files changed, 47 insertions(+)

diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index 7dc93268b..4d3653a53 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -67,12 +67,16 @@ struct gatt_db {
 
 	struct queue *notify_list;
 	unsigned int next_notify_id;
+
+	gatt_db_authorize_cb_t authorize;
+	void *authorize_data;
 };
 
 struct notify {
 	unsigned int id;
 	gatt_db_attribute_cb_t service_added;
 	gatt_db_attribute_cb_t service_removed;
+	gatt_db_authorize_cb_t authorize_cb;
 	gatt_db_destroy_func_t destroy;
 	void *user_data;
 };
@@ -736,6 +740,18 @@ bool gatt_db_unregister(struct gatt_db *db, unsigned int id)
 	return true;
 }
 
+bool gatt_db_set_authorize(struct gatt_db *db, gatt_db_authorize_cb_t cb,
+							void *user_data)
+{
+	if (!db)
+		return false;
+
+	db->authorize = cb;
+	db->authorize_data = user_data;
+
+	return true;
+}
+
 static uint16_t get_attribute_index(struct gatt_db_service *service,
 							int end_offset)
 {
@@ -1821,6 +1837,17 @@ static bool read_timeout(void *user_data)
 	return false;
 }
 
+static uint8_t attribute_authorize(struct gatt_db_attribute *attrib,
+					uint8_t opcode, struct bt_att *att)
+{
+	struct gatt_db *db = attrib->service->db;
+
+	if (!db->authorize)
+		return 0;
+
+	return db->authorize(attrib, opcode, att, db->authorize_data);
+}
+
 bool gatt_db_attribute_read(struct gatt_db_attribute *attrib, uint16_t offset,
 				uint8_t opcode, struct bt_att *att,
 				gatt_db_attribute_read_t func, void *user_data)
@@ -1832,6 +1859,13 @@ bool gatt_db_attribute_read(struct gatt_db_attribute *attrib, uint16_t offset,
 
 	if (attrib->read_func) {
 		struct pending_read *p;
+		uint8_t err;
+
+		err = attribute_authorize(attrib, opcode, att);
+		if (err) {
+			func(attrib, err, NULL, 0, user_data);
+			return true;
+		}
 
 		p = new0(struct pending_read, 1);
 		p->attrib = attrib;
@@ -1913,6 +1947,13 @@ bool gatt_db_attribute_write(struct gatt_db_attribute *attrib, uint16_t offset,
 
 	if (attrib->write_func) {
 		struct pending_write *p;
+		uint8_t err;
+
+		err = attribute_authorize(attrib, opcode, att);
+		if (err) {
+			func(attrib, err, user_data);
+			return true;
+		}
 
 		p = new0(struct pending_write, 1);
 		p->attrib = attrib;
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index f3ab803d1..398d69ad8 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -198,6 +198,12 @@ unsigned int gatt_db_register(struct gatt_db *db,
 					gatt_db_destroy_func_t destroy);
 bool gatt_db_unregister(struct gatt_db *db, unsigned int id);
 
+typedef uint8_t (*gatt_db_authorize_cb_t)(struct gatt_db_attribute *attrib,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data);
+bool gatt_db_set_authorize(struct gatt_db *db, gatt_db_authorize_cb_t cb,
+					void *user_data);
+
 struct gatt_db_attribute *gatt_db_get_service(struct gatt_db *db,
 							uint16_t handle);
 
-- 
2.17.2


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH BlueZ 11/15] shared/gatt-server: Add bt_gatt_server_set_authorize
  2019-01-29 13:26 [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
                   ` (8 preceding siblings ...)
  2019-01-29 13:26 ` [PATCH BlueZ 10/15] shared/gatt-db: Add gatt_db_set_authorize Luiz Augusto von Dentz
@ 2019-01-29 13:26 ` Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 12/15] gatt: Implement Robust Caching handling for server Luiz Augusto von Dentz
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Luiz Augusto von Dentz @ 2019-01-29 13:26 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

bt_gatt_server_set_authorize can be used to set the callback to
authorize attribute operations which is required in order to send
out of sync error as that has to happen even in case the handle is
not valid.
---
 src/shared/gatt-server.c | 34 ++++++++++++++++++++++++++++++++++
 src/shared/gatt-server.h |  7 +++++++
 2 files changed, 41 insertions(+)

diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index 2c8b50065..0d9bb0762 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -114,6 +114,9 @@ struct bt_gatt_server {
 	bt_gatt_server_debug_func_t debug_callback;
 	bt_gatt_server_destroy_func_t debug_destroy;
 	void *debug_data;
+
+	bt_gatt_server_authorize_cb_t authorize;
+	void *authorize_data;
 };
 
 static void bt_gatt_server_free(struct bt_gatt_server *server)
@@ -785,6 +788,16 @@ static void write_complete_cb(struct gatt_db_attribute *attr, int err,
 	async_write_op_destroy(op);
 }
 
+static uint8_t authorize_req(struct bt_gatt_server *server,
+					uint8_t opcode, uint16_t handle)
+{
+	if (!server->authorize)
+		return 0;
+
+	return server->authorize(server->att, opcode, handle,
+						server->authorize_data);
+}
+
 static void write_cb(uint8_t opcode, const void *pdu,
 					uint16_t length, void *user_data)
 {
@@ -799,6 +812,10 @@ static void write_cb(uint8_t opcode, const void *pdu,
 		goto error;
 	}
 
+	ecode = authorize_req(server, opcode, handle);
+	if (ecode)
+		goto error;
+
 	handle = get_le16(pdu);
 	attr = gatt_db_get_attribute(server->db, handle);
 	if (!attr) {
@@ -906,6 +923,10 @@ static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode,
 	uint8_t ecode;
 	struct async_read_op *op = NULL;
 
+	ecode = authorize_req(server, opcode, handle);
+	if (ecode)
+		goto error;
+
 	attr = gatt_db_get_attribute(server->db, handle);
 	if (!attr) {
 		ecode = BT_ATT_ERROR_INVALID_HANDLE;
@@ -1725,3 +1746,16 @@ bool bt_gatt_server_send_indication(struct bt_gatt_server *server,
 
 	return result;
 }
+
+bool bt_gatt_server_set_authorize(struct bt_gatt_server *server,
+					bt_gatt_server_authorize_cb_t cb,
+					void *user_data)
+{
+	if (!server)
+		return false;
+
+	server->authorize = cb;
+	server->authorize_data = user_data;
+
+	return true;
+}
diff --git a/src/shared/gatt-server.h b/src/shared/gatt-server.h
index 8d88ccee8..c3d83f225 100644
--- a/src/shared/gatt-server.h
+++ b/src/shared/gatt-server.h
@@ -43,6 +43,13 @@ bool bt_gatt_server_set_debug(struct bt_gatt_server *server,
 					void *user_data,
 					bt_gatt_server_destroy_func_t destroy);
 
+typedef uint8_t (*bt_gatt_server_authorize_cb_t)(struct bt_att *att,
+					uint8_t opcode, uint16_t handle,
+					void *user_data);
+bool bt_gatt_server_set_authorize(struct bt_gatt_server *server,
+					bt_gatt_server_authorize_cb_t cb,
+					void *user_data);
+
 bool bt_gatt_server_send_notification(struct bt_gatt_server *server,
 					uint16_t handle, const uint8_t *value,
 					uint16_t length);
-- 
2.17.2


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH BlueZ 12/15] gatt: Implement Robust Caching handling for server
  2019-01-29 13:26 [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
                   ` (9 preceding siblings ...)
  2019-01-29 13:26 ` [PATCH BlueZ 11/15] shared/gatt-server: Add bt_gatt_server_set_authorize Luiz Augusto von Dentz
@ 2019-01-29 13:26 ` Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 13/15] shared/gatt-client: Read database hash if available Luiz Augusto von Dentz
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Luiz Augusto von Dentz @ 2019-01-29 13:26 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This detects when a client becomes change-unware returning
DB Out of Sync error when that happens.
---
 src/device.c        |  2 +-
 src/gatt-database.c | 75 ++++++++++++++++++++++++++++++++++++++++++---
 src/gatt-database.h |  4 +--
 3 files changed, 74 insertions(+), 7 deletions(-)

diff --git a/src/device.c b/src/device.c
index 79ede3729..060251f66 100644
--- a/src/device.c
+++ b/src/device.c
@@ -4922,7 +4922,7 @@ static void gatt_server_init(struct btd_device *device,
 	bt_att_set_enc_key_size(device->att, device->ltk_enc_size);
 	bt_gatt_server_set_debug(device->server, gatt_debug, NULL, NULL);
 
-	btd_gatt_database_att_connected(database, device->att);
+	btd_gatt_database_server_connected(database, device->server);
 }
 
 static bool local_counter(uint32_t *sign_cnt, void *user_data)
diff --git a/src/gatt-database.c b/src/gatt-database.c
index d72f2c781..aecfa0e43 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -180,6 +180,8 @@ struct device_state {
 	uint8_t bdaddr_type;
 	unsigned int disc_id;
 	uint8_t cli_feat[1];
+	bool change_aware;
+	bool out_of_sync;
 	struct queue *ccc_states;
 	struct notify *pending;
 };
@@ -328,6 +330,7 @@ static void att_disconnected(int err, void *user_data)
 	DBG("");
 
 	state->disc_id = 0;
+	state->out_of_sync = false;
 
 	device = btd_adapter_find_device(state->db->adapter, &state->bdaddr,
 							state->bdaddr_type);
@@ -1119,10 +1122,13 @@ static void cli_feat_write_cb(struct gatt_db_attribute *attrib,
 	/* Shall we reallocate the feat array if bigger? */
 	len = MIN(sizeof(state->cli_feat), len);
 	while (len) {
-		state->cli_feat[len - 1] |= value[len -1];
+		state->cli_feat[len - 1] |= value[len - 1];
 		len--;
 	}
 
+	state->cli_feat[0] &= BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING;
+	state->change_aware = true;
+
 done:
 	gatt_db_attribute_write_result(attrib, id, ecode);
 }
@@ -1134,12 +1140,22 @@ static void db_hash_read_cb(struct gatt_db_attribute *attrib,
 {
 	struct btd_gatt_database *database = user_data;
 	const uint8_t *hash;
+	struct device_state *state;
+	bdaddr_t bdaddr;
+	uint8_t bdaddr_type;
 
 	DBG("Database Hash read");
 
 	hash = gatt_db_get_hash(database->db);
 
 	gatt_db_attribute_read_result(attrib, id, 0, hash, 16);
+
+	if (!get_dst_info(att, &bdaddr, &bdaddr_type))
+		return;
+
+	state = find_device_state(database, &bdaddr, bdaddr_type);
+	if (state)
+		state->change_aware = true;
 }
 
 static void populate_gatt_service(struct btd_gatt_database *database)
@@ -1197,7 +1213,14 @@ static void conf_cb(void *user_data)
 
 static void service_changed_conf(void *user_data)
 {
+	struct device_state *state = user_data;
+
 	DBG("");
+
+	if (!state)
+		return;
+
+	state->change_aware = true;
 }
 
 static void state_set_pending(struct device_state *state, struct notify *notify)
@@ -1239,6 +1262,14 @@ static void send_notification_to_device(void *data, void *user_data)
 	struct btd_device *device;
 	struct bt_gatt_server *server;
 
+	if (notify->conf == service_changed_conf) {
+		if (device_state->cli_feat[0] &
+				BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING) {
+			device_state->change_aware = false;
+			notify->user_data = device_state;
+		}
+	}
+
 	ccc = find_ccc_state(device_state, notify->ccc_handle);
 	if (!ccc)
 		return;
@@ -3352,6 +3383,40 @@ static const GDBusMethodTable manager_methods[] = {
 	{ }
 };
 
+static uint8_t server_authorize(struct bt_att *att, uint8_t opcode,
+					uint16_t handle, void *user_data)
+{
+	struct btd_gatt_database *database = user_data;
+	struct device_state *state;
+	bdaddr_t bdaddr;
+	uint8_t bdaddr_type;
+
+	if (!get_dst_info(att, &bdaddr, &bdaddr_type))
+		return 0;
+
+	/* Skip if there is no device state */
+	state = find_device_state(database, &bdaddr, bdaddr_type);
+	if (!state)
+		return 0;
+
+	/* Skip if client doesn't support Robust Caching */
+	if (!(state->cli_feat[0] & BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING))
+		return 0;
+
+	if (state->change_aware)
+		return 0;
+
+	if (state->out_of_sync) {
+		state->out_of_sync = false;
+		state->change_aware = true;
+		return 0;
+	}
+
+	state->out_of_sync = true;
+
+	return BT_ATT_ERROR_DB_OUT_OF_SYNC;
+}
+
 struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter)
 {
 	struct btd_gatt_database *database;
@@ -3413,7 +3478,6 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter)
 	if (!database->db_id)
 		goto fail;
 
-
 	return database;
 
 fail:
@@ -3442,9 +3506,10 @@ struct gatt_db *btd_gatt_database_get_db(struct btd_gatt_database *database)
 	return database->db;
 }
 
-void btd_gatt_database_att_connected(struct btd_gatt_database *database,
-						struct bt_att *att)
+void btd_gatt_database_server_connected(struct btd_gatt_database *database,
+						struct bt_gatt_server *server)
 {
+	struct bt_att *att = bt_gatt_server_get_att(server);
 	struct device_state *state;
 	bdaddr_t bdaddr;
 	uint8_t bdaddr_type;
@@ -3452,6 +3517,8 @@ void btd_gatt_database_att_connected(struct btd_gatt_database *database,
 	if (!get_dst_info(att, &bdaddr, &bdaddr_type))
 		return;
 
+	bt_gatt_server_set_authorize(server, server_authorize, database);
+
 	state = find_device_state(database, &bdaddr, bdaddr_type);
 	if (!state || !state->pending)
 		return;
diff --git a/src/gatt-database.h b/src/gatt-database.h
index a77a0fb20..154d3243f 100644
--- a/src/gatt-database.h
+++ b/src/gatt-database.h
@@ -23,9 +23,9 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter);
 void btd_gatt_database_destroy(struct btd_gatt_database *database);
 
 struct gatt_db *btd_gatt_database_get_db(struct btd_gatt_database *database);
-void btd_gatt_database_att_connected(struct btd_gatt_database *database,
-						struct bt_att *att);
 void btd_gatt_database_att_disconnected(struct btd_gatt_database *database,
 						struct btd_device *device);
+void btd_gatt_database_server_connected(struct btd_gatt_database *database,
+						struct bt_gatt_server *server);
 
 void btd_gatt_database_restore_svc_chng_ccc(struct btd_gatt_database *database);
-- 
2.17.2


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH BlueZ 13/15] shared/gatt-client: Read database hash if available
  2019-01-29 13:26 [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
                   ` (10 preceding siblings ...)
  2019-01-29 13:26 ` [PATCH BlueZ 12/15] gatt: Implement Robust Caching handling for server Luiz Augusto von Dentz
@ 2019-01-29 13:26 ` Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 14/15] shared/gatt-client: Write Client Features Luiz Augusto von Dentz
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 16+ messages in thread
From: Luiz Augusto von Dentz @ 2019-01-29 13:26 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This reads the database hash if available and compares to the existing
value, if it matches consider the discovery is completed.
---
 src/shared/gatt-client.c | 177 +++++++++++++++++++++++++++++++++------
 1 file changed, 152 insertions(+), 25 deletions(-)

diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 3c2aa293b..d5f9b1294 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -325,6 +325,7 @@ struct discovery_op {
 	struct queue *pending_chrcs;
 	struct queue *ext_prop_desc;
 	struct gatt_db_attribute *cur_svc;
+	struct gatt_db_attribute *hash;
 	bool success;
 	uint16_t start;
 	uint16_t end;
@@ -349,11 +350,19 @@ static void discovery_op_free(struct discovery_op *op)
 	free(op);
 }
 
+static bool read_db_hash(struct discovery_op *op);
+
 static void discovery_op_complete(struct discovery_op *op, bool success,
 								uint8_t err)
 {
 	const struct queue_entry *svc;
 
+	op->success = success;
+
+	/* Read database hash if discovery has been successful */
+	if (success && read_db_hash(op))
+		return;
+
 	/*
 	 * Unregister remove callback so it is not called when clearing unused
 	 * range.
@@ -380,7 +389,6 @@ static void discovery_op_complete(struct discovery_op *op, bool success,
 	if (op->last != UINT16_MAX)
 		gatt_db_clear_range(op->client->db, op->last + 1, UINT16_MAX);
 
-	op->success = success;
 	op->complete_func(op, success, err);
 }
 
@@ -1249,6 +1257,139 @@ static void notify_client_ready(struct bt_gatt_client *client, bool success,
 	bt_gatt_client_unref(client);
 }
 
+static void discover_all(struct discovery_op *op)
+{
+	struct bt_gatt_client *client = op->client;
+
+	client->discovery_req = bt_gatt_discover_all_primary_services(
+							client->att, NULL,
+							discover_primary_cb,
+							discovery_op_ref(op),
+							discovery_op_unref);
+	if (client->discovery_req)
+		return;
+
+	util_debug(client->debug_callback, client->debug_data,
+			"Failed to initiate primary service discovery");
+
+	client->in_init = false;
+	notify_client_ready(client, false, BT_ATT_ERROR_UNLIKELY);
+
+	discovery_op_unref(op);
+}
+
+static void db_hash_write_value_cb(struct gatt_db_attribute *attrib,
+						int err, void *user_data)
+{
+	struct bt_gatt_client *client = user_data;
+
+	util_debug(client->debug_callback, client->debug_data,
+						"Value set status: %d", err);
+}
+
+static void db_hash_read_value_cb(struct gatt_db_attribute *attrib,
+						int err, const uint8_t *value,
+						size_t length, void *user_data)
+{
+	const uint8_t **hash = user_data;
+
+	if (err || (length != 16))
+		return;
+
+	*hash = value;
+}
+
+static void db_hash_read_cb(bool success, uint8_t att_ecode,
+						struct bt_gatt_result *result,
+						void *user_data)
+{
+	struct discovery_op *op = user_data;
+	struct bt_gatt_client *client = op->client;
+	const uint8_t *hash = NULL, *value;
+	uint16_t len, handle;
+	struct bt_gatt_iter iter;
+
+	if (!success)
+		goto discover;
+
+	bt_gatt_iter_init(&iter, result);
+	bt_gatt_iter_next_read_by_type(&iter, &handle, &len, &value);
+
+	util_debug(client->debug_callback, client->debug_data,
+				"DB Hash found: handle 0x%04x length 0x%04x",
+				handle, len);
+
+	if (len != 16)
+		goto discover;
+
+	/* Read stored value in the db */
+	gatt_db_attribute_read(op->hash, 0, BT_ATT_OP_READ_REQ, NULL,
+					db_hash_read_value_cb, &hash);
+
+	/* Check if the has has changed since last time */
+	if (hash && !memcmp(hash, value, len)) {
+		util_debug(client->debug_callback, client->debug_data,
+				"DB Hash match: skipping discovery");
+		queue_remove_all(op->pending_svcs, NULL, NULL, NULL);
+		discovery_op_complete(op, true, 0);
+		return;
+	}
+
+	util_debug(client->debug_callback, client->debug_data,
+						"DB Hash value:");
+	util_hexdump(' ', value, len, client->debug_callback,
+						client->debug_data);
+
+	/* Store the new hash in the db */
+	gatt_db_attribute_write(op->hash, 0, value, len, 0, NULL,
+					db_hash_write_value_cb, client);
+
+discover:
+	if (!op->success) {
+		discover_all(op);
+		return;
+	}
+
+	discovery_op_complete(op, true, 0);
+}
+
+static void get_first_attribute(struct gatt_db_attribute *attrib,
+								void *user_data)
+{
+	struct gatt_db_attribute **stored = user_data;
+
+	if (*stored)
+		return;
+
+	*stored = attrib;
+}
+
+static bool read_db_hash(struct discovery_op *op)
+{
+	struct bt_gatt_client *client = op->client;
+	bt_uuid_t uuid;
+
+	/* Check if hash was already read */
+	if (op->hash)
+		return false;
+
+	bt_uuid16_create(&uuid, GATT_CHARAC_DB_HASH);
+	gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid,
+						get_first_attribute, &op->hash);
+	if (!op->hash)
+		return false;
+
+	if (!bt_gatt_read_by_type(client->att, 0x0001, 0xffff, &uuid,
+							db_hash_read_cb,
+							discovery_op_ref(op),
+							discovery_op_unref)) {
+		discovery_op_unref(op);
+		return false;
+	}
+
+	return true;
+}
+
 static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
 {
 	struct discovery_op *op = user_data;
@@ -1282,21 +1423,12 @@ static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
 					bt_att_get_mtu(client->att));
 
 discover:
-	client->discovery_req = bt_gatt_discover_all_primary_services(
-							client->att, NULL,
-							discover_primary_cb,
-							discovery_op_ref(op),
-							discovery_op_unref);
-	if (client->discovery_req)
+	if (read_db_hash(op)) {
+		op->success = false;
 		return;
+	}
 
-	util_debug(client->debug_callback, client->debug_data,
-			"Failed to initiate primary service discovery");
-
-	client->in_init = false;
-	notify_client_ready(client, false, att_ecode);
-
-	discovery_op_unref(op);
+	discover_all(op);
 }
 
 struct service_changed_op {
@@ -1491,17 +1623,6 @@ static unsigned int register_notify(struct bt_gatt_client *client,
 	return notify_data->id;
 }
 
-static void get_first_attribute(struct gatt_db_attribute *attrib,
-								void *user_data)
-{
-	struct gatt_db_attribute **stored = user_data;
-
-	if (*stored)
-		return;
-
-	*stored = attrib;
-}
-
 static void service_changed_register_cb(uint16_t att_ecode, void *user_data)
 {
 	bool success;
@@ -1747,6 +1868,11 @@ static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
 	return true;
 
 discover:
+	if (read_db_hash(op)) {
+		op->success = false;
+		goto done;
+	}
+
 	client->discovery_req = bt_gatt_discover_all_primary_services(
 							client->att, NULL,
 							discover_primary_cb,
@@ -1757,6 +1883,7 @@ discover:
 		return false;
 	}
 
+done:
 	client->in_init = true;
 	return true;
 }
-- 
2.17.2


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH BlueZ 14/15] shared/gatt-client: Write Client Features
  2019-01-29 13:26 [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
                   ` (11 preceding siblings ...)
  2019-01-29 13:26 ` [PATCH BlueZ 13/15] shared/gatt-client: Read database hash if available Luiz Augusto von Dentz
@ 2019-01-29 13:26 ` Luiz Augusto von Dentz
  2019-01-29 13:26 ` [PATCH BlueZ 15/15] device: Store Database Hash on storage Luiz Augusto von Dentz
  2019-02-06 11:49 ` [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
  14 siblings, 0 replies; 16+ messages in thread
From: Luiz Augusto von Dentz @ 2019-01-29 13:26 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This writes the supported client features setting Robust Caching bit.
---
 src/shared/gatt-client.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index d5f9b1294..1119d1a34 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -1798,6 +1798,27 @@ static void service_changed_cb(uint16_t value_handle, const uint8_t *value,
 	queue_push_tail(client->svc_chngd_queue, op);
 }
 
+static void write_client_features(struct bt_gatt_client *client)
+{
+	bt_uuid_t uuid;
+	struct gatt_db_attribute *attr = NULL;
+	uint16_t handle;
+	uint8_t value;
+
+	bt_uuid16_create(&uuid, GATT_CHARAC_CLI_FEAT);
+
+	gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid,
+						get_first_attribute, &attr);
+	if (!attr)
+		return;
+
+	handle = gatt_db_attribute_get_handle(attr);
+	value = BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING;
+
+	bt_gatt_client_write_value(client, handle, &value, sizeof(value), NULL,
+								NULL, NULL);
+}
+
 static void init_complete(struct discovery_op *op, bool success,
 							uint8_t att_ecode)
 {
@@ -1808,6 +1829,8 @@ static void init_complete(struct discovery_op *op, bool success,
 	if (!success)
 		goto fail;
 
+	write_client_features(client);
+
 	if (register_service_changed(client))
 		goto done;
 
-- 
2.17.2


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH BlueZ 15/15] device: Store Database Hash on storage
  2019-01-29 13:26 [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
                   ` (12 preceding siblings ...)
  2019-01-29 13:26 ` [PATCH BlueZ 14/15] shared/gatt-client: Write Client Features Luiz Augusto von Dentz
@ 2019-01-29 13:26 ` Luiz Augusto von Dentz
  2019-02-06 11:49 ` [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
  14 siblings, 0 replies; 16+ messages in thread
From: Luiz Augusto von Dentz @ 2019-01-29 13:26 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This stores the hash on the storage so it can be reloaded when the
daemon/system restarts.
---
 src/device.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 77 insertions(+), 9 deletions(-)

diff --git a/src/device.c b/src/device.c
index 060251f66..8ddfa28ff 100644
--- a/src/device.c
+++ b/src/device.c
@@ -2143,6 +2143,18 @@ struct gatt_saver {
 	GKeyFile *key_file;
 };
 
+static void db_hash_read_value_cb(struct gatt_db_attribute *attrib,
+						int err, const uint8_t *value,
+						size_t length, void *user_data)
+{
+	const uint8_t **hash = user_data;
+
+	if (err || (length != 16))
+		return;
+
+	*hash = value;
+}
+
 static void store_desc(struct gatt_db_attribute *attr, void *user_data)
 {
 	struct gatt_saver *saver = user_data;
@@ -2174,7 +2186,7 @@ static void store_chrc(struct gatt_db_attribute *attr, void *user_data)
 	char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR];
 	uint16_t handle_num, value_handle;
 	uint8_t properties;
-	bt_uuid_t uuid;
+	bt_uuid_t uuid, hash_uuid;
 
 	if (!gatt_db_attribute_get_char_data(attr, &handle_num, &value_handle,
 						&properties, &saver->ext_props,
@@ -2185,8 +2197,34 @@ static void store_chrc(struct gatt_db_attribute *attr, void *user_data)
 
 	sprintf(handle, "%04hx", handle_num);
 	bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
-	sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hhx:%s", value_handle,
-							properties, uuid_str);
+
+	/* Store Database Hash  value if available */
+	bt_uuid16_create(&hash_uuid, GATT_CHARAC_DB_HASH);
+	if (!bt_uuid_cmp(&uuid, &hash_uuid)) {
+		const uint8_t *hash = NULL;
+
+		attr = gatt_db_get_attribute(saver->device->db, value_handle);
+
+		gatt_db_attribute_read(attr, 0, BT_ATT_OP_READ_REQ, NULL,
+					db_hash_read_value_cb, &hash);
+		if (hash)
+			sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hhx:"
+				"%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
+				"%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
+				"%02hhx%02hhx:%s", value_handle, properties,
+				hash[0], hash[1], hash[2], hash[3],
+				hash[4], hash[5], hash[6], hash[7],
+				hash[8], hash[9], hash[10], hash[11],
+				hash[12], hash[13], hash[14], hash[15],
+				uuid_str);
+		else
+			sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hhx:%s",
+				value_handle, properties, uuid_str);
+
+	} else
+		sprintf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hhx:%s",
+				value_handle, properties, uuid_str);
+
 	g_key_file_set_string(key_file, "Attributes", handle, value);
 
 	gatt_db_service_foreach_desc(attr, store_desc, saver);
@@ -3228,6 +3266,20 @@ static void load_desc_value(struct gatt_db_attribute *attrib,
 		warn("loading descriptor value to db failed");
 }
 
+static ssize_t str2val(const char *str, uint8_t *val, size_t len)
+{
+	const char *pos = str;
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (sscanf(pos, "%2hhx", &val[i]) != 1)
+			break;
+		pos += 2;
+	}
+
+	return i;
+}
+
 static int load_desc(char *handle, char *value,
 					struct gatt_db_attribute *service)
 {
@@ -3247,7 +3299,7 @@ static int load_desc(char *handle, char *value,
 		val = 0;
 	}
 
-	DBG("loading descriptor handle: 0x%04x, value: 0x%04x, uuid: %s",
+	DBG("loading descriptor handle: 0x%04x, value: 0x%04x, value uuid: %s",
 				handle_int, val, uuid_str);
 
 	bt_string_to_uuid(&uuid, uuid_str);
@@ -3282,21 +3334,31 @@ static int load_chrc(char *handle, char *value,
 	uint16_t properties, value_handle, handle_int;
 	char uuid_str[MAX_LEN_UUID_STR];
 	struct gatt_db_attribute *att;
+	char val_str[32];
+	uint8_t val[16];
+	size_t val_len;
 	bt_uuid_t uuid;
 
 	if (sscanf(handle, "%04hx", &handle_int) != 1)
 		return -EIO;
 
-	if (sscanf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hx:%s", &value_handle,
-						&properties, uuid_str) != 3)
-		return -EIO;
+	/* Check if there is any value stored */
+	if (sscanf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hx:%32s:%s",
+			&value_handle, &properties, val_str, uuid_str) != 4) {
+		if (sscanf(value, GATT_CHARAC_UUID_STR ":%04hx:%02hx:%s",
+				&value_handle, &properties, uuid_str) != 3)
+			return -EIO;
+		val_len = 0;
+	} else
+		val_len = str2val(val_str, val, sizeof(val));
 
 	bt_string_to_uuid(&uuid, uuid_str);
 
 	/* Log debug message. */
 	DBG("loading characteristic handle: 0x%04x, value handle: 0x%04x,"
-				" properties 0x%04x uuid: %s", handle_int,
-				value_handle, properties, uuid_str);
+				" properties 0x%04x value: %s uuid: %s",
+				handle_int, value_handle, properties,
+				val_len ? val_str : "", uuid_str);
 
 	att = gatt_db_service_insert_characteristic(service, value_handle,
 							&uuid, 0, properties,
@@ -3306,6 +3368,12 @@ static int load_chrc(char *handle, char *value,
 		return -EIO;
 	}
 
+	if (val_len) {
+		if (!gatt_db_attribute_write(att, 0, val, val_len, 0, NULL,
+						load_desc_value, NULL))
+			return -EIO;
+	}
+
 	return 0;
 }
 
-- 
2.17.2


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash
  2019-01-29 13:26 [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
                   ` (13 preceding siblings ...)
  2019-01-29 13:26 ` [PATCH BlueZ 15/15] device: Store Database Hash on storage Luiz Augusto von Dentz
@ 2019-02-06 11:49 ` Luiz Augusto von Dentz
  14 siblings, 0 replies; 16+ messages in thread
From: Luiz Augusto von Dentz @ 2019-02-06 11:49 UTC (permalink / raw)
  To: linux-bluetooth

Hi,
On Tue, Jan 29, 2019 at 3:26 PM Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
>
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>
> The bt_crypto_gatt_hash uses AES-CCM to create a hash with the contents
> stored in the iovec.
>
> Note: The spec disregards the byte order so both the key, msg and res
> are not swapped.
> ---
>  src/shared/crypto.c | 31 +++++++++++++++++++++++++++++++
>  src/shared/crypto.h |  3 +++
>  2 files changed, 34 insertions(+)
>
> diff --git a/src/shared/crypto.c b/src/shared/crypto.c
> index ce0dcd638..5c5e1217d 100644
> --- a/src/shared/crypto.c
> +++ b/src/shared/crypto.c
> @@ -688,3 +688,34 @@ bool bt_crypto_h6(struct bt_crypto *crypto, const uint8_t w[16],
>
>         return true;
>  }
> +
> +bool bt_crypto_gatt_hash(struct bt_crypto *crypto, struct iovec *iov,
> +                               size_t iov_len, uint8_t res[16])
> +{
> +       const uint8_t key[16] = {};
> +       ssize_t len;
> +       int fd;
> +
> +       if (!crypto)
> +               return false;
> +
> +       fd = alg_new(crypto->cmac_aes, key, 16);
> +       if (fd < 0)
> +               return false;
> +
> +       len = writev(fd, iov, iov_len);
> +       if (len < 0) {
> +               close(fd);
> +               return false;
> +       }
> +
> +       len = read(fd, res, 16);
> +       if (len < 0) {
> +               close(fd);
> +               return false;
> +       }
> +
> +       close(fd);
> +
> +       return true;
> +}
> diff --git a/src/shared/crypto.h b/src/shared/crypto.h
> index 1e1b4836d..c58d2e104 100644
> --- a/src/shared/crypto.h
> +++ b/src/shared/crypto.h
> @@ -23,6 +23,7 @@
>
>  #include <stdbool.h>
>  #include <stdint.h>
> +#include <sys/uio.h>
>
>  struct bt_crypto;
>
> @@ -61,3 +62,5 @@ bool bt_crypto_h6(struct bt_crypto *crypto, const uint8_t w[16],
>  bool bt_crypto_sign_att(struct bt_crypto *crypto, const uint8_t key[16],
>                                 const uint8_t *m, uint16_t m_len,
>                                 uint32_t sign_cnt, uint8_t signature[12]);
> +bool bt_crypto_gatt_hash(struct bt_crypto *crypto, struct iovec *iov,
> +                               size_t iov_len, uint8_t res[16]);
> --
> 2.17.2

Applied.

-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, back to index

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-29 13:26 [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz
2019-01-29 13:26 ` [PATCH BlueZ 02/15] unit/test-crypto: Add test for bt_crypto_gatt_hash Luiz Augusto von Dentz
2019-01-29 13:26 ` [PATCH BlueZ 03/15] lib/uuid: Introduce definition for GATT caching attributes Luiz Augusto von Dentz
2019-01-29 13:26 ` [PATCH BlueZ 04/15] shared/util: Add decoding support " Luiz Augusto von Dentz
2019-01-29 13:26 ` [PATCH BlueZ 05/15] shared/att-types: Add errors introduced by GATT caching Luiz Augusto von Dentz
2019-01-29 13:26 ` [PATCH BlueZ 06/15] monitor: Decode GATT Caching errors Luiz Augusto von Dentz
2019-01-29 13:26 ` [PATCH BlueZ 07/15] shared/gatt-db: Introduce Database Hash Luiz Augusto von Dentz
2019-01-29 13:26 ` [PATCH BlueZ 08/15] shared/gatt-db: Generate database hash Luiz Augusto von Dentz
2019-01-29 13:26 ` [PATCH BlueZ 09/15] gatt: Add caching support for server Luiz Augusto von Dentz
2019-01-29 13:26 ` [PATCH BlueZ 10/15] shared/gatt-db: Add gatt_db_set_authorize Luiz Augusto von Dentz
2019-01-29 13:26 ` [PATCH BlueZ 11/15] shared/gatt-server: Add bt_gatt_server_set_authorize Luiz Augusto von Dentz
2019-01-29 13:26 ` [PATCH BlueZ 12/15] gatt: Implement Robust Caching handling for server Luiz Augusto von Dentz
2019-01-29 13:26 ` [PATCH BlueZ 13/15] shared/gatt-client: Read database hash if available Luiz Augusto von Dentz
2019-01-29 13:26 ` [PATCH BlueZ 14/15] shared/gatt-client: Write Client Features Luiz Augusto von Dentz
2019-01-29 13:26 ` [PATCH BlueZ 15/15] device: Store Database Hash on storage Luiz Augusto von Dentz
2019-02-06 11:49 ` [PATCH BlueZ 01/15] shared/crypto: Add bt_crypto_gatt_hash Luiz Augusto von Dentz

Linux-Bluetooth Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-bluetooth/0 linux-bluetooth/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-bluetooth linux-bluetooth/ https://lore.kernel.org/linux-bluetooth \
		linux-bluetooth@vger.kernel.org linux-bluetooth@archiver.kernel.org
	public-inbox-index linux-bluetooth


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-bluetooth


AGPL code for this site: git clone https://public-inbox.org/ public-inbox