Commit 4ab25391 authored by jdoerrie's avatar jdoerrie Committed by Commit Bot

[fido] Add FidoBleDevice::IsInPairingMode()

This change introduces the possibility to query a FidoBleDevice whether
or not it is currently in pairing mode. In accordance with the CTAP spec
this is done by looking at the advertisment data flags if possible, and
falling back to investigating service data if required.

Bug: 763303
Change-Id: I373151a10d63e83ca2ac57f7b53c1269b9d60cfc
Reviewed-on: https://chromium-review.googlesource.com/1096234Reviewed-by: default avatarGiovanni Ortuño Urquidi <ortuno@chromium.org>
Reviewed-by: default avatarJames Hawkins <jhawkins@chromium.org>
Reviewed-by: default avatarBalazs Engedy <engedy@chromium.org>
Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#569929}
parent ad84abf0
...@@ -206,8 +206,9 @@ class ProximityAuthBluetoothLowEnergyConnectionFinderTest ...@@ -206,8 +206,9 @@ class ProximityAuthBluetoothLowEnergyConnectionFinderTest
uuid_list.push_back(advertisement_uuid); uuid_list.push_back(advertisement_uuid);
device::BluetoothDevice::ServiceDataMap service_data_map; device::BluetoothDevice::ServiceDataMap service_data_map;
service_data_map[advertisement_uuid] = eid_vector; service_data_map[advertisement_uuid] = eid_vector;
device_->UpdateAdvertisementData(kRssi, uuid_list, service_data_map, device_->UpdateAdvertisementData(
{} /* manufacturer_data */, nullptr); kRssi, uuid_list, service_data_map, {} /* manufacturer_data */,
nullptr /* tx_power */, nullptr /* flags */);
} }
scoped_refptr<device::MockBluetoothAdapter> adapter_; scoped_refptr<device::MockBluetoothAdapter> adapter_;
......
...@@ -238,7 +238,7 @@ void BluetoothAdapterAndroid::CreateOrUpdateDeviceOnScan( ...@@ -238,7 +238,7 @@ void BluetoothAdapterAndroid::CreateOrUpdateDeviceOnScan(
service_data_map, manufacturer_data_map, service_data_map, manufacturer_data_map,
// Android uses INT32_MIN to indicate no Advertised Tx Power. // Android uses INT32_MIN to indicate no Advertised Tx Power.
// https://developer.android.com/reference/android/bluetooth/le/ScanRecord.html#getTxPowerLevel() // https://developer.android.com/reference/android/bluetooth/le/ScanRecord.html#getTxPowerLevel()
tx_power == INT32_MIN ? nullptr : &clamped_tx_power); tx_power == INT32_MIN ? nullptr : &clamped_tx_power, nullptr /* flags */);
if (is_new_device) { if (is_new_device) {
devices_[device_address] = std::move(device_android_owner); devices_[device_address] = std::move(device_android_owner);
......
...@@ -623,7 +623,7 @@ void BluetoothAdapterMac::LowEnergyDeviceUpdated( ...@@ -623,7 +623,7 @@ void BluetoothAdapterMac::LowEnergyDeviceUpdated(
device_mac->UpdateAdvertisementData( device_mac->UpdateAdvertisementData(
BluetoothDevice::ClampPower(rssi), std::move(advertised_uuids), BluetoothDevice::ClampPower(rssi), std::move(advertised_uuids),
std::move(service_data_map), std::move(manufacturer_data_map), std::move(service_data_map), std::move(manufacturer_data_map),
tx_power == nil ? nullptr : &clamped_tx_power); tx_power == nil ? nullptr : &clamped_tx_power, nullptr /* flags */);
if (is_new_device) { if (is_new_device) {
std::string device_address = std::string device_address =
......
...@@ -175,9 +175,10 @@ void ExtractAndUpdateAdvertisementData( ...@@ -175,9 +175,10 @@ void ExtractAndUpdateAdvertisementData(
// TODO(https://crbug.com/821766): Implement extraction of service data, // TODO(https://crbug.com/821766): Implement extraction of service data,
// manufacturer data and tx power. // manufacturer data and tx power.
device->UpdateAdvertisementData( device->UpdateAdvertisementData(rssi, std::move(*advertised_uuids),
rssi, std::move(*advertised_uuids), BluetoothDevice::ServiceDataMap(), BluetoothDevice::ServiceDataMap(),
BluetoothDevice::ManufacturerDataMap(), nullptr /* tx_power */); BluetoothDevice::ManufacturerDataMap(),
nullptr /* tx_power */, nullptr /* flags */);
} }
} // namespace } // namespace
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <iterator> #include <iterator>
#include <memory> #include <memory>
#include <string> #include <string>
#include <utility>
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
...@@ -418,7 +419,8 @@ void BluetoothDevice::UpdateAdvertisementData( ...@@ -418,7 +419,8 @@ void BluetoothDevice::UpdateAdvertisementData(
UUIDList advertised_uuids, UUIDList advertised_uuids,
ServiceDataMap service_data, ServiceDataMap service_data,
ManufacturerDataMap manufacturer_data, ManufacturerDataMap manufacturer_data,
const int8_t* tx_power) { const int8_t* tx_power,
const uint8_t* flags) {
UpdateTimestamp(); UpdateTimestamp();
inquiry_rssi_ = rssi; inquiry_rssi_ = rssi;
...@@ -432,6 +434,12 @@ void BluetoothDevice::UpdateAdvertisementData( ...@@ -432,6 +434,12 @@ void BluetoothDevice::UpdateAdvertisementData(
} else { } else {
inquiry_tx_power_ = base::nullopt; inquiry_tx_power_ = base::nullopt;
} }
if (flags != nullptr) {
advertising_data_flags_ = *flags;
} else {
advertising_data_flags_.reset();
}
} }
void BluetoothDevice::ClearAdvertisementData() { void BluetoothDevice::ClearAdvertisementData() {
......
...@@ -548,7 +548,8 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice { ...@@ -548,7 +548,8 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice {
UUIDList advertised_uuids, UUIDList advertised_uuids,
ServiceDataMap service_data, ServiceDataMap service_data,
ManufacturerDataMap manufacturer_data, ManufacturerDataMap manufacturer_data,
const int8_t* tx_power); const int8_t* tx_power,
const uint8_t* flags);
// Called by BluetoothAdapter when it stops discoverying. // Called by BluetoothAdapter when it stops discoverying.
void ClearAdvertisementData(); void ClearAdvertisementData();
......
...@@ -94,7 +94,8 @@ void FakeCentral::SimulateAdvertisementReceived( ...@@ -94,7 +94,8 @@ void FakeCentral::SimulateAdvertisementReceived(
std::move(manufacturer_data), std::move(manufacturer_data),
(scan_result_ptr->scan_record->tx_power->has_value) (scan_result_ptr->scan_record->tx_power->has_value)
? &scan_result_ptr->scan_record->tx_power->value ? &scan_result_ptr->scan_record->tx_power->value
: nullptr); : nullptr,
nullptr /* flags */);
if (is_new_device) { if (is_new_device) {
// Call DeviceAdded on observers because it is a newly detected peripheral. // Call DeviceAdded on observers because it is a newly detected peripheral.
......
...@@ -91,6 +91,10 @@ FidoBleConnection::~FidoBleConnection() { ...@@ -91,6 +91,10 @@ FidoBleConnection::~FidoBleConnection() {
adapter_->RemoveObserver(this); adapter_->RemoveObserver(this);
} }
const BluetoothDevice* FidoBleConnection::GetBleDevice() const {
return adapter_ ? adapter_->GetDevice(address()) : nullptr;
}
void FidoBleConnection::Connect() { void FidoBleConnection::Connect() {
BluetoothAdapterFactory::GetAdapter( BluetoothAdapterFactory::GetAdapter(
base::Bind(&FidoBleConnection::OnGetAdapter, weak_factory_.GetWeakPtr())); base::Bind(&FidoBleConnection::OnGetAdapter, weak_factory_.GetWeakPtr()));
......
...@@ -68,6 +68,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleConnection ...@@ -68,6 +68,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleConnection
~FidoBleConnection() override; ~FidoBleConnection() override;
const std::string& address() const { return address_; } const std::string& address() const { return address_; }
const BluetoothDevice* GetBleDevice() const;
virtual void Connect(); virtual void Connect();
virtual void ReadControlPointLength(ControlPointLengthCallback callback); virtual void ReadControlPointLength(ControlPointLengthCallback callback);
...@@ -80,6 +81,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleConnection ...@@ -80,6 +81,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleConnection
protected: protected:
explicit FidoBleConnection(std::string device_address); explicit FidoBleConnection(std::string device_address);
std::string address_;
ConnectionStatusCallback connection_status_callback_;
ReadCallback read_callback_;
private: private:
// BluetoothAdapter::Observer: // BluetoothAdapter::Observer:
void DeviceAdded(BluetoothAdapter* adapter, BluetoothDevice* device) override; void DeviceAdded(BluetoothAdapter* adapter, BluetoothDevice* device) override;
...@@ -137,10 +142,6 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleConnection ...@@ -137,10 +142,6 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleConnection
static void OnWriteError(WriteCallback callback, static void OnWriteError(WriteCallback callback,
BluetoothGattService::GattErrorCode error_code); BluetoothGattService::GattErrorCode error_code);
std::string address_;
ConnectionStatusCallback connection_status_callback_;
ReadCallback read_callback_;
scoped_refptr<BluetoothAdapter> adapter_; scoped_refptr<BluetoothAdapter> adapter_;
std::unique_ptr<BluetoothGattConnection> connection_; std::unique_ptr<BluetoothGattConnection> connection_;
std::unique_ptr<BluetoothGattNotifySession> notify_session_; std::unique_ptr<BluetoothGattNotifySession> notify_session_;
......
...@@ -4,10 +4,14 @@ ...@@ -4,10 +4,14 @@
#include "device/fido/fido_ble_device.h" #include "device/fido/fido_ble_device.h"
#include <bitset>
#include "base/bind.h" #include "base/bind.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "components/apdu/apdu_response.h" #include "components/apdu/apdu_response.h"
#include "device/bluetooth/bluetooth_uuid.h"
#include "device/fido/fido_ble_frames.h" #include "device/fido/fido_ble_frames.h"
#include "device/fido/fido_ble_uuids.h"
#include "device/fido/fido_constants.h" #include "device/fido/fido_constants.h"
namespace device { namespace device {
...@@ -63,6 +67,35 @@ std::string FidoBleDevice::GetId() const { ...@@ -63,6 +67,35 @@ std::string FidoBleDevice::GetId() const {
return GetId(connection_->address()); return GetId(connection_->address());
} }
bool FidoBleDevice::IsInPairingMode() const {
const BluetoothDevice* const ble_device = connection_->GetBleDevice();
if (!ble_device)
return false;
// The spec requires exactly one of the LE Limited Discoverable Mode and LE
// General Discoverable Mode bits to be set to one when in pairing mode.
// https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#ble-advertising-format
const base::Optional<uint8_t> flags = ble_device->GetAdvertisingDataFlags();
if (flags.has_value()) {
const std::bitset<8> flags_set = *flags;
return flags_set[kLeLimitedDiscoverableModeBit] ^
flags_set[kLeGeneralDiscoverableModeBit];
}
// Since the advertisement flags might not be available due to platform
// limitations, authenticators should also provide a specific pairing mode bit
// in FIDO's service data.
// https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#ble-pairing-authnr-considerations
const std::vector<uint8_t>* const fido_service_data =
ble_device->GetServiceDataForUUID(BluetoothUUID(kFidoServiceUUID));
if (!fido_service_data)
return false;
return !fido_service_data->empty() &&
(fido_service_data->front() &
static_cast<int>(FidoServiceDataFlags::kPairingMode)) != 0;
}
FidoBleConnection::ConnectionStatusCallback FidoBleConnection::ConnectionStatusCallback
FidoBleDevice::GetConnectionStatusCallbackForTesting() { FidoBleDevice::GetConnectionStatusCallbackForTesting() {
return base::BindRepeating(&FidoBleDevice::OnConnectionStatus, return base::BindRepeating(&FidoBleDevice::OnConnectionStatus,
......
...@@ -42,6 +42,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleDevice : public FidoDevice { ...@@ -42,6 +42,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleDevice : public FidoDevice {
void Cancel() override; void Cancel() override;
std::string GetId() const override; std::string GetId() const override;
// Returns whether or not the underlying BLE device is currently in pairing
// mode by investigating the advertisement payload.
bool IsInPairingMode() const;
FidoBleConnection::ConnectionStatusCallback FidoBleConnection::ConnectionStatusCallback
GetConnectionStatusCallbackForTesting(); GetConnectionStatusCallbackForTesting();
FidoBleConnection::ReadCallback GetReadCallbackForTesting(); FidoBleConnection::ReadCallback GetReadCallbackForTesting();
......
...@@ -7,7 +7,11 @@ ...@@ -7,7 +7,11 @@
#include "base/optional.h" #include "base/optional.h"
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_task_environment.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/test/bluetooth_test.h" #include "device/bluetooth/test/bluetooth_test.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_device.h"
#include "device/fido/fido_ble_uuids.h"
#include "device/fido/fido_constants.h" #include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h" #include "device/fido/fido_parsing_utils.h"
#include "device/fido/mock_fido_ble_connection.h" #include "device/fido/mock_fido_ble_connection.h"
...@@ -24,6 +28,9 @@ using ::testing::Test; ...@@ -24,6 +28,9 @@ using ::testing::Test;
using TestDeviceCallbackReceiver = using TestDeviceCallbackReceiver =
test::ValueCallbackReceiver<base::Optional<std::vector<uint8_t>>>; test::ValueCallbackReceiver<base::Optional<std::vector<uint8_t>>>;
using NiceMockBluetoothAdapter = ::testing::NiceMock<MockBluetoothAdapter>;
using NiceMockBluetoothDevice = ::testing::NiceMock<MockBluetoothDevice>;
constexpr uint16_t kControlPointLength = 20; constexpr uint16_t kControlPointLength = 20;
constexpr uint8_t kTestData[] = {'T', 'E', 'S', 'T'}; constexpr uint8_t kTestData[] = {'T', 'E', 'S', 'T'};
...@@ -46,6 +53,23 @@ std::vector<std::vector<uint8_t>> ToSerializedFragments( ...@@ -46,6 +53,23 @@ std::vector<std::vector<uint8_t>> ToSerializedFragments(
return serialized_fragments; return serialized_fragments;
} }
void SetAdvertisingDataFlags(BluetoothDevice* device,
base::Optional<uint8_t> flags) {
device->UpdateAdvertisementData(
/* rssi */ 0, BluetoothDevice::UUIDList(),
BluetoothDevice::ServiceDataMap(), BluetoothDevice::ManufacturerDataMap(),
/* tx_power */ nullptr, base::OptionalOrNullptr(flags));
}
void SetServiceData(BluetoothDevice* device,
BluetoothDevice::ServiceDataMap service_data) {
device->UpdateAdvertisementData(
/* rssi */ 0, BluetoothDevice::UUIDList(), std::move(service_data),
BluetoothDevice::ManufacturerDataMap(),
/* tx_power */ nullptr,
/* flags */ nullptr);
}
} // namespace } // namespace
class FidoBleDeviceTest : public Test { class FidoBleDeviceTest : public Test {
...@@ -196,4 +220,68 @@ TEST_F(FidoBleDeviceTest, GetIdTest) { ...@@ -196,4 +220,68 @@ TEST_F(FidoBleDeviceTest, GetIdTest) {
device()->GetId()); device()->GetId());
} }
TEST_F(FidoBleDeviceTest, IsInPairingMode) {
// By default, a device is not in pairing mode.
EXPECT_FALSE(device()->IsInPairingMode());
// Initiate default connection behavior, which will attempt to obtain an
// adapter.
auto mock_adapter = base::MakeRefCounted<NiceMockBluetoothAdapter>();
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
EXPECT_CALL(*connection(), Connect()).WillOnce(Invoke([this] {
connection()->FidoBleConnection::Connect();
}));
device()->Connect();
// Add a mock fido device. This should also not be considered to be in pairing
// mode.
auto mock_bluetooth_device = std::make_unique<NiceMockBluetoothDevice>(
mock_adapter.get(), /* bluetooth_class */ 0u,
BluetoothTestBase::kTestDeviceNameU2f,
BluetoothTestBase::kTestDeviceAddress1, /* paired */ true,
/* connected */ false);
EXPECT_CALL(*mock_adapter, GetDevice(BluetoothTestBase::kTestDeviceAddress1))
.WillRepeatedly(Return(mock_bluetooth_device.get()));
EXPECT_FALSE(device()->IsInPairingMode());
// Provide advertisement flags, but set neither the Limited nor General LE
// Discoverable Mode bit.
SetAdvertisingDataFlags(mock_bluetooth_device.get(), 0);
EXPECT_FALSE(device()->IsInPairingMode());
// Set the limited bit, expect to be in pairing mode.
SetAdvertisingDataFlags(mock_bluetooth_device.get(),
1 << kLeLimitedDiscoverableModeBit);
EXPECT_TRUE(device()->IsInPairingMode());
// Set the general bit, expect to be in pairing mode.
SetAdvertisingDataFlags(mock_bluetooth_device.get(),
1 << kLeGeneralDiscoverableModeBit);
EXPECT_TRUE(device()->IsInPairingMode());
// Set both bits, expect to be NOT in pairing mode.
SetAdvertisingDataFlags(
mock_bluetooth_device.get(),
1 << kLeLimitedDiscoverableModeBit | 1 << kLeGeneralDiscoverableModeBit);
EXPECT_FALSE(device()->IsInPairingMode());
// Remove flags, should not result in pairing mode.
SetAdvertisingDataFlags(mock_bluetooth_device.get(), base::nullopt);
EXPECT_FALSE(device()->IsInPairingMode());
// Update the advertised service data to include the corresponding pairing
// mode flag. This should result in the device to be considered in pairing
// mode.
SetServiceData(mock_bluetooth_device.get(),
{{BluetoothUUID(kFidoServiceUUID),
{static_cast<int>(FidoServiceDataFlags::kPairingMode)}}});
EXPECT_TRUE(device()->IsInPairingMode());
// Clear out the service data again, device should not be considered to be in
// pairing mode anymore.
SetServiceData(mock_bluetooth_device.get(), {});
EXPECT_FALSE(device()->IsInPairingMode());
}
} // namespace device } // namespace device
...@@ -161,7 +161,8 @@ class CableMockAdapter : public MockBluetoothAdapter { ...@@ -161,7 +161,8 @@ class CableMockAdapter : public MockBluetoothAdapter {
mock_device->UpdateAdvertisementData( mock_device->UpdateAdvertisementData(
1 /* rssi */, BluetoothDevice::UUIDList(), std::move(service_data_map), 1 /* rssi */, BluetoothDevice::UUIDList(), std::move(service_data_map),
BluetoothDevice::ManufacturerDataMap(), nullptr /* tx_power*/); BluetoothDevice::ManufacturerDataMap(), nullptr /* tx_power*/,
nullptr /* flags */);
auto* mock_device_ptr = mock_device.get(); auto* mock_device_ptr = mock_device.get();
AddMockDevice(std::move(mock_device)); AddMockDevice(std::move(mock_device));
......
...@@ -222,6 +222,18 @@ enum class FidoBleDeviceCommand : uint8_t { ...@@ -222,6 +222,18 @@ enum class FidoBleDeviceCommand : uint8_t {
kError = 0xBF, kError = 0xBF,
}; };
// Relevant LE Discoverable Mode bits. Reference:
// Bluetooth Core Specification Supplement, Part A, section 1.3
constexpr uint8_t kLeLimitedDiscoverableModeBit = 0;
constexpr uint8_t kLeGeneralDiscoverableModeBit = 1;
// Fido Service Data Flags as specified in
// https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#ble-pairing-authnr-considerations
enum class FidoServiceDataFlags : uint8_t {
kPairingMode = 0x80,
kPasskeyEntry = 0x40,
};
// Authenticator API commands supported by CTAP devices, as specified in // Authenticator API commands supported by CTAP devices, as specified in
// https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#authenticator-api // https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#authenticator-api
enum class CtapRequestCommand : uint8_t { enum class CtapRequestCommand : uint8_t {
......
...@@ -45,9 +45,6 @@ class MockFidoBleConnection : public FidoBleConnection { ...@@ -45,9 +45,6 @@ class MockFidoBleConnection : public FidoBleConnection {
ReadCallback& read_callback() { return read_callback_; } ReadCallback& read_callback() { return read_callback_; }
private: private:
ConnectionStatusCallback connection_status_callback_;
ReadCallback read_callback_;
DISALLOW_COPY_AND_ASSIGN(MockFidoBleConnection); DISALLOW_COPY_AND_ASSIGN(MockFidoBleConnection);
}; };
......
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