/****************************************************************************** * * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ #include #include #include #include "common/testing/wired_pair_of_bidi_queues.h" #include "hci/le_security_interface.h" #include "packet/raw_builder.h" #include "security/pairing_handler_le.h" #include "security/test/mocks.h" using namespace std::chrono_literals; using testing::_; using testing::Invoke; using testing::InvokeWithoutArgs; using testing::Matcher; using testing::SaveArg; using bluetooth::hci::Address; using bluetooth::hci::AddressType; using bluetooth::hci::CommandCompleteView; using bluetooth::hci::CommandStatusView; using bluetooth::hci::EncryptionChangeBuilder; using bluetooth::hci::EncryptionEnabled; using bluetooth::hci::ErrorCode; using bluetooth::hci::EventBuilder; using bluetooth::hci::EventView; using bluetooth::hci::LeSecurityCommandBuilder; // run: // out/host/linux-x86/nativetest/bluetooth_test_gd/bluetooth_test_gd --gtest_filter=Pairing* // adb shell /data/nativetest/bluetooth_test_gd/bluetooth_test_gd --gtest_filter=PairingHandlerPairTest.* // --gtest_repeat=10 --gtest_shuffle namespace bluetooth { namespace security { CommandView CommandBuilderToView(std::unique_ptr builder) { std::shared_ptr> packet_bytes = std::make_shared>(); BitInserter it(*packet_bytes); builder->Serialize(it); PacketView packet_bytes_view(packet_bytes); auto temp_cmd_view = CommandView::Create(packet_bytes_view); return CommandView::Create(temp_cmd_view); } EventView EventBuilderToView(std::unique_ptr builder) { std::shared_ptr> packet_bytes = std::make_shared>(); BitInserter it(*packet_bytes); builder->Serialize(it); PacketView packet_bytes_view(packet_bytes); auto temp_evt_view = EventView::Create(packet_bytes_view); return EventView::Create(temp_evt_view); } } // namespace security } // namespace bluetooth namespace { constexpr uint16_t CONN_HANDLE_CENTRAL = 0x31, CONN_HANDLE_PERIPHERAL = 0x32; std::unique_ptr pairing_handler_a, pairing_handler_b; } // namespace namespace bluetooth { namespace security { namespace { Address ADDRESS_CENTRAL{{0x26, 0x64, 0x76, 0x86, 0xab, 0xba}}; AddressType ADDRESS_TYPE_CENTRAL = AddressType::RANDOM_DEVICE_ADDRESS; Address IDENTITY_ADDRESS_CENTRAL{{0x12, 0x34, 0x56, 0x78, 0x90, 0xaa}}; AddressType IDENTITY_ADDRESS_TYPE_CENTRAL = AddressType::PUBLIC_DEVICE_ADDRESS; crypto_toolbox::Octet16 IRK_CENTRAL = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; Address ADDRESS_PERIPHERAL{{0x33, 0x58, 0x24, 0x76, 0x11, 0x89}}; AddressType ADDRESS_TYPE_PERIPHERAL = AddressType::RANDOM_DEVICE_ADDRESS; Address IDENTITY_ADDRESS_PERIPHERAL{{0x21, 0x43, 0x65, 0x87, 0x09, 0x44}}; AddressType IDENTITY_ADDRESS_TYPE_PERIPHERAL = AddressType::PUBLIC_DEVICE_ADDRESS; crypto_toolbox::Octet16 IRK_PERIPHERAL = { 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01}; std::optional pairing_result_central; std::optional pairing_result_peripheral; void OnPairingFinishedCentral(PairingResultOrFailure r) { pairing_result_central = r; if (std::holds_alternative(r)) { LOG_INFO("pairing finished successfully with %s", std::get(r).connection_address.ToString().c_str()); } else { LOG_INFO("pairing with ... failed: %s", std::get(r).message.c_str()); } } void OnPairingFinishedPeripheral(PairingResultOrFailure r) { pairing_result_peripheral = r; if (std::holds_alternative(r)) { LOG_INFO("pairing finished successfully with %s", std::get(r).connection_address.ToString().c_str()); } else { LOG_INFO("pairing with ... failed: %s", std::get(r).message.c_str()); } } }; // namespace // We obtain this mutex when we start initializing the handlers, and relese it when both handlers are initialized std::mutex handlers_initialization_guard; class PairingHandlerPairTest : public testing::Test { void dequeue_callback_central() { auto packet_bytes_view = l2cap_->GetQueueAUpEnd()->TryDequeue(); if (!packet_bytes_view) LOG_ERROR("Received dequeue, but no data ready..."); auto temp_cmd_view = CommandView::Create(*packet_bytes_view); if (!first_command_sent) { first_command = std::make_unique(CommandView::Create(temp_cmd_view)); first_command_sent = true; return; } if (!pairing_handler_a) LOG_ALWAYS_FATAL("Peripheral handler not initlized yet!"); pairing_handler_a->OnCommandView(CommandView::Create(temp_cmd_view)); } void dequeue_callback_peripheral() { auto packet_bytes_view = l2cap_->GetQueueBUpEnd()->TryDequeue(); if (!packet_bytes_view) LOG_ERROR("Received dequeue, but no data ready..."); auto temp_cmd_view = CommandView::Create(*packet_bytes_view); if (!first_command_sent) { first_command = std::make_unique(CommandView::Create(temp_cmd_view)); first_command_sent = true; return; } if (!pairing_handler_b) LOG_ALWAYS_FATAL("Central handler not initlized yet!"); pairing_handler_b->OnCommandView(CommandView::Create(temp_cmd_view)); } protected: void SetUp() { thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL); handler_ = new os::Handler(thread_); l2cap_ = new common::testing::WiredPairOfL2capQueues(handler_); // central sends it's packet into l2cap->down_buffer_b_ // peripheral sends it's packet into l2cap->down_buffer_a_ l2cap_->GetQueueAUpEnd()->RegisterDequeue( handler_, common::Bind(&PairingHandlerPairTest::dequeue_callback_central, common::Unretained(this))); l2cap_->GetQueueBUpEnd()->RegisterDequeue( handler_, common::Bind(&PairingHandlerPairTest::dequeue_callback_peripheral, common::Unretained(this))); up_buffer_a_ = std::make_unique>(l2cap_->GetQueueAUpEnd()); up_buffer_b_ = std::make_unique>(l2cap_->GetQueueBUpEnd()); central_setup = { .my_role = hci::Role::CENTRAL, .my_connection_address = {ADDRESS_CENTRAL, ADDRESS_TYPE_CENTRAL}, .my_identity_address = {IDENTITY_ADDRESS_CENTRAL, IDENTITY_ADDRESS_TYPE_CENTRAL}, .my_identity_resolving_key = IRK_CENTRAL, .myPairingCapabilities = {.io_capability = IoCapability::NO_INPUT_NO_OUTPUT, .oob_data_flag = OobDataFlag::NOT_PRESENT, .auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc, .maximum_encryption_key_size = 16, .initiator_key_distribution = KeyMaskId | KeyMaskSign, .responder_key_distribution = KeyMaskId | KeyMaskSign}, .remotely_initiated = false, .connection_handle = CONN_HANDLE_CENTRAL, .remote_connection_address = {ADDRESS_PERIPHERAL, ADDRESS_TYPE_PERIPHERAL}, .user_interface = ¢ral_user_interface, .user_interface_handler = handler_, .le_security_interface = ¢ral_le_security_mock, .proper_l2cap_interface = up_buffer_a_.get(), .l2cap_handler = handler_, .OnPairingFinished = OnPairingFinishedCentral, }; peripheral_setup = { .my_role = hci::Role::PERIPHERAL, .my_connection_address = {ADDRESS_PERIPHERAL, ADDRESS_TYPE_PERIPHERAL}, .my_identity_address = {IDENTITY_ADDRESS_PERIPHERAL, IDENTITY_ADDRESS_TYPE_PERIPHERAL}, .my_identity_resolving_key = IRK_PERIPHERAL, .myPairingCapabilities = {.io_capability = IoCapability::NO_INPUT_NO_OUTPUT, .oob_data_flag = OobDataFlag::NOT_PRESENT, .auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc, .maximum_encryption_key_size = 16, .initiator_key_distribution = KeyMaskId | KeyMaskSign, .responder_key_distribution = KeyMaskId | KeyMaskSign}, .remotely_initiated = true, .connection_handle = CONN_HANDLE_PERIPHERAL, .remote_connection_address = {ADDRESS_CENTRAL, ADDRESS_TYPE_CENTRAL}, .user_interface = &peripheral_user_interface, .user_interface_handler = handler_, .le_security_interface = &peripheral_le_security_mock, .proper_l2cap_interface = up_buffer_b_.get(), .l2cap_handler = handler_, .OnPairingFinished = OnPairingFinishedPeripheral, }; RecordSuccessfulEncryptionComplete(); } void TearDown() { ::testing::Mock::VerifyAndClearExpectations(&peripheral_user_interface); ::testing::Mock::VerifyAndClearExpectations(¢ral_user_interface); ::testing::Mock::VerifyAndClearExpectations(&peripheral_le_security_mock); ::testing::Mock::VerifyAndClearExpectations(¢ral_le_security_mock); pairing_handler_a.reset(); pairing_handler_b.reset(); pairing_result_central.reset(); pairing_result_peripheral.reset(); first_command_sent = false; first_command.reset(); l2cap_->GetQueueAUpEnd()->UnregisterDequeue(); l2cap_->GetQueueBUpEnd()->UnregisterDequeue(); delete l2cap_; handler_->Clear(); delete handler_; delete thread_; } void RecordPairingPromptHandling(UIMock& ui_mock, std::unique_ptr* handler) { EXPECT_CALL(ui_mock, DisplayPairingPrompt(_, _)).Times(1).WillOnce(InvokeWithoutArgs([handler]() { LOG_INFO("UI mock received pairing prompt"); { // By grabbing the lock, we ensure initialization of both pairing handlers is finished. std::lock_guard lock(handlers_initialization_guard); } if (!(*handler)) LOG_ALWAYS_FATAL("handler not initalized yet!"); // Simulate user accepting the pairing in UI (*handler)->OnUiAction(PairingEvent::PAIRING_ACCEPTED, 0x01 /* Non-zero value means success */); })); } void RecordSuccessfulEncryptionComplete() { // For now, all tests are succeeding to go through Encryption. Record that in the setup. // Once we test failure cases, move this to each test EXPECT_CALL( central_le_security_mock, EnqueueCommand(_, Matcher>(_))) .Times(1) .WillOnce([](std::unique_ptr command, common::ContextualOnceCallback on_status) { // TODO: on_status.Run(); pairing_handler_a->OnHciEvent(EventBuilderToView( EncryptionChangeBuilder::Create(ErrorCode::SUCCESS, CONN_HANDLE_CENTRAL, EncryptionEnabled::ON))); pairing_handler_b->OnHciEvent(EventBuilderToView( hci::LeLongTermKeyRequestBuilder::Create(CONN_HANDLE_PERIPHERAL, {0, 0, 0, 0, 0, 0, 0, 0}, 0))); pairing_handler_b->OnHciEvent(EventBuilderToView( EncryptionChangeBuilder::Create(ErrorCode::SUCCESS, CONN_HANDLE_PERIPHERAL, EncryptionEnabled::ON))); }); } public: std::unique_ptr WaitFirstL2capCommand() { while (!first_command_sent) { std::this_thread::sleep_for(1ms); LOG_INFO("waiting for first command..."); } return std::move(first_command); } InitialInformations central_setup; InitialInformations peripheral_setup; UIMock central_user_interface; UIMock peripheral_user_interface; LeSecurityInterfaceMock central_le_security_mock; LeSecurityInterfaceMock peripheral_le_security_mock; uint16_t first_command_sent = false; std::unique_ptr first_command; os::Thread* thread_; os::Handler* handler_; common::testing::WiredPairOfL2capQueues* l2cap_; std::unique_ptr> up_buffer_a_; std::unique_ptr> up_buffer_b_; }; /* This test verifies that Just Works pairing flow works. * Both simulated devices specify capabilities as NO_INPUT_NO_OUTPUT, and secure connecitons support */ TEST_F(PairingHandlerPairTest, test_secure_connections_just_works) { central_setup.myPairingCapabilities.io_capability = IoCapability::NO_INPUT_NO_OUTPUT; central_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT; peripheral_setup.myPairingCapabilities.io_capability = IoCapability::NO_INPUT_NO_OUTPUT; peripheral_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT; { std::unique_lock lock(handlers_initialization_guard); pairing_handler_a = std::make_unique(PairingHandlerLe::PHASE1, central_setup); auto first_pkt = WaitFirstL2capCommand(); peripheral_setup.pairing_request = PairingRequestView::Create(*first_pkt); EXPECT_CALL(peripheral_user_interface, DisplayPairingPrompt(_, _)).Times(1).WillOnce(InvokeWithoutArgs([] { LOG_INFO("UI mock received pairing prompt"); { // By grabbing the lock, we ensure initialization of both pairing handlers is finished. std::lock_guard lock(handlers_initialization_guard); } if (!pairing_handler_b) LOG_ALWAYS_FATAL("handler not initalized yet!"); // Simulate user accepting the pairing in UI pairing_handler_b->OnUiAction(PairingEvent::PAIRING_ACCEPTED, 0x01 /* Non-zero value means success */); })); pairing_handler_b = std::make_unique(PairingHandlerLe::PHASE1, peripheral_setup); } pairing_handler_a->WaitUntilPairingFinished(); pairing_handler_b->WaitUntilPairingFinished(); EXPECT_TRUE(std::holds_alternative(pairing_result_central.value())); EXPECT_TRUE(std::holds_alternative(pairing_result_peripheral.value())); auto central_result = std::get(pairing_result_central.value()); ASSERT_EQ(central_result.distributed_keys.remote_identity_address->GetAddress(), IDENTITY_ADDRESS_PERIPHERAL); ASSERT_EQ( central_result.distributed_keys.remote_identity_address->GetAddressType(), IDENTITY_ADDRESS_TYPE_PERIPHERAL); ASSERT_EQ(*central_result.distributed_keys.remote_irk, IRK_PERIPHERAL); auto peripheral_result = std::get(pairing_result_peripheral.value()); ASSERT_EQ(peripheral_result.distributed_keys.remote_identity_address->GetAddress(), IDENTITY_ADDRESS_CENTRAL); ASSERT_EQ( peripheral_result.distributed_keys.remote_identity_address->GetAddressType(), IDENTITY_ADDRESS_TYPE_CENTRAL); ASSERT_EQ(*peripheral_result.distributed_keys.remote_irk, IRK_CENTRAL); } TEST_F(PairingHandlerPairTest, test_secure_connections_just_works_peripheral_initiated) { central_setup = { .my_role = hci::Role::CENTRAL, .my_connection_address = {ADDRESS_CENTRAL, ADDRESS_TYPE_CENTRAL}, .my_identity_address = {IDENTITY_ADDRESS_CENTRAL, IDENTITY_ADDRESS_TYPE_CENTRAL}, .my_identity_resolving_key = IRK_CENTRAL, .myPairingCapabilities = {.io_capability = IoCapability::NO_INPUT_NO_OUTPUT, .oob_data_flag = OobDataFlag::NOT_PRESENT, .auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc, .maximum_encryption_key_size = 16, .initiator_key_distribution = KeyMaskId | KeyMaskSign, .responder_key_distribution = KeyMaskId | KeyMaskSign}, .remotely_initiated = true, .connection_handle = CONN_HANDLE_CENTRAL, .remote_connection_address = {ADDRESS_PERIPHERAL, ADDRESS_TYPE_PERIPHERAL}, .user_interface = ¢ral_user_interface, .user_interface_handler = handler_, .le_security_interface = ¢ral_le_security_mock, .proper_l2cap_interface = up_buffer_a_.get(), .l2cap_handler = handler_, .OnPairingFinished = OnPairingFinishedCentral, }; peripheral_setup = { .my_role = hci::Role::PERIPHERAL, .my_connection_address = {ADDRESS_PERIPHERAL, ADDRESS_TYPE_PERIPHERAL}, .my_identity_address = {IDENTITY_ADDRESS_PERIPHERAL, IDENTITY_ADDRESS_TYPE_PERIPHERAL}, .my_identity_resolving_key = IRK_PERIPHERAL, .myPairingCapabilities = {.io_capability = IoCapability::NO_INPUT_NO_OUTPUT, .oob_data_flag = OobDataFlag::NOT_PRESENT, .auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc, .maximum_encryption_key_size = 16, .initiator_key_distribution = KeyMaskId | KeyMaskSign, .responder_key_distribution = KeyMaskId | KeyMaskSign}, .remotely_initiated = false, .connection_handle = CONN_HANDLE_PERIPHERAL, .remote_connection_address = {ADDRESS_CENTRAL, ADDRESS_TYPE_CENTRAL}, .user_interface = &peripheral_user_interface, .user_interface_handler = handler_, .le_security_interface = &peripheral_le_security_mock, .proper_l2cap_interface = up_buffer_b_.get(), .l2cap_handler = handler_, .OnPairingFinished = OnPairingFinishedPeripheral, }; std::unique_ptr first_pkt; { std::unique_lock lock(handlers_initialization_guard); pairing_handler_b = std::make_unique(PairingHandlerLe::PHASE1, peripheral_setup); first_pkt = WaitFirstL2capCommand(); EXPECT_CALL(central_user_interface, DisplayPairingPrompt(_, _)) .Times(1) .WillOnce(InvokeWithoutArgs([&first_pkt, this] { LOG_INFO("UI mock received pairing prompt"); { // By grabbing the lock, we ensure initialization of both pairing handlers is finished. std::lock_guard lock(handlers_initialization_guard); } if (!pairing_handler_a) LOG_ALWAYS_FATAL("handler not initalized yet!"); // Simulate user accepting the pairing in UI pairing_handler_a->OnUiAction(PairingEvent::PAIRING_ACCEPTED, 0x01 /* Non-zero value means success */); // Send the first packet from the peripheral to central auto view_to_packet = std::make_unique(); view_to_packet->AddOctets(std::vector(first_pkt->begin(), first_pkt->end())); up_buffer_b_->Enqueue(std::move(view_to_packet), handler_); })); pairing_handler_a = std::make_unique(PairingHandlerLe::PHASE1, central_setup); } pairing_handler_a->WaitUntilPairingFinished(); pairing_handler_b->WaitUntilPairingFinished(); EXPECT_TRUE(std::holds_alternative(pairing_result_central.value())); EXPECT_TRUE(std::holds_alternative(pairing_result_peripheral.value())); } TEST_F(PairingHandlerPairTest, test_secure_connections_numeric_comparison) { central_setup.myPairingCapabilities.io_capability = IoCapability::DISPLAY_YES_NO; central_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT; central_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc; peripheral_setup.myPairingCapabilities.io_capability = IoCapability::DISPLAY_YES_NO; peripheral_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT; peripheral_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc; ConfirmationData data_peripheral; { std::unique_lock lock(handlers_initialization_guard); // Initiator must be initialized after the responder. pairing_handler_a = std::make_unique(PairingHandlerLe::PHASE1, central_setup); while (!first_command_sent) { std::this_thread::sleep_for(1ms); LOG_INFO("waiting for first command..."); } peripheral_setup.pairing_request = PairingRequestView::Create(*first_command); RecordPairingPromptHandling(peripheral_user_interface, &pairing_handler_b); EXPECT_CALL(peripheral_user_interface, DisplayConfirmValue(_)).WillOnce(SaveArg<0>(&data_peripheral)); EXPECT_CALL(central_user_interface, DisplayConfirmValue(_)).WillOnce(Invoke([&](ConfirmationData data) { EXPECT_EQ(data_peripheral.GetNumericValue(), data.GetNumericValue()); if (data_peripheral.GetNumericValue() == data.GetNumericValue()) { pairing_handler_a->OnUiAction(PairingEvent::CONFIRM_YESNO, 0x01); pairing_handler_b->OnUiAction(PairingEvent::CONFIRM_YESNO, 0x01); } })); pairing_handler_b = std::make_unique(PairingHandlerLe::PHASE1, peripheral_setup); } pairing_handler_a->WaitUntilPairingFinished(); pairing_handler_b->WaitUntilPairingFinished(); EXPECT_TRUE(std::holds_alternative(pairing_result_central.value())); EXPECT_TRUE(std::holds_alternative(pairing_result_peripheral.value())); } TEST_F(PairingHandlerPairTest, test_secure_connections_passkey_entry) { central_setup.myPairingCapabilities.io_capability = IoCapability::KEYBOARD_ONLY; central_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT; central_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc; peripheral_setup.myPairingCapabilities.io_capability = IoCapability::DISPLAY_ONLY; peripheral_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT; peripheral_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc; // In this test either central or peripheral display the UI prompt first. This variable makes sure both prompts are // displayed before passkey is confirmed. Since both UI handlers are same thread, it's safe. int ui_prompts_count = 0; uint32_t passkey_ = std::numeric_limits::max(); { std::unique_lock lock(handlers_initialization_guard); pairing_handler_a = std::make_unique(PairingHandlerLe::PHASE1, central_setup); while (!first_command_sent) { std::this_thread::sleep_for(1ms); LOG_INFO("waiting for first command..."); } peripheral_setup.pairing_request = PairingRequestView::Create(*first_command); RecordPairingPromptHandling(peripheral_user_interface, &pairing_handler_b); EXPECT_CALL(peripheral_user_interface, DisplayPasskey(_)).WillOnce(Invoke([&](ConfirmationData data) { passkey_ = data.GetNumericValue(); ui_prompts_count++; if (ui_prompts_count == 2) { pairing_handler_a->OnUiAction(PairingEvent::PASSKEY, passkey_); } })); EXPECT_CALL(central_user_interface, DisplayEnterPasskeyDialog(_)).WillOnce(Invoke([&](ConfirmationData data) { ui_prompts_count++; if (ui_prompts_count == 2) { pairing_handler_a->OnUiAction(PairingEvent::PASSKEY, passkey_); } })); pairing_handler_b = std::make_unique(PairingHandlerLe::PHASE1, peripheral_setup); } // Initiator must be initialized after the responder. pairing_handler_a->WaitUntilPairingFinished(); pairing_handler_b->WaitUntilPairingFinished(); EXPECT_TRUE(std::holds_alternative(pairing_result_central.value())); EXPECT_TRUE(std::holds_alternative(pairing_result_peripheral.value())); } TEST_F(PairingHandlerPairTest, test_secure_connections_out_of_band) { central_setup.myPairingCapabilities.io_capability = IoCapability::KEYBOARD_ONLY; central_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT; central_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc, peripheral_setup.myPairingCapabilities.io_capability = IoCapability::DISPLAY_ONLY; peripheral_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::PRESENT; peripheral_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc, central_setup.my_oob_data = std::make_optional(PairingHandlerLe::GenerateOobData()); peripheral_setup.remote_oob_data = std::make_optional(InitialInformations::out_of_band_data{ .le_sc_c = central_setup.my_oob_data->c, .le_sc_r = central_setup.my_oob_data->r, }); { std::unique_lock lock(handlers_initialization_guard); pairing_handler_a = std::make_unique(PairingHandlerLe::PHASE1, central_setup); while (!first_command_sent) { std::this_thread::sleep_for(1ms); LOG_INFO("waiting for first command..."); } peripheral_setup.pairing_request = PairingRequestView::Create(*first_command); RecordPairingPromptHandling(peripheral_user_interface, &pairing_handler_b); pairing_handler_b = std::make_unique(PairingHandlerLe::PHASE1, peripheral_setup); } pairing_handler_a->WaitUntilPairingFinished(); pairing_handler_b->WaitUntilPairingFinished(); EXPECT_TRUE(std::holds_alternative(pairing_result_central.value())); EXPECT_TRUE(std::holds_alternative(pairing_result_peripheral.value())); } TEST_F(PairingHandlerPairTest, test_secure_connections_out_of_band_two_way) { central_setup.myPairingCapabilities.io_capability = IoCapability::KEYBOARD_ONLY; central_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::PRESENT; central_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc, peripheral_setup.myPairingCapabilities.io_capability = IoCapability::DISPLAY_ONLY; peripheral_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::PRESENT; peripheral_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm | AuthReqMaskSc, central_setup.my_oob_data = std::make_optional(PairingHandlerLe::GenerateOobData()); peripheral_setup.remote_oob_data = std::make_optional(InitialInformations::out_of_band_data{ .le_sc_c = central_setup.my_oob_data->c, .le_sc_r = central_setup.my_oob_data->r, }); peripheral_setup.my_oob_data = std::make_optional(PairingHandlerLe::GenerateOobData()); central_setup.remote_oob_data = std::make_optional(InitialInformations::out_of_band_data{ .le_sc_c = peripheral_setup.my_oob_data->c, .le_sc_r = peripheral_setup.my_oob_data->r, }); { std::unique_lock lock(handlers_initialization_guard); pairing_handler_a = std::make_unique(PairingHandlerLe::PHASE1, central_setup); while (!first_command_sent) { std::this_thread::sleep_for(1ms); LOG_INFO("waiting for first command..."); } peripheral_setup.pairing_request = PairingRequestView::Create(*first_command); RecordPairingPromptHandling(peripheral_user_interface, &pairing_handler_b); pairing_handler_b = std::make_unique(PairingHandlerLe::PHASE1, peripheral_setup); } pairing_handler_a->WaitUntilPairingFinished(); pairing_handler_b->WaitUntilPairingFinished(); EXPECT_TRUE(std::holds_alternative(pairing_result_central.value())); EXPECT_TRUE(std::holds_alternative(pairing_result_peripheral.value())); } TEST_F(PairingHandlerPairTest, test_legacy_just_works) { central_setup.myPairingCapabilities.io_capability = IoCapability::NO_INPUT_NO_OUTPUT; central_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT; central_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm, peripheral_setup.myPairingCapabilities.io_capability = IoCapability::NO_INPUT_NO_OUTPUT; peripheral_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT; peripheral_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm; { std::unique_lock lock(handlers_initialization_guard); pairing_handler_a = std::make_unique(PairingHandlerLe::PHASE1, central_setup); while (!first_command_sent) { std::this_thread::sleep_for(1ms); LOG_INFO("waiting for first command..."); } peripheral_setup.pairing_request = PairingRequestView::Create(*first_command); RecordPairingPromptHandling(peripheral_user_interface, &pairing_handler_b); pairing_handler_b = std::make_unique(PairingHandlerLe::PHASE1, peripheral_setup); } pairing_handler_a->WaitUntilPairingFinished(); pairing_handler_b->WaitUntilPairingFinished(); EXPECT_TRUE(std::holds_alternative(pairing_result_central.value())); EXPECT_TRUE(std::holds_alternative(pairing_result_peripheral.value())); } TEST_F(PairingHandlerPairTest, test_legacy_passkey_entry) { central_setup.myPairingCapabilities.io_capability = IoCapability::KEYBOARD_DISPLAY; central_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT; central_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm, peripheral_setup.myPairingCapabilities.io_capability = IoCapability::KEYBOARD_ONLY; peripheral_setup.myPairingCapabilities.oob_data_flag = OobDataFlag::NOT_PRESENT; peripheral_setup.myPairingCapabilities.auth_req = AuthReqMaskBondingFlag | AuthReqMaskMitm; { std::unique_lock lock(handlers_initialization_guard); pairing_handler_a = std::make_unique(PairingHandlerLe::PHASE1, central_setup); while (!first_command_sent) { std::this_thread::sleep_for(1ms); LOG_INFO("waiting for first command..."); } peripheral_setup.pairing_request = PairingRequestView::Create(*first_command); RecordPairingPromptHandling(peripheral_user_interface, &pairing_handler_b); EXPECT_CALL(peripheral_user_interface, DisplayEnterPasskeyDialog(_)); EXPECT_CALL(central_user_interface, DisplayConfirmValue(_)).WillOnce(Invoke([&](ConfirmationData data) { LOG_INFO("Passkey prompt displayed entering passkey: %08x", data.GetNumericValue()); std::this_thread::sleep_for(1ms); // TODO: handle case where prompts are displayed in different order in the test! pairing_handler_b->OnUiAction(PairingEvent::PASSKEY, data.GetNumericValue()); })); pairing_handler_b = std::make_unique(PairingHandlerLe::PHASE1, peripheral_setup); } pairing_handler_a->WaitUntilPairingFinished(); pairing_handler_b->WaitUntilPairingFinished(); EXPECT_TRUE(std::holds_alternative(pairing_result_central.value())); EXPECT_TRUE(std::holds_alternative(pairing_result_peripheral.value())); } } // namespace security } // namespace bluetooth