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");
3643static boolean doConnect = false ;
3744static boolean connected = false ;
3845static boolean doScan = false ;
46+ static BLEClient *pClient = nullptr ;
3947static BLERemoteCharacteristic *pRemoteInsecureCharacteristic;
4048static BLERemoteCharacteristic *pRemoteSecureCharacteristic;
4149static 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
44100static 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+ 65140bool 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.
0 commit comments