Merge branch 'feat/enc_adv_data_v5.0' into 'release/v5.0'
Some checks failed
docker / docker (push) Has been cancelled

NimBLE: Added support of encrypted advertising data (v5.0)

See merge request espressif/esp-idf!32046
This commit is contained in:
Rahul Tank 2024-07-12 21:07:06 +08:00
commit a1ce7ed947
25 changed files with 1870 additions and 8 deletions

View File

@ -570,6 +570,8 @@ if(CONFIG_BT_ENABLED)
"host/nimble/nimble/nimble/host/src/ble_hs_mqueue.c"
"host/nimble/nimble/nimble/host/src/ble_hs_periodic_sync.c"
"host/nimble/nimble/nimble/host/src/ble_att.c"
"host/nimble/nimble/nimble/host/src/ble_ead.c"
"host/nimble/nimble/nimble/host/src/ble_aes_ccm.c"
"host/nimble/nimble/nimble/host/src/ble_gattc.c"
"host/nimble/nimble/nimble/host/src/ble_store.c"
"host/nimble/nimble/nimble/host/src/ble_sm_lgcy.c"

View File

@ -655,3 +655,17 @@ config BT_NIMBLE_VS_SUPPORT
help
This option is used to enable support for sending Vendor Specific HCI commands and handling
Vendor Specific HCI Events.
config BT_NIMBLE_ENC_ADV_DATA
bool "Encrypted Advertising Data"
depends on SOC_ESP_NIMBLE_CONTROLLER && BT_NIMBLE_50_FEATURE_SUPPORT
select BT_NIMBLE_EXT_ADV
help
This option is used to enable encrypted advertising data.
config BT_NIMBLE_MAX_EADS
int "Maximum number of EAD devices to save across reboots"
default 10
depends on BT_NIMBLE_ENABLED && BT_NIMBLE_ENC_ADV_DATA
help
Defines maximum number of encrypted advertising data key material to save

@ -1 +1 @@
Subproject commit 1e6768d610cb49a3c00c8be2bc83a30c3a5be685
Subproject commit 749a79093753d3df1450e16e45a7ea5a7f12e523

View File

@ -97,6 +97,12 @@
#define MYNEWT_VAL_BLE_EXT_ADV_MAX_SIZE (CONFIG_BT_NIMBLE_EXT_ADV_MAX_SIZE)
#endif
#ifndef CONFIG_BT_NIMBLE_ENC_ADV_DATA
#define MYNEWT_VAL_ENC_ADV_DATA (0)
#else
#define MYNEWT_VAL_ENC_ADV_DATA (CONFIG_BT_NIMBLE_ENC_ADV_DATA)
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
#define BLE_SCAN_RSP_DATA_MAX_LEN_N (1650)
#else
@ -825,6 +831,10 @@
#define MYNEWT_VAL_BLE_STORE_MAX_CCCDS CONFIG_BT_NIMBLE_MAX_CCCDS
#endif
#ifdef CONFIG_BT_NIMBLE_MAX_EADS
#define MYNEWT_VAL_BLE_STORE_MAX_EADS CONFIG_BT_NIMBLE_MAX_EADS
#endif
#ifndef MYNEWT_VAL_BLE_STORE_CONFIG_PERSIST
#ifdef CONFIG_BT_NIMBLE_NVS_PERSIST
#define MYNEWT_VAL_BLE_STORE_CONFIG_PERSIST (1)

View File

@ -93,6 +93,12 @@ examples/bluetooth/nimble:
reason: not tested yet
examples/bluetooth/nimble/ble_enc_adv_data:
enable:
- if: IDF_TARGET in ["esp32c2", "esp32c3", "esp32s3"]
temporary: true
reason: the other targets are not tested yet
examples/bluetooth/nimble/ble_htp:
enable:
- if: IDF_TARGET in ["esp32", "esp32c2", "esp32c3", "esp32h2", "esp32s3"]

View File

@ -0,0 +1,8 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/bluetooth/nimble/common/nimble_central_utils)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(enc_adv_data_cent)

View File

