diff options
author | sacomoto <sacomoto@chromium.org> | 2015-07-08 10:11:28 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-07-08 17:11:59 +0000 |
commit | b8bc433ac7d9254ac9b58b33d3073831c6deeb87 (patch) | |
tree | 760770ddf7829f10f8eced9bc515e3bf0e97b117 /components/proximity_auth | |
parent | 41634b16099cde386dc1090423f566bd4624070c (diff) | |
download | chromium_src-b8bc433ac7d9254ac9b58b33d3073831c6deeb87.zip chromium_src-b8bc433ac7d9254ac9b58b33d3073831c6deeb87.tar.gz chromium_src-b8bc433ac7d9254ac9b58b33d3073831c6deeb87.tar.bz2 |
Persistent whitelist for Bluetooth low energy devices.
This CL adds a persistent whitelist for BLE devices to be used with proximity_auth::ProximityAuthBleSytem and proximity_auth::BluetoothLowEnergyConnectionFinder.
Currently, in BLE connection finder a device has to satisfy two conditions for the connection to be established: (i) paired and (ii) has a given service visible (either advertising or cached).
The problem with this strategy is that when the service cache is dropped by CrOS and the device has the service but is not advertising BLE connection finder is unable to connect.
This CL solves this problem, if a given device is whitelisted then BLE connection finder will connect even if (ii) is not satisfied.
A device is whitelisted if a connection is successfully established and its public key is register in CryptAuth. When a device is removed from CryptAuth it also removed from the whitelist.
TEST=unit tests
BUG=505464
Review URL: https://codereview.chromium.org/1208073003
Cr-Commit-Position: refs/heads/master@{#337845}
Diffstat (limited to 'components/proximity_auth')
12 files changed, 566 insertions, 27 deletions
diff --git a/components/proximity_auth/ble/BUILD.gn b/components/proximity_auth/ble/BUILD.gn index 8ce5e49..d8737eb 100644 --- a/components/proximity_auth/ble/BUILD.gn +++ b/components/proximity_auth/ble/BUILD.gn @@ -12,8 +12,12 @@ source_set("ble") { "bluetooth_low_energy_connection.h", "bluetooth_low_energy_connection_finder.cc", "bluetooth_low_energy_connection_finder.h", + "bluetooth_low_energy_device_whitelist.cc", + "bluetooth_low_energy_device_whitelist.h", "fake_wire_message.cc", "fake_wire_message.h", + "pref_names.cc", + "pref_names.h", "proximity_auth_ble_system.cc", "proximity_auth_ble_system.h", "remote_attribute.h", @@ -37,6 +41,7 @@ source_set("unit_tests") { "bluetooth_low_energy_characteristics_finder_unittest.cc", "bluetooth_low_energy_connection_finder_unittest.cc", "bluetooth_low_energy_connection_unittest.cc", + "bluetooth_low_energy_device_whitelist_unittest.cc", "proximity_auth_ble_system_unittest.cc", ] diff --git a/components/proximity_auth/ble/bluetooth_low_energy_connection.cc b/components/proximity_auth/ble/bluetooth_low_energy_connection.cc index a6d732a..e4959e8 100644 --- a/components/proximity_auth/ble/bluetooth_low_energy_connection.cc +++ b/components/proximity_auth/ble/bluetooth_low_energy_connection.cc @@ -96,6 +96,7 @@ void BluetoothLowEnergyConnection::Disconnect() { if (sub_status_ != SubStatus::DISCONNECTED) { ClearWriteRequestsQueue(); StopNotifySession(); + characteristic_finder_.reset(); if (gatt_connection_) { PA_LOG(INFO) << "Disconnect from device " << gatt_connection_->GetDeviceAddress(); diff --git a/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.cc b/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.cc index 37276e8..be966d0 100644 --- a/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.cc +++ b/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.cc @@ -13,6 +13,7 @@ #include "base/memory/scoped_ptr.h" #include "base/thread_task_runner_handle.h" #include "components/proximity_auth/ble/bluetooth_low_energy_connection.h" +#include "components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.h" #include "components/proximity_auth/connection.h" #include "components/proximity_auth/logging/logging.h" #include "device/bluetooth/bluetooth_adapter_factory.h" @@ -27,7 +28,7 @@ using device::BluetoothDiscoveryFilter; namespace proximity_auth { namespace { -const int kMinDiscoveryRSSI = -100; +const int kMinDiscoveryRSSI = -90; const int kDelayAfterGattConnectionMilliseconds = 1000; } // namespace @@ -35,11 +36,13 @@ BluetoothLowEnergyConnectionFinder::BluetoothLowEnergyConnectionFinder( const std::string& remote_service_uuid, const std::string& to_peripheral_char_uuid, const std::string& from_peripheral_char_uuid, + const BluetoothLowEnergyDeviceWhitelist* device_whitelist, int max_number_of_tries) : remote_service_uuid_(device::BluetoothUUID(remote_service_uuid)), to_peripheral_char_uuid_(device::BluetoothUUID(to_peripheral_char_uuid)), from_peripheral_char_uuid_( device::BluetoothUUID(from_peripheral_char_uuid)), + device_whitelist_(device_whitelist), connected_(false), max_number_of_tries_(max_number_of_tries), delay_after_gatt_connection_(base::TimeDelta::FromMilliseconds( @@ -133,8 +136,14 @@ void BluetoothLowEnergyConnectionFinder::HandleDeviceUpdated( PA_LOG(INFO) << "Pending connection to device " << device->GetAddress(); return; } - if (HasService(device) && device->IsPaired()) { - PA_LOG(INFO) << "Connecting to paired device " << device->GetAddress(); + if (device->IsPaired() && + (HasService(device) || + device_whitelist_->HasDeviceWithAddress(device->GetAddress()))) { + PA_LOG(INFO) << "Connecting to paired device " << device->GetAddress() + << " with service (" << HasService(device) + << ") or is whitelisted (" + << device_whitelist_->HasDeviceWithAddress( + device->GetAddress()) << ")"; pending_connections_.insert(device); CreateGattConnection(device); } @@ -330,6 +339,8 @@ void BluetoothLowEnergyConnectionFinder::OnConnectionStatusChanged( Connection::Status old_status, Connection::Status new_status) { DCHECK_EQ(connection, connection_.get()); + PA_LOG(INFO) << "OnConnectionStatusChanged: " << old_status << " -> " + << new_status; if (!connection_callback_.is_null() && connection_->IsConnected()) { adapter_->RemoveObserver(this); diff --git a/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.h b/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.h index 7d0310e..0b22598 100644 --- a/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.h +++ b/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.h @@ -24,6 +24,8 @@ namespace proximity_auth { +class BluetoothLowEnergyDeviceWhitelist; + // This ConnectionFinder implementation is specialized in finding a Bluetooth // Low Energy remote device. class BluetoothLowEnergyConnectionFinder @@ -35,6 +37,7 @@ class BluetoothLowEnergyConnectionFinder const std::string& remote_service_uuid, const std::string& to_peripheral_char_uuid, const std::string& from_peripheral_char_uuid, + const BluetoothLowEnergyDeviceWhitelist* device_whitelist, int max_number_of_tries); ~BluetoothLowEnergyConnectionFinder() override; @@ -131,6 +134,10 @@ class BluetoothLowEnergyConnectionFinder // Characteristic used to receive data from the remote device. device::BluetoothUUID from_peripheral_char_uuid_; + // Devices in |device_whitelist_| don't need to have |remote_service_uuid_| + // cached or advertised. Not owned, must outlive this instance. + const BluetoothLowEnergyDeviceWhitelist* device_whitelist_; + // The Bluetooth adapter over which the Bluetooth connection will be made. scoped_refptr<device::BluetoothAdapter> adapter_; diff --git a/components/proximity_auth/ble/bluetooth_low_energy_connection_finder_unittest.cc b/components/proximity_auth/ble/bluetooth_low_energy_connection_finder_unittest.cc index 86be60e..71448b0 100644 --- a/components/proximity_auth/ble/bluetooth_low_energy_connection_finder_unittest.cc +++ b/components/proximity_auth/ble/bluetooth_low_energy_connection_finder_unittest.cc @@ -4,12 +4,15 @@ #include "components/proximity_auth/ble/bluetooth_low_energy_connection_finder.h" +#include <string> + #include "base/bind.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/time/time.h" +#include "components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.h" #include "components/proximity_auth/connection.h" #include "components/proximity_auth/remote_device.h" #include "components/proximity_auth/wire_message.h" @@ -61,13 +64,25 @@ class MockConnection : public Connection { DISALLOW_COPY_AND_ASSIGN(MockConnection); }; +class MockBluetoothLowEnergyDeviceWhitelist + : public BluetoothLowEnergyDeviceWhitelist { + public: + MockBluetoothLowEnergyDeviceWhitelist() + : BluetoothLowEnergyDeviceWhitelist(nullptr) {} + ~MockBluetoothLowEnergyDeviceWhitelist() override {} + + MOCK_CONST_METHOD1(HasDeviceWithAddress, bool(const std::string&)); +}; + class MockBluetoothLowEnergyConnectionFinder : public BluetoothLowEnergyConnectionFinder { public: - MockBluetoothLowEnergyConnectionFinder() + MockBluetoothLowEnergyConnectionFinder( + const BluetoothLowEnergyDeviceWhitelist* device_whitelist) : BluetoothLowEnergyConnectionFinder(kServiceUUID, kToPeripheralCharUUID, kFromPeripheralCharUUID, + device_whitelist, kMaxNumberOfAttempts) { SetDelayForTesting(base::TimeDelta()); } @@ -128,6 +143,7 @@ class ProximityAuthBluetoothLowEnergyConnectionFinderTest kBluetoothAddress, false, false)), + device_whitelist_(new MockBluetoothLowEnergyDeviceWhitelist()), last_discovery_session_alias_(nullptr) { device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_); @@ -136,6 +152,9 @@ class ProximityAuthBluetoothLowEnergyConnectionFinderTest ON_CALL(*adapter_, IsPresent()).WillByDefault(Return(true)); ON_CALL(*adapter_, IsPowered()).WillByDefault(Return(true)); + + ON_CALL(*device_whitelist_, HasDeviceWithAddress(_)) + .WillByDefault(Return(false)); } void OnConnectionFound(scoped_ptr<Connection> connection) { @@ -194,6 +213,7 @@ class ProximityAuthBluetoothLowEnergyConnectionFinderTest ConnectionFinder::ConnectionCallback connection_callback_; scoped_ptr<device::MockBluetoothDevice> device_; scoped_ptr<Connection> last_found_connection_; + scoped_ptr<MockBluetoothLowEnergyDeviceWhitelist> device_whitelist_; device::MockBluetoothDiscoverySession* last_discovery_session_alias_; private: @@ -206,14 +226,14 @@ TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, // should not crash. BluetoothLowEnergyConnectionFinder connection_finder( kServiceUUID, kToPeripheralCharUUID, kFromPeripheralCharUUID, - kMaxNumberOfAttempts); + device_whitelist_.get(), kMaxNumberOfAttempts); } TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, Find_StartsDiscoverySession) { BluetoothLowEnergyConnectionFinder connection_finder( kServiceUUID, kToPeripheralCharUUID, kFromPeripheralCharUUID, - kMaxNumberOfAttempts); + device_whitelist_.get(), kMaxNumberOfAttempts); EXPECT_CALL(*adapter_, StartDiscoverySessionWithFilterRaw(_, _, _)); EXPECT_CALL(*adapter_, AddObserver(_)); @@ -224,7 +244,7 @@ TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, Find_StopsDiscoverySessionBeforeDestroying) { BluetoothLowEnergyConnectionFinder connection_finder( kServiceUUID, kToPeripheralCharUUID, kFromPeripheralCharUUID, - kMaxNumberOfAttempts); + device_whitelist_.get(), kMaxNumberOfAttempts); device::BluetoothAdapter::DiscoverySessionCallback discovery_callback; scoped_ptr<device::MockBluetoothDiscoverySession> discovery_session( @@ -246,10 +266,32 @@ TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, } TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, + Find_CreatesGattConnectionWhenWhitelistedDeviceIsAdded) { + BluetoothLowEnergyConnectionFinder connection_finder( + kServiceUUID, kToPeripheralCharUUID, kFromPeripheralCharUUID, + device_whitelist_.get(), kMaxNumberOfAttempts); + device::BluetoothDevice::GattConnectionCallback gatt_connection_callback; + FindAndExpectStartDiscovery(connection_finder); + ExpectStopDiscoveryAndRemoveObserver(); + + std::vector<device::BluetoothUUID> uuids; + ON_CALL(*device_, GetUUIDs()).WillByDefault(Return(uuids)); + ON_CALL(*device_, IsPaired()).WillByDefault(Return(true)); + ON_CALL(*device_whitelist_, HasDeviceWithAddress(_)) + .WillByDefault(Return(true)); + + EXPECT_CALL(*device_, CreateGattConnection(_, _)) + .WillOnce(SaveArg<0>(&gatt_connection_callback)); + EXPECT_TRUE(gatt_connection_callback.is_null()); + connection_finder.DeviceAdded(adapter_.get(), device_.get()); + EXPECT_FALSE(gatt_connection_callback.is_null()); +} + +TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, Find_CreatesGattConnectionWhenRightDeviceIsAdded) { BluetoothLowEnergyConnectionFinder connection_finder( kServiceUUID, kToPeripheralCharUUID, kFromPeripheralCharUUID, - kMaxNumberOfAttempts); + device_whitelist_.get(), kMaxNumberOfAttempts); device::BluetoothDevice::GattConnectionCallback gatt_connection_callback; FindAndExpectStartDiscovery(connection_finder); ExpectStopDiscoveryAndRemoveObserver(); @@ -263,7 +305,7 @@ TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, Find_DoesntCreateGattConnectionWhenWrongDeviceIsAdded) { BluetoothLowEnergyConnectionFinder connection_finder( kServiceUUID, kToPeripheralCharUUID, kFromPeripheralCharUUID, - kMaxNumberOfAttempts); + device_whitelist_.get(), kMaxNumberOfAttempts); FindAndExpectStartDiscovery(connection_finder); ExpectStopDiscoveryAndRemoveObserver(); @@ -275,7 +317,7 @@ TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, Find_CreatesGattConnectionWhenRightDeviceIsChanged) { BluetoothLowEnergyConnectionFinder connection_finder( kServiceUUID, kToPeripheralCharUUID, kFromPeripheralCharUUID, - kMaxNumberOfAttempts); + device_whitelist_.get(), kMaxNumberOfAttempts); device::BluetoothDevice::GattConnectionCallback gatt_connection_callback; FindAndExpectStartDiscovery(connection_finder); ExpectStopDiscoveryAndRemoveObserver(); @@ -289,7 +331,7 @@ TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, Find_DoesntCreateGattConnectionWhenWrongDeviceIsChanged) { BluetoothLowEnergyConnectionFinder connection_finder( kServiceUUID, kToPeripheralCharUUID, kFromPeripheralCharUUID, - kMaxNumberOfAttempts); + device_whitelist_.get(), kMaxNumberOfAttempts); FindAndExpectStartDiscovery(connection_finder); ExpectStopDiscoveryAndRemoveObserver(); @@ -299,7 +341,8 @@ TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, Find_CreatesTwoGattConnections) { - StrictMock<MockBluetoothLowEnergyConnectionFinder> connection_finder; + StrictMock<MockBluetoothLowEnergyConnectionFinder> connection_finder( + device_whitelist_.get()); FindAndExpectStartDiscovery(connection_finder); ExpectStopDiscoveryAndRemoveObserver(); connection_finder.ExpectCreateConnection(); @@ -341,7 +384,8 @@ TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, Find_ConnectionSucceeds) { - StrictMock<MockBluetoothLowEnergyConnectionFinder> connection_finder; + StrictMock<MockBluetoothLowEnergyConnectionFinder> connection_finder( + device_whitelist_.get()); // Starting discovery. FindAndExpectStartDiscovery(connection_finder); @@ -367,7 +411,8 @@ TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, Find_ConnectionFails_RestartDiscoveryAndConnectionSucceeds) { - StrictMock<MockBluetoothLowEnergyConnectionFinder> connection_finder; + StrictMock<MockBluetoothLowEnergyConnectionFinder> connection_finder( + device_whitelist_.get()); // Starting discovery. FindAndExpectStartDiscovery(connection_finder); @@ -435,7 +480,8 @@ TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest, Find_AdapterRemoved_RestartDiscoveryAndConnectionSucceeds) { - StrictMock<MockBluetoothLowEnergyConnectionFinder> connection_finder; + StrictMock<MockBluetoothLowEnergyConnectionFinder> connection_finder( + device_whitelist_.get()); // Starting discovery. FindAndExpectStartDiscovery(connection_finder); diff --git a/components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.cc b/components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.cc new file mode 100644 index 0000000..8bf905e --- /dev/null +++ b/components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.cc @@ -0,0 +1,115 @@ +// Copyright 2015 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 "components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.h" + +#include <vector> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/prefs/pref_registry_simple.h" +#include "base/prefs/pref_service.h" +#include "base/prefs/scoped_user_pref_update.h" +#include "base/values.h" +#include "components/proximity_auth/ble/pref_names.h" +#include "components/proximity_auth/logging/logging.h" + +namespace proximity_auth { + +BluetoothLowEnergyDeviceWhitelist::BluetoothLowEnergyDeviceWhitelist( + PrefService* pref_service) + : pref_service_(pref_service) { +} + +BluetoothLowEnergyDeviceWhitelist::~BluetoothLowEnergyDeviceWhitelist() { +} + +// static +void BluetoothLowEnergyDeviceWhitelist::RegisterPrefs( + PrefRegistrySimple* registry) { + registry->RegisterDictionaryPref(prefs::kBluetoothLowEnergyDeviceWhitelist); +} + +bool BluetoothLowEnergyDeviceWhitelist::HasDeviceWithAddress( + const std::string& bluetooth_address) const { + return pref_service_->GetDictionary(prefs::kBluetoothLowEnergyDeviceWhitelist) + ->HasKey(bluetooth_address); +} + +bool BluetoothLowEnergyDeviceWhitelist::HasDeviceWithPublicKey( + const std::string& public_key) const { + return !GetDeviceAddress(public_key).empty(); +} + +std::string BluetoothLowEnergyDeviceWhitelist::GetDevicePublicKey( + const std::string& bluetooth_address) const { + std::string public_key; + GetWhitelistPrefs()->GetStringWithoutPathExpansion(bluetooth_address, + &public_key); + return public_key; +} + +std::string BluetoothLowEnergyDeviceWhitelist::GetDeviceAddress( + const std::string& public_key) const { + const base::DictionaryValue* device_whitelist = GetWhitelistPrefs(); + for (base::DictionaryValue::Iterator it(*device_whitelist); !it.IsAtEnd(); + it.Advance()) { + std::string value_string; + DCHECK(it.value().IsType(base::Value::TYPE_STRING)); + if (it.value().GetAsString(&value_string) && value_string == public_key) + return it.key(); + } + return std::string(); +} + +std::vector<std::string> BluetoothLowEnergyDeviceWhitelist::GetPublicKeys() + const { + std::vector<std::string> public_keys; + const base::DictionaryValue* device_whitelist = GetWhitelistPrefs(); + for (base::DictionaryValue::Iterator it(*device_whitelist); !it.IsAtEnd(); + it.Advance()) { + std::string value_string; + DCHECK(it.value().IsType(base::Value::TYPE_STRING)); + it.value().GetAsString(&value_string); + public_keys.push_back(value_string); + } + return public_keys; +} + +void BluetoothLowEnergyDeviceWhitelist::AddOrUpdateDevice( + const std::string& bluetooth_address, + const std::string& public_key) { + if (HasDeviceWithPublicKey(public_key) && + GetDeviceAddress(public_key) != bluetooth_address) { + PA_LOG(WARNING) << "Two devices with different bluetooth address, but the " + "same public key were added: " << public_key; + RemoveDeviceWithPublicKey(public_key); + } + + DictionaryPrefUpdate device_whitelist_update( + pref_service_, prefs::kBluetoothLowEnergyDeviceWhitelist); + device_whitelist_update->SetStringWithoutPathExpansion(bluetooth_address, + public_key); +} + +bool BluetoothLowEnergyDeviceWhitelist::RemoveDeviceWithAddress( + const std::string& bluetooth_address) { + DictionaryPrefUpdate device_whitelist_update( + pref_service_, prefs::kBluetoothLowEnergyDeviceWhitelist); + return device_whitelist_update->RemoveWithoutPathExpansion(bluetooth_address, + nullptr); +} + +bool BluetoothLowEnergyDeviceWhitelist::RemoveDeviceWithPublicKey( + const std::string& public_key) { + return RemoveDeviceWithAddress(GetDeviceAddress(public_key)); +} + +const base::DictionaryValue* +BluetoothLowEnergyDeviceWhitelist::GetWhitelistPrefs() const { + return pref_service_->GetDictionary( + prefs::kBluetoothLowEnergyDeviceWhitelist); +} + +} // namespace proximity_auth diff --git a/components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.h b/components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.h new file mode 100644 index 0000000..46e6aa8 --- /dev/null +++ b/components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.h @@ -0,0 +1,72 @@ +// Copyright 2015 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 COMPONENTS_PROXIMITY_AUTH_BLE_BLUETOOTH_LOW_ENERGY_WHITELIST_H +#define COMPONENTS_PROXIMITY_AUTH_BLE_BLUETOOTH_LOW_ENERGY_WHITELIST_H + +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" + +class PrefRegistrySimple; +class PrefService; + +namespace base { +class DictionaryValue; +} // namespace base + +namespace proximity_auth { + +// This class stores a persistent set of whitelisted bluetooth devices +// represented by pairs (bluetooth_address, public_key). The class enforces a +// bijective mapping: no two device share the same bluetooth_address or +// the same public_key. +class BluetoothLowEnergyDeviceWhitelist { + public: + // Creates a device whitelist backed by preferences registered in + // |pref_service| (persistent across browser restarts). |pref_service| should + // have been registered using RegisterPrefs(). Not owned, must out live this + // instance. + explicit BluetoothLowEnergyDeviceWhitelist(PrefService* pref_service); + virtual ~BluetoothLowEnergyDeviceWhitelist(); + + // Registers the prefs used by this class to the given |pref_service|. + static void RegisterPrefs(PrefRegistrySimple* registry); + + // Virtual for testing + virtual bool HasDeviceWithAddress(const std::string& bluetooth_address) const; + bool HasDeviceWithPublicKey(const std::string& public_key) const; + + std::string GetDevicePublicKey(const std::string& bluetooth_address) const; + std::string GetDeviceAddress(const std::string& public_key) const; + std::vector<std::string> GetPublicKeys() const; + + // Adds a device with |bluetooth_address| and |public_key|. The + // bluetooth_address <-> public_key mapping is unique, i.e. if there is + // another device with same bluetooth address or public key that device will + // be replaced. + void AddOrUpdateDevice(const std::string& bluetooth_address, + const std::string& public_key); + + // Removes the unique device with |bluetooth_address| (|public_key|). Returns + // false if no device was found. + bool RemoveDeviceWithAddress(const std::string& bluetooth_address); + bool RemoveDeviceWithPublicKey(const std::string& public_key); + + private: + const base::DictionaryValue* GetWhitelistPrefs() const; + + // Contains perferences that outlive the lifetime of this object and across + // process restarts. + // Not owned and must outlive this instance. + PrefService* pref_service_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothLowEnergyDeviceWhitelist); +}; + +} // namespace proximity_auth + +#endif // COMPONENTS_PROXIMITY_AUTH_BLE_BLUETOOTH_WHITELIST_H diff --git a/components/proximity_auth/ble/bluetooth_low_energy_device_whitelist_unittest.cc b/components/proximity_auth/ble/bluetooth_low_energy_device_whitelist_unittest.cc new file mode 100644 index 0000000..4db930d --- /dev/null +++ b/components/proximity_auth/ble/bluetooth_low_energy_device_whitelist_unittest.cc @@ -0,0 +1,163 @@ +// Copyright 2015 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 "components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.h" + +#include <algorithm> +#include <string> +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "base/prefs/testing_pref_service.h" +#include "components/proximity_auth/ble/pref_names.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace proximity_auth { +namespace { + +const char kBluetoothAddress1[] = "11:22:33:44:55:66"; +const char kPublicKey1[] = "public key 1"; + +const char kBluetoothAddress2[] = "22:33:44:55:66:77"; +const char kPublicKey2[] = "public key 2"; + +} // namespace + +class ProximityAuthBluetoothLowEnergyDeviceWhitelistTest + : public testing::Test { + protected: + ProximityAuthBluetoothLowEnergyDeviceWhitelistTest() {} + + void SetUp() override { + BluetoothLowEnergyDeviceWhitelist::RegisterPrefs(pref_service_.registry()); + } + + void CheckWhitelistedDevice( + const std::string& bluetooth_address, + const std::string& public_key, + BluetoothLowEnergyDeviceWhitelist& device_whitelist) { + EXPECT_TRUE(device_whitelist.HasDeviceWithAddress(bluetooth_address)); + EXPECT_EQ(device_whitelist.GetDevicePublicKey(bluetooth_address), + public_key); + + EXPECT_TRUE(device_whitelist.HasDeviceWithPublicKey(public_key)); + EXPECT_EQ(device_whitelist.GetDeviceAddress(public_key), bluetooth_address); + } + + TestingPrefServiceSimple pref_service_; + + private: + DISALLOW_COPY_AND_ASSIGN(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest); +}; + +TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest, RegisterPrefs) { + TestingPrefServiceSimple pref_service; + BluetoothLowEnergyDeviceWhitelist::RegisterPrefs(pref_service.registry()); + EXPECT_TRUE( + pref_service.FindPreference(prefs::kBluetoothLowEnergyDeviceWhitelist)); +} + +TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest, AddDevice) { + BluetoothLowEnergyDeviceWhitelist device_whitelist(&pref_service_); + + device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey1); + CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey1, device_whitelist); +} + +TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest, + AddDevice_TwoDevicesWithSameAddress) { + BluetoothLowEnergyDeviceWhitelist device_whitelist(&pref_service_); + + device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey1); + CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey1, device_whitelist); + + device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey2); + CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey2, device_whitelist); + + EXPECT_FALSE(device_whitelist.HasDeviceWithPublicKey(kPublicKey1)); +} + +TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest, + AddDevice_TwoDevicesWithSamePublicKey) { + BluetoothLowEnergyDeviceWhitelist device_whitelist(&pref_service_); + + device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey1); + CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey1, device_whitelist); + + device_whitelist.AddOrUpdateDevice(kBluetoothAddress2, kPublicKey1); + CheckWhitelistedDevice(kBluetoothAddress2, kPublicKey1, device_whitelist); + + EXPECT_FALSE(device_whitelist.HasDeviceWithAddress(kBluetoothAddress1)); +} + +TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest, + RemoveDeviceWithAddress) { + BluetoothLowEnergyDeviceWhitelist device_whitelist(&pref_service_); + + device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey1); + CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey1, device_whitelist); + + ASSERT_TRUE(device_whitelist.RemoveDeviceWithAddress(kBluetoothAddress1)); + EXPECT_FALSE(device_whitelist.HasDeviceWithAddress(kBluetoothAddress1)); + EXPECT_FALSE(device_whitelist.HasDeviceWithPublicKey(kPublicKey1)); +} + +TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest, + RemoveDeviceWithAddress_DeviceNotPresent) { + BluetoothLowEnergyDeviceWhitelist device_whitelist(&pref_service_); + + device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey1); + CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey1, device_whitelist); + + ASSERT_FALSE(device_whitelist.RemoveDeviceWithAddress(kBluetoothAddress2)); + EXPECT_TRUE(device_whitelist.HasDeviceWithAddress(kBluetoothAddress1)); + EXPECT_TRUE(device_whitelist.HasDeviceWithPublicKey(kPublicKey1)); +} + +TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest, + RemoveDeviceWithPublicKey) { + BluetoothLowEnergyDeviceWhitelist device_whitelist(&pref_service_); + + device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey1); + CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey1, device_whitelist); + + ASSERT_TRUE(device_whitelist.RemoveDeviceWithPublicKey(kPublicKey1)); + EXPECT_FALSE(device_whitelist.HasDeviceWithAddress(kBluetoothAddress1)); + EXPECT_FALSE(device_whitelist.HasDeviceWithPublicKey(kPublicKey1)); +} + +TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest, + RemoveDeviceWithPublicKey_DeviceNotPresent) { + BluetoothLowEnergyDeviceWhitelist device_whitelist(&pref_service_); + + device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey1); + CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey1, device_whitelist); + + ASSERT_FALSE(device_whitelist.RemoveDeviceWithPublicKey(kPublicKey2)); + EXPECT_TRUE(device_whitelist.HasDeviceWithAddress(kBluetoothAddress1)); + EXPECT_TRUE(device_whitelist.HasDeviceWithPublicKey(kPublicKey1)); +} + +TEST_F(ProximityAuthBluetoothLowEnergyDeviceWhitelistTest, GetPublicKeys) { + BluetoothLowEnergyDeviceWhitelist device_whitelist(&pref_service_); + + device_whitelist.AddOrUpdateDevice(kBluetoothAddress1, kPublicKey1); + CheckWhitelistedDevice(kBluetoothAddress1, kPublicKey1, device_whitelist); + + device_whitelist.AddOrUpdateDevice(kBluetoothAddress2, kPublicKey2); + CheckWhitelistedDevice(kBluetoothAddress2, kPublicKey2, device_whitelist); + + std::vector<std::string> public_keys = device_whitelist.GetPublicKeys(); + + // Note: it's not guarantee that the order in |public_key| is the same as + // insertion, so directly comparing vectors would not work. + EXPECT_TRUE(public_keys.size() == 2); + EXPECT_TRUE(std::find(public_keys.begin(), public_keys.end(), kPublicKey1) != + public_keys.end()); + EXPECT_TRUE(std::find(public_keys.begin(), public_keys.end(), kPublicKey2) != + public_keys.end()); +} + +} // namespace proximity_auth diff --git a/components/proximity_auth/ble/pref_names.cc b/components/proximity_auth/ble/pref_names.cc new file mode 100644 index 0000000..ad60e86 --- /dev/null +++ b/components/proximity_auth/ble/pref_names.cc @@ -0,0 +1,16 @@ +// Copyright 2015 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 "components/proximity_auth/ble/pref_names.h" + +namespace proximity_auth { +namespace prefs { + +// The dictionary containing whitelisted BLE devices used by +// proximity_auth::BluetoothLowEnergyDeviceWhitelist. +const char kBluetoothLowEnergyDeviceWhitelist[] = + "proximity_auth_bluetooth_low_energy_device_whitelist"; + +} // namespace prefs +} // namespace proximity_auth diff --git a/components/proximity_auth/ble/pref_names.h b/components/proximity_auth/ble/pref_names.h new file mode 100644 index 0000000..167a343 --- /dev/null +++ b/components/proximity_auth/ble/pref_names.h @@ -0,0 +1,16 @@ +// Copyright 2015 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 COMPONENTS_PROXIMITY_AUTH_BLE_PREF_NAMES_H +#define COMPONENTS_PROXIMITY_AUTH_BLE_PREF_NAMES_H + +namespace proximity_auth { +namespace prefs { + +extern const char kBluetoothLowEnergyDeviceWhitelist[]; + +} // namespace prefs +} // proximity_auth + +#endif // COMPONENTS_PROXIMITY_AUTH_BLE_PREF_NAMES_H diff --git a/components/proximity_auth/ble/proximity_auth_ble_system.cc b/components/proximity_auth/ble/proximity_auth_ble_system.cc index 1a399b4..7b7fad4 100644 --- a/components/proximity_auth/ble/proximity_auth_ble_system.cc +++ b/components/proximity_auth/ble/proximity_auth_ble_system.cc @@ -4,12 +4,16 @@ #include "components/proximity_auth/ble/proximity_auth_ble_system.h" +#include <string> +#include <vector> + #include "base/bind.h" #include "base/location.h" #include "base/logging.h" #include "base/thread_task_runner_handle.h" #include "components/proximity_auth/ble/bluetooth_low_energy_connection.h" #include "components/proximity_auth/ble/bluetooth_low_energy_connection_finder.h" +#include "components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.h" #include "components/proximity_auth/ble/fake_wire_message.h" #include "components/proximity_auth/connection.h" #include "components/proximity_auth/cryptauth/base64url.h" @@ -43,6 +47,9 @@ const char kScreenUnlocked[] = "Screen Unlocked"; // String send to poll the remote device screen state. const char kPollScreenState[] = "PollScreenState"; +// String prefix received with the public key. +const char kPublicKeyMessagePrefix[] = "PublicKey:"; + // BluetoothLowEnergyConnection parameter, number of attempts to send a write // request before failing. const int kMaxNumberOfTries = 2; @@ -78,11 +85,14 @@ void ProximityAuthBleSystem::ScreenlockBridgeAdapter::Unlock( ProximityAuthBleSystem::ProximityAuthBleSystem( ScreenlockBridge* screenlock_bridge, ProximityAuthClient* proximity_auth_client, - scoped_ptr<CryptAuthClientFactory> cryptauth_client_factory) + scoped_ptr<CryptAuthClientFactory> cryptauth_client_factory, + PrefService* pref_service) : screenlock_bridge_(new ProximityAuthBleSystem::ScreenlockBridgeAdapter( screenlock_bridge)), proximity_auth_client_(proximity_auth_client), cryptauth_client_factory_(cryptauth_client_factory.Pass()), + device_whitelist_(new BluetoothLowEnergyDeviceWhitelist(pref_service)), + device_authenticated_(false), is_polling_screen_state_(false), weak_ptr_factory_(this) { PA_LOG(INFO) << "Starting Proximity Auth over Bluetooth Low Energy."; @@ -107,6 +117,10 @@ ProximityAuthBleSystem::~ProximityAuthBleSystem() { connection_->RemoveObserver(this); } +void ProximityAuthBleSystem::RegisterPrefs(PrefRegistrySimple* registry) { + BluetoothLowEnergyDeviceWhitelist::RegisterPrefs(registry); +} + void ProximityAuthBleSystem::OnGetMyDevices( const cryptauth::GetMyDevicesResponse& response) { PA_LOG(INFO) << "Found " << response.devices_size() @@ -125,6 +139,8 @@ void ProximityAuthBleSystem::OnGetMyDevices( } } PA_LOG(INFO) << "Found " << unlock_keys_.size() << " unlock keys."; + + RemoveStaleWhitelistedDevices(); } void ProximityAuthBleSystem::OnGetMyDevicesError(const std::string& error) { @@ -135,6 +151,7 @@ void ProximityAuthBleSystem::OnGetMyDevicesError(const std::string& error) { // calling |GetUnlockKeys| from the constructor cause |GetMyDevices| to always // return an error. void ProximityAuthBleSystem::GetUnlockKeys() { + PA_LOG(INFO) << "Fetching unlock keys."; if (cryptauth_client_factory_) { cryptauth_client_ = cryptauth_client_factory_->CreateInstance(); cryptauth::GetMyDevicesRequest request; @@ -146,6 +163,22 @@ void ProximityAuthBleSystem::GetUnlockKeys() { } } +void ProximityAuthBleSystem::RemoveStaleWhitelistedDevices() { + PA_LOG(INFO) << "Removing stale whitelist devices."; + std::vector<std::string> public_keys = device_whitelist_->GetPublicKeys(); + PA_LOG(INFO) << "There were " << public_keys.size() + << " whitelisted devices."; + + for (const auto& public_key : public_keys) { + if (unlock_keys_.find(public_key) == unlock_keys_.end()) { + PA_LOG(INFO) << "Removing device: " << public_key; + device_whitelist_->RemoveDeviceWithPublicKey(public_key); + } + } + public_keys = device_whitelist_->GetPublicKeys(); + PA_LOG(INFO) << "There are " << public_keys.size() << " whitelisted devices."; +} + void ProximityAuthBleSystem::OnScreenDidLock( ScreenlockBridge::LockHandler::ScreenType screen_type) { PA_LOG(INFO) << "OnScreenDidLock: " << screen_type; @@ -169,7 +202,7 @@ void ProximityAuthBleSystem::OnScreenDidLock( ConnectionFinder* ProximityAuthBleSystem::CreateConnectionFinder() { return new BluetoothLowEnergyConnectionFinder( kSmartLockServiceUUID, kToPeripheralCharUUID, kFromPeripheralCharUUID, - kMaxNumberOfTries); + device_whitelist_.get(), kMaxNumberOfTries); } void ProximityAuthBleSystem::OnScreenDidUnlock( @@ -189,6 +222,7 @@ void ProximityAuthBleSystem::OnScreenDidUnlock( // created. connection_->RemoveObserver(this); connection_->Disconnect(); + device_authenticated_ = false; } connection_.reset(); @@ -204,6 +238,31 @@ void ProximityAuthBleSystem::OnMessageReceived(const Connection& connection, // TODO(sacomoto): change this when WireMessage is fully implemented. PA_LOG(INFO) << "Message received: " << message.payload(); + // The first message should contain a public key registered in |unlock_keys_| + // to authenticate the device. + if (!device_authenticated_) { + std::string out_public_key; + if (HasUnlockKey(message.payload(), &out_public_key)) { + PA_LOG(INFO) << "Device authenticated. Adding " + << connection_->remote_device().bluetooth_address << ", " + << out_public_key << " to whitelist."; + device_whitelist_->AddOrUpdateDevice( + connection_->remote_device().bluetooth_address, out_public_key); + device_authenticated_ = true; + + // Only start polling the screen state if the device is authenticated. + if (!is_polling_screen_state_) { + is_polling_screen_state_ = true; + StartPollingScreenState(); + } + + } else { + PA_LOG(INFO) << "Key not found. Authentication failed."; + connection_->Disconnect(); + } + return; + } + // Unlock the screen when the remote device sends an unlock signal. // // Note that this magically unlocks Chrome (no user interaction is needed). @@ -218,22 +277,18 @@ void ProximityAuthBleSystem::OnMessageReceived(const Connection& connection, void ProximityAuthBleSystem::OnConnectionFound( scoped_ptr<Connection> connection) { PA_LOG(INFO) << "Connection found."; + DCHECK(connection); connection_ = connection.Pass(); - if (connection_) { - connection_->AddObserver(this); - - if (!is_polling_screen_state_) { - is_polling_screen_state_ = true; - StartPollingScreenState(); - } - } + connection_->AddObserver(this); } void ProximityAuthBleSystem::OnConnectionStatusChanged( Connection* connection, Connection::Status old_status, Connection::Status new_status) { + PA_LOG(INFO) << "OnConnectionStatusChanged: " << old_status << " -> " + << new_status; if (old_status == Connection::CONNECTED && new_status == Connection::DISCONNECTED) { StopPollingScreenState(); @@ -249,6 +304,7 @@ void ProximityAuthBleSystem::OnConnectionStatusChanged( } void ProximityAuthBleSystem::StartPollingScreenState() { + PA_LOG(INFO) << "Start polling."; if (is_polling_screen_state_) { if (!connection_ || !connection_->IsConnected()) { PA_LOG(INFO) << "Polling stopped."; @@ -260,7 +316,7 @@ void ProximityAuthBleSystem::StartPollingScreenState() { connection_->SendMessage( make_scoped_ptr(new FakeWireMessage(kPollScreenState))); - // Schedules the next message in |kPollingIntervalSeconds| ms. + // Schedules the next message in |kPollingIntervalSeconds| s. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&ProximityAuthBleSystem::StartPollingScreenState, weak_ptr_factory_.GetWeakPtr()), @@ -272,4 +328,15 @@ void ProximityAuthBleSystem::StopPollingScreenState() { is_polling_screen_state_ = false; } +bool ProximityAuthBleSystem::HasUnlockKey(const std::string& message, + std::string* out_public_key) { + std::string message_prefix(kPublicKeyMessagePrefix); + if (message.substr(0, message_prefix.size()) != message_prefix) + return false; + std::string public_key = message.substr(message_prefix.size()); + if (out_public_key) + (*out_public_key) = public_key; + return unlock_keys_.find(public_key) != unlock_keys_.end(); +} + } // namespace proximity_auth diff --git a/components/proximity_auth/ble/proximity_auth_ble_system.h b/components/proximity_auth/ble/proximity_auth_ble_system.h index 50f8704..34d4b2c 100644 --- a/components/proximity_auth/ble/proximity_auth_ble_system.h +++ b/components/proximity_auth/ble/proximity_auth_ble_system.h @@ -15,6 +15,9 @@ #include "components/proximity_auth/cryptauth/cryptauth_client.h" #include "components/proximity_auth/screenlock_bridge.h" +class PrefRegistrySimple; +class PrefService; + namespace device { class BluetoothGattConnection; } @@ -23,6 +26,7 @@ namespace proximity_auth { class BluetoothLowEnergyConnection; class BluetoothLowEnergyConnectionFinder; +class BluetoothLowEnergyDeviceWhitelist; class Connection; class ConnectionFinder; class ProximityAuthClient; @@ -37,9 +41,13 @@ class ProximityAuthBleSystem : public ScreenlockBridge::Observer, ProximityAuthBleSystem( ScreenlockBridge* screenlock_bridge, ProximityAuthClient* proximity_auth_client, - scoped_ptr<CryptAuthClientFactory> cryptauth_client_factory); + scoped_ptr<CryptAuthClientFactory> cryptauth_client_factory, + PrefService* pref_service); ~ProximityAuthBleSystem() override; + // Registers the prefs used by this class + static void RegisterPrefs(PrefRegistrySimple* registry); + // ScreenlockBridge::Observer: void OnScreenDidLock( ScreenlockBridge::LockHandler::ScreenType screen_type) override; @@ -83,6 +91,10 @@ class ProximityAuthBleSystem : public ScreenlockBridge::Observer, // Fetches the the public keys of devices that can be used as unlock keys. void GetUnlockKeys(); + // Checks if the devices in |device_whitelist_| have their public keys + // registered in CryptAuth (|unlock_keys_|), removes the ones that do not. + void RemoveStaleWhitelistedDevices(); + // Callbacks for cryptauth::CryptAuthClient::GetMyDevices. void OnGetMyDevices(const cryptauth::GetMyDevicesResponse& response); void OnGetMyDevicesError(const std::string& error); @@ -97,6 +109,10 @@ class ProximityAuthBleSystem : public ScreenlockBridge::Observer, // Stop polling for screen state of the remote device, if currently active. void StopPollingScreenState(); + // Checks if |message| contains a valid public key (registered in + // |unlock_keys_|). If so, returns the public key in |out_public_key|. + bool HasUnlockKey(const std::string& message, std::string* out_public_key); + scoped_ptr<ScreenlockBridgeAdapter> screenlock_bridge_; // Not owned. Must outlive this object. @@ -115,8 +131,12 @@ class ProximityAuthBleSystem : public ScreenlockBridge::Observer, scoped_ptr<Connection> connection_; + scoped_ptr<BluetoothLowEnergyDeviceWhitelist> device_whitelist_; + const base::TimeDelta polling_interval_; + bool device_authenticated_; + bool is_polling_screen_state_; base::WeakPtrFactory<ProximityAuthBleSystem> weak_ptr_factory_; |