microbit: Alpha version of standard BLE MITM passkey security

- refactored BLE funcitonality into BLEManager class.
- added security requirements standard BLE profile services.
- updated bluezone pairing process to use BLE passkey exchange.
This commit is contained in:
Joe Finney 2015-11-16 13:44:27 +00:00
parent 79c739674c
commit ac381fa5dc
12 changed files with 171 additions and 56 deletions

View file

@ -136,11 +136,6 @@ class MicroBit
*/
void init();
/**
* Derives the friendly name for this device, autogenerated from our hardware Device ID.
*/
void deriveName();
/**
* Return the friendly name for this device.
*

View file

@ -33,6 +33,10 @@
#include "MicroBitTemperatureService.h"
#include "ExternalEvents.h"
#define MICROBIT_BLE_PAIR_REQUEST 0x01
#define MICROBIT_BLE_PAIR_COMPLETE 0x02
#define MICROBIT_BLE_PAIR_SUCCESSFUL 0x04
/**
* Class definition for the MicroBitBLEManager.
*
@ -68,7 +72,7 @@ class MicroBitBLEManager
void init(ManagedString deviceName, ManagedString serialNumber);
/**
* Enter BLUEZOE mode. This is mode is called to initiate pairing, and to enable FOTA programming
* Enter BLUEZONE mode. This is mode is called to initiate pairing, and to enable FOTA programming
* of the micro:bit in cases where BLE is disabled during normal operation.
*
* @param display a MicroBitDisplay to use when displaying pairing information.
@ -81,6 +85,29 @@ class MicroBitBLEManager
* this callback to restart advertising.
*/
void onDisconnectionCallback();
/**
* Displays the device's ID code as a histogram on the LED matrix display.
*/
void showNameHistogram(MicroBitDisplay &display);
/**
* A request to pair has been received from a BLE device.
* If we're in BLUEZONE mode, display the passkey to the user.
*/
void pairingRequested(ManagedString passKey);
/**
* A pairing request has been sucesfully completed.
* If we're in BLUEZONE mode, display feedback to the user.
*/
void pairingComplete(bool success);
private:
int pairingStatus;
ManagedString passKey;
ManagedString deviceName;
};

View file

@ -1,8 +1,5 @@
#include "MicroBit.h"
char MICROBIT_BLE_DEVICE_NAME[] = "BBC micro:bit [xxxxx]";
/**
* custom function for panic for malloc & new due to scoping issue.
*/
@ -90,9 +87,6 @@ void MicroBit::init()
// Seed our random number generator
seedRandom();
// Generate the name for our device.
this->deriveName();
#if CONFIG_ENABLED(MICROBIT_BLE_ENABLED)
// Start the BLE stack.
bleManager.init(this->getName(), this->getSerial());
@ -104,10 +98,13 @@ void MicroBit::init()
}
/**
* Derives the friendly name for this device, autogenerated from our hardware Device ID.
* Return the friendly name for this device.
*
* @return A string representing the friendly name of this device.
*/
void MicroBit::deriveName()
ManagedString MicroBit::getName()
{
char nameBuffer[MICROBIT_NAME_LENGTH];
const uint8_t codebook[MICROBIT_NAME_LENGTH][MICROBIT_NAME_CODE_LETTERS] =
{
{'z', 'v', 'g', 'p', 't'},
@ -117,13 +114,12 @@ void MicroBit::deriveName()
{'z', 'v', 'g', 'p', 't'}
};
char *name = MICROBIT_BLE_DEVICE_NAME+15;
// We count right to left, so fast forward the pointer.
// We count right to left, so create a pointer to the end of the buffer.
char *name = nameBuffer;
name += MICROBIT_NAME_LENGTH;
// Derive our name from the nrf51822's unique ID.
uint32_t n = NRF_FICR->DEVICEID[1];
int ld = 1;
int d = MICROBIT_NAME_CODE_LETTERS;
int h;
@ -136,16 +132,8 @@ void MicroBit::deriveName()
ld *= MICROBIT_NAME_CODE_LETTERS;
*--name = codebook[i][h];
}
}
/**
* Return the friendly name for this device.
*
* @return A string representing the friendly name of this device.
*/
ManagedString MicroBit::getName()
{
return ManagedString(MICROBIT_BLE_DEVICE_NAME+15, MICROBIT_NAME_LENGTH);
return ManagedString(nameBuffer, MICROBIT_NAME_LENGTH);
}
/**

View file

@ -29,7 +29,11 @@ MicroBitAccelerometerService::MicroBitAccelerometerService(BLEDevice &_ble) :
accelerometerDataCharacteristicBuffer[1] = 0;
accelerometerDataCharacteristicBuffer[2] = 0;
accelerometerPeriodCharacteristicBuffer = uBit.accelerometer.getPeriod();
// Set default security requirements
accelerometerDataCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
accelerometerPeriodCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
GattCharacteristic *characteristics[] = {&accelerometerDataCharacteristic, &accelerometerPeriodCharacteristic};
GattService service(MicroBitAccelerometerServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));

View file

@ -33,35 +33,20 @@ static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::
{
(void) handle; /* -Wunused-param */
printf("Input passKey: ");
for (unsigned i = 0; i < Gap::ADDR_LEN; i++) {
printf("%c", passkey[i]);
}
printf("\r\n");
ManagedString passKey((const char *)passkey, SecurityManager::PASSKEY_LEN);
if (manager)
manager->pairingRequested(passKey);
}
static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status)
{
(void) handle; /* -Wunused-param */
if (status == SecurityManager::SEC_STATUS_SUCCESS) {
printf("Security success %d\r\n", status);
} else {
printf("Security failed %d\r\n", status);
}
if (manager)
manager->pairingComplete(status == SecurityManager::SEC_STATUS_SUCCESS);
}
static void securitySetupInitiatedCallback(Gap::Handle_t handle, bool allowBonding, bool requireMITM, SecurityManager::SecurityIOCapabilities_t iocaps)
{
(void) handle; /* -Wunused-param */
(void) allowBonding; /* -Wunused-param */
(void) requireMITM; /* -Wunused-param */
(void) iocaps; /* -Wunused-param */
printf("Security setup initiated\r\n");
}
/**
* Constructor.
*
@ -72,7 +57,9 @@ static void securitySetupInitiatedCallback(Gap::Handle_t handle, bool allowBondi
*/
MicroBitBLEManager::MicroBitBLEManager()
{
manager = this;
this->ble = NULL;
this->pairingStatus = 0;
}
/**
@ -99,6 +86,12 @@ void MicroBitBLEManager::onDisconnectionCallback()
*/
void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumber)
{
ManagedString prefix("BBC micro:bit [");
ManagedString postfix("]");
ManagedString BLEName = prefix + deviceName + postfix;
this->deviceName = deviceName;
// Start the BLE stack.
ble = new BLEDevice();
ble->init();
@ -107,7 +100,6 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb
ble->onDisconnection(bleDisconnectionCallback);
// Setup our security requirements.
ble->securityManager().onSecuritySetupInitiated(securitySetupInitiatedCallback);
ble->securityManager().onPasskeyDisplay(passkeyDisplayCallback);
ble->securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);
ble->securityManager().init(MICROBIT_BLE_ENABLE_BONDING, MICROBIT_BLE_REQUIRE_MITM, SecurityManager::IO_CAPS_DISPLAY_ONLY);
@ -159,20 +151,96 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb
// Setup advertising.
ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)deviceName.toCharArray(), deviceName.length());
ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)BLEName.toCharArray(), BLEName.length());
ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
ble->setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(200));
ble->startAdvertising();
}
/**
* A request to pair has been received from a BLE device.
* If we're in BLUEZONE mode, display the passkey to the user.
*/
void MicroBitBLEManager::pairingRequested(ManagedString passKey)
{
this->passKey = passKey;
this->pairingStatus = MICROBIT_BLE_PAIR_REQUEST;
}
/**
* A pairing request has been sucesfully completed.
* If we're in BLUEZONE mode, display feedback to the user.
*/
void MicroBitBLEManager::pairingComplete(bool success)
{
this->pairingStatus &= ~MICROBIT_BLE_PAIR_REQUEST;
this->pairingStatus |= MICROBIT_BLE_PAIR_COMPLETE;
if(success)
this->pairingStatus |= MICROBIT_BLE_PAIR_SUCCESSFUL;
}
/**
* Enter BLUEZONE mode. This is mode is called to initiate pairing, and to enable FOTA programming
* of the micro:bit in cases where BLE is disabled during normal operation.
*/
void MicroBitBLEManager::bluezone(MicroBitDisplay &display)
{
// TODO:
while(1);
{
// Stop any running animations on the display
display.stopAnimation();
// Display a welcome message
display.scroll("Hi! I'm ");
display.scroll(deviceName);
// Display our name, visualised as a histogram in the display to aid identification.
showNameHistogram(display);
while(1)
{
if (pairingStatus & MICROBIT_BLE_PAIR_REQUEST)
{
display.scroll("Pair: ");
display.scroll(passKey);
}
if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE)
{
if (pairingStatus & MICROBIT_BLE_PAIR_SUCCESSFUL)
{
MicroBitImage tick("0,0,0,0,0\n0,0,0,0,255\n0,0,0,255,0\n255,0,255,0,0\n0,255,0,0,0\n");
display.print(tick,0,0,0);
}
else
{
MicroBitImage cross("255,0,0,0,255\n0,255,0,255,0\n0,0,255,0,0\n0,255,0,255,0\n255,0,0,0,255\n");
display.print(cross,0,0,0);
}
}
}
}
/**
* Displays the device's ID code as a histogram on the LED matrix display.
*/
void MicroBitBLEManager::showNameHistogram(MicroBitDisplay &display)
{
uint32_t n = NRF_FICR->DEVICEID[1];
int ld = 1;
int d = MICROBIT_DFU_HISTOGRAM_HEIGHT;
int h;
display.clear();
for (int i=0; i<MICROBIT_DFU_HISTOGRAM_WIDTH;i++)
{
h = (n % d) / ld;
n -= h;
d *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
ld *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
for (int j=0; j<h+1; j++)
display.image.setPixelValue(MICROBIT_DFU_HISTOGRAM_WIDTH-i-1, MICROBIT_DFU_HISTOGRAM_HEIGHT-j-1, 255);
}
}

View file

@ -27,6 +27,10 @@ MicroBitButtonService::MicroBitButtonService(BLEDevice &_ble) :
// Initialise our characteristic values.
buttonADataCharacteristicBuffer = 0;
buttonBDataCharacteristicBuffer = 0;
// Set default security requirements
buttonADataCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
buttonBDataCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
GattCharacteristic *characteristics[] = {&buttonADataCharacteristic, &buttonBDataCharacteristic};
GattService service(MicroBitButtonServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));

View file

@ -38,6 +38,10 @@ MicroBitDFUService::MicroBitDFUService(BLEDevice &_ble) :
controlByte = 0x00;
flashCode = 0x00;
// Set default security requirements
microBitDFUServiceControlCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
microBitDFUServiceFlashCodeCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
GattCharacteristic *characteristics[] = {&microBitDFUServiceControlCharacteristic, &microBitDFUServiceFlashCodeCharacteristic};
GattService service(MicroBitDFUServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));

View file

@ -34,6 +34,12 @@ MicroBitEventService::MicroBitEventService(BLEDevice &_ble) :
messageBusListenerOffset = 0;
// Set default security requirements
microBitEventCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
clientEventCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
clientRequirementsCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
microBitRequirementsCharacteristic->requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
GattCharacteristic *characteristics[] = {&microBitEventCharacteristic, &clientEventCharacteristic, &clientRequirementsCharacteristic, microBitRequirementsCharacteristic};
GattService service(MicroBitEventServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));

View file

@ -30,6 +30,11 @@ MicroBitIOPinService::MicroBitIOPinService(BLEDevice &_ble) :
ioPinServiceADCharacteristicBuffer = 0;
ioPinServiceIOCharacteristicBuffer = 0;
memset(ioPinServiceIOData, 0, sizeof(ioPinServiceIOData));
// Set default security requirements
ioPinServiceADCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
ioPinServiceIOCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
ioPinServiceDataCharacteristic->requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
GattCharacteristic *characteristics[] = {&ioPinServiceADCharacteristic, &ioPinServiceIOCharacteristic, ioPinServiceDataCharacteristic};
GattService service(MicroBitIOPinServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));

View file

@ -32,6 +32,11 @@ MicroBitLEDService::MicroBitLEDService(BLEDevice &_ble) :
matrixCharacteristic.setReadAuthorizationCallback(this, &MicroBitLEDService::onDataRead);
// Set default security requirements
matrixCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
textCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
scrollingSpeedCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
GattCharacteristic *characteristics[] = {&matrixCharacteristic, &textCharacteristic, &scrollingSpeedCharacteristic};
GattService service(MicroBitLEDServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));

View file

@ -33,7 +33,12 @@ MicroBitMagnetometerService::MicroBitMagnetometerService(BLEDevice &_ble) :
magnetometerDataCharacteristicBuffer[2] = 0;
magnetometerBearingCharacteristicBuffer = 0;
magnetometerPeriodCharacteristicBuffer = uBit.compass.getPeriod();
// Set default security requirements
magnetometerDataCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
magnetometerBearingCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
magnetometerPeriodCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
GattCharacteristic *characteristics[] = {&magnetometerDataCharacteristic, &magnetometerBearingCharacteristic, &magnetometerPeriodCharacteristic};
GattService service(MicroBitMagnetometerServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));

View file

@ -26,7 +26,11 @@ MicroBitTemperatureService::MicroBitTemperatureService(BLEDevice &_ble) :
// Initialise our characteristic values.
temperatureDataCharacteristicBuffer = 0;
temperaturePeriodCharacteristicBuffer = uBit.thermometer.getPeriod();
// Set default security requirements
temperatureDataCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
temperaturePeriodCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
GattCharacteristic *characteristics[] = {&temperatureDataCharacteristic, &temperaturePeriodCharacteristic};
GattService service(MicroBitTemperatureServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));