// 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 "device/fido/attested_credential_data.h" #include #include #include "base/numerics/safe_math.h" #include "device/fido/opaque_public_key.h" #include "device/fido/public_key.h" #include "device/fido/u2f_parsing_utils.h" namespace device { // static base::Optional AttestedCredentialData::DecodeFromCtapResponse( base::span buffer) { if (buffer.size() < kAaguidLength + kCredentialIdLengthLength) return base::nullopt; std::array aaguid; if (!u2f_parsing_utils::ExtractArray(buffer, 0, &aaguid)) return base::nullopt; std::array credential_id_length_array; if (!u2f_parsing_utils::ExtractArray(buffer, kAaguidLength, &credential_id_length_array)) { return base::nullopt; } static_assert(kCredentialIdLengthLength == 2u, "L must be 2 bytes"); const size_t credential_id_length = (base::strict_cast(credential_id_length_array[0]) << 8) | base::strict_cast(credential_id_length_array[1]); auto credential_id = u2f_parsing_utils::Extract( buffer, kAaguidLength + kCredentialIdLengthLength, credential_id_length); if (credential_id.empty()) return base::nullopt; DCHECK_LE(kAaguidLength + kCredentialIdLengthLength + credential_id_length, buffer.size()); auto credential_public_key_data = std::make_unique(buffer.subspan( kAaguidLength + kCredentialIdLengthLength + credential_id_length)); return AttestedCredentialData( std::move(aaguid), std::move(credential_id_length_array), std::move(credential_id), std::move(credential_public_key_data)); } // static base::Optional AttestedCredentialData::CreateFromU2fRegisterResponse( base::span u2f_data, std::unique_ptr 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 extracted_length = u2f_parsing_utils::Extract( u2f_data, u2f_parsing_utils::kU2fResponseKeyHandleLengthPos, 1); if (extracted_length.empty()) { return base::nullopt; } // For U2F register request, device AAGUID is set to zeros. std::array aaguid; aaguid.fill(0); // Note that U2F responses only use one byte for length. std::array credential_id_length = { 0, extracted_length[0]}; // Extract the credential id (i.e. key handle). std::vector credential_id = u2f_parsing_utils::Extract( u2f_data, u2f_parsing_utils::kU2fResponseKeyHandleStartPos, base::strict_cast(credential_id_length[1])); if (credential_id.empty()) { return base::nullopt; } return AttestedCredentialData( std::move(aaguid), std::move(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; void AttestedCredentialData::DeleteAaguid() { std::fill(aaguid_.begin(), aaguid_.end(), 0); } std::vector AttestedCredentialData::SerializeAsBytes() const { std::vector attestation_data; u2f_parsing_utils::Append(&attestation_data, base::make_span(aaguid_.data(), kAaguidLength)); u2f_parsing_utils::Append( &attestation_data, base::make_span(credential_id_length_.data(), kCredentialIdLengthLength)); u2f_parsing_utils::Append(&attestation_data, credential_id_); u2f_parsing_utils::Append(&attestation_data, public_key_->EncodeAsCOSEKey()); return attestation_data; } AttestedCredentialData::AttestedCredentialData( std::array aaguid, std::array credential_id_length, std::vector credential_id, std::unique_ptr public_key) : aaguid_(std::move(aaguid)), credential_id_length_(std::move(credential_id_length)), credential_id_(std::move(credential_id)), public_key_(std::move(public_key)) {} } // namespace device