1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use authenticator::{
authenticatorservice::AuthenticatorService,
ctap2::commands::StatusCode,
errors::{AuthenticatorError, CommandError, HIDError},
statecallback::StateCallback,
StatusUpdate,
};
use getopts::Options;
use std::env;
use std::sync::mpsc::{channel, RecvError};
fn print_usage(program: &str, opts: Options) {
let brief = format!("Usage: {program} [options]");
print!("{}", opts.usage(&brief));
}
fn main() {
env_logger::init();
let args: Vec<String> = env::args().collect();
let program = args[0].clone();
let mut opts = Options::new();
opts.optflag("h", "help", "print this help menu").optopt(
"t",
"timeout",
"timeout in seconds",
"SEC",
);
opts.optflag("h", "help", "print this help menu");
let matches = match opts.parse(&args[1..]) {
Ok(m) => m,
Err(f) => panic!("{}", f.to_string()),
};
if matches.opt_present("help") {
print_usage(&program, opts);
return;
}
let mut manager =
AuthenticatorService::new().expect("The auth service should initialize safely");
manager.add_u2f_usb_hid_platform_transports();
let timeout_ms = match matches.opt_get_default::<u64>("timeout", 25) {
Ok(timeout_s) => {
println!("Using {}s as the timeout", &timeout_s);
timeout_s * 1_000
}
Err(e) => {
println!("{e}");
print_usage(&program, opts);
return;
}
};
println!(
"NOTE: Please unplug all devices, type in 'yes' and plug in the device that should be reset."
);
loop {
let mut s = String::new();
println!("ATTENTION: Resetting a device will wipe all credentials! Do you wish to continue? [yes/N]");
std::io::stdin()
.read_line(&mut s)
.expect("Did not enter a correct string");
let trimmed = s.trim();
if trimmed.is_empty() || trimmed == "N" || trimmed == "n" {
println!("Exiting without reset.");
return;
}
if trimmed == "y" {
println!("Please type in the whole word 'yes'");
continue;
}
if trimmed == "yes" {
break;
}
}
let (status_tx, status_rx) = channel::<StatusUpdate>();
let (reset_tx, reset_rx) = channel();
let rs_tx = reset_tx;
let callback = StateCallback::new(Box::new(move |rv| {
let _ = rs_tx.send(rv);
}));
if let Err(e) = manager.reset(timeout_ms, status_tx, callback) {
panic!("Couldn't register: {:?}", e);
};
loop {
match status_rx.recv() {
Ok(StatusUpdate::SelectDeviceNotice) => {
println!("ERROR: Please unplug all other tokens that should not be reset!");
// Needed to give the tokens enough time to start blinking
// otherwise we may cancel pre-maturely and this binary will hang
std::thread::sleep(std::time::Duration::from_millis(200));
manager.cancel().unwrap();
return;
}
Ok(StatusUpdate::PresenceRequired) => {
println!("STATUS: waiting for user presence");
break;
}
Ok(StatusUpdate::PinUvError(..)) => panic!("Reset should never ask for a PIN!"),
Ok(_) => { /* Ignore all other updates */ }
Err(RecvError) => {
println!("RecvError");
return;
}
}
}
let reset_result = reset_rx
.recv()
.expect("Problem receiving, unable to continue");
match reset_result {
Ok(()) => {
println!("Token successfully reset!");
}
Err(AuthenticatorError::HIDError(HIDError::Command(CommandError::StatusCode(
StatusCode::NotAllowed,
_,
)))) => {
println!("Resetting is only allowed within the first 10 seconds after powering up.");
println!("Please unplug your device, plug it back in and try again.");
}
Err(e) => panic!("Reset failed: {:?}", e),
};
}
|