Skip to content

A library for implementing secure Bluetooth Low Energy (BLE) pairing and encryption on Raspberry Pi Pico using the arduino-pico core.

License

Notifications You must be signed in to change notification settings

IoT-gamer/pico-ble-secure

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pico BLE Secure

A library for implementing secure Bluetooth Low Energy (BLE) pairing and encryption on Raspberry Pi Pico using the arduino-pico core.

Overview

The pico-ble-secure library provides an easy-to-use interface for implementing secure BLE connections on Raspberry Pi Pico microcontrollers. It supports various security levels, pairing methods, and bond management.

This library extends the functionality of the arduino-pico core's BTstack implementation to make secure BLE connections more accessible to developers.

Features

  • Multiple Security Levels:

    • LOW: No encryption, no authentication
    • MEDIUM: Encryption without MITM protection ("Just Works" pairing)
    • HIGH: Encryption with MITM protection
    • HIGH_SC: Encryption with MITM protection and Secure Connections (highest security)
  • Flexible Pairing Methods:

    • Just Works pairing
    • Passkey Display
    • Passkey Entry
    • Numeric Comparison
  • Bonding Support:

    • Store and manage paired device information
    • Reconnect to previously bonded devices
  • Callback System:

    • Receive notifications for all pairing events
    • Display pairing codes
    • Confirm pairing with numeric comparison
    • Automatic pairing management through connection callbacks

Installation

Using PlatformIO

  1. Add this to your platformio.ini file:
[env:rpipicow]
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
board = rpipicow
framework = arduino
board_build.core = earlephilhower
board_build.filesystem_size = 0.5m
build_flags = 
    -DPIO_FRAMEWORK_ARDUINO_ENABLE_BLUETOOTH
    -DPIO_FRAMEWORK_ARDUINO_ENABLE_IPV4
lib_deps = 
    pico-ble-secure
  1. PlatformIO will automatically download and install the library.

Manual Installation

  1. Download the library as a ZIP file
  2. In Arduino IDE: Sketch → Include Library → Add .ZIP Library
  3. Select the downloaded ZIP file

Requirements

Optional Hardware

  • I2C/SPI display for showing passkeys
  • Buttons for user input (e.g., confirming numeric comparison)
  • LEDs for visual feedback during pairing
    • Can use onboard LED
  • Keypad or other input methods for entering passkeys

Usage

Basic Setup

#include <Arduino.h>
#include <BTstackLib.h>
#include <BLESecure.h>

// Callbacks for BLE events
void bleDeviceConnected(BLEStatus status, BLEDevice* device) {
  if (status == BLE_STATUS_OK) {
    Serial.println("Device connected!");
    // No need to manually call BLESecure.requestPairing() here
    // It's automatically handled if requestPairingOnConnect is enabled
  }
}

void bleDeviceDisconnected(BLEDevice* device) {
  Serial.println("Device disconnected!");
}

void setup() {
  Serial.begin(115200);
  
  // Set device name
  BTstack.setup("SecureBLE");
  
  // Initialize BLE security with Numeric Comparison capability
  BLESecure.begin(IO_CAPABILITY_DISPLAY_YES_NO);
  
  // Set security level to HIGH - encryption with MITM protection
  BLESecure.setSecurityLevel(SECURITY_HIGH, true);
  
  // Request pairing automatically when a device connects
  BLESecure.requestPairingOnConnect(true);
  
  // Register connection callbacks through BLESecure (not BTstack)
  BLESecure.setBLEDeviceConnectedCallback(bleDeviceConnected);
  BLESecure.setBLEDeviceDisconnectedCallback(bleDeviceDisconnected);
  
  // Start advertising
  BTstack.startAdvertising();
}

void loop() {
  // Process BLE events
  BTstack.loop();
  
  delay(10);
}

Connection Management

Register connection and disconnection callbacks through BLESecure instead of directly through BTstack:

// Register callbacks through BLESecure for automatic pairing management
BLESecure.setBLEDeviceConnectedCallback(bleDeviceConnected);
BLESecure.setBLEDeviceDisconnectedCallback(bleDeviceDisconnected);

This allows the BLESecure library to automatically handle pairing requests based on your requestPairingOnConnect setting.

Setting Up Security Level

Choose the appropriate security level for your application:

// Just Works pairing (vulnerable to MITM attacks)
BLESecure.setSecurityLevel(SECURITY_MEDIUM, true);

// Secure pairing with MITM protection
BLESecure.setSecurityLevel(SECURITY_HIGH, true);

