blob: b2949471c58c2fef4e4e7dc1731394d54286c388 [file] [log] [blame]
// Copyright 2017 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 "modules/credentialmanager/WebAuthenticationClient.h"
#include <utility>
#include "bindings/core/v8/ScriptPromise.h"
#include "bindings/core/v8/ScriptPromiseResolver.h"
#include "bindings/core/v8/array_buffer_or_array_buffer_view.h"
#include "core/dom/DOMException.h"
#include "core/dom/Document.h"
#include "core/dom/ExceptionCode.h"
#include "core/frame/LocalFrame.h"
#include "core/typed_arrays/DOMArrayBuffer.h"
#include "modules/credentialmanager/AuthenticatorAttestationResponse.h"
#include "modules/credentialmanager/MakeCredentialOptions.h"
#include "public/platform/InterfaceProvider.h"
#include "services/service_manager/public/cpp/interface_provider.h"
namespace blink {
typedef ArrayBufferOrArrayBufferView BufferSource;
namespace {
using PublicKeyCallbacks = WebAuthenticationClient::PublicKeyCallbacks;
WebCredentialManagerError GetWebCredentialManagerErrorFromStatus(
webauth::mojom::blink::AuthenticatorStatus status) {
switch (status) {
case webauth::mojom::blink::AuthenticatorStatus::NOT_ALLOWED_ERROR:
return WebCredentialManagerError::kWebCredentialManagerNotAllowedError;
case webauth::mojom::blink::AuthenticatorStatus::NOT_SUPPORTED_ERROR:
return WebCredentialManagerError::kWebCredentialManagerNotSupportedError;
case webauth::mojom::blink::AuthenticatorStatus::SECURITY_ERROR:
return WebCredentialManagerError::kWebCredentialManagerSecurityError;
case webauth::mojom::blink::AuthenticatorStatus::UNKNOWN_ERROR:
return WebCredentialManagerError::kWebCredentialManagerUnknownError;
case webauth::mojom::blink::AuthenticatorStatus::CANCELLED:
return WebCredentialManagerError::kWebCredentialManagerCancelledError;
case webauth::mojom::blink::AuthenticatorStatus::NOT_IMPLEMENTED:
return blink::kWebCredentialManagerNotImplementedError;
case webauth::mojom::blink::AuthenticatorStatus::SUCCESS:
NOTREACHED();
break;
};
NOTREACHED();
return blink::WebCredentialManagerError::kWebCredentialManagerUnknownError;
}
void RespondToPublicKeyCallback(
std::unique_ptr<PublicKeyCallbacks> callbacks,
webauth::mojom::blink::AuthenticatorStatus status,
webauth::mojom::blink::PublicKeyCredentialInfoPtr credential) {
if (status != webauth::mojom::AuthenticatorStatus::SUCCESS) {
DCHECK(!credential);
callbacks->OnError(GetWebCredentialManagerErrorFromStatus(status));
return;
}
// Ensure we have an AuthenticatorAttestationResponse
DCHECK(credential);
DCHECK(!credential->client_data_json.IsEmpty());
DCHECK(!credential->response->attestation_object.IsEmpty());
callbacks->OnSuccess(std::move(credential));
}
} // namespace
} // namespace blink
namespace {
// Time to wait for an authenticator to successfully complete an operation.
constexpr WTF::TimeDelta kAdjustedTimeoutLower = WTF::TimeDelta::FromMinutes(1);
constexpr WTF::TimeDelta kAdjustedTimeoutUpper = WTF::TimeDelta::FromMinutes(2);
} // namespace
namespace mojo {
using webauth::mojom::blink::AuthenticatorStatus;
using webauth::mojom::blink::MakeCredentialOptionsPtr;
using webauth::mojom::blink::PublicKeyCredentialEntity;
using webauth::mojom::blink::PublicKeyCredentialEntityPtr;
using webauth::mojom::blink::PublicKeyCredentialParameters;
using webauth::mojom::blink::PublicKeyCredentialParametersPtr;
using webauth::mojom::blink::PublicKeyCredentialType;
using webauth::mojom::blink::AuthenticatorTransport;
template <>
struct TypeConverter<Vector<uint8_t>, blink::BufferSource> {
static Vector<uint8_t> Convert(const blink::BufferSource& buffer) {
DCHECK(!buffer.IsNull());
Vector<uint8_t> vector;
if (buffer.IsArrayBuffer()) {
vector.Append(static_cast<uint8_t*>(buffer.GetAsArrayBuffer()->Data()),
buffer.GetAsArrayBuffer()->ByteLength());
} else {
DCHECK(buffer.IsArrayBufferView());
vector.Append(static_cast<uint8_t*>(
buffer.GetAsArrayBufferView().View()->BaseAddress()),
buffer.GetAsArrayBufferView().View()->byteLength());
}
return vector;
}
};
template <>
struct TypeConverter<PublicKeyCredentialType, String> {
static PublicKeyCredentialType Convert(const String& type) {
if (type == "public-key")
return PublicKeyCredentialType::PUBLIC_KEY;
NOTREACHED();
return PublicKeyCredentialType::PUBLIC_KEY;
}
};
template <>
struct TypeConverter<AuthenticatorTransport, String> {
static AuthenticatorTransport Convert(const String& transport) {
if (transport == "usb")
return AuthenticatorTransport::USB;
if (transport == "nfc")
return AuthenticatorTransport::NFC;
if (transport == "ble")
return AuthenticatorTransport::BLE;
NOTREACHED();
return AuthenticatorTransport::USB;
}
};
template <>
struct TypeConverter<PublicKeyCredentialEntityPtr,
blink::PublicKeyCredentialUserEntity> {
static PublicKeyCredentialEntityPtr Convert(
const blink::PublicKeyCredentialUserEntity& user) {
// These manual checks are here because while these aspects are required,
// the IDL itself doesn't mark them as required to allow for future
// types of entities where these params may not be required.
if (!(user.hasId() && user.hasName() && user.hasDisplayName())) {
return nullptr;
}
auto entity = webauth::mojom::blink::PublicKeyCredentialEntity::New();
entity->id = user.id();
entity->name = user.name();
if (user.hasIcon()) {
entity->icon = blink::KURL(blink::KURL(), user.icon());
}
entity->display_name = user.displayName();
return entity;
}
};
template <>
struct TypeConverter<PublicKeyCredentialEntityPtr,
blink::PublicKeyCredentialEntity> {
static PublicKeyCredentialEntityPtr Convert(
const blink::PublicKeyCredentialEntity& rp) {
if (!rp.hasName()) {
return nullptr;
}
auto entity = webauth::mojom::blink::PublicKeyCredentialEntity::New();
entity->id = rp.id();
entity->name = rp.name();
if (rp.hasIcon()) {
entity->icon = blink::KURL(blink::KURL(), rp.icon());
}
return entity;
}
};
template <>
struct TypeConverter<PublicKeyCredentialParametersPtr,
blink::PublicKeyCredentialParameters> {
static PublicKeyCredentialParametersPtr Convert(
const blink::PublicKeyCredentialParameters& parameter) {
auto mojo_parameter =
webauth::mojom::blink::PublicKeyCredentialParameters::New();
mojo_parameter->type = ConvertTo<PublicKeyCredentialType>(parameter.type());
// A COSEAlgorithmIdentifier's value is a number identifying a cryptographic
// algorithm. Values are registered in the IANA COSE Algorithms registry.
// https://www.iana.org/assignments/cose/cose.xhtml#algorithms
mojo_parameter->algorithm_identifier = parameter.algorithm();
return mojo_parameter;
}
};
template <>
struct TypeConverter<MakeCredentialOptionsPtr, blink::MakeCredentialOptions> {
static MakeCredentialOptionsPtr Convert(
const blink::MakeCredentialOptions options) {
auto mojo_options = webauth::mojom::blink::MakeCredentialOptions::New();
mojo_options->relying_party = PublicKeyCredentialEntity::From(options.rp());
mojo_options->user = PublicKeyCredentialEntity::From(options.user());
if (!mojo_options->relying_party | !mojo_options->user) {
return nullptr;
}
mojo_options->challenge = ConvertTo<Vector<uint8_t>>(options.challenge());
// Step 4 of https://w3c.github.io/webauthn/#createCredential
if (options.hasTimeout()) {
WTF::TimeDelta adjusted_timeout;
adjusted_timeout = WTF::TimeDelta::FromMilliseconds(options.timeout());
mojo_options->adjusted_timeout =
std::max(kAdjustedTimeoutLower,
std::min(kAdjustedTimeoutUpper, adjusted_timeout));
} else {
mojo_options->adjusted_timeout = kAdjustedTimeoutLower;
}
// Steps 8 and 9 of
// https://www.w3.org/TR/2017/WD-webauthn-20170505/#createCredential
Vector<PublicKeyCredentialParametersPtr> parameters;
for (const auto& parameter : options.parameters()) {
PublicKeyCredentialParametersPtr normalized_parameter =
PublicKeyCredentialParameters::From(parameter);
if (normalized_parameter) {
parameters.push_back(std::move(normalized_parameter));
}
}
if (parameters.IsEmpty() && options.hasParameters()) {
return nullptr;
}
mojo_options->crypto_parameters = std::move(parameters);
if (options.hasExcludeList()) {
// Adds the excludeList members
for (const blink::PublicKeyCredentialDescriptor& descriptor :
options.excludeList()) {
auto mojo_descriptor =
webauth::mojom::blink::PublicKeyCredentialDescriptor::New();
mojo_descriptor->type =
ConvertTo<PublicKeyCredentialType>(descriptor.type());
mojo_descriptor->id = ConvertTo<Vector<uint8_t>>((descriptor.id()));
if (descriptor.hasTransports()) {
for (const auto& transport : descriptor.transports()) {
mojo_descriptor->transports.push_back(
ConvertTo<AuthenticatorTransport>(transport));
}
}
mojo_options->exclude_credentials.push_back(std::move(mojo_descriptor));
}
}
return mojo_options;
}
};
} // namespace mojo
namespace blink {
WebAuthenticationClient::WebAuthenticationClient(LocalFrame& frame) {
frame.GetInterfaceProvider().GetInterface(mojo::MakeRequest(&authenticator_));
authenticator_.set_connection_error_handler(ConvertToBaseCallback(
WTF::Bind(&WebAuthenticationClient::OnAuthenticatorConnectionError,
WrapWeakPersistent(this))));
}
WebAuthenticationClient::~WebAuthenticationClient() {}
void WebAuthenticationClient::DispatchMakeCredential(
const MakeCredentialOptions& publicKey,
std::unique_ptr<PublicKeyCallbacks> callbacks) {
auto options = webauth::mojom::blink::MakeCredentialOptions::From(publicKey);
if (!options) {
callbacks->OnError(
WebCredentialManagerError::kWebCredentialManagerNotSupportedError);
return;
}
authenticator_->MakeCredential(
std::move(options),
ConvertToBaseCallback(WTF::Bind(&RespondToPublicKeyCallback,
WTF::Passed(std::move(callbacks)))));
return;
}
void WebAuthenticationClient::GetAssertion(
const PublicKeyCredentialRequestOptions& publicKey,
PublicKeyCallbacks* callbacks) {
// TODO (kpaulhamus): implement GetAssertion and removed NOTREACHED().
NOTREACHED();
return;
}
void WebAuthenticationClient::OnAuthenticatorConnectionError() {
Cleanup();
}
void WebAuthenticationClient::Trace(blink::Visitor* visitor) {}
// Closes the Mojo connection.
void WebAuthenticationClient::Cleanup() {
authenticator_.reset();
}
} // namespace blink