ble_error_t btle_initializeSecurity(bool enableBonding, bool requireMITM, SecurityManager::SecurityIOCapabilities_t iocaps, const SecurityManager::Passkey_t passkey) { /* guard against multiple initializations */ if (initialized) { return BLE_ERROR_NONE; } if (pstorage_init() != NRF_SUCCESS) { return BLE_ERROR_UNSPECIFIED; } ret_code_t rc; if (passkey) { ble_opt_t opts; opts.gap_opt.passkey.p_passkey = const_cast<uint8_t *>(passkey); if ((rc = sd_ble_opt_set(BLE_GAP_OPT_PASSKEY, &opts)) != NRF_SUCCESS) { switch (rc) { case BLE_ERROR_INVALID_CONN_HANDLE: case NRF_ERROR_INVALID_ADDR: case NRF_ERROR_INVALID_PARAM: default: return BLE_ERROR_INVALID_PARAM; case NRF_ERROR_INVALID_STATE: return BLE_ERROR_INVALID_STATE; case NRF_ERROR_BUSY: return BLE_STACK_BUSY; } } } dm_init_param_t dm_init_param = { .clear_persistent_data = false /* Set to true in case the module should clear all persistent data. */ }; if (dm_init(&dm_init_param) != NRF_SUCCESS) { return BLE_ERROR_UNSPECIFIED; } // update default security parameters with function call parameters securityParameters.bond = enableBonding; securityParameters.mitm = requireMITM; securityParameters.io_caps = iocaps; const dm_application_param_t dm_param = { .evt_handler = dm_handler, .service_type = DM_PROTOCOL_CNTXT_GATT_CLI_ID, .sec_param = securityParameters }; if ((rc = dm_register(&applicationInstance, &dm_param)) != NRF_SUCCESS) { switch (rc) { case NRF_ERROR_INVALID_STATE: return BLE_ERROR_INVALID_STATE; case NRF_ERROR_NO_MEM: return BLE_ERROR_NO_MEM; default: return BLE_ERROR_UNSPECIFIED; } } initialized = true; return BLE_ERROR_NONE; } ble_error_t btle_purgeAllBondingState(void) { ret_code_t rc; if ((rc = dm_device_delete_all(&applicationInstance)) == NRF_SUCCESS) { return BLE_ERROR_NONE; } switch (rc) { case NRF_ERROR_INVALID_STATE: return BLE_ERROR_INVALID_STATE; case NRF_ERROR_NO_MEM: return BLE_ERROR_NO_MEM; default: return BLE_ERROR_UNSPECIFIED; } }
/** * Post constructor initialisation method as the BLE stack cannot be brought * up in a static context. * * @param deviceName The name used when advertising * @param serialNumber The serial number exposed by the device information service * @param messageBus An instance of an EventModel, used during pairing. * @param enableBonding If true, the security manager enabled bonding. * * @code * bleManager.init(uBit.getName(), uBit.getSerial(), uBit.messageBus, true); * @endcode */ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumber, EventModel& messageBus, bool enableBonding) { ManagedString BLEName("BBC micro:bit"); this->deviceName = deviceName; #if !(CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)) ManagedString namePrefix(" ["); ManagedString namePostfix("]"); BLEName = BLEName + namePrefix + deviceName + namePostfix; #endif // Start the BLE stack. #if CONFIG_ENABLED(MICROBIT_HEAP_REUSE_SD) btle_set_gatt_table_size(MICROBIT_SD_GATT_TABLE_SIZE); #endif ble = new BLEDevice(); ble->init(); // automatically restart advertising after a device disconnects. ble->gap().onDisconnection(bleDisconnectionCallback); ble->gattServer().onSysAttrMissing(bleSysAttrMissingCallback); // generate an event when a Bluetooth connection is established ble->gap().onConnection(bleConnectionCallback); // Configure the stack to hold onto the CPU during critical timing events. // mbed-classic performs __disable_irq() calls in its timers that can cause // MIC failures on secure BLE channels... ble_common_opt_radio_cpu_mutex_t opt; opt.enable = 1; sd_ble_opt_set(BLE_COMMON_OPT_RADIO_CPU_MUTEX, (const ble_opt_t *)&opt); #if CONFIG_ENABLED(MICROBIT_BLE_PRIVATE_ADDRESSES) // Configure for private addresses, so kids' behaviour can't be easily tracked. ble->gap().setAddress(BLEProtocol::AddressType::RANDOM_PRIVATE_RESOLVABLE, {0}); #endif // Setup our security requirements. ble->securityManager().onPasskeyDisplay(passkeyDisplayCallback); ble->securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback); ble->securityManager().init(enableBonding, (SecurityManager::MICROBIT_BLE_SECURITY_LEVEL == SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM), SecurityManager::IO_CAPS_DISPLAY_ONLY); if (enableBonding) { // If we're in pairing mode, review the size of the bond table. int bonds = getBondCount(); // TODO: It would be much better to implement some sort of LRU/NFU policy here, // but this isn't currently supported in mbed, so we'd need to layer break... // If we're full, empty the bond table. if (bonds >= MICROBIT_BLE_MAXIMUM_BONDS) ble->securityManager().purgeAllBondingState(); } #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST) // Configure a whitelist to filter all connection requetss from unbonded devices. // Most BLE stacks only permit one connection at a time, so this prevents denial of service attacks. BLEProtocol::Address_t bondedAddresses[MICROBIT_BLE_MAXIMUM_BONDS]; Gap::Whitelist_t whitelist; whitelist.addresses = bondedAddresses; whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS; ble->securityManager().getAddressesFromBondTable(whitelist); ble->gap().setWhitelist(whitelist); ble->gap().setScanningPolicyMode(Gap::SCAN_POLICY_IGNORE_WHITELIST); ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_FILTER_CONN_REQS); #endif // Configure the radio at our default power level setTransmitPower(MICROBIT_BLE_DEFAULT_TX_POWER); // Bring up core BLE services. #if CONFIG_ENABLED(MICROBIT_BLE_DFU_SERVICE) new MicroBitDFUService(*ble); #endif #if CONFIG_ENABLED(MICROBIT_BLE_DEVICE_INFORMATION_SERVICE) DeviceInformationService ble_device_information_service (*ble, MICROBIT_BLE_MANUFACTURER, MICROBIT_BLE_MODEL, serialNumber.toCharArray(), MICROBIT_BLE_HARDWARE_VERSION, MICROBIT_BLE_FIRMWARE_VERSION, MICROBIT_BLE_SOFTWARE_VERSION); #endif #if CONFIG_ENABLED(MICROBIT_BLE_EVENT_SERVICE) new MicroBitEventService(*ble, messageBus); #else (void)messageBus; #endif // Configure for high speed mode where possible. Gap::ConnectionParams_t fast; ble->getPreferredConnectionParams(&fast); fast.minConnectionInterval = 8; // 10 ms fast.maxConnectionInterval = 16; // 20 ms fast.slaveLatency = 0; ble->setPreferredConnectionParams(&fast); // Setup advertising. #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST) ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED); #else ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); #endif ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)BLEName.toCharArray(), BLEName.length()); ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); ble->setAdvertisingInterval(200); #if (MICROBIT_BLE_ADVERTISING_TIMEOUT > 0) ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT); #endif // If we have whitelisting enabled, then prevent only enable advertising of we have any binded devices... // This is to further protect kids' privacy. If no-one initiates BLE, then the device is unreachable. // If whiltelisting is disabled, then we always advertise. #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST) if (whitelist.size > 0) #endif ble->startAdvertising(); }