| // Copyright 2018 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_get_info_response.h" |
| |
| #include <utility> |
| |
| #include "base/containers/contains.h" |
| #include "base/ranges/algorithm.h" |
| #include "components/cbor/values.h" |
| #include "components/cbor/writer.h" |
| #include "device/fido/fido_parsing_utils.h" |
| |
| namespace device { |
| |
| namespace { |
| |
| template <typename Container> |
| cbor::Value::ArrayValue ToArrayValue(const Container& container) { |
| cbor::Value::ArrayValue value; |
| value.reserve(container.size()); |
| for (const auto& item : container) |
| value.emplace_back(item); |
| return value; |
| } |
| |
| } // namespace |
| |
| AuthenticatorGetInfoResponse::AuthenticatorGetInfoResponse( |
| base::flat_set<ProtocolVersion> in_versions, |
| base::flat_set<Ctap2Version> in_ctap2_versions, |
| base::span<const uint8_t, kAaguidLength> in_aaguid) |
| : versions(std::move(in_versions)), |
| ctap2_versions(std::move(in_ctap2_versions)), |
| aaguid(fido_parsing_utils::Materialize(in_aaguid)) { |
| DCHECK_NE(base::Contains(versions, ProtocolVersion::kCtap2), |
| ctap2_versions.empty()); |
| } |
| |
| AuthenticatorGetInfoResponse::AuthenticatorGetInfoResponse( |
| AuthenticatorGetInfoResponse&& that) = default; |
| |
| AuthenticatorGetInfoResponse& AuthenticatorGetInfoResponse::operator=( |
| AuthenticatorGetInfoResponse&& other) = default; |
| |
| AuthenticatorGetInfoResponse::~AuthenticatorGetInfoResponse() = default; |
| |
| // static |
| std::vector<uint8_t> AuthenticatorGetInfoResponse::EncodeToCBOR( |
| const AuthenticatorGetInfoResponse& response) { |
| cbor::Value::ArrayValue version_array; |
| for (const auto& version : response.versions) { |
| switch (version) { |
| case ProtocolVersion::kCtap2: |
| for (const auto& ctap2_version : response.ctap2_versions) { |
| switch (ctap2_version) { |
| case Ctap2Version::kCtap2_0: |
| version_array.emplace_back(kCtap2Version); |
| break; |
| case Ctap2Version::kCtap2_1: |
| version_array.emplace_back(kCtap2_1Version); |
| break; |
| } |
| } |
| break; |
| case ProtocolVersion::kU2f: |
| version_array.emplace_back(kU2fVersion); |
| break; |
| case ProtocolVersion::kUnknown: |
| NOTREACHED(); |
| } |
| } |
| cbor::Value::MapValue device_info_map; |
| device_info_map.emplace(0x01, std::move(version_array)); |
| |
| if (response.extensions) |
| device_info_map.emplace(0x02, ToArrayValue(*response.extensions)); |
| |
| device_info_map.emplace(0x03, response.aaguid); |
| device_info_map.emplace(0x04, AsCBOR(response.options)); |
| |
| if (response.max_msg_size) { |
| device_info_map.emplace(0x05, |
| base::strict_cast<int64_t>(*response.max_msg_size)); |
| } |
| |
| if (response.pin_protocols) { |
| cbor::Value::ArrayValue pin_protocols; |
| for (const PINUVAuthProtocol p : *response.pin_protocols) { |
| pin_protocols.push_back(cbor::Value(static_cast<int>(p))); |
| } |
| device_info_map.emplace(0x06, std::move(pin_protocols)); |
| } |
| |
| if (response.max_credential_count_in_list) { |
| device_info_map.emplace(0x07, base::strict_cast<int64_t>( |
| *response.max_credential_count_in_list)); |
| } |
| |
| if (response.max_credential_id_length) { |
| device_info_map.emplace( |
| 0x08, base::strict_cast<int64_t>(*response.max_credential_id_length)); |
| } |
| |
| if (response.remaining_discoverable_credentials) { |
| device_info_map.emplace(0x14, |
| base::strict_cast<int64_t>( |
| *response.remaining_discoverable_credentials)); |
| } |
| |
| if (!response.algorithms.empty()) { |
| std::vector<cbor::Value> algorithms_cbor; |
| algorithms_cbor.reserve(response.algorithms.size()); |
| for (const auto& algorithm : response.algorithms) { |
| // Entries are PublicKeyCredentialParameters |
| // https://w3c.github.io/webauthn/#dictdef-publickeycredentialparameters |
| cbor::Value::MapValue entry; |
| entry.emplace("type", "public-key"); |
| entry.emplace("alg", algorithm); |
| algorithms_cbor.emplace_back(cbor::Value(entry)); |
| } |
| device_info_map.emplace(0x0a, std::move(algorithms_cbor)); |
| } |
| |
| if (response.max_serialized_large_blob_array) { |
| device_info_map.emplace( |
| 0x0b, |
| base::strict_cast<int64_t>(*response.max_serialized_large_blob_array)); |
| } |
| |
| if (response.force_pin_change) { |
| device_info_map.emplace(0x0c, cbor::Value(*response.force_pin_change)); |
| } |
| |
| if (response.min_pin_length) { |
| device_info_map.emplace( |
| 0x0d, |
| cbor::Value(base::strict_cast<int64_t>(*response.min_pin_length))); |
| } |
| |
| if (response.max_cred_blob_length) { |
| device_info_map.emplace( |
| 0x0f, base::strict_cast<int64_t>(*response.max_cred_blob_length)); |
| } |
| |
| auto encoded_bytes = |
| cbor::Writer::Write(cbor::Value(std::move(device_info_map))); |
| DCHECK(encoded_bytes); |
| return *encoded_bytes; |
| } |
| |
| bool AuthenticatorGetInfoResponse::SupportsAtLeast( |
| Ctap2Version ctap2_version) const { |
| return base::ranges::any_of(ctap2_versions, |
| [ctap2_version](const Ctap2Version& version) { |
| return version >= ctap2_version; |
| }); |
| } |
| |
| } // namespace device |