blob: 9d47fb8fa0514a3bd83bb4a8f8db03fc75a676bb [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/fido/virtual_fido_device.h"
#include <algorithm>
#include <tuple>
#include <utility>
#include "base/bind.h"
#include "base/containers/cxx20_erase.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "components/cbor/values.h"
#include "components/cbor/writer.h"
#include "crypto/ec_private_key.h"
#include "crypto/ec_signature_creator.h"
#include "crypto/openssl_util.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/p256_public_key.h"
#include "device/fido/public_key.h"
#include "third_party/boringssl/src/include/openssl/bn.h"
#include "third_party/boringssl/src/include/openssl/bytestring.h"
#include "third_party/boringssl/src/include/openssl/ec.h"
#include "third_party/boringssl/src/include/openssl/ec_key.h"
#include "third_party/boringssl/src/include/openssl/evp.h"
#include "third_party/boringssl/src/include/openssl/mem.h"
#include "third_party/boringssl/src/include/openssl/rsa.h"
namespace device {
namespace {
// The example attestation private key from the U2F spec at
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html#registration-example
//
// PKCS.8 encoded without encryption.
constexpr uint8_t kAttestationKey[]{
0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20,
0xf3, 0xfc, 0xcc, 0x0d, 0x00, 0xd8, 0x03, 0x19, 0x54, 0xf9, 0x08, 0x64,
0xd4, 0x3c, 0x24, 0x7f, 0x4b, 0xf5, 0xf0, 0x66, 0x5c, 0x6b, 0x50, 0xcc,
0x17, 0x74, 0x9a, 0x27, 0xd1, 0xcf, 0x76, 0x64, 0xa1, 0x44, 0x03, 0x42,
0x00, 0x04, 0x8d, 0x61, 0x7e, 0x65, 0xc9, 0x50, 0x8e, 0x64, 0xbc, 0xc5,
0x67, 0x3a, 0xc8, 0x2a, 0x67, 0x99, 0xda, 0x3c, 0x14, 0x46, 0x68, 0x2c,
0x25, 0x8c, 0x46, 0x3f, 0xff, 0xdf, 0x58, 0xdf, 0xd2, 0xfa, 0x3e, 0x6c,
0x37, 0x8b, 0x53, 0xd7, 0x95, 0xc4, 0xa4, 0xdf, 0xfb, 0x41, 0x99, 0xed,
0xd7, 0x86, 0x2f, 0x23, 0xab, 0xaf, 0x02, 0x03, 0xb4, 0xb8, 0x91, 0x1b,
0xa0, 0x56, 0x99, 0x94, 0xe1, 0x01};
// The default large-blob array. This is an empty CBOR array (0x80) followed by
// LEFT(SHA-256(h'80'), 16).
constexpr std::array<uint8_t, 17> kDefaultLargeBlobArray = {
0x80, 0x76, 0xbe, 0x8b, 0x52, 0x8d, 0x00, 0x75, 0xf7,
0xaa, 0xe9, 0x8d, 0x6f, 0xa5, 0x7a, 0x6d, 0x3c};
// CBBFunctionToVector converts a BoringSSL function that writes to a CBB to one
// that returns a std::vector. Invoke for a function, f, with:
// CBBFunctionToVector<decltype(&f), f>(args, to, f);
template <typename F, F function, typename... Args>
std::vector<uint8_t> CBBFunctionToVector(Args&&... args) {
uint8_t* der = nullptr;
size_t der_len = 0;
bssl::ScopedCBB cbb;
CHECK(CBB_init(cbb.get(), 0) &&
function(cbb.get(), std::forward<Args>(args)...) &&
CBB_finish(cbb.get(), &der, &der_len));
std::vector<uint8_t> ret(der, der + der_len);
OPENSSL_free(der);
return ret;
}
// EVPBackedPrivateKey is an abstract class that implements some of the
// |PrivateKey| interface using BoringSSL's EVP layer.
class EVPBackedPrivateKey : public VirtualFidoDevice::PrivateKey {
protected:
EVPBackedPrivateKey(int type, int (*const config_key_gen)(EVP_PKEY_CTX*)) {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
bssl::UniquePtr<EVP_PKEY_CTX> gen_ctx(
EVP_PKEY_CTX_new_id(type, /*e=*/nullptr));
EVP_PKEY* pkey_ptr = nullptr;
CHECK(EVP_PKEY_keygen_init(gen_ctx.get()) &&
config_key_gen(gen_ctx.get()) &&
EVP_PKEY_keygen(gen_ctx.get(), &pkey_ptr));
pkey_.reset(pkey_ptr);
}
explicit EVPBackedPrivateKey(bssl::UniquePtr<EVP_PKEY> pkey)
: pkey_(std::move(pkey)) {}
public:
std::vector<uint8_t> Sign(base::span<const uint8_t> msg) override {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
bssl::ScopedEVP_MD_CTX md_ctx;
std::vector<uint8_t> ret;
ret.resize(EVP_PKEY_size(pkey_.get()));
size_t sig_len = ret.size();
// Ed25519 does not separate out the hash function as an independent
// variable so it must be nullptr in that case.
const EVP_MD* digest =
EVP_PKEY_id(pkey_.get()) == EVP_PKEY_ED25519 ? nullptr : EVP_sha256();
CHECK(EVP_DigestSignInit(md_ctx.get(), /*pctx=*/nullptr, digest,
/*e=*/nullptr, pkey_.get()) &&
EVP_DigestSign(md_ctx.get(), ret.data(), &sig_len, msg.data(),
msg.size()) &&
sig_len <= ret.size());
ret.resize(sig_len);
return ret;
}
std::vector<uint8_t> GetPKCS8PrivateKey() const override {
return CBBFunctionToVector<decltype(&EVP_marshal_private_key),
EVP_marshal_private_key>(pkey_.get());
}
protected:
bssl::UniquePtr<EVP_PKEY> pkey_;
};
class P256PrivateKey : public EVPBackedPrivateKey {
public:
P256PrivateKey() : EVPBackedPrivateKey(EVP_PKEY_EC, ConfigureKeyGen) {}
explicit P256PrivateKey(bssl::UniquePtr<EVP_PKEY> pkey)
: EVPBackedPrivateKey(std::move(pkey)) {
CHECK_EQ(NID_X9_62_prime256v1, EC_GROUP_get_curve_name(EC_KEY_get0_group(
EVP_PKEY_get0_EC_KEY(pkey_.get()))));
}
std::vector<uint8_t> GetX962PublicKey() const override {
const EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(pkey_.get());
return CBBFunctionToVector<decltype(&EC_POINT_point2cbb),
EC_POINT_point2cbb>(
EC_KEY_get0_group(ec_key), EC_KEY_get0_public_key(ec_key),
POINT_CONVERSION_UNCOMPRESSED, /*ctx=*/nullptr);
}
std::unique_ptr<PublicKey> GetPublicKey() const override {
return P256PublicKey::ParseX962Uncompressed(
static_cast<int32_t>(CoseAlgorithmIdentifier::kEs256),
GetX962PublicKey());
}
private:
static int ConfigureKeyGen(EVP_PKEY_CTX* ctx) {
return EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, NID_X9_62_prime256v1);
}
};
class RSAPrivateKey : public EVPBackedPrivateKey {
public:
RSAPrivateKey() : EVPBackedPrivateKey(EVP_PKEY_RSA, ConfigureKeyGen) {}
explicit RSAPrivateKey(bssl::UniquePtr<EVP_PKEY> pkey)
: EVPBackedPrivateKey(std::move(pkey)) {}
std::unique_ptr<PublicKey> GetPublicKey() const override {
const RSA* rsa = EVP_PKEY_get0_RSA(pkey_.get());
const BIGNUM* n = RSA_get0_n(rsa);
const BIGNUM* e = RSA_get0_e(rsa);
std::vector<uint8_t> modulus(BN_num_bytes(n));
BN_bn2bin(n, modulus.data());
std::vector<uint8_t> public_exponent(BN_num_bytes(e));
BN_bn2bin(e, public_exponent.data());
cbor::Value::MapValue map;
map.emplace(static_cast<int64_t>(CoseKeyKey::kAlg),
static_cast<int64_t>(CoseAlgorithmIdentifier::kRs256));
map.emplace(static_cast<int64_t>(CoseKeyKey::kKty),
static_cast<int64_t>(CoseKeyTypes::kRSA));
map.emplace(static_cast<int64_t>(CoseKeyKey::kRSAModulus),
std::move(modulus));
map.emplace(static_cast<int64_t>(CoseKeyKey::kRSAPublicExponent),
std::move(public_exponent));
absl::optional<std::vector<uint8_t>> cbor_bytes(
cbor::Writer::Write(cbor::Value(std::move(map))));
std::vector<uint8_t> der_bytes(
CBBFunctionToVector<decltype(&EVP_marshal_public_key),
EVP_marshal_public_key>(pkey_.get()));
return std::make_unique<PublicKey>(
static_cast<int32_t>(CoseAlgorithmIdentifier::kRs256), *cbor_bytes,
std::move(der_bytes));
}
private:
static int ConfigureKeyGen(EVP_PKEY_CTX* ctx) {
return EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048);
}
};
class Ed25519PrivateKey : public EVPBackedPrivateKey {
public:
Ed25519PrivateKey()
: EVPBackedPrivateKey(EVP_PKEY_ED25519, ConfigureKeyGen) {}
explicit Ed25519PrivateKey(bssl::UniquePtr<EVP_PKEY> pkey)
: EVPBackedPrivateKey(std::move(pkey)) {}
std::unique_ptr<PublicKey> GetPublicKey() const override {
uint8_t public_key[32];
size_t public_key_len = sizeof(public_key);
CHECK(
EVP_PKEY_get_raw_public_key(pkey_.get(), public_key, &public_key_len) &&
public_key_len == sizeof(public_key));
cbor::Value::MapValue map;
map.emplace(static_cast<int64_t>(CoseKeyKey::kAlg),
static_cast<int64_t>(CoseAlgorithmIdentifier::kEdDSA));
map.emplace(static_cast<int64_t>(CoseKeyKey::kKty),
static_cast<int64_t>(CoseKeyTypes::kOKP));
map.emplace(static_cast<int64_t>(CoseKeyKey::kEllipticCurve),
static_cast<int64_t>(CoseCurves::kEd25519));
map.emplace(static_cast<int64_t>(CoseKeyKey::kEllipticX),
base::span<const uint8_t>(public_key, sizeof(public_key)));
absl::optional<std::vector<uint8_t>> cbor_bytes(
cbor::Writer::Write(cbor::Value(std::move(map))));
std::vector<uint8_t> der_bytes(
CBBFunctionToVector<decltype(&EVP_marshal_public_key),
EVP_marshal_public_key>(pkey_.get()));
return std::make_unique<PublicKey>(
static_cast<int32_t>(CoseAlgorithmIdentifier::kRs256), *cbor_bytes,
std::move(der_bytes));
}
private:
static int ConfigureKeyGen(EVP_PKEY_CTX* ctx) { return 1; }
};
class InvalidForTestingPrivateKey : public VirtualFidoDevice::PrivateKey {
public:
InvalidForTestingPrivateKey() = default;
std::vector<uint8_t> Sign(base::span<const uint8_t> message) override {
return {'s', 'i', 'g'};
}
std::vector<uint8_t> GetPKCS8PrivateKey() const override {
CHECK(false);
return {};
}
std::unique_ptr<PublicKey> GetPublicKey() const override {
cbor::Value::MapValue map;
map.emplace(
static_cast<int64_t>(CoseKeyKey::kAlg),
static_cast<int64_t>(CoseAlgorithmIdentifier::kInvalidForTesting));
map.emplace(static_cast<int64_t>(CoseKeyKey::kKty),
static_cast<int64_t>(CoseKeyTypes::kInvalidForTesting));
absl::optional<std::vector<uint8_t>> cbor_bytes(
cbor::Writer::Write(cbor::Value(std::move(map))));
return std::make_unique<PublicKey>(
static_cast<int32_t>(CoseAlgorithmIdentifier::kInvalidForTesting),
*cbor_bytes, absl::nullopt);
}
};
} // namespace
// VirtualFidoDevice::PrivateKey ----------------------------------------------
VirtualFidoDevice::PrivateKey::~PrivateKey() = default;
std::vector<uint8_t> VirtualFidoDevice::PrivateKey::GetX962PublicKey() const {
// Not generally possible to encode in X9.62 format. Elliptic-specific
// subclasses can override.
CHECK(false);
return std::vector<uint8_t>();
}
// static
absl::optional<std::unique_ptr<VirtualFidoDevice::PrivateKey>>
VirtualFidoDevice::PrivateKey::FromPKCS8(
base::span<const uint8_t> pkcs8_private_key) {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
CBS cbs;
CBS_init(&cbs, pkcs8_private_key.data(), pkcs8_private_key.size());
bssl::UniquePtr<EVP_PKEY> pkey(EVP_parse_private_key(&cbs));
if (!pkey || CBS_len(&cbs) != 0) {
return absl::nullopt;
}
switch (EVP_PKEY_id(pkey.get())) {
case EVP_PKEY_EC:
if (EC_GROUP_get_curve_name(EC_KEY_get0_group(
EVP_PKEY_get0_EC_KEY(pkey.get()))) != NID_X9_62_prime256v1) {
return absl::nullopt;
}
return std::unique_ptr<PrivateKey>(new P256PrivateKey(std::move(pkey)));
case EVP_PKEY_RSA:
return std::unique_ptr<PrivateKey>(new RSAPrivateKey(std::move(pkey)));
case EVP_PKEY_ED25519:
return std::unique_ptr<PrivateKey>(
new Ed25519PrivateKey(std::move(pkey)));
default:
return absl::nullopt;
}
}
// static
std::unique_ptr<VirtualFidoDevice::PrivateKey>
VirtualFidoDevice::PrivateKey::FreshP256Key() {
return std::make_unique<P256PrivateKey>();
}
// static
std::unique_ptr<VirtualFidoDevice::PrivateKey>
VirtualFidoDevice::PrivateKey::FreshRSAKey() {
return std::make_unique<RSAPrivateKey>();
}
// static
std::unique_ptr<VirtualFidoDevice::PrivateKey>
VirtualFidoDevice::PrivateKey::FreshEd25519Key() {
return std::make_unique<Ed25519PrivateKey>();
}
// static
std::unique_ptr<VirtualFidoDevice::PrivateKey>
VirtualFidoDevice::PrivateKey::FreshInvalidForTestingKey() {
return std::make_unique<InvalidForTestingPrivateKey>();
}
// VirtualFidoDevice::RegistrationData ----------------------------------------
VirtualFidoDevice::RegistrationData::RegistrationData() = default;
VirtualFidoDevice::RegistrationData::RegistrationData(const std::string& rp_id)
: application_parameter(fido_parsing_utils::CreateSHA256Hash(rp_id)) {}
VirtualFidoDevice::RegistrationData::RegistrationData(
std::unique_ptr<PrivateKey> private_key,
base::span<const uint8_t, kRpIdHashLength> application_parameter,
uint32_t counter)
: private_key(std::move(private_key)),
application_parameter(
fido_parsing_utils::Materialize(application_parameter)),
counter(counter) {}
VirtualFidoDevice::RegistrationData::RegistrationData(RegistrationData&& data) =
default;
VirtualFidoDevice::RegistrationData::~RegistrationData() = default;
VirtualFidoDevice::RegistrationData&
VirtualFidoDevice::RegistrationData::operator=(RegistrationData&& other) =
default;
// VirtualFidoDevice::State ---------------------------------------------------
VirtualFidoDevice::State::State()
: attestation_cert_common_name("Batch Certificate"),
individual_attestation_cert_common_name("Individual Certificate") {
large_blob.assign(kDefaultLargeBlobArray.begin(),
kDefaultLargeBlobArray.end());
}
VirtualFidoDevice::State::~State() = default;
bool VirtualFidoDevice::State::InjectRegistration(
base::span<const uint8_t> credential_id,
RegistrationData registration) {
bool was_inserted;
std::tie(std::ignore, was_inserted) = registrations.emplace(
fido_parsing_utils::Materialize(credential_id), std::move(registration));
return was_inserted;
}
bool VirtualFidoDevice::State::InjectRegistration(
base::span<const uint8_t> credential_id,
const std::string& relying_party_id) {
return InjectRegistration(credential_id, RegistrationData(relying_party_id));
}
bool VirtualFidoDevice::State::InjectResidentKey(
base::span<const uint8_t> credential_id,
device::PublicKeyCredentialRpEntity rp,
device::PublicKeyCredentialUserEntity user,
int32_t signature_counter,
std::unique_ptr<PrivateKey> private_key) {
auto application_parameter = fido_parsing_utils::CreateSHA256Hash(rp.id);
// Cannot create a duplicate credential for the same (RP ID, user ID) pair.
for (const auto& registration : registrations) {
if (registration.second.is_resident &&
application_parameter == registration.second.application_parameter &&
user.id == registration.second.user->id) {
return false;
}
}
RegistrationData registration(std::move(private_key),
std::move(application_parameter),
signature_counter);
registration.is_resident = true;
registration.rp = std::move(rp);
registration.user = std::move(user);
bool was_inserted;
std::tie(std::ignore, was_inserted) = registrations.emplace(
fido_parsing_utils::Materialize(credential_id), std::move(registration));
return was_inserted;
}
bool VirtualFidoDevice::State::InjectResidentKey(
base::span<const uint8_t> credential_id,
device::PublicKeyCredentialRpEntity rp,
device::PublicKeyCredentialUserEntity user) {
return InjectResidentKey(std::move(credential_id), std::move(rp),
std::move(user), /*signature_counter=*/0,
PrivateKey::FreshP256Key());
}
bool VirtualFidoDevice::State::InjectResidentKey(
base::span<const uint8_t> credential_id,
const std::string& relying_party_id,
base::span<const uint8_t> user_id,
absl::optional<std::string> user_name,
absl::optional<std::string> user_display_name) {
return InjectResidentKey(
credential_id, PublicKeyCredentialRpEntity(std::move(relying_party_id)),
PublicKeyCredentialUserEntity(fido_parsing_utils::Materialize(user_id),
std::move(user_name),
std::move(user_display_name)));
}
absl::optional<LargeBlob> VirtualFidoDevice::State::GetLargeBlob(
const RegistrationData& credential) {
if (!credential.large_blob_key) {
return absl::nullopt;
}
LargeBlobArrayReader reader;
reader.Append(large_blob);
absl::optional<std::vector<LargeBlobData>> large_blob_array =
reader.Materialize();
if (!large_blob_array) {
return absl::nullopt;
}
for (const auto& data : *large_blob_array) {
absl::optional<LargeBlob> blob = data.Decrypt(*credential.large_blob_key);
if (blob) {
return blob;
}
}
return absl::nullopt;
}
void VirtualFidoDevice::State::InjectLargeBlob(RegistrationData* credential,
LargeBlob blob) {
LargeBlobArrayReader reader;
reader.Append(large_blob);
std::vector<LargeBlobData> large_blob_array =
reader.Materialize().value_or(std::vector<LargeBlobData>());
if (credential->large_blob_key) {
base::EraseIf(large_blob_array, [&credential](const LargeBlobData& blob) {
return blob.Decrypt(*credential->large_blob_key).has_value();
});
} else {
credential->large_blob_key.emplace();
base::RandBytes(credential->large_blob_key->data(),
credential->large_blob_key->size());
}
large_blob_array.insert(
large_blob_array.end(),
LargeBlobData(*credential->large_blob_key, std::move(blob)));
LargeBlobArrayWriter writer(large_blob_array);
large_blob = writer.Pop(writer.size()).bytes;
}
void VirtualFidoDevice::State::ClearLargeBlobs() {
large_blob.assign(kDefaultLargeBlobArray.begin(),
kDefaultLargeBlobArray.end());
}
// VirtualFidoDevice ----------------------------------------------------------
VirtualFidoDevice::VirtualFidoDevice() = default;
VirtualFidoDevice::VirtualFidoDevice(scoped_refptr<State> state)
: state_(std::move(state)) {}
VirtualFidoDevice::~VirtualFidoDevice() = default;
std::string VirtualFidoDevice::GetId() const {
return state_->device_id_override.value_or(id_);
}
// static
std::vector<uint8_t> VirtualFidoDevice::GetAttestationKey() {
return fido_parsing_utils::Materialize(kAttestationKey);
}
bool VirtualFidoDevice::Sign(crypto::ECPrivateKey* private_key,
base::span<const uint8_t> sign_buffer,
std::vector<uint8_t>* signature) {
auto signer = crypto::ECSignatureCreator::Create(private_key);
return signer->Sign(sign_buffer, signature);
}
absl::optional<std::vector<uint8_t>>
VirtualFidoDevice::GenerateAttestationCertificate(
bool individual_attestation_requested,
bool include_transports) const {
std::unique_ptr<crypto::ECPrivateKey> attestation_private_key =
crypto::ECPrivateKey::CreateFromPrivateKeyInfo(GetAttestationKey());
constexpr uint32_t kAttestationCertSerialNumber = 1;
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-authenticator-transports-extension-v1.2-ps-20170411.html#fido-u2f-certificate-transports-extension
static constexpr uint8_t kTransportTypesOID[] = {
0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xe5, 0x1c, 0x02, 0x01, 0x01};
uint8_t transport_bit;
switch (DeviceTransport()) {
case FidoTransportProtocol::kBluetoothLowEnergy:
case FidoTransportProtocol::kHybrid:
transport_bit = 1;
break;
case FidoTransportProtocol::kUsbHumanInterfaceDevice:
transport_bit = 2;
break;
case FidoTransportProtocol::kNearFieldCommunication:
transport_bit = 3;
break;
case FidoTransportProtocol::kInternal:
transport_bit = 4;
break;
case FidoTransportProtocol::kAndroidAccessory:
transport_bit = 1;
break;
}
const uint8_t kTransportTypesContents[] = {
3, // BIT STRING
2, // two bytes long
static_cast<uint8_t>(8 - transport_bit - 1), // trailing bits unused
static_cast<uint8_t>(0b10000000 >> transport_bit),
// transport
};
// https://www.w3.org/TR/webauthn/#sctn-packed-attestation-cert-requirements
// The Basic Constraints extension MUST have the CA component set to false.
// Since that is the default value, DER requires omitting it. Simply include
// an empty sequence.
static constexpr uint8_t kBasicContraintsOID[] = {0x55, 0x1d, 0x13};
static constexpr uint8_t kBasicContraintsContents[] = {
0x30, // SEQUENCE
0x00, // zero bytes long
};
std::vector<net::x509_util::Extension> extensions = {
{kBasicContraintsOID, /*critical=*/true, kBasicContraintsContents},
};
if (include_transports) {
extensions.push_back(
{kTransportTypesOID, /*critical=*/false, kTransportTypesContents});
}
// https://w3c.github.io/webauthn/#sctn-packed-attestation-cert-requirements
// Make the certificate expire about 20 years from now.
base::Time expiry_date = base::Time::Now() + base::Days(365 * 20);
std::string attestation_cert;
if (!net::x509_util::CreateSelfSignedCert(
attestation_private_key->key(), net::x509_util::DIGEST_SHA256,
"C=US, O=Chromium, OU=Authenticator Attestation, CN=" +
(individual_attestation_requested
? state_->individual_attestation_cert_common_name
: state_->attestation_cert_common_name),
kAttestationCertSerialNumber, base::Time::FromTimeT(1500000000),
expiry_date, extensions, &attestation_cert)) {
DVLOG(2) << "Failed to create attestation certificate";
return absl::nullopt;
}
return std::vector<uint8_t>(attestation_cert.begin(), attestation_cert.end());
}
void VirtualFidoDevice::StoreNewKey(
base::span<const uint8_t> key_handle,
VirtualFidoDevice::RegistrationData registration_data) {
// Skip storing the registration if this is a dummy request. This prevents
// dummy credentials to be returned by the GetCredentials method of the
// virtual authenticator API.
if (registration_data.application_parameter == device::kBogusAppParam ||
registration_data.application_parameter ==
fido_parsing_utils::CreateSHA256Hash(kDummyRpID)) {
return;
}
// Store the registration. Because the key handle is the hashed public key we
// just generated, no way this should already be registered.
bool did_insert = false;
std::tie(std::ignore, did_insert) = mutable_state()->registrations.emplace(
fido_parsing_utils::Materialize(key_handle),
std::move(registration_data));
DCHECK(did_insert);
}
VirtualFidoDevice::RegistrationData* VirtualFidoDevice::FindRegistrationData(
base::span<const uint8_t> key_handle,
base::span<const uint8_t, kRpIdHashLength> application_parameter) {
// Check if this is our key_handle and it's for this appId.
auto it = mutable_state()->registrations.find(key_handle);
if (it == mutable_state()->registrations.end())
return nullptr;
if (!std::equal(application_parameter.begin(), application_parameter.end(),
it->second.application_parameter.begin(),
it->second.application_parameter.end())) {
return nullptr;
}
return &it->second;
}
bool VirtualFidoDevice::SimulatePress() {
if (!state_->simulate_press_callback)
return true;
auto weak_this = GetWeakPtr();
bool result = state_->simulate_press_callback.Run(this);
// |this| might have been destroyed at this point - accessing state from the
// object without checking weak_this is dangerous.
return weak_this && result;
}
void VirtualFidoDevice::TryWink(base::OnceClosure cb) {
std::move(cb).Run();
}
FidoTransportProtocol VirtualFidoDevice::DeviceTransport() const {
return state_->transport;
}
// static
std::string VirtualFidoDevice::MakeVirtualFidoDeviceId() {
uint8_t rand_bytes[32];
base::RandBytes(rand_bytes, sizeof(rand_bytes));
return "VirtualFidoDevice-" + base::HexEncode(rand_bytes);
}
} // namespace device