Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BLE Pairing and Encryption #156

Merged
merged 23 commits into from May 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,265 @@
/*
Battery Monitor

This example creates a BLE peripheral with the standard battery service and
level characteristic. The A0 pin is used to calculate the battery level.

The circuit:
- Arduino MKR WiFi 1010, Arduino Uno WiFi Rev2 board, Arduino Nano 33 IoT,
Arduino Nano 33 BLE, or Arduino Nano 33 BLE Sense board.

You can use a generic BLE central app, like LightBlue (iOS and Android) or
nRF Connect (Android), to interact with the services and characteristics
created in this sketch.

This example code is in the public domain.
*/

#include <ArduinoBLE.h>


#define PAIR_BUTTON 3 // button for pairing
#define PAIR_LED 24 // LED used to signal pairing
#define PAIR_LED_ON LOW // Blue LED on Nano BLE has inverted logic
#define PAIR_INTERVAL 30000 // interval for pairing after button press in ms

#define CTRL_LED LED_BUILTIN


// BLE Battery Service
BLEService batteryService("180F");

// BLE Battery Level Characteristic
BLEUnsignedCharCharacteristic batteryLevelChar("2A19", // standard 16-bit characteristic UUID
BLERead | BLENotify); // remote clients will be able to get notifications if this characteristic changes
BLEStringCharacteristic stringcharacteristic("183E", BLERead | BLEWrite, 31);


// Add BLEEncryption tag to require pairing. This controls the LED.
BLEUnsignedCharCharacteristic secretValue("2a3F", BLERead | BLEWrite | BLEEncryption);

int oldBatteryLevel = 0; // last battery level reading from analog input
unsigned long previousMillis = 0; // last time the battery level was checked, in ms
unsigned long pairingStarted = 0; // pairing start time when button is pressed
bool wasConnected = 0;
bool acceptOrReject = true;

