I am setting up a BLE GATT Service using Bluez 5.65 in Linux. I have my own process that I compile together with BlueZ sources. In the makefiles I have added it as another tool. I base my new tool on the existing bluez "tool" gatt-service.c
I'm using Bluez own dbus and gdbus interfaces to talk to the bluetoothd process, just like gatt-service. The overall design of the tool is identical to gatt-service.
I managed to setup a service with two characteristics, one for writing to the service and one for reading/indication back out to the client.
I'm looking into adding pairing now, to enable encryption and keyphrases. I have managed to setup a paired connection to my phone such that it can perform write & read to the two characteristics configured like this:
static const char *tx_characteristic_properties[] = { "encrypt-authenticated-read", "encrypt-authenticated-indicate", NULL };
static const char *rx_characteristic_properties[] = { "encrypt-authenticated-write", NULL };
This is all the encryption configuration I have added so far, but it seems to work. It does not allow me to read or write the characteristics until I have successfully paired the device with the service. So far I do the pairing manually via bluetoothctl, I will look into making it automatic later.
My current problem is that Indications no longer work after I activate encryption. When I activate Indications on the characteristic from my phone, btmon spits this out:
> ACL Data RX: Handle 68 flags 0x02 dlen 9 #25387 [hci0] 75486.664071
ATT: Write Request (0x12) len 4
Handle: 0x009b Type: Client Characteristic Configuration (0x2902)
Data: 0200
Indication (0x02)
< ACL Data TX: Handle 68 flags 0x00 dlen 9 #25388 [hci0] 75486.664606
ATT: Error Response (0x01) len 4
Write Request (0x12)
Handle: 0x009b
Error: Write Not Permitted (0x03)
> HCI Event: Number of Completed Packets (0x13) plen 5 #25389 [hci0] 75486.784052
Num handles: 1
Handle: 68 Address: 66:65:18:91:30:D5 (Resolvable)
Identity type: Public (0x00)
Identity: A8:79:8D:EB:B9:8B (OUI A8-79-8D)
Count: 1
My understanding is that I am not anymore permitted to modify the CCC descriptor to activate Indications due to the new security settings. If that is correct, my question is how to configure the CCC to allow writing/reading it from a paired device?
So far I follow the design of the gatt-service.c tool. I do not create any CCC descriptor for the characteristics, it seems to be automatically added as a default descriptor to every Indication characteristic I add. This can be seen in the screenshot from an android BLE scanner app I'm using to test the connection.
Update 1:
gatt-database::parse_chrc_flags is responsible for the initial CCC permissions flag setting based on the characteristic property strings sent in by the user.
if (!strcmp("encrypt-authenticated-indicate", flag)) {
*ccc_perm |= BT_ATT_PERM_WRITE_AUTHEN;
*props |= BT_GATT_CHRC_PROP_INDICATE;
When we later want to activate indications by writing to the CCC descriptor, gatt-server::check_permissions will fail the attempt because BT_ATT_PERM_WRITE bit is not set.
static uint8_t check_permissions(struct bt_gatt_server *server,
struct gatt_db_attribute *attr, uint32_t mask)
{
uint8_t enc_size;
uint32_t perm;
int security;
perm = gatt_db_attribute_get_permissions(attr);
if (perm && mask & BT_ATT_PERM_READ && !(perm & BT_ATT_PERM_READ))
return BT_ATT_ERROR_READ_NOT_PERMITTED;
if (perm && mask & BT_ATT_PERM_WRITE && !(perm & BT_ATT_PERM_WRITE))
return BT_ATT_ERROR_WRITE_NOT_PERMITTED;
If I modify gatt-database::parse_chrc_flags and initialize ccc_perm by also setting the BT_ATT_PERM_WRITE flag, the permission check goes through and indications begin working also with the encrypt-authenticated-indicate property. To read CCC, I also need to add the BT_ATT_PERM_READ permission.
else if (!strcmp("encrypt-authenticated-indicate", flag)) {
*ccc_perm |= BT_ATT_PERM_WRITE_AUTHEN;
*ccc_perm |= BT_ATT_PERM_WRITE;
*ccc_perm |= BT_ATT_PERM_READ;
*props |= BT_GATT_CHRC_PROP_INDICATE;
Considering that we initially set the BT_ATT_PERM_WRITE_AUTHEN, I would have assumed that the permission checking should be focused on BT_ATT_PERM_WRITE_AUTHEN and not BT_ATT_PERM_WRITE. I wonder if the CCC write permission checking is somehow not understanding that we are in the permission write authentication mode?
My assumption for now is that this is a bug in BlueZ.
Likely the recently added encrypt-authenticated-write type characteristic properties do not properly set all permission flags that are required for ccc_perm.
The characteristic property setting interface gatt-database::parse_chrc_flags sets only one specialized write flag in ccc_perm, and never the general BT_ATT_PERM_WRITE flag.
if (!strcmp("encrypt-authenticated-indicate", flag)) {
*ccc_perm |= BT_ATT_PERM_WRITE_AUTHEN;
*props |= BT_GATT_CHRC_PROP_INDICATE;
If we check how the flags are set in gatt-database::parse_desc_flags, we can see that BT_ATT_PERM_WRITE is always set together with the more specialized write permission flags:
if (!strcmp("encrypt-authenticated-write", flag))
*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN;
I will report this as a bug to the BlueZ github repo unless someone else comes up with a better explanation :)
Update 1
Yep, it was an issue. Fixed some months back. https://github.com/bluez/bluez/issues/607