Skip to content

Commit 4852eb9

Browse files
committed
feat(irk): Add peer's IRK retrieval methods
1 parent 5aa4a48 commit 4852eb9

File tree

4 files changed

+338
-9
lines changed

4 files changed

+338
-9
lines changed

libraries/BLE/examples/Client_secure_static_passkey/Client_secure_static_passkey.ino

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
/*
2-
Secure client with static passkey
2+
Secure client with static passkey and IRK retrieval
33
44
This example demonstrates how to create a secure BLE client that connects to
55
a secure BLE server using a static passkey without prompting the user.
66
The client will automatically use the same passkey (123456) as the server.
77
8+
After successful bonding, the example demonstrates how to retrieve the
9+
server's Identity Resolving Key (IRK) in multiple formats:
10+
- Comma-separated hex format: 0x1A,0x1B,0x1C,...
11+
- Base64 encoded (for Home Assistant Private BLE Device service)
12+
- Reverse hex order (for Home Assistant ESPresense)
13+
814
This client is designed to work with the Server_secure_static_passkey example.
915
1016
Note that ESP32 uses Bluedroid by default and the other SoCs use NimBLE.
1117
Bluedroid initiates security on-connect, while NimBLE initiates security on-demand.
1218
This means that in NimBLE you can read the insecure characteristic without entering
1319
the passkey. This is not possible in Bluedroid.
1420
15-
IMPORTANT: MITM (Man-In-The-Middle protection) must be enabled for password prompts
16-
to work. Without MITM, the BLE stack assumes no user interaction is needed and will use
17-
"Just Works" pairing method (with encryption if secure connection is enabled).
21+
IMPORTANT:
22+
- MITM (Man-In-The-Middle protection) must be enabled for password prompts to work.
23+
- Bonding must be enabled to store and retrieve the IRK.
24+
- The server must distribute its Identity Key during pairing.
1825
1926
Based on examples from Neil Kolban and h2zero.
2027
Created by lucasssvaz.
@@ -36,10 +43,59 @@ static BLEUUID secureCharUUID("ff1d2614-e2d6-4c87-9154-6625d39ca7f8");
3643
static boolean doConnect = false;
3744
static boolean connected = false;
3845
static boolean doScan = false;
46+
static BLEClient *pClient = nullptr;
3947
static BLERemoteCharacteristic *pRemoteInsecureCharacteristic;
4048
static BLERemoteCharacteristic *pRemoteSecureCharacteristic;
4149
static BLEAdvertisedDevice *myDevice;
4250

51+
// Print an IRK buffer as hex with leading zeros and ':' separator
52+
static void printIrkBinary(uint8_t *irk) {
53+
for (int i = 0; i < 16; i++) {
54+
if (irk[i] < 0x10) {
55+
Serial.print("0");
56+
}
57+
Serial.print(irk[i], HEX);
58+
if (i < 15) {
59+
Serial.print(":");
60+
}
61+
}
62+
}
63+
64+
static void get_peer_irk(BLEAddress peerAddr) {
65+
Serial.println("\n=== Retrieving peer IRK (Server) ===\n");
66+
67+
uint8_t irk[16];
68+
69+
// Get IRK in binary format
70+
if (BLEDevice::getPeerIRK(peerAddr, irk)) {
71+
Serial.println("Successfully retrieved peer IRK in binary format:");
72+
printIrkBinary(irk);
73+
Serial.println("\n");
74+
}
75+
76+
// Get IRK in different string formats
77+
String irkString = BLEDevice::getPeerIRKString(peerAddr);
78+
String irkBase64 = BLEDevice::getPeerIRKBase64(peerAddr);
79+
String irkReverse = BLEDevice::getPeerIRKReverse(peerAddr);
80+
81+
if (irkString.length() > 0) {
82+
Serial.println("Successfully retrieved peer IRK in multiple formats:\n");
83+
Serial.print("IRK (comma-separated hex): ");
84+
Serial.println(irkString);
85+
Serial.print("IRK (Base64 for Home Assistant Private BLE Device): ");
86+
Serial.println(irkBase64);
87+
Serial.print("IRK (reverse hex for Home Assistant ESPresense): ");
88+
Serial.println(irkReverse);
89+
Serial.println();
90+
} else {
91+
Serial.println("!!! Failed to retrieve peer IRK !!!");
92+
Serial.println("This is expected if bonding is disabled or the peer doesn't distribute its Identity Key.");
93+
Serial.println("To enable bonding, change setAuthenticationMode to: pSecurity->setAuthenticationMode(true, true, true);\n");
94+
}
95+
96+
Serial.println("=======================================\n");
97+
}
98+
4399
// Callback function to handle notifications
44100
static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) {
45101
Serial.print("Notify callback for characteristic ");
@@ -62,11 +118,30 @@ class MyClientCallback : public BLEClientCallbacks {
62118
}
63119
};
64120

121+
// Security callbacks to print IRKs once authentication completes
122+
class MySecurityCallbacks : public BLESecurityCallbacks {
123+
#if defined(CONFIG_BLUEDROID_ENABLED)
124+
void onAuthenticationComplete(esp_ble_auth_cmpl_t desc) override {
125+
// Print the IRK received by the peer
126+
BLEAddress peerAddr(desc.bd_addr);
127+
get_peer_irk(peerAddr);
128+
}
129+
#endif
130+
131+
#if defined(CONFIG_NIMBLE_ENABLED)
132+
void onAuthenticationComplete(ble_gap_conn_desc *desc) override {
133+
// Print the IRK received by the peer
134+
BLEAddress peerAddr(desc->peer_id_addr.val, desc->peer_id_addr.type);
135+
get_peer_irk(peerAddr);
136+
}
137+
#endif
138+
};
139+
65140
bool connectToServer() {
66141
Serial.print("Forming a secure connection to ");
67142
Serial.println(myDevice->getAddress().toString().c_str());
68143

69-
BLEClient *pClient = BLEDevice::createClient();
144+
pClient = BLEDevice::createClient();
70145
Serial.println(" - Created client");
71146

72147
pClient->setClientCallbacks(new MyClientCallback());
@@ -192,15 +267,19 @@ void setup() {
192267
pSecurity->setPassKey(true, CLIENT_PIN);
193268

194269
// Set authentication mode to match server requirements
195-
// Enable secure connection and MITM (for password prompts) for this example
196-
pSecurity->setAuthenticationMode(false, true, true);
270+
// Enable bonding, MITM (for password prompts), and secure connection for this example
271+
// Bonding is required to store and retrieve the IRK
272+
pSecurity->setAuthenticationMode(true, true, true);
197273

198274
// Set IO capability to KeyboardOnly
199275
// We need the proper IO capability for MITM authentication even
200276
// if the passkey is static and won't be entered by the user
201277
// See https://www.bluetooth.com/blog/bluetooth-pairing-part-2-key-generation-methods/
202278
pSecurity->setCapability(ESP_IO_CAP_IN);
203279

280+
// Set callbacks to handle authentication completion and print IRKs
281+
BLEDevice::setSecurityCallbacks(new MySecurityCallbacks());
282+
204283
// Retrieve a Scanner and set the callback we want to use to be informed when we
205284
// have detected a new device. Specify that we want active scanning and start the
206285
// scan to run for 5 seconds.

libraries/BLE/examples/Server_secure_static_passkey/Server_secure_static_passkey.ino

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,73 @@
4040
// This is an example passkey. You should use a different or random passkey.
4141
#define SERVER_PIN 123456
4242

43+
// Print an IRK buffer as hex with leading zeros and ':' separator
44+
static void printIrkBinary(uint8_t *irk) {
45+
for (int i = 0; i < 16; i++) {
46+
if (irk[i] < 0x10) {
47+
Serial.print("0");
48+
}
49+
Serial.print(irk[i], HEX);
50+
if (i < 15) {
51+
Serial.print(":");
52+
}
53+
}
54+
}
55+
56+
static void get_peer_irk(BLEAddress peerAddr) {
57+
Serial.println("\n=== Retrieving peer IRK (Client) ===\n");
58+
59+
uint8_t irk[16];
60+
61+
// Get IRK in binary format
62+
if (BLEDevice::getPeerIRK(peerAddr, irk)) {
63+
Serial.println("Successfully retrieved peer IRK in binary format:");
64+
printIrkBinary(irk);
65+
Serial.println("\n");
66+
}
67+
68+
// Get IRK in different string formats
69+
String irkString = BLEDevice::getPeerIRKString(peerAddr);
70+
String irkBase64 = BLEDevice::getPeerIRKBase64(peerAddr);
71+
String irkReverse = BLEDevice::getPeerIRKReverse(peerAddr);
72+
73+
if (irkString.length() > 0) {
74+
Serial.println("Successfully retrieved peer IRK in multiple formats:\n");
75+
Serial.print("IRK (comma-separated hex): ");
76+
Serial.println(irkString);
77+
Serial.print("IRK (Base64 for Home Assistant Private BLE Device): ");
78+
Serial.println(irkBase64);
79+
Serial.print("IRK (reverse hex for Home Assistant ESPresense): ");
80+
Serial.println(irkReverse);
81+
Serial.println();
82+
} else {
83+
Serial.println("!!! Failed to retrieve peer IRK !!!");
84+
Serial.println("This is expected if bonding is disabled or the peer doesn't distribute its Identity Key.");
85+
Serial.println("To enable bonding, change setAuthenticationMode to: pSecurity->setAuthenticationMode(true, true, true);\n");
86+
}
87+
88+
Serial.println("=======================================\n");
89+
}
90+
91+
// Security callbacks to print IRKs once authentication completes
92+
class MySecurityCallbacks : public BLESecurityCallbacks {
93+
#if defined(CONFIG_BLUEDROID_ENABLED)
94+
void onAuthenticationComplete(esp_ble_auth_cmpl_t desc) override {
95+
// Print the IRK received by the peer
96+
BLEAddress peerAddr(desc.bd_addr);
97+
get_peer_irk(peerAddr);
98+
}
99+
#endif
100+
101+
#if defined(CONFIG_NIMBLE_ENABLED)
102+
void onAuthenticationComplete(ble_gap_conn_desc *desc) override {
103+
// Print the IRK received by the peer
104+
BLEAddress peerAddr(desc->peer_id_addr.val, desc->peer_id_addr.type);
105+
get_peer_irk(peerAddr);
106+
}
107+
#endif
108+
};
109+
43110
void setup() {
44111
Serial.begin(115200);
45112
Serial.println("Starting BLE work!");
@@ -76,8 +143,11 @@ void setup() {
76143
pSecurity->setCapability(ESP_IO_CAP_OUT);
77144

78145
// Set authentication mode
79-
// Require secure connection and MITM (for password prompts) for this example
80-
pSecurity->setAuthenticationMode(false, true, true);
146+
// Enable bonding, MITM (for password prompts), and secure connection for this example
147+
pSecurity->setAuthenticationMode(true, true, true);
148+
149+
// Set callbacks to handle authentication completion and print IRKs
150+
BLEDevice::setSecurityCallbacks(new MySecurityCallbacks());
81151

82152
BLEServer *pServer = BLEDevice::createServer();
83153
pServer->advertiseOnDisconnect(true);

0 commit comments

Comments
 (0)