void setup() {
Serial.begin(9600); // initialize serial communication
while (!Serial);

pinMode(CTRL_LED, OUTPUT); // initialize the built-in LED pin to indicate when a central is connected
pinMode(PAIR_LED, OUTPUT);
pinMode(PAIR_BUTTON, INPUT_PULLUP);


Serial.println("Serial connected");

// Callback function with confirmation code when new device is pairing.
BLE.setDisplayCode([](uint32_t confirmCode){
Serial.println("New device pairing request.");
Serial.print("Confirm code matches pairing device: ");
char code[6];
sprintf(code, "%06d", confirmCode);
Serial.println(code);
});

// Callback to allow accepting or rejecting pairing
BLE.setBinaryConfirmPairing([&acceptOrReject](){
Serial.print("Should we confirm pairing? ");
delay(5000);
if(acceptOrReject){
acceptOrReject = false;
Serial.println("yes");
return true;
}else{
acceptOrReject = true;
Serial.println("no");
return false;
}
});

// IRKs are keys that identify the true owner of a random mac address.
// Add IRKs of devices you are bonded with.
BLE.setGetIRKs([](uint8_t* nIRKs, uint8_t** BDaddrTypes, uint8_t*** BDAddrs, uint8_t*** IRKs){
// Set to number of devices
*nIRKs = 2;

*BDAddrs = new uint8_t*[*nIRKs];
*IRKs = new uint8_t*[*nIRKs];
*BDaddrTypes = new uint8_t[*nIRKs];

// Set these to the mac and IRK for your bonded devices as printed in the serial console after bonding.
uint8_t device1Mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t device1IRK[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

uint8_t device2Mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t device2IRK[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};


(*BDaddrTypes)[0] = 0; // Type 0 is for pubc address, type 1 is for static random
(*BDAddrs)[0] = new uint8_t[6];
(*IRKs)[0] = new uint8_t[16];
memcpy((*IRKs)[0] , device1IRK,16);
memcpy((*BDAddrs)[0], device1Mac, 6);


(*BDaddrTypes)[1] = 0;
(*BDAddrs)[1] = new uint8_t[6];
(*IRKs)[1] = new uint8_t[16];
memcpy((*IRKs)[1] , device2IRK,16);
memcpy((*BDAddrs)[1], device2Mac, 6);


return 1;
});
// The LTK is the secret key which is used to encrypt bluetooth traffic
BLE.setGetLTK([](uint8_t* address, uint8_t* LTK){
// address is input
Serial.print("Received request for address: ");
btct.printBytes(address,6);

// Set these to the MAC and LTK of your devices after bonding.
uint8_t device1Mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t device1LTK[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t device2Mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t device2LTK[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};


if(memcmp(device1Mac, address, 6) == 0) {
memcpy(LTK, device1LTK, 16);
return 1;
}else if(memcmp(device2Mac, address, 6) == 0) {
memcpy(LTK, device2LTK, 16);
return 1;
}
return 0;
});
BLE.setStoreIRK([](uint8_t* address, uint8_t* IRK){
Serial.print(F("New device with MAC : "));
btct.printBytes(address,6);
Serial.print(F("Need to store IRK : "));
btct.printBytes(IRK,16);
return 1;
});
BLE.setStoreLTK([](uint8_t* address, uint8_t* LTK){
Serial.print(F("New device with MAC : "));
btct.printBytes(address,6);
Serial.print(F("Need to store LTK : "));
btct.printBytes(LTK,16);
return 1;
});

while(1){
// begin initialization
if (!BLE.begin()) {
Serial.println("starting BLE failed!");
delay(200);
continue;
}
Serial.println("BT init");
delay(200);

/* Set a local name for the BLE device
This name will appear in advertising packets
and can be used by remote devices to identify this BLE device
The name can be changed but maybe be truncated based on space left in advertisement packet
*/

BLE.setDeviceName("Arduino");
BLE.setLocalName("BatteryMonitor");

BLE.setAdvertisedService(batteryService); // add the service UUID
batteryService.addCharacteristic(batteryLevelChar); // add the battery level characteristic
batteryService.addCharacteristic(stringcharacteristic);
batteryService.addCharacteristic(secretValue);

BLE.addService(batteryService); // Add the battery service
batteryLevelChar.writeValue(oldBatteryLevel); // set initial value for this characteristic
char* stringCharValue = new char[32];
stringCharValue = "string";
stringcharacteristic.writeValue(stringCharValue);
secretValue.writeValue(0);

delay(1000);

// prevent pairing until button is pressed (will show a pairing rejected message)
BLE.setPairable(false);

/* Start advertising BLE. It will start continuously transmitting BLE
advertising packets and will be visible to remote BLE central devices
until it receives a new connection */

// start advertising
if(!BLE.advertise()){
Serial.println("failed to advertise bluetooth.");
BLE.stopAdvertise();
delay(500);
}else{
Serial.println("advertising...");
break;
}
BLE.end();
delay(100);
}
}


void loop() {
// wait for a BLE central
BLEDevice central = BLE.central();


// If button is pressed, allow pairing for 30 sec
if (!BLE.pairable() && digitalRead(PAIR_BUTTON) == LOW){
pairingStarted = millis();
BLE.setPairable(Pairable::ONCE);
Serial.println("Accepting pairing for 30s");
} else if (BLE.pairable() && millis() > pairingStarted + PAIR_INTERVAL){
BLE.setPairable(false);
Serial.println("No longer accepting pairing");
}
// Make LED blink while pairing is allowed
digitalWrite(PAIR_LED, (BLE.pairable() ? (millis()%400)<200 : BLE.paired()) ? PAIR_LED_ON : !PAIR_LED_ON);


// if a central is connected to the peripheral:
if (central && central.connected()) {
if (!wasConnected){
wasConnected = true;
Serial.print("Connected to central: ");
// print the central's BT address:
Serial.println(central.address());
}

// check the battery level every 200ms
// while the central is connected:
long currentMillis = millis();
// if 200ms have passed, check the battery level:
if (currentMillis - previousMillis >= 1000) {
previousMillis = currentMillis;
updateBatteryLevel();
digitalWrite(CTRL_LED, secretValue.value()>0 ? HIGH : LOW);
}
} else if (wasConnected){
wasConnected = false;
Serial.print("Disconnected from central: ");
Serial.println(central.address());
}

}

void updateBatteryLevel() {
/* Read the current voltage level on the A0 analog input pin.
This is used here to simulate the charge level of a battery.
*/
int battery = analogRead(A0);
int batteryLevel = map(battery, 0, 1023, 0, 100);

if (batteryLevel != oldBatteryLevel) { // if the battery level has changed
// Serial.print("Battery Level % is now: "); // print it
// Serial.println(batteryLevel);
batteryLevelChar.writeValue(batteryLevel); // and update the battery level characteristic
oldBatteryLevel = batteryLevel; // save the level for next comparison
}
}
3 changes: 3 additions & 0 deletions keywords.txt
Expand Up @@ -77,9 +77,12 @@ setEventHandler KEYWORD2
setAdvertisingInterval KEYWORD2
setConnectionInterval KEYWORD2
setConnectable KEYWORD2
setPairable KEYWORD2
setTimeout KEYWORD2
debug KEYWORD2
noDebug KEYWORD2
pairable KEYWORD2
paired KEYWORD2

properties KEYWORD2
valueSize KEYWORD2
Expand Down
1 change: 1 addition & 0 deletions src/ArduinoBLE.h
Expand Up @@ -24,5 +24,6 @@
#include "BLEProperty.h"
#include "BLEStringCharacteristic.h"
#include "BLETypedCharacteristics.h"
#include "utility/btct.h"

#endif
8 changes: 4 additions & 4 deletions src/BLECharacteristic.cpp
Expand Up @@ -47,13 +47,13 @@ BLECharacteristic::BLECharacteristic(BLERemoteCharacteristic* remote) :
}
}

