Bug 1400668 - Process key handle exclusion list when registering a token r=jcj
This commit is contained in:
Родитель
d5e05c2fd0
Коммит
4d965aacd7
|
@ -52,9 +52,13 @@ fn main() {
|
|||
|
||||
let (tx, rx) = channel();
|
||||
manager
|
||||
.register(15_000, chall_bytes.clone(), app_bytes.clone(), move |rv| {
|
||||
tx.send(rv.unwrap()).unwrap();
|
||||
})
|
||||
.register(
|
||||
15_000,
|
||||
chall_bytes.clone(),
|
||||
app_bytes.clone(),
|
||||
vec![],
|
||||
move |rv| { tx.send(rv.unwrap()).unwrap(); },
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let register_data = rx.recv().unwrap();
|
||||
|
|
|
@ -115,6 +115,7 @@ pub unsafe extern "C" fn rust_u2f_mgr_register(
|
|||
challenge_len: usize,
|
||||
application_ptr: *const u8,
|
||||
application_len: usize,
|
||||
khs: *const U2FKeyHandles,
|
||||
) -> u64 {
|
||||
if mgr.is_null() {
|
||||
return 0;
|
||||
|
@ -127,9 +128,10 @@ pub unsafe extern "C" fn rust_u2f_mgr_register(
|
|||
|
||||
let challenge = from_raw(challenge_ptr, challenge_len);
|
||||
let application = from_raw(application_ptr, application_len);
|
||||
let key_handles = (*khs).clone();
|
||||
|
||||
let tid = new_tid();
|
||||
let res = (*mgr).register(timeout, challenge, application, move |rv| {
|
||||
let res = (*mgr).register(timeout, challenge, application, key_handles, move |rv| {
|
||||
if let Ok(registration) = rv {
|
||||
let mut result = U2FResult::new();
|
||||
result.insert(RESBUF_ID_REGISTRATION, registration);
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
/* 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 std::collections::{HashMap, HashSet};
|
||||
use std::collections::hash_map::IterMut;
|
||||
use std::hash::Hash;
|
||||
|
||||
// The KeyHandleMatcher is a helper class that tracks which key handles belong
|
||||
// to which device (token). It is initialized with a list of key handles we
|
||||
// were passed by the U2F or WebAuthn API.
|
||||
//
|
||||
// 'a = the lifetime of key handles, we don't want to own them
|
||||
// K = the type we use to identify devices on this platform (path, device_ref)
|
||||
pub struct KeyHandleMatcher<'a, K> {
|
||||
key_handles: &'a Vec<Vec<u8>>,
|
||||
map: HashMap<K, Vec<&'a Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl<'a, K> KeyHandleMatcher<'a, K>
|
||||
where
|
||||
K: Clone + Eq + Hash,
|
||||
{
|
||||
pub fn new(key_handles: &'a Vec<Vec<u8>>) -> Self {
|
||||
Self {
|
||||
key_handles,
|
||||
map: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// `update()` will iterate all devices and ignore the ones we've already
|
||||
// checked. It will then call the given closure exactly once for each key
|
||||
// handle and device combination to let the caller decide whether we have
|
||||
// a match.
|
||||
//
|
||||
// If a device was removed since the last `update()` call we'll remove it
|
||||
// from the internal map as well to ensure we query a device that reuses a
|
||||
// dev_ref or path next time.
|
||||
//
|
||||
// TODO In theory, it might be possible to replace a token between
|
||||
// `update()` calls while reusing the device_ref/path. We should refactor
|
||||
// this part of the code and probably merge the KeyHandleMatcher into the
|
||||
// DeviceMap and Monitor somehow so that we query key handle/token
|
||||
// assignments right when we start tracking a new device.
|
||||
pub fn update<F, V>(&mut self, devices: IterMut<K, V>, is_match: F)
|
||||
where
|
||||
F: Fn(&mut V, &Vec<u8>) -> bool,
|
||||
{
|
||||
// Collect all currently known device references.
|
||||
let mut to_remove: HashSet<K> = self.map.keys().cloned().collect();
|
||||
|
||||
for (dev_ref, device) in devices {
|
||||
// This device is still connected.
|
||||
to_remove.remove(dev_ref);
|
||||
|
||||
// Skip devices we've already seen.
|
||||
if self.map.contains_key(dev_ref) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut matches = vec![];
|
||||
|
||||
// Collect all matching key handles.
|
||||
for key_handle in self.key_handles {
|
||||
if is_match(device, key_handle) {
|
||||
matches.push(key_handle);
|
||||
}
|
||||
}
|
||||
|
||||
self.map.insert((*dev_ref).clone(), matches);
|
||||
}
|
||||
|
||||
// Remove devices that disappeared since the last call.
|
||||
for dev_ref in to_remove {
|
||||
self.map.remove(&dev_ref);
|
||||
}
|
||||
}
|
||||
|
||||
// `get()` allows retrieving key handle/token assignments that were
|
||||
// process by calls to `update()` before.
|
||||
pub fn get(&self, dev_ref: &K) -> &Vec<&'a Vec<u8>> {
|
||||
self.map.get(dev_ref).expect("unknown device")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::KeyHandleMatcher;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
// Three key handles.
|
||||
let khs = vec![
|
||||
vec![0x01, 0x02, 0x03, 0x04],
|
||||
vec![0x02, 0x03, 0x04, 0x05],
|
||||
vec![0x03, 0x04, 0x05, 0x06],
|
||||
];
|
||||
|
||||
// Start with three devices.
|
||||
let mut map = HashMap::new();
|
||||
map.insert("device1", 1);
|
||||
map.insert("device2", 2);
|
||||
map.insert("device3", 3);
|
||||
|
||||
// Assign key handles to devices.
|
||||
let mut khm = KeyHandleMatcher::new(&khs);
|
||||
khm.update(map.iter_mut(), |device, key_handle| *device > key_handle[0]);
|
||||
|
||||
// Check assignments.
|
||||
assert!(khm.get(&"device1").is_empty());
|
||||
assert_eq!(*khm.get(&"device2"), vec![&vec![0x01, 0x02, 0x03, 0x04]]);
|
||||
assert_eq!(
|
||||
*khm.get(&"device3"),
|
||||
vec![&vec![0x01, 0x02, 0x03, 0x04], &vec![0x02, 0x03, 0x04, 0x05]]
|
||||
);
|
||||
|
||||
// Check we don't check a device twice.
|
||||
map.insert("device4", 4);
|
||||
khm.update(map.iter_mut(), |device, key_handle| {
|
||||
assert_eq!(*device, 4);
|
||||
key_handle[0] & 1 == 1
|
||||
});
|
||||
|
||||
// Check assignments.
|
||||
assert_eq!(
|
||||
*khm.get(&"device4"),
|
||||
vec![&vec![0x01, 0x02, 0x03, 0x04], &vec![0x03, 0x04, 0x05, 0x06]]
|
||||
);
|
||||
|
||||
// Remove device #2.
|
||||
map.remove("device2");
|
||||
khm.update(map.iter_mut(), |_, _| {
|
||||
assert!(false);
|
||||
false
|
||||
});
|
||||
|
||||
// Re-insert device #2 matching different key handles.
|
||||
map.insert("device2", 2);
|
||||
khm.update(map.iter_mut(), |device, _| {
|
||||
assert_eq!(*device, 2);
|
||||
true
|
||||
});
|
||||
|
||||
// Check assignments.
|
||||
assert_eq!(
|
||||
*khm.get(&"device2"),
|
||||
vec![
|
||||
&vec![0x01, 0x02, 0x03, 0x04],
|
||||
&vec![0x02, 0x03, 0x04, 0x05],
|
||||
&vec![0x03, 0x04, 0x05, 0x06],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -35,6 +35,7 @@ extern crate boxfnonce;
|
|||
extern crate runloop;
|
||||
|
||||
mod consts;
|
||||
mod khmatcher;
|
||||
mod u2ftypes;
|
||||
mod u2fprotocol;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* 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 std::collections::hash_map::ValuesMut;
|
||||
use std::collections::hash_map::IterMut;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsString;
|
||||
|
||||
|
@ -19,8 +19,8 @@ impl DeviceMap {
|
|||
Self { map: HashMap::new() }
|
||||
}
|
||||
|
||||
pub fn values_mut(&mut self) -> ValuesMut<OsString, Device> {
|
||||
self.map.values_mut()
|
||||
pub fn iter_mut(&mut self) -> IterMut<OsString, Device> {
|
||||
self.map.iter_mut()
|
||||
}
|
||||
|
||||
pub fn process_event(&mut self, event: Event) {
|
||||
|
|
|
@ -11,6 +11,7 @@ mod hidraw;
|
|||
mod monitor;
|
||||
|
||||
use consts::PARAMETER_SIZE;
|
||||
use khmatcher::KeyHandleMatcher;
|
||||
use runloop::RunLoop;
|
||||
use util::{io_err, OnceCallback};
|
||||
use u2fprotocol::{u2f_is_keyhandle_valid, u2f_register, u2f_sign};
|
||||
|
@ -33,6 +34,7 @@ impl PlatformManager {
|
|||
timeout: u64,
|
||||
challenge: Vec<u8>,
|
||||
application: Vec<u8>,
|
||||
key_handles: Vec<Vec<u8>>,
|
||||
callback: OnceCallback<Vec<u8>>,
|
||||
) {
|
||||
// Abort any prior register/sign calls.
|
||||
|
@ -43,7 +45,8 @@ impl PlatformManager {
|
|||
let thread = RunLoop::new_with_timeout(
|
||||
move |alive| {
|
||||
let mut devices = DeviceMap::new();
|
||||
let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
|
||||
let monitor = try_or!(Monitor::new(), |e| callback.call(Err(e)));
|
||||
let mut matches = KeyHandleMatcher::new(&key_handles);
|
||||
|
||||
while alive() && monitor.alive() {
|
||||
// Add/remove devices.
|
||||
|
@ -51,13 +54,22 @@ impl PlatformManager {
|
|||
devices.process_event(event);
|
||||
}
|
||||
|
||||
// Try to register each device.
|
||||
for device in devices.values_mut() {
|
||||
// Query newly added devices.
|
||||
matches.update(devices.iter_mut(), |device, key_handle| {
|
||||
u2f_is_keyhandle_valid(device, &challenge, &application, key_handle)
|
||||
.unwrap_or(false /* no match on failure */)
|
||||
});
|
||||
|
||||
// Iterate all devices that don't match any of the handles
|
||||
// in the exclusion list and try to register.
|
||||
for (path, device) in devices.iter_mut() {
|
||||
if matches.get(path).is_empty() {
|
||||
if let Ok(bytes) = u2f_register(device, &challenge, &application) {
|
||||
callback.call(Ok(bytes));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait a little before trying again.
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
|
@ -90,7 +102,8 @@ impl PlatformManager {
|
|||
let thread = RunLoop::new_with_timeout(
|
||||
move |alive| {
|
||||
let mut devices = DeviceMap::new();
|
||||
let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
|
||||
let monitor = try_or!(Monitor::new(), |e| callback.call(Err(e)));
|
||||
let mut matches = KeyHandleMatcher::new(&key_handles);
|
||||
|
||||
while alive() && monitor.alive() {
|
||||
// Add/remove devices.
|
||||
|
@ -98,22 +111,30 @@ impl PlatformManager {
|
|||
devices.process_event(event);
|
||||
}
|
||||
|
||||
// Try signing with each device.
|
||||
for key_handle in &key_handles {
|
||||
for device in devices.values_mut() {
|
||||
// Check if they key handle belongs to the current device.
|
||||
let is_valid = match u2f_is_keyhandle_valid(
|
||||
device,
|
||||
&challenge,
|
||||
&application,
|
||||
key_handle,
|
||||
) {
|
||||
Ok(valid) => valid,
|
||||
Err(_) => continue, // Skip this device for now.
|
||||
};
|
||||
// Query newly added devices.
|
||||
matches.update(devices.iter_mut(), |device, key_handle| {
|
||||
u2f_is_keyhandle_valid(device, &challenge, &application, key_handle)
|
||||
.unwrap_or(false /* no match on failure */)
|
||||
});
|
||||
|
||||
if is_valid {
|
||||
// If yes, try to sign.
|
||||
// Iterate all devices.
|
||||
for (path, device) in devices.iter_mut() {
|
||||
let key_handles = matches.get(path);
|
||||
|
||||
// If the device matches none of the given key handles
|
||||
// then just make it blink with bogus data.
|
||||
if key_handles.is_empty() {
|
||||
let blank = vec![0u8; PARAMETER_SIZE];
|
||||
if let Ok(_) = u2f_register(device, &blank, &blank) {
|
||||
callback.call(Err(io_err("invalid key")));
|
||||
return;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, try to sign.
|
||||
for key_handle in key_handles {
|
||||
if let Ok(bytes) = u2f_sign(
|
||||
device,
|
||||
&challenge,
|
||||
|
@ -121,17 +142,9 @@ impl PlatformManager {
|
|||
key_handle,
|
||||
)
|
||||
{
|
||||
callback.call(Ok((key_handle.clone(), bytes)));
|
||||
callback.call(Ok((key_handle.to_vec(), bytes)));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// If no, keep registering and blinking with bogus data
|
||||
let blank = vec![0u8; PARAMETER_SIZE];
|
||||
if let Ok(_) = u2f_register(device, &blank, &blank) {
|
||||
callback.call(Err(io_err("invalid key")));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* 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 std::collections::hash_map::ValuesMut;
|
||||
use std::collections::hash_map::IterMut;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use u2fprotocol::u2f_init_device;
|
||||
|
@ -20,8 +20,8 @@ impl DeviceMap {
|
|||
Self { map: HashMap::new() }
|
||||
}
|
||||
|
||||
pub fn values_mut(&mut self) -> ValuesMut<IOHIDDeviceRef, Device> {
|
||||
self.map.values_mut()
|
||||
pub fn iter_mut(&mut self) -> IterMut<IOHIDDeviceRef, Device> {
|
||||
self.map.iter_mut()
|
||||
}
|
||||
|
||||
pub fn process_event(&mut self, event: Event) {
|
||||
|
|
|
@ -18,6 +18,7 @@ use self::devicemap::DeviceMap;
|
|||
use self::monitor::Monitor;
|
||||
|
||||
use consts::PARAMETER_SIZE;
|
||||
use khmatcher::KeyHandleMatcher;
|
||||
use runloop::RunLoop;
|
||||
use util::{io_err, OnceCallback};
|
||||
use u2fprotocol::{u2f_register, u2f_sign, u2f_is_keyhandle_valid};
|
||||
|
@ -38,6 +39,7 @@ impl PlatformManager {
|
|||
timeout: u64,
|
||||
challenge: Vec<u8>,
|
||||
application: Vec<u8>,
|
||||
key_handles: Vec<Vec<u8>>,
|
||||
callback: OnceCallback<Vec<u8>>,
|
||||
) {
|
||||
// Abort any prior register/sign calls.
|
||||
|
@ -48,19 +50,29 @@ impl PlatformManager {
|
|||
let thread = RunLoop::new_with_timeout(
|
||||
move |alive| {
|
||||
let mut devices = DeviceMap::new();
|
||||
let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
|
||||
let monitor = try_or!(Monitor::new(), |e| callback.call(Err(e)));
|
||||
let mut matches = KeyHandleMatcher::new(&key_handles);
|
||||
|
||||
'top: while alive() && monitor.alive() {
|
||||
for event in monitor.events() {
|
||||
devices.process_event(event);
|
||||
}
|
||||
|
||||
for device in devices.values_mut() {
|
||||
// Caller asked us to register, so the first token that does wins
|
||||
// Query newly added devices.
|
||||
matches.update(devices.iter_mut(), |device, key_handle| {
|
||||
u2f_is_keyhandle_valid(device, &challenge, &application, key_handle)
|
||||
.unwrap_or(false /* no match on failure */)
|
||||
});
|
||||
|
||||
// Iterate all devices that don't match any of the handles
|
||||
// in the exclusion list and try to register.
|
||||
for (path, device) in devices.iter_mut() {
|
||||
if matches.get(path).is_empty() {
|
||||
if let Ok(bytes) = u2f_register(device, &challenge, &application) {
|
||||
callback.call(Ok(bytes));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if monitor.events has any hotplug events that we'll need
|
||||
// to handle
|
||||
|
@ -101,28 +113,38 @@ impl PlatformManager {
|
|||
let thread = RunLoop::new_with_timeout(
|
||||
move |alive| {
|
||||
let mut devices = DeviceMap::new();
|
||||
let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
|
||||
let monitor = try_or!(Monitor::new(), |e| callback.call(Err(e)));
|
||||
let mut matches = KeyHandleMatcher::new(&key_handles);
|
||||
|
||||
'top: while alive() && monitor.alive() {
|
||||
for event in monitor.events() {
|
||||
devices.process_event(event);
|
||||
}
|
||||
|
||||
for key_handle in &key_handles {
|
||||
for device in devices.values_mut() {
|
||||
// Determine if this key handle belongs to this token
|
||||
let is_valid = match u2f_is_keyhandle_valid(
|
||||
device,
|
||||
&challenge,
|
||||
&application,
|
||||
key_handle,
|
||||
) {
|
||||
Ok(result) => result,
|
||||
Err(_) => continue, // Skip this device for now.
|
||||
};
|
||||
// Query newly added devices.
|
||||
matches.update(devices.iter_mut(), |device, key_handle| {
|
||||
u2f_is_keyhandle_valid(device, &challenge, &application, key_handle)
|
||||
.unwrap_or(false /* no match on failure */)
|
||||
});
|
||||
|
||||
if is_valid {
|
||||
// It does, we can sign
|
||||
// Iterate all devices.
|
||||
for (path, device) in devices.iter_mut() {
|
||||
let key_handles = matches.get(path);
|
||||
|
||||
// If the device matches none of the given key handles
|
||||
// then just make it blink with bogus data.
|
||||
if key_handles.is_empty() {
|
||||
let blank = vec![0u8; PARAMETER_SIZE];
|
||||
if let Ok(_) = u2f_register(device, &blank, &blank) {
|
||||
callback.call(Err(io_err("invalid key")));
|
||||
return;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, try to sign.
|
||||
for key_handle in key_handles {
|
||||
if let Ok(bytes) = u2f_sign(
|
||||
device,
|
||||
&challenge,
|
||||
|
@ -130,17 +152,7 @@ impl PlatformManager {
|
|||
key_handle,
|
||||
)
|
||||
{
|
||||
callback.call(Ok((key_handle.clone(), bytes)));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// If doesn't, so blink anyway (using bogus data)
|
||||
let blank = vec![0u8; PARAMETER_SIZE];
|
||||
|
||||
if u2f_register(device, &blank, &blank).is_ok() {
|
||||
// If the user selects this token that can't satisfy, it's an
|
||||
// error
|
||||
callback.call(Err(io_err("invalid key")));
|
||||
callback.call(Ok((key_handle.to_vec(), bytes)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +164,6 @@ impl PlatformManager {
|
|||
continue 'top;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ pub enum QueueAction {
|
|||
timeout: u64,
|
||||
challenge: Vec<u8>,
|
||||
application: Vec<u8>,
|
||||
key_handles: Vec<Vec<u8>>,
|
||||
callback: OnceCallback<Vec<u8>>,
|
||||
},
|
||||
Sign {
|
||||
|
@ -47,10 +48,11 @@ impl U2FManager {
|
|||
timeout,
|
||||
challenge,
|
||||
application,
|
||||
key_handles,
|
||||
callback,
|
||||
}) => {
|
||||
// This must not block, otherwise we can't cancel.
|
||||
pm.register(timeout, challenge, application, callback);
|
||||
pm.register(timeout, challenge, application, key_handles, callback);
|
||||
}
|
||||
Ok(QueueAction::Sign {
|
||||
timeout,
|
||||
|
@ -89,6 +91,7 @@ impl U2FManager {
|
|||
timeout: u64,
|
||||
challenge: Vec<u8>,
|
||||
application: Vec<u8>,
|
||||
key_handles: Vec<Vec<u8>>,
|
||||
callback: F,
|
||||
) -> io::Result<()>
|
||||
where
|
||||
|
@ -102,12 +105,22 @@ impl U2FManager {
|
|||
));
|
||||
}
|
||||
|
||||
for key_handle in &key_handles {
|
||||
if key_handle.len() > 256 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Key handle too large",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let callback = OnceCallback::new(callback);
|
||||
let action = QueueAction::Register {
|
||||
timeout: timeout,
|
||||
challenge: challenge,
|
||||
application: application,
|
||||
callback: callback,
|
||||
timeout,
|
||||
challenge,
|
||||
application,
|
||||
key_handles,
|
||||
callback,
|
||||
};
|
||||
self.tx.send(action).map_err(to_io_err)
|
||||
}
|
||||
|
@ -149,11 +162,11 @@ impl U2FManager {
|
|||
|
||||
let callback = OnceCallback::new(callback);
|
||||
let action = QueueAction::Sign {
|
||||
timeout: timeout,
|
||||
challenge: challenge,
|
||||
application: application,
|
||||
key_handles: key_handles,
|
||||
callback: callback,
|
||||
timeout,
|
||||
challenge,
|
||||
application,
|
||||
key_handles,
|
||||
callback,
|
||||
};
|
||||
self.tx.send(action).map_err(to_io_err)
|
||||
}
|
||||
|
|
|
@ -45,7 +45,8 @@ uint64_t rust_u2f_mgr_register(rust_u2f_manager* mgr,
|
|||
const uint8_t* challenge_ptr,
|
||||
size_t challenge_len,
|
||||
const uint8_t* application_ptr,
|
||||
size_t application_len);
|
||||
size_t application_len,
|
||||
const rust_u2f_key_handles* khs);
|
||||
|
||||
uint64_t rust_u2f_mgr_sign(rust_u2f_manager* mgr,
|
||||
uint64_t timeout,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* 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 std::collections::hash_map::ValuesMut;
|
||||
use std::collections::hash_map::IterMut;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use platform::device::Device;
|
||||
|
@ -18,8 +18,8 @@ impl DeviceMap {
|
|||
Self { map: HashMap::new() }
|
||||
}
|
||||
|
||||
pub fn values_mut(&mut self) -> ValuesMut<String, Device> {
|
||||
self.map.values_mut()
|
||||
pub fn iter_mut(&mut self) -> IterMut<String, Device> {
|
||||
self.map.iter_mut()
|
||||
}
|
||||
|
||||
pub fn process_event(&mut self, event: Event) {
|
||||
|
|
|
@ -11,6 +11,7 @@ mod monitor;
|
|||
mod winapi;
|
||||
|
||||
use consts::PARAMETER_SIZE;
|
||||
use khmatcher::KeyHandleMatcher;
|
||||
use runloop::RunLoop;
|
||||
use util::{io_err, OnceCallback};
|
||||
use u2fprotocol::{u2f_register, u2f_sign, u2f_is_keyhandle_valid};
|
||||
|
@ -33,6 +34,7 @@ impl PlatformManager {
|
|||
timeout: u64,
|
||||
challenge: Vec<u8>,
|
||||
application: Vec<u8>,
|
||||
key_handles: Vec<Vec<u8>>,
|
||||
callback: OnceCallback<Vec<u8>>,
|
||||
) {
|
||||
// Abort any prior register/sign calls.
|
||||
|
@ -43,7 +45,8 @@ impl PlatformManager {
|
|||
let thread = RunLoop::new_with_timeout(
|
||||
move |alive| {
|
||||
let mut devices = DeviceMap::new();
|
||||
let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
|
||||
let monitor = try_or!(Monitor::new(), |e| callback.call(Err(e)));
|
||||
let mut matches = KeyHandleMatcher::new(&key_handles);
|
||||
|
||||
while alive() && monitor.alive() {
|
||||
// Add/remove devices.
|
||||
|
@ -51,13 +54,22 @@ impl PlatformManager {
|
|||
devices.process_event(event);
|
||||
}
|
||||
|
||||
// Try to register each device.
|
||||
for device in devices.values_mut() {
|
||||
// Query newly added devices.
|
||||
matches.update(devices.iter_mut(), |device, key_handle| {
|
||||
u2f_is_keyhandle_valid(device, &challenge, &application, key_handle)
|
||||
.unwrap_or(false /* no match on failure */)
|
||||
});
|
||||
|
||||
// Iterate all devices that don't match any of the handles
|
||||
// in the exclusion list and try to register.
|
||||
for (path, device) in devices.iter_mut() {
|
||||
if matches.get(path).is_empty() {
|
||||
if let Ok(bytes) = u2f_register(device, &challenge, &application) {
|
||||
callback.call(Ok(bytes));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait a little before trying again.
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
|
@ -89,7 +101,8 @@ impl PlatformManager {
|
|||
let thread = RunLoop::new_with_timeout(
|
||||
move |alive| {
|
||||
let mut devices = DeviceMap::new();
|
||||
let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
|
||||
let monitor = try_or!(Monitor::new(), |e| callback.call(Err(e)));
|
||||
let mut matches = KeyHandleMatcher::new(&key_handles);
|
||||
|
||||
while alive() && monitor.alive() {
|
||||
// Add/remove devices.
|
||||
|
@ -97,39 +110,39 @@ impl PlatformManager {
|
|||
devices.process_event(event);
|
||||
}
|
||||
|
||||
// Try signing with each device.
|
||||
for key_handle in &key_handles {
|
||||
for device in devices.values_mut() {
|
||||
// Check if they key handle belongs to the current device.
|
||||
let is_valid = match u2f_is_keyhandle_valid(
|
||||
device,
|
||||
&challenge,
|
||||
&application,
|
||||
&key_handle,
|
||||
) {
|
||||
Ok(valid) => valid,
|
||||
Err(_) => continue, // Skip this device for now.
|
||||
};
|
||||
// Query newly added devices.
|
||||
matches.update(devices.iter_mut(), |device, key_handle| {
|
||||
u2f_is_keyhandle_valid(device, &challenge, &application, key_handle)
|
||||
.unwrap_or(false /* no match on failure */)
|
||||
});
|
||||
|
||||
if is_valid {
|
||||
// If yes, try to sign.
|
||||
if let Ok(bytes) = u2f_sign(
|
||||
device,
|
||||
&challenge,
|
||||
&application,
|
||||
&key_handle,
|
||||
)
|
||||
{
|
||||
callback.call(Ok((key_handle.clone(), bytes)));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// If no, keep registering and blinking with bogus data
|
||||
// Iterate all devices.
|
||||
for (path, device) in devices.iter_mut() {
|
||||
let key_handles = matches.get(path);
|
||||
|
||||
// If the device matches none of the given key handles
|
||||
// then just make it blink with bogus data.
|
||||
if key_handles.is_empty() {
|
||||
let blank = vec![0u8; PARAMETER_SIZE];
|
||||
if let Ok(_) = u2f_register(device, &blank, &blank) {
|
||||
callback.call(Err(io_err("invalid key")));
|
||||
return;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, try to sign.
|
||||
for key_handle in key_handles {
|
||||
if let Ok(bytes) = u2f_sign(
|
||||
device,
|
||||
&challenge,
|
||||
&application,
|
||||
key_handle,
|
||||
)
|
||||
{
|
||||
callback.call(Ok((key_handle.to_vec(), bytes)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче