| // 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/device_response_converter.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/numerics/safe_conversions.h" |
| #include "base/optional.h" |
| #include "base/stl_util.h" |
| #include "components/cbor/cbor_reader.h" |
| #include "components/cbor/cbor_writer.h" |
| #include "device/fido/authenticator_supported_options.h" |
| #include "device/fido/ctap_constants.h" |
| |
| namespace device { |
| |
| using CBOR = cbor::CBORValue; |
| |
| CtapDeviceResponseCode GetResponseCode(const std::vector<uint8_t>& buffer) { |
| if (buffer.empty()) |
| return CtapDeviceResponseCode::kCtap2ErrInvalidCBOR; |
| |
| auto code = static_cast<CtapDeviceResponseCode>(buffer[0]); |
| return base::ContainsValue(GetCtapResponseCodeList(), code) |
| ? code |
| : CtapDeviceResponseCode::kCtap2ErrInvalidCBOR; |
| } |
| |
| // Decodes byte array response from authenticator to CBOR value object and |
| // checks for correct encoding format. Then re-serialize the decoded CBOR value |
| // to byte array in format specified by the WebAuthN spec (i.e the keys for |
| // CBOR map value are converted from unsigned integers to string type.) |
| base::Optional<AuthenticatorMakeCredentialResponse> |
| ReadCTAPMakeCredentialResponse(CtapDeviceResponseCode response_code, |
| const std::vector<uint8_t>& buffer) { |
| base::Optional<CBOR> decoded_response = cbor::CBORReader::Read(buffer); |
| |
| if (!decoded_response || !decoded_response->is_map()) |
| return base::nullopt; |
| |
| const auto& decoded_map = decoded_response->GetMap(); |
| CBOR::MapValue response_map; |
| |
| auto it = decoded_map.find(CBOR(1)); |
| if (it == decoded_map.end() || !it->second.is_string()) |
| return base::nullopt; |
| |
| response_map[CBOR("fmt")] = it->second.Clone(); |
| |
| it = decoded_map.find(CBOR(2)); |
| if (it == decoded_map.end() || !it->second.is_bytestring()) |
| return base::nullopt; |
| |
| response_map[CBOR("authData")] = it->second.Clone(); |
| |
| it = decoded_map.find(CBOR(3)); |
| if (it == decoded_map.end() || !it->second.is_map()) |
| return base::nullopt; |
| |
| response_map[CBOR("attStmt")] = it->second.Clone(); |
| |
| auto attestation_object = |
| cbor::CBORWriter::Write(CBOR(std::move(response_map))); |
| if (!attestation_object) |
| return base::nullopt; |
| |
| return AuthenticatorMakeCredentialResponse(response_code, |
| std::move(*attestation_object)); |
| } |
| |
| base::Optional<AuthenticatorGetAssertionResponse> ReadCTAPGetAssertionResponse( |
| CtapDeviceResponseCode response_code, |
| const std::vector<uint8_t>& buffer) { |
| base::Optional<CBOR> decoded_response = cbor::CBORReader::Read(buffer); |
| |
| if (!decoded_response || !decoded_response->is_map()) |
| return base::nullopt; |
| |
| auto& response_map = decoded_response->GetMap(); |
| |
| auto it = response_map.find(CBOR(4)); |
| if (it == response_map.end() || !it->second.is_map()) |
| return base::nullopt; |
| |
| auto user = PublicKeyCredentialUserEntity::CreateFromCBORValue(it->second); |
| if (!user) |
| return base::nullopt; |
| |
| it = response_map.find(CBOR(2)); |
| if (it == response_map.end() || !it->second.is_bytestring()) |
| return base::nullopt; |
| auto auth_data = it->second.GetBytestring(); |
| |
| it = response_map.find(CBOR(3)); |
| if (it == response_map.end() || !it->second.is_bytestring()) |
| return base::nullopt; |
| auto signature = it->second.GetBytestring(); |
| |
| AuthenticatorGetAssertionResponse response( |
| response_code, std::move(auth_data), std::move(signature), |
| std::move(*user)); |
| |
| it = response_map.find(CBOR(1)); |
| if (it != response_map.end()) { |
| auto descriptor = |
| PublicKeyCredentialDescriptor::CreateFromCBORValue(it->second); |
| if (!descriptor) |
| return base::nullopt; |
| |
| response.SetCredential(std::move(*descriptor)); |
| } |
| |
| it = response_map.find(CBOR(5)); |
| if (it != response_map.end()) { |
| if (!it->second.is_unsigned()) |
| return base::nullopt; |
| |
| response.SetNumCredentials(it->second.GetUnsigned()); |
| } |
| |
| return response; |
| } |
| |
| base::Optional<AuthenticatorGetInfoResponse> ReadCTAPGetInfoResponse( |
| CtapDeviceResponseCode response_code, |
| const std::vector<uint8_t>& buffer) { |
| base::Optional<CBOR> decoded_response = cbor::CBORReader::Read(buffer); |
| |
| if (!decoded_response || !decoded_response->is_map()) |
| return base::nullopt; |
| |
| const auto& response_map = decoded_response->GetMap(); |
| |
| auto it = response_map.find(CBOR(1)); |
| if (it == response_map.end() || !it->second.is_array()) |
| return base::nullopt; |
| |
| std::vector<std::string> versions; |
| for (const auto& version : it->second.GetArray()) { |
| if (!version.is_string()) |
| return base::nullopt; |
| |
| versions.push_back(version.GetString()); |
| } |
| |
| it = response_map.find(CBOR(3)); |
| if (it == response_map.end() || !it->second.is_bytestring()) |
| return base::nullopt; |
| |
| AuthenticatorGetInfoResponse response(response_code, std::move(versions), |
| it->second.GetBytestring()); |
| |
| it = response_map.find(CBOR(2)); |
| if (it != response_map.end()) { |
| if (!it->second.is_array()) |
| return base::nullopt; |
| |
| std::vector<std::string> extensions; |
| for (const auto& extension : it->second.GetArray()) { |
| if (!extension.is_string()) |
| return base::nullopt; |
| |
| extensions.push_back(extension.GetString()); |
| } |
| response.SetExtensions(std::move(extensions)); |
| } |
| |
| it = response_map.find(CBOR(4)); |
| if (it != response_map.end()) { |
| if (!it->second.is_map()) |
| return base::nullopt; |
| |
| const auto& option_map = it->second.GetMap(); |
| AuthenticatorSupportedOptions options; |
| |
| auto option_map_it = option_map.find(CBOR("plat")); |
| if (option_map_it != option_map.end()) { |
| if (!option_map_it->second.is_bool()) |
| return base::nullopt; |
| |
| options.SetIsPlatformDevice(option_map_it->second.GetBool()); |
| } |
| |
| option_map_it = option_map.find(CBOR("rk")); |
| if (option_map_it != option_map.end()) { |
| if (!option_map_it->second.is_bool()) |
| return base::nullopt; |
| |
| options.SetSupportsResidentKey(option_map_it->second.GetBool()); |
| } |
| |
| option_map_it = option_map.find(CBOR("up")); |
| if (option_map_it != option_map.end()) { |
| if (!option_map_it->second.is_bool()) |
| return base::nullopt; |
| |
| options.SetUserPresenceRequired(option_map_it->second.GetBool()); |
| } |
| |
| option_map_it = option_map.find(CBOR("uv")); |
| if (option_map_it != option_map.end()) { |
| if (!option_map_it->second.is_bool()) |
| return base::nullopt; |
| |
| options.SetUserVerificationRequired(option_map_it->second.GetBool()); |
| } |
| |
| option_map_it = option_map.find(CBOR("client_pin")); |
| if (option_map_it != option_map.end()) { |
| if (!option_map_it->second.is_bool()) |
| return base::nullopt; |
| |
| options.SetClientPinStored(option_map_it->second.GetBool()); |
| } |
| response.SetOptions(std::move(options)); |
| } |
| |
| it = response_map.find(CBOR(5)); |
| if (it != response_map.end()) { |
| if (!it->second.is_unsigned()) |
| return base::nullopt; |
| |
| response.SetMaxMsgSize(it->second.GetUnsigned()); |
| } |
| |
| it = response_map.find(CBOR(6)); |
| if (it != response_map.end()) { |
| if (!it->second.is_array()) |
| return base::nullopt; |
| |
| std::vector<uint8_t> supported_pin_protocols; |
| for (const auto& protocol : it->second.GetArray()) { |
| if (!protocol.is_unsigned()) |
| return base::nullopt; |
| |
| supported_pin_protocols.push_back(protocol.GetUnsigned()); |
| } |
| response.SetPinProtocols(std::move(supported_pin_protocols)); |
| } |
| |
| return response; |
| } |
| |
| } // namespace device |