@ -0,0 +1,171 @@
| Supported Targets | ESP32-C2 | ESP32-C3 | ESP32-S3 |
| ----------------- | -------- | -------- | -------- |
# BLE Encrypted Advertising Data Central Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example creates GATT client and performs passive scan, it then connects to peripheral device if the device advertises connectability and the device advertises support for the custom service (0x2C01) as primary service UUID which has key material characteristic (0x2B88).
After connection it enables bonding and link encryprion if the `Enable Link Encryption` flag is set in the example config.
It performs the following operations against the specified peer:
* Scans the peripheral devices and saves the peer address if it supports gap service as a primary service.
* Check if session key or IV exists for the connected peer. If it's not present, read the key material characteric. The value read will contain session key and IV for decrypting advertising data.
* Disconnect from the peer once read is completed.
* While scanning for the second time, if the peer's address is saved and respestive session key and IV are present, decrypt the advertising data.
If the peer does not support a required service, characteristic, or descriptor, then the peer lied when it claimed support for the gap service! When this happens, or if a GATT procedure fails, this function immediately terminates the connection.
It uses ESP32's Bluetooth controller and NimBLE stack based BLE host.
This example aims at understanding BLE client scanning devices which advertise encrypted data.
To test this demo, use any BLE GATT server app that advertises support for the gap service (0x1800), includes it in the GATT database and has key material characteristic support.
## How to Use Example
Before project configuration and build, be sure to set the correct chip target using:
```bash
idf.py set-target <chip_name>
```
### Hardware Required
* A development board with ESP32/ESP32-C2/ESP32-C3/ESP32-S3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
* A USB cable for Power supply and programming
See [Development Boards](https://www.espressif.com/en/products/devkits) for more information about it.
### Configure the Project
Open the project configuration menu:
```bash
idf.py menuconfig
```
In the `Component config` menu:
* Select encrypted adv data from `Component config -> Bluetooth -> NimBLE Options -> BT_NIMBLE_ENC_ADV_DATA`
In the `Example Configuration` menu:
* Change the `Peer Address` option if needed.
### Build and Flash
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects.
## Example Output
This is the console output on successful connection:
```
I (436) NimBLE: BLE Host Task Started
I (446) main_task: Returned from app_main()
I (506) NimBLE: Adding peer addr : 60:55:f9:f7:4f:42
I (546) NimBLE: Connection established
I (546) NimBLE:
I (546) NimBLE: GATT procedure initiated: exchange mtu
I (546) NimBLE: GATT procedure initiated: discover all services
I (636) NimBLE: mtu update event; conn_handle=1 cid=4 mtu=256
I (696) NimBLE: GATT procedure initiated: discover all characteristics;
I (696) NimBLE: start_handle=1 end_handle=7
I (846) NimBLE: GATT procedure initiated: discover all characteristics;
I (846) NimBLE: start_handle=8 end_handle=11
I (966) NimBLE: GATT procedure initiated: discover all characteristics;
I (966) NimBLE: start_handle=12 end_handle=65535
I (1116) NimBLE: GATT procedure initiated: discover all descriptors;
I (1116) NimBLE: chr_val_handle=10 end_handle=11
I (1176) NimBLE: GATT procedure initiated: discover all descriptors;
I (1176) NimBLE: chr_val_handle=14 end_handle=65535
I (1386) NimBLE: Service discovery complete; status=0 conn_handle=1
I (1386) NimBLE: GATT procedure initiated: read;
I (1386) NimBLE: att_handle=7
I (1476) NimBLE: Read complete; status=0 conn_handle=1
I (1476) NimBLE: attr_handle=7 value=
I (1476) NimBLE: 0xc0
I (1486) NimBLE: :0xc1
I (1486) NimBLE: :0xc2
I (1486) NimBLE: :0xc3
I (1486) NimBLE: :0xc4
I (1496) NimBLE: :0xc5
I (1496) NimBLE: :0xc6
I (1496) NimBLE: :0xc7
I (1506) NimBLE: :0xc8
I (1506) NimBLE: :0xc9
I (1506) NimBLE: :0xca
I (1516) NimBLE: :0xcb
I (1516) NimBLE: :0xcc
I (1516) NimBLE: :0xcd
I (1526) NimBLE: :0xce
I (1526) NimBLE: :0xcf
I (1526) NimBLE: :0xfb
I (1526) NimBLE: :0x56
I (1536) NimBLE: :0xe1
I (1536) NimBLE: :0xda
I (1536) NimBLE: :0xdc
I (1546) NimBLE: :0x7e
I (1546) NimBLE: :0xad
I (1546) NimBLE: :0xf5
I (1556) NimBLE: Writing of session key, iv, and peer addr to NVS success
I (1556) NimBLE: GAP procedure initiated: terminate connection; conn_handle=1 hci_reason=19
I (1596) NimBLE: disconnect; reason=534
I (1596) NimBLE:
I (1626) NimBLE: Peer was already added with addr : 60:55:f9:f7:4f:42
I (1626) NimBLE: Read session key and iv from NVS successfully
I (1636) NimBLE: Decryption of adv data done successfully
I (1726) NimBLE: Connection established
I (1726) NimBLE:
I (1726) NimBLE: GATT procedure initiated: exchange mtu
I (1736) NimBLE: GATT procedure initiated: discover all services
I (1826) NimBLE: mtu update event; conn_handle=1 cid=4 mtu=256
I (1886) NimBLE: GATT procedure initiated: discover all characteristics;
I (1886) NimBLE: start_handle=1 end_handle=7
I (2006) NimBLE: GATT procedure initiated: discover all characteristics;
I (2006) NimBLE: start_handle=8 end_handle=11
I (2156) NimBLE: GATT procedure initiated: discover all characteristics;
I (2156) NimBLE: start_handle=12 end_handle=65535
I (2276) NimBLE: GATT procedure initiated: discover all descriptors;
I (2276) NimBLE: chr_val_handle=10 end_handle=11
I (2366) NimBLE: GATT procedure initiated: discover all descriptors;
I (2366) NimBLE: chr_val_handle=14 end_handle=65535
I (2546) NimBLE: Service discovery complete; status=0 conn_handle=1
Done
```
## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

View File

@ -0,0 +1,4 @@
set(srcs "main.c")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,17 @@
menu "Example Configuration"
config EXAMPLE_PEER_ADDR
string "Peer Address"
default "ADDR_ANY"
help
Enter the peer address in aa:bb:cc:dd:ee:ff form to connect to a specific peripheral
config EXAMPLE_ENC_ADV_DATA
bool
depends on SOC_ESP_NIMBLE_CONTROLLER && BT_NIMBLE_50_FEATURE_SUPPORT
prompt "Enable encrypted advertising data feature support"
default y
select BT_NIMBLE_ENC_ADV_DATA
help
This option enables support of encrypted advertising data.
endmenu

View File

@ -0,0 +1,39 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#ifndef H_ENC_ADV_DATA_CENT_
#define H_ENC_ADV_DATA_CENT_
#if CONFIG_EXAMPLE_ENC_ADV_DATA
#include "modlog/modlog.h"
#include "esp_central.h"
#include "host/ble_ead.h"
#ifdef __cplusplus
extern "C" {
#endif
struct ble_hs_adv_fields;
struct ble_gap_conn_desc;
struct ble_hs_cfg;
union ble_store_value;
union ble_store_key;
#define BLE_SVC_GAP_UUID16 0x1800
#define BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME 0x2a00
#define BLE_SVC_GAP_CHR_UUID16_KEY_MATERIAL 0x2B88
struct km_peer {
bool key_material_exist;
uint8_t peer_addr[PEER_ADDR_VAL_SIZE];
};
#ifdef __cplusplus
}
#endif
#endif
#endif

View File

@ -0,0 +1,597 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "esp_log.h"
#include "nvs_flash.h"
/* BLE */
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "console/console.h"
#include "services/gap/ble_svc_gap.h"
#include "enc_adv_data_cent.h"
#if CONFIG_EXAMPLE_ENC_ADV_DATA
static int counter = 0;
static struct km_peer kmp[CONFIG_BT_NIMBLE_MAX_CONNECTIONS + 1] = {0};
static const char *tag = "ENC_ADV_DATA_CENT";
static int enc_adv_data_cent_gap_event(struct ble_gap_event *event, void *arg);
static uint8_t peer_addr[6];
static int mtu_def = 512;
void ble_store_config_init(void);
static int
enc_adv_data_find_peer(const uint8_t *peer_addr)
{
for (int i = 0; i <= CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) {
if (memcmp(peer_addr, &kmp[i].peer_addr, PEER_ADDR_VAL_SIZE) == 0) {
return i;
}
}
return -1;
}
static int
enc_adv_data_set_km_exist(const uint8_t *peer_addr)
{
int ind = enc_adv_data_find_peer(peer_addr);
if (ind == -1) {
return -1;
}
kmp[ind].key_material_exist = true;
return 0;
}
static bool
enc_adv_data_check_km_exist(const uint8_t *peer_addr)
{
int ind;
ind = enc_adv_data_find_peer(peer_addr);
if (ind == -1) {
return false;
}
return kmp[ind].key_material_exist;
}
/**
* Application callback. Called when the read has completed.
*/
static int
enc_adv_data_cent_on_read(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr,
void *arg)
{
int rc;
struct ble_store_value_ead value_ead = {0};
struct peer *p;
MODLOG_DFLT(INFO, "Read complete; status=%d conn_handle=%d", error->status,
conn_handle);
if (error->status == 0) {
MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle);
print_mbuf(attr->om);
} else {
goto err;
}
p = peer_find(conn_handle);
if (p == NULL) {
goto err;
}
rc = enc_adv_data_set_km_exist(p->peer_addr);
if (rc != 0) {
MODLOG_DFLT(INFO, "Setting key material exist flag failed");
}
value_ead.km_present = 1;
value_ead.km = (struct key_material *) malloc (sizeof(struct key_material));
memset(value_ead.km, 0, sizeof(struct key_material));
os_mbuf_copydata(attr->om, 0, BLE_EAD_KEY_SIZE, &value_ead.km->session_key);
os_mbuf_copydata(attr->om, BLE_EAD_KEY_SIZE, BLE_EAD_IV_SIZE, &value_ead.km->iv);
MODLOG_DFLT(DEBUG, "Session key:");
print_bytes(value_ead.km->session_key, BLE_EAD_KEY_SIZE);
MODLOG_DFLT(DEBUG, "IV:");
print_bytes(value_ead.km->iv, BLE_EAD_IV_SIZE);
memcpy(&value_ead.peer_addr.val, &p->peer_addr, PEER_ADDR_VAL_SIZE);
rc = ble_store_write_ead(&value_ead);
if (rc == 0) {
MODLOG_DFLT(INFO, "Writing of session key, iv, and peer addr to NVS success");
}
err:
/* Terminate the connection. */
return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
}
static void
enc_adv_data_cent_read(const struct peer *peer)
{
const struct peer_chr *chr = NULL;
int rc;
/* Read the supported-new-alert-category characteristic. */
chr = peer_chr_find_uuid(peer,
BLE_UUID16_DECLARE(BLE_SVC_GAP_UUID16),
BLE_UUID16_DECLARE(BLE_SVC_GAP_CHR_UUID16_KEY_MATERIAL));
if (chr == NULL) {
MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Key"
"Material characteristic\n");
goto err;
}
rc = ble_gattc_read(peer->conn_handle, chr->chr.val_handle,
enc_adv_data_cent_on_read, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error: Failed to read characteristic; rc=%d\n",
rc);
goto err;
}
return;
err:
/* Terminate the connection. */
ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
}
/**
* Called when service discovery of the specified peer has completed.
*/
static void
enc_adv_data_cent_on_disc_complete(const struct peer *peer, int status, void *arg)
{
if (status != 0) {
/* Service discovery failed. Terminate the connection. */
MODLOG_DFLT(ERROR, "Error: Service discovery failed; status=%d "
"conn_handle=%d\n", status, peer->conn_handle);
ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return;
}
/* Service discovery has completed successfully. Now we have a complete
* list of services, characteristics, and descriptors that the peer
* supports.
*/
MODLOG_DFLT(INFO, "Service discovery complete; status=%d "
"conn_handle=%d\n", status, peer->conn_handle);
if (!enc_adv_data_check_km_exist(peer->peer_addr)) {
/* Now perform GATT read procedures against the peer */
enc_adv_data_cent_read(peer);
}
}
/**
* Initiates the GAP general discovery procedure.
*/
static void
enc_adv_data_cent_scan(void)
{
uint8_t own_addr_type;
struct ble_gap_disc_params disc_params;
int rc;
/* Figure out address to use while advertising (no privacy for now) */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
return;
}
/* Tell the controller to filter duplicates; we don't want to process
* repeated advertisements from the same device.
*/
disc_params.filter_duplicates = 1;
/**
* Perform a passive scan. I.e., don't send follow-up scan requests to
* each advertiser.
*/
disc_params.passive = 1;
/* Use defaults for the rest of the parameters. */
disc_params.itvl = 0;
disc_params.window = 0;
disc_params.filter_policy = 0;
disc_params.limited = 0;
rc = ble_gap_disc(own_addr_type, BLE_HS_FOREVER, &disc_params,
enc_adv_data_cent_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n",
rc);
}
}
static int
enc_adv_data_cent_decrypt(uint8_t length_data, const uint8_t *data, const uint8_t *peer_addr)
{
uint8_t op;
uint8_t len, offset = 0;
uint8_t *enc_data;
int rc;
uint8_t dec_data_len;
struct ble_store_key_ead key_ead = {0};
struct ble_store_value_ead value_ead = {0};
while (offset < length_data) {
len = data[offset];
op = data[offset + 1];
uint8_t temp[len];
switch (op) {
case BLE_GAP_ENC_ADV_DATA:
enc_data = (uint8_t *) malloc (sizeof(uint8_t) * len);
memcpy(enc_data, data + offset + 2, len);
memcpy(&key_ead.peer_addr.val, peer_addr, PEER_ADDR_VAL_SIZE);
rc = ble_store_read_ead(&key_ead, &value_ead);
if (rc != 0 || !value_ead.km_present) {
MODLOG_DFLT(INFO, "Reading of session key and iv from NVS failed rc = %d", rc);
return 0;
} else {
MODLOG_DFLT(INFO, "Read session key and iv from NVS successfully");
}
rc = ble_ead_decrypt(value_ead.km->session_key, value_ead.km->iv, enc_data, len,
temp);
if (rc == 0) {
MODLOG_DFLT(INFO, "Decryption of adv data done successfully");
} else {
MODLOG_DFLT(INFO, "Decryption of adv data failed");
return 0;
}
dec_data_len = temp[0];
MODLOG_DFLT(DEBUG, "Data after decryption:");
print_bytes(temp, dec_data_len);
return 1;
default:
break;
}
offset += len + 1;
}
return 1;
}
/**
* Indicates whether we should try to connect to the sender of the specified
* advertisement. The function returns a positive result if the device
* advertises connectability and support for the Key Characteristic service.
*/
static int
ext_enc_adv_data_cent_should_connect(const struct ble_gap_ext_disc_desc *disc)
{
int offset = 0;
int ad_struct_len = 0;
if (disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
return 0;
}
if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen ("ADDR_ANY")) != 0)) {
MODLOG_DFLT(INFO, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR);
/* Convert string to address */
sscanf(CONFIG_EXAMPLE_PEER_ADDR, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&peer_addr[5], &peer_addr[4], &peer_addr[3],
&peer_addr[2], &peer_addr[1], &peer_addr[0]);
if (memcmp(peer_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
return 0;
}
}
/* The device has to advertise support for the Key Characteristic
* service (0x2B88)
*/
do {
ad_struct_len = disc->data[offset];
if (!ad_struct_len) {
break;
}
/* Search if custom service UUID (0x2C01) is advertised */
if (disc->data[offset] == 0x03 && disc->data[offset + 1] == 0x03) {
if ( disc->data[offset + 2] == 0x2C && disc->data[offset + 3] == 0x01 ) {
if (enc_adv_data_find_peer(disc->addr.val) != -1) {
MODLOG_DFLT(INFO, "Peer was already added with addr : %s",
addr_str(&disc->addr.val));
} else {
MODLOG_DFLT(INFO, "Adding peer addr : %s", addr_str(&disc->addr.val));
memcpy(&kmp[counter].peer_addr, &disc->addr.val, PEER_ADDR_VAL_SIZE);
counter++;
if (counter > CONFIG_BT_NIMBLE_MAX_CONNECTIONS) {
counter = 0;
}
}
if (enc_adv_data_check_km_exist(disc->addr.val)) {
return enc_adv_data_cent_decrypt(disc->length_data, disc->data, disc->addr.val);
} else {
return 1;
}
}
}
offset += ad_struct_len + 1;
} while ( offset < disc->length_data );
return 0;
}
/**
* Connects to the sender of the specified advertisement of it looks
* interesting. A device is "interesting" if it advertises connectability and
* support for the Key Characteristic service.
*/
static void
enc_adv_data_cent_connect_if_interesting(void *disc)
{
uint8_t own_addr_type;
int rc;
ble_addr_t *addr;
/* Don't do anything if we don't care about this advertiser. */
if (!ext_enc_adv_data_cent_should_connect((struct ble_gap_ext_disc_desc *)disc)) {
return;
}
/* Scanning must be stopped before a connection can be initiated. */
rc = ble_gap_disc_cancel();
if (rc != 0) {
MODLOG_DFLT(DEBUG, "Failed to cancel scan; rc=%d\n", rc);
return;
}
/* Figure out address to use for connect (no privacy for now) */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
return;
}
/* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
* timeout.
*/
addr = &((struct ble_gap_ext_disc_desc *)disc)->addr;
rc = ble_gap_connect(own_addr_type, addr, 30000, NULL,
enc_adv_data_cent_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d "
"addr=%s; rc=%d\n",
addr->type, addr_str(addr->val), rc);
return;
}
}
/**
* The nimble host executes this callback when a GAP event occurs. The
* application associates a GAP event callback with each connection that is
* established. enc_adv_data_cent uses the same callback for all connections.
*
* @param event The event being signalled.
* @param arg Application-specified argument; unused by
* enc_adv_data_cent.
*
* @return 0 if the application successfully handled the
* event; nonzero on failure. The semantics
* of the return code is specific to the
* particular GAP event being signalled.
*/
static int
enc_adv_data_cent_gap_event(struct ble_gap_event *event, void *arg)
{
struct ble_gap_conn_desc desc;
struct ble_hs_adv_fields fields;
int rc;
switch (event->type) {
case BLE_GAP_EVENT_DISC:
rc = ble_hs_adv_parse_fields(&fields, event->disc.data,
event->disc.length_data);
if (rc != 0) {
return 0;
}
/* An advertisment report was received during GAP discovery. */
print_adv_fields(&fields);
/* Try to connect to the advertiser if it looks interesting. */
enc_adv_data_cent_connect_if_interesting(&event->disc);
return 0;
case BLE_GAP_EVENT_CONNECT:
/* A new connection was established or a connection attempt failed. */
if (event->connect.status == 0) {
/* Connection successfully established. */
MODLOG_DFLT(INFO, "Connection established ");
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
print_conn_desc(&desc);
MODLOG_DFLT(INFO, "");
rc = ble_att_set_preferred_mtu(mtu_def);
if (rc != 0) {
ESP_LOGE(tag, "Failed to set preferred MTU; rc = %d", rc);
}
rc = ble_gattc_exchange_mtu(event->connect.conn_handle, NULL, NULL);
if (rc != 0) {
ESP_LOGE(tag, "Failed to negotiate MTU; rc = %d", rc);
}
/* Remember peer. */
rc = peer_add(event->connect.conn_handle);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to add peer; rc=%d\n", rc);
return 0;
}
rc = peer_set_addr(event->connect.conn_handle, desc.peer_id_addr.val);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to set peer addr; rc=%d\n", rc);
return 0;
}
/* Perform service discovery */
rc = peer_disc_all(event->connect.conn_handle,
enc_adv_data_cent_on_disc_complete, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
return 0;
}
} else {
/* Connection attempt failed; resume scanning. */
MODLOG_DFLT(ERROR, "Error: Connection failed; status=%d\n",
event->connect.status);
enc_adv_data_cent_scan();
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
/* Connection terminated. */
MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason);
print_conn_desc(&event->disconnect.conn);
MODLOG_DFLT(INFO, "");
/* Forget about peer. */
peer_delete(event->disconnect.conn.conn_handle);
/* Resume scanning. */
enc_adv_data_cent_scan();
return 0;
case BLE_GAP_EVENT_DISC_COMPLETE:
MODLOG_DFLT(INFO, "discovery complete; reason=%d\n",
event->disc_complete.reason);
return 0;
case BLE_GAP_EVENT_NOTIFY_RX:
/* Peer sent us a notification or indication. */
MODLOG_DFLT(INFO, "received %s; conn_handle=%d attr_handle=%d "
"attr_len=%d\n",
event->notify_rx.indication ?
"indication" :
"notification",
event->notify_rx.conn_handle,
event->notify_rx.attr_handle,
OS_MBUF_PKTLEN(event->notify_rx.om));
/* Attribute data is contained in event->notify_rx.om. Use
* `os_mbuf_copydata` to copy the data received in notification mbuf */
return 0;
case BLE_GAP_EVENT_MTU:
MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n",
event->mtu.conn_handle,
event->mtu.channel_id,
event->mtu.value);
return 0;
case BLE_GAP_EVENT_EXT_DISC:
/* An advertisment report was received during GAP discovery. */
ext_print_adv_report(&event->disc);
enc_adv_data_cent_connect_if_interesting(&event->disc);
return 0;
default:
return 0;
}
}
static void
enc_adv_data_cent_on_reset(int reason)
{
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
}
static void
enc_adv_data_cent_on_sync(void)
{
int rc;
/* Make sure we have proper identity address set (public preferred) */
rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
enc_adv_data_cent_scan();
}
void enc_adv_data_cent_host_task(void *param)
{
MODLOG_DFLT(INFO, "BLE Host Task Started");
/* This function will return only when nimble_port_stop() is executed */
nimble_port_run();
nimble_port_freertos_deinit();
}
void
app_main(void)
{
int rc;
/* Initialize NVS — it is used to store PHY calibration data */
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ret = nimble_port_init();
if (ret != ESP_OK) {
ESP_LOGE(tag, "Failed to init nimble %d ", ret);
return;
}
/* Configure the host. */
ble_hs_cfg.reset_cb = enc_adv_data_cent_on_reset;
ble_hs_cfg.sync_cb = enc_adv_data_cent_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
/* Initialize data structures to track connected peers. */
rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64);
assert(rc == 0);
/* Set the default device name. */
rc = ble_svc_gap_device_name_set("nimble-enc_adv_data_cent");
assert(rc == 0);
/* XXX Need to have template for store */
ble_store_config_init();
nimble_port_freertos_init(enc_adv_data_cent_host_task);
}
#else
void
app_main(void)
{
return;
}
#endif

