diff --git a/hal/inc/ble_hal.h b/hal/inc/ble_hal.h index 694dedaa52..edbbc14ea5 100644 --- a/hal/inc/ble_hal.h +++ b/hal/inc/ble_hal.h @@ -42,7 +42,7 @@ extern "C" { * @{ */ -#define BLE_API_VERSION 1 +#define BLE_API_VERSION 2 // Particle's company ID #define PARTICLE_COMPANY_ID 0x0662 @@ -120,6 +120,19 @@ typedef enum hal_ble_pairing_io_caps_t { BLE_IO_CAPS_KEYBOARD_DISPLAY = 4 } hal_ble_pairing_io_caps_t; +typedef enum hal_ble_pairing_algorithm_t { + BLE_PAIRING_ALGORITHM_AUTO = 0, + BLE_PAIRING_ALGORITHM_LEGACY_ONLY = 1, + BLE_PAIRING_ALGORITHM_LESC_ONLY = 2 +} hal_ble_pairing_algorithm_t; + +typedef enum hal_ble_pairing_auth_data_type_t { + BLE_PAIRING_AUTH_DATA_NUMERIC_COMPARISON = 0, + BLE_PAIRING_AUTH_DATA_PASSKEY = 1, + BLE_PAIRING_AUTH_DATA_LEGACY_OOB = 2, + BLE_PAIRING_AITH_DATA_LESC_OOB = 3 +} hal_ble_pairing_auth_data_type_t; + typedef enum hal_ble_evts_type_t { BLE_EVT_UNKNOWN = 0x00, BLE_EVT_ADV_STOPPED = 0x01, @@ -137,6 +150,7 @@ typedef enum hal_ble_evts_type_t { BLE_EVT_PAIRING_PASSKEY_DISPLAY = 0x0D, BLE_EVT_PAIRING_PASSKEY_INPUT = 0x0E, BLE_EVT_PAIRING_STATUS_UPDATED = 0x0F, + BLE_EVT_PAIRING_NUMERIC_COMPARISON = 0x10, BLE_EVT_MAX = 0x7FFFFFFF } hal_ble_evts_type_t; @@ -305,6 +319,18 @@ typedef struct hal_ble_pairing_status_updated_evt_t { uint8_t reserved[3]; } hal_ble_pairing_status_updated_evt_t; +typedef struct hal_ble_pairing_auth_data_t { + uint16_t version; + uint16_t size; + hal_ble_pairing_auth_data_type_t type; + union { + bool equal; //For numeric comparison result + const uint8_t* passkey; // For passkey + const uint8_t* legacy_oob; // For legacy OOB data + const uint8_t* lesc_oob; // For LESC OOB data + } params; +} hal_ble_pairing_auth_data_t; + typedef struct hal_ble_link_evt_t { hal_ble_evts_type_t type; union { @@ -397,7 +423,8 @@ typedef struct hal_ble_pairing_config_t { uint16_t version; uint16_t size; hal_ble_pairing_io_caps_t io_caps; - uint8_t reserved[3]; + hal_ble_pairing_algorithm_t algorithm; + uint8_t reserved[2]; } hal_ble_pairing_config_t; /** @@ -833,6 +860,15 @@ int hal_ble_gap_get_rssi(hal_ble_conn_handle_t conn_handle, void* reserved); */ int hal_ble_gap_set_pairing_config(const hal_ble_pairing_config_t* config, void* reserved); +/** + * Get pairing configurations + * + * @param[in] config Pointer to where it stores the configuration. + * + * @returns 0 on success, system_error_t on error. + */ +int hal_ble_gap_get_pairing_config(hal_ble_pairing_config_t* config, void* reserved); + /** * Start pairing with the peer device. * @@ -851,6 +887,16 @@ int hal_ble_gap_start_pairing(hal_ble_conn_handle_t conn_handle, void* reserved) */ int hal_ble_gap_reject_pairing(hal_ble_conn_handle_t conn_handle, void* reserved); +/** + * Set pairing authentication data. + * + * @param[in] conn_handle BLE connection handle. + * @param[in] auth Authentication data. + * + * @returns 0 on success, system_error_t on error. + */ +int hal_ble_gap_set_pairing_auth_data(hal_ble_conn_handle_t conn_handle, const hal_ble_pairing_auth_data_t* auth, void* reserved); + /** * Provide the 6-digits passkey that is observed from the peer's display. * @@ -858,7 +904,7 @@ int hal_ble_gap_reject_pairing(hal_ble_conn_handle_t conn_handle, void* reserved * * @returns 0 on success, system_error_t on error. */ -int hal_ble_gap_set_pairing_passkey(hal_ble_conn_handle_t conn_handle, const uint8_t* passkey, void* reserved); +int hal_ble_gap_set_pairing_passkey_deprecated(hal_ble_conn_handle_t conn_handle, const uint8_t* passkey, void* reserved); /** * Check if pairing with peer device is in progress diff --git a/hal/inc/ble_hal_defines.h b/hal/inc/ble_hal_defines.h index 20dc342b3e..84e301674c 100644 --- a/hal/inc/ble_hal_defines.h +++ b/hal/inc/ble_hal_defines.h @@ -37,6 +37,9 @@ // BLE authentication passkey length #define BLE_PAIRING_PASSKEY_LEN (6) +// BLE legacy authentication OOB data length +#define BLE_PAIRING_OOB_DATA_LEN (16) + // BLE device address type typedef enum ble_sig_addr_type_t { BLE_SIG_ADDR_TYPE_PUBLIC = 0x00, /**< Public (identity) address.*/ diff --git a/hal/inc/hal_dynalib_ble.h b/hal/inc/hal_dynalib_ble.h index a6a0d25348..cf7d8613ff 100644 --- a/hal/inc/hal_dynalib_ble.h +++ b/hal/inc/hal_dynalib_ble.h @@ -100,9 +100,11 @@ DYNALIB_FN(65, hal_ble, hal_ble_gatt_server_indicate_characteristic_value, ssize DYNALIB_FN(66, hal_ble, hal_ble_gap_set_pairing_config, int(const hal_ble_pairing_config_t*, void*)) DYNALIB_FN(67, hal_ble, hal_ble_gap_start_pairing, int(hal_ble_conn_handle_t, void*)) DYNALIB_FN(68, hal_ble, hal_ble_gap_reject_pairing, int(hal_ble_conn_handle_t, void*)) -DYNALIB_FN(69, hal_ble, hal_ble_gap_set_pairing_passkey, int(hal_ble_conn_handle_t, const uint8_t*, void*)) +DYNALIB_FN(69, hal_ble, hal_ble_gap_set_pairing_passkey_deprecated, int(hal_ble_conn_handle_t, const uint8_t*, void*)) DYNALIB_FN(70, hal_ble, hal_ble_gap_is_pairing, bool(hal_ble_conn_handle_t, void*)) DYNALIB_FN(71, hal_ble, hal_ble_gap_is_paired, bool(hal_ble_conn_handle_t, void*)) +DYNALIB_FN(72, hal_ble, hal_ble_gap_set_pairing_auth_data, int(hal_ble_conn_handle_t, const hal_ble_pairing_auth_data_t*, void*)) +DYNALIB_FN(73, hal_ble, hal_ble_gap_get_pairing_config, int(hal_ble_pairing_config_t*, void*)) DYNALIB_END(hal_ble) diff --git a/hal/src/nRF52840/ble_hal.cpp b/hal/src/nRF52840/ble_hal.cpp index 847cea3c1f..c42a694c45 100644 --- a/hal/src/nRF52840/ble_hal.cpp +++ b/hal/src/nRF52840/ble_hal.cpp @@ -55,6 +55,9 @@ LOG_SOURCE_CATEGORY("hal.ble"); #include "check.h" #include "scope_guard.h" +#include "mbedtls/ecdh.h" +#include "mbedtls_util.h" + using namespace particle; #include "intrusive_list.h" @@ -69,6 +72,9 @@ using namespace particle::ble; #endif #define SUB1(X) (((X)>0) ? ((X)-1) : (X)) +#define BLE_API_VERSION_1 1 +#define BLE_API_VERSION_2 2 + //anonymous namespace namespace { @@ -230,6 +236,7 @@ class BleObject::BleEventDispatcher { return evtDispatcherinitialized_; } void enqueue(ble_evt_t** event); + void enqueue(const ble_evt_t* event); void* allocEventData(size_t size) { return pool_.alloc(size); @@ -405,8 +412,14 @@ class BleObject::ConnectionsManager { disconnectSemaphore_(nullptr), attMtuExchangeConnHandle_(BLE_INVALID_CONN_HANDLE), attMtuExchangeTimer_(nullptr), - ioCaps_(BLE_IO_CAPS_NONE) { + ecdhContextInit_(false) { connectingAddr_ = {}; + pairingConfig_ = { + .version = BLE_API_VERSION, + .size = sizeof(hal_ble_pairing_config_t), + .io_caps = BLE_IO_CAPS_NONE, + .algorithm = BLE_PAIRING_ALGORITHM_AUTO + }; } ~ConnectionsManager() = default; int init(); @@ -425,9 +438,10 @@ class BleObject::ConnectionsManager { int updateConnectionParams(hal_ble_conn_handle_t connHandle, const hal_ble_conn_params_t* params); int getConnectionInfo(hal_ble_conn_handle_t connHandle, hal_ble_conn_info_t* info); int setPairingConfig(const hal_ble_pairing_config_t* config); + int getPairingConfig(hal_ble_pairing_config_t* config) const; int startPairing(hal_ble_conn_handle_t connHandle); int rejectPairing(hal_ble_conn_handle_t connHandle); - int setPairingPasskey(hal_ble_conn_handle_t connHandle, const uint8_t* passkey); + int setPairingAuthData(hal_ble_conn_handle_t connHandle, const hal_ble_pairing_auth_data_t* auth); bool isPairing(hal_ble_conn_handle_t connHandle); bool isPaired(hal_ble_conn_handle_t connHandle); bool valid(hal_ble_conn_handle_t connHandle); @@ -462,13 +476,18 @@ class BleObject::ConnectionsManager { BleLinkEventHandler handler; // It is used for central link only. bool isMtuExchanged; BlePairingState pairState; + std::unique_ptr peerPublicKey; }; + int initSecParams(const hal_ble_pairing_config_t& config, ble_gap_sec_params_t& params); + int generateEcdhKeyPair(); + int publicKeysPrepare(BleConnection* connection); + int computeDhkey(const uint8_t peerPublicKey[BLE_GAP_LESC_P256_PK_LEN], uint8_t dhKey[BLE_GAP_LESC_DHKEY_LEN]); int configureAttMtu(hal_ble_conn_handle_t connHandle, size_t effective); bool attMtuExchanged(hal_ble_conn_handle_t connHandle); BleConnection* fetchConnection(hal_ble_conn_handle_t connHandle); BleConnection* fetchConnection(const hal_ble_addr_t* address); - int addConnection(const BleConnection& connection); + int addConnection(BleConnection&& connection); void removeConnection(hal_ble_conn_handle_t connHandle); void initiateConnParamsUpdateIfNeeded(const BleConnection* connection); bool isConnParamsFeeded(const hal_ble_conn_params_t* params) const; @@ -494,11 +513,14 @@ class BleObject::ConnectionsManager { os_semaphore_t disconnectSemaphore_; /**< Semaphore to wait until connection disconnected. */ volatile hal_ble_conn_handle_t attMtuExchangeConnHandle_; /**< Current handle of the connection to execute ATT_MTU exchange procedure. */ os_timer_t attMtuExchangeTimer_; /**< Timer used for sending the exchanging ATT_MTU request after connection established. */ - hal_ble_pairing_io_caps_t ioCaps_; // GATT Server and GATT client share the same ATT_MTU. static size_t desiredAttMtu_; Vector connections_; Vector peripheralLinkEventHandlers_; /**< It is used for peripheral link only. */ + hal_ble_pairing_config_t pairingConfig_; + mbedtls_ecdh_context ecdhContext_; + volatile bool ecdhContextInit_; + std::unique_ptr localPublicKey_; }; size_t BleObject::ConnectionsManager::desiredAttMtu_ = BLE_MAX_ATT_MTU_SIZE; @@ -684,6 +706,16 @@ void BleObject::BleEventDispatcher::enqueue(ble_evt_t** event) { } } +void BleObject::BleEventDispatcher::enqueue(const ble_evt_t* event) { + ble_evt_t* pBleEvent = (ble_evt_t*)allocEventData(sizeof(ble_evt_t)); + if (!pBleEvent) { + LOG(ERROR, "Allocate memory for BLE event failed."); + SPARK_ASSERT(false); + } + memcpy(pBleEvent, event, sizeof(ble_evt_t)); + enqueue(&pBleEvent); +} + os_thread_return_t BleObject::BleEventDispatcher::processBleEventFromThread(void* param) { BleEventDispatcher* dispatcher = static_cast(param); while (1) { @@ -740,7 +772,10 @@ os_thread_return_t BleObject::BleEventDispatcher::processBleEventFromThread(void } case BLE_GATTC_EVT_HVX: { BleObject::getInstance().gattc()->processDataNotifiedEventFromThread(event); + break; } + case BLE_GAP_EVT_SEC_INFO_REQUEST: + case BLE_GAP_EVT_LESC_DHKEY_REQUEST: case BLE_GAP_EVT_PASSKEY_DISPLAY: case BLE_GAP_EVT_AUTH_KEY_REQUEST: case BLE_GAP_EVT_SEC_REQUEST: @@ -748,6 +783,7 @@ os_thread_return_t BleObject::BleEventDispatcher::processBleEventFromThread(void case BLE_GAP_EVT_AUTH_STATUS: case BLE_GAP_EVT_CONN_SEC_UPDATE: { BleObject::getInstance().connMgr()->processSecurityEventFromThread(event); + break; } default: { break; @@ -1883,9 +1919,83 @@ int BleObject::ConnectionsManager::getConnectionInfo(hal_ble_conn_handle_t connH return SYSTEM_ERROR_NONE; } +int BleObject::ConnectionsManager::initSecParams(const hal_ble_pairing_config_t& config, ble_gap_sec_params_t& secParams) { + if (config.io_caps == BLE_IO_CAPS_NONE) { + secParams.mitm = false; + } else { + secParams.mitm = true; + } + if (config.algorithm == BLE_PAIRING_ALGORITHM_LEGACY_ONLY) { + secParams.lesc = false; + } else { + secParams.lesc = true; + } + secParams.io_caps = toPlatformIoCaps(config.io_caps); + secParams.keypress = false; + secParams.bond = false; + secParams.oob = false; + secParams.min_key_size = BLE_ENC_MIN_KEY_SIZE; + secParams.max_key_size = BLE_ENC_MAX_KEY_SIZE; + secParams.kdist_own.enc = true; + secParams.kdist_own.id = true; + secParams.kdist_peer.enc = true; + secParams.kdist_peer.id = true; + return SYSTEM_ERROR_NONE; +} + +int BleObject::ConnectionsManager::generateEcdhKeyPair(void) { + ecdhContextInit_ = false; + mbedtls_ecdh_init(&ecdhContext_); + CHECK_MBEDTLS(mbedtls_ecdh_setup(&ecdhContext_, MBEDTLS_ECP_DP_SECP256R1)); + CHECK_MBEDTLS(mbedtls_ecdh_gen_public(&ecdhContext_.ctx.mbed_ecdh.grp, &ecdhContext_.ctx.mbed_ecdh.d, + &ecdhContext_.ctx.mbed_ecdh.Q, mbedtls_default_rng, nullptr)); + ecdhContextInit_ = true; + return SYSTEM_ERROR_NONE; +} + +int BleObject::ConnectionsManager::publicKeysPrepare(BleConnection* connection) { + if (!ecdhContextInit_) { + generateEcdhKeyPair(); + } + CHECK_TRUE(ecdhContextInit_, SYSTEM_ERROR_INVALID_STATE); + // Local public key: won't be freed. + if (!localPublicKey_) { + localPublicKey_ = std::make_unique(); + CHECK_TRUE(localPublicKey_, SYSTEM_ERROR_NO_MEMORY); + } + const uint8_t pointLen = BLE_GAP_LESC_P256_PK_LEN / 2; + CHECK_MBEDTLS(mbedtls_mpi_write_binary_le(&ecdhContext_.ctx.mbed_ecdh.Q.X, &(localPublicKey_.get()->pk[0]), pointLen)); + CHECK_MBEDTLS(mbedtls_mpi_write_binary_le(&ecdhContext_.ctx.mbed_ecdh.Q.Y, &(localPublicKey_.get()->pk[pointLen]), pointLen)); + // Peer public key: will be freed on authentication status updated or disconnected. + connection->peerPublicKey = std::make_unique(); + CHECK_TRUE(connection->peerPublicKey, SYSTEM_ERROR_NO_MEMORY); + memset(connection->peerPublicKey.get(), 0x00, BLE_GAP_LESC_P256_PK_LEN); + return SYSTEM_ERROR_NONE; +} + +int BleObject::ConnectionsManager::computeDhkey(const uint8_t peerPublicKey[BLE_GAP_LESC_P256_PK_LEN], uint8_t dhKey[BLE_GAP_LESC_DHKEY_LEN]) { + CHECK_TRUE(ecdhContextInit_, SYSTEM_ERROR_INVALID_STATE); + const uint8_t pointLen = BLE_GAP_LESC_P256_PK_LEN / 2; + CHECK_MBEDTLS(mbedtls_mpi_read_binary_le(&ecdhContext_.ctx.mbed_ecdh.Qp.X, &peerPublicKey[0], pointLen)); + CHECK_MBEDTLS(mbedtls_mpi_read_binary_le(&ecdhContext_.ctx.mbed_ecdh.Qp.Y, &peerPublicKey[pointLen], pointLen)); + CHECK_MBEDTLS(mbedtls_mpi_lset(&ecdhContext_.ctx.mbed_ecdh.Qp.Z, 1)); + CHECK_MBEDTLS(mbedtls_ecdh_compute_shared(&ecdhContext_.ctx.mbed_ecdh.grp, &ecdhContext_.ctx.mbed_ecdh.z, + &ecdhContext_.ctx.mbed_ecdh.Qp, &ecdhContext_.ctx.mbed_ecdh.d, mbedtls_default_rng, nullptr)); + CHECK_MBEDTLS(mbedtls_mpi_write_binary_le(&ecdhContext_.ctx.mbed_ecdh.z, dhKey, BLE_GAP_LESC_DHKEY_LEN)); + return SYSTEM_ERROR_NONE; +} + int BleObject::ConnectionsManager::setPairingConfig(const hal_ble_pairing_config_t* config) { CHECK_TRUE(config, SYSTEM_ERROR_INVALID_ARGUMENT); - ioCaps_ = config->io_caps; + pairingConfig_ = {}; + pairingConfig_.size = sizeof(hal_ble_pairing_config_t); + memcpy(&pairingConfig_, config, std::min(pairingConfig_.size, config->size)); + return SYSTEM_ERROR_NONE; +} + +int BleObject::ConnectionsManager::getPairingConfig(hal_ble_pairing_config_t* config) const { + CHECK_TRUE(config, SYSTEM_ERROR_INVALID_ARGUMENT); + memcpy(config, &pairingConfig_, std::min(pairingConfig_.size, config->size)); return SYSTEM_ERROR_NONE; } @@ -1896,19 +2006,7 @@ int BleObject::ConnectionsManager::startPairing(hal_ble_conn_handle_t connHandle connection->pairState == BLE_PAIRING_STATE_REJECTED || connection->pairState == BLE_PAIRING_STATE_SEC_REQ_RECEIVED, SYSTEM_ERROR_INVALID_STATE); ble_gap_sec_params_t secParams = {}; - if (ioCaps_ != BLE_IO_CAPS_NONE) { - secParams.mitm = true; - } - if (connection->info.role == BLE_ROLE_CENTRAL) { - secParams.io_caps = toPlatformIoCaps(ioCaps_); - secParams.oob = false; - secParams.min_key_size = BLE_ENC_MIN_KEY_SIZE; - secParams.max_key_size = BLE_ENC_MAX_KEY_SIZE; - secParams.kdist_own.enc = true; - secParams.kdist_own.id = true; - secParams.kdist_peer.enc = true; - secParams.kdist_peer.id = true; - } + initSecParams(pairingConfig_, secParams); LOG_DEBUG(TRACE, "Own security params: %d, %d, %d, %d, %d, %d", secParams.bond, secParams.mitm, secParams.lesc, secParams.io_caps, secParams.keypress, secParams.oob); int ret = sd_ble_gap_authenticate(connection->info.conn_handle, &secParams); if (ret != NRF_SUCCESS) { @@ -1935,22 +2033,24 @@ int BleObject::ConnectionsManager::rejectPairing(hal_ble_conn_handle_t connHandl CHECK_TRUE(connection, SYSTEM_ERROR_NOT_FOUND); switch (connection->pairState) { case BLE_PAIRING_STATE_SEC_REQ_RECEIVED: { - connection->pairState = BLE_PAIRING_STATE_REJECTED; int ret = sd_ble_gap_authenticate(connection->info.conn_handle, nullptr); CHECK_NRF_RETURN(ret, nrf_system_error(ret)); + // We should not tamper the pairing state to be out of sync with the SoftDevice. + // Only if the SD call returns success we then mark the pairing state as rejected. + connection->pairState = BLE_PAIRING_STATE_REJECTED; break; } case BLE_PAIRING_STATE_PAIRING_REQ_RECEIVED: { - connection->pairState = BLE_PAIRING_STATE_REJECTED; int ret = sd_ble_gap_sec_params_reply(connHandle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, nullptr, nullptr); CHECK_NRF_RETURN(ret, nrf_system_error(ret)); + connection->pairState = BLE_PAIRING_STATE_REJECTED; break; } case BLE_GAP_EVT_PASSKEY_DISPLAY: case BLE_PAIRING_STATE_PASSKEY_INPUT: { - connection->pairState = BLE_PAIRING_STATE_REJECTED; int ret = sd_ble_gap_auth_key_reply(connHandle, BLE_GAP_AUTH_KEY_TYPE_NONE, nullptr); CHECK_NRF_RETURN(ret, nrf_system_error(ret)); + connection->pairState = BLE_PAIRING_STATE_REJECTED; break; } default: { @@ -1960,24 +2060,31 @@ int BleObject::ConnectionsManager::rejectPairing(hal_ble_conn_handle_t connHandl return SYSTEM_ERROR_NONE; } -int BleObject::ConnectionsManager::setPairingPasskey(hal_ble_conn_handle_t connHandle, const uint8_t* passkey) { +int BleObject::ConnectionsManager::setPairingAuthData(hal_ble_conn_handle_t connHandle, const hal_ble_pairing_auth_data_t* auth) { + CHECK_TRUE(auth, SYSTEM_ERROR_INVALID_ARGUMENT); BleConnection* connection = fetchConnection(connHandle); CHECK_TRUE(connection, SYSTEM_ERROR_NOT_FOUND); - CHECK_TRUE(connection->pairState == BLE_PAIRING_STATE_PASSKEY_INPUT, SYSTEM_ERROR_INVALID_STATE); - for (uint8_t i = 0; i < 6; i++) { - if (!std::isdigit(passkey[i])) { - sd_ble_gap_auth_key_reply(connHandle, BLE_GAP_AUTH_KEY_TYPE_NONE/*reject*/, nullptr); - LOG(ERROR, "Invalid digits."); - connection->pairState = BLE_PAIRING_STATE_REJECTED; - return SYSTEM_ERROR_INVALID_ARGUMENT; + int ret; + if (auth->type == BLE_PAIRING_AUTH_DATA_NUMERIC_COMPARISON) { + CHECK_TRUE(connection->pairState == BLE_PAIRING_STATE_PASSKEY_DISPLAY, SYSTEM_ERROR_INVALID_STATE); + if (auth->params.equal) { + ret = sd_ble_gap_auth_key_reply(connHandle, BLE_GAP_AUTH_KEY_TYPE_PASSKEY, nullptr); + } else { + return rejectPairing(connHandle); } + } else if (auth->type == BLE_PAIRING_AUTH_DATA_PASSKEY) { + CHECK_TRUE(connection->pairState == BLE_PAIRING_STATE_PASSKEY_INPUT, SYSTEM_ERROR_INVALID_STATE); + for (uint8_t i = 0; i < BLE_PAIRING_PASSKEY_LEN; i++) { + if (!std::isdigit(auth->params.passkey[i])) { + LOG(ERROR, "Invalid digits."); + return rejectPairing(connHandle); + } + } + ret = sd_ble_gap_auth_key_reply(connHandle, BLE_GAP_AUTH_KEY_TYPE_PASSKEY, auth->params.passkey); + } else { + return SYSTEM_ERROR_NOT_SUPPORTED; } - int ret = sd_ble_gap_auth_key_reply(connHandle, BLE_GAP_AUTH_KEY_TYPE_PASSKEY, passkey); - if (ret != NRF_SUCCESS) { - connection->pairState = BLE_PAIRING_STATE_NOT_INITIATED; - return nrf_system_error(ret); - } - return SYSTEM_ERROR_NONE; + return nrf_system_error(ret); } bool BleObject::ConnectionsManager::isPairing(hal_ble_conn_handle_t connHandle) { @@ -2079,9 +2186,9 @@ BleObject::ConnectionsManager::BleConnection* BleObject::ConnectionsManager::fet return nullptr; } -int BleObject::ConnectionsManager::addConnection(const BleConnection& connection) { +int BleObject::ConnectionsManager::addConnection(BleConnection&& connection) { CHECK_TRUE(fetchConnection(connection.info.conn_handle) == nullptr, SYSTEM_ERROR_INTERNAL); - CHECK_TRUE(connections_.append(connection), SYSTEM_ERROR_NO_MEMORY); + CHECK_TRUE(connections_.append(std::move(connection)), SYSTEM_ERROR_NO_MEMORY); return SYSTEM_ERROR_NONE; } @@ -2208,7 +2315,7 @@ int BleObject::ConnectionsManager::processConnectedEventFromThread(const ble_evt connection.info.att_mtu = BLE_DEFAULT_ATT_MTU_SIZE; // Use the default ATT_MTU on connected. connection.isMtuExchanged = false; connection.pairState = BLE_PAIRING_STATE_NOT_INITIATED; - int ret = addConnection(connection); + int ret = addConnection(std::move(connection)); if (ret != SYSTEM_ERROR_NONE) { LOG(ERROR, "Add new connection failed. Disconnects from peer."); if (sd_ble_gap_disconnect(connection.info.conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION) != NRF_SUCCESS) { @@ -2305,18 +2412,22 @@ int BleObject::ConnectionsManager::processSecurityEventFromThread(const ble_evt_ notifyLinkEvent(linkEvent); // User application may call rejectPairing() to update the pairing state in event handler if (connection->pairState != BLE_PAIRING_STATE_REJECTED) { + const ble_gap_evt_sec_request_t& secRequest = event->evt.gap_evt.params.sec_request; + if (pairingConfig_.algorithm == BLE_PAIRING_ALGORITHM_LESC_ONLY) { + if (secRequest.lesc == false) { + rejectPairing(event->evt.gap_evt.conn_handle); + } + } return startPairing(event->evt.gap_evt.conn_handle); } } } else if (event->header.evt_id == BLE_GAP_EVT_SEC_PARAMS_REQUEST) { LOG_DEBUG(TRACE, "BLE event: BLE_GAP_EVT_SEC_PARAMS_REQUEST"); - // const ble_gap_sec_params_t& peerSecParams = event->evt.gap_evt.params.sec_params_request.peer_params; - // LOG_DEBUG(TRACE, "Peer security params: %d, %d, %d, %d, %d, %d", peerSecParams.bond, peerSecParams.mitm, peerSecParams.lesc, peerSecParams.io_caps, peerSecParams.keypress, peerSecParams.oob); - + const ble_gap_sec_params_t& peerSecParams = event->evt.gap_evt.params.sec_params_request.peer_params; + LOG_DEBUG(TRACE, "Peer bond:%d, mitm:%d, lesc:%d, iocaps:%d, keypress:%d, oob:%d", + peerSecParams.bond, peerSecParams.mitm, peerSecParams.lesc, toHalIoCaps(peerSecParams.io_caps), peerSecParams.keypress, peerSecParams.oob); int ret = NRF_SUCCESS; - if (connection->info.role == BLE_ROLE_CENTRAL) { - ret = sd_ble_gap_sec_params_reply(event->evt.gap_evt.conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, nullptr, nullptr); - } else { + if (connection->info.role == BLE_ROLE_PERIPHERAL) { // If peripheral requests to pair, don't notify the event. if (connection->pairState != BLE_PAIRING_STATE_SEC_REQ_SENT) { connection->pairState = BLE_PAIRING_STATE_PAIRING_REQ_RECEIVED; @@ -2325,24 +2436,40 @@ int BleObject::ConnectionsManager::processSecurityEventFromThread(const ble_evt_ linkEvent.conn_handle = connection->info.conn_handle; notifyLinkEvent(linkEvent); } - // User application may call rejectPairing() to update the pairing state in event handler - if (connection->pairState != BLE_PAIRING_STATE_REJECTED) { - ble_gap_sec_params_t secParams = {}; - if (ioCaps_ != BLE_IO_CAPS_NONE) { - secParams.mitm = true; - } - secParams.io_caps = toPlatformIoCaps(ioCaps_); - secParams.oob = false; - secParams.min_key_size = BLE_ENC_MIN_KEY_SIZE; - secParams.max_key_size = BLE_ENC_MAX_KEY_SIZE; - secParams.kdist_own.enc = true; - secParams.kdist_own.id = true; - secParams.kdist_peer.enc = true; - secParams.kdist_peer.id = true; - ret = sd_ble_gap_sec_params_reply(event->evt.gap_evt.conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, &secParams, nullptr); + } + // User application may call rejectPairing() to update the pairing state in event handler + if (connection->pairState == BLE_PAIRING_STATE_REJECTED) { + return ret; + } + ble_gap_sec_params_t secParams = {}; + ble_gap_sec_params_t* pSecParams = nullptr; // For central role, default it to nullptr, since we have set the secure parameters in startPairing(). + ble_gap_sec_keyset_t keyset = {}; + ble_gap_sec_keyset_t* pKeyset = nullptr; // nullptr for BLE legacy pairing + bool reject = false; + if (pairingConfig_.algorithm == BLE_PAIRING_ALGORITHM_LESC_ONLY) { + if (peerSecParams.lesc == false) { + reject = true; + } + } + if (!reject && pairingConfig_.algorithm != BLE_PAIRING_ALGORITHM_LEGACY_ONLY ) { + if (publicKeysPrepare(connection) != SYSTEM_ERROR_NONE) { + LOG(ERROR, "Failed to allocate memory for public keys."); + reject = true; + } else { + keyset.keys_own.p_pk = localPublicKey_.get(); + keyset.keys_peer.p_pk = connection->peerPublicKey.get(); + pKeyset = &keyset; } } + if (!reject && connection->info.role == BLE_ROLE_PERIPHERAL) { + initSecParams(pairingConfig_, secParams); + pSecParams = &secParams; + } + ret = sd_ble_gap_sec_params_reply(event->evt.gap_evt.conn_handle, reject ? BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP : BLE_GAP_SEC_STATUS_SUCCESS, pSecParams, pKeyset); if (ret != NRF_SUCCESS) { + connection->peerPublicKey.reset(); + // At this point, the BLE_GAP_EVT_AUTH_STATUS won't get triggered, + // But we need to notify user that the pairing has failed. connection->pairState = BLE_PAIRING_STATE_NOT_INITIATED; hal_ble_link_evt_t linkEvent = {}; linkEvent.type = BLE_EVT_PAIRING_STATUS_UPDATED; @@ -2356,7 +2483,11 @@ int BleObject::ConnectionsManager::processSecurityEventFromThread(const ble_evt_ connection->pairState = BLE_PAIRING_STATE_PASSKEY_DISPLAY; const ble_gap_evt_passkey_display_t& passkeyDisplay = event->evt.gap_evt.params.passkey_display; hal_ble_link_evt_t linkEvent = {}; - linkEvent.type = BLE_EVT_PAIRING_PASSKEY_DISPLAY; + if (passkeyDisplay.match_request) { + linkEvent.type = BLE_EVT_PAIRING_NUMERIC_COMPARISON; + } else { + linkEvent.type = BLE_EVT_PAIRING_PASSKEY_DISPLAY; + } linkEvent.conn_handle = connection->info.conn_handle; linkEvent.params.passkey_display.passkey = passkeyDisplay.passkey; notifyLinkEvent(linkEvent); @@ -2378,6 +2509,8 @@ int BleObject::ConnectionsManager::processSecurityEventFromThread(const ble_evt_ // LOG_DEBUG(TRACE, "Secure mode: %d, level: %d", secUpdate.conn_sec.sec_mode.sm, secUpdate.conn_sec.sec_mode.lv); } else if (event->header.evt_id == BLE_GAP_EVT_AUTH_STATUS) { LOG_DEBUG(TRACE, "BLE event: BLE_GAP_EVT_AUTH_STATUS"); + // Now we can safely free the memory for peer public key + connection->peerPublicKey.reset(); const ble_gap_evt_auth_status_t& authStatus = event->evt.gap_evt.params.auth_status; LOG_DEBUG(TRACE, "Authen status: 0x%02X, reason: %d, bond: %d, lesc: %d", authStatus.auth_status, authStatus.error_src, authStatus.bonded, authStatus.lesc); if (authStatus.auth_status == BLE_GAP_SEC_STATUS_SUCCESS) { @@ -2392,6 +2525,31 @@ int BleObject::ConnectionsManager::processSecurityEventFromThread(const ble_evt_ linkEvent.params.pairing_status.bonded = authStatus.bonded; linkEvent.params.pairing_status.lesc = authStatus.lesc; notifyLinkEvent(linkEvent); + if (authStatus.lesc) { + /* To protect a device's private key, a device should implement a method to + * prevent an attacker from retrieving useful information about the device's private + * key. For this purpose, a device should change its private key after every pairing + * (successful or failed). Otherwise, it should change its private key whenever S + + * 3F > 8, where S is the number of successful pairings and F the number of + * failed attempts since the key was last changed. */ + generateEcdhKeyPair(); + } + } else if (event->header.evt_id == BLE_GAP_EVT_LESC_DHKEY_REQUEST) { + LOG_DEBUG(TRACE, "BLE event: BLE_GAP_EVT_LESC_DHKEY_REQUEST"); + const ble_gap_evt_lesc_dhkey_request_t& dhkeyReq = event->evt.gap_evt.params.lesc_dhkey_request; + ble_gap_lesc_dhkey_t dhkey = {}; + if (computeDhkey(dhkeyReq.p_pk_peer->pk, dhkey.key) != SYSTEM_ERROR_NONE) { + LOG(ERROR, "Failed to compute DHKey."); + // Set an invalid DHKey to terminate the pairing progress. + mbedtls_default_rng(nullptr, dhkey.key, BLE_GAP_LESC_DHKEY_LEN); + } + int ret = sd_ble_gap_lesc_dhkey_reply(connection->info.conn_handle, &dhkey); + if (ret != NRF_SUCCESS) { + LOG(ERROR, "sd_ble_gap_lesc_dhkey_reply() failed: %d.", ret); + return nrf_system_error(ret); + } + } else { + LOG_DEBUG(TRACE, "Unhandled BLE security event: %d", event->header.evt_id); } return SYSTEM_ERROR_NONE; } @@ -2474,15 +2632,7 @@ void BleObject::ConnectionsManager::processConnectionEvents(const ble_evt_t* eve } case BLE_GAP_EVT_DISCONNECTED: { LOG_DEBUG(TRACE, "BLE GAP event: disconnected."); - ble_evt_t* disconnectedEvent = (ble_evt_t*)BleObject::getInstance().dispatcher()->allocEventData(sizeof(ble_evt_t)); - if (!disconnectedEvent) { - LOG(ERROR, "Allocate memory for BLE event failed."); - // Assert it since the connection is cached but invalid for further reference. - SPARK_ASSERT(false); - break; - } - memcpy(disconnectedEvent, event, sizeof(ble_evt_t)); - BleObject::getInstance().dispatcher()->enqueue(&disconnectedEvent); + BleObject::getInstance().dispatcher()->enqueue(event); break; } case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: { @@ -2495,30 +2645,18 @@ void BleObject::ConnectionsManager::processConnectionEvents(const ble_evt_t* eve } case BLE_GAP_EVT_CONN_PARAM_UPDATE: { LOG_DEBUG(TRACE, "BLE GAP event: connection parameters updated."); - ble_evt_t* connParamsUpdateEvent = (ble_evt_t*)BleObject::getInstance().dispatcher()->allocEventData(sizeof(ble_evt_t)); - if (!connParamsUpdateEvent) { - LOG(ERROR, "Allocate memory for BLE event failed."); - SPARK_ASSERT(false); - break; - } - memcpy(connParamsUpdateEvent, event, sizeof(ble_evt_t)); - BleObject::getInstance().dispatcher()->enqueue(&connParamsUpdateEvent); + BleObject::getInstance().dispatcher()->enqueue(event); break; } + case BLE_GAP_EVT_SEC_INFO_REQUEST: + case BLE_GAP_EVT_LESC_DHKEY_REQUEST: case BLE_GAP_EVT_PASSKEY_DISPLAY: case BLE_GAP_EVT_AUTH_KEY_REQUEST: case BLE_GAP_EVT_SEC_REQUEST: case BLE_GAP_EVT_SEC_PARAMS_REQUEST: case BLE_GAP_EVT_CONN_SEC_UPDATE: case BLE_GAP_EVT_AUTH_STATUS: { - ble_evt_t* secEvent = (ble_evt_t*)BleObject::getInstance().dispatcher()->allocEventData(sizeof(ble_evt_t)); - if (!secEvent) { - LOG(ERROR, "Allocate memory for BLE event failed."); - SPARK_ASSERT(false); - break; - } - memcpy(secEvent, event, sizeof(ble_evt_t)); - BleObject::getInstance().dispatcher()->enqueue(&secEvent); + BleObject::getInstance().dispatcher()->enqueue(event); break; } case BLE_GAP_EVT_TIMEOUT: { @@ -2539,14 +2677,7 @@ void BleObject::ConnectionsManager::processConnectionEvents(const ble_evt_t* eve LOG_DEBUG(TRACE, "sd_ble_gatts_exchange_mtu_reply() failed: %d", ret); break; } - ble_evt_t* attMtuExchangeEvent = (ble_evt_t*)BleObject::getInstance().dispatcher()->allocEventData(sizeof(ble_evt_t)); - if (!attMtuExchangeEvent) { - LOG(ERROR, "Allocate memory for BLE event failed."); - SPARK_ASSERT(false); - break; - } - memcpy(attMtuExchangeEvent, event, sizeof(ble_evt_t)); - BleObject::getInstance().dispatcher()->enqueue(&attMtuExchangeEvent); + BleObject::getInstance().dispatcher()->enqueue(event); break; } case BLE_GATTC_EVT_EXCHANGE_MTU_RSP: { @@ -4060,6 +4191,13 @@ int hal_ble_gap_set_pairing_config(const hal_ble_pairing_config_t* config, void* return BleObject::getInstance().connMgr()->setPairingConfig(config); } +int hal_ble_gap_get_pairing_config(hal_ble_pairing_config_t* config, void* reserved) { + BleLock lk; + LOG_DEBUG(TRACE, "hal_ble_gap_get_pairing_config()."); + CHECK_TRUE(BleObject::getInstance().initialized(), SYSTEM_ERROR_INVALID_STATE); + return BleObject::getInstance().connMgr()->getPairingConfig(config); +} + int hal_ble_gap_start_pairing(hal_ble_conn_handle_t conn_handle, void* reserved) { BleLock lk; LOG_DEBUG(TRACE, "hal_ble_gap_start_pairing()."); @@ -4074,11 +4212,23 @@ int hal_ble_gap_reject_pairing(hal_ble_conn_handle_t conn_handle, void* reserved return BleObject::getInstance().connMgr()->rejectPairing(conn_handle); } -int hal_ble_gap_set_pairing_passkey(hal_ble_conn_handle_t conn_handle, const uint8_t* passkey, void* reserved) { +int hal_ble_gap_set_pairing_auth_data(hal_ble_conn_handle_t conn_handle, const hal_ble_pairing_auth_data_t* auth, void* reserved) { + BleLock lk; + LOG_DEBUG(TRACE, "hal_ble_gap_set_pairing_auth_data()."); + CHECK_TRUE(BleObject::getInstance().initialized(), SYSTEM_ERROR_INVALID_STATE); + return BleObject::getInstance().connMgr()->setPairingAuthData(conn_handle, auth); +} + +int hal_ble_gap_set_pairing_passkey_deprecated(hal_ble_conn_handle_t conn_handle, const uint8_t* passkey, void* reserved) { BleLock lk; - LOG_DEBUG(TRACE, "hal_ble_gap_set_pairing_passkey()."); + LOG_DEBUG(TRACE, "hal_ble_gap_set_pairing_passkey_deprecated()."); CHECK_TRUE(BleObject::getInstance().initialized(), SYSTEM_ERROR_INVALID_STATE); - return BleObject::getInstance().connMgr()->setPairingPasskey(conn_handle, passkey); + hal_ble_pairing_auth_data_t auth = {}; + auth.version = BLE_API_VERSION; + auth.size = sizeof(hal_ble_pairing_auth_data_t); + auth.type = BLE_PAIRING_AUTH_DATA_PASSKEY; + auth.params.passkey = passkey; + return BleObject::getInstance().connMgr()->setPairingAuthData(conn_handle, &auth); } bool hal_ble_gap_is_pairing(hal_ble_conn_handle_t conn_handle, void* reserved) { diff --git a/user/tests/wiring/api/ble.cpp b/user/tests/wiring/api/ble.cpp index f1fc6c8051..3ba8bcaaff 100644 --- a/user/tests/wiring/api/ble.cpp +++ b/user/tests/wiring/api/ble.cpp @@ -50,7 +50,7 @@ void pairingEventHandlerFunc(const BlePairingEvent& event, void* context) { (void)status; (void)bonded; (void)lesc; - } else if (event.type == BlePairingEventType::PASSKEY_DISPLAY) { + } else if (event.type == BlePairingEventType::PASSKEY_DISPLAY || event.type == BlePairingEventType::NUMERIC_COMPARISON) { uint8_t key = event.payload.passkey[0]; (void)key; } else if (event.type == BlePairingEventType::PASSKEY_INPUT) { @@ -170,6 +170,14 @@ test(ble_uuid_order) { (void)order; } +test(ble_pairing_algorithm) { + BlePairingAlgorithm algorithm; + API_COMPILE({ algorithm = BlePairingAlgorithm::LEGACY_ONLY; }); + API_COMPILE({ algorithm = BlePairingAlgorithm::LESC_ONLY; }); + API_COMPILE({ algorithm = BlePairingAlgorithm::AUTO; }); + (void)algorithm; +} + test(ble_pairing_io_caps) { BlePairingIoCaps ioCaps; API_COMPILE({ ioCaps = BlePairingIoCaps::NONE; }); @@ -186,6 +194,7 @@ test(ble_pairing_event_type) { API_COMPILE({ type = BlePairingEventType::PASSKEY_DISPLAY; }); API_COMPILE({ type = BlePairingEventType::PASSKEY_INPUT; }); API_COMPILE({ type = BlePairingEventType::STATUS_UPDATED; }); + API_COMPILE({ type = BlePairingEventType::NUMERIC_COMPARISON; }); (void)type; } @@ -787,9 +796,11 @@ test(ble_local_device_class) { API_COMPILE({ BLE.onDisconnected(std::bind(&Handlers::disconnectedHandler, &bleHandlerInstance, _1)); }); API_COMPILE({ int ret = BLE.setPairingIoCaps(BlePairingIoCaps::NONE); (void)ret; }); + API_COMPILE({ int ret = BLE.setPairingAlgorithm(BlePairingAlgorithm::AUTO); (void)ret; }); API_COMPILE({ int ret = BLE.startPairing(BlePeerDevice()); (void)ret; }); API_COMPILE({ int ret = BLE.rejectPairing(BlePeerDevice()); (void)ret; }); API_COMPILE({ uint8_t passkey[6]; int ret = BLE.setPairingPasskey(BlePeerDevice(), passkey); (void)ret; }); + API_COMPILE({ int ret = BLE.setPairingNumericComparison(BlePeerDevice(), true); (void)ret; }); API_COMPILE({ bool ret = BLE.isPairing(BlePeerDevice()); (void)ret; }); API_COMPILE({ bool ret = BLE.isPaired(BlePeerDevice()); (void)ret; }); API_COMPILE({ BLE.onPairingEvent(pairingEventHandlerFunc); }); diff --git a/user/tests/wiring/ble_central_peripheral/ble_central/central.cpp b/user/tests/wiring/ble_central_peripheral/ble_central/central.cpp index ef06fb8fa0..586785671f 100644 --- a/user/tests/wiring/ble_central_peripheral/ble_central/central.cpp +++ b/user/tests/wiring/ble_central_peripheral/ble_central/central.cpp @@ -275,197 +275,160 @@ test(BLE_22_Central_Pairing_Sync) { static bool pairingRequested = false; static int pairingStatus = -1; - -static void pairingTestRoutine(bool request) { +static bool lesc; +const BlePairingIoCaps pairingIoCaps[5] = { + BlePairingIoCaps::NONE, + BlePairingIoCaps::DISPLAY_ONLY, + BlePairingIoCaps::DISPLAY_YESNO, + BlePairingIoCaps::KEYBOARD_ONLY, + BlePairingIoCaps::KEYBOARD_DISPLAY +}; +const char* ioCapsStr[5] = { + "BlePairingIoCaps::NONE", + "BlePairingIoCaps::DISPLAY_ONLY", + "BlePairingIoCaps::DISPLAY_YESNO", + "BlePairingIoCaps::KEYBOARD_ONLY", + "BlePairingIoCaps::KEYBOARD_DISPLAY" +}; + +static void pairingTestRoutine(bool request, BlePairingAlgorithm algorithm) { peer = BLE.connect(peerAddr, false); assertTrue(peer.connected()); - - pairingStatus = -1; - pairingRequested = false; - BLE.onPairingEvent([&](const BlePairingEvent& event) { - if (event.type == BlePairingEventType::REQUEST_RECEIVED) { - // Serial.println("Request received"); - pairingRequested = true; - } else if (event.type == BlePairingEventType::STATUS_UPDATED) { - pairingStatus = event.payload.status.status; - // Serial.println("status updateed"); - } else if (event.type == BlePairingEventType::PASSKEY_DISPLAY) { - Serial.print("Please enter the following passkey on the other side: "); - for (uint8_t i = 0; i < BLE_PAIRING_PASSKEY_LEN; i++) { - Serial.printf("%c", event.payload.passkey[i]); - } - Serial.println(""); - } else if (event.type == BlePairingEventType::PASSKEY_INPUT) { - Serial.print("Please enter 6-digits passkey displayed on the other side: "); - uint8_t i = 0; - uint8_t passkey[BLE_PAIRING_PASSKEY_LEN]; - while (i < BLE_PAIRING_PASSKEY_LEN) { - if (Serial.available()) { - passkey[i] = Serial.read(); - Serial.write(passkey[i++]); + { + SCOPE_GUARD ({ + delay(500); + assertEqual(BLE.disconnect(peer), (int)SYSTEM_ERROR_NONE); + assertFalse(BLE.connected()); + delay(500); + }); + + pairingStatus = -1; + pairingRequested = false; + BLE.onPairingEvent([&](const BlePairingEvent& event) { + if (event.type == BlePairingEventType::REQUEST_RECEIVED) { + // Serial.println("Request received"); + pairingRequested = true; + } else if (event.type == BlePairingEventType::STATUS_UPDATED) { + pairingStatus = event.payload.status.status; + lesc = event.payload.status.lesc; + // Serial.println("status updateed"); + } else if (event.type == BlePairingEventType::PASSKEY_DISPLAY || event.type == BlePairingEventType::NUMERIC_COMPARISON) { + Serial.print("Passkey display: "); + for (uint8_t i = 0; i < BLE_PAIRING_PASSKEY_LEN; i++) { + Serial.printf("%c", event.payload.passkey[i]); + } + Serial.println(""); + if (event.type == BlePairingEventType::NUMERIC_COMPARISON) { + while (Serial.available()) { + Serial.read(); + } + Serial.print("Please confirm if the passkey is identical (y/n): "); + while (!Serial.available()); + char c = Serial.read(); + Serial.write(c); + Serial.println(""); + BLE.setPairingNumericComparison(event.peer, (c == 'y') ? true : false); + } + } else if (event.type == BlePairingEventType::PASSKEY_INPUT) { + while (Serial.available()) { + Serial.read(); } + Serial.print("Passkey input (must be identical to the peer's): "); + uint8_t i = 0; + uint8_t passkey[BLE_PAIRING_PASSKEY_LEN]; + while (i < BLE_PAIRING_PASSKEY_LEN) { + if (Serial.available()) { + passkey[i] = Serial.read(); + Serial.write(passkey[i++]); + } + } + Serial.println(""); + BLE.setPairingPasskey(event.peer, passkey); } - Serial.println(""); - BLE.setPairingPasskey(event.peer, passkey); - } - }); + }); - if (request) { - assertEqual(BLE.startPairing(peer), (int)SYSTEM_ERROR_NONE); - } else { - assertTrue(waitFor([&]{ return pairingRequested; }, 5000)); + if (request) { + assertEqual(BLE.startPairing(peer), (int)SYSTEM_ERROR_NONE); + } else { + assertTrue(waitFor([&]{ return pairingRequested; }, 5000)); + } + assertTrue(BLE.isPairing(peer)); + assertTrue(waitFor([&]{ return !BLE.isPairing(peer); }, 20000)); + assertTrue(BLE.isPaired(peer)); + assertEqual(pairingStatus, (int)SYSTEM_ERROR_NONE); + if (algorithm != BlePairingAlgorithm::LEGACY_ONLY) { + assertTrue(lesc); + } else { + assertFalse(lesc); + } } - assertTrue(BLE.isPairing(peer)); - assertTrue(waitFor([&]{ return !BLE.isPairing(peer); }, 20000)); - assertTrue(BLE.isPaired(peer)); - assertEqual(pairingStatus, (int)SYSTEM_ERROR_NONE); - - delay(500); - BLE.disconnect(peer); - delay(500); -} - -test(BLE_23_Central_Initiate_Pairing_Central_Io_Caps_None_Peripheral_Io_Caps_None) { - BLE.setPairingIoCaps(BlePairingIoCaps::NONE); - pairingTestRoutine(true); -} - -test(BLE_24_Central_Pairing_Receiption_Central_Io_Caps_None_Peripheral_Io_Caps_Dispaly_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::NONE); - pairingTestRoutine(false); -} - -test(BLE_25_Central_Initiate_Pairing_Central_Io_Caps_None_Peripheral_Io_Caps_Dispaly_Yesno) { - BLE.setPairingIoCaps(BlePairingIoCaps::NONE); - pairingTestRoutine(true); -} - -test(BLE_26_Central_Pairing_Receiption_Central_Io_Caps_None_Peripheral_Io_Caps_Keyboard_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::NONE); - pairingTestRoutine(false); -} - -test(BLE_27_Central_Initiate_Pairing_Central_Io_Caps_None_Peripheral_Io_Caps_Keyboard_Dispaly) { - BLE.setPairingIoCaps(BlePairingIoCaps::NONE); - pairingTestRoutine(true); -} - -test(BLE_28_Central_Pairing_Receiption_Central_Io_Caps_Dispaly_Only_Peripheral_Io_Caps_None) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); - pairingTestRoutine(false); -} - -test(BLE_29_Central_Initiate_Pairing_Central_Io_Caps_Dispaly_Only_Peripheral_Io_Caps_Display_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); - pairingTestRoutine(true); -} - -test(BLE_30_Central_Pairing_Receiption_Central_Io_Caps_Dispaly_Only_Peripheral_Io_Caps_Display_Yesno) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); - pairingTestRoutine(false); -} - -test(BLE_31_Central_Initiate_Pairing_Central_Io_Caps_Dispaly_Only_Peripheral_Io_Caps_Keyboard_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); - pairingTestRoutine(true); -} - -test(BLE_32_Central_Pairing_Receiption_Central_Io_Caps_Dispaly_Only_Peripheral_Io_Caps_Keyboard_Display) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); - pairingTestRoutine(false); -} - -test(BLE_33_Central_Initiate_Pairing_Central_Io_Caps_Dispaly_Yesno_Peripheral_Io_Caps_None) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); - pairingTestRoutine(true); -} - -test(BLE_34_Central_Pairing_Receiption_Central_Io_Caps_Dispaly_Yesno_Peripheral_Io_Caps_Display_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); - pairingTestRoutine(false); -} - -test(BLE_35_Central_Initiate_Pairing_Central_Io_Caps_Dispaly_Yesno_Peripheral_Io_Caps_Display_Yesno) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); - pairingTestRoutine(true); -} - -test(BLE_36_Central_Pairing_Receiption_Central_Io_Caps_Dispaly_Yesno_Peripheral_Io_Caps_Keyboard_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); - pairingTestRoutine(false); -} - -test(BLE_37_Central_Initiate_Pairing_Central_Io_Caps_Dispaly_Yesno_Peripheral_Io_Caps_Keyboard_Display) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); - pairingTestRoutine(true); -} - -test(BLE_38_Central_Pairing_Receiption_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_None) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); - pairingTestRoutine(false); -} - -test(BLE_39_Central_Initiate_Pairing_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_Display_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); - pairingTestRoutine(true); -} - -test(BLE_40_Central_Pairing_Receiption_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_Display_Yesno) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); - pairingTestRoutine(false); -} - -test(BLE_41_Central_Initiate_Pairing_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_Keyboard_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); - Serial.println("Please enter the same 6-digits key on each side"); - pairingTestRoutine(true); } -test(BLE_42_Central_Pairing_Receiption_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_Keyboard_Display) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); - pairingTestRoutine(false); -} - -test(BLE_43_Central_Initiate_Pairing_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_None) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); - pairingTestRoutine(true); +test(BLE_24_Central_Pairing_Algorithm_Auto_Io_Caps) { + for (uint8_t l = 0; l < 5; l++) { // Local I/O capabilities + assertEqual(BLE.setPairingIoCaps(pairingIoCaps[l]), (int)SYSTEM_ERROR_NONE); + for (uint8_t p = 0; p < 5; p++) { // Peer I/O capabilities + Serial.printlnf("Local I/O Caps: %s", ioCapsStr[l]); + pairingTestRoutine(l % 2, BlePairingAlgorithm::AUTO); + } + } } -test(BLE_44_Central_Pairing_Receiption_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_Display_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); - pairingTestRoutine(false); +test(BLE_25_Central_Pairing_Algorithm_Legacy_Only) { + assertEqual(BLE.setPairingIoCaps(BlePairingIoCaps::NONE), (int)SYSTEM_ERROR_NONE); + assertEqual(BLE.setPairingAlgorithm(BlePairingAlgorithm::LEGACY_ONLY), (int)SYSTEM_ERROR_NONE); + pairingTestRoutine(true, BlePairingAlgorithm::LEGACY_ONLY); } -test(BLE_45_Central_Initiate_Pairing_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_Display_Yesno) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); - pairingTestRoutine(true); -} +test(BLE_26_Central_Pairing_Algorithm_Lesc_Only_Reject_Legacy) { + peer = BLE.connect(peerAddr, false); + assertTrue(peer.connected()); + { + SCOPE_GUARD ({ + delay(500); + assertEqual(BLE.disconnect(peer), (int)SYSTEM_ERROR_NONE); + assertFalse(BLE.connected()); + delay(500); + }); + + pairingStatus = -1; + BLE.onPairingEvent([&](const BlePairingEvent& event) { + if (event.type == BlePairingEventType::STATUS_UPDATED) { + pairingStatus = event.payload.status.status; + // Serial.println("status updateed"); + } + }); -test(BLE_46_Central_Pairing_Receiption_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_Keyboard_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); - pairingTestRoutine(false); -} + assertEqual(BLE.setPairingIoCaps(BlePairingIoCaps::NONE), (int)SYSTEM_ERROR_NONE); + assertEqual(BLE.setPairingAlgorithm(BlePairingAlgorithm::LESC_ONLY), (int)SYSTEM_ERROR_NONE); -test(BLE_47_Central_Initiate_Pairing_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_Keyboard_Display) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); - pairingTestRoutine(true); + assertEqual(BLE.startPairing(peer), (int)SYSTEM_ERROR_NONE); + assertTrue(BLE.isPairing(peer)); + assertTrue(waitFor([&]{ return !BLE.isPairing(peer); }, 20000)); + assertFalse(BLE.isPaired(peer)); + assertNotEqual(pairingStatus, (int)SYSTEM_ERROR_NONE); + } } -test(BLE_48_Central_Initiate_Pairing_Peripheral_Being_Reject) { +test(BLE_27_Central_Initiate_Pairing_Being_Rejected) { peer = BLE.connect(peerAddr, false); assertTrue(peer.connected()); + { + SCOPE_GUARD ({ + delay(500); + assertEqual(BLE.disconnect(peer), (int)SYSTEM_ERROR_NONE); + assertFalse(BLE.connected()); + delay(500); + }); - assertEqual(BLE.startPairing(peer), (int)SYSTEM_ERROR_NONE); - assertTrue(BLE.isPairing(peer)); - assertTrue(waitFor([&]{ return !BLE.isPairing(peer); }, 20000)); - assertFalse(BLE.isPaired(peer)); - - delay(500); - BLE.disconnect(peer); - delay(500); + assertEqual(BLE.startPairing(peer), (int)SYSTEM_ERROR_NONE); + assertTrue(BLE.isPairing(peer)); + assertTrue(waitFor([&]{ return !BLE.isPairing(peer); }, 20000)); + assertFalse(BLE.isPaired(peer)); + } } -test(BLE_49_Central_Pairing_Receiption_Reject) { +test(BLE_28_Central_Pairing_Receiption_Reject) { pairingStatus = 0; BLE.onPairingEvent([&](const BlePairingEvent& event) { if (event.type == BlePairingEventType::REQUEST_RECEIVED) { @@ -478,12 +441,16 @@ test(BLE_49_Central_Pairing_Receiption_Reject) { peer = BLE.connect(peerAddr, false); assertTrue(peer.connected()); - - assertTrue(waitFor([]{ return pairingStatus != 0; }, 5000)); - - delay(500); - BLE.disconnect(peer); - delay(500); + { + SCOPE_GUARD ({ + delay(500); + assertEqual(BLE.disconnect(peer), (int)SYSTEM_ERROR_NONE); + assertFalse(BLE.connected()); + delay(500); + }); + + assertTrue(waitFor([]{ return pairingStatus != 0; }, 5000)); + } } #endif // #if Wiring_BLE == 1 diff --git a/user/tests/wiring/ble_central_peripheral/ble_peripheral/peripheral.cpp b/user/tests/wiring/ble_central_peripheral/ble_peripheral/peripheral.cpp index 55213d5796..67b80eac28 100644 --- a/user/tests/wiring/ble_central_peripheral/ble_peripheral/peripheral.cpp +++ b/user/tests/wiring/ble_central_peripheral/ble_peripheral/peripheral.cpp @@ -91,6 +91,13 @@ test(BLE_01_Peripheral_Advertising) { ret = BLE.advertise(&advData, &srData); assertEqual(ret, 0); + BLE.onConnected([](const BlePeerDevice& peer) { + Serial.println("Connected."); + }); + BLE.onDisconnected([](const BlePeerDevice& peer) { + Serial.println("Disconnected."); + }); + Serial.println("BLE starts advertising..."); assertTrue(BLE.advertising()); @@ -98,7 +105,6 @@ test(BLE_01_Peripheral_Advertising) { test(BLE_02_Peripheral_Connected) { assertTrue(waitFor(BLE.connected, 20000)); - Serial.println("BLE connected."); } // For the first data transmission, we need to wait longer to make sure @@ -187,16 +193,31 @@ test(BLE_18_Peripheral_Notify_Characteristic_With_Notify_Indicate_Property_Nack) test(BLE_19_Peripheral_Pairing_Sync) { // The central will perform some service and characteristic discovery tests - // before starting thee pairing tests + // before starting the pairing tests assertTrue(waitFor([]{ return pairingStarted; }, 20000)); assertTrue(waitFor([]{ return !BLE.connected(); }, 5000)); } static bool pairingRequested = false; static int pairingStatus = -1; +static bool lesc; static BlePeerDevice peer; - -static void pairingTestRoutine(bool request) { +const BlePairingIoCaps pairingIoCaps[5] = { + BlePairingIoCaps::NONE, + BlePairingIoCaps::DISPLAY_ONLY, + BlePairingIoCaps::DISPLAY_YESNO, + BlePairingIoCaps::KEYBOARD_ONLY, + BlePairingIoCaps::KEYBOARD_DISPLAY +}; +const char* ioCapsStr[5] = { + "BlePairingIoCaps::NONE", + "BlePairingIoCaps::DISPLAY_ONLY", + "BlePairingIoCaps::DISPLAY_YESNO", + "BlePairingIoCaps::KEYBOARD_ONLY", + "BlePairingIoCaps::KEYBOARD_DISPLAY" +}; + +static void pairingTestRoutine(bool request, BlePairingAlgorithm algorithm) { pairingRequested = false; pairingStatus = -1; @@ -207,15 +228,30 @@ static void pairingTestRoutine(bool request) { // Serial.println("Request received"); } else if (event.type == BlePairingEventType::STATUS_UPDATED) { pairingStatus = event.payload.status.status; + lesc = event.payload.status.lesc; // Serial.println("status updateed"); - } else if (event.type == BlePairingEventType::PASSKEY_DISPLAY) { + } else if (event.type == BlePairingEventType::PASSKEY_DISPLAY || event.type == BlePairingEventType::NUMERIC_COMPARISON) { Serial.print("Passkey display: "); for (uint8_t i = 0; i < BLE_PAIRING_PASSKEY_LEN; i++) { Serial.printf("%c", event.payload.passkey[i]); } Serial.println(""); + if (event.type == BlePairingEventType::NUMERIC_COMPARISON) { + while (Serial.available()) { + Serial.read(); + } + Serial.print("Please confirm if the passkey is identical (y/n): "); + while (!Serial.available()); + char c = Serial.read(); + Serial.write(c); + Serial.println(""); + BLE.setPairingNumericComparison(event.peer, (c == 'y') ? true : false); + } } else if (event.type == BlePairingEventType::PASSKEY_INPUT) { - Serial.print("Passkey input: "); + while (Serial.available()) { + Serial.read(); + } + Serial.print("Passkey input (must be identical to the peer's): "); uint8_t i = 0; uint8_t passkey[BLE_PAIRING_PASSKEY_LEN]; while (i < BLE_PAIRING_PASSKEY_LEN) { @@ -230,148 +266,75 @@ static void pairingTestRoutine(bool request) { }); assertTrue(waitFor(BLE.connected, 20000)); + { + SCOPE_GUARD ({ + assertTrue(waitFor([]{ return !BLE.connected(); }, 5000)); + assertFalse(BLE.connected()); + }); + + if (request) { + peer = BLE.peerCentral(); + Serial.println("Start pairing..."); + assertEqual(BLE.startPairing(peer), (int)SYSTEM_ERROR_NONE); + } else { + assertTrue(waitFor([&]{ return pairingRequested; }, 20000)); + } + assertTrue(BLE.isPairing(peer)); + assertTrue(waitFor([&]{ return !BLE.isPairing(peer); }, 20000)); + assertTrue(BLE.isPaired(peer)); + assertEqual(pairingStatus, (int)SYSTEM_ERROR_NONE); + if (algorithm != BlePairingAlgorithm::LEGACY_ONLY) { + assertTrue(lesc); + } else { + assertFalse(lesc); + } - if (request) { - peer = BLE.peerCentral(); - assertEqual(BLE.startPairing(peer), (int)SYSTEM_ERROR_NONE); - } else { - assertTrue(waitFor([&]{ return pairingRequested; }, 20000)); } - assertTrue(BLE.isPairing(peer)); - assertTrue(waitFor([&]{ return !BLE.isPairing(peer); }, 20000)); - assertTrue(BLE.isPaired(peer)); - assertEqual(pairingStatus, (int)SYSTEM_ERROR_NONE); - - assertTrue(waitFor([]{ return !BLE.connected(); }, 5000)); -} - -test(BLE_20_Peripheral_Pairing_Receiption_Central_Io_Caps_None_Peripheral_Io_Caps_None) { - BLE.setPairingIoCaps(BlePairingIoCaps::NONE); - pairingTestRoutine(false); -} - -test(BLE_21_Peripheral_Initiate_Pairing_Central_Io_Caps_None_Peripheral_Io_Caps_Dispaly_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); - pairingTestRoutine(true); -} - -test(BLE_22_Peripheral_Pairing_Receiption_Cnetral_Io_Caps_None_Peripheral_Io_Caps_Dispaly_Yesno) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); - pairingTestRoutine(false); -} - -test(BLE_23_Peripheral_Initiate_Pairing_Central_Io_Caps_None_Peripheral_Io_Caps_Keyboard_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); - pairingTestRoutine(true); -} - -test(BLE_24_Peripheral_Pairing_Receiption_Central_Io_Caps_None_Peripheral_Io_Caps_Keyboard_Dispaly) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); - pairingTestRoutine(false); -} - -test(BLE_25_Peripheral_Initiate_Pairing_Central_Io_Caps_Display_Only_Peripheral_Io_Caps_None) { - BLE.setPairingIoCaps(BlePairingIoCaps::NONE); - pairingTestRoutine(true); -} - -test(BLE_26_Peripheral_Pairing_Receiption_Central_Io_Caps_Display_Only_Peripheral_Io_Caps_Dispaly_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); - pairingTestRoutine(false); -} - -test(BLE_27_Peripheral_Initiate_Pairing_Central_Io_Caps_Display_Only_Peripheral_Io_Caps_Display_Yesno) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); - pairingTestRoutine(true); -} - -test(BLE_28_Peripheral_Pairing_Receiption_Central_Io_Caps_Display_Only_Peripheral_Io_Caps_Keyboard_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); - pairingTestRoutine(false); -} - -test(BLE_29_Peripheral_Initiate_Pairing_Central_Io_Caps_Display_Only_Peripheral_Io_Caps_Keyboard_Display) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); - pairingTestRoutine(true); -} - -test(BLE_30_Peripheral_Pairing_Receiption_Central_Io_Caps_Display_Yesno_Peripheral_Io_Caps_None) { - BLE.setPairingIoCaps(BlePairingIoCaps::NONE); - pairingTestRoutine(false); -} - -test(BLE_31_Peripheral_Initiate_Pairing_Central_Io_Caps_Display_Yesno_Peripheral_Io_Caps_Display_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); - pairingTestRoutine(true); } -test(BLE_32_Peripheral_Pairing_Receiption_Central_Io_Caps_Display_Yesno_Peripheral_Io_Caps_Display_Yesno) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); - pairingTestRoutine(false); -} - -test(BLE_33_Peripheral_Initiate_Pairing_Central_Io_Caps_Display_Yesno_Peripheral_Io_Caps_Keyboard_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); - pairingTestRoutine(true); -} - -test(BLE_34_Peripheral_Pairing_Receiption_Central_Io_Caps_Display_Yesno_Peripheral_Io_Caps_Keyboard_Display) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); - pairingTestRoutine(false); -} - -test(BLE_35_Peripheral_Initiate_Pairing_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_None) { - BLE.setPairingIoCaps(BlePairingIoCaps::NONE); - pairingTestRoutine(true); -} - -test(BLE_36_Peripheral_Pairing_Receiption_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_Display_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); - pairingTestRoutine(false); -} - -test(BLE_37_Peripheral_Initiate_Pairing_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_Display_Yesno) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); - pairingTestRoutine(true); -} - -test(BLE_38_Peripheral_Pairing_Receiption_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_Keyboard_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); - Serial.println("Please enter the same 6-digits key on each side"); - pairingTestRoutine(false); -} - -test(BLE_39_Peripheral_Initiate_Pairing_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_Keyboard_Display) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); - pairingTestRoutine(true); -} - -test(BLE_40_Peripheral_Pairing_Receiption_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_None) { - BLE.setPairingIoCaps(BlePairingIoCaps::NONE); - pairingTestRoutine(false); +test(BLE_20_Peripheral_Pairing_Algorithm_Auto_Io_Caps) { + for (uint8_t p = 0; p < 5; p++) { // Peer I/O capabilities + for (uint8_t l = 0; l < 5; l++) { + assertEqual(BLE.setPairingIoCaps(pairingIoCaps[l]), (int)SYSTEM_ERROR_NONE); // Local I/O capabilities + Serial.printlnf("Local I/O Caps: %s", ioCapsStr[l]); + pairingTestRoutine(!(p % 2), BlePairingAlgorithm::AUTO); + } + } } -test(BLE_41_Peripheral_Initiate_Pairing_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_Display_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); - pairingTestRoutine(true); +test(BLE_21_Peripheral_Pairing_Algorithm_Legacy_Only) { + assertEqual(BLE.setPairingIoCaps(BlePairingIoCaps::NONE), (int)SYSTEM_ERROR_NONE); + assertEqual(BLE.setPairingAlgorithm(BlePairingAlgorithm::LEGACY_ONLY), (int)SYSTEM_ERROR_NONE); + pairingTestRoutine(false, BlePairingAlgorithm::LEGACY_ONLY); } -test(BLE_42_Peripheral_Pairing_Receiption_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_Display_Yesno) { - BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); - pairingTestRoutine(false); -} +test(BLE_22_Peripheral_Pairing_Algorithm_Legacy_Only_Being_Rejected) { + pairingRequested = false; + BLE.onPairingEvent([&](const BlePairingEvent& event) { + if (event.type == BlePairingEventType::REQUEST_RECEIVED) { + pairingRequested = true; + peer = event.peer; + // Serial.println("Request received"); + } + }); + assertEqual(BLE.setPairingIoCaps(BlePairingIoCaps::NONE), (int)SYSTEM_ERROR_NONE); + assertEqual(BLE.setPairingAlgorithm(BlePairingAlgorithm::LEGACY_ONLY), (int)SYSTEM_ERROR_NONE); -test(BLE_43_Peripheral_Initiate_Pairing_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_Keyboard_Only) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); - pairingTestRoutine(true); -} + assertTrue(waitFor(BLE.connected, 20000)); + { + SCOPE_GUARD ({ + assertTrue(waitFor([]{ return !BLE.connected(); }, 5000)); + assertFalse(BLE.connected()); + }); -test(BLE_44_Peripheral_Pairing_Receiption_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_Keyboard_Display) { - BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); - pairingTestRoutine(false); + assertTrue(waitFor([&]{ return pairingRequested; }, 20000)); + assertTrue(BLE.isPairing(peer)); + assertTrue(waitFor([&]{ return !BLE.isPairing(peer); }, 20000)); + assertFalse(BLE.isPaired(peer)); + } } -test(BLE_45_Peripheral_Pairing_Receiption_Reject) { +test(BLE_23_Peripheral_Pairing_Receiption_Reject) { pairingStatus = 0; BLE.onPairingEvent([&](const BlePairingEvent& event) { if (event.type == BlePairingEventType::REQUEST_RECEIVED) { @@ -383,21 +346,31 @@ test(BLE_45_Peripheral_Pairing_Receiption_Reject) { }); assertTrue(waitFor(BLE.connected, 20000)); + { + SCOPE_GUARD ({ + assertTrue(waitFor([]{ return !BLE.connected(); }, 5000)); + assertFalse(BLE.connected()); + }); - assertTrue(waitFor([]{ return pairingStatus != 0; }, 5000)); - - assertTrue(waitFor([]{ return !BLE.connected(); }, 5000)); + assertTrue(waitFor([]{ return pairingStatus != 0; }, 5000)); + } } -test(BLE_46_Peripheral_Initiate_Pairing_Being_Rejected) { +test(BLE_24_Peripheral_Initiate_Pairing_Being_Rejected) { assertTrue(waitFor(BLE.connected, 20000)); + { + SCOPE_GUARD ({ + assertTrue(waitFor([]{ return !BLE.connected(); }, 5000)); + assertFalse(BLE.connected()); + }); - peer = BLE.peerCentral(); - assertEqual(BLE.startPairing(peer), (int)SYSTEM_ERROR_NONE); + peer = BLE.peerCentral(); + assertEqual(BLE.startPairing(peer), (int)SYSTEM_ERROR_NONE); - assertTrue(BLE.isPairing(peer)); - assertTrue(waitFor([&]{ return !BLE.isPairing(peer); }, 20000)); - assertFalse(BLE.isPaired(peer)); + assertTrue(BLE.isPairing(peer)); + assertTrue(waitFor([&]{ return !BLE.isPairing(peer); }, 20000)); + assertFalse(BLE.isPaired(peer)); + } } #endif // #if Wiring_BLE == 1 diff --git a/wiring/inc/spark_wiring_ble.h b/wiring/inc/spark_wiring_ble.h index cc15944dbe..24bdbb679b 100644 --- a/wiring/inc/spark_wiring_ble.h +++ b/wiring/inc/spark_wiring_ble.h @@ -155,11 +155,18 @@ enum class BlePairingIoCaps : uint8_t { KEYBOARD_DISPLAY = BLE_IO_CAPS_KEYBOARD_DISPLAY }; +enum class BlePairingAlgorithm : uint8_t { + AUTO = BLE_PAIRING_ALGORITHM_AUTO, + LEGACY_ONLY = BLE_PAIRING_ALGORITHM_LEGACY_ONLY, + LESC_ONLY = BLE_PAIRING_ALGORITHM_LESC_ONLY +}; + enum class BlePairingEventType : uint8_t { REQUEST_RECEIVED = BLE_EVT_PAIRING_REQUEST_RECEIVED, PASSKEY_DISPLAY = BLE_EVT_PAIRING_PASSKEY_DISPLAY, PASSKEY_INPUT = BLE_EVT_PAIRING_PASSKEY_INPUT, - STATUS_UPDATED = BLE_EVT_PAIRING_STATUS_UPDATED + STATUS_UPDATED = BLE_EVT_PAIRING_STATUS_UPDATED, + NUMERIC_COMPARISON = BLE_EVT_PAIRING_NUMERIC_COMPARISON }; struct BlePairingStatus { @@ -1043,8 +1050,10 @@ class BleLocalDevice { BlePeerDevice connect(const BleAddress& addr, bool automatic = true) const; int setPairingIoCaps(BlePairingIoCaps ioCaps) const; + int setPairingAlgorithm(BlePairingAlgorithm algorithm) const; int startPairing(const BlePeerDevice& peer) const; int rejectPairing(const BlePeerDevice& peer) const; + int setPairingNumericComparison(const BlePeerDevice& peer, bool equal) const; int setPairingPasskey(const BlePeerDevice& peer, const uint8_t* passkey) const; bool isPairing(const BlePeerDevice& peer) const; bool isPaired(const BlePeerDevice& peer) const; diff --git a/wiring/src/spark_wiring_ble.cpp b/wiring/src/spark_wiring_ble.cpp index f42cc45476..85d35c7882 100644 --- a/wiring/src/spark_wiring_ble.cpp +++ b/wiring/src/spark_wiring_ble.cpp @@ -1194,7 +1194,8 @@ class BleLocalDeviceImpl { case BLE_EVT_PAIRING_REQUEST_RECEIVED: case BLE_EVT_PAIRING_PASSKEY_DISPLAY: case BLE_EVT_PAIRING_PASSKEY_INPUT: - case BLE_EVT_PAIRING_STATUS_UPDATED: { + case BLE_EVT_PAIRING_STATUS_UPDATED: + case BLE_EVT_PAIRING_NUMERIC_COMPARISON: { BlePeerDevice* peer = impl->findPeerDevice(event->conn_handle); if (peer) { if (impl->pairingEventCallback_) { @@ -1202,7 +1203,7 @@ class BleLocalDeviceImpl { .peer = *peer, .type = static_cast(event->type) }; - if (event->type == BLE_EVT_PAIRING_PASSKEY_DISPLAY) { + if (event->type == BLE_EVT_PAIRING_PASSKEY_DISPLAY || event->type == BLE_EVT_PAIRING_NUMERIC_COMPARISON) { pairingEvent.payload.passkey = event->params.passkey_display.passkey; pairingEvent.payloadLen = BLE_PAIRING_PASSKEY_LEN; } else if (event->type == BLE_EVT_PAIRING_STATUS_UPDATED) { @@ -2590,10 +2591,20 @@ int BleLocalDevice::setPairingIoCaps(BlePairingIoCaps ioCaps) const { hal_ble_pairing_config_t config = {}; config.version = BLE_API_VERSION; config.size = sizeof(hal_ble_pairing_config_t); + CHECK(hal_ble_gap_get_pairing_config(&config, nullptr)); config.io_caps = static_cast(ioCaps); return hal_ble_gap_set_pairing_config(&config, nullptr); } +int BleLocalDevice::setPairingAlgorithm(BlePairingAlgorithm algorithm) const { + hal_ble_pairing_config_t config = {}; + config.version = BLE_API_VERSION; + config.size = sizeof(hal_ble_pairing_config_t); + CHECK(hal_ble_gap_get_pairing_config(&config, nullptr)); + config.algorithm = static_cast(algorithm); + return hal_ble_gap_set_pairing_config(&config, nullptr); +} + int BleLocalDevice::startPairing(const BlePeerDevice& peer) const { return hal_ble_gap_start_pairing(peer.impl()->connHandle(), nullptr); } @@ -2602,8 +2613,22 @@ int BleLocalDevice::rejectPairing(const BlePeerDevice& peer) const { return hal_ble_gap_reject_pairing(peer.impl()->connHandle(), nullptr); } +int BleLocalDevice::setPairingNumericComparison(const BlePeerDevice& peer, bool equal) const { + hal_ble_pairing_auth_data_t auth = {}; + auth.version = BLE_API_VERSION; + auth.size = sizeof(hal_ble_pairing_auth_data_t); + auth.type = BLE_PAIRING_AUTH_DATA_NUMERIC_COMPARISON; + auth.params.equal = equal; + return hal_ble_gap_set_pairing_auth_data(peer.impl()->connHandle(), &auth, nullptr); +} + int BleLocalDevice::setPairingPasskey(const BlePeerDevice& peer, const uint8_t* passkey) const { - return hal_ble_gap_set_pairing_passkey(peer.impl()->connHandle(), passkey, nullptr); + hal_ble_pairing_auth_data_t auth = {}; + auth.version = BLE_API_VERSION; + auth.size = sizeof(hal_ble_pairing_auth_data_t); + auth.type = BLE_PAIRING_AUTH_DATA_PASSKEY; + auth.params.passkey = passkey; + return hal_ble_gap_set_pairing_auth_data(peer.impl()->connHandle(), &auth, nullptr); } bool BleLocalDevice::isPairing(const BlePeerDevice& peer) const {