blob: 995a0569775185e277726c1bd744a7c7d2072529 [file] [log] [blame]
// Copyright 2018 The Chromium OS 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 U2FD_G2F_TOOLS_G2F_CLIENT_H_
#define U2FD_G2F_TOOLS_G2F_CLIENT_H_
#include <cstdint>
#include <string>
#include <base/optional.h>
#include <brillo/secure_blob.h>
#include <hidapi/hidapi.h>
#include "u2fd/u2fhid.h"
namespace g2f_client {
struct FrameBlob;
// Represents the HID layer of a U2F HID device.
class HidDevice {
public:
// Channel ID, as specifid in section 2.4 of FIDO U2F HID Spec.
struct Cid {
uint8_t raw[4];
constexpr uint32_t value() const {
// Cid is stored in little endian byte order.
// Note: Cid address may be unaligned, so can't use ntohl.
return static_cast<uint32_t>(raw[0]) |
(static_cast<uint32_t>(raw[1]) << 8) |
(static_cast<uint32_t>(raw[2]) << 16) |
(static_cast<uint32_t>(raw[3]) << 24);
}
constexpr uint32_t IsBroadcast() const { return value() == 0xFFFFFFFFu; }
} __attribute__((__packed__));
static_assert(sizeof(Cid) == 4, "Wrong Cid size");
// Broadcast Channel ID, used during INIT command to create a channel.
static constexpr Cid kCidBroadcast = {{0xFF, 0xFF, 0xFF, 0xFF}};
// Creates a new instance for the device at the specified path.
// Does not open the device.
explicit HidDevice(const std::string& path);
virtual ~HidDevice();
virtual bool IsOpened() const { return dev_ != nullptr; }
// Attempts to open the device, returns true on success.
virtual bool Open();
// Closes the device, if open.
virtual void Close();
// Sends a message to the given channel, with the specified command
// and payload. Payload will be split into multiple messages if necessary.
// Returns true on success.
virtual bool SendRequest(const Cid& cid,
uint8_t cmd,
const brillo::Blob& payload);
// Reads a response for the given channel. This should follow a call
// to SendRequest. Returns true on success, false if a packet could
// not be read, or an unexpected packet was read.
virtual bool RecvResponse(const Cid& cid,
uint8_t* cmd,
brillo::Blob* payload,
int timeout_ms);
private:
bool WriteBlob(const FrameBlob& blob);
bool ReadBlob(FrameBlob* blob, int timeout_ms);
// Returns true if res == expected, prints out an error message and
// returns false otherwise.
bool CheckDeviceError(const std::string& func, int res, int expected);
// Non-null iff the device is open.
hid_device* dev_ = nullptr;
// Path to the device.
std::string path_;
};
// Represents U2F layer of a U2F HID device.
class U2FHid {
public:
using CommandCode = u2f::U2fHid::U2fHidCommand;
using ErrorCode = u2f::U2fHid::U2fHidError;
// Represents a U2F command, e.g. REGISTER or AUTHENTICATE.
struct Command {
uint8_t cmd;
brillo::Blob payload;
Command() = default;
Command(CommandCode code, const brillo::Blob& payload)
: cmd(static_cast<uint8_t>(code)), payload(payload) {}
constexpr bool IsError() const {
return cmd == static_cast<uint8_t>(CommandCode::kError);
}
constexpr uint8_t ErrorCode() const;
// Returns true if the command was succesful, and prints
// an error message if not.
bool CheckSuccess(const std::string& descr) const;
// Returns a description of the command.
std::string Description() const;
// Returns a hex-encoded dump of the command payload.
std::string FullDump() const;
// Returns a human-readable name for the command.
std::string CommandName() const;
// Returns a human-readable name for the error.
std::string ErrorName() const;
};
// U2F HID Device version information, returned as part of
// Init command.
struct Version {
uint8_t protocol;
uint8_t major;
uint8_t minor;
uint8_t build;
};
// Creates a new instance for the specified hid_device, which must
// outlive this instance.
explicit U2FHid(HidDevice* hid_device);
virtual ~U2FHid() = default;
// Sends a raw Command and retrieves the response. Returns
// true on success.
bool RawCommand(const Command& request, Command* response);
// Creates a new U2F HID Channel if necessary. Must be called
// before calling any other commands below. If force_realloc is
// true then a new channel will be created even if one already
// exists. This sends a U2FHID_INIT command.
bool Init(bool force_realloc);
// Locks the device so that it only accepts commands from the
// most recently created channel for the specified time.
// This sends a U2FHID_LOCK command.
bool Lock(uint8_t lock_timeout_seconds);
// This sends the specified message to the device. The message
// should be formatted according to section 4.1.1 of the FIDO
// U2F HID spec. This sends a U2FHID_MSG command.
virtual bool Msg(const brillo::Blob& request, brillo::Blob* response);
// This sends a ping of the specified size to the device.
// This is a U2FHID_PING command.
bool Ping(size_t size);
// This sends a U2FHID_WINK command, causing the device to
// blink (or similar).
bool Wink();
// Returns true if a channel has been created, and the device
// is ready to send commands (other than Init).
bool Initialized() const { return !cid_.IsBroadcast(); }
// Returns the device version information, provided by the
// device during initialization. Must successfully call Init()
// for this to be valid.
const Version& GetVersion() const { return version_; }
// Returns device capabilities, provided by the device during
// initialization. Must successfully call Init() for this to be
// valid. Capabilities format is specified in section 4.1.2 of
// the FIDO U2FHID spec.
uint8_t GetCaps() const { return caps_; }
// Sets the channel ID to use (which must already exist).
void SetCid(const HidDevice::Cid& cid) { cid_ = cid; }
private:
bool GetSuccessfulResponse(const Command& request, Command* response);
HidDevice* hid_device_;
HidDevice::Cid cid_ = HidDevice::kCidBroadcast;
int timeout_ms_ = -1;
Version version_;
uint8_t caps_;
};
// Represents the U2F layer, responsible for sending
// register, authenticate, etc messages.
class U2F {
public:
// Creates a new instance to wrap the specified
// u2f_device, which must outlive this instance.
explicit U2F(U2FHid* u2f_device) : u2f_device_(u2f_device) {}
// This sends a U2F_REGISTER message.
bool Register(base::Optional<uint8_t> p1,
const brillo::Blob& challenge,
const brillo::Blob& application,
bool use_g2f_att_key,
brillo::Blob* public_key,
brillo::Blob* key_handle,
brillo::Blob* certificate_and_signature);
// This sends a U2F_AUTHENTICATE message.
bool Authenticate(base::Optional<uint8_t> p1,
const brillo::Blob& challenge,
const brillo::Blob& application,
const brillo::Blob& key_handle,
bool* presence_verified,
brillo::Blob* counter,
brillo::Blob* signature);
static void AppendBlob(const brillo::Blob& from, brillo::Blob* to);
private:
// Sends a U2F message, checks that the response is sufficiently
// long, and that the returned status word is SW_NO_ERROR. The
// size specified should not include the two SW1 and SW2 bytes.
// Returns false if size or status conditions are not met.
bool SendMsg(const brillo::Blob& request,
int min_response_size,
brillo::Blob* response);
// Copies data from a blob and appends to the end of another.
// Start position is specified by from_it, which is updated
// to point to the next unread byte, allowing subsequent calls
// to re-use the same iterator to read the next available bytes.
// Returns false if the source blob did not have enough data to
// copy the requested length, true otherwise.
static bool AppendFromResponse(const brillo::Blob& from,
brillo::Blob::iterator* from_it,
size_t length,
brillo::Blob* to);
U2FHid* u2f_device_;
// This is the minimum set of sizes necessary to successfully
// parse the response into sections; this does not imply that
// further parsing of individual sections (e.g. the certificate)
// will succeed.
static constexpr int kRegResponseMinSize =
1 + // Reserved Byte (legacy reasons)
65 + // User Public key
1 + // Key Handle Length
1 + // Key Handle (minimum)
1; // Certificate + signature (minimum)
static constexpr int kRegResponseStartOffset = 1;
static constexpr int kRegResponsePublicKeyLength = 65;
// This size should be interpreted in the same way as
// kRegResponseMinSize above.
static constexpr int kAuthResponseMinSize = 1 + // User presence
4 + // Counter
1; // Signature
static constexpr int kAuthResponseCounterOffset = 1;
static constexpr int kAuthResponseCounterLength = 4;
};
} // namespace g2f_client
#endif // U2FD_G2F_TOOLS_G2F_CLIENT_H_