View File

@ -0,0 +1,14 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y
CONFIG_BT_NIMBLE_EXT_ADV=y
CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=y

View File

@ -0,0 +1,8 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/bluetooth/nimble/common/nimble_peripheral_utils)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(enc_adv_data_prph)

View File

@ -0,0 +1,109 @@
| Supported Targets | ESP32-C2 | ESP32-C3 | ESP32-S3 |
| ----------------- | -------- | -------- | -------- |
# BLE Encrypted Advertising Data Peripheral Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example creates GATT server and then starts normal advertising and encrypted advertising data, waiting to be connected to a GATT client.
This example aims at understanding advertising encrypted data.
For RPA feature (currently Host based privacy feature is supported), use API `ble_hs_pvcy_rpa_config` to enable/disable host based privacy, `own_addr_type` needs to be set to `BLE_ADDR_RANDOM` to use this feature. Please include `ble_hs_pvcy.h` while using this API. As `ble_hs_pvcy_rpa_config` configures host privacy and sets address in controller, it is necessary to call this API after host-controller are synced (e.g. in `bleprph_on_sync` callback).
## How to Use Example
Before project configuration and build, be sure to set the correct chip target using:
```bash
idf.py set-target <chip_name>
```
### Configure the project
Open the project configuration menu:
```bash
idf.py menuconfig
```
In the `Component config` menu:
* Select encrypted adv data from `Component config -> Bluetooth -> NimBLE Options -> BT_NIMBLE_ENC_ADV_DATA`
In the `Example Configuration` menu:
* Select I/O capabilities of device from `Example Configuration --> I/O Capability`, default is `Just_works`.
* Enable/Disable other security related parameters `Bonding, MITM option, secure connection(SM SC)`.
### Build and Flash
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects.
## Example Output
There is this console output when bleprph is connected and characteristic is read:
```
I (445) NimBLE: BLE Host Task Started
I (445) NimBLE: Device Address:
I (445) NimBLE: 60:55:f9:f7:4f:42
I (445) NimBLE:
I (455) NimBLE: Encryption of adv data done successfully
I (465) main_task: Returned from app_main()
I (575) NimBLE: connection established; status=0
I (575) NimBLE: handle=1 our_ota_addr_type=0 our_ota_addr=
I (575) NimBLE: 60:55:f9:f7:4f:42
I (585) NimBLE: our_id_addr_type=0 our_id_addr=
I (585) NimBLE: 60:55:f9:f7:4f:42
I (595) NimBLE: peer_ota_addr_type=0 peer_ota_addr=
I (595) NimBLE: 60:55:f9:f7:51:0a
I (605) NimBLE: peer_id_addr_type=0 peer_id_addr=
I (605) NimBLE: 60:55:f9:f7:51:0a
I (615) NimBLE: conn_itvl=24 conn_latency=0 supervision_timeout=256 encrypted=0 authenticated=0 bonded=0
I (625) NimBLE:
I (625) NimBLE: advertise complete; reason=0
I (635) NimBLE: Encryption of adv data done successfully
I (1635) NimBLE: disconnect; reason=531
I (1635) NimBLE: handle=1 our_ota_addr_type=0 our_ota_addr=
I (1635) NimBLE: 60:55:f9:f7:4f:42
I (1635) NimBLE: our_id_addr_type=0 our_id_addr=
I (1635) NimBLE: 60:55:f9:f7:4f:42
I (1645) NimBLE: peer_ota_addr_type=0 peer_ota_addr=
I (1645) NimBLE: 60:55:f9:f7:51:0a
I (1655) NimBLE: peer_id_addr_type=0 peer_id_addr=
I (1655) NimBLE: 60:55:f9:f7:51:0a
I (1665) NimBLE: conn_itvl=24 conn_latency=0 supervision_timeout=256 encrypted=0 authenticated=0 bonded=0
I (1675) NimBLE:
I (1675) NimBLE: connection established; status=0
I (1685) NimBLE: handle=1 our_ota_addr_type=0 our_ota_addr=
I (1685) NimBLE: 60:55:f9:f7:4f:42
I (1695) NimBLE: our_id_addr_type=0 our_id_addr=
I (1695) NimBLE: 60:55:f9:f7:4f:42
I (1705) NimBLE: peer_ota_addr_type=0 peer_ota_addr=
I (1705) NimBLE: 60:55:f9:f7:51:0a
I (1715) NimBLE: peer_id_addr_type=0 peer_id_addr=
I (1715) NimBLE: 60:55:f9:f7:51:0a
I (1725) NimBLE: conn_itvl=24 conn_latency=0 supervision_timeout=256 encrypted=0 authenticated=0 bonded=0
I (1735) NimBLE:
I (1735) NimBLE: advertise complete; reason=0
I (1745) NimBLE: Encryption of adv data done successfully
I (1755) NimBLE: mtu update event; conn_handle=1 cid=4 mtu=256
```
## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

View File

@ -0,0 +1,5 @@
set(srcs "main.c"
"gatt_svr.c")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,65 @@
menu "Example Configuration"
choice EXAMPLE_USE_IO_TYPE
prompt "I/O Capability"
default BLE_SM_IO_CAP_NO_IO
help
I/O capability of device.
config BLE_SM_IO_CAP_DISP_ONLY
bool "DISPLAY ONLY"
config BLE_SM_IO_CAP_DISP_YES_NO
bool "DISPLAY YESNO"
config BLE_SM_IO_CAP_KEYBOARD_ONLY
bool "KEYBOARD ONLY"
config BLE_SM_IO_CAP_NO_IO
bool "Just works"
config BLE_SM_IO_CAP_KEYBOARD_DISP
bool "Both KEYBOARD & DISPLAY"
endchoice
config EXAMPLE_IO_TYPE
int
default 0 if BLE_SM_IO_CAP_DISP_ONLY
default 1 if BLE_SM_IO_CAP_DISP_YES_NO
default 2 if BLE_SM_IO_CAP_KEYBOARD_ONLY
default 3 if BLE_SM_IO_CAP_NO_IO
default 4 if BLE_SM_IO_CAP_KEYBOARD_DISP
config EXAMPLE_BONDING
bool
default n
prompt "Use Bonding"
help
Use this option to enable/disable bonding.
config EXAMPLE_USE_SC
bool
depends on BT_NIMBLE_SM_SC
default n
prompt "Use Secure Connection feature"
help
Use this option to enable/disable Security Manager Secure Connection 4.2 feature.
config EXAMPLE_RANDOM_ADDR
bool
prompt "Advertise RANDOM Address"
help
Use this option to advertise a random address instead of public address
config EXAMPLE_RESOLVE_PEER_ADDR
bool
prompt "Enable resolving peer address"
help
Use this option to enable resolving peer's address.
config EXAMPLE_ENC_ADV_DATA
bool
prompt "Enable encrypted advertising data feature support"
depends on SOC_ESP_NIMBLE_CONTROLLER && BT_NIMBLE_50_FEATURE_SUPPORT
default y
select BT_NIMBLE_ENC_ADV_DATA
help
This option enables support of encrypted advertising data.
endmenu

