Commit 78356dac authored by Yuankai Yu's avatar Yuankai Yu Committed by Commit Bot

Add parsing for caBLE advertisements that come from iOS phones.

Apple doesn't allow advertising with service data, as a result we are
advertising the EID in the form of service UUID in the advertisement.
Chrome on Mac advertises in this form, however Chrome on all platform
needs to parse this form of advertisement as well since iOS phone can
only advertise in this form.

Bug: 875074
Change-Id: I654d2cfbbf5692cf9b17876b2d05bfad07f7bba2
Reviewed-on: https://chromium-review.googlesource.com/1171253
Commit-Queue: Yuankai Yu <yyk@google.com>
Reviewed-by: default avatarJun Choi <hongjunchoi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#584145}
parent d86e2c71
...@@ -17,10 +17,8 @@ const char kFidoServiceRevisionUUID[] = "00002a28-0000-1000-8000-00805f9b34fb"; ...@@ -17,10 +17,8 @@ const char kFidoServiceRevisionUUID[] = "00002a28-0000-1000-8000-00805f9b34fb";
const char kFidoServiceRevisionBitfieldUUID[] = const char kFidoServiceRevisionBitfieldUUID[] =
"f1d0fff4-deaa-ecee-b42f-c9ba7ed623bb"; "f1d0fff4-deaa-ecee-b42f-c9ba7ed623bb";
#if defined(OS_MACOSX) const char kCableAdvertisementUUID16[] = "fde2";
const char kCableAdvertisementUUID[] = "fde2"; const char kCableAdvertisementUUID128[] =
#else "0000fde2-0000-1000-8000-00805f9b34fb";
const char kCableAdvertisementUUID[] = "0000fde2-0000-1000-8000-00805f9b34fb";
#endif
} // namespace device } // namespace device
...@@ -24,7 +24,8 @@ COMPONENT_EXPORT(DEVICE_FIDO) ...@@ -24,7 +24,8 @@ COMPONENT_EXPORT(DEVICE_FIDO)
extern const char kFidoServiceRevisionBitfieldUUID[]; extern const char kFidoServiceRevisionBitfieldUUID[];
// TODO(hongjunchoi): Add URL to the specification once CaBLE protocol is // TODO(hongjunchoi): Add URL to the specification once CaBLE protocol is
// standardized. // standardized.
COMPONENT_EXPORT(DEVICE_FIDO) extern const char kCableAdvertisementUUID[]; COMPONENT_EXPORT(DEVICE_FIDO) extern const char kCableAdvertisementUUID16[];
COMPONENT_EXPORT(DEVICE_FIDO) extern const char kCableAdvertisementUUID128[];
} // namespace device } // namespace device
......
...@@ -27,42 +27,20 @@ namespace device { ...@@ -27,42 +27,20 @@ namespace device {
namespace { namespace {
#if defined(OS_MACOSX) const BluetoothUUID& CableAdvertisementUUID128() {
static const BluetoothUUID service_uuid(kCableAdvertisementUUID128);
// Convert byte array into GUID formatted string as defined by RFC 4122. return service_uuid;
// As we are converting 128 bit UUID, |bytes| must be have length of 16.
// https://tools.ietf.org/html/rfc4122
std::string ConvertBytesToUuid(base::span<const uint8_t, 16> bytes) {
uint64_t most_significant_bytes = 0;
for (size_t i = 0; i < sizeof(uint64_t); i++) {
most_significant_bytes |= base::strict_cast<uint64_t>(bytes[i])
<< 8 * (7 - i);
}
uint64_t least_significant_bytes = 0;
for (size_t i = 0; i < sizeof(uint64_t); i++) {
least_significant_bytes |= base::strict_cast<uint64_t>(bytes[i + 8])
<< 8 * (7 - i);
}
return base::StringPrintf(
"%08x-%04x-%04x-%04x-%012llx",
static_cast<unsigned int>(most_significant_bytes >> 32),
static_cast<unsigned int>((most_significant_bytes >> 16) & 0x0000ffff),
static_cast<unsigned int>(most_significant_bytes & 0x0000ffff),
static_cast<unsigned int>(least_significant_bytes >> 48),
least_significant_bytes & 0x0000ffff'ffffffffULL);
} }
#endif const BluetoothUUID& CableAdvertisementUUID16() {
static const BluetoothUUID service_uuid(kCableAdvertisementUUID16);
const BluetoothUUID& CableAdvertisementUUID() {
static const BluetoothUUID service_uuid(kCableAdvertisementUUID);
return service_uuid; return service_uuid;
} }
bool IsCableDevice(const BluetoothDevice* device) { bool IsCableDevice(const BluetoothDevice* device) {
return base::ContainsKey(device->GetServiceData(), CableAdvertisementUUID()); return base::ContainsKey(device->GetServiceData(),
CableAdvertisementUUID128()) ||
base::ContainsKey(device->GetUUIDs(), CableAdvertisementUUID16());
} }
// Construct advertisement data with different formats depending on client's // Construct advertisement data with different formats depending on client's
...@@ -79,8 +57,8 @@ std::unique_ptr<BluetoothAdvertisement::Data> ConstructAdvertisementData( ...@@ -79,8 +57,8 @@ std::unique_ptr<BluetoothAdvertisement::Data> ConstructAdvertisementData(
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
auto list = std::make_unique<BluetoothAdvertisement::UUIDList>(); auto list = std::make_unique<BluetoothAdvertisement::UUIDList>();
list->emplace_back(kCableAdvertisementUUID); list->emplace_back(kCableAdvertisementUUID16);
list->emplace_back(ConvertBytesToUuid(client_eid)); list->emplace_back(fido_parsing_utils::ConvertBytesToUuid(client_eid));
advertisement_data->set_service_uuids(std::move(list)); advertisement_data->set_service_uuids(std::move(list));
#elif defined(OS_WIN) #elif defined(OS_WIN)
...@@ -109,7 +87,8 @@ std::unique_ptr<BluetoothAdvertisement::Data> ConstructAdvertisementData( ...@@ -109,7 +87,8 @@ std::unique_ptr<BluetoothAdvertisement::Data> ConstructAdvertisementData(
service_data_value[1] = version_number; service_data_value[1] = version_number;
std::copy(client_eid.begin(), client_eid.end(), std::copy(client_eid.begin(), client_eid.end(),
service_data_value.begin() + 2); service_data_value.begin() + 2);
service_data->emplace(kCableAdvertisementUUID, std::move(service_data_value)); service_data->emplace(kCableAdvertisementUUID128,
std::move(service_data_value));
advertisement_data->set_service_data(std::move(service_data)); advertisement_data->set_service_data(std::move(service_data));
#endif #endif
...@@ -313,18 +292,35 @@ void FidoCableDiscovery::ValidateAuthenticatorHandshakeMessage( ...@@ -313,18 +292,35 @@ void FidoCableDiscovery::ValidateAuthenticatorHandshakeMessage(
if (!handshake_response) if (!handshake_response)
return; return;
if (!handshake_handler->ValidateAuthenticatorHandshakeMessage( if (handshake_handler->ValidateAuthenticatorHandshakeMessage(
*handshake_response)) *handshake_response)) {
return; DVLOG(2) << "Authenticator handshake validated";
AddDevice(std::move(cable_device));
AddDevice(std::move(cable_device)); } else {
DVLOG(2) << "Authenticator handshake invalid";
}
} }
const FidoCableDiscovery::CableDiscoveryData* const FidoCableDiscovery::CableDiscoveryData*
FidoCableDiscovery::GetFoundCableDiscoveryData( FidoCableDiscovery::GetFoundCableDiscoveryData(
const BluetoothDevice* device) const { const BluetoothDevice* device) const {
const auto* discovery_data =
GetFoundCableDiscoveryDataFromServiceData(device);
if (discovery_data != nullptr) {
return discovery_data;
}
return GetFoundCableDiscoveryDataFromServiceUUIDs(device);
}
const FidoCableDiscovery::CableDiscoveryData*
FidoCableDiscovery::GetFoundCableDiscoveryDataFromServiceData(
const BluetoothDevice* device) const {
const auto* service_data = const auto* service_data =
device->GetServiceDataForUUID(CableAdvertisementUUID()); device->GetServiceDataForUUID(CableAdvertisementUUID128());
if (!service_data) {
return nullptr;
}
DCHECK(service_data); DCHECK(service_data);
// Received service data from authenticator must have a flag that signals that // Received service data from authenticator must have a flag that signals that
...@@ -349,4 +345,27 @@ FidoCableDiscovery::GetFoundCableDiscoveryData( ...@@ -349,4 +345,27 @@ FidoCableDiscovery::GetFoundCableDiscoveryData(
: nullptr; : nullptr;
} }
const FidoCableDiscovery::CableDiscoveryData*
FidoCableDiscovery::GetFoundCableDiscoveryDataFromServiceUUIDs(
const BluetoothDevice* device) const {
const auto service_uuids = device->GetUUIDs();
for (const auto& uuid : service_uuids) {
if (uuid == CableAdvertisementUUID128() ||
uuid == CableAdvertisementUUID16()) {
continue;
}
auto discovery_data_iterator = std::find_if(
discovery_data_.begin(), discovery_data_.end(),
[&uuid](const auto& data) {
std::string received_eid_string =
fido_parsing_utils::ConvertBytesToUuid(data.authenticator_eid);
return uuid == BluetoothUUID(received_eid_string);
});
if (discovery_data_iterator != discovery_data_.end()) {
return &(*discovery_data_iterator);
}
}
return nullptr;
}
} // namespace device } // namespace device
...@@ -112,6 +112,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery ...@@ -112,6 +112,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery
const CableDiscoveryData* GetFoundCableDiscoveryData( const CableDiscoveryData* GetFoundCableDiscoveryData(
const BluetoothDevice* device) const; const BluetoothDevice* device) const;
const CableDiscoveryData* GetFoundCableDiscoveryDataFromServiceData(
const BluetoothDevice* device) const;
const CableDiscoveryData* GetFoundCableDiscoveryDataFromServiceUUIDs(
const BluetoothDevice* device) const;
std::vector<CableDiscoveryData> discovery_data_; std::vector<CableDiscoveryData> discovery_data_;
size_t advertisement_success_counter_ = 0; size_t advertisement_success_counter_ = 0;
......
...@@ -108,7 +108,7 @@ MATCHER_P2(IsAdvertisementContent, ...@@ -108,7 +108,7 @@ MATCHER_P2(IsAdvertisementContent,
#elif defined(OS_LINUX) || defined(OS_CHROMEOS) #elif defined(OS_LINUX) || defined(OS_CHROMEOS)
const auto service_data = arg->service_data(); const auto service_data = arg->service_data();
const auto service_data_with_uuid = const auto service_data_with_uuid =
service_data->find(kCableAdvertisementUUID); service_data->find(kCableAdvertisementUUID128);
if (service_data_with_uuid == service_data->end()) if (service_data_with_uuid == service_data->end())
return false; return false;
...@@ -158,7 +158,8 @@ class CableMockAdapter : public MockBluetoothAdapter { ...@@ -158,7 +158,8 @@ class CableMockAdapter : public MockBluetoothAdapter {
std::copy(authenticator_eid.begin(), authenticator_eid.end(), std::copy(authenticator_eid.begin(), authenticator_eid.end(),
service_data.begin() + 2); service_data.begin() + 2);
BluetoothDevice::ServiceDataMap service_data_map; BluetoothDevice::ServiceDataMap service_data_map;
service_data_map.emplace(kCableAdvertisementUUID, std::move(service_data)); service_data_map.emplace(kCableAdvertisementUUID128,
std::move(service_data));
mock_device->UpdateAdvertisementData( mock_device->UpdateAdvertisementData(
1 /* rssi */, base::nullopt /* flags */, BluetoothDevice::UUIDList(), 1 /* rssi */, base::nullopt /* flags */, BluetoothDevice::UUIDList(),
...@@ -172,6 +173,23 @@ class CableMockAdapter : public MockBluetoothAdapter { ...@@ -172,6 +173,23 @@ class CableMockAdapter : public MockBluetoothAdapter {
observer.DeviceAdded(this, mock_device_ptr); observer.DeviceAdded(this, mock_device_ptr);
} }
void AddNewTestAppleBluetoothDevice(
base::span<const uint8_t, FidoCableDiscovery::kEphemeralIdSize>
authenticator_eid) {
auto mock_device = CreateTestBluetoothDevice();
// Apple doesn't allow advertising service data, so we advertise a 16 bit
// UUID plus the EID converted into 128 bit UUID.
mock_device->AddUUID(BluetoothUUID("fde2"));
mock_device->AddUUID(BluetoothUUID(
fido_parsing_utils::ConvertBytesToUuid(authenticator_eid)));
auto* mock_device_ptr = mock_device.get();
AddMockDevice(std::move(mock_device));
for (auto& observer : GetObservers())
observer.DeviceAdded(this, mock_device_ptr);
}
void ExpectRegisterAdvertisementWithResponse( void ExpectRegisterAdvertisementWithResponse(
bool simulate_success, bool simulate_success,
base::span<const uint8_t> expected_client_eid, base::span<const uint8_t> expected_client_eid,
...@@ -215,12 +233,18 @@ class CableMockAdapter : public MockBluetoothAdapter { ...@@ -215,12 +233,18 @@ class CableMockAdapter : public MockBluetoothAdapter {
} }
void ExpectDiscoveryWithScanCallback( void ExpectDiscoveryWithScanCallback(
base::span<const uint8_t, FidoCableDiscovery::kEphemeralIdSize> eid) { base::span<const uint8_t, FidoCableDiscovery::kEphemeralIdSize> eid,
bool is_apple_device = false) {
EXPECT_CALL(*this, StartDiscoverySessionWithFilterRaw(_, _, _)) EXPECT_CALL(*this, StartDiscoverySessionWithFilterRaw(_, _, _))
.WillOnce(::testing::WithArg<1>([this, eid](const auto& callback) { .WillOnce(::testing::WithArg<1>(
callback.Run(nullptr); [this, eid, is_apple_device](const auto& callback) {
AddNewTestBluetoothDevice(eid); callback.Run(nullptr);
})); if (is_apple_device) {
AddNewTestAppleBluetoothDevice(eid);
} else {
AddNewTestBluetoothDevice(eid);
}
}));
} }
protected: protected:
...@@ -298,6 +322,25 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsNewDevice) { ...@@ -298,6 +322,25 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsNewDevice) {
scoped_task_environment_.RunUntilIdle(); scoped_task_environment_.RunUntilIdle();
} }
// Tests successful discovery flow for Apple Cable device.
TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsNewAppleDevice) {
auto cable_discovery = CreateDiscovery();
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer, DeviceAdded(_, _));
cable_discovery->set_observer(&mock_observer);
auto mock_adapter =
base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>();
mock_adapter->ExpectSuccessCallbackToSetPowered();
mock_adapter->ExpectDiscoveryWithScanCallback(kAuthenticatorEid, true);
mock_adapter->ExpectRegisterAdvertisementWithResponse(
true /* simulate_success */, kClientEid, kUuidFormattedClientEid);
BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
cable_discovery->Start();
scoped_task_environment_.RunUntilIdle();
}
// Tests a scenario where upon broadcasting advertisement and scanning, client // Tests a scenario where upon broadcasting advertisement and scanning, client
// discovers a device with an incorrect authenticator EID. Observer::AddDevice() // discovers a device with an incorrect authenticator EID. Observer::AddDevice()
// must not be called. // must not be called.
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#include "device/fido/fido_parsing_utils.h" #include "device/fido/fido_parsing_utils.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/stringprintf.h"
namespace device { namespace device {
namespace fido_parsing_utils { namespace fido_parsing_utils {
...@@ -88,5 +90,27 @@ base::StringPiece ConvertToStringPiece(base::span<const uint8_t> data) { ...@@ -88,5 +90,27 @@ base::StringPiece ConvertToStringPiece(base::span<const uint8_t> data) {
return {reinterpret_cast<const char*>(data.data()), data.size()}; return {reinterpret_cast<const char*>(data.data()), data.size()};
} }
std::string ConvertBytesToUuid(base::span<const uint8_t, 16> bytes) {
uint64_t most_significant_bytes = 0;
for (size_t i = 0; i < sizeof(uint64_t); i++) {
most_significant_bytes |= base::strict_cast<uint64_t>(bytes[i])
<< 8 * (7 - i);
}
uint64_t least_significant_bytes = 0;
for (size_t i = 0; i < sizeof(uint64_t); i++) {
least_significant_bytes |= base::strict_cast<uint64_t>(bytes[i + 8])
<< 8 * (7 - i);
}
return base::StringPrintf(
"%08x-%04x-%04x-%04x-%012llx",
static_cast<unsigned int>(most_significant_bytes >> 32),
static_cast<unsigned int>((most_significant_bytes >> 16) & 0x0000ffff),
static_cast<unsigned int>(most_significant_bytes & 0x0000ffff),
static_cast<unsigned int>(least_significant_bytes >> 48),
least_significant_bytes & 0x0000ffff'ffffffffULL);
}
} // namespace fido_parsing_utils } // namespace fido_parsing_utils
} // namespace device } // namespace device
...@@ -114,6 +114,12 @@ std::array<uint8_t, crypto::kSHA256Length> CreateSHA256Hash( ...@@ -114,6 +114,12 @@ std::array<uint8_t, crypto::kSHA256Length> CreateSHA256Hash(
COMPONENT_EXPORT(DEVICE_FIDO) COMPONENT_EXPORT(DEVICE_FIDO)
base::StringPiece ConvertToStringPiece(base::span<const uint8_t> data); base::StringPiece ConvertToStringPiece(base::span<const uint8_t> data);
// Convert byte array into GUID formatted string as defined by RFC 4122.
// As we are converting 128 bit UUID, |bytes| must be have length of 16.
// https://tools.ietf.org/html/rfc4122
COMPONENT_EXPORT(DEVICE_FIDO)
std::string ConvertBytesToUuid(base::span<const uint8_t, 16> bytes);
} // namespace fido_parsing_utils } // namespace fido_parsing_utils
} // namespace device } // namespace device
......
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