diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-02-13 15:05:36 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-02-14 10:33:47 +0000 |
commit | e684a3455bcc29a6e3e66a004e352dea4e1141e7 (patch) | |
tree | d55b4003bde34d7d05f558f02cfd82b2a66a7aac /chromium/device | |
parent | 2b94bfe47ccb6c08047959d1c26e392919550e86 (diff) | |
download | qtwebengine-chromium-e684a3455bcc29a6e3e66a004e352dea4e1141e7.tar.gz |
BASELINE: Update Chromium to 72.0.3626.110 and Ninja to 1.9.0
Change-Id: Ic57220b00ecc929a893c91f5cc552f5d3e99e922
Reviewed-by: Michael Brüning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/device')
201 files changed, 5088 insertions, 1825 deletions
diff --git a/chromium/device/BUILD.gn b/chromium/device/BUILD.gn index 7edaa8891cd..fa1385f0e15 100644 --- a/chromium/device/BUILD.gn +++ b/chromium/device/BUILD.gn @@ -106,6 +106,13 @@ test("device_unittests") { "test/run_all_unittests.cc", ] + if (is_fuchsia) { + sources += [ + "bluetooth/test/bluetooth_test_fuchsia.cc", + "bluetooth/test/bluetooth_test_fuchsia.h", + ] + } + deps = [ "//base/test:test_support", "//base/third_party/dynamic_annotations:dynamic_annotations", diff --git a/chromium/device/OWNERS b/chromium/device/OWNERS index 2d212568dac..a0f1773a4f8 100644 --- a/chromium/device/OWNERS +++ b/chromium/device/OWNERS @@ -1,5 +1,5 @@ reillyg@chromium.org -rockot@chromium.org +rockot@google.com per-file BUILD.gn=* diff --git a/chromium/device/base/device_monitor_linux.cc b/chromium/device/base/device_monitor_linux.cc index d65d3e60293..1218bb1aeaa 100644 --- a/chromium/device/base/device_monitor_linux.cc +++ b/chromium/device/base/device_monitor_linux.cc @@ -84,6 +84,7 @@ void DeviceMonitorLinux::RemoveObserver(Observer* observer) { void DeviceMonitorLinux::Enumerate(const EnumerateCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); + base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK); ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev_.get())); if (!enumerate) { @@ -114,6 +115,7 @@ DeviceMonitorLinux::~DeviceMonitorLinux() { void DeviceMonitorLinux::OnMonitorCanReadWithoutBlocking() { DCHECK(thread_checker_.CalledOnValidThread()); + base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK); ScopedUdevDevicePtr device(udev_monitor_receive_device(monitor_.get())); if (!device) return; diff --git a/chromium/device/bluetooth/BUILD.gn b/chromium/device/bluetooth/BUILD.gn index 96e66736e90..851b4f384f2 100644 --- a/chromium/device/bluetooth/BUILD.gn +++ b/chromium/device/bluetooth/BUILD.gn @@ -156,6 +156,8 @@ component("bluetooth") { "bluetooth_low_energy_defs_win.h", "bluetooth_low_energy_device_mac.h", "bluetooth_low_energy_device_mac.mm", + "bluetooth_low_energy_device_watcher_mac.h", + "bluetooth_low_energy_device_watcher_mac.mm", "bluetooth_low_energy_discovery_manager_mac.h", "bluetooth_low_energy_discovery_manager_mac.mm", "bluetooth_low_energy_peripheral_delegate.h", diff --git a/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java b/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java index 43d3f744cfe..da726ec8d0e 100644 --- a/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java +++ b/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java @@ -131,12 +131,12 @@ final class ChromeBluetoothDevice { @Override public void run() { if (newState == android.bluetooth.BluetoothProfile.STATE_CONNECTED) { - RecordHistogram.recordSparseSlowlyHistogram( + RecordHistogram.recordSparseHistogram( "Bluetooth.Web.Android.onConnectionStateChange.Status.Connected", status); mBluetoothGatt.discoverServices(); } else if (newState == android.bluetooth.BluetoothProfile.STATE_DISCONNECTED) { - RecordHistogram.recordSparseSlowlyHistogram( + RecordHistogram.recordSparseHistogram( "Bluetooth.Web.Android.onConnectionStateChange.Status.Disconnected", status); if (mBluetoothGatt != null) { @@ -144,7 +144,7 @@ final class ChromeBluetoothDevice { mBluetoothGatt = null; } } else { - RecordHistogram.recordSparseSlowlyHistogram( + RecordHistogram.recordSparseHistogram( "Bluetooth.Web.Android.onConnectionStateChange.Status.InvalidState", status); } @@ -167,13 +167,13 @@ final class ChromeBluetoothDevice { // When the device disconnects it deletes // mBluetoothGatt, so we need to check it's not null. if (mBluetoothGatt == null) { - RecordHistogram.recordSparseSlowlyHistogram( + RecordHistogram.recordSparseHistogram( "Bluetooth.Web.Android.onServicesDiscovered.Status." + "Disconnected", status); return; } - RecordHistogram.recordSparseSlowlyHistogram( + RecordHistogram.recordSparseHistogram( "Bluetooth.Web.Android.onServicesDiscovered.Status.Connected", status); @@ -231,7 +231,7 @@ final class ChromeBluetoothDevice { // when the event races object destruction. Log.v(TAG, "onCharacteristicRead when chromeCharacteristic == null."); } else { - RecordHistogram.recordSparseSlowlyHistogram( + RecordHistogram.recordSparseHistogram( "Bluetooth.Web.Android.onCharacteristicRead.Status", status); chromeCharacteristic.onCharacteristicRead(status); } @@ -253,7 +253,7 @@ final class ChromeBluetoothDevice { // when the event races object destruction. Log.v(TAG, "onCharacteristicWrite when chromeCharacteristic == null."); } else { - RecordHistogram.recordSparseSlowlyHistogram( + RecordHistogram.recordSparseHistogram( "Bluetooth.Web.Android.onCharacteristicWrite.Status", status); chromeCharacteristic.onCharacteristicWrite(status); } @@ -274,7 +274,7 @@ final class ChromeBluetoothDevice { // when the event races object destruction. Log.v(TAG, "onDescriptorRead when chromeDescriptor == null."); } else { - RecordHistogram.recordSparseSlowlyHistogram( + RecordHistogram.recordSparseHistogram( "Bluetooth.Web.Android.onDescriptorRead.Status", status); chromeDescriptor.onDescriptorRead(status); } @@ -295,7 +295,7 @@ final class ChromeBluetoothDevice { // when the event races object destruction. Log.v(TAG, "onDescriptorWrite when chromeDescriptor == null."); } else { - RecordHistogram.recordSparseSlowlyHistogram( + RecordHistogram.recordSparseHistogram( "Bluetooth.Web.Android.onDescriptorWrite.Status", status); chromeDescriptor.onDescriptorWrite(status); } diff --git a/chromium/device/bluetooth/bluetooth_adapter.h b/chromium/device/bluetooth/bluetooth_adapter.h index a67ca355555..c0de60694fd 100644 --- a/chromium/device/bluetooth/bluetooth_adapter.h +++ b/chromium/device/bluetooth/bluetooth_adapter.h @@ -89,6 +89,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapter // |device| changes: // * GetAddress() // * GetAppearance() + // * GetName() (Chrome OS and Windows only) // * GetBluetoothClass() // * GetInquiryRSSI() // * GetInquiryTxPower() @@ -151,6 +152,13 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapter BluetoothDevice* device, int16_t rssi, const std::vector<uint8_t>& eir) {} + + // This function is implemented for ChromeOS only. + // Called when |device|'s state has changed from connected to not connected + // or vice versa. + virtual void DeviceConnectedStateChanged(BluetoothAdapter* adapter, + BluetoothDevice* device, + bool is_now_connected) {} #endif // Called when the device |device| is removed from the adapter |adapter|, diff --git a/chromium/device/bluetooth/bluetooth_adapter_factory_wrapper.cc b/chromium/device/bluetooth/bluetooth_adapter_factory_wrapper.cc index bb06297d163..c1756cb4617 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_factory_wrapper.cc +++ b/chromium/device/bluetooth/bluetooth_adapter_factory_wrapper.cc @@ -16,7 +16,7 @@ namespace { static base::LazyInstance<device::BluetoothAdapterFactoryWrapper>::Leaky - g_singleton = LAZY_INSTANCE_INITIALIZER; + g_bluetooth_adapter_factory_wrapper_singleton = LAZY_INSTANCE_INITIALIZER; } // namespace @@ -32,7 +32,7 @@ BluetoothAdapterFactoryWrapper::~BluetoothAdapterFactoryWrapper() { // static BluetoothAdapterFactoryWrapper& BluetoothAdapterFactoryWrapper::Get() { - return g_singleton.Get(); + return g_bluetooth_adapter_factory_wrapper_singleton.Get(); } bool BluetoothAdapterFactoryWrapper::IsLowEnergySupported() { diff --git a/chromium/device/bluetooth/bluetooth_adapter_mac.h b/chromium/device/bluetooth/bluetooth_adapter_mac.h index 3c13e213006..f5280d6f0bd 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_mac.h +++ b/chromium/device/bluetooth/bluetooth_adapter_mac.h @@ -7,8 +7,10 @@ #include <IOKit/IOReturn.h> +#include <map> #include <memory> #include <string> +#include <unordered_map> #include <vector> #include "base/containers/hash_tables.h" @@ -23,6 +25,7 @@ #include "device/bluetooth/bluetooth_export.h" #include "device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.h" #include "device/bluetooth/bluetooth_low_energy_device_mac.h" +#include "device/bluetooth/bluetooth_low_energy_device_watcher_mac.h" #include "device/bluetooth/bluetooth_low_energy_discovery_manager_mac.h" #include "device/bluetooth/bluetooth_uuid.h" @@ -120,12 +123,21 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterMac void DidFailToConnectPeripheral(CBPeripheral* peripheral, NSError* error); void DidDisconnectPeripheral(CBPeripheral* peripheral, NSError* error); + bool IsBluetoothLowEnergyDeviceSystemPaired( + base::StringPiece device_identifier) const; + protected: + using GetDevicePairedStatusCallback = + base::RepeatingCallback<bool(const std::string& address)>; + // BluetoothAdapter override: bool SetPoweredImpl(bool powered) override; void RemovePairingDelegateInternal( device::BluetoothDevice::PairingDelegate* pairing_delegate) override; + void UpdateKnownLowEnergyDevices( + std::map<std::string, std::string> updated_low_energy_device_info); + private: // Struct bundling information about the state of the HostController. struct HostControllerState { @@ -165,6 +177,14 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterMac void SetPowerStateFunctionForTesting( SetControllerPowerStateFunction power_state_function); + // Allow the mocking out of BluetoothLowEnergyDeviceWatcher for testing. + void SetLowEnergyDeviceWatcherForTesting( + scoped_refptr<BluetoothLowEnergyDeviceWatcherMac> watcher); + + // Allow the mocking of out GetDevicePairedStatusCallback for testing. + void SetGetDevicePairedStatusCallbackForTesting( + GetDevicePairedStatusCallback callback); + // The length of time that must elapse since the last Inquiry response (on // Classic devices) or call to BluetoothLowEnergyDevice::Update() (on Low // Energy) before a discovered device is considered to be no longer available. @@ -284,6 +304,17 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterMac base::scoped_nsobject<BluetoothLowEnergyPeripheralManagerDelegate> low_energy_peripheral_manager_delegate_; + GetDevicePairedStatusCallback device_paired_status_callback_; + + // Watches system file /Library/Preferences/com.apple.Bluetooth.plist to + // obtain information about system paired bluetooth devices. + scoped_refptr<BluetoothLowEnergyDeviceWatcherMac> + bluetooth_low_energy_device_watcher_; + + // Map of UUID formatted device identifiers of paired Bluetooth devices and + // corresponding device address. + std::map<std::string, std::string> low_energy_devices_info_; + base::WeakPtrFactory<BluetoothAdapterMac> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(BluetoothAdapterMac); diff --git a/chromium/device/bluetooth/bluetooth_adapter_mac.mm b/chromium/device/bluetooth/bluetooth_adapter_mac.mm index 24a63be8a4b..fafb98764b5 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_mac.mm +++ b/chromium/device/bluetooth/bluetooth_adapter_mac.mm @@ -21,7 +21,9 @@ #include "base/memory/ptr_util.h" #include "base/numerics/safe_conversions.h" #include "base/single_thread_task_runner.h" +#include "base/stl_util.h" #include "base/strings/sys_string_conversions.h" +#include "base/task/task_traits.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "device/bluetooth/bluetooth_adapter_mac_metrics.h" @@ -31,6 +33,7 @@ #include "device/bluetooth/bluetooth_discovery_session.h" #include "device/bluetooth/bluetooth_discovery_session_outcome.h" #include "device/bluetooth/bluetooth_low_energy_central_manager_delegate.h" +#include "device/bluetooth/bluetooth_low_energy_device_watcher_mac.h" #include "device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.h" #include "device/bluetooth/bluetooth_socket_mac.h" @@ -53,6 +56,12 @@ namespace { // The frequency with which to poll the adapter for updates. const int kPollIntervalMs = 500; +bool IsDeviceSystemPaired(const std::string& device_address) { + IOBluetoothDevice* device = [IOBluetoothDevice + deviceWithAddressString:base::SysUTF8ToNSString(device_address)]; + return device && [device isPaired]; +} + } // namespace namespace device { @@ -125,6 +134,8 @@ BluetoothAdapterMac::BluetoothAdapterMac() should_update_name_(true), classic_discovery_manager_( BluetoothDiscoveryManagerMac::CreateClassic(this)), + device_paired_status_callback_( + base::BindRepeating(&IsDeviceSystemPaired)), weak_ptr_factory_(this) { if (IsLowEnergyAvailable()) { low_energy_discovery_manager_.reset( @@ -339,6 +350,28 @@ BluetoothAdapterMac::GetHostControllerState() { return state; } +void BluetoothAdapterMac::UpdateKnownLowEnergyDevices( + std::map<std::string, std::string> updated_low_energy_devices_info) { + std::map<std::string, std::string> changed_devices; + // Notify DeviceChanged() to devices that have been newly paired as well as to + // devices that have been removed from the pairing list. + std::set_symmetric_difference( + updated_low_energy_devices_info.begin(), + updated_low_energy_devices_info.end(), low_energy_devices_info_.begin(), + low_energy_devices_info_.end(), + std::inserter(changed_devices, changed_devices.end())); + + low_energy_devices_info_ = std::move(updated_low_energy_devices_info); + for (const auto& info : changed_devices) { + auto it = devices_.find( + BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(info.first)); + if (it == devices_.end()) + continue; + + NotifyDeviceChanged(it->second.get()); + } +} + void BluetoothAdapterMac::SetCentralManagerForTesting( CBCentralManager* central_manager) { CHECK(BluetoothAdapterMac::IsLowEnergyAvailable()); @@ -366,6 +399,19 @@ void BluetoothAdapterMac::SetPowerStateFunctionForTesting( power_state_function_ = std::move(power_state_function); } +void BluetoothAdapterMac::SetLowEnergyDeviceWatcherForTesting( + scoped_refptr<BluetoothLowEnergyDeviceWatcherMac> + bluetooth_low_energy_device_watcher) { + bluetooth_low_energy_device_watcher_ = + std::move(bluetooth_low_energy_device_watcher); + bluetooth_low_energy_device_watcher_->Init(); +} + +void BluetoothAdapterMac::SetGetDevicePairedStatusCallbackForTesting( + GetDevicePairedStatusCallback device_paired_status_callback) { + device_paired_status_callback_ = std::move(device_paired_status_callback); +} + void BluetoothAdapterMac::AddDiscoverySession( BluetoothDiscoveryFilter* discovery_filter, const base::Closure& callback, @@ -496,6 +542,16 @@ void BluetoothAdapterMac::Init() { low_energy_advertisement_manager_->Init(ui_task_runner_, low_energy_peripheral_manager_); PollAdapter(); + + // To obtain list of low energy devices known to the system, we need to parse + // and watch system property list file for paired device addresses. + bluetooth_low_energy_device_watcher_ = + BluetoothLowEnergyDeviceWatcherMac::CreateAndStartWatching( + ui_task_runner_, + base::BindRepeating(&BluetoothAdapterMac::UpdateKnownLowEnergyDevices, + weak_ptr_factory_.GetWeakPtr())); + + bluetooth_low_energy_device_watcher_->ReadBluetoothPropertyListFile(); } void BluetoothAdapterMac::InitForTest( @@ -795,6 +851,17 @@ void BluetoothAdapterMac::DidDisconnectPeripheral(CBPeripheral* peripheral, device_mac->DidDisconnectPeripheral(error); } +bool BluetoothAdapterMac::IsBluetoothLowEnergyDeviceSystemPaired( + base::StringPiece device_identifier) const { + auto it = std::find_if( + low_energy_devices_info_.begin(), low_energy_devices_info_.end(), + [&](const auto& info) { return info.first == device_identifier; }); + if (it == low_energy_devices_info_.end()) + return false; + + return device_paired_status_callback_.Run(it->second); +} + BluetoothLowEnergyDeviceMac* BluetoothAdapterMac::GetBluetoothLowEnergyDeviceMac(CBPeripheral* peripheral) { std::string device_address = diff --git a/chromium/device/bluetooth/bluetooth_adapter_mac_unittest.mm b/chromium/device/bluetooth/bluetooth_adapter_mac_unittest.mm index 05d44819034..3a5aca107ae 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_mac_unittest.mm +++ b/chromium/device/bluetooth/bluetooth_adapter_mac_unittest.mm @@ -9,9 +9,15 @@ #include <memory> #include "base/bind.h" +#include "base/files/file_path.h" +#include "base/files/file_path_watcher.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" +#include "base/sequenced_task_runner.h" #include "base/test/bind_test_util.h" +#include "base/test/scoped_task_environment.h" #include "base/test/test_simple_task_runner.h" #include "build/build_config.h" #include "device/bluetooth/bluetooth_adapter.h" @@ -19,6 +25,7 @@ #include "device/bluetooth/bluetooth_discovery_session.h" #include "device/bluetooth/bluetooth_discovery_session_outcome.h" #import "device/bluetooth/bluetooth_low_energy_device_mac.h" +#include "device/bluetooth/bluetooth_low_energy_device_watcher_mac.h" #import "device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h" #import "device/bluetooth/test/mock_bluetooth_central_manager_mac.h" #import "device/bluetooth/test/test_bluetooth_adapter_observer.h" @@ -38,16 +45,71 @@ void IOBluetoothPreferenceSetControllerPowerState(int state); } namespace { + +const char kTestPropertyListFileName[] = "test_property_list_file.plist"; + // |kTestHashAddress| is the hash corresponding to identifier |kTestNSUUID|. const char kTestNSUUID[] = "00000000-1111-2222-3333-444444444444"; const char kTestHashAddress[] = "D1:6F:E3:22:FD:5B"; const int kTestRssi = 0; + +NSDictionary* CreateTestPropertyListData() { + return @{ + @"CoreBluetoothCache" : @{ + @"00000000-1111-2222-3333-444444444444" : @{ + @"DeviceAddress" : @"22-22-22-22-22-22", + @"DeviceAddressType" : @1, + @"ServiceChangedHandle" : @3, + @"ServiceChangeSubscribed" : @0, + @"ServiceDiscoveryComplete" : @0 + } + } + }; +} + +bool IsTestDeviceSystemPaired(const std::string& address) { + return true; +} + } // namespace namespace device { class BluetoothAdapterMacTest : public testing::Test { public: + class FakeBluetoothLowEnergyDeviceWatcherMac + : public BluetoothLowEnergyDeviceWatcherMac { + public: + FakeBluetoothLowEnergyDeviceWatcherMac( + scoped_refptr<base::SequencedTaskRunner> ui_thread_task_runner, + LowEnergyDeviceListUpdatedCallback callback) + : BluetoothLowEnergyDeviceWatcherMac(ui_thread_task_runner, callback), + weak_ptr_factory_(this) {} + + void SimulatePropertyListFileChanged( + const base::FilePath& path, + const std::string& changed_file_content) { + auto expected_file_size = changed_file_content.length(); + ASSERT_EQ(static_cast<int>(expected_file_size), + base::WriteFile(path, changed_file_content.data(), + expected_file_size)); + OnPropertyListFileChangedOnFileThread(path, false /* error */); + } + + private: + ~FakeBluetoothLowEnergyDeviceWatcherMac() override = default; + + void Init() override { ReadBluetoothPropertyListFile(); } + + void ReadBluetoothPropertyListFile() override { + low_energy_device_list_updated_callback().Run( + ParseBluetoothDevicePropertyListData(CreateTestPropertyListData())); + } + + base::WeakPtrFactory<FakeBluetoothLowEnergyDeviceWatcherMac> + weak_ptr_factory_; + }; + BluetoothAdapterMacTest() : ui_task_runner_(new base::TestSimpleTaskRunner()), adapter_(new BluetoothAdapterMac()), @@ -55,9 +117,25 @@ class BluetoothAdapterMacTest : public testing::Test { observer_(adapter_), callback_count_(0), error_callback_count_(0) { + adapter_mac_->SetGetDevicePairedStatusCallbackForTesting( + base::BindRepeating(&IsTestDeviceSystemPaired)); adapter_mac_->InitForTest(ui_task_runner_); + fake_low_energy_device_watcher_ = + base::MakeRefCounted<FakeBluetoothLowEnergyDeviceWatcherMac>( + ui_task_runner_, + base::BindRepeating( + &BluetoothAdapterMac::UpdateKnownLowEnergyDevices, + adapter_mac_->weak_ptr_factory_.GetWeakPtr())); + } + + void SetUp() override { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + test_property_list_file_path_ = + temp_dir_.GetPath().AppendASCII(kTestPropertyListFileName); } + void TearDown() override { scoped_task_environment_.RunUntilIdle(); } + // Helper methods for setup and access to BluetoothAdapterMacTest's members. void PollAdapter() { adapter_mac_->PollAdapter(); } @@ -141,6 +219,11 @@ class BluetoothAdapterMacTest : public testing::Test { int NumDiscoverySessions() { return adapter_mac_->num_discovery_sessions_; } + void SetFakeLowEnergyDeviceWatcher() { + adapter_mac_->SetLowEnergyDeviceWatcherForTesting( + fake_low_energy_device_watcher_); + } + // Generic callbacks. void Callback() { ++callback_count_; } void ErrorCallback() { ++error_callback_count_; } @@ -149,9 +232,12 @@ class BluetoothAdapterMacTest : public testing::Test { } protected: + base::test::ScopedTaskEnvironment scoped_task_environment_; scoped_refptr<base::TestSimpleTaskRunner> ui_task_runner_; scoped_refptr<BluetoothAdapter> adapter_; BluetoothAdapterMac* adapter_mac_; + scoped_refptr<FakeBluetoothLowEnergyDeviceWatcherMac> + fake_low_energy_device_watcher_; TestBluetoothAdapterObserver observer_; // Owned by |adapter_mac_|. @@ -159,6 +245,8 @@ class BluetoothAdapterMacTest : public testing::Test { int callback_count_; int error_callback_count_; + base::ScopedTempDir temp_dir_; + base::FilePath test_property_list_file_path_; }; // Test if private IOBluetooth APIs are callable on all supported macOS @@ -314,4 +402,127 @@ TEST_F(BluetoothAdapterMacTest, LowEnergyDeviceUpdatedNewDevice) { EXPECT_TRUE(DevicePresent(mock_peripheral)); } +TEST_F(BluetoothAdapterMacTest, GetSystemPairedLowEnergyDevice) { + SetFakeLowEnergyDeviceWatcher(); + ui_task_runner_->RunUntilIdle(); + EXPECT_TRUE( + adapter_mac_->IsBluetoothLowEnergyDeviceSystemPaired(kTestNSUUID)); +} + +TEST_F(BluetoothAdapterMacTest, GetNewlyPairedLowEnergyDevice) { + constexpr char kPropertyListFileContentWithAddedDevice[] = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " + "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" + "<plist version=\"1.0\">" + "<dict>" + " <key>CoreBluetoothCache</key>" + " <dict> " + " <key>E7F8589A-A7D9-4B94-9A08-D89076A159F4</key>" + " <dict> " + " <key>DeviceAddress</key>" + " <string>11-11-11-11-11-11</string>" + " <key>DeviceAddressType</key>" + " <integer>1</integer>" + " <key>ServiceChangedHandle</key>" + " <integer>3</integer>" + " <key>ServiceChangeSubscribed</key>" + " <integer>0</integer>" + " <key>ServiceDiscoveryComplete</key>" + " <integer>0</integer>" + " </dict>" + " <key>00000000-1111-2222-3333-444444444444</key>" + " <dict> " + " <key>DeviceAddress</key>" + " <string>22-22-22-22-22-22</string>" + " <key>DeviceAddressType</key>" + " <integer>1</integer>" + " <key>ServiceChangedHandle</key>" + " <integer>3</integer>" + " <key>ServiceChangeSubscribed</key>" + " <integer>0</integer>" + " <key>ServiceDiscoveryComplete</key>" + " <integer>0</integer>" + " </dict>" + + " </dict>" + "</dict>" + "</plist>"; + + const char kTestAddedDeviceNSUUID[] = "E7F8589A-A7D9-4B94-9A08-D89076A159F4"; + + ASSERT_TRUE(SetMockCentralManager(CBCentralManagerStatePoweredOn)); + + base::scoped_nsobject<CBPeripheral> mock_peripheral_one( + CreateMockPeripheral(kTestNSUUID)); + ASSERT_TRUE(mock_peripheral_one); + + LowEnergyDeviceUpdated( + mock_peripheral_one, + base::scoped_nsobject<NSDictionary>(AdvertisementData()), kTestRssi); + + base::scoped_nsobject<CBPeripheral> mock_peripheral_two( + CreateMockPeripheral(kTestAddedDeviceNSUUID)); + ASSERT_TRUE(mock_peripheral_two); + + LowEnergyDeviceUpdated( + mock_peripheral_two, + base::scoped_nsobject<NSDictionary>(AdvertisementData()), kTestRssi); + observer_.Reset(); + + // BluetoothAdapterMac only notifies observers of changed devices detected by + // BluetoothLowEnergyDeviceWatcherMac if the device has been already known to + // the system(i.e. the changed device is in BluetoothAdatper::devices_). As + // so, add mock devices prior to setting BluetoothLowenergyDeviceWatcherMac. + SetFakeLowEnergyDeviceWatcher(); + + EXPECT_EQ(1, observer_.device_changed_count()); + observer_.Reset(); + + fake_low_energy_device_watcher_->SimulatePropertyListFileChanged( + test_property_list_file_path_, kPropertyListFileContentWithAddedDevice); + ui_task_runner_->RunUntilIdle(); + EXPECT_EQ(1, observer_.device_changed_count()); + EXPECT_TRUE(adapter_mac_->IsBluetoothLowEnergyDeviceSystemPaired( + kTestAddedDeviceNSUUID)); +} + +TEST_F(BluetoothAdapterMacTest, NotifyObserverWhenDeviceIsUnpaired) { + constexpr char kPropertyListFileContentWithRemovedDevice[] = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " + "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" + "<plist version=\"1.0\">" + "<dict>" + " <key>CoreBluetoothCache</key>" + " <dict> " + " </dict>" + "</dict>" + "</plist>"; + + if (!SetMockCentralManager(CBCentralManagerStatePoweredOn)) + return; + + base::scoped_nsobject<CBPeripheral> mock_peripheral( + CreateMockPeripheral(kTestNSUUID)); + if (!mock_peripheral) + return; + + LowEnergyDeviceUpdated( + mock_peripheral, base::scoped_nsobject<NSDictionary>(AdvertisementData()), + kTestRssi); + observer_.Reset(); + + SetFakeLowEnergyDeviceWatcher(); + EXPECT_EQ(1, observer_.device_changed_count()); + observer_.Reset(); + + fake_low_energy_device_watcher_->SimulatePropertyListFileChanged( + test_property_list_file_path_, kPropertyListFileContentWithRemovedDevice); + ui_task_runner_->RunUntilIdle(); + EXPECT_EQ(1, observer_.device_changed_count()); + EXPECT_FALSE( + adapter_mac_->IsBluetoothLowEnergyDeviceSystemPaired(kTestNSUUID)); +} + } // namespace device diff --git a/chromium/device/bluetooth/bluetooth_adapter_unittest.cc b/chromium/device/bluetooth/bluetooth_adapter_unittest.cc index 274ca34a628..25c725679d5 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_unittest.cc +++ b/chromium/device/bluetooth/bluetooth_adapter_unittest.cc @@ -39,6 +39,8 @@ #include "device/bluetooth/test/bluetooth_test_cast.h" #elif defined(OS_CHROMEOS) || defined(OS_LINUX) #include "device/bluetooth/test/bluetooth_test_bluez.h" +#elif defined(OS_FUCHSIA) +#include "device/bluetooth/test/bluetooth_test_fuchsia.h" #endif using device::BluetoothDevice; diff --git a/chromium/device/bluetooth/bluetooth_adapter_winrt.cc b/chromium/device/bluetooth/bluetooth_adapter_winrt.cc index f53da79c97d..1052a6a8aaf 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_winrt.cc +++ b/chromium/device/bluetooth/bluetooth_adapter_winrt.cc @@ -421,9 +421,8 @@ ComPtr<IBluetoothLEAdvertisement> GetAdvertisement( return advertisement; } -base::Optional<std::string> GetDeviceName( - IBluetoothLEAdvertisementReceivedEventArgs* received) { - ComPtr<IBluetoothLEAdvertisement> advertisement = GetAdvertisement(received); +base::Optional<std::string> ExtractDeviceName( + IBluetoothLEAdvertisement* advertisement) { if (!advertisement) return base::nullopt; @@ -435,6 +434,10 @@ base::Optional<std::string> GetDeviceName( return base::nullopt; } + // Return early otherwise ScopedHString will create an empty string. + if (!local_name) + return base::nullopt; + return base::win::ScopedHString(local_name).GetAsUTF8(); } @@ -449,6 +452,8 @@ void ExtractAndUpdateAdvertisementData( } ComPtr<IBluetoothLEAdvertisement> advertisement = GetAdvertisement(received); + static_cast<BluetoothDeviceWinrt*>(device)->UpdateLocalName( + ExtractDeviceName(advertisement.Get())); device->UpdateAdvertisementData(rssi, ExtractFlags(advertisement.Get()), ExtractAdvertisedUUIDs(advertisement.Get()), ExtractTxPower(advertisement.Get()), @@ -871,10 +876,8 @@ BluetoothAdapterWinrt::CreateAdvertisement() const { } std::unique_ptr<BluetoothDeviceWinrt> BluetoothAdapterWinrt::CreateDevice( - uint64_t raw_address, - base::Optional<std::string> local_name) { - return std::make_unique<BluetoothDeviceWinrt>(this, raw_address, - std::move(local_name)); + uint64_t raw_address) { + return std::make_unique<BluetoothDeviceWinrt>(this, raw_address); } void BluetoothAdapterWinrt::OnGetDefaultAdapter( @@ -1144,8 +1147,7 @@ void BluetoothAdapterWinrt::OnAdvertisementReceived( if (is_new_device) { bool was_inserted = false; std::tie(it, was_inserted) = devices_.emplace( - std::move(bluetooth_address), - CreateDevice(raw_bluetooth_address, GetDeviceName(received))); + std::move(bluetooth_address), CreateDevice(raw_bluetooth_address)); DCHECK(was_inserted); } diff --git a/chromium/device/bluetooth/bluetooth_adapter_winrt.h b/chromium/device/bluetooth/bluetooth_adapter_winrt.h index 25611d511da..3daa169f8cf 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_winrt.h +++ b/chromium/device/bluetooth/bluetooth_adapter_winrt.h @@ -117,8 +117,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterWinrt : public BluetoothAdapter { const; virtual std::unique_ptr<BluetoothDeviceWinrt> CreateDevice( - uint64_t raw_address, - base::Optional<std::string> local_name); + uint64_t raw_address); private: void OnGetDefaultAdapter( diff --git a/chromium/device/bluetooth/bluetooth_device_unittest.cc b/chromium/device/bluetooth/bluetooth_device_unittest.cc index a443aeffba4..4e1d05c0804 100644 --- a/chromium/device/bluetooth/bluetooth_device_unittest.cc +++ b/chromium/device/bluetooth/bluetooth_device_unittest.cc @@ -26,6 +26,8 @@ #include "device/bluetooth/test/bluetooth_test_cast.h" #elif defined(OS_CHROMEOS) || defined(OS_LINUX) #include "device/bluetooth/test/bluetooth_test_bluez.h" +#elif defined(OS_FUCHSIA) +#include "device/bluetooth/test/bluetooth_test_fuchsia.h" #endif namespace device { @@ -291,6 +293,30 @@ TEST_F(BluetoothTest, LowEnergyDeviceProperties) { base::ContainsKey(uuids, BluetoothUUID(kTestUUIDGenericAttribute))); } +// Verifies that the device name can be populated by later advertisement +// packets and is persistent. +#if defined(OS_WIN) +TEST_P(BluetoothTestWinrt, LowEnergyDeviceNameDelayed) { +#else +// This test does not yet pass on any other platform. +TEST_F(BluetoothTest, DISABLED_LowEnergyDeviceNameDelayed) { +#endif + if (!PlatformSupportsLowEnergy()) { + LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; + return; + } + InitWithFakeAdapter(); + StartLowEnergyDiscoverySession(); + BluetoothDevice* device = SimulateLowEnergyDevice(3); + ASSERT_TRUE(device); + // GetName() returns a base::Optional<std:string> however some backends still + // return an empty string rather than nullopt when no name is available. + EXPECT_TRUE(!device->GetName().has_value() || device->GetName()->empty()); + + SimulateLowEnergyDevice(1); + EXPECT_EQ(base::UTF8ToUTF16(kTestDeviceName), device->GetNameForDisplay()); +} + // Device with no advertised Service UUIDs. #if defined(OS_WIN) TEST_P(BluetoothTestWinrt, LowEnergyDeviceNoUUIDs) { @@ -2128,4 +2154,34 @@ TEST_F(BluetoothTest, MAYBE_GetPrimaryServicesByUUID) { } } +#if defined(OS_WIN) +TEST_P(BluetoothTestWinrtOnly, GattConnectedNameChange) { +#else +// The SimulateGattNameChange() function is not yet available on other +// platforms. +TEST_F(BluetoothTest, DISABLED_GattConnectedNameChange) { +#endif + if (!PlatformSupportsLowEnergy()) { + LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; + return; + } + InitWithFakeAdapter(); + + StartLowEnergyDiscoverySession(); + BluetoothDevice* device = SimulateLowEnergyDevice(3); + device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED), + GetConnectErrorCallback(Call::NOT_EXPECTED)); + SimulateGattConnection(device); + base::RunLoop().RunUntilIdle(); + // GetName() returns a base::Optional<std:string> however some backends still + // return an empty string rather than nullopt when no name is available. + EXPECT_TRUE(!device->GetName() || device->GetName()->empty()); + + TestBluetoothAdapterObserver observer(adapter_); + SimulateGattNameChange(device, kTestDeviceName); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, observer.device_changed_count()); + EXPECT_EQ(base::UTF8ToUTF16(kTestDeviceName), device->GetNameForDisplay()); +} + } // namespace device diff --git a/chromium/device/bluetooth/bluetooth_device_winrt.cc b/chromium/device/bluetooth/bluetooth_device_winrt.cc index 0313a90e235..da92568c452 100644 --- a/chromium/device/bluetooth/bluetooth_device_winrt.cc +++ b/chromium/device/bluetooth/bluetooth_device_winrt.cc @@ -136,24 +136,27 @@ void RemoveGattServicesChangedHandler(IBluetoothLEDevice* ble_device, } } +void RemoveNameChangedHandler(IBluetoothLEDevice* ble_device, + EventRegistrationToken token) { + HRESULT hr = ble_device->remove_NameChanged(token); + if (FAILED(hr)) { + VLOG(2) << "Removing NameChanged Handler failed: " + << logging::SystemErrorCodeToString(hr); + } +} + } // namespace -BluetoothDeviceWinrt::BluetoothDeviceWinrt( - BluetoothAdapterWinrt* adapter, - uint64_t raw_address, - base::Optional<std::string> local_name) +BluetoothDeviceWinrt::BluetoothDeviceWinrt(BluetoothAdapterWinrt* adapter, + uint64_t raw_address) : BluetoothDevice(adapter), raw_address_(raw_address), address_(CanonicalizeAddress(raw_address)), - local_name_(std::move(local_name)), weak_ptr_factory_(this) {} BluetoothDeviceWinrt::~BluetoothDeviceWinrt() { CloseDevice(ble_device_); - if (!connection_changed_token_) - return; - - RemoveConnectionStatusHandler(ble_device_.Get(), *connection_changed_token_); + ClearEventRegistrations(); } uint32_t BluetoothDeviceWinrt::GetBluetoothClass() const { @@ -202,6 +205,10 @@ base::Optional<std::string> BluetoothDeviceWinrt::GetName() const { return local_name_; } + // Prefer returning |local_name_| over an empty string. + if (!name) + return local_name_; + return base::win::ScopedHString(name).GetAsUTF8(); } @@ -404,6 +411,14 @@ std::string BluetoothDeviceWinrt::CanonicalizeAddress(uint64_t address) { return bluetooth_address; } +void BluetoothDeviceWinrt::UpdateLocalName( + base::Optional<std::string> local_name) { + if (!local_name) + return; + + local_name_ = std::move(local_name); +} + void BluetoothDeviceWinrt::CreateGattConnectionImpl() { ComPtr<IBluetoothLEDeviceStatics> device_statics; HRESULT hr = GetBluetoothLEDeviceStaticsActivationFactory(&device_statics); @@ -482,15 +497,12 @@ void BluetoothDeviceWinrt::OnFromBluetoothAddress( return; } - if (connection_changed_token_) { - // As we are about to replace |ble_device_| with |ble_device| we will also - // unregister the existing event handler and add a new one to the new - // device. - RemoveConnectionStatusHandler(ble_device_.Get(), - *connection_changed_token_); - } + // As we are about to replace |ble_device_| with |ble_device| existing event + // handlers need to be unregistered. New ones will be added below. + ClearEventRegistrations(); ble_device_ = std::move(ble_device); + connection_changed_token_ = AddTypedEventHandler( ble_device_.Get(), &IBluetoothLEDevice::add_ConnectionStatusChanged, base::BindRepeating(&BluetoothDeviceWinrt::OnConnectionStatusChanged, @@ -503,11 +515,6 @@ void BluetoothDeviceWinrt::OnFromBluetoothAddress( if (IsGattConnected()) DidConnectGatt(); - if (gatt_services_changed_token_) { - RemoveGattServicesChangedHandler(ble_device_.Get(), - *gatt_services_changed_token_); - } - gatt_services_changed_token_ = AddTypedEventHandler( ble_device_.Get(), &IBluetoothLEDevice::add_GattServicesChanged, base::BindRepeating(&BluetoothDeviceWinrt::OnGattServicesChanged, @@ -520,6 +527,11 @@ void BluetoothDeviceWinrt::OnFromBluetoothAddress( gatt_discoverer_->StartGattDiscovery( base::BindOnce(&BluetoothDeviceWinrt::OnGattDiscoveryComplete, weak_ptr_factory_.GetWeakPtr())); + + name_changed_token_ = AddTypedEventHandler( + ble_device_.Get(), &IBluetoothLEDevice::add_NameChanged, + base::BindRepeating(&BluetoothDeviceWinrt::OnNameChanged, + weak_ptr_factory_.GetWeakPtr())); } void BluetoothDeviceWinrt::OnConnectionStatusChanged( @@ -554,6 +566,12 @@ void BluetoothDeviceWinrt::OnGattServicesChanged(IBluetoothLEDevice* ble_device, } } +void BluetoothDeviceWinrt::OnNameChanged(IBluetoothLEDevice* ble_device, + IInspectable* object) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + adapter_->NotifyDeviceChanged(this); +} + void BluetoothDeviceWinrt::OnGattDiscoveryComplete(bool success) { if (!success) { if (!IsGattConnected()) @@ -600,4 +618,19 @@ void BluetoothDeviceWinrt::ClearGattServices() { SetGattServicesDiscoveryComplete(false); } +void BluetoothDeviceWinrt::ClearEventRegistrations() { + if (connection_changed_token_) { + RemoveConnectionStatusHandler(ble_device_.Get(), + *connection_changed_token_); + } + + if (gatt_services_changed_token_) { + RemoveGattServicesChangedHandler(ble_device_.Get(), + *gatt_services_changed_token_); + } + + if (name_changed_token_) + RemoveNameChangedHandler(ble_device_.Get(), *name_changed_token_); +} + } // namespace device diff --git a/chromium/device/bluetooth/bluetooth_device_winrt.h b/chromium/device/bluetooth/bluetooth_device_winrt.h index 93644ab66cd..c7fe84aad23 100644 --- a/chromium/device/bluetooth/bluetooth_device_winrt.h +++ b/chromium/device/bluetooth/bluetooth_device_winrt.h @@ -37,9 +37,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceWinrt : public BluetoothDevice { static constexpr uint8_t k32BitServiceDataSection = 0x20; static constexpr uint8_t k128BitServiceDataSection = 0x21; - BluetoothDeviceWinrt(BluetoothAdapterWinrt* adapter, - uint64_t raw_address, - base::Optional<std::string> local_name); + BluetoothDeviceWinrt(BluetoothAdapterWinrt* adapter, uint64_t raw_address); ~BluetoothDeviceWinrt() override; // BluetoothDevice: @@ -91,6 +89,9 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceWinrt : public BluetoothDevice { // each 'X' is a hex digit. static std::string CanonicalizeAddress(uint64_t address); + // Called by BluetoothAdapterWinrt when an advertisement packet is received. + void UpdateLocalName(base::Optional<std::string> local_name); + protected: // BluetoothDevice: void CreateGattConnectionImpl() override; @@ -117,9 +118,14 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceWinrt : public BluetoothDevice { ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice* ble_device, IInspectable* object); + void OnNameChanged( + ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice* ble_device, + IInspectable* object); + void OnGattDiscoveryComplete(bool success); void ClearGattServices(); + void ClearEventRegistrations(); uint64_t raw_address_; std::string address_; @@ -131,6 +137,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceWinrt : public BluetoothDevice { base::Optional<EventRegistrationToken> connection_changed_token_; base::Optional<EventRegistrationToken> gatt_services_changed_token_; + base::Optional<EventRegistrationToken> name_changed_token_; THREAD_CHECKER(thread_checker_); diff --git a/chromium/device/bluetooth/bluetooth_low_energy_device_mac.h b/chromium/device/bluetooth/bluetooth_low_energy_device_mac.h index b6b6e59fe69..15b98aa4880 100644 --- a/chromium/device/bluetooth/bluetooth_low_energy_device_mac.h +++ b/chromium/device/bluetooth/bluetooth_low_energy_device_mac.h @@ -105,6 +105,8 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothLowEnergyDeviceMac // we switch to using bluetooth identifiers throughout Chrome. // http://crbug.com/507824 static std::string GetPeripheralHashAddress(CBPeripheral* peripheral); + static std::string GetPeripheralHashAddress( + base::StringPiece device_identifier); private: friend class BluetoothAdapterMac; @@ -125,6 +127,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothLowEnergyDeviceMac // Returns the Bluetooth adapter. BluetoothAdapterMac* GetMacAdapter(); + BluetoothAdapterMac* GetMacAdapter() const; // Returns the CoreBluetooth Peripheral. CBPeripheral* GetPeripheral(); diff --git a/chromium/device/bluetooth/bluetooth_low_energy_device_mac.mm b/chromium/device/bluetooth/bluetooth_low_energy_device_mac.mm index a65d9fe3932..8d9df355abc 100644 --- a/chromium/device/bluetooth/bluetooth_low_energy_device_mac.mm +++ b/chromium/device/bluetooth/bluetooth_low_energy_device_mac.mm @@ -103,7 +103,7 @@ base::Optional<std::string> BluetoothLowEnergyDeviceMac::GetName() const { } bool BluetoothLowEnergyDeviceMac::IsPaired() const { - return false; + return GetMacAdapter()->IsBluetoothLowEnergyDeviceSystemPaired(identifier_); } bool BluetoothLowEnergyDeviceMac::IsConnected() const { @@ -409,12 +409,17 @@ std::string BluetoothLowEnergyDeviceMac::GetPeripheralIdentifier( // static std::string BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress( CBPeripheral* peripheral) { + return GetPeripheralHashAddress(GetPeripheralIdentifier(peripheral)); +} + +// static +std::string BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress( + base::StringPiece device_identifier) { const size_t kCanonicalAddressNumberOfBytes = 6; char raw[kCanonicalAddressNumberOfBytes]; - crypto::SHA256HashString(GetPeripheralIdentifier(peripheral), raw, - sizeof(raw)); - std::string hash = base::HexEncode(raw, sizeof(raw)); - return BluetoothDevice::CanonicalizeAddress(hash); + crypto::SHA256HashString(device_identifier, raw, sizeof(raw)); + return BluetoothDevice::CanonicalizeAddress( + base::HexEncode(raw, sizeof(raw))); } void BluetoothLowEnergyDeviceMac::DidConnectPeripheral() { @@ -463,6 +468,10 @@ BluetoothAdapterMac* BluetoothLowEnergyDeviceMac::GetMacAdapter() { return static_cast<BluetoothAdapterMac*>(this->adapter_); } +BluetoothAdapterMac* BluetoothLowEnergyDeviceMac::GetMacAdapter() const { + return static_cast<BluetoothAdapterMac*>(this->adapter_); +} + CBPeripheral* BluetoothLowEnergyDeviceMac::GetPeripheral() { return peripheral_; } diff --git a/chromium/device/bluetooth/bluetooth_low_energy_device_watcher_mac.h b/chromium/device/bluetooth/bluetooth_low_energy_device_watcher_mac.h new file mode 100644 index 00000000000..51cd3f96070 --- /dev/null +++ b/chromium/device/bluetooth/bluetooth_low_energy_device_watcher_mac.h @@ -0,0 +1,94 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_BLUETOOTH_BLUETOOTH_LOW_ENERGY_DEVICE_WATCHER_MAC_H_ +#define DEVICE_BLUETOOTH_BLUETOOTH_LOW_ENERGY_DEVICE_WATCHER_MAC_H_ + +#include <map> +#include <memory> +#include <string> + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/files/file_path_watcher.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/sequence_checker.h" +#include "base/sequenced_task_runner.h" +#include "base/task/cancelable_task_tracker.h" +#include "base/task/post_task.h" +#include "device/bluetooth/bluetooth_export.h" + +@class NSDictionary; + +namespace device { + +// Manages watching and reading system bluetooth property list file in +// background thread to obtain a list of known Bluetooth low energy devices. +class DEVICE_BLUETOOTH_EXPORT BluetoothLowEnergyDeviceWatcherMac + : public base::RefCountedThreadSafe<BluetoothLowEnergyDeviceWatcherMac> { + public: + using LowEnergyDeviceListUpdatedCallback = + base::RepeatingCallback<void(std::map<std::string, std::string>)>; + + static scoped_refptr<BluetoothLowEnergyDeviceWatcherMac> + CreateAndStartWatching( + scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner, + LowEnergyDeviceListUpdatedCallback + update_low_energy_device_list_callback); + + BluetoothLowEnergyDeviceWatcherMac( + scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner, + LowEnergyDeviceListUpdatedCallback + update_low_energy_device_list_callback); + + protected: + virtual ~BluetoothLowEnergyDeviceWatcherMac(); + + // Read system bluetooth property list file for change and fetches + // identifier and device address of system paired bluetooth devices. + void OnPropertyListFileChangedOnFileThread(const base::FilePath& path, + bool error); + + // Overriden in tests. + virtual void Init(); + virtual void ReadBluetoothPropertyListFile(); + + std::map<std::string, std::string> ParseBluetoothDevicePropertyListData( + NSDictionary* data); + + LowEnergyDeviceListUpdatedCallback low_energy_device_list_updated_callback() { + return low_energy_device_list_updated_callback_; + } + + scoped_refptr<base::SequencedTaskRunner> ui_thread_task_runner() { + return ui_thread_task_runner_; + } + + private: + friend class BluetoothAdapterMac; + friend class base::RefCountedThreadSafe<BluetoothLowEnergyDeviceWatcherMac>; + + void AddBluetoothPropertyListFileWatcher(); + + static const base::FilePath& BluetoothPlistFilePath(); + + // Thread runner to watch, read, and parse bluetooth property list file. + scoped_refptr<base::SequencedTaskRunner> file_thread_task_runner_ = + base::CreateSequencedTaskRunnerWithTraits( + {base::MayBlock(), base::TaskPriority::BEST_EFFORT, + base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}); + scoped_refptr<base::SequencedTaskRunner> ui_thread_task_runner_; + LowEnergyDeviceListUpdatedCallback low_energy_device_list_updated_callback_; + std::unique_ptr<base::FilePathWatcher> property_list_watcher_ = + std::make_unique<base::FilePathWatcher>(); + + SEQUENCE_CHECKER(sequence_checker_); + + DISALLOW_COPY_AND_ASSIGN(BluetoothLowEnergyDeviceWatcherMac); +}; + +} // namespace device + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_LOW_ENERGY_DEVICE_WATCHER_MAC_H_ diff --git a/chromium/device/bluetooth/bluetooth_low_energy_device_watcher_mac.mm b/chromium/device/bluetooth/bluetooth_low_energy_device_watcher_mac.mm new file mode 100644 index 00000000000..955d36777f8 --- /dev/null +++ b/chromium/device/bluetooth/bluetooth_low_energy_device_watcher_mac.mm @@ -0,0 +1,153 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/bluetooth/bluetooth_low_energy_device_watcher_mac.h" + +#include <utility> + +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/strings/sys_string_conversions.h" +#include "base/task/task_traits.h" +#include "device/bluetooth/bluetooth_adapter_mac.h" + +namespace device { + +namespace { + +constexpr char kBluetoothPlistFilePath[] = + "/Library/Preferences/com.apple.Bluetooth.plist"; + +} // namespace + +// static +scoped_refptr<BluetoothLowEnergyDeviceWatcherMac> +BluetoothLowEnergyDeviceWatcherMac::CreateAndStartWatching( + scoped_refptr<base::SequencedTaskRunner> ui_thread_task_runner, + LowEnergyDeviceListUpdatedCallback + low_energy_device_list_updated_callback) { + auto watcher = base::MakeRefCounted<BluetoothLowEnergyDeviceWatcherMac>( + std::move(ui_thread_task_runner), + std::move(low_energy_device_list_updated_callback)); + watcher->Init(); + return watcher; +} + +BluetoothLowEnergyDeviceWatcherMac::BluetoothLowEnergyDeviceWatcherMac( + scoped_refptr<base::SequencedTaskRunner> ui_thread_task_runner, + LowEnergyDeviceListUpdatedCallback low_energy_device_list_updated_callback) + : ui_thread_task_runner_(std::move(ui_thread_task_runner)), + low_energy_device_list_updated_callback_( + std::move(low_energy_device_list_updated_callback)) { + DETACH_FROM_SEQUENCE(sequence_checker_); +} + +BluetoothLowEnergyDeviceWatcherMac::~BluetoothLowEnergyDeviceWatcherMac() { + file_thread_task_runner_->DeleteSoon(FROM_HERE, + property_list_watcher_.release()); +} + +void BluetoothLowEnergyDeviceWatcherMac::OnPropertyListFileChangedOnFileThread( + const base::FilePath& path, + bool error) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (error) { + LOG(WARNING) << "Failed to read com.apple.Bluetooth.plist."; + return; + } + + // Bluetooth property list file is expected to have the following format: + // + // "CoreBluetoothCache" => { + // "E7F8589A-A7D9-4B94-9A08-D89076A159F4" => { + // "DeviceAddress" => "11-11-11-11-11-11" + // "DeviceAddressType" => 1 + // "ServiceChangedHandle" => 3 + // "ServiceChangedSubscribed" => 0 + // "ServiceDiscoveryComplete" => 0 + // } + // "D3CAC59E-C501-4599-97DA-2DF491544EEE" => { + // "DeviceAddress" => "22-22-22-22-22-22" + // "DeviceAddressType" => 1 + // "ServiceChangedHandle" => 3 + // "ServiceChangedSubscribed" => 0 + // "ServiceDiscoveryComplete" => 0 + // } + // } + NSString* plist_file_path = base::SysUTF8ToNSString(path.value()); + NSDictionary* bluetooth_info_dictionary = + [NSDictionary dictionaryWithContentsOfFile:plist_file_path]; + + // |bluetooth_info_dictionary| is nil if there was an error reading the file + // or if the content of the read file cannot be represented by a dictionary. + if (!bluetooth_info_dictionary) + return; + + auto parsed_data = + ParseBluetoothDevicePropertyListData(bluetooth_info_dictionary); + ui_thread_task_runner_->PostTask( + FROM_HERE, base::BindOnce(low_energy_device_list_updated_callback_, + std::move(parsed_data))); +} + +void BluetoothLowEnergyDeviceWatcherMac::Init() { + file_thread_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&BluetoothLowEnergyDeviceWatcherMac:: + AddBluetoothPropertyListFileWatcher, + this)); +} + +void BluetoothLowEnergyDeviceWatcherMac::ReadBluetoothPropertyListFile() { + file_thread_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&BluetoothLowEnergyDeviceWatcherMac:: + OnPropertyListFileChangedOnFileThread, + this, BluetoothPlistFilePath(), false /* error */)); +} + +std::map<std::string, std::string> +BluetoothLowEnergyDeviceWatcherMac::ParseBluetoothDevicePropertyListData( + NSDictionary* data) { + std::map<std::string, std::string> updated_low_energy_devices_info; + NSDictionary* low_energy_devices_info = data[@"CoreBluetoothCache"]; + if (!low_energy_devices_info) + return updated_low_energy_devices_info; + + for (NSString* identifier in low_energy_devices_info) { + NSDictionary* device_info = low_energy_devices_info[identifier]; + if (!device_info) + continue; + + NSString* raw_device_address = device_info[@"DeviceAddress"]; + if (!raw_device_address) + continue; + + NSString* formatted_device_address = + [raw_device_address stringByReplacingOccurrencesOfString:@"-" + withString:@":"]; + updated_low_energy_devices_info[base::SysNSStringToUTF8(identifier)] = + base::SysNSStringToUTF8(formatted_device_address); + } + + return updated_low_energy_devices_info; +} + +// static +const base::FilePath& +BluetoothLowEnergyDeviceWatcherMac::BluetoothPlistFilePath() { + static const base::FilePath file_path(kBluetoothPlistFilePath); + return file_path; +} + +void BluetoothLowEnergyDeviceWatcherMac::AddBluetoothPropertyListFileWatcher() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + property_list_watcher_->Watch( + BluetoothPlistFilePath(), false /* recursive */, + base::BindRepeating(&BluetoothLowEnergyDeviceWatcherMac:: + OnPropertyListFileChangedOnFileThread, + this)); +} + +} // namespace device diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc index e6c286b2d5e..0f388cdabfa 100644 --- a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc +++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc @@ -25,6 +25,8 @@ #include "device/bluetooth/test/bluetooth_test_cast.h" #elif defined(OS_CHROMEOS) || defined(OS_LINUX) #include "device/bluetooth/test/bluetooth_test_bluez.h" +#elif defined(OS_FUCHSIA) +#include "device/bluetooth/test/bluetooth_test_fuchsia.h" #endif namespace device { diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc index 3c3e5d0726d..95f3d222208 100644 --- a/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc +++ b/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc @@ -18,6 +18,8 @@ #include "device/bluetooth/test/bluetooth_test_cast.h" #elif defined(OS_CHROMEOS) || defined(OS_LINUX) #include "device/bluetooth/test/bluetooth_test_bluez.h" +#elif defined(OS_FUCHSIA) +#include "device/bluetooth/test/bluetooth_test_fuchsia.h" #endif namespace device { diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc index 9574c4dd0cc..9969ed56bf8 100644 --- a/chromium/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc +++ b/chromium/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc @@ -19,6 +19,8 @@ #include "device/bluetooth/test/bluetooth_test_cast.h" #elif defined(OS_CHROMEOS) || defined(OS_LINUX) #include "device/bluetooth/test/bluetooth_test_bluez.h" +#elif defined(OS_FUCHSIA) +#include "device/bluetooth/test/bluetooth_test_fuchsia.h" #endif namespace device { diff --git a/chromium/device/bluetooth/bluez/bluetooth_adapter_bluez.cc b/chromium/device/bluetooth/bluez/bluetooth_adapter_bluez.cc index 81427385990..e570b92f0f4 100644 --- a/chromium/device/bluetooth/bluez/bluetooth_adapter_bluez.cc +++ b/chromium/device/bluetooth/bluez/bluetooth_adapter_bluez.cc @@ -690,6 +690,10 @@ void BluetoothAdapterBlueZ::DevicePropertyChanged( NotifyDeviceAdvertisementReceived(device_bluez, properties->rssi.value(), properties->eir.value()); + if (property_name == properties->connected.name()) + NotifyDeviceConnectedStateChanged(device_bluez, + properties->connected.value()); + if (property_name == properties->services_resolved.name() && properties->services_resolved.value()) { device_bluez->UpdateGattServices(object_path); @@ -1152,6 +1156,16 @@ void BluetoothAdapterBlueZ::NotifyDeviceAdvertisementReceived( observer.DeviceAdvertisementReceived(this, device, rssi, eir); } +void BluetoothAdapterBlueZ::NotifyDeviceConnectedStateChanged( + BluetoothDeviceBlueZ* device, + bool is_now_connected) { + DCHECK_EQ(device->adapter_, this); + DCHECK_EQ(device->IsConnected(), is_now_connected); + + for (auto& observer : observers_) + observer.DeviceConnectedStateChanged(this, device, is_now_connected); +} + void BluetoothAdapterBlueZ::UseProfile( const BluetoothUUID& uuid, const dbus::ObjectPath& device_path, diff --git a/chromium/device/bluetooth/bluez/bluetooth_adapter_bluez.h b/chromium/device/bluetooth/bluez/bluetooth_adapter_bluez.h index 7c1cc85f0e3..ef5205c3f8e 100644 --- a/chromium/device/bluetooth/bluez/bluetooth_adapter_bluez.h +++ b/chromium/device/bluetooth/bluez/bluetooth_adapter_bluez.h @@ -172,6 +172,10 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterBlueZ int16_t rssi, const std::vector<uint8_t>& eir); + // Announce to observers that |device| has changed its connected state. + void NotifyDeviceConnectedStateChanged(BluetoothDeviceBlueZ* device, + bool is_now_connected); + // Returns the object path of the adapter. const dbus::ObjectPath& object_path() const { return object_path_; } diff --git a/chromium/device/bluetooth/bluez/bluetooth_bluez_unittest.cc b/chromium/device/bluetooth/bluez/bluetooth_bluez_unittest.cc index 3e36926a2ac..f3cf5d3b299 100644 --- a/chromium/device/bluetooth/bluez/bluetooth_bluez_unittest.cc +++ b/chromium/device/bluetooth/bluez/bluetooth_bluez_unittest.cc @@ -11,6 +11,7 @@ #include "base/bind_helpers.h" #include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_current.h" #include "base/run_loop.h" #include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" @@ -224,7 +225,7 @@ class BluetoothBlueZTest : public testing::Test { // without using this function. void DiscoverDevice(const std::string& address) { ASSERT_TRUE(adapter_.get() != nullptr); - ASSERT_TRUE(base::MessageLoop::current() != nullptr); + ASSERT_TRUE(base::MessageLoopCurrent::IsSet()); fake_bluetooth_device_client_->SetSimulationIntervalMs(10); TestBluetoothAdapterObserver observer(adapter_); @@ -981,10 +982,11 @@ TEST_F(BluetoothBlueZTest, UnexpectedChangesDuringMultipleDiscoverySessions) { // bluez::FakeBluetoothAdapterClient's count should be only 1 and a single // call to // bluez::FakeBluetoothAdapterClient::StopDiscovery should work. - fake_bluetooth_adapter_client_->StopDiscovery( + fake_bluetooth_adapter_client_->BluetoothAdapterClient::StopDiscovery( dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath), - GetCallback(), base::Bind(&BluetoothBlueZTest::DBusErrorCallback, - base::Unretained(this))); + GetCallback(), + base::Bind(&BluetoothBlueZTest::DBusErrorCallback, + base::Unretained(this))); base::RunLoop().Run(); EXPECT_EQ(2, observer.discovering_changed_count()); EXPECT_EQ(4, callback_count_); @@ -1090,10 +1092,11 @@ TEST_F(BluetoothBlueZTest, UnexpectedChangesDuringMultipleDiscoverySessions) { // Stop discovery via D-Bus. The fake client's reference count will drop but // the discovery state won't change since our BluetoothAdapter also just // requested it via D-Bus. - fake_bluetooth_adapter_client_->StopDiscovery( + fake_bluetooth_adapter_client_->BluetoothAdapterClient::StopDiscovery( dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath), - GetCallback(), base::Bind(&BluetoothBlueZTest::DBusErrorCallback, - base::Unretained(this))); + GetCallback(), + base::Bind(&BluetoothBlueZTest::DBusErrorCallback, + base::Unretained(this))); base::RunLoop().Run(); EXPECT_EQ(5, observer.discovering_changed_count()); EXPECT_EQ(10, callback_count_); @@ -1168,10 +1171,11 @@ TEST_F(BluetoothBlueZTest, InvalidatedDiscoverySessions) { // should become inactive, but more importantly, we shouldn't run into any // memory errors as the sessions that we explicitly deleted should get // cleaned up. - fake_bluetooth_adapter_client_->StopDiscovery( + fake_bluetooth_adapter_client_->BluetoothAdapterClient::StopDiscovery( dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath), - GetCallback(), base::Bind(&BluetoothBlueZTest::DBusErrorCallback, - base::Unretained(this))); + GetCallback(), + base::Bind(&BluetoothBlueZTest::DBusErrorCallback, + base::Unretained(this))); base::RunLoop().Run(); EXPECT_EQ(2, observer.discovering_changed_count()); EXPECT_EQ(4, callback_count_); @@ -2434,6 +2438,36 @@ TEST_F(BluetoothBlueZTest, DeviceAdvertisementReceived) { EXPECT_EQ(1, observer.device_advertisement_received_count()); EXPECT_EQ(eir, observer.device_eir()); } + +TEST_F(BluetoothBlueZTest, DeviceConnectedStateChanged) { + GetAdapter(); + + fake_bluetooth_device_client_->CreateDevice( + dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath)); + BluetoothDevice* device = + adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress); + ASSERT_TRUE(device); + + // Install an observer; expect DeviceConnectedStateChanged method to be + // called. + TestBluetoothAdapterObserver observer(adapter_); + + bluez::FakeBluetoothDeviceClient::Properties* properties = + fake_bluetooth_device_client_->GetProperties( + dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath)); + + // The device starts out disconnected. + EXPECT_FALSE(device->IsConnected()); + + properties->connected.ReplaceValue(true); + EXPECT_EQ(1u, observer.device_connected_state_changed_values().size()); + EXPECT_TRUE(observer.device_connected_state_changed_values()[0]); + + properties->connected.ReplaceValue(false); + EXPECT_EQ(2u, observer.device_connected_state_changed_values().size()); + EXPECT_FALSE(observer.device_connected_state_changed_values()[1]); +} #endif TEST_F(BluetoothBlueZTest, DeviceUuidsChanged) { diff --git a/chromium/device/bluetooth/cast/OWNERS b/chromium/device/bluetooth/cast/OWNERS index 999dc46f8d1..e3a9c035733 100644 --- a/chromium/device/bluetooth/cast/OWNERS +++ b/chromium/device/bluetooth/cast/OWNERS @@ -1,2 +1 @@ -slan@chromium.org -bcf@chromium.org +halliwell@chromium.org diff --git a/chromium/device/bluetooth/dbus/bluetooth_adapter_client.cc b/chromium/device/bluetooth/dbus/bluetooth_adapter_client.cc index 2829a2d045d..d0b09b2fb53 100644 --- a/chromium/device/bluetooth/dbus/bluetooth_adapter_client.cc +++ b/chromium/device/bluetooth/dbus/bluetooth_adapter_client.cc @@ -5,6 +5,7 @@ #include "device/bluetooth/dbus/bluetooth_adapter_client.h" #include <string> +#include <utility> #include "base/bind.h" #include "base/callback.h" @@ -95,6 +96,31 @@ void WriteAttribute(dbus::MessageWriter* writer, writer->CloseContainer(&struct_writer); } +BluetoothAdapterClient::Error ErrorResponseToError( + dbus::ErrorResponse* response) { + BluetoothAdapterClient::Error error(BluetoothAdapterClient::kNoResponseError, + ""); + if (response) { + dbus::MessageReader reader(response); + error.name = response->GetErrorName(); + reader.PopString(&error.message); + } + + return error; +} + +void OnResponseAdapter( + const base::Closure& callback, + BluetoothAdapterClient::ErrorCallback error_callback, + const base::Optional<BluetoothAdapterClient::Error>& error) { + if (!error) { + callback.Run(); + return; + } + + std::move(error_callback).Run(error->name, error->message); +} + } // namespace BluetoothAdapterClient::DiscoveryFilter::DiscoveryFilter() = default; @@ -124,6 +150,10 @@ void BluetoothAdapterClient::DiscoveryFilter::CopyFrom( uuids.reset(); } +BluetoothAdapterClient::Error::Error(const std::string& name, + const std::string& message) + : name(name), message(message) {} + const char BluetoothAdapterClient::kNoResponseError[] = "org.chromium.Error.NoResponse"; const char BluetoothAdapterClient::kUnknownAdapterError[] = @@ -206,48 +236,40 @@ class BluetoothAdapterClientImpl : public BluetoothAdapterClient, // BluetoothAdapterClient override. void StartDiscovery(const dbus::ObjectPath& object_path, - const base::Closure& callback, - ErrorCallback error_callback) override { + ResponseCallback callback) override { dbus::MethodCall method_call(bluetooth_adapter::kBluetoothAdapterInterface, bluetooth_adapter::kStartDiscovery); dbus::ObjectProxy* object_proxy = object_manager_->GetObjectProxy(object_path); if (!object_proxy) { - std::move(error_callback).Run(kUnknownAdapterError, ""); + std::move(callback).Run(Error(kUnknownAdapterError, "")); return; } - object_proxy->CallMethodWithErrorCallback( + object_proxy->CallMethodWithErrorResponse( &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, - base::BindOnce(&BluetoothAdapterClientImpl::OnSuccess, - weak_ptr_factory_.GetWeakPtr(), callback), - base::BindOnce(&BluetoothAdapterClientImpl::OnError, - weak_ptr_factory_.GetWeakPtr(), - std::move(error_callback))); + base::BindOnce(&BluetoothAdapterClientImpl::OnResponse, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } // BluetoothAdapterClient override. void StopDiscovery(const dbus::ObjectPath& object_path, - const base::Closure& callback, - ErrorCallback error_callback) override { + ResponseCallback callback) override { dbus::MethodCall method_call(bluetooth_adapter::kBluetoothAdapterInterface, bluetooth_adapter::kStopDiscovery); dbus::ObjectProxy* object_proxy = object_manager_->GetObjectProxy(object_path); if (!object_proxy) { - std::move(error_callback).Run(kUnknownAdapterError, ""); + std::move(callback).Run(Error(kUnknownAdapterError, "")); return; } - object_proxy->CallMethodWithErrorCallback( + object_proxy->CallMethodWithErrorResponse( &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, - base::BindOnce(&BluetoothAdapterClientImpl::OnSuccess, - weak_ptr_factory_.GetWeakPtr(), callback), - base::BindOnce(&BluetoothAdapterClientImpl::OnError, - weak_ptr_factory_.GetWeakPtr(), - std::move(error_callback))); + base::BindOnce(&BluetoothAdapterClientImpl::OnResponse, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } // BluetoothAdapterClient override. @@ -541,6 +563,17 @@ class BluetoothAdapterClientImpl : public BluetoothAdapterClient, std::move(error_callback).Run(error_name, error_message); } + void OnResponse(ResponseCallback callback, + dbus::Response* response, + dbus::ErrorResponse* error_response) { + if (response) { + std::move(callback).Run(base::nullopt); + return; + } + + std::move(callback).Run(ErrorResponseToError(error_response)); + } + dbus::ObjectManager* object_manager_; // List of observers interested in event notifications from us. @@ -563,4 +596,18 @@ BluetoothAdapterClient* BluetoothAdapterClient::Create() { return new BluetoothAdapterClientImpl; } +void BluetoothAdapterClient::StartDiscovery(const dbus::ObjectPath& object_path, + const base::Closure& callback, + ErrorCallback error_callback) { + StartDiscovery(object_path, base::BindOnce(&OnResponseAdapter, callback, + std::move(error_callback))); +} + +void BluetoothAdapterClient::StopDiscovery(const dbus::ObjectPath& object_path, + const base::Closure& callback, + ErrorCallback error_callback) { + StopDiscovery(object_path, base::BindOnce(&OnResponseAdapter, callback, + std::move(error_callback))); +} + } // namespace bluez diff --git a/chromium/device/bluetooth/dbus/bluetooth_adapter_client.h b/chromium/device/bluetooth/dbus/bluetooth_adapter_client.h index 03ff66d74f9..2415c80f5ac 100644 --- a/chromium/device/bluetooth/dbus/bluetooth_adapter_client.h +++ b/chromium/device/bluetooth/dbus/bluetooth_adapter_client.h @@ -12,6 +12,7 @@ #include "base/callback_forward.h" #include "base/macros.h" +#include "base/optional.h" #include "dbus/object_path.h" #include "dbus/property.h" #include "device/bluetooth/bluetooth_export.h" @@ -47,6 +48,14 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterClient : public BluezDBusClient { DISALLOW_COPY_AND_ASSIGN(DiscoveryFilter); }; + // Represent an error sent through DBus. + struct Error { + Error(const std::string& name, const std::string& message); + + std::string name; + std::string message; + }; + // Structure of properties associated with bluetooth adapters. struct Properties : public dbus::PropertySet { // The Bluetooth device address of the adapter. Read-only. @@ -147,16 +156,27 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterClient : public BluezDBusClient { const std::string& error_message)> ErrorCallback; + // Callback used by adapter methods to indicate that a response was + // received with an optional Error in case an error occurred. + using ResponseCallback = + base::OnceCallback<void(const base::Optional<Error>&)>; + // Starts a device discovery on the adapter with object path |object_path|. virtual void StartDiscovery(const dbus::ObjectPath& object_path, - const base::Closure& callback, - ErrorCallback error_callback) = 0; + ResponseCallback callback) = 0; + // DEPRECATED: Use StartDiscovery() above. + void StartDiscovery(const dbus::ObjectPath& object_path, + const base::Closure& callback, + ErrorCallback error_callback); // Cancels any previous device discovery on the adapter with object path // |object_path|. virtual void StopDiscovery(const dbus::ObjectPath& object_path, - const base::Closure& callback, - ErrorCallback error_callback) = 0; + ResponseCallback callback) = 0; + // DEPRECATED: Use StopDiscovery() above. + void StopDiscovery(const dbus::ObjectPath& object_path, + const base::Closure& callback, + ErrorCallback error_callback); // Pauses all discovery sessions. virtual void PauseDiscovery(const dbus::ObjectPath& object_path, diff --git a/chromium/device/bluetooth/dbus/bluez_dbus_manager.cc b/chromium/device/bluetooth/dbus/bluez_dbus_manager.cc index 4ae5de84dcc..c5683311d77 100644 --- a/chromium/device/bluetooth/dbus/bluez_dbus_manager.cc +++ b/chromium/device/bluetooth/dbus/bluez_dbus_manager.cc @@ -10,7 +10,7 @@ #include "base/command_line.h" #include "base/feature_list.h" #include "base/memory/ptr_util.h" -#include "base/sys_info.h" +#include "base/system/sys_info.h" #include "base/threading/thread.h" #include "build/build_config.h" #include "dbus/bus.h" diff --git a/chromium/device/bluetooth/dbus/fake_bluetooth_adapter_client.cc b/chromium/device/bluetooth/dbus/fake_bluetooth_adapter_client.cc index 573019b5028..9865d675651 100644 --- a/chromium/device/bluetooth/dbus/fake_bluetooth_adapter_client.cc +++ b/chromium/device/bluetooth/dbus/fake_bluetooth_adapter_client.cc @@ -134,18 +134,17 @@ FakeBluetoothAdapterClient::GetProperties(const dbus::ObjectPath& object_path) { void FakeBluetoothAdapterClient::StartDiscovery( const dbus::ObjectPath& object_path, - const base::Closure& callback, - ErrorCallback error_callback) { + ResponseCallback callback) { if (object_path != dbus::ObjectPath(kAdapterPath)) { PostDelayedTask( - base::BindOnce(std::move(error_callback), kNoResponseError, "")); + base::BindOnce(std::move(callback), Error(kNoResponseError, ""))); return; } ++discovering_count_; VLOG(1) << "StartDiscovery: " << object_path.value() << ", " << "count is now " << discovering_count_; - PostDelayedTask(callback); + PostDelayedTask(base::BindOnce(std::move(callback), base::nullopt)); if (discovering_count_ == 1) { properties_->discovering.ReplaceValue(true); @@ -159,25 +158,24 @@ void FakeBluetoothAdapterClient::StartDiscovery( void FakeBluetoothAdapterClient::StopDiscovery( const dbus::ObjectPath& object_path, - const base::Closure& callback, - ErrorCallback error_callback) { + ResponseCallback callback) { if (object_path != dbus::ObjectPath(kAdapterPath)) { PostDelayedTask( - base::BindOnce(std::move(error_callback), kNoResponseError, "")); + base::BindOnce(std::move(callback), Error(kNoResponseError, ""))); return; } if (!discovering_count_) { LOG(WARNING) << "StopDiscovery called when not discovering"; PostDelayedTask( - base::BindOnce(std::move(error_callback), kNoResponseError, "")); + base::BindOnce(std::move(callback), Error(kNoResponseError, ""))); return; } --discovering_count_; VLOG(1) << "StopDiscovery: " << object_path.value() << ", " << "count is now " << discovering_count_; - PostDelayedTask(callback); + PostDelayedTask(base::BindOnce(std::move(callback), base::nullopt)); if (discovering_count_ == 0) { FakeBluetoothDeviceClient* device_client = diff --git a/chromium/device/bluetooth/dbus/fake_bluetooth_adapter_client.h b/chromium/device/bluetooth/dbus/fake_bluetooth_adapter_client.h index 59adc334a6a..c971c7a9362 100644 --- a/chromium/device/bluetooth/dbus/fake_bluetooth_adapter_client.h +++ b/chromium/device/bluetooth/dbus/fake_bluetooth_adapter_client.h @@ -5,6 +5,7 @@ #ifndef DEVICE_BLUETOOTH_DBUS_FAKE_BLUETOOTH_ADAPTER_CLIENT_H_ #define DEVICE_BLUETOOTH_DBUS_FAKE_BLUETOOTH_ADAPTER_CLIENT_H_ +#include <map> #include <memory> #include <string> #include <vector> @@ -49,11 +50,9 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothAdapterClient std::vector<dbus::ObjectPath> GetAdapters() override; Properties* GetProperties(const dbus::ObjectPath& object_path) override; void StartDiscovery(const dbus::ObjectPath& object_path, - const base::Closure& callback, - ErrorCallback error_callback) override; + ResponseCallback callback) override; void StopDiscovery(const dbus::ObjectPath& object_path, - const base::Closure& callback, - ErrorCallback error_callback) override; + ResponseCallback callback) override; void PauseDiscovery(const dbus::ObjectPath& object_path, const base::Closure& callback, ErrorCallback error_callback) override; diff --git a/chromium/device/bluetooth/strings/BUILD.gn b/chromium/device/bluetooth/strings/BUILD.gn index 6eaeb0ce90b..7102935a46b 100644 --- a/chromium/device/bluetooth/strings/BUILD.gn +++ b/chromium/device/bluetooth/strings/BUILD.gn @@ -3,6 +3,7 @@ # found in the LICENSE file. import("//tools/grit/grit_rule.gni") +import("//tools/grit/repack.gni") grit("strings") { source = "../bluetooth_strings.grd" @@ -64,3 +65,13 @@ grit("strings") { "bluetooth_strings_zh-TW.pak", ] } + +repack("bluetooth_test_strings") { + sources = [ + "$root_gen_dir/device/bluetooth/strings/bluetooth_strings_en-US.pak", + ] + output = "$root_out_dir/bluetooth_test_strings.pak" + deps = [ + ":strings", + ] +} diff --git a/chromium/device/fido/BUILD.gn b/chromium/device/fido/BUILD.gn index b1cbe9983ff..8572852222f 100644 --- a/chromium/device/fido/BUILD.gn +++ b/chromium/device/fido/BUILD.gn @@ -64,6 +64,8 @@ component("fido") { "device_response_converter.h", "ec_public_key.cc", "ec_public_key.h", + "features.cc", + "features.h", "fido_authenticator.h", "fido_constants.cc", "fido_constants.h", @@ -75,6 +77,8 @@ component("fido") { "fido_device_discovery.h", "fido_discovery_base.cc", "fido_discovery_base.h", + "fido_discovery_factory.cc", + "fido_discovery_factory.h", "fido_parsing_utils.cc", "fido_parsing_utils.h", "fido_request_handler.h", @@ -138,6 +142,7 @@ component("fido") { "//services/service_manager/public/cpp", "//services/service_manager/public/mojom", "//third_party/boringssl", + "//third_party/microsoft_webauthn", "//ui/base", ] @@ -193,6 +198,19 @@ component("fido") { "Security.framework", ] } + + if (is_win) { + sources += [ + "win/authenticator.cc", + "win/authenticator.h", + "win/discovery.cc", + "win/discovery.h", + "win/type_conversions.cc", + "win/type_conversions.h", + "win/webauthn_api.cc", + "win/webauthn_api.h", + ] + } } source_set("mocks") { @@ -280,6 +298,8 @@ source_set("test_support") { "//device/fido", "//mojo/public/cpp/bindings", "//services/device/public/mojom", + "//services/service_manager/public/cpp", + "//services/service_manager/public/mojom", "//testing/gmock", "//testing/gtest", ] @@ -302,4 +322,11 @@ source_set("test_support") { "mac/scoped_touch_id_test_environment.mm", ] } + + if (is_win) { + sources += [ + "win/fake_webauthn_api.cc", + "win/fake_webauthn_api.h", + ] + } } diff --git a/chromium/device/fido/DEPS b/chromium/device/fido/DEPS index 3d3a449eff9..18edc13fd28 100644 --- a/chromium/device/fido/DEPS +++ b/chromium/device/fido/DEPS @@ -6,4 +6,5 @@ include_rules = [ "+net/cert", "+ui/base/l10n", "+third_party/boringssl/src/include", + "+third_party/microsoft_webauthn", ] diff --git a/chromium/device/fido/OWNERS b/chromium/device/fido/OWNERS index 48920ce5d4d..6e1ff3c9d85 100644 --- a/chromium/device/fido/OWNERS +++ b/chromium/device/fido/OWNERS @@ -1,8 +1,23 @@ -reillyg@chromium.org -jdoerrie@chromium.org -engedy@chromium.org +# Webauthn OWNERS + +# General, esp chrome/ UI and Android integration. kpaulhamus@chromium.org + +# General, esp U2F and CTAP2 code. hongjunchoi@chromium.org -# TEAM: security-dev@chromium.org +# TouchID, Windows Hello. +martinkr@chromium.org + +# Bluetooth. +jdoerrie@chromium.org + +# Attestation and legacy U2F implementation. +agl@chromium.org + +# Emeritus; for occasional reviews, esp those in the depths of view & frame +# lifetimes. +engedy@chromium.org + +# TEAM: identity-dev@chromium.org # COMPONENT: Blink>WebAuthentication diff --git a/chromium/device/fido/attestation_object.cc b/chromium/device/fido/attestation_object.cc index 217ee4d6c33..552223aa14f 100644 --- a/chromium/device/fido/attestation_object.cc +++ b/chromium/device/fido/attestation_object.cc @@ -6,8 +6,8 @@ #include <utility> -#include "components/cbor/cbor_values.h" -#include "components/cbor/cbor_writer.h" +#include "components/cbor/values.h" +#include "components/cbor/writer.h" #include "device/fido/attestation_statement.h" #include "device/fido/fido_constants.h" @@ -29,17 +29,22 @@ std::vector<uint8_t> AttestationObject::GetCredentialId() const { return authenticator_data_.GetCredentialId(); } -void AttestationObject::EraseAttestationStatement() { +void AttestationObject::EraseAttestationStatement( + AttestationObject::AAGUID erase_aaguid) { attestation_statement_ = std::make_unique<NoneAttestationStatement>(); - authenticator_data_.DeleteDeviceAaguid(); + if (erase_aaguid == AAGUID::kErase) { + authenticator_data_.DeleteDeviceAaguid(); + } // Attested credential data is optional section within authenticator data. But // if present, the first 16 bytes of it represents a device AAGUID which must -// be set to zeros for none attestation statement format. +// be set to zeros for none attestation statement format, unless explicitly +// requested otherwise (we make an exception for platform authenticators). #if DCHECK_IS_ON() if (!authenticator_data_.attested_data()) return; - DCHECK(authenticator_data_.attested_data()->IsAaguidZero()); + DCHECK(erase_aaguid == AAGUID::kInclude || + authenticator_data_.attested_data()->IsAaguidZero()); #endif } @@ -59,24 +64,24 @@ bool AttestationObject::IsAttestationCertificateInappropriatelyIdentifying() { } std::vector<uint8_t> AttestationObject::SerializeToCBOREncodedBytes() const { - cbor::CBORValue::MapValue map; - map[cbor::CBORValue(kFormatKey)] = - cbor::CBORValue(attestation_statement_->format_name()); - map[cbor::CBORValue(kAuthDataKey)] = - cbor::CBORValue(authenticator_data_.SerializeToByteArray()); - map[cbor::CBORValue(kAttestationStatementKey)] = - cbor::CBORValue(attestation_statement_->GetAsCBORMap()); - return cbor::CBORWriter::Write(cbor::CBORValue(std::move(map))) + cbor::Value::MapValue map; + map[cbor::Value(kFormatKey)] = + cbor::Value(attestation_statement_->format_name()); + map[cbor::Value(kAuthDataKey)] = + cbor::Value(authenticator_data_.SerializeToByteArray()); + map[cbor::Value(kAttestationStatementKey)] = + cbor::Value(attestation_statement_->GetAsCBORMap()); + return cbor::Writer::Write(cbor::Value(std::move(map))) .value_or(std::vector<uint8_t>()); } std::vector<uint8_t> SerializeToCtapStyleCborEncodedBytes( const AttestationObject& object) { - cbor::CBORValue::MapValue map; + cbor::Value::MapValue map; map.emplace(1, object.attestation_statement().format_name()); map.emplace(2, object.authenticator_data().SerializeToByteArray()); map.emplace(3, object.attestation_statement().GetAsCBORMap()); - auto encoded_bytes = cbor::CBORWriter::Write(cbor::CBORValue(std::move(map))); + auto encoded_bytes = cbor::Writer::Write(cbor::Value(std::move(map))); DCHECK(encoded_bytes); return std::move(*encoded_bytes); } diff --git a/chromium/device/fido/attestation_object.h b/chromium/device/fido/attestation_object.h index 36f463d8493..2eba61f5fce 100644 --- a/chromium/device/fido/attestation_object.h +++ b/chromium/device/fido/attestation_object.h @@ -36,10 +36,16 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AttestationObject { std::vector<uint8_t> GetCredentialId() const; - // Replaces the attestation statement with a “none” attestation and replaces - // device AAGUID with zero bytes as specified for step 20.3 in + enum class AAGUID { + kErase, + kInclude, + }; + + // Replaces the attestation statement with a “none” attestation, and replaces + // device AAGUID with zero bytes (unless |erase_aaguid| is kInclude) as + // specified for step 20.3 in // https://w3c.github.io/webauthn/#createCredential. - void EraseAttestationStatement(); + void EraseAttestationStatement(AAGUID erase_aaguid); // Returns true if the attestation is a "self" attestation, i.e. is just the // private key signing itself to show that it is fresh. See diff --git a/chromium/device/fido/attestation_statement.cc b/chromium/device/fido/attestation_statement.cc index 04c96b5300c..44bc680e62c 100644 --- a/chromium/device/fido/attestation_statement.cc +++ b/chromium/device/fido/attestation_statement.cc @@ -35,8 +35,8 @@ NoneAttestationStatement::GetLeafCertificate() const { return base::nullopt; } -cbor::CBORValue::MapValue NoneAttestationStatement::GetAsCBORMap() const { - return cbor::CBORValue::MapValue(); +cbor::Value::MapValue NoneAttestationStatement::GetAsCBORMap() const { + return cbor::Value::MapValue(); } } // namespace device diff --git a/chromium/device/fido/attestation_statement.h b/chromium/device/fido/attestation_statement.h index cf5421317c5..2119f430ccf 100644 --- a/chromium/device/fido/attestation_statement.h +++ b/chromium/device/fido/attestation_statement.h @@ -11,7 +11,7 @@ #include "base/containers/span.h" #include "base/macros.h" #include "base/optional.h" -#include "components/cbor/cbor_values.h" +#include "components/cbor/values.h" namespace device { @@ -31,7 +31,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AttestationStatement { // https://www.w3.org/TR/2017/WD-webauthn-20170505/#defined-attestation-formats // This is not a CBOR-encoded byte array, but the map that will be // nested within another CBOR object and encoded then. - virtual cbor::CBORValue::MapValue GetAsCBORMap() const = 0; + virtual cbor::Value::MapValue GetAsCBORMap() const = 0; // Returns true if the attestation is a "self" attestation, i.e. is just the // private key signing itself to show that it is fresh. @@ -68,7 +68,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) NoneAttestationStatement bool IsSelfAttestation() override; bool IsAttestationCertificateInappropriatelyIdentifying() override; - cbor::CBORValue::MapValue GetAsCBORMap() const override; + cbor::Value::MapValue GetAsCBORMap() const override; base::Optional<base::span<const uint8_t>> GetLeafCertificate() const override; private: diff --git a/chromium/device/fido/attestation_statement_formats.cc b/chromium/device/fido/attestation_statement_formats.cc index ae9310300e8..dfa057b6286 100644 --- a/chromium/device/fido/attestation_statement_formats.cc +++ b/chromium/device/fido/attestation_statement_formats.cc @@ -119,18 +119,18 @@ FidoAttestationStatement::FidoAttestationStatement( FidoAttestationStatement::~FidoAttestationStatement() = default; -cbor::CBORValue::MapValue FidoAttestationStatement::GetAsCBORMap() const { - cbor::CBORValue::MapValue attestation_statement_map; - attestation_statement_map[cbor::CBORValue(kSignatureKey)] = - cbor::CBORValue(signature_); +cbor::Value::MapValue FidoAttestationStatement::GetAsCBORMap() const { + cbor::Value::MapValue attestation_statement_map; + attestation_statement_map[cbor::Value(kSignatureKey)] = + cbor::Value(signature_); - std::vector<cbor::CBORValue> certificate_array; + std::vector<cbor::Value> certificate_array; for (const auto& cert : x509_certificates_) { - certificate_array.push_back(cbor::CBORValue(cert)); + certificate_array.push_back(cbor::Value(cert)); } - attestation_statement_map[cbor::CBORValue(kX509CertKey)] = - cbor::CBORValue(std::move(certificate_array)); + attestation_statement_map[cbor::Value(kX509CertKey)] = + cbor::Value(std::move(certificate_array)); return attestation_statement_map; } @@ -174,22 +174,22 @@ PackedAttestationStatement::PackedAttestationStatement( PackedAttestationStatement::~PackedAttestationStatement() = default; -cbor::CBORValue::MapValue PackedAttestationStatement::GetAsCBORMap() const { - cbor::CBORValue::MapValue attestation_statement_map; +cbor::Value::MapValue PackedAttestationStatement::GetAsCBORMap() const { + cbor::Value::MapValue attestation_statement_map; // alg - attestation_statement_map[cbor::CBORValue(kAlgorithmKey)] = - cbor::CBORValue(static_cast<int>(algorithm_)); + attestation_statement_map[cbor::Value(kAlgorithmKey)] = + cbor::Value(static_cast<int>(algorithm_)); // sig - attestation_statement_map[cbor::CBORValue(kSignatureKey)] = - cbor::CBORValue(signature_); + attestation_statement_map[cbor::Value(kSignatureKey)] = + cbor::Value(signature_); // x5c (optional) if (!x509_certificates_.empty()) { - std::vector<cbor::CBORValue> certificate_array; + std::vector<cbor::Value> certificate_array; for (const auto& cert : x509_certificates_) { - certificate_array.push_back(cbor::CBORValue(cert)); + certificate_array.push_back(cbor::Value(cert)); } - attestation_statement_map[cbor::CBORValue(kX509CertKey)] = - cbor::CBORValue(std::move(certificate_array)); + attestation_statement_map[cbor::Value(kX509CertKey)] = + cbor::Value(std::move(certificate_array)); } return attestation_statement_map; } diff --git a/chromium/device/fido/attestation_statement_formats.h b/chromium/device/fido/attestation_statement_formats.h index 2db98ddd686..3bb9137936e 100644 --- a/chromium/device/fido/attestation_statement_formats.h +++ b/chromium/device/fido/attestation_statement_formats.h @@ -12,7 +12,7 @@ #include "base/component_export.h" #include "base/containers/span.h" #include "base/macros.h" -#include "components/cbor/cbor_values.h" +#include "components/cbor/values.h" #include "device/fido/attestation_statement.h" #include "device/fido/fido_constants.h" @@ -30,7 +30,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoAttestationStatement ~FidoAttestationStatement() override; // AttestationStatement - cbor::CBORValue::MapValue GetAsCBORMap() const override; + cbor::Value::MapValue GetAsCBORMap() const override; bool IsSelfAttestation() override; bool IsAttestationCertificateInappropriatelyIdentifying() override; base::Optional<base::span<const uint8_t>> GetLeafCertificate() const override; @@ -57,7 +57,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) PackedAttestationStatement ~PackedAttestationStatement() override; // AttestationStatement - cbor::CBORValue::MapValue GetAsCBORMap() const override; + cbor::Value::MapValue GetAsCBORMap() const override; bool IsSelfAttestation() override; bool IsAttestationCertificateInappropriatelyIdentifying() override; base::Optional<base::span<const uint8_t>> GetLeafCertificate() const override; diff --git a/chromium/device/fido/attestation_statement_formats_unittest.cc b/chromium/device/fido/attestation_statement_formats_unittest.cc index 4bed60bd605..8db6090672e 100644 --- a/chromium/device/fido/attestation_statement_formats_unittest.cc +++ b/chromium/device/fido/attestation_statement_formats_unittest.cc @@ -4,8 +4,8 @@ #include <vector> -#include "components/cbor/cbor_reader.h" -#include "components/cbor/cbor_writer.h" +#include "components/cbor/reader.h" +#include "components/cbor/writer.h" #include "device/fido/attestation_statement_formats.h" #include "device/fido/fido_constants.h" #include "device/fido/fido_parsing_utils.h" @@ -83,17 +83,17 @@ constexpr uint8_t kCertificates[] = { TEST(PackedAttestationStatementTest, CBOR) { EXPECT_THAT( - *cbor::CBORWriter::Write( - cbor::CBORValue(PackedAttestationStatement( - CoseAlgorithmIdentifier::kCoseEs256, - fido_parsing_utils::Materialize(kSignature), - {fido_parsing_utils::Materialize(kCertificates)}) - .GetAsCBORMap())), + *cbor::Writer::Write( + cbor::Value(PackedAttestationStatement( + CoseAlgorithmIdentifier::kCoseEs256, + fido_parsing_utils::Materialize(kSignature), + {fido_parsing_utils::Materialize(kCertificates)}) + .GetAsCBORMap())), testing::ElementsAreArray(test_data::kPackedAttestationStatementCBOR)); } TEST(PackedAttestationStatementTest, CBOR_NoCerts) { - EXPECT_THAT(*cbor::CBORWriter::Write(cbor::CBORValue( + EXPECT_THAT(*cbor::Writer::Write(cbor::Value( PackedAttestationStatement( CoseAlgorithmIdentifier::kCoseEs256, fido_parsing_utils::Materialize(kSignature), {}) @@ -104,7 +104,7 @@ TEST(PackedAttestationStatementTest, CBOR_NoCerts) { TEST(OpaqueAttestationStatementTest, GetLeafCertificate) { auto attestation_map = - cbor::CBORReader::Read(test_data::kPackedAttestationStatementCBOR); + cbor::Reader::Read(test_data::kPackedAttestationStatementCBOR); ASSERT_TRUE(attestation_map); OpaqueAttestationStatement statement("packed", std::move(*attestation_map)); EXPECT_FALSE(statement.IsSelfAttestation()); diff --git a/chromium/device/fido/attested_credential_data.cc b/chromium/device/fido/attested_credential_data.cc index 5b93d8cd8d9..7f59a16bb7d 100644 --- a/chromium/device/fido/attested_credential_data.cc +++ b/chromium/device/fido/attested_credential_data.cc @@ -8,6 +8,7 @@ #include <utility> #include "base/numerics/safe_math.h" +#include "components/cbor/reader.h" #include "device/fido/fido_constants.h" #include "device/fido/fido_parsing_utils.h" #include "device/fido/opaque_public_key.h" @@ -16,8 +17,8 @@ namespace device { // static -base::Optional<AttestedCredentialData> -AttestedCredentialData::DecodeFromCtapResponse( +base::Optional<std::pair<AttestedCredentialData, base::span<const uint8_t>>> +AttestedCredentialData::ConsumeFromCtapResponse( base::span<const uint8_t> buffer) { if (buffer.size() < kAaguidLength) return base::nullopt; @@ -40,11 +41,22 @@ AttestedCredentialData::DecodeFromCtapResponse( auto credential_id = buffer.first(credential_id_length); buffer = buffer.subspan(credential_id_length); - auto credential_public_key_data = std::make_unique<OpaquePublicKey>(buffer); - - return AttestedCredentialData(aaguid, credential_id_length_span, - fido_parsing_utils::Materialize(credential_id), - std::move(credential_public_key_data)); + // The public key is a CBOR map and is thus variable length. Therefore the + // CBOR parser needs to be invoked to find its length, even though the result + // is discarded. + size_t bytes_read; + if (!cbor::Reader::Read(buffer, &bytes_read)) { + return base::nullopt; + } + auto credential_public_key_data = + std::make_unique<OpaquePublicKey>(buffer.first(bytes_read)); + buffer = buffer.subspan(bytes_read); + + return std::make_pair( + AttestedCredentialData(aaguid, credential_id_length_span, + fido_parsing_utils::Materialize(credential_id), + std::move(credential_public_key_data)), + buffer); } // static diff --git a/chromium/device/fido/attested_credential_data.h b/chromium/device/fido/attested_credential_data.h index 73ed9f5e00a..61cf0fd956b 100644 --- a/chromium/device/fido/attested_credential_data.h +++ b/chromium/device/fido/attested_credential_data.h @@ -23,8 +23,12 @@ class PublicKey; // https://www.w3.org/TR/2017/WD-webauthn-20170505/#sec-attestation-data class COMPONENT_EXPORT(DEVICE_FIDO) AttestedCredentialData { public: - static base::Optional<AttestedCredentialData> DecodeFromCtapResponse( - base::span<const uint8_t> buffer); + // Parses an |AttestedCredentialData| from a prefix of |*buffer|. Returns + // nullopt on error, or else the parse return and a (possibly empty) suffix of + // |buffer| that was not parsed. + static base::Optional< + std::pair<AttestedCredentialData, base::span<const uint8_t>>> + ConsumeFromCtapResponse(base::span<const uint8_t> buffer); static base::Optional<AttestedCredentialData> CreateFromU2fRegisterResponse( base::span<const uint8_t> u2f_data, diff --git a/chromium/device/fido/authenticator_data.cc b/chromium/device/fido/authenticator_data.cc index 32a105f5af2..104967a476d 100644 --- a/chromium/device/fido/authenticator_data.cc +++ b/chromium/device/fido/authenticator_data.cc @@ -6,6 +6,8 @@ #include <utility> +#include "components/cbor/reader.h" +#include "components/cbor/writer.h" #include "device/fido/attested_credential_data.h" #include "device/fido/fido_parsing_utils.h" @@ -27,24 +29,51 @@ base::Optional<AuthenticatorData> AuthenticatorData::DecodeAuthenticatorData( uint8_t flag_byte = auth_data[kRpIdHashLength]; auto counter = auth_data.subspan<kRpIdHashLength + kFlagsLength, kSignCounterLength>(); - auto attested_credential_data = - AttestedCredentialData::DecodeFromCtapResponse( - auth_data.subspan(kAttestedCredentialDataOffset)); + + auth_data = auth_data.subspan(kAttestedCredentialDataOffset); + base::Optional<AttestedCredentialData> attested_credential_data; + if (flag_byte & static_cast<uint8_t>(Flag::kAttestation)) { + auto maybe_result = + AttestedCredentialData::ConsumeFromCtapResponse(auth_data); + if (!maybe_result) { + return base::nullopt; + } + std::tie(attested_credential_data, auth_data) = std::move(*maybe_result); + } + + base::Optional<cbor::Value> extensions; + if (flag_byte & static_cast<uint8_t>(Flag::kExtensionDataIncluded)) { + extensions = cbor::Reader::Read(auth_data); + if (!extensions || !extensions->is_map()) { + return base::nullopt; + } + } else if (!auth_data.empty()) { + return base::nullopt; + } return AuthenticatorData(application_parameter, flag_byte, counter, - std::move(attested_credential_data)); + std::move(attested_credential_data), + std::move(extensions)); } AuthenticatorData::AuthenticatorData( base::span<const uint8_t, kRpIdHashLength> application_parameter, uint8_t flags, base::span<const uint8_t, kSignCounterLength> counter, - base::Optional<AttestedCredentialData> data) + base::Optional<AttestedCredentialData> data, + base::Optional<cbor::Value> extensions) : application_parameter_( fido_parsing_utils::Materialize(application_parameter)), flags_(flags), counter_(fido_parsing_utils::Materialize(counter)), - attested_data_(std::move(data)) {} + attested_data_(std::move(data)), + extensions_(std::move(extensions)) { + DCHECK(!extensions_ || extensions_->is_map()); + DCHECK_EQ((flags_ & static_cast<uint8_t>(Flag::kExtensionDataIncluded)) != 0, + !!extensions_); + DCHECK_EQ(((flags_ & static_cast<uint8_t>(Flag::kAttestation)) != 0), + !!attested_data_); +} AuthenticatorData::AuthenticatorData(AuthenticatorData&& other) = default; AuthenticatorData& AuthenticatorData::operator=(AuthenticatorData&& other) = @@ -64,12 +93,21 @@ std::vector<uint8_t> AuthenticatorData::SerializeToByteArray() const { fido_parsing_utils::Append(&authenticator_data, application_parameter_); authenticator_data.insert(authenticator_data.end(), flags_); fido_parsing_utils::Append(&authenticator_data, counter_); + if (attested_data_) { // Attestations are returned in registration responses but not in assertion // responses. fido_parsing_utils::Append(&authenticator_data, attested_data_->SerializeAsBytes()); } + + if (extensions_) { + const auto maybe_extensions = cbor::Writer::Write(*extensions_); + if (maybe_extensions) { + fido_parsing_utils::Append(&authenticator_data, *maybe_extensions); + } + } + return authenticator_data; } diff --git a/chromium/device/fido/authenticator_data.h b/chromium/device/fido/authenticator_data.h index e4e9e565833..3f4f440ab5a 100644 --- a/chromium/device/fido/authenticator_data.h +++ b/chromium/device/fido/authenticator_data.h @@ -16,6 +16,7 @@ #include "base/macros.h" #include "base/numerics/safe_conversions.h" #include "base/optional.h" +#include "components/cbor/values.h" #include "device/fido/attested_credential_data.h" #include "device/fido/fido_constants.h" @@ -34,11 +35,15 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorData { static base::Optional<AuthenticatorData> DecodeAuthenticatorData( base::span<const uint8_t> auth_data); + // The attested credential |data| must be specified iff |flags| have + // kAttestation set; and |extensions| must be specified iff |flags| have + // kExtensionDataIncluded set. AuthenticatorData( base::span<const uint8_t, kRpIdHashLength> application_parameter, uint8_t flags, base::span<const uint8_t, kSignCounterLength> counter, - base::Optional<AttestedCredentialData> data); + base::Optional<AttestedCredentialData> data, + base::Optional<cbor::Value> extensions = base::nullopt); // Moveable. AuthenticatorData(AuthenticatorData&& other); @@ -65,6 +70,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorData { return attested_data_; } + // If a value is returned then the result of calling |is_map()| on it can be + // assumed to be true. + const base::Optional<cbor::Value>& extensions() const { return extensions_; } + const std::array<uint8_t, kRpIdHashLength>& application_parameter() const { return application_parameter_; } @@ -105,6 +114,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorData { // Signature counter, 32-bit unsigned big-endian integer. std::array<uint8_t, kSignCounterLength> counter_; base::Optional<AttestedCredentialData> attested_data_; + // If |extensions_| has a value, then it will be a CBOR map. + base::Optional<cbor::Value> extensions_; DISALLOW_COPY_AND_ASSIGN(AuthenticatorData); }; diff --git a/chromium/device/fido/authenticator_get_assertion_response.cc b/chromium/device/fido/authenticator_get_assertion_response.cc index a02868d4413..44d304bc796 100644 --- a/chromium/device/fido/authenticator_get_assertion_response.cc +++ b/chromium/device/fido/authenticator_get_assertion_response.cc @@ -7,8 +7,8 @@ #include <utility> #include "base/optional.h" -#include "components/cbor/cbor_values.h" -#include "components/cbor/cbor_writer.h" +#include "components/cbor/values.h" +#include "components/cbor/writer.h" #include "device/fido/authenticator_data.h" #include "device/fido/fido_parsing_utils.h" @@ -36,9 +36,15 @@ AuthenticatorGetAssertionResponse::CreateFromU2fSignResponse( if (key_handle.empty()) return base::nullopt; - auto flags = u2f_data.subspan<kFlagIndex, kFlagLength>(); + auto flags = u2f_data.subspan<kFlagIndex, kFlagLength>()[0]; + if (flags & + (static_cast<uint8_t>(AuthenticatorData::Flag::kExtensionDataIncluded) | + static_cast<uint8_t>(AuthenticatorData::Flag::kAttestation))) { + // U2F responses cannot assert CTAP2 features. + return base::nullopt; + } auto counter = u2f_data.subspan<kCounterIndex, kCounterLength>(); - AuthenticatorData authenticator_data(relying_party_id_hash, flags[0], counter, + AuthenticatorData authenticator_data(relying_party_id_hash, flags, counter, base::nullopt); auto signature = @@ -93,7 +99,7 @@ AuthenticatorGetAssertionResponse::SetNumCredentials(uint8_t num_credentials) { std::vector<uint8_t> GetSerializedCtapDeviceResponse( const AuthenticatorGetAssertionResponse& response) { - cbor::CBORValue::MapValue response_map; + cbor::Value::MapValue response_map; if (response.credential()) response_map.emplace(1, response.credential()->ConvertToCBOR()); @@ -106,7 +112,7 @@ std::vector<uint8_t> GetSerializedCtapDeviceResponse( // Multiple account selection is not supported. response_map.emplace(5, 1); auto encoded_response = - cbor::CBORWriter::Write(cbor::CBORValue(std::move(response_map))); + cbor::Writer::Write(cbor::Value(std::move(response_map))); DCHECK(encoded_response); return *encoded_response; } diff --git a/chromium/device/fido/authenticator_get_info_response.cc b/chromium/device/fido/authenticator_get_info_response.cc index 37de6493c09..d5f63d4f7c0 100644 --- a/chromium/device/fido/authenticator_get_info_response.cc +++ b/chromium/device/fido/authenticator_get_info_response.cc @@ -6,8 +6,8 @@ #include <utility> -#include "components/cbor/cbor_values.h" -#include "components/cbor/cbor_writer.h" +#include "components/cbor/values.h" +#include "components/cbor/writer.h" #include "device/fido/fido_parsing_utils.h" namespace device { @@ -15,8 +15,8 @@ namespace device { namespace { template <typename Container> -cbor::CBORValue::ArrayValue ToArrayValue(const Container& container) { - cbor::CBORValue::ArrayValue value; +cbor::Value::ArrayValue ToArrayValue(const Container& container) { + cbor::Value::ArrayValue value; value.reserve(container.size()); for (const auto& item : container) value.emplace_back(item); @@ -65,12 +65,12 @@ AuthenticatorGetInfoResponse& AuthenticatorGetInfoResponse::SetOptions( std::vector<uint8_t> EncodeToCBOR( const AuthenticatorGetInfoResponse& response) { - cbor::CBORValue::ArrayValue version_array; + cbor::Value::ArrayValue version_array; for (const auto& version : response.versions()) { version_array.emplace_back(version == ProtocolVersion::kCtap ? kCtap2Version : kU2fVersion); } - cbor::CBORValue::MapValue device_info_map; + cbor::Value::MapValue device_info_map; device_info_map.emplace(1, std::move(version_array)); if (response.extensions()) @@ -89,7 +89,7 @@ std::vector<uint8_t> EncodeToCBOR( } auto encoded_bytes = - cbor::CBORWriter::Write(cbor::CBORValue(std::move(device_info_map))); + cbor::Writer::Write(cbor::Value(std::move(device_info_map))); DCHECK(encoded_bytes); return *encoded_bytes; } diff --git a/chromium/device/fido/authenticator_make_credential_response.cc b/chromium/device/fido/authenticator_make_credential_response.cc index 7efafaf1724..2559a51b9c4 100644 --- a/chromium/device/fido/authenticator_make_credential_response.cc +++ b/chromium/device/fido/authenticator_make_credential_response.cc @@ -18,7 +18,7 @@ namespace device { // static base::Optional<AuthenticatorMakeCredentialResponse> AuthenticatorMakeCredentialResponse::CreateFromU2fRegisterResponse( - FidoTransportProtocol transport_used, + base::Optional<FidoTransportProtocol> transport_used, base::span<const uint8_t, kRpIdHashLength> relying_party_id_hash, base::span<const uint8_t> u2f_data) { auto public_key = ECPublicKey::ExtractFromU2fRegistrationResponse( @@ -58,7 +58,7 @@ AuthenticatorMakeCredentialResponse::CreateFromU2fRegisterResponse( } AuthenticatorMakeCredentialResponse::AuthenticatorMakeCredentialResponse( - FidoTransportProtocol transport_used, + base::Optional<FidoTransportProtocol> transport_used, AttestationObject attestation_object) : ResponseData(attestation_object.GetCredentialId()), attestation_object_(std::move(attestation_object)), @@ -78,8 +78,9 @@ AuthenticatorMakeCredentialResponse::GetCBOREncodedAttestationObject() const { return attestation_object_.SerializeToCBOREncodedBytes(); } -void AuthenticatorMakeCredentialResponse::EraseAttestationStatement() { - attestation_object_.EraseAttestationStatement(); +void AuthenticatorMakeCredentialResponse::EraseAttestationStatement( + AttestationObject::AAGUID erase_aaguid) { + attestation_object_.EraseAttestationStatement(erase_aaguid); } bool AuthenticatorMakeCredentialResponse::IsSelfAttestation() { diff --git a/chromium/device/fido/authenticator_make_credential_response.h b/chromium/device/fido/authenticator_make_credential_response.h index 0f9c581c80c..e55193dd807 100644 --- a/chromium/device/fido/authenticator_make_credential_response.h +++ b/chromium/device/fido/authenticator_make_credential_response.h @@ -30,12 +30,13 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorMakeCredentialResponse public: static base::Optional<AuthenticatorMakeCredentialResponse> CreateFromU2fRegisterResponse( - FidoTransportProtocol transport_used, + base::Optional<FidoTransportProtocol> transport_used, base::span<const uint8_t, kRpIdHashLength> relying_party_id_hash, base::span<const uint8_t> u2f_data); - AuthenticatorMakeCredentialResponse(FidoTransportProtocol transport_used, - AttestationObject attestation_object); + AuthenticatorMakeCredentialResponse( + base::Optional<FidoTransportProtocol> transport_used, + AttestationObject attestation_object); AuthenticatorMakeCredentialResponse( AuthenticatorMakeCredentialResponse&& that); AuthenticatorMakeCredentialResponse& operator=( @@ -44,10 +45,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorMakeCredentialResponse std::vector<uint8_t> GetCBOREncodedAttestationObject() const; - // Replaces the attestation statement with a “none” attestation and removes - // AAGUID from authenticator data section. + // Replaces the attestation statement with a “none” attestation, and removes + // AAGUID from authenticator data section unless |preserve_aaguid| is true. // https://w3c.github.io/webauthn/#createCredential - void EraseAttestationStatement(); + void EraseAttestationStatement(AttestationObject::AAGUID erase_aaguid); // Returns true if the attestation is a "self" attestation, i.e. is just the // private key signing itself to show that it is fresh and the AAGUID is zero. @@ -66,13 +67,16 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorMakeCredentialResponse return attestation_object_; } - FidoTransportProtocol transport_used() const { return transport_used_; } + base::Optional<FidoTransportProtocol> transport_used() const { + return transport_used_; + } private: AttestationObject attestation_object_; - // Contains the transport used to register the credential in this case. - FidoTransportProtocol transport_used_; + // Contains the transport used to register the credential in this case. It is + // nullopt for cases where we cannot determine the transport (Windows). + base::Optional<FidoTransportProtocol> transport_used_; DISALLOW_COPY_AND_ASSIGN(AuthenticatorMakeCredentialResponse); }; diff --git a/chromium/device/fido/authenticator_selection_criteria.cc b/chromium/device/fido/authenticator_selection_criteria.cc index 598f9d93fce..7229f566c2f 100644 --- a/chromium/device/fido/authenticator_selection_criteria.cc +++ b/chromium/device/fido/authenticator_selection_criteria.cc @@ -9,10 +9,10 @@ namespace device { AuthenticatorSelectionCriteria::AuthenticatorSelectionCriteria() = default; AuthenticatorSelectionCriteria::AuthenticatorSelectionCriteria( - AuthenticatorAttachment authenticator_attachement, + AuthenticatorAttachment authenticator_attachment, bool require_resident_key, UserVerificationRequirement user_verification_requirement) - : authenticator_attachement_(authenticator_attachement), + : authenticator_attachment_(authenticator_attachment), require_resident_key_(require_resident_key), user_verification_requirement_(user_verification_requirement) {} diff --git a/chromium/device/fido/authenticator_selection_criteria.h b/chromium/device/fido/authenticator_selection_criteria.h index 82d22d075ae..e3841ba05f3 100644 --- a/chromium/device/fido/authenticator_selection_criteria.h +++ b/chromium/device/fido/authenticator_selection_criteria.h @@ -15,15 +15,9 @@ namespace device { // https://w3c.github.io/webauthn/#authenticatorSelection class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorSelectionCriteria { public: - enum class AuthenticatorAttachment { - kAny, - kPlatform, - kCrossPlatform, - }; - AuthenticatorSelectionCriteria(); AuthenticatorSelectionCriteria( - AuthenticatorAttachment authenticator_attachement, + AuthenticatorAttachment authenticator_attachment, bool require_resident_key, UserVerificationRequirement user_verification_requirement); AuthenticatorSelectionCriteria(const AuthenticatorSelectionCriteria& other); @@ -34,8 +28,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorSelectionCriteria { AuthenticatorSelectionCriteria&& other); ~AuthenticatorSelectionCriteria(); - AuthenticatorAttachment authenticator_attachement() const { - return authenticator_attachement_; + AuthenticatorAttachment authenticator_attachment() const { + return authenticator_attachment_; } bool require_resident_key() const { return require_resident_key_; } @@ -45,7 +39,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorSelectionCriteria { } private: - AuthenticatorAttachment authenticator_attachement_ = + AuthenticatorAttachment authenticator_attachment_ = AuthenticatorAttachment::kAny; bool require_resident_key_ = false; UserVerificationRequirement user_verification_requirement_ = diff --git a/chromium/device/fido/authenticator_supported_options.cc b/chromium/device/fido/authenticator_supported_options.cc index af08a3c6e70..819bd6afeb4 100644 --- a/chromium/device/fido/authenticator_supported_options.cc +++ b/chromium/device/fido/authenticator_supported_options.cc @@ -11,13 +11,14 @@ namespace device { AuthenticatorSupportedOptions::AuthenticatorSupportedOptions() = default; - +AuthenticatorSupportedOptions::AuthenticatorSupportedOptions( + const AuthenticatorSupportedOptions& other) = default; AuthenticatorSupportedOptions::AuthenticatorSupportedOptions( AuthenticatorSupportedOptions&& other) = default; - +AuthenticatorSupportedOptions& AuthenticatorSupportedOptions::operator=( + const AuthenticatorSupportedOptions& other) = default; AuthenticatorSupportedOptions& AuthenticatorSupportedOptions::operator=( AuthenticatorSupportedOptions&& other) = default; - AuthenticatorSupportedOptions::~AuthenticatorSupportedOptions() = default; AuthenticatorSupportedOptions& @@ -54,8 +55,8 @@ AuthenticatorSupportedOptions::SetIsPlatformDevice(bool is_platform_device) { return *this; } -cbor::CBORValue ConvertToCBOR(const AuthenticatorSupportedOptions& options) { - cbor::CBORValue::MapValue option_map; +cbor::Value ConvertToCBOR(const AuthenticatorSupportedOptions& options) { + cbor::Value::MapValue option_map; option_map.emplace(kResidentKeyMapKey, options.supports_resident_key()); option_map.emplace(kUserPresenceMapKey, options.user_presence_required()); option_map.emplace(kPlatformDeviceMapKey, options.is_platform_device()); @@ -88,7 +89,7 @@ cbor::CBORValue ConvertToCBOR(const AuthenticatorSupportedOptions& options) { break; } - return cbor::CBORValue(std::move(option_map)); + return cbor::Value(std::move(option_map)); } } // namespace device diff --git a/chromium/device/fido/authenticator_supported_options.h b/chromium/device/fido/authenticator_supported_options.h index 0c6bed03f05..0d8fe25c1bf 100644 --- a/chromium/device/fido/authenticator_supported_options.h +++ b/chromium/device/fido/authenticator_supported_options.h @@ -8,7 +8,7 @@ #include "base/component_export.h" #include "base/macros.h" #include "base/optional.h" -#include "components/cbor/cbor_values.h" +#include "components/cbor/values.h" namespace device { @@ -33,8 +33,11 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorSupportedOptions { }; AuthenticatorSupportedOptions(); + AuthenticatorSupportedOptions(const AuthenticatorSupportedOptions& other); AuthenticatorSupportedOptions(AuthenticatorSupportedOptions&& other); AuthenticatorSupportedOptions& operator=( + const AuthenticatorSupportedOptions& other); + AuthenticatorSupportedOptions& operator=( AuthenticatorSupportedOptions&& other); ~AuthenticatorSupportedOptions(); @@ -74,12 +77,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorSupportedOptions { // optional if client pin capability is not supported by the authenticator. ClientPinAvailability client_pin_availability_ = ClientPinAvailability::kNotSupported; - - DISALLOW_COPY_AND_ASSIGN(AuthenticatorSupportedOptions); }; COMPONENT_EXPORT(DEVICE_FIDO) -cbor::CBORValue ConvertToCBOR(const AuthenticatorSupportedOptions& options); +cbor::Value ConvertToCBOR(const AuthenticatorSupportedOptions& options); } // namespace device diff --git a/chromium/device/fido/ble/fido_ble_connection_unittest.cc b/chromium/device/fido/ble/fido_ble_connection_unittest.cc index 507297fb88b..e04c24fa05c 100644 --- a/chromium/device/fido/ble/fido_ble_connection_unittest.cc +++ b/chromium/device/fido/ble/fido_ble_connection_unittest.cc @@ -36,6 +36,8 @@ #include "device/bluetooth/test/bluetooth_test_win.h" #elif defined(OS_CHROMEOS) || defined(OS_LINUX) #include "device/bluetooth/test/bluetooth_test_bluez.h" +#elif defined(OS_FUCHSIA) +#include "device/bluetooth/test/bluetooth_test_fuchsia.h" #endif namespace device { diff --git a/chromium/device/fido/ble/fido_ble_device.cc b/chromium/device/fido/ble/fido_ble_device.cc index 5ea6e1e6b78..da6a3a1e58d 100644 --- a/chromium/device/fido/ble/fido_ble_device.cc +++ b/chromium/device/fido/ble/fido_ble_device.cc @@ -21,7 +21,7 @@ FidoBleDevice::FidoBleDevice(BluetoothAdapter* adapter, std::string address) connection_ = std::make_unique<FidoBleConnection>( adapter, std::move(address), base::BindRepeating(&FidoBleDevice::OnStatusMessage, - base::Unretained(this))); + weak_factory_.GetWeakPtr())); } FidoBleDevice::FidoBleDevice(std::unique_ptr<FidoBleConnection> connection) @@ -36,7 +36,7 @@ void FidoBleDevice::Connect() { StartTimeout(); state_ = State::kBusy; connection_->Connect( - base::BindOnce(&FidoBleDevice::OnConnected, base::Unretained(this))); + base::BindOnce(&FidoBleDevice::OnConnected, weak_factory_.GetWeakPtr())); } void FidoBleDevice::SendPing(std::vector<uint8_t> data, @@ -108,9 +108,17 @@ bool FidoBleDevice::IsInPairingMode() const { static_cast<int>(FidoServiceDataFlags::kPairingMode)) != 0; } +bool FidoBleDevice::IsPaired() const { + const BluetoothDevice* const ble_device = connection_->GetBleDevice(); + if (!ble_device) + return false; + + return ble_device->IsPaired(); +} + FidoBleConnection::ReadCallback FidoBleDevice::GetReadCallbackForTesting() { return base::BindRepeating(&FidoBleDevice::OnStatusMessage, - base::Unretained(this)); + weak_factory_.GetWeakPtr()); } void FidoBleDevice::DeviceTransact(std::vector<uint8_t> command, @@ -148,8 +156,9 @@ void FidoBleDevice::Transition() { case State::kConnected: StartTimeout(); state_ = State::kBusy; - connection_->ReadControlPointLength(base::BindOnce( - &FidoBleDevice::OnReadControlPointLength, base::Unretained(this))); + connection_->ReadControlPointLength( + base::BindOnce(&FidoBleDevice::OnReadControlPointLength, + weak_factory_.GetWeakPtr())); break; case State::kReady: if (!pending_frames_.empty()) { @@ -215,8 +224,8 @@ void FidoBleDevice::SendRequestFrame(FidoBleFrame frame, transaction_.emplace(connection_.get(), control_point_length_); transaction_->WriteRequestFrame( std::move(frame), - base::BindOnce(&FidoBleDevice::OnResponseFrame, base::Unretained(this), - std::move(callback))); + base::BindOnce(&FidoBleDevice::OnResponseFrame, + weak_factory_.GetWeakPtr(), std::move(callback))); } void FidoBleDevice::StartTimeout() { diff --git a/chromium/device/fido/ble/fido_ble_device.h b/chromium/device/fido/ble/fido_ble_device.h index e8636206767..26a37104189 100644 --- a/chromium/device/fido/ble/fido_ble_device.h +++ b/chromium/device/fido/ble/fido_ble_device.h @@ -50,6 +50,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleDevice : public FidoDevice { // mode by investigating the advertisement payload. bool IsInPairingMode() const override; + bool IsPaired() const override; + FidoBleConnection::ReadCallback GetReadCallbackForTesting(); protected: diff --git a/chromium/device/fido/ble/fido_ble_discovery.cc b/chromium/device/fido/ble/fido_ble_discovery.cc index 4a6ade31391..36e8e950319 100644 --- a/chromium/device/fido/ble/fido_ble_discovery.cc +++ b/chromium/device/fido/ble/fido_ble_discovery.cc @@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/time/time.h" #include "device/bluetooth/bluetooth_adapter.h" #include "device/bluetooth/bluetooth_common.h" #include "device/bluetooth/bluetooth_discovery_session.h" @@ -15,6 +16,7 @@ #include "device/fido/ble/fido_ble_device.h" #include "device/fido/ble/fido_ble_uuids.h" #include "device/fido/fido_authenticator.h" +#include "device/fido/fido_constants.h" #include "device/fido/fido_device_authenticator.h" namespace device { @@ -38,9 +40,11 @@ void FidoBleDiscovery::OnSetPowered() { for (BluetoothDevice* device : adapter()->GetDevices()) { if (!CheckForExcludedDeviceAndCacheAddress(device) && base::ContainsKey(device->GetUUIDs(), FidoServiceUUID())) { - VLOG(2) << "U2F BLE device: " << device->GetAddress(); - AddDevice( - std::make_unique<FidoBleDevice>(adapter(), device->GetAddress())); + const auto& device_address = device->GetAddress(); + VLOG(2) << "FIDO BLE device: " << device_address; + AddDevice(std::make_unique<FidoBleDevice>(adapter(), device_address)); + CheckAndRecordDevicePairingModeOnDiscovery( + FidoBleDevice::GetId(device_address)); } } @@ -62,8 +66,11 @@ void FidoBleDiscovery::DeviceAdded(BluetoothAdapter* adapter, BluetoothDevice* device) { if (!CheckForExcludedDeviceAndCacheAddress(device) && base::ContainsKey(device->GetUUIDs(), FidoServiceUUID())) { - VLOG(2) << "Discovered U2F BLE device: " << device->GetAddress(); - AddDevice(std::make_unique<FidoBleDevice>(adapter, device->GetAddress())); + const auto& device_address = device->GetAddress(); + VLOG(2) << "Discovered FIDO BLE device: " << device_address; + AddDevice(std::make_unique<FidoBleDevice>(adapter, device_address)); + CheckAndRecordDevicePairingModeOnDiscovery( + FidoBleDevice::GetId(device_address)); } } @@ -74,29 +81,29 @@ void FidoBleDiscovery::DeviceChanged(BluetoothAdapter* adapter, return; } - const auto device_id = FidoBleDevice::GetId(device->GetAddress()); - auto* authenticator = GetAuthenticator(device_id); + auto authenticator_id = FidoBleDevice::GetId(device->GetAddress()); + auto* authenticator = GetAuthenticator(authenticator_id); if (!authenticator) { - VLOG(2) << "Discovered U2F service on existing BLE device: " + VLOG(2) << "Discovered FIDO service on existing BLE device: " << device->GetAddress(); AddDevice(std::make_unique<FidoBleDevice>(adapter, device->GetAddress())); + CheckAndRecordDevicePairingModeOnDiscovery(std::move(authenticator_id)); return; } - // Our model of FIDO BLE security key assumes that if BLE device is in pairing - // mode long enough time without pairing attempt, the device stops advertising - // and BluetoothAdapter::DeviceRemoved() is invoked instead of returning back - // to regular "non-pairing" mode. As so, we only notify observer when - // |fido_device| goes into pairing mode. - if (observer() && authenticator->device()->IsInPairingMode()) - observer()->AuthenticatorPairingModeChanged(this, device_id); + if (authenticator->device()->IsInPairingMode()) { + RecordDevicePairingStatus(std::move(authenticator_id), + PairingModeChangeType::kUnobserved); + } } void FidoBleDiscovery::DeviceRemoved(BluetoothAdapter* adapter, BluetoothDevice* device) { if (base::ContainsKey(device->GetUUIDs(), FidoServiceUUID())) { - VLOG(2) << "U2F BLE device removed: " << device->GetAddress(); - RemoveDevice(FidoBleDevice::GetId(device->GetAddress())); + VLOG(2) << "FIDO BLE device removed: " << device->GetAddress(); + auto device_id = FidoBleDevice::GetId(device->GetAddress()); + RemoveDevice(device_id); + RemoveDeviceFromPairingTracker(device_id); } } @@ -120,8 +127,17 @@ void FidoBleDiscovery::DeviceAddressChanged(BluetoothAdapter* adapter, VLOG(2) << "Discovered FIDO BLE device address change from old address : " << old_address << " to new address : " << device->GetAddress(); - authenticators_.emplace(new_device_id, std::move(it->second)); - authenticators_.erase(it); + + auto change_map_keys = [&](auto* map) { + auto it = map->find(previous_device_id); + if (it != map->end()) { + map->emplace(new_device_id, std::move(it->second)); + map->erase(it); + } + }; + + change_map_keys(&authenticators_); + change_map_keys(&pairing_mode_device_tracker_); if (observer()) { observer()->AuthenticatorIdChanged(this, previous_device_id, @@ -151,4 +167,46 @@ bool FidoBleDiscovery::CheckForExcludedDeviceAndCacheAddress( return false; } +void FidoBleDiscovery::CheckAndRecordDevicePairingModeOnDiscovery( + std::string authenticator_id) { + auto* authenticator = GetAuthenticator(authenticator_id); + DCHECK(authenticator); + if (authenticator->device()->IsInPairingMode()) { + RecordDevicePairingStatus(std::move(authenticator_id), + PairingModeChangeType::kObserved); + } +} + +void FidoBleDiscovery::RecordDevicePairingStatus(std::string device_id, + PairingModeChangeType type) { + auto it = pairing_mode_device_tracker_.find(device_id); + if (it != pairing_mode_device_tracker_.end()) { + it->second->Reset(); + return; + } + + if (observer() && type == PairingModeChangeType::kUnobserved) { + observer()->AuthenticatorPairingModeChanged(this, device_id, + true /* is_in_pairing_mode */); + } + + auto pairing_mode_timer = std::make_unique<base::OneShotTimer>(); + pairing_mode_timer->Start( + FROM_HERE, kBleDevicePairingModeWaitingInterval, + base::BindOnce(&FidoBleDiscovery::RemoveDeviceFromPairingTracker, + weak_factory_.GetWeakPtr(), device_id)); + pairing_mode_device_tracker_.emplace(std::move(device_id), + std::move(pairing_mode_timer)); +} + +void FidoBleDiscovery::RemoveDeviceFromPairingTracker( + const std::string& device_id) { + // Destroying the timer stops the timer scheduled task. + pairing_mode_device_tracker_.erase(device_id); + if (observer()) { + observer()->AuthenticatorPairingModeChanged(this, device_id, + false /* is_in_pairing_mode */); + } +} + } // namespace device diff --git a/chromium/device/fido/ble/fido_ble_discovery.h b/chromium/device/fido/ble/fido_ble_discovery.h index 0b03b863233..f786fc06ee5 100644 --- a/chromium/device/fido/ble/fido_ble_discovery.h +++ b/chromium/device/fido/ble/fido_ble_discovery.h @@ -5,6 +5,8 @@ #ifndef DEVICE_FIDO_BLE_FIDO_BLE_DISCOVERY_H_ #define DEVICE_FIDO_BLE_FIDO_BLE_DISCOVERY_H_ +#include <functional> +#include <map> #include <memory> #include <set> #include <string> @@ -12,6 +14,7 @@ #include "base/component_export.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "base/timer/timer.h" #include "device/fido/ble/fido_ble_discovery_base.h" namespace device { @@ -26,6 +29,16 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleDiscovery ~FidoBleDiscovery() override; private: + FRIEND_TEST_ALL_PREFIXES(FidoBleDiscoveryTest, + DiscoveryNotifiesObserverWhenDeviceInPairingMode); + FRIEND_TEST_ALL_PREFIXES(FidoBleDiscoveryTest, + DiscoveryNotifiesObserverWhenDeviceInNonPairingMode); + + enum class PairingModeChangeType { + kUnobserved, + kObserved, + }; + static const BluetoothUUID& FidoServiceUUID(); // FidoBleDiscoveryBase: @@ -46,7 +59,20 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleDiscovery // to |blacklisted_cable_device_addresses_|. bool CheckForExcludedDeviceAndCacheAddress(const BluetoothDevice* device); + void CheckAndRecordDevicePairingModeOnDiscovery(std::string authenticator_id); + + // If |device_id| does not exist in |pairing_mode_device_tracker_|, add + // |device_id| to the map and start a timer. If the map element already + // exists, restart the timer. + void RecordDevicePairingStatus(std::string device_id, + PairingModeChangeType type); + void RemoveDeviceFromPairingTracker(const std::string& device_id); + std::set<std::string> excluded_cable_device_addresses_; + + // Maps Bluetooth FIDO authenticators that are known to be in pairing mode. + std::map<std::string, std::unique_ptr<base::OneShotTimer>> + pairing_mode_device_tracker_; base::WeakPtrFactory<FidoBleDiscovery> weak_factory_; DISALLOW_COPY_AND_ASSIGN(FidoBleDiscovery); diff --git a/chromium/device/fido/ble/fido_ble_discovery_base.cc b/chromium/device/fido/ble/fido_ble_discovery_base.cc index 240513c74c3..19c24c12d08 100644 --- a/chromium/device/fido/ble/fido_ble_discovery_base.cc +++ b/chromium/device/fido/ble/fido_ble_discovery_base.cc @@ -83,23 +83,9 @@ void FidoBleDiscoveryBase::OnGetAdapter( } void FidoBleDiscoveryBase::StartInternal() { - auto& factory = BluetoothAdapterFactory::Get(); - auto callback = base::BindOnce(&FidoBleDiscoveryBase::OnGetAdapter, - weak_factory_.GetWeakPtr()); -#if defined(OS_MACOSX) - // BluetoothAdapter may invoke the callback synchronously on Mac, but - // StartInternal() never wants to invoke to NotifyDiscoveryStarted() - // immediately, so ensure there is at least post-task at this bottleneck. - // See: https://crbug.com/823686. - callback = base::BindOnce( - [](BluetoothAdapterFactory::AdapterCallback callback, - scoped_refptr<BluetoothAdapter> adapter) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(std::move(callback), adapter)); - }, - base::AdaptCallbackForRepeating(std::move(callback))); -#endif // defined(OS_MACOSX) - factory.GetAdapter(base::AdaptCallbackForRepeating(std::move(callback))); + BluetoothAdapterFactory::Get().GetAdapter( + base::AdaptCallbackForRepeating(base::BindOnce( + &FidoBleDiscoveryBase::OnGetAdapter, weak_factory_.GetWeakPtr()))); } } // namespace device diff --git a/chromium/device/fido/ble/fido_ble_discovery_unittest.cc b/chromium/device/fido/ble/fido_ble_discovery_unittest.cc index 48533cc9e47..77f823433ef 100644 --- a/chromium/device/fido/ble/fido_ble_discovery_unittest.cc +++ b/chromium/device/fido/ble/fido_ble_discovery_unittest.cc @@ -9,6 +9,8 @@ #include "base/bind.h" #include "base/run_loop.h" +#include "base/test/bind_test_util.h" +#include "base/test/scoped_task_environment.h" #include "build/build_config.h" #include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/bluetooth/test/bluetooth_test.h" @@ -36,7 +38,9 @@ namespace device { namespace { using ::testing::_; +using ::testing::Return; using TestMockDevice = ::testing::NiceMock<MockBluetoothDevice>; +using NiceMockBluetoothAdapter = ::testing::NiceMock<MockBluetoothAdapter>; constexpr char kDeviceName[] = "device_name"; constexpr char kDeviceAddress[] = "device_address"; @@ -54,48 +58,92 @@ MATCHER_P(IdMatches, id, "") { } // namespace -TEST_F(BluetoothTest, FidoBleDiscoveryNotifyObserverWhenAdapterNotPresent) { - FidoBleDiscovery discovery; - MockFidoDiscoveryObserver observer; - discovery.set_observer(&observer); - auto mock_adapter = - base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>(); - EXPECT_CALL(*mock_adapter, IsPresent()).WillOnce(::testing::Return(false)); - EXPECT_CALL(*mock_adapter, SetPowered).Times(0); - BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter); - EXPECT_CALL(observer, DiscoveryStarted(&discovery, false)); - discovery.Start(); +class FidoBleDiscoveryTest : public ::testing::Test { + public: + FidoBleDiscoveryTest() { discovery_.set_observer(&observer_); } + + std::unique_ptr<TestMockDevice> CreateMockFidoDevice() { + DCHECK(adapter_); + auto mock_device = std::make_unique<TestMockDevice>( + adapter_.get(), 0 /* bluetooth_class */, kDeviceName, kDeviceAddress, + false /* paired */, false /* connected */); + EXPECT_CALL(*mock_device, GetUUIDs) + .WillRepeatedly(Return( + std::vector<BluetoothUUID>{BluetoothUUID(kFidoServiceUUID)})); + EXPECT_CALL(*mock_device, GetAddress) + .WillRepeatedly(Return(kDeviceAddress)); + + EXPECT_CALL(*adapter(), GetDevice(kDeviceAddress)) + .WillRepeatedly(Return(mock_device.get())); + + return mock_device; + } + + void SetDeviceInPairingMode(TestMockDevice* device) { + // Update device advertisement data so that it represents BLE device in + // pairing mode. + DCHECK(adapter_); + device->UpdateAdvertisementData( + 0 /* rssi */, 1 << kLeLimitedDiscoverableModeBit, + std::vector<BluetoothUUID>{BluetoothUUID(kFidoServiceUUID)}, + base::nullopt /* tx_power */, BluetoothDevice::ServiceDataMap(), + BluetoothDevice::ManufacturerDataMap()); + adapter_->NotifyDeviceChanged(device); + } + + void SetMockBluetoothAdapter() { + adapter_ = base::MakeRefCounted<NiceMockBluetoothAdapter>(); + BluetoothAdapterFactory::SetAdapterForTesting(adapter_); + } + + FidoBleDiscovery* discovery() { return &discovery_; } + MockFidoDiscoveryObserver* observer() { return &observer_; } + MockBluetoothAdapter* adapter() { + DCHECK(adapter_); + return adapter_.get(); + } + + protected: + base::test::ScopedTaskEnvironment scoped_task_environment_{ + base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME}; + + private: + FidoBleDiscovery discovery_; + MockFidoDiscoveryObserver observer_; + scoped_refptr<MockBluetoothAdapter> adapter_; +}; + +TEST_F(FidoBleDiscoveryTest, + FidoBleDiscoveryNotifyObserverWhenAdapterNotPresent) { + SetMockBluetoothAdapter(); + EXPECT_CALL(*adapter(), IsPresent()).WillOnce(Return(false)); + EXPECT_CALL(*adapter(), SetPowered).Times(0); + EXPECT_CALL(*observer(), DiscoveryStarted(discovery(), false)); + discovery()->Start(); + scoped_task_environment_.FastForwardUntilNoTasksRemain(); } -TEST_F(BluetoothTest, FidoBleDiscoveryResumeScanningAfterPoweredOn) { - FidoBleDiscovery discovery; - MockFidoDiscoveryObserver observer; - discovery.set_observer(&observer); - auto mock_adapter = - base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>(); - EXPECT_CALL(*mock_adapter, IsPresent()).WillOnce(::testing::Return(true)); - EXPECT_CALL(*mock_adapter, IsPowered()).WillOnce(::testing::Return(false)); +TEST_F(FidoBleDiscoveryTest, FidoBleDiscoveryResumeScanningAfterPoweredOn) { + SetMockBluetoothAdapter(); + EXPECT_CALL(*adapter(), IsPresent()).WillOnce(Return(true)); + EXPECT_CALL(*adapter(), IsPowered()).WillOnce(Return(false)); // After BluetoothAdapter is powered on, we expect that discovery session // starts again. - EXPECT_CALL(*mock_adapter, StartDiscoverySessionWithFilterRaw); - BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter); - discovery.Start(); - mock_adapter->NotifyAdapterPoweredChanged(true); + EXPECT_CALL(*adapter(), StartDiscoverySessionWithFilterRaw); + discovery()->Start(); + scoped_task_environment_.FastForwardUntilNoTasksRemain(); + adapter()->NotifyAdapterPoweredChanged(true); } -TEST_F(BluetoothTest, FidoBleDiscoveryNoAdapter) { +TEST_F(FidoBleDiscoveryTest, FidoBleDiscoveryNoAdapter) { // We purposefully construct a temporary and provide no fake adapter, // simulating cases where the discovery is destroyed before obtaining a handle // to an adapter. This should be handled gracefully and not result in a crash. - FidoBleDiscovery discovery; - // We don't expect any calls to the notification methods. - MockFidoDiscoveryObserver observer; - discovery.set_observer(&observer); - EXPECT_CALL(observer, DiscoveryStarted(&discovery, _)).Times(0); - EXPECT_CALL(observer, AuthenticatorAdded(&discovery, _)).Times(0); - EXPECT_CALL(observer, AuthenticatorRemoved(&discovery, _)).Times(0); + EXPECT_CALL(*observer(), DiscoveryStarted(discovery(), _)).Times(0); + EXPECT_CALL(*observer(), AuthenticatorAdded(discovery(), _)).Times(0); + EXPECT_CALL(*observer(), AuthenticatorRemoved(discovery(), _)).Times(0); } TEST_F(BluetoothTest, FidoBleDiscoveryFindsKnownDevice) { @@ -250,84 +298,101 @@ TEST_F(BluetoothTest, FidoBleDiscoveryRejectsCableDevice) { SimulateLowEnergyDevice(7); } -TEST_F(BluetoothTest, DiscoveryDoesNotAddDuplicateDeviceOnAddressChanged) { - using TestMockDevice = ::testing::NiceMock<MockBluetoothDevice>; - - MockFidoDiscoveryObserver observer; - FidoBleDiscovery discovery; - discovery.set_observer(&observer); - - auto mock_adapter = - base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>(); - EXPECT_CALL(*mock_adapter, IsPresent()).WillOnce(::testing::Return(true)); - - auto mock_device = std::make_unique<TestMockDevice>( - mock_adapter.get(), 0 /* bluetooth_class */, kDeviceName, kDeviceAddress, - false /* paired */, false /* connected */); - - EXPECT_CALL(*mock_device.get(), GetUUIDs) - .WillRepeatedly(::testing::Return( - std::vector<BluetoothUUID>{BluetoothUUID(kFidoServiceUUID)})); +TEST_F(FidoBleDiscoveryTest, + DiscoveryDoesNotAddDuplicateDeviceOnAddressChanged) { + SetMockBluetoothAdapter(); + EXPECT_CALL(*adapter(), IsPresent()).WillOnce(Return(true)); + auto mock_device = CreateMockFidoDevice(); - BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter.get()); - EXPECT_CALL(observer, AuthenticatorIdChanged(&discovery, kAuthenticatorId, - kAuthenticatorChangedId)); - discovery.Start(); + EXPECT_CALL(*observer(), AuthenticatorIdChanged(discovery(), kAuthenticatorId, + kAuthenticatorChangedId)); + discovery()->Start(); + scoped_task_environment_.FastForwardUntilNoTasksRemain(); - EXPECT_CALL(*mock_device.get(), GetAddress) - .WillRepeatedly(::testing::Return(kDeviceAddress)); - mock_adapter->NotifyDeviceChanged(mock_device.get()); + adapter()->NotifyDeviceChanged(mock_device.get()); ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(mock_device.get())); EXPECT_CALL(*mock_device.get(), GetAddress) - .WillRepeatedly(::testing::Return(kDeviceChangedAddress)); - for (auto& observer : mock_adapter->GetObservers()) { - observer.DeviceAddressChanged(mock_adapter.get(), mock_device.get(), - kDeviceAddress); + .WillRepeatedly(Return(kDeviceChangedAddress)); + for (auto& observer : adapter()->GetObservers()) { + observer.DeviceAddressChanged(adapter(), mock_device.get(), kDeviceAddress); } - mock_adapter->NotifyDeviceChanged(mock_device.get()); + adapter()->NotifyDeviceChanged(mock_device.get()); - EXPECT_EQ(1u, discovery.GetAuthenticatorsForTesting().size()); - EXPECT_TRUE(discovery.GetAuthenticatorForTesting(kAuthenticatorChangedId)); + EXPECT_EQ(1u, discovery()->GetAuthenticatorsForTesting().size()); + EXPECT_TRUE(discovery()->GetAuthenticatorForTesting(kAuthenticatorChangedId)); } -TEST_F(BluetoothTest, DiscoveryNotifiesObserverWhenDeviceInPairingMode) { - FidoBleDiscovery discovery; - MockFidoDiscoveryObserver observer; - discovery.set_observer(&observer); +TEST_F(FidoBleDiscoveryTest, DiscoveryNotifiesObserverWhenDeviceInPairingMode) { + SetMockBluetoothAdapter(); + EXPECT_CALL(*adapter(), IsPresent()).WillOnce(Return(true)); + auto mock_device = CreateMockFidoDevice(); - auto mock_adapter = - base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>(); - EXPECT_CALL(*mock_adapter, IsPresent()).WillOnce(::testing::Return(true)); + const auto device_id = FidoBleDevice::GetId(kDeviceAddress); + discovery()->Start(); + scoped_task_environment_.FastForwardUntilNoTasksRemain(); + + ::testing::InSequence sequence; + EXPECT_CALL(*observer(), + AuthenticatorAdded(discovery(), IdMatches(kDeviceAddress))); + adapter()->NotifyDeviceChanged(mock_device.get()); + + EXPECT_CALL(*observer(), + AuthenticatorPairingModeChanged(discovery(), device_id, true)); + SetDeviceInPairingMode(mock_device.get()); + auto it = discovery()->pairing_mode_device_tracker_.find(device_id); + EXPECT_TRUE(it != discovery()->pairing_mode_device_tracker_.end()); + EXPECT_TRUE(it->second->IsRunning()); +} - auto mock_device = std::make_unique<TestMockDevice>( - mock_adapter.get(), 0 /* bluetooth_class */, kDeviceName, kDeviceAddress, - false /* paired */, false /* connected */); - EXPECT_CALL(*mock_device.get(), GetUUIDs) - .WillRepeatedly(::testing::Return( - std::vector<BluetoothUUID>{BluetoothUUID(kFidoServiceUUID)})); - EXPECT_CALL(*mock_adapter, GetDevice(kDeviceAddress)) - .WillRepeatedly(::testing::Return(mock_device.get())); +TEST_F(FidoBleDiscoveryTest, + DiscoveryNotifiesObserverWhenDeviceInNonPairingMode) { + SetMockBluetoothAdapter(); + EXPECT_CALL(*adapter(), IsPresent()).WillOnce(Return(true)); + auto mock_device = CreateMockFidoDevice(); - BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter.get()); const auto device_id = FidoBleDevice::GetId(kDeviceAddress); - discovery.Start(); + discovery()->Start(); + scoped_task_environment_.FastForwardUntilNoTasksRemain(); ::testing::InSequence sequence; - EXPECT_CALL(observer, - AuthenticatorAdded(&discovery, IdMatches(kDeviceAddress))); - mock_adapter->NotifyDeviceChanged(mock_device.get()); - - EXPECT_CALL(observer, AuthenticatorPairingModeChanged(&discovery, _)); - // Update device advertisement data so that it represents BLE device in - // pairing mode. - mock_device->UpdateAdvertisementData( - 0 /* rssi */, 1 << kLeLimitedDiscoverableModeBit, - std::vector<BluetoothUUID>{BluetoothUUID(kFidoServiceUUID)}, - base::nullopt /* tx_power */, BluetoothDevice::ServiceDataMap(), - BluetoothDevice::ManufacturerDataMap()); - mock_adapter->NotifyDeviceChanged(mock_device.get()); + EXPECT_CALL(*observer(), + AuthenticatorAdded(discovery(), IdMatches(kDeviceAddress))); + adapter()->NotifyDeviceChanged(mock_device.get()); + + EXPECT_CALL(*observer(), + AuthenticatorPairingModeChanged(discovery(), device_id, true)); + + SetDeviceInPairingMode(mock_device.get()); + auto it = discovery()->pairing_mode_device_tracker_.find(device_id); + ASSERT_TRUE(it != discovery()->pairing_mode_device_tracker_.end()); + EXPECT_TRUE(it->second->IsRunning()); + + // Simulates BLE device sending advertisement packet after some interval. + // Since device did not advertise that it is in pairing mode for longer than 3 + // seconds, we expect that the discovery should + // 1) First set the device to be in non-pairing mode and notify the + // observer. + // 2) When advertisement packet arrives after delay, device is re-set to + // pairing mode and observer is notified. + // 3) When timer completes due to FastForwardUntilNoTasksRemain(), + // authenticator is re-set to non-pairing mode. + ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(observer())); + EXPECT_CALL(*observer(), + AuthenticatorPairingModeChanged(discovery(), device_id, false)); + EXPECT_CALL(*observer(), + AuthenticatorPairingModeChanged(discovery(), device_id, true)); + EXPECT_CALL(*observer(), + AuthenticatorPairingModeChanged(discovery(), device_id, false)); + + scoped_task_environment_.GetMainThreadTaskRunner().get()->PostDelayedTask( + FROM_HERE, base::BindLambdaForTesting([&, this] { + adapter()->NotifyDeviceChanged(mock_device.get()); + }), + base::TimeDelta::FromSeconds(4)); + + scoped_task_environment_.FastForwardUntilNoTasksRemain(); } } // namespace device diff --git a/chromium/device/fido/ble/fido_ble_frames_unittest.cc b/chromium/device/fido/ble/fido_ble_frames_unittest.cc index 2d20c8d5370..ba9c4bdae3e 100644 --- a/chromium/device/fido/ble/fido_ble_frames_unittest.cc +++ b/chromium/device/fido/ble/fido_ble_frames_unittest.cc @@ -4,6 +4,7 @@ #include "device/fido/ble/fido_ble_frames.h" +#include <algorithm> #include <vector> #include "testing/gtest/include/gtest/gtest.h" @@ -38,7 +39,9 @@ TEST(FidoBleFramesTest, InitializationFragment) { FidoBleFrameInitializationFragment::Parse(buffer, &parsed_fragment)); EXPECT_EQ(kDataLength, parsed_fragment.data_length()); - EXPECT_EQ(base::make_span(data), parsed_fragment.fragment()); + EXPECT_TRUE(std::equal(data.begin(), data.end(), + parsed_fragment.fragment().begin(), + parsed_fragment.fragment().end())); EXPECT_EQ(FidoBleDeviceCommand::kMsg, parsed_fragment.command()); } @@ -58,7 +61,9 @@ TEST(FidoBleFramesTest, ContinuationFragment) { ASSERT_TRUE( FidoBleFrameContinuationFragment::Parse(buffer, &parsed_fragment)); - EXPECT_EQ(base::make_span(data), parsed_fragment.fragment()); + EXPECT_TRUE(std::equal(data.begin(), data.end(), + parsed_fragment.fragment().begin(), + parsed_fragment.fragment().end())); EXPECT_EQ(kSequence, parsed_fragment.sequence()); } diff --git a/chromium/device/fido/ble_adapter_manager_unittest.cc b/chromium/device/fido/ble_adapter_manager_unittest.cc index c09e20377b7..eb6ef23b620 100644 --- a/chromium/device/fido/ble_adapter_manager_unittest.cc +++ b/chromium/device/fido/ble_adapter_manager_unittest.cc @@ -47,7 +47,8 @@ class MockTransportAvailabilityObserver MOCK_METHOD2(FidoAuthenticatorIdChanged, void(base::StringPiece old_authenticator_id, std::string new_authenticator_id)); - MOCK_METHOD1(FidoAuthenticatorPairingModeChanged, void(base::StringPiece)); + MOCK_METHOD2(FidoAuthenticatorPairingModeChanged, + void(base::StringPiece, bool)); private: DISALLOW_COPY_AND_ASSIGN(MockTransportAvailabilityObserver); diff --git a/chromium/device/fido/cable/fido_cable_device_unittest.cc b/chromium/device/fido/cable/fido_cable_device_unittest.cc index e99e691a8d1..5d4a9e29316 100644 --- a/chromium/device/fido/cable/fido_cable_device_unittest.cc +++ b/chromium/device/fido/cable/fido_cable_device_unittest.cc @@ -267,8 +267,7 @@ TEST_F(FidoCableDeviceTest, TestCableDeviceFailOnUnexpectedCounter) { ConnectWithLength(kControlPointLength); EXPECT_CALL(*connection(), WriteControlPointPtr(_, _)) - .WillOnce(Invoke([this, kIncorrectAuthenticatorCounter](const auto& data, - auto* cb) { + .WillOnce(Invoke([this](const auto& data, auto* cb) { base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(*cb), true)); diff --git a/chromium/device/fido/cable/fido_cable_discovery_unittest.cc b/chromium/device/fido/cable/fido_cable_discovery_unittest.cc index 814033e0733..138f23379b3 100644 --- a/chromium/device/fido/cable/fido_cable_discovery_unittest.cc +++ b/chromium/device/fido/cable/fido_cable_discovery_unittest.cc @@ -116,8 +116,9 @@ MATCHER_P2(IsAdvertisementContent, manufacturer_data_payload[1] == 0x15 && // Manufacturer Data Type manufacturer_data_payload[2] == 0x20 && // Cable Flags manufacturer_data_payload[3] == kTestCableVersionNumber && - base::make_span(manufacturer_data_payload).subspan(4) == - expected_client_eid; + std::equal(manufacturer_data_payload.begin() + 4, + manufacturer_data_payload.end(), + expected_client_eid.begin(), expected_client_eid.end()); #elif defined(OS_LINUX) || defined(OS_CHROMEOS) const auto service_data = arg->service_data(); @@ -131,7 +132,8 @@ MATCHER_P2(IsAdvertisementContent, return (service_data_value[0] >> 5 & 1) && service_data_value[1] == kTestCableVersionNumber && service_data_value.size() == 18u && - base::make_span(service_data_value).subspan(2) == expected_client_eid; + std::equal(service_data_value.begin() + 2, service_data_value.end(), + expected_client_eid.begin(), expected_client_eid.end()); #endif @@ -239,7 +241,7 @@ class CableMockAdapter : public MockBluetoothAdapter { void ExpectDiscoveryWithScanCallback() { EXPECT_CALL(*this, StartDiscoverySessionWithFilterRaw(_, _, _)) .WillOnce(::testing::WithArg<1>( - [this](const auto& callback) { callback.Run(nullptr); })); + [](const auto& callback) { callback.Run(nullptr); })); } void ExpectDiscoveryWithScanCallback( diff --git a/chromium/device/fido/cable/fido_cable_handshake_handler.cc b/chromium/device/fido/cable/fido_cable_handshake_handler.cc index a227377a096..8c6e6fc2c86 100644 --- a/chromium/device/fido/cable/fido_cable_handshake_handler.cc +++ b/chromium/device/fido/cable/fido_cable_handshake_handler.cc @@ -9,9 +9,9 @@ #include "base/containers/span.h" #include "base/threading/thread_task_runner_handle.h" -#include "components/cbor/cbor_reader.h" -#include "components/cbor/cbor_values.h" -#include "components/cbor/cbor_writer.h" +#include "components/cbor/reader.h" +#include "components/cbor/values.h" +#include "components/cbor/writer.h" #include "crypto/hkdf.h" #include "crypto/hmac.h" #include "crypto/random.h" @@ -44,10 +44,10 @@ std::string GenerateKey(base::StringPiece secret, base::Optional<std::array<uint8_t, kClientHelloMessageSize>> ConstructHandshakeMessage(base::StringPiece handshake_key, base::span<const uint8_t, 16> client_random_nonce) { - cbor::CBORValue::MapValue map; + cbor::Value::MapValue map; map.emplace(0, kCableClientHelloMessage); map.emplace(1, client_random_nonce); - auto client_hello = cbor::CBORWriter::Write(cbor::CBORValue(std::move(map))); + auto client_hello = cbor::Writer::Write(cbor::Value(std::move(map))); DCHECK(client_hello); crypto::HMAC hmac(crypto::HMAC::SHA256); @@ -125,15 +125,14 @@ bool FidoCableHandshakeHandler::ValidateAuthenticatorHandshakeMessage( return false; } - const auto authenticator_hello_cbor = - cbor::CBORReader::Read(authenticator_hello); + const auto authenticator_hello_cbor = cbor::Reader::Read(authenticator_hello); if (!authenticator_hello_cbor || !authenticator_hello_cbor->is_map() || authenticator_hello_cbor->GetMap().size() != 2) { return false; } const auto authenticator_hello_msg = - authenticator_hello_cbor->GetMap().find(cbor::CBORValue(0)); + authenticator_hello_cbor->GetMap().find(cbor::Value(0)); if (authenticator_hello_msg == authenticator_hello_cbor->GetMap().end() || !authenticator_hello_msg->second.is_string() || authenticator_hello_msg->second.GetString() != @@ -142,7 +141,7 @@ bool FidoCableHandshakeHandler::ValidateAuthenticatorHandshakeMessage( } const auto authenticator_random_nonce = - authenticator_hello_cbor->GetMap().find(cbor::CBORValue(1)); + authenticator_hello_cbor->GetMap().find(cbor::Value(1)); if (authenticator_random_nonce == authenticator_hello_cbor->GetMap().end() || !authenticator_random_nonce->second.is_bytestring() || authenticator_random_nonce->second.GetBytestring().size() != 16) { diff --git a/chromium/device/fido/cable/fido_cable_handshake_handler_unittest.cc b/chromium/device/fido/cable/fido_cable_handshake_handler_unittest.cc index 49ea55d3169..7c2117a4662 100644 --- a/chromium/device/fido/cable/fido_cable_handshake_handler_unittest.cc +++ b/chromium/device/fido/cable/fido_cable_handshake_handler_unittest.cc @@ -13,9 +13,9 @@ #include "base/optional.h" #include "base/test/scoped_task_environment.h" #include "base/threading/sequenced_task_runner_handle.h" -#include "components/cbor/cbor_reader.h" -#include "components/cbor/cbor_values.h" -#include "components/cbor/cbor_writer.h" +#include "components/cbor/reader.h" +#include "components/cbor/values.h" +#include "components/cbor/writer.h" #include "crypto/hkdf.h" #include "crypto/hmac.h" #include "device/bluetooth/test/bluetooth_test.h" @@ -208,13 +208,13 @@ class FakeCableAuthenticator { return false; } - const auto& client_hello_cbor = cbor::CBORReader::Read(client_hello); + const auto& client_hello_cbor = cbor::Reader::Read(client_hello); if (!client_hello_cbor) return false; const auto& message_map = client_hello_cbor->GetMap(); - auto hello_message_it = message_map.find(cbor::CBORValue(0)); - auto client_random_nonce_it = message_map.find(cbor::CBORValue(1)); + auto hello_message_it = message_map.find(cbor::Value(0)); + auto client_random_nonce_it = message_map.find(cbor::Value(1)); if (hello_message_it == message_map.end() || client_random_nonce_it == message_map.end()) return false; diff --git a/chromium/device/fido/ctap_get_assertion_request.cc b/chromium/device/fido/ctap_get_assertion_request.cc index 64285cb5565..b2912a2be03 100644 --- a/chromium/device/fido/ctap_get_assertion_request.cc +++ b/chromium/device/fido/ctap_get_assertion_request.cc @@ -9,47 +9,18 @@ #include <utility> #include "base/numerics/safe_conversions.h" -#include "components/cbor/cbor_reader.h" -#include "components/cbor/cbor_writer.h" +#include "components/cbor/writer.h" #include "device/fido/fido_constants.h" #include "device/fido/fido_parsing_utils.h" namespace device { -namespace { - -bool AreGetAssertionRequestMapKeysCorrect( - const cbor::CBORValue::MapValue& request_map) { - return std::all_of(request_map.begin(), request_map.end(), - [](const auto& param) { - if (!param.first.is_integer()) - return false; - - const auto& key = param.first.GetInteger(); - return (key <= 7u || key >= 1u); - }); -} - -bool IsGetAssertionOptionMapFormatCorrect( - const cbor::CBORValue::MapValue& option_map) { - return std::all_of( - option_map.begin(), option_map.end(), [](const auto& param) { - if (!param.first.is_string()) - return false; - - const auto& key = param.first.GetString(); - return (key == kUserPresenceMapKey || key == kUserVerificationMapKey) && - param.second.is_bool(); - }); -} - -} // namespace - -CtapGetAssertionRequest::CtapGetAssertionRequest( - std::string rp_id, - base::span<const uint8_t, kClientDataHashLength> client_data_hash) +CtapGetAssertionRequest::CtapGetAssertionRequest(std::string rp_id, + std::string client_data_json) : rp_id_(std::move(rp_id)), - client_data_hash_(fido_parsing_utils::Materialize(client_data_hash)) {} + client_data_json_(std::move(client_data_json)), + client_data_hash_( + fido_parsing_utils::CreateSHA256Hash(client_data_json_)) {} CtapGetAssertionRequest::CtapGetAssertionRequest( const CtapGetAssertionRequest& that) = default; @@ -66,46 +37,44 @@ CtapGetAssertionRequest& CtapGetAssertionRequest::operator=( CtapGetAssertionRequest::~CtapGetAssertionRequest() = default; std::vector<uint8_t> CtapGetAssertionRequest::EncodeAsCBOR() const { - cbor::CBORValue::MapValue cbor_map; - cbor_map[cbor::CBORValue(1)] = cbor::CBORValue(rp_id_); - cbor_map[cbor::CBORValue(2)] = cbor::CBORValue(client_data_hash_); + cbor::Value::MapValue cbor_map; + cbor_map[cbor::Value(1)] = cbor::Value(rp_id_); + cbor_map[cbor::Value(2)] = cbor::Value(client_data_hash_); if (allow_list_) { - cbor::CBORValue::ArrayValue allow_list_array; + cbor::Value::ArrayValue allow_list_array; for (const auto& descriptor : *allow_list_) { allow_list_array.push_back(descriptor.ConvertToCBOR()); } - cbor_map[cbor::CBORValue(3)] = cbor::CBORValue(std::move(allow_list_array)); + cbor_map[cbor::Value(3)] = cbor::Value(std::move(allow_list_array)); } if (pin_auth_) { - cbor_map[cbor::CBORValue(6)] = cbor::CBORValue(*pin_auth_); + cbor_map[cbor::Value(6)] = cbor::Value(*pin_auth_); } if (pin_protocol_) { - cbor_map[cbor::CBORValue(7)] = cbor::CBORValue(*pin_protocol_); + cbor_map[cbor::Value(7)] = cbor::Value(*pin_protocol_); } - cbor::CBORValue::MapValue option_map; + cbor::Value::MapValue option_map; // User presence is required by default. if (!user_presence_required_) { - option_map[cbor::CBORValue(kUserPresenceMapKey)] = - cbor::CBORValue(user_presence_required_); + option_map[cbor::Value(kUserPresenceMapKey)] = + cbor::Value(user_presence_required_); } // User verification is not required by default. if (user_verification_ == UserVerificationRequirement::kRequired) { - option_map[cbor::CBORValue(kUserVerificationMapKey)] = - cbor::CBORValue(true); + option_map[cbor::Value(kUserVerificationMapKey)] = cbor::Value(true); } if (!option_map.empty()) { - cbor_map[cbor::CBORValue(5)] = cbor::CBORValue(std::move(option_map)); + cbor_map[cbor::Value(5)] = cbor::Value(std::move(option_map)); } - auto serialized_param = - cbor::CBORWriter::Write(cbor::CBORValue(std::move(cbor_map))); + auto serialized_param = cbor::Writer::Write(cbor::Value(std::move(cbor_map))); DCHECK(serialized_param); std::vector<uint8_t> cbor_request({base::strict_cast<uint8_t>( @@ -151,106 +120,20 @@ CtapGetAssertionRequest& CtapGetAssertionRequest::SetCableExtension( return *this; } -CtapGetAssertionRequest& -CtapGetAssertionRequest::SetAlternativeApplicationParameter( - base::span<const uint8_t, kRpIdHashLength> - alternative_application_parameter) { +CtapGetAssertionRequest& CtapGetAssertionRequest::SetAppId(std::string app_id) { + app_id_ = std::move(app_id); alternative_application_parameter_ = - fido_parsing_utils::Materialize(alternative_application_parameter); + std::array<uint8_t, crypto::kSHA256Length>(); + crypto::SHA256HashString(*app_id_, alternative_application_parameter_->data(), + alternative_application_parameter_->size()); return *this; } bool CtapGetAssertionRequest::CheckResponseRpIdHash( const std::array<uint8_t, kRpIdHashLength>& response_rp_id_hash) { return response_rp_id_hash == fido_parsing_utils::CreateSHA256Hash(rp_id_) || - (alternative_application_parameter_ && - response_rp_id_hash == *alternative_application_parameter_); -} - -base::Optional<CtapGetAssertionRequest> ParseCtapGetAssertionRequest( - base::span<const uint8_t> request_bytes) { - const auto& cbor_request = cbor::CBORReader::Read(request_bytes); - if (!cbor_request || !cbor_request->is_map()) - return base::nullopt; - - const auto& request_map = cbor_request->GetMap(); - if (!AreGetAssertionRequestMapKeysCorrect(request_map)) - return base::nullopt; - - const auto rp_id_it = request_map.find(cbor::CBORValue(1)); - if (rp_id_it == request_map.end() || !rp_id_it->second.is_string()) - return base::nullopt; - - const auto client_data_hash_it = request_map.find(cbor::CBORValue(2)); - if (client_data_hash_it == request_map.end() || - !client_data_hash_it->second.is_bytestring()) - return base::nullopt; - - const auto client_data_hash = - base::make_span(client_data_hash_it->second.GetBytestring()) - .subspan<0, kClientDataHashLength>(); - CtapGetAssertionRequest request(rp_id_it->second.GetString(), - client_data_hash); - - const auto allow_list_it = request_map.find(cbor::CBORValue(3)); - if (allow_list_it != request_map.end()) { - if (!allow_list_it->second.is_array()) - return base::nullopt; - - const auto& credential_descriptors = allow_list_it->second.GetArray(); - std::vector<PublicKeyCredentialDescriptor> allow_list; - for (const auto& credential_descriptor : credential_descriptors) { - auto allowed_credential = - PublicKeyCredentialDescriptor::CreateFromCBORValue( - credential_descriptor); - if (!allowed_credential) - return base::nullopt; - - allow_list.push_back(std::move(*allowed_credential)); - } - request.SetAllowList(std::move(allow_list)); - } - - const auto option_it = request_map.find(cbor::CBORValue(5)); - if (option_it != request_map.end()) { - if (!option_it->second.is_map()) - return base::nullopt; - - const auto& option_map = option_it->second.GetMap(); - if (!IsGetAssertionOptionMapFormatCorrect(option_map)) - return base::nullopt; - - const auto user_presence_option = - option_map.find(cbor::CBORValue(kUserPresenceMapKey)); - if (user_presence_option != option_map.end()) - request.SetUserPresenceRequired(user_presence_option->second.GetBool()); - - const auto uv_option = - option_map.find(cbor::CBORValue(kUserVerificationMapKey)); - if (uv_option != option_map.end()) - request.SetUserVerification( - uv_option->second.GetBool() - ? UserVerificationRequirement::kRequired - : UserVerificationRequirement::kPreferred); - } - - const auto pin_auth_it = request_map.find(cbor::CBORValue(6)); - if (pin_auth_it != request_map.end()) { - if (!pin_auth_it->second.is_bytestring()) - return base::nullopt; - request.SetPinAuth(pin_auth_it->second.GetBytestring()); - } - - const auto pin_protocol_it = request_map.find(cbor::CBORValue(7)); - if (pin_protocol_it != request_map.end()) { - if (!pin_protocol_it->second.is_unsigned() || - pin_protocol_it->second.GetUnsigned() > - std::numeric_limits<uint8_t>::max()) - return base::nullopt; - request.SetPinProtocol(pin_auth_it->second.GetUnsigned()); - } - - return request; + (app_id_ && + response_rp_id_hash == *alternative_application_parameter()); } } // namespace device diff --git a/chromium/device/fido/ctap_get_assertion_request.h b/chromium/device/fido/ctap_get_assertion_request.h index 4264606d9bb..84982ee3858 100644 --- a/chromium/device/fido/ctap_get_assertion_request.h +++ b/chromium/device/fido/ctap_get_assertion_request.h @@ -7,6 +7,7 @@ #include <stdint.h> +#include <array> #include <string> #include <vector> @@ -14,6 +15,7 @@ #include "base/containers/span.h" #include "base/macros.h" #include "base/optional.h" +#include "crypto/sha2.h" #include "device/fido/cable/cable_discovery_data.h" #include "device/fido/fido_constants.h" #include "device/fido/public_key_credential_descriptor.h" @@ -25,9 +27,9 @@ namespace device { // https://fidoalliance.org/specs/fido-v2.0-rd-20161004/fido-client-to-authenticator-protocol-v2.0-rd-20161004.html#authenticatorgetassertion class COMPONENT_EXPORT(DEVICE_FIDO) CtapGetAssertionRequest { public: - CtapGetAssertionRequest( - std::string rp_id, - base::span<const uint8_t, kClientDataHashLength> client_data_hash); + using ClientDataHash = std::array<uint8_t, kClientDataHashLength>; + + CtapGetAssertionRequest(std::string rp_id, std::string client_data_json); CtapGetAssertionRequest(const CtapGetAssertionRequest& that); CtapGetAssertionRequest(CtapGetAssertionRequest&& that); CtapGetAssertionRequest& operator=(const CtapGetAssertionRequest& other); @@ -48,9 +50,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapGetAssertionRequest { CtapGetAssertionRequest& SetPinProtocol(uint8_t pin_protocol); CtapGetAssertionRequest& SetCableExtension( std::vector<CableDiscoveryData> cable_extension); - CtapGetAssertionRequest& SetAlternativeApplicationParameter( - base::span<const uint8_t, kRpIdHashLength> - alternative_application_parameter); + CtapGetAssertionRequest& SetAppId(std::string app_id); // Return true if the given RP ID hash from a response is valid for this // request. @@ -58,6 +58,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapGetAssertionRequest { const std::array<uint8_t, kRpIdHashLength>& response_rp_id_hash); const std::string& rp_id() const { return rp_id_; } + const std::string& client_data_json() const { return client_data_json_; } const std::array<uint8_t, kClientDataHashLength>& client_data_hash() const { return client_data_hash_; } @@ -85,9 +86,16 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapGetAssertionRequest { alternative_application_parameter() const { return alternative_application_parameter_; } + const base::Optional<std::string>& app_id() const { return app_id_; } + + bool is_incognito_mode() const { return is_incognito_mode_; } + void set_is_incognito_mode(bool is_incognito_mode) { + is_incognito_mode_ = is_incognito_mode; + } private: std::string rp_id_; + std::string client_data_json_; std::array<uint8_t, kClientDataHashLength> client_data_hash_; UserVerificationRequirement user_verification_ = UserVerificationRequirement::kPreferred; @@ -97,13 +105,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapGetAssertionRequest { base::Optional<std::vector<uint8_t>> pin_auth_; base::Optional<uint8_t> pin_protocol_; base::Optional<std::vector<CableDiscoveryData>> cable_extension_; - base::Optional<std::array<uint8_t, kRpIdHashLength>> + base::Optional<std::string> app_id_; + base::Optional<std::array<uint8_t, crypto::kSHA256Length>> alternative_application_parameter_; -}; -COMPONENT_EXPORT(DEVICE_FIDO) -base::Optional<CtapGetAssertionRequest> ParseCtapGetAssertionRequest( - base::span<const uint8_t> request_bytes); + bool is_incognito_mode_ = false; +}; } // namespace device diff --git a/chromium/device/fido/ctap_make_credential_request.cc b/chromium/device/fido/ctap_make_credential_request.cc index 5300df4a4f3..256aa28ccaf 100644 --- a/chromium/device/fido/ctap_make_credential_request.cc +++ b/chromium/device/fido/ctap_make_credential_request.cc @@ -9,48 +9,20 @@ #include <utility> #include "base/numerics/safe_conversions.h" -#include "components/cbor/cbor_reader.h" -#include "components/cbor/cbor_writer.h" +#include "components/cbor/writer.h" #include "device/fido/fido_constants.h" #include "device/fido/fido_parsing_utils.h" namespace device { -namespace { - -bool AreMakeCredentialRequestMapKeysCorrect( - const cbor::CBORValue::MapValue& request_map) { - return std::all_of(request_map.begin(), request_map.end(), - [](const auto& param) { - if (!param.first.is_integer()) - return false; - - const auto& key = param.first.GetInteger(); - return (key <= 9u && key >= 1u); - }); -} - -bool IsMakeCredentialOptionMapFormatCorrect( - const cbor::CBORValue::MapValue& option_map) { - return std::all_of( - option_map.begin(), option_map.end(), [](const auto& param) { - if (!param.first.is_string()) - return false; - - const auto& key = param.first.GetString(); - return ((key == kResidentKeyMapKey || key == kUserVerificationMapKey) && - param.second.is_bool()); - }); -} - -} // namespace - CtapMakeCredentialRequest::CtapMakeCredentialRequest( - base::span<const uint8_t, kClientDataHashLength> client_data_hash, + std::string client_data_json, PublicKeyCredentialRpEntity rp, PublicKeyCredentialUserEntity user, PublicKeyCredentialParams public_key_credential_params) - : client_data_hash_(fido_parsing_utils::Materialize(client_data_hash)), + : client_data_json_(std::move(client_data_json)), + client_data_hash_( + fido_parsing_utils::CreateSHA256Hash(client_data_json_)), rp_(std::move(rp)), user_(std::move(user)), public_key_credential_params_(std::move(public_key_credential_params)) {} @@ -70,47 +42,51 @@ CtapMakeCredentialRequest& CtapMakeCredentialRequest::operator=( CtapMakeCredentialRequest::~CtapMakeCredentialRequest() = default; std::vector<uint8_t> CtapMakeCredentialRequest::EncodeAsCBOR() const { - cbor::CBORValue::MapValue cbor_map; - cbor_map[cbor::CBORValue(1)] = cbor::CBORValue(client_data_hash_); - cbor_map[cbor::CBORValue(2)] = rp_.ConvertToCBOR(); - cbor_map[cbor::CBORValue(3)] = user_.ConvertToCBOR(); - cbor_map[cbor::CBORValue(4)] = public_key_credential_params_.ConvertToCBOR(); + cbor::Value::MapValue cbor_map; + cbor_map[cbor::Value(1)] = cbor::Value(client_data_hash_); + cbor_map[cbor::Value(2)] = rp_.ConvertToCBOR(); + cbor_map[cbor::Value(3)] = user_.ConvertToCBOR(); + cbor_map[cbor::Value(4)] = public_key_credential_params_.ConvertToCBOR(); if (exclude_list_) { - cbor::CBORValue::ArrayValue exclude_list_array; + cbor::Value::ArrayValue exclude_list_array; for (const auto& descriptor : *exclude_list_) { exclude_list_array.push_back(descriptor.ConvertToCBOR()); } - cbor_map[cbor::CBORValue(5)] = - cbor::CBORValue(std::move(exclude_list_array)); + cbor_map[cbor::Value(5)] = cbor::Value(std::move(exclude_list_array)); + } + + if (hmac_secret_) { + cbor::Value::MapValue extensions; + extensions[cbor::Value(kExtensionHmacSecret)] = cbor::Value(true); + cbor_map[cbor::Value(6)] = cbor::Value(std::move(extensions)); } + if (pin_auth_) { - cbor_map[cbor::CBORValue(8)] = cbor::CBORValue(*pin_auth_); + cbor_map[cbor::Value(8)] = cbor::Value(*pin_auth_); } if (pin_protocol_) { - cbor_map[cbor::CBORValue(9)] = cbor::CBORValue(*pin_protocol_); + cbor_map[cbor::Value(9)] = cbor::Value(*pin_protocol_); } - cbor::CBORValue::MapValue option_map; + cbor::Value::MapValue option_map; - // Resident keys are not supported by default. - if (resident_key_supported_) { - option_map[cbor::CBORValue(kResidentKeyMapKey)] = - cbor::CBORValue(resident_key_supported_); + // Resident keys are not required by default. + if (resident_key_required_) { + option_map[cbor::Value(kResidentKeyMapKey)] = + cbor::Value(resident_key_required_); } // User verification is not required by default. - if (user_verification_required_) { - option_map[cbor::CBORValue(kUserVerificationMapKey)] = - cbor::CBORValue(user_verification_required_); + if (user_verification_ == UserVerificationRequirement::kRequired) { + option_map[cbor::Value(kUserVerificationMapKey)] = cbor::Value(true); } if (!option_map.empty()) { - cbor_map[cbor::CBORValue(7)] = cbor::CBORValue(std::move(option_map)); + cbor_map[cbor::Value(7)] = cbor::Value(std::move(option_map)); } - auto serialized_param = - cbor::CBORWriter::Write(cbor::CBORValue(std::move(cbor_map))); + auto serialized_param = cbor::Writer::Write(cbor::Value(std::move(cbor_map))); DCHECK(serialized_param); std::vector<uint8_t> cbor_request({base::strict_cast<uint8_t>( @@ -121,15 +97,21 @@ std::vector<uint8_t> CtapMakeCredentialRequest::EncodeAsCBOR() const { } CtapMakeCredentialRequest& -CtapMakeCredentialRequest::SetUserVerificationRequired( - bool user_verification_required) { - user_verification_required_ = user_verification_required; +CtapMakeCredentialRequest::SetAuthenticatorAttachment( + AuthenticatorAttachment authenticator_attachment) { + authenticator_attachment_ = authenticator_attachment; return *this; } -CtapMakeCredentialRequest& CtapMakeCredentialRequest::SetResidentKeySupported( - bool resident_key_supported) { - resident_key_supported_ = resident_key_supported; +CtapMakeCredentialRequest& CtapMakeCredentialRequest::SetUserVerification( + UserVerificationRequirement user_verification) { + user_verification_ = user_verification; + return *this; +} + +CtapMakeCredentialRequest& CtapMakeCredentialRequest::SetResidentKeyRequired( + bool resident_key_required) { + resident_key_required_ = resident_key_required; return *this; } @@ -158,112 +140,10 @@ CtapMakeCredentialRequest::SetIsIndividualAttestation( return *this; } -base::Optional<CtapMakeCredentialRequest> ParseCtapMakeCredentialRequest( - base::span<const uint8_t> request_bytes) { - const auto& cbor_request = cbor::CBORReader::Read(request_bytes); - if (!cbor_request || !cbor_request->is_map()) - return base::nullopt; - - const auto& request_map = cbor_request->GetMap(); - if (!AreMakeCredentialRequestMapKeysCorrect(request_map)) - return base::nullopt; - - const auto client_data_hash_it = request_map.find(cbor::CBORValue(1)); - if (client_data_hash_it == request_map.end() || - !client_data_hash_it->second.is_bytestring()) - return base::nullopt; - - const auto client_data_hash = - base::make_span(client_data_hash_it->second.GetBytestring()) - .subspan<0, kClientDataHashLength>(); - - const auto rp_entity_it = request_map.find(cbor::CBORValue(2)); - if (rp_entity_it == request_map.end() || !rp_entity_it->second.is_map()) - return base::nullopt; - - auto rp_entity = - PublicKeyCredentialRpEntity::CreateFromCBORValue(rp_entity_it->second); - if (!rp_entity) - return base::nullopt; - - const auto user_entity_it = request_map.find(cbor::CBORValue(3)); - if (user_entity_it == request_map.end() || !user_entity_it->second.is_map()) - return base::nullopt; - - auto user_entity = PublicKeyCredentialUserEntity::CreateFromCBORValue( - user_entity_it->second); - if (!user_entity) - return base::nullopt; - - const auto credential_params_it = request_map.find(cbor::CBORValue(4)); - if (credential_params_it == request_map.end()) - return base::nullopt; - - auto credential_params = PublicKeyCredentialParams::CreateFromCBORValue( - credential_params_it->second); - if (!credential_params) - return base::nullopt; - - CtapMakeCredentialRequest request(client_data_hash, std::move(*rp_entity), - std::move(*user_entity), - std::move(*credential_params)); - - const auto exclude_list_it = request_map.find(cbor::CBORValue(5)); - if (exclude_list_it != request_map.end()) { - if (!exclude_list_it->second.is_array()) - return base::nullopt; - - const auto& credential_descriptors = exclude_list_it->second.GetArray(); - std::vector<PublicKeyCredentialDescriptor> exclude_list; - for (const auto& credential_descriptor : credential_descriptors) { - auto excluded_credential = - PublicKeyCredentialDescriptor::CreateFromCBORValue( - credential_descriptor); - if (!excluded_credential) - return base::nullopt; - - exclude_list.push_back(std::move(*excluded_credential)); - } - request.SetExcludeList(std::move(exclude_list)); - } - - const auto option_it = request_map.find(cbor::CBORValue(7)); - if (option_it != request_map.end()) { - if (!option_it->second.is_map()) - return base::nullopt; - - const auto& option_map = option_it->second.GetMap(); - if (!IsMakeCredentialOptionMapFormatCorrect(option_map)) - return base::nullopt; - - const auto resident_key_option = - option_map.find(cbor::CBORValue(kResidentKeyMapKey)); - if (resident_key_option != option_map.end()) - request.SetResidentKeySupported(resident_key_option->second.GetBool()); - - const auto uv_option = - option_map.find(cbor::CBORValue(kUserVerificationMapKey)); - if (uv_option != option_map.end()) - request.SetUserVerificationRequired(uv_option->second.GetBool()); - } - - const auto pin_auth_it = request_map.find(cbor::CBORValue(8)); - if (pin_auth_it != request_map.end()) { - if (!pin_auth_it->second.is_bytestring()) - return base::nullopt; - request.SetPinAuth(pin_auth_it->second.GetBytestring()); - } - - const auto pin_protocol_it = request_map.find(cbor::CBORValue(9)); - if (pin_protocol_it != request_map.end()) { - if (!pin_protocol_it->second.is_unsigned() || - pin_protocol_it->second.GetUnsigned() > - std::numeric_limits<uint8_t>::max()) - return base::nullopt; - request.SetPinProtocol(pin_auth_it->second.GetUnsigned()); - } - - return request; +CtapMakeCredentialRequest& CtapMakeCredentialRequest::SetHmacSecret( + bool hmac_secret) { + hmac_secret_ = hmac_secret; + return *this; } } // namespace device diff --git a/chromium/device/fido/ctap_make_credential_request.h b/chromium/device/fido/ctap_make_credential_request.h index 3b434915529..d639b0ada8f 100644 --- a/chromium/device/fido/ctap_make_credential_request.h +++ b/chromium/device/fido/ctap_make_credential_request.h @@ -15,6 +15,7 @@ #include "base/containers/span.h" #include "base/macros.h" #include "base/optional.h" +#include "device/fido/fido_constants.h" #include "device/fido/public_key_credential_descriptor.h" #include "device/fido/public_key_credential_params.h" #include "device/fido/public_key_credential_rp_entity.h" @@ -27,8 +28,10 @@ namespace device { // https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html class COMPONENT_EXPORT(DEVICE_FIDO) CtapMakeCredentialRequest { public: + using ClientDataHash = std::array<uint8_t, kClientDataHashLength>; + CtapMakeCredentialRequest( - base::span<const uint8_t, kClientDataHashLength> client_data_hash, + std::string client_data_json, PublicKeyCredentialRpEntity rp, PublicKeyCredentialUserEntity user, PublicKeyCredentialParams public_key_credential_params); @@ -43,16 +46,20 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapMakeCredentialRequest { // https://drafts.fidoalliance.org/fido-2/latest/fido-client-to-authenticator-protocol-v2.0-wd-20180305.html#authenticatorMakeCredential std::vector<uint8_t> EncodeAsCBOR() const; - CtapMakeCredentialRequest& SetUserVerificationRequired( - bool user_verfication_required); - CtapMakeCredentialRequest& SetResidentKeySupported(bool resident_key); + CtapMakeCredentialRequest& SetAuthenticatorAttachment( + AuthenticatorAttachment authenticator_attachment); + CtapMakeCredentialRequest& SetUserVerification( + UserVerificationRequirement user_verification); + CtapMakeCredentialRequest& SetResidentKeyRequired(bool resident_key); CtapMakeCredentialRequest& SetExcludeList( std::vector<PublicKeyCredentialDescriptor> exclude_list); CtapMakeCredentialRequest& SetPinAuth(std::vector<uint8_t> pin_auth); CtapMakeCredentialRequest& SetPinProtocol(uint8_t pin_protocol); CtapMakeCredentialRequest& SetIsIndividualAttestation( bool is_individual_attestation); + CtapMakeCredentialRequest& SetHmacSecret(bool hmac_secret); + const std::string& client_data_json() const { return client_data_json_; } const std::array<uint8_t, kClientDataHashLength>& client_data_hash() const { return client_data_hash_; } @@ -61,11 +68,15 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapMakeCredentialRequest { const PublicKeyCredentialParams& public_key_credential_params() const { return public_key_credential_params_; } - bool user_verification_required() const { - return user_verification_required_; + UserVerificationRequirement user_verification() const { + return user_verification_; + } + AuthenticatorAttachment authenticator_attachment() const { + return authenticator_attachment_; } - bool resident_key_supported() const { return resident_key_supported_; } + bool resident_key_required() const { return resident_key_required_; } bool is_individual_attestation() const { return is_individual_attestation_; } + bool hmac_secret() const { return hmac_secret_; } const base::Optional<std::vector<PublicKeyCredentialDescriptor>>& exclude_list() const { return exclude_list_; @@ -74,24 +85,40 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapMakeCredentialRequest { return pin_auth_; } + void set_is_u2f_only(bool is_u2f_only) { is_u2f_only_ = is_u2f_only; } + bool is_u2f_only() { return is_u2f_only_; } + + bool is_incognito_mode() const { return is_incognito_mode_; } + void set_is_incognito_mode(bool is_incognito_mode) { + is_incognito_mode_ = is_incognito_mode; + } + private: + std::string client_data_json_; std::array<uint8_t, kClientDataHashLength> client_data_hash_; PublicKeyCredentialRpEntity rp_; PublicKeyCredentialUserEntity user_; PublicKeyCredentialParams public_key_credential_params_; - bool user_verification_required_ = false; - bool resident_key_supported_ = false; + UserVerificationRequirement user_verification_ = + UserVerificationRequirement::kPreferred; + AuthenticatorAttachment authenticator_attachment_ = + AuthenticatorAttachment::kAny; + bool resident_key_required_ = false; bool is_individual_attestation_ = false; + // hmac_secret_ indicates whether the "hmac-secret" extension should be + // asserted to CTAP2 authenticators. + bool hmac_secret_ = false; + + // If true, instruct the request handler only to dispatch this request via + // U2F. + bool is_u2f_only_ = false; + bool is_incognito_mode_ = false; base::Optional<std::vector<PublicKeyCredentialDescriptor>> exclude_list_; base::Optional<std::vector<uint8_t>> pin_auth_; base::Optional<uint8_t> pin_protocol_; }; -COMPONENT_EXPORT(DEVICE_FIDO) -base::Optional<CtapMakeCredentialRequest> ParseCtapMakeCredentialRequest( - base::span<const uint8_t> request_bytes); - } // namespace device #endif // DEVICE_FIDO_CTAP_MAKE_CREDENTIAL_REQUEST_H_ diff --git a/chromium/device/fido/ctap_request_unittest.cc b/chromium/device/fido/ctap_request_unittest.cc index 2599eca7c9b..83a0c13c0b6 100644 --- a/chromium/device/fido/ctap_request_unittest.cc +++ b/chromium/device/fido/ctap_request_unittest.cc @@ -8,6 +8,7 @@ #include "device/fido/fido_constants.h" #include "device/fido/fido_parsing_utils.h" #include "device/fido/fido_test_data.h" +#include "device/fido/virtual_ctap2_device.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -26,19 +27,20 @@ TEST(CTAPRequestTest, TestConstructMakeCredentialRequestParam) { .SetIconUrl(GURL("https://pics.acme.com/00/p/aBjjjpqPb.png")); CtapMakeCredentialRequest make_credential_param( - test_data::kClientDataHash, std::move(rp), std::move(user), + test_data::kClientDataJson, std::move(rp), std::move(user), PublicKeyCredentialParams({{CredentialType::kPublicKey, 7}, {CredentialType::kPublicKey, 257}})); - auto serialized_data = make_credential_param.SetResidentKeySupported(true) - .SetUserVerificationRequired(true) - .EncodeAsCBOR(); + auto serialized_data = + make_credential_param.SetResidentKeyRequired(true) + .SetUserVerification(UserVerificationRequirement::kRequired) + .EncodeAsCBOR(); EXPECT_THAT(serialized_data, ::testing::ElementsAreArray( test_data::kCtapMakeCredentialRequest)); } TEST(CTAPRequestTest, TestConstructGetAssertionRequest) { CtapGetAssertionRequest get_assertion_req("acme.com", - test_data::kClientDataHash); + test_data::kClientDataJson); std::vector<PublicKeyCredentialDescriptor> allowed_list; allowed_list.push_back(PublicKeyCredentialDescriptor( @@ -80,39 +82,42 @@ TEST(CTAPRequestTest, TestConstructCtapAuthenticatorRequestParam) { ::testing::ElementsAre(kSerializedResetCmd)); } -TEST(CTAPRequestTest, ParseMakeCredentialRequestForVirtualCtapKey) { - const auto request = ParseCtapMakeCredentialRequest( +TEST(VirtualCtap2DeviceTest, ParseMakeCredentialRequestForVirtualCtapKey) { + const auto request_and_hash = ParseCtapMakeCredentialRequest( base::make_span(test_data::kCtapMakeCredentialRequest).subspan(1)); - ASSERT_TRUE(request); - EXPECT_THAT(request->client_data_hash(), + ASSERT_TRUE(request_and_hash); + auto request = std::get<0>(*request_and_hash); + auto client_data_hash = std::get<1>(*request_and_hash); + EXPECT_THAT(client_data_hash, ::testing::ElementsAreArray(test_data::kClientDataHash)); - EXPECT_EQ(test_data::kRelyingPartyId, request->rp().rp_id()); - EXPECT_EQ("Acme", request->rp().rp_name()); - EXPECT_THAT(request->user().user_id(), + EXPECT_EQ(test_data::kRelyingPartyId, request.rp().rp_id()); + EXPECT_EQ("Acme", request.rp().rp_name()); + EXPECT_THAT(request.user().user_id(), ::testing::ElementsAreArray(test_data::kUserId)); - ASSERT_TRUE(request->user().user_name()); - EXPECT_EQ("johnpsmith@example.com", *request->user().user_name()); - ASSERT_TRUE(request->user().user_display_name()); - EXPECT_EQ("John P. Smith", *request->user().user_display_name()); - ASSERT_TRUE(request->user().user_icon_url()); + ASSERT_TRUE(request.user().user_name()); + EXPECT_EQ("johnpsmith@example.com", *request.user().user_name()); + ASSERT_TRUE(request.user().user_display_name()); + EXPECT_EQ("John P. Smith", *request.user().user_display_name()); + ASSERT_TRUE(request.user().user_icon_url()); EXPECT_EQ("https://pics.acme.com/00/p/aBjjjpqPb.png", - request->user().user_icon_url()->spec()); - ASSERT_EQ(2u, request->public_key_credential_params() + request.user().user_icon_url()->spec()); + ASSERT_EQ(2u, request.public_key_credential_params() .public_key_credential_params() .size()); - EXPECT_EQ(7, request->public_key_credential_params() + EXPECT_EQ(7, request.public_key_credential_params() .public_key_credential_params() .at(0) .algorithm); - EXPECT_EQ(257, request->public_key_credential_params() + EXPECT_EQ(257, request.public_key_credential_params() .public_key_credential_params() .at(1) .algorithm); - EXPECT_TRUE(request->user_verification_required()); - EXPECT_TRUE(request->resident_key_supported()); + EXPECT_EQ(UserVerificationRequirement::kRequired, + request.user_verification()); + EXPECT_TRUE(request.resident_key_required()); } -TEST(CTAPRequestTest, ParseGetAssertionRequestForVirtualCtapKey) { +TEST(VirtualCtap2DeviceTest, ParseGetAssertionRequestForVirtualCtapKey) { constexpr uint8_t kAllowedCredentialOne[] = { 0xf2, 0x20, 0x06, 0xde, 0x4f, 0x90, 0x5a, 0xf6, 0x8a, 0x43, 0x94, 0x2f, 0x02, 0x4f, 0x2a, 0x5e, 0xce, 0x60, 0x3d, 0x9c, 0x6d, 0x4b, @@ -127,22 +132,24 @@ TEST(CTAPRequestTest, ParseGetAssertionRequestForVirtualCtapKey) { 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}; - const auto request = ParseCtapGetAssertionRequest( + auto request_and_hash = ParseCtapGetAssertionRequest( base::make_span(test_data::kTestComplexCtapGetAssertionRequest) .subspan(1)); - ASSERT_TRUE(request); - EXPECT_THAT(request->client_data_hash(), + ASSERT_TRUE(request_and_hash); + auto request = std::get<0>(*request_and_hash); + auto client_data_hash = std::get<1>(*request_and_hash); + EXPECT_THAT(client_data_hash, ::testing::ElementsAreArray(test_data::kClientDataHash)); - EXPECT_EQ(test_data::kRelyingPartyId, request->rp_id()); + EXPECT_EQ(test_data::kRelyingPartyId, request.rp_id()); EXPECT_EQ(UserVerificationRequirement::kRequired, - request->user_verification()); - EXPECT_FALSE(request->user_presence_required()); - ASSERT_TRUE(request->allow_list()); - ASSERT_EQ(2u, request->allow_list()->size()); + request.user_verification()); + EXPECT_FALSE(request.user_presence_required()); + ASSERT_TRUE(request.allow_list()); + ASSERT_EQ(2u, request.allow_list()->size()); - EXPECT_THAT(request->allow_list()->at(0).id(), + EXPECT_THAT(request.allow_list()->at(0).id(), ::testing::ElementsAreArray(kAllowedCredentialOne)); - EXPECT_THAT(request->allow_list()->at(1).id(), + EXPECT_THAT(request.allow_list()->at(1).id(), ::testing::ElementsAreArray(kAllowedCredentialTwo)); } } // namespace device diff --git a/chromium/device/fido/ctap_response_fuzzer.cc b/chromium/device/fido/ctap_response_fuzzer.cc index 4f251e6795c..c3abf101dde 100644 --- a/chromium/device/fido/ctap_response_fuzzer.cc +++ b/chromium/device/fido/ctap_response_fuzzer.cc @@ -35,14 +35,14 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { auto response = device::ReadCTAPMakeCredentialResponse( FidoTransportProtocol::kUsbHumanInterfaceDevice, input); if (response) - response->EraseAttestationStatement(); + response->EraseAttestationStatement(AttestationObject::AAGUID::kErase); response = device::AuthenticatorMakeCredentialResponse:: CreateFromU2fRegisterResponse( FidoTransportProtocol::kUsbHumanInterfaceDevice, relying_party_id_hash, input); if (response) - response->EraseAttestationStatement(); + response->EraseAttestationStatement(AttestationObject::AAGUID::kErase); device::ReadCTAPGetAssertionResponse(input); std::vector<uint8_t> u2f_response_data(data, data + size); diff --git a/chromium/device/fido/ctap_response_unittest.cc b/chromium/device/fido/ctap_response_unittest.cc index a201c75d6ab..609965c945e 100644 --- a/chromium/device/fido/ctap_response_unittest.cc +++ b/chromium/device/fido/ctap_response_unittest.cc @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/cbor/cbor_reader.h" -#include "components/cbor/cbor_values.h" -#include "components/cbor/cbor_writer.h" +#include "components/cbor/reader.h" +#include "components/cbor/values.h" +#include "components/cbor/writer.h" #include "device/fido/attestation_statement_formats.h" #include "device/fido/authenticator_get_assertion_response.h" #include "device/fido/authenticator_make_credential_response.h" @@ -318,43 +318,43 @@ TEST(CTAPResponseTest, TestReadMakeCredentialResponse) { FidoTransportProtocol::kUsbHumanInterfaceDevice, test_data::kTestMakeCredentialResponse); ASSERT_TRUE(make_credential_response); - auto cbor_attestation_object = cbor::CBORReader::Read( + auto cbor_attestation_object = cbor::Reader::Read( make_credential_response->GetCBOREncodedAttestationObject()); ASSERT_TRUE(cbor_attestation_object); ASSERT_TRUE(cbor_attestation_object->is_map()); const auto& attestation_object_map = cbor_attestation_object->GetMap(); - auto it = attestation_object_map.find(cbor::CBORValue(kFormatKey)); + auto it = attestation_object_map.find(cbor::Value(kFormatKey)); ASSERT_TRUE(it != attestation_object_map.end()); ASSERT_TRUE(it->second.is_string()); EXPECT_EQ(it->second.GetString(), "packed"); - it = attestation_object_map.find(cbor::CBORValue(kAuthDataKey)); + it = attestation_object_map.find(cbor::Value(kAuthDataKey)); ASSERT_TRUE(it != attestation_object_map.end()); ASSERT_TRUE(it->second.is_bytestring()); EXPECT_THAT( it->second.GetBytestring(), ::testing::ElementsAreArray(test_data::kCtap2MakeCredentialAuthData)); - it = attestation_object_map.find(cbor::CBORValue(kAttestationStatementKey)); + it = attestation_object_map.find(cbor::Value(kAttestationStatementKey)); ASSERT_TRUE(it != attestation_object_map.end()); ASSERT_TRUE(it->second.is_map()); const auto& attestation_statement_map = it->second.GetMap(); - auto attStmt_it = attestation_statement_map.find(cbor::CBORValue("alg")); + auto attStmt_it = attestation_statement_map.find(cbor::Value("alg")); ASSERT_TRUE(attStmt_it != attestation_statement_map.end()); ASSERT_TRUE(attStmt_it->second.is_integer()); EXPECT_EQ(attStmt_it->second.GetInteger(), -7); - attStmt_it = attestation_statement_map.find(cbor::CBORValue("sig")); + attStmt_it = attestation_statement_map.find(cbor::Value("sig")); ASSERT_TRUE(attStmt_it != attestation_statement_map.end()); ASSERT_TRUE(attStmt_it->second.is_bytestring()); EXPECT_THAT( attStmt_it->second.GetBytestring(), ::testing::ElementsAreArray(test_data::kCtap2MakeCredentialSignature)); - attStmt_it = attestation_statement_map.find(cbor::CBORValue("x5c")); + attStmt_it = attestation_statement_map.find(cbor::Value("x5c")); ASSERT_TRUE(attStmt_it != attestation_statement_map.end()); const auto& certificate = attStmt_it->second; ASSERT_TRUE(certificate.is_array()); @@ -373,7 +373,8 @@ TEST(CTAPResponseTest, TestMakeCredentialNoneAttestationResponse) { FidoTransportProtocol::kUsbHumanInterfaceDevice, test_data::kTestMakeCredentialResponse); ASSERT_TRUE(make_credential_response); - make_credential_response->EraseAttestationStatement(); + make_credential_response->EraseAttestationStatement( + AttestationObject::AAGUID::kErase); EXPECT_THAT(make_credential_response->GetCBOREncodedAttestationObject(), ::testing::ElementsAreArray(test_data::kNoneAttestationResponse)); } @@ -425,8 +426,8 @@ TEST(CTAPResponseTest, TestParseU2fAttestationStatementCBOR) { FidoAttestationStatement::CreateFromU2fRegisterResponse( test_data::kTestU2fRegisterResponse); ASSERT_TRUE(fido_attestation_statement); - auto cbor = cbor::CBORWriter::Write( - cbor::CBORValue(fido_attestation_statement->GetAsCBORMap())); + auto cbor = cbor::Writer::Write( + cbor::Value(fido_attestation_statement->GetAsCBORMap())); ASSERT_TRUE(cbor); EXPECT_THAT(*cbor, ::testing::ElementsAreArray( test_data::kU2fAttestationStatementCBOR)); @@ -529,6 +530,21 @@ TEST(CTAPResponseTest, TestParseU2fSignWithNullResponse) { EXPECT_FALSE(response); } +TEST(CTAPResponseTest, TestParseU2fSignWithCTAP2Flags) { + std::vector<uint8_t> sign_response = GetTestSignResponse(); + // Set two flags that should only be set in CTAP2 responses and expect parsing + // to fail. + sign_response[0] |= + static_cast<uint8_t>(AuthenticatorData::Flag::kExtensionDataIncluded); + sign_response[0] |= + static_cast<uint8_t>(AuthenticatorData::Flag::kAttestation); + + auto response = AuthenticatorGetAssertionResponse::CreateFromU2fSignResponse( + test_data::kApplicationParameter, sign_response, + GetTestCredentialRawIdBytes()); + EXPECT_FALSE(response); +} + TEST(CTAPResponseTest, TestParseU2fSignWithNullCorruptedCounter) { // A sign response of less than 5 bytes. auto response = AuthenticatorGetAssertionResponse::CreateFromU2fSignResponse( @@ -600,13 +616,24 @@ TEST(CTAPResponseTest, TestSerializeGetInfoResponse) { TEST(CTAPResponseTest, TestSerializeMakeCredentialResponse) { constexpr uint8_t kCoseEncodedPublicKey[] = { - 0xa3, 0x63, 0x61, 0x6c, 0x67, 0x65, 0x45, 0x53, 0x32, 0x35, 0x36, 0x61, - 0x78, 0x58, 0x20, 0xf7, 0xc4, 0xf4, 0xa6, 0xf1, 0xd7, 0x95, 0x38, 0xdf, - 0xa4, 0xc9, 0xac, 0x50, 0x84, 0x8d, 0xf7, 0x08, 0xbc, 0x1c, 0x99, 0xf5, - 0xe6, 0x0e, 0x51, 0xb4, 0x2a, 0x52, 0x1b, 0x35, 0xd3, 0xb6, 0x9a, 0x61, - 0x79, 0x58, 0x20, 0xde, 0x7b, 0x7d, 0x6c, 0xa5, 0x64, 0xe7, 0x0e, 0xa3, - 0x21, 0xa4, 0xd5, 0xd9, 0x6e, 0xa0, 0x0e, 0xf0, 0xe2, 0xdb, 0x89, 0xdd, - 0x61, 0xd4, 0x89, 0x4c, 0x15, 0xac, 0x58, 0x5b, 0xd2, 0x36, 0x84, + // map(3) + 0xa3, + // "x" + 0x61, 0x78, + // byte(32) + 0x58, 0x20, 0xf7, 0xc4, 0xf4, 0xa6, 0xf1, 0xd7, 0x95, 0x38, 0xdf, 0xa4, + 0xc9, 0xac, 0x50, 0x84, 0x8d, 0xf7, 0x08, 0xbc, 0x1c, 0x99, 0xf5, 0xe6, + 0x0e, 0x51, 0xb4, 0x2a, 0x52, 0x1b, 0x35, 0xd3, 0xb6, 0x9a, + // "y" + 0x61, 0x79, + // byte(32) + 0x58, 0x20, 0xde, 0x7b, 0x7d, 0x6c, 0xa5, 0x64, 0xe7, 0x0e, 0xa3, 0x21, + 0xa4, 0xd5, 0xd9, 0x6e, 0xa0, 0x0e, 0xf0, 0xe2, 0xdb, 0x89, 0xdd, 0x61, + 0xd4, 0x89, 0x4c, 0x15, 0xac, 0x58, 0x5b, 0xd2, 0x36, 0x84, + // "fmt" + 0x63, 0x61, 0x6c, 0x67, + // "ES256" + 0x65, 0x45, 0x53, 0x32, 0x35, 0x36, }; const auto application_parameter = @@ -631,11 +658,11 @@ TEST(CTAPResponseTest, TestSerializeMakeCredentialResponse) { signature_counter, std::move(attested_credential_data)); - cbor::CBORValue::MapValue attestation_map; + cbor::Value::MapValue attestation_map; attestation_map.emplace("alg", -7); attestation_map.emplace("sig", fido_parsing_utils::Materialize( test_data::kCtap2MakeCredentialSignature)); - cbor::CBORValue::ArrayValue certificate_chain; + cbor::Value::ArrayValue certificate_chain; certificate_chain.emplace_back(fido_parsing_utils::Materialize( test_data::kCtap2MakeCredentialCertificate)); attestation_map.emplace("x5c", std::move(certificate_chain)); @@ -644,7 +671,7 @@ TEST(CTAPResponseTest, TestSerializeMakeCredentialResponse) { AttestationObject( std::move(authenticator_data), std::make_unique<OpaqueAttestationStatement>( - "packed", cbor::CBORValue(std::move(attestation_map))))); + "packed", cbor::Value(std::move(attestation_map))))); EXPECT_THAT( GetSerializedCtapDeviceResponse(response), ::testing::ElementsAreArray( diff --git a/chromium/device/fido/device_response_converter.cc b/chromium/device/fido/device_response_converter.cc index 4cfe62b173a..a31f7669639 100644 --- a/chromium/device/fido/device_response_converter.cc +++ b/chromium/device/fido/device_response_converter.cc @@ -12,8 +12,8 @@ #include "base/numerics/safe_conversions.h" #include "base/optional.h" #include "base/stl_util.h" -#include "components/cbor/cbor_reader.h" -#include "components/cbor/cbor_writer.h" +#include "components/cbor/reader.h" +#include "components/cbor/writer.h" #include "device/fido/authenticator_data.h" #include "device/fido/authenticator_supported_options.h" #include "device/fido/fido_constants.h" @@ -36,7 +36,7 @@ ProtocolVersion ConvertStringToProtocolVersion(base::StringPiece version) { } // namespace -using CBOR = cbor::CBORValue; +using CBOR = cbor::Value; CtapDeviceResponseCode GetResponseCode(base::span<const uint8_t> buffer) { if (buffer.empty()) @@ -56,8 +56,7 @@ ReadCTAPMakeCredentialResponse(FidoTransportProtocol transport_used, if (buffer.size() <= kResponseCodeLength) return base::nullopt; - base::Optional<CBOR> decoded_response = - cbor::CBORReader::Read(buffer.subspan(1)); + base::Optional<CBOR> decoded_response = cbor::Reader::Read(buffer.subspan(1)); if (!decoded_response || !decoded_response->is_map()) return base::nullopt; @@ -92,8 +91,7 @@ base::Optional<AuthenticatorGetAssertionResponse> ReadCTAPGetAssertionResponse( if (buffer.size() <= kResponseCodeLength) return base::nullopt; - base::Optional<CBOR> decoded_response = - cbor::CBORReader::Read(buffer.subspan(1)); + base::Optional<CBOR> decoded_response = cbor::Reader::Read(buffer.subspan(1)); if (!decoded_response || !decoded_response->is_map()) return base::nullopt; @@ -151,8 +149,7 @@ base::Optional<AuthenticatorGetInfoResponse> ReadCTAPGetInfoResponse( GetResponseCode(buffer) != CtapDeviceResponseCode::kSuccess) return base::nullopt; - base::Optional<CBOR> decoded_response = - cbor::CBORReader::Read(buffer.subspan(1)); + base::Optional<CBOR> decoded_response = cbor::Reader::Read(buffer.subspan(1)); if (!decoded_response || !decoded_response->is_map()) return base::nullopt; diff --git a/chromium/device/fido/ec_public_key.cc b/chromium/device/fido/ec_public_key.cc index effded9a490..207ae333a4b 100644 --- a/chromium/device/fido/ec_public_key.cc +++ b/chromium/device/fido/ec_public_key.cc @@ -6,7 +6,7 @@ #include <utility> -#include "components/cbor/cbor_writer.h" +#include "components/cbor/writer.h" #include "device/fido/fido_parsing_utils.h" namespace device { @@ -65,13 +65,13 @@ ECPublicKey::ECPublicKey(std::string algorithm, ECPublicKey::~ECPublicKey() = default; std::vector<uint8_t> ECPublicKey::EncodeAsCOSEKey() const { - cbor::CBORValue::MapValue map; - map[cbor::CBORValue(1)] = cbor::CBORValue(2); - map[cbor::CBORValue(3)] = cbor::CBORValue(-7); - map[cbor::CBORValue(-1)] = cbor::CBORValue(1); - map[cbor::CBORValue(-2)] = cbor::CBORValue(x_coordinate_); - map[cbor::CBORValue(-3)] = cbor::CBORValue(y_coordinate_); - return *cbor::CBORWriter::Write(cbor::CBORValue(std::move(map))); + cbor::Value::MapValue map; + map[cbor::Value(1)] = cbor::Value(2); + map[cbor::Value(3)] = cbor::Value(-7); + map[cbor::Value(-1)] = cbor::Value(1); + map[cbor::Value(-2)] = cbor::Value(x_coordinate_); + map[cbor::Value(-3)] = cbor::Value(y_coordinate_); + return *cbor::Writer::Write(cbor::Value(std::move(map))); } } // namespace device diff --git a/chromium/device/fido/fake_fido_discovery.cc b/chromium/device/fido/fake_fido_discovery.cc index 7e49f9a4a90..66b8fe4e4da 100644 --- a/chromium/device/fido/fake_fido_discovery.cc +++ b/chromium/device/fido/fake_fido_discovery.cc @@ -80,7 +80,7 @@ FakeFidoDiscovery* ScopedFakeFidoDiscoveryFactory::ForgeNextCableDiscovery( return next_cable_discovery_.get(); } -std::unique_ptr<FidoDeviceDiscovery> +std::unique_ptr<FidoDiscoveryBase> ScopedFakeFidoDiscoveryFactory::CreateFidoDiscovery( FidoTransportProtocol transport, ::service_manager::Connector* connector) { diff --git a/chromium/device/fido/fake_fido_discovery.h b/chromium/device/fido/fake_fido_discovery.h index 57a65b7b555..e83c91a7d99 100644 --- a/chromium/device/fido/fake_fido_discovery.h +++ b/chromium/device/fido/fake_fido_discovery.h @@ -11,7 +11,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/run_loop.h" -#include "device/fido/fido_device_discovery.h" +#include "device/fido/fido_discovery_factory.h" #include "device/fido/fido_transport_protocol.h" namespace service_manager { @@ -117,7 +117,7 @@ class ScopedFakeFidoDiscoveryFactory StartMode mode = StartMode::kManual); protected: - std::unique_ptr<FidoDeviceDiscovery> CreateFidoDiscovery( + std::unique_ptr<FidoDiscoveryBase> CreateFidoDiscovery( FidoTransportProtocol transport, ::service_manager::Connector* connector) override; diff --git a/chromium/device/fido/fake_fido_discovery_unittest.cc b/chromium/device/fido/fake_fido_discovery_unittest.cc index 407d710a3d4..a17c20aea99 100644 --- a/chromium/device/fido/fake_fido_discovery_unittest.cc +++ b/chromium/device/fido/fake_fido_discovery_unittest.cc @@ -11,6 +11,7 @@ #include "base/test/scoped_task_environment.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" +#include "device/fido/fido_discovery_factory.h" #include "device/fido/fido_test_data.h" #include "device/fido/mock_fido_device.h" #include "device/fido/mock_fido_discovery_observer.h" @@ -150,7 +151,7 @@ TEST_F(ScopedFakeFidoDiscoveryFactoryTest, auto* injected_fake_discovery = factory.ForgeNextHidDiscovery(); ASSERT_EQ(FidoTransportProtocol::kUsbHumanInterfaceDevice, injected_fake_discovery->transport()); - auto produced_discovery = FidoDeviceDiscovery::Create( + auto produced_discovery = FidoDiscoveryFactory::Create( FidoTransportProtocol::kUsbHumanInterfaceDevice, nullptr); EXPECT_TRUE(produced_discovery); EXPECT_EQ(injected_fake_discovery, produced_discovery.get()); @@ -164,14 +165,14 @@ TEST_F(ScopedFakeFidoDiscoveryFactoryTest, auto* injected_fake_discovery_1 = factory.ForgeNextBleDiscovery(); ASSERT_EQ(FidoTransportProtocol::kBluetoothLowEnergy, injected_fake_discovery_1->transport()); - auto produced_discovery_1 = FidoDeviceDiscovery::Create( + auto produced_discovery_1 = FidoDiscoveryFactory::Create( FidoTransportProtocol::kBluetoothLowEnergy, nullptr); EXPECT_EQ(injected_fake_discovery_1, produced_discovery_1.get()); auto* injected_fake_discovery_2 = factory.ForgeNextBleDiscovery(); ASSERT_EQ(FidoTransportProtocol::kBluetoothLowEnergy, injected_fake_discovery_2->transport()); - auto produced_discovery_2 = FidoDeviceDiscovery::Create( + auto produced_discovery_2 = FidoDiscoveryFactory::Create( FidoTransportProtocol::kBluetoothLowEnergy, nullptr); EXPECT_EQ(injected_fake_discovery_2, produced_discovery_2.get()); } diff --git a/chromium/device/fido/features.cc b/chromium/device/fido/features.cc new file mode 100644 index 00000000000..dad7f0d1335 --- /dev/null +++ b/chromium/device/fido/features.cc @@ -0,0 +1,30 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/fido/features.h" + +#include "base/feature_list.h" +#include "build/build_config.h" + +namespace device { + +#if defined(OS_WIN) +// Controls whether on Windows, U2F/CTAP2 requests are forwarded to the +// native WebAuthentication API, where available. +const base::Feature kWebAuthUseNativeWinApi{"WebAuthenticationUseNativeWinApi", + base::FEATURE_DISABLED_BY_DEFAULT}; + +// If true, the minimum API version check for integration with the native +// Windows WebAuthentication API is disabled. This is an interim solution for +// for manual testing while we await the release of a DLL that implements the +// version check. +const base::Feature kWebAuthDisableWinApiVersionCheckForTesting{ + "WebAuthenticationDisableWinApiVersionCheckForTesting", + base::FEATURE_DISABLED_BY_DEFAULT}; +#endif // defined(OS_WIN) + +extern const base::Feature kWebAuthProxyCryptotoken{ + "WebAuthenticationProxyCryptotoken", base::FEATURE_DISABLED_BY_DEFAULT}; + +} // namespace device diff --git a/chromium/device/fido/features.h b/chromium/device/fido/features.h new file mode 100644 index 00000000000..dbcb880d0c6 --- /dev/null +++ b/chromium/device/fido/features.h @@ -0,0 +1,28 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_FIDO_FEATURES_H_ +#define DEVICE_FIDO_FEATURES_H_ + +#include "base/component_export.h" +#include "base/feature_list.h" +#include "build/build_config.h" + +namespace device { + +#if defined(OS_WIN) +COMPONENT_EXPORT(DEVICE_FIDO) +extern const base::Feature kWebAuthUseNativeWinApi; + +COMPONENT_EXPORT(DEVICE_FIDO) +extern const base::Feature kWebAuthDisableWinApiVersionCheckForTesting; +#endif // defined(OS_WIN) + +// Controls the proxying of Cryptotoken requests through WebAuthn. +COMPONENT_EXPORT(DEVICE_FIDO) +extern const base::Feature kWebAuthProxyCryptotoken; + +} // namespace device + +#endif // DEVICE_FIDO_FEATURES_H_ diff --git a/chromium/device/fido/fido_authenticator.h b/chromium/device/fido/fido_authenticator.h index 4fb6b7bbcdc..6c00f3fbe2c 100644 --- a/chromium/device/fido/fido_authenticator.h +++ b/chromium/device/fido/fido_authenticator.h @@ -15,11 +15,11 @@ #include "base/strings/string16.h" #include "device/fido/authenticator_get_assertion_response.h" #include "device/fido/authenticator_make_credential_response.h" +#include "device/fido/authenticator_supported_options.h" #include "device/fido/fido_transport_protocol.h" namespace device { -class AuthenticatorSupportedOptions; class CtapGetAssertionRequest; class CtapMakeCredentialRequest; @@ -42,17 +42,19 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoAuthenticator { // call is received, |callback| is invoked. Below MakeCredential() and // GetAssertion() must only called after |callback| is invoked. virtual void InitializeAuthenticator(base::OnceClosure callback) = 0; - virtual void MakeCredential( - CtapMakeCredentialRequest request, - MakeCredentialCallback callback) = 0; + virtual void MakeCredential(CtapMakeCredentialRequest request, + MakeCredentialCallback callback) = 0; virtual void GetAssertion(CtapGetAssertionRequest request, GetAssertionCallback callback) = 0; virtual void Cancel() = 0; virtual std::string GetId() const = 0; virtual base::string16 GetDisplayName() const = 0; - virtual const AuthenticatorSupportedOptions& Options() const = 0; - virtual FidoTransportProtocol AuthenticatorTransport() const = 0; + virtual const base::Optional<AuthenticatorSupportedOptions>& Options() + const = 0; + virtual base::Optional<FidoTransportProtocol> AuthenticatorTransport() + const = 0; virtual bool IsInPairingMode() const = 0; + virtual bool IsPaired() const = 0; virtual base::WeakPtr<FidoAuthenticator> GetWeakPtr() = 0; private: diff --git a/chromium/device/fido/fido_constants.cc b/chromium/device/fido/fido_constants.cc index bae7c52d45e..8d477d2091a 100644 --- a/chromium/device/fido/fido_constants.cc +++ b/chromium/device/fido/fido_constants.cc @@ -77,4 +77,9 @@ const char kCableClientHelloMessage[] = "caBLE v1 client hello"; const char kCtap2Version[] = "FIDO_2_0"; const char kU2fVersion[] = "U2F_V2"; +const char kExtensionHmacSecret[] = "hmac-secret"; + +const base::TimeDelta kBleDevicePairingModeWaitingInterval = + base::TimeDelta::FromSeconds(2); + } // namespace device diff --git a/chromium/device/fido/fido_constants.h b/chromium/device/fido/fido_constants.h index 2dc2a2136ac..e8723a7dd51 100644 --- a/chromium/device/fido/fido_constants.h +++ b/chromium/device/fido/fido_constants.h @@ -92,10 +92,8 @@ enum class CtapDeviceResponseCode : uint8_t { kCtap1ErrChannelBusy = 0x06, kCtap1ErrLockRequired = 0x0A, kCtap1ErrInvalidChannel = 0x0B, - kCtap2ErrCBORParsing = 0x10, - kCtap2ErrUnexpectedType = 0x11, + kCtap2ErrCBORUnexpectedType = 0x11, kCtap2ErrInvalidCBOR = 0x12, - kCtap2ErrInvalidCBORType = 0x13, kCtap2ErrMissingParameter = 0x14, kCtap2ErrLimitExceeded = 0x15, kCtap2ErrUnsupportedExtension = 0x16, @@ -145,10 +143,8 @@ constexpr std::array<CtapDeviceResponseCode, 51> GetCtapResponseCodeList() { CtapDeviceResponseCode::kCtap1ErrChannelBusy, CtapDeviceResponseCode::kCtap1ErrLockRequired, CtapDeviceResponseCode::kCtap1ErrInvalidChannel, - CtapDeviceResponseCode::kCtap2ErrCBORParsing, - CtapDeviceResponseCode::kCtap2ErrUnexpectedType, + CtapDeviceResponseCode::kCtap2ErrCBORUnexpectedType, CtapDeviceResponseCode::kCtap2ErrInvalidCBOR, - CtapDeviceResponseCode::kCtap2ErrInvalidCBORType, CtapDeviceResponseCode::kCtap2ErrMissingParameter, CtapDeviceResponseCode::kCtap2ErrLimitExceeded, CtapDeviceResponseCode::kCtap2ErrUnsupportedExtension, @@ -259,6 +255,16 @@ enum class U2fApduInstruction : uint8_t { enum class CredentialType { kPublicKey }; +// Authenticator attachment constraint passed on from the relying party as a +// parameter for AuthenticatorSelectionCriteria. |kAny| is equivalent to the +// (optional) attachment field not being present. +// https://w3c.github.io/webauthn/#attachment +enum class AuthenticatorAttachment { + kAny, + kPlatform, + kCrossPlatform, +}; + // User verification constraint passed on from the relying party as a parameter // for AuthenticatorSelectionCriteria and for CtapGetAssertion request. // https://w3c.github.io/webauthn/#enumdef-userverificationrequirement @@ -373,6 +379,17 @@ COMPONENT_EXPORT(DEVICE_FIDO) extern const char kCableClientHelloMessage[]; COMPONENT_EXPORT(DEVICE_FIDO) extern const char kCtap2Version[]; COMPONENT_EXPORT(DEVICE_FIDO) extern const char kU2fVersion[]; +COMPONENT_EXPORT(DEVICE_FIDO) extern const char kExtensionHmacSecret[]; + +// Maximum number of seconds the browser waits for Bluetooth authenticator to +// send packets that advertises that the device is in pairing mode before +// setting pairing mode to false. The interval time is set to 2 seconds, which +// is equivalent to the maximum Bluetooth error wait interval set by the CTAP +// spec. +// https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#BTCORE +COMPONENT_EXPORT(DEVICE_FIDO) +extern const base::TimeDelta kBleDevicePairingModeWaitingInterval; + } // namespace device #endif // DEVICE_FIDO_FIDO_CONSTANTS_H_ diff --git a/chromium/device/fido/fido_device.cc b/chromium/device/fido/fido_device.cc index edd97a2a2ed..b27a1e1bea9 100644 --- a/chromium/device/fido/fido_device.cc +++ b/chromium/device/fido/fido_device.cc @@ -27,6 +27,10 @@ bool FidoDevice::IsInPairingMode() const { return false; } +bool FidoDevice::IsPaired() const { + return false; +} + void FidoDevice::DiscoverSupportedProtocolAndDeviceInfo( base::OnceClosure done) { if (base::FeatureList::IsEnabled(kNewCtap2Device)) { diff --git a/chromium/device/fido/fido_device.h b/chromium/device/fido/fido_device.h index 5a8bb387085..e9ecfa77060 100644 --- a/chromium/device/fido/fido_device.h +++ b/chromium/device/fido/fido_device.h @@ -63,6 +63,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDevice { virtual base::string16 GetDisplayName() const; virtual FidoTransportProtocol DeviceTransport() const = 0; virtual bool IsInPairingMode() const; + virtual bool IsPaired() const; virtual base::WeakPtr<FidoDevice> GetWeakPtr() = 0; // Sends a speculative AuthenticatorGetInfo request to determine whether the diff --git a/chromium/device/fido/fido_device_authenticator.cc b/chromium/device/fido/fido_device_authenticator.cc index f011e9ed46f..e68c7625994 100644 --- a/chromium/device/fido/fido_device_authenticator.cc +++ b/chromium/device/fido/fido_device_authenticator.cc @@ -27,8 +27,29 @@ void FidoDeviceAuthenticator::InitializeAuthenticator( base::OnceClosure callback) { base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::BindOnce(&FidoDevice::DiscoverSupportedProtocolAndDeviceInfo, - device()->GetWeakPtr(), std::move(callback))); + base::BindOnce( + &FidoDevice::DiscoverSupportedProtocolAndDeviceInfo, + device()->GetWeakPtr(), + base::BindOnce(&FidoDeviceAuthenticator::InitializeAuthenticatorDone, + weak_factory_.GetWeakPtr(), std::move(callback)))); +} + +void FidoDeviceAuthenticator::InitializeAuthenticatorDone( + base::OnceClosure callback) { + DCHECK(!options_); + switch (device_->supported_protocol()) { + case ProtocolVersion::kU2f: + options_ = AuthenticatorSupportedOptions(); + break; + case ProtocolVersion::kCtap: + DCHECK(device_->device_info()) << "uninitialized device"; + options_ = device_->device_info()->options(); + break; + case ProtocolVersion::kUnknown: + NOTREACHED() << "uninitialized device"; + options_ = AuthenticatorSupportedOptions(); + } + std::move(callback).Run(); } void FidoDeviceAuthenticator::MakeCredential(CtapMakeCredentialRequest request, @@ -36,6 +57,18 @@ void FidoDeviceAuthenticator::MakeCredential(CtapMakeCredentialRequest request, DCHECK(!task_); DCHECK(device_->SupportedProtocolIsInitialized()) << "InitializeAuthenticator() must be called first."; + DCHECK(Options()); + + // Update the request to the "effective" user verification requirement. + // https://w3c.github.io/webauthn/#effective-user-verification-requirement-for-credential-creation + if (Options()->user_verification_availability() == + AuthenticatorSupportedOptions::UserVerificationAvailability:: + kSupportedAndConfigured) { + request.SetUserVerification(UserVerificationRequirement::kRequired); + } else { + request.SetUserVerification(UserVerificationRequirement::kDiscouraged); + } + // TODO(martinkr): Change FidoTasks to take all request parameters by const // reference, so we can avoid copying these from the RequestHandler. task_ = std::make_unique<MakeCredentialTask>( @@ -44,8 +77,21 @@ void FidoDeviceAuthenticator::MakeCredential(CtapMakeCredentialRequest request, void FidoDeviceAuthenticator::GetAssertion(CtapGetAssertionRequest request, GetAssertionCallback callback) { + DCHECK(!task_); DCHECK(device_->SupportedProtocolIsInitialized()) << "InitializeAuthenticator() must be called first."; + DCHECK(Options()); + + // Update the request to the "effective" user verification requirement. + // https://w3c.github.io/webauthn/#effective-user-verification-requirement-for-assertion + if (Options()->user_verification_availability() == + AuthenticatorSupportedOptions::UserVerificationAvailability:: + kSupportedAndConfigured) { + request.SetUserVerification(UserVerificationRequirement::kRequired); + } else { + request.SetUserVerification(UserVerificationRequirement::kDiscouraged); + } + task_ = std::make_unique<GetAssertionTask>(device_.get(), std::move(request), std::move(callback)); } @@ -65,22 +111,13 @@ base::string16 FidoDeviceAuthenticator::GetDisplayName() const { return device_->GetDisplayName(); } -const AuthenticatorSupportedOptions& FidoDeviceAuthenticator::Options() const { - static const AuthenticatorSupportedOptions default_options; - switch (device_->supported_protocol()) { - case ProtocolVersion::kU2f: - return default_options; - case ProtocolVersion::kCtap: - DCHECK(device_->device_info()) << "uninitialized device"; - return device_->device_info()->options(); - case ProtocolVersion::kUnknown: - NOTREACHED() << "uninitialized device"; - } - NOTREACHED(); - return default_options; +const base::Optional<AuthenticatorSupportedOptions>& +FidoDeviceAuthenticator::Options() const { + return options_; } -FidoTransportProtocol FidoDeviceAuthenticator::AuthenticatorTransport() const { +base::Optional<FidoTransportProtocol> +FidoDeviceAuthenticator::AuthenticatorTransport() const { return device_->DeviceTransport(); } @@ -88,6 +125,10 @@ bool FidoDeviceAuthenticator::IsInPairingMode() const { return device_->IsInPairingMode(); } +bool FidoDeviceAuthenticator::IsPaired() const { + return device_->IsPaired(); +} + void FidoDeviceAuthenticator::SetTaskForTesting( std::unique_ptr<FidoTask> task) { task_ = std::move(task); diff --git a/chromium/device/fido/fido_device_authenticator.h b/chromium/device/fido/fido_device_authenticator.h index 06738e4b545..0d7e4fa8b00 100644 --- a/chromium/device/fido/fido_device_authenticator.h +++ b/chromium/device/fido/fido_device_authenticator.h @@ -41,12 +41,14 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDeviceAuthenticator void Cancel() override; std::string GetId() const override; base::string16 GetDisplayName() const override; - const AuthenticatorSupportedOptions& Options() const override; - FidoTransportProtocol AuthenticatorTransport() const override; + const base::Optional<AuthenticatorSupportedOptions>& Options() const override; + base::Optional<FidoTransportProtocol> AuthenticatorTransport() const override; bool IsInPairingMode() const override; + bool IsPaired() const override; base::WeakPtr<FidoAuthenticator> GetWeakPtr() override; FidoDevice* device() { return device_.get(); } + void SetTaskForTesting(std::unique_ptr<FidoTask> task); protected: void OnCtapMakeCredentialResponseReceived( @@ -56,10 +58,11 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDeviceAuthenticator GetAssertionCallback callback, base::Optional<std::vector<uint8_t>> response_data); - void SetTaskForTesting(std::unique_ptr<FidoTask> task); - private: + void InitializeAuthenticatorDone(base::OnceClosure callback); + const std::unique_ptr<FidoDevice> device_; + base::Optional<AuthenticatorSupportedOptions> options_; std::unique_ptr<FidoTask> task_; base::WeakPtrFactory<FidoDeviceAuthenticator> weak_factory_; diff --git a/chromium/device/fido/fido_device_discovery.cc b/chromium/device/fido/fido_device_discovery.cc index fe7fbe1af0a..5e18cff4e85 100644 --- a/chromium/device/fido/fido_device_discovery.cc +++ b/chromium/device/fido/fido_device_discovery.cc @@ -7,81 +7,15 @@ #include <utility> #include "base/bind.h" -#include "build/build_config.h" -#include "device/fido/ble/fido_ble_discovery.h" -#include "device/fido/cable/fido_cable_discovery.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "device/fido/fido_authenticator.h" #include "device/fido/fido_device.h" #include "device/fido/fido_device_authenticator.h" -// HID is not supported on Android. -#if !defined(OS_ANDROID) -#include "device/fido/hid/fido_hid_discovery.h" -#endif // !defined(OS_ANDROID) - namespace device { -namespace { - -std::unique_ptr<FidoDeviceDiscovery> CreateFidoDiscoveryImpl( - FidoTransportProtocol transport, - service_manager::Connector* connector) { - switch (transport) { - case FidoTransportProtocol::kUsbHumanInterfaceDevice: -#if !defined(OS_ANDROID) - DCHECK(connector); - return std::make_unique<FidoHidDiscovery>(connector); -#else - NOTREACHED() << "USB HID not supported on Android."; - return nullptr; -#endif // !defined(OS_ANDROID) - case FidoTransportProtocol::kBluetoothLowEnergy: - return std::make_unique<FidoBleDiscovery>(); - case FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy: - NOTREACHED() << "Cable discovery is constructed using the dedicated " - "factory method."; - return nullptr; - case FidoTransportProtocol::kNearFieldCommunication: - // TODO(https://crbug.com/825949): Add NFC support. - return nullptr; - case FidoTransportProtocol::kInternal: - NOTREACHED() << "Internal authenticators should be handled separately."; - return nullptr; - } - NOTREACHED() << "Unhandled transport type"; - return nullptr; -} - -std::unique_ptr<FidoDeviceDiscovery> CreateCableDiscoveryImpl( - std::vector<CableDiscoveryData> cable_data) { - return std::make_unique<FidoCableDiscovery>(std::move(cable_data)); -} - -} // namespace - FidoDeviceDiscovery::Observer::~Observer() = default; -// static -FidoDeviceDiscovery::FactoryFuncPtr FidoDeviceDiscovery::g_factory_func_ = - &CreateFidoDiscoveryImpl; - -// static -FidoDeviceDiscovery::CableFactoryFuncPtr - FidoDeviceDiscovery::g_cable_factory_func_ = &CreateCableDiscoveryImpl; - -// static -std::unique_ptr<FidoDeviceDiscovery> FidoDeviceDiscovery::Create( - FidoTransportProtocol transport, - service_manager::Connector* connector) { - return (*g_factory_func_)(transport, connector); -} - -// static -std::unique_ptr<FidoDeviceDiscovery> FidoDeviceDiscovery::CreateCable( - std::vector<CableDiscoveryData> cable_data) { - return (*g_cable_factory_func_)(std::move(cable_data)); -} - FidoDeviceDiscovery::FidoDeviceDiscovery(FidoTransportProtocol transport) : FidoDiscoveryBase(transport), weak_factory_(this) {} @@ -90,10 +24,12 @@ FidoDeviceDiscovery::~FidoDeviceDiscovery() = default; void FidoDeviceDiscovery::Start() { DCHECK_EQ(state_, State::kIdle); state_ = State::kStarting; - // TODO(hongjunchoi): Fix so that NotifiyStarted() is never called - // synchronously after StartInternal(). - // See: https://crbug.com/823686 - StartInternal(); + + // To ensure that that NotifiyStarted() is never invoked synchronously, + // post task asynchronously. + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&FidoDeviceDiscovery::StartInternal, + weak_factory_.GetWeakPtr())); } void FidoDeviceDiscovery::NotifyDiscoveryStarted(bool success) { @@ -174,50 +110,4 @@ bool FidoDeviceDiscovery::RemoveDevice(base::StringPiece device_id) { return true; } -// ScopedFidoDiscoveryFactory ------------------------------------------------- - -namespace internal { - -ScopedFidoDiscoveryFactory::ScopedFidoDiscoveryFactory() { - DCHECK(!g_current_factory); - g_current_factory = this; - original_factory_func_ = - std::exchange(FidoDeviceDiscovery::g_factory_func_, - &ForwardCreateFidoDiscoveryToCurrentFactory); - original_cable_factory_func_ = - std::exchange(FidoDeviceDiscovery::g_cable_factory_func_, - &ForwardCreateCableDiscoveryToCurrentFactory); -} - -ScopedFidoDiscoveryFactory::~ScopedFidoDiscoveryFactory() { - g_current_factory = nullptr; - FidoDeviceDiscovery::g_factory_func_ = original_factory_func_; - FidoDeviceDiscovery::g_cable_factory_func_ = original_cable_factory_func_; -} - -// static -std::unique_ptr<FidoDeviceDiscovery> -ScopedFidoDiscoveryFactory::ForwardCreateFidoDiscoveryToCurrentFactory( - FidoTransportProtocol transport, - ::service_manager::Connector* connector) { - DCHECK(g_current_factory); - return g_current_factory->CreateFidoDiscovery(transport, connector); -} - -// static -std::unique_ptr<FidoDeviceDiscovery> -ScopedFidoDiscoveryFactory::ForwardCreateCableDiscoveryToCurrentFactory( - std::vector<CableDiscoveryData> cable_data) { - DCHECK(g_current_factory); - g_current_factory->set_last_cable_data(std::move(cable_data)); - return g_current_factory->CreateFidoDiscovery( - FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy, - nullptr /* connector */); -} - -// static -ScopedFidoDiscoveryFactory* ScopedFidoDiscoveryFactory::g_current_factory = - nullptr; - -} // namespace internal } // namespace device diff --git a/chromium/device/fido/fido_device_discovery.h b/chromium/device/fido/fido_device_discovery.h index d5b844fac46..fd8037f023a 100644 --- a/chromium/device/fido/fido_device_discovery.h +++ b/chromium/device/fido/fido_device_discovery.h @@ -17,23 +17,14 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/strings/string_piece.h" -#include "device/fido/cable/cable_discovery_data.h" #include "device/fido/fido_discovery_base.h" #include "device/fido/fido_transport_protocol.h" -namespace service_manager { -class Connector; -} - namespace device { class FidoDevice; class FidoDeviceAuthenticator; -namespace internal { -class ScopedFidoDiscoveryFactory; -} - class COMPONENT_EXPORT(DEVICE_FIDO) FidoDeviceDiscovery : public FidoDiscoveryBase { public: @@ -43,18 +34,6 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDeviceDiscovery kRunning, }; - // Factory functions to construct an instance that discovers authenticators on - // the given |transport| protocol. The first variant is for everything except - // for cloud-assisted BLE which is handled by the second variant. - // - // FidoTransportProtocol::kUsbHumanInterfaceDevice requires specifying a valid - // |connector| on Desktop, and is not valid on Android. - static std::unique_ptr<FidoDeviceDiscovery> Create( - FidoTransportProtocol transport, - ::service_manager::Connector* connector); - static std::unique_ptr<FidoDeviceDiscovery> CreateCable( - std::vector<CableDiscoveryData> cable_data); - ~FidoDeviceDiscovery() override; bool is_start_requested() const { return state_ != State::kIdle; } @@ -94,67 +73,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDeviceDiscovery authenticators_; private: - friend class internal::ScopedFidoDiscoveryFactory; - - // Factory function can be overridden by tests to construct fakes. - using FactoryFuncPtr = decltype(&Create); - using CableFactoryFuncPtr = decltype(&CreateCable); - static FactoryFuncPtr g_factory_func_; - static CableFactoryFuncPtr g_cable_factory_func_; - State state_ = State::kIdle; base::WeakPtrFactory<FidoDeviceDiscovery> weak_factory_; DISALLOW_COPY_AND_ASSIGN(FidoDeviceDiscovery); }; -namespace internal { - -// Base class for a scoped override of FidoDeviceDiscovery::Create, used in unit -// tests, layout tests, and when running with the Web Authn Testing API enabled. -// -// While there is a subclass instance in scope, calls to the factory method will -// be hijacked such that the derived class's CreateFidoDiscovery method will be -// invoked instead. -class COMPONENT_EXPORT(DEVICE_FIDO) ScopedFidoDiscoveryFactory { - public: - // There should be at most one instance of any subclass in scope at a time. - ScopedFidoDiscoveryFactory(); - virtual ~ScopedFidoDiscoveryFactory(); - - const std::vector<CableDiscoveryData>& last_cable_data() const { - return last_cable_data_; - } - - protected: - void set_last_cable_data(std::vector<CableDiscoveryData> cable_data) { - last_cable_data_ = std::move(cable_data); - } - - virtual std::unique_ptr<FidoDeviceDiscovery> CreateFidoDiscovery( - FidoTransportProtocol transport, - ::service_manager::Connector* connector) = 0; - - private: - static std::unique_ptr<FidoDeviceDiscovery> - ForwardCreateFidoDiscoveryToCurrentFactory( - FidoTransportProtocol transport, - ::service_manager::Connector* connector); - - static std::unique_ptr<FidoDeviceDiscovery> - ForwardCreateCableDiscoveryToCurrentFactory( - std::vector<CableDiscoveryData> cable_data); - - static ScopedFidoDiscoveryFactory* g_current_factory; - - FidoDeviceDiscovery::FactoryFuncPtr original_factory_func_; - FidoDeviceDiscovery::CableFactoryFuncPtr original_cable_factory_func_; - std::vector<CableDiscoveryData> last_cable_data_; - - DISALLOW_COPY_AND_ASSIGN(ScopedFidoDiscoveryFactory); -}; - -} // namespace internal } // namespace device #endif // DEVICE_FIDO_FIDO_DEVICE_DISCOVERY_H_ diff --git a/chromium/device/fido/fido_device_discovery_unittest.cc b/chromium/device/fido/fido_device_discovery_unittest.cc index 9e984c94e4a..f4d092bba23 100644 --- a/chromium/device/fido/fido_device_discovery_unittest.cc +++ b/chromium/device/fido/fido_device_discovery_unittest.cc @@ -59,6 +59,8 @@ TEST(FidoDiscoveryTest, TestAddAndRemoveObserver) { } TEST(FidoDiscoveryTest, TestNotificationsOnSuccessfulStart) { + base::test::ScopedTaskEnvironment scoped_task_environment_; + ConcreteFidoDiscovery discovery(FidoTransportProtocol::kBluetoothLowEnergy); MockFidoDiscoveryObserver observer; discovery.set_observer(&observer); @@ -68,6 +70,8 @@ TEST(FidoDiscoveryTest, TestNotificationsOnSuccessfulStart) { EXPECT_CALL(discovery, StartInternal()); discovery.Start(); + scoped_task_environment_.RunUntilIdle(); + EXPECT_TRUE(discovery.is_start_requested()); EXPECT_FALSE(discovery.is_running()); ::testing::Mock::VerifyAndClearExpectations(&discovery); @@ -80,11 +84,14 @@ TEST(FidoDiscoveryTest, TestNotificationsOnSuccessfulStart) { } TEST(FidoDiscoveryTest, TestNotificationsOnFailedStart) { + base::test::ScopedTaskEnvironment scoped_task_environment_; + ConcreteFidoDiscovery discovery(FidoTransportProtocol::kBluetoothLowEnergy); MockFidoDiscoveryObserver observer; discovery.set_observer(&observer); discovery.Start(); + scoped_task_environment_.RunUntilIdle(); EXPECT_CALL(observer, DiscoveryStarted(&discovery, false)); discovery.NotifyDiscoveryStarted(false); diff --git a/chromium/device/fido/fido_discovery_base.h b/chromium/device/fido/fido_discovery_base.h index 0ba4a6228ed..6fd43084880 100644 --- a/chromium/device/fido/fido_discovery_base.h +++ b/chromium/device/fido/fido_discovery_base.h @@ -41,11 +41,11 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDiscoveryBase { const std::string& previous_id, std::string new_id) = 0; - // Invoked when connected Bluetooth device advertises that it is in pairing - // mode. - virtual void AuthenticatorPairingModeChanged( - FidoDiscoveryBase* discovery, - const std::string& device_id) = 0; + // Invoked when connected Bluetooth device advertises that its pairing mode + // has changed. + virtual void AuthenticatorPairingModeChanged(FidoDiscoveryBase* discovery, + const std::string& device_id, + bool is_in_pairing_mode) = 0; }; // Start authenticator discovery. The Observer must have been set before this diff --git a/chromium/device/fido/fido_discovery_factory.cc b/chromium/device/fido/fido_discovery_factory.cc new file mode 100644 index 00000000000..bcf26c59af4 --- /dev/null +++ b/chromium/device/fido/fido_discovery_factory.cc @@ -0,0 +1,158 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/fido/fido_discovery_factory.h" + +#include "base/logging.h" +#include "build/build_config.h" +#include "device/fido/ble/fido_ble_discovery.h" +#include "device/fido/cable/fido_cable_discovery.h" +#include "device/fido/features.h" +#include "device/fido/fido_discovery_base.h" + +// HID is not supported on Android. +#if !defined(OS_ANDROID) +#include "device/fido/hid/fido_hid_discovery.h" +#endif // !defined(OS_ANDROID) + +#if defined(OS_WIN) +#include <Winuser.h> +#include "device/fido/win/discovery.h" +#include "device/fido/win/webauthn_api.h" +#endif // defined(OS_WIN) + +namespace device { + +namespace { + +std::unique_ptr<FidoDiscoveryBase> CreateUsbFidoDiscovery( + service_manager::Connector* connector) { +#if defined(OS_ANDROID) + NOTREACHED() << "USB HID not supported on Android."; + return nullptr; +#else + + DCHECK(connector); + return std::make_unique<FidoHidDiscovery>(connector); +#endif // !defined(OS_ANDROID) +} + +std::unique_ptr<FidoDiscoveryBase> CreateFidoDiscoveryImpl( + FidoTransportProtocol transport, + service_manager::Connector* connector) { + switch (transport) { + case FidoTransportProtocol::kUsbHumanInterfaceDevice: + return CreateUsbFidoDiscovery(connector); + case FidoTransportProtocol::kBluetoothLowEnergy: + return std::make_unique<FidoBleDiscovery>(); + case FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy: + NOTREACHED() << "Cable discovery is constructed using the dedicated " + "factory method."; + return nullptr; + case FidoTransportProtocol::kNearFieldCommunication: + // TODO(https://crbug.com/825949): Add NFC support. + return nullptr; + case FidoTransportProtocol::kInternal: + NOTREACHED() << "Internal authenticators should be handled separately."; + return nullptr; + } + NOTREACHED() << "Unhandled transport type"; + return nullptr; +} + +std::unique_ptr<FidoDiscoveryBase> CreateCableDiscoveryImpl( + std::vector<CableDiscoveryData> cable_data) { + return std::make_unique<FidoCableDiscovery>(std::move(cable_data)); +} + +} // namespace + +// static +FidoDiscoveryFactory::FactoryFuncPtr FidoDiscoveryFactory::g_factory_func_ = + &CreateFidoDiscoveryImpl; + +// static +FidoDiscoveryFactory::CableFactoryFuncPtr + FidoDiscoveryFactory::g_cable_factory_func_ = &CreateCableDiscoveryImpl; + +// static +std::unique_ptr<FidoDiscoveryBase> FidoDiscoveryFactory::Create( + FidoTransportProtocol transport, + service_manager::Connector* connector) { + return (*g_factory_func_)(transport, connector); +} + +// static +std::unique_ptr<FidoDiscoveryBase> FidoDiscoveryFactory::CreateCable( + std::vector<CableDiscoveryData> cable_data) { + return (*g_cable_factory_func_)(std::move(cable_data)); +} + +// static +#if defined(OS_WIN) +std::unique_ptr<FidoDiscoveryBase> +FidoDiscoveryFactory::MaybeCreateWinWebAuthnApiDiscovery() { + if (!base::FeatureList::IsEnabled(device::kWebAuthUseNativeWinApi) || + !WinWebAuthnApi::GetDefault()->IsAvailable()) { + return nullptr; + } + return std::make_unique<WinWebAuthnApiAuthenticatorDiscovery>( + WinWebAuthnApi::GetDefault(), + // TODO(martinkr): Inject the window from which the request + // originated. Windows uses this parameter to center the + // dialog over the parent. The dialog should be centered + // over the originating Chrome Window; the foreground window + // may have changed to something else since the request was + // issued. + GetForegroundWindow()); +} +#endif // defined(OS_WIN) + +// ScopedFidoDiscoveryFactory ------------------------------------------------- + +namespace internal { + +ScopedFidoDiscoveryFactory::ScopedFidoDiscoveryFactory() { + DCHECK(!g_current_factory); + g_current_factory = this; + original_factory_func_ = + std::exchange(FidoDiscoveryFactory::g_factory_func_, + &ForwardCreateFidoDiscoveryToCurrentFactory); + original_cable_factory_func_ = + std::exchange(FidoDiscoveryFactory::g_cable_factory_func_, + &ForwardCreateCableDiscoveryToCurrentFactory); +} + +ScopedFidoDiscoveryFactory::~ScopedFidoDiscoveryFactory() { + g_current_factory = nullptr; + FidoDiscoveryFactory::g_factory_func_ = original_factory_func_; + FidoDiscoveryFactory::g_cable_factory_func_ = original_cable_factory_func_; +} + +// static +std::unique_ptr<FidoDiscoveryBase> +ScopedFidoDiscoveryFactory::ForwardCreateFidoDiscoveryToCurrentFactory( + FidoTransportProtocol transport, + ::service_manager::Connector* connector) { + DCHECK(g_current_factory); + return g_current_factory->CreateFidoDiscovery(transport, connector); +} + +// static +std::unique_ptr<FidoDiscoveryBase> +ScopedFidoDiscoveryFactory::ForwardCreateCableDiscoveryToCurrentFactory( + std::vector<CableDiscoveryData> cable_data) { + DCHECK(g_current_factory); + g_current_factory->set_last_cable_data(std::move(cable_data)); + return g_current_factory->CreateFidoDiscovery( + FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy, + nullptr /* connector */); +} + +// static +ScopedFidoDiscoveryFactory* ScopedFidoDiscoveryFactory::g_current_factory = + nullptr; + +} // namespace internal +} // namespace device diff --git a/chromium/device/fido/fido_discovery_factory.h b/chromium/device/fido/fido_discovery_factory.h new file mode 100644 index 00000000000..cc21790622c --- /dev/null +++ b/chromium/device/fido/fido_discovery_factory.h @@ -0,0 +1,109 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_FIDO_FIDO_DISCOVERY_FACTORY_H_ +#define DEVICE_FIDO_FIDO_DISCOVERY_FACTORY_H_ + +#include <memory> +#include <vector> + +#include "base/component_export.h" +#include "build/build_config.h" +#include "device/fido/cable/cable_discovery_data.h" +#include "device/fido/fido_device_discovery.h" +#include "device/fido/fido_discovery_base.h" +#include "device/fido/fido_transport_protocol.h" + +namespace service_manager { +class Connector; +} + +namespace device { + +namespace internal { +class ScopedFidoDiscoveryFactory; +} + +// FidoDiscoveryFactory offers methods to construct instances of +// FidoDiscoveryBase for a given |transport| protocol. +class COMPONENT_EXPORT(DEVICE_FIDO) FidoDiscoveryFactory { + public: + // Instantiates a FidoDiscoveryBase for all protocols except caBLE and + // internal/platform. + // + // FidoTransportProtocol::kUsbHumanInterfaceDevice requires specifying a + // valid |connector| on Desktop, and is not valid on Android. + static std::unique_ptr<FidoDiscoveryBase> Create( + FidoTransportProtocol transport, + ::service_manager::Connector* connector); + // Instantiates a FidoDiscovery for caBLE. + static std::unique_ptr<FidoDiscoveryBase> CreateCable( + std::vector<CableDiscoveryData> cable_data); +#if defined(OS_WIN) + // Instantiates a FidoDiscovery for the native Windows WebAuthn + // API where available. Returns nullptr otherwise. + static std::unique_ptr<FidoDiscoveryBase> + MaybeCreateWinWebAuthnApiDiscovery(); +#endif // defined(OS_WIN) + + private: + friend class internal::ScopedFidoDiscoveryFactory; + + // Factory function can be overridden by tests to construct fakes. + using FactoryFuncPtr = decltype(&Create); + using CableFactoryFuncPtr = decltype(&CreateCable); + static FactoryFuncPtr g_factory_func_; + static CableFactoryFuncPtr g_cable_factory_func_; +}; + +namespace internal { +// Base class for a scoped override of FidoDiscoveryFactory::Create, used in +// unit tests, layout tests, and when running with the Web Authn Testing API +// enabled. +// +// While there is a subclass instance in scope, calls to the factory method will +// be hijacked such that the derived class's CreateFidoDiscovery method will be +// invoked instead. +class COMPONENT_EXPORT(DEVICE_FIDO) ScopedFidoDiscoveryFactory { + public: + // There should be at most one instance of any subclass in scope at a time. + ScopedFidoDiscoveryFactory(); + virtual ~ScopedFidoDiscoveryFactory(); + + const std::vector<CableDiscoveryData>& last_cable_data() const { + return last_cable_data_; + } + + protected: + void set_last_cable_data(std::vector<CableDiscoveryData> cable_data) { + last_cable_data_ = std::move(cable_data); + } + + virtual std::unique_ptr<FidoDiscoveryBase> CreateFidoDiscovery( + FidoTransportProtocol transport, + ::service_manager::Connector* connector) = 0; + + private: + static std::unique_ptr<FidoDiscoveryBase> + ForwardCreateFidoDiscoveryToCurrentFactory( + FidoTransportProtocol transport, + ::service_manager::Connector* connector); + + static std::unique_ptr<FidoDiscoveryBase> + ForwardCreateCableDiscoveryToCurrentFactory( + std::vector<CableDiscoveryData> cable_data); + + static ScopedFidoDiscoveryFactory* g_current_factory; + + FidoDiscoveryFactory::FactoryFuncPtr original_factory_func_; + FidoDiscoveryFactory::CableFactoryFuncPtr original_cable_factory_func_; + std::vector<CableDiscoveryData> last_cable_data_; + + DISALLOW_COPY_AND_ASSIGN(ScopedFidoDiscoveryFactory); +}; + +} // namespace internal +} // namespace device + +#endif // DEVICE_FIDO_FIDO_DISCOVERY_FACTORY_H_ diff --git a/chromium/device/fido/fido_parsing_utils.h b/chromium/device/fido/fido_parsing_utils.h index 6bf982dad91..fa6d5d2fdc9 100644 --- a/chromium/device/fido/fido_parsing_utils.h +++ b/chromium/device/fido/fido_parsing_utils.h @@ -10,6 +10,7 @@ #include <algorithm> #include <array> +#include <iterator> #include <utility> #include <vector> @@ -22,14 +23,16 @@ namespace device { namespace fido_parsing_utils { -// Comparator object that calls base::make_span on its arguments before -// comparing them with operator<. Useful when comparing sequence containers that -// are of different types, but have similar semantics. -struct SpanLess { +// Comparator object that calls std::lexicographical_compare on the begin and +// end iterators of the passed in ranges. Useful when comparing sequence +// containers that are of different types, but have similar semantics. +struct RangeLess { template <typename T, typename U> constexpr bool operator()(T&& lhs, U&& rhs) const { - return base::make_span(std::forward<T>(lhs)) < - base::make_span(std::forward<U>(rhs)); + using std::begin; + using std::end; + return std::lexicographical_compare(begin(lhs), end(lhs), begin(rhs), + end(rhs)); } using is_transparent = void; diff --git a/chromium/device/fido/fido_parsing_utils_unittest.cc b/chromium/device/fido/fido_parsing_utils_unittest.cc index 41ff123f595..a553d4dcdb2 100644 --- a/chromium/device/fido/fido_parsing_utils_unittest.cc +++ b/chromium/device/fido/fido_parsing_utils_unittest.cc @@ -20,64 +20,64 @@ constexpr uint8_t kThree[] = {0x03}; constexpr uint8_t kOneTwoThree[] = {0x01, 0x02, 0x03}; } // namespace -TEST(U2fParsingUtils, SpanLess) { +TEST(U2fParsingUtils, RangeLess) { const std::array<int, 4> kOneTwoThreeFour = {1, 2, 3, 4}; - EXPECT_FALSE(SpanLess()(kOne, kOne)); - EXPECT_TRUE(SpanLess()(kOne, kOneTwo)); - EXPECT_TRUE(SpanLess()(kOne, kTwo)); - EXPECT_TRUE(SpanLess()(kOne, kTwoThree)); - EXPECT_TRUE(SpanLess()(kOne, kThree)); - EXPECT_TRUE(SpanLess()(kOne, kOneTwoThree)); - EXPECT_TRUE(SpanLess()(kOne, kOneTwoThreeFour)); - - EXPECT_FALSE(SpanLess()(kOneTwo, kOne)); - EXPECT_FALSE(SpanLess()(kOneTwo, kOneTwo)); - EXPECT_TRUE(SpanLess()(kOneTwo, kTwo)); - EXPECT_TRUE(SpanLess()(kOneTwo, kTwoThree)); - EXPECT_TRUE(SpanLess()(kOneTwo, kThree)); - EXPECT_TRUE(SpanLess()(kOneTwo, kOneTwoThree)); - EXPECT_TRUE(SpanLess()(kOneTwo, kOneTwoThreeFour)); - - EXPECT_FALSE(SpanLess()(kTwo, kOne)); - EXPECT_FALSE(SpanLess()(kTwo, kOneTwo)); - EXPECT_FALSE(SpanLess()(kTwo, kTwo)); - EXPECT_TRUE(SpanLess()(kTwo, kTwoThree)); - EXPECT_TRUE(SpanLess()(kTwo, kThree)); - EXPECT_FALSE(SpanLess()(kTwo, kOneTwoThree)); - EXPECT_FALSE(SpanLess()(kTwo, kOneTwoThreeFour)); - - EXPECT_FALSE(SpanLess()(kTwoThree, kOne)); - EXPECT_FALSE(SpanLess()(kTwoThree, kOneTwo)); - EXPECT_FALSE(SpanLess()(kTwoThree, kTwo)); - EXPECT_FALSE(SpanLess()(kTwoThree, kTwoThree)); - EXPECT_TRUE(SpanLess()(kTwoThree, kThree)); - EXPECT_FALSE(SpanLess()(kTwoThree, kOneTwoThree)); - EXPECT_FALSE(SpanLess()(kTwoThree, kOneTwoThreeFour)); - - EXPECT_FALSE(SpanLess()(kThree, kOne)); - EXPECT_FALSE(SpanLess()(kThree, kOneTwo)); - EXPECT_FALSE(SpanLess()(kThree, kTwo)); - EXPECT_FALSE(SpanLess()(kThree, kTwoThree)); - EXPECT_FALSE(SpanLess()(kThree, kThree)); - EXPECT_FALSE(SpanLess()(kThree, kOneTwoThree)); - EXPECT_FALSE(SpanLess()(kThree, kOneTwoThreeFour)); - - EXPECT_FALSE(SpanLess()(kOneTwoThree, kOne)); - EXPECT_FALSE(SpanLess()(kOneTwoThree, kOneTwo)); - EXPECT_TRUE(SpanLess()(kOneTwoThree, kTwo)); - EXPECT_TRUE(SpanLess()(kOneTwoThree, kTwoThree)); - EXPECT_TRUE(SpanLess()(kOneTwoThree, kThree)); - EXPECT_FALSE(SpanLess()(kOneTwoThree, kOneTwoThree)); - EXPECT_TRUE(SpanLess()(kOneTwoThree, kOneTwoThreeFour)); - - EXPECT_FALSE(SpanLess()(kOneTwoThreeFour, kOne)); - EXPECT_FALSE(SpanLess()(kOneTwoThreeFour, kOneTwo)); - EXPECT_TRUE(SpanLess()(kOneTwoThreeFour, kTwo)); - EXPECT_TRUE(SpanLess()(kOneTwoThreeFour, kTwoThree)); - EXPECT_TRUE(SpanLess()(kOneTwoThreeFour, kThree)); - EXPECT_FALSE(SpanLess()(kOneTwoThreeFour, kOneTwoThree)); - EXPECT_FALSE(SpanLess()(kOneTwoThreeFour, kOneTwoThreeFour)); + EXPECT_FALSE(RangeLess()(kOne, kOne)); + EXPECT_TRUE(RangeLess()(kOne, kOneTwo)); + EXPECT_TRUE(RangeLess()(kOne, kTwo)); + EXPECT_TRUE(RangeLess()(kOne, kTwoThree)); + EXPECT_TRUE(RangeLess()(kOne, kThree)); + EXPECT_TRUE(RangeLess()(kOne, kOneTwoThree)); + EXPECT_TRUE(RangeLess()(kOne, kOneTwoThreeFour)); + + EXPECT_FALSE(RangeLess()(kOneTwo, kOne)); + EXPECT_FALSE(RangeLess()(kOneTwo, kOneTwo)); + EXPECT_TRUE(RangeLess()(kOneTwo, kTwo)); + EXPECT_TRUE(RangeLess()(kOneTwo, kTwoThree)); + EXPECT_TRUE(RangeLess()(kOneTwo, kThree)); + EXPECT_TRUE(RangeLess()(kOneTwo, kOneTwoThree)); + EXPECT_TRUE(RangeLess()(kOneTwo, kOneTwoThreeFour)); + + EXPECT_FALSE(RangeLess()(kTwo, kOne)); + EXPECT_FALSE(RangeLess()(kTwo, kOneTwo)); + EXPECT_FALSE(RangeLess()(kTwo, kTwo)); + EXPECT_TRUE(RangeLess()(kTwo, kTwoThree)); + EXPECT_TRUE(RangeLess()(kTwo, kThree)); + EXPECT_FALSE(RangeLess()(kTwo, kOneTwoThree)); + EXPECT_FALSE(RangeLess()(kTwo, kOneTwoThreeFour)); + + EXPECT_FALSE(RangeLess()(kTwoThree, kOne)); + EXPECT_FALSE(RangeLess()(kTwoThree, kOneTwo)); + EXPECT_FALSE(RangeLess()(kTwoThree, kTwo)); + EXPECT_FALSE(RangeLess()(kTwoThree, kTwoThree)); + EXPECT_TRUE(RangeLess()(kTwoThree, kThree)); + EXPECT_FALSE(RangeLess()(kTwoThree, kOneTwoThree)); + EXPECT_FALSE(RangeLess()(kTwoThree, kOneTwoThreeFour)); + + EXPECT_FALSE(RangeLess()(kThree, kOne)); + EXPECT_FALSE(RangeLess()(kThree, kOneTwo)); + EXPECT_FALSE(RangeLess()(kThree, kTwo)); + EXPECT_FALSE(RangeLess()(kThree, kTwoThree)); + EXPECT_FALSE(RangeLess()(kThree, kThree)); + EXPECT_FALSE(RangeLess()(kThree, kOneTwoThree)); + EXPECT_FALSE(RangeLess()(kThree, kOneTwoThreeFour)); + + EXPECT_FALSE(RangeLess()(kOneTwoThree, kOne)); + EXPECT_FALSE(RangeLess()(kOneTwoThree, kOneTwo)); + EXPECT_TRUE(RangeLess()(kOneTwoThree, kTwo)); + EXPECT_TRUE(RangeLess()(kOneTwoThree, kTwoThree)); + EXPECT_TRUE(RangeLess()(kOneTwoThree, kThree)); + EXPECT_FALSE(RangeLess()(kOneTwoThree, kOneTwoThree)); + EXPECT_TRUE(RangeLess()(kOneTwoThree, kOneTwoThreeFour)); + + EXPECT_FALSE(RangeLess()(kOneTwoThreeFour, kOne)); + EXPECT_FALSE(RangeLess()(kOneTwoThreeFour, kOneTwo)); + EXPECT_TRUE(RangeLess()(kOneTwoThreeFour, kTwo)); + EXPECT_TRUE(RangeLess()(kOneTwoThreeFour, kTwoThree)); + EXPECT_TRUE(RangeLess()(kOneTwoThreeFour, kThree)); + EXPECT_FALSE(RangeLess()(kOneTwoThreeFour, kOneTwoThree)); + EXPECT_FALSE(RangeLess()(kOneTwoThreeFour, kOneTwoThreeFour)); } TEST(U2fParsingUtils, Materialize) { diff --git a/chromium/device/fido/fido_request_handler.h b/chromium/device/fido/fido_request_handler.h index b1ac9025864..f54b79b0e1d 100644 --- a/chromium/device/fido/fido_request_handler.h +++ b/chromium/device/fido/fido_request_handler.h @@ -25,10 +25,10 @@ namespace device { template <class Response> class FidoRequestHandler : public FidoRequestHandlerBase { public: - using CompletionCallback = - base::OnceCallback<void(FidoReturnCode status_code, - base::Optional<Response> response_data, - FidoTransportProtocol transport_used)>; + using CompletionCallback = base::OnceCallback<void( + FidoReturnCode status_code, + base::Optional<Response> response_data, + base::Optional<FidoTransportProtocol> transport_used)>; // The |available_transports| should be the intersection of transports // supported by the client and allowed by the relying party. diff --git a/chromium/device/fido/fido_request_handler_base.cc b/chromium/device/fido/fido_request_handler_base.cc index 6164f843369..d07ddc2f201 100644 --- a/chromium/device/fido/fido_request_handler_base.cc +++ b/chromium/device/fido/fido_request_handler_base.cc @@ -14,6 +14,7 @@ #include "base/threading/sequenced_task_runner_handle.h" #include "build/build_config.h" #include "device/fido/ble_adapter_manager.h" +#include "device/fido/fido_discovery_factory.h" #include "services/service_manager/public/cpp/connector.h" namespace device { @@ -57,7 +58,16 @@ FidoRequestHandlerBase::TransportAvailabilityObserver:: FidoRequestHandlerBase::FidoRequestHandlerBase( service_manager::Connector* connector, const base::flat_set<FidoTransportProtocol>& available_transports) - : weak_factory_(this) { + : connector_(connector), weak_factory_(this) { +#if defined(OS_WIN) + InitDiscoveriesWin(available_transports); +#else + InitDiscoveries(available_transports); +#endif // !defined(OS_WIN) +} + +void FidoRequestHandlerBase::InitDiscoveries( + const base::flat_set<FidoTransportProtocol>& available_transports) { // The number of times |notify_observer_callback_| needs to be invoked before // Observer::OnTransportAvailabilityEnumerated is dispatched. Essentially this // is used to wait until all the parts of |transport_availability_info_| are @@ -84,7 +94,7 @@ FidoRequestHandlerBase::FidoRequestHandlerBase( continue; } - auto discovery = FidoDeviceDiscovery::Create(transport, connector); + auto discovery = FidoDiscoveryFactory::Create(transport, connector_); if (discovery == nullptr) { // This can occur in tests when a ScopedVirtualU2fDevice is in effect and // HID transports are not configured. @@ -115,6 +125,55 @@ FidoRequestHandlerBase::FidoRequestHandlerBase( weak_factory_.GetWeakPtr())); } +#if defined(OS_WIN) +void FidoRequestHandlerBase::InitDiscoveriesWin( + const base::flat_set<FidoTransportProtocol>& available_transports) { + // Try to instantiate the discovery for proxying requests to the native + // Windows WebAuthn API; or fall back to using the regular device transport + // discoveries if the API is unavailable. + auto discovery = FidoDiscoveryFactory::MaybeCreateWinWebAuthnApiDiscovery(); + if (!discovery) { + InitDiscoveries(available_transports); + return; + } + + // The Windows WebAuthn API is available. On this platform, communicating + // with authenticator devices directly is blocked by the OS, so we need to go + // through the native API instead. No device discoveries may be instantiated. + // + // The Windows API supports USB, NFC, BLE and platform authenticators, but + // not caBLE. Communicating with caBLE devices directly is subject to the + // same block by the OS, so this platform is without caBLE support for now. + // + // TODO(martinkr): Re-enable the caBLE discovery once caBLE has moved to a + // different UUID. See crbug.com/905111. + + discovery->set_observer(this); + discoveries_.push_back(std::move(discovery)); + + // Tell the embedder to not render a UI and ignore all future callbacks. Also + // don't report any available transports; the embedder is not supposed to use + // this information anyway. + transport_availability_info_.disable_embedder_ui = true; + transport_availability_info_.available_transports = {}; + + // The number of times |notify_observer_callback_| needs to be invoked before + // Observer::OnTransportAvailabilityEnumerated is dispatched. Essentially this + // is used to wait until all the parts of |transport_availability_info_| are + // filled out. In the case of Windows, there are no transport discoveries to + // wait for, so the |notify_observer_callback_| is only invoked in: + // 1) SetPlatformAuthenticatorOrMarkUnavailable(). + // 2) set_observer(). + constexpr size_t transport_info_callback_count = 2u; + + notify_observer_callback_ = base::BarrierClosure( + transport_info_callback_count, + base::BindOnce( + &FidoRequestHandlerBase::NotifyObserverTransportAvailability, + weak_factory_.GetWeakPtr())); +} +#endif // defined(OS_WIN) + FidoRequestHandlerBase::~FidoRequestHandlerBase() = default; void FidoRequestHandlerBase::StartAuthenticatorRequest( @@ -238,14 +297,16 @@ void FidoRequestHandlerBase::AuthenticatorIdChanged( void FidoRequestHandlerBase::AuthenticatorPairingModeChanged( FidoDiscoveryBase* discovery, - const std::string& device_id) { + const std::string& device_id, + bool is_in_pairing_mode) { DCHECK_EQ(FidoTransportProtocol::kBluetoothLowEnergy, discovery->transport()); auto it = active_authenticators_.find(device_id); if (it == active_authenticators_.end()) return; if (observer_) - observer_->FidoAuthenticatorPairingModeChanged(device_id); + observer_->FidoAuthenticatorPairingModeChanged(device_id, + is_in_pairing_mode); } void FidoRequestHandlerBase::AddAuthenticator( @@ -284,8 +345,9 @@ void FidoRequestHandlerBase::SetPlatformAuthenticatorOrMarkUnavailable( FidoTransportProtocol::kInternal)) { DCHECK(platform_authenticator_info->authenticator); DCHECK( - (platform_authenticator_info->authenticator->AuthenticatorTransport() == - FidoTransportProtocol::kInternal)); + platform_authenticator_info->authenticator->AuthenticatorTransport() && + *platform_authenticator_info->authenticator->AuthenticatorTransport() == + FidoTransportProtocol::kInternal); transport_availability_info_.has_recognized_mac_touch_id_credential = platform_authenticator_info->has_recognized_mac_touch_id_credential; platform_authenticator_ = diff --git a/chromium/device/fido/fido_request_handler_base.h b/chromium/device/fido/fido_request_handler_base.h index 76a301cc513..125984e4941 100644 --- a/chromium/device/fido/fido_request_handler_base.h +++ b/chromium/device/fido/fido_request_handler_base.h @@ -19,8 +19,9 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/strings/string_piece_forward.h" +#include "build/build_config.h" #include "device/fido/fido_device_authenticator.h" -#include "device/fido/fido_device_discovery.h" +#include "device/fido/fido_discovery_base.h" #include "device/fido/fido_transport_protocol.h" namespace service_manager { @@ -87,6 +88,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase bool has_recognized_mac_touch_id_credential = false; bool is_ble_powered = false; bool can_power_on_ble_adapter = false; + + // If true, dispatch of the request cannot be controlled by + // the embedder. The embedder must not display a UI for this + // request and must ignore all subsequent invocations of the + // TransportAvailabilityObserver interface methods. + bool disable_embedder_ui = false; }; class COMPONENT_EXPORT(DEVICE_FIDO) TransportAvailabilityObserver { @@ -117,7 +124,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase base::StringPiece old_authenticator_id, std::string new_authenticator_id) = 0; virtual void FidoAuthenticatorPairingModeChanged( - base::StringPiece authenticator_id) = 0; + base::StringPiece authenticator_id, + bool is_in_pairing_mode) = 0; }; // TODO(https://crbug.com/769631): Remove the dependency on Connector once @@ -179,6 +187,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase return transport_availability_info_; } + const AuthenticatorMap& AuthenticatorsForTesting() { + return active_authenticators_; + } + protected: // Subclasses implement this method to dispatch their request onto the given // FidoAuthenticator. The FidoAuthenticator is owned by this @@ -196,6 +208,13 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase private: friend class FidoRequestHandlerTest; + void InitDiscoveries( + const base::flat_set<FidoTransportProtocol>& available_transports); +#if defined(OS_WIN) + void InitDiscoveriesWin( + const base::flat_set<FidoTransportProtocol>& available_transports); +#endif + // FidoDiscoveryBase::Observer void AuthenticatorAdded(FidoDiscoveryBase* discovery, FidoAuthenticator* authenticator) final; @@ -205,7 +224,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase const std::string& previous_id, std::string new_id) final; void AuthenticatorPairingModeChanged(FidoDiscoveryBase* discovery, - const std::string& device_id) final; + const std::string& device_id, + bool is_in_pairing_mode) final; void AddAuthenticator(FidoAuthenticator* authenticator); void NotifyObserverTransportAvailability(); @@ -226,6 +246,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase // TODO(martinkr): Inject platform authenticators through a new // FidoDiscoveryBase specialization and hold ownership there. std::unique_ptr<FidoAuthenticator> platform_authenticator_; + service_manager::Connector* const connector_; base::WeakPtrFactory<FidoRequestHandlerBase> weak_factory_; DISALLOW_COPY_AND_ASSIGN(FidoRequestHandlerBase); diff --git a/chromium/device/fido/fido_request_handler_unittest.cc b/chromium/device/fido/fido_request_handler_unittest.cc index aea5531777e..bb4fea50748 100644 --- a/chromium/device/fido/fido_request_handler_unittest.cc +++ b/chromium/device/fido/fido_request_handler_unittest.cc @@ -33,14 +33,10 @@ namespace { using FakeTaskCallback = base::OnceCallback<void(CtapDeviceResponseCode status_code, base::Optional<std::vector<uint8_t>>)>; -using FakeHandlerCallback = - base::OnceCallback<void(FidoReturnCode status_code, - base::Optional<std::vector<uint8_t>> response_data, - FidoTransportProtocol)>; -using FakeHandlerCallbackReceiver = - test::StatusAndValuesCallbackReceiver<FidoReturnCode, - base::Optional<std::vector<uint8_t>>, - FidoTransportProtocol>; +using FakeHandlerCallbackReceiver = test::StatusAndValuesCallbackReceiver< + FidoReturnCode, + base::Optional<std::vector<uint8_t>>, + base::Optional<FidoTransportProtocol>>; enum class FakeTaskResponse : uint8_t { kSuccess = 0x00, @@ -104,8 +100,8 @@ class TestTransportAvailabilityObserver authenticator_id_change_notification_receiver_.callback().Run( std::move(new_authenticator_id)); } - void FidoAuthenticatorPairingModeChanged( - base::StringPiece authenticator_id) override {} + void FidoAuthenticatorPairingModeChanged(base::StringPiece authenticator_id, + bool is_in_pairing_mode) override {} private: TransportAvailabilityNotificationReceiver @@ -161,34 +157,35 @@ class FakeFidoTask : public FidoTask { base::WeakPtrFactory<FakeFidoTask> weak_factory_; }; -class FakeFidoAuthenticator : public FidoDeviceAuthenticator { - public: - explicit FakeFidoAuthenticator(std::unique_ptr<FidoDevice> device) - : FidoDeviceAuthenticator(std::move(device)) {} - - void RunFakeTask(FakeTaskCallback callback) { - SetTaskForTesting( - std::make_unique<FakeFidoTask>(device(), std::move(callback))); - } -}; - class FakeFidoRequestHandler : public FidoRequestHandler<std::vector<uint8_t>> { public: - FakeFidoRequestHandler(const base::flat_set<FidoTransportProtocol>& protocols, - FakeHandlerCallback callback) - : FidoRequestHandler(nullptr /* connector */, - protocols, - std::move(callback)), + FakeFidoRequestHandler(service_manager::Connector* connector, + const base::flat_set<FidoTransportProtocol>& protocols, + CompletionCallback callback) + : FidoRequestHandler(connector, protocols, std::move(callback)), weak_factory_(this) { Start(); } + FakeFidoRequestHandler(const base::flat_set<FidoTransportProtocol>& protocols, + CompletionCallback callback) + : FakeFidoRequestHandler(nullptr /* connector */, + protocols, + std::move(callback)) {} ~FakeFidoRequestHandler() override = default; void DispatchRequest(FidoAuthenticator* authenticator) override { - static_cast<FakeFidoAuthenticator*>(authenticator) - ->RunFakeTask( - base::BindOnce(&FakeFidoRequestHandler::OnAuthenticatorResponse, - weak_factory_.GetWeakPtr(), authenticator)); + // FidoRequestHandlerTest uses ScopedFakeDiscovery to inject mock devices + // that get wrapped in a FidoDeviceAuthenticator, so we can safely cast + // here. + auto* device_authenticator = + static_cast<FidoDeviceAuthenticator*>(authenticator); + // Instead of sending a real CTAP request, send an empty byte array. Note + // that during discovery, the device already has received a GetInfo command + // at this point. + device_authenticator->SetTaskForTesting(std::make_unique<FakeFidoTask>( + device_authenticator->device(), + base::BindOnce(&FakeFidoRequestHandler::OnAuthenticatorResponse, + weak_factory_.GetWeakPtr(), authenticator))); } private: @@ -507,7 +504,7 @@ TEST_F(FidoRequestHandlerTest, TestSetPlatformAuthenticator) { CreateFakeSuccessDeviceResponse()); device->SetDeviceTransport(FidoTransportProtocol::kInternal); auto authenticator = - std::make_unique<FakeFidoAuthenticator>(std::move(device)); + std::make_unique<FidoDeviceAuthenticator>(std::move(device)); TestTransportAvailabilityObserver observer; auto request_handler = std::make_unique<FakeFidoRequestHandler>( @@ -543,7 +540,7 @@ TEST_F(FidoRequestHandlerTest, CreateFakeSuccessDeviceResponse()); device->SetDeviceTransport(FidoTransportProtocol::kInternal); auto authenticator = - std::make_unique<FakeFidoAuthenticator>(std::move(device)); + std::make_unique<FidoDeviceAuthenticator>(std::move(device)); TestTransportAvailabilityObserver observer; auto request_handler = std::make_unique<FakeFidoRequestHandler>( diff --git a/chromium/device/fido/fido_test_data.h b/chromium/device/fido/fido_test_data.h index aac7760d294..95192fdeace 100644 --- a/chromium/device/fido/fido_test_data.h +++ b/chromium/device/fido/fido_test_data.h @@ -19,41 +19,49 @@ namespace test_data { // Sample U2F register request parameters used in example 6 of the CTAP spec. // https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#using-the-ctap2-authenticatormakecredential-command-with-ctap1-u2f-authenticators constexpr uint8_t kChallengeParameter[] = { - 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xEC, 0x17, 0x20, 0x2E, 0x42, - 0x50, 0x5F, 0x8E, 0xD2, 0xB1, 0x6A, 0xE2, 0x2F, 0x16, 0xBB, 0x05, - 0xB8, 0x8C, 0x25, 0xDB, 0x9E, 0x60, 0x26, 0x45, 0xF1, 0x41, + // kClientDataHash + 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, + 0xea, 0xfa, 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, + 0x44, 0x3, 0xa2, 0xe, 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, }; +// SHA256(kRelyingPartyId) constexpr uint8_t kApplicationParameter[] = { 0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7, 0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF, 0x01, 0x3D, 0xE9, 0x6D, 0x4E, 0xFB, 0x17, 0xDE, }; +// SHA256(kAppId) constexpr uint8_t kAlternativeApplicationParameter[] = { - 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, - 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, - 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, -}; + 0x91, 0x14, 0xf2, 0xc9, 0xf2, 0x0b, 0x30, 0x7b, 0x49, 0xac, 0x96, + 0x2c, 0xf7, 0x6e, 0xa2, 0x08, 0x3c, 0xa7, 0xa7, 0x8d, 0xe1, 0xcd, + 0x4e, 0x82, 0x5e, 0xca, 0x3a, 0x98, 0x0f, 0x1a, 0x25, 0x6d}; + +constexpr char kClientDataJson[] = + R"({"challenge":"foobar","new_keys_may_be_added_here":"do not compare clientDataJSON against a template. See https://goo.gl/yabPex","origin":"https://google.com","type":"webauthn.create"})"; +// SHA-256 hash of kClientDataJson. constexpr uint8_t kClientDataHash[] = { - 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xec, 0x17, 0x20, 0x2e, 0x42, - 0x50, 0x5f, 0x8e, 0xd2, 0xb1, 0x6a, 0xe2, 0x2f, 0x16, 0xbb, 0x05, - 0xb8, 0x8c, 0x25, 0xdb, 0x9e, 0x60, 0x26, 0x45, 0xf1, 0x41}; + 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, + 0xea, 0xfa, 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, + 0x44, 0x3, 0xa2, 0xe, 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, +}; constexpr uint8_t kUserId[] = {0x10, 0x98, 0x23, 0x72, 0x35, 0x40, 0x98, 0x72}; constexpr char kRelyingPartyId[] = "acme.com"; +constexpr char kAppId[] = "acme.com/"; constexpr uint8_t kU2fRegisterCommandApduWithIndividualAttestation[] = { // CLA, INS, P1, P2 APDU instructions 0x00, 0x01, 0x83, 0x00, // Data length in 3 bytes in big endian order 0x00, 0x00, 0x40, - // Challenge parameter - 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xEC, 0x17, 0x20, 0x2E, 0x42, 0x50, - 0x5F, 0x8E, 0xD2, 0xB1, 0x6A, 0xE2, 0x2F, 0x16, 0xBB, 0x05, 0xB8, 0x8C, - 0x25, 0xDB, 0x9E, 0x60, 0x26, 0x45, 0xF1, 0x41, + // Challenge parameter -- see kClientDataHash + 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, 0xea, 0xfa, + 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, 0xa2, 0xe, + 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // Application parameter 0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7, 0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF, @@ -67,10 +75,10 @@ constexpr uint8_t kU2fRegisterCommandApdu[] = { 0x00, 0x01, 0x03, 0x00, // Data length in 3 bytes in big endian order. 0x00, 0x00, 0x40, - // Challenge parameter - 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xEC, 0x17, 0x20, 0x2E, 0x42, 0x50, - 0x5F, 0x8E, 0xD2, 0xB1, 0x6A, 0xE2, 0x2F, 0x16, 0xBB, 0x05, 0xB8, 0x8C, - 0x25, 0xDB, 0x9E, 0x60, 0x26, 0x45, 0xF1, 0x41, + // Challenge parameter -- see kClientDataHash + 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, 0xea, 0xfa, + 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, 0xa2, 0xe, + 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // Application parameter 0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7, 0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF, @@ -111,10 +119,10 @@ constexpr uint8_t kU2fCheckOnlySignCommandApduWithKeyAlpha[] = { 0x00, 0x02, 0x07, 0x00, // Data Length (3 bytes in big endian order) 0x00, 0x00, 0x42, - // Challenge parameter - 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xEC, 0x17, 0x20, 0x2E, 0x42, 0x50, - 0x5F, 0x8E, 0xD2, 0xB1, 0x6A, 0xE2, 0x2F, 0x16, 0xBB, 0x05, 0xB8, 0x8C, - 0x25, 0xDB, 0x9E, 0x60, 0x26, 0x45, 0xF1, 0x41, + // Challenge parameter -- see kClientDataHash + 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, 0xea, 0xfa, + 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, 0xa2, 0xe, + 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // Application parameter 0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7, 0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF, @@ -132,10 +140,10 @@ constexpr uint8_t kU2fCheckOnlySignCommandApduWithKeyBeta[] = { 0x00, 0x02, 0x07, 0x00, // Data Length (3 bytes in big endian order) 0x00, 0x00, 0x42, - // Challenge parameter - 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xEC, 0x17, 0x20, 0x2E, 0x42, 0x50, - 0x5F, 0x8E, 0xD2, 0xB1, 0x6A, 0xE2, 0x2F, 0x16, 0xBB, 0x05, 0xB8, 0x8C, - 0x25, 0xDB, 0x9E, 0x60, 0x26, 0x45, 0xF1, 0x41, + // Challenge parameter -- see kClientDataHash + 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, 0xea, 0xfa, + 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, 0xa2, 0xe, + 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // Application parameter 0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7, 0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF, @@ -152,10 +160,10 @@ constexpr uint8_t kU2fCheckOnlySignCommandApduWithKeyGamma[] = { 0x00, 0x02, 0x07, 0x00, // Data Length (3 bytes in big endian order) 0x00, 0x00, 0x42, - // Challenge parameter - 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xEC, 0x17, 0x20, 0x2E, 0x42, 0x50, - 0x5F, 0x8E, 0xD2, 0xB1, 0x6A, 0xE2, 0x2F, 0x16, 0xBB, 0x05, 0xB8, 0x8C, - 0x25, 0xDB, 0x9E, 0x60, 0x26, 0x45, 0xF1, 0x41, + // Challenge parameter -- see kClientDataHash + 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, 0xea, 0xfa, + 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, 0xa2, 0xe, + 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // Application parameter 0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7, 0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF, @@ -173,10 +181,10 @@ constexpr uint8_t kU2fSignCommandApduWithKeyAlpha[] = { 0x00, 0x02, 0x03, 0x00, // Data Length (3 bytes in big endian order) 0x00, 0x00, 0x42, - // Challenge parameter - 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xEC, 0x17, 0x20, 0x2E, 0x42, 0x50, - 0x5F, 0x8E, 0xD2, 0xB1, 0x6A, 0xE2, 0x2F, 0x16, 0xBB, 0x05, 0xB8, 0x8C, - 0x25, 0xDB, 0x9E, 0x60, 0x26, 0x45, 0xF1, 0x41, + // Challenge parameter -- see kClientDataHash + 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, 0xea, 0xfa, + 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, 0xa2, 0xe, + 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // Application parameter 0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7, 0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF, @@ -194,10 +202,10 @@ constexpr uint8_t kU2fSignCommandApduWithKeyBeta[] = { 0x00, 0x02, 0x03, 0x00, // Data Length (3 bytes in big endian order) 0x00, 0x00, 0x42, - // Challenge parameter - 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xEC, 0x17, 0x20, 0x2E, 0x42, 0x50, - 0x5F, 0x8E, 0xD2, 0xB1, 0x6A, 0xE2, 0x2F, 0x16, 0xBB, 0x05, 0xB8, 0x8C, - 0x25, 0xDB, 0x9E, 0x60, 0x26, 0x45, 0xF1, 0x41, + // Challenge parameter -- see kClientDataHash + 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, 0xea, 0xfa, + 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, 0xa2, 0xe, + 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // Application parameter 0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7, 0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF, @@ -215,10 +223,10 @@ constexpr uint8_t kU2fSignCommandApduWithKeyGamma[] = { 0x00, 0x02, 0x03, 0x00, // Data Length (3 bytes in big endian order) 0x00, 0x00, 0x42, - // Challenge parameter - 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xEC, 0x17, 0x20, 0x2E, 0x42, 0x50, - 0x5F, 0x8E, 0xD2, 0xB1, 0x6A, 0xE2, 0x2F, 0x16, 0xBB, 0x05, 0xB8, 0x8C, - 0x25, 0xDB, 0x9E, 0x60, 0x26, 0x45, 0xF1, 0x41, + // Challenge parameter -- see kClientDataHash + 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, 0xea, 0xfa, + 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, 0xa2, 0xe, + 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // Application parameter 0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7, 0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF, @@ -236,10 +244,10 @@ constexpr uint8_t kU2fSignCommandApdu[] = { 0x00, 0x02, 0x03, 0x00, // Data Length (3 bytes in big endian order) 0x00, 0x00, 0x81, - // Challenge parameter - 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xEC, 0x17, 0x20, 0x2E, 0x42, 0x50, - 0x5F, 0x8E, 0xD2, 0xB1, 0x6A, 0xE2, 0x2F, 0x16, 0xBB, 0x05, 0xB8, 0x8C, - 0x25, 0xDB, 0x9E, 0x60, 0x26, 0x45, 0xF1, 0x41, + // Challenge parameter -- see kClientDataHash + 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, 0xea, 0xfa, + 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, 0xa2, 0xe, + 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // Application parameter 0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7, 0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF, @@ -262,10 +270,10 @@ constexpr uint8_t kU2fCheckOnlySignCommandApdu[] = { 0x00, 0x02, 0x07, 0x00, // Data Length (3 bytes in big endian order). 0x00, 0x00, 0x81, - // Challenge parameter - 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xEC, 0x17, 0x20, 0x2E, 0x42, 0x50, - 0x5F, 0x8E, 0xD2, 0xB1, 0x6A, 0xE2, 0x2F, 0x16, 0xBB, 0x05, 0xB8, 0x8C, - 0x25, 0xDB, 0x9E, 0x60, 0x26, 0x45, 0xF1, 0x41, + // Challenge parameter -- see kClientDataHash + 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, 0xea, 0xfa, + 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, 0xa2, 0xe, + 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // Application parameter 0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7, 0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF, @@ -288,14 +296,14 @@ constexpr uint8_t kU2fSignCommandApduWithAlternativeApplicationParameter[] = { 0x00, 0x02, 0x03, 0x00, // Data Length (3 bytes in big endian order) 0x00, 0x00, 0x81, - // Challenge parameter - 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xEC, 0x17, 0x20, 0x2E, 0x42, 0x50, - 0x5F, 0x8E, 0xD2, 0xB1, 0x6A, 0xE2, 0x2F, 0x16, 0xBB, 0x05, 0xB8, 0x8C, - 0x25, 0xDB, 0x9E, 0x60, 0x26, 0x45, 0xF1, 0x41, + // Challenge parameter -- see kClientDataHash + 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, 0xea, 0xfa, + 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, 0xa2, 0xe, + 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // Alternative application parameter - 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, - 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, - 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, + 0x91, 0x14, 0xf2, 0xc9, 0xf2, 0x0b, 0x30, 0x7b, 0x49, 0xac, 0x96, 0x2c, + 0xf7, 0x6e, 0xa2, 0x08, 0x3c, 0xa7, 0xa7, 0x8d, 0xe1, 0xcd, 0x4e, 0x82, + 0x5e, 0xca, 0x3a, 0x98, 0x0f, 0x1a, 0x25, 0x6d, // Key handle length 0x40, // Key handle @@ -315,14 +323,14 @@ constexpr uint8_t 0x00, 0x02, 0x07, 0x00, // Data Length (3 bytes in big endian order). 0x00, 0x00, 0x81, - // Challenge parameter - 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xEC, 0x17, 0x20, 0x2E, 0x42, 0x50, - 0x5F, 0x8E, 0xD2, 0xB1, 0x6A, 0xE2, 0x2F, 0x16, 0xBB, 0x05, 0xB8, 0x8C, - 0x25, 0xDB, 0x9E, 0x60, 0x26, 0x45, 0xF1, 0x41, + // Challenge parameter -- see kClientDataHash + 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, 0xea, + 0xfa, 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, + 0xa2, 0xe, 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // Alternative application parameter - 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, - 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, - 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, + 0x91, 0x14, 0xf2, 0xc9, 0xf2, 0x0b, 0x30, 0x7b, 0x49, 0xac, 0x96, 0x2c, + 0xf7, 0x6e, 0xa2, 0x08, 0x3c, 0xa7, 0xa7, 0x8d, 0xe1, 0xcd, 0x4e, 0x82, + 0x5e, 0xca, 0x3a, 0x98, 0x0f, 0x1a, 0x25, 0x6d, // Key handle length 0x40, // Key handle @@ -344,10 +352,10 @@ constexpr uint8_t kU2fSignCommandWithoutKeyHandle[] = { 0x00, 0x02, 0x03, 0x00, // Data Length (3 bytes in big endian order). 0x00, 0x00, 0x61, - // Challenge parameter - 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xEC, 0x17, 0x20, 0x2E, 0x42, 0x50, - 0x5F, 0x8E, 0xD2, 0xB1, 0x6A, 0xE2, 0x2F, 0x16, 0xBB, 0x05, 0xB8, 0x8C, - 0x25, 0xDB, 0x9E, 0x60, 0x26, 0x45, 0xF1, 0x41, + // Challenge parameter -- see kClientDataHash + 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, 0xea, 0xfa, + 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, 0xa2, 0xe, + 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // Application parameter 0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7, 0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF, @@ -842,10 +850,10 @@ constexpr uint8_t kCtapMakeCredentialRequest[] = { 0xa5, // key(1) - clientDataHash 0x01, - // bytes(32) - 0x58, 0x20, 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xec, 0x17, 0x20, 0x2e, - 0x42, 0x50, 0x5f, 0x8e, 0xd2, 0xb1, 0x6a, 0xe2, 0x2f, 0x16, 0xbb, 0x05, - 0xb8, 0x8c, 0x25, 0xdb, 0x9e, 0x60, 0x26, 0x45, 0xf1, 0x41, + // bytes(32) -- see kClientDataHash + 0x58, 0x20, 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, + 0xea, 0xfa, 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, + 0xa2, 0xe, 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // key(2) - rp 0x02, // map(2) @@ -931,10 +939,10 @@ constexpr uint8_t kTestComplexCtapGetAssertionRequest[] = { 0x68, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f, 0x6d, // key(02) - client data hash 0x02, - // value - bytes(32) - 0x58, 0x20, 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xec, 0x17, 0x20, 0x2e, - 0x42, 0x50, 0x5f, 0x8e, 0xd2, 0xb1, 0x6a, 0xe2, 0x2f, 0x16, 0xbb, 0x05, - 0xb8, 0x8c, 0x25, 0xdb, 0x9e, 0x60, 0x26, 0x45, 0xf1, 0x41, + // bytes(32) -- see kClientDataHash + 0x58, 0x20, 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, + 0xea, 0xfa, 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, + 0xa2, 0xe, 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // key(03) - allow list 0x03, // value - array(2) @@ -993,10 +1001,10 @@ constexpr uint8_t kCtapGetAssertionRequest[] = { 0x68, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f, 0x6d, // key(02) - client data hash 0x02, - // value - bytes(32) - 0x58, 0x20, 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xec, 0x17, 0x20, 0x2e, - 0x42, 0x50, 0x5f, 0x8e, 0xd2, 0xb1, 0x6a, 0xe2, 0x2f, 0x16, 0xbb, 0x05, - 0xb8, 0x8c, 0x25, 0xdb, 0x9e, 0x60, 0x26, 0x45, 0xf1, 0x41, + // bytes(32) -- see kClientDataHash + 0x58, 0x20, 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, + 0xea, 0xfa, 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, + 0xa2, 0xe, 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // key(03) - allow list 0x03, // value - array(1) @@ -1029,10 +1037,10 @@ constexpr uint8_t kCtapSilentGetAssertionRequest[] = { 0x68, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f, 0x6d, // key(02) - client data hash 0x02, - // value - bytes(32) - 0x58, 0x20, 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xec, 0x17, 0x20, 0x2e, - 0x42, 0x50, 0x5f, 0x8e, 0xd2, 0xb1, 0x6a, 0xe2, 0x2f, 0x16, 0xbb, 0x05, - 0xb8, 0x8c, 0x25, 0xdb, 0x9e, 0x60, 0x26, 0x45, 0xf1, 0x41, + // bytes(32) -- see kClientDataHash + 0x58, 0x20, 0x8d, 0xd8, 0x74, 0x4d, 0x79, 0x3, 0xb0, 0xa3, 0x53, 0x8a, 0x49, + 0xea, 0xfa, 0xae, 0xc8, 0x33, 0xac, 0xbf, 0xd2, 0x85, 0xa5, 0xdf, 0x44, 0x3, + 0xa2, 0xe, 0x4e, 0x13, 0xe3, 0xd5, 0x3e, 0x50, // key(03) - allow list 0x03, // value - array(1) @@ -1170,19 +1178,42 @@ constexpr uint8_t kTestMakeCredentialResponse[] = { // key(02) - Authenticator Data 0x02, // Byte(154) - 0x58, 0x9a, 0x11, 0x94, 0x22, 0x8d, 0xa8, 0xfd, 0xbd, 0xee, 0xfd, 0x26, - 0x1b, 0xd7, 0xb6, 0x59, 0x5c, 0xfd, 0x70, 0xa5, 0x0d, 0x70, 0xc6, 0x40, - 0x7b, 0xcf, 0x01, 0x3d, 0xe9, 0x6d, 0x4e, 0xfb, 0x17, 0xde, 0x41, 0x00, - 0x00, 0x00, 0x0b, 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, - 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00, 0x10, 0x89, 0x59, 0xce, - 0xad, 0x5b, 0x5c, 0x48, 0x16, 0x4e, 0x8a, 0xbc, 0xd6, 0xd9, 0x43, 0x5c, - 0x6f, 0xa3, 0x63, 0x61, 0x6c, 0x67, 0x65, 0x45, 0x53, 0x32, 0x35, 0x36, - 0x61, 0x78, 0x58, 0x20, 0xf7, 0xc4, 0xf4, 0xa6, 0xf1, 0xd7, 0x95, 0x38, - 0xdf, 0xa4, 0xc9, 0xac, 0x50, 0x84, 0x8d, 0xf7, 0x08, 0xbc, 0x1c, 0x99, - 0xf5, 0xe6, 0x0e, 0x51, 0xb4, 0x2a, 0x52, 0x1b, 0x35, 0xd3, 0xb6, 0x9a, - 0x61, 0x79, 0x58, 0x20, 0xde, 0x7b, 0x7d, 0x6c, 0xa5, 0x64, 0xe7, 0x0e, - 0xa3, 0x21, 0xa4, 0xd5, 0xd9, 0x6e, 0xa0, 0x0e, 0xf0, 0xe2, 0xdb, 0x89, - 0xdd, 0x61, 0xd4, 0x89, 0x4c, 0x15, 0xac, 0x58, 0x5b, 0xd2, 0x36, 0x84, + 0x58, 0x9a, + // RP ID hash + 0x11, 0x94, 0x22, 0x8d, 0xa8, 0xfd, 0xbd, 0xee, 0xfd, 0x26, 0x1b, 0xd7, + 0xb6, 0x59, 0x5c, 0xfd, 0x70, 0xa5, 0x0d, 0x70, 0xc6, 0x40, 0x7b, 0xcf, + 0x01, 0x3d, 0xe9, 0x6d, 0x4e, 0xfb, 0x17, 0xde, + // Flags: user-presence + attested credential data + 0x41, + // Signature counter + 0x00, 0x00, 0x00, 0x0b, + // AAGUID + 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17, 0x11, + 0x1f, 0x9e, 0xdc, 0x7d, + // Credential ID length (16) + 0x00, 0x10, + // Credential ID + 0x89, 0x59, 0xce, 0xad, 0x5b, 0x5c, 0x48, 0x16, 0x4e, 0x8a, 0xbc, 0xd6, + 0xd9, 0x43, 0x5c, 0x6f, + // Public key in COSE_key format + // map(3) + 0xa3, + // "x" + 0x61, 0x78, + // Byte(32) + 0x58, 0x20, 0xf7, 0xc4, 0xf4, 0xa6, 0xf1, 0xd7, 0x95, 0x38, 0xdf, 0xa4, + 0xc9, 0xac, 0x50, 0x84, 0x8d, 0xf7, 0x08, 0xbc, 0x1c, 0x99, 0xf5, 0xe6, + 0x0e, 0x51, 0xb4, 0x2a, 0x52, 0x1b, 0x35, 0xd3, 0xb6, 0x9a, + // "y" + 0x61, 0x79, + // Byte(32) + 0x58, 0x20, 0xde, 0x7b, 0x7d, 0x6c, 0xa5, 0x64, 0xe7, 0x0e, 0xa3, 0x21, + 0xa4, 0xd5, 0xd9, 0x6e, 0xa0, 0x0e, 0xf0, 0xe2, 0xdb, 0x89, 0xdd, 0x61, + 0xd4, 0x89, 0x4c, 0x15, 0xac, 0x58, 0x5b, 0xd2, 0x36, 0x84, + // "alg" + 0x63, 0x61, 0x6c, 0x67, + // "ES256" + 0x65, 0x45, 0x53, 0x32, 0x35, 0x36, // Key(03) - Attestation object 0x03, // Map - Attestation object @@ -1399,19 +1430,42 @@ constexpr uint8_t kCtap2MakeCredentialCertificate[] = { 0xc5, 0xd3, 0x43, 0xcb, 0x2f, 0x11, 0x3d, 0xa2, 0x37, 0x23, 0xf3}; constexpr uint8_t kCtap2MakeCredentialAuthData[] = { + // RP ID hash 0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7, 0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF, - 0x01, 0x3D, 0xE9, 0x6D, 0x4E, 0xFB, 0x17, 0xDE, 0x41, 0x00, 0x00, 0x00, - 0x0b, 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17, - 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00, 0x10, 0x89, 0x59, 0xce, 0xad, 0x5b, - 0x5c, 0x48, 0x16, 0x4e, 0x8a, 0xbc, 0xd6, 0xd9, 0x43, 0x5c, 0x6f, 0xa3, - 0x63, 0x61, 0x6c, 0x67, 0x65, 0x45, 0x53, 0x32, 0x35, 0x36, 0x61, 0x78, + 0x01, 0x3D, 0xE9, 0x6D, 0x4E, 0xFB, 0x17, 0xDE, + // Flags: user-presence + attested-credential-data + 0x41, + // Signature counter + 0x00, 0x00, 0x00, 0x0b, + // AAGUID + 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17, 0x11, + 0x1f, 0x9e, 0xdc, 0x7d, + // Credential ID length (16) + 0x00, 0x10, + // Credential ID + 0x89, 0x59, 0xce, 0xad, 0x5b, 0x5c, 0x48, 0x16, 0x4e, 0x8a, 0xbc, 0xd6, + 0xd9, 0x43, 0x5c, 0x6f, + // Public key in COSE_key format + // map(3) + 0xa3, + // "x" + 0x61, 0x78, + // byte(32) 0x58, 0x20, 0xf7, 0xc4, 0xf4, 0xa6, 0xf1, 0xd7, 0x95, 0x38, 0xdf, 0xa4, 0xc9, 0xac, 0x50, 0x84, 0x8d, 0xf7, 0x08, 0xbc, 0x1c, 0x99, 0xf5, 0xe6, - 0x0e, 0x51, 0xb4, 0x2a, 0x52, 0x1b, 0x35, 0xd3, 0xb6, 0x9a, 0x61, 0x79, + 0x0e, 0x51, 0xb4, 0x2a, 0x52, 0x1b, 0x35, 0xd3, 0xb6, 0x9a, + // "y" + 0x61, 0x79, + // byte(32) 0x58, 0x20, 0xde, 0x7b, 0x7d, 0x6c, 0xa5, 0x64, 0xe7, 0x0e, 0xa3, 0x21, 0xa4, 0xd5, 0xd9, 0x6e, 0xa0, 0x0e, 0xf0, 0xe2, 0xdb, 0x89, 0xdd, 0x61, - 0xd4, 0x89, 0x4c, 0x15, 0xac, 0x58, 0x5b, 0xd2, 0x36, 0x84}; + 0xd4, 0x89, 0x4c, 0x15, 0xac, 0x58, 0x5b, 0xd2, 0x36, 0x84, + // "alg" + 0x63, 0x61, 0x6c, 0x67, + // "ES256" + 0x65, 0x45, 0x53, 0x32, 0x35, 0x36, +}; constexpr uint8_t kCtap2MakeCredentialSignature[] = { 0x30, 0x45, 0x02, 0x20, 0x13, 0xf7, 0x3c, 0x5d, 0x9d, 0x53, 0x0e, 0x8c, @@ -1457,16 +1511,28 @@ constexpr uint8_t kNoneAttestationResponse[] = { // Replaced device AAGUID 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // Credential information + // Credential ID length (16) 0x00, 0x10, 0x89, 0x59, 0xce, 0xad, 0x5b, 0x5c, 0x48, 0x16, 0x4e, 0x8a, - 0xbc, 0xd6, 0xd9, 0x43, 0x5c, 0x6f, 0xa3, 0x63, 0x61, 0x6c, 0x67, 0x65, - 0x45, 0x53, 0x32, 0x35, 0x36, 0x61, 0x78, 0x58, 0x20, 0xf7, 0xc4, 0xf4, - 0xa6, 0xf1, 0xd7, 0x95, 0x38, 0xdf, 0xa4, 0xc9, 0xac, 0x50, 0x84, 0x8d, - 0xf7, 0x08, 0xbc, 0x1c, 0x99, 0xf5, 0xe6, 0x0e, 0x51, 0xb4, 0x2a, 0x52, - 0x1b, 0x35, 0xd3, 0xb6, 0x9a, 0x61, 0x79, 0x58, 0x20, 0xde, 0x7b, 0x7d, - 0x6c, 0xa5, 0x64, 0xe7, 0x0e, 0xa3, 0x21, 0xa4, 0xd5, 0xd9, 0x6e, 0xa0, - 0x0e, 0xf0, 0xe2, 0xdb, 0x89, 0xdd, 0x61, 0xd4, 0x89, 0x4c, 0x15, 0xac, - 0x58, 0x5b, 0xd2, 0x36, 0x84, + 0xbc, 0xd6, 0xd9, 0x43, 0x5c, 0x6f, + // Public key in COSE_key format + // map(3) + 0xa3, + // "x" + 0x61, 0x78, + // byte(32) + 0x58, 0x20, 0xf7, 0xc4, 0xf4, 0xa6, 0xf1, 0xd7, 0x95, 0x38, 0xdf, 0xa4, + 0xc9, 0xac, 0x50, 0x84, 0x8d, 0xf7, 0x08, 0xbc, 0x1c, 0x99, 0xf5, 0xe6, + 0x0e, 0x51, 0xb4, 0x2a, 0x52, 0x1b, 0x35, 0xd3, 0xb6, 0x9a, + // "y" + 0x61, 0x79, + // byte(32) + 0x58, 0x20, 0xde, 0x7b, 0x7d, 0x6c, 0xa5, 0x64, 0xe7, 0x0e, 0xa3, 0x21, + 0xa4, 0xd5, 0xd9, 0x6e, 0xa0, 0x0e, 0xf0, 0xe2, 0xdb, 0x89, 0xdd, 0x61, + 0xd4, 0x89, 0x4c, 0x15, 0xac, 0x58, 0x5b, 0xd2, 0x36, 0x84, + // "alg" + 0x63, 0x61, 0x6c, 0x67, + // "ES256" + 0x65, 0x45, 0x53, 0x32, 0x35, 0x36, }; constexpr uint8_t kCtap2GetAssertionAuthData[] = { diff --git a/chromium/device/fido/get_assertion_handler_unittest.cc b/chromium/device/fido/get_assertion_handler_unittest.cc index 9f92be8a5e0..30391c3d161 100644 --- a/chromium/device/fido/get_assertion_handler_unittest.cc +++ b/chromium/device/fido/get_assertion_handler_unittest.cc @@ -7,8 +7,10 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/stl_util.h" #include "base/test/scoped_feature_list.h" #include "base/test/scoped_task_environment.h" +#include "build/build_config.h" #include "device/base/features.h" #include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/bluetooth/test/mock_bluetooth_adapter.h" @@ -16,16 +18,23 @@ #include "device/fido/ctap_get_assertion_request.h" #include "device/fido/device_response_converter.h" #include "device/fido/fake_fido_discovery.h" +#include "device/fido/features.h" #include "device/fido/fido_constants.h" #include "device/fido/fido_parsing_utils.h" #include "device/fido/fido_test_data.h" #include "device/fido/fido_transport_protocol.h" #include "device/fido/get_assertion_request_handler.h" +#include "device/fido/hid/fake_hid_impl_for_testing.h" #include "device/fido/mock_fido_device.h" #include "device/fido/test_callback_receiver.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(OS_WIN) +#include "device/fido/win/authenticator.h" +#include "device/fido/win/fake_webauthn_api.h" +#endif // defined(OS_WIN) + namespace device { namespace { @@ -35,7 +44,7 @@ constexpr uint8_t kBogusCredentialId[] = {0x01, 0x02, 0x03, 0x04}; using TestGetAssertionRequestCallback = test::StatusAndValuesCallbackReceiver< FidoReturnCode, base::Optional<AuthenticatorGetAssertionResponse>, - FidoTransportProtocol>; + base::Optional<FidoTransportProtocol>>; } // namespace @@ -56,14 +65,14 @@ class FidoGetAssertionHandlerTest : public ::testing::Test { CtapGetAssertionRequest CreateTestRequestWithCableExtension() { CtapGetAssertionRequest request(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); request.SetCableExtension({}); return request; } std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerU2f() { CtapGetAssertionRequest request(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); request.SetAllowList( {{CredentialType::kPublicKey, fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}}); @@ -72,7 +81,7 @@ class FidoGetAssertionHandlerTest : public ::testing::Test { std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerCtap() { CtapGetAssertionRequest request(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); request.SetAllowList({{CredentialType::kPublicKey, fido_parsing_utils::Materialize( test_data::kTestGetAssertionCredentialId)}}); @@ -183,7 +192,7 @@ class FidoGetAssertionHandlerTest : public ::testing::Test { TEST_F(FidoGetAssertionHandlerTest, TransportAvailabilityInfo) { auto request_handler = CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest( - test_data::kRelyingPartyId, test_data::kClientDataHash)); + test_data::kRelyingPartyId, test_data::kClientDataJson)); EXPECT_EQ(FidoRequestHandlerBase::RequestType::kGetAssertion, request_handler->transport_availability_info().request_type); @@ -252,7 +261,7 @@ TEST_F(FidoGetAssertionHandlerTest, TestU2fSignWithoutCtapFlag) { TEST_F(FidoGetAssertionHandlerTest, TestIncompatibleUserVerificationSetting) { auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); request.SetUserVerification(UserVerificationRequirement::kRequired); auto request_handler = CreateGetAssertionHandlerWithRequest(std::move(request)); @@ -270,7 +279,7 @@ TEST_F(FidoGetAssertionHandlerTest, TestIncompatibleUserVerificationSetting) { TEST_F(FidoGetAssertionHandlerTest, TestU2fSignRequestWithUserVerificationRequired) { auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); request.SetAllowList( {{CredentialType::kPublicKey, fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}}); @@ -289,7 +298,7 @@ TEST_F(FidoGetAssertionHandlerTest, TEST_F(FidoGetAssertionHandlerTest, IncorrectRpIdHash) { auto request_handler = CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest( - test_data::kRelyingPartyId, test_data::kClientDataHash)); + test_data::kRelyingPartyId, test_data::kClientDataJson)); discovery()->WaitForCallToStartAndSimulateSuccess(); auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(); device->ExpectCtap2CommandAndRespondWith( @@ -306,7 +315,7 @@ TEST_F(FidoGetAssertionHandlerTest, IncorrectRpIdHash) { // is not included in the allowed list. TEST_F(FidoGetAssertionHandlerTest, InvalidCredential) { CtapGetAssertionRequest request(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); request.SetAllowList( {{CredentialType::kPublicKey, fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha)}}); @@ -358,7 +367,7 @@ TEST_F(FidoGetAssertionHandlerTest, IncorrectUserEntity) { // Use a GetAssertion request with an empty allow list. auto request_handler = CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest( - test_data::kRelyingPartyId, test_data::kClientDataHash)); + test_data::kRelyingPartyId, test_data::kClientDataJson)); discovery()->WaitForCallToStartAndSimulateSuccess(); auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(); device->ExpectCtap2CommandAndRespondWith( @@ -434,7 +443,7 @@ TEST_F(FidoGetAssertionHandlerTest, TEST_F(FidoGetAssertionHandlerTest, CableDisabledIfAllowCredentialsListUndefinedButCableExtensionMissing) { CtapGetAssertionRequest request(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); ASSERT_FALSE(!!request.cable_extension()); EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true)); auto request_handler = @@ -449,7 +458,7 @@ TEST_F(FidoGetAssertionHandlerTest, TEST_F(FidoGetAssertionHandlerTest, CableDisabledIfExplicitlyAllowedButCableExtensionMissing) { CtapGetAssertionRequest request(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); ASSERT_FALSE(!!request.cable_extension()); request.SetAllowList({ {CredentialType::kPublicKey, @@ -711,4 +720,63 @@ TEST_F(FidoGetAssertionHandlerTest, get_assertion_callback().status()); } +#if defined(OS_WIN) +class GetAssertionRequestHandlerWinTest : public ::testing::Test { + protected: + base::test::ScopedTaskEnvironment scoped_task_environment_; + ScopedFakeWinWebAuthnApi scoped_fake_win_webauthn_api_; +}; + +// Verify that the request handler instantiates a HID device backed +// FidoDeviceAuthenticator or a WinNativeCrossPlatformAuthenticator, depending +// on feature flag and API availability. +TEST_F(GetAssertionRequestHandlerWinTest, TestWinUsbDiscovery) { + enum class DeviceType { + kHid, + kWinNative, + }; + const struct TestCase { + bool enable_win_webauthn_api; + bool enable_feature_flag; + DeviceType expect_device_type; + } test_cases[] = { + {false, false, DeviceType::kHid}, + {false, true, DeviceType::kHid}, + {true, false, DeviceType::kHid}, + {true, true, DeviceType::kWinNative}, + }; + size_t i = 0; + for (const auto& test : test_cases) { + SCOPED_TRACE(i++); + scoped_fake_win_webauthn_api_.set_available(test.enable_win_webauthn_api); + base::test::ScopedFeatureList scoped_feature_list; + // Feature is default off (even with API present). + if (test.enable_feature_flag) + scoped_feature_list.InitAndEnableFeature(kWebAuthUseNativeWinApi); + + // Simulate a connected HID device. + ScopedFakeHidManager fake_hid_manager; + fake_hid_manager.AddFidoHidDevice("guid"); + + TestGetAssertionRequestCallback cb; + auto handler = std::make_unique<GetAssertionRequestHandler>( + fake_hid_manager.service_manager_connector(), + base::flat_set<FidoTransportProtocol>( + {FidoTransportProtocol::kUsbHumanInterfaceDevice}), + CtapGetAssertionRequest(test_data::kRelyingPartyId, + test_data::kClientDataJson), + + cb.callback()); + scoped_task_environment_.RunUntilIdle(); + + EXPECT_EQ(1u, handler->AuthenticatorsForTesting().size()); + // Crudely distinguish authenticator type by FidoAuthenticator::GetId. + EXPECT_EQ(test.expect_device_type == DeviceType::kHid + ? "hid:guid" + : WinWebAuthnApiAuthenticator::kAuthenticatorId, + handler->AuthenticatorsForTesting().begin()->second->GetId()); + } +} +#endif // defined(OS_WIN) + } // namespace device diff --git a/chromium/device/fido/get_assertion_request_handler.cc b/chromium/device/fido/get_assertion_request_handler.cc index 78d6424e79b..94e52a680e9 100644 --- a/chromium/device/fido/get_assertion_request_handler.cc +++ b/chromium/device/fido/get_assertion_request_handler.cc @@ -13,6 +13,7 @@ #include "device/fido/authenticator_get_assertion_response.h" #include "device/fido/cable/fido_cable_discovery.h" #include "device/fido/fido_authenticator.h" +#include "device/fido/fido_discovery_factory.h" #include "device/fido/get_assertion_task.h" namespace device { @@ -70,17 +71,19 @@ bool CheckResponseCredentialIdMatchesRequestAllowList( const auto& allow_list = request.allow_list(); if (!allow_list || allow_list->empty()) { // Allow list can't be empty for authenticators w/o resident key support. - return authenticator.Options().supports_resident_key(); + return !authenticator.Options() || + authenticator.Options()->supports_resident_key(); } // Credential ID may be omitted if allow list has size 1. Otherwise, it needs // to match. - const auto transport_used = authenticator.AuthenticatorTransport(); + const auto opt_transport_used = authenticator.AuthenticatorTransport(); return (allow_list->size() == 1 && !response.credential()) || std::any_of(allow_list->cbegin(), allow_list->cend(), - [&response, transport_used](const auto& credential) { + [&response, opt_transport_used](const auto& credential) { return credential.id() == response.raw_credential_id() && - base::ContainsKey(credential.transports(), - transport_used); + (!opt_transport_used || + base::ContainsKey(credential.transports(), + *opt_transport_used)); }); } @@ -97,36 +100,22 @@ void SetCredentialIdForResponseWithEmptyCredential( } // Checks UserVerificationRequirement enum passed from the relying party is -// compatible with the authenticator, and updates the request to the -// "effective" user verification requirement. -// https://w3c.github.io/webauthn/#effective-user-verification-requirement-for-assertion +// compatible with the authenticator. bool CheckUserVerificationCompatible(FidoAuthenticator* authenticator, - CtapGetAssertionRequest* request) { - const auto uv_availability = - authenticator->Options().user_verification_availability(); + const CtapGetAssertionRequest& request) { + const auto& opt_options = authenticator->Options(); + if (!opt_options) { + // This authenticator doesn't know its capabilities yet, so we need + // to assume it can handle the request. This is the case for Windows, + // where we proxy the request to the native API. + return true; + } - switch (request->user_verification()) { - case UserVerificationRequirement::kRequired: - return uv_availability == + return request.user_verification() != + UserVerificationRequirement::kRequired || + opt_options->user_verification_availability() == AuthenticatorSupportedOptions::UserVerificationAvailability:: kSupportedAndConfigured; - - case UserVerificationRequirement::kDiscouraged: - return true; - - case UserVerificationRequirement::kPreferred: - if (uv_availability == - AuthenticatorSupportedOptions::UserVerificationAvailability:: - kSupportedAndConfigured) { - request->SetUserVerification(UserVerificationRequirement::kRequired); - } else { - request->SetUserVerification(UserVerificationRequirement::kDiscouraged); - } - return true; - } - - NOTREACHED(); - return false; } base::flat_set<FidoTransportProtocol> GetTransportsAllowedByRP( @@ -170,7 +159,7 @@ GetAssertionRequestHandler::GetAssertionRequestHandler( service_manager::Connector* connector, const base::flat_set<FidoTransportProtocol>& supported_transports, CtapGetAssertionRequest request, - SignResponseCallback completion_callback) + CompletionCallback completion_callback) : FidoRequestHandler( connector, base::STLSetIntersection<base::flat_set<FidoTransportProtocol>>( @@ -188,7 +177,7 @@ GetAssertionRequestHandler::GetAssertionRequestHandler( FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy)) { DCHECK(request_.cable_extension()); auto discovery = - FidoDeviceDiscovery::CreateCable(*request_.cable_extension()); + FidoDiscoveryFactory::CreateCable(*request_.cable_extension()); discovery->set_observer(this); discoveries().push_back(std::move(discovery)); } @@ -200,17 +189,12 @@ GetAssertionRequestHandler::~GetAssertionRequestHandler() = default; void GetAssertionRequestHandler::DispatchRequest( FidoAuthenticator* authenticator) { - // The user verification field of the request may be adjusted to the - // authenticator, so we need to make a copy. - CtapGetAssertionRequest request_copy = request_; - if (!CheckUserVerificationCompatible(authenticator, &request_copy)) { + if (!CheckUserVerificationCompatible(authenticator, request_)) return; - } authenticator->GetAssertion( - std::move(request_copy), - base::BindOnce(&GetAssertionRequestHandler::HandleResponse, - weak_factory_.GetWeakPtr(), authenticator)); + request_, base::BindOnce(&GetAssertionRequestHandler::HandleResponse, + weak_factory_.GetWeakPtr(), authenticator)); } void GetAssertionRequestHandler::HandleResponse( diff --git a/chromium/device/fido/get_assertion_request_handler.h b/chromium/device/fido/get_assertion_request_handler.h index de6e0d378f9..0f5940383ce 100644 --- a/chromium/device/fido/get_assertion_request_handler.h +++ b/chromium/device/fido/get_assertion_request_handler.h @@ -25,11 +25,6 @@ namespace device { class FidoAuthenticator; class AuthenticatorGetAssertionResponse; -using SignResponseCallback = - base::OnceCallback<void(FidoReturnCode, - base::Optional<AuthenticatorGetAssertionResponse>, - FidoTransportProtocol)>; - class COMPONENT_EXPORT(DEVICE_FIDO) GetAssertionRequestHandler : public FidoRequestHandler<AuthenticatorGetAssertionResponse> { public: @@ -37,7 +32,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) GetAssertionRequestHandler service_manager::Connector* connector, const base::flat_set<FidoTransportProtocol>& supported_transports, CtapGetAssertionRequest request_parameter, - SignResponseCallback completion_callback); + CompletionCallback completion_callback); ~GetAssertionRequestHandler() override; private: diff --git a/chromium/device/fido/get_assertion_task_unittest.cc b/chromium/device/fido/get_assertion_task_unittest.cc index 1ed026af0d3..5227452a1d2 100644 --- a/chromium/device/fido/get_assertion_task_unittest.cc +++ b/chromium/device/fido/get_assertion_task_unittest.cc @@ -63,7 +63,7 @@ TEST_F(FidoGetAssertionTaskTest, TestGetAssertionSuccess) { test_data::kTestGetAssertionResponse); CtapGetAssertionRequest request_param(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); request_param.SetAllowList({{CredentialType::kPublicKey, fido_parsing_utils::Materialize( test_data::kTestGetAssertionCredentialId)}}); @@ -88,7 +88,7 @@ TEST_F(FidoGetAssertionTaskTest, TestU2fSignSuccess) { test_data::kApduEncodedNoErrorSignResponse); CtapGetAssertionRequest request_param(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); request_param.SetAllowList( {{CredentialType::kPublicKey, fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}}); @@ -110,7 +110,7 @@ TEST_F(FidoGetAssertionTaskTest, TestSignSuccessWithFake) { auto hash = fido_parsing_utils::CreateSHA256Hash(public_key); std::vector<uint8_t> key_handle(hash.begin(), hash.end()); CtapGetAssertionRequest request_param(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); request_param.SetAllowList({{CredentialType::kPublicKey, key_handle}}); auto device = std::make_unique<VirtualCtap2Device>(); @@ -162,7 +162,7 @@ TEST_F(FidoGetAssertionTaskTest, TestU2fSignWithoutFlag) { test_data::kApduEncodedNoErrorSignResponse); CtapGetAssertionRequest request_param(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); request_param.SetAllowList( {{CredentialType::kPublicKey, fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}}); @@ -185,7 +185,7 @@ TEST_F(FidoGetAssertionTaskTest, TestIncorrectGetAssertionResponse) { auto task = std::make_unique<GetAssertionTask>( device.get(), CtapGetAssertionRequest(test_data::kRelyingPartyId, - test_data::kClientDataHash), + test_data::kClientDataJson), get_assertion_callback_receiver().callback()); get_assertion_callback_receiver().WaitForCallback(); @@ -196,7 +196,7 @@ TEST_F(FidoGetAssertionTaskTest, TestIncorrectGetAssertionResponse) { TEST_F(FidoGetAssertionTaskTest, TestU2fSignRequestWithEmptyAllowedList) { auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); auto device = MockFidoDevice::MakeU2f(); device->ExpectRequestAndRespondWith( @@ -218,14 +218,13 @@ TEST_F(FidoGetAssertionTaskTest, TestU2fSignRequestWithEmptyAllowedList) { // of valid credentials via silent authentication. TEST_F(FidoGetAssertionTaskTest, TestSilentSignInWhenAppIdExtensionPresent) { CtapGetAssertionRequest request(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); std::vector<PublicKeyCredentialDescriptor> allowed_list; allowed_list.push_back(PublicKeyCredentialDescriptor( CredentialType::kPublicKey, fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle))); - request.SetAlternativeApplicationParameter( - test_data::kAlternativeApplicationParameter); + request.SetAppId(test_data::kAppId); request.SetAllowList(std::move(allowed_list)); auto device = MockFidoDevice::MakeCtap(); @@ -245,14 +244,13 @@ TEST_F(FidoGetAssertionTaskTest, TestSilentSignInWhenAppIdExtensionPresent) { TEST_F(FidoGetAssertionTaskTest, TestU2fFallbackForAppIdExtension) { CtapGetAssertionRequest request(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); std::vector<PublicKeyCredentialDescriptor> allowed_list; allowed_list.push_back(PublicKeyCredentialDescriptor( CredentialType::kPublicKey, fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle))); - request.SetAlternativeApplicationParameter( - test_data::kAlternativeApplicationParameter); + request.SetAppId(test_data::kAppId); request.SetAllowList(std::move(allowed_list)); ::testing::InSequence s; @@ -287,15 +285,14 @@ TEST_F(FidoGetAssertionTaskTest, TestU2fFallbackForAppIdExtension) { TEST_F(FidoGetAssertionTaskTest, TestAvoidSilentSignInForCtapOnlyDevice) { CtapGetAssertionRequest request(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); std::vector<PublicKeyCredentialDescriptor> allowed_list; allowed_list.push_back(PublicKeyCredentialDescriptor( CredentialType::kPublicKey, fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle))); - request.SetAlternativeApplicationParameter( - test_data::kAlternativeApplicationParameter); + request.SetAppId(test_data::kAppId); request.SetAllowList(std::move(allowed_list)); auto device = MockFidoDevice::MakeCtap(ReadCTAPGetInfoResponse( diff --git a/chromium/device/fido/hid/fake_hid_impl_for_testing.cc b/chromium/device/fido/hid/fake_hid_impl_for_testing.cc index cf86174175a..7170324c560 100644 --- a/chromium/device/fido/hid/fake_hid_impl_for_testing.cc +++ b/chromium/device/fido/hid/fake_hid_impl_for_testing.cc @@ -7,6 +7,10 @@ #include <utility> #include "device/fido/fido_parsing_utils.h" +#include "services/device/public/mojom/constants.mojom.h" +#include "services/device/public/mojom/hid.mojom.h" +#include "services/service_manager/public/cpp/connector.h" +#include "services/service_manager/public/mojom/connector.mojom.h" namespace device { @@ -126,6 +130,20 @@ void FakeHidManager::AddBinding2(device::mojom::HidManagerRequest request) { bindings_.AddBinding(this, std::move(request)); } +void FakeHidManager::AddFidoHidDevice(std::string guid) { + auto c_info = device::mojom::HidCollectionInfo::New(); + c_info->usage = device::mojom::HidUsageAndPage::New(1, 0xf1d0); + auto device = device::mojom::HidDeviceInfo::New(); + device->guid = std::move(guid); + device->product_name = "Test Fido Device"; + device->serial_number = "123FIDO"; + device->bus_type = device::mojom::HidBusType::kHIDBusTypeUSB; + device->collections.push_back(std::move(c_info)); + device->max_input_report_size = 64; + device->max_output_report_size = 64; + AddDevice(std::move(device)); +} + void FakeHidManager::GetDevicesAndSetClient( device::mojom::HidManagerClientAssociatedPtrInfo client, GetDevicesCallback callback) { @@ -184,4 +202,15 @@ void FakeHidManager::RemoveDevice(const std::string device_guid) { devices_.erase(it); } +ScopedFakeHidManager::ScopedFakeHidManager() { + service_manager::mojom::ConnectorRequest request; + connector_ = service_manager::Connector::Create(&request); + connector_->OverrideBinderForTesting( + service_manager::ServiceFilter::ByName(device::mojom::kServiceName), + device::mojom::HidManager::Name_, + base::BindRepeating(&FakeHidManager::AddBinding, base::Unretained(this))); +} + +ScopedFakeHidManager::~ScopedFakeHidManager() = default; + } // namespace device diff --git a/chromium/device/fido/hid/fake_hid_impl_for_testing.h b/chromium/device/fido/hid/fake_hid_impl_for_testing.h index b69945adc0c..022775c0709 100644 --- a/chromium/device/fido/hid/fake_hid_impl_for_testing.h +++ b/chromium/device/fido/hid/fake_hid_impl_for_testing.h @@ -20,6 +20,10 @@ #include "services/device/public/mojom/hid.mojom.h" #include "testing/gmock/include/gmock/gmock.h" +namespace service_manager { +class Connector; +} + namespace device { class MockHidConnection : public device::mojom::HidConnection { @@ -95,6 +99,9 @@ class FakeHidManager : public device::mojom::HidManager { FakeHidManager(); ~FakeHidManager() override; + // Invoke AddDevice with a device info struct that mirrors a FIDO USB device. + void AddFidoHidDevice(std::string guid); + // device::mojom::HidManager implementation: void GetDevicesAndSetClient( device::mojom::HidManagerClientAssociatedPtrInfo client, @@ -118,6 +125,23 @@ class FakeHidManager : public device::mojom::HidManager { DISALLOW_COPY_AND_ASSIGN(FakeHidManager); }; +// ScopedFakeHidManager automatically binds itself to the device service for the +// duration of its lifetime. +class ScopedFakeHidManager : public FakeHidManager { + public: + ScopedFakeHidManager(); + ~ScopedFakeHidManager() override; + + service_manager::Connector* service_manager_connector() { + return connector_.get(); + } + + private: + std::unique_ptr<service_manager::Connector> connector_; + + DISALLOW_COPY_AND_ASSIGN(ScopedFakeHidManager); +}; + } // namespace device #endif // DEVICE_FIDO_HID_FAKE_HID_IMPL_FOR_TESTING_H_ diff --git a/chromium/device/fido/hid/fido_hid_device.cc b/chromium/device/fido/hid/fido_hid_device.cc index 9734a739218..db134be7e04 100644 --- a/chromium/device/fido/hid/fido_hid_device.cc +++ b/chromium/device/fido/hid/fido_hid_device.cc @@ -167,7 +167,8 @@ void FidoHidDevice::OnAllocateChannel(std::vector<uint8_t> nonce, auto received_nonce = base::make_span(payload).first(8); // Received a broadcast message for a different client. Disregard and continue // reading. - if (base::make_span(nonce) != received_nonce) { + if (!std::equal(nonce.begin(), nonce.end(), received_nonce.begin(), + received_nonce.end())) { auto repeating_callback = base::AdaptCallbackForRepeating(std::move(callback)); ArmTimeout(repeating_callback); diff --git a/chromium/device/fido/hid/fido_hid_discovery_unittest.cc b/chromium/device/fido/hid/fido_hid_discovery_unittest.cc index ebae8e79ea7..345e1ad9b98 100644 --- a/chromium/device/fido/hid/fido_hid_discovery_unittest.cc +++ b/chromium/device/fido/hid/fido_hid_discovery_unittest.cc @@ -25,21 +25,6 @@ using ::testing::_; namespace { -device::mojom::HidDeviceInfoPtr MakeFidoHidDevice(std::string guid) { - auto c_info = device::mojom::HidCollectionInfo::New(); - c_info->usage = device::mojom::HidUsageAndPage::New(1, 0xf1d0); - - auto u2f_device = device::mojom::HidDeviceInfo::New(); - u2f_device->guid = std::move(guid); - u2f_device->product_name = "Test Fido Device"; - u2f_device->serial_number = "123FIDO"; - u2f_device->bus_type = device::mojom::HidBusType::kHIDBusTypeUSB; - u2f_device->collections.push_back(std::move(c_info)); - u2f_device->max_input_report_size = 64; - u2f_device->max_output_report_size = 64; - return u2f_device; -} - device::mojom::HidDeviceInfoPtr MakeOtherDevice(std::string guid) { auto other_device = device::mojom::HidDeviceInfo::New(); other_device->guid = std::move(guid); @@ -56,35 +41,16 @@ MATCHER_P(IdMatches, id, "") { } // namespace class FidoHidDiscoveryTest : public ::testing::Test { - public: - base::test::ScopedTaskEnvironment& scoped_task_environment() { - return scoped_task_environment_; - } - - void SetUp() override { - fake_hid_manager_ = std::make_unique<FakeHidManager>(); - - service_manager::mojom::ConnectorRequest request; - connector_ = service_manager::Connector::Create(&request); - service_manager::Connector::TestApi test_api(connector_.get()); - test_api.OverrideBinderForTesting( - service_manager::Identity(device::mojom::kServiceName), - device::mojom::HidManager::Name_, - base::Bind(&FakeHidManager::AddBinding, - base::Unretained(fake_hid_manager_.get()))); - } - protected: base::test::ScopedTaskEnvironment scoped_task_environment_; - std::unique_ptr<service_manager::Connector> connector_; - std::unique_ptr<FakeHidManager> fake_hid_manager_; + ScopedFakeHidManager fake_hid_manager_; }; TEST_F(FidoHidDiscoveryTest, TestAddRemoveDevice) { - FidoHidDiscovery discovery(connector_.get()); + FidoHidDiscovery discovery(fake_hid_manager_.service_manager_connector()); MockFidoDiscoveryObserver observer; - fake_hid_manager_->AddDevice(MakeFidoHidDevice("known")); + fake_hid_manager_.AddFidoHidDevice("known"); EXPECT_CALL(observer, DiscoveryStarted(&discovery, true)); discovery.set_observer(&observer); @@ -93,28 +59,28 @@ TEST_F(FidoHidDiscoveryTest, TestAddRemoveDevice) { // Devices initially known to the service before discovery started should be // reported as KNOWN. EXPECT_CALL(observer, AuthenticatorAdded(&discovery, IdMatches("known"))); - scoped_task_environment().RunUntilIdle(); + scoped_task_environment_.RunUntilIdle(); // Devices added during the discovery should be reported as ADDED. EXPECT_CALL(observer, AuthenticatorAdded(&discovery, IdMatches("added"))); - fake_hid_manager_->AddDevice(MakeFidoHidDevice("added")); - scoped_task_environment().RunUntilIdle(); + fake_hid_manager_.AddFidoHidDevice("added"); + scoped_task_environment_.RunUntilIdle(); // Added non-U2F devices should not be reported at all. EXPECT_CALL(observer, AuthenticatorAdded(_, _)).Times(0); - fake_hid_manager_->AddDevice(MakeOtherDevice("other")); + fake_hid_manager_.AddDevice(MakeOtherDevice("other")); // Removed non-U2F devices should not be reported at all. EXPECT_CALL(observer, AuthenticatorRemoved(_, _)).Times(0); - fake_hid_manager_->RemoveDevice("other"); - scoped_task_environment().RunUntilIdle(); + fake_hid_manager_.RemoveDevice("other"); + scoped_task_environment_.RunUntilIdle(); // Removed U2F devices should be reported as REMOVED. EXPECT_CALL(observer, AuthenticatorRemoved(&discovery, IdMatches("known"))); EXPECT_CALL(observer, AuthenticatorRemoved(&discovery, IdMatches("added"))); - fake_hid_manager_->RemoveDevice("known"); - fake_hid_manager_->RemoveDevice("added"); - scoped_task_environment().RunUntilIdle(); + fake_hid_manager_.RemoveDevice("known"); + fake_hid_manager_.RemoveDevice("added"); + scoped_task_environment_.RunUntilIdle(); } } // namespace device diff --git a/chromium/device/fido/mac/OWNERS b/chromium/device/fido/mac/OWNERS index eac9cee5ed5..8bd04477873 100644 --- a/chromium/device/fido/mac/OWNERS +++ b/chromium/device/fido/mac/OWNERS @@ -1,5 +1,4 @@ -martinkr@chromium.org -martinkr@google.com +file://device/fido/OWNERS -# TEAM: security-dev@chromium.org # COMPONENT: Blink>WebAuthentication +# TEAM: identity-dev@chromium.org diff --git a/chromium/device/fido/mac/authenticator.h b/chromium/device/fido/mac/authenticator.h index 1728623e756..1358f3ba91e 100644 --- a/chromium/device/fido/mac/authenticator.h +++ b/chromium/device/fido/mac/authenticator.h @@ -58,9 +58,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) TouchIdAuthenticator void Cancel() override; std::string GetId() const override; base::string16 GetDisplayName() const override; - const AuthenticatorSupportedOptions& Options() const override; - FidoTransportProtocol AuthenticatorTransport() const override; + const base::Optional<AuthenticatorSupportedOptions>& Options() const override; + base::Optional<FidoTransportProtocol> AuthenticatorTransport() const override; bool IsInPairingMode() const override; + bool IsPaired() const override; base::WeakPtr<FidoAuthenticator> GetWeakPtr() override; private: diff --git a/chromium/device/fido/mac/authenticator.mm b/chromium/device/fido/mac/authenticator.mm index 2e055b83ec6..edcea5b6988 100644 --- a/chromium/device/fido/mac/authenticator.mm +++ b/chromium/device/fido/mac/authenticator.mm @@ -136,7 +136,8 @@ base::string16 TouchIdAuthenticator::GetDisplayName() const { return base::string16(); } -FidoTransportProtocol TouchIdAuthenticator::AuthenticatorTransport() const { +base::Optional<FidoTransportProtocol> +TouchIdAuthenticator::AuthenticatorTransport() const { return FidoTransportProtocol::kInternal; } @@ -155,8 +156,9 @@ AuthenticatorSupportedOptions TouchIdAuthenticatorOptions() { } // namespace -const AuthenticatorSupportedOptions& TouchIdAuthenticator::Options() const { - static const AuthenticatorSupportedOptions options = +const base::Optional<AuthenticatorSupportedOptions>& +TouchIdAuthenticator::Options() const { + static const base::Optional<AuthenticatorSupportedOptions> options = TouchIdAuthenticatorOptions(); return options; } @@ -165,6 +167,10 @@ bool TouchIdAuthenticator::IsInPairingMode() const { return false; } +bool TouchIdAuthenticator::IsPaired() const { + return false; +} + base::WeakPtr<FidoAuthenticator> TouchIdAuthenticator::GetWeakPtr() { return weak_factory_.GetWeakPtr(); } diff --git a/chromium/device/fido/mac/browsing_data_deletion_unittest.mm b/chromium/device/fido/mac/browsing_data_deletion_unittest.mm index e1a6d6b444f..4ba0c955672 100644 --- a/chromium/device/fido/mac/browsing_data_deletion_unittest.mm +++ b/chromium/device/fido/mac/browsing_data_deletion_unittest.mm @@ -16,6 +16,7 @@ #include "device/base/features.h" #include "device/fido/ctap_make_credential_request.h" #include "device/fido/fido_constants.h" +#include "device/fido/fido_test_data.h" #include "device/fido/mac/authenticator.h" #include "device/fido/mac/authenticator_config.h" #include "device/fido/mac/keychain.h" @@ -47,7 +48,6 @@ constexpr char kKeychainAccessGroup[] = constexpr char kMetadataSecret[] = "supersecret"; constexpr char kOtherMetadataSecret[] = "reallynotsosecret"; -constexpr std::array<uint8_t, kClientDataHashLength> kClientDataHash = {}; constexpr char kRpId[] = "rp.example.com"; const std::vector<uint8_t> kUserId = {10, 11, 12, 13, 14, 15}; @@ -123,7 +123,7 @@ class BrowsingDataDeletionTest : public testing::Test { protected: CtapMakeCredentialRequest MakeRequest() { return CtapMakeCredentialRequest( - kClientDataHash, PublicKeyCredentialRpEntity(kRpId), + test_data::kClientDataJson, PublicKeyCredentialRpEntity(kRpId), PublicKeyCredentialUserEntity(kUserId), PublicKeyCredentialParams( {{PublicKeyCredentialParams:: diff --git a/chromium/device/fido/mac/credential_metadata.cc b/chromium/device/fido/mac/credential_metadata.cc index 60d9feafd77..54c69ed4728 100644 --- a/chromium/device/fido/mac/credential_metadata.cc +++ b/chromium/device/fido/mac/credential_metadata.cc @@ -7,9 +7,9 @@ #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" -#include "components/cbor/cbor_reader.h" -#include "components/cbor/cbor_values.h" -#include "components/cbor/cbor_writer.h" +#include "components/cbor/reader.h" +#include "components/cbor/values.h" +#include "components/cbor/writer.h" #include "device/fido/public_key_credential_user_entity.h" #include "third_party/boringssl/src/include/openssl/digest.h" #include "third_party/boringssl/src/include/openssl/hkdf.h" @@ -19,9 +19,9 @@ namespace device { namespace fido { namespace mac { -using cbor::CBORWriter; -using cbor::CBORReader; -using cbor::CBORValue; +using cbor::Writer; +using cbor::Reader; +using cbor::Value; // static std::string CredentialMetadata::GenerateRandomSecret() { @@ -93,13 +93,12 @@ base::Optional<std::vector<uint8_t>> CredentialMetadata::SealCredentialId( // The remaining bytes are the CBOR-encoded UserEntity, encrypted with // AES-256-GCM and authenticated with the version and RP ID. - CBORValue::ArrayValue cbor_user; - cbor_user.emplace_back(CBORValue(user.id)); - cbor_user.emplace_back(CBORValue(user.name, CBORValue::Type::BYTE_STRING)); - cbor_user.emplace_back( - CBORValue(user.display_name, CBORValue::Type::BYTE_STRING)); + Value::ArrayValue cbor_user; + cbor_user.emplace_back(Value(user.id)); + cbor_user.emplace_back(Value(user.name, Value::Type::BYTE_STRING)); + cbor_user.emplace_back(Value(user.display_name, Value::Type::BYTE_STRING)); base::Optional<std::vector<uint8_t>> pt = - CBORWriter::Write(CBORValue(std::move(cbor_user))); + Writer::Write(Value(std::move(cbor_user))); if (!pt) { return base::nullopt; } @@ -138,12 +137,12 @@ CredentialMetadata::UnsealCredentialId( } // The recovered plaintext should decode into the UserEntity struct. - base::Optional<CBORValue> maybe_array = CBORReader::Read(base::make_span( + base::Optional<Value> maybe_array = Reader::Read(base::make_span( reinterpret_cast<const uint8_t*>(plaintext->data()), plaintext->size())); if (!maybe_array || !maybe_array->is_array()) { return base::nullopt; } - const CBORValue::ArrayValue& array = maybe_array->GetArray(); + const Value::ArrayValue& array = maybe_array->GetArray(); if (array.size() != 3 || !array[0].is_bytestring() || !array[1].is_bytestring() || !array[2].is_bytestring()) { return base::nullopt; diff --git a/chromium/device/fido/mac/get_assertion_operation_unittest_mac.mm b/chromium/device/fido/mac/get_assertion_operation_unittest_mac.mm index 62457f7ee97..447d4be467c 100644 --- a/chromium/device/fido/mac/get_assertion_operation_unittest_mac.mm +++ b/chromium/device/fido/mac/get_assertion_operation_unittest_mac.mm @@ -13,6 +13,7 @@ #include "base/test/scoped_task_environment.h" #include "device/fido/fido_constants.h" +#include "device/fido/fido_test_data.h" #include "device/fido/mac/make_credential_operation.h" #include "device/fido/test_callback_receiver.h" #include "testing/gmock/include/gmock/gmock.h" @@ -25,14 +26,13 @@ namespace { using test::TestCallbackReceiver; -const std::array<uint8_t, kClientDataHashLength> kClientDataHash = {}; const std::string kRpId = "rp.example.com"; const std::vector<uint8_t> kUserId = {10, 11, 12, 13, 14, 15}; const char kKeychainAccessGroup[] = "EQHXZ8M8AV.com.google.chrome.webauthn.test"; CtapGetAssertionRequest MakeTestRequest() { - return CtapGetAssertionRequest(kRpId, kClientDataHash); + return CtapGetAssertionRequest(kRpId, test_data::kClientDataJson); } bool MakeCredential() API_AVAILABLE(macos(10.12.2)) { @@ -40,7 +40,7 @@ bool MakeCredential() API_AVAILABLE(macos(10.12.2)) { base::Optional<AuthenticatorMakeCredentialResponse>> callback_receiver; auto request = CtapMakeCredentialRequest( - kClientDataHash, PublicKeyCredentialRpEntity(kRpId), + test_data::kClientDataJson, PublicKeyCredentialRpEntity(kRpId), PublicKeyCredentialUserEntity(kUserId), PublicKeyCredentialParams( {{PublicKeyCredentialParams:: diff --git a/chromium/device/fido/mac/make_credential_operation_unittest_mac.mm b/chromium/device/fido/mac/make_credential_operation_unittest_mac.mm index 183646af040..07c5f987267 100644 --- a/chromium/device/fido/mac/make_credential_operation_unittest_mac.mm +++ b/chromium/device/fido/mac/make_credential_operation_unittest_mac.mm @@ -13,6 +13,7 @@ #include "base/test/scoped_task_environment.h" #include "device/fido/fido_constants.h" +#include "device/fido/fido_test_data.h" #include "device/fido/test_callback_receiver.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -24,7 +25,6 @@ namespace { using test::TestCallbackReceiver; -const std::array<uint8_t, kClientDataHashLength> kClientDataHash = {}; const std::string kRpId = "rp.example.com"; const std::vector<uint8_t> kUserId = {10, 11, 12, 13, 14, 15}; const char kKeychainAccessGroup[] = @@ -32,7 +32,7 @@ const char kKeychainAccessGroup[] = CtapMakeCredentialRequest MakeTestRequest() { return CtapMakeCredentialRequest( - kClientDataHash, PublicKeyCredentialRpEntity(kRpId), + test_data::kClientDataJson, PublicKeyCredentialRpEntity(kRpId), PublicKeyCredentialUserEntity(kUserId), PublicKeyCredentialParams( {{PublicKeyCredentialParams:: diff --git a/chromium/device/fido/mac/util.mm b/chromium/device/fido/mac/util.mm index b91556f5643..bd891c44745 100644 --- a/chromium/device/fido/mac/util.mm +++ b/chromium/device/fido/mac/util.mm @@ -16,7 +16,7 @@ #include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_nsobject.h" #include "base/strings/string_number_conversions.h" -#include "components/cbor/cbor_writer.h" +#include "components/cbor/writer.h" #include "device/fido/ec_public_key.h" #include "device/fido/fido_constants.h" #include "device/fido/fido_parsing_utils.h" @@ -28,14 +28,15 @@ namespace mac { using base::ScopedCFTypeRef; using base::scoped_nsobject; -using cbor::CBORWriter; -using cbor::CBORValue; +using cbor::Writer; +using cbor::Value; -// WebAuthn requires an all-zero AAGUID for authenticators using -// self-attestation. -constexpr std::array<uint8_t, 16> kAaguid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00}; +// The Touch ID authenticator AAGUID value. Despite using self-attestation, +// Chrome will return this non-zero AAGUID for all MakeCredential +// responses coming from the Touch ID platform authenticator. +constexpr std::array<uint8_t, 16> kAaguid = {0xad, 0xce, 0x00, 0x02, 0x35, 0xbc, + 0xc6, 0x0a, 0x64, 0x8b, 0x0b, 0x25, + 0xf1, 0xf0, 0x55, 0x03}; // SecKeyRefToECPublicKey converts a SecKeyRef for a public key into an // equivalent |ECPublicKey| instance. It returns |nullptr| if the key cannot be diff --git a/chromium/device/fido/make_credential_handler_unittest.cc b/chromium/device/fido/make_credential_handler_unittest.cc index 9fb5b67c3fe..dd3b3188916 100644 --- a/chromium/device/fido/make_credential_handler_unittest.cc +++ b/chromium/device/fido/make_credential_handler_unittest.cc @@ -39,7 +39,7 @@ namespace { using TestMakeCredentialRequestCallback = test::StatusAndValuesCallbackReceiver< FidoReturnCode, base::Optional<AuthenticatorMakeCredentialResponse>, - FidoTransportProtocol>; + base::Optional<FidoTransportProtocol>>; } // namespace @@ -73,7 +73,7 @@ class FidoMakeCredentialHandlerTest : public ::testing::Test { std::vector<PublicKeyCredentialParams::CredentialInfo>(1)); auto request_parameter = CtapMakeCredentialRequest( - test_data::kClientDataHash, std::move(rp), std::move(user), + test_data::kClientDataJson, std::move(rp), std::move(user), std::move(credential_params)); auto handler = std::make_unique<MakeCredentialRequestHandler>( @@ -215,8 +215,7 @@ TEST_F(FidoMakeCredentialHandlerTest, U2fRegisterWithUserVerificationRequired) { auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny, - false /* require_resident_key */, + AuthenticatorAttachment::kAny, false /* require_resident_key */, UserVerificationRequirement::kRequired)); discovery()->WaitForCallToStartAndSimulateSuccess(); @@ -231,8 +230,7 @@ TEST_F(FidoMakeCredentialHandlerTest, U2fRegisterWithResidentKeyRequirement) { auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny, - true /* require_resident_key */, + AuthenticatorAttachment::kAny, true /* require_resident_key */, UserVerificationRequirement::kPreferred)); discovery()->WaitForCallToStartAndSimulateSuccess(); @@ -247,8 +245,7 @@ TEST_F(FidoMakeCredentialHandlerTest, UserVerificationRequirementNotMet) { auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny, - false /* require_resident_key */, + AuthenticatorAttachment::kAny, false /* require_resident_key */, UserVerificationRequirement::kRequired)); discovery()->WaitForCallToStartAndSimulateSuccess(); @@ -271,8 +268,7 @@ TEST_F(FidoMakeCredentialHandlerTest, AnyAttachment) { auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny, - false /* require_resident_key */, + AuthenticatorAttachment::kAny, false /* require_resident_key */, UserVerificationRequirement::kPreferred)); // MakeCredentialHandler will not dispatch the kAny request to the platform @@ -294,8 +290,7 @@ TEST_F(FidoMakeCredentialHandlerTest, CrossPlatformAttachment) { auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment:: - kCrossPlatform, + AuthenticatorAttachment::kCrossPlatform, false /* require_resident_key */, UserVerificationRequirement::kPreferred)); @@ -320,8 +315,7 @@ TEST_F(FidoMakeCredentialHandlerTest, PlatformAttachment) { auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment:: - kPlatform, + AuthenticatorAttachment::kPlatform, false /* require_resident_key */, UserVerificationRequirement::kRequired)); @@ -333,8 +327,7 @@ TEST_F(FidoMakeCredentialHandlerTest, ResidentKeyRequirementNotMet) { auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny, - true /* require_resident_key */, + AuthenticatorAttachment::kAny, true /* require_resident_key */, UserVerificationRequirement::kPreferred)); discovery()->WaitForCallToStartAndSimulateSuccess(); @@ -352,8 +345,7 @@ TEST_F(FidoMakeCredentialHandlerTest, auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment:: - kCrossPlatform, + AuthenticatorAttachment::kCrossPlatform, true /* require_resident_key */, UserVerificationRequirement::kRequired)); discovery()->WaitForCallToStartAndSimulateSuccess(); @@ -392,8 +384,7 @@ TEST_F(FidoMakeCredentialHandlerTest, auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment:: - kPlatform, + AuthenticatorAttachment::kPlatform, true /* require_resident_key */, UserVerificationRequirement::kRequired)); @@ -412,8 +403,7 @@ TEST_F(FidoMakeCredentialHandlerTest, auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment:: - kCrossPlatform, + AuthenticatorAttachment::kCrossPlatform, false /* require_resident_key */, UserVerificationRequirement::kPreferred)); discovery()->WaitForCallToStartAndSimulateSuccess(); @@ -443,8 +433,7 @@ TEST_F(FidoMakeCredentialHandlerTest, auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment:: - kPlatform, + AuthenticatorAttachment::kPlatform, true /* require_resident_key */, UserVerificationRequirement::kRequired)); @@ -463,8 +452,7 @@ TEST_F(FidoMakeCredentialHandlerTest, SupportedTransportsAreOnlyBleAndNfc) { auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment:: - kCrossPlatform, + AuthenticatorAttachment::kCrossPlatform, false /* require_resident_key */, UserVerificationRequirement::kPreferred)); @@ -475,8 +463,7 @@ TEST_F(FidoMakeCredentialHandlerTest, IncorrectRpIdHash) { auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny, - false /* require_resident_key */, + AuthenticatorAttachment::kAny, false /* require_resident_key */, UserVerificationRequirement::kPreferred)); discovery()->WaitForCallToStartAndSimulateSuccess(); @@ -503,8 +490,7 @@ TEST_F(FidoMakeCredentialHandlerTest, auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny, - true /* require_resident_key */, + AuthenticatorAttachment::kAny, true /* require_resident_key */, UserVerificationRequirement::kPreferred)); discovery()->WaitForCallToStartAndSimulateSuccess(); @@ -523,8 +509,7 @@ TEST_F(FidoMakeCredentialHandlerTest, auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny, - true /* require_resident_key */, + AuthenticatorAttachment::kAny, true /* require_resident_key */, UserVerificationRequirement::kPreferred)); discovery()->WaitForCallToStartAndSimulateSuccess(); @@ -550,8 +535,7 @@ TEST_F(FidoMakeCredentialHandlerTest, auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment:: - kPlatform, + AuthenticatorAttachment::kPlatform, false /* require_resident_key */, UserVerificationRequirement::kPreferred)); @@ -572,8 +556,7 @@ TEST_F(FidoMakeCredentialHandlerTest, auto request_handler = CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria( AuthenticatorSelectionCriteria( - AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny, - false /* require_resident_key */, + AuthenticatorAttachment::kAny, false /* require_resident_key */, UserVerificationRequirement::kPreferred)); discovery()->WaitForCallToStartAndSimulateSuccess(); diff --git a/chromium/device/fido/make_credential_request_handler.cc b/chromium/device/fido/make_credential_request_handler.cc index ef72a287562..67410bf5898 100644 --- a/chromium/device/fido/make_credential_request_handler.cc +++ b/chromium/device/fido/make_credential_request_handler.cc @@ -22,57 +22,49 @@ namespace { bool CheckIfAuthenticatorSelectionCriteriaAreSatisfied( FidoAuthenticator* authenticator, - const AuthenticatorSelectionCriteria& authenticator_selection_criteria, - CtapMakeCredentialRequest* request) { - using AuthenticatorAttachment = - AuthenticatorSelectionCriteria::AuthenticatorAttachment; + const AuthenticatorSelectionCriteria& authenticator_selection_criteria) { using UvAvailability = AuthenticatorSupportedOptions::UserVerificationAvailability; - const auto& options = authenticator->Options(); - if ((authenticator_selection_criteria.authenticator_attachement() == + const auto& opt_options = authenticator->Options(); + if (!opt_options) { + // This authenticator doesn't know its capabilities yet, so we need + // to assume it can handle the request. This is the case for Windows, + // where we proxy the request to the native API. + return true; + } + + if ((authenticator_selection_criteria.authenticator_attachment() == AuthenticatorAttachment::kPlatform && - !options.is_platform_device()) || - (authenticator_selection_criteria.authenticator_attachement() == + !opt_options->is_platform_device()) || + (authenticator_selection_criteria.authenticator_attachment() == AuthenticatorAttachment::kCrossPlatform && - options.is_platform_device())) { + opt_options->is_platform_device())) return false; - } if (authenticator_selection_criteria.require_resident_key() && - !options.supports_resident_key()) { + !opt_options->supports_resident_key()) return false; - } - request->SetResidentKeySupported( - authenticator_selection_criteria.require_resident_key()); - const auto& user_verification_requirement = - authenticator_selection_criteria.user_verification_requirement(); - if (user_verification_requirement == UserVerificationRequirement::kRequired) { - request->SetUserVerificationRequired(true); - } - - return user_verification_requirement != + return authenticator_selection_criteria.user_verification_requirement() != UserVerificationRequirement::kRequired || - options.user_verification_availability() == + opt_options->user_verification_availability() == UvAvailability::kSupportedAndConfigured; } base::flat_set<FidoTransportProtocol> GetTransportsAllowedByRP( const AuthenticatorSelectionCriteria& authenticator_selection_criteria) { - using AttachmentType = - AuthenticatorSelectionCriteria::AuthenticatorAttachment; const auto attachment_type = - authenticator_selection_criteria.authenticator_attachement(); + authenticator_selection_criteria.authenticator_attachment(); switch (attachment_type) { - case AttachmentType::kPlatform: + case AuthenticatorAttachment::kPlatform: return {FidoTransportProtocol::kInternal}; - case AttachmentType::kCrossPlatform: + case AuthenticatorAttachment::kCrossPlatform: // Cloud-assisted BLE is not yet supported for MakeCredential requests. return {FidoTransportProtocol::kUsbHumanInterfaceDevice, FidoTransportProtocol::kBluetoothLowEnergy, FidoTransportProtocol::kNearFieldCommunication}; - case AttachmentType::kAny: + case AuthenticatorAttachment::kAny: // Cloud-assisted BLE is not yet supported for MakeCredential requests. return {FidoTransportProtocol::kInternal, FidoTransportProtocol::kNearFieldCommunication, @@ -91,7 +83,7 @@ MakeCredentialRequestHandler::MakeCredentialRequestHandler( const base::flat_set<FidoTransportProtocol>& supported_transports, CtapMakeCredentialRequest request, AuthenticatorSelectionCriteria authenticator_selection_criteria, - RegisterResponseCallback completion_callback) + CompletionCallback completion_callback) : FidoRequestHandler( connector, base::STLSetIntersection<base::flat_set<FidoTransportProtocol>>( @@ -112,16 +104,23 @@ MakeCredentialRequestHandler::~MakeCredentialRequestHandler() = default; void MakeCredentialRequestHandler::DispatchRequest( FidoAuthenticator* authenticator) { - // The user verification field of the request may be adjusted to the - // authenticator, so we need to make a copy. - CtapMakeCredentialRequest request_copy = request_parameter_; if (!CheckIfAuthenticatorSelectionCriteriaAreSatisfied( - authenticator, authenticator_selection_criteria_, &request_copy)) { + authenticator, authenticator_selection_criteria_)) return; - } + + // Set the rk, uv and attachment fields, which were only initialized to + // default values up to here. TODO(martinkr): Initialize these fields earlier + // (in AuthenticatorImpl) and get rid of the separate + // AuthenticatorSelectionCriteriaParameter. + request_parameter_.SetResidentKeyRequired( + authenticator_selection_criteria_.require_resident_key()); + request_parameter_.SetUserVerification( + authenticator_selection_criteria_.user_verification_requirement()); + request_parameter_.SetAuthenticatorAttachment( + authenticator_selection_criteria_.authenticator_attachment()); authenticator->MakeCredential( - std::move(request_copy), + request_parameter_, base::BindOnce(&MakeCredentialRequestHandler::HandleResponse, weak_factory_.GetWeakPtr(), authenticator)); } @@ -157,8 +156,8 @@ void MakeCredentialRequestHandler::SetPlatformAuthenticatorOrMarkUnavailable( const bool has_transport_selection_ui = observer() && observer()->EmbedderControlsAuthenticatorDispatch( *platform_authenticator_info->authenticator); - if (authenticator_selection_criteria_.authenticator_attachement() == - AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny && + if (authenticator_selection_criteria_.authenticator_attachment() == + AuthenticatorAttachment::kAny && !has_transport_selection_ui) { platform_authenticator_info = base::nullopt; } diff --git a/chromium/device/fido/make_credential_request_handler.h b/chromium/device/fido/make_credential_request_handler.h index a5e799f0f7f..90a3102c838 100644 --- a/chromium/device/fido/make_credential_request_handler.h +++ b/chromium/device/fido/make_credential_request_handler.h @@ -27,11 +27,6 @@ namespace device { class FidoAuthenticator; class AuthenticatorMakeCredentialResponse; -using RegisterResponseCallback = - base::OnceCallback<void(FidoReturnCode, - base::Optional<AuthenticatorMakeCredentialResponse>, - FidoTransportProtocol)>; - class COMPONENT_EXPORT(DEVICE_FIDO) MakeCredentialRequestHandler : public FidoRequestHandler<AuthenticatorMakeCredentialResponse> { public: @@ -40,7 +35,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) MakeCredentialRequestHandler const base::flat_set<FidoTransportProtocol>& supported_transports, CtapMakeCredentialRequest request_parameter, AuthenticatorSelectionCriteria authenticator_criteria, - RegisterResponseCallback completion_callback); + CompletionCallback completion_callback); ~MakeCredentialRequestHandler() override; // FidoRequestHandlerBase: diff --git a/chromium/device/fido/make_credential_task.cc b/chromium/device/fido/make_credential_task.cc index 233a82fd749..1206da092cb 100644 --- a/chromium/device/fido/make_credential_task.cc +++ b/chromium/device/fido/make_credential_task.cc @@ -25,10 +25,11 @@ namespace { // is not required and the device supports U2F protocol. // TODO(hongjunchoi): Remove this once ClientPin command is implemented. // See: https://crbug.com/870892 -bool IsClientPinOptionCompatible(const FidoDevice* device, - const CtapMakeCredentialRequest& request) { - if (request.user_verification_required()) - return true; +bool ShouldUseU2fBecauseCtapRequiresClientPin( + const FidoDevice* device, + const CtapMakeCredentialRequest& request) { + if (request.user_verification() == UserVerificationRequirement::kRequired) + return false; DCHECK(device && device->device_info()); bool client_pin_set = @@ -36,7 +37,7 @@ bool IsClientPinOptionCompatible(const FidoDevice* device, AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedAndPinSet; bool supports_u2f = base::ContainsKey(device->device_info()->versions(), ProtocolVersion::kU2f); - return !client_pin_set || !supports_u2f; + return client_pin_set && supports_u2f; } } // namespace @@ -55,7 +56,8 @@ MakeCredentialTask::~MakeCredentialTask() = default; void MakeCredentialTask::StartTask() { if (base::FeatureList::IsEnabled(kNewCtap2Device) && device()->supported_protocol() == ProtocolVersion::kCtap && - IsClientPinOptionCompatible(device(), request_parameter_)) { + !request_parameter_.is_u2f_only() && + !ShouldUseU2fBecauseCtapRequiresClientPin(device(), request_parameter_)) { MakeCredential(); } else { device()->set_supported_protocol(ProtocolVersion::kU2f); diff --git a/chromium/device/fido/make_credential_task_unittest.cc b/chromium/device/fido/make_credential_task_unittest.cc index b150e876166..a63a27275f1 100644 --- a/chromium/device/fido/make_credential_task_unittest.cc +++ b/chromium/device/fido/make_credential_task_unittest.cc @@ -52,7 +52,7 @@ class FidoMakeCredentialTaskTest : public testing::Test { return std::make_unique<MakeCredentialTask>( device, CtapMakeCredentialRequest( - test_data::kClientDataHash, std::move(rp), std::move(user), + test_data::kClientDataJson, std::move(rp), std::move(user), PublicKeyCredentialParams( std::vector<PublicKeyCredentialParams::CredentialInfo>(1))), callback_receiver_.callback()); @@ -174,10 +174,10 @@ TEST_F(FidoMakeCredentialTaskTest, EnforceClientPinWhenUserVerificationSet) { PublicKeyCredentialUserEntity user( fido_parsing_utils::Materialize(test_data::kUserId)); auto request = CtapMakeCredentialRequest( - test_data::kClientDataHash, std::move(rp), std::move(user), + test_data::kClientDataJson, std::move(rp), std::move(user), PublicKeyCredentialParams( std::vector<PublicKeyCredentialParams::CredentialInfo>(1))); - request.SetUserVerificationRequired(true); + request.SetUserVerification(UserVerificationRequirement::kRequired); const auto task = std::make_unique<MakeCredentialTask>( device.get(), std::move(request), callback_receiver_.callback()); @@ -187,5 +187,32 @@ TEST_F(FidoMakeCredentialTaskTest, EnforceClientPinWhenUserVerificationSet) { EXPECT_FALSE(make_credential_callback_receiver().value()); } +TEST_F(FidoMakeCredentialTaskTest, TestU2fOnly) { + // Regardless of the device's supported protocol, it should receive a U2F + // request, because the task is instantiated in U2F-only mode. + auto device = MockFidoDevice::MakeCtap(); + + device->ExpectRequestAndRespondWith( + test_data::kU2fRegisterCommandApdu, + test_data::kApduEncodedNoErrorRegisterResponse); + + PublicKeyCredentialRpEntity rp(test_data::kRelyingPartyId); + PublicKeyCredentialUserEntity user( + fido_parsing_utils::Materialize(test_data::kUserId)); + auto request = CtapMakeCredentialRequest( + test_data::kClientDataJson, std::move(rp), std::move(user), + PublicKeyCredentialParams( + std::vector<PublicKeyCredentialParams::CredentialInfo>(1))); + request.set_is_u2f_only(true); + const auto task = std::make_unique<MakeCredentialTask>( + device.get(), std::move(request), callback_receiver_.callback()); + make_credential_callback_receiver().WaitForCallback(); + + EXPECT_EQ(CtapDeviceResponseCode::kSuccess, + make_credential_callback_receiver().status()); + EXPECT_TRUE(make_credential_callback_receiver().value()); + EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kU2f); +} + } // namespace } // namespace device diff --git a/chromium/device/fido/mock_fido_discovery_observer.h b/chromium/device/fido/mock_fido_discovery_observer.h index 883b20a3a97..ce022186b6c 100644 --- a/chromium/device/fido/mock_fido_discovery_observer.h +++ b/chromium/device/fido/mock_fido_discovery_observer.h @@ -29,8 +29,8 @@ class MockFidoDiscoveryObserver : public FidoDiscoveryBase::Observer { void(FidoDiscoveryBase*, FidoAuthenticator*)); MOCK_METHOD3(AuthenticatorIdChanged, void(FidoDiscoveryBase*, const std::string&, std::string)); - MOCK_METHOD2(AuthenticatorPairingModeChanged, - void(FidoDiscoveryBase*, const std::string&)); + MOCK_METHOD3(AuthenticatorPairingModeChanged, + void(FidoDiscoveryBase*, const std::string&, bool)); private: DISALLOW_COPY_AND_ASSIGN(MockFidoDiscoveryObserver); diff --git a/chromium/device/fido/opaque_attestation_statement.cc b/chromium/device/fido/opaque_attestation_statement.cc index 8d6da11fd9a..8edb74207ed 100644 --- a/chromium/device/fido/opaque_attestation_statement.cc +++ b/chromium/device/fido/opaque_attestation_statement.cc @@ -6,24 +6,24 @@ #include <utility> -#include "components/cbor/cbor_values.h" +#include "components/cbor/values.h" -using cbor::CBORValue; +using cbor::Value; namespace device { OpaqueAttestationStatement::OpaqueAttestationStatement( std::string attestation_format, - CBORValue attestation_statement_map) + Value attestation_statement_map) : AttestationStatement(std::move(attestation_format)), attestation_statement_map_(std::move(attestation_statement_map)) {} OpaqueAttestationStatement::~OpaqueAttestationStatement() = default; // Returns the deep copied cbor map value of |attestation_statement_map_|. -CBORValue::MapValue OpaqueAttestationStatement::GetAsCBORMap() const { +Value::MapValue OpaqueAttestationStatement::GetAsCBORMap() const { DCHECK(attestation_statement_map_.is_map()); - CBORValue::MapValue new_map; + Value::MapValue new_map; new_map.reserve(attestation_statement_map_.GetMap().size()); for (const auto& map_it : attestation_statement_map_.GetMap()) { new_map.try_emplace(new_map.end(), map_it.first.Clone(), @@ -34,9 +34,9 @@ CBORValue::MapValue OpaqueAttestationStatement::GetAsCBORMap() const { bool OpaqueAttestationStatement::IsSelfAttestation() { DCHECK(attestation_statement_map_.is_map()); - const CBORValue::MapValue& m(attestation_statement_map_.GetMap()); - const CBORValue alg("alg"); - const CBORValue sig("sig"); + const Value::MapValue& m(attestation_statement_map_.GetMap()); + const Value alg("alg"); + const Value sig("sig"); return format_ == "packed" && m.size() == 2 && m.count(std::move(alg)) == 1 && m.count(std::move(sig)) == 1; @@ -50,14 +50,14 @@ bool OpaqueAttestationStatement:: base::Optional<base::span<const uint8_t>> OpaqueAttestationStatement::GetLeafCertificate() const { DCHECK(attestation_statement_map_.is_map()); - const CBORValue::MapValue& m(attestation_statement_map_.GetMap()); - const CBORValue x5c("x5c"); + const Value::MapValue& m(attestation_statement_map_.GetMap()); + const Value x5c("x5c"); const auto it = m.find(x5c); if (it == m.end() || !it->second.is_array()) { return base::nullopt; } - const CBORValue::ArrayValue& certs = it->second.GetArray(); + const Value::ArrayValue& certs = it->second.GetArray(); if (certs.empty() || !certs[0].is_bytestring()) { return base::nullopt; } diff --git a/chromium/device/fido/opaque_attestation_statement.h b/chromium/device/fido/opaque_attestation_statement.h index 98bb2b97e3c..7f6cef00aeb 100644 --- a/chromium/device/fido/opaque_attestation_statement.h +++ b/chromium/device/fido/opaque_attestation_statement.h @@ -9,7 +9,7 @@ #include "base/component_export.h" #include "base/macros.h" -#include "components/cbor/cbor_values.h" +#include "components/cbor/values.h" #include "device/fido/attestation_statement.h" namespace device { @@ -19,17 +19,17 @@ class COMPONENT_EXPORT(DEVICE_FIDO) OpaqueAttestationStatement : public AttestationStatement { public: OpaqueAttestationStatement(std::string attestation_format, - cbor::CBORValue attestation_statement_map); + cbor::Value attestation_statement_map); ~OpaqueAttestationStatement() override; // AttestationStatement: - cbor::CBORValue::MapValue GetAsCBORMap() const override; + cbor::Value::MapValue GetAsCBORMap() const override; bool IsSelfAttestation() override; bool IsAttestationCertificateInappropriatelyIdentifying() override; base::Optional<base::span<const uint8_t>> GetLeafCertificate() const override; private: - cbor::CBORValue attestation_statement_map_; + cbor::Value attestation_statement_map_; DISALLOW_COPY_AND_ASSIGN(OpaqueAttestationStatement); }; diff --git a/chromium/device/fido/public_key_credential_descriptor.cc b/chromium/device/fido/public_key_credential_descriptor.cc index 8bce337ce19..f0afd12334d 100644 --- a/chromium/device/fido/public_key_credential_descriptor.cc +++ b/chromium/device/fido/public_key_credential_descriptor.cc @@ -18,19 +18,18 @@ constexpr char kCredentialTypeKey[] = "type"; // static base::Optional<PublicKeyCredentialDescriptor> -PublicKeyCredentialDescriptor::CreateFromCBORValue( - const cbor::CBORValue& cbor) { +PublicKeyCredentialDescriptor::CreateFromCBORValue(const cbor::Value& cbor) { if (!cbor.is_map()) { return base::nullopt; } - const cbor::CBORValue::MapValue& map = cbor.GetMap(); - auto type = map.find(cbor::CBORValue(kCredentialTypeKey)); + const cbor::Value::MapValue& map = cbor.GetMap(); + auto type = map.find(cbor::Value(kCredentialTypeKey)); if (type == map.end() || !type->second.is_string() || type->second.GetString() != kPublicKey) return base::nullopt; - auto id = map.find(cbor::CBORValue(kCredentialIdKey)); + auto id = map.find(cbor::Value(kCredentialIdKey)); if (id == map.end() || !id->second.is_bytestring()) return base::nullopt; @@ -72,12 +71,12 @@ PublicKeyCredentialDescriptor& PublicKeyCredentialDescriptor::operator=( PublicKeyCredentialDescriptor::~PublicKeyCredentialDescriptor() = default; -cbor::CBORValue PublicKeyCredentialDescriptor::ConvertToCBOR() const { - cbor::CBORValue::MapValue cbor_descriptor_map; - cbor_descriptor_map[cbor::CBORValue(kCredentialIdKey)] = cbor::CBORValue(id_); - cbor_descriptor_map[cbor::CBORValue(kCredentialTypeKey)] = - cbor::CBORValue(CredentialTypeToString(credential_type_)); - return cbor::CBORValue(std::move(cbor_descriptor_map)); +cbor::Value PublicKeyCredentialDescriptor::ConvertToCBOR() const { + cbor::Value::MapValue cbor_descriptor_map; + cbor_descriptor_map[cbor::Value(kCredentialIdKey)] = cbor::Value(id_); + cbor_descriptor_map[cbor::Value(kCredentialTypeKey)] = + cbor::Value(CredentialTypeToString(credential_type_)); + return cbor::Value(std::move(cbor_descriptor_map)); } } // namespace device diff --git a/chromium/device/fido/public_key_credential_descriptor.h b/chromium/device/fido/public_key_credential_descriptor.h index 626832666d9..1dde94a1765 100644 --- a/chromium/device/fido/public_key_credential_descriptor.h +++ b/chromium/device/fido/public_key_credential_descriptor.h @@ -12,7 +12,7 @@ #include "base/component_export.h" #include "base/containers/flat_set.h" #include "base/optional.h" -#include "components/cbor/cbor_values.h" +#include "components/cbor/values.h" #include "device/fido/fido_constants.h" #include "device/fido/fido_transport_protocol.h" @@ -25,7 +25,7 @@ namespace device { class COMPONENT_EXPORT(DEVICE_FIDO) PublicKeyCredentialDescriptor { public: static base::Optional<PublicKeyCredentialDescriptor> CreateFromCBORValue( - const cbor::CBORValue& cbor); + const cbor::Value& cbor); PublicKeyCredentialDescriptor(CredentialType credential_type, std::vector<uint8_t> id); @@ -41,7 +41,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) PublicKeyCredentialDescriptor { PublicKeyCredentialDescriptor&& other); ~PublicKeyCredentialDescriptor(); - cbor::CBORValue ConvertToCBOR() const; + cbor::Value ConvertToCBOR() const; CredentialType credential_type() const { return credential_type_; } const std::vector<uint8_t>& id() const { return id_; } diff --git a/chromium/device/fido/public_key_credential_params.cc b/chromium/device/fido/public_key_credential_params.cc index d9bc3b4a120..0ea270a8975 100644 --- a/chromium/device/fido/public_key_credential_params.cc +++ b/chromium/device/fido/public_key_credential_params.cc @@ -10,8 +10,7 @@ namespace device { // static base::Optional<PublicKeyCredentialParams> -PublicKeyCredentialParams::CreateFromCBORValue( - const cbor::CBORValue& cbor_value) { +PublicKeyCredentialParams::CreateFromCBORValue(const cbor::Value& cbor_value) { if (!cbor_value.is_array()) return base::nullopt; @@ -22,9 +21,9 @@ PublicKeyCredentialParams::CreateFromCBORValue( const auto& credential_map = credential.GetMap(); const auto credential_type_it = - credential_map.find(cbor::CBORValue(kCredentialTypeMapKey)); + credential_map.find(cbor::Value(kCredentialTypeMapKey)); const auto algorithm_type_it = - credential_map.find(cbor::CBORValue(kCredentialAlgorithmMapKey)); + credential_map.find(cbor::Value(kCredentialAlgorithmMapKey)); if (credential_type_it == credential_map.end() || !credential_type_it->second.is_string() || @@ -59,19 +58,19 @@ PublicKeyCredentialParams& PublicKeyCredentialParams::operator=( PublicKeyCredentialParams::~PublicKeyCredentialParams() = default; -cbor::CBORValue PublicKeyCredentialParams::ConvertToCBOR() const { - cbor::CBORValue::ArrayValue credential_param_array; +cbor::Value PublicKeyCredentialParams::ConvertToCBOR() const { + cbor::Value::ArrayValue credential_param_array; credential_param_array.reserve(public_key_credential_params_.size()); for (const auto& credential : public_key_credential_params_) { - cbor::CBORValue::MapValue cbor_credential_map; + cbor::Value::MapValue cbor_credential_map; cbor_credential_map.emplace(kCredentialTypeMapKey, CredentialTypeToString(credential.type)); cbor_credential_map.emplace(kCredentialAlgorithmMapKey, credential.algorithm); credential_param_array.emplace_back(std::move(cbor_credential_map)); } - return cbor::CBORValue(std::move(credential_param_array)); + return cbor::Value(std::move(credential_param_array)); } } // namespace device diff --git a/chromium/device/fido/public_key_credential_params.h b/chromium/device/fido/public_key_credential_params.h index 06d2dc3d40d..cfb6f142355 100644 --- a/chromium/device/fido/public_key_credential_params.h +++ b/chromium/device/fido/public_key_credential_params.h @@ -13,7 +13,7 @@ #include "base/macros.h" #include "base/numerics/safe_conversions.h" #include "base/optional.h" -#include "components/cbor/cbor_values.h" +#include "components/cbor/values.h" #include "device/fido/fido_constants.h" namespace device { @@ -29,7 +29,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) PublicKeyCredentialParams { }; static base::Optional<PublicKeyCredentialParams> CreateFromCBORValue( - const cbor::CBORValue& cbor_value); + const cbor::Value& cbor_value); explicit PublicKeyCredentialParams( std::vector<CredentialInfo> credential_params); @@ -39,7 +39,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) PublicKeyCredentialParams { PublicKeyCredentialParams& operator=(PublicKeyCredentialParams&& other); ~PublicKeyCredentialParams(); - cbor::CBORValue ConvertToCBOR() const; + cbor::Value ConvertToCBOR() const; const std::vector<CredentialInfo>& public_key_credential_params() const { return public_key_credential_params_; } diff --git a/chromium/device/fido/public_key_credential_rp_entity.cc b/chromium/device/fido/public_key_credential_rp_entity.cc index 7b83513ac27..c46d0dfc046 100644 --- a/chromium/device/fido/public_key_credential_rp_entity.cc +++ b/chromium/device/fido/public_key_credential_rp_entity.cc @@ -13,7 +13,7 @@ namespace device { // static base::Optional<PublicKeyCredentialRpEntity> -PublicKeyCredentialRpEntity::CreateFromCBORValue(const cbor::CBORValue& cbor) { +PublicKeyCredentialRpEntity::CreateFromCBORValue(const cbor::Value& cbor) { if (!cbor.is_map() || cbor.GetMap().size() > 3) return base::nullopt; @@ -31,9 +31,9 @@ PublicKeyCredentialRpEntity::CreateFromCBORValue(const cbor::CBORValue& cbor) { if (!is_rp_map_format_correct) return base::nullopt; - const auto& id_it = rp_map.find(cbor::CBORValue(kEntityIdMapKey)); - const auto& name_it = rp_map.find(cbor::CBORValue(kEntityNameMapKey)); - const auto& icon_it = rp_map.find(cbor::CBORValue(kIconUrlMapKey)); + const auto& id_it = rp_map.find(cbor::Value(kEntityIdMapKey)); + const auto& name_it = rp_map.find(cbor::Value(kEntityNameMapKey)); + const auto& icon_it = rp_map.find(cbor::Value(kIconUrlMapKey)); if (id_it == rp_map.end()) return base::nullopt; PublicKeyCredentialRpEntity rp(id_it->second.GetString()); @@ -76,8 +76,8 @@ PublicKeyCredentialRpEntity& PublicKeyCredentialRpEntity::SetRpIconUrl( return *this; } -cbor::CBORValue PublicKeyCredentialRpEntity::ConvertToCBOR() const { - cbor::CBORValue::MapValue rp_map; +cbor::Value PublicKeyCredentialRpEntity::ConvertToCBOR() const { + cbor::Value::MapValue rp_map; rp_map.emplace(kEntityIdMapKey, rp_id_); if (rp_name_) rp_map.emplace(kEntityNameMapKey, *rp_name_); @@ -85,7 +85,7 @@ cbor::CBORValue PublicKeyCredentialRpEntity::ConvertToCBOR() const { if (rp_icon_url_) rp_map.emplace(kIconUrlMapKey, rp_icon_url_->spec()); - return cbor::CBORValue(std::move(rp_map)); + return cbor::Value(std::move(rp_map)); } } // namespace device diff --git a/chromium/device/fido/public_key_credential_rp_entity.h b/chromium/device/fido/public_key_credential_rp_entity.h index d8c16e3a0ed..d7a7d23a28e 100644 --- a/chromium/device/fido/public_key_credential_rp_entity.h +++ b/chromium/device/fido/public_key_credential_rp_entity.h @@ -11,7 +11,7 @@ #include "base/component_export.h" #include "base/macros.h" #include "base/optional.h" -#include "components/cbor/cbor_values.h" +#include "components/cbor/values.h" #include "url/gurl.h" namespace device { @@ -22,7 +22,7 @@ namespace device { class COMPONENT_EXPORT(DEVICE_FIDO) PublicKeyCredentialRpEntity { public: static base::Optional<PublicKeyCredentialRpEntity> CreateFromCBORValue( - const cbor::CBORValue& cbor); + const cbor::Value& cbor); explicit PublicKeyCredentialRpEntity(std::string rp_id); PublicKeyCredentialRpEntity(const PublicKeyCredentialRpEntity& other); @@ -32,7 +32,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) PublicKeyCredentialRpEntity { PublicKeyCredentialRpEntity& operator=(PublicKeyCredentialRpEntity&& other); ~PublicKeyCredentialRpEntity(); - cbor::CBORValue ConvertToCBOR() const; + cbor::Value ConvertToCBOR() const; PublicKeyCredentialRpEntity& SetRpName(std::string rp_name); PublicKeyCredentialRpEntity& SetRpIconUrl(GURL icon_url); diff --git a/chromium/device/fido/public_key_credential_user_entity.cc b/chromium/device/fido/public_key_credential_user_entity.cc index 114f1a24a94..7db7cb8f06c 100644 --- a/chromium/device/fido/public_key_credential_user_entity.cc +++ b/chromium/device/fido/public_key_credential_user_entity.cc @@ -12,31 +12,30 @@ namespace device { // static base::Optional<PublicKeyCredentialUserEntity> -PublicKeyCredentialUserEntity::CreateFromCBORValue( - const cbor::CBORValue& cbor) { +PublicKeyCredentialUserEntity::CreateFromCBORValue(const cbor::Value& cbor) { if (!cbor.is_map()) return base::nullopt; - const cbor::CBORValue::MapValue& cbor_map = cbor.GetMap(); + const cbor::Value::MapValue& cbor_map = cbor.GetMap(); - auto user_id = cbor_map.find(cbor::CBORValue(kEntityIdMapKey)); + auto user_id = cbor_map.find(cbor::Value(kEntityIdMapKey)); if (user_id == cbor_map.end() || !user_id->second.is_bytestring()) return base::nullopt; PublicKeyCredentialUserEntity user(user_id->second.GetBytestring()); - auto user_name = cbor_map.find(cbor::CBORValue(kEntityNameMapKey)); + auto user_name = cbor_map.find(cbor::Value(kEntityNameMapKey)); if (user_name != cbor_map.end() && user_name->second.is_string()) { user.SetUserName(user_name->second.GetString()); } - auto user_display_name = cbor_map.find(cbor::CBORValue(kDisplayNameMapKey)); + auto user_display_name = cbor_map.find(cbor::Value(kDisplayNameMapKey)); if (user_display_name != cbor_map.end() && user_display_name->second.is_string()) { user.SetDisplayName(user_display_name->second.GetString()); } - auto user_icon_url = cbor_map.find(cbor::CBORValue(kIconUrlMapKey)); + auto user_icon_url = cbor_map.find(cbor::Value(kIconUrlMapKey)); if (user_icon_url != cbor_map.end() && user_icon_url->second.is_string()) { user.SetIconUrl(GURL(user_icon_url->second.GetString())); } @@ -62,8 +61,8 @@ PublicKeyCredentialUserEntity& PublicKeyCredentialUserEntity::operator=( PublicKeyCredentialUserEntity::~PublicKeyCredentialUserEntity() = default; -cbor::CBORValue PublicKeyCredentialUserEntity::ConvertToCBOR() const { - cbor::CBORValue::MapValue user_map; +cbor::Value PublicKeyCredentialUserEntity::ConvertToCBOR() const { + cbor::Value::MapValue user_map; user_map.emplace(kEntityIdMapKey, user_id_); if (user_name_) user_map.emplace(kEntityNameMapKey, *user_name_); @@ -72,7 +71,7 @@ cbor::CBORValue PublicKeyCredentialUserEntity::ConvertToCBOR() const { if (user_display_name_) { user_map.emplace(kDisplayNameMapKey, *user_display_name_); } - return cbor::CBORValue(std::move(user_map)); + return cbor::Value(std::move(user_map)); } PublicKeyCredentialUserEntity& PublicKeyCredentialUserEntity::SetUserName( diff --git a/chromium/device/fido/public_key_credential_user_entity.h b/chromium/device/fido/public_key_credential_user_entity.h index d2d7bf5cd04..fcd77eb9729 100644 --- a/chromium/device/fido/public_key_credential_user_entity.h +++ b/chromium/device/fido/public_key_credential_user_entity.h @@ -11,7 +11,7 @@ #include "base/component_export.h" #include "base/optional.h" -#include "components/cbor/cbor_values.h" +#include "components/cbor/values.h" #include "url/gurl.h" namespace device { @@ -23,7 +23,7 @@ namespace device { class COMPONENT_EXPORT(DEVICE_FIDO) PublicKeyCredentialUserEntity { public: static base::Optional<PublicKeyCredentialUserEntity> CreateFromCBORValue( - const cbor::CBORValue& cbor); + const cbor::Value& cbor); explicit PublicKeyCredentialUserEntity(std::vector<uint8_t> user_id); PublicKeyCredentialUserEntity(const PublicKeyCredentialUserEntity& other); @@ -34,7 +34,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) PublicKeyCredentialUserEntity { PublicKeyCredentialUserEntity&& other); ~PublicKeyCredentialUserEntity(); - cbor::CBORValue ConvertToCBOR() const; + cbor::Value ConvertToCBOR() const; PublicKeyCredentialUserEntity& SetUserName(std::string user_name); PublicKeyCredentialUserEntity& SetDisplayName(std::string display_name); PublicKeyCredentialUserEntity& SetIconUrl(GURL icon_url); diff --git a/chromium/device/fido/scoped_virtual_fido_device.cc b/chromium/device/fido/scoped_virtual_fido_device.cc index 7bfd960d045..4d5f999b54b 100644 --- a/chromium/device/fido/scoped_virtual_fido_device.cc +++ b/chromium/device/fido/scoped_virtual_fido_device.cc @@ -65,8 +65,7 @@ VirtualFidoDevice::State* ScopedVirtualFidoDevice::mutable_state() { return state_.get(); } -std::unique_ptr<FidoDeviceDiscovery> -ScopedVirtualFidoDevice::CreateFidoDiscovery( +std::unique_ptr<FidoDiscoveryBase> ScopedVirtualFidoDevice::CreateFidoDiscovery( FidoTransportProtocol transport, ::service_manager::Connector* connector) { if (transport != FidoTransportProtocol::kUsbHumanInterfaceDevice) { diff --git a/chromium/device/fido/scoped_virtual_fido_device.h b/chromium/device/fido/scoped_virtual_fido_device.h index 88bc6b4eb2e..bbe4a5dd213 100644 --- a/chromium/device/fido/scoped_virtual_fido_device.h +++ b/chromium/device/fido/scoped_virtual_fido_device.h @@ -9,7 +9,7 @@ #include "base/macros.h" #include "device/fido/fido_constants.h" -#include "device/fido/fido_device_discovery.h" +#include "device/fido/fido_discovery_factory.h" #include "device/fido/virtual_fido_device.h" namespace device { @@ -28,7 +28,7 @@ class ScopedVirtualFidoDevice VirtualFidoDevice::State* mutable_state(); protected: - std::unique_ptr<FidoDeviceDiscovery> CreateFidoDiscovery( + std::unique_ptr<FidoDiscoveryBase> CreateFidoDiscovery( FidoTransportProtocol transport, ::service_manager::Connector* connector) override; diff --git a/chromium/device/fido/u2f_command_constructor.cc b/chromium/device/fido/u2f_command_constructor.cc index c195f22735d..733d99cfeb8 100644 --- a/chromium/device/fido/u2f_command_constructor.cc +++ b/chromium/device/fido/u2f_command_constructor.cc @@ -15,7 +15,8 @@ namespace device { bool IsConvertibleToU2fRegisterCommand( const CtapMakeCredentialRequest& request) { - if (request.user_verification_required() || request.resident_key_supported()) + if (request.user_verification() == UserVerificationRequirement::kRequired || + request.resident_key_required()) return false; const auto& public_key_credential_info = diff --git a/chromium/device/fido/u2f_command_constructor_unittest.cc b/chromium/device/fido/u2f_command_constructor_unittest.cc index c44a777c29c..d567709320c 100644 --- a/chromium/device/fido/u2f_command_constructor_unittest.cc +++ b/chromium/device/fido/u2f_command_constructor_unittest.cc @@ -29,13 +29,13 @@ CtapMakeCredentialRequest ConstructMakeCredentialRequest() { .SetIconUrl(GURL("https://pics.acme.com/00/p/aBjjjpqPb.png")); return CtapMakeCredentialRequest( - test_data::kClientDataHash, std::move(rp), std::move(user), + test_data::kClientDataJson, std::move(rp), std::move(user), PublicKeyCredentialParams(PublicKeyCredentialParams( std::vector<PublicKeyCredentialParams::CredentialInfo>(1)))); } CtapGetAssertionRequest ConstructGetAssertionRequest() { - return CtapGetAssertionRequest("acme.com", test_data::kClientDataHash); + return CtapGetAssertionRequest("acme.com", test_data::kClientDataJson); } } // namespace @@ -120,7 +120,7 @@ TEST(U2fCommandConstructorTest, TestU2fRegisterCredentialAlgorithmRequirement) { .SetIconUrl(GURL("https://pics.acme.com/00/p/aBjjjpqPb.png")); CtapMakeCredentialRequest make_credential_param( - test_data::kClientDataHash, std::move(rp), std::move(user), + test_data::kClientDataJson, std::move(rp), std::move(user), PublicKeyCredentialParams({{CredentialType::kPublicKey, -257}})); EXPECT_FALSE(IsConvertibleToU2fRegisterCommand(make_credential_param)); @@ -128,14 +128,15 @@ TEST(U2fCommandConstructorTest, TestU2fRegisterCredentialAlgorithmRequirement) { TEST(U2fCommandConstructorTest, TestU2fRegisterUserVerificationRequirement) { auto make_credential_param = ConstructMakeCredentialRequest(); - make_credential_param.SetUserVerificationRequired(true); + make_credential_param.SetUserVerification( + UserVerificationRequirement::kRequired); EXPECT_FALSE(IsConvertibleToU2fRegisterCommand(make_credential_param)); } TEST(U2fCommandConstructorTest, TestU2fRegisterResidentKeyRequirement) { auto make_credential_param = ConstructMakeCredentialRequest(); - make_credential_param.SetResidentKeySupported(true); + make_credential_param.SetResidentKeyRequired(true); EXPECT_FALSE(IsConvertibleToU2fRegisterCommand(make_credential_param)); } diff --git a/chromium/device/fido/u2f_register_operation_unittest.cc b/chromium/device/fido/u2f_register_operation_unittest.cc index 2986830eab6..dacdfd3ff14 100644 --- a/chromium/device/fido/u2f_register_operation_unittest.cc +++ b/chromium/device/fido/u2f_register_operation_unittest.cc @@ -35,7 +35,7 @@ CtapMakeCredentialRequest CreateRegisterRequestWithRegisteredKeys( fido_parsing_utils::Materialize(test_data::kUserId)); CtapMakeCredentialRequest request( - test_data::kClientDataHash, std::move(rp), std::move(user), + test_data::kClientDataJson, std::move(rp), std::move(user), PublicKeyCredentialParams( std::vector<PublicKeyCredentialParams::CredentialInfo>(1))); request.SetExcludeList(std::move(registered_keys)); diff --git a/chromium/device/fido/u2f_sign_operation_unittest.cc b/chromium/device/fido/u2f_sign_operation_unittest.cc index 7b53bb83226..47ba426a96f 100644 --- a/chromium/device/fido/u2f_sign_operation_unittest.cc +++ b/chromium/device/fido/u2f_sign_operation_unittest.cc @@ -38,7 +38,7 @@ class U2fSignOperationTest : public ::testing::Test { CtapGetAssertionRequest CreateSignRequest( std::vector<std::vector<uint8_t>> key_handles) { CtapGetAssertionRequest request(test_data::kRelyingPartyId, - test_data::kClientDataHash); + test_data::kClientDataJson); std::vector<PublicKeyCredentialDescriptor> allowed_list; for (auto& key_handle : key_handles) { @@ -347,8 +347,7 @@ TEST_F(U2fSignOperationTest, SignWithCorruptedResponse) { TEST_F(U2fSignOperationTest, AlternativeApplicationParameter) { auto request = CreateSignRequest( {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}); - request.SetAlternativeApplicationParameter( - test_data::kAlternativeApplicationParameter); + request.SetAppId(test_data::kAppId); auto device = std::make_unique<MockFidoDevice>(); EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device")); @@ -387,8 +386,7 @@ TEST_F(U2fSignOperationTest, AlternativeApplicationParameter) { TEST_F(U2fSignOperationTest, AlternativeApplicationParameterRejection) { auto request = CreateSignRequest( {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}); - request.SetAlternativeApplicationParameter( - test_data::kAlternativeApplicationParameter); + request.SetAppId(test_data::kAppId); auto device = std::make_unique<MockFidoDevice>(); EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device")); diff --git a/chromium/device/fido/virtual_ctap2_device.cc b/chromium/device/fido/virtual_ctap2_device.cc index 8c13c295052..0a12d8600af 100644 --- a/chromium/device/fido/virtual_ctap2_device.cc +++ b/chromium/device/fido/virtual_ctap2_device.cc @@ -12,7 +12,8 @@ #include "base/logging.h" #include "base/numerics/safe_conversions.h" #include "base/threading/thread_task_runner_handle.h" -#include "components/cbor/cbor_writer.h" +#include "components/cbor/reader.h" +#include "components/cbor/writer.h" #include "crypto/ec_private_key.h" #include "device/fido/authenticator_get_assertion_response.h" #include "device/fido/authenticator_make_credential_response.h" @@ -51,10 +52,11 @@ void ReturnCtap2Response( bool AreMakeCredentialOptionsValid(const AuthenticatorSupportedOptions& options, const CtapMakeCredentialRequest& request) { - if (request.resident_key_supported() && !options.supports_resident_key()) + if (request.resident_key_required() && !options.supports_resident_key()) return false; - return !request.user_verification_required() || + return request.user_verification() != + UserVerificationRequirement::kRequired || options.user_verification_availability() == AuthenticatorSupportedOptions::UserVerificationAvailability:: kSupportedAndConfigured; @@ -112,12 +114,12 @@ std::vector<uint8_t> ConstructMakeCredentialResponse( const base::Optional<std::vector<uint8_t>> attestation_certificate, base::span<const uint8_t> signature, AuthenticatorData authenticator_data) { - cbor::CBORValue::MapValue attestation_map; + cbor::Value::MapValue attestation_map; attestation_map.emplace("alg", -7); attestation_map.emplace("sig", fido_parsing_utils::Materialize(signature)); if (attestation_certificate) { - cbor::CBORValue::ArrayValue certificate_chain; + cbor::Value::ArrayValue certificate_chain; certificate_chain.emplace_back(std::move(*attestation_certificate)); attestation_map.emplace("x5c", std::move(certificate_chain)); } @@ -127,7 +129,7 @@ std::vector<uint8_t> ConstructMakeCredentialResponse( AttestationObject( std::move(authenticator_data), std::make_unique<OpaqueAttestationStatement>( - "packed", cbor::CBORValue(std::move(attestation_map))))); + "packed", cbor::Value(std::move(attestation_map))))); return GetSerializedCtapDeviceResponse(make_credential_response); } @@ -145,6 +147,56 @@ std::vector<uint8_t> ConstructGetAssertionResponse( return GetSerializedCtapDeviceResponse(response); } +bool IsMakeCredentialOptionMapFormatCorrect( + const cbor::Value::MapValue& option_map) { + return std::all_of( + option_map.begin(), option_map.end(), [](const auto& param) { + if (!param.first.is_string()) + return false; + + const auto& key = param.first.GetString(); + return ((key == kResidentKeyMapKey || key == kUserVerificationMapKey) && + param.second.is_bool()); + }); +} + +bool AreMakeCredentialRequestMapKeysCorrect( + const cbor::Value::MapValue& request_map) { + return std::all_of(request_map.begin(), request_map.end(), + [](const auto& param) { + if (!param.first.is_integer()) + return false; + + const auto& key = param.first.GetInteger(); + return (key <= 9u && key >= 1u); + }); +} + +bool IsGetAssertionOptionMapFormatCorrect( + const cbor::Value::MapValue& option_map) { + return std::all_of( + option_map.begin(), option_map.end(), [](const auto& param) { + if (!param.first.is_string()) + return false; + + const auto& key = param.first.GetString(); + return (key == kUserPresenceMapKey || key == kUserVerificationMapKey) && + param.second.is_bool(); + }); +} + +bool AreGetAssertionRequestMapKeysCorrect( + const cbor::Value::MapValue& request_map) { + return std::all_of(request_map.begin(), request_map.end(), + [](const auto& param) { + if (!param.first.is_integer()) + return false; + + const auto& key = param.first.GetInteger(); + return (key <= 7u || key >= 1u); + }); +} + } // namespace VirtualCtap2Device::VirtualCtap2Device() @@ -214,30 +266,33 @@ void VirtualCtap2Device::SetAuthenticatorSupportedOptions( CtapDeviceResponseCode VirtualCtap2Device::OnMakeCredential( base::span<const uint8_t> request_bytes, std::vector<uint8_t>* response) { - auto request = ParseCtapMakeCredentialRequest(request_bytes); - if (!request) { + auto request_and_hash = ParseCtapMakeCredentialRequest(request_bytes); + if (!request_and_hash) { DLOG(ERROR) << "Incorrectly formatted MakeCredential request."; return CtapDeviceResponseCode::kCtap2ErrOther; } + CtapMakeCredentialRequest request = std::get<0>(*request_and_hash); + CtapMakeCredentialRequest::ClientDataHash client_data_hash = + std::get<1>(*request_and_hash); - if (!AreMakeCredentialOptionsValid(device_info_.options(), *request) || - !AreMakeCredentialParamsValid(*request)) { + if (!AreMakeCredentialOptionsValid(device_info_.options(), request) || + !AreMakeCredentialParamsValid(request)) { DLOG(ERROR) << "Virtual CTAP2 device does not support options required by " "the request."; return CtapDeviceResponseCode::kCtap2ErrOther; } // Client pin is not supported. - if (request->pin_auth()) { + if (request.pin_auth()) { DLOG(ERROR) << "Virtual CTAP2 device does not support client pin."; return CtapDeviceResponseCode::kCtap2ErrPinInvalid; } // Check for already registered credentials. const auto rp_id_hash = - fido_parsing_utils::CreateSHA256Hash(request->rp().rp_id()); - if (request->exclude_list()) { - for (const auto& excluded_credential : *request->exclude_list()) { + fido_parsing_utils::CreateSHA256Hash(request.rp().rp_id()); + if (request.exclude_list()) { + for (const auto& excluded_credential : *request.exclude_list()) { if (FindRegistrationData(excluded_credential.id(), rp_id_hash)) return CtapDeviceResponseCode::kCtap2ErrCredentialExcluded; } @@ -267,10 +322,19 @@ CtapDeviceResponseCode VirtualCtap2Device::OnMakeCredential( AttestedCredentialData attested_credential_data( aaguid, sha256_length, key_handle, ConstructECPublicKey(public_key)); + base::Optional<cbor::Value> extensions; + if (request.hmac_secret()) { + cbor::Value::MapValue extensions_map; + extensions_map.emplace(cbor::Value(kExtensionHmacSecret), + cbor::Value(true)); + extensions = cbor::Value(std::move(extensions_map)); + } + auto authenticator_data = ConstructAuthenticatorData( - rp_id_hash, 01ul, std::move(attested_credential_data)); + rp_id_hash, 01ul, std::move(attested_credential_data), + std::move(extensions)); auto sign_buffer = - ConstructSignatureBuffer(authenticator_data, request->client_data_hash()); + ConstructSignatureBuffer(authenticator_data, client_data_hash); // Sign with attestation key. // Note: Non-deterministic, you need to mock this out if you rely on @@ -301,36 +365,38 @@ CtapDeviceResponseCode VirtualCtap2Device::OnMakeCredential( CtapDeviceResponseCode VirtualCtap2Device::OnGetAssertion( base::span<const uint8_t> request_bytes, std::vector<uint8_t>* response) { - auto request = ParseCtapGetAssertionRequest(request_bytes); - if (!request) { + auto request_and_hash = ParseCtapGetAssertionRequest(request_bytes); + if (!request_and_hash) { DLOG(ERROR) << "Incorrectly formatted GetAssertion request."; return CtapDeviceResponseCode::kCtap2ErrOther; } + CtapGetAssertionRequest request = std::get<0>(*request_and_hash); + CtapGetAssertionRequest::ClientDataHash client_data_hash = + std::get<1>(*request_and_hash); // Resident keys are not supported. - if (!request->allow_list() || request->allow_list()->empty()) { + if (!request.allow_list() || request.allow_list()->empty()) { DLOG(ERROR) << "Allowed credential list is empty, but Virtual CTAP2 device " "does not support resident keys."; return CtapDeviceResponseCode::kCtap2ErrNoCredentials; } // Client pin option is not supported. - if (request->pin_auth()) { + if (request.pin_auth()) { DLOG(ERROR) << "Virtual CTAP2 device does not support client pin."; return CtapDeviceResponseCode::kCtap2ErrOther; } - if (!AreGetAssertionOptionsValid(device_info_.options(), *request)) { + if (!AreGetAssertionOptionsValid(device_info_.options(), request)) { DLOG(ERROR) << "Unsupported options required from the request."; return CtapDeviceResponseCode::kCtap2ErrOther; } - const auto rp_id_hash = - fido_parsing_utils::CreateSHA256Hash(request->rp_id()); + const auto rp_id_hash = fido_parsing_utils::CreateSHA256Hash(request.rp_id()); RegistrationData* found_data = nullptr; base::span<const uint8_t> credential_id; - for (const auto& allowed_credential : *request->allow_list()) { + for (const auto& allowed_credential : *request.allow_list()) { if ((found_data = FindRegistrationData(allowed_credential.id(), rp_id_hash))) { credential_id = allowed_credential.id(); @@ -342,10 +408,10 @@ CtapDeviceResponseCode VirtualCtap2Device::OnGetAssertion( return CtapDeviceResponseCode::kCtap2ErrNoCredentials; found_data->counter++; - auto authenticator_data = - ConstructAuthenticatorData(rp_id_hash, found_data->counter); + auto authenticator_data = ConstructAuthenticatorData( + rp_id_hash, found_data->counter, base::nullopt, base::nullopt); auto signature_buffer = - ConstructSignatureBuffer(authenticator_data, request->client_data_hash()); + ConstructSignatureBuffer(authenticator_data, client_data_hash); // Sign with the private key of the received key handle. std::vector<uint8_t> sig; @@ -367,7 +433,8 @@ CtapDeviceResponseCode VirtualCtap2Device::OnAuthenticatorGetInfo( AuthenticatorData VirtualCtap2Device::ConstructAuthenticatorData( base::span<const uint8_t, kRpIdHashLength> rp_id_hash, uint32_t current_signature_count, - base::Optional<AttestedCredentialData> attested_credential_data) { + base::Optional<AttestedCredentialData> attested_credential_data, + base::Optional<cbor::Value> extensions) { uint8_t flag = base::strict_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserPresence); std::array<uint8_t, kSignCounterLength> signature_counter; @@ -375,6 +442,10 @@ AuthenticatorData VirtualCtap2Device::ConstructAuthenticatorData( // Constructing AuthenticatorData for registration operation. if (attested_credential_data) flag |= base::strict_cast<uint8_t>(AuthenticatorData::Flag::kAttestation); + if (extensions) { + flag |= base::strict_cast<uint8_t>( + AuthenticatorData::Flag::kExtensionDataIncluded); + } signature_counter[0] = (current_signature_count >> 24) & 0xff; signature_counter[1] = (current_signature_count >> 16) & 0xff; @@ -382,7 +453,227 @@ AuthenticatorData VirtualCtap2Device::ConstructAuthenticatorData( signature_counter[3] = (current_signature_count)&0xff; return AuthenticatorData(rp_id_hash, flag, signature_counter, - std::move(attested_credential_data)); + std::move(attested_credential_data), + std::move(extensions)); +} + +base::Optional<std::pair<CtapMakeCredentialRequest, + CtapMakeCredentialRequest::ClientDataHash>> +ParseCtapMakeCredentialRequest(base::span<const uint8_t> request_bytes) { + const auto& cbor_request = cbor::Reader::Read(request_bytes); + if (!cbor_request || !cbor_request->is_map()) + return base::nullopt; + + const auto& request_map = cbor_request->GetMap(); + if (!AreMakeCredentialRequestMapKeysCorrect(request_map)) + return base::nullopt; + + const auto client_data_hash_it = request_map.find(cbor::Value(1)); + if (client_data_hash_it == request_map.end() || + !client_data_hash_it->second.is_bytestring()) + return base::nullopt; + + const auto client_data_hash = + base::make_span(client_data_hash_it->second.GetBytestring()) + .subspan<0, kClientDataHashLength>(); + + const auto rp_entity_it = request_map.find(cbor::Value(2)); + if (rp_entity_it == request_map.end() || !rp_entity_it->second.is_map()) + return base::nullopt; + + auto rp_entity = + PublicKeyCredentialRpEntity::CreateFromCBORValue(rp_entity_it->second); + if (!rp_entity) + return base::nullopt; + + const auto user_entity_it = request_map.find(cbor::Value(3)); + if (user_entity_it == request_map.end() || !user_entity_it->second.is_map()) + return base::nullopt; + + auto user_entity = PublicKeyCredentialUserEntity::CreateFromCBORValue( + user_entity_it->second); + if (!user_entity) + return base::nullopt; + + const auto credential_params_it = request_map.find(cbor::Value(4)); + if (credential_params_it == request_map.end()) + return base::nullopt; + + auto credential_params = PublicKeyCredentialParams::CreateFromCBORValue( + credential_params_it->second); + if (!credential_params) + return base::nullopt; + + CtapMakeCredentialRequest request( + std::string() /* client_data_json */, std::move(*rp_entity), + std::move(*user_entity), std::move(*credential_params)); + + const auto exclude_list_it = request_map.find(cbor::Value(5)); + if (exclude_list_it != request_map.end()) { + if (!exclude_list_it->second.is_array()) + return base::nullopt; + + const auto& credential_descriptors = exclude_list_it->second.GetArray(); + std::vector<PublicKeyCredentialDescriptor> exclude_list; + for (const auto& credential_descriptor : credential_descriptors) { + auto excluded_credential = + PublicKeyCredentialDescriptor::CreateFromCBORValue( + credential_descriptor); + if (!excluded_credential) + return base::nullopt; + + exclude_list.push_back(std::move(*excluded_credential)); + } + request.SetExcludeList(std::move(exclude_list)); + } + + const auto extensions_it = request_map.find(cbor::Value(6)); + if (extensions_it != request_map.end()) { + if (!extensions_it->second.is_map()) { + return base::nullopt; + } + + const auto& extensions = extensions_it->second.GetMap(); + const auto hmac_secret_it = + extensions.find(cbor::Value(kExtensionHmacSecret)); + if (hmac_secret_it != extensions.end()) { + if (!hmac_secret_it->second.is_bool()) { + return base::nullopt; + } + request.SetHmacSecret(hmac_secret_it->second.GetBool()); + } + } + + const auto option_it = request_map.find(cbor::Value(7)); + if (option_it != request_map.end()) { + if (!option_it->second.is_map()) + return base::nullopt; + + const auto& option_map = option_it->second.GetMap(); + if (!IsMakeCredentialOptionMapFormatCorrect(option_map)) + return base::nullopt; + + const auto resident_key_option = + option_map.find(cbor::Value(kResidentKeyMapKey)); + if (resident_key_option != option_map.end()) + request.SetResidentKeyRequired(resident_key_option->second.GetBool()); + + const auto uv_option = + option_map.find(cbor::Value(kUserVerificationMapKey)); + if (uv_option != option_map.end()) + request.SetUserVerification( + uv_option->second.GetBool() + ? UserVerificationRequirement::kRequired + : UserVerificationRequirement::kDiscouraged); + } + + const auto pin_auth_it = request_map.find(cbor::Value(8)); + if (pin_auth_it != request_map.end()) { + if (!pin_auth_it->second.is_bytestring()) + return base::nullopt; + request.SetPinAuth(pin_auth_it->second.GetBytestring()); + } + + const auto pin_protocol_it = request_map.find(cbor::Value(9)); + if (pin_protocol_it != request_map.end()) { + if (!pin_protocol_it->second.is_unsigned() || + pin_protocol_it->second.GetUnsigned() > + std::numeric_limits<uint8_t>::max()) + return base::nullopt; + request.SetPinProtocol(pin_auth_it->second.GetUnsigned()); + } + + return std::make_pair(std::move(request), + fido_parsing_utils::Materialize(client_data_hash)); +} + +base::Optional< + std::pair<CtapGetAssertionRequest, CtapGetAssertionRequest::ClientDataHash>> +ParseCtapGetAssertionRequest(base::span<const uint8_t> request_bytes) { + const auto& cbor_request = cbor::Reader::Read(request_bytes); + if (!cbor_request || !cbor_request->is_map()) + return base::nullopt; + + const auto& request_map = cbor_request->GetMap(); + if (!AreGetAssertionRequestMapKeysCorrect(request_map)) + return base::nullopt; + + const auto rp_id_it = request_map.find(cbor::Value(1)); + if (rp_id_it == request_map.end() || !rp_id_it->second.is_string()) + return base::nullopt; + + const auto client_data_hash_it = request_map.find(cbor::Value(2)); + if (client_data_hash_it == request_map.end() || + !client_data_hash_it->second.is_bytestring()) + return base::nullopt; + + const auto client_data_hash = + base::make_span(client_data_hash_it->second.GetBytestring()) + .subspan<0, kClientDataHashLength>(); + + CtapGetAssertionRequest request(rp_id_it->second.GetString(), + std::string() /* client_data_json */); + + const auto allow_list_it = request_map.find(cbor::Value(3)); + if (allow_list_it != request_map.end()) { + if (!allow_list_it->second.is_array()) + return base::nullopt; + + const auto& credential_descriptors = allow_list_it->second.GetArray(); + std::vector<PublicKeyCredentialDescriptor> allow_list; + for (const auto& credential_descriptor : credential_descriptors) { + auto allowed_credential = + PublicKeyCredentialDescriptor::CreateFromCBORValue( + credential_descriptor); + if (!allowed_credential) + return base::nullopt; + + allow_list.push_back(std::move(*allowed_credential)); + } + request.SetAllowList(std::move(allow_list)); + } + + const auto option_it = request_map.find(cbor::Value(5)); + if (option_it != request_map.end()) { + if (!option_it->second.is_map()) + return base::nullopt; + + const auto& option_map = option_it->second.GetMap(); + if (!IsGetAssertionOptionMapFormatCorrect(option_map)) + return base::nullopt; + + const auto user_presence_option = + option_map.find(cbor::Value(kUserPresenceMapKey)); + if (user_presence_option != option_map.end()) + request.SetUserPresenceRequired(user_presence_option->second.GetBool()); + + const auto uv_option = + option_map.find(cbor::Value(kUserVerificationMapKey)); + if (uv_option != option_map.end()) + request.SetUserVerification( + uv_option->second.GetBool() + ? UserVerificationRequirement::kRequired + : UserVerificationRequirement::kPreferred); + } + + const auto pin_auth_it = request_map.find(cbor::Value(6)); + if (pin_auth_it != request_map.end()) { + if (!pin_auth_it->second.is_bytestring()) + return base::nullopt; + request.SetPinAuth(pin_auth_it->second.GetBytestring()); + } + + const auto pin_protocol_it = request_map.find(cbor::Value(7)); + if (pin_protocol_it != request_map.end()) { + if (!pin_protocol_it->second.is_unsigned() || + pin_protocol_it->second.GetUnsigned() > + std::numeric_limits<uint8_t>::max()) + return base::nullopt; + request.SetPinProtocol(pin_auth_it->second.GetUnsigned()); + } + + return std::make_pair(std::move(request), + fido_parsing_utils::Materialize(client_data_hash)); } } // namespace device diff --git a/chromium/device/fido/virtual_ctap2_device.h b/chromium/device/fido/virtual_ctap2_device.h index fa84a9c0425..622d4248a29 100644 --- a/chromium/device/fido/virtual_ctap2_device.h +++ b/chromium/device/fido/virtual_ctap2_device.h @@ -15,9 +15,12 @@ #include "base/macros.h" #include "base/memory/scoped_refptr.h" #include "base/optional.h" +#include "components/cbor/values.h" #include "device/fido/attested_credential_data.h" #include "device/fido/authenticator_data.h" #include "device/fido/authenticator_supported_options.h" +#include "device/fido/ctap_get_assertion_request.h" +#include "device/fido/ctap_make_credential_request.h" #include "device/fido/fido_constants.h" #include "device/fido/virtual_fido_device.h" @@ -50,8 +53,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) VirtualCtap2Device AuthenticatorData ConstructAuthenticatorData( base::span<const uint8_t, kRpIdHashLength> rp_id_hash, uint32_t current_signature_count, - base::Optional<AttestedCredentialData> attested_credential_data = - base::nullopt); + base::Optional<AttestedCredentialData> attested_credential_data, + base::Optional<cbor::Value> extensions); AuthenticatorGetInfoResponse device_info_; base::WeakPtrFactory<FidoDevice> weak_factory_; @@ -59,6 +62,22 @@ class COMPONENT_EXPORT(DEVICE_FIDO) VirtualCtap2Device DISALLOW_COPY_AND_ASSIGN(VirtualCtap2Device); }; +// Decodes a CBOR-encoded CTAP2 authenticatorMakeCredential request message. The +// request's client_data_json() value will be empty, and the hashed client data +// is returned separately. +COMPONENT_EXPORT(DEVICE_FIDO) +base::Optional<std::pair<CtapMakeCredentialRequest, + CtapMakeCredentialRequest::ClientDataHash>> +ParseCtapMakeCredentialRequest(base::span<const uint8_t> request_bytes); + +// Decodes a CBOR-encoded CTAP2 authenticatorGetAssertion request message. The +// request's client_data_json() value will be empty, and the hashed client data +// is returned separately. +COMPONENT_EXPORT(DEVICE_FIDO) +base::Optional< + std::pair<CtapGetAssertionRequest, CtapGetAssertionRequest::ClientDataHash>> +ParseCtapGetAssertionRequest(base::span<const uint8_t> request_bytes); + } // namespace device #endif // DEVICE_FIDO_VIRTUAL_CTAP2_DEVICE_H_ diff --git a/chromium/device/fido/virtual_fido_device.cc b/chromium/device/fido/virtual_fido_device.cc index 05bf30651cd..4ce078679e6 100644 --- a/chromium/device/fido/virtual_fido_device.cc +++ b/chromium/device/fido/virtual_fido_device.cc @@ -4,6 +4,7 @@ #include "device/fido/virtual_fido_device.h" +#include <algorithm> #include <tuple> #include <utility> @@ -156,9 +157,11 @@ VirtualFidoDevice::RegistrationData* VirtualFidoDevice::FindRegistrationData( if (it == mutable_state()->registrations.end()) return nullptr; - if (application_parameter != - base::make_span(it->second.application_parameter)) + if (!std::equal(application_parameter.begin(), application_parameter.end(), + it->second.application_parameter.begin(), + it->second.application_parameter.end())) { return nullptr; + } return &(it->second); } @@ -173,8 +176,7 @@ std::string VirtualFidoDevice::GetId() const { } FidoTransportProtocol VirtualFidoDevice::DeviceTransport() const { - // Virtual device are injected as HID devices. - return FidoTransportProtocol::kUsbHumanInterfaceDevice; + return state_->transport; } } // namespace device diff --git a/chromium/device/fido/virtual_fido_device.h b/chromium/device/fido/virtual_fido_device.h index 06a65f7b870..c6cfb19187b 100644 --- a/chromium/device/fido/virtual_fido_device.h +++ b/chromium/device/fido/virtual_fido_device.h @@ -70,7 +70,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) VirtualFidoDevice : public FidoDevice { // Registered keys. Keyed on key handle (a.k.a. "credential ID"). std::map<std::vector<uint8_t>, RegistrationData, - fido_parsing_utils::SpanLess> + fido_parsing_utils::RangeLess> registrations; // If set, this callback is called whenever a "press" is required. It allows @@ -89,6 +89,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) VirtualFidoDevice : public FidoDevice { // zero, in violation of the rules for self-attestation. bool non_zero_aaguid_with_self_attestation = false; + FidoTransportProtocol transport = + FidoTransportProtocol::kUsbHumanInterfaceDevice; + // Adds a registration for the specified credential ID with the application // parameter set to be valid for the given relying party ID (which would // typically be a domain, e.g. "example.com"). diff --git a/chromium/device/fido/win/authenticator.cc b/chromium/device/fido/win/authenticator.cc new file mode 100644 index 00000000000..eca65dabf8f --- /dev/null +++ b/chromium/device/fido/win/authenticator.cc @@ -0,0 +1,294 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/fido/win/authenticator.h" + +#include <Combaseapi.h> +#include <windows.h> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/containers/flat_map.h" +#include "base/logging.h" +#include "base/optional.h" +#include "base/stl_util.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "device/fido/authenticator_supported_options.h" +#include "device/fido/ctap_get_assertion_request.h" +#include "device/fido/ctap_make_credential_request.h" +#include "device/fido/fido_constants.h" +#include "device/fido/fido_transport_protocol.h" +#include "device/fido/win/type_conversions.h" +#include "third_party/microsoft_webauthn/webauthn.h" + +namespace device { + +// Time out all Windows API requests after 5 minutes. We maintain our own +// timeout and cancel the operation when it expires, so this value simply needs +// to be larger than the largest internal request timeout. +constexpr uint32_t kWinWebAuthnTimeoutMilliseconds = 1000 * 60 * 5; + +// static +const char WinWebAuthnApiAuthenticator::kAuthenticatorId[] = + "WinWebAuthnApiAuthenticator"; + +// static +bool WinWebAuthnApiAuthenticator:: + IsUserVerifyingPlatformAuthenticatorAvailable() { + BOOL result; + return WinWebAuthnApi::GetDefault()->IsAvailable() && + WinWebAuthnApi::GetDefault() + ->IsUserVerifyingPlatformAuthenticatorAvailable(&result) == + S_OK && + result == TRUE; +} + +WinWebAuthnApiAuthenticator::WinWebAuthnApiAuthenticator( + WinWebAuthnApi* win_api, + HWND current_window) + : FidoAuthenticator(), + win_api_(win_api), + current_window_(current_window), + weak_factory_(this) { + CHECK(win_api_->IsAvailable()); + CoCreateGuid(&cancellation_id_); +} + +WinWebAuthnApiAuthenticator::~WinWebAuthnApiAuthenticator() { + Cancel(); +} + +void WinWebAuthnApiAuthenticator::InitializeAuthenticator( + base::OnceClosure callback) { + std::move(callback).Run(); +} + +void WinWebAuthnApiAuthenticator::MakeCredential( + CtapMakeCredentialRequest request, + MakeCredentialCallback callback) { + DCHECK(!is_pending_); + if (is_pending_) + return; + + is_pending_ = true; + + auto rp = request.rp(); + auto user = request.user(); + std::string client_data_json = request.client_data_json(); + std::vector<WEBAUTHN_COSE_CREDENTIAL_PARAMETER> + cose_credential_parameter_values; + for (const PublicKeyCredentialParams::CredentialInfo& credential_info : + request.public_key_credential_params().public_key_credential_params()) { + if (credential_info.type != CredentialType::kPublicKey) { + continue; + } + cose_credential_parameter_values.push_back( + {WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION, + WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, credential_info.algorithm}); + } + std::vector<WEBAUTHN_EXTENSION> extensions; + if (request.hmac_secret()) { + static BOOL kHMACSecretTrue = TRUE; + extensions.emplace_back( + WEBAUTHN_EXTENSION{WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET, + sizeof(BOOL), static_cast<void*>(&kHMACSecretTrue)}); + } + auto exclude_list = request.exclude_list(); + + uint32_t authenticator_attachment; + if (request.is_u2f_only()) { + authenticator_attachment = + WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM_U2F_V2; + } else if (request.is_incognito_mode()) { + // Disable all platform authenticators in incognito mode. We are going to + // revisit this in crbug/908622. + authenticator_attachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM; + } else { + authenticator_attachment = + ToWinAuthenticatorAttachment(request.authenticator_attachment()); + } + win_api_->AuthenticatorMakeCredential( + current_window_, cancellation_id_, std::move(rp), std::move(user), + std::move(cose_credential_parameter_values), std::move(client_data_json), + std::move(extensions), std::move(exclude_list), + WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS{ + WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_3, + kWinWebAuthnTimeoutMilliseconds, + WEBAUTHN_CREDENTIALS{ + 0, nullptr}, // Ignored because pExcludeCredentialList is set. + WEBAUTHN_EXTENSIONS{0, nullptr}, // will be set later + authenticator_attachment, request.resident_key_required(), + ToWinUserVerificationRequirement(request.user_verification()), + WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT, 0 /* flags */, + nullptr, // pCancellationId -- will be set later + nullptr, // pExcludeCredentialList -- will be set later + }, + base::BindOnce(&WinWebAuthnApiAuthenticator::MakeCredentialDone, + weak_factory_.GetWeakPtr(), std::move(request), + std::move(callback))); +} + +void WinWebAuthnApiAuthenticator::MakeCredentialDone( + CtapMakeCredentialRequest request, + MakeCredentialCallback callback, + HRESULT hresult, + WinWebAuthnApi::ScopedCredentialAttestation credential_attestation) { + DCHECK(is_pending_); + is_pending_ = false; + const CtapDeviceResponseCode status = + hresult == S_OK ? CtapDeviceResponseCode::kSuccess + : WinErrorNameToCtapDeviceResponseCode( + base::string16(win_api_->GetErrorName(hresult))); + if (waiting_for_cancellation_) { + // Don't bother invoking the reply callback if the caller has already + // cancelled the operation. + waiting_for_cancellation_ = false; + return; + } + + if (status != CtapDeviceResponseCode::kSuccess) { + std::move(callback).Run(status, base::nullopt); + return; + } + + base::Optional<AuthenticatorMakeCredentialResponse> response = + credential_attestation + ? ToAuthenticatorMakeCredentialResponse(*credential_attestation) + : base::nullopt; + if (!response) { + std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrInvalidCBOR, + base::nullopt); + return; + } + + std::move(callback).Run(status, std::move(response)); +} + +void WinWebAuthnApiAuthenticator::GetAssertion(CtapGetAssertionRequest request, + GetAssertionCallback callback) { + DCHECK(!is_pending_); + if (is_pending_) + return; + + is_pending_ = true; + + base::string16 rp_id16 = base::UTF8ToUTF16(request.rp_id()); + base::Optional<base::string16> opt_app_id16 = base::nullopt; + if (request.app_id()) { + opt_app_id16 = base::UTF8ToUTF16(base::StringPiece( + reinterpret_cast<const char*>(request.app_id()->data()), + request.app_id()->size())); + } + std::string client_data_json = request.client_data_json(); + auto allow_list = request.allow_list(); + + uint32_t authenticator_attachment; + if (opt_app_id16) { + authenticator_attachment = + WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM_U2F_V2; + } else if (request.is_incognito_mode()) { + // Disable all platform authenticators in incognito mode. We are going to + // revisit this in crbug/908622. + authenticator_attachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM; + } else { + authenticator_attachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY; + } + + win_api_->AuthenticatorGetAssertion( + current_window_, cancellation_id_, std::move(rp_id16), + std::move(opt_app_id16), std::move(client_data_json), + std::move(allow_list), + WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS{ + WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_4, + kWinWebAuthnTimeoutMilliseconds, + WEBAUTHN_CREDENTIALS{ + 0, nullptr}, // Ignored because pAllowCredentialList is set. + WEBAUTHN_EXTENSIONS{0, nullptr}, // None supported. + authenticator_attachment, + ToWinUserVerificationRequirement(request.user_verification()), + 0, // flags + nullptr, // pwszU2fAppId -- will be set later + nullptr, // pbU2fAppId -- will be set later + nullptr, // pCancellationId -- will be set later + nullptr, // pAllowCredentialList -- will be set later + }, + base::BindOnce(&WinWebAuthnApiAuthenticator::GetAssertionDone, + weak_factory_.GetWeakPtr(), std::move(request), + std::move(callback))); +} + +void WinWebAuthnApiAuthenticator::GetAssertionDone( + CtapGetAssertionRequest request, + GetAssertionCallback callback, + HRESULT hresult, + WinWebAuthnApi::ScopedAssertion assertion) { + DCHECK(is_pending_); + is_pending_ = false; + const CtapDeviceResponseCode status = + hresult == S_OK ? CtapDeviceResponseCode::kSuccess + : WinErrorNameToCtapDeviceResponseCode( + base::string16(win_api_->GetErrorName(hresult))); + if (waiting_for_cancellation_) { + // Don't bother invoking the reply callback if the caller has already + // cancelled the operation. + waiting_for_cancellation_ = false; + return; + } + + base::Optional<AuthenticatorGetAssertionResponse> response = + (hresult == S_OK && assertion) + ? ToAuthenticatorGetAssertionResponse(*assertion) + : base::nullopt; + std::move(callback).Run(status, std::move(response)); +} + +void WinWebAuthnApiAuthenticator::Cancel() { + if (!is_pending_ || waiting_for_cancellation_) + return; + + waiting_for_cancellation_ = true; + // This returns immediately. + win_api_->CancelCurrentOperation(&cancellation_id_); +} + +std::string WinWebAuthnApiAuthenticator::GetId() const { + return kAuthenticatorId; +} + +base::string16 WinWebAuthnApiAuthenticator::GetDisplayName() const { + return base::UTF8ToUTF16(GetId()); +} + +bool WinWebAuthnApiAuthenticator::IsInPairingMode() const { + return false; +} + +bool WinWebAuthnApiAuthenticator::IsPaired() const { + return false; +} + +base::Optional<FidoTransportProtocol> +WinWebAuthnApiAuthenticator::AuthenticatorTransport() const { + // The Windows API could potentially use any external or + // platform authenticator. + return base::nullopt; +} + +const base::Optional<AuthenticatorSupportedOptions>& +WinWebAuthnApiAuthenticator::Options() const { + // The request can potentially be fulfilled by any device that Windows + // communicates with, so returning AuthenticatorSupportedOptions really + // doesn't make much sense. + static const base::Optional<AuthenticatorSupportedOptions> no_options = + base::nullopt; + return no_options; +} + +base::WeakPtr<FidoAuthenticator> WinWebAuthnApiAuthenticator::GetWeakPtr() { + return weak_factory_.GetWeakPtr(); +} + +} // namespace device diff --git a/chromium/device/fido/win/authenticator.h b/chromium/device/fido/win/authenticator.h new file mode 100644 index 00000000000..555705424a0 --- /dev/null +++ b/chromium/device/fido/win/authenticator.h @@ -0,0 +1,72 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_FIDO_WIN_AUTHENTICATOR_H_ +#define DEVICE_FIDO_WIN_AUTHENTICATOR_H_ + +#include <memory> +#include <string> + +#include "base/component_export.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/optional.h" +#include "device/fido/fido_authenticator.h" +#include "device/fido/win/webauthn_api.h" + +namespace device { + +// WinWebAuthnApiAuthenticator forwards WebAuthn requests to external +// authenticators via the native Windows WebAuthentication API +// (webauthn.dll). +class COMPONENT_EXPORT(DEVICE_FIDO) WinWebAuthnApiAuthenticator + : public FidoAuthenticator { + public: + // The return value of |GetId|. + static const char kAuthenticatorId[]; + + static bool IsUserVerifyingPlatformAuthenticatorAvailable(); + + WinWebAuthnApiAuthenticator(WinWebAuthnApi* win_api, HWND current_window); + ~WinWebAuthnApiAuthenticator() override; + + // FidoAuthenticator + void InitializeAuthenticator(base::OnceClosure callback) override; + void MakeCredential(CtapMakeCredentialRequest request, + MakeCredentialCallback callback) override; + void GetAssertion(CtapGetAssertionRequest request, + GetAssertionCallback callback) override; + void Cancel() override; + std::string GetId() const override; + base::string16 GetDisplayName() const override; + bool IsInPairingMode() const override; + bool IsPaired() const override; + const base::Optional<AuthenticatorSupportedOptions>& Options() const override; + base::Optional<FidoTransportProtocol> AuthenticatorTransport() const override; + base::WeakPtr<FidoAuthenticator> GetWeakPtr() override; + + private: + void MakeCredentialDone( + CtapMakeCredentialRequest request, + MakeCredentialCallback callback, + HRESULT result, + WinWebAuthnApi::ScopedCredentialAttestation credential_attestation); + void GetAssertionDone(CtapGetAssertionRequest request, + GetAssertionCallback callback, + HRESULT hresult, + WinWebAuthnApi::ScopedAssertion assertion); + + WinWebAuthnApi* win_api_; + HWND current_window_; + + bool is_pending_ = false; + bool waiting_for_cancellation_ = false; + GUID cancellation_id_ = {}; + base::WeakPtrFactory<WinWebAuthnApiAuthenticator> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(WinWebAuthnApiAuthenticator); +}; + +} // namespace device + +#endif // DEVICE_FIDO_WIN_AUTHENTICATOR_H_ diff --git a/chromium/device/fido/win/discovery.cc b/chromium/device/fido/win/discovery.cc new file mode 100644 index 00000000000..0e72b97ff12 --- /dev/null +++ b/chromium/device/fido/win/discovery.cc @@ -0,0 +1,36 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/fido/win/discovery.h" + +namespace device { + +WinWebAuthnApiAuthenticatorDiscovery::WinWebAuthnApiAuthenticatorDiscovery( + WinWebAuthnApi* const win_webauthn_api, + HWND parent_window) + : FidoDiscoveryBase(FidoTransportProtocol::kUsbHumanInterfaceDevice), + win_webauthn_api_(win_webauthn_api), + parent_window_(parent_window) {} + +WinWebAuthnApiAuthenticatorDiscovery::~WinWebAuthnApiAuthenticatorDiscovery() = + default; + +void WinWebAuthnApiAuthenticatorDiscovery::Start() { + DCHECK(!authenticator_); + if (!observer()) { + return; + } + + if (!win_webauthn_api_->IsAvailable()) { + observer()->DiscoveryStarted(this, false /* discovery failed */); + return; + } + + observer()->DiscoveryStarted(this, true /* success */); + authenticator_ = std::make_unique<WinWebAuthnApiAuthenticator>( + WinWebAuthnApi::GetDefault(), parent_window_); + observer()->AuthenticatorAdded(this, authenticator_.get()); +} + +} // namespace device diff --git a/chromium/device/fido/win/discovery.h b/chromium/device/fido/win/discovery.h new file mode 100644 index 00000000000..ba7a803af8d --- /dev/null +++ b/chromium/device/fido/win/discovery.h @@ -0,0 +1,37 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_FIDO_WIN_DISCOVERY_H_ +#define DEVICE_FIDO_WIN_DISCOVERY_H_ + +#include <memory> + +#include "base/component_export.h" +#include "device/fido/fido_discovery_base.h" +#include "device/fido/win/authenticator.h" +#include "device/fido/win/webauthn_api.h" + +namespace device { + +// Instantiates the authenticator subclass for forwarding requests to external +// authenticators via the Windows WebAuthn API. +class COMPONENT_EXPORT(DEVICE_FIDO) WinWebAuthnApiAuthenticatorDiscovery + : public FidoDiscoveryBase { + public: + WinWebAuthnApiAuthenticatorDiscovery(WinWebAuthnApi* const win_webauthn_api, + HWND parent_window); + ~WinWebAuthnApiAuthenticatorDiscovery() override; + + // FidoDiscoveryBase: + void Start() override; + + private: + std::unique_ptr<WinWebAuthnApiAuthenticator> authenticator_; + WinWebAuthnApi* const win_webauthn_api_; + const HWND parent_window_; +}; + +} // namespace device + +#endif // DEVICE_FIDO_WIN_DISCOVERY_H_ diff --git a/chromium/device/fido/win/fake_webauthn_api.cc b/chromium/device/fido/win/fake_webauthn_api.cc new file mode 100644 index 00000000000..d741cb755f8 --- /dev/null +++ b/chromium/device/fido/win/fake_webauthn_api.cc @@ -0,0 +1,71 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/fido/win/fake_webauthn_api.h" + +#include "base/logging.h" +#include "base/optional.h" + +namespace device { + +FakeWinWebAuthnApi::FakeWinWebAuthnApi() = default; +FakeWinWebAuthnApi::~FakeWinWebAuthnApi() = default; + +bool FakeWinWebAuthnApi::IsAvailable() const { + return is_available_; +} + +HRESULT FakeWinWebAuthnApi::IsUserVerifyingPlatformAuthenticatorAvailable( + BOOL* result) { + DCHECK(is_available_); + *result = is_uvpaa_; + return S_OK; +} + +void FakeWinWebAuthnApi::AuthenticatorMakeCredential( + HWND h_wnd, + GUID cancellation_id, + PublicKeyCredentialRpEntity rp, + PublicKeyCredentialUserEntity user, + std::vector<WEBAUTHN_COSE_CREDENTIAL_PARAMETER> + cose_credential_parameter_values, + std::string client_data_json, + std::vector<WEBAUTHN_EXTENSION> extensions, + base::Optional<std::vector<PublicKeyCredentialDescriptor>> exclude_list, + WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS options, + AuthenticatorMakeCredentialCallback callback) { + DCHECK(is_available_); +} + +void FakeWinWebAuthnApi::AuthenticatorGetAssertion( + HWND h_wnd, + GUID cancellation_id, + base::string16 rp_id, + base::Optional<base::string16> opt_app_id, + std::string client_data_json, + base::Optional<std::vector<PublicKeyCredentialDescriptor>> allow_list, + WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS options, + AuthenticatorGetAssertionCallback callback) { + DCHECK(is_available_); +} + +HRESULT FakeWinWebAuthnApi::CancelCurrentOperation(GUID* cancellation_id) { + DCHECK(is_available_); + return E_NOTIMPL; +} + +const wchar_t* FakeWinWebAuthnApi::GetErrorName(HRESULT hr) { + DCHECK(is_available_); + return L"not implemented"; +}; + +ScopedFakeWinWebAuthnApi::ScopedFakeWinWebAuthnApi() : FakeWinWebAuthnApi() { + WinWebAuthnApi::SetDefaultForTesting(this); +} + +ScopedFakeWinWebAuthnApi::~ScopedFakeWinWebAuthnApi() { + WinWebAuthnApi::ClearDefaultForTesting(); +} + +} // namespace device diff --git a/chromium/device/fido/win/fake_webauthn_api.h b/chromium/device/fido/win/fake_webauthn_api.h new file mode 100644 index 00000000000..96c65ab6074 --- /dev/null +++ b/chromium/device/fido/win/fake_webauthn_api.h @@ -0,0 +1,70 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_FIDO_WIN_FAKE_WEBAUTHN_API_H_ +#define DEVICE_FIDO_WIN_FAKE_WEBAUTHN_API_H_ + +#include "base/macros.h" +#include "device/fido/win/webauthn_api.h" + +namespace device { + +class FakeWinWebAuthnApi : public WinWebAuthnApi { + public: + FakeWinWebAuthnApi(); + ~FakeWinWebAuthnApi() override; + + // Inject the return value for WinWebAuthnApi::IsAvailable(). + void set_available(bool available) { is_available_ = available; } + // Inject the return value for + // WinWebAuthnApi::IsUserverifyingPlatformAuthenticatorAvailable(). + void set_is_uvpaa(bool is_uvpaa) { is_uvpaa_ = is_uvpaa; } + + // WinWebAuthnApi: + bool IsAvailable() const override; + // The following methods all return E_NOTIMPL immediately. + HRESULT IsUserVerifyingPlatformAuthenticatorAvailable( + BOOL* available) override; + void AuthenticatorMakeCredential( + HWND h_wnd, + GUID cancellation_id, + PublicKeyCredentialRpEntity rp, + PublicKeyCredentialUserEntity user, + std::vector<WEBAUTHN_COSE_CREDENTIAL_PARAMETER> + cose_credential_parameter_values, + std::string client_data_json, + std::vector<WEBAUTHN_EXTENSION> extensions, + base::Optional<std::vector<PublicKeyCredentialDescriptor>> exclude_list, + WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS options, + AuthenticatorMakeCredentialCallback callback) override; + void AuthenticatorGetAssertion( + HWND h_wnd, + GUID cancellation_id, + base::string16 rp_id, + base::Optional<base::string16> opt_app_id, + std::string client_data_json, + base::Optional<std::vector<PublicKeyCredentialDescriptor>> allow_list, + WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS options, + AuthenticatorGetAssertionCallback callback) override; + HRESULT CancelCurrentOperation(GUID* cancellation_id) override; + // Returns L"not implemented". + const wchar_t* GetErrorName(HRESULT hr) override; + + private: + bool is_available_ = true; + bool is_uvpaa_ = false; +}; + +// ScopedFakeWinWebAuthnApi overrides the value returned +// by WinWebAuthnApi::GetDefault with itself for the duration of its +// lifetime. +class ScopedFakeWinWebAuthnApi : public FakeWinWebAuthnApi { + public: + ScopedFakeWinWebAuthnApi(); + ~ScopedFakeWinWebAuthnApi() override; +}; + +} // namespace device + +#endif // DEVICE_FIDO_WIN_FAKE_WEBAUTHN_API_H_ diff --git a/chromium/device/fido/win/type_conversions.cc b/chromium/device/fido/win/type_conversions.cc new file mode 100644 index 00000000000..e7db7760090 --- /dev/null +++ b/chromium/device/fido/win/type_conversions.cc @@ -0,0 +1,229 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/fido/win/type_conversions.h" + +#include <vector> + +#include "base/containers/span.h" +#include "base/logging.h" +#include "base/optional.h" +#include "base/strings/string16.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "components/cbor/reader.h" +#include "device/fido/authenticator_get_assertion_response.h" +#include "device/fido/authenticator_make_credential_response.h" +#include "device/fido/fido_transport_protocol.h" +#include "device/fido/opaque_attestation_statement.h" + +namespace device { + +base::Optional<AuthenticatorMakeCredentialResponse> +ToAuthenticatorMakeCredentialResponse( + const WEBAUTHN_CREDENTIAL_ATTESTATION& credential_attestation) { + auto authenticator_data = AuthenticatorData::DecodeAuthenticatorData( + base::span<const uint8_t>(credential_attestation.pbAuthenticatorData, + credential_attestation.cbAuthenticatorData)); + if (!authenticator_data) { + DLOG(ERROR) << "DecodeAuthenticatorData failed: " + << base::HexEncode(credential_attestation.pbAuthenticatorData, + credential_attestation.cbAuthenticatorData); + return base::nullopt; + } + base::Optional<cbor::Value> cbor_attestation_statement = cbor::Reader::Read( + base::span<const uint8_t>(credential_attestation.pbAttestation, + credential_attestation.cbAttestation)); + if (!cbor_attestation_statement) { + DLOG(ERROR) << "CBOR decoding attestation statement failed: " + << base::HexEncode(credential_attestation.pbAttestation, + credential_attestation.cbAttestation); + return base::nullopt; + } + + base::Optional<FidoTransportProtocol> transport_used; + if (credential_attestation.dwVersion >= + WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_3) { + // dwUsedTransport should have exactly one of the + // WEBAUTHN_CTAP_TRANSPORT_* values set. + switch (credential_attestation.dwUsedTransport) { + case WEBAUTHN_CTAP_TRANSPORT_USB: + transport_used = FidoTransportProtocol::kUsbHumanInterfaceDevice; + break; + case WEBAUTHN_CTAP_TRANSPORT_NFC: + transport_used = FidoTransportProtocol::kNearFieldCommunication; + break; + case WEBAUTHN_CTAP_TRANSPORT_BLE: + transport_used = FidoTransportProtocol::kBluetoothLowEnergy; + break; + case WEBAUTHN_CTAP_TRANSPORT_INTERNAL: + transport_used = FidoTransportProtocol::kInternal; + break; + default: + // Ignore _TEST and possibly future others. + break; + } + } + + return AuthenticatorMakeCredentialResponse( + base::nullopt /* transport_used */, + AttestationObject( + std::move(*authenticator_data), + std::make_unique<OpaqueAttestationStatement>( + base::UTF16ToUTF8(credential_attestation.pwszFormatType), + std::move(*cbor_attestation_statement)))); +} + +base::Optional<AuthenticatorGetAssertionResponse> +ToAuthenticatorGetAssertionResponse(const WEBAUTHN_ASSERTION& assertion) { + auto authenticator_data = + AuthenticatorData::DecodeAuthenticatorData(base::span<const uint8_t>( + assertion.pbAuthenticatorData, assertion.cbAuthenticatorData)); + if (!authenticator_data) { + DLOG(ERROR) << "DecodeAuthenticatorData failed: " + << base::HexEncode(assertion.pbAuthenticatorData, + assertion.cbAuthenticatorData); + return base::nullopt; + } + AuthenticatorGetAssertionResponse response( + std::move(*authenticator_data), + std::vector<uint8_t>(assertion.pbSignature, + assertion.pbSignature + assertion.cbSignature)); + if (assertion.Credential.cbId > 0) { + response.SetCredential(PublicKeyCredentialDescriptor( + CredentialType::kPublicKey, + std::vector<uint8_t>( + assertion.Credential.pbId, + assertion.Credential.pbId + assertion.Credential.cbId))); + } + if (assertion.cbUserId > 0) { + response.SetUserEntity(PublicKeyCredentialUserEntity(std::vector<uint8_t>( + assertion.pbUserId, assertion.pbUserId + assertion.cbUserId))); + } + return response; +} + +uint32_t ToWinUserVerificationRequirement( + UserVerificationRequirement user_verification_requirement) { + switch (user_verification_requirement) { + case UserVerificationRequirement::kRequired: + return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; + case UserVerificationRequirement::kPreferred: + return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED; + case UserVerificationRequirement::kDiscouraged: + return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED; + } + NOTREACHED(); + return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; +} + +uint32_t ToWinAuthenticatorAttachment( + AuthenticatorAttachment authenticator_attachment) { + switch (authenticator_attachment) { + case AuthenticatorAttachment::kAny: + return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY; + case AuthenticatorAttachment::kPlatform: + return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM; + case AuthenticatorAttachment::kCrossPlatform: + return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM; + } + NOTREACHED(); + return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY; +} + +static uint32_t ToWinTransportsMask( + const base::flat_set<FidoTransportProtocol>& transports) { + uint32_t result = 0; + for (const FidoTransportProtocol transport : transports) { + switch (transport) { + case FidoTransportProtocol::kUsbHumanInterfaceDevice: + result |= WEBAUTHN_CTAP_TRANSPORT_USB; + break; + case FidoTransportProtocol::kNearFieldCommunication: + result |= WEBAUTHN_CTAP_TRANSPORT_NFC; + break; + case FidoTransportProtocol::kBluetoothLowEnergy: + result |= WEBAUTHN_CTAP_TRANSPORT_BLE; + break; + case FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy: + // caBLE is unsupported by the Windows API. + break; + case FidoTransportProtocol::kInternal: + result |= WEBAUTHN_CTAP_TRANSPORT_INTERNAL; + break; + } + } + return result; +} + +std::vector<WEBAUTHN_CREDENTIAL> ToWinCredentialVector( + const base::Optional<std::vector<PublicKeyCredentialDescriptor>>& + credentials) { + if (!credentials) { + return {}; + } + std::vector<WEBAUTHN_CREDENTIAL> result; + for (const auto& credential : *credentials) { + if (credential.credential_type() != CredentialType::kPublicKey) { + continue; + } + result.push_back(WEBAUTHN_CREDENTIAL{ + WEBAUTHN_CREDENTIAL_CURRENT_VERSION, + credential.id().size(), + const_cast<unsigned char*>(credential.id().data()), + WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, + }); + } + return result; +} + +std::vector<WEBAUTHN_CREDENTIAL_EX> ToWinCredentialExVector( + const base::Optional<std::vector<PublicKeyCredentialDescriptor>>& + credentials) { + if (!credentials) { + return {}; + } + std::vector<WEBAUTHN_CREDENTIAL_EX> result; + for (const auto& credential : *credentials) { + if (credential.credential_type() != CredentialType::kPublicKey) { + continue; + } + result.push_back(WEBAUTHN_CREDENTIAL_EX{ + WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION, credential.id().size(), + const_cast<unsigned char*>(credential.id().data()), + WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, + ToWinTransportsMask(credential.transports())}); + } + return result; +} + +CtapDeviceResponseCode WinErrorNameToCtapDeviceResponseCode( + const base::string16& error_name) { + // TODO(crbug/896522): Another mismatch of our authenticator models. Windows + // returns WebAuthn authenticator model status, whereas FidoAuthenticator + // wants to pass on CTAP-level response codes. Do a best effort at mapping + // them back down for now. + // + // See WebAuthNGetErrorName in <webauthn.h> for these string values. + static base::flat_map<base::string16, CtapDeviceResponseCode> + kResponseCodeMap({ + {L"Success", CtapDeviceResponseCode::kSuccess}, + // This should be something else for GetAssertion but that currently + // doesn't make a difference. + {L"InvalidStateError", + CtapDeviceResponseCode::kCtap2ErrCredentialExcluded}, + {L"ConstraintError", + CtapDeviceResponseCode::kCtap2ErrUnsupportedOption}, + {L"NotSupportedError", + CtapDeviceResponseCode::kCtap2ErrUnsupportedAlgorithms}, + {L"NotAllowedError", + CtapDeviceResponseCode::kCtap2ErrOperationDenied}, + {L"UnknownError", CtapDeviceResponseCode::kCtap2ErrOther}, + }); + return base::ContainsKey(kResponseCodeMap, error_name) + ? kResponseCodeMap[error_name] + : CtapDeviceResponseCode::kCtap2ErrOther; +} + +} // namespace device diff --git a/chromium/device/fido/win/type_conversions.h b/chromium/device/fido/win/type_conversions.h new file mode 100644 index 00000000000..b0656aea91f --- /dev/null +++ b/chromium/device/fido/win/type_conversions.h @@ -0,0 +1,54 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_FIDO_WIN_TYPE_CONVERSIONS_H_ +#define DEVICE_FIDO_WIN_TYPE_CONVERSIONS_H_ + +#include <windows.h> + +#include "base/component_export.h" +#include "base/optional.h" +#include "base/strings/string16.h" +#include "device/fido/authenticator_get_assertion_response.h" +#include "device/fido/authenticator_make_credential_response.h" +#include "device/fido/fido_constants.h" +#include "third_party/microsoft_webauthn/webauthn.h" + +namespace device { + +COMPONENT_EXPORT(DEVICE_FIDO) +base::Optional<AuthenticatorMakeCredentialResponse> +ToAuthenticatorMakeCredentialResponse( + const WEBAUTHN_CREDENTIAL_ATTESTATION& credential_attestation); + +COMPONENT_EXPORT(DEVICE_FIDO) +base::Optional<AuthenticatorGetAssertionResponse> +ToAuthenticatorGetAssertionResponse( + const WEBAUTHN_ASSERTION& credential_attestation); + +COMPONENT_EXPORT(DEVICE_FIDO) +uint32_t ToWinUserVerificationRequirement( + UserVerificationRequirement user_verification_requirement); + +COMPONENT_EXPORT(DEVICE_FIDO) +uint32_t ToWinAuthenticatorAttachment( + AuthenticatorAttachment authenticator_attachment); + +COMPONENT_EXPORT(DEVICE_FIDO) +std::vector<WEBAUTHN_CREDENTIAL> ToWinCredentialVector( + const base::Optional<std::vector<PublicKeyCredentialDescriptor>>& + credentials); + +COMPONENT_EXPORT(DEVICE_FIDO) +std::vector<WEBAUTHN_CREDENTIAL_EX> ToWinCredentialExVector( + const base::Optional<std::vector<PublicKeyCredentialDescriptor>>& + credentials); + +COMPONENT_EXPORT(DEVICE_FIDO) +CtapDeviceResponseCode WinErrorNameToCtapDeviceResponseCode( + const base::string16& error_name); + +} // namespace device + +#endif // DEVICE_FIDO_WIN_TYPE_CONVERSIONS_H_ diff --git a/chromium/device/fido/win/webauthn_api.cc b/chromium/device/fido/win/webauthn_api.cc new file mode 100644 index 00000000000..22765b41685 --- /dev/null +++ b/chromium/device/fido/win/webauthn_api.cc @@ -0,0 +1,348 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/fido/win/webauthn_api.h" + +#include "base/bind.h" +#include "base/feature_list.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/memory/ref_counted.h" +#include "base/native_library.h" +#include "base/no_destructor.h" +#include "base/sequenced_task_runner.h" +#include "base/strings/utf_string_conversions.h" +#include "base/task_runner_util.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "base/threading/thread.h" +#include "device/fido/features.h" +#include "device/fido/win/type_conversions.h" + +namespace device { + +// We do not integrate with older API versions of webauthn.dll because they +// don't support BLE and direct device access to USB and BLE FIDO devices is +// not yet blocked on those platforms. +constexpr uint32_t kMinWinWebAuthnApiVersion = WEBAUTHN_API_VERSION_1; + +namespace { +base::string16 OptionalGURLToUTF16(const base::Optional<GURL>& in) { + return in ? base::UTF8ToUTF16(in->spec()) : base::string16(); +} +} // namespace + +// WinWebAuthnApiImpl is the default implementation of WinWebAuthnApi, which +// attempts to load webauthn.dll on intialization. +class WinWebAuthnApiImpl : public WinWebAuthnApi { + public: + WinWebAuthnApiImpl() + : is_bound_(false), + thread_(std::make_unique<base::Thread>("WindowsWebAuthnAPIRequest")) { + static HMODULE webauthn_dll = LoadLibraryA("webauthn.dll"); + if (!webauthn_dll) { + return; + } + +#define BIND_FN(fn_pointer, lib_handle, fn_name) \ + DCHECK(!fn_pointer); \ + fn_pointer = reinterpret_cast<decltype(fn_pointer)>( \ + GetProcAddress(lib_handle, fn_name)); + +#define BIND_FN_OR_RETURN(fn_pointer, lib_handle, fn_name) \ + BIND_FN(fn_pointer, lib_handle, fn_name); \ + if (!fn_pointer) { \ + DLOG(ERROR) << "failed to bind " << fn_name; \ + return; \ + } + + BIND_FN_OR_RETURN(is_user_verifying_platform_authenticator_available_, + webauthn_dll, + "WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable"); + BIND_FN_OR_RETURN(authenticator_make_credential_, webauthn_dll, + "WebAuthNAuthenticatorMakeCredential"); + BIND_FN_OR_RETURN(authenticator_get_assertion_, webauthn_dll, + "WebAuthNAuthenticatorGetAssertion"); + BIND_FN_OR_RETURN(cancel_current_operation_, webauthn_dll, + "WebAuthNCancelCurrentOperation"); + BIND_FN_OR_RETURN(get_error_name_, webauthn_dll, "WebAuthNGetErrorName"); + BIND_FN_OR_RETURN(free_credential_attestation_, webauthn_dll, + "WebAuthNFreeCredentialAttestation"); + BIND_FN_OR_RETURN(free_assertion_, webauthn_dll, "WebAuthNFreeAssertion"); + + is_bound_ = true; + + // Determine the API version of webauthn.dll. There is a version currently + // shipping with Windows RS5 from before WebAuthNGetApiVersionNumber was + // added (i.e., before WEBAUTHN_API_VERSION_1). Therefore we allow this + // function to be missing. + BIND_FN(get_api_version_number_, webauthn_dll, + "WebAuthNGetApiVersionNumber"); + api_version_ = get_api_version_number_ ? get_api_version_number_() : 0; + thread_->Start(); + } + + ~WinWebAuthnApiImpl() override {} + + // WinWebAuthnApi: + bool IsAvailable() const override { + return is_bound_ && (api_version_ >= kMinWinWebAuthnApiVersion || + base::FeatureList::IsEnabled( + kWebAuthDisableWinApiVersionCheckForTesting)); + } + + HRESULT IsUserVerifyingPlatformAuthenticatorAvailable(BOOL* result) override { + DCHECK(is_bound_); + return is_user_verifying_platform_authenticator_available_(result); + } + + void AuthenticatorMakeCredential( + HWND h_wnd, + GUID cancellation_id, + PublicKeyCredentialRpEntity rp, + PublicKeyCredentialUserEntity user, + std::vector<WEBAUTHN_COSE_CREDENTIAL_PARAMETER> + cose_credential_parameter_values, + std::string client_data_json, + std::vector<WEBAUTHN_EXTENSION> extensions, + base::Optional<std::vector<PublicKeyCredentialDescriptor>> exclude_list, + WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS options, + AuthenticatorMakeCredentialCallback callback) override { + DCHECK(is_bound_); + base::PostTaskAndReplyWithResult( + thread_->task_runner().get(), FROM_HERE, + base::BindOnce(&WinWebAuthnApiImpl::AuthenticatorMakeCredentialBlocking, + base::Unretained(this), // |thread_| is owned by this. + h_wnd, cancellation_id, std::move(rp), std::move(user), + std::move(cose_credential_parameter_values), + std::move(client_data_json), std::move(extensions), + std::move(exclude_list), std::move(options)), + base::BindOnce(&WinWebAuthnApiImpl::AuthenticatorMakeCredentialDone, + base::Unretained(this), + base::SequencedTaskRunnerHandle::Get(), + std::move(callback))); + } + + void AuthenticatorGetAssertion( + HWND h_wnd, + GUID cancellation_id, + base::string16 rp_id, + base::Optional<base::string16> opt_app_id, + std::string client_data_json, + base::Optional<std::vector<PublicKeyCredentialDescriptor>> allow_list, + WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS options, + AuthenticatorGetAssertionCallback callback) override { + DCHECK(is_bound_); + base::PostTaskAndReplyWithResult( + thread_->task_runner().get(), FROM_HERE, + base::BindOnce(&WinWebAuthnApiImpl::AuthenticatorGetAssertionBlocking, + base::Unretained(this), // |thread_| is owned by this. + h_wnd, cancellation_id, std::move(rp_id), + std::move(opt_app_id), std::move(client_data_json), + std::move(allow_list), std::move(options)), + base::BindOnce(&WinWebAuthnApiImpl::AuthenticatorGetAssertionDone, + base::Unretained(this), + base::SequencedTaskRunnerHandle::Get(), + std::move(callback))); + } + + HRESULT CancelCurrentOperation(GUID* cancellation_id) override { + return cancel_current_operation_(cancellation_id); + } + + const wchar_t* GetErrorName(HRESULT hr) override { + DCHECK(is_bound_); + return get_error_name_(hr); + }; + + private: + std::pair<HRESULT, ScopedCredentialAttestation> + AuthenticatorMakeCredentialBlocking( + HWND h_wnd, + GUID cancellation_id, + PublicKeyCredentialRpEntity rp, + PublicKeyCredentialUserEntity user, + std::vector<WEBAUTHN_COSE_CREDENTIAL_PARAMETER> + cose_credential_parameter_values, + std::string client_data_json, + std::vector<WEBAUTHN_EXTENSION> extensions, + base::Optional<std::vector<PublicKeyCredentialDescriptor>> exclude_list, + WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS options) { + base::string16 rp_id = base::UTF8ToUTF16(rp.rp_id()); + base::string16 rp_name = base::UTF8ToUTF16(rp.rp_name().value_or("")); + base::string16 rp_icon_url = OptionalGURLToUTF16(rp.rp_icon_url()); + base::string16 user_name = base::UTF8ToUTF16(user.user_name().value_or("")); + base::string16 user_icon_url = OptionalGURLToUTF16(user.user_icon_url()); + WEBAUTHN_RP_ENTITY_INFORMATION rp_info{ + WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION, rp_id.c_str(), + rp_name.c_str(), rp_icon_url.c_str()}; + + base::string16 user_display_name = + base::UTF8ToUTF16(user.user_display_name().value_or("")); + WEBAUTHN_USER_ENTITY_INFORMATION user_info{ + WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION, + user.user_id().size(), + const_cast<unsigned char*>(user.user_id().data()), + user_name.c_str(), + user_icon_url.c_str(), + user_display_name.c_str(), // This appears to be ignored. + }; + + WEBAUTHN_COSE_CREDENTIAL_PARAMETERS cose_credential_parameters{ + cose_credential_parameter_values.size(), + cose_credential_parameter_values.data()}; + + WEBAUTHN_CLIENT_DATA client_data{ + WEBAUTHN_CLIENT_DATA_CURRENT_VERSION, client_data_json.size(), + const_cast<unsigned char*>( + reinterpret_cast<const unsigned char*>(client_data_json.data())), + WEBAUTHN_HASH_ALGORITHM_SHA_256}; + + options.Extensions = + WEBAUTHN_EXTENSIONS{extensions.size(), extensions.data()}; + options.pCancellationId = &cancellation_id; + + std::vector<WEBAUTHN_CREDENTIAL_EX> exclude_list_credentials = + ToWinCredentialExVector(exclude_list); + std::vector<WEBAUTHN_CREDENTIAL_EX*> exclude_list_ptrs; + std::transform(exclude_list_credentials.begin(), + exclude_list_credentials.end(), + std::back_inserter(exclude_list_ptrs), + [](auto& cred) { return &cred; }); + WEBAUTHN_CREDENTIAL_LIST exclude_credential_list{exclude_list_ptrs.size(), + exclude_list_ptrs.data()}; + options.pExcludeCredentialList = &exclude_credential_list; + + WEBAUTHN_CREDENTIAL_ATTESTATION* credential_attestation_ptr = nullptr; + HRESULT hresult = authenticator_make_credential_( + h_wnd, &rp_info, &user_info, &cose_credential_parameters, &client_data, + &options, &credential_attestation_ptr); + return std::make_pair( + hresult, ScopedCredentialAttestation(credential_attestation_ptr, + free_credential_attestation_)); + } + + std::pair<HRESULT, ScopedAssertion> AuthenticatorGetAssertionBlocking( + HWND h_wnd, + GUID cancellation_id, + base::string16 rp_id, + base::Optional<base::string16> opt_app_id, + std::string client_data_json, + base::Optional<std::vector<PublicKeyCredentialDescriptor>> allow_list, + WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS options) { + if (opt_app_id) { + options.pwszU2fAppId = opt_app_id->data(); + static BOOL kUseAppIdTrue = TRUE; // const + options.pbU2fAppId = &kUseAppIdTrue; + } else { + static BOOL kUseAppIdFalse = FALSE; // const + options.pbU2fAppId = &kUseAppIdFalse; + } + options.pCancellationId = &cancellation_id; + + std::vector<WEBAUTHN_CREDENTIAL_EX> allow_list_credentials = + ToWinCredentialExVector(allow_list); + std::vector<WEBAUTHN_CREDENTIAL_EX*> allow_list_ptrs; + std::transform(allow_list_credentials.begin(), allow_list_credentials.end(), + std::back_inserter(allow_list_ptrs), + [](auto& cred) { return &cred; }); + WEBAUTHN_CREDENTIAL_LIST allow_credential_list{allow_list_ptrs.size(), + allow_list_ptrs.data()}; + options.pAllowCredentialList = &allow_credential_list; + + // As of Nov 2018, the WebAuthNAuthenticatorGetAssertion method will fail + // to challenge credentials via CTAP1 if the allowList is passed in the + // extended form in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS (i.e. + // pAllowCredentialList instead of CredentialList). The legacy + // CredentialList field works fine, but does not support setting + // transport restrictions on the credential descriptor. + // + // As a workaround, MS tells us to also set the CredentialList parameter + // with an accurate cCredentials count and some arbitrary pCredentials + // data. + auto legacy_credentials = ToWinCredentialVector(allow_list); + options.CredentialList = WEBAUTHN_CREDENTIALS{legacy_credentials.size(), + legacy_credentials.data()}; + + WEBAUTHN_CLIENT_DATA client_data{ + WEBAUTHN_CLIENT_DATA_CURRENT_VERSION, client_data_json.size(), + const_cast<unsigned char*>( + reinterpret_cast<const unsigned char*>(client_data_json.data())), + WEBAUTHN_HASH_ALGORITHM_SHA_256}; + + WEBAUTHN_ASSERTION* assertion_ptr = nullptr; + HRESULT hresult = authenticator_get_assertion_( + h_wnd, rp_id.data(), &client_data, &options, &assertion_ptr); + return std::make_pair(hresult, + ScopedAssertion(assertion_ptr, free_assertion_)); + } + + void AuthenticatorMakeCredentialDone( + scoped_refptr<base::SequencedTaskRunner> callback_runner, + AuthenticatorMakeCredentialCallback callback, + std::pair<HRESULT, ScopedCredentialAttestation> result) { + callback_runner->PostTask(FROM_HERE, + base::BindOnce(std::move(callback), result.first, + std::move(result.second))); + } + + void AuthenticatorGetAssertionDone( + scoped_refptr<base::SequencedTaskRunner> callback_runner, + AuthenticatorGetAssertionCallback callback, + std::pair<HRESULT, ScopedAssertion> result) { + callback_runner->PostTask(FROM_HERE, + base::BindOnce(std::move(callback), result.first, + std::move(result.second))); + } + + decltype(&WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable) + is_user_verifying_platform_authenticator_available_ = nullptr; + decltype( + &WebAuthNAuthenticatorMakeCredential) authenticator_make_credential_ = + nullptr; + decltype(&WebAuthNAuthenticatorGetAssertion) authenticator_get_assertion_ = + nullptr; + decltype(&WebAuthNCancelCurrentOperation) cancel_current_operation_ = nullptr; + decltype(&WebAuthNGetErrorName) get_error_name_ = nullptr; + decltype(&WebAuthNFreeCredentialAttestation) free_credential_attestation_ = + nullptr; + decltype(&WebAuthNFreeAssertion) free_assertion_ = nullptr; + + // This method is not available in all versions of webauthn.dll. + decltype(&WebAuthNGetApiVersionNumber) get_api_version_number_ = nullptr; + + bool is_bound_ = false; + uint32_t api_version_ = 0; + + std::unique_ptr<base::Thread> thread_; +}; + +static WinWebAuthnApi* kDefaultForTesting = nullptr; + +// static +WinWebAuthnApi* WinWebAuthnApi::GetDefault() { + if (kDefaultForTesting) { + return kDefaultForTesting; + } + + static base::NoDestructor<WinWebAuthnApiImpl> api; + return api.get(); +} + +// static +void WinWebAuthnApi::SetDefaultForTesting(WinWebAuthnApi* api) { + DCHECK(!kDefaultForTesting); + kDefaultForTesting = api; +} + +// static +void WinWebAuthnApi::ClearDefaultForTesting() { + DCHECK(kDefaultForTesting); + kDefaultForTesting = nullptr; +} + +WinWebAuthnApi::~WinWebAuthnApi() = default; + +} // namespace device diff --git a/chromium/device/fido/win/webauthn_api.h b/chromium/device/fido/win/webauthn_api.h new file mode 100644 index 00000000000..10f2a50c69a --- /dev/null +++ b/chromium/device/fido/win/webauthn_api.h @@ -0,0 +1,116 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_FIDO_WIN_WEBAUTHN_API_H_ +#define DEVICE_FIDO_WIN_WEBAUTHN_API_H_ + +#include <windows.h> +#include <functional> +#include <memory> + +#include "base/callback.h" +#include "base/component_export.h" +#include "base/macros.h" +#include "base/optional.h" +#include "device/fido/public_key_credential_descriptor.h" +#include "device/fido/public_key_credential_rp_entity.h" +#include "device/fido/public_key_credential_user_entity.h" +#include "third_party/microsoft_webauthn/webauthn.h" + +namespace device { + +// WinWebAuthnApi is a wrapper for the native Windows WebAuthn API. +// +// The default singleton instance can be obtained by calling |GetDefault|. +// Users must check the result of |IsAvailable| on the instance to verify that +// the native library was loaded successfully before invoking any of the other +// methods. +class COMPONENT_EXPORT(DEVICE_FIDO) WinWebAuthnApi { + public: + // ScopedCredentialAttestation is a scoped deleter for a + // WEB_AUTHN_CREDENTIAL_ATTESTATION pointer. + // + // Instances must not outlive their WinWebAuthnApi. + using ScopedCredentialAttestation = + std::unique_ptr<WEBAUTHN_CREDENTIAL_ATTESTATION, + std::function<void(WEBAUTHN_CREDENTIAL_ATTESTATION*)>>; + + // ScopedAssertion is a scoped deleter for a WEB_AUTHN_ASSERTION pointer. + // + // Instances must not outlive their WinWebAuthnApi. + using ScopedAssertion = + std::unique_ptr<WEBAUTHN_ASSERTION, + std::function<void(WEBAUTHN_ASSERTION*)>>; + + using AuthenticatorMakeCredentialCallback = + base::OnceCallback<void(HRESULT, ScopedCredentialAttestation)>; + using AuthenticatorGetAssertionCallback = + base::OnceCallback<void(HRESULT, ScopedAssertion)>; + + // Returns the default implementation of WinWebAuthnApi backed by + // webauthn.dll. May return nullptr if webauthn.dll cannot be loaded. + static WinWebAuthnApi* GetDefault(); + + virtual ~WinWebAuthnApi(); + + // Returns whether the API is available on this system. If this returns + // false, none of the other methods on this instance may be called. + virtual bool IsAvailable() const = 0; + + // See WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable in <webauthn.h>. + virtual HRESULT IsUserVerifyingPlatformAuthenticatorAvailable( + BOOL* available) = 0; + + // See WebAuthNAuthenticatorMakeCredential in <webauthn.h>. + // + // The following fields in |options| are ignored because they get filled in + // from the other parameters: + // - Extensions + // - pCancellationId + // - CredentialList / pExcludeCredentialList + virtual void AuthenticatorMakeCredential( + HWND h_wnd, + GUID cancellation_id, + PublicKeyCredentialRpEntity rp, + PublicKeyCredentialUserEntity user, + std::vector<WEBAUTHN_COSE_CREDENTIAL_PARAMETER> + cose_credential_parameter_values, + std::string client_data_json, + std::vector<WEBAUTHN_EXTENSION> extensions, + base::Optional<std::vector<PublicKeyCredentialDescriptor>> exclude_list, + WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS options, + AuthenticatorMakeCredentialCallback callback) = 0; + + // See WebAuthNAuthenticatorGetAssertion in <webauthn.h>. + // + // The following fields in |options| are ignored because they get filled in + // from the other parameters: + // - pwszU2fAppId / pbU2fAppId + // - pCancellationId + // - CredentialList / pAllowCredentialList + virtual void AuthenticatorGetAssertion( + HWND h_wnd, + GUID cancellation_id, + base::string16 rp_id, + base::Optional<base::string16> opt_app_id, + std::string client_data_json, + base::Optional<std::vector<PublicKeyCredentialDescriptor>> allow_list, + WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS options, + AuthenticatorGetAssertionCallback callback) = 0; + + // See WebAuthNCancelCurrentOperation in <webauthn.h>. + virtual HRESULT CancelCurrentOperation(GUID* cancellation_id) = 0; + + // See WebAuthNGetErrorName in <webauthn.h>. + virtual const wchar_t* GetErrorName(HRESULT hr) = 0; + + private: + friend class ScopedFakeWinWebAuthnApi; + static void SetDefaultForTesting(WinWebAuthnApi* api); + static void ClearDefaultForTesting(); +}; + +} // namespace device + +#endif // DEVICE_FIDO_WIN_WEBAUTHN_API_H_ diff --git a/chromium/device/gamepad/gamepad_provider.cc b/chromium/device/gamepad/gamepad_provider.cc index 8b7b1b0c080..1078b56639b 100644 --- a/chromium/device/gamepad/gamepad_provider.cc +++ b/chromium/device/gamepad/gamepad_provider.cc @@ -144,8 +144,7 @@ void GamepadProvider::Pause() { base::AutoLock lock(is_paused_lock_); is_paused_ = true; } - base::MessageLoop* polling_loop = polling_thread_->message_loop(); - polling_loop->task_runner()->PostTask( + polling_thread_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&GamepadProvider::SendPauseHint, Unretained(this), true)); } @@ -158,11 +157,10 @@ void GamepadProvider::Resume() { is_paused_ = false; } - base::MessageLoop* polling_loop = polling_thread_->message_loop(); - polling_loop->task_runner()->PostTask( + polling_thread_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&GamepadProvider::SendPauseHint, Unretained(this), false)); - polling_loop->task_runner()->PostTask( + polling_thread_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&GamepadProvider::ScheduleDoPoll, Unretained(this))); } diff --git a/chromium/device/udev_linux/udev_watcher.cc b/chromium/device/udev_linux/udev_watcher.cc index d9e490de63a..a2bf268e9f9 100644 --- a/chromium/device/udev_linux/udev_watcher.cc +++ b/chromium/device/udev_linux/udev_watcher.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/memory/ptr_util.h" +#include "base/threading/scoped_blocking_call.h" namespace device { @@ -16,6 +17,7 @@ void UdevWatcher::Observer::OnDeviceAdded(ScopedUdevDevicePtr device) {} void UdevWatcher::Observer::OnDeviceRemoved(ScopedUdevDevicePtr device) {} std::unique_ptr<UdevWatcher> UdevWatcher::StartWatching(Observer* observer) { + base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK); ScopedUdevPtr udev(udev_new()); if (!udev) { LOG(ERROR) << "Failed to initialize udev."; @@ -50,6 +52,7 @@ UdevWatcher::~UdevWatcher() { void UdevWatcher::EnumerateExistingDevices() { DCHECK(sequence_checker_.CalledOnValidSequence()); + base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK); ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev_.get())); if (!enumerate) { LOG(ERROR) << "Failed to initialize a udev enumerator."; @@ -84,6 +87,7 @@ UdevWatcher::UdevWatcher(ScopedUdevPtr udev, } void UdevWatcher::OnMonitorReadable() { + base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK); ScopedUdevDevicePtr device(udev_monitor_receive_device(udev_monitor_.get())); if (!device) return; diff --git a/chromium/device/usb/OWNERS b/chromium/device/usb/OWNERS index b78b7788c92..a855859018f 100644 --- a/chromium/device/usb/OWNERS +++ b/chromium/device/usb/OWNERS @@ -1,5 +1,5 @@ pfeldman@chromium.org reillyg@chromium.org -rockot@chromium.org +rockot@google.com # COMPONENT: IO>USB diff --git a/chromium/device/usb/mojo/device_impl.cc b/chromium/device/usb/mojo/device_impl.cc index f97bcf079b0..49aa637f698 100644 --- a/chromium/device/usb/mojo/device_impl.cc +++ b/chromium/device/usb/mojo/device_impl.cc @@ -109,6 +109,11 @@ DeviceImpl::DeviceImpl(scoped_refptr<device::UsbDevice> device, weak_factory_(this) { DCHECK(device_); observer_.Add(device_.get()); + + if (client_) { + client_.set_connection_error_handler(base::BindOnce( + &DeviceImpl::OnClientConnectionError, weak_factory_.GetWeakPtr())); + } } void DeviceImpl::CloseHandle() { @@ -392,5 +397,11 @@ void DeviceImpl::OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) { binding_->Close(); } +void DeviceImpl::OnClientConnectionError() { + // Close the connection with Blink when WebUsbServiceImpl notifies the + // permission revocation from settings UI. + binding_->Close(); +} + } // namespace usb } // namespace device diff --git a/chromium/device/usb/mojo/device_impl.h b/chromium/device/usb/mojo/device_impl.h index 74062646312..e1d99949273 100644 --- a/chromium/device/usb/mojo/device_impl.h +++ b/chromium/device/usb/mojo/device_impl.h @@ -95,6 +95,8 @@ class DeviceImpl : public mojom::UsbDevice, public device::UsbDevice::Observer { // device::UsbDevice::Observer implementation: void OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) override; + void OnClientConnectionError(); + const scoped_refptr<device::UsbDevice> device_; ScopedObserver<device::UsbDevice, device::UsbDevice::Observer> observer_; diff --git a/chromium/device/usb/mojo/device_manager_impl.cc b/chromium/device/usb/mojo/device_manager_impl.cc index 9a3e29e084c..f622141341b 100644 --- a/chromium/device/usb/mojo/device_manager_impl.cc +++ b/chromium/device/usb/mojo/device_manager_impl.cc @@ -37,11 +37,19 @@ void DeviceManagerImpl::AddBinding(mojom::UsbDeviceManagerRequest request) { bindings_.AddBinding(this, std::move(request)); } +void DeviceManagerImpl::EnumerateDevicesAndSetClient( + mojom::UsbDeviceManagerClientAssociatedPtrInfo client, + EnumerateDevicesAndSetClientCallback callback) { + usb_service_->GetDevices(base::Bind( + &DeviceManagerImpl::OnGetDevices, weak_factory_.GetWeakPtr(), + /*options=*/nullptr, base::Passed(&client), base::Passed(&callback))); +} + void DeviceManagerImpl::GetDevices(mojom::UsbEnumerationOptionsPtr options, GetDevicesCallback callback) { - usb_service_->GetDevices( - base::Bind(&DeviceManagerImpl::OnGetDevices, weak_factory_.GetWeakPtr(), - base::Passed(&options), base::Passed(&callback))); + usb_service_->GetDevices(base::Bind( + &DeviceManagerImpl::OnGetDevices, weak_factory_.GetWeakPtr(), + base::Passed(&options), /*client=*/nullptr, base::Passed(&callback))); } void DeviceManagerImpl::GetDevice(const std::string& guid, @@ -65,6 +73,7 @@ void DeviceManagerImpl::SetClient( void DeviceManagerImpl::OnGetDevices( mojom::UsbEnumerationOptionsPtr options, + mojom::UsbDeviceManagerClientAssociatedPtrInfo client, GetDevicesCallback callback, const std::vector<scoped_refptr<UsbDevice>>& devices) { std::vector<mojom::UsbDeviceFilterPtr> filters; @@ -79,6 +88,9 @@ void DeviceManagerImpl::OnGetDevices( } std::move(callback).Run(std::move(device_infos)); + + if (client) + SetClient(std::move(client)); } void DeviceManagerImpl::OnDeviceAdded(scoped_refptr<UsbDevice> device) { diff --git a/chromium/device/usb/mojo/device_manager_impl.h b/chromium/device/usb/mojo/device_manager_impl.h index 1b6066ec782..139e401297d 100644 --- a/chromium/device/usb/mojo/device_manager_impl.h +++ b/chromium/device/usb/mojo/device_manager_impl.h @@ -39,6 +39,9 @@ class DeviceManagerImpl : public mojom::UsbDeviceManager, private: // DeviceManager implementation: + void EnumerateDevicesAndSetClient( + mojom::UsbDeviceManagerClientAssociatedPtrInfo client, + EnumerateDevicesAndSetClientCallback callback) override; void GetDevices(mojom::UsbEnumerationOptionsPtr options, GetDevicesCallback callback) override; void GetDevice(const std::string& guid, @@ -49,6 +52,7 @@ class DeviceManagerImpl : public mojom::UsbDeviceManager, // Callbacks to handle the async responses from the underlying UsbService. void OnGetDevices(mojom::UsbEnumerationOptionsPtr options, + mojom::UsbDeviceManagerClientAssociatedPtrInfo client, GetDevicesCallback callback, const std::vector<scoped_refptr<UsbDevice>>& devices); diff --git a/chromium/device/usb/mojo/type_converters.cc b/chromium/device/usb/mojo/type_converters.cc index 89034c933fe..c834249c282 100644 --- a/chromium/device/usb/mojo/type_converters.cc +++ b/chromium/device/usb/mojo/type_converters.cc @@ -113,6 +113,7 @@ TypeConverter<device::mojom::UsbDeviceInfoPtr, device::UsbDevice>::Convert( info->manufacturer_name = device.manufacturer_string(); info->product_name = device.product_string(); info->serial_number = device.serial_number(); + info->webusb_landing_page = device.webusb_landing_page(); const device::UsbConfigDescriptor* config = device.active_configuration(); info->active_configuration = config ? config->configuration_value : 0; info->configurations = diff --git a/chromium/device/usb/public/cpp/BUILD.gn b/chromium/device/usb/public/cpp/BUILD.gn index df68a9d4b9a..ea55d7cf853 100644 --- a/chromium/device/usb/public/cpp/BUILD.gn +++ b/chromium/device/usb/public/cpp/BUILD.gn @@ -33,5 +33,6 @@ static_library("test_support") { public_deps = [ "//base", "//device/usb/public/mojom", + "//url:url", ] } diff --git a/chromium/device/usb/public/cpp/fake_usb_device.cc b/chromium/device/usb/public/cpp/fake_usb_device.cc index a1a5b95a93b..c5929833509 100644 --- a/chromium/device/usb/public/cpp/fake_usb_device.cc +++ b/chromium/device/usb/public/cpp/fake_usb_device.cc @@ -35,6 +35,11 @@ FakeUsbDevice::FakeUsbDevice(scoped_refptr<FakeUsbDeviceInfo> device, : device_(device), observer_(this), client_(std::move(client)) { DCHECK(device_); observer_.Add(device_.get()); + + if (client_) { + client_.set_connection_error_handler(base::BindOnce( + &FakeUsbDevice::OnClientConnectionError, base::Unretained(this))); + } } void FakeUsbDevice::CloseHandle() { @@ -130,4 +135,10 @@ void FakeUsbDevice::OnDeviceRemoved(scoped_refptr<FakeUsbDeviceInfo> device) { binding_->Close(); } +void FakeUsbDevice::OnClientConnectionError() { + // Close the binding with Blink when WebUsbService finds permission revoked + // from setting UI. + binding_->Close(); +} + } // namespace device diff --git a/chromium/device/usb/public/cpp/fake_usb_device.h b/chromium/device/usb/public/cpp/fake_usb_device.h index be85bdb73cd..c1e8d1ed6cb 100644 --- a/chromium/device/usb/public/cpp/fake_usb_device.h +++ b/chromium/device/usb/public/cpp/fake_usb_device.h @@ -77,6 +77,8 @@ class FakeUsbDevice : public mojom::UsbDevice, // FakeUsbDeviceInfo::Observer implementation: void OnDeviceRemoved(scoped_refptr<FakeUsbDeviceInfo> device) override; + void OnClientConnectionError(); + void CloseHandle(); const scoped_refptr<FakeUsbDeviceInfo> device_; diff --git a/chromium/device/usb/public/cpp/fake_usb_device_info.cc b/chromium/device/usb/public/cpp/fake_usb_device_info.cc index 8fad4d7b9b3..b014504dbb0 100644 --- a/chromium/device/usb/public/cpp/fake_usb_device_info.cc +++ b/chromium/device/usb/public/cpp/fake_usb_device_info.cc @@ -33,13 +33,27 @@ FakeUsbDeviceInfo::FakeUsbDeviceInfo(uint16_t vendor_id, uint16_t product_id, const std::string& manufacturer_string, const std::string& product_string, - const std::string& serial_number) { + const std::string& serial_number) + : FakeUsbDeviceInfo(vendor_id, + product_id, + manufacturer_string, + product_string, + serial_number, + GURL()) {} + +FakeUsbDeviceInfo::FakeUsbDeviceInfo(uint16_t vendor_id, + uint16_t product_id, + const std::string& manufacturer_string, + const std::string& product_string, + const std::string& serial_number, + const GURL& webusb_landing_page) { SetDefault(); device_info_.vendor_id = vendor_id; device_info_.product_id = product_id; device_info_.manufacturer_name = base::UTF8ToUTF16(manufacturer_string); device_info_.product_name = base::UTF8ToUTF16(product_string), device_info_.serial_number = base::UTF8ToUTF16(serial_number); + device_info_.webusb_landing_page = webusb_landing_page; } FakeUsbDeviceInfo::~FakeUsbDeviceInfo() = default; diff --git a/chromium/device/usb/public/cpp/fake_usb_device_info.h b/chromium/device/usb/public/cpp/fake_usb_device_info.h index fe3ec66b3e9..d69b442c079 100644 --- a/chromium/device/usb/public/cpp/fake_usb_device_info.h +++ b/chromium/device/usb/public/cpp/fake_usb_device_info.h @@ -13,6 +13,7 @@ #include "base/memory/weak_ptr.h" #include "device/usb/public/mojom/device.mojom.h" #include "mojo/public/cpp/bindings/strong_binding.h" +#include "url/gurl.h" namespace device { @@ -33,6 +34,12 @@ class FakeUsbDeviceInfo : public base::RefCounted<FakeUsbDeviceInfo> { const std::string& manufacturer_string, const std::string& product_string, const std::string& serial_number); + FakeUsbDeviceInfo(uint16_t vendor_id, + uint16_t product_id, + const std::string& manufacturer_string, + const std::string& product_string, + const std::string& serial_number, + const GURL& webusb_landing_page); void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); diff --git a/chromium/device/usb/public/cpp/fake_usb_device_manager.cc b/chromium/device/usb/public/cpp/fake_usb_device_manager.cc index de2c0617251..046013fbf26 100644 --- a/chromium/device/usb/public/cpp/fake_usb_device_manager.cc +++ b/chromium/device/usb/public/cpp/fake_usb_device_manager.cc @@ -19,6 +19,13 @@ FakeUsbDeviceManager::FakeUsbDeviceManager() : weak_factory_(this) {} FakeUsbDeviceManager::~FakeUsbDeviceManager() {} +void FakeUsbDeviceManager::EnumerateDevicesAndSetClient( + mojom::UsbDeviceManagerClientAssociatedPtrInfo client, + EnumerateDevicesAndSetClientCallback callback) { + GetDevices(nullptr, std::move(callback)); + SetClient(std::move(client)); +} + // mojom::UsbDeviceManager implementation: void FakeUsbDeviceManager::GetDevices(mojom::UsbEnumerationOptionsPtr options, GetDevicesCallback callback) { diff --git a/chromium/device/usb/public/cpp/fake_usb_device_manager.h b/chromium/device/usb/public/cpp/fake_usb_device_manager.h index 1473fc0235d..d89a1a7a033 100644 --- a/chromium/device/usb/public/cpp/fake_usb_device_manager.h +++ b/chromium/device/usb/public/cpp/fake_usb_device_manager.h @@ -48,6 +48,9 @@ class FakeUsbDeviceManager : public mojom::UsbDeviceManager { private: // mojom::UsbDeviceManager implementation: + void EnumerateDevicesAndSetClient( + mojom::UsbDeviceManagerClientAssociatedPtrInfo client, + EnumerateDevicesAndSetClientCallback callback) override; void GetDevices(mojom::UsbEnumerationOptionsPtr options, GetDevicesCallback callback) override; void GetDevice(const std::string& guid, diff --git a/chromium/device/usb/public/mojom/BUILD.gn b/chromium/device/usb/public/mojom/BUILD.gn index 703ff1e77d6..276a40410cf 100644 --- a/chromium/device/usb/public/mojom/BUILD.gn +++ b/chromium/device/usb/public/mojom/BUILD.gn @@ -10,8 +10,9 @@ mojom("mojom") { "device_manager.mojom", ] - deps = [ + public_deps = [ "//mojo/public/mojom/base", + "//url/mojom:url_mojom_gurl", ] # USB Mojom interfaces are exposed publicly to layout tests which use diff --git a/chromium/device/usb/public/mojom/device.mojom b/chromium/device/usb/public/mojom/device.mojom index 17b2b498442..59d77d5b3ec 100644 --- a/chromium/device/usb/public/mojom/device.mojom +++ b/chromium/device/usb/public/mojom/device.mojom @@ -5,6 +5,7 @@ module device.mojom; import "mojo/public/mojom/base/string16.mojom"; +import "url/mojom/url.mojom"; enum UsbOpenDeviceError { // Opening the device succeeded. @@ -88,6 +89,7 @@ struct UsbDeviceInfo { mojo_base.mojom.String16? manufacturer_name; mojo_base.mojom.String16? product_name; mojo_base.mojom.String16? serial_number; + url.mojom.Url? webusb_landing_page; uint8 active_configuration; array<UsbConfigurationInfo> configurations; }; diff --git a/chromium/device/usb/public/mojom/device_manager.mojom b/chromium/device/usb/public/mojom/device_manager.mojom index 633fbcb679f..95ecff70049 100644 --- a/chromium/device/usb/public/mojom/device_manager.mojom +++ b/chromium/device/usb/public/mojom/device_manager.mojom @@ -32,6 +32,11 @@ struct UsbEnumerationOptions { interface UsbDeviceManager { // Retrieves information about all devices available to the DeviceManager + // implementation and set client for device added/removed events. + EnumerateDevicesAndSetClient(associated UsbDeviceManagerClient client) + => (array<UsbDeviceInfo> results); + + // Retrieves devices information filtered by |options| to the DeviceManager // implementation. GetDevices(UsbEnumerationOptions? options) => (array<UsbDeviceInfo> results); diff --git a/chromium/device/usb/usb_device_handle_win.cc b/chromium/device/usb/usb_device_handle_win.cc index 1084d49aa93..265c3f29e81 100644 --- a/chromium/device/usb/usb_device_handle_win.cc +++ b/chromium/device/usb/usb_device_handle_win.cc @@ -90,50 +90,61 @@ uint8_t BuildRequestFlags(UsbTransferDirection direction, // Encapsulates waiting for the completion of an overlapped event. class UsbDeviceHandleWin::Request : public base::win::ObjectWatcher::Delegate { public: - explicit Request(HANDLE handle) - : handle_(handle), event_(CreateEvent(nullptr, false, false, nullptr)) { + Request(HANDLE handle, bool winusb_handle) + : handle_(handle), + winusb_handle_(winusb_handle), + event_(CreateEvent(nullptr, false, false, nullptr)) { memset(&overlapped_, 0, sizeof(overlapped_)); overlapped_.hEvent = event_.Get(); } - ~Request() override { - if (callback_) { - SetLastError(ERROR_REQUEST_ABORTED); - std::move(callback_).Run(this, false, 0); - } - } + ~Request() override = default; // Starts watching for completion of the overlapped event. void MaybeStartWatching( BOOL success, + DWORD last_error, base::OnceCallback<void(Request*, DWORD, size_t)> callback) { callback_ = std::move(callback); if (success) { OnObjectSignaled(event_.Get()); } else { - DWORD error = GetLastError(); - if (error == ERROR_IO_PENDING) { + if (last_error == ERROR_IO_PENDING) watcher_.StartWatchingOnce(event_.Get(), this); - } else { - std::move(callback_).Run(this, error, 0); - } + else + std::move(callback_).Run(this, last_error, 0); } } + void Abort() { + watcher_.StopWatching(); + std::move(callback_).Run(this, ERROR_REQUEST_ABORTED, 0); + } + OVERLAPPED* overlapped() { return &overlapped_; } // base::win::ObjectWatcher::Delegate void OnObjectSignaled(HANDLE object) override { DCHECK_EQ(object, event_.Get()); DWORD size; - if (GetOverlappedResult(handle_, &overlapped_, &size, true)) + BOOL result; + if (winusb_handle_) + result = WinUsb_GetOverlappedResult(handle_, &overlapped_, &size, true); + else + result = GetOverlappedResult(handle_, &overlapped_, &size, true); + DWORD last_error = GetLastError(); + + if (result) std::move(callback_).Run(this, ERROR_SUCCESS, size); else - std::move(callback_).Run(this, GetLastError(), 0); + std::move(callback_).Run(this, last_error, 0); } private: HANDLE handle_; + // Records whether |handle_| above is a true HANDLE or a + // WINUSB_INTERFACE_HANDLE. + bool winusb_handle_; OVERLAPPED overlapped_; base::win::ScopedHandle event_; base::win::ObjectWatcher watcher_; @@ -163,7 +174,17 @@ void UsbDeviceHandleWin::Close() { hub_handle_.Close(); } - requests_.clear(); + if (function_handle_.IsValid()) { + CancelIo(function_handle_.Get()); + function_handle_.Close(); + first_interface_handle_ = INVALID_HANDLE_VALUE; + } + + // Aborting requests may run or destroy callbacks holding the last reference + // to this object so hold a reference for the rest of this method. + scoped_refptr<UsbDeviceHandleWin> self(this); + while (!requests_.empty()) + requests_.begin()->second->Abort(); } void UsbDeviceHandleWin::SetConfiguration(int configuration_value, @@ -285,13 +306,15 @@ void UsbDeviceHandleWin::ControlTransfer( auto* node_connection_info = new USB_NODE_CONNECTION_INFORMATION_EX; node_connection_info->ConnectionIndex = device_->port_number(); - Request* request = MakeRequest(hub_handle_.Get()); + Request* request = MakeRequest(false /* winusb_handle */); + BOOL result = DeviceIoControl( + hub_handle_.Get(), IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, + node_connection_info, sizeof(*node_connection_info), + node_connection_info, sizeof(*node_connection_info), nullptr, + request->overlapped()); + DWORD last_error = GetLastError(); request->MaybeStartWatching( - DeviceIoControl(hub_handle_.Get(), - IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, - node_connection_info, sizeof(*node_connection_info), - node_connection_info, sizeof(*node_connection_info), - nullptr, request->overlapped()), + result, last_error, base::BindOnce(&UsbDeviceHandleWin::GotNodeConnectionInformation, weak_factory_.GetWeakPtr(), std::move(callback), base::Owned(node_connection_info), buffer)); @@ -309,13 +332,14 @@ void UsbDeviceHandleWin::ControlTransfer( descriptor_request->SetupPacket.wIndex = index; descriptor_request->SetupPacket.wLength = buffer->size(); - Request* request = MakeRequest(hub_handle_.Get()); + Request* request = MakeRequest(false /* winusb_handle */); + BOOL result = DeviceIoControl( + hub_handle_.Get(), IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, + request_buffer->front(), size, request_buffer->front(), size, + nullptr, request->overlapped()); + DWORD last_error = GetLastError(); request->MaybeStartWatching( - DeviceIoControl(hub_handle_.Get(), - IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, - request_buffer->front(), size, - request_buffer->front(), size, nullptr, - request->overlapped()), + result, last_error, base::BindOnce(&UsbDeviceHandleWin::GotDescriptorFromNodeConnection, weak_factory_.GetWeakPtr(), std::move(callback), request_buffer, buffer)); @@ -350,12 +374,16 @@ void UsbDeviceHandleWin::ControlTransfer( setup.Index = index; setup.Length = buffer->size(); - Request* control_request = MakeRequest(handle); - control_request->MaybeStartWatching( + Request* control_request = MakeRequest(true /* winusb_handle */); + BOOL result = WinUsb_ControlTransfer(handle, setup, buffer->front(), buffer->size(), - nullptr, control_request->overlapped()), + nullptr, control_request->overlapped()); + DWORD last_error = GetLastError(); + control_request->MaybeStartWatching( + result, last_error, base::BindOnce(&UsbDeviceHandleWin::TransferComplete, - weak_factory_.GetWeakPtr(), std::move(callback), buffer)); + weak_factory_.GetWeakPtr(), nullptr, std::move(callback), + buffer)); } void UsbDeviceHandleWin::IsochronousTransferIn( @@ -465,6 +493,8 @@ bool UsbDeviceHandleWin::OpenInterfaceHandle(Interface* interface) { USB_PLOG(ERROR) << "Failed to initialize WinUSB handle"; return false; } + + first_interface_handle_ = handle; } else { auto first_interface_it = interfaces_.find(interface->first_interface); DCHECK(first_interface_it != interfaces_.end()); @@ -532,8 +562,11 @@ WINUSB_INTERFACE_HANDLE UsbDeviceHandleWin::GetInterfaceForControlTransfer( return interface->handle.Get(); } -UsbDeviceHandleWin::Request* UsbDeviceHandleWin::MakeRequest(HANDLE handle) { - auto request = std::make_unique<Request>(hub_handle_.Get()); +UsbDeviceHandleWin::Request* UsbDeviceHandleWin::MakeRequest( + bool winusb_handle) { + auto request = std::make_unique<Request>( + winusb_handle ? first_interface_handle_ : hub_handle_.Get(), + winusb_handle); Request* request_ptr = request.get(); requests_[request_ptr] = std::move(request); return request_ptr; @@ -602,22 +635,32 @@ void UsbDeviceHandleWin::GotDescriptorFromNodeConnection( } void UsbDeviceHandleWin::TransferComplete( + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, TransferCallback callback, scoped_refptr<base::RefCountedBytes> buffer, Request* request_ptr, DWORD win32_result, size_t bytes_transferred) { std::unique_ptr<Request> request = UnlinkRequest(request_ptr); + UsbTransferStatus status = UsbTransferStatus::COMPLETED; if (win32_result != ERROR_SUCCESS) { SetLastError(win32_result); USB_PLOG(ERROR) << "Transfer failed"; - std::move(callback).Run(UsbTransferStatus::TRANSFER_ERROR, nullptr, 0); - return; + + buffer = nullptr; + bytes_transferred = 0; + status = UsbTransferStatus::TRANSFER_ERROR; } - std::move(callback).Run(UsbTransferStatus::COMPLETED, std::move(buffer), - bytes_transferred); + if (!callback_task_runner || + callback_task_runner->RunsTasksInCurrentSequence()) { + std::move(callback).Run(status, std::move(buffer), bytes_transferred); + } else { + callback_task_runner->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), status, + std::move(buffer), bytes_transferred)); + } } void UsbDeviceHandleWin::GenericTransferInternal( @@ -654,7 +697,7 @@ void UsbDeviceHandleWin::GenericTransferInternal( } DCHECK(interface->handle.IsValid()); - Request* request = MakeRequest(interface->handle.Get()); + Request* request = MakeRequest(true /* winusb_handle */); BOOL result; if (direction == UsbTransferDirection::INBOUND) { result = WinUsb_ReadPipe(interface->handle.Get(), endpoint_address, @@ -665,10 +708,13 @@ void UsbDeviceHandleWin::GenericTransferInternal( buffer->front(), buffer->size(), nullptr, request->overlapped()); } + DWORD last_error = GetLastError(); request->MaybeStartWatching( - result, base::BindOnce(&UsbDeviceHandleWin::TransferComplete, - weak_factory_.GetWeakPtr(), std::move(callback), - std::move(buffer))); + result, last_error, + base::BindOnce(&UsbDeviceHandleWin::TransferComplete, + weak_factory_.GetWeakPtr(), + std::move(callback_task_runner), std::move(callback), + std::move(buffer))); } void UsbDeviceHandleWin::ReportIsochronousError( diff --git a/chromium/device/usb/usb_device_handle_win.h b/chromium/device/usb/usb_device_handle_win.h index 75ccbd06fa4..4547ac543da 100644 --- a/chromium/device/usb/usb_device_handle_win.h +++ b/chromium/device/usb/usb_device_handle_win.h @@ -121,7 +121,7 @@ class UsbDeviceHandleWin : public UsbDeviceHandle { void SetInterfaceAlternateSettingComplete(uint8_t interface_number, uint8_t alternate_setting, const ResultCallback& callback); - Request* MakeRequest(HANDLE handle); + Request* MakeRequest(bool winusb_handle); std::unique_ptr<Request> UnlinkRequest(Request* request); void GotNodeConnectionInformation(TransferCallback callback, void* node_connection_info, @@ -136,11 +136,13 @@ class UsbDeviceHandleWin : public UsbDeviceHandle { Request* request_ptr, DWORD win32_result, size_t bytes_transferred); - void TransferComplete(TransferCallback callback, - scoped_refptr<base::RefCountedBytes> buffer, - Request* request_ptr, - DWORD win32_result, - size_t bytes_transferred); + void TransferComplete( + scoped_refptr<base::SequencedTaskRunner> callback_task_runner, + TransferCallback callback, + scoped_refptr<base::RefCountedBytes> buffer, + Request* request_ptr, + DWORD win32_result, + size_t bytes_transferred); void GenericTransferInternal( UsbTransferDirection direction, uint8_t endpoint_number, @@ -163,6 +165,9 @@ class UsbDeviceHandleWin : public UsbDeviceHandle { base::win::ScopedHandle hub_handle_; base::win::ScopedHandle function_handle_; + // The handle returned by WinUsb_Initialize is special. + WINUSB_INTERFACE_HANDLE first_interface_handle_ = INVALID_HANDLE_VALUE; + std::map<uint8_t, Interface> interfaces_; std::map<uint8_t, Endpoint> endpoints_; std::map<Request*, std::unique_ptr<Request>> requests_; diff --git a/chromium/device/vr/BUILD.gn b/chromium/device/vr/BUILD.gn index 03921dcf8ed..a8577f98735 100644 --- a/chromium/device/vr/BUILD.gn +++ b/chromium/device/vr/BUILD.gn @@ -63,6 +63,8 @@ if (enable_vr) { "android/gvr/gvr_gamepad_data_fetcher.cc", "android/gvr/gvr_gamepad_data_fetcher.h", "android/gvr/gvr_gamepad_data_provider.h", + "android/gvr/vr_module_delegate.cc", + "android/gvr/vr_module_delegate.h", ] if (enable_arcore) { diff --git a/chromium/device/vr/PRESUBMIT.py b/chromium/device/vr/PRESUBMIT.py deleted file mode 100644 index efd7ca550a0..00000000000 --- a/chromium/device/vr/PRESUBMIT.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) 2018 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Top-level presubmit script for device/vr. - -See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts -for more details about the presubmit API built into depot_tools. -""" - - -def PostUploadHook(cl, change, output_api): - """git cl upload will call this hook after the issue is created/modified. - - This hook modifies the CL description in order to run extra GPU - tests (in particular, WebXR and WebVR browser tests) in addition - to the regular CQ try bots. This test suite is too large to run - against all Chromium commits, but should be run against changes - likely to affect these tests. - """ - return output_api.EnsureCQIncludeTrybotsAreAdded( - cl, - ['luci.chromium.try:win_optional_gpu_tests_rel'], - 'Automatically added optional GPU tests to run on CQ.') diff --git a/chromium/device/vr/android/gvr/gvr_delegate_provider.h b/chromium/device/vr/android/gvr/gvr_delegate_provider.h index 60c3419e363..17625eea070 100644 --- a/chromium/device/vr/android/gvr/gvr_delegate_provider.h +++ b/chromium/device/vr/android/gvr/gvr_delegate_provider.h @@ -19,7 +19,6 @@ class DEVICE_VR_EXPORT GvrDelegateProvider { public: GvrDelegateProvider() = default; virtual bool ShouldDisableGvrDevice() = 0; - virtual void SetDeviceId(mojom::XRDeviceId device_id) = 0; virtual void StartWebXRPresentation( mojom::VRDisplayInfoPtr display_info, mojom::XRRuntimeSessionOptionsPtr options, @@ -28,7 +27,7 @@ class DEVICE_VR_EXPORT GvrDelegateProvider { virtual void OnListeningForActivateChanged(bool listening) = 0; protected: - virtual ~GvrDelegateProvider() {} + virtual ~GvrDelegateProvider() = default; private: DISALLOW_COPY_AND_ASSIGN(GvrDelegateProvider); diff --git a/chromium/device/vr/android/gvr/gvr_device.cc b/chromium/device/vr/android/gvr/gvr_device.cc index a8a3118d278..f3a24a448d5 100644 --- a/chromium/device/vr/android/gvr/gvr_device.cc +++ b/chromium/device/vr/android/gvr/gvr_device.cc @@ -16,6 +16,7 @@ #include "device/vr/android/gvr/gvr_delegate_provider.h" #include "device/vr/android/gvr/gvr_delegate_provider_factory.h" #include "device/vr/android/gvr/gvr_device_provider.h" +#include "device/vr/android/gvr/vr_module_delegate.h" #include "device/vr/vr_display_impl.h" #include "jni/NonPresentingGvrContext_jni.h" #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr.h" @@ -161,33 +162,12 @@ void GvrDevice::RequestSession( mojom::XRRuntimeSessionOptionsPtr options, mojom::XRRuntime::RequestSessionCallback callback) { if (!gvr_api_) { - EnsureGvrReady(); - if (!gvr_api_) { - std::move(callback).Run(nullptr, nullptr); - return; - } - } - - if (!options->immersive) { - // TODO(https://crbug.com/695937): This should be NOTREACHED() once we no - // longer need the hacked GRV non-immersive mode. This should now only be - // hit if orientation devices are disabled by flag. - ReturnNonImmersiveSession(std::move(callback)); + Init(base::BindOnce(&GvrDevice::OnInitRequestSessionFinished, + base::Unretained(this), std::move(options), + std::move(callback))); return; } - - GvrDelegateProvider* delegate_provider = GetGvrDelegateProvider(); - if (!delegate_provider) { - std::move(callback).Run(nullptr, nullptr); - return; - } - - // StartWebXRPresentation is async as we may trigger a DON (Device ON) flow - // that pauses Chrome. - delegate_provider->StartWebXRPresentation( - GetVRDisplayInfo(), std::move(options), - base::BindOnce(&GvrDevice::OnStartPresentResult, - weak_ptr_factory_.GetWeakPtr(), std::move(callback))); + OnInitRequestSessionFinished(std::move(options), std::move(callback), true); } void GvrDevice::OnStartPresentResult( @@ -233,31 +213,7 @@ void GvrDevice::StopPresenting() { exclusive_controller_binding_.Close(); } -void GvrDevice::EnsureGvrReady() { - if (!non_presenting_context_.obj() || !gvr_api_) { - GvrDelegateProvider* delegate_provider = GetGvrDelegateProvider(); - if (!delegate_provider || delegate_provider->ShouldDisableGvrDevice()) - return; - JNIEnv* env = base::android::AttachCurrentThread(); - non_presenting_context_.Reset(Java_NonPresentingGvrContext_create( - env, reinterpret_cast<jlong>(this))); - if (!non_presenting_context_.obj()) - return; - jlong context = Java_NonPresentingGvrContext_getNativeGvrContext( - env, non_presenting_context_); - gvr_api_ = - gvr::GvrApi::WrapNonOwned(reinterpret_cast<gvr_context*>(context)); - SetVRDisplayInfo(CreateVRDisplayInfo(gvr_api_.get(), GetId())); - - if (paused_) { - PauseTracking(); - } else { - ResumeTracking(); - } - } -} - -void GvrDevice::OnMagicWindowFrameDataRequest( +void GvrDevice::OnGetInlineFrameData( mojom::XRFrameDataProvider::GetFrameDataCallback callback) { if (!gvr_api_) { std::move(callback).Run(nullptr); @@ -295,17 +251,15 @@ void GvrDevice::ResumeTracking() { } void GvrDevice::EnsureInitialized(EnsureInitializedCallback callback) { - EnsureGvrReady(); - std::move(callback).Run(); + Init(base::BindOnce([](EnsureInitializedCallback callback, + bool success) { std::move(callback).Run(); }, + std::move(callback))); } GvrDelegateProvider* GvrDevice::GetGvrDelegateProvider() { - // GvrDelegateProviderFactory::Create() may fail transiently, so every time we - // try to get it, set the device ID. - GvrDelegateProvider* delegate_provider = GvrDelegateProviderFactory::Create(); - if (delegate_provider) - delegate_provider->SetDeviceId(GetId()); - return delegate_provider; + // GvrDelegateProviderFactory::Create() may return a different + // pointer each time. Do not cache it. + return GvrDelegateProviderFactory::Create(); } void GvrDevice::OnDisplayConfigurationChanged(JNIEnv* env, @@ -319,4 +273,85 @@ void GvrDevice::Activate(mojom::VRDisplayEventReason reason, OnActivate(reason, std::move(on_handled)); } +void GvrDevice::Init(base::OnceCallback<void(bool)> on_finished) { + VrModuleDelegate* module_delegate = VrModuleDelegate::Get(); + if (!module_delegate) { + std::move(on_finished).Run(false); + return; + } + if (!module_delegate->ModuleInstalled()) { + module_delegate->InstallModule( + base::BindOnce(&GvrDevice::OnVrModuleInstalled, base::Unretained(this), + std::move(on_finished))); + return; + } + OnVrModuleInstalled(std::move(on_finished), true); +} + +void GvrDevice::OnVrModuleInstalled(base::OnceCallback<void(bool)> on_finished, + bool success) { + if (!success) { + std::move(on_finished).Run(false); + return; + } + GvrDelegateProvider* delegate_provider = GetGvrDelegateProvider(); + if (!delegate_provider || delegate_provider->ShouldDisableGvrDevice()) { + std::move(on_finished).Run(false); + return; + } + CreateNonPresentingContext(); + std::move(on_finished).Run(non_presenting_context_.obj() != nullptr); +} + +void GvrDevice::CreateNonPresentingContext() { + if (non_presenting_context_.obj()) + return; + JNIEnv* env = base::android::AttachCurrentThread(); + non_presenting_context_.Reset( + Java_NonPresentingGvrContext_create(env, reinterpret_cast<jlong>(this))); + if (!non_presenting_context_.obj()) + return; + jlong context = Java_NonPresentingGvrContext_getNativeGvrContext( + env, non_presenting_context_); + gvr_api_ = gvr::GvrApi::WrapNonOwned(reinterpret_cast<gvr_context*>(context)); + SetVRDisplayInfo(CreateVRDisplayInfo(gvr_api_.get(), GetId())); + + if (paused_) { + PauseTracking(); + } else { + ResumeTracking(); + } +} + +void GvrDevice::OnInitRequestSessionFinished( + mojom::XRRuntimeSessionOptionsPtr options, + mojom::XRRuntime::RequestSessionCallback callback, + bool success) { + if (!success) { + std::move(callback).Run(nullptr, nullptr); + return; + } + + GvrDelegateProvider* delegate_provider = GetGvrDelegateProvider(); + if (!delegate_provider) { + std::move(callback).Run(nullptr, nullptr); + return; + } + + if (!options->immersive) { + // TODO(https://crbug.com/695937): This should be NOTREACHED() once we no + // longer need the hacked GVR non-immersive mode. This should now only be + // hit if orientation devices are disabled by flag. + ReturnNonImmersiveSession(std::move(callback)); + return; + } + + // StartWebXRPresentation is async as we may trigger a DON (Device ON) flow + // that pauses Chrome. + delegate_provider->StartWebXRPresentation( + GetVRDisplayInfo(), std::move(options), + base::BindOnce(&GvrDevice::OnStartPresentResult, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); +} + } // namespace device diff --git a/chromium/device/vr/android/gvr/gvr_device.h b/chromium/device/vr/android/gvr/gvr_device.h index 93388c05a79..552787c276c 100644 --- a/chromium/device/vr/android/gvr/gvr_device.h +++ b/chromium/device/vr/android/gvr/gvr_device.h @@ -42,7 +42,7 @@ class DEVICE_VR_EXPORT GvrDevice : public VRDeviceBase, private: // VRDeviceBase void OnListeningForActivate(bool listening) override; - void OnMagicWindowFrameDataRequest( + void OnGetInlineFrameData( mojom::XRFrameDataProvider::GetFrameDataCallback callback) override; void OnStartPresentResult(mojom::XRRuntime::RequestSessionCallback callback, @@ -53,9 +53,17 @@ class DEVICE_VR_EXPORT GvrDevice : public VRDeviceBase, void OnPresentingControllerMojoConnectionError(); void StopPresenting(); - void EnsureGvrReady(); GvrDelegateProvider* GetGvrDelegateProvider(); + void Init(base::OnceCallback<void(bool)> on_finished); + void OnVrModuleInstalled(base::OnceCallback<void(bool)> on_finished, + bool success); + void CreateNonPresentingContext(); + void OnInitRequestSessionFinished( + mojom::XRRuntimeSessionOptionsPtr options, + mojom::XRRuntime::RequestSessionCallback callback, + bool success); + base::android::ScopedJavaGlobalRef<jobject> non_presenting_context_; std::unique_ptr<gvr::GvrApi> gvr_api_; diff --git a/chromium/device/vr/android/gvr/vr_module_delegate.cc b/chromium/device/vr/android/gvr/vr_module_delegate.cc new file mode 100644 index 00000000000..d20434701f8 --- /dev/null +++ b/chromium/device/vr/android/gvr/vr_module_delegate.cc @@ -0,0 +1,28 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/vr/android/gvr/vr_module_delegate.h" + +namespace device { + +namespace { +// Storing the global delegate in a raw pointer - as opposed to e.g. an +// std::unique_ptr - to avoid adding a static initializer. +VrModuleDelegate* g_vr_module_delegate = nullptr; +} // namespace + +// static +VrModuleDelegate* VrModuleDelegate::Get() { + return g_vr_module_delegate; +} + +// static +void VrModuleDelegate::Set(std::unique_ptr<VrModuleDelegate> delegate) { + if (g_vr_module_delegate) { + delete g_vr_module_delegate; + } + g_vr_module_delegate = delegate.release(); +} + +} // namespace device diff --git a/chromium/device/vr/android/gvr/vr_module_delegate.h b/chromium/device/vr/android/gvr/vr_module_delegate.h new file mode 100644 index 00000000000..9e6c204f3a8 --- /dev/null +++ b/chromium/device/vr/android/gvr/vr_module_delegate.h @@ -0,0 +1,38 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_VR_ANDROID_GVR_VR_MODULE_DELEGATE_H_ +#define DEVICE_VR_ANDROID_GVR_VR_MODULE_DELEGATE_H_ + +#include "base/callback.h" +#include "base/macros.h" +#include "device/vr/vr_export.h" + +namespace device { + +// Delegates installation of the VR module. +class DEVICE_VR_EXPORT VrModuleDelegate { + public: + // Returns the global module delegate. + static VrModuleDelegate* Get(); + // Sets the global module delegate. + static void Set(std::unique_ptr<VrModuleDelegate> delegate); + + VrModuleDelegate() = default; + virtual ~VrModuleDelegate() = default; + // Returns true if the VR module is installed. + virtual bool ModuleInstalled() = 0; + // Asynchronously requests to install the VR module. |on_finished| is called + // after the module install is completed. If |success| is false the module + // install failed. + virtual void InstallModule( + base::OnceCallback<void(bool success)> on_finished) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(VrModuleDelegate); +}; + +} // namespace device + +#endif // DEVICE_VR_ANDROID_GVR_VR_MODULE_DELEGATE_H_ diff --git a/chromium/device/vr/oculus/oculus_device.cc b/chromium/device/vr/oculus/oculus_device.cc index 8b8929f7b0c..2af79021205 100644 --- a/chromium/device/vr/oculus/oculus_device.cc +++ b/chromium/device/vr/oculus/oculus_device.cc @@ -242,7 +242,7 @@ void OculusDevice::StopOvrSession() { } } -void OculusDevice::OnMagicWindowFrameDataRequest( +void OculusDevice::OnGetInlineFrameData( mojom::XRFrameDataProvider::GetFrameDataCallback callback) { if (!session_) { std::move(callback).Run(nullptr); diff --git a/chromium/device/vr/oculus/oculus_device.h b/chromium/device/vr/oculus/oculus_device.h index 95b514f4d00..b8861b15d41 100644 --- a/chromium/device/vr/oculus/oculus_device.h +++ b/chromium/device/vr/oculus/oculus_device.h @@ -31,7 +31,7 @@ class DEVICE_VR_EXPORT OculusDevice void RequestSession( mojom::XRRuntimeSessionOptionsPtr options, mojom::XRRuntime::RequestSessionCallback callback) override; - void OnMagicWindowFrameDataRequest( + void OnGetInlineFrameData( mojom::XRFrameDataProvider::GetFrameDataCallback callback) override; void OnRequestSessionResult(mojom::XRRuntime::RequestSessionCallback callback, bool result, diff --git a/chromium/device/vr/oculus/oculus_render_loop.cc b/chromium/device/vr/oculus/oculus_render_loop.cc index 84564c261c3..73a2284f3b2 100644 --- a/chromium/device/vr/oculus/oculus_render_loop.cc +++ b/chromium/device/vr/oculus/oculus_render_loop.cc @@ -80,6 +80,15 @@ mojom::XRGamepadDataPtr OculusRenderLoop::GetNextGamepadData() { return OculusGamepadHelper::GetGamepadData(session_); } +void OculusRenderLoop::GetEnvironmentIntegrationProvider( + mojom::XREnvironmentIntegrationProviderAssociatedRequest + environment_provider) { + // Environment integration is not supported. This call should not + // be made on this device. + mojo::ReportBadMessage("Environment integration is not supported."); + return; +} + bool OculusRenderLoop::StartRuntime() { if (!session_) { ovrInitParams initParams = {ovrInit_RequestVersion | ovrInit_MixedRendering, diff --git a/chromium/device/vr/oculus/oculus_render_loop.h b/chromium/device/vr/oculus/oculus_render_loop.h index 7e2f41b7ff3..9cbddaa15d7 100644 --- a/chromium/device/vr/oculus/oculus_render_loop.h +++ b/chromium/device/vr/oculus/oculus_render_loop.h @@ -33,6 +33,9 @@ class OculusRenderLoop : public XRCompositorCommon { // XRDeviceAbstraction: mojom::XRFrameDataPtr GetNextFrameData() override; mojom::XRGamepadDataPtr GetNextGamepadData() override; + void GetEnvironmentIntegrationProvider( + mojom::XREnvironmentIntegrationProviderAssociatedRequest + environment_provider) override; bool StartRuntime() override; void StopRuntime() override; void OnSessionStart() override; diff --git a/chromium/device/vr/openvr/openvr_device.cc b/chromium/device/vr/openvr/openvr_device.cc index d3be3f9e1a6..6d0c39322cd 100644 --- a/chromium/device/vr/openvr/openvr_device.cc +++ b/chromium/device/vr/openvr/openvr_device.cc @@ -298,7 +298,7 @@ void OpenVRDevice::OnPresentingControllerMojoConnectionError() { exclusive_controller_binding_.Close(); } -void OpenVRDevice::OnMagicWindowFrameDataRequest( +void OpenVRDevice::OnGetInlineFrameData( mojom::XRFrameDataProvider::GetFrameDataCallback callback) { if (!openvr_) { std::move(callback).Run(nullptr); diff --git a/chromium/device/vr/openvr/openvr_device.h b/chromium/device/vr/openvr/openvr_device.h index 1360a8c97d3..3faed27fb4f 100644 --- a/chromium/device/vr/openvr/openvr_device.h +++ b/chromium/device/vr/openvr/openvr_device.h @@ -47,7 +47,7 @@ class DEVICE_VR_EXPORT OpenVRDevice private: // VRDeviceBase - void OnMagicWindowFrameDataRequest( + void OnGetInlineFrameData( mojom::XRFrameDataProvider::GetFrameDataCallback callback) override; // XRSessionController diff --git a/chromium/device/vr/openvr/openvr_render_loop.cc b/chromium/device/vr/openvr/openvr_render_loop.cc index 5986403265e..1e7e744d129 100644 --- a/chromium/device/vr/openvr/openvr_render_loop.cc +++ b/chromium/device/vr/openvr/openvr_render_loop.cc @@ -141,18 +141,13 @@ void OpenVRRenderLoop::OnSessionStart() { openvr_->GetCompositor()->SuspendRendering(false); // Measure the VrViewerType we are presenting with. - using ViewerMap = std::map<std::string, VrViewerType>; - CR_DEFINE_STATIC_LOCAL(ViewerMap, viewer_types, - ({ - {"Oculus Rift CV1", VrViewerType::OPENVR_RIFT_CV1}, - {"Vive MV", VrViewerType::OPENVR_VIVE}, - })); - VrViewerType type = VrViewerType::OPENVR_UNKNOWN; std::string model = GetOpenVRString(openvr_->GetSystem(), vr::Prop_ModelNumber_String); - auto it = viewer_types.find(model); - if (it != viewer_types.end()) - type = it->second; + VrViewerType type = VrViewerType::OPENVR_UNKNOWN; + if (model == "Oculus Rift CV1") + type = VrViewerType::OPENVR_RIFT_CV1; + else if (model == "Vive MV") + type = VrViewerType::OPENVR_VIVE; base::UmaHistogramSparse("VRViewerType", static_cast<int>(type)); } @@ -200,6 +195,15 @@ mojom::XRFrameDataPtr OpenVRRenderLoop::GetNextFrameData() { return frame_data; } +void OpenVRRenderLoop::GetEnvironmentIntegrationProvider( + mojom::XREnvironmentIntegrationProviderAssociatedRequest + environment_provider) { + // Environment integration is not supported. This call should not + // be made on this device. + mojo::ReportBadMessage("Environment integration is not supported."); + return; +} + std::vector<mojom::XRInputSourceStatePtr> OpenVRRenderLoop::GetInputState( vr::TrackedDevicePose_t* poses, uint32_t count) { diff --git a/chromium/device/vr/openvr/openvr_render_loop.h b/chromium/device/vr/openvr/openvr_render_loop.h index 2fb39602f17..a4cc3f9189b 100644 --- a/chromium/device/vr/openvr/openvr_render_loop.h +++ b/chromium/device/vr/openvr/openvr_render_loop.h @@ -34,6 +34,9 @@ class OpenVRRenderLoop : public XRCompositorCommon { // XRDeviceAbstraction: mojom::XRFrameDataPtr GetNextFrameData() override; mojom::XRGamepadDataPtr GetNextGamepadData() override; + void GetEnvironmentIntegrationProvider( + mojom::XREnvironmentIntegrationProviderAssociatedRequest + environment_provider) override; bool StartRuntime() override; void StopRuntime() override; void OnSessionStart() override; diff --git a/chromium/device/vr/orientation/orientation_device.cc b/chromium/device/vr/orientation/orientation_device.cc index 0d7fb6947ba..9f040c516fd 100644 --- a/chromium/device/vr/orientation/orientation_device.cc +++ b/chromium/device/vr/orientation/orientation_device.cc @@ -147,7 +147,7 @@ void VROrientationDevice::RequestSession( ReturnNonImmersiveSession(std::move(callback)); } -void VROrientationDevice::OnMagicWindowFrameDataRequest( +void VROrientationDevice::OnGetInlineFrameData( mojom::XRFrameDataProvider::GetFrameDataCallback callback) { mojom::VRPosePtr pose = mojom::VRPose::New(); pose->orientation.emplace(4); diff --git a/chromium/device/vr/orientation/orientation_device.h b/chromium/device/vr/orientation/orientation_device.h index d6f3c329142..f6e39daa704 100644 --- a/chromium/device/vr/orientation/orientation_device.h +++ b/chromium/device/vr/orientation/orientation_device.h @@ -48,7 +48,7 @@ class DEVICE_VR_EXPORT VROrientationDevice : public VRDeviceBase, mojom::XRRuntime::RequestSessionCallback callback) override; // VRDeviceBase - void OnMagicWindowFrameDataRequest( + void OnGetInlineFrameData( mojom::XRFrameDataProvider::GetFrameDataCallback callback) override; // Indicates whether the device was able to connect to orientation events. diff --git a/chromium/device/vr/orientation/orientation_device_provider_unittest.cc b/chromium/device/vr/orientation/orientation_device_provider_unittest.cc index d4fc8cfcac4..ea6e47dab3c 100644 --- a/chromium/device/vr/orientation/orientation_device_provider_unittest.cc +++ b/chromium/device/vr/orientation/orientation_device_provider_unittest.cc @@ -42,9 +42,8 @@ class VROrientationDeviceProviderTest : public testing::Test { service_manager::mojom::ConnectorRequest request; connector_ = service_manager::Connector::Create(&request); - service_manager::Connector::TestApi test_api(connector_.get()); - test_api.OverrideBinderForTesting( - service_manager::Identity(mojom::kServiceName), + connector_->OverrideBinderForTesting( + service_manager::ServiceFilter::ByName(mojom::kServiceName), mojom::SensorProvider::Name_, base::BindRepeating(&FakeSensorProvider::Bind, base::Unretained(fake_sensor_provider_.get()))); diff --git a/chromium/device/vr/orientation/orientation_device_unittest.cc b/chromium/device/vr/orientation/orientation_device_unittest.cc index 9934679c10e..12f4f8f63b8 100644 --- a/chromium/device/vr/orientation/orientation_device_unittest.cc +++ b/chromium/device/vr/orientation/orientation_device_unittest.cc @@ -134,7 +134,7 @@ class VROrientationDeviceTest : public testing::Test { base::RunLoop loop; - device_->OnMagicWindowFrameDataRequest(base::BindOnce( + device_->OnGetInlineFrameData(base::BindOnce( [](base::OnceClosure quit_closure, base::OnceCallback<void(mojom::VRPosePtr)> callback, mojom::XRFrameDataPtr ptr) { @@ -233,7 +233,7 @@ TEST_F(VROrientationDeviceTest, SensorIsAvailableTest) { } TEST_F(VROrientationDeviceTest, GetOrientationTest) { - // Tests that OnMagicWindowFrameDataRequest returns a pose ptr without mishap. + // Tests that OnGetInlineFrameData returns a pose ptr without mishap. InitializeDevice(FakeInitParams()); diff --git a/chromium/device/vr/public/mojom/isolated_xr_service.mojom b/chromium/device/vr/public/mojom/isolated_xr_service.mojom index ca921e0d161..acb578ca21e 100644 --- a/chromium/device/vr/public/mojom/isolated_xr_service.mojom +++ b/chromium/device/vr/public/mojom/isolated_xr_service.mojom @@ -41,7 +41,7 @@ interface XRRuntimeEventListener { struct XRRuntimeSessionOptions { bool immersive; - bool provide_passthrough_camera; + bool environment_integration; // The following options are used for permission requests. // TODO(crbug.com/854655): remove these fields, and do permission checks in @@ -81,6 +81,8 @@ interface XRRuntime { EnsureInitialized() => (); SetListeningForActivate(bool listen_for_activation); + + SetInlinePosesEnabled(bool enable); }; // Represents the state of a single button or trigger. diff --git a/chromium/device/vr/public/mojom/vr_service.mojom b/chromium/device/vr/public/mojom/vr_service.mojom index e752a0729b3..8c9f3f22c06 100644 --- a/chromium/device/vr/public/mojom/vr_service.mojom +++ b/chromium/device/vr/public/mojom/vr_service.mojom @@ -43,7 +43,7 @@ enum XRTargetRayMode { struct XRSessionOptions { bool immersive; - bool provide_passthrough_camera; + bool environment_integration; // A flag to indicate if there has been a user activation when the request // session is made. @@ -73,7 +73,6 @@ struct XRSession { // info to more sensible places so that this doesn't need to be sent here. VRDisplayInfo display_info; XRPresentationConnection? submit_frame_sink; - XREnvironmentIntegrationProvider? environment_provider; }; // This structure contains the infomation and interfaces needed to create a two @@ -165,11 +164,11 @@ struct VRDisplayCapabilities { // Indicates whether the display can actively show imagery on a headset. bool canPresent; - // If true, this is an AR display that can provide a background image along - // with each pose. Clients who want them should send a frame image request - // via getFrameData instead of getPose. - // TODO(https://crbug.com/836349): this may need to change. - bool can_provide_pass_through_images; + // Whether the display gathers data about the environment (for AR like + // planes, point clouds, meshes, etc.). The backend will decide whether + // it needs to provide camera frames or not based on whether it is a + // see-through HMD or camera-based AR system. + bool canProvideEnvironmentIntegration; }; // Information about the optical properties for an eye in a VRDisplay. @@ -303,17 +302,6 @@ interface XRDevice { ExitPresent(); }; -// Provides the necessary functionality for a WebXR session to get data for -// drawing frames. The kind of data it gets depends on what kind of session was -// requested. -// This interface is hosted in the Browser process, but will move to a sandboxed -// utility process on Windows. The render process communicates with it. -interface XRFrameDataProvider { - // frame_data is optional and will not be set if and only if the call fails - // for some reason, such as device disconnection. - GetFrameData() => (XRFrameData? frame_data); -}; - // Provides functionality for integrating environment information into an // XRSession. For example, some AR sessions would implement hit test to allow // developers to get the information about the world that its sensors supply. @@ -335,6 +323,19 @@ interface XREnvironmentIntegrationProvider { RequestHitTest(XRRay ray) => (array<XRHitResult>? results); }; +// Provides the necessary functionality for a WebXR session to get data for +// drawing frames. The kind of data it gets depends on what kind of session was +// requested. +// This interface is hosted in the Browser process, but will move to a sandboxed +// utility process on Windows. The render process communicates with it. +interface XRFrameDataProvider { + // frame_data is optional and will not be set if and only if the call fails + // for some reason, such as device disconnection. + GetFrameData() => (XRFrameData? frame_data); + GetEnvironmentIntegrationProvider( + associated XREnvironmentIntegrationProvider& environment_provider); +}; + // Provides the necessary functionality for sending frames to a headset. // This interface is hosted in the Browser process, but will move to a sandboxed // utility process on Windows. The render process communicates with it. diff --git a/chromium/device/vr/vr_device_base.cc b/chromium/device/vr/vr_device_base.cc index 615568424d6..603f2e26024 100644 --- a/chromium/device/vr/vr_device_base.cc +++ b/chromium/device/vr/vr_device_base.cc @@ -40,10 +40,6 @@ bool VRDeviceBase::HasExclusiveSession() { return presenting_; } -void VRDeviceBase::SetMagicWindowEnabled(bool enabled) { - magic_window_enabled_ = enabled; -} - void VRDeviceBase::ListenToDeviceChanges( mojom::XRRuntimeEventListenerAssociatedPtrInfo listener_info, mojom::XRRuntime::ListenToDeviceChangesCallback callback) { @@ -51,14 +47,14 @@ void VRDeviceBase::ListenToDeviceChanges( std::move(callback).Run(display_info_.Clone()); } -void VRDeviceBase::GetFrameData( +void VRDeviceBase::GetInlineFrameData( mojom::XRFrameDataProvider::GetFrameDataCallback callback) { - if (!magic_window_enabled_) { + if (!inline_poses_enabled_) { std::move(callback).Run(nullptr); return; } - OnMagicWindowFrameDataRequest(std::move(callback)); + OnGetInlineFrameData(std::move(callback)); } void VRDeviceBase::SetVRDisplayInfo(mojom::VRDisplayInfoPtr display_info) { @@ -88,7 +84,7 @@ bool VRDeviceBase::ShouldPauseTrackingWhenFrameDataRestricted() { void VRDeviceBase::OnListeningForActivate(bool listening) {} -void VRDeviceBase::OnMagicWindowFrameDataRequest( +void VRDeviceBase::OnGetInlineFrameData( mojom::XRFrameDataProvider::GetFrameDataCallback callback) { std::move(callback).Run(nullptr); } @@ -101,6 +97,10 @@ void VRDeviceBase::EnsureInitialized(EnsureInitializedCallback callback) { std::move(callback).Run(); } +void VRDeviceBase::SetInlinePosesEnabled(bool enable) { + inline_poses_enabled_ = enable; +} + void VRDeviceBase::RequestHitTest( mojom::XRRayPtr ray, mojom::XREnvironmentIntegrationProvider::RequestHitTestCallback callback) { @@ -113,16 +113,11 @@ void VRDeviceBase::ReturnNonImmersiveSession( mojom::XRFrameDataProviderPtr data_provider; mojom::XREnvironmentIntegrationProviderPtr environment_provider; mojom::XRSessionControllerPtr controller; - magic_window_sessions_.push_back( - std::make_unique<VRDisplayImpl>(this, mojo::MakeRequest(&data_provider), - mojo::MakeRequest(&environment_provider), - mojo::MakeRequest(&controller))); + magic_window_sessions_.push_back(std::make_unique<VRDisplayImpl>( + this, mojo::MakeRequest(&data_provider), mojo::MakeRequest(&controller))); auto session = mojom::XRSession::New(); session->data_provider = data_provider.PassInterface(); - // TODO(http://crbug.com/876135) Not all sessions want the environment - // provider. This should be refactored to only be passed when requested. - session->environment_provider = environment_provider.PassInterface(); if (display_info_) { session->display_info = display_info_.Clone(); } diff --git a/chromium/device/vr/vr_device_base.h b/chromium/device/vr/vr_device_base.h index be6daa5a66e..ef9bf20b5d3 100644 --- a/chromium/device/vr/vr_device_base.h +++ b/chromium/device/vr/vr_device_base.h @@ -31,8 +31,10 @@ class DEVICE_VR_EXPORT VRDeviceBase : public mojom::XRRuntime { mojom::XRRuntime::ListenToDeviceChangesCallback callback) final; void SetListeningForActivate(bool is_listening) override; void EnsureInitialized(EnsureInitializedCallback callback) override; + void SetInlinePosesEnabled(bool enable) override; - void GetFrameData(mojom::XRFrameDataProvider::GetFrameDataCallback callback); + void GetInlineFrameData( + mojom::XRFrameDataProvider::GetFrameDataCallback callback); virtual void RequestHitTest( mojom::XRRayPtr ray, @@ -52,7 +54,6 @@ class DEVICE_VR_EXPORT VRDeviceBase : public mojom::XRRuntime { // GVR delegate. virtual void PauseTracking(); virtual void ResumeTracking(); - void SetMagicWindowEnabled(bool enabled); mojom::VRDisplayInfoPtr GetVRDisplayInfo(); @@ -84,7 +85,7 @@ class DEVICE_VR_EXPORT VRDeviceBase : public mojom::XRRuntime { private: // TODO(https://crbug.com/842227): Rename methods to HandleOnXXX virtual void OnListeningForActivate(bool listening); - virtual void OnMagicWindowFrameDataRequest( + virtual void OnGetInlineFrameData( mojom::XRFrameDataProvider::GetFrameDataCallback callback); mojom::XRRuntimeEventListenerAssociatedPtr listener_; @@ -92,7 +93,7 @@ class DEVICE_VR_EXPORT VRDeviceBase : public mojom::XRRuntime { bool presenting_ = false; device::mojom::XRDeviceId id_; - bool magic_window_enabled_ = true; + bool inline_poses_enabled_ = true; mojo::Binding<mojom::XRRuntime> runtime_binding_; diff --git a/chromium/device/vr/vr_device_base_unittest.cc b/chromium/device/vr/vr_device_base_unittest.cc index 952ae70b00d..666f0f46553 100644 --- a/chromium/device/vr/vr_device_base_unittest.cc +++ b/chromium/device/vr/vr_device_base_unittest.cc @@ -160,10 +160,10 @@ TEST_F(VRDeviceTest, NoMagicWindowPosesWhileBrowsing) { std::make_unique<FakeVRDevice>(static_cast<device::mojom::XRDeviceId>(1)); device->SetPose(mojom::VRPose::New()); - device->GetFrameData(base::BindOnce( + device->GetInlineFrameData(base::BindOnce( [](device::mojom::XRFrameDataPtr data) { EXPECT_TRUE(data); })); - device->SetMagicWindowEnabled(false); - device->GetFrameData(base::BindOnce( + device->SetInlinePosesEnabled(false); + device->GetInlineFrameData(base::BindOnce( [](device::mojom::XRFrameDataPtr data) { EXPECT_FALSE(data); })); } diff --git a/chromium/device/vr/vr_display_impl.cc b/chromium/device/vr/vr_display_impl.cc index ce0c958e77e..2deff0c7914 100644 --- a/chromium/device/vr/vr_display_impl.cc +++ b/chromium/device/vr/vr_display_impl.cc @@ -18,10 +18,9 @@ namespace device { VRDisplayImpl::VRDisplayImpl( VRDeviceBase* device, mojom::XRFrameDataProviderRequest magic_window_request, - mojom::XREnvironmentIntegrationProviderRequest environment_request, mojom::XRSessionControllerRequest session_request) : magic_window_binding_(this, std::move(magic_window_request)), - environment_binding_(this, std::move(environment_request)), + environment_binding_(this), session_controller_binding_(this, std::move(session_request)), device_(device) { // Unretained is safe because the binding will close when we are destroyed, @@ -40,7 +39,21 @@ void VRDisplayImpl::GetFrameData( return; } - device_->GetFrameData(std::move(callback)); + device_->GetInlineFrameData(std::move(callback)); +} + +void VRDisplayImpl::GetEnvironmentIntegrationProvider( + mojom::XREnvironmentIntegrationProviderAssociatedRequest + environment_request) { + if (!device_->GetVRDisplayInfo() + ->capabilities->canProvideEnvironmentIntegration) { + // Environment integration is not supported. This call should not + // be made on this device. + mojo::ReportBadMessage("Environment integration is not supported."); + return; + } + + environment_binding_.Bind(std::move(environment_request)); } void VRDisplayImpl::UpdateSessionGeometry(const gfx::Size& frame_size, diff --git a/chromium/device/vr/vr_display_impl.h b/chromium/device/vr/vr_display_impl.h index 219c9c8c611..72c7a774b3f 100644 --- a/chromium/device/vr/vr_display_impl.h +++ b/chromium/device/vr/vr_display_impl.h @@ -13,6 +13,7 @@ #include "device/vr/public/mojom/vr_service.mojom.h" #include "device/vr/vr_device.h" #include "device/vr/vr_export.h" +#include "mojo/public/cpp/bindings/associated_binding.h" #include "mojo/public/cpp/bindings/binding.h" #include "ui/display/display.h" @@ -31,10 +32,12 @@ class DEVICE_VR_EXPORT VRDisplayImpl public: VRDisplayImpl(VRDeviceBase* device, mojom::XRFrameDataProviderRequest, - mojom::XREnvironmentIntegrationProviderRequest, mojom::XRSessionControllerRequest); ~VRDisplayImpl() override; + void GetEnvironmentIntegrationProvider( + mojom::XREnvironmentIntegrationProviderAssociatedRequest + environment_provider) override; gfx::Size sessionFrameSize() { return session_frame_size_; }; display::Display::Rotation sessionRotation() { return session_rotation_; }; @@ -55,7 +58,8 @@ class DEVICE_VR_EXPORT VRDisplayImpl void OnMojoConnectionError(); mojo::Binding<mojom::XRFrameDataProvider> magic_window_binding_; - mojo::Binding<mojom::XREnvironmentIntegrationProvider> environment_binding_; + mojo::AssociatedBinding<mojom::XREnvironmentIntegrationProvider> + environment_binding_; mojo::Binding<mojom::XRSessionController> session_controller_binding_; device::VRDeviceBase* device_; bool restrict_frame_data_ = true; diff --git a/chromium/device/vr/vr_display_impl_unittest.cc b/chromium/device/vr/vr_display_impl_unittest.cc index fc8ad17219b..1e8ebf4d1ab 100644 --- a/chromium/device/vr/vr_display_impl_unittest.cc +++ b/chromium/device/vr/vr_display_impl_unittest.cc @@ -33,10 +33,8 @@ class VRDisplayImplTest : public testing::Test { std::unique_ptr<VRDisplayImpl> MakeDisplay( mojom::XRSessionControllerPtr* controller) { mojom::XRFrameDataProviderPtr data_provider; - mojom::XREnvironmentIntegrationProviderPtr environment_provider; auto display = std::make_unique<VRDisplayImpl>( device(), mojo::MakeRequest(&data_provider), - mojo::MakeRequest(&environment_provider), mojo::MakeRequest(controller)); static_cast<mojom::XRSessionController*>(display.get()) ->SetFrameDataRestricted(true); |