summaryrefslogtreecommitdiffstats
path: root/third_party/rust/authenticator/examples/reset.rs
blob: 867ab5448f3e70725217c4d81052b4ee45a99747 (plain)
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),
    };
}