| // 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; |
| } |