View File

@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#ifndef H_ENC_ADV_DATA_PRPH_
#define H_ENC_ADV_DATA_PRPH_
#if CONFIG_EXAMPLE_ENC_ADV_DATA
#include <stdbool.h>
#include "nimble/ble.h"
#include "modlog/modlog.h"
#include "esp_peripheral.h"
#include "host/ble_ead.h"
#ifdef __cplusplus
extern "C" {
#endif
struct ble_hs_cfg;
struct ble_gatt_register_ctxt;
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
int gatt_svr_init(void);
#ifdef __cplusplus
}
#endif
#endif
#endif

View File

@ -0,0 +1,246 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#include "enc_adv_data_prph.h"
/*** Maximum number of characteristics with the notify flag ***/
#define MAX_NOTIFY 5
static const ble_uuid128_t gatt_svr_svc_uuid =
BLE_UUID128_INIT(0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12,
0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59);
/* A characteristic that can be subscribed to */
static uint8_t gatt_svr_chr_val;
static uint16_t gatt_svr_chr_val_handle;
static const ble_uuid128_t gatt_svr_chr_uuid =
BLE_UUID128_INIT(0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11,
0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33);
/* A custom descriptor */
static uint8_t gatt_svr_dsc_val;
static const ble_uuid128_t gatt_svr_dsc_uuid =
BLE_UUID128_INIT(0x01, 0x01, 0x01, 0x01, 0x12, 0x12, 0x12, 0x12,
0x23, 0x23, 0x23, 0x23, 0x34, 0x34, 0x34, 0x34);
static int
gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
{
/*** Service ***/
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &gatt_svr_svc_uuid.u,
.characteristics = (struct ble_gatt_chr_def[])
{ {
/*** This characteristic can be subscribed to by writing 0x00 and 0x01 to the CCCD ***/
.uuid = &gatt_svr_chr_uuid.u,
.access_cb = gatt_svc_access,
#if CONFIG_EXAMPLE_ENCRYPTION
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE |
BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC |
BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_INDICATE,
#else
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_INDICATE,
#endif
.val_handle = &gatt_svr_chr_val_handle,
.descriptors = (struct ble_gatt_dsc_def[]) {
{
.uuid = &gatt_svr_dsc_uuid.u,
#if CONFIG_EXAMPLE_ENCRYPTION
.att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_ENC,
#else
.att_flags = BLE_ATT_F_READ,
#endif
.access_cb = gatt_svc_access,
}, {
0, /* No more descriptors in this characteristic */
}
},
}, {
0, /* No more characteristics in this service. */
}
},
},
{
0, /* No more services. */
},
};
static int
gatt_svr_write(struct os_mbuf *om, uint16_t min_len, uint16_t max_len,
void *dst, uint16_t *len)
{
uint16_t om_len;
int rc;
om_len = OS_MBUF_PKTLEN(om);
if (om_len < min_len || om_len > max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
rc = ble_hs_mbuf_to_flat(om, dst, max_len, len);
if (rc != 0) {
return BLE_ATT_ERR_UNLIKELY;
}
return 0;
}
/**
* Access callback whenever a characteristic/descriptor is read or written to.
* Here reads and writes need to be handled.
* ctxt->op tells weather the operation is read or write and
* weather it is on a characteristic or descriptor,
* ctxt->dsc->uuid tells which characteristic/descriptor is accessed.
* attr_handle give the value handle of the attribute being accessed.
* Accordingly do:
* Append the value to ctxt->om if the operation is READ
* Write ctxt->om to the value if the operation is WRITE
**/
static int
gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
const ble_uuid_t *uuid;
int rc;
switch (ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_CHR:
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
MODLOG_DFLT(INFO, "Characteristic read; conn_handle=%d attr_handle=%d\n",
conn_handle, attr_handle);
} else {
MODLOG_DFLT(INFO, "Characteristic read by NimBLE stack; attr_handle=%d\n",
attr_handle);
}
uuid = ctxt->chr->uuid;
if (attr_handle == gatt_svr_chr_val_handle) {
rc = os_mbuf_append(ctxt->om,
&gatt_svr_chr_val,
sizeof(gatt_svr_chr_val));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
goto unknown;
case BLE_GATT_ACCESS_OP_WRITE_CHR:
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
MODLOG_DFLT(INFO, "Characteristic write; conn_handle=%d attr_handle=%d",
conn_handle, attr_handle);
} else {
MODLOG_DFLT(INFO, "Characteristic write by NimBLE stack; attr_handle=%d",
attr_handle);
}
uuid = ctxt->chr->uuid;
if (attr_handle == gatt_svr_chr_val_handle) {
rc = gatt_svr_write(ctxt->om,
sizeof(gatt_svr_chr_val),
sizeof(gatt_svr_chr_val),
&gatt_svr_chr_val, NULL);
ble_gatts_chr_updated(attr_handle);
MODLOG_DFLT(INFO, "Notification/Indication scheduled for "
"all subscribed peers.\n");
return rc;
}
goto unknown;
case BLE_GATT_ACCESS_OP_READ_DSC:
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
MODLOG_DFLT(INFO, "Descriptor read; conn_handle=%d attr_handle=%d\n",
conn_handle, attr_handle);
} else {
MODLOG_DFLT(INFO, "Descriptor read by NimBLE stack; attr_handle=%d\n",
attr_handle);
}
uuid = ctxt->dsc->uuid;
if (ble_uuid_cmp(uuid, &gatt_svr_dsc_uuid.u) == 0) {
rc = os_mbuf_append(ctxt->om,
&gatt_svr_dsc_val,
sizeof(gatt_svr_chr_val));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
goto unknown;
case BLE_GATT_ACCESS_OP_WRITE_DSC:
goto unknown;
default:
goto unknown;
}
unknown:
/* Unknown characteristic/descriptor;
* The NimBLE host should not have called this function;
*/
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
void
gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
{
char buf[BLE_UUID_STR_LEN];
switch (ctxt->op) {
case BLE_GATT_REGISTER_OP_SVC:
MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n",
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
ctxt->svc.handle);
break;
case BLE_GATT_REGISTER_OP_CHR:
MODLOG_DFLT(DEBUG, "registering characteristic %s with "
"def_handle=%d val_handle=%d\n",
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
ctxt->chr.def_handle,
ctxt->chr.val_handle);
break;
case BLE_GATT_REGISTER_OP_DSC:
MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n",
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
ctxt->dsc.handle);
break;
default:
assert(0);
break;
}
}
int
gatt_svr_init(void)
{
int rc;
ble_svc_gap_init();
ble_svc_gatt_init();
rc = ble_gatts_count_cfg(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
rc = ble_gatts_add_svcs(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
/* Setting a value for the read-only descriptor */
gatt_svr_dsc_val = 0x99;
return 0;
}

View File

@ -0,0 +1,420 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "esp_log.h"
#include "nvs_flash.h"
/* BLE */
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "console/console.h"
#include "services/gap/ble_svc_gap.h"
#include "enc_adv_data_prph.h"
#if CONFIG_EXAMPLE_ENC_ADV_DATA
static uint8_t km_adv_pattern_1[] = {
0x02, 0x01, 0x06,
0x03, 0x03, 0x2C, 0x01,
0x04, 0X09, 'k', 'e', 'y',
};
static const char *tag = "ENC_ADV_DATA_PRPH";
static int enc_adv_data_prph_gap_event(struct ble_gap_event *event, void *arg);
static uint8_t ext_adv_pattern_1[] = {
0x02, 0x01, 0x06,
0x03, 0x03, 0x2C, 0x00,
0x05, 0X09, 'p', 'r', 'p', 'h',
};
struct key_material km = {
.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},
};
#if CONFIG_EXAMPLE_RANDOM_ADDR
static uint8_t own_addr_type = BLE_OWN_ADDR_RANDOM;
#else
static uint8_t own_addr_type;
#endif
void ble_store_config_init(void);
/**
* Logs information about a connection to the console.
*/
static void
enc_adv_data_prph_print_conn_desc(struct ble_gap_conn_desc *desc)
{
MODLOG_DFLT(INFO, "handle=%d our_ota_addr_type=%d our_ota_addr=",
desc->conn_handle, desc->our_ota_addr.type);
print_addr(desc->our_ota_addr.val);
MODLOG_DFLT(INFO, " our_id_addr_type=%d our_id_addr=",
desc->our_id_addr.type);
print_addr(desc->our_id_addr.val);
MODLOG_DFLT(INFO, " peer_ota_addr_type=%d peer_ota_addr=",
desc->peer_ota_addr.type);
print_addr(desc->peer_ota_addr.val);
MODLOG_DFLT(INFO, " peer_id_addr_type=%d peer_id_addr=",
desc->peer_id_addr.type);
print_addr(desc->peer_id_addr.val);
MODLOG_DFLT(INFO, " conn_itvl=%d conn_latency=%d supervision_timeout=%d "
"encrypted=%d authenticated=%d bonded=%d\n",
desc->conn_itvl, desc->conn_latency,
desc->supervision_timeout,
desc->sec_state.encrypted,
desc->sec_state.authenticated,
desc->sec_state.bonded);
}
static const struct enc_adv_data ead[] = {
ENC_ADV_DATA(BLE_GAP_ENC_ADV_DATA, ext_adv_pattern_1, sizeof(ext_adv_pattern_1)),
};
static void enc_adv_data_prph_encrypt_set(uint8_t instance, struct os_mbuf *data)
{
int rc;
uint8_t enc_data_flag = BLE_GAP_ENC_ADV_DATA; //0x31
uint8_t ext_adv_pattern_sz = ead[0].len;
size_t adv_data_sz = BLE_GAP_DATA_SERIALIZED_SIZE(ext_adv_pattern_sz);
uint8_t adv_data[adv_data_sz];
size_t enc_adv_data_sz = BLE_EAD_ENCRYPTED_PAYLOAD_SIZE(adv_data_sz);
uint8_t enc_adv_data[enc_adv_data_sz];
ble_ead_serialize_data(&ead[0], adv_data);
MODLOG_DFLT(DEBUG, "Data before encryption:");
print_bytes(adv_data, adv_data_sz);
MODLOG_DFLT(DEBUG, "\n");
rc = ble_ead_encrypt(km.session_key, km.iv, adv_data, adv_data_sz, enc_adv_data);
if (rc == 0) {
MODLOG_DFLT(INFO, "Encryption of adv data done successfully");
} else {
MODLOG_DFLT(INFO, "Encryption of adv data failed");
return;
}
MODLOG_DFLT(DEBUG, "Data after encryption:");
print_bytes(enc_adv_data, enc_adv_data_sz);
MODLOG_DFLT(DEBUG, "\n");
//Copying encrypted data
rc = os_mbuf_append(data, &enc_adv_data_sz, sizeof(uint8_t));
rc = os_mbuf_append(data, &enc_data_flag, sizeof(uint8_t));
rc = os_mbuf_append(data, enc_adv_data, enc_adv_data_sz);
assert(rc == 0);
MODLOG_DFLT(INFO, "Advertising data:");
print_mbuf(data);
}
/**
* Enables advertising with the following parameters:
* o General discoverable mode.
* o Undirected connectable mode.
*/
static void
ext_enc_adv_data_prph_advertise(void)
{
struct ble_gap_ext_adv_params params;
uint8_t instance = 0;
int rc;
struct os_mbuf *data;
/* First check if any instance is already active */
if (ble_gap_ext_adv_active(instance)) {
return;
}
/* use defaults for non-set params */
memset (&params, 0, sizeof(params));
/* enable connectable advertising */
params.connectable = 1;
/* advertise using random addr */
params.own_addr_type = BLE_OWN_ADDR_PUBLIC;
params.primary_phy = BLE_HCI_LE_PHY_1M;
params.secondary_phy = BLE_HCI_LE_PHY_2M;
//params.tx_power = 127;
params.sid = 1;
params.itvl_min = BLE_GAP_ADV_FAST_INTERVAL1_MIN;
params.itvl_max = BLE_GAP_ADV_FAST_INTERVAL1_MIN;
/* configure instance 0 */
rc = ble_gap_ext_adv_configure(instance, &params, NULL,
enc_adv_data_prph_gap_event, NULL);
assert (rc == 0);
/* in this case only scan response is allowed */
/* get mbuf for scan rsp data */
data = os_msys_get_pkthdr(sizeof(km_adv_pattern_1), 0);
assert(data);
rc = os_mbuf_append(data, km_adv_pattern_1, sizeof(km_adv_pattern_1));
assert(rc == 0);
//Encrypted advertising data
enc_adv_data_prph_encrypt_set(instance, data);
rc = ble_gap_ext_adv_set_data(instance, data);
assert (rc == 0);
/* start advertising */
rc = ble_gap_ext_adv_start(instance, 0, 0);
assert (rc == 0);
}
/**
* The nimble host executes this callback when a GAP event occurs. The
* application associates a GAP event callback with each connection that forms.
* enc_adv_data_prph uses the same callback for all connections.
*
* @param event The type of event being signalled.
* @param ctxt Various information pertaining to the event.
* @param arg Application-specified argument; unused by
* enc_adv_data_prph.
*
* @return 0 if the application successfully handled the
* event; nonzero on failure. The semantics
* of the return code is specific to the
* particular GAP event being signalled.
*/
static int
enc_adv_data_prph_gap_event(struct ble_gap_event *event, void *arg)
{
struct ble_gap_conn_desc desc;
int rc;
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
/* A new connection was established or a connection attempt failed. */
MODLOG_DFLT(INFO, "connection %s; status=%d ",
event->connect.status == 0 ? "established" : "failed",
event->connect.status);
if (event->connect.status == 0) {
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
enc_adv_data_prph_print_conn_desc(&desc);
}
MODLOG_DFLT(INFO, "\n");
if (event->connect.status != 0) {
/* Connection failed; resume advertising. */
ext_enc_adv_data_prph_advertise();
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason);
enc_adv_data_prph_print_conn_desc(&event->disconnect.conn);
MODLOG_DFLT(INFO, "\n");
/* Connection terminated; resume advertising. */
ext_enc_adv_data_prph_advertise();
return 0;
case BLE_GAP_EVENT_CONN_UPDATE:
/* The central has updated the connection parameters. */
MODLOG_DFLT(INFO, "connection updated; status=%d ",
event->conn_update.status);
rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc);
assert(rc == 0);
enc_adv_data_prph_print_conn_desc(&desc);
MODLOG_DFLT(INFO, "\n");
return 0;
case BLE_GAP_EVENT_ADV_COMPLETE:
MODLOG_DFLT(INFO, "advertise complete; reason=%d",
event->adv_complete.reason);
return 0;
case BLE_GAP_EVENT_NOTIFY_TX:
MODLOG_DFLT(INFO, "notify_tx event; conn_handle=%d attr_handle=%d "
"status=%d is_indication=%d",
event->notify_tx.conn_handle,
event->notify_tx.attr_handle,
event->notify_tx.status,
event->notify_tx.indication);
return 0;
case BLE_GAP_EVENT_SUBSCRIBE:
MODLOG_DFLT(INFO, "subscribe event; conn_handle=%d attr_handle=%d "
"reason=%d prevn=%d curn=%d previ=%d curi=%d\n",
event->subscribe.conn_handle,
event->subscribe.attr_handle,
event->subscribe.reason,
event->subscribe.prev_notify,
event->subscribe.cur_notify,
event->subscribe.prev_indicate,
event->subscribe.cur_indicate);
return 0;
case BLE_GAP_EVENT_MTU:
MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n",
event->mtu.conn_handle,
event->mtu.channel_id,
event->mtu.value);
return 0;
}
return 0;
}
static void
enc_adv_data_prph_on_reset(int reason)
{
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
}
#if CONFIG_EXAMPLE_RANDOM_ADDR
static void
ble_app_set_addr(void)
{
ble_addr_t addr;
int rc;
/* generate new non-resolvable private address */
rc = ble_hs_id_gen_rnd(0, &addr);
assert(rc == 0);
/* set generated address */
rc = ble_hs_id_set_rnd(addr.val);
assert(rc == 0);
}
#endif
static void
enc_adv_data_prph_on_sync(void)
{
int rc;
#if CONFIG_EXAMPLE_RANDOM_ADDR
/* Generate a non-resolvable private address. */
ble_app_set_addr();
#endif
/* Make sure we have proper identity address set (public preferred) */
#if CONFIG_EXAMPLE_RANDOM_ADDR
rc = ble_hs_util_ensure_addr(1);
#else
rc = ble_hs_util_ensure_addr(0);
#endif
assert(rc == 0);
/* Figure out address to use while advertising (no privacy for now) */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
return;
}
/* Printing ADDR */
uint8_t addr_val[6] = {0};
rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
MODLOG_DFLT(INFO, "Device Address: ");
print_addr(addr_val);
MODLOG_DFLT(INFO, "\n");
/* Begin advertising. */
ext_enc_adv_data_prph_advertise();
}
void enc_adv_data_prph_host_task(void *param)
{
MODLOG_DFLT(INFO, "BLE Host Task Started");
/* This function will return only when nimble_port_stop() is executed */
nimble_port_run();
nimble_port_freertos_deinit();
}
void
app_main(void)
{
int rc;
/* Initialize NVS — it is used to store PHY calibration data */
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ret = nimble_port_init();
if (ret != ESP_OK) {
ESP_LOGE(tag, "Failed to init nimble %d ", ret);
return;
}
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = enc_adv_data_prph_on_reset;
ble_hs_cfg.sync_cb = enc_adv_data_prph_on_sync;
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
ble_hs_cfg.sm_io_cap = CONFIG_EXAMPLE_IO_TYPE;
#ifdef CONFIG_EXAMPLE_BONDING
ble_hs_cfg.sm_bonding = 1;
/* Enable the appropriate bit masks to make sure the keys
* that are needed are exchanged
*/
ble_hs_cfg.sm_our_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC;
ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC;
#endif
ble_hs_cfg.sm_mitm = 1;
#ifdef CONFIG_EXAMPLE_USE_SC
ble_hs_cfg.sm_sc = 1;
#else
ble_hs_cfg.sm_sc = 0;
#endif
#ifdef CONFIG_EXAMPLE_RESOLVE_PEER_ADDR
/* Stores the IRK */
ble_hs_cfg.sm_our_key_dist |= BLE_SM_PAIR_KEY_DIST_ID;
ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ID;
#endif
rc = gatt_svr_init();
assert(rc == 0);
/* Set the default device name. */
rc = ble_svc_gap_device_name_set("enc_adv_data_prph");
assert(rc == 0);
/* Set the session key and initialization vector */
rc = ble_svc_gap_device_key_material_set(km.session_key, km.iv);
assert(rc == 0);
/* XXX Need to have template for store */
ble_store_config_init();
nimble_port_freertos_init(enc_adv_data_prph_host_task);
}
#else
void
app_main(void)
{
return;
}
#endif

