Bluetooth: Samples: Add Encrypted Advertising sample
Add a new sample that demonstrate the following points: - Exchange of Key Material; - Advertising encrypted data; - Read advertising data; - Decrypt advertising data that are encrypted. Signed-off-by: Théo Battrel <theo.battrel@nordicsemi.no>
This commit is contained in:
parent
1008bf186b
commit
12e9be7cc0
13 changed files with 1386 additions and 0 deletions
90
samples/bluetooth/encrypted_advertising/README.rst
Normal file
90
samples/bluetooth/encrypted_advertising/README.rst
Normal file
|
@ -0,0 +1,90 @@
|
|||
.. _bluetooth_encrypted_advertising_sample:
|
||||
|
||||
Bluetooth: Encrypted Advertising
|
||||
################################
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
This sample demonstrates the use of the encrypted advertising feature, such as:
|
||||
|
||||
- the exchange of the session key and the initialization vector using the Key
|
||||
Material characteristic,
|
||||
- the encryption of advertising payloads,
|
||||
- the decryption of those advertising payloads,
|
||||
- and the update of the Randomizer field whenever the RPA is changed.
|
||||
|
||||
To use the `bt_ead_encrypt` and `bt_ead_decrypt` functions, you must enable
|
||||
the Kconfig symbol :kconfig:option:`CONFIG_BT_EAD`.
|
||||
|
||||
While this sample uses extended advertising, it is **not** mandatory when using
|
||||
the encrypted advertising. The feature can be used with legacy advertising as
|
||||
well.
|
||||
|
||||
Requirements
|
||||
************
|
||||
|
||||
* Two boards with Bluetooth Low Energy support
|
||||
* Two boards with a push button connected via a GPIO pin, see :ref:`Button
|
||||
sample <button-sample>` for more details
|
||||
|
||||
Building and Running
|
||||
********************
|
||||
|
||||
This sample can be found under
|
||||
:zephyr_file:`samples/bluetooth/encrypted_advertising` in the Zephyr tree.
|
||||
|
||||
See :ref:`bluetooth samples section <bluetooth-samples>` for details.
|
||||
|
||||
This sample uses two applications, so two devices need to be setup.
|
||||
Flash one device with the central application, and another device with the
|
||||
peripheral application.
|
||||
|
||||
The two devices should automatically connect if they are close enough.
|
||||
|
||||
After the boards are connected, you will be asked to press the configured push
|
||||
button on both boards to confirm the pairing request.
|
||||
|
||||
Here are the outputs you should get by default:
|
||||
|
||||
Peripheral:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
*** Booting Zephyr OS build zephyr-v3.3.0-1872-g6fac3c7581dc ***
|
||||
<inf> ead_peripheral_sample: Advertising data size: 64
|
||||
Passkey for 46:04:2E:6F:80:12 (random): 059306
|
||||
Confirm passkey by pressing button at gpio@50000000 pin 11...
|
||||
Passkey confirmed.
|
||||
<inf> ead_peripheral_sample: Advertising data size: 64
|
||||
|
||||
|
||||
Central:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
*** Booting Zephyr OS build zephyr-v3.3.0-1872-g6fac3c7581dc ***
|
||||
Passkey for 6C:7F:67:C2:8B:29 (random): 059306
|
||||
Confirm passkey by pressing button at gpio@50000000 pin 11...
|
||||
Passkey confirmed.
|
||||
<inf> ead_central_sample: Received data size: 64
|
||||
<inf> ead_central_sample: len : 10
|
||||
<inf> ead_central_sample: type: 0x09
|
||||
<inf> ead_central_sample: data:
|
||||
45 41 44 20 53 61 6d 70 6c 65 |EAD Samp le
|
||||
<inf> ead_central_sample: len : 8
|
||||
<inf> ead_central_sample: type: 0xff
|
||||
<inf> ead_central_sample: data:
|
||||
05 f1 5a 65 70 68 79 72 |..Zephyr
|
||||
<inf> ead_central_sample: len : 7
|
||||
<inf> ead_central_sample: type: 0xff
|
||||
<inf> ead_central_sample: data:
|
||||
05 f1 49 d2 f4 55 76 |..I..Uv
|
||||
<inf> ead_central_sample: len : 4
|
||||
<inf> ead_central_sample: type: 0xff
|
||||
<inf> ead_central_sample: data:
|
||||
05 f1 c1 25 |...%
|
||||
<inf> ead_central_sample: len : 3
|
||||
<inf> ead_central_sample: type: 0xff
|
||||
<inf> ead_central_sample: data:
|
||||
05 f1 17 |...
|
|
@ -0,0 +1,16 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(central_ead)
|
||||
|
||||
target_sources(app PRIVATE
|
||||
src/main.c
|
||||
src/central_ead.c
|
||||
)
|
||||
|
||||
zephyr_library_include_directories(
|
||||
${ZEPHYR_BASE}/samples/bluetooth
|
||||
${CMAKE_CURRENT_LIST_DIR}/../include
|
||||
)
|
17
samples/bluetooth/encrypted_advertising/central/prj.conf
Normal file
17
samples/bluetooth/encrypted_advertising/central/prj.conf
Normal file
|
@ -0,0 +1,17 @@
|
|||
CONFIG_BT=y
|
||||
CONFIG_BT_CENTRAL=y
|
||||
CONFIG_BT_DEVICE_NAME="Central test EAD"
|
||||
|
||||
CONFIG_GPIO=y
|
||||
|
||||
CONFIG_BT_GATT_CLIENT=y
|
||||
CONFIG_BT_SMP=y
|
||||
CONFIG_BT_PRIVACY=y
|
||||
|
||||
CONFIG_BT_EAD=y
|
||||
|
||||
CONFIG_LOG=y
|
||||
CONFIG_BT_EAD_LOG_LEVEL_INF=y
|
||||
|
||||
CONFIG_BT_EXT_ADV=y
|
||||
CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX=64
|
|
@ -0,0 +1,7 @@
|
|||
sample:
|
||||
name: Bluetooth Central Encrypted Advertising Data
|
||||
tests:
|
||||
sample.bluetooth.central_ead:
|
||||
harness: bluetooth
|
||||
platform_allow: nrf52840dk_nrf52840
|
||||
tags: bluetooth
|
|
@ -0,0 +1,533 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include <zephyr/net/buf.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <zephyr/bluetooth/ead.h>
|
||||
#include <zephyr/bluetooth/att.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
#include <zephyr/bluetooth/addr.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/uuid.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
|
||||
#include "common/bt_str.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
LOG_MODULE_REGISTER(ead_central_sample, CONFIG_BT_EAD_LOG_LEVEL);
|
||||
|
||||
static uint8_t *received_data;
|
||||
static size_t received_data_size;
|
||||
static struct key_material keymat;
|
||||
|
||||
static bt_addr_le_t peer_addr;
|
||||
static struct bt_conn *default_conn;
|
||||
|
||||
static struct bt_conn_cb central_cb;
|
||||
static struct bt_conn_auth_cb central_auth_cb;
|
||||
|
||||
static struct k_poll_signal conn_signal;
|
||||
static struct k_poll_signal sec_update_signal;
|
||||
static struct k_poll_signal passkey_enter_signal;
|
||||
static struct k_poll_signal device_found_cb_completed;
|
||||
|
||||
/* GATT Discover data */
|
||||
static uint8_t gatt_disc_err;
|
||||
static uint16_t gatt_disc_end_handle;
|
||||
static uint16_t gatt_disc_start_handle;
|
||||
static struct k_poll_signal gatt_disc_signal;
|
||||
|
||||
/* GATT Read data */
|
||||
static uint8_t gatt_read_err;
|
||||
static uint8_t *gatt_read_res;
|
||||
static uint16_t gatt_read_len;
|
||||
static uint16_t gatt_read_handle;
|
||||
static struct k_poll_signal gatt_read_signal;
|
||||
|
||||
static bool data_parse_cb(struct bt_data *data, void *user_data)
|
||||
{
|
||||
size_t *parsed_data_size = (size_t *)user_data;
|
||||
|
||||
if (data->type == BT_DATA_ENCRYPTED_AD_DATA) {
|
||||
int err;
|
||||
struct net_buf_simple decrypted_buf;
|
||||
size_t decrypted_data_size = BT_EAD_DECRYPTED_PAYLOAD_SIZE(data->data_len);
|
||||
uint8_t decrypted_data[decrypted_data_size];
|
||||
|
||||
err = bt_ead_decrypt(keymat.session_key, keymat.iv, data->data, data->data_len,
|
||||
decrypted_data);
|
||||
if (err < 0) {
|
||||
LOG_ERR("Error during decryption (err %d)", err);
|
||||
}
|
||||
|
||||
net_buf_simple_init_with_data(&decrypted_buf, &decrypted_data[0],
|
||||
decrypted_data_size);
|
||||
|
||||
bt_data_parse(&decrypted_buf, &data_parse_cb, user_data);
|
||||
} else {
|
||||
LOG_INF("len : %u", data->data_len);
|
||||
LOG_INF("type: 0x%02x", data->type);
|
||||
LOG_HEXDUMP_INF(data->data, data->data_len, "data:");
|
||||
|
||||
/* Copy the data out if we are running in a test context */
|
||||
if (received_data != NULL) {
|
||||
if (bt_data_get_len(data, 1) <=
|
||||
(received_data_size - (*parsed_data_size))) {
|
||||
*parsed_data_size +=
|
||||
bt_data_serialize(data, &received_data[*parsed_data_size]);
|
||||
}
|
||||
} else {
|
||||
*parsed_data_size += bt_data_get_len(data, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
|
||||
struct net_buf_simple *ad)
|
||||
{
|
||||
int err;
|
||||
size_t parsed_data_size;
|
||||
char addr_str[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
if (default_conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
|
||||
|
||||
/* We are only interested in the previously connected device. */
|
||||
if (!bt_addr_le_eq(addr, &peer_addr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("Peer found.");
|
||||
|
||||
parsed_data_size = 0;
|
||||
LOG_INF("Received data size: %zu", ad->len);
|
||||
bt_data_parse(ad, data_parse_cb, &parsed_data_size);
|
||||
|
||||
LOG_DBG("All data parsed. (total size: %zu)", parsed_data_size);
|
||||
|
||||
err = bt_le_scan_stop();
|
||||
if (err) {
|
||||
LOG_DBG("Failed to stop scanner (err %d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
k_poll_signal_raise(&device_found_cb_completed, 0);
|
||||
}
|
||||
|
||||
static void connect_device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
|
||||
struct net_buf_simple *ad)
|
||||
{
|
||||
int err;
|
||||
char addr_str[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
if (default_conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Connect only to devices in close range */
|
||||
if (rssi < -70) {
|
||||
return;
|
||||
}
|
||||
|
||||
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
|
||||
|
||||
LOG_DBG("Device found: %s (RSSI %d)", addr_str, rssi);
|
||||
|
||||
err = bt_le_scan_stop();
|
||||
if (err) {
|
||||
LOG_DBG("Failed to stop scanner (err %d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT,
|
||||
&default_conn);
|
||||
if (err) {
|
||||
LOG_DBG("Failed to connect to %s (err %d)", addr_str, err);
|
||||
return;
|
||||
}
|
||||
|
||||
k_poll_signal_raise(&device_found_cb_completed, 0);
|
||||
}
|
||||
|
||||
static int start_scan(bool connect)
|
||||
{
|
||||
int err;
|
||||
|
||||
k_poll_signal_reset(&conn_signal);
|
||||
k_poll_signal_reset(&device_found_cb_completed);
|
||||
|
||||
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, connect ? connect_device_found : device_found);
|
||||
if (err) {
|
||||
LOG_DBG("Scanning failed to start (err %d)", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_DBG("Scanning started.");
|
||||
|
||||
if (connect) {
|
||||
LOG_DBG("Waiting for connection");
|
||||
await_signal(&conn_signal);
|
||||
}
|
||||
|
||||
await_signal(&device_found_cb_completed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t att_err,
|
||||
struct bt_gatt_read_params *params, const void *data, uint16_t read_len)
|
||||
{
|
||||
gatt_read_err = att_err;
|
||||
gatt_read_len = read_len;
|
||||
gatt_read_handle = params->by_uuid.start_handle;
|
||||
|
||||
if (!att_err) {
|
||||
memcpy(gatt_read_res, data, read_len);
|
||||
k_poll_signal_raise(&gatt_read_signal, 0);
|
||||
} else {
|
||||
LOG_ERR("ATT error (err %d)", att_err);
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
static int gatt_read(struct bt_conn *conn, const struct bt_uuid *uuid, size_t read_size,
|
||||
uint16_t start_handle, uint16_t end_handle, uint8_t *buf)
|
||||
{
|
||||
int err;
|
||||
size_t offset;
|
||||
uint16_t handle;
|
||||
struct bt_gatt_read_params params;
|
||||
|
||||
gatt_read_res = &buf[0];
|
||||
|
||||
params.handle_count = 0;
|
||||
params.by_uuid.start_handle = start_handle;
|
||||
params.by_uuid.end_handle = end_handle;
|
||||
params.by_uuid.uuid = uuid;
|
||||
params.func = gatt_read_cb;
|
||||
|
||||
k_poll_signal_reset(&gatt_read_signal);
|
||||
|
||||
err = bt_gatt_read(conn, ¶ms);
|
||||
if (err) {
|
||||
LOG_DBG("GATT read failed (err %d)", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
await_signal(&gatt_read_signal);
|
||||
|
||||
offset = gatt_read_len;
|
||||
handle = gatt_read_handle;
|
||||
|
||||
while (offset < read_size) {
|
||||
gatt_read_res = &buf[offset];
|
||||
|
||||
params.handle_count = 1;
|
||||
params.single.handle = handle;
|
||||
params.single.offset = offset;
|
||||
|
||||
k_poll_signal_reset(&gatt_read_signal);
|
||||
|
||||
err = bt_gatt_read(conn, ¶ms);
|
||||
if (err) {
|
||||
LOG_DBG("GATT read failed (err %d)", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
await_signal(&gatt_read_signal);
|
||||
|
||||
offset += gatt_read_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t gatt_discover_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
struct bt_gatt_discover_params *params)
|
||||
{
|
||||
gatt_disc_err = attr ? 0 : BT_ATT_ERR_ATTRIBUTE_NOT_FOUND;
|
||||
|
||||
if (attr) {
|
||||
gatt_disc_start_handle = attr->handle;
|
||||
gatt_disc_end_handle = ((struct bt_gatt_service_val *)attr->user_data)->end_handle;
|
||||
}
|
||||
|
||||
k_poll_signal_raise(&gatt_disc_signal, 0);
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
static int gatt_discover_primary_service(struct bt_conn *conn, const struct bt_uuid *service_type,
|
||||
uint16_t *start_handle, uint16_t *end_handle)
|
||||
{
|
||||
int err;
|
||||
struct bt_gatt_discover_params params;
|
||||
|
||||
params.type = BT_GATT_DISCOVER_PRIMARY;
|
||||
params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
|
||||
params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
|
||||
params.uuid = service_type;
|
||||
params.func = gatt_discover_cb;
|
||||
|
||||
k_poll_signal_reset(&gatt_disc_signal);
|
||||
|
||||
err = bt_gatt_discover(conn, ¶ms);
|
||||
if (err) {
|
||||
LOG_DBG("Primary service discover failed (err %d)", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
await_signal(&gatt_disc_signal);
|
||||
|
||||
*start_handle = gatt_disc_start_handle;
|
||||
*end_handle = gatt_disc_end_handle;
|
||||
|
||||
return gatt_disc_err;
|
||||
}
|
||||
|
||||
static void connected(struct bt_conn *conn, uint8_t conn_err)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
if (conn_err) {
|
||||
LOG_DBG("Failed to connect to %s (err %u)", addr, conn_err);
|
||||
|
||||
bt_conn_unref(default_conn);
|
||||
default_conn = NULL;
|
||||
|
||||
(void)start_scan(true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("Connected to: %s", addr);
|
||||
|
||||
k_poll_signal_raise(&conn_signal, 0);
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
LOG_DBG("Disconnected: %s (reason 0x%02x)", addr, reason);
|
||||
|
||||
if (default_conn != conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
bt_conn_unref(default_conn);
|
||||
default_conn = NULL;
|
||||
}
|
||||
|
||||
static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
if (!err) {
|
||||
LOG_DBG("Security changed: %s level %u", addr, level);
|
||||
k_poll_signal_raise(&sec_update_signal, 0);
|
||||
} else {
|
||||
LOG_DBG("Security failed: %s level %u err %d", addr, level, err);
|
||||
}
|
||||
}
|
||||
|
||||
static void identity_resolved(struct bt_conn *conn, const bt_addr_le_t *rpa,
|
||||
const bt_addr_le_t *identity)
|
||||
{
|
||||
char addr_identity[BT_ADDR_LE_STR_LEN];
|
||||
char addr_rpa[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(identity, addr_identity, sizeof(addr_identity));
|
||||
bt_addr_le_to_str(rpa, addr_rpa, sizeof(addr_rpa));
|
||||
|
||||
LOG_DBG("Identity resolved %s -> %s", addr_rpa, addr_identity);
|
||||
|
||||
bt_addr_le_copy(&peer_addr, identity);
|
||||
}
|
||||
|
||||
static void auth_passkey_confirm(struct bt_conn *conn, unsigned int passkey)
|
||||
{
|
||||
char passkey_str[7];
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
snprintk(passkey_str, ARRAY_SIZE(passkey_str), "%06u", passkey);
|
||||
|
||||
printk("Passkey for %s: %s\n", addr, passkey_str);
|
||||
|
||||
k_poll_signal_raise(&passkey_enter_signal, 0);
|
||||
}
|
||||
|
||||
static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
|
||||
{
|
||||
char passkey_str[7];
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
snprintk(passkey_str, ARRAY_SIZE(passkey_str), "%06u", passkey);
|
||||
|
||||
LOG_DBG("Passkey for %s: %s", addr, passkey_str);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
LOG_DBG("Pairing cancelled: %s", addr);
|
||||
}
|
||||
|
||||
static int init_bt(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
default_conn = NULL;
|
||||
|
||||
k_poll_signal_init(&conn_signal);
|
||||
k_poll_signal_init(&passkey_enter_signal);
|
||||
k_poll_signal_init(&sec_update_signal);
|
||||
k_poll_signal_init(&gatt_disc_signal);
|
||||
k_poll_signal_init(&gatt_read_signal);
|
||||
k_poll_signal_init(&device_found_cb_completed);
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err) {
|
||||
LOG_ERR("Bluetooth init failed (err %d)", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_DBG("Bluetooth initialized");
|
||||
|
||||
err = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
|
||||
if (err) {
|
||||
LOG_ERR("Unpairing failed (err %d)", err);
|
||||
}
|
||||
|
||||
central_cb.connected = connected;
|
||||
central_cb.disconnected = disconnected;
|
||||
central_cb.security_changed = security_changed;
|
||||
central_cb.identity_resolved = identity_resolved;
|
||||
|
||||
bt_conn_cb_register(¢ral_cb);
|
||||
|
||||
central_auth_cb.pairing_confirm = NULL;
|
||||
central_auth_cb.passkey_confirm = auth_passkey_confirm;
|
||||
central_auth_cb.passkey_display = auth_passkey_display;
|
||||
central_auth_cb.passkey_entry = NULL;
|
||||
central_auth_cb.oob_data_request = NULL;
|
||||
central_auth_cb.cancel = auth_cancel;
|
||||
|
||||
err = bt_conn_auth_cb_register(¢ral_auth_cb);
|
||||
if (err) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int run_central_sample(int get_passkey_confirmation(struct bt_conn *conn),
|
||||
uint8_t *test_received_data, size_t test_received_data_size,
|
||||
struct key_material *test_received_keymat)
|
||||
{
|
||||
int err;
|
||||
bool connect;
|
||||
uint16_t end_handle;
|
||||
uint16_t start_handle;
|
||||
|
||||
if (test_received_data != NULL) {
|
||||
received_data = test_received_data;
|
||||
received_data_size = test_received_data_size;
|
||||
}
|
||||
|
||||
/* Initialize Bluetooth and callbacks */
|
||||
err = init_bt();
|
||||
if (err) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Start scan and connect to our peripheral */
|
||||
connect = true;
|
||||
err = start_scan(connect);
|
||||
if (err) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Update connection security level */
|
||||
err = bt_conn_set_security(default_conn, BT_SECURITY_L4);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to set security (err %d)", err);
|
||||
return -3;
|
||||
}
|
||||
|
||||
await_signal(&passkey_enter_signal);
|
||||
|
||||
err = get_passkey_confirmation(default_conn);
|
||||
if (err) {
|
||||
LOG_ERR("Security update failed");
|
||||
return -4;
|
||||
}
|
||||
|
||||
/* Locate the primary service */
|
||||
err = gatt_discover_primary_service(default_conn, BT_UUID_CUSTOM_SERVICE, &start_handle,
|
||||
&end_handle);
|
||||
if (err) {
|
||||
LOG_ERR("Service not found (err %d)", err);
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* Read the Key Material characteristic */
|
||||
err = gatt_read(default_conn, BT_UUID_GATT_EDKM, sizeof(keymat), start_handle, end_handle,
|
||||
(uint8_t *)&keymat);
|
||||
if (err) {
|
||||
LOG_ERR("GATT read failed (err %d)", err);
|
||||
return -6;
|
||||
}
|
||||
|
||||
LOG_HEXDUMP_DBG(keymat.session_key, BT_EAD_KEY_SIZE, "Session Key");
|
||||
LOG_HEXDUMP_DBG(keymat.iv, BT_EAD_IV_SIZE, "IV");
|
||||
|
||||
if (test_received_keymat != NULL) {
|
||||
memcpy(test_received_keymat, &keymat, sizeof(keymat));
|
||||
}
|
||||
|
||||
/* Start a new scan to get and decrypt the Advertising Data */
|
||||
err = bt_conn_disconnect(default_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to disconnect.");
|
||||
return -7;
|
||||
}
|
||||
|
||||
connect = false;
|
||||
err = start_scan(connect);
|
||||
if (err) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
118
samples/bluetooth/encrypted_advertising/central/src/main.c
Normal file
118
samples/bluetooth/encrypted_advertising/central/src/main.c
Normal file
|
@ -0,0 +1,118 @@
|
|||
/* main.c - Application main entry point */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
|
||||
#include <zephyr/bluetooth/ead.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
LOG_MODULE_DECLARE(ead_central_sample, CONFIG_BT_EAD_LOG_LEVEL);
|
||||
|
||||
/*
|
||||
* Get button configuration from the devicetree sw0 alias. This is mandatory.
|
||||
*/
|
||||
#define SW0_NODE DT_ALIAS(sw0)
|
||||
#if !DT_NODE_HAS_STATUS(SW0_NODE, okay)
|
||||
#error "Unsupported board: sw0 devicetree alias is not defined"
|
||||
#endif
|
||||
static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0});
|
||||
static struct gpio_callback button_cb_data;
|
||||
|
||||
extern int run_central_sample(int get_passkey_confirmation(struct bt_conn *conn),
|
||||
uint8_t *received_data, size_t received_data_size,
|
||||
struct key_material *keymat);
|
||||
|
||||
static struct k_poll_signal button_pressed_signal;
|
||||
|
||||
static void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
|
||||
{
|
||||
LOG_DBG("Button pressed...");
|
||||
|
||||
k_poll_signal_raise(&button_pressed_signal, 0);
|
||||
k_sleep(K_SECONDS(1));
|
||||
k_poll_signal_reset(&button_pressed_signal);
|
||||
}
|
||||
|
||||
static int get_passkey_confirmation(struct bt_conn *conn)
|
||||
{
|
||||
int err;
|
||||
|
||||
printk("Confirm passkey by pressing button at %s pin %d...\n", button.port->name,
|
||||
button.pin);
|
||||
|
||||
await_signal(&button_pressed_signal);
|
||||
|
||||
err = bt_conn_auth_passkey_confirm(conn);
|
||||
if (err) {
|
||||
LOG_DBG("Failed to confirm passkey.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printk("Passkey confirmed.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_btn(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
k_poll_signal_init(&button_pressed_signal);
|
||||
|
||||
if (!gpio_is_ready_dt(&button)) {
|
||||
LOG_ERR("Error: button device %s is not ready", button.port->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Error %d: failed to configure %s pin %d", ret, button.port->name,
|
||||
button.pin);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_TO_ACTIVE);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Error %d: failed to configure interrupt on %s pin %d", ret,
|
||||
button.port->name, button.pin);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin));
|
||||
gpio_add_callback(button.port, &button_cb_data);
|
||||
LOG_DBG("Set up button at %s pin %d", button.port->name, button.pin);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = setup_btn();
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("Starting central sample...");
|
||||
|
||||
(void)run_central_sample(get_passkey_confirmation, NULL, 0, NULL);
|
||||
}
|
32
samples/bluetooth/encrypted_advertising/include/common.h
Normal file
32
samples/bluetooth/encrypted_advertising/include/common.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/bluetooth/ead.h>
|
||||
|
||||
#ifndef __EAD_SAMPLE_COMMON_H
|
||||
#define __EAD_SAMPLE_COMMON_H
|
||||
|
||||
struct key_material {
|
||||
uint8_t session_key[BT_EAD_KEY_SIZE];
|
||||
uint8_t iv[BT_EAD_IV_SIZE];
|
||||
} __packed;
|
||||
|
||||
#define CUSTOM_SERVICE_TYPE BT_UUID_128_ENCODE(0x2e2b8dc3, 0x06e0, 0x4f93, 0x9bb2, 0x734091c356f0)
|
||||
#define BT_UUID_CUSTOM_SERVICE BT_UUID_DECLARE_128(CUSTOM_SERVICE_TYPE)
|
||||
|
||||
static inline void await_signal(struct k_poll_signal *sig)
|
||||
{
|
||||
struct k_poll_event events[] = {
|
||||
K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, sig),
|
||||
};
|
||||
|
||||
k_poll(events, ARRAY_SIZE(events), K_FOREVER);
|
||||
}
|
||||
|
||||
#endif /* __EAD_SAMPLE_COMMON_H */
|
|
@ -0,0 +1,16 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(peripheral_ead)
|
||||
|
||||
target_sources(app PRIVATE
|
||||
src/main.c
|
||||
src/peripheral_ead.c
|
||||
)
|
||||
|
||||
zephyr_library_include_directories(
|
||||
${ZEPHYR_BASE}/samples/bluetooth
|
||||
${CMAKE_CURRENT_LIST_DIR}/../include
|
||||
)
|
18
samples/bluetooth/encrypted_advertising/peripheral/prj.conf
Normal file
18
samples/bluetooth/encrypted_advertising/peripheral/prj.conf
Normal file
|
@ -0,0 +1,18 @@
|
|||
CONFIG_BT=y
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_DEVICE_NAME="Peripheral test EAD"
|
||||
|
||||
CONFIG_GPIO=y
|
||||
|
||||
CONFIG_BT_SMP=y
|
||||
CONFIG_BT_PRIVACY=y
|
||||
CONFIG_BT_RPA_TIMEOUT=300
|
||||
|
||||
CONFIG_BT_EXT_ADV=y
|
||||
|
||||
CONFIG_BT_EAD=y
|
||||
|
||||
CONFIG_LOG=y
|
||||
CONFIG_BT_EAD_LOG_LEVEL_INF=y
|
||||
|
||||
CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=64
|
|
@ -0,0 +1,7 @@
|
|||
sample:
|
||||
name: Bluetooth Peripheral Encrypted Advertising Data
|
||||
tests:
|
||||
sample.bluetooth.peripheral_ead:
|
||||
harness: bluetooth
|
||||
platform_allow: nrf52840dk_nrf52840
|
||||
tags: bluetooth
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include <zephyr/bluetooth/gap.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define LF_ID_MSB ((BT_COMP_ID_LF >> 8) & 0xff)
|
||||
#define LF_ID_LSB ((BT_COMP_ID_LF) & 0xff)
|
||||
|
||||
#define AD_DATA_0_SIZE 10
|
||||
static uint8_t ad_data_0[] = {'E', 'A', 'D', ' ', 'S', 'a', 'm', 'p', 'l', 'e'};
|
||||
BUILD_ASSERT(sizeof(ad_data_0) == AD_DATA_0_SIZE);
|
||||
|
||||
#define AD_DATA_1_SIZE 8
|
||||
static uint8_t ad_data_1[] = {LF_ID_MSB, LF_ID_LSB, 'Z', 'e', 'p', 'h', 'y', 'r'};
|
||||
BUILD_ASSERT(sizeof(ad_data_1) == AD_DATA_1_SIZE);
|
||||
|
||||
#define AD_DATA_2_SIZE 7
|
||||
static uint8_t ad_data_2[] = {LF_ID_MSB, LF_ID_LSB, 0x49, 0xd2, 0xf4, 0x55, 0x76};
|
||||
BUILD_ASSERT(sizeof(ad_data_2) == AD_DATA_2_SIZE);
|
||||
|
||||
#define AD_DATA_3_SIZE 4
|
||||
static uint8_t ad_data_3[] = {LF_ID_MSB, LF_ID_LSB, 0xc1, 0x25};
|
||||
BUILD_ASSERT(sizeof(ad_data_3) == AD_DATA_3_SIZE);
|
||||
|
||||
#define AD_DATA_4_SIZE 3
|
||||
static uint8_t ad_data_4[] = {LF_ID_MSB, LF_ID_LSB, 0x17};
|
||||
BUILD_ASSERT(sizeof(ad_data_4) == AD_DATA_4_SIZE);
|
||||
|
||||
static const struct bt_data ad[] = {
|
||||
BT_DATA(BT_DATA_NAME_COMPLETE, ad_data_0, sizeof(ad_data_0)),
|
||||
BT_DATA(BT_DATA_MANUFACTURER_DATA, ad_data_1, sizeof(ad_data_1)),
|
||||
BT_DATA(BT_DATA_MANUFACTURER_DATA, ad_data_2, sizeof(ad_data_2)),
|
||||
BT_DATA(BT_DATA_MANUFACTURER_DATA, ad_data_3, sizeof(ad_data_3)),
|
||||
BT_DATA(BT_DATA_MANUFACTURER_DATA, ad_data_4, sizeof(ad_data_4)),
|
||||
};
|
||||
|
||||
static struct key_material mk = {
|
||||
.session_key = {0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB,
|
||||
0xCC, 0xCD, 0xCE, 0xCF},
|
||||
.iv = {0xFB, 0x56, 0xE1, 0xDA, 0xDC, 0x7E, 0xAD, 0xF5},
|
||||
};
|
112
samples/bluetooth/encrypted_advertising/peripheral/src/main.c
Normal file
112
samples/bluetooth/encrypted_advertising/peripheral/src/main.c
Normal file
|
@ -0,0 +1,112 @@
|
|||
/* main.c - Application main entry point */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
LOG_MODULE_DECLARE(ead_peripheral_sample, CONFIG_BT_EAD_LOG_LEVEL);
|
||||
|
||||
/*
|
||||
* Get button configuration from the devicetree sw0 alias. This is mandatory.
|
||||
*/
|
||||
#define SW0_NODE DT_ALIAS(sw0)
|
||||
#if !DT_NODE_HAS_STATUS(SW0_NODE, okay)
|
||||
#error "Unsupported board: sw0 devicetree alias is not defined"
|
||||
#endif
|
||||
static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0});
|
||||
static struct gpio_callback button_cb_data;
|
||||
|
||||
extern int run_peripheral_sample(int get_passkey_confirmation(struct bt_conn *conn));
|
||||
|
||||
static struct k_poll_signal button_pressed_signal;
|
||||
|
||||
static void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
|
||||
{
|
||||
LOG_DBG("Button pressed...");
|
||||
|
||||
k_poll_signal_raise(&button_pressed_signal, 0);
|
||||
k_sleep(K_SECONDS(1));
|
||||
k_poll_signal_reset(&button_pressed_signal);
|
||||
}
|
||||
|
||||
static int get_passkey_confirmation(struct bt_conn *conn)
|
||||
{
|
||||
int err;
|
||||
|
||||
printk("Confirm passkey by pressing button at %s pin %d...\n", button.port->name,
|
||||
button.pin);
|
||||
|
||||
await_signal(&button_pressed_signal);
|
||||
|
||||
err = bt_conn_auth_passkey_confirm(conn);
|
||||
if (err) {
|
||||
LOG_DBG("Failed to confirm passkey.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printk("Passkey confirmed.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_btn(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
k_poll_signal_init(&button_pressed_signal);
|
||||
|
||||
if (!gpio_is_ready_dt(&button)) {
|
||||
LOG_ERR("Error: button device %s is not ready", button.port->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Error %d: failed to configure %s pin %d", ret, button.port->name,
|
||||
button.pin);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_TO_ACTIVE);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Error %d: failed to configure interrupt on %s pin %d", ret,
|
||||
button.port->name, button.pin);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin));
|
||||
gpio_add_callback(button.port, &button_cb_data);
|
||||
LOG_DBG("Set up button at %s pin %d", button.port->name, button.pin);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = setup_btn();
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("Starting peripheral sample...");
|
||||
|
||||
(void)run_peripheral_sample(get_passkey_confirmation);
|
||||
}
|
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
|
||||
#include <zephyr/bluetooth/ead.h>
|
||||
#include <zephyr/bluetooth/gap.h>
|
||||
#include <zephyr/bluetooth/addr.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
#include <zephyr/bluetooth/uuid.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "data.h"
|
||||
|
||||
LOG_MODULE_REGISTER(ead_peripheral_sample, CONFIG_BT_EAD_LOG_LEVEL);
|
||||
|
||||
static struct bt_le_ext_adv_cb adv_cb;
|
||||
static struct bt_conn_cb peripheral_cb;
|
||||
static struct bt_conn_auth_cb peripheral_auth_cb;
|
||||
|
||||
static struct bt_conn *default_conn;
|
||||
|
||||
static struct k_poll_signal disconn_signal;
|
||||
static struct k_poll_signal passkey_enter_signal;
|
||||
|
||||
static ssize_t read_key_material(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
|
||||
uint16_t len, uint16_t offset)
|
||||
{
|
||||
return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data,
|
||||
sizeof(struct key_material));
|
||||
}
|
||||
|
||||
BT_GATT_SERVICE_DEFINE(key_mat, BT_GATT_PRIMARY_SERVICE(BT_UUID_CUSTOM_SERVICE),
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_GATT_EDKM, BT_GATT_CHRC_READ,
|
||||
BT_GATT_PERM_READ_AUTHEN, read_key_material, NULL,
|
||||
&mk));
|
||||
|
||||
static int update_ad_data(struct bt_le_ext_adv *adv)
|
||||
{
|
||||
int err;
|
||||
size_t offset;
|
||||
|
||||
/* Encrypt ad structure 1 */
|
||||
|
||||
size_t size_ad_1 = BT_DATA_SERIALIZED_SIZE(ad[1].data_len);
|
||||
uint8_t ad_1[size_ad_1];
|
||||
size_t size_ead_1 = BT_EAD_ENCRYPTED_PAYLOAD_SIZE(size_ad_1);
|
||||
uint8_t ead_1[size_ead_1];
|
||||
|
||||
bt_data_serialize(&ad[1], ad_1);
|
||||
|
||||
err = bt_ead_encrypt(mk.session_key, mk.iv, ad_1, size_ad_1, ead_1);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Error during first encryption");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Encrypt ad structures 3 and 4 */
|
||||
|
||||
size_t size_ad_3_4 =
|
||||
BT_DATA_SERIALIZED_SIZE(ad[3].data_len) + BT_DATA_SERIALIZED_SIZE(ad[4].data_len);
|
||||
uint8_t ad_3_4[size_ad_3_4];
|
||||
size_t size_ead_2 = BT_EAD_ENCRYPTED_PAYLOAD_SIZE(size_ad_3_4);
|
||||
uint8_t ead_2[size_ead_2];
|
||||
|
||||
offset = bt_data_serialize(&ad[3], &ad_3_4[0]);
|
||||
bt_data_serialize(&ad[4], &ad_3_4[offset]);
|
||||
|
||||
err = bt_ead_encrypt(mk.session_key, mk.iv, ad_3_4, size_ad_3_4, ead_2);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Error during second encryption");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Concatenate and update the advertising data */
|
||||
struct bt_data ad_structs[] = {
|
||||
ad[0],
|
||||
BT_DATA(BT_DATA_ENCRYPTED_AD_DATA, ead_1, size_ead_1),
|
||||
ad[2],
|
||||
BT_DATA(BT_DATA_ENCRYPTED_AD_DATA, ead_2, size_ead_2),
|
||||
};
|
||||
|
||||
LOG_INF("Advertising data size: %zu", bt_data_get_len(ad_structs, ARRAY_SIZE(ad_structs)));
|
||||
|
||||
err = bt_le_ext_adv_set_data(adv, ad_structs, ARRAY_SIZE(ad_structs), NULL, 0);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to set advertising data (%d)", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_DBG("Advertising Data Updated");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_ad_data(struct bt_le_ext_adv *adv)
|
||||
{
|
||||
return update_ad_data(adv);
|
||||
}
|
||||
|
||||
static bool rpa_expired_cb(struct bt_le_ext_adv *adv)
|
||||
{
|
||||
LOG_DBG("RPA expired");
|
||||
|
||||
/* The Bluetooth Core Specification say that the Randomizer and thus the
|
||||
* Advertising Data shall be updated each time the address is changed.
|
||||
*
|
||||
* ref:
|
||||
* Supplement to the Bluetooth Core Specification | v11, Part A 1.23.4
|
||||
*/
|
||||
update_ad_data(adv);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int create_adv(struct bt_le_ext_adv **adv)
|
||||
{
|
||||
int err;
|
||||
struct bt_le_adv_param params;
|
||||
|
||||
memset(¶ms, 0, sizeof(struct bt_le_adv_param));
|
||||
|
||||
params.options |= BT_LE_ADV_OPT_CONNECTABLE;
|
||||
params.options |= BT_LE_ADV_OPT_EXT_ADV;
|
||||
|
||||
params.id = BT_ID_DEFAULT;
|
||||
params.sid = 0;
|
||||
params.interval_min = BT_GAP_ADV_FAST_INT_MIN_2;
|
||||
params.interval_max = BT_GAP_ADV_FAST_INT_MAX_2;
|
||||
|
||||
adv_cb.rpa_expired = rpa_expired_cb;
|
||||
|
||||
err = bt_le_ext_adv_create(¶ms, &adv_cb, adv);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to create advertiser (%d)", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int start_adv(struct bt_le_ext_adv *adv)
|
||||
{
|
||||
int err;
|
||||
int32_t timeout = 0;
|
||||
uint8_t num_events = 0;
|
||||
|
||||
struct bt_le_ext_adv_start_param start_params;
|
||||
|
||||
start_params.timeout = timeout;
|
||||
start_params.num_events = num_events;
|
||||
|
||||
err = bt_le_ext_adv_start(adv, &start_params);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to start advertiser (%d)", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_DBG("Advertiser started");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stop_and_delete_adv(struct bt_le_ext_adv *adv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_le_ext_adv_stop(adv);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to stop advertiser (err %d)", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = bt_le_ext_adv_delete(adv);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to delete advertiser (err %d)", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void connected(struct bt_conn *conn, uint8_t err)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("Failed to connect to %s (%u)", addr, err);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("Connected to %s", addr);
|
||||
|
||||
default_conn = conn;
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
LOG_DBG("Disconnected from %s (reason 0x%02x)", addr, reason);
|
||||
|
||||
k_poll_signal_raise(&disconn_signal, 0);
|
||||
}
|
||||
|
||||
static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
if (!err) {
|
||||
LOG_DBG("Security changed: %s level %u", addr, level);
|
||||
} else {
|
||||
LOG_DBG("Security failed: %s level %u err %d", addr, level, err);
|
||||
}
|
||||
}
|
||||
|
||||
static void auth_passkey_confirm(struct bt_conn *conn, unsigned int passkey)
|
||||
{
|
||||
char passkey_str[7];
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
snprintk(passkey_str, ARRAY_SIZE(passkey_str), "%06u", passkey);
|
||||
|
||||
printk("Passkey for %s: %s\n", addr, passkey_str);
|
||||
|
||||
k_poll_signal_raise(&passkey_enter_signal, 0);
|
||||
}
|
||||
|
||||
static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
|
||||
{
|
||||
char passkey_str[7];
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
snprintk(passkey_str, ARRAY_SIZE(passkey_str), "%06u", passkey);
|
||||
|
||||
LOG_DBG("Passkey for %s: %s", addr, passkey_str);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
LOG_DBG("Pairing cancelled: %s", addr);
|
||||
}
|
||||
|
||||
static int init_bt(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
k_poll_signal_init(&disconn_signal);
|
||||
k_poll_signal_init(&passkey_enter_signal);
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err) {
|
||||
LOG_ERR("Bluetooth init failed (err %d)", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_DBG("Bluetooth initialized");
|
||||
|
||||
err = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
|
||||
if (err) {
|
||||
LOG_ERR("Unpairing failed (err %d)", err);
|
||||
}
|
||||
|
||||
peripheral_cb.connected = connected;
|
||||
peripheral_cb.disconnected = disconnected;
|
||||
peripheral_cb.security_changed = security_changed;
|
||||
|
||||
bt_conn_cb_register(&peripheral_cb);
|
||||
|
||||
peripheral_auth_cb.pairing_confirm = NULL;
|
||||
peripheral_auth_cb.passkey_confirm = auth_passkey_confirm;
|
||||
peripheral_auth_cb.passkey_display = auth_passkey_display;
|
||||
peripheral_auth_cb.passkey_entry = NULL;
|
||||
peripheral_auth_cb.oob_data_request = NULL;
|
||||
peripheral_auth_cb.cancel = auth_cancel;
|
||||
|
||||
err = bt_conn_auth_cb_register(&peripheral_auth_cb);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to register bt_conn_auth_cb (err %d)", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int run_peripheral_sample(int get_passkey_confirmation(struct bt_conn *conn))
|
||||
{
|
||||
int err;
|
||||
struct bt_le_ext_adv *adv = NULL;
|
||||
|
||||
err = init_bt();
|
||||
if (err) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Setup advertiser */
|
||||
err = create_adv(&adv);
|
||||
if (err) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
err = start_adv(adv);
|
||||
if (err) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
err = set_ad_data(adv);
|
||||
if (err) {
|
||||
return -4;
|
||||
}
|
||||
|
||||
/* Wait for the peer to update security */
|
||||
await_signal(&passkey_enter_signal);
|
||||
|
||||
err = get_passkey_confirmation(default_conn);
|
||||
if (err) {
|
||||
LOG_ERR("Failure during security update");
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* Wait for the peer to disconnect */
|
||||
await_signal(&disconn_signal);
|
||||
|
||||
/* Restart advertising */
|
||||
err = start_adv(adv);
|
||||
if (err) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
err = set_ad_data(adv);
|
||||
if (err) {
|
||||
return -4;
|
||||
}
|
||||
|
||||
/* Wait 10s before stopping and deleting the advertiser */
|
||||
k_sleep(K_SECONDS(10));
|
||||
|
||||
err = stop_and_delete_adv(adv);
|
||||
if (err) {
|
||||
return -6;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue