blob: c5eedf770156d6773abb10ac57a38604c495c762 [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 "third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.h"
#include <algorithm>
#include <utility>
#include "build/build_config.h"
#include "third_party/blink/public/mojom/webauthn/authenticator.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/array_buffer_or_array_buffer_view.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_authentication_extensions_client_inputs.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_authenticator_selection_criteria.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_cable_authentication_data.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_cable_registration_data.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_public_key_credential_creation_options.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_public_key_credential_descriptor.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_public_key_credential_parameters.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_public_key_credential_request_options.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_public_key_credential_rp_entity.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_public_key_credential_user_entity.h"
#include "third_party/blink/renderer/modules/credentialmanager/credential.h"
#include "third_party/blink/renderer/modules/credentialmanager/federated_credential.h"
#include "third_party/blink/renderer/modules/credentialmanager/password_credential.h"
#include "third_party/blink/renderer/modules/credentialmanager/public_key_credential.h"
namespace {
// Time to wait for an authenticator to successfully complete an operation.
constexpr base::TimeDelta kAdjustedTimeoutLower =
base::TimeDelta::FromSeconds(10);
constexpr base::TimeDelta kAdjustedTimeoutUpper =
base::TimeDelta::FromMinutes(10);
base::TimeDelta AdjustTimeout(uint32_t timeout) {
base::TimeDelta adjusted_timeout;
adjusted_timeout = base::TimeDelta::FromMilliseconds(timeout);
return std::max(kAdjustedTimeoutLower,
std::min(kAdjustedTimeoutUpper, adjusted_timeout));
}
} // namespace
namespace mojo {
using blink::mojom::blink::AttestationConveyancePreference;
using blink::mojom::blink::AuthenticatorAttachment;
using blink::mojom::blink::AuthenticatorSelectionCriteria;
using blink::mojom::blink::AuthenticatorSelectionCriteriaPtr;
using blink::mojom::blink::AuthenticatorStatus;
using blink::mojom::blink::AuthenticatorTransport;
using blink::mojom::blink::CableAuthentication;
using blink::mojom::blink::CableAuthenticationPtr;
using blink::mojom::blink::CableRegistration;
using blink::mojom::blink::CableRegistrationPtr;
using blink::mojom::blink::CredentialInfo;
using blink::mojom::blink::CredentialInfoPtr;
using blink::mojom::blink::CredentialType;
using blink::mojom::blink::CredentialManagerError;
using blink::mojom::blink::PublicKeyCredentialCreationOptionsPtr;
using blink::mojom::blink::PublicKeyCredentialDescriptor;
using blink::mojom::blink::PublicKeyCredentialDescriptorPtr;
using blink::mojom::blink::PublicKeyCredentialRpEntity;
using blink::mojom::blink::PublicKeyCredentialRpEntityPtr;
using blink::mojom::blink::PublicKeyCredentialUserEntity;
using blink::mojom::blink::PublicKeyCredentialUserEntityPtr;
using blink::mojom::blink::PublicKeyCredentialParameters;
using blink::mojom::blink::PublicKeyCredentialParametersPtr;
using blink::mojom::blink::PublicKeyCredentialRequestOptionsPtr;
using blink::mojom::blink::PublicKeyCredentialType;
using blink::mojom::blink::UserVerificationRequirement;
// static
CredentialInfoPtr TypeConverter<CredentialInfoPtr, blink::Credential*>::Convert(
blink::Credential* credential) {
auto info = CredentialInfo::New();
info->id = credential->id();
if (credential->IsPasswordCredential()) {
::blink::PasswordCredential* password_credential =
static_cast<::blink::PasswordCredential*>(credential);
info->type = CredentialType::PASSWORD;
info->password = password_credential->password();
info->name = password_credential->name();
info->icon = password_credential->iconURL();
info->federation = blink::SecurityOrigin::CreateUniqueOpaque();
} else {
DCHECK(credential->IsFederatedCredential());
::blink::FederatedCredential* federated_credential =
static_cast<::blink::FederatedCredential*>(credential);
info->type = CredentialType::FEDERATED;
info->password = g_empty_string;
info->federation = federated_credential->GetProviderAsOrigin();
info->name = federated_credential->name();
info->icon = federated_credential->iconURL();
}
return info;
}
// static
blink::Credential*
TypeConverter<blink::Credential*, CredentialInfoPtr>::Convert(
const CredentialInfoPtr& info) {
switch (info->type) {
case CredentialType::FEDERATED:
return blink::FederatedCredential::Create(info->id, info->federation,
info->name, info->icon);
case CredentialType::PASSWORD:
return blink::PasswordCredential::Create(info->id, info->password,
info->name, info->icon);
case CredentialType::EMPTY:
return nullptr;
}
NOTREACHED();
return nullptr;
}
// static
CredentialManagerError
TypeConverter<CredentialManagerError, AuthenticatorStatus>::Convert(
const AuthenticatorStatus& status) {
switch (status) {
case blink::mojom::blink::AuthenticatorStatus::NOT_ALLOWED_ERROR:
return CredentialManagerError::NOT_ALLOWED;
case blink::mojom::blink::AuthenticatorStatus::ABORT_ERROR:
return CredentialManagerError::ABORT;
case blink::mojom::blink::AuthenticatorStatus::UNKNOWN_ERROR:
return CredentialManagerError::UNKNOWN;
case blink::mojom::blink::AuthenticatorStatus::PENDING_REQUEST:
return CredentialManagerError::PENDING_REQUEST;
case blink::mojom::blink::AuthenticatorStatus::INVALID_DOMAIN:
return CredentialManagerError::INVALID_DOMAIN;
case blink::mojom::blink::AuthenticatorStatus::INVALID_ICON_URL:
return CredentialManagerError::INVALID_ICON_URL;
case blink::mojom::blink::AuthenticatorStatus::CREDENTIAL_EXCLUDED:
return CredentialManagerError::CREDENTIAL_EXCLUDED;
case blink::mojom::blink::AuthenticatorStatus::CREDENTIAL_NOT_RECOGNIZED:
return CredentialManagerError::CREDENTIAL_NOT_RECOGNIZED;
case blink::mojom::blink::AuthenticatorStatus::NOT_IMPLEMENTED:
return CredentialManagerError::NOT_IMPLEMENTED;
case blink::mojom::blink::AuthenticatorStatus::NOT_FOCUSED:
return CredentialManagerError::NOT_FOCUSED;
case blink::mojom::blink::AuthenticatorStatus::
RESIDENT_CREDENTIALS_UNSUPPORTED:
return CredentialManagerError::RESIDENT_CREDENTIALS_UNSUPPORTED;
case blink::mojom::blink::AuthenticatorStatus::ALGORITHM_UNSUPPORTED:
return CredentialManagerError::ANDROID_ALGORITHM_UNSUPPORTED;
case blink::mojom::blink::AuthenticatorStatus::EMPTY_ALLOW_CREDENTIALS:
return CredentialManagerError::ANDROID_EMPTY_ALLOW_CREDENTIALS;
case blink::mojom::blink::AuthenticatorStatus::
ANDROID_NOT_SUPPORTED_ERROR:
return CredentialManagerError::ANDROID_NOT_SUPPORTED_ERROR;
case blink::mojom::blink::AuthenticatorStatus::
USER_VERIFICATION_UNSUPPORTED:
return CredentialManagerError::ANDROID_USER_VERIFICATION_UNSUPPORTED;
case blink::mojom::blink::AuthenticatorStatus::
PROTECTION_POLICY_INCONSISTENT:
return CredentialManagerError::PROTECTION_POLICY_INCONSISTENT;
case blink::mojom::blink::AuthenticatorStatus::OPAQUE_DOMAIN:
return CredentialManagerError::OPAQUE_DOMAIN;
case blink::mojom::blink::AuthenticatorStatus::INVALID_PROTOCOL:
return CredentialManagerError::INVALID_PROTOCOL;
case blink::mojom::blink::AuthenticatorStatus::BAD_RELYING_PARTY_ID:
return CredentialManagerError::BAD_RELYING_PARTY_ID;
case blink::mojom::blink::AuthenticatorStatus::SUCCESS:
NOTREACHED();
break;
}
NOTREACHED();
return CredentialManagerError::UNKNOWN;
}
// static helper method.
Vector<uint8_t> ConvertFixedSizeArray(
const blink::ArrayBufferOrArrayBufferView& buffer,
unsigned length) {
if (buffer.IsArrayBuffer() &&
(buffer.GetAsArrayBuffer()->ByteLengthAsSizeT() != length)) {
return Vector<uint8_t>();
}
if (buffer.IsArrayBufferView() &&
buffer.GetAsArrayBufferView().View()->byteLengthAsSizeT() != length) {
return Vector<uint8_t>();
}
return ConvertTo<Vector<uint8_t>>(buffer);
}
// static
Vector<uint8_t>
TypeConverter<Vector<uint8_t>, blink::ArrayBufferOrArrayBufferView>::Convert(
const blink::ArrayBufferOrArrayBufferView& buffer) {
DCHECK(!buffer.IsNull());
Vector<uint8_t> vector;
if (buffer.IsArrayBuffer()) {
vector.Append(static_cast<uint8_t*>(buffer.GetAsArrayBuffer()->Data()),
buffer.GetAsArrayBuffer()->DeprecatedByteLengthAsUnsigned());
} else {
DCHECK(buffer.IsArrayBufferView());
vector.Append(
static_cast<uint8_t*>(
buffer.GetAsArrayBufferView().View()->BaseAddress()),
buffer.GetAsArrayBufferView().View()->deprecatedByteLengthAsUnsigned());
}
return vector;
}
// static
PublicKeyCredentialType TypeConverter<PublicKeyCredentialType, String>::Convert(
const String& type) {
if (type == "public-key")
return PublicKeyCredentialType::PUBLIC_KEY;
NOTREACHED();
return PublicKeyCredentialType::PUBLIC_KEY;
}
// static
base::Optional<AuthenticatorTransport>
TypeConverter<base::Optional<AuthenticatorTransport>, String>::Convert(
const String& transport) {
if (transport == "usb")
return AuthenticatorTransport::USB;
if (transport == "nfc")
return AuthenticatorTransport::NFC;
if (transport == "ble")
return AuthenticatorTransport::BLE;
if (transport == "cable")
return AuthenticatorTransport::CABLE;
if (transport == "internal")
return AuthenticatorTransport::INTERNAL;
return base::nullopt;
}
// static
String TypeConverter<String, AuthenticatorTransport>::Convert(
const AuthenticatorTransport& transport) {
if (transport == AuthenticatorTransport::USB)
return "usb";
if (transport == AuthenticatorTransport::NFC)
return "nfc";
if (transport == AuthenticatorTransport::BLE)
return "ble";
if (transport == AuthenticatorTransport::CABLE)
return "cable";
if (transport == AuthenticatorTransport::INTERNAL)
return "internal";
NOTREACHED();
return "usb";
}
// static
UserVerificationRequirement
TypeConverter<UserVerificationRequirement, String>::Convert(
const String& requirement) {
if (requirement == "required")
return UserVerificationRequirement::REQUIRED;
if (requirement == "preferred")
return UserVerificationRequirement::PREFERRED;
if (requirement == "discouraged")
return UserVerificationRequirement::DISCOURAGED;
NOTREACHED();
return UserVerificationRequirement::PREFERRED;
}
// static
AttestationConveyancePreference
TypeConverter<AttestationConveyancePreference, String>::Convert(
const String& preference) {
if (preference == "none")
return AttestationConveyancePreference::NONE;
if (preference == "indirect")
return AttestationConveyancePreference::INDIRECT;
if (preference == "direct")
return AttestationConveyancePreference::DIRECT;
if (preference == "enterprise")
return AttestationConveyancePreference::ENTERPRISE;
NOTREACHED();
return AttestationConveyancePreference::NONE;
}
// static
AuthenticatorAttachment TypeConverter<AuthenticatorAttachment, String>::Convert(
const String& attachment) {
if (attachment.IsNull())
return AuthenticatorAttachment::NO_PREFERENCE;
if (attachment == "platform")
return AuthenticatorAttachment::PLATFORM;
if (attachment == "cross-platform")
return AuthenticatorAttachment::CROSS_PLATFORM;
NOTREACHED();
return AuthenticatorAttachment::NO_PREFERENCE;
}
// static
AuthenticatorSelectionCriteriaPtr
TypeConverter<AuthenticatorSelectionCriteriaPtr,
blink::AuthenticatorSelectionCriteria*>::
Convert(const blink::AuthenticatorSelectionCriteria* criteria) {
auto mojo_criteria =
blink::mojom::blink::AuthenticatorSelectionCriteria::New();
mojo_criteria->authenticator_attachment =
ConvertTo<AuthenticatorAttachment>(criteria->authenticatorAttachment());
mojo_criteria->require_resident_key = criteria->requireResidentKey();
mojo_criteria->user_verification = UserVerificationRequirement::PREFERRED;
if (criteria->hasUserVerification()) {
mojo_criteria->user_verification =
ConvertTo<UserVerificationRequirement>(criteria->userVerification());
}
return mojo_criteria;
}
// static
PublicKeyCredentialUserEntityPtr
TypeConverter<PublicKeyCredentialUserEntityPtr,
blink::PublicKeyCredentialUserEntity*>::
Convert(const blink::PublicKeyCredentialUserEntity* user) {
auto entity = PublicKeyCredentialUserEntity::New();
entity->id = ConvertTo<Vector<uint8_t>>(user->id());
entity->name = user->name();
if (user->hasIcon()) {
if (user->icon().IsEmpty())
entity->icon = blink::KURL();
else
entity->icon = blink::KURL(user->icon());
}
entity->display_name = user->displayName();
return entity;
}
// static
PublicKeyCredentialRpEntityPtr
TypeConverter<PublicKeyCredentialRpEntityPtr,
blink::PublicKeyCredentialRpEntity*>::
Convert(const blink::PublicKeyCredentialRpEntity* rp) {
auto entity = PublicKeyCredentialRpEntity::New();
if (rp->hasId()) {
entity->id = rp->id();
}
if (!rp->name()) {
return nullptr;
}
entity->name = rp->name();
if (rp->hasIcon()) {
if (rp->icon().IsEmpty())
entity->icon = blink::KURL();
else
entity->icon = blink::KURL(rp->icon());
}
return entity;
}
// static
PublicKeyCredentialDescriptorPtr
TypeConverter<PublicKeyCredentialDescriptorPtr,
blink::PublicKeyCredentialDescriptor*>::
Convert(const blink::PublicKeyCredentialDescriptor* descriptor) {
auto mojo_descriptor = PublicKeyCredentialDescriptor::New();
mojo_descriptor->type =
ConvertTo<PublicKeyCredentialType>(descriptor->type());
mojo_descriptor->id = ConvertTo<Vector<uint8_t>>(descriptor->id());
if (descriptor->hasTransports() && !descriptor->transports().IsEmpty()) {
for (const auto& transport : descriptor->transports()) {
auto maybe_transport(
ConvertTo<base::Optional<AuthenticatorTransport>>(transport));
if (maybe_transport) {
mojo_descriptor->transports.push_back(*maybe_transport);
}
}
} else {
mojo_descriptor->transports = {
AuthenticatorTransport::USB, AuthenticatorTransport::BLE,
AuthenticatorTransport::NFC, AuthenticatorTransport::CABLE,
AuthenticatorTransport::INTERNAL};
}
return mojo_descriptor;
}
// static
PublicKeyCredentialParametersPtr
TypeConverter<PublicKeyCredentialParametersPtr,
blink::PublicKeyCredentialParameters*>::
Convert(const blink::PublicKeyCredentialParameters* parameter) {
auto mojo_parameter = 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->alg();
return mojo_parameter;
}
// static
PublicKeyCredentialCreationOptionsPtr
TypeConverter<PublicKeyCredentialCreationOptionsPtr,
blink::PublicKeyCredentialCreationOptions*>::
Convert(const blink::PublicKeyCredentialCreationOptions* options) {
auto mojo_options =
blink::mojom::blink::PublicKeyCredentialCreationOptions::New();
mojo_options->relying_party =
PublicKeyCredentialRpEntity::From(options->rp());
mojo_options->user = PublicKeyCredentialUserEntity::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()) {
mojo_options->adjusted_timeout = AdjustTimeout(options->timeout());
} else {
mojo_options->adjusted_timeout = kAdjustedTimeoutUpper;
}
// Steps 8 and 9 of
// https://www.w3.org/TR/2017/WD-webauthn-20170505/#createCredential
Vector<PublicKeyCredentialParametersPtr> parameters;
for (auto& parameter : options->pubKeyCredParams()) {
PublicKeyCredentialParametersPtr normalized_parameter =
PublicKeyCredentialParameters::From(parameter.Get());
if (normalized_parameter) {
parameters.push_back(std::move(normalized_parameter));
}
}
if (parameters.IsEmpty() && options->hasPubKeyCredParams()) {
return nullptr;
}
mojo_options->public_key_parameters = std::move(parameters);
if (options->hasAuthenticatorSelection()) {
mojo_options->authenticator_selection =
AuthenticatorSelectionCriteria::From(options->authenticatorSelection());
}
if (options->hasExcludeCredentials()) {
// Adds the excludeCredentials members
for (auto& descriptor : options->excludeCredentials()) {
PublicKeyCredentialDescriptorPtr mojo_descriptor =
PublicKeyCredentialDescriptor::From(descriptor.Get());
if (mojo_descriptor) {
mojo_options->exclude_credentials.push_back(std::move(mojo_descriptor));
}
}
}
mojo_options->attestation =
blink::mojom::AttestationConveyancePreference::NONE;
if (options->hasAttestation()) {
const auto& attestation = options->attestation();
if (attestation == "none") {
// Default value.
} else if (attestation == "indirect") {
mojo_options->attestation =
blink::mojom::AttestationConveyancePreference::INDIRECT;
} else if (attestation == "direct") {
mojo_options->attestation =
blink::mojom::AttestationConveyancePreference::DIRECT;
} else if (attestation == "enterprise") {
mojo_options->attestation =
blink::mojom::AttestationConveyancePreference::ENTERPRISE;
} else {
return nullptr;
}
}
mojo_options->protection_policy = blink::mojom::ProtectionPolicy::UNSPECIFIED;
mojo_options->enforce_protection_policy = false;
if (options->hasExtensions()) {
auto* extensions = options->extensions();
if (extensions->hasCableRegistration()) {
CableRegistrationPtr mojo_cable =
CableRegistration::From(extensions->cableRegistration());
if (mojo_cable) {
mojo_options->cable_registration_data = std::move(mojo_cable);
}
}
if (extensions->hasHmacCreateSecret()) {
mojo_options->hmac_create_secret = extensions->hmacCreateSecret();
}
if (extensions->hasAppidExclude()) {
mojo_options->appid_exclude = extensions->appidExclude();
}
#if defined(OS_ANDROID)
if (extensions->hasUvm()) {
mojo_options->user_verification_methods = extensions->uvm();
}
#endif
if (extensions->hasCredentialProtectionPolicy()) {
const auto& policy = extensions->credentialProtectionPolicy();
if (policy == "userVerificationOptional") {
mojo_options->protection_policy = blink::mojom::ProtectionPolicy::NONE;
} else if (policy == "userVerificationOptionalWithCredentialIDList") {
mojo_options->protection_policy =
blink::mojom::ProtectionPolicy::UV_OR_CRED_ID_REQUIRED;
} else if (policy == "userVerificationRequired") {
mojo_options->protection_policy =
blink::mojom::ProtectionPolicy::UV_REQUIRED;
} else {
return nullptr;
}
}
if (extensions->hasEnforceCredentialProtectionPolicy() &&
extensions->enforceCredentialProtectionPolicy()) {
mojo_options->enforce_protection_policy = true;
}
}
return mojo_options;
}
// static
CableAuthenticationPtr
TypeConverter<CableAuthenticationPtr, blink::CableAuthenticationData*>::Convert(
const blink::CableAuthenticationData* data) {
auto entity = CableAuthentication::New();
entity->version = data->version();
entity->client_eid = ConvertFixedSizeArray(data->clientEid(), 16);
entity->authenticator_eid =
ConvertFixedSizeArray(data->authenticatorEid(), 16);
entity->session_pre_key = ConvertFixedSizeArray(data->sessionPreKey(), 32);
if (entity->client_eid.IsEmpty() || entity->authenticator_eid.IsEmpty() ||
entity->session_pre_key.IsEmpty()) {
return nullptr;
}
return entity;
}
// static
CableRegistrationPtr
TypeConverter<CableRegistrationPtr, blink::CableRegistrationData*>::Convert(
const blink::CableRegistrationData* data) {
auto entity = CableRegistration::New();
entity->versions = data->versions();
entity->relying_party_public_key =
ConvertFixedSizeArray(data->rpPublicKey(), 65);
if (entity->relying_party_public_key.IsEmpty()) {
return nullptr;
}
return entity;
}
// static
PublicKeyCredentialRequestOptionsPtr
TypeConverter<PublicKeyCredentialRequestOptionsPtr,
blink::PublicKeyCredentialRequestOptions*>::
Convert(const blink::PublicKeyCredentialRequestOptions* options) {
auto mojo_options =
blink::mojom::blink::PublicKeyCredentialRequestOptions::New();
mojo_options->challenge = ConvertTo<Vector<uint8_t>>(options->challenge());
if (options->hasTimeout()) {
mojo_options->adjusted_timeout = AdjustTimeout(options->timeout());
} else {
mojo_options->adjusted_timeout = kAdjustedTimeoutUpper;
}
mojo_options->relying_party_id = options->rpId();
if (options->hasAllowCredentials()) {
// Adds the allowList members
for (auto descriptor : options->allowCredentials()) {
PublicKeyCredentialDescriptorPtr mojo_descriptor =
PublicKeyCredentialDescriptor::From(descriptor.Get());
if (mojo_descriptor) {
mojo_options->allow_credentials.push_back(std::move(mojo_descriptor));
}
}
}
mojo_options->user_verification = UserVerificationRequirement::PREFERRED;
if (options->hasUserVerification()) {
mojo_options->user_verification =
ConvertTo<UserVerificationRequirement>(options->userVerification());
}
if (options->hasExtensions()) {
auto* extensions = options->extensions();
if (extensions->hasAppid()) {
mojo_options->appid = extensions->appid();
}
if (extensions->hasCableAuthentication()) {
Vector<CableAuthenticationPtr> mojo_data;
for (auto& data : extensions->cableAuthentication()) {
if (data->version() != 1) {
continue;
}
CableAuthenticationPtr mojo_cable =
CableAuthentication::From(data.Get());
if (mojo_cable) {
mojo_data.push_back(std::move(mojo_cable));
}
}
if (mojo_data.size() > 0) {
mojo_options->cable_authentication_data = std::move(mojo_data);
}
}
#if defined(OS_ANDROID)
if (extensions->hasUvm()) {
mojo_options->user_verification_methods = extensions->uvm();
}
#endif
}
return mojo_options;
}
} // namespace mojo