[go: nahoru, domu]

blob: 0d71c819ef9f304f6acad71850083df1d2696b3c [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 "device/fido/win/type_conversions.h"
#include <algorithm>
#include <string>
#include <vector>
#include "base/containers/span.h"
#include "base/logging.h"
#include "base/optional.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "components/cbor/reader.h"
#include "components/device_event_log/device_event_log.h"
#include "device/fido/authenticator_get_assertion_response.h"
#include "device/fido/authenticator_make_credential_response.h"
#include "device/fido/fido_transport_protocol.h"
#include "device/fido/get_assertion_request_handler.h"
#include "device/fido/make_credential_request_handler.h"
#include "device/fido/opaque_attestation_statement.h"
namespace device {
base::Optional<AuthenticatorMakeCredentialResponse>
ToAuthenticatorMakeCredentialResponse(
const WEBAUTHN_CREDENTIAL_ATTESTATION& credential_attestation) {
auto authenticator_data = AuthenticatorData::DecodeAuthenticatorData(
base::span<const uint8_t>(credential_attestation.pbAuthenticatorData,
credential_attestation.cbAuthenticatorData));
if (!authenticator_data) {
DLOG(ERROR) << "DecodeAuthenticatorData failed: "
<< base::HexEncode(credential_attestation.pbAuthenticatorData,
credential_attestation.cbAuthenticatorData);
return base::nullopt;
}
base::Optional<cbor::Value> cbor_attestation_statement = cbor::Reader::Read(
base::span<const uint8_t>(credential_attestation.pbAttestation,
credential_attestation.cbAttestation));
if (!cbor_attestation_statement || !cbor_attestation_statement->is_map()) {
DLOG(ERROR) << "CBOR decoding attestation statement failed: "
<< base::HexEncode(credential_attestation.pbAttestation,
credential_attestation.cbAttestation);
return base::nullopt;
}
base::Optional<FidoTransportProtocol> transport_used;
if (credential_attestation.dwVersion >=
WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_3) {
// dwUsedTransport should have exactly one of the
// WEBAUTHN_CTAP_TRANSPORT_* values set.
switch (credential_attestation.dwUsedTransport) {
case WEBAUTHN_CTAP_TRANSPORT_USB:
transport_used = FidoTransportProtocol::kUsbHumanInterfaceDevice;
break;
case WEBAUTHN_CTAP_TRANSPORT_NFC:
transport_used = FidoTransportProtocol::kNearFieldCommunication;
break;
case WEBAUTHN_CTAP_TRANSPORT_BLE:
transport_used = FidoTransportProtocol::kBluetoothLowEnergy;
break;
case WEBAUTHN_CTAP_TRANSPORT_INTERNAL:
transport_used = FidoTransportProtocol::kInternal;
break;
default:
// Ignore _TEST and possibly future others.
break;
}
}
return AuthenticatorMakeCredentialResponse(
transport_used,
AttestationObject(
std::move(*authenticator_data),
std::make_unique<OpaqueAttestationStatement>(
base::WideToUTF8(credential_attestation.pwszFormatType),
std::move(*cbor_attestation_statement))));
}
base::Optional<AuthenticatorGetAssertionResponse>
ToAuthenticatorGetAssertionResponse(
const WEBAUTHN_ASSERTION& assertion,
const std::vector<PublicKeyCredentialDescriptor>& allow_list) {
auto authenticator_data =
AuthenticatorData::DecodeAuthenticatorData(base::span<const uint8_t>(
assertion.pbAuthenticatorData, assertion.cbAuthenticatorData));
if (!authenticator_data) {
DLOG(ERROR) << "DecodeAuthenticatorData failed: "
<< base::HexEncode(assertion.pbAuthenticatorData,
assertion.cbAuthenticatorData);
return base::nullopt;
}
AuthenticatorGetAssertionResponse response(
std::move(*authenticator_data),
std::vector<uint8_t>(assertion.pbSignature,
assertion.pbSignature + assertion.cbSignature));
response.credential = PublicKeyCredentialDescriptor(
CredentialType::kPublicKey,
std::vector<uint8_t>(
assertion.Credential.pbId,
assertion.Credential.pbId + assertion.Credential.cbId));
if (assertion.cbUserId > 0) {
response.user_entity = PublicKeyCredentialUserEntity(std::vector<uint8_t>(
assertion.pbUserId, assertion.pbUserId + assertion.cbUserId));
}
return response;
}
uint32_t ToWinUserVerificationRequirement(
UserVerificationRequirement user_verification_requirement) {
switch (user_verification_requirement) {
case UserVerificationRequirement::kRequired:
return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED;
case UserVerificationRequirement::kPreferred:
return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED;
case UserVerificationRequirement::kDiscouraged:
return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED;
}
NOTREACHED();
return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED;
}
uint32_t ToWinAuthenticatorAttachment(
AuthenticatorAttachment authenticator_attachment) {
switch (authenticator_attachment) {
case AuthenticatorAttachment::kAny:
return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;
case AuthenticatorAttachment::kPlatform:
return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM;
case AuthenticatorAttachment::kCrossPlatform:
return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM;
}
NOTREACHED();
return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;
}
static uint32_t ToWinTransportsMask(
const base::flat_set<FidoTransportProtocol>& transports) {
uint32_t result = 0;
for (const FidoTransportProtocol transport : transports) {
switch (transport) {
case FidoTransportProtocol::kUsbHumanInterfaceDevice:
result |= WEBAUTHN_CTAP_TRANSPORT_USB;
break;
case FidoTransportProtocol::kNearFieldCommunication:
result |= WEBAUTHN_CTAP_TRANSPORT_NFC;
break;
case FidoTransportProtocol::kBluetoothLowEnergy:
result |= WEBAUTHN_CTAP_TRANSPORT_BLE;
break;
case FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy:
case FidoTransportProtocol::kAndroidAccessory:
// caBLE is unsupported by the Windows API.
break;
case FidoTransportProtocol::kInternal:
result |= WEBAUTHN_CTAP_TRANSPORT_INTERNAL;
break;
}
}
return result;
}
std::vector<WEBAUTHN_CREDENTIAL> ToWinCredentialVector(
const std::vector<PublicKeyCredentialDescriptor>* credentials) {
std::vector<WEBAUTHN_CREDENTIAL> result;
for (const auto& credential : *credentials) {
if (credential.credential_type() != CredentialType::kPublicKey) {
continue;
}
result.push_back(WEBAUTHN_CREDENTIAL{
WEBAUTHN_CREDENTIAL_CURRENT_VERSION,
credential.id().size(),
const_cast<unsigned char*>(credential.id().data()),
WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY,
});
}
return result;
}
std::vector<WEBAUTHN_CREDENTIAL_EX> ToWinCredentialExVector(
const std::vector<PublicKeyCredentialDescriptor>* credentials) {
std::vector<WEBAUTHN_CREDENTIAL_EX> result;
for (const auto& credential : *credentials) {
if (credential.credential_type() != CredentialType::kPublicKey) {
continue;
}
result.push_back(WEBAUTHN_CREDENTIAL_EX{
WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION, credential.id().size(),
const_cast<unsigned char*>(credential.id().data()),
WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY,
ToWinTransportsMask(credential.transports())});
}
return result;
}
CtapDeviceResponseCode WinErrorNameToCtapDeviceResponseCode(
const std::u16string& error_name) {
// See WebAuthNGetErrorName in <webauthn.h> for these string literals.
//
// Note that the set of errors that browser are allowed to return in a
// response to a WebAuthn call is much narrower than what the Windows
// WebAuthn API returns. According to the WebAuthn spec, the only
// permissible errors are "InvalidStateError" (aka CREDENTIAL_EXCLUDED in
// Chromium code) and "NotAllowedError". Hence, we can collapse the set of
// Windows errors to a smaller set of CtapDeviceResponseCodes.
static base::flat_map<std::u16string, CtapDeviceResponseCode>
kResponseCodeMap({
{u"Success", CtapDeviceResponseCode::kSuccess},
{u"InvalidStateError",
CtapDeviceResponseCode::kCtap2ErrCredentialExcluded},
{u"ConstraintError",
CtapDeviceResponseCode::kCtap2ErrOperationDenied},
{u"NotSupportedError",
CtapDeviceResponseCode::kCtap2ErrOperationDenied},
{u"NotAllowedError",
CtapDeviceResponseCode::kCtap2ErrOperationDenied},
{u"UnknownError", CtapDeviceResponseCode::kCtap2ErrOperationDenied},
});
if (!base::Contains(kResponseCodeMap, error_name)) {
FIDO_LOG(ERROR) << "Unexpected error name: " << error_name;
return CtapDeviceResponseCode::kCtap2ErrOperationDenied;
}
return kResponseCodeMap[error_name];
}
COMPONENT_EXPORT(DEVICE_FIDO)
MakeCredentialStatus WinCtapDeviceResponseCodeToMakeCredentialStatus(
CtapDeviceResponseCode status) {
switch (status) {
case CtapDeviceResponseCode::kSuccess:
return MakeCredentialStatus::kSuccess;
case CtapDeviceResponseCode::kCtap2ErrCredentialExcluded:
return MakeCredentialStatus::kWinInvalidStateError;
case CtapDeviceResponseCode::kCtap2ErrOperationDenied:
return MakeCredentialStatus::kWinNotAllowedError;
default:
NOTREACHED() << "Must only be called with a status returned from "
"WinErrorNameToCtapDeviceResponseCode().";
FIDO_LOG(ERROR) << "Unexpected CtapDeviceResponseCode: "
<< static_cast<int>(status);
return MakeCredentialStatus::kWinNotAllowedError;
}
}
COMPONENT_EXPORT(DEVICE_FIDO)
GetAssertionStatus WinCtapDeviceResponseCodeToGetAssertionStatus(
CtapDeviceResponseCode status) {
switch (status) {
case CtapDeviceResponseCode::kSuccess:
return GetAssertionStatus::kSuccess;
case CtapDeviceResponseCode::kCtap2ErrOperationDenied:
return GetAssertionStatus::kWinNotAllowedError;
case CtapDeviceResponseCode::kCtap2ErrCredentialExcluded:
// The API should never return InvalidStateError for GetAssertion.
FIDO_LOG(ERROR) << "Unexpected CtapDeviceResponseCode: "
<< static_cast<int>(status);
return GetAssertionStatus::kWinNotAllowedError;
default:
NOTREACHED() << "Must only be called with a status returned from "
"WinErrorNameToCtapDeviceResponseCode().";
FIDO_LOG(ERROR) << "Unexpected CtapDeviceResponseCode: "
<< static_cast<int>(status);
return GetAssertionStatus::kWinNotAllowedError;
}
}
uint32_t ToWinAttestationConveyancePreference(
const AttestationConveyancePreference& value) {
switch (value) {
case AttestationConveyancePreference::kNone:
return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE;
case AttestationConveyancePreference::kIndirect:
return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT;
case AttestationConveyancePreference::kDirect:
return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT;
case AttestationConveyancePreference::kEnterpriseIfRPListedOnAuthenticator:
case AttestationConveyancePreference::kEnterpriseApprovedByBrowser:
// Windows does not support enterprise attestation.
return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE;
}
NOTREACHED();
return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE;
}
} // namespace device