Esempio n. 1
0
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();
}