Commit 66761161 authored by Jun Choi's avatar Jun Choi Committed by Commit Bot

Implement MakeCredential operation for softkey

Add MakeCredential operation to VirtualCtap2Device. VirtualCtap2Device
only supports ES256 algorithm and provides packed attestation statement
during registration.

Bug: 829413
Change-Id: I70782952bdbb2f9f749bb27756a84a6bd37af46d
Reviewed-on: https://chromium-review.googlesource.com/1115900
Commit-Queue: Jun Choi <hongjunchoi@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@{#572032}
parent 461ac6de
...@@ -110,6 +110,8 @@ component("fido") { ...@@ -110,6 +110,8 @@ component("fido") {
"u2f_register_operation.h", "u2f_register_operation.h",
"u2f_sign_operation.cc", "u2f_sign_operation.cc",
"u2f_sign_operation.h", "u2f_sign_operation.h",
"virtual_ctap2_device.cc",
"virtual_ctap2_device.h",
"virtual_fido_device.cc", "virtual_fido_device.cc",
"virtual_fido_device.h", "virtual_fido_device.h",
"virtual_u2f_device.cc", "virtual_u2f_device.cc",
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "components/cbor/cbor_values.h" #include "components/cbor/cbor_values.h"
#include "components/cbor/cbor_writer.h" #include "components/cbor/cbor_writer.h"
#include "device/fido/fido_parsing_utils.h"
namespace device { namespace device {
...@@ -26,8 +27,9 @@ cbor::CBORValue::ArrayValue ToArrayValue(const Container& container) { ...@@ -26,8 +27,9 @@ cbor::CBORValue::ArrayValue ToArrayValue(const Container& container) {
AuthenticatorGetInfoResponse::AuthenticatorGetInfoResponse( AuthenticatorGetInfoResponse::AuthenticatorGetInfoResponse(
base::flat_set<ProtocolVersion> versions, base::flat_set<ProtocolVersion> versions,
std::vector<uint8_t> aaguid) base::span<const uint8_t, kAaguidLength> aaguid)
: versions_(std::move(versions)), aaguid_(std::move(aaguid)) {} : versions_(std::move(versions)),
aaguid_(fido_parsing_utils::Materialize(aaguid)) {}
AuthenticatorGetInfoResponse::AuthenticatorGetInfoResponse( AuthenticatorGetInfoResponse::AuthenticatorGetInfoResponse(
AuthenticatorGetInfoResponse&& that) = default; AuthenticatorGetInfoResponse&& that) = default;
......
...@@ -26,7 +26,7 @@ namespace device { ...@@ -26,7 +26,7 @@ namespace device {
class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorGetInfoResponse { class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorGetInfoResponse {
public: public:
AuthenticatorGetInfoResponse(base::flat_set<ProtocolVersion> versions, AuthenticatorGetInfoResponse(base::flat_set<ProtocolVersion> versions,
std::vector<uint8_t> aaguid); base::span<const uint8_t, kAaguidLength> aaguid);
AuthenticatorGetInfoResponse(AuthenticatorGetInfoResponse&& that); AuthenticatorGetInfoResponse(AuthenticatorGetInfoResponse&& that);
AuthenticatorGetInfoResponse& operator=(AuthenticatorGetInfoResponse&& other); AuthenticatorGetInfoResponse& operator=(AuthenticatorGetInfoResponse&& other);
~AuthenticatorGetInfoResponse(); ~AuthenticatorGetInfoResponse();
...@@ -40,7 +40,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorGetInfoResponse { ...@@ -40,7 +40,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorGetInfoResponse {
AuthenticatorSupportedOptions options); AuthenticatorSupportedOptions options);
const base::flat_set<ProtocolVersion>& versions() const { return versions_; } const base::flat_set<ProtocolVersion>& versions() const { return versions_; }
const std::vector<uint8_t>& aaguid() const { return aaguid_; } const std::array<uint8_t, kAaguidLength>& aaguid() const { return aaguid_; }
const base::Optional<uint32_t>& max_msg_size() const { return max_msg_size_; } const base::Optional<uint32_t>& max_msg_size() const { return max_msg_size_; }
const base::Optional<std::vector<uint8_t>>& pin_protocol() const { const base::Optional<std::vector<uint8_t>>& pin_protocol() const {
return pin_protocols_; return pin_protocols_;
...@@ -52,7 +52,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorGetInfoResponse { ...@@ -52,7 +52,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorGetInfoResponse {
private: private:
base::flat_set<ProtocolVersion> versions_; base::flat_set<ProtocolVersion> versions_;
std::vector<uint8_t> aaguid_; std::array<uint8_t, kAaguidLength> aaguid_;
base::Optional<uint32_t> max_msg_size_; base::Optional<uint32_t> max_msg_size_;
base::Optional<std::vector<uint8_t>> pin_protocols_; base::Optional<std::vector<uint8_t>> pin_protocols_;
base::Optional<std::vector<std::string>> extensions_; base::Optional<std::vector<std::string>> extensions_;
......
...@@ -70,6 +70,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapMakeCredentialRequest { ...@@ -70,6 +70,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapMakeCredentialRequest {
exclude_list() const { exclude_list() const {
return exclude_list_; return exclude_list_;
} }
const base::Optional<std::vector<uint8_t>>& pin_auth() const {
return pin_auth_;
}
private: private:
std::array<uint8_t, kClientDataHashLength> client_data_hash_; std::array<uint8_t, kClientDataHashLength> client_data_hash_;
......
...@@ -198,9 +198,9 @@ constexpr uint8_t kAuthDataCBOR[] = { ...@@ -198,9 +198,9 @@ constexpr uint8_t kAuthDataCBOR[] = {
// and test_data::kTestECPublicKeyCOSE. // and test_data::kTestECPublicKeyCOSE.
0x58, 0xC4}; 0x58, 0xC4};
constexpr uint8_t kTestDeviceAaguid[] = {0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, constexpr std::array<uint8_t, kAaguidLength> kTestDeviceAaguid = {
0x4D, 0x15, 0x80, 0x06, 0x17, 0x11, {0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15, 0x80, 0x06, 0x17, 0x11,
0x1F, 0x9E, 0xDC, 0x7D}; 0x1F, 0x9E, 0xDC, 0x7D}};
std::vector<uint8_t> GetTestAttestedCredentialDataBytes() { std::vector<uint8_t> GetTestAttestedCredentialDataBytes() {
// Combine kTestAttestedCredentialDataPrefix and kTestECPublicKeyCOSE. // Combine kTestAttestedCredentialDataPrefix and kTestECPublicKeyCOSE.
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "device/fido/make_credential_task.h" #include "device/fido/make_credential_task.h"
#include "device/fido/mock_fido_device.h" #include "device/fido/mock_fido_device.h"
#include "device/fido/test_callback_receiver.h" #include "device/fido/test_callback_receiver.h"
#include "device/fido/virtual_ctap2_device.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -107,6 +108,21 @@ TEST_F(FidoMakeCredentialTaskTest, MakeCredentialSuccess) { ...@@ -107,6 +108,21 @@ TEST_F(FidoMakeCredentialTaskTest, MakeCredentialSuccess) {
EXPECT_TRUE(device->device_info()); EXPECT_TRUE(device->device_info());
} }
TEST_F(FidoMakeCredentialTaskTest, TestRegisterSuccessWithFake) {
auto device = std::make_unique<VirtualCtap2Device>();
const auto task = CreateMakeCredentialTask(device.get());
make_credential_callback_receiver().WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
make_credential_callback_receiver().status());
// We don't verify the response from the fake, but do a quick sanity check.
ASSERT_TRUE(make_credential_callback_receiver().value());
EXPECT_EQ(
32u,
make_credential_callback_receiver().value()->raw_credential_id().size());
}
TEST_F(FidoMakeCredentialTaskTest, MakeCredentialWithIncorrectRpIdHash) { TEST_F(FidoMakeCredentialTaskTest, MakeCredentialWithIncorrectRpIdHash) {
auto device = std::make_unique<MockFidoDevice>(); auto device = std::make_unique<MockFidoDevice>();
......
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.
#ifndef DEVICE_FIDO_VIRTUAL_CTAP2_DEVICE_H_
#define DEVICE_FIDO_VIRTUAL_CTAP2_DEVICE_H_
#include <stdint.h>
#include <memory>
#include <vector>
#include "base/component_export.h"
#include "base/containers/span.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "device/fido/attested_credential_data.h"
#include "device/fido/authenticator_data.h"
#include "device/fido/authenticator_supported_options.h"
#include "device/fido/fido_constants.h"
#include "device/fido/virtual_fido_device.h"
namespace device {
class COMPONENT_EXPORT(DEVICE_FIDO) VirtualCtap2Device
: public VirtualFidoDevice {
public:
VirtualCtap2Device();
explicit VirtualCtap2Device(scoped_refptr<State> state);
~VirtualCtap2Device() override;
// FidoDevice:
void Cancel() override;
void DeviceTransact(std::vector<uint8_t> command, DeviceCallback cb) override;
base::WeakPtr<FidoDevice> GetWeakPtr() override;
void SetAuthenticatorSupportedOptions(AuthenticatorSupportedOptions options);
private:
CtapDeviceResponseCode OnMakeCredential(base::span<const uint8_t> request,
std::vector<uint8_t>* response);
CtapDeviceResponseCode OnAuthenticatorGetInfo(
std::vector<uint8_t>* response) const;
AuthenticatorData ConstructAuthenticatorData(
base::span<const uint8_t, kRpIdHashLength> rp_id_hash,
base::Optional<AttestedCredentialData> attested_credential_data);
AuthenticatorGetInfoResponse device_info_;
base::WeakPtrFactory<FidoDevice> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(VirtualCtap2Device);
};
} // namespace device
#endif // DEVICE_FIDO_VIRTUAL_CTAP2_DEVICE_H_
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
#include <tuple> #include <tuple>
#include <utility> #include <utility>
#include "crypto/ec_private_key.h"
#include "crypto/ec_signature_creator.h" #include "crypto/ec_signature_creator.h"
#include "device/fido/fido_parsing_utils.h" #include "device/fido/fido_parsing_utils.h"
...@@ -122,6 +121,34 @@ VirtualFidoDevice::GenerateAttestationCertificate( ...@@ -122,6 +121,34 @@ VirtualFidoDevice::GenerateAttestationCertificate(
return std::vector<uint8_t>(attestation_cert.begin(), attestation_cert.end()); return std::vector<uint8_t>(attestation_cert.begin(), attestation_cert.end());
} }
void VirtualFidoDevice::StoreNewKey(
base::span<const uint8_t, kRpIdHashLength> application_parameter,
base::span<const uint8_t> key_handle,
std::unique_ptr<crypto::ECPrivateKey> private_key) {
// Store the registration. Because the key handle is the hashed public key we
// just generated, no way this should already be registered.
bool did_insert = false;
std::tie(std::ignore, did_insert) = mutable_state()->registrations.emplace(
fido_parsing_utils::Materialize(key_handle),
RegistrationData(std::move(private_key), application_parameter, 1));
DCHECK(did_insert);
}
VirtualFidoDevice::RegistrationData* VirtualFidoDevice::FindRegistrationData(
base::span<const uint8_t> key_handle,
base::span<const uint8_t, kRpIdHashLength> application_parameter) {
// Check if this is our key_handle and it's for this appId.
auto it = mutable_state()->registrations.find(key_handle);
if (it == mutable_state()->registrations.end())
return nullptr;
if (application_parameter !=
base::make_span(it->second.application_parameter))
return nullptr;
return &(it->second);
}
void VirtualFidoDevice::TryWink(WinkCallback cb) { void VirtualFidoDevice::TryWink(WinkCallback cb) {
std::move(cb).Run(); std::move(cb).Run();
} }
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/scoped_refptr.h" #include "base/memory/scoped_refptr.h"
#include "base/optional.h" #include "base/optional.h"
#include "crypto/ec_private_key.h"
#include "device/fido/fido_constants.h" #include "device/fido/fido_constants.h"
#include "device/fido/fido_device.h" #include "device/fido/fido_device.h"
#include "device/fido/fido_parsing_utils.h" #include "device/fido/fido_parsing_utils.h"
...@@ -120,6 +121,15 @@ class COMPONENT_EXPORT(DEVICE_FIDO) VirtualFidoDevice : public FidoDevice { ...@@ -120,6 +121,15 @@ class COMPONENT_EXPORT(DEVICE_FIDO) VirtualFidoDevice : public FidoDevice {
base::Optional<std::vector<uint8_t>> GenerateAttestationCertificate( base::Optional<std::vector<uint8_t>> GenerateAttestationCertificate(
bool individual_attestation_requested) const; bool individual_attestation_requested) const;
void StoreNewKey(
base::span<const uint8_t, kRpIdHashLength> application_parameter,
base::span<const uint8_t> key_handle,
std::unique_ptr<crypto::ECPrivateKey> private_key);
RegistrationData* FindRegistrationData(
base::span<const uint8_t> key_handle,
base::span<const uint8_t, kRpIdHashLength> application_parameter);
// FidoDevice: // FidoDevice:
void TryWink(WinkCallback cb) override; void TryWink(WinkCallback cb) override;
std::string GetId() const override; std::string GetId() const override;
......
...@@ -176,14 +176,7 @@ base::Optional<std::vector<uint8_t>> VirtualU2fDevice::DoRegister( ...@@ -176,14 +176,7 @@ base::Optional<std::vector<uint8_t>> VirtualU2fDevice::DoRegister(
Append(&response, *attestation_cert); Append(&response, *attestation_cert);
Append(&response, sig); Append(&response, sig);
// Store the registration. Because the key handle is the hashed public key we StoreNewKey(application_parameter, key_handle, std::move(private_key));
// just generated, no way this should already be registered.
bool did_insert = false;
std::tie(std::ignore, did_insert) = mutable_state()->registrations.emplace(
std::move(key_handle),
RegistrationData(std::move(private_key), application_parameter, 1));
DCHECK(did_insert);
return apdu::ApduResponse(std::move(response), return apdu::ApduResponse(std::move(response),
apdu::ApduResponse::Status::SW_NO_ERROR) apdu::ApduResponse::Status::SW_NO_ERROR)
.GetEncodedResponse(); .GetEncodedResponse();
...@@ -204,42 +197,29 @@ base::Optional<std::vector<uint8_t>> VirtualU2fDevice::DoSign( ...@@ -204,42 +197,29 @@ base::Optional<std::vector<uint8_t>> VirtualU2fDevice::DoSign(
mutable_state()->simulate_press_callback.Run(); mutable_state()->simulate_press_callback.Run();
} }
if (data.size() < 32 + 32 + 1) { if (data.size() < 32 + 32 + 1)
return ErrorStatus(apdu::ApduResponse::Status::SW_WRONG_LENGTH); return ErrorStatus(apdu::ApduResponse::Status::SW_WRONG_LENGTH);
}
auto challenge_param = data.first<32>(); auto challenge_param = data.first<32>();
auto application_parameter = data.subspan<32, 32>(); auto application_parameter = data.subspan<32, 32>();
size_t key_handle_length = data[64]; size_t key_handle_length = data[64];
if (data.size() != 32 + 32 + 1 + key_handle_length) { if (data.size() != 32 + 32 + 1 + key_handle_length)
return ErrorStatus(apdu::ApduResponse::Status::SW_WRONG_LENGTH); return ErrorStatus(apdu::ApduResponse::Status::SW_WRONG_LENGTH);
}
auto key_handle = data.last(key_handle_length);
// Check if this is our key_handle and it's for this appId. auto key_handle = data.last(key_handle_length);
auto it = mutable_state()->registrations.find(key_handle); auto* registration = FindRegistrationData(key_handle, application_parameter);
if (it == mutable_state()->registrations.end()) { if (!registration)
return ErrorStatus(apdu::ApduResponse::Status::SW_WRONG_DATA);
}
base::span<const uint8_t> registered_app_id_hash =
base::make_span(it->second.application_parameter);
if (application_parameter != registered_app_id_hash) {
// It's important this error looks identical to the previous one, as
// tokens should not reveal the existence of keyHandles to unrelated appIds.
return ErrorStatus(apdu::ApduResponse::Status::SW_WRONG_DATA); return ErrorStatus(apdu::ApduResponse::Status::SW_WRONG_DATA);
}
auto& registration = it->second; ++registration->counter;
++registration.counter;
// First create the part of the response that gets signed over. // First create the part of the response that gets signed over.
std::vector<uint8_t> response; std::vector<uint8_t> response;
response.push_back(0x01); // Always pretend we got a touch. response.push_back(0x01); // Always pretend we got a touch.
response.push_back(registration.counter >> 24); response.push_back(registration->counter >> 24);
response.push_back(registration.counter >> 16); response.push_back(registration->counter >> 16);
response.push_back(registration.counter >> 8); response.push_back(registration->counter >> 8);
response.push_back(registration.counter); response.push_back(registration->counter);
std::vector<uint8_t> sign_buffer; std::vector<uint8_t> sign_buffer;
sign_buffer.reserve(application_parameter.size() + response.size() + sign_buffer.reserve(application_parameter.size() + response.size() +
...@@ -250,7 +230,7 @@ base::Optional<std::vector<uint8_t>> VirtualU2fDevice::DoSign( ...@@ -250,7 +230,7 @@ base::Optional<std::vector<uint8_t>> VirtualU2fDevice::DoSign(
// Sign with credential key. // Sign with credential key.
std::vector<uint8_t> sig; std::vector<uint8_t> sig;
bool status = Sign(registration.private_key.get(), sign_buffer, &sig); bool status = Sign(registration->private_key.get(), sign_buffer, &sig);
DCHECK(status); DCHECK(status);
// Add signature for full response. // Add signature for full response.
......
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