// Highest security with Secure Connections
BLESecure.setSecurityLevel(SECURITY_HIGH_SC, true);

Pairing Callbacks

// Callback for displaying passkey during pairing
void onPasskeyDisplay(uint32_t passkey) {
  Serial.print("Please enter this passkey on your device: ");
  Serial.println(passkey);
}

// Callback for numeric comparison during pairing
void onNumericComparison(uint32_t passkey, BLEDevice* device) {
  Serial.print("Do the following numbers match? ");
  Serial.println(passkey);
  
  // Get user confirmation (e.g., via button press)
  // Then accept or reject:
  BLESecure.acceptNumericComparison(true); // or false to reject
}

// Callback for pairing status updates
void onPairingStatus(BLEPairingStatus status, BLEDevice* device) {
  switch (status) {
    case PAIRING_IDLE:
      Serial.println("Pairing idle");
      break;
    case PAIRING_STARTED:
      Serial.println("Pairing started");
      break;
    case PAIRING_COMPLETE:
      Serial.println("Pairing complete - connection is now secure!");
      break;
    case PAIRING_FAILED:
      Serial.println("Pairing failed");
      break;
  }
}

void setup() {
  // ... other setup code ...
  
  // Register callbacks
  BLESecure.setPasskeyDisplayCallback(onPasskeyDisplay);
  BLESecure.setNumericComparisonCallback(onNumericComparison);
  BLESecure.setPairingStatusCallback(onPairingStatus);
}

Handling Re-encryption Failures

Problem

When a Pico device has been bonded with a central device (like a smartphone) and then the Pico is flashed with new firmware, the following error sequence may occur when attempting to reconnect:

Re-encryption started with bonded device
Pairing started
Re-encryption failed, status: 61
Pairing failed

This occurs because:

  1. The central device (smartphone) still has the bonding information
  2. The Pico has lost all bonding data after being flashed
  3. The central device attempts to use the old bonding keys to re-establish a secure connection
  4. The pairing fails because the Pico no longer recognizes these keys

Solutions

There are several approaches to handle this situation:

Option 1: Remove Bonding on Central Device

The simplest approach for development is to remove the bonded device from your smartphone/central device:

  • On Android: Go to Settings → Bluetooth → Previously Connected Devices → Tap the gear or arrow icon next to your device → "Forget" or "Unpair"
  • On iOS: Go to Settings → Bluetooth → Tap the "i" next to your device → "Forget This Device"

Option 2: Implement Persistent Bonding Storage

For production devices, consider implementing persistent storage of bonding information:

  • Store bonding keys in flash memory (e.g., using the LittleFS on Pico)
  • Restore bonding information after firmware updates
  • See the arduino-pico documentation for file system usage

Option 3: Clear Bond on Reconnection Failure

Implement automatic bond clearing after a failed reconnection attempt:

// In your onPairingStatus callback
void onPairingStatus(BLEPairingStatus status, BLEDevice *device) {
  switch (status) {
    case PAIRING_FAILED:
      Serial.println("Pairing failed - attempting to clear existing bond");
      // In a real application, you might want to:
      // 1. Request the central device to forget the bond
      // 2. Try a fresh pairing after a delay
      
      // For now, we can just disconnect to force a fresh connection
      if (device) {
        gap_disconnect(device->getHandle());
      }
      break;
    // Other cases...
  }
}

Recommendation for Development

During development, use Option 1 (removing the bond on the central device) for simplicity. For production devices, consider implementing a more robust solution with persistent storage (Option 2).

Important Note

This behavior is common in BLE development and not specific to this library. Any time bonding information is lost on either the peripheral or central device, re-encryption will fail, and the bond must be reestablished.

Migration Guide

Version 1.0.1 to 1.1.0

Version 1.1.0 introduces a new way to handle connection events and automatic pairing. Here are the key changes:

  1. Connection Callbacks: Register connection and disconnection callbacks through BLESecure instead of BTstack:

    // Old way:
    BTstack.setBLEDeviceConnectedCallback(bleDeviceConnected);
    BTstack.setBLEDeviceDisconnectedCallback(bleDeviceDisconnected);
    
    // New way:
    BLESecure.setBLEDeviceConnectedCallback(bleDeviceConnected);
    BLESecure.setBLEDeviceDisconnectedCallback(bleDeviceDisconnected);
  2. Automatic Pairing: When requestPairingOnConnect(true) is set, pairing is now automatically initiated when a device connects. You no longer need to manually check the flag and call requestPairing() in your connection callback:

    // Old way:
    void bleDeviceConnected(BLEStatus status, BLEDevice* device) {
      if (status == BLE_STATUS_OK) {
        // Manually check and request pairing
        if (BLESecure._requestPairingOnConnect) {
          BLESecure.requestPairing(device);
        }
      }
    }
    
    // New way:
    void bleDeviceConnected(BLEStatus status, BLEDevice* device) {
      if (status == BLE_STATUS_OK) {
        // Pairing is automatically handled if enabled
        // No manual check needed
      }
    }

