// 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/authenticator_data.h" #include #include "base/strings/string_number_conversions.h" #include "components/cbor/diagnostic_writer.h" #include "components/cbor/reader.h" #include "components/cbor/writer.h" #include "components/device_event_log/device_event_log.h" #include "device/fido/attested_credential_data.h" #include "device/fido/fido_parsing_utils.h" namespace device { namespace { constexpr size_t kAttestedCredentialDataOffset = kRpIdHashLength + kFlagsLength + kSignCounterLength; uint8_t AuthenticatorDataFlags(bool user_present, bool user_verified, bool has_attested_credential_data, bool has_extension_data) { return (user_present ? base::strict_cast( AuthenticatorData::Flag::kTestOfUserPresence) : 0) | (user_verified ? base::strict_cast( AuthenticatorData::Flag::kTestOfUserVerification) : 0) | (has_attested_credential_data ? base::strict_cast( AuthenticatorData::Flag::kAttestation) : 0) | (has_extension_data ? base::strict_cast( AuthenticatorData::Flag::kExtensionDataIncluded) : 0); } } // namespace // static base::Optional AuthenticatorData::DecodeAuthenticatorData( base::span auth_data) { if (auth_data.size() < kAttestedCredentialDataOffset) return base::nullopt; auto application_parameter = auth_data.first(); uint8_t flag_byte = auth_data[kRpIdHashLength]; auto counter = auth_data.subspan(); auth_data = auth_data.subspan(kAttestedCredentialDataOffset); base::Optional attested_credential_data; if (flag_byte & static_cast(Flag::kAttestation)) { auto maybe_result = AttestedCredentialData::ConsumeFromCtapResponse(auth_data); if (!maybe_result) { return base::nullopt; } std::tie(attested_credential_data, auth_data) = std::move(*maybe_result); } base::Optional extensions; if (flag_byte & static_cast(Flag::kExtensionDataIncluded)) { cbor::Reader::DecoderError error; extensions = cbor::Reader::Read(auth_data, &error); if (!extensions) { FIDO_LOG(ERROR) << "CBOR decoding of authenticator data extensions failed (" << cbor::Reader::ErrorCodeToString(error) << ") from " << base::HexEncode(auth_data.data(), auth_data.size()); return base::nullopt; } if (!extensions->is_map()) { FIDO_LOG(ERROR) << "Incorrect CBOR structure of authenticator data extensions: " << cbor::DiagnosticWriter::Write(*extensions); return base::nullopt; } } else if (!auth_data.empty()) { return base::nullopt; } return AuthenticatorData(application_parameter, flag_byte, counter, std::move(attested_credential_data), std::move(extensions)); } AuthenticatorData::AuthenticatorData( base::span rp_id_hash, uint8_t flags, base::span counter, base::Optional data, base::Optional extensions) : application_parameter_(fido_parsing_utils::Materialize(rp_id_hash)), flags_(flags), counter_(fido_parsing_utils::Materialize(counter)), attested_data_(std::move(data)), extensions_(std::move(extensions)) { DCHECK(!extensions_ || extensions_->is_map()); DCHECK_EQ((flags_ & static_cast(Flag::kExtensionDataIncluded)) != 0, !!extensions_); DCHECK_EQ(((flags_ & static_cast(Flag::kAttestation)) != 0), !!attested_data_); } AuthenticatorData::AuthenticatorData( base::span rp_id_hash, bool user_present, bool user_verified, uint32_t sign_counter, base::Optional attested_credential_data, base::Optional extensions) : AuthenticatorData( rp_id_hash, AuthenticatorDataFlags(user_present, user_verified, attested_credential_data.has_value(), extensions.has_value()), std::array{ (sign_counter >> 24) & 0xff, (sign_counter >> 16) & 0xff, (sign_counter >> 8) & 0xff, sign_counter & 0xff}, std::move(attested_credential_data), std::move(extensions)) {} AuthenticatorData::AuthenticatorData(AuthenticatorData&& other) = default; AuthenticatorData& AuthenticatorData::operator=(AuthenticatorData&& other) = default; AuthenticatorData::~AuthenticatorData() = default; void AuthenticatorData::DeleteDeviceAaguid() { if (!attested_data_) return; attested_data_->DeleteAaguid(); } std::vector AuthenticatorData::SerializeToByteArray() const { std::vector authenticator_data; fido_parsing_utils::Append(&authenticator_data, application_parameter_); authenticator_data.insert(authenticator_data.end(), flags_); fido_parsing_utils::Append(&authenticator_data, counter_); if (attested_data_) { // Attestations are returned in registration responses but not in assertion // responses. fido_parsing_utils::Append(&authenticator_data, attested_data_->SerializeAsBytes()); } if (extensions_) { const auto maybe_extensions = cbor::Writer::Write(*extensions_); if (maybe_extensions) { fido_parsing_utils::Append(&authenticator_data, *maybe_extensions); } } return authenticator_data; } std::vector AuthenticatorData::GetCredentialId() const { if (!attested_data_) return std::vector(); return attested_data_->credential_id(); } } // namespace device