Commit 29d078b8 authored by Jun Choi's avatar Jun Choi Committed by Commit Bot

Add parser to deserialize authenticator responses

Add support for parsing response received from authenticator to
corresponding CTAPResponse objects.

Bug: 804626
Change-Id: I3030125371c4ac66c443a6bae938f63757066db7
Reviewed-on: https://chromium-review.googlesource.com/850815
Commit-Queue: Jun Choi <hongjunchoi@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarChris Palmer <palmer@chromium.org>
Reviewed-by: default avatarKim Paulhamus <kpaulhamus@chromium.org>
Reviewed-by: default avatarJan Wilken Dörrie <jdoerrie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#533895}
parent aa36bffb
...@@ -65,6 +65,7 @@ test("device_unittests") { ...@@ -65,6 +65,7 @@ test("device_unittests") {
"bluetooth/test/test_bluetooth_local_gatt_service_delegate.h", "bluetooth/test/test_bluetooth_local_gatt_service_delegate.h",
"bluetooth/uribeacon/uri_encoder_unittest.cc", "bluetooth/uribeacon/uri_encoder_unittest.cc",
"ctap/ctap_request_unittest.cc", "ctap/ctap_request_unittest.cc",
"ctap/ctap_response_unittest.cc",
"gamepad/abstract_haptic_gamepad_unittest.cc", "gamepad/abstract_haptic_gamepad_unittest.cc",
"gamepad/gamepad_provider_unittest.cc", "gamepad/gamepad_provider_unittest.cc",
"gamepad/gamepad_service_unittest.cc", "gamepad/gamepad_service_unittest.cc",
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# found in the LICENSE file. # found in the LICENSE file.
import("//build/config/features.gni") import("//build/config/features.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
source_set("ctap") { source_set("ctap") {
sources = [ sources = [
...@@ -24,6 +25,8 @@ source_set("ctap") { ...@@ -24,6 +25,8 @@ source_set("ctap") {
"ctap_make_credential_request_param.h", "ctap_make_credential_request_param.h",
"ctap_request_param.cc", "ctap_request_param.cc",
"ctap_request_param.h", "ctap_request_param.h",
"device_response_converter.cc",
"device_response_converter.h",
"public_key_credential_descriptor.cc", "public_key_credential_descriptor.cc",
"public_key_credential_descriptor.h", "public_key_credential_descriptor.h",
"public_key_credential_params.cc", "public_key_credential_params.cc",
...@@ -42,3 +45,14 @@ source_set("ctap") { ...@@ -42,3 +45,14 @@ source_set("ctap") {
"//services/service_manager/public/interfaces", "//services/service_manager/public/interfaces",
] ]
} }
fuzzer_test("ctap_response_fuzzer") {
sources = [
"ctap_response_fuzzer.cc",
]
deps = [
":ctap",
]
seed_corpus = "ctap_response_fuzzer_corpus/"
libfuzzer_options = [ "max_len=65537" ]
}
...@@ -24,7 +24,7 @@ TEST(CTAPRequestTest, TestConstructMakeCredentialRequestParam) { ...@@ -24,7 +24,7 @@ TEST(CTAPRequestTest, TestConstructMakeCredentialRequestParam) {
0x03, 0x02, 0x01, 0x02, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82}; 0x03, 0x02, 0x01, 0x02, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82};
static constexpr uint8_t kSerializedRequest[] = { static constexpr uint8_t kSerializedRequest[] = {
// clang format-off // clang-format off
0x01, // authenticatorMakeCredential command 0x01, // authenticatorMakeCredential command
0xa5, // map(5) 0xa5, // map(5)
0x01, // clientDataHash 0x01, // clientDataHash
...@@ -104,8 +104,7 @@ TEST(CTAPRequestTest, TestConstructMakeCredentialRequestParam) { ...@@ -104,8 +104,7 @@ TEST(CTAPRequestTest, TestConstructMakeCredentialRequestParam) {
0x62, // text(2) 0x62, // text(2)
0x75, 0x76, // "uv" 0x75, 0x76, // "uv"
0xf5 // True(21) 0xf5 // True(21)
// clang-format on
// clang format-on
}; };
PublicKeyCredentialRPEntity rp("acme.com"); PublicKeyCredentialRPEntity rp("acme.com");
...@@ -135,7 +134,7 @@ TEST(CTAPRequestTest, TestConstructGetAssertionRequest) { ...@@ -135,7 +134,7 @@ TEST(CTAPRequestTest, TestConstructGetAssertionRequest) {
0xb8, 0x8c, 0x25, 0xdb, 0x9e, 0x60, 0x26, 0x45, 0xf1, 0x41}; 0xb8, 0x8c, 0x25, 0xdb, 0x9e, 0x60, 0x26, 0x45, 0xf1, 0x41};
static constexpr uint8_t kSerializedRequest[] = { static constexpr uint8_t kSerializedRequest[] = {
// clang format-off // clang-format off
0x02, // authenticatorGetAssertion command 0x02, // authenticatorGetAssertion command
0xa4, // map(4) 0xa4, // map(4)
...@@ -194,7 +193,7 @@ TEST(CTAPRequestTest, TestConstructGetAssertionRequest) { ...@@ -194,7 +193,7 @@ TEST(CTAPRequestTest, TestConstructGetAssertionRequest) {
0x75, 0x76, // "uv" 0x75, 0x76, // "uv"
0xf5 // True(21) 0xf5 // True(21)
// clang format-on // clang-format on
}; };
CTAPGetAssertionRequestParam get_assertion_req( CTAPGetAssertionRequestParam get_assertion_req(
......
// 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 <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <vector>
#include "device/ctap/authenticator_get_assertion_response.h"
#include "device/ctap/authenticator_get_info_response.h"
#include "device/ctap/authenticator_make_credential_response.h"
#include "device/ctap/ctap_constants.h"
#include "device/ctap/device_response_converter.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
std::vector<uint8_t> input(data, data + size);
device::ReadCTAPMakeCredentialResponse(
device::CTAPDeviceResponseCode::kSuccess, input);
device::ReadCTAPGetAssertionResponse(device::CTAPDeviceResponseCode::kSuccess,
input);
device::ReadCTAPGetInfoResponse(device::CTAPDeviceResponseCode::kSuccess,
input);
return 0;
}
This diff is collapsed.
// 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/ctap/device_response_converter.h"
#include <string>
#include <utility>
#include "base/numerics/safe_conversions.h"
#include "base/optional.h"
#include "components/cbor/cbor_reader.h"
#include "components/cbor/cbor_writer.h"
#include "device/ctap/authenticator_supported_options.h"
#include "device/ctap/ctap_constants.h"
namespace device {
using CBOR = cbor::CBORValue;
CTAPDeviceResponseCode GetResponseCode(const std::vector<uint8_t>& buffer) {
if (buffer.empty())
return CTAPDeviceResponseCode::kCtap2ErrInvalidCBOR;
std::array<CTAPDeviceResponseCode, 51> response_codes =
GetCTAPResponseCodeList();
std::array<CTAPDeviceResponseCode, 51>::iterator found = std::find_if(
response_codes.begin(), response_codes.end(),
[&buffer](CTAPDeviceResponseCode response_code) {
return buffer[0] == base::strict_cast<uint8_t>(response_code);
});
return found == response_codes.end()
? CTAPDeviceResponseCode::kCtap2ErrInvalidCBOR
: *found;
}
// 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
// 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.
#ifndef DEVICE_CTAP_DEVICE_RESPONSE_CONVERTER_H_
#define DEVICE_CTAP_DEVICE_RESPONSE_CONVERTER_H_
#include <stdint.h>
#include <vector>
#include "base/optional.h"
#include "device/ctap/authenticator_get_assertion_response.h"
#include "device/ctap/authenticator_get_info_response.h"
#include "device/ctap/authenticator_make_credential_response.h"
#include "device/ctap/ctap_constants.h"
// Converts response from authenticators to CTAPResponse objects. If the
// response of the authenticator does not conform to format specified by the
// CTAP protocol, null optional is returned.
namespace device {
// Parses response code from response received from the authenticator. If
// unknown response code value is received, then CTAP2_ERR_OTHER is returned.
CTAPDeviceResponseCode GetResponseCode(const std::vector<uint8_t>& buffer);
// De-serializes CBOR encoded response, checks for valid CBOR map formatting,
// and converts response to AuthenticatorMakeCredentialResponse object with
// CBOR map keys that conform to format of attestation object defined by the
// WebAuthN spec : https://w3c.github.io/webauthn/#fig-attStructs
base::Optional<AuthenticatorMakeCredentialResponse>
ReadCTAPMakeCredentialResponse(CTAPDeviceResponseCode response_code,
const std::vector<uint8_t>& buffer);
// De-serializes CBOR encoded response to AuthenticatorGetAssertion /
// AuthenticatorGetNextAssertion request to AuthenticatorGetAssertionResponse
// object.
base::Optional<AuthenticatorGetAssertionResponse> ReadCTAPGetAssertionResponse(
CTAPDeviceResponseCode response_code,
const std::vector<uint8_t>& buffer);
// De-serializes CBOR encoded response to AuthenticatorGetInfo request to
// AuthenticatorGetInfoResponse object.
base::Optional<AuthenticatorGetInfoResponse> ReadCTAPGetInfoResponse(
CTAPDeviceResponseCode response_code,
const std::vector<uint8_t>& buffer);
} // namespace device
#endif // DEVICE_CTAP_DEVICE_RESPONSE_CONVERTER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment