blob: e8717f0fa7fc30c8fa9cfe7a86d60ae088913717 [file] [log] [blame]
// Copyright 2017 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_U2F_REQUEST_H_
#define DEVICE_FIDO_U2F_REQUEST_H_
#include <stdint.h>
#include <list>
#include <memory>
#include <string>
#include <vector>
#include "base/cancelable_callback.h"
#include "base/component_export.h"
#include "base/containers/flat_set.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/optional.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_device.h"
#include "device/fido/fido_discovery.h"
#include "device/fido/fido_transport_protocol.h"
namespace service_manager {
class Connector;
}
namespace device {
class COMPONENT_EXPORT(DEVICE_FIDO) U2fRequest
: public FidoDiscovery::Observer {
public:
using VersionCallback = base::OnceCallback<void(ProtocolVersion version)>;
// U2fRequest will create a discovery instance and register itself as an
// observer for each passed in transport protocol.
// TODO(https://crbug.com/769631): Remove the dependency on Connector once U2F
// is servicified.
U2fRequest(service_manager::Connector* connector,
const base::flat_set<FidoTransportProtocol>& transports,
std::vector<uint8_t> application_parameter,
std::vector<uint8_t> challenge_digest,
std::vector<std::vector<uint8_t>> registered_keys);
~U2fRequest() override;
void Start();
// Functions below are implemented in U2fRequest interface(and not in its
// respective subclasses) as both {register, sign} commands are used during
// registration process. That is, check-only sign command is sent during
// registration to prevent duplicate registration.
//
// Returns APDU U2F request commands. Null optional is returned for
// incorrectly formatted parameter.
base::Optional<std::vector<uint8_t>> GetU2fSignApduCommand(
const std::vector<uint8_t>& application_parameter,
const std::vector<uint8_t>& key_handle,
bool is_check_only_sign = false) const;
base::Optional<std::vector<uint8_t>> GetU2fRegisterApduCommand(
bool is_individual_attestation) const;
protected:
enum class State {
INIT,
BUSY,
WINK,
IDLE,
OFF,
COMPLETE,
};
void Transition();
// Starts sign, register, and version request transaction on
// |current_device_|.
void InitiateDeviceTransaction(base::Optional<std::vector<uint8_t>> cmd,
FidoDevice::DeviceCallback callback);
virtual void TryDevice() = 0;
// Moves |current_device_| to the list of |abandoned_devices_| and iterates
// |current_device_|. Expects |current_device_| to be valid prior to calling
// this method.
void AbandonCurrentDeviceAndTransition();
// Hold handles to the devices known to the system. Known devices are
// partitioned into four parts:
// [attempted_devices_), current_device_, [devices_), [abandoned_devices_)
// During device iteration the |current_device_| gets pushed to
// |attempted_devices_|, and, if possible, the first element of |devices_|
// gets popped and becomes the new |current_device_|. Once all |devices_| are
// exhausted, |attempted_devices_| get moved into |devices_| and
// |current_device_| is reset. |abandoned_devices_| contains a list of devices
// that have been tried in the past, but were abandoned because of an error.
// Devices in this list won't be tried again.
FidoDevice* current_device_ = nullptr;
std::list<FidoDevice*> devices_;
std::list<FidoDevice*> attempted_devices_;
std::list<FidoDevice*> abandoned_devices_;
State state_;
std::vector<std::unique_ptr<FidoDiscovery>> discoveries_;
std::vector<uint8_t> application_parameter_;
std::vector<uint8_t> challenge_digest_;
std::vector<std::vector<uint8_t>> registered_keys_;
private:
FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestIterateDevice);
FRIEND_TEST_ALL_PREFIXES(U2fRequestTest,
TestAbandonCurrentDeviceAndTransition);
FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestBasicMachine);
FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestAlreadyPresentDevice);
FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestMultipleDiscoveries);
FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestSlowDiscovery);
FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestMultipleDiscoveriesWithFailures);
FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestLegacyVersionRequest);
// FidoDiscovery::Observer
void DiscoveryStarted(FidoDiscovery* discovery, bool success) override;
void DeviceAdded(FidoDiscovery* discovery, FidoDevice* device) override;
void DeviceRemoved(FidoDiscovery* discovery, FidoDevice* device) override;
void IterateDevice();
void OnWaitComplete();
base::CancelableClosure delay_callback_;
size_t started_count_ = 0;
base::WeakPtrFactory<U2fRequest> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(U2fRequest);
};
} // namespace device
#endif // DEVICE_FIDO_U2F_REQUEST_H_