These changes simplify the code required to implement secure BLE connections and ensure consistent behavior across applications.

Examples

The library includes several example platformIO projects demonstrating its usage:

  • SecurePairingMedium: Basic encryption without MITM protection using the "Just Works" method
  • SecurePairingHigh: Encryption with MITM protection using passkey or numeric comparison
  • SecurePairingHighSC: The highest security level using Secure Connections
  • ClearBondingTest: Clears bonding information in flash memory via BOOTSEL button press

Test with nRF Connect mobile app

  • connect pico-W to computer with USB
  • compile and upload the example platformio project
  • open Serial Monitor in platformio
  • open nRF Connect on your phone
  • scan for devices
  • connect to the Pico device
    • named either MediumSecBLE, HighSecBLE, or HighSCSecBLE
  • follow the prompts for pairing
    • on android, you may see two popups for pairing (click both)
  • for HighSecBLE and HighSCSecBLE, you will see a passkey on the Serial Monitor
    • enter the passkey on your phone before timeout
  • subscribe to the characteristic notifications to receive encrypted messages

drawing drawing drawing drawing

API Reference

Class: BLESecureClass

Initialization

  • void begin(io_capability_t ioCapability = IO_CAPABILITY_DISPLAY_YES_NO): Initialize the security manager with specified IO capability

Security Configuration

  • void setSecurityLevel(BLESecurityLevel level, bool enableBonding = true): Set the security level for connections
  • void allowReconnectionWithoutDatabaseEntry(bool allow): Allow LTK reconstruction without a device DB entry (for peripheral role)
  • void setFixedPasskey(uint32_t passkey): Set fixed passkey (0-999999) or NULL for random

Pairing Control

  • void requestPairingOnConnect(bool enable): Request pairing automatically when a device connects
  • bool requestPairing(BLEDevice* device): Manually request pairing with a connected device
  • bool bondWithDevice(BLEDevice* device): Bond with a device (store keys for reconnection)
  • bool removeBonding(BLEDevice* device): Remove bonding information for a device
  • void clearAllBondings(): Remove all stored bonding information

Connection Management

  • void setBLEDeviceConnectedCallback(void (*callback)(BLEStatus status, BLEDevice* device)): Register callback for device connection events
  • void setBLEDeviceDisconnectedCallback(void (*callback)(BLEDevice* device)): Register callback for device disconnection events

Callback Registration

  • void setPasskeyDisplayCallback(void (*callback)(uint32_t passkey)): Callback for handling passkey display
  • void setPasskeyEntryCallback(void (*callback)(void)): Callback for handling passkey entry requests
  • void setPairingStatusCallback(void (*callback)(BLEPairingStatus status, BLEDevice* device)): Callback for pairing status updates
  • void setNumericComparisonCallback(void (*callback)(uint32_t passkey, BLEDevice* device)): Callback for numeric comparison

Status and Control

  • void setEnteredPasskey(uint32_t passkey): Set passkey for entry method (call this from the passkey entry callback)
  • void acceptNumericComparison(bool accept): Accept or reject numeric comparison
  • BLEPairingStatus getPairingStatus(): Get the current pairing status
  • bool isEncrypted(BLEDevice* device): Get the encryption status for a connection

Compatibility

This library is designed for:

  • Raspberry Pi Pico and Pico W
  • Arduino-pico core with BTstack support

License

This library is licensed under the MIT License - see the LICENSE file for details.

Note: This library depends on the arduino-pico core which is licensed under LGPL-2.1. When using this library, you must comply with the terms of both licenses.

Contributing

Contributions to improve the library are welcome! Please submit pull requests or open issues on the repository.

Acknowledgements and Credits

This library is based on the BTstackLib library for Arduino-Core and Raspberry Pi Pico. Special thanks to the BTstack developers for their work on BLE stack implementation and arduino-pico core maintainers.

About

A library for implementing secure Bluetooth Low Energy (BLE) pairing and encryption on Raspberry Pi Pico using the arduino-pico core.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages