blob: 094e442c7f17584f517df50740a07ac376a4589a [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Copyright (C) 2018-2021 Apple Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "config.h"
#include "DeviceRequestConverter.h"
#if ENABLE(WEB_AUTHN)
#include "CBORWriter.h"
#include "PublicKeyCredentialCreationOptions.h"
#include "PublicKeyCredentialRequestOptions.h"
#include "ResidentKeyRequirement.h"
#include <wtf/Vector.h>
namespace fido {
using namespace WebCore;
using namespace cbor;
using UVAvailability = AuthenticatorSupportedOptions::UserVerificationAvailability;
static CBORValue convertRpEntityToCBOR(const PublicKeyCredentialCreationOptions::RpEntity& rpEntity)
{
CBORValue::MapValue rpMap;
rpMap.emplace(CBORValue(kEntityNameMapKey), CBORValue(rpEntity.name));
if (!rpEntity.icon.isEmpty())
rpMap.emplace(CBORValue(kIconUrlMapKey), CBORValue(rpEntity.icon));
if (!rpEntity.id.isEmpty())
rpMap.emplace(CBORValue(kEntityIdMapKey), CBORValue(rpEntity.id));
return CBORValue(WTFMove(rpMap));
}
static CBORValue convertUserEntityToCBOR(const PublicKeyCredentialCreationOptions::UserEntity& userEntity)
{
CBORValue::MapValue userMap;
userMap.emplace(CBORValue(kEntityNameMapKey), CBORValue(userEntity.name));
if (!userEntity.icon.isEmpty())
userMap.emplace(CBORValue(kIconUrlMapKey), CBORValue(userEntity.icon));
userMap.emplace(CBORValue(kEntityIdMapKey), CBORValue(userEntity.id));
userMap.emplace(CBORValue(kDisplayNameMapKey), CBORValue(userEntity.displayName));
return CBORValue(WTFMove(userMap));
}
static CBORValue convertParametersToCBOR(const Vector<PublicKeyCredentialCreationOptions::Parameters>& parameters)
{
auto credentialParamArray = parameters.map([](auto& credential) {
CBORValue::MapValue cborCredentialMap;
cborCredentialMap.emplace(CBORValue(kCredentialTypeMapKey), CBORValue(publicKeyCredentialTypeToString(credential.type)));
cborCredentialMap.emplace(CBORValue(kCredentialAlgorithmMapKey), CBORValue(credential.alg));
return CBORValue { WTFMove(cborCredentialMap) };
});
return CBORValue(WTFMove(credentialParamArray));
}
static CBORValue convertDescriptorToCBOR(const PublicKeyCredentialDescriptor& descriptor)
{
CBORValue::MapValue cborDescriptorMap;
cborDescriptorMap[CBORValue(kCredentialTypeKey)] = CBORValue(publicKeyCredentialTypeToString(descriptor.type));
cborDescriptorMap[CBORValue(kCredentialIdKey)] = CBORValue(descriptor.id);
return CBORValue(WTFMove(cborDescriptorMap));
}
Vector<uint8_t> encodeMakeCredenitalRequestAsCBOR(const Vector<uint8_t>& hash, const PublicKeyCredentialCreationOptions& options, UVAvailability uvCapability, AuthenticatorSupportedOptions::ResidentKeyAvailability residentKeyAvailability, std::optional<PinParameters> pin)
{
CBORValue::MapValue cborMap;
cborMap[CBORValue(1)] = CBORValue(hash);
cborMap[CBORValue(2)] = convertRpEntityToCBOR(options.rp);
cborMap[CBORValue(3)] = convertUserEntityToCBOR(options.user);
cborMap[CBORValue(4)] = convertParametersToCBOR(options.pubKeyCredParams);
if (!options.excludeCredentials.isEmpty()) {
CBORValue::ArrayValue excludeListArray;
for (const auto& descriptor : options.excludeCredentials)
excludeListArray.append(convertDescriptorToCBOR(descriptor));
cborMap[CBORValue(5)] = CBORValue(WTFMove(excludeListArray));
}
CBORValue::MapValue optionMap;
if (options.authenticatorSelection) {
// Resident keys are not supported by default.
if (options.authenticatorSelection->residentKey) {
if (*options.authenticatorSelection->residentKey == ResidentKeyRequirement::Required
|| (*options.authenticatorSelection->residentKey == ResidentKeyRequirement::Preferred && residentKeyAvailability == AuthenticatorSupportedOptions::ResidentKeyAvailability::kSupported))
optionMap[CBORValue(kResidentKeyMapKey)] = CBORValue(true);
} else if (options.authenticatorSelection->requireResidentKey)
optionMap[CBORValue(kResidentKeyMapKey)] = CBORValue(true);
// User verification is not required by default.
bool requireUserVerification = false;
switch (options.authenticatorSelection->userVerification) {
case UserVerificationRequirement::Required:
case UserVerificationRequirement::Preferred:
requireUserVerification = uvCapability == UVAvailability::kSupportedAndConfigured;
break;
case UserVerificationRequirement::Discouraged:
requireUserVerification = false;
}
if (requireUserVerification)
optionMap[CBORValue(kUserVerificationMapKey)] = CBORValue(requireUserVerification);
}
if (!optionMap.empty())
cborMap[CBORValue(7)] = CBORValue(WTFMove(optionMap));
if (pin) {
ASSERT(pin->protocol >= 0);
cborMap[CBORValue(8)] = CBORValue(WTFMove(pin->auth));
cborMap[CBORValue(9)] = CBORValue(pin->protocol);
}
auto serializedParam = CBORWriter::write(CBORValue(WTFMove(cborMap)));
ASSERT(serializedParam);
Vector<uint8_t> cborRequest({ static_cast<uint8_t>(CtapRequestCommand::kAuthenticatorMakeCredential) });
cborRequest.appendVector(*serializedParam);
return cborRequest;
}
Vector<uint8_t> encodeGetAssertionRequestAsCBOR(const Vector<uint8_t>& hash, const PublicKeyCredentialRequestOptions& options, UVAvailability uvCapability, std::optional<PinParameters> pin)
{
CBORValue::MapValue cborMap;
cborMap[CBORValue(1)] = CBORValue(options.rpId);
cborMap[CBORValue(2)] = CBORValue(hash);
if (!options.allowCredentials.isEmpty()) {
CBORValue::ArrayValue allowListArray;
for (const auto& descriptor : options.allowCredentials)
allowListArray.append(convertDescriptorToCBOR(descriptor));
cborMap[CBORValue(3)] = CBORValue(WTFMove(allowListArray));
}
CBORValue::MapValue optionMap;
// User verification is not required by default.
bool requireUserVerification = false;
switch (options.userVerification) {
case UserVerificationRequirement::Required:
case UserVerificationRequirement::Preferred:
requireUserVerification = uvCapability == UVAvailability::kSupportedAndConfigured;
break;
case UserVerificationRequirement::Discouraged:
requireUserVerification = false;
}
if (requireUserVerification)
optionMap[CBORValue(kUserVerificationMapKey)] = CBORValue(requireUserVerification);
optionMap[CBORValue(kUserPresenceMapKey)] = CBORValue(true);
if (!optionMap.empty())
cborMap[CBORValue(5)] = CBORValue(WTFMove(optionMap));
if (pin) {
ASSERT(pin->protocol >= 0);
cborMap[CBORValue(6)] = CBORValue(WTFMove(pin->auth));
cborMap[CBORValue(7)] = CBORValue(pin->protocol);
}
auto serializedParam = CBORWriter::write(CBORValue(WTFMove(cborMap)));
ASSERT(serializedParam);
Vector<uint8_t> cborRequest({ static_cast<uint8_t>(CtapRequestCommand::kAuthenticatorGetAssertion) });
cborRequest.appendVector(*serializedParam);
return cborRequest;
}
Vector<uint8_t> encodeEmptyAuthenticatorRequest(CtapRequestCommand cmd)
{
return { static_cast<uint8_t>(cmd) };
}
} // namespace fido
#endif // ENABLE(WEB_AUTHN)