blob: 0ac3665f494c9d22bc59012ff6ea02d7f6da5258 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/fido/fido_discovery_factory.h"
#include "base/containers/contains.h"
#include "base/notreached.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "components/device_event_log/device_event_log.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/fido/aoa/android_accessory_discovery.h"
#include "device/fido/cable/fido_cable_discovery.h"
#include "device/fido/cable/v2_discovery.h"
#include "device/fido/features.h"
#include "device/fido/fido_discovery_base.h"
#include "device/fido/mac/icloud_keychain.h"
// HID is not supported on Android.
#if !BUILDFLAG(IS_ANDROID)
#include "device/fido/hid/fido_hid_discovery.h"
#endif // !BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_WIN)
// rpc.h needs to be included before winuser.h.
#include <rpc.h>
#include <Winuser.h>
#include "device/fido/win/discovery.h"
#include "device/fido/win/webauthn_api.h"
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_MAC)
#include "base/process/process_info.h"
#include "device/fido/mac/discovery.h"
#endif // BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_CHROMEOS)
#include "device/fido/cros/discovery.h"
#endif // BUILDFLAG(IS_CHROMEOS)
namespace device {
FidoDiscoveryFactory::FidoDiscoveryFactory() = default;
FidoDiscoveryFactory::~FidoDiscoveryFactory() = default;
std::vector<std::unique_ptr<FidoDiscoveryBase>> FidoDiscoveryFactory::Create(
FidoTransportProtocol transport) {
switch (transport) {
case FidoTransportProtocol::kUsbHumanInterfaceDevice:
return SingleDiscovery(
std::make_unique<FidoHidDiscovery>(hid_ignore_list_));
case FidoTransportProtocol::kBluetoothLowEnergy:
return {};
case FidoTransportProtocol::kHybrid:
#if BUILDFLAG(IS_MAC)
if (!base::IsProcessSelfResponsible()) {
// On recent macOS a process must have listed Bluetooth metadata in
// its Info.plist in order to call Bluetooth APIs. Failure to do so
// results in the system killing with process with SIGABRT once
// Bluetooth calls are made.
//
// However, unless Chromium is started from the Finder, or with special
// posix_spawn flags, then the responsible process—the one that needs
// to have the right Info.plist—is one of the parent processes, often
// the terminal emulator. This can lead to Chromium getting killed when
// trying to do WebAuthn. This also affects layout tests.
//
// Thus, if the responsible process is not Chromium itself, then we
// disable caBLE (and thus avoid Bluetooth calls).
FIDO_LOG(ERROR) << "Cannot start caBLE because process is not "
"self-responsible. Launch from Finder to fix.";
return {};
}
#endif
if (device::BluetoothAdapterFactory::Get()->IsLowEnergySupported() &&
(cable_data_.has_value() || qr_generator_key_.has_value())) {
auto v1_discovery = std::make_unique<FidoCableDiscovery>(
cable_data_.value_or(std::vector<CableDiscoveryData>()));
std::vector<std::unique_ptr<FidoDiscoveryBase>> ret;
const bool have_v2_discovery_data =
cable_data_.has_value() &&
base::Contains(*cable_data_, CableDiscoveryData::Version::V2,
&CableDiscoveryData::version);
if (qr_generator_key_.has_value() || have_v2_discovery_data) {
ret.emplace_back(std::make_unique<cablev2::Discovery>(
request_type_.value(), network_context_, qr_generator_key_,
v1_discovery->GetV2AdvertStream(), std::move(v2_pairings_),
std::move(contact_device_stream_),
cable_data_.value_or(std::vector<CableDiscoveryData>()),
std::move(cable_pairing_callback_),
std::move(cable_invalidated_pairing_callback_)));
}
ret.emplace_back(std::move(v1_discovery));
return ret;
}
return {};
case FidoTransportProtocol::kNearFieldCommunication:
// TODO(https://crbug.com/825949): Add NFC support.
return {};
case FidoTransportProtocol::kInternal: {
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
return MaybeCreatePlatformDiscovery();
#else
return {};
#endif
}
case FidoTransportProtocol::kAndroidAccessory:
if (usb_device_manager_) {
auto ret = SingleDiscovery(std::make_unique<AndroidAccessoryDiscovery>(
std::move(usb_device_manager_.value()),
std::move(aoa_request_description_)));
usb_device_manager_.reset();
return ret;
}
return {};
}
NOTREACHED() << "Unhandled transport type";
return {};
}
bool FidoDiscoveryFactory::IsTestOverride() {
return false;
}
void FidoDiscoveryFactory::set_cable_data(
FidoRequestType request_type,
std::vector<CableDiscoveryData> cable_data,
const absl::optional<std::array<uint8_t, cablev2::kQRKeySize>>&
qr_generator_key,
std::vector<std::unique_ptr<cablev2::Pairing>> v2_pairings) {
request_type_ = request_type;
cable_data_ = std::move(cable_data);
qr_generator_key_ = std::move(qr_generator_key);
v2_pairings_ = std::move(v2_pairings);
}
void FidoDiscoveryFactory::set_android_accessory_params(
mojo::Remote<device::mojom::UsbDeviceManager> usb_device_manager,
std::string aoa_request_description) {
usb_device_manager_.emplace(std::move(usb_device_manager));
aoa_request_description_ = std::move(aoa_request_description);
}
void FidoDiscoveryFactory::set_network_context(
network::mojom::NetworkContext* network_context) {
network_context_ = network_context;
}
void FidoDiscoveryFactory::set_cable_pairing_callback(
base::RepeatingCallback<void(std::unique_ptr<cablev2::Pairing>)> callback) {
cable_pairing_callback_.emplace(std::move(callback));
}
void FidoDiscoveryFactory::set_cable_invalidated_pairing_callback(
base::RepeatingCallback<void(size_t)> callback) {
cable_invalidated_pairing_callback_.emplace(std::move(callback));
}
base::RepeatingCallback<void(size_t)>
FidoDiscoveryFactory::get_cable_contact_callback() {
DCHECK(!contact_device_stream_);
base::RepeatingCallback<void(size_t)> ret;
std::tie(ret, contact_device_stream_) =
FidoDeviceDiscovery::EventStream<size_t>::New();
return ret;
}
void FidoDiscoveryFactory::set_hid_ignore_list(
base::flat_set<VidPid> hid_ignore_list) {
hid_ignore_list_ = std::move(hid_ignore_list);
}
// static
std::vector<std::unique_ptr<FidoDiscoveryBase>>
FidoDiscoveryFactory::SingleDiscovery(
std::unique_ptr<FidoDiscoveryBase> discovery) {
if (!discovery) {
return {};
}
std::vector<std::unique_ptr<FidoDiscoveryBase>> ret;
ret.emplace_back(std::move(discovery));
return ret;
}
#if BUILDFLAG(IS_WIN)
void FidoDiscoveryFactory::set_win_webauthn_api(WinWebAuthnApi* api) {
win_webauthn_api_ = api;
}
WinWebAuthnApi* FidoDiscoveryFactory::win_webauthn_api() const {
return win_webauthn_api_;
}
std::unique_ptr<FidoDiscoveryBase>
FidoDiscoveryFactory::MaybeCreateWinWebAuthnApiDiscovery() {
// TODO(martinkr): Inject the window from which the request originated.
// Windows uses this parameter to center the dialog over the parent. The
// dialog should be centered over the originating Chrome Window; the
// foreground window may have changed to something else since the request
// was issued.
return win_webauthn_api_ && win_webauthn_api_->IsAvailable()
? std::make_unique<WinWebAuthnApiAuthenticatorDiscovery>(
GetForegroundWindow(), win_webauthn_api_)
: nullptr;
}
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_MAC)
std::vector<std::unique_ptr<FidoDiscoveryBase>>
FidoDiscoveryFactory::MaybeCreatePlatformDiscovery() const {
std::vector<std::unique_ptr<FidoDiscoveryBase>> ret;
if (mac_touch_id_config_) {
ret.emplace_back(std::make_unique<fido::mac::FidoTouchIdDiscovery>(
*mac_touch_id_config_));
}
if (base::FeatureList::IsEnabled(kWebAuthnICloudKeychain) &&
fido::icloud_keychain::IsSupported()) {
ret.emplace_back(fido::icloud_keychain::NewDiscovery(nswindow_));
}
return ret;
}
#endif
#if BUILDFLAG(IS_CHROMEOS)
std::vector<std::unique_ptr<FidoDiscoveryBase>>
FidoDiscoveryFactory::MaybeCreatePlatformDiscovery() const {
if (base::FeatureList::IsEnabled(kWebAuthCrosPlatformAuthenticator)) {
auto discovery = std::make_unique<FidoChromeOSDiscovery>(
generate_request_id_callback_,
std::move(get_assertion_request_for_legacy_credential_check_));
discovery->set_require_power_button_mode(
require_legacy_cros_authenticator_);
return SingleDiscovery(std::move(discovery));
}
return {};
}
void FidoDiscoveryFactory::set_generate_request_id_callback(
base::RepeatingCallback<std::string()> callback) {
generate_request_id_callback_ = std::move(callback);
}
void FidoDiscoveryFactory::set_require_legacy_cros_authenticator(bool value) {
require_legacy_cros_authenticator_ = value;
}
void FidoDiscoveryFactory::
set_get_assertion_request_for_legacy_credential_check(
CtapGetAssertionRequest request) {
get_assertion_request_for_legacy_credential_check_ = std::move(request);
}
#endif
} // namespace device