Commit 022e06fe authored by Jun Choi's avatar Jun Choi Committed by Commit Bot

Plumb Bluetooth pairing API to WebAuthN embedder

Expose BluetoothDevice::Pair() function to WebAuthN embedder layer via
BleAdapterManager. Previously named BleAdapterPowerManager(which only
handled notifying BluetoothAdapter power changes to the embedder) is now
renamed to BleAdapterManager and it handles both
  a) Handling pairing events received from the UI layer.
  b) Handling events related to triggering/notifying BluetoothAdapter changes.

Bug: 877344
TBR: ortuno@chromium.org
Change-Id: I078f60cd4b0d491973276a3279503642c94d8d9d
Reviewed-on: https://chromium-review.googlesource.com/c/1239621
Commit-Queue: Jun Choi <hongjunchoi@chromium.org>
Reviewed-by: default avatarBalazs Engedy <engedy@chromium.org>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Cr-Commit-Position: refs/heads/master@{#597251}
parent 76854f6c
......@@ -142,7 +142,8 @@ void ChromeAuthenticatorRequestDelegate::DidFailWithInterestingReason(
void ChromeAuthenticatorRequestDelegate::RegisterActionCallbacks(
base::OnceClosure cancel_callback,
device::FidoRequestHandlerBase::RequestCallback request_callback,
base::RepeatingClosure bluetooth_adapter_power_on_callback) {
base::RepeatingClosure bluetooth_adapter_power_on_callback,
device::FidoRequestHandlerBase::BlePairingCallback ble_pairing_callback) {
request_callback_ = request_callback;
cancel_callback_ = std::move(cancel_callback);
......
......@@ -73,7 +73,9 @@ class ChromeAuthenticatorRequestDelegate
void RegisterActionCallbacks(
base::OnceClosure cancel_callback,
device::FidoRequestHandlerBase::RequestCallback request_callback,
base::RepeatingClosure bluetooth_adapter_power_on_callback) override;
base::RepeatingClosure bluetooth_adapter_power_on_callback,
device::FidoRequestHandlerBase::BlePairingCallback ble_pairing_callback)
override;
bool ShouldPermitIndividualAttestation(
const std::string& relying_party_id) override;
void ShouldReturnAttestation(
......
......@@ -612,7 +612,10 @@ void AuthenticatorImpl::MakeCredential(
request_->GetWeakPtr()) /* request_callback */,
base::BindRepeating(
&device::FidoRequestHandlerBase::PowerOnBluetoothAdapter,
request_->GetWeakPtr()) /* bluetooth_adapter_power_on_callback */);
request_->GetWeakPtr()) /* bluetooth_adapter_power_on_callback */,
base::BindRepeating(
&device::FidoRequestHandlerBase::InitiatePairingWithDevice,
request_->GetWeakPtr()) /* ble_pairing_callback*/);
request_->set_observer(request_delegate_.get());
request_->SetPlatformAuthenticatorOrMarkUnavailable(
......@@ -703,7 +706,10 @@ void AuthenticatorImpl::GetAssertion(
request_->GetWeakPtr()) /* request_callback */,
base::BindRepeating(
&device::FidoRequestHandlerBase::PowerOnBluetoothAdapter,
request_->GetWeakPtr()) /* bluetooth_adapter_power_on_callback */);
request_->GetWeakPtr()) /* bluetooth_adapter_power_on_callback */,
base::BindRepeating(
&device::FidoRequestHandlerBase::InitiatePairingWithDevice,
request_->GetWeakPtr()) /* ble_pairing_callback*/);
request_->set_observer(request_delegate_.get());
request_->SetPlatformAuthenticatorOrMarkUnavailable(
......
......@@ -1124,7 +1124,9 @@ class TestAuthenticatorRequestDelegate
void RegisterActionCallbacks(
base::OnceClosure cancel_callback,
device::FidoRequestHandlerBase::RequestCallback request_callback,
base::RepeatingClosure bluetooth_adapter_power_on_callback) override {
base::RepeatingClosure bluetooth_adapter_power_on_callback,
device::FidoRequestHandlerBase::BlePairingCallback ble_pairing_callback)
override {
ASSERT_TRUE(action_callbacks_registered_callback_)
<< "RegisterActionCallbacks called twice.";
std::move(action_callbacks_registered_callback_).Run();
......
......@@ -22,7 +22,8 @@ void AuthenticatorRequestClientDelegate::DidFailWithInterestingReason(
void AuthenticatorRequestClientDelegate::RegisterActionCallbacks(
base::OnceClosure cancel_callback,
device::FidoRequestHandlerBase::RequestCallback request_callback,
base::RepeatingClosure bluetooth_adapter_power_on_callback) {}
base::RepeatingClosure bluetooth_adapter_power_on_callback,
device::FidoRequestHandlerBase::BlePairingCallback ble_pairing_callback) {}
bool AuthenticatorRequestClientDelegate::ShouldPermitIndividualAttestation(
const std::string& relying_party_id) {
......
......@@ -54,7 +54,8 @@ class CONTENT_EXPORT AuthenticatorRequestClientDelegate
virtual void RegisterActionCallbacks(
base::OnceClosure cancel_callback,
device::FidoRequestHandlerBase::RequestCallback request_callback,
base::RepeatingClosure bluetooth_adapter_power_on_callback);
base::RepeatingClosure bluetooth_adapter_power_on_callback,
device::FidoRequestHandlerBase::BlePairingCallback ble_pairing_callback);
// Returns true if the given relying party ID is permitted to receive
// individual attestation certificates. This:
......
......@@ -88,4 +88,25 @@ void FidoBlePairingDelegate::ChangeStoredDeviceAddress(
}
}
void FidoBlePairingDelegate::CancelPairingOnAllKnownDevices(
BluetoothAdapter* adapter) {
DCHECK(adapter);
auto bluetooth_devices = adapter->GetDevices();
for (const auto& may_be_paired_device_info : bluetooth_device_pincode_map_) {
const auto& authenticator_id = may_be_paired_device_info.first;
auto it = std::find_if(
bluetooth_devices.begin(), bluetooth_devices.end(),
[&authenticator_id](const auto* device) {
return FidoBleDevice::GetId(device->GetAddress()) == authenticator_id;
});
if (it == bluetooth_devices.end())
continue;
// TODO(hongjunchoi): Change this so that this is only invoked when we know
// that WebAuthN request was in middle of pairing -- not unconditionally.
// See: https://crbug.com/892697
(*it)->CancelPairing();
}
}
} // namespace device
......@@ -42,9 +42,11 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBlePairingDelegate
std::string pin_code);
void ChangeStoredDeviceAddress(const std::string& old_address,
std::string new_address);
void CancelPairingOnAllKnownDevices(BluetoothAdapter* adapter);
private:
friend class FidoBlePairingDelegateTest;
friend class FidoBleAdapterManagerTest;
base::flat_map<std::string, std::string> bluetooth_device_pincode_map_;
......
......@@ -7,7 +7,9 @@
#include <utility>
#include "base/bind_helpers.h"
#include "base/callback_helpers.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/fido/ble/fido_ble_device.h"
namespace device {
......@@ -21,8 +23,10 @@ BleAdapterManager::~BleAdapterManager() {
if (adapter_powered_on_programmatically_)
SetAdapterPower(false /* set_power_on */);
if (adapter_)
if (adapter_) {
adapter_->RemoveObserver(this);
pairing_delegate_.CancelPairingOnAllKnownDevices(adapter_.get());
}
}
void BleAdapterManager::SetAdapterPower(bool set_power_on) {
......@@ -32,11 +36,54 @@ void BleAdapterManager::SetAdapterPower(bool set_power_on) {
adapter_->SetPowered(set_power_on, base::DoNothing(), base::DoNothing());
}
void BleAdapterManager::InitiatePairing(std::string authenticator_id,
std::string pin_code,
base::OnceClosure success_callback,
base::OnceClosure error_callback) {
DCHECK(adapter_);
auto device_list = adapter_->GetDevices();
auto device_it = std::find_if(
device_list.begin(), device_list.end(),
[&authenticator_id](const auto& bluetooth_device) {
return FidoBleDevice::GetId(bluetooth_device->GetAddress()) ==
authenticator_id;
});
if (device_it == device_list.end() ||
!request_handler_->HasAuthenticator(authenticator_id)) {
std::move(error_callback).Run();
return;
}
pairing_delegate_.StoreBlePinCodeForDevice(std::move(authenticator_id),
std::move(pin_code));
auto failure_callback = base::BindOnce(
[](base::OnceClosure callback,
BluetoothDevice::ConnectErrorCode error_code) {
std::move(callback).Run();
},
std::move(error_callback));
(*device_it)
->Pair(&pairing_delegate_,
base::AdaptCallbackForRepeating(std::move(success_callback)),
base::AdaptCallbackForRepeating(std::move(failure_callback)));
}
void BleAdapterManager::AdapterPoweredChanged(BluetoothAdapter* adapter,
bool powered) {
request_handler_->OnBluetoothAdapterPowerChanged(powered);
}
void BleAdapterManager::DeviceAddressChanged(BluetoothAdapter* adapter,
BluetoothDevice* device,
const std::string& old_address) {
pairing_delegate_.ChangeStoredDeviceAddress(
FidoBleDevice::GetId(old_address),
FidoBleDevice::GetId(device->GetAddress()));
}
void BleAdapterManager::Start(scoped_refptr<BluetoothAdapter> adapter) {
DCHECK(!adapter_);
adapter_ = std::move(adapter);
......
......@@ -5,11 +5,16 @@
#ifndef DEVICE_FIDO_BLE_ADAPTER_MANAGER_H_
#define DEVICE_FIDO_BLE_ADAPTER_MANAGER_H_
#include <string>
#include "base/callback.h"
#include "base/component_export.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/fido/ble/fido_ble_pairing_delegate.h"
#include "device/fido/fido_request_handler_base.h"
namespace device {
......@@ -17,26 +22,34 @@ namespace device {
class COMPONENT_EXPORT(DEVICE_FIDO) BleAdapterManager
: public BluetoothAdapter::Observer {
public:
// Handles notifying |request_handler| when BluetoothAdapter is powered on and
// off. Exposes API for |request_handler| to power on BluetoothAdapter
// programmatically, and if BluetoothAdapter was powered on programmatically,
// powers off BluetoothAdapter when |this| goes out of scope.
// |request_handler| must outlive |this|.
// Handles notifying events from/exposing API's in BluetoothAdapter to
// FidoRequestHandler. Namely, handles the following logic:
// a) Exposing API to initiate Bluetooth pairing event.
// b) Exposing API to trigger power Bluetooth adapter on/off.
// c) Notifying FidoRequestHandler when Bluetooth adapter power changes.
BleAdapterManager(FidoRequestHandlerBase* request_handler);
~BleAdapterManager() override;
void SetAdapterPower(bool set_power_on);
void InitiatePairing(std::string fido_authenticator_id,
std::string pin_code,
base::OnceClosure success_callback,
base::OnceClosure error_callback);
private:
friend class FidoBleAdapterManagerTest;
// BluetoothAdapter::Observer:
void AdapterPoweredChanged(BluetoothAdapter* adapter, bool powered) override;
void DeviceAddressChanged(BluetoothAdapter* adapter,
BluetoothDevice* device,
const std::string& old_address) override;
void Start(scoped_refptr<BluetoothAdapter> adapter);
FidoRequestHandlerBase* const request_handler_;
scoped_refptr<BluetoothAdapter> adapter_;
FidoBlePairingDelegate pairing_delegate_;
bool adapter_powered_on_programmatically_ = false;
base::WeakPtrFactory<BleAdapterManager> weak_factory_;
......
......@@ -4,12 +4,18 @@
#include "device/fido/ble_adapter_manager.h"
#include <map>
#include <memory>
#include <utility>
#include "base/bind_helpers.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_device.h"
#include "device/fido/fido_request_handler_base.h"
#include "device/fido/test_callback_receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -19,6 +25,11 @@ namespace {
using ::testing::_;
constexpr char kTestBluetoothDeviceAddress[] = "test_device_address";
constexpr char kTestFidoBleAuthenticatorId[] = "ble:test_device_address";
constexpr char kTestPinCode[] = "1234";
constexpr char kTestBluetoothDisplayName[] = "device_name";
class MockTransportAvailabilityObserver
: public FidoRequestHandlerBase::TransportAvailabilityObserver {
public:
......@@ -49,9 +60,20 @@ class FakeFidoRequestHandlerBase : public FidoRequestHandlerBase {
set_observer(observer);
}
void SimulateFidoRequestHandlerHasAuthenticator(bool simulate_authenticator) {
simulate_authenticator_ = simulate_authenticator;
}
private:
void DispatchRequest(FidoAuthenticator*) override {}
bool HasAuthenticator(
const std::string& authentictator_address) const override {
return simulate_authenticator_;
}
bool simulate_authenticator_ = false;
DISALLOW_COPY_AND_ASSIGN(FakeFidoRequestHandlerBase);
};
......@@ -67,6 +89,16 @@ class FidoBleAdapterManagerTest : public ::testing::Test {
return std::make_unique<BleAdapterManager>(fake_request_handler_.get());
}
MockBluetoothDevice* AddMockBluetoothDeviceToAdapter() {
auto mock_bluetooth_device = std::make_unique<MockBluetoothDevice>(
adapter_.get(), 0 /* bluetooth_class */, kTestBluetoothDisplayName,
kTestBluetoothDeviceAddress, false /* paired */, false /* connected */);
auto* mock_bluetooth_device_ptr = mock_bluetooth_device.get();
adapter_->AddMockDevice(std::move(mock_bluetooth_device));
return mock_bluetooth_device_ptr;
}
MockBluetoothAdapter* adapter() { return adapter_.get(); }
MockTransportAvailabilityObserver* observer() { return mock_observer_.get(); }
bool adapter_powered_on_programmatically(
......@@ -74,6 +106,20 @@ class FidoBleAdapterManagerTest : public ::testing::Test {
return adapter_manager.adapter_powered_on_programmatically_;
}
FakeFidoRequestHandlerBase* fake_request_handler() {
return fake_request_handler_.get();
}
const base::flat_map<std::string, std::string>& device_pincode_map(
const FidoBlePairingDelegate& delegate) const {
return delegate.bluetooth_device_pincode_map_;
}
const FidoBlePairingDelegate& ble_pairing_delegate(
const BleAdapterManager& ble_adapter_manager) {
return ble_adapter_manager.pairing_delegate_;
}
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
scoped_refptr<MockBluetoothAdapter> adapter_ =
......@@ -117,9 +163,9 @@ TEST_F(FidoBleAdapterManagerTest, AdapaterPresentAndPowered) {
}
TEST_F(FidoBleAdapterManagerTest, AdapaterPresentAndCanBePowered) {
EXPECT_CALL(*adapter(), IsPresent()).WillOnce(::testing::Return(true));
EXPECT_CALL(*adapter(), IsPowered()).WillOnce(::testing::Return(false));
EXPECT_CALL(*adapter(), CanPower()).WillOnce(::testing::Return(true));
EXPECT_CALL(*adapter(), IsPresent).WillOnce(::testing::Return(true));
EXPECT_CALL(*adapter(), IsPowered).WillOnce(::testing::Return(false));
EXPECT_CALL(*adapter(), CanPower).WillOnce(::testing::Return(true));
FidoRequestHandlerBase::TransportAvailabilityInfo data;
EXPECT_CALL(*observer(), OnTransportAvailabilityEnumerated(_))
......@@ -132,7 +178,7 @@ TEST_F(FidoBleAdapterManagerTest, AdapaterPresentAndCanBePowered) {
EXPECT_TRUE(data.can_power_on_ble_adapter);
}
TEST_F(FidoBleAdapterManagerTest, TestSetBluetoothPowerOn) {
TEST_F(FidoBleAdapterManagerTest, SetBluetoothPowerOn) {
auto power_manager = CreateTestBleAdapterManager();
::testing::InSequence s;
EXPECT_CALL(*adapter(), SetPowered(true, _, _));
......@@ -142,4 +188,90 @@ TEST_F(FidoBleAdapterManagerTest, TestSetBluetoothPowerOn) {
power_manager.reset();
}
TEST_F(FidoBleAdapterManagerTest, SuccessfulPairing) {
fake_request_handler()->SimulateFidoRequestHandlerHasAuthenticator(
true /* simulate_authenticator */);
auto* mock_bluetooth_device = AddMockBluetoothDeviceToAdapter();
EXPECT_CALL(*adapter(), GetDevices())
.WillRepeatedly(::testing::Return(adapter()->GetConstMockDevices()));
EXPECT_CALL(*mock_bluetooth_device, Pair)
.WillOnce(::testing::WithArgs<0, 1>(
[mock_bluetooth_device](
BluetoothDevice::PairingDelegate* delegate,
const base::RepeatingClosure& success_callback) {
delegate->RequestPinCode(mock_bluetooth_device);
success_callback.Run();
}));
EXPECT_CALL(*mock_bluetooth_device, SetPinCode(kTestPinCode));
auto adapter_manager = CreateTestBleAdapterManager();
test::TestCallbackReceiver<> callback_receiver;
adapter_manager->InitiatePairing(kTestFidoBleAuthenticatorId, kTestPinCode,
callback_receiver.callback(),
base::DoNothing());
callback_receiver.WaitForCallback();
const auto& pin_code_map =
device_pincode_map(ble_pairing_delegate(*adapter_manager));
EXPECT_EQ(1u, pin_code_map.size());
ASSERT_TRUE(base::ContainsKey(pin_code_map, kTestFidoBleAuthenticatorId));
EXPECT_EQ(kTestPinCode,
pin_code_map.find(kTestFidoBleAuthenticatorId)->second);
}
TEST_F(FidoBleAdapterManagerTest, PairingFailsOnUnknownDevice) {
auto* mock_bluetooth_device = AddMockBluetoothDeviceToAdapter();
EXPECT_CALL(*adapter(), GetDevices())
.WillRepeatedly(::testing::Return(adapter()->GetConstMockDevices()));
EXPECT_CALL(*mock_bluetooth_device, Pair).Times(0);
auto power_manager = CreateTestBleAdapterManager();
test::TestCallbackReceiver<> callback_receiver;
power_manager->InitiatePairing(kTestFidoBleAuthenticatorId, kTestPinCode,
base::DoNothing(),
callback_receiver.callback());
callback_receiver.WaitForCallback();
const auto& pin_code_map =
device_pincode_map(ble_pairing_delegate(*power_manager));
EXPECT_TRUE(pin_code_map.empty());
}
TEST_F(FidoBleAdapterManagerTest, PairingCancelledOnDestruction) {
fake_request_handler()->SimulateFidoRequestHandlerHasAuthenticator(
true /* simulate_authenticator */);
auto* mock_bluetooth_device = AddMockBluetoothDeviceToAdapter();
EXPECT_CALL(*adapter(), GetDevices())
.WillRepeatedly(::testing::Return(adapter()->GetConstMockDevices()));
EXPECT_CALL(*mock_bluetooth_device, Pair)
.WillOnce(::testing::WithArg<1>(
[](const base::RepeatingClosure& success_callback) {
success_callback.Run();
}));
auto adapter_manager = CreateTestBleAdapterManager();
test::TestCallbackReceiver<> callback_receiver;
adapter_manager->InitiatePairing(kTestFidoBleAuthenticatorId, kTestPinCode,
callback_receiver.callback(),
base::DoNothing());
callback_receiver.WaitForCallback();
const auto& pin_code_map =
device_pincode_map(ble_pairing_delegate(*adapter_manager));
EXPECT_EQ(1u, pin_code_map.size());
ASSERT_TRUE(base::ContainsKey(pin_code_map, kTestFidoBleAuthenticatorId));
EXPECT_EQ(kTestPinCode,
pin_code_map.find(kTestFidoBleAuthenticatorId)->second);
// Destroying BleAdapterManager should call CancelPairing() on all
// BluetoothDevice which has been attempted to be paried by the pairing
// delegate.
testing::Mock::VerifyAndClearExpectations(mock_bluetooth_device);
EXPECT_CALL(*mock_bluetooth_device, CancelPairing);
adapter_manager.reset();
}
} // namespace device
......@@ -176,6 +176,19 @@ void FidoRequestHandlerBase::PowerOnBluetoothAdapter() {
bluetooth_adapter_manager_->SetAdapterPower(true /* set_power_on */);
}
void FidoRequestHandlerBase::InitiatePairingWithDevice(
std::string authenticator_id,
std::string pin_code,
base::OnceClosure success_callback,
base::OnceClosure error_callback) {
if (!bluetooth_adapter_manager_)
return;
bluetooth_adapter_manager_->InitiatePairing(
std::move(authenticator_id), std::move(pin_code),
std::move(success_callback), std::move(error_callback));
}
base::WeakPtr<FidoRequestHandlerBase> FidoRequestHandlerBase::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
......@@ -275,6 +288,11 @@ void FidoRequestHandlerBase::SetPlatformAuthenticatorOrMarkUnavailable(
notify_observer_callback_.Run();
}
bool FidoRequestHandlerBase::HasAuthenticator(
const std::string& authenticator_id) const {
return base::ContainsKey(active_authenticators_, authenticator_id);
}
void FidoRequestHandlerBase::NotifyObserverTransportAvailability() {
DCHECK(observer_);
observer_->OnTransportAvailabilityEnumerated(transport_availability_info_);
......
......@@ -54,6 +54,11 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase
using AuthenticatorMap =
std::map<std::string, FidoAuthenticator*, std::less<>>;
using RequestCallback = base::RepeatingCallback<void(const std::string&)>;
using BlePairingCallback =
base::RepeatingCallback<void(std::string authenticator_id,
std::string pin_code,
base::OnceClosure success_callback,
base::OnceClosure error_callback)>;
enum class RequestType { kMakeCredential, kGetAssertion };
......@@ -141,6 +146,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase
bool can_power_on);
void OnBluetoothAdapterPowerChanged(bool is_powered_on);
void PowerOnBluetoothAdapter();
void InitiatePairingWithDevice(std::string authenticator_id,
std::string pin_code,
base::OnceClosure success_callback,
base::OnceClosure error_callback);
base::WeakPtr<FidoRequestHandlerBase> GetWeakPtr();
......@@ -158,6 +167,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase
virtual void SetPlatformAuthenticatorOrMarkUnavailable(
base::Optional<PlatformAuthenticatorInfo> platform_authenticator_info);
// Returns whether FidoAuthenticator with id equal to |authenticator_id|
// exists. Fake FidoRequestHandler objects used in testing overrides this
// function to simulate scenarios where authenticator with |authenticator_id|
// is known to the system.
virtual bool HasAuthenticator(const std::string& authentiator_id) const;
TransportAvailabilityInfo& transport_availability_info() {
return transport_availability_info_;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment