This adds support to set different permissions to CCCD so security can be checked when enabling notification which conforms to: BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G page 2360: '3.3.3.3 Client Characteristic Configuration Authentication and authorization may be required by the server to write the configuration descriptor.' In addition to that also ensure that notification are not re-enabled until the proper security level is reached to conform to the following statement: '10.3.1.1 Handling of GATT indications and notifications A client “requests” a server to send indications and notifications by appropriately configuring the server via a Client Characteristic Configuration Descriptor. Since the configuration is persistent across a disconnection and reconnection, security requirements must be checked against the configuration upon a reconnection before sending indications or notifications. When a server reconnects to a client to send an indication or notification for which security is required, the server shall initiate or request encryption with the client prior to sending an indication or notification. If the client does not have an LTK indicating that the client has lost the bond, enabling encryption will fail.' Fixes #17983 Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
367 lines
9.2 KiB
C
367 lines
9.2 KiB
C
/* main.c - Application main entry point */
|
|
|
|
/*
|
|
* Copyright (c) 2015-2016 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/types.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <sys/printk.h>
|
|
#include <sys/byteorder.h>
|
|
#include <zephyr.h>
|
|
|
|
#include <settings/settings.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
|
#include <bluetooth/hci.h>
|
|
#include <bluetooth/conn.h>
|
|
#include <bluetooth/uuid.h>
|
|
#include <bluetooth/gatt.h>
|
|
#include <bluetooth/services/bas.h>
|
|
#include <bluetooth/services/hrs.h>
|
|
|
|
#include "cts.h"
|
|
|
|
/* Custom Service Variables */
|
|
static struct bt_uuid_128 vnd_uuid = BT_UUID_INIT_128(
|
|
0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
|
|
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12);
|
|
|
|
static struct bt_uuid_128 vnd_enc_uuid = BT_UUID_INIT_128(
|
|
0xf1, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
|
|
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12);
|
|
|
|
static struct bt_uuid_128 vnd_auth_uuid = BT_UUID_INIT_128(
|
|
0xf2, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
|
|
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12);
|
|
|
|
static u8_t vnd_value[] = { 'V', 'e', 'n', 'd', 'o', 'r' };
|
|
|
|
static ssize_t read_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
|
void *buf, u16_t len, u16_t offset)
|
|
{
|
|
const char *value = attr->user_data;
|
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
|
|
strlen(value));
|
|
}
|
|
|
|
static ssize_t write_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
|
const void *buf, u16_t len, u16_t offset,
|
|
u8_t flags)
|
|
{
|
|
u8_t *value = attr->user_data;
|
|
|
|
if (offset + len > sizeof(vnd_value)) {
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
|
}
|
|
|
|
memcpy(value + offset, buf, len);
|
|
|
|
return len;
|
|
}
|
|
|
|
static u8_t simulate_vnd;
|
|
static u8_t indicating;
|
|
static struct bt_gatt_indicate_params ind_params;
|
|
|
|
static void vnd_ccc_cfg_changed(const struct bt_gatt_attr *attr, u16_t value)
|
|
{
|
|
simulate_vnd = (value == BT_GATT_CCC_INDICATE) ? 1 : 0;
|
|
}
|
|
|
|
static void indicate_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
|
u8_t err)
|
|
{
|
|
printk("Indication %s\n", err != 0U ? "fail" : "success");
|
|
indicating = 0U;
|
|
}
|
|
|
|
#define MAX_DATA 74
|
|
static u8_t vnd_long_value[] = {
|
|
'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '1',
|
|
'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '2',
|
|
'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '3',
|
|
'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '4',
|
|
'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '5',
|
|
'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '6',
|
|
'.', ' ' };
|
|
|
|
static ssize_t read_long_vnd(struct bt_conn *conn,
|
|
const struct bt_gatt_attr *attr, void *buf,
|
|
u16_t len, u16_t offset)
|
|
{
|
|
const char *value = attr->user_data;
|
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
|
|
sizeof(vnd_long_value));
|
|
}
|
|
|
|
static ssize_t write_long_vnd(struct bt_conn *conn,
|
|
const struct bt_gatt_attr *attr, const void *buf,
|
|
u16_t len, u16_t offset, u8_t flags)
|
|
{
|
|
u8_t *value = attr->user_data;
|
|
|
|
if (flags & BT_GATT_WRITE_FLAG_PREPARE) {
|
|
return 0;
|
|
}
|
|
|
|
if (offset + len > sizeof(vnd_long_value)) {
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
|
}
|
|
|
|
memcpy(value + offset, buf, len);
|
|
|
|
return len;
|
|
}
|
|
|
|
static const struct bt_uuid_128 vnd_long_uuid = BT_UUID_INIT_128(
|
|
0xf3, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
|
|
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12);
|
|
|
|
static struct bt_gatt_cep vnd_long_cep = {
|
|
.properties = BT_GATT_CEP_RELIABLE_WRITE,
|
|
};
|
|
|
|
static int signed_value;
|
|
|
|
static ssize_t read_signed(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
|
void *buf, u16_t len, u16_t offset)
|
|
{
|
|
const char *value = attr->user_data;
|
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
|
|
sizeof(signed_value));
|
|
}
|
|
|
|
static ssize_t write_signed(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
|
const void *buf, u16_t len, u16_t offset,
|
|
u8_t flags)
|
|
{
|
|
u8_t *value = attr->user_data;
|
|
|
|
if (offset + len > sizeof(signed_value)) {
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
|
}
|
|
|
|
memcpy(value + offset, buf, len);
|
|
|
|
return len;
|
|
}
|
|
|
|
static const struct bt_uuid_128 vnd_signed_uuid = BT_UUID_INIT_128(
|
|
0xf3, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x13,
|
|
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x13);
|
|
|
|
static const struct bt_uuid_128 vnd_write_cmd_uuid = BT_UUID_INIT_128(
|
|
0xf4, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
|
|
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12);
|
|
|
|
static ssize_t write_without_rsp_vnd(struct bt_conn *conn,
|
|
const struct bt_gatt_attr *attr,
|
|
const void *buf, u16_t len, u16_t offset,
|
|
u8_t flags)
|
|
{
|
|
u8_t *value = attr->user_data;
|
|
|
|
/* Write request received. Reject it since this char only accepts
|
|
* Write Commands.
|
|
*/
|
|
if (!(flags & BT_GATT_WRITE_FLAG_CMD)) {
|
|
return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
|
|
}
|
|
|
|
if (offset + len > sizeof(vnd_value)) {
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
|
}
|
|
|
|
memcpy(value + offset, buf, len);
|
|
|
|
return len;
|
|
}
|
|
|
|
/* Vendor Primary Service Declaration */
|
|
BT_GATT_SERVICE_DEFINE(vnd_svc,
|
|
BT_GATT_PRIMARY_SERVICE(&vnd_uuid),
|
|
BT_GATT_CHARACTERISTIC(&vnd_enc_uuid.uuid,
|
|
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE |
|
|
BT_GATT_CHRC_INDICATE,
|
|
BT_GATT_PERM_READ_ENCRYPT |
|
|
BT_GATT_PERM_WRITE_ENCRYPT,
|
|
read_vnd, write_vnd, vnd_value),
|
|
BT_GATT_CCC(vnd_ccc_cfg_changed,
|
|
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE_ENCRYPT),
|
|
BT_GATT_CHARACTERISTIC(&vnd_auth_uuid.uuid,
|
|
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
|
|
BT_GATT_PERM_READ_AUTHEN |
|
|
BT_GATT_PERM_WRITE_AUTHEN,
|
|
read_vnd, write_vnd, vnd_value),
|
|
BT_GATT_CHARACTERISTIC(&vnd_long_uuid.uuid, BT_GATT_CHRC_READ |
|
|
BT_GATT_CHRC_WRITE | BT_GATT_CHRC_EXT_PROP,
|
|
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE |
|
|
BT_GATT_PERM_PREPARE_WRITE,
|
|
read_long_vnd, write_long_vnd, &vnd_long_value),
|
|
BT_GATT_CEP(&vnd_long_cep),
|
|
BT_GATT_CHARACTERISTIC(&vnd_signed_uuid.uuid, BT_GATT_CHRC_READ |
|
|
BT_GATT_CHRC_WRITE | BT_GATT_CHRC_AUTH,
|
|
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
|
|
read_signed, write_signed, &signed_value),
|
|
BT_GATT_CHARACTERISTIC(&vnd_write_cmd_uuid.uuid,
|
|
BT_GATT_CHRC_WRITE_WITHOUT_RESP,
|
|
BT_GATT_PERM_WRITE, NULL,
|
|
write_without_rsp_vnd, &vnd_value),
|
|
);
|
|
|
|
static const struct bt_data ad[] = {
|
|
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
|
BT_DATA_BYTES(BT_DATA_UUID16_ALL,
|
|
0x0d, 0x18, 0x0f, 0x18, 0x05, 0x18),
|
|
BT_DATA_BYTES(BT_DATA_UUID128_ALL,
|
|
0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
|
|
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12),
|
|
};
|
|
|
|
static void connected(struct bt_conn *conn, u8_t err)
|
|
{
|
|
if (err) {
|
|
printk("Connection failed (err 0x%02x)\n", err);
|
|
} else {
|
|
printk("Connected\n");
|
|
}
|
|
}
|
|
|
|
static void disconnected(struct bt_conn *conn, u8_t reason)
|
|
{
|
|
printk("Disconnected (reason 0x%02x)\n", reason);
|
|
}
|
|
|
|
static struct bt_conn_cb conn_callbacks = {
|
|
.connected = connected,
|
|
.disconnected = disconnected,
|
|
};
|
|
|
|
static void bt_ready(int err)
|
|
{
|
|
if (err) {
|
|
printk("Bluetooth init failed (err %d)\n", err);
|
|
return;
|
|
}
|
|
|
|
printk("Bluetooth initialized\n");
|
|
|
|
cts_init();
|
|
|
|
if (IS_ENABLED(CONFIG_SETTINGS)) {
|
|
settings_load();
|
|
}
|
|
|
|
err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
|
|
if (err) {
|
|
printk("Advertising failed to start (err %d)\n", err);
|
|
return;
|
|
}
|
|
|
|
printk("Advertising successfully started\n");
|
|
}
|
|
|
|
static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
|
|
{
|
|
char addr[BT_ADDR_LE_STR_LEN];
|
|
|
|
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
|
|
|
printk("Passkey for %s: %06u\n", addr, passkey);
|
|
}
|
|
|
|
static void auth_cancel(struct bt_conn *conn)
|
|
{
|
|
char addr[BT_ADDR_LE_STR_LEN];
|
|
|
|
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
|
|
|
printk("Pairing cancelled: %s\n", addr);
|
|
}
|
|
|
|
static struct bt_conn_auth_cb auth_cb_display = {
|
|
.passkey_display = auth_passkey_display,
|
|
.passkey_entry = NULL,
|
|
.cancel = auth_cancel,
|
|
};
|
|
|
|
static void bas_notify(void)
|
|
{
|
|
u8_t battery_level = bt_gatt_bas_get_battery_level();
|
|
|
|
battery_level--;
|
|
|
|
if (!battery_level) {
|
|
battery_level = 100U;
|
|
}
|
|
|
|
bt_gatt_bas_set_battery_level(battery_level);
|
|
}
|
|
|
|
static void hrs_notify(void)
|
|
{
|
|
static u8_t heartrate = 90U;
|
|
|
|
/* Heartrate measurements simulation */
|
|
heartrate++;
|
|
if (heartrate == 160U) {
|
|
heartrate = 90U;
|
|
}
|
|
|
|
bt_gatt_hrs_notify(heartrate);
|
|
}
|
|
|
|
void main(void)
|
|
{
|
|
int err;
|
|
|
|
err = bt_enable(bt_ready);
|
|
if (err) {
|
|
printk("Bluetooth init failed (err %d)\n", err);
|
|
return;
|
|
}
|
|
|
|
bt_conn_cb_register(&conn_callbacks);
|
|
bt_conn_auth_cb_register(&auth_cb_display);
|
|
|
|
/* Implement notification. At the moment there is no suitable way
|
|
* of starting delayed work so we do it here
|
|
*/
|
|
while (1) {
|
|
k_sleep(MSEC_PER_SEC);
|
|
|
|
/* Current Time Service updates only when time is changed */
|
|
cts_notify();
|
|
|
|
/* Heartrate measurements simulation */
|
|
hrs_notify();
|
|
|
|
/* Battery level simulation */
|
|
bas_notify();
|
|
|
|
/* Vendor indication simulation */
|
|
if (simulate_vnd) {
|
|
if (indicating) {
|
|
continue;
|
|
}
|
|
|
|
ind_params.attr = &vnd_svc.attrs[2];
|
|
ind_params.func = indicate_cb;
|
|
ind_params.data = &indicating;
|
|
ind_params.len = sizeof(indicating);
|
|
|
|
if (bt_gatt_indicate(NULL, &ind_params) == 0) {
|
|
indicating = 1U;
|
|
}
|
|
}
|
|
}
|
|
}
|