Commit edc8fe49 authored by Arnar Birgisson's avatar Arnar Birgisson Committed by Commit Bot

Implement a fake U2F device for testing

Bug: 785955
Change-Id: I9e55da4b8a3f6f1abb1246d14281cc99c7594dc5
Reviewed-on: https://chromium-review.googlesource.com/939737Reviewed-by: default avatarKim Paulhamus <kpaulhamus@chromium.org>
Reviewed-by: default avatarBalazs Engedy <engedy@chromium.org>
Reviewed-by: default avatarJan Wilken Dörrie <jdoerrie@chromium.org>
Commit-Queue: Arnar Birgisson <arnarb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#542675}
parent 996b4eb8
...@@ -215,6 +215,8 @@ is_linux_without_udev = is_linux && !use_udev ...@@ -215,6 +215,8 @@ is_linux_without_udev = is_linux && !use_udev
source_set("test_support") { source_set("test_support") {
testonly = true testonly = true
sources = [ sources = [
"fake_u2f_device.cc",
"fake_u2f_device.h",
"fake_u2f_discovery.cc", "fake_u2f_discovery.cc",
"fake_u2f_discovery.h", "fake_u2f_discovery.h",
"test_callback_receiver.h", "test_callback_receiver.h",
......
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_FAKE_U2F_DEVICE_H_
#define DEVICE_FIDO_FAKE_U2F_DEVICE_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/containers/span.h"
#include "base/macros.h"
#include "device/fido/u2f_device.h"
namespace crypto {
class ECPrivateKey;
} // namespace crypto
namespace device {
namespace test {
class FakeU2fDevice : public U2fDevice {
public:
FakeU2fDevice();
~FakeU2fDevice() override;
void AddRegistration(std::vector<uint8_t> key_handle,
std::unique_ptr<crypto::ECPrivateKey> private_key,
std::vector<uint8_t> app_id_hash,
uint32_t counter);
protected:
// U2fDevice:
void TryWink(WinkCallback cb) override;
std::string GetId() const override;
void DeviceTransact(std::vector<uint8_t> command, DeviceCallback cb) override;
base::WeakPtr<U2fDevice> GetWeakPtr() override;
private:
struct RegistrationData {
RegistrationData();
RegistrationData(std::unique_ptr<crypto::ECPrivateKey> private_key,
std::vector<uint8_t> app_id_hash,
uint32_t counter);
RegistrationData(RegistrationData&& data);
RegistrationData& operator=(RegistrationData&& other);
~RegistrationData();
std::unique_ptr<crypto::ECPrivateKey> private_key;
std::vector<uint8_t> app_id_hash;
uint32_t counter = 0;
DISALLOW_COPY_AND_ASSIGN(RegistrationData);
};
void DoRegister(uint8_t ins,
uint8_t p1,
uint8_t p2,
base::span<const uint8_t> data,
DeviceCallback cb);
void DoSign(uint8_t ins,
uint8_t p1,
uint8_t p2,
base::span<const uint8_t> data,
DeviceCallback cb);
std::unique_ptr<crypto::ECPrivateKey> attestation_private_key_;
std::vector<uint8_t> attestation_cert_;
// Keyed on appId/rpId hash (aka "applicationParam")
std::map<std::vector<uint8_t>, RegistrationData> registrations_;
base::WeakPtrFactory<U2fDevice> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FakeU2fDevice);
};
} // namespace test
} // namespace device
#endif // DEVICE_FIDO_FAKE_U2F_DEVICE_H_
...@@ -14,6 +14,10 @@ ...@@ -14,6 +14,10 @@
namespace device { namespace device {
namespace test {
class FakeU2fDevice;
}
// APDU commands are defined as part of ISO 7816-4. Commands can be serialized // APDU commands are defined as part of ISO 7816-4. Commands can be serialized
// into either short length encodings, where the maximum data length is 256 // into either short length encodings, where the maximum data length is 256
// bytes, or an extended length encoding, where the maximum data length is 65536 // bytes, or an extended length encoding, where the maximum data length is 65536
...@@ -73,6 +77,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) U2fApduCommand { ...@@ -73,6 +77,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) U2fApduCommand {
FRIEND_TEST_ALL_PREFIXES(U2fApduTest, TestCreateVersion); FRIEND_TEST_ALL_PREFIXES(U2fApduTest, TestCreateVersion);
FRIEND_TEST_ALL_PREFIXES(U2fApduTest, TestCreateLegacyVersion); FRIEND_TEST_ALL_PREFIXES(U2fApduTest, TestCreateLegacyVersion);
// Built-in software key for testing.
friend class test::FakeU2fDevice;
static constexpr size_t kApduMinHeader = 4; static constexpr size_t kApduMinHeader = 4;
static constexpr size_t kApduMaxHeader = 7; static constexpr size_t kApduMaxHeader = 7;
// As defined in ISO7816-4, extended length APDU request data is limited to // As defined in ISO7816-4, extended length APDU request data is limited to
......
...@@ -25,6 +25,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) U2fApduResponse { ...@@ -25,6 +25,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) U2fApduResponse {
SW_CONDITIONS_NOT_SATISFIED = 0x6985, SW_CONDITIONS_NOT_SATISFIED = 0x6985,
SW_WRONG_DATA = 0x6A80, SW_WRONG_DATA = 0x6A80,
SW_WRONG_LENGTH = 0x6700, SW_WRONG_LENGTH = 0x6700,
SW_INS_NOT_SUPPORTED = 0x6d00,
}; };
U2fApduResponse(std::vector<uint8_t> message, Status response_status); U2fApduResponse(std::vector<uint8_t> message, Status response_status);
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "device/fido/attested_credential_data.h" #include "device/fido/attested_credential_data.h"
#include "device/fido/authenticator_data.h" #include "device/fido/authenticator_data.h"
#include "device/fido/ec_public_key.h" #include "device/fido/ec_public_key.h"
#include "device/fido/fake_u2f_device.h"
#include "device/fido/fake_u2f_discovery.h" #include "device/fido/fake_u2f_discovery.h"
#include "device/fido/fido_attestation_statement.h" #include "device/fido/fido_attestation_statement.h"
#include "device/fido/mock_u2f_device.h" #include "device/fido/mock_u2f_device.h"
...@@ -397,6 +398,21 @@ TEST_F(U2fRegisterTest, TestRegisterSuccess) { ...@@ -397,6 +398,21 @@ TEST_F(U2fRegisterTest, TestRegisterSuccess) {
register_callback_receiver().value()->raw_id()); register_callback_receiver().value()->raw_id());
} }
TEST_F(U2fRegisterTest, TestRegisterSuccessWithFake) {
auto request = CreateRegisterRequest();
request->Start();
discovery()->WaitForCallToStartAndSimulateSuccess();
auto device = std::make_unique<test::FakeU2fDevice>();
discovery()->AddDevice(std::move(device));
register_callback_receiver().WaitForCallback();
EXPECT_EQ(U2fReturnCode::SUCCESS, register_callback_receiver().status());
// We don't verify the response from the fake, but do a quick sanity check.
EXPECT_EQ(32ul, register_callback_receiver().value()->raw_id().size());
}
TEST_F(U2fRegisterTest, TestDelayedSuccess) { TEST_F(U2fRegisterTest, TestDelayedSuccess) {
auto request = CreateRegisterRequest(); auto request = CreateRegisterRequest();
request->Start(); request->Start();
......
...@@ -8,7 +8,10 @@ ...@@ -8,7 +8,10 @@
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_task_environment.h"
#include "crypto/ec_private_key.h"
#include "crypto/sha2.h"
#include "device/fido/authenticator_data.h" #include "device/fido/authenticator_data.h"
#include "device/fido/fake_u2f_device.h"
#include "device/fido/fake_u2f_discovery.h" #include "device/fido/fake_u2f_discovery.h"
#include "device/fido/mock_u2f_device.h" #include "device/fido/mock_u2f_device.h"
#include "device/fido/sign_response_data.h" #include "device/fido/sign_response_data.h"
...@@ -68,6 +71,11 @@ constexpr uint8_t kTestSignAuthenticatorData[] = { ...@@ -68,6 +71,11 @@ constexpr uint8_t kTestSignAuthenticatorData[] = {
// clang-format on // clang-format on
}; };
std::vector<uint8_t> GetTestRelyingPartyIdSHA256() {
return std::vector<uint8_t>(std::begin(kTestRelyingPartyIdSHA256),
std::end(kTestRelyingPartyIdSHA256));
}
std::vector<uint8_t> GetTestCredentialRawIdBytes() { std::vector<uint8_t> GetTestCredentialRawIdBytes() {
return std::vector<uint8_t>(std::begin(test_data::kTestCredentialRawIdBytes), return std::vector<uint8_t>(std::begin(test_data::kTestCredentialRawIdBytes),
std::end(test_data::kTestCredentialRawIdBytes)); std::end(test_data::kTestCredentialRawIdBytes));
...@@ -127,8 +135,9 @@ class U2fSignTest : public ::testing::Test { ...@@ -127,8 +135,9 @@ class U2fSignTest : public ::testing::Test {
nullptr /* connector */, nullptr /* connector */,
base::flat_set<U2fTransportProtocol>( base::flat_set<U2fTransportProtocol>(
{U2fTransportProtocol::kUsbHumanInterfaceDevice}), {U2fTransportProtocol::kUsbHumanInterfaceDevice}),
registered_keys, std::vector<uint8_t>(32), std::vector<uint8_t>(32), registered_keys, std::vector<uint8_t>(32),
base::nullopt, sign_callback_receiver_.callback()); GetTestRelyingPartyIdSHA256(), base::nullopt,
sign_callback_receiver_.callback());
} }
test::FakeU2fDiscovery* discovery() const { return discovery_; } test::FakeU2fDiscovery* discovery() const { return discovery_; }
...@@ -232,6 +241,40 @@ TEST_F(U2fSignTest, TestSignSuccess) { ...@@ -232,6 +241,40 @@ TEST_F(U2fSignTest, TestSignSuccess) {
EXPECT_EQ(signing_key_handle, sign_callback_receiver().value()->raw_id()); EXPECT_EQ(signing_key_handle, sign_callback_receiver().value()->raw_id());
} }
TEST_F(U2fSignTest, TestSignSuccessWithFake) {
auto private_key = crypto::ECPrivateKey::Create();
std::string public_key;
private_key->ExportRawPublicKey(&public_key);
std::vector<uint8_t> key_handle(32);
crypto::SHA256HashString(public_key, key_handle.data(), key_handle.size());
std::vector<std::vector<uint8_t>> handles{key_handle};
auto request = CreateSignRequestWithKeys(handles);
request->Start();
discovery()->WaitForCallToStartAndSimulateSuccess();
auto device = std::make_unique<test::FakeU2fDevice>();
device->AddRegistration(key_handle, std::move(private_key),
GetTestRelyingPartyIdSHA256(), 42);
discovery()->AddDevice(std::move(device));
sign_callback_receiver().WaitForCallback();
EXPECT_EQ(U2fReturnCode::SUCCESS, sign_callback_receiver().status());
// Just a sanity check, we don't verify the actual signature.
ASSERT_GE(
(size_t)(32 + 1 + 4 + 8), // Minimal ECDSA signature is 8 bytes
sign_callback_receiver().value()->GetAuthenticatorDataBytes().size());
EXPECT_EQ(0x01,
sign_callback_receiver()
.value()
->GetAuthenticatorDataBytes()[32]); // UP flag
EXPECT_EQ(43,
sign_callback_receiver()
.value()
->GetAuthenticatorDataBytes()[36]); // counter
}
TEST_F(U2fSignTest, TestDelayedSuccess) { TEST_F(U2fSignTest, TestDelayedSuccess) {
const std::vector<uint8_t> signing_key_handle(32, 0x0A); const std::vector<uint8_t> signing_key_handle(32, 0x0A);
auto request = CreateSignRequestWithKeys({signing_key_handle}); auto request = CreateSignRequestWithKeys({signing_key_handle});
......
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