Skip to content

Commit 329b4d5

Browse files
committed
Add a higher-level AuthenticatorService that can query multiple backends
1 parent b4bd7bd commit 329b4d5

File tree

6 files changed

+258
-70
lines changed

6 files changed

+258
-70
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,4 @@ bitflags = "1.0"
4343
sha2 = "^0.8.2"
4444
base64 = "^0.10"
4545
env_logger = "^0.6"
46+
getopts = "^0.2"

examples/main.rs

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,19 @@
44

55
extern crate authenticator;
66
extern crate base64;
7+
extern crate env_logger;
8+
extern crate getopts;
9+
extern crate log;
710
extern crate sha2;
11+
812
use authenticator::{
9-
AuthenticatorTransports, KeyHandle, RegisterFlags, SignFlags, StatusUpdate, U2FManager,
13+
authenticatorservice::AuthenticatorService, AuthenticatorTransports, KeyHandle, RegisterFlags,
14+
SignFlags, StatusUpdate,
1015
};
16+
use getopts::Options;
1117
use sha2::{Digest, Sha256};
1218
use std::sync::mpsc::{channel, RecvError};
13-
use std::{io, thread};
14-
15-
extern crate env_logger;
16-
extern crate log;
17-
18-
macro_rules! try_or {
19-
($val:expr, $or:expr) => {
20-
match $val {
21-
Ok(v) => v,
22-
Err(e) => {
23-
return $or(e);
24-
}
25-
}
26-
};
27-
}
19+
use std::{env, io, thread};
2820

