blob: c52d651d7620d55cf574bf8683ccc0759f092ef3 [file] [log] [blame]
// 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 "chromeos/components/tether/ble_service_data_helper_impl.h"
#include <algorithm>
#include <iterator>
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "chromeos/chromeos_features.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/components/proximity_auth/logging/logging.h"
#include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
#include "chromeos/services/secure_channel/ble_constants.h"
#include "components/cryptauth/background_eid_generator.h"
#include "components/cryptauth/ble/ble_advertisement_generator.h"
#include "components/cryptauth/foreground_eid_generator.h"
#include "components/cryptauth/local_device_data_provider.h"
namespace chromeos {
namespace tether {
namespace {
// Valid advertisement service data must be at least 2 bytes.
// As of March 2018, valid background advertisement service data is exactly 2
// bytes, which identify the advertising device to the scanning device.
// Valid foreground advertisement service data must include at least 4 bytes:
// 2 bytes associated with the scanning device (used as a scan filter) and 2
// bytes which identify the advertising device to the scanning device.
const size_t kMinNumBytesInServiceData = 2;
const size_t kMaxNumBytesInBackgroundServiceData = 3;
const size_t kMinNumBytesInForegroundServiceData = 4;
} // namespace
// static
BleServiceDataHelperImpl::Factory*
BleServiceDataHelperImpl::Factory::test_factory_ = nullptr;
// static
BleServiceDataHelperImpl::Factory* BleServiceDataHelperImpl::Factory::Get() {
if (test_factory_)
return test_factory_;
static base::NoDestructor<Factory> factory;
return factory.get();
}
// static
void BleServiceDataHelperImpl::Factory::SetFactoryForTesting(
Factory* test_factory) {
test_factory_ = test_factory;
}
BleServiceDataHelperImpl::Factory::~Factory() = default;
std::unique_ptr<secure_channel::BleServiceDataHelper>
BleServiceDataHelperImpl::Factory::BuildInstance(
TetherHostFetcher* tether_host_fetcher,
cryptauth::LocalDeviceDataProvider* local_device_data_provider,
device_sync::DeviceSyncClient* device_sync_client) {
return base::WrapUnique(new BleServiceDataHelperImpl(
tether_host_fetcher, local_device_data_provider, device_sync_client));
}
BleServiceDataHelperImpl::BleServiceDataHelperImpl(
TetherHostFetcher* tether_host_fetcher,
cryptauth::LocalDeviceDataProvider* local_device_data_provider,
device_sync::DeviceSyncClient* device_sync_client)
: tether_host_fetcher_(tether_host_fetcher),
local_device_data_provider_(local_device_data_provider),
device_sync_client_(device_sync_client),
background_eid_generator_(
std::make_unique<cryptauth::BackgroundEidGenerator>()),
foreground_eid_generator_(
std::make_unique<cryptauth::ForegroundEidGenerator>()),
weak_ptr_factory_(this) {
tether_host_fetcher_->AddObserver(this);
OnTetherHostsUpdated();
}
BleServiceDataHelperImpl::~BleServiceDataHelperImpl() {
tether_host_fetcher_->RemoveObserver(this);
}
base::Optional<secure_channel::BleServiceDataHelper::DeviceWithBackgroundBool>
BleServiceDataHelperImpl::IdentifyRemoteDevice(
const std::string& service_data,
const std::vector<std::string>& remote_device_ids) {
base::Optional<std::string> local_device_public_key =
GetLocalDevicePublicKey();
if (!local_device_public_key)
return base::nullopt;
// BleServiceDataHelper::IdentifyRemoteDevice() verifies that the devices its
// subclasses return are actually in |device_id_pair_set|. However, clients of
// BleServiceDataHelperImpl don't have easy access to local device metadata;
// therefore, pass along that required data here.
secure_channel::DeviceIdPairSet device_id_pair_set;
for (const auto& remote_device_id : remote_device_ids) {
device_id_pair_set.insert(secure_channel::DeviceIdPair(
remote_device_id, cryptauth::RemoteDeviceRef::GenerateDeviceId(
*local_device_public_key)));
}
return secure_channel::BleServiceDataHelper::IdentifyRemoteDevice(
service_data, device_id_pair_set);
}
std::unique_ptr<cryptauth::DataWithTimestamp>
BleServiceDataHelperImpl::GenerateForegroundAdvertisement(
const secure_channel::DeviceIdPair& device_id_pair) {
base::Optional<std::string> local_device_public_key =
GetLocalDevicePublicKey();
if (!local_device_public_key) {
PA_LOG(ERROR) << "Local device public key is invalid.";
return nullptr;
}
base::Optional<cryptauth::RemoteDeviceRef> remote_device;
const auto remote_device_it = std::find_if(
tether_hosts_from_last_fetch_.begin(),
tether_hosts_from_last_fetch_.end(),
[&device_id_pair](auto remote_device) {
return device_id_pair.remote_device_id() == remote_device.GetDeviceId();
});
if (remote_device_it == tether_hosts_from_last_fetch_.end()) {
PA_LOG(WARNING) << "Requested remote device ID is not a valid host: "
<< cryptauth::RemoteDeviceRef::TruncateDeviceIdForLogs(
device_id_pair.remote_device_id());
return nullptr;
}
return cryptauth::BleAdvertisementGenerator::GenerateBleAdvertisement(
*remote_device_it, *local_device_public_key);
}
base::Optional<secure_channel::BleServiceDataHelper::DeviceWithBackgroundBool>
BleServiceDataHelperImpl::PerformIdentifyRemoteDevice(
const std::string& service_data,
const secure_channel::DeviceIdPairSet& device_id_pair_set) {
std::vector<std::string> remote_device_ids;
for (const auto& device_id_pair : device_id_pair_set) {
// It's fine to ignore device_id_pair.local_device_id(); it's the same for
// each entry.
remote_device_ids.push_back(device_id_pair.remote_device_id());
}
std::string device_id;
bool is_background_advertisement = false;
// First try, identifying |service_data| as a foreground advertisement.
if (service_data.size() >= kMinNumBytesInForegroundServiceData) {
std::vector<cryptauth::BeaconSeed> beacon_seeds;
if (base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)) {
beacon_seeds =
device_sync_client_->GetLocalDeviceMetadata()->beacon_seeds();
} else if (!local_device_data_provider_->GetLocalDeviceData(
nullptr, &beacon_seeds)) {
PA_LOG(ERROR) << "Cannot fetch local beacon seeds.";
return base::nullopt;
}
device_id = foreground_eid_generator_->IdentifyRemoteDeviceByAdvertisement(
service_data, remote_device_ids, beacon_seeds);
}
// If the device has not yet been identified, try identifying |service_data|
// as a background advertisement.
if (chromeos::switches::IsInstantTetheringBackgroundAdvertisingSupported() &&
device_id.empty() && service_data.size() >= kMinNumBytesInServiceData &&
service_data.size() <= kMaxNumBytesInBackgroundServiceData) {
cryptauth::RemoteDeviceRefList remote_devices;
for (auto remote_device : tether_hosts_from_last_fetch_) {
if (base::ContainsValue(remote_device_ids, remote_device.GetDeviceId()))
remote_devices.push_back(remote_device);
}
device_id = background_eid_generator_->IdentifyRemoteDeviceByAdvertisement(
service_data, remote_devices);
is_background_advertisement = true;
}
// If the service data does not correspond to an advertisement from a device
// on this account, ignore it.
if (device_id.empty())
return base::nullopt;
for (const auto& remote_device_ref : tether_hosts_from_last_fetch_) {
if (remote_device_ref.GetDeviceId() == device_id) {
return secure_channel::BleServiceDataHelper::DeviceWithBackgroundBool(
remote_device_ref, is_background_advertisement);
}
}
PA_LOG(ERROR) << "Identified remote device ID is not a valid host: "
<< cryptauth::RemoteDeviceRef::TruncateDeviceIdForLogs(
device_id);
NOTREACHED();
return base::nullopt;
}
void BleServiceDataHelperImpl::OnTetherHostsUpdated() {
tether_host_fetcher_->FetchAllTetherHosts(
base::Bind(&BleServiceDataHelperImpl::OnTetherHostsFetched,
weak_ptr_factory_.GetWeakPtr()));
}
void BleServiceDataHelperImpl::OnTetherHostsFetched(
const cryptauth::RemoteDeviceRefList& tether_hosts) {
tether_hosts_from_last_fetch_ = tether_hosts;
}
base::Optional<std::string>
BleServiceDataHelperImpl::GetLocalDevicePublicKey() {
std::string local_device_public_key;
if (base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)) {
if (!device_sync_client_->GetLocalDeviceMetadata())
return base::nullopt;
local_device_public_key =
device_sync_client_->GetLocalDeviceMetadata()->public_key();
} else if (!local_device_data_provider_->GetLocalDeviceData(
&local_device_public_key, nullptr)) {
return base::nullopt;
}
if (local_device_public_key.empty())
return base::nullopt;
return local_device_public_key;
}
void BleServiceDataHelperImpl::SetTestDoubles(
std::unique_ptr<cryptauth::BackgroundEidGenerator> background_eid_generator,
std::unique_ptr<cryptauth::ForegroundEidGenerator>
foreground_eid_generator) {
background_eid_generator_ = std::move(background_eid_generator);
foreground_eid_generator_ = std::move(foreground_eid_generator);
}
} // namespace tether
} // namespace chromeos