blob: a1fb37e19976b09238d5ff87da88e1cd8b3d3d82 [file] [log] [blame]
// Copyright 2020 The Chromium OS 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 "cryptohome/fido/attested_credential_data.h"
#include <algorithm>
#include <string>
#include <utility>
#include <base/numerics/safe_math.h>
#include <base/strings/string_number_conversions.h>
#include <chromeos/cbor/reader.h>
#include "cryptohome/fido/ec_public_key.h"
#include "cryptohome/fido/fido_constants.h"
#include "cryptohome/fido/fido_parsing_utils.h"
#include "cryptohome/fido/public_key.h"
#include "cryptohome/fido/utils.h"
namespace cryptohome {
namespace fido_device {
// static
base::Optional<std::pair<AttestedCredentialData, base::span<const uint8_t>>>
AttestedCredentialData::ConsumeFromCtapResponse(
base::span<const uint8_t> buffer) {
if (buffer.size() < kAaguidLength)
return base::nullopt;
auto aaguid = buffer.first<kAaguidLength>();
buffer = buffer.subspan(kAaguidLength);
if (buffer.size() < kCredentialIdLengthLength)
return base::nullopt;
auto credential_id_length_span = buffer.first<kCredentialIdLengthLength>();
const size_t credential_id_length =
(base::strict_cast<size_t>(credential_id_length_span[0]) << 8) |
base::strict_cast<size_t>(credential_id_length_span[1]);
buffer = buffer.subspan(kCredentialIdLengthLength);
if (buffer.size() < credential_id_length)
return base::nullopt;
auto credential_id = buffer.first(credential_id_length);
buffer = buffer.subspan(credential_id_length);
// The public key is a CBOR map and is thus variable length. Therefore the
// CBOR parser needs to be invoked to find its length, even though the result
// is discarded.
size_t bytes_read;
if (!cbor::Reader::Read(buffer, &bytes_read)) {
return base::nullopt;
}
// Only EC Public key is supported for now.
auto credential_public_key =
ECPublicKey::ParseECPublicKey(buffer.first(bytes_read));
if (!credential_public_key)
return base::nullopt;
buffer = buffer.subspan(bytes_read);
return std::make_pair(
AttestedCredentialData(aaguid, credential_id_length_span,
fido_parsing_utils::Materialize(credential_id),
std::move(credential_public_key)),
buffer);
}
// static
base::Optional<AttestedCredentialData>
AttestedCredentialData::CreateFromU2fRegisterResponse(
base::span<const uint8_t> u2f_data, std::unique_ptr<PublicKey> public_key) {
// TODO(crbug/799075): Introduce a CredentialID class to do this extraction.
// Extract the length of the credential (i.e. of the U2FResponse key
// handle). Length is big endian.
std::vector<uint8_t> extracted_length =
fido_parsing_utils::Extract(u2f_data, kU2fKeyHandleLengthOffset, 1);
if (extracted_length.empty()) {
return base::nullopt;
}
// For U2F register request, device AAGUID is set to zeros.
std::array<uint8_t, kAaguidLength> aaguid = {};
// Note that U2F responses only use one byte for length.
std::array<uint8_t, kCredentialIdLengthLength> credential_id_length = {
0, extracted_length[0]};
// Extract the credential id (i.e. key handle).
std::vector<uint8_t> credential_id = fido_parsing_utils::Extract(
u2f_data, kU2fKeyHandleOffset,
base::strict_cast<size_t>(credential_id_length[1]));
if (credential_id.empty()) {
return base::nullopt;
}
return AttestedCredentialData(aaguid, credential_id_length,
std::move(credential_id),
std::move(public_key));
}
AttestedCredentialData::AttestedCredentialData(AttestedCredentialData&& other) =
default;
AttestedCredentialData& AttestedCredentialData::operator=(
AttestedCredentialData&& other) = default;
AttestedCredentialData::~AttestedCredentialData() = default;
bool AttestedCredentialData::IsAaguidZero() const {
return std::all_of(aaguid_.begin(), aaguid_.end(),
[](uint8_t v) { return v == 0; });
}
void AttestedCredentialData::DeleteAaguid() {
std::fill(aaguid_.begin(), aaguid_.end(), 0);
}
std::vector<uint8_t> AttestedCredentialData::SerializeAsBytes() const {
std::vector<uint8_t> attestation_data;
fido_parsing_utils::Append(&attestation_data, aaguid_);
fido_parsing_utils::Append(&attestation_data, credential_id_length_);
fido_parsing_utils::Append(&attestation_data, credential_id_);
fido_parsing_utils::Append(&attestation_data, public_key_->EncodeAsCOSEKey());
return attestation_data;
}
const PublicKey* AttestedCredentialData::GetPublicKey() const {
return public_key_.get();
}
uint16_t AttestedCredentialData::GetCredentialIdLength() {
uint16_t length;
fido::ReadBigEndian<uint16_t>(
reinterpret_cast<const char*>(&credential_id_length_[0]), &length);
return length;
}
std::string AttestedCredentialData::ToString() {
std::stringstream ss;
ss << "attested data: {"
<< "aaguid: " << base::HexEncode(aaguid_.data(), aaguid_.size()) << ", "
<< "credential length: " << GetCredentialIdLength() << ", "
<< "credential id: "
<< base::HexEncode(credential_id_.data(), credential_id_.size()) << ", "
<< "credential: " << public_key_->ToString();
return ss.str();
}
AttestedCredentialData::AttestedCredentialData(
base::span<const uint8_t, kAaguidLength> aaguid,
base::span<const uint8_t, kCredentialIdLengthLength> credential_id_length,
std::vector<uint8_t> credential_id,
std::unique_ptr<PublicKey> public_key)
: aaguid_(fido_parsing_utils::Materialize(aaguid)),
credential_id_length_(
fido_parsing_utils::Materialize(credential_id_length)),
credential_id_(std::move(credential_id)),
public_key_(std::move(public_key)) {}
} // namespace fido_device
} // namespace cryptohome