/* 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, statecallback::StateCallback, Pin, StatusPinUv, StatusUpdate, }; use getopts::Options; use std::sync::mpsc::{channel, RecvError}; use std::{env, thread}; 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 = env::args().collect(); let program = args[0].clone(); let mut opts = Options::new(); opts.optflag("x", "no-u2f-usb-hid", "do not enable u2f-usb-hid platforms"); 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"); if !matches.opt_present("no-u2f-usb-hid") { manager.add_u2f_usb_hid_platform_transports(); } let timeout_ms = match matches.opt_get_default::("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; } }; let new_pin = rpassword::prompt_password_stderr("Enter new PIN: ").expect("Failed to read PIN"); let repeat_new_pin = rpassword::prompt_password_stderr("Enter it again: ").expect("Failed to read PIN"); if new_pin != repeat_new_pin { println!("PINs did not match!"); return; } let (status_tx, status_rx) = channel::(); thread::spawn(move || loop { match status_rx.recv() { Ok(StatusUpdate::InteractiveManagement(..)) => { panic!("STATUS: This can't happen when doing non-interactive usage"); } Ok(StatusUpdate::DeviceAvailable { dev_info }) => { println!("STATUS: device available: {dev_info}") } Ok(StatusUpdate::DeviceUnavailable { dev_info }) => { println!("STATUS: device unavailable: {dev_info}") } Ok(StatusUpdate::Success { dev_info }) => { println!("STATUS: success using device: {dev_info}"); } Ok(StatusUpdate::SelectDeviceNotice) => { println!("STATUS: Please select a device by touching one of them."); } Ok(StatusUpdate::DeviceSelected(dev_info)) => { println!("STATUS: Continuing with device: {dev_info}"); } Ok(StatusUpdate::PresenceRequired) => { println!("STATUS: waiting for user presence"); } Ok(StatusUpdate::PinUvError(StatusPinUv::PinRequired(sender))) => { let raw_pin = rpassword::prompt_password_stderr("Enter PIN: ").expect("Failed to read PIN"); sender.send(Pin::new(&raw_pin)).expect("Failed to send PIN"); continue; } Ok(StatusUpdate::PinUvError(StatusPinUv::InvalidPin(sender, attempts))) => { println!( "Wrong PIN! {}", attempts.map_or("Try again.".to_string(), |a| format!( "You have {a} attempts left." )) ); let raw_pin = rpassword::prompt_password_stderr("Enter PIN: ").expect("Failed to read PIN"); sender.send(Pin::new(&raw_pin)).expect("Failed to send PIN"); continue; } Ok(StatusUpdate::PinUvError(StatusPinUv::PinAuthBlocked)) => { panic!("Too many failed attempts in one row. Your device has been temporarily blocked. Please unplug it and plug in again.") } Ok(StatusUpdate::PinUvError(StatusPinUv::PinBlocked)) => { panic!("Too many failed attempts. Your device has been blocked. Reset it.") } Ok(StatusUpdate::PinUvError(StatusPinUv::InvalidUv(attempts))) => { println!( "Wrong UV! {}", attempts.map_or("Try again.".to_string(), |a| format!( "You have {a} attempts left." )) ); continue; } Ok(StatusUpdate::PinUvError(StatusPinUv::UvBlocked)) => { println!("Too many failed UV-attempts."); continue; } Ok(StatusUpdate::PinUvError(e)) => { panic!("Unexpected error: {:?}", e) } Err(RecvError) => { println!("STATUS: end"); return; } } }); 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.set_pin(timeout_ms, Pin::new(&new_pin), status_tx, callback) { panic!("Couldn't call set_pin: {:?}", e); }; let reset_result = reset_rx .recv() .expect("Problem receiving, unable to continue"); match reset_result { Ok(()) => { println!("PIN successfully set!"); } Err(e) => panic!("Setting PIN failed: {:?}", e), }; }