View File

@ -0,0 +1,14 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y
CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70
CONFIG_BT_NIMBLE_EXT_ADV=y

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -12,6 +12,8 @@
extern "C" {
#endif
#define PEER_ADDR_VAL_SIZE 6
/** Misc. */
void print_bytes(const uint8_t *bytes, int len);
void print_mbuf(const struct os_mbuf *om);
@ -48,11 +50,22 @@ SLIST_HEAD(peer_svc_list, peer_svc);
struct peer;
typedef void peer_disc_fn(const struct peer *peer, int status, void *arg);
/**
* @brief The callback function for the devices traversal.
*
* @param peer
* @param arg
* @return int 0, continue; Others, stop the traversal.
*
*/
typedef int peer_traverse_fn(const struct peer *peer, void *arg);
struct peer {
SLIST_ENTRY(peer) next;
uint16_t conn_handle;
uint8_t peer_addr[PEER_ADDR_VAL_SIZE];
/** List of discovered GATT services. */
struct peer_svc_list svcs;
@ -65,6 +78,8 @@ struct peer {
void *disc_cb_arg;
};
void peer_traverse_all(peer_traverse_fn *trav_cb, void *arg);
int peer_disc_all(uint16_t conn_handle, peer_disc_fn *disc_cb,
void *disc_cb_arg);
const struct peer_dsc *
@ -80,8 +95,9 @@ int peer_add(uint16_t conn_handle);
int peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs);
struct peer *
peer_find(uint16_t conn_handle);
#if MYNEWT_VAL(ENC_ADV_DATA)
int peer_set_addr(uint16_t conn_handle, uint8_t *peer_addr);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -88,7 +88,7 @@ print_conn_desc(const struct ble_gap_conn_desc *desc)
desc->sec_state.bonded);
}
#if CONFIG_EXAMPLE_EXTENDED_ADV
#if MYNEWT_VAL(BLE_EXT_ADV)
void
print_addr(const void *addr, const char *name)
{

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -702,6 +702,38 @@ peer_add(uint16_t conn_handle)
return 0;
}
void
peer_traverse_all(peer_traverse_fn *trav_cb, void *arg)
{
struct peer *peer;
if (!trav_cb) {
return;
}
SLIST_FOREACH(peer, &peers, next) {
if (trav_cb(peer, arg)) {
return;
}
}
}
#if MYNEWT_VAL(ENC_ADV_DATA)
int
peer_set_addr(uint16_t conn_handle, uint8_t *peer_addr)
{
struct peer *peer;
peer = peer_find(conn_handle);
if (peer == NULL) {
return BLE_HS_ENOTCONN;
}
memcpy(&peer->peer_addr, peer_addr, PEER_ADDR_VAL_SIZE);
return 0;
}
#endif
static void
peer_free_mem(void)
{

View File

@ -21,6 +21,8 @@ int scli_receive_key(int *key);
/** Misc. */
void print_bytes(const uint8_t *bytes, int len);
void print_addr(const void *addr);
char *addr_str(const void *addr);
void print_mbuf(const struct os_mbuf *om);
#ifdef __cplusplus
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -28,3 +28,35 @@ print_addr(const void *addr)
MODLOG_DFLT(INFO, "%02x:%02x:%02x:%02x:%02x:%02x",
u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
}
char *
addr_str(const void *addr)
{
static char buf[6 * 2 + 5 + 1];
const uint8_t *u8p;
u8p = addr;
sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
return buf;
}
void
print_mbuf(const struct os_mbuf *om)
{
int colon, i;
colon = 0;
while (om != NULL) {
if (colon) {
MODLOG_DFLT(DEBUG, ":");
} else {
colon = 1;
}
for (i = 0; i < om->om_len; i++) {
MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", om->om_data[i]);
}
om = SLIST_NEXT(om, om_next);
}
}