BLECharacteristic::BLECharacteristic(const char* uuid, uint8_t properties, int valueSize, bool fixedLength) :
BLECharacteristic(new BLELocalCharacteristic(uuid, properties, valueSize, fixedLength))
BLECharacteristic::BLECharacteristic(const char* uuid, uint16_t permissions, int valueSize, bool fixedLength) :
BLECharacteristic(new BLELocalCharacteristic(uuid, permissions, valueSize, fixedLength))
{
}

BLECharacteristic::BLECharacteristic(const char* uuid, uint8_t properties, const char* value) :
BLECharacteristic(new BLELocalCharacteristic(uuid, properties, value))
BLECharacteristic::BLECharacteristic(const char* uuid, uint16_t permissions, const char* value) :
BLECharacteristic(new BLELocalCharacteristic(uuid, permissions, value))
{
}

Expand Down
4 changes: 2 additions & 2 deletions src/BLECharacteristic.h
Expand Up @@ -45,8 +45,8 @@ class BLERemoteCharacteristic;
class BLECharacteristic {
public:
BLECharacteristic();
BLECharacteristic(const char* uuid, uint8_t properties, int valueSize, bool fixedLength = false);
BLECharacteristic(const char* uuid, uint8_t properties, const char* value);
BLECharacteristic(const char* uuid, uint16_t permissions, int valueSize, bool fixedLength = false);
BLECharacteristic(const char* uuid, uint16_t permissions, const char* value);
BLECharacteristic(const BLECharacteristic& other);
virtual ~BLECharacteristic();

Expand Down
49 changes: 48 additions & 1 deletion src/BLEProperty.h
Expand Up @@ -17,6 +17,8 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

// #include <stdint.h>

#ifndef _BLE_PROPERTY_H_
#define _BLE_PROPERTY_H_

Expand All @@ -26,7 +28,52 @@ enum BLEProperty {
BLEWriteWithoutResponse = 0x04,
BLEWrite = 0x08,
BLENotify = 0x10,
BLEIndicate = 0x20
BLEIndicate = 0x20,
BLEAuthSignedWrite = 1 << 6,
BLEExtProp = 1 << 7,
};

enum BLEPermission {
BLEEncryption = 1 << 9,
BLEAuthentication = 1 << 10,
BLEAuthorization = 1 << 11,
// BLEWriteEncryption = 1 << 11,
// BLEWriteAuthentication = 1 << 12,
// BLEWriteAuthorization = 1 << 13,
};

#define ESP_GATT_CHAR_PROP_BIT_BROADCAST (1 << 0) /* 0x01 */ /* relate to BTA_GATT_CHAR_PROP_BIT_BROADCAST in bta/bta_gatt_api.h */
#define ESP_GATT_CHAR_PROP_BIT_READ (1 << 1) /* 0x02 */ /* relate to BTA_GATT_CHAR_PROP_BIT_READ in bta/bta_gatt_api.h */
#define ESP_GATT_CHAR_PROP_BIT_WRITE_NR (1 << 2) /* 0x04 */ /* relate to BTA_GATT_CHAR_PROP_BIT_WRITE_NR in bta/bta_gatt_api.h */
#define ESP_GATT_CHAR_PROP_BIT_WRITE (1 << 3) /* 0x08 */ /* relate to BTA_GATT_CHAR_PROP_BIT_WRITE in bta/bta_gatt_api.h */
#define ESP_GATT_CHAR_PROP_BIT_NOTIFY (1 << 4) /* 0x10 */ /* relate to BTA_GATT_CHAR_PROP_BIT_NOTIFY in bta/bta_gatt_api.h */
#define ESP_GATT_CHAR_PROP_BIT_INDICATE (1 << 5) /* 0x20 */ /* relate to BTA_GATT_CHAR_PROP_BIT_INDICATE in bta/bta_gatt_api.h */
#define ESP_GATT_CHAR_PROP_BIT_AUTH (1 << 6) /* 0x40 */ /* relate to BTA_GATT_CHAR_PROP_BIT_AUTH in bta/bta_gatt_api.h */
#define ESP_GATT_CHAR_PROP_BIT_EXT_PROP (1 << 7) /* 0x80 */ /* relate to BTA_GATT_CHAR_PROP_BIT_EXT_PROP in bta/bta_gatt_api.h */

#define ESP_GATT_PERM_READ (1 << 0) /* bit 0 - 0x0001 */ /* relate to BTA_GATT_PERM_READ in bta/bta_gatt_api.h */
#define ESP_GATT_PERM_READ_ENCRYPTED (1 << 1) /* bit 1 - 0x0002 */ /* relate to BTA_GATT_PERM_READ_ENCRYPTED in bta/bta_gatt_api.h */
#define ESP_GATT_PERM_READ_ENC_MITM (1 << 2) /* bit 2 - 0x0004 */ /* relate to BTA_GATT_PERM_READ_ENC_MITM in bta/bta_gatt_api.h */
#define ESP_GATT_PERM_WRITE (1 << 4) /* bit 4 - 0x0010 */ /* relate to BTA_GATT_PERM_WRITE in bta/bta_gatt_api.h */
#define ESP_GATT_PERM_WRITE_ENCRYPTED (1 << 5) /* bit 5 - 0x0020 */ /* relate to BTA_GATT_PERM_WRITE_ENCRYPTED in bta/bta_gatt_api.h */
#define ESP_GATT_PERM_WRITE_ENC_MITM (1 << 6) /* bit 6 - 0x0040 */ /* relate to BTA_GATT_PERM_WRITE_ENC_MITM in bta/bta_gatt_api.h */
#define ESP_GATT_PERM_WRITE_SIGNED (1 << 7) /* bit 7 - 0x0080 */ /* relate to BTA_GATT_PERM_WRITE_SIGNED in bta/bta_gatt_api.h */
#define ESP_GATT_PERM_WRITE_SIGNED_MITM (1 << 8) /* bit 8 - 0x0100 */ /* relate to BTA_GATT_PERM_WRITE_SIGNED_MITM in bta/bta_gatt_api.h */
#define ESP_GATT_PERM_READ_AUTHORIZATION (1 << 9) /* bit 9 - 0x0200 */
#define ESP_GATT_PERM_WRITE_AUTHORIZATION (1 << 10) /* bit 10 - 0x0400 */

enum BLE_GATT_PERM_ {
READ = 1 << 0,
READ_ENCRYPTED = 1 << 1,
READ_ENC_MITM = 1 << 2,
WRITE = 1 << 4,
WRITE_ENCRYPTED = 1 << 5,
WRITE_ENC_MITM = 1 << 6,
WRITE_SIGNED = 1 << 7,
WRITE_SIGNED_MITM = 1 << 8,
READ_AUTHORIZATION = 1 << 9,
WRITE_AUTHORIZATION = 1 << 10,
};


#endif
6 changes: 3 additions & 3 deletions src/BLETypedCharacteristic.h
Expand Up @@ -25,7 +25,7 @@
template<typename T> class BLETypedCharacteristic : public BLECharacteristic
{
public:
BLETypedCharacteristic(const char* uuid, unsigned char properties);
BLETypedCharacteristic(const char* uuid, unsigned int permissions);

int writeValue(T value);
int setValue(T value) { return writeValue(value); }
Expand All @@ -43,8 +43,8 @@ template<typename T> class BLETypedCharacteristic : public BLECharacteristic
T byteSwap(T value);
};

template<typename T> BLETypedCharacteristic<T>::BLETypedCharacteristic(const char* uuid, unsigned char properties) :
BLECharacteristic(uuid, properties, sizeof(T), true)
template<typename T> BLETypedCharacteristic<T>::BLETypedCharacteristic(const char* uuid, unsigned int permissions) :
BLECharacteristic(uuid, permissions, sizeof(T), true)
{
T value;
memset(&value, 0x00, sizeof(value));
Expand Down