blob: b92e73d6da85a670f8fd545e1df0dcb47d4fc692 [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.
#include <sysexits.h>
#include <algorithm>
#include <iostream>
#include <memory>
#include <string>
#include <base/command_line.h>
#include <base/logging.h>
#include <base/macros.h>
#include <base/strings/string_number_conversions.h>
#include <brillo/flag_helper.h>
#include <brillo/syslog_logging.h>
#include "u2fd/g2f_tools/g2f_client.h"
int main(int argc, char* argv[]) {
DEFINE_bool(syslog, false, "also log to syslog");
DEFINE_string(dev, "", "path to G2F device");
DEFINE_bool(ping, false, "{action} ping device");
DEFINE_bool(raw, false, "{action} send raw HID command");
DEFINE_bool(msg, false, "{action} send U2F message");
DEFINE_bool(wink, false, "{action} wink");
DEFINE_bool(reg, false, "{action} send U2F_REGISTER message");
DEFINE_bool(auth, false, "{action} send U2F_AUTHENTICATE message");
DEFINE_bool(lock, false, "lock channel before action");
DEFINE_int32(ping_size, 10, "size of ping data");
DEFINE_int32(lock_timeout, 10, "lock_timeout in seconds [0..10]");
DEFINE_string(payload, "", "request payload bytes (hex) for --raw or --msg");
DEFINE_int32(cc, -1, "command code to send for --raw");
DEFINE_int32(p1, -1, "P1 value to send for --reg or --auth, -1 for default");
DEFINE_string(challenge, "", "challenge parameter for --reg or --auth");
DEFINE_string(application, "", "application parameter for --reg or --auth");
DEFINE_string(key_handle, "", "key handle parameter for --auth");
DEFINE_bool(g2f, false, "use fixed G2F attestation key for --reg");
DEFINE_int32(v, 0, "verbosity level (up to 3)");
brillo::FlagHelper::Init(argc, argv, "g2ftool - G2F testing tool");
int log_flags = brillo::kLogToStderrIfTty;
if (FLAGS_syslog) {
log_flags |= brillo::kLogToSyslog;
}
brillo::InitLog(log_flags);
if (FLAGS_dev.empty()) {
LOG(ERROR) << "Must provide a non-empty device";
return EX_USAGE;
}
g2f_client::HidDevice hid_device(FLAGS_dev);
g2f_client::U2FHid u2f_hid(&hid_device);
g2f_client::U2F u2f(&u2f_hid);
const std::vector<bool> actions = {FLAGS_ping, FLAGS_wink, FLAGS_raw,
FLAGS_msg, FLAGS_reg, FLAGS_auth};
if (std::count(actions.cbegin(), actions.cend(), true) != 1) {
LOG(ERROR) << "Must specify exactly one action";
return EX_USAGE;
}
if (FLAGS_lock) {
if (FLAGS_lock_timeout < 0 || FLAGS_lock_timeout > 10) {
LOG(ERROR) << "Lock timeout must be in [0..10]";
return EX_USAGE;
}
if (!u2f_hid.Lock(static_cast<uint8_t>(FLAGS_lock_timeout))) {
return EX_SOFTWARE;
}
std::cout << "Locked for " << FLAGS_lock_timeout << " seconds."
<< std::endl;
}
if (FLAGS_ping) {
if (!u2f_hid.Ping(FLAGS_ping_size)) {
return EX_SOFTWARE;
}
std::cout << "Ping success." << std::endl;
} else if (FLAGS_wink) {
if (!u2f_hid.Wink()) {
return EX_SOFTWARE;
}
std::cout << "Wink success." << std::endl;
} else if (FLAGS_raw) {
if (!u2f_hid.Init(false /* force_realloc */)) {
return EX_SOFTWARE;
}
g2f_client::U2FHid::Command request;
if (!FLAGS_payload.empty() &&
!base::HexStringToBytes(FLAGS_payload, &request.payload)) {
LOG(ERROR) << "Failed to convert --payload to bytes";
return EX_USAGE;
}
if (FLAGS_cc < 0 || FLAGS_cc > 0xFF) {
LOG(ERROR) << "Must provide --cc in [0..255]";
return EX_USAGE;
}
request.cmd = static_cast<uint8_t>(FLAGS_cc);
g2f_client::U2FHid::Command response;
if (!u2f_hid.RawCommand(request, &response)) {
return EX_SOFTWARE;
}
std::cout << response.FullDump() << std::endl;
} else if (FLAGS_msg) {
brillo::Blob request;
if (!FLAGS_payload.empty() &&
!base::HexStringToBytes(FLAGS_payload, &request)) {
LOG(ERROR) << "Failed to convert --payload to bytes";
return EX_USAGE;
}
brillo::Blob response;
if (!u2f_hid.Msg(request, &response)) {
return EX_SOFTWARE;
}
std::cout << base::HexEncode(response.data(), response.size()) << std::endl;
} else if (FLAGS_reg) {
if (FLAGS_p1 < -1 || FLAGS_p1 > 255) {
LOG(ERROR) << "P1 value should be 0-255, or -1 for default" << std::endl;
return EX_USAGE;
}
base::Optional<uint8_t> p1;
if (FLAGS_p1 > -1) {
p1 = FLAGS_p1;
}
brillo::Blob challenge;
if (!FLAGS_challenge.empty() &&
!base::HexStringToBytes(FLAGS_challenge, &challenge)) {
LOG(ERROR) << "Failed to convert --reg_challenge to bytes" << std::endl;
return EX_USAGE;
}
brillo::Blob application;
if (!FLAGS_application.empty() &&
!base::HexStringToBytes(FLAGS_application, &application)) {
std::cout << "Failed to convert --reg_application to bytes" << std::endl;
return EX_USAGE;
}
brillo::Blob public_key;
brillo::Blob key_handle;
brillo::Blob certificate_and_signature;
if (!u2f.Register(p1, challenge, application, FLAGS_g2f, &public_key,
&key_handle, &certificate_and_signature)) {
return EX_SOFTWARE;
}
std::cout << "public_key="
<< base::HexEncode(public_key.data(), public_key.size())
<< std::endl
<< "key_handle="
<< base::HexEncode(key_handle.data(), key_handle.size())
<< std::endl
<< "certificate_and_signature="
<< base::HexEncode(certificate_and_signature.data(),
certificate_and_signature.size())
<< std::endl;
} else if (FLAGS_auth) {
if (FLAGS_p1 < -1 || FLAGS_p1 > 255) {
LOG(ERROR) << "P1 value should be 0-255, or -1 for default" << std::endl;
return EX_USAGE;
}
base::Optional<uint8_t> p1;
if (FLAGS_p1 > -1) {
p1 = FLAGS_p1;
}
brillo::Blob challenge;
if (!FLAGS_challenge.empty() &&
!base::HexStringToBytes(FLAGS_challenge, &challenge)) {
std::cout << "Failed to convert --challenge to bytes" << std::endl;
return EX_USAGE;
}
brillo::Blob application;
if (!FLAGS_application.empty() &&
!base::HexStringToBytes(FLAGS_application, &application)) {
std::cout << "Failed to convert --application to bytes" << std::endl;
return EX_USAGE;
}
brillo::Blob key_handle;
if (!FLAGS_key_handle.empty() &&
!base::HexStringToBytes(FLAGS_key_handle, &key_handle)) {
std::cout << "Failed to convert --key_handle to bytes" << std::endl;
return EX_USAGE;
}
bool presence_verified;
brillo::Blob counter;
brillo::Blob signature;
if (!u2f.Authenticate(p1, challenge, application, key_handle,
&presence_verified, &counter, &signature)) {
return EX_SOFTWARE;
}
std::cout << "presence_verified=" << presence_verified << std::endl
<< "counter=" << base::HexEncode(counter.data(), counter.size())
<< std::endl
<< "signature="
<< base::HexEncode(signature.data(), signature.size())
<< std::endl;
}
return EX_OK;
}