Skip to content

Commit 3f549d3

Browse files
committed
Test IDF call
1 parent 8af12a5 commit 3f549d3

File tree

7 files changed

+512
-0
lines changed

7 files changed

+512
-0
lines changed
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
/*
2+
Secure client with static passkey
3+
4+
This example demonstrates how to create a secure BLE client that connects to
5+
a secure BLE server using a static passkey without prompting the user.
6+
The client will automatically use the same passkey (123456) as the server.
7+
8+
This client is designed to work with the Server_secure_static_passkey example.
9+
10+
Note that ESP32 uses Bluedroid by default and the other SoCs use NimBLE.
11+
Bluedroid initiates security on-connect, while NimBLE initiates security on-demand.
12+
This means that in NimBLE you can read the unsecure characteristic without entering
13+
the passkey. This is not possible in Bluedroid.
14+
15+
Also, the SoC stores the authentication info in the NVS memory. After a successful
16+
connection it is possible that a passkey change will be ineffective.
17+
To avoid this, clear the memory of the SoC's between security tests.
18+
19+
Based on examples from Neil Kolban and h2zero.
20+
Created by lucasssvaz.
21+
*/
22+
23+
#include "BLEDevice.h"
24+
#include "BLESecurity.h"
25+
26+
// The remote service we wish to connect to.
27+
static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
28+
// The characteristics of the remote service we are interested in.
29+
static BLEUUID unsecureCharUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8");
30+
static BLEUUID secureCharUUID("ff1d2614-e2d6-4c87-9154-6625d39ca7f8");
31+
32+
// This must match the server's passkey
33+
#define CLIENT_PIN 123456
34+
35+
static boolean doConnect = false;
36+
static boolean connected = false;
37+
static boolean doScan = false;
38+
static BLERemoteCharacteristic *pRemoteUnsecureCharacteristic;
39+
static BLERemoteCharacteristic *pRemoteSecureCharacteristic;
40+
static BLEAdvertisedDevice *myDevice;
41+
42+
// Callback function to handle notifications
43+
static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) {
44+
Serial.print("Notify callback for characteristic ");
45+
Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
46+
Serial.print(" of data length ");
47+
Serial.println(length);
48+
Serial.print("data: ");
49+
Serial.write(pData, length);
50+
Serial.println();
51+
}
52+
53+
class MyClientCallback : public BLEClientCallbacks {
54+
void onConnect(BLEClient *pclient) {
55+
Serial.println("Connected to secure server");
56+
}
57+
58+
void onDisconnect(BLEClient *pclient) {
59+
connected = false;
60+
Serial.println("Disconnected from server");
61+
}
62+
};
63+
64+
bool connectToServer() {
65+
Serial.print("Forming a secure connection to ");
66+
Serial.println(myDevice->getAddress().toString().c_str());
67+
68+
BLEClient *pClient = BLEDevice::createClient();
69+
Serial.println(" - Created client");
70+
71+
pClient->setClientCallbacks(new MyClientCallback());
72+
73+
// Connect to the remote BLE Server.
74+
pClient->connect(myDevice);
75+
Serial.println(" - Connected to server");
76+
77+
// Set MTU to maximum for better performance
78+
pClient->setMTU(517);
79+
80+
// Obtain a reference to the service we are after in the remote BLE server.
81+
BLERemoteService *pRemoteService = pClient->getService(serviceUUID);
82+
if (pRemoteService == nullptr) {
83+
Serial.print("Failed to find our service UUID: ");
84+
Serial.println(serviceUUID.toString().c_str());
85+
pClient->disconnect();
86+
return false;
87+
}
88+
Serial.println(" - Found our service");
89+
90+
// Obtain a reference to the unsecure characteristic
91+
pRemoteUnsecureCharacteristic = pRemoteService->getCharacteristic(unsecureCharUUID);
92+
if (pRemoteUnsecureCharacteristic == nullptr) {
93+
Serial.print("Failed to find unsecure characteristic UUID: ");
94+
Serial.println(unsecureCharUUID.toString().c_str());
95+
pClient->disconnect();
96+
return false;
97+
}
98+
Serial.println(" - Found unsecure characteristic");
99+
100+
// Obtain a reference to the secure characteristic
101+
pRemoteSecureCharacteristic = pRemoteService->getCharacteristic(secureCharUUID);
102+
if (pRemoteSecureCharacteristic == nullptr) {
103+
Serial.print("Failed to find secure characteristic UUID: ");
104+
Serial.println(secureCharUUID.toString().c_str());
105+
pClient->disconnect();
106+
return false;
107+
}
108+
Serial.println(" - Found secure characteristic");
109+
110+
// Read the value of the unsecure characteristic (should work without authentication)
111+
if (pRemoteUnsecureCharacteristic->canRead()) {
112+
String value = pRemoteUnsecureCharacteristic->readValue();
113+
Serial.print("Unsecure characteristic value: ");
114+
Serial.println(value.c_str());
115+
}
116+
117+
// For Bluedroid, we need to set the authentication request type for the secure characteristic
118+
// This is not needed for NimBLE and will be ignored.
119+
pRemoteSecureCharacteristic->setAuth(ESP_GATT_AUTH_REQ_MITM);
120+
121+
// Try to read the secure characteristic (this will trigger security negotiation in NimBLE)
122+
if (pRemoteSecureCharacteristic->canRead()) {
123+
Serial.println("Attempting to read secure characteristic...");
124+
String value = pRemoteSecureCharacteristic->readValue();
125+
Serial.print("Secure characteristic value: ");
126+
Serial.println(value.c_str());
127+
}
128+
129+
// Register for notifications on both characteristics if they support it
130+
if (pRemoteUnsecureCharacteristic->canNotify()) {
131+
pRemoteUnsecureCharacteristic->registerForNotify(notifyCallback);
132+
Serial.println(" - Registered for unsecure characteristic notifications");
133+
}
134+
135+
if (pRemoteSecureCharacteristic->canNotify()) {
136+
pRemoteSecureCharacteristic->registerForNotify(notifyCallback);
137+
Serial.println(" - Registered for secure characteristic notifications");
138+
}
139+
140+
connected = true;
141+
return true;
142+
}
143+
144+
/**
145+
* Scan for BLE servers and find the first one that advertises the service we are looking for.
146+
*/
147+
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
148+
/**
149+
* Called for each advertising BLE server.
150+
*/
151+
void onResult(BLEAdvertisedDevice advertisedDevice) {
152+
Serial.print("BLE Advertised Device found: ");
153+
Serial.println(advertisedDevice.toString().c_str());
154+
155+
// We have found a device, let us now see if it contains the service we are looking for.
156+
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
157+
Serial.println("Found our secure server!");
158+
BLEDevice::getScan()->stop();
159+
myDevice = new BLEAdvertisedDevice(advertisedDevice);
160+
doConnect = true;
161+
doScan = true;
162+
}
163+
}
164+
};
165+
166+
void setup() {
167+
Serial.begin(115200);
168+
Serial.println("Starting Secure BLE Client application...");
169+
170+
BLEDevice::init("Secure BLE Client");
171+
172+
// Set up security with the same passkey as the server
173+
BLESecurity *pSecurity = new BLESecurity();
174+
175+
// Set security parameters
176+
// Deafult parameters:
177+
// - IO capability is set to NONE
178+
// - Initiator and responder key distribution flags are set to both encryption and identity keys.
179+
// - Passkey is set to BLE_SM_DEFAULT_PASSKEY (123456). It will warn if you don't change it.
180+
// - Max key size is set to 16 bytes
181+
182+
// Set the same static passkey as the server
183+
// The first argument defines if the passkey is static or random.
184+
// The second argument is the passkey (ignored when using a random passkey).
185+
pSecurity->setPassKey(true, CLIENT_PIN);
186+
187+
// Set authentication mode to match server requirements
188+
// Enable bonding, with MITM and secure connection
189+
pSecurity->setAuthenticationMode(true, true, true);
190+
191+
// Retrieve a Scanner and set the callback we want to use to be informed when we
192+
// have detected a new device. Specify that we want active scanning and start the
193+
// scan to run for 5 seconds.
194+
BLEScan *pBLEScan = BLEDevice::getScan();
195+
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
196+
pBLEScan->setInterval(1349);
197+
pBLEScan->setWindow(449);
198+
pBLEScan->setActiveScan(true);
199+
pBLEScan->start(5, false);
200+
}
201+
202+
void loop() {
203+
// If the flag "doConnect" is true then we have scanned for and found the desired
204+
// BLE Server with which we wish to connect. Now we connect to it.
205+
if (doConnect == true) {
206+
if (connectToServer()) {
207+
Serial.println("We are now connected to the secure BLE Server.");
208+
} else {
209+
Serial.println("We have failed to connect to the server; there is nothing more we will do.");
210+
}
211+
doConnect = false;
212+
}
213+
214+
// If we are connected to a peer BLE Server, demonstrate secure communication
215+
if (connected) {
216+
// Write to the unsecure characteristic
217+
String unsecureValue = "Client time: " + String(millis() / 1000);
218+
if (pRemoteUnsecureCharacteristic->canWrite()) {
219+
pRemoteUnsecureCharacteristic->writeValue(unsecureValue.c_str(), unsecureValue.length());
220+
Serial.println("Wrote to unsecure characteristic: " + unsecureValue);
221+
}
222+
223+
// Write to the secure characteristic
224+
String secureValue = "Secure client time: " + String(millis() / 1000);
225+
if (pRemoteSecureCharacteristic->canWrite()) {
226+
pRemoteSecureCharacteristic->writeValue(secureValue.c_str(), secureValue.length());
227+
Serial.println("Wrote to secure characteristic: " + secureValue);
228+
}
229+
} else if (doScan) {
230+
// Restart scanning if we're disconnected
231+
BLEDevice::getScan()->start(0);
232+
}
233+
234+
delay(2000); // Delay 2 seconds between loops
235+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"fqbn_append": "PartitionScheme=huge_app",
3+
"requires": [
4+
"CONFIG_SOC_BLE_SUPPORTED=y"
5+
]
6+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
BLE Server Authorization Test
3+
4+
This example demonstrates how to use authorization in both NimBLE and Bluedroid.
5+
The server creates a service with two characteristics:
6+
- One characteristic that requires read/write authorization
7+
- One characteristic that does not require authorization
8+
9+
When authorization is required, the server will call the onAuthorizationRequest callback.
10+
This example shows a simple authorization logic that accepts requests from connection handle 0
11+
and rejects requests from all other connection handles.
12+
13+
In a real application, you would implement more sophisticated authorization logic
14+
based on your security requirements.
15+
16+
This example works with both NimBLE and Bluedroid implementations.
17+
18+
Created by lucasssvaz.
19+
*/
20+
21+
#include <BLEDevice.h>
22+
#include <BLEUtils.h>
23+
#include <BLEServer.h>
24+
#include <BLESecurity.h>
25+
#include <string>
26+
27+
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
28+
#define OPEN_CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
29+
#define AUTHORIZED_CHARACTERISTIC_UUID "ff1d2614-e2d6-4c87-9154-6625d39ca7f8"
30+
31+
class MySecurityCallbacks : public BLESecurityCallbacks {
32+
public:
33+
bool onAuthorizationRequest(uint16_t connHandle, uint16_t attrHandle, bool isRead) override {
34+
Serial.printf("Authorization request: conn_handle=%d, attr_handle=%d, is_read=%s\n",
35+
connHandle, attrHandle, isRead ? "true" : "false");
36+
37+
// Simple authorization logic: allow only connection handle 0
38+
// In a real application, you would implement more sophisticated logic
39+
bool authorized = (connHandle == 0);
40+
41+
Serial.printf("Authorization %s for connection handle %d\n",
42+
authorized ? "granted" : "denied", connHandle);
43+
44+
return authorized;
45+
}
46+
};
47+
48+
class MyCharacteristicCallbacks : public BLECharacteristicCallbacks {
49+
void onWrite(BLECharacteristic* pCharacteristic) override {
50+
String value = pCharacteristic->getValue();
51+
Serial.printf("Characteristic written: UUID=%s, value=%s\n",
52+
pCharacteristic->getUUID().toString().c_str(), value.c_str());
53+
}
54+
55+
void onRead(BLECharacteristic* pCharacteristic) override {
56+
Serial.printf("Characteristic read: UUID=%s\n",
57+
pCharacteristic->getUUID().toString().c_str());
58+
}
59+
};
60+
61+
void setup() {
62+
Serial.begin(115200);
63+
Serial.println("Starting BLE Authorization Test Server!");
64+
65+
Serial.printf("Using BLE stack: %s\n", BLEDevice::getBLEStackString());
66+
67+
BLEDevice::init("Authorization Test Server");
68+
69+
// Set security callbacks
70+
BLEDevice::setSecurityCallbacks(new MySecurityCallbacks());
71+
72+
// Create the BLE Server
73+
BLEServer* pServer = BLEDevice::createServer();
74+
75+
// Create the BLE Service
76+
BLEService* pService = pServer->createService(SERVICE_UUID);
77+
78+
// Create open characteristic (no authorization required)
79+
BLECharacteristic* pOpenCharacteristic = pService->createCharacteristic(
80+
OPEN_CHARACTERISTIC_UUID,
81+
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE
82+
);
83+
pOpenCharacteristic->setCallbacks(new MyCharacteristicCallbacks());
84+
pOpenCharacteristic->setValue("Open characteristic - no authorization required");
85+
86+
// Create authorized characteristic (authorization required for read/write)
87+
BLECharacteristic* pAuthorizedCharacteristic = pService->createCharacteristic(
88+
AUTHORIZED_CHARACTERISTIC_UUID,
89+
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE
90+
);
91+
pAuthorizedCharacteristic->setCallbacks(new MyCharacteristicCallbacks());
92+
pAuthorizedCharacteristic->setValue("Authorized characteristic - authorization required");
93+
94+
// Set authorization permissions for the authorized characteristic
95+
#if defined(CONFIG_BLUEDROID_ENABLED)
96+
pAuthorizedCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_AUTHORIZATION | ESP_GATT_PERM_WRITE_AUTHORIZATION);
97+
Serial.println("Bluedroid: Set authorization permissions using setAccessPermissions()");
98+
#endif
99+
100+
#if defined(CONFIG_NIMBLE_ENABLED)
101+
// In NimBLE, authorization is controlled by the characteristic properties
102+
// The PROPERTY_READ_AUTHOR and PROPERTY_WRITE_AUTHOR flags trigger authorization
103+
pAuthorizedCharacteristic = pService->createCharacteristic(
104+
AUTHORIZED_CHARACTERISTIC_UUID,
105+
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE |
106+
BLECharacteristic::PROPERTY_READ_AUTHOR | BLECharacteristic::PROPERTY_WRITE_AUTHOR
107+
);
108+
pAuthorizedCharacteristic->setCallbacks(new MyCharacteristicCallbacks());
109+
pAuthorizedCharacteristic->setValue("Authorized characteristic - authorization required");
110+
Serial.println("NimBLE: Set authorization permissions using PROPERTY_READ_AUTHOR and PROPERTY_WRITE_AUTHOR");
111+
#endif
112+
113+
// Start the service
114+
pService->start();
115+
116+
// Start advertising
117+
BLEAdvertising* pAdvertising = BLEDevice::getAdvertising();
118+
pAdvertising->addServiceUUID(SERVICE_UUID);
119+
pAdvertising->setScanResponse(false);
120+
pAdvertising->setMinPreferred(0x0); // Set value to 0x00 to not advertise this parameter
121+
122+
BLEDevice::startAdvertising();
123+
Serial.println("Waiting for client connection to test authorization...");
124+
Serial.println();
125+
Serial.println("Connect with a BLE client and try to:");
126+
Serial.println("1. Read/write the open characteristic (should work without authorization)");
127+
Serial.println("2. Read/write the authorized characteristic (will trigger authorization callback)");
128+
Serial.println();
129+
}
130+
131+
void loop() {
132+
// put your main code here, to run repeatedly:
133+
delay(2000);
134+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"targets": [
3+
"esp32",
4+
"esp32s3",
5+
"esp32c3",
6+
"esp32c6",
7+
"esp32h2"
8+
]
9+
}

0 commit comments

Comments
 (0)