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";
const char kFidoServiceRevisionBitfieldUUID[] =
"f1d0fff4-deaa-ecee-b42f-c9ba7ed623bb";
#if defined(OS_MACOSX)
const char kCableAdvertisementUUID[] = "fde2";
#else
const char kCableAdvertisementUUID[] = "0000fde2-0000-1000-8000-00805f9b34fb";
#endif
const char kCableAdvertisementUUID16[] = "fde2";
const char kCableAdvertisementUUID128[] =
"0000fde2-0000-1000-8000-00805f9b34fb";
} // namespace device
......@@ -24,7 +24,8 @@ COMPONENT_EXPORT(DEVICE_FIDO)
extern const char kFidoServiceRevisionBitfieldUUID[];
// TODO(hongjunchoi): Add URL to the specification once CaBLE protocol is
// 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
......
......@@ -27,42 +27,20 @@ namespace device {
namespace {
#if defined(OS_MACOSX)
// 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
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);
const BluetoothUUID& CableAdvertisementUUID128() {
static const BluetoothUUID service_uuid(kCableAdvertisementUUID128);
return service_uuid;
}
#endif
const BluetoothUUID& CableAdvertisementUUID() {
static const BluetoothUUID service_uuid(kCableAdvertisementUUID);
const BluetoothUUID& CableAdvertisementUUID16() {
static const BluetoothUUID service_uuid(kCableAdvertisementUUID16);
return service_uuid;
}
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
......@@ -79,8 +57,8 @@ std::unique_ptr<BluetoothAdvertisement::Data> ConstructAdvertisementData(
#if defined(OS_MACOSX)
auto list = std::make_unique<BluetoothAdvertisement::UUIDList>();
list->emplace_back(kCableAdvertisementUUID);
list->emplace_back(ConvertBytesToUuid(client_eid));
list->emplace_back(kCableAdvertisementUUID16);
list->emplace_back(fido_parsing_utils::ConvertBytesToUuid(client_eid));
advertisement_data->set_service_uuids(std::move(list));
#elif defined(OS_WIN)
......@@ -109,7 +87,8 @@ std::unique_ptr<BluetoothAdvertisement::Data> ConstructAdvertisementData(
service_data_value[1] = version_number;
std::copy(client_eid.begin(), client_eid.end(),
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));
#endif
......@@ -313,18 +292,35 @@ void FidoCableDiscovery::ValidateAuthenticatorHandshakeMessage(
if (!handshake_response)
return;
if (!handshake_handler->ValidateAuthenticatorHandshakeMessage(
*handshake_response))
return;
if (handshake_handler->ValidateAuthenticatorHandshakeMessage(
*handshake_response)) {
DVLOG(2) << "Authenticator handshake validated";
AddDevice(std::move(cable_device));
} else {
DVLOG(2) << "Authenticator handshake invalid";
}
}
const FidoCableDiscovery::CableDiscoveryData*
FidoCableDiscovery::GetFoundCableDiscoveryData(
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 =
device->GetServiceDataForUUID(CableAdvertisementUUID());
device->GetServiceDataForUUID(CableAdvertisementUUID128());
if (!service_data) {
return nullptr;
}
DCHECK(service_data);
// Received service data from authenticator must have a flag that signals that
......@@ -349,4 +345,27 @@ FidoCableDiscovery::GetFoundCableDiscoveryData(
: 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
......@@ -112,6 +112,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery
const CableDiscoveryData* GetFoundCableDiscoveryData(
const BluetoothDevice* device) const;
const CableDiscoveryData* GetFoundCableDiscoveryDataFromServiceData(
const BluetoothDevice* device) const;
const CableDiscoveryData* GetFoundCableDiscoveryDataFromServiceUUIDs(
const BluetoothDevice* device) const;
std::vector<CableDiscoveryData> discovery_data_;
size_t advertisement_success_counter_ = 0;
......
......@@ -108,7 +108,7 @@ MATCHER_P2(IsAdvertisementContent,
#elif defined(OS_LINUX) || defined(OS_CHROMEOS)
const auto service_data = arg->service_data();
const auto service_data_with_uuid =
service_data->find(kCableAdvertisementUUID);
service_data->find(kCableAdvertisementUUID128);
if (service_data_with_uuid == service_data->end())
return false;
......@@ -158,7 +158,8 @@ class CableMockAdapter : public MockBluetoothAdapter {
std::copy(authenticator_eid.begin(), authenticator_eid.end(),
service_data.begin() + 2);
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(
1 /* rssi */, base::nullopt /* flags */, BluetoothDevice::UUIDList(),
......@@ -172,6 +173,23 @@ class CableMockAdapter : public MockBluetoothAdapter {
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(
bool simulate_success,
base::span<const uint8_t> expected_client_eid,
......@@ -215,11 +233,17 @@ class CableMockAdapter : public MockBluetoothAdapter {
}
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(_, _, _))
.WillOnce(::testing::WithArg<1>([this, eid](const auto& callback) {
.WillOnce(::testing::WithArg<1>(
[this, eid, is_apple_device](const auto& callback) {
callback.Run(nullptr);
if (is_apple_device) {
AddNewTestAppleBluetoothDevice(eid);
} else {
AddNewTestBluetoothDevice(eid);
}
}));
}
......@@ -298,6 +322,25 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsNewDevice) {
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
// discovers a device with an incorrect authenticator EID. Observer::AddDevice()
// must not be called.
......
......@@ -5,6 +5,8 @@
#include "device/fido/fido_parsing_utils.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/stringprintf.h"
namespace device {
namespace fido_parsing_utils {
......@@ -88,5 +90,27 @@ base::StringPiece ConvertToStringPiece(base::span<const uint8_t> data) {
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 device
......@@ -114,6 +114,12 @@ std::array<uint8_t, crypto::kSHA256Length> CreateSHA256Hash(
COMPONENT_EXPORT(DEVICE_FIDO)
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 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