/** * Class definition for a MicroBit Device Firmware Update loader. * * This is actually just a frontend to a memory resident nordic DFU loader. * Here we deal with the MicroBit 'pairing' functionality with BLE devices, and * very basic authentication and authorization. * * This implementation is not intended to be fully secure, but rather intends to: * * 1. Provide a simple mechanism to identify an individual MicroBit amongst a classroom of others * 2. Allow BLE devices to discover and cache a passcode that can be used to flash the device over BLE. * 3. Provide a BLE escape route for programs that 'brick' the MicroBit. * * Represents the device as a whole, and includes member variables to that reflect the components of the system. */ #include "MicroBit.h" #include "ble/UUID.h" /** * Constructor. * Create a representation of a MicroBit device. * @param messageBus callback function to receive MicroBitMessageBus events. */ MicroBitDFUService::MicroBitDFUService(BLEDevice &_ble) : ble(_ble), microBitDFUServiceControlCharacteristic(MicroBitDFUServiceControlCharacteristicUUID, &controlByte), microBitDFUServiceFlashCodeCharacteristic(MicroBitDFUServiceFlashCodeCharacteristicUUID, (uint8_t *)&flashCode, 0, sizeof(uint32_t), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY) { authenticated = false; flashCodeRequested = false; controlByte = 0x00; flashCode = 0x00; GattCharacteristic *characteristics[] = {µBitDFUServiceControlCharacteristic, µBitDFUServiceFlashCodeCharacteristic}; GattService service(MicroBitDFUServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *)); ble.addService(service); ble.onDataWritten(this, &MicroBitDFUService::onDataWritten); } /** * Returns the friendly name for this device, autogenerated from our Device ID. * * @param name Pointer to a string where the data will be written. * @return The number of bytes written. */ int MicroBitDFUService::getName(char *name) { const uint8_t codebook[5][5] = { {'z', 'v', 'g', 'p', 't'}, {'u', 'o', 'i', 'e', 'a'}, {'z', 'v', 'g', 'p', 't'}, {'u', 'o', 'i', 'e', 'a'}, {'z', 'v', 'g', 'p', 't'} }; // We count right to left, so fast forward the pointer. name += MICROBIT_DFU_HISTOGRAM_WIDTH; uint32_t n = NRF_FICR->DEVICEID[1]; int ld = 1; int d = MICROBIT_DFU_HISTOGRAM_HEIGHT; int h; #ifdef MICROBIT_DEBUG pc.printf("MicroBitDFUService::getName: Called [%.8x]\n",n); #endif for (int i=0; ihandle == microBitDFUServiceControlCharacteristic.getValueHandle()) { #ifdef MICROBIT_DEBUG pc.printf("Control Point:\n "); #endif if (params->len < 1) { #ifdef MICROBIT_DEBUG pc.printf(" invalid. Ignoring.\n"); #endif return; } #ifdef MICROBIT_DEBUG for (int i=0; ilen; i++) pc.printf("%.2x ", params->data[i]); pc.printf("\n"); #endif switch(params->data[0]) { case MICROBIT_DFU_OPCODE_START_DFU: if (authenticated) { #ifdef MICROBIT_DEBUG pc.printf(" ACTIVATING BOOTLOADER.\n"); #endif bootloader_start(); } break; case MICROBIT_DFU_OPCODE_START_PAIR: #ifdef MICROBIT_DEBUG pc.printf(" START_PAIR: "); #endif flashCodeRequested = true; break; } } if (params->handle == microBitDFUServiceFlashCodeCharacteristic.getValueHandle()) { #ifdef MICROBIT_DEBUG pc.printf("FlashCode\n\n"); #endif if (params->len >= 4) { uint32_t lockCode=0; memcpy(&lockCode, params->data, 4); if (lockCode == NRF_FICR->DEVICEID[0]) { #ifdef MICROBIT_DEBUG pc.printf("AUTHENTICATED\n"); #endif authenticated = true; }else{ #ifdef MICROBIT_DEBUG pc.printf("NOT AUTHENTICATED: %8x\n", lockCode); #endif authenticated = false; } } } } /** * Displays the device's ID code as a histogram on the LED matrix display. */ void MicroBitDFUService::showTick() { uBit.display.resetAnimation(0); uBit.display.image.setPixelValue(0,3, 255); uBit.display.image.setPixelValue(1,4, 255); uBit.display.image.setPixelValue(2,3, 255); uBit.display.image.setPixelValue(3,2, 255); uBit.display.image.setPixelValue(4,1, 255); } /** * Displays the device's ID code as a histogram on the LED matrix display. */ void MicroBitDFUService::showNameHistogram() { #ifdef MICROBIT_DEBUG pc.printf("MicroBitDFUService::showNameHistogram: Called\n"); #endif uBit.display.resetAnimation(0); uint32_t n = NRF_FICR->DEVICEID[1]; int ld = 1; int d = MICROBIT_DFU_HISTOGRAM_HEIGHT; int h; uBit.display.clear(); for (int i=0; iDEVICEID[0]; ble.updateCharacteristicValue(microBitDFUServiceFlashCodeCharacteristic.getValueHandle(), (uint8_t *)&flashCode, sizeof(uint32_t)); } /** * UUID definitions for BLE Services and Characteristics. */ const uint8_t MicroBitDFUServiceUUID[] = { 0xd8,0xaf,0x99,0x1c,0x71,0x44,0x43,0xd7,0x95,0x4b,0x99,0x51,0x2f,0x95,0xf9,0x9c }; const uint8_t MicroBitDFUServiceControlCharacteristicUUID[] = { 0x97,0x10,0x95,0x47,0xe6,0x3a,0x44,0x2a,0xbf,0x89,0x9d,0x73,0x04,0x13,0xdc,0x2f }; const uint8_t MicroBitDFUServiceFlashCodeCharacteristicUUID[] = { 0x94,0x7b,0x69,0x34,0x64,0xd1,0x4f,0xad,0x9b,0xd0,0xcc,0x9d,0x6e,0x9f,0x3e,0xa3 };