2921
fn u2f_get_key_handle_from_register_response(register_response: &[u8]) -> io::Result<Vec<u8>> {
3022
if register_response[0] != 0x05 {
@@ -42,9 +34,35 @@ fn u2f_get_key_handle_from_register_response(register_response: &[u8]) -> io::Re
4234
Ok(key_handle)
4335
}
4436

37+
fn print_usage(program: &str, opts: Options) {
38+
let brief = format!("Usage: {} [options]", program);
39+
print!("{}", opts.usage(&brief));
40+
}
41+
4542
fn main() {
4643
env_logger::init();
4744

45+
let args: Vec<String> = env::args().collect();
46+
let program = args[0].clone();
47+
48+
let mut opts = Options::new();
49+
opts.optflag("x", "no-u2f-usb-hid", "do not enable u2f-usb-hid platforms");
50+
opts.optflag("h", "help", "print this help menu");
51+
let matches = match opts.parse(&args[1..]) {
52+
Ok(m) => m,
53+
Err(f) => panic!(f.to_string()),
54+
};
55+
if matches.opt_present("help") {
56+
print_usage(&program, opts);
57+
return;
58+
}
59+
60+
let mut manager =
61+
AuthenticatorService::new().expect("The auth service should initialize safely");
62+
if !matches.opt_present("no-u2f-usb-hid") {
63+
manager.add_u2f_usb_hid_platform_transports();
64+
}
65+
4866
println!("Asking a security key to register now...");
4967
let challenge_str = format!(
5068
"{}{}",
@@ -59,7 +77,6 @@ fn main() {
5977
application.input(b"http://demo.yubico.com");
6078
let app_bytes = application.result().to_vec();
6179

62-
let manager = U2FManager::new().unwrap();
6380
let flags = RegisterFlags::empty();
6481

6582
let (status_tx, status_rx) = channel::<StatusUpdate>();
@@ -94,13 +111,12 @@ fn main() {
94111
register_tx.send(rv).unwrap();
95112
},
96113
)
97-
.unwrap();
114+
.expect("Couldn't register");
98115

99-
let register_result = try_or!(register_rx.recv(), |_| {
100-
panic!("Problem receiving, unable to continue");
101-
});
102-
let (register_data, device_info) =
103-
register_result.unwrap_or_else(|e| panic!("Registration failed: {:?}", e));
116+
let register_result = register_rx
117+
.recv()
118+
.expect("Problem receiving, unable to continue");
119+
let (register_data, device_info) = register_result.expect("Registration failed");
104120

105121
println!("Register result: {}", base64::encode(&register_data));
106122
println!("Device info: {}", &device_info);
@@ -113,25 +129,24 @@ fn main() {
113129

114130
let flags = SignFlags::empty();
115131
let (sign_tx, sign_rx) = channel();
116-
manager
117-
.sign(
118-
flags,
119-
15_000,
120-
chall_bytes,
121-
vec![app_bytes],
122-
vec![key_handle],
123-
status_tx,
124-
move |rv| {
125-
sign_tx.send(rv).unwrap();
126-
},
127-
)
128-
.unwrap();
132+
if let Err(e) = manager.sign(
133+
flags,
134+
15_000,
135+
chall_bytes,
136+
vec![app_bytes],
137+
vec![key_handle],
138+
status_tx,
139+
move |rv| {
140+
sign_tx.send(rv).unwrap();
141+
},
142+
) {
143+
panic!("Couldn't register: {:?}", e);
144+
}
129145

130-
let sign_result = try_or!(sign_rx.recv(), |_| {
131-
panic!("Problem receiving, unable to continue");
132-
});
133-
let (_, handle_used, sign_data, device_info) =
134-
sign_result.unwrap_or_else(|e| panic!("Sign failed: {:?}", e));
146+
let sign_result = sign_rx
147+
.recv()
148+
.expect("Problem receiving, unable to continue");
149+
let (_, handle_used, sign_data, device_info) = sign_result.expect("Sign failed");
135150

136151
println!("Sign result: {}", base64::encode(&sign_data));
137152
println!("Key handle used: {}", base64::encode(&handle_used));

src/authenticatorservice.rs

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
use std::io;
6+
use std::sync::mpsc::Sender;
7+
8+
use crate::consts::PARAMETER_SIZE;
9+
use crate::util::StateCallback;
10+
11+
pub trait AuthenticatorTransport {
12+
/// The implementation of this method must return quickly and should
13+
/// report its status via the status and callback methods
14+
fn register(
15+
&self,
16+
flags: crate::RegisterFlags,
17+
timeout: u64,
18+
challenge: Vec<u8>,
19+
application: crate::AppId,
20+
key_handles: Vec<crate::KeyHandle>,
21+
status: Sender<crate::StatusUpdate>,
22+
callback: StateCallback<Result<crate::RegisterResult, crate::Error>>,
23+
) -> Result<(), crate::Error>;
24+
25+
/// The implementation of this method must return quickly and should
26+
/// report its status via the status and callback methods
27+
fn sign(
28+
&self,
29+
flags: crate::SignFlags,
30+
timeout: u64,
31+
challenge: Vec<u8>,
32+
app_ids: Vec<crate::AppId>,
33+
key_handles: Vec<crate::KeyHandle>,
34+
status: Sender<crate::StatusUpdate>,
35+
callback: StateCallback<Result<crate::SignResult, crate::Error>>,
36+
) -> Result<(), crate::Error>;
37+
38+
fn cancel(&self) -> Result<(), crate::Error>;
39+
}
40+
41+
pub struct AuthenticatorService {
42+
transports: Vec<Box<dyn AuthenticatorTransport>>,
43+
}
44+
45+
impl AuthenticatorService {
46+
pub fn new() -> io::Result<Self> {
47+
Ok(Self {
48+
transports: Vec::new(),
49+
})
50+
}
51+
52+
/// Add any detected platform transports
53+
pub fn add_detected_transports(&mut self) {
54+
self.add_u2f_usb_hid_platform_transports();
55+
}
56+
57+
pub fn add_u2f_usb_hid_platform_transports(&mut self) {
58+
if let Ok(u2fhid) = crate::U2FManager::new() {
59+
self.transports.push(Box::new(u2fhid));
60+
}
61+
}
62+
63+
pub fn register<F>(
64+
&self,
65+
flags: crate::RegisterFlags,
66+
timeout: u64,
67+
challenge: Vec<u8>,
68+
application: crate::AppId,
69+
key_handles: Vec<crate::KeyHandle>,
70+
status: Sender<crate::StatusUpdate>,
71+
callback: F,
72+
) -> Result<(), crate::Error>
73+
where
74+
F: Fn(Result<crate::RegisterResult, crate::Error>),
75+
F: Send + 'static,
76+
{
77+
if challenge.len() != PARAMETER_SIZE || application.len() != PARAMETER_SIZE {
78+
return Err(crate::Error::Unknown);
79+
}
80+
81+
for key_handle in &key_handles {
82+
if key_handle.credential.len() > 256 {
83+
return Err(crate::Error::Unknown);
84+
}
85+
}
86+
87+
if self.transports.is_empty() {
88+
return Err(crate::Error::NotSupported);
89+
}
90+
91+
let callback = StateCallback::new(Box::new(callback));
92+
93+
for transport in &self.transports {
94+
transport.register(
95+
flags.clone(),
96+
timeout,
97+
challenge.clone(),
98+
application.clone(),
99+
key_handles.clone(),
100+
status.clone(),
101+
callback.clone(),
102+
)?;
103+
}
104+
105+
Ok(())
106+
}
107+
108+
pub fn sign<F>(
109+
&self,
110+
flags: crate::SignFlags,
111+
timeout: u64,
112+
challenge: Vec<u8>,
113+
app_ids: Vec<crate::AppId>,
114+
key_handles: Vec<crate::KeyHandle>,
115+
status: Sender<crate::StatusUpdate>,
116+
callback: F,
117+
) -> Result<(), crate::Error>
118+
where
119+
F: Fn(Result<crate::SignResult, crate::Error>),
120+
F: Send + 'static,
121+
{
122+
if challenge.len() != PARAMETER_SIZE {
123+
return Err(crate::Error::Unknown);
124+
}
125+
126+
if app_ids.is_empty() {
127+
return Err(crate::Error::Unknown);
128+
}
129+
130+
for app_id in &app_ids {
131+
if app_id.len() != PARAMETER_SIZE {
132+
return Err(crate::Error::Unknown);
133+
}
134+
}
135+
136+
for key_handle in &key_handles {
137+
if key_handle.credential.len() > 256 {
138+
return Err(crate::Error::Unknown);
139+
}
140+
}
141+
142+
if self.transports.is_empty() {
143+
return Err(crate::Error::NotSupported);
144+
}
145+
146+
let callback = StateCallback::new(Box::new(callback));
147+
148+
for transport in &self.transports {
149+
transport.sign(
150+
flags.clone(),
151+
timeout,
152+
challenge.clone(),
153+
app_ids.clone(),
154+
key_handles.clone(),
155+
status.clone(),
156+
callback.clone(),
157+
)?;
158+
}
159+
160+
Ok(())
161+
}
162+
163+
pub fn cancel(&self) -> Result<(), crate::Error> {
164+
if self.transports.is_empty() {
165+
return Err(crate::Error::NotSupported);
166+
}
167+
168+
for transport in &self.transports {
169+
transport.cancel()?;
170+
}
171+
172+
Ok(())
173+
}
174+
}

0 commit comments

Comments
 (0)