Skip to content

Commit

Permalink
Merge pull request #56 from ttaubert/no-khmatcher-linux
Browse files Browse the repository at this point in the history
Linux-part of #47: Implement per-device threads, don't use the KeyHandleMatcher
  • Loading branch information
ttaubert committed Nov 20, 2017
2 parents ceede2f + 3272176 commit 7d9f31a
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 234 deletions.
2 changes: 1 addition & 1 deletion src/lib.rs
Expand Up @@ -27,7 +27,7 @@ pub mod platform;
#[path = "stub/mod.rs"]
pub mod platform;

#[cfg(not(any(target_os = "macos")))]
#[cfg(target_os = "windows")]
mod khmatcher;

#[macro_use]
Expand Down
50 changes: 0 additions & 50 deletions src/linux/devicemap.rs

This file was deleted.

191 changes: 84 additions & 107 deletions src/linux/mod.rs
Expand Up @@ -6,27 +6,24 @@ use std::time::Duration;
use std::thread;

mod device;
mod devicemap;
mod hidraw;
mod monitor;
mod transaction;

use consts::PARAMETER_SIZE;
use khmatcher::KeyHandleMatcher;
use runloop::RunLoop;
use platform::device::Device;
use platform::transaction::Transaction;
use util::{io_err, OnceCallback};
use u2fprotocol::{u2f_is_keyhandle_valid, u2f_register, u2f_sign};

use self::devicemap::DeviceMap;
use self::monitor::Monitor;
use u2fprotocol::{u2f_init_device, u2f_is_keyhandle_valid, u2f_register, u2f_sign};

#[derive(Default)]
pub struct PlatformManager {
// Handle to the thread loop.
thread: Option<RunLoop>,
transaction: Option<Transaction>,
}

impl PlatformManager {
pub fn new() -> Self {
Self { thread: None }
Default::default()
}

pub fn register(
Expand All @@ -42,48 +39,42 @@ impl PlatformManager {

let cbc = callback.clone();

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 mut matches = KeyHandleMatcher::new(&key_handles);

while alive() && monitor.alive() {
// Add/remove devices.
for event in monitor.events() {
devices.process_event(event);
}

// 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));
let transaction = Transaction::new(timeout, cbc.clone(), move |path, alive| {
// Create a new device.
let dev = &mut match Device::new(path) {
Ok(dev) => dev,
_ => return,
};

// Try initializing it.
if !dev.is_u2f() || !u2f_init_device(dev) {
return;
}

// Iterate the exclude list and see if there are any matches.
// Abort the state machine if we found a valid key handle.
if key_handles.iter().any(|key_handle| {
u2f_is_keyhandle_valid(dev, &challenge, &application, key_handle)
.unwrap_or(false) /* no match on failure */
})
{
return;
}

while alive() {
if let Ok(bytes) = u2f_register(dev, &challenge, &application) {
callback.call(Ok(bytes));
break;
}

callback.call(Err(io_err("aborted or timed out")));
},
timeout,
);
// Sleep a bit before trying again.
thread::sleep(Duration::from_millis(100));
}
});

self.thread = Some(try_or!(
thread,
|_| cbc.call(Err(io_err("couldn't create runloop")))
));
self.transaction = Some(try_or!(transaction, |_| {
cbc.call(Err(io_err("couldn't create transaction")))
}));
}

pub fn sign(
Expand All @@ -99,74 +90,60 @@ impl PlatformManager {

let cbc = callback.clone();

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 mut matches = KeyHandleMatcher::new(&key_handles);

while alive() && monitor.alive() {
// Add/remove devices.
for event in monitor.events() {
devices.process_event(event);
let transaction = Transaction::new(timeout, cbc.clone(), move |path, alive| {
// Create a new device.
let dev = &mut match Device::new(path) {
Ok(dev) => dev,
_ => return,
};

// Try initializing it.
if !dev.is_u2f() || !u2f_init_device(dev) {
return;
}

// Find all matching key handles.
let key_handles = key_handles
.iter()
.filter(|key_handle| {
u2f_is_keyhandle_valid(dev, &challenge, &application, key_handle)
.unwrap_or(false) /* no match on failure */
})
.collect::<Vec<_>>();

while alive() {
// 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(dev, &blank, &blank) {
callback.call(Err(io_err("invalid key")));
break;
}

// 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.
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;
}
} else {
// Otherwise, try to sign.
for key_handle in &key_handles {
if let Ok(bytes) = u2f_sign(dev, &challenge, &application, key_handle) {
callback.call(Ok((key_handle.to_vec(), bytes)));
break;
}
}

// Wait a little before trying again.
thread::sleep(Duration::from_millis(100));
}

callback.call(Err(io_err("aborted or timed out")));
},
timeout,
);
// Sleep a bit before trying again.
thread::sleep(Duration::from_millis(100));
}
});

self.thread = Some(try_or!(
thread,
|_| cbc.call(Err(io_err("couldn't create runloop")))
));
self.transaction = Some(try_or!(transaction, |_| {
cbc.call(Err(io_err("couldn't create transaction")))
}));
}

// This blocks.
pub fn cancel(&mut self) {
if let Some(thread) = self.thread.take() {
thread.cancel();
if let Some(mut transaction) = self.transaction.take() {
transaction.cancel();
}
}
}

0 comments on commit 7d9f31a

Please sign in to comment.