Commit 2fc1b779 authored by Yicheng Li's avatar Yicheng Li Committed by Commit Bot

fido: Add ChromeOS authenticator and MakeCredential operation

This change implements the MakeCredential operation in ChromeOS
authenticator. The authenticator data is extracted from the dbus
response returned by ChromeOS platform daemons.

Bug: b:144861739
Change-Id: I4332a973e75aa900e348658bd639c7a78335caa6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1986976Reviewed-by: default avatarMartin Kreichgauer <martinkr@google.com>
Reviewed-by: default avatarSteven Bennetts <stevenjb@chromium.org>
Reviewed-by: default avatarMike Frysinger <vapier@chromium.org>
Commit-Queue: Yicheng Li <yichengli@chromium.org>
Cr-Commit-Position: refs/heads/master@{#740797}
parent a346f003
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import("//build/config/features.gni") import("//build/config/features.gni")
import("//testing/libfuzzer/fuzzer_test.gni") import("//testing/libfuzzer/fuzzer_test.gni")
import("//third_party/protobuf/proto_library.gni")
component("fido") { component("fido") {
sources = [ sources = [
...@@ -231,6 +232,28 @@ component("fido") { ...@@ -231,6 +232,28 @@ component("fido") {
"win/webauthn_api.h", "win/webauthn_api.h",
] ]
} }
if (is_chromeos) {
sources += [
"cros/authenticator.cc",
"cros/authenticator.h",
]
deps += [
":u2f_proto",
"//dbus",
]
}
}
if (is_chromeos) {
proto_library("u2f_proto") {
sources = [
"//third_party/cros_system_api/dbus/u2f/u2f_interface.proto",
]
proto_out_dir = "chromeos/dbus/u2f"
}
} }
source_set("mocks") { source_set("mocks") {
......
...@@ -2,9 +2,11 @@ include_rules = [ ...@@ -2,9 +2,11 @@ include_rules = [
"+components/apdu", "+components/apdu",
"+components/cbor", "+components/cbor",
"+crypto", "+crypto",
"+dbus",
"+net/base", "+net/base",
"+net/cert", "+net/cert",
"+ui/base/l10n", "+ui/base/l10n",
"+third_party/boringssl/src/include", "+third_party/boringssl/src/include",
"+third_party/cros_system_api",
"+third_party/microsoft_webauthn", "+third_party/microsoft_webauthn",
] ]
// Copyright 2019 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 <memory>
#include <utility>
#include <vector>
#include "device/fido/cros/authenticator.h"
#include "base/bind.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "chromeos/dbus/u2f/u2f_interface.pb.h"
#include "components/cbor/reader.h"
#include "components/device_event_log/device_event_log.h"
#include "device/fido/attestation_statement_formats.h"
#include "device/fido/authenticator_data.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/opaque_attestation_statement.h"
#include "third_party/cros_system_api/dbus/u2f/dbus-constants.h"
namespace device {
namespace fido {
namespace cros {
ChromeOSAuthenticator::ChromeOSAuthenticator() : weak_factory_(this) {}
ChromeOSAuthenticator::~ChromeOSAuthenticator() {}
std::string ChromeOSAuthenticator::GetId() const {
return "ChromeOSAuthenticator";
}
base::string16 ChromeOSAuthenticator::GetDisplayName() const {
return base::string16(base::ASCIIToUTF16("ChromeOS Authenticator"));
}
namespace {
AuthenticatorSupportedOptions ChromeOSAuthenticatorOptions() {
AuthenticatorSupportedOptions options;
options.is_platform_device = true;
options.supports_resident_key = true;
// Even if the user has no fingerprints enrolled, we will have password
// as fallback.
options.user_verification_availability = AuthenticatorSupportedOptions::
UserVerificationAvailability::kSupportedAndConfigured;
options.supports_user_presence = true;
return options;
}
} // namespace
const base::Optional<AuthenticatorSupportedOptions>&
ChromeOSAuthenticator::Options() const {
static const base::Optional<AuthenticatorSupportedOptions> options =
ChromeOSAuthenticatorOptions();
return options;
}
base::Optional<FidoTransportProtocol>
ChromeOSAuthenticator::AuthenticatorTransport() const {
return FidoTransportProtocol::kInternal;
}
void ChromeOSAuthenticator::InitializeAuthenticator(
base::OnceClosure callback) {
std::move(callback).Run();
}
void ChromeOSAuthenticator::MakeCredential(CtapMakeCredentialRequest request,
MakeCredentialCallback callback) {
dbus::Bus::Options options;
options.bus_type = dbus::Bus::SYSTEM;
scoped_refptr<dbus::Bus> bus = new dbus::Bus(options);
dbus::ObjectProxy* u2f_proxy = bus->GetObjectProxy(
u2f::kU2FServiceName, dbus::ObjectPath(u2f::kU2FServicePath));
if (!u2f_proxy) {
FIDO_LOG(ERROR) << "Couldn't get u2f proxy";
std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOther,
base::nullopt);
return;
}
u2f::MakeCredentialRequest req;
// Requests with UserPresence get upgraded to UserVerification.
req.set_verification_type(u2f::VERIFICATION_USER_VERIFICATION);
req.set_rp_id(request.rp.id);
req.set_user_entity(
std::string(request.user.id.begin(), request.user.id.end()));
req.set_resident_credential(request.resident_key_required);
dbus::MethodCall method_call(u2f::kU2FInterface, u2f::kU2FMakeCredential);
dbus::MessageWriter writer(&method_call);
writer.AppendProtoAsArrayOfBytes(req);
u2f_proxy->CallMethodWithErrorResponse(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&ChromeOSAuthenticator::OnMakeCredentialResp,
weak_factory_.GetWeakPtr(), std::move(request),
std::move(callback)));
}
void ChromeOSAuthenticator::OnMakeCredentialResp(
CtapMakeCredentialRequest request,
MakeCredentialCallback callback,
dbus::Response* dbus_response,
dbus::ErrorResponse* error) {
if (error) {
FIDO_LOG(ERROR) << "MakeCredential dbus call failed with error: "
<< error->ToString();
std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOther,
base::nullopt);
return;
}
dbus::MessageReader reader(dbus_response);
u2f::MakeCredentialResponse resp;
if (!reader.PopArrayOfBytesAsProto(&resp)) {
FIDO_LOG(ERROR) << "Failed to parse reply for call to MakeCredential";
std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOther,
base::nullopt);
return;
}
FIDO_LOG(DEBUG) << "Make credential status: " << resp.status();
if (resp.status() !=
u2f::MakeCredentialResponse_MakeCredentialStatus_SUCCESS) {
std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOperationDenied,
base::nullopt);
return;
}
base::Optional<AuthenticatorData> authenticator_data =
AuthenticatorData::DecodeAuthenticatorData(
base::as_bytes(base::make_span(resp.authenticator_data())));
if (!authenticator_data) {
FIDO_LOG(ERROR) << "Authenticator data corrupted.";
std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOther,
base::nullopt);
return;
}
base::Optional<cbor::Value> statement_map = cbor::Reader::Read(
base::as_bytes(base::make_span(resp.attestation_statement())));
if (!statement_map ||
statement_map.value().type() != cbor::Value::Type::MAP) {
FIDO_LOG(ERROR) << "Attestation statement is not a CBOR map.";
std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOther,
base::nullopt);
}
auto statement = std::make_unique<OpaqueAttestationStatement>(
resp.attestation_format(), std::move(*statement_map));
AuthenticatorMakeCredentialResponse response(
FidoTransportProtocol::kInternal,
AttestationObject(std::move(*authenticator_data), std::move(statement)));
std::move(callback).Run(CtapDeviceResponseCode::kSuccess,
std::move(response));
}
bool ChromeOSAuthenticator::IsInPairingMode() const {
return false;
}
bool ChromeOSAuthenticator::IsPaired() const {
return false;
}
bool ChromeOSAuthenticator::RequiresBlePairingPin() const {
return false;
}
base::WeakPtr<FidoAuthenticator> ChromeOSAuthenticator::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
} // namespace cros
} // namespace fido
} // namespace device
// Copyright 2019 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 DEVICE_FIDO_CROS_AUTHENTICATOR_H_
#define DEVICE_FIDO_CROS_AUTHENTICATOR_H_
#include <string>
#include "base/component_export.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_proxy.h"
#include "device/fido/authenticator_supported_options.h"
#include "device/fido/ctap_get_assertion_request.h"
#include "device/fido/ctap_make_credential_request.h"
#include "device/fido/fido_authenticator.h"
#include "device/fido/fido_transport_protocol.h"
namespace device {
namespace fido {
namespace cros {
class COMPONENT_EXPORT(DEVICE_FIDO) ChromeOSAuthenticator
: public FidoAuthenticator {
public:
ChromeOSAuthenticator();
~ChromeOSAuthenticator() override;
// FidoAuthenticator
void InitializeAuthenticator(base::OnceClosure callback) override;
void MakeCredential(CtapMakeCredentialRequest request,
MakeCredentialCallback callback) override;
void GetAssertion(CtapGetAssertionRequest request,
GetAssertionCallback callback) override {}
void GetNextAssertion(GetAssertionCallback callback) override {}
void Cancel() override {}
std::string GetId() const override;
base::string16 GetDisplayName() const override;
const base::Optional<AuthenticatorSupportedOptions>& Options() const override;
base::Optional<FidoTransportProtocol> AuthenticatorTransport() const override;
bool IsInPairingMode() const override;
bool IsPaired() const override;
bool RequiresBlePairingPin() const override;
void GetTouch(base::OnceClosure callback) override {}
base::WeakPtr<FidoAuthenticator> GetWeakPtr() override;
private:
void OnMakeCredentialResp(CtapMakeCredentialRequest request,
MakeCredentialCallback callback,
dbus::Response* dbus_response,
dbus::ErrorResponse* error);
base::WeakPtrFactory<ChromeOSAuthenticator> weak_factory_;
};
} // namespace cros
} // namespace fido
} // namespace device
#endif // DEVICE_FIDO_CROS_AUTHENTICATOR_H_
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