Commit 9f659100 authored by Pavel Kalinnikov's avatar Pavel Kalinnikov Committed by Commit Bot

[u2f] Add BLE device discovery.

Bug: 763303
Change-Id: If6f2549b302b06587c87268d78f4392dbce6aa59
Reviewed-on: https://chromium-review.googlesource.com/684514
Commit-Queue: Pavel Kalinnikov <pkalinnikov@chromium.org>
Reviewed-by: default avatarVincent Scheib <scheib@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarJan Wilken Dörrie <jdoerrie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#513296}
parent a33960a0
...@@ -132,8 +132,6 @@ test("device_unittests") { ...@@ -132,8 +132,6 @@ test("device_unittests") {
"//device/hid", "//device/hid",
"//device/hid:mocks", "//device/hid:mocks",
"//device/serial", "//device/serial",
"//device/u2f",
"//device/u2f:mocks",
"//services/service_manager/public/cpp", "//services/service_manager/public/cpp",
"//services/service_manager/public/interfaces", "//services/service_manager/public/interfaces",
] ]
...@@ -222,6 +220,11 @@ test("device_unittests") { ...@@ -222,6 +220,11 @@ test("device_unittests") {
deps += [ "//dbus" ] deps += [ "//dbus" ]
} }
# BLE discovery: works on Linux.
if (is_linux) {
sources += [ "u2f/u2f_ble_discovery_unittest.cc" ]
}
if (is_mac) { if (is_mac) {
deps += [ "//third_party/ocmock" ] deps += [ "//third_party/ocmock" ]
ldflags = [ "-ObjC" ] ldflags = [ "-ObjC" ]
......
...@@ -20,6 +20,7 @@ const char BluetoothTestBase::kTestAdapterAddress[] = "A1:B2:C3:D4:E5:F6"; ...@@ -20,6 +20,7 @@ const char BluetoothTestBase::kTestAdapterAddress[] = "A1:B2:C3:D4:E5:F6";
const char BluetoothTestBase::kTestDeviceName[] = "FakeBluetoothDevice"; const char BluetoothTestBase::kTestDeviceName[] = "FakeBluetoothDevice";
const char BluetoothTestBase::kTestDeviceNameEmpty[] = ""; const char BluetoothTestBase::kTestDeviceNameEmpty[] = "";
const char BluetoothTestBase::kTestDeviceNameU2f[] = "U2F FakeDevice";
const char BluetoothTestBase::kTestDeviceAddress1[] = "01:00:00:90:1E:BE"; const char BluetoothTestBase::kTestDeviceAddress1[] = "01:00:00:90:1E:BE";
const char BluetoothTestBase::kTestDeviceAddress2[] = "02:00:00:8B:74:63"; const char BluetoothTestBase::kTestDeviceAddress2[] = "02:00:00:8B:74:63";
...@@ -36,6 +37,8 @@ const char BluetoothTestBase::kTestUUIDLinkLoss[] = ...@@ -36,6 +37,8 @@ const char BluetoothTestBase::kTestUUIDLinkLoss[] =
"00001803-0000-1000-8000-00805f9b34fb"; "00001803-0000-1000-8000-00805f9b34fb";
const char BluetoothTestBase::kTestUUIDHeartRate[] = const char BluetoothTestBase::kTestUUIDHeartRate[] =
"0000180d-0000-1000-8000-00805f9b34fb"; "0000180d-0000-1000-8000-00805f9b34fb";
const char BluetoothTestBase::kTestUUIDU2f[] =
"0000fffd-0000-1000-8000-00805f9b34fb";
// Characteristic UUIDs // Characteristic UUIDs
const char BluetoothTestBase::kTestUUIDDeviceName[] = const char BluetoothTestBase::kTestUUIDDeviceName[] =
"00002a00-0000-1000-8000-00805f9b34fb"; "00002a00-0000-1000-8000-00805f9b34fb";
...@@ -45,6 +48,8 @@ const char BluetoothTestBase::kTestUUIDReconnectionAddress[] = ...@@ -45,6 +48,8 @@ const char BluetoothTestBase::kTestUUIDReconnectionAddress[] =
"00002a03-0000-1000-8000-00805f9b34fb"; "00002a03-0000-1000-8000-00805f9b34fb";
const char BluetoothTestBase::kTestUUIDHeartRateMeasurement[] = const char BluetoothTestBase::kTestUUIDHeartRateMeasurement[] =
"00002a37-0000-1000-8000-00805f9b34fb"; "00002a37-0000-1000-8000-00805f9b34fb";
const char BluetoothTestBase::kTestUUIDU2fControlPointLength[] =
"f1d0fff3-deaa-ecee-b42f-c9ba7ed623bb";
// Descriptor UUIDs // Descriptor UUIDs
const char BluetoothTestBase::kTestUUIDCharacteristicUserDescription[] = const char BluetoothTestBase::kTestUUIDCharacteristicUserDescription[] =
"00002901-0000-1000-8000-00805f9b34fb"; "00002901-0000-1000-8000-00805f9b34fb";
......
...@@ -67,6 +67,7 @@ class BluetoothTestBase : public testing::Test { ...@@ -67,6 +67,7 @@ class BluetoothTestBase : public testing::Test {
static const char kTestDeviceName[]; static const char kTestDeviceName[];
static const char kTestDeviceNameEmpty[]; static const char kTestDeviceNameEmpty[];
static const char kTestDeviceNameU2f[];
static const char kTestDeviceAddress1[]; static const char kTestDeviceAddress1[];
static const char kTestDeviceAddress2[]; static const char kTestDeviceAddress2[];
...@@ -93,6 +94,7 @@ class BluetoothTestBase : public testing::Test { ...@@ -93,6 +94,7 @@ class BluetoothTestBase : public testing::Test {
static const char kTestUUIDImmediateAlert[]; static const char kTestUUIDImmediateAlert[];
static const char kTestUUIDLinkLoss[]; static const char kTestUUIDLinkLoss[];
static const char kTestUUIDHeartRate[]; static const char kTestUUIDHeartRate[];
static const char kTestUUIDU2f[];
// Characteristics // Characteristics
// The following three characteristics are for kTestUUIDGenericAccess. // The following three characteristics are for kTestUUIDGenericAccess.
static const char kTestUUIDDeviceName[]; static const char kTestUUIDDeviceName[];
...@@ -100,6 +102,8 @@ class BluetoothTestBase : public testing::Test { ...@@ -100,6 +102,8 @@ class BluetoothTestBase : public testing::Test {
static const char kTestUUIDReconnectionAddress[]; static const char kTestUUIDReconnectionAddress[];
// This characteristic is for kTestUUIDHeartRate. // This characteristic is for kTestUUIDHeartRate.
static const char kTestUUIDHeartRateMeasurement[]; static const char kTestUUIDHeartRateMeasurement[];
// This characteristic is for kTestUUIDU2f.
static const char kTestUUIDU2fControlPointLength[];
// Descriptors // Descriptors
static const char kTestUUIDCharacteristicUserDescription[]; static const char kTestUUIDCharacteristicUserDescription[];
static const char kTestUUIDClientCharacteristicConfiguration[]; static const char kTestUUIDClientCharacteristicConfiguration[];
...@@ -193,6 +197,13 @@ class BluetoothTestBase : public testing::Test { ...@@ -193,6 +197,13 @@ class BluetoothTestBase : public testing::Test {
// No Service Data // No Service Data
// No Tx Power // No Tx Power
// Supports BR/EDR and LE. // Supports BR/EDR and LE.
// 7: Name: kTestDeviceNameU2f
// Address: kTestDeviceAddress1
// RSSI: kTestRSSI1,
// Advertised UUIDs: {kTestUUIDU2fControlPointLength}
// Service Data: {kTestUUIDU2fControlPointLength: [0, 20]}
// No Tx Power
// Supports LE.
virtual BluetoothDevice* SimulateLowEnergyDevice(int device_ordinal); virtual BluetoothDevice* SimulateLowEnergyDevice(int device_ordinal);
// Simulates a connected low energy device. Used before starting a low energy // Simulates a connected low energy device. Used before starting a low energy
......
...@@ -115,7 +115,7 @@ void BluetoothTestBlueZ::InitWithFakeAdapter() { ...@@ -115,7 +115,7 @@ void BluetoothTestBlueZ::InitWithFakeAdapter() {
BluetoothDevice* BluetoothTestBlueZ::SimulateLowEnergyDevice( BluetoothDevice* BluetoothTestBlueZ::SimulateLowEnergyDevice(
int device_ordinal) { int device_ordinal) {
if (device_ordinal > 6 || device_ordinal < 1) if (device_ordinal > 7 || device_ordinal < 1)
return nullptr; return nullptr;
base::Optional<std::string> device_name = std::string(kTestDeviceName); base::Optional<std::string> device_name = std::string(kTestDeviceName);
...@@ -149,6 +149,11 @@ BluetoothDevice* BluetoothTestBlueZ::SimulateLowEnergyDevice( ...@@ -149,6 +149,11 @@ BluetoothDevice* BluetoothTestBlueZ::SimulateLowEnergyDevice(
device_address = kTestDeviceAddress2; device_address = kTestDeviceAddress2;
device_type = BLUETOOTH_TRANSPORT_DUAL; device_type = BLUETOOTH_TRANSPORT_DUAL;
break; break;
case 7:
device_name.emplace(kTestDeviceNameU2f);
service_uuids.push_back(kTestUUIDU2f);
service_data[kTestUUIDU2fControlPointLength] = {0x00, 0x14};
break;
} }
BluetoothDevice* device = adapter_->GetDevice(device_address); BluetoothDevice* device = adapter_->GetDevice(device_address);
......
...@@ -11,8 +11,11 @@ source_set("u2f") { ...@@ -11,8 +11,11 @@ source_set("u2f") {
"u2f_apdu_command.h", "u2f_apdu_command.h",
"u2f_apdu_response.cc", "u2f_apdu_response.cc",
"u2f_apdu_response.h", "u2f_apdu_response.h",
"u2f_ble_discovery.cc",
"u2f_ble_discovery.h",
"u2f_ble_frames.cc", "u2f_ble_frames.cc",
"u2f_ble_frames.h", "u2f_ble_frames.h",
"u2f_ble_uuids.h",
"u2f_command_type.h", "u2f_command_type.h",
"u2f_device.cc", "u2f_device.cc",
"u2f_device.h", "u2f_device.h",
......
// Copyright 2017 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/u2f/u2f_ble_discovery.h"
#include "base/bind.h"
#include "base/stl_util.h"
#include "base/strings/string_piece.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_common.h"
#include "device/bluetooth/bluetooth_discovery_filter.h"
#include "device/bluetooth/bluetooth_discovery_session.h"
#include "device/bluetooth/bluetooth_uuid.h"
#include "device/u2f/u2f_apdu_command.h"
#include "device/u2f/u2f_ble_uuids.h"
#include "device/u2f/u2f_device.h"
namespace device {
namespace {
// TODO(crbug/763303): Remove this once a U2fDevice for BLE is implemented.
class U2fFakeBleDevice : public U2fDevice {
public:
static std::string GetId(base::StringPiece address) {
std::string result = "ble:";
result.append(address.data(), address.size());
return result;
}
U2fFakeBleDevice(base::StringPiece address)
: id_(GetId(address)), weak_factory_(this) {}
~U2fFakeBleDevice() override = default;
void TryWink(const WinkCallback& callback) override {}
std::string GetId() const override { return id_; }
protected:
void DeviceTransact(std::unique_ptr<U2fApduCommand> command,
const DeviceCallback& callback) override {
callback.Run(false, nullptr);
}
base::WeakPtr<U2fDevice> GetWeakPtr() override {
return weak_factory_.GetWeakPtr();
}
private:
std::string id_;
base::WeakPtrFactory<U2fFakeBleDevice> weak_factory_;
};
} // namespace
U2fBleDiscovery::U2fBleDiscovery() : weak_factory_(this) {}
U2fBleDiscovery::~U2fBleDiscovery() {
adapter_->RemoveObserver(this);
}
void U2fBleDiscovery::Start() {
auto& factory = BluetoothAdapterFactory::Get();
factory.GetAdapter(base::Bind(&U2fBleDiscovery::GetAdapterCallback,
weak_factory_.GetWeakPtr()));
}
void U2fBleDiscovery::Stop() {
adapter_->RemoveObserver(this);
discovery_session_->Stop(
base::Bind(&U2fBleDiscovery::OnStopped, weak_factory_.GetWeakPtr(), true),
base::Bind(&U2fBleDiscovery::OnStopped, weak_factory_.GetWeakPtr(),
false));
}
// static
const BluetoothUUID& U2fBleDiscovery::U2fServiceUUID() {
static const BluetoothUUID service_uuid(U2F_SERVICE_UUID);
return service_uuid;
}
void U2fBleDiscovery::GetAdapterCallback(
scoped_refptr<BluetoothAdapter> adapter) {
adapter_ = std::move(adapter);
DCHECK(adapter_);
VLOG(2) << "Got adapter " << adapter_->GetAddress();
adapter_->AddObserver(this);
if (adapter_->IsPowered()) {
OnSetPowered();
} else {
adapter_->SetPowered(
true,
base::Bind(&U2fBleDiscovery::OnSetPowered, weak_factory_.GetWeakPtr()),
base::Bind(
[](base::WeakPtr<Delegate> delegate) {
LOG(ERROR) << "Failed to power on the adapter.";
if (delegate)
delegate->OnStarted(false);
},
delegate_));
}
}
void U2fBleDiscovery::OnSetPowered() {
VLOG(2) << "Adapter " << adapter_->GetAddress() << " is powered on.";
for (BluetoothDevice* device : adapter_->GetDevices()) {
if (base::ContainsKey(device->GetUUIDs(), U2fServiceUUID())) {
VLOG(2) << "U2F BLE device: " << device->GetAddress();
if (delegate_) {
delegate_->OnDeviceAdded(
std::make_unique<U2fFakeBleDevice>(device->GetAddress()));
}
}
}
auto filter = std::make_unique<BluetoothDiscoveryFilter>(
BluetoothTransport::BLUETOOTH_TRANSPORT_LE);
filter->AddUUID(U2fServiceUUID());
adapter_->StartDiscoverySessionWithFilter(
std::move(filter),
base::Bind(&U2fBleDiscovery::DiscoverySessionStarted,
weak_factory_.GetWeakPtr()),
base::Bind(
[](base::WeakPtr<Delegate> delegate) {
LOG(ERROR) << "Discovery session not started.";
if (delegate)
delegate->OnStarted(false);
},
delegate_));
}
void U2fBleDiscovery::DiscoverySessionStarted(
std::unique_ptr<BluetoothDiscoverySession> session) {
discovery_session_ = std::move(session);
VLOG(2) << "Discovery session started.";
if (delegate_)
delegate_->OnStarted(true);
}
void U2fBleDiscovery::DeviceAdded(BluetoothAdapter* adapter,
BluetoothDevice* device) {
if (base::ContainsKey(device->GetUUIDs(), U2fServiceUUID())) {
VLOG(2) << "Discovered U2F BLE device: " << device->GetAddress();
if (delegate_) {
delegate_->OnDeviceAdded(
std::make_unique<U2fFakeBleDevice>(device->GetAddress()));
}
}
}
void U2fBleDiscovery::DeviceRemoved(BluetoothAdapter* adapter,
BluetoothDevice* device) {
if (base::ContainsKey(device->GetUUIDs(), U2fServiceUUID())) {
VLOG(2) << "U2F BLE device removed: " << device->GetAddress();
if (delegate_)
delegate_->OnDeviceRemoved(U2fFakeBleDevice::GetId(device->GetAddress()));
}
}
void U2fBleDiscovery::OnStopped(bool success) {
discovery_session_.reset();
if (delegate_)
delegate_->OnStopped(success);
}
} // namespace device
// Copyright 2017 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_U2F_U2F_BLE_DISCOVERY_H_
#define DEVICE_U2F_U2F_BLE_DISCOVERY_H_
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/u2f/u2f_discovery.h"
#include <memory>
namespace device {
class BluetoothDevice;
class BluetoothDiscoverySession;
class BluetoothUUID;
class U2fBleDiscovery : public U2fDiscovery, BluetoothAdapter::Observer {
public:
U2fBleDiscovery();
~U2fBleDiscovery() override;
// U2fDiscovery:
void Start() override;
void Stop() override;
private:
static const BluetoothUUID& U2fServiceUUID();
void GetAdapterCallback(scoped_refptr<BluetoothAdapter> adapter);
void OnSetPowered();
void DiscoverySessionStarted(std::unique_ptr<BluetoothDiscoverySession>);
// BluetoothAdapter::Observer:
void DeviceAdded(BluetoothAdapter* adapter, BluetoothDevice* device) override;
void DeviceRemoved(BluetoothAdapter* adapter,
BluetoothDevice* device) override;
void OnStopped(bool success);
scoped_refptr<BluetoothAdapter> adapter_;
std::unique_ptr<BluetoothDiscoverySession> discovery_session_;
base::WeakPtrFactory<U2fBleDiscovery> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(U2fBleDiscovery);
};
} // namespace device
#endif // DEVICE_U2F_U2F_BLE_DISCOVERY_H_
// Copyright 2017 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/u2f/u2f_ble_discovery.h"
#include "base/bind.h"
#include "build/build_config.h"
#include "device/bluetooth/test/bluetooth_test.h"
#include "device/u2f/mock_u2f_discovery.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_ANDROID)
#include "device/bluetooth/test/bluetooth_test_android.h"
#elif defined(OS_MACOSX)
#include "device/bluetooth/test/bluetooth_test_mac.h"
#elif defined(OS_WIN)
#include "device/bluetooth/test/bluetooth_test_win.h"
#elif defined(OS_CHROMEOS) || defined(OS_LINUX)
#include "device/bluetooth/test/bluetooth_test_bluez.h"
#endif
namespace device {
ACTION_P(ReturnFromAsyncCall, closure) {
closure.Run();
}
std::string GetTestDeviceId(std::string address) {
return "ble:" + address;
}
TEST_F(BluetoothTest, U2fBleDiscoveryFindsKnownDevice) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
SimulateLowEnergyDevice(4); // This device should be ignored.
SimulateLowEnergyDevice(7);
U2fBleDiscovery discovery;
MockU2fDiscovery::MockDelegate delegate;
discovery.SetDelegate(delegate.GetWeakPtr());
{
base::RunLoop run_loop;
auto quit = run_loop.QuitClosure();
EXPECT_CALL(delegate, OnDeviceAddedStr(GetTestDeviceId(
BluetoothTestBase::kTestDeviceAddress1)));
EXPECT_CALL(delegate, OnStarted(true)).WillOnce(ReturnFromAsyncCall(quit));
discovery.Start();
run_loop.Run();
}
// TODO(crbug/763303): Delete device and check OnDeviceDeleted invocation.
{
base::RunLoop run_loop;
auto quit = run_loop.QuitClosure();
EXPECT_CALL(delegate, OnStopped(true)).WillOnce(ReturnFromAsyncCall(quit));
discovery.Stop();
run_loop.Run();
}
}
TEST_F(BluetoothTest, U2fBleDiscoveryFindsNewDevice) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
U2fBleDiscovery discovery;
MockU2fDiscovery::MockDelegate delegate;
discovery.SetDelegate(delegate.GetWeakPtr());
{
base::RunLoop run_loop;
auto quit = run_loop.QuitClosure();
EXPECT_CALL(delegate, OnStarted(true)).WillOnce(ReturnFromAsyncCall(quit));
discovery.Start();
run_loop.Run();
}
{
base::RunLoop run_loop;
auto quit = run_loop.QuitClosure();
EXPECT_CALL(delegate, OnDeviceAddedStr(GetTestDeviceId(
BluetoothTestBase::kTestDeviceAddress1)))
.WillOnce(ReturnFromAsyncCall(quit));
SimulateLowEnergyDevice(4); // This device should be ignored.
SimulateLowEnergyDevice(7);
run_loop.Run();
}
// TODO(crbug/763303): Delete device and check OnDeviceDeleted invocation.
{
base::RunLoop run_loop;
auto quit = run_loop.QuitClosure();
EXPECT_CALL(delegate, OnStopped(true)).WillOnce(ReturnFromAsyncCall(quit));
discovery.Stop();
run_loop.Run();
}
}
} // namespace device
// Copyright 2017 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_U2F_U2F_BLE_UUIDS_H_
#define DEVICE_U2F_U2F_BLE_UUIDS_H_
namespace device {
// U2F GATT Service's UUIDs as defined by the standard:
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-bt-protocol-v1.2-ps-20170411.html#h3_u2f-service
//
// For details on how the short U2F Service UUID (0xfffd) was converted to the
// long one below, see
// https://www.bluetooth.com/specifications/assigned-numbers/service-discovery
static constexpr const char U2F_SERVICE_UUID[] =
"0000fffd-0000-1000-8000-00805f9b34fb";
static constexpr const char U2F_CONTROL_POINT_UUID[] =
"f1d0fff1-deaa-ecee-b42f-c9ba7ed623bb";
static constexpr const char U2F_STATUS_UUID[] =
"f1d0fff2-deaa-ecee-b42f-c9ba7ed623bb";
static constexpr const char U2F_CONTROL_POINT_LENGTH_UUID[] =
"f1d0fff3-deaa-ecee-b42f-c9ba7ed623bb";
} // namespace device
#endif // DEVICE_U2F_U2F_BLE_UUIDS_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment