diff --git a/Cargo.toml b/Cargo.toml index f8ab2ea9..302b6542 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,3 +50,4 @@ sha2 = "^0.8.2" base64 = "^0.10" env_logger = "^0.6" getopts = "^0.2" +assert_matches = "1.2" diff --git a/src/authenticatorservice.rs b/src/authenticatorservice.rs index 0a4f2686..aaa708cd 100644 --- a/src/authenticatorservice.rs +++ b/src/authenticatorservice.rs @@ -5,6 +5,7 @@ use std::sync::{mpsc::Sender, Arc, Mutex}; use crate::consts::PARAMETER_SIZE; +use crate::errors::*; use crate::statecallback::StateCallback; pub trait AuthenticatorTransport { @@ -18,8 +19,8 @@ pub trait AuthenticatorTransport { application: crate::AppId, key_handles: Vec, status: Sender, - callback: StateCallback>, - ) -> Result<(), crate::Error>; + callback: StateCallback>, + ) -> crate::Result<()>; /// The implementation of this method must return quickly and should /// report its status via the status and callback methods @@ -31,10 +32,10 @@ pub trait AuthenticatorTransport { app_ids: Vec, key_handles: Vec, status: Sender, - callback: StateCallback>, - ) -> Result<(), crate::Error>; + callback: StateCallback>, + ) -> crate::Result<()>; - fn cancel(&mut self) -> Result<(), crate::Error>; + fn cancel(&mut self) -> crate::Result<()>; } pub struct AuthenticatorService { @@ -61,7 +62,7 @@ fn clone_and_configure_cancellation_callback( } impl AuthenticatorService { - pub fn new() -> Result { + pub fn new() -> crate::Result { Ok(Self { transports: Vec::new(), }) @@ -91,21 +92,21 @@ impl AuthenticatorService { application: crate::AppId, key_handles: Vec, status: Sender, - callback: StateCallback>, - ) -> Result<(), crate::Error> { + callback: StateCallback>, + ) -> crate::Result<()> { if challenge.len() != PARAMETER_SIZE || application.len() != PARAMETER_SIZE { - return Err(crate::Error::Unknown); + return Err(AuthenticatorError::InvalidRelyingPartyInput); } for key_handle in &key_handles { if key_handle.credential.len() > 256 { - return Err(crate::Error::Unknown); + return Err(AuthenticatorError::InvalidRelyingPartyInput); } } let iterable_transports = self.transports.clone(); if iterable_transports.is_empty() { - return Err(crate::Error::NotSupported); + return Err(AuthenticatorError::NoConfiguredTransports); } debug!( @@ -145,31 +146,31 @@ impl AuthenticatorService { app_ids: Vec, key_handles: Vec, status: Sender, - callback: StateCallback>, - ) -> Result<(), crate::Error> { + callback: StateCallback>, + ) -> crate::Result<()> { if challenge.len() != PARAMETER_SIZE { - return Err(crate::Error::Unknown); + return Err(AuthenticatorError::InvalidRelyingPartyInput); } if app_ids.is_empty() { - return Err(crate::Error::Unknown); + return Err(AuthenticatorError::InvalidRelyingPartyInput); } for app_id in &app_ids { if app_id.len() != PARAMETER_SIZE { - return Err(crate::Error::Unknown); + return Err(AuthenticatorError::InvalidRelyingPartyInput); } } for key_handle in &key_handles { if key_handle.credential.len() > 256 { - return Err(crate::Error::Unknown); + return Err(AuthenticatorError::InvalidRelyingPartyInput); } } let iterable_transports = self.transports.clone(); if iterable_transports.is_empty() { - return Err(crate::Error::NotSupported); + return Err(AuthenticatorError::NoConfiguredTransports); } for (idx, transport_mutex) in iterable_transports.iter().enumerate() { @@ -190,9 +191,9 @@ impl AuthenticatorService { Ok(()) } - pub fn cancel(&mut self) -> Result<(), crate::Error> { + pub fn cancel(&mut self) -> crate::Result<()> { if self.transports.is_empty() { - return Err(crate::Error::NotSupported); + return Err(AuthenticatorError::NoConfiguredTransports); } for transport_mutex in &mut self.transports { @@ -259,8 +260,8 @@ mod tests { _application: crate::AppId, _key_handles: Vec, _status: Sender, - callback: StateCallback>, - ) -> Result<(), crate::Error> { + callback: StateCallback>, + ) -> crate::Result<()> { if self.consent { let rv = Ok((vec![0u8; 16], self.dev_info())); thread::spawn(move || callback.call(rv)); @@ -276,8 +277,8 @@ mod tests { _app_ids: Vec, _key_handles: Vec, _status: Sender, - callback: StateCallback>, - ) -> Result<(), crate::Error> { + callback: StateCallback>, + ) -> crate::Result<()> { if self.consent { let rv = Ok((vec![0u8; 0], vec![0u8; 0], vec![0u8; 0], self.dev_info())); thread::spawn(move || callback.call(rv)); @@ -285,10 +286,15 @@ mod tests { Ok(()) } - fn cancel(&mut self) -> Result<(), crate::Error> { + fn cancel(&mut self) -> crate::Result<()> { self.was_cancelled .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) - .map_or(Err(crate::Error::InvalidState), |_| Ok(())) + .map_or( + Err(crate::errors::AuthenticatorError::U2FToken( + crate::errors::U2FTokenError::InvalidState, + )), + |_| Ok(()), + ) } } @@ -315,7 +321,7 @@ mod tests { let mut s = AuthenticatorService::new().unwrap(); s.add_transport(Box::new(TestTransportDriver::new(true).unwrap())); - assert_eq!( + assert_matches!( s.register( RegisterFlags::empty(), 1_000, @@ -326,10 +332,10 @@ mod tests { StateCallback::new(Box::new(move |_rv| {})), ) .unwrap_err(), - crate::Error::Unknown + crate::errors::AuthenticatorError::InvalidRelyingPartyInput ); - assert_eq!( + assert_matches!( s.sign( SignFlags::empty(), 1_000, @@ -340,7 +346,7 @@ mod tests { StateCallback::new(Box::new(move |_rv| {})), ) .unwrap_err(), - crate::Error::Unknown + crate::errors::AuthenticatorError::InvalidRelyingPartyInput ); } @@ -352,7 +358,7 @@ mod tests { let mut s = AuthenticatorService::new().unwrap(); s.add_transport(Box::new(TestTransportDriver::new(true).unwrap())); - assert_eq!( + assert_matches!( s.register( RegisterFlags::empty(), 1_000, @@ -363,10 +369,10 @@ mod tests { StateCallback::new(Box::new(move |_rv| {})), ) .unwrap_err(), - crate::Error::Unknown + crate::errors::AuthenticatorError::InvalidRelyingPartyInput ); - assert_eq!( + assert_matches!( s.sign( SignFlags::empty(), 1_000, @@ -377,7 +383,7 @@ mod tests { StateCallback::new(Box::new(move |_rv| {})), ) .unwrap_err(), - crate::Error::Unknown + crate::errors::AuthenticatorError::InvalidRelyingPartyInput ); } @@ -392,7 +398,7 @@ mod tests { let mut s = AuthenticatorService::new().unwrap(); s.add_transport(Box::new(TestTransportDriver::new(true).unwrap())); - assert_eq!( + assert_matches!( s.register( RegisterFlags::empty(), 100, @@ -405,7 +411,7 @@ mod tests { Ok(()) ); - assert_eq!( + assert_matches!( s.sign( SignFlags::empty(), 100, @@ -432,7 +438,7 @@ mod tests { let mut s = AuthenticatorService::new().unwrap(); s.add_transport(Box::new(TestTransportDriver::new(true).unwrap())); - assert_eq!( + assert_matches!( s.register( RegisterFlags::empty(), 1_000, @@ -443,10 +449,10 @@ mod tests { StateCallback::new(Box::new(move |_rv| {})), ) .unwrap_err(), - crate::Error::Unknown + crate::errors::AuthenticatorError::InvalidRelyingPartyInput ); - assert_eq!( + assert_matches!( s.sign( SignFlags::empty(), 1_000, @@ -457,7 +463,7 @@ mod tests { StateCallback::new(Box::new(move |_rv| {})), ) .unwrap_err(), - crate::Error::Unknown + crate::errors::AuthenticatorError::InvalidRelyingPartyInput ); } @@ -467,7 +473,7 @@ mod tests { let (status_tx, _) = channel::(); let mut s = AuthenticatorService::new().unwrap(); - assert_eq!( + assert_matches!( s.register( RegisterFlags::empty(), 1_000, @@ -478,10 +484,10 @@ mod tests { StateCallback::new(Box::new(move |_rv| {})), ) .unwrap_err(), - crate::Error::NotSupported + crate::errors::AuthenticatorError::NoConfiguredTransports ); - assert_eq!( + assert_matches!( s.sign( SignFlags::empty(), 1_000, @@ -492,10 +498,13 @@ mod tests { StateCallback::new(Box::new(move |_rv| {})), ) .unwrap_err(), - crate::Error::NotSupported + crate::errors::AuthenticatorError::NoConfiguredTransports ); - assert_eq!(s.cancel().unwrap_err(), crate::Error::NotSupported); + assert_matches!( + s.cancel().unwrap_err(), + crate::errors::AuthenticatorError::NoConfiguredTransports + ); } #[test] @@ -517,8 +526,8 @@ mod tests { s.add_transport(Box::new(ttd_three)); let callback = StateCallback::new(Box::new(move |_rv| {})); - assert_eq!( - s.register( + assert!(s + .register( RegisterFlags::empty(), 1_000, mk_challenge(), @@ -526,9 +535,8 @@ mod tests { vec![], status_tx.clone(), callback.clone(), - ), - Ok(()) - ); + ) + .is_ok()); callback.wait(); assert_eq!(was_cancelled_one.load(Ordering::SeqCst), false); @@ -555,8 +563,8 @@ mod tests { s.add_transport(Box::new(ttd_three)); let callback = StateCallback::new(Box::new(move |_rv| {})); - assert_eq!( - s.sign( + assert!(s + .sign( SignFlags::empty(), 1_000, mk_challenge(), @@ -564,9 +572,8 @@ mod tests { vec![mk_key()], status_tx, callback.clone(), - ), - Ok(()) - ); + ) + .is_ok()); callback.wait(); assert_eq!(was_cancelled_one.load(Ordering::SeqCst), false); @@ -591,8 +598,8 @@ mod tests { s.add_transport(Box::new(ttd_two)); let callback = StateCallback::new(Box::new(move |_rv| {})); - assert_eq!( - s.register( + assert!(s + .register( RegisterFlags::empty(), 1_000, mk_challenge(), @@ -600,9 +607,8 @@ mod tests { vec![], status_tx.clone(), callback.clone(), - ), - Ok(()) - ); + ) + .is_ok()); callback.wait(); let one = was_cancelled_one.load(Ordering::SeqCst); diff --git a/src/capi.rs b/src/capi.rs index 2e89a05f..ea712350 100644 --- a/src/capi.rs +++ b/src/capi.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::authenticatorservice::AuthenticatorService; +use crate::errors; use crate::statecallback::StateCallback; use crate::{RegisterResult, SignResult}; use libc::size_t; @@ -18,7 +19,7 @@ type U2FCallback = extern "C" fn(u64, *mut U2FResult); pub enum U2FResult { Success(HashMap>), - Error(crate::Error), + Error(errors::AuthenticatorError), } const RESBUF_ID_REGISTRATION: u8 = 0; @@ -136,11 +137,11 @@ pub unsafe extern "C" fn rust_u2f_khs_free(khs: *mut U2FKeyHandles) { #[no_mangle] pub unsafe extern "C" fn rust_u2f_result_error(res: *const U2FResult) -> u8 { if res.is_null() { - return crate::Error::Unknown as u8; + return errors::U2FTokenError::Unknown as u8; } if let U2FResult::Error(ref err) = *res { - return *err as u8; + return err.as_u2f_errorcode(); } 0 /* No error, the request succeeded. */ @@ -246,24 +247,23 @@ pub unsafe extern "C" fn rust_u2f_mgr_register( let tid = new_tid(); - let state_callback = - StateCallback::>::new(Box::new(move |rv| { - let result = match rv { - Ok((registration, dev_info)) => { - let mut bufs = HashMap::new(); - bufs.insert(RESBUF_ID_REGISTRATION, registration); - bufs.insert(RESBUF_ID_VENDOR_NAME, dev_info.vendor_name); - bufs.insert(RESBUF_ID_DEVICE_NAME, dev_info.device_name); - bufs.insert(RESBUF_ID_FIRMWARE_MAJOR, vec![dev_info.version_major]); - bufs.insert(RESBUF_ID_FIRMWARE_MINOR, vec![dev_info.version_minor]); - bufs.insert(RESBUF_ID_FIRMWARE_BUILD, vec![dev_info.version_build]); - U2FResult::Success(bufs) - } - Err(e) => U2FResult::Error(e), - }; - - callback(tid, Box::into_raw(Box::new(result))); - })); + let state_callback = StateCallback::>::new(Box::new(move |rv| { + let result = match rv { + Ok((registration, dev_info)) => { + let mut bufs = HashMap::new(); + bufs.insert(RESBUF_ID_REGISTRATION, registration); + bufs.insert(RESBUF_ID_VENDOR_NAME, dev_info.vendor_name); + bufs.insert(RESBUF_ID_DEVICE_NAME, dev_info.device_name); + bufs.insert(RESBUF_ID_FIRMWARE_MAJOR, vec![dev_info.version_major]); + bufs.insert(RESBUF_ID_FIRMWARE_MINOR, vec![dev_info.version_minor]); + bufs.insert(RESBUF_ID_FIRMWARE_BUILD, vec![dev_info.version_build]); + U2FResult::Success(bufs) + } + Err(e) => U2FResult::Error(e), + }; + + callback(tid, Box::into_raw(Box::new(result))); + })); let res = (*mgr).register( flags, @@ -327,26 +327,25 @@ pub unsafe extern "C" fn rust_u2f_mgr_sign( }); let tid = new_tid(); - let state_callback = - StateCallback::>::new(Box::new(move |rv| { - let result = match rv { - Ok((app_id, key_handle, signature, dev_info)) => { - let mut bufs = HashMap::new(); - bufs.insert(RESBUF_ID_KEYHANDLE, key_handle); - bufs.insert(RESBUF_ID_SIGNATURE, signature); - bufs.insert(RESBUF_ID_APPID, app_id); - bufs.insert(RESBUF_ID_VENDOR_NAME, dev_info.vendor_name); - bufs.insert(RESBUF_ID_DEVICE_NAME, dev_info.device_name); - bufs.insert(RESBUF_ID_FIRMWARE_MAJOR, vec![dev_info.version_major]); - bufs.insert(RESBUF_ID_FIRMWARE_MINOR, vec![dev_info.version_minor]); - bufs.insert(RESBUF_ID_FIRMWARE_BUILD, vec![dev_info.version_build]); - U2FResult::Success(bufs) - } - Err(e) => U2FResult::Error(e), - }; - - callback(tid, Box::into_raw(Box::new(result))); - })); + let state_callback = StateCallback::>::new(Box::new(move |rv| { + let result = match rv { + Ok((app_id, key_handle, signature, dev_info)) => { + let mut bufs = HashMap::new(); + bufs.insert(RESBUF_ID_KEYHANDLE, key_handle); + bufs.insert(RESBUF_ID_SIGNATURE, signature); + bufs.insert(RESBUF_ID_APPID, app_id); + bufs.insert(RESBUF_ID_VENDOR_NAME, dev_info.vendor_name); + bufs.insert(RESBUF_ID_DEVICE_NAME, dev_info.device_name); + bufs.insert(RESBUF_ID_FIRMWARE_MAJOR, vec![dev_info.version_major]); + bufs.insert(RESBUF_ID_FIRMWARE_MINOR, vec![dev_info.version_minor]); + bufs.insert(RESBUF_ID_FIRMWARE_BUILD, vec![dev_info.version_build]); + U2FResult::Success(bufs) + } + Err(e) => U2FResult::Error(e), + }; + + callback(tid, Box::into_raw(Box::new(result))); + })); let res = (*mgr).sign( flags, diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 00000000..ee63cfac --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,96 @@ +/* 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::fmt; +use std::io; +use std::sync::mpsc; + +// This composite error type is patterned from Phil Daniels' blog: +// https://www.philipdaniels.com/blog/2019/defining-rust-error-types/ + +#[derive(Debug)] +pub enum AuthenticatorError { + // Errors from external libraries... + Io(io::Error), + // Errors raised by us... + InvalidRelyingPartyInput, + NoConfiguredTransports, + Platform, + InternalError(String), + U2FToken(U2FTokenError), + Custom(String), +} + +impl AuthenticatorError { + pub fn as_u2f_errorcode(&self) -> u8 { + match *self { + AuthenticatorError::U2FToken(ref err) => *err as u8, + _ => U2FTokenError::Unknown as u8, + } + } +} + +impl std::error::Error for AuthenticatorError {} + +impl fmt::Display for AuthenticatorError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + AuthenticatorError::Io(ref err) => err.fmt(f), + AuthenticatorError::InvalidRelyingPartyInput => { + write!(f, "invalid input from relying party") + } + AuthenticatorError::NoConfiguredTransports => write!( + f, + "no transports were configured in the authenticator service" + ), + AuthenticatorError::Platform => write!(f, "unknown platform error"), + AuthenticatorError::InternalError(ref err) => write!(f, "internal error: {}", err), + AuthenticatorError::U2FToken(ref err) => { + write!(f, "A u2f token error occurred {:?}", err) + } + AuthenticatorError::Custom(ref err) => write!(f, "A custom error occurred {:?}", err), + } + } +} + +impl From for AuthenticatorError { + fn from(err: io::Error) -> AuthenticatorError { + AuthenticatorError::Io(err) + } +} + +impl From> for AuthenticatorError { + fn from(err: mpsc::SendError) -> AuthenticatorError { + AuthenticatorError::InternalError(err.to_string()) + } +} + +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum U2FTokenError { + Unknown = 1, + NotSupported = 2, + InvalidState = 3, + ConstraintError = 4, + NotAllowed = 5, +} + +impl U2FTokenError { + fn as_str(&self) -> &str { + match *self { + U2FTokenError::Unknown => "unknown", + U2FTokenError::NotSupported => "not supported", + U2FTokenError::InvalidState => "invalid state", + U2FTokenError::ConstraintError => "constraint error", + U2FTokenError::NotAllowed => "not allowed", + } + } +} + +impl std::fmt::Display for U2FTokenError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.as_str()) + } +} + +impl std::error::Error for U2FTokenError {} diff --git a/src/freebsd/transaction.rs b/src/freebsd/transaction.rs index 26858510..e7cd00f1 100644 --- a/src/freebsd/transaction.rs +++ b/src/freebsd/transaction.rs @@ -2,6 +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 crate::errors; use crate::platform::monitor::Monitor; use crate::statecallback::StateCallback; use runloop::RunLoop; @@ -15,9 +16,9 @@ pub struct Transaction { impl Transaction { pub fn new( timeout: u64, - callback: StateCallback>, + callback: StateCallback>, new_device_cb: F, - ) -> Result + ) -> crate::Result where F: Fn(OsString, &dyn Fn() -> bool) + Sync + Send + 'static, T: 'static, @@ -29,14 +30,16 @@ impl Transaction { // Start polling for new devices. try_or!(monitor.run(alive), |_| callback - .call(Err(crate::Error::Unknown))); + .call(Err(errors::AuthenticatorError::Platform))); // Send an error, if the callback wasn't called already. - callback.call(Err(crate::Error::NotAllowed)); + callback.call(Err(errors::AuthenticatorError::U2FToken( + errors::U2FTokenError::NotAllowed, + ))); }, timeout, ) - .map_err(|_| crate::Error::Unknown)?; + .map_err(|_| errors::AuthenticatorError::Platform)?; Ok(Self { thread: Some(thread), diff --git a/src/lib.rs b/src/lib.rs index ca0e0710..ed438d07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,6 +73,7 @@ pub use crate::manager::U2FManager; mod capi; pub use crate::capi::*; +pub mod errors; pub mod statecallback; // Keep this in sync with the constants in u2fhid-capi.h. @@ -106,22 +107,7 @@ pub type AppId = Vec; pub type RegisterResult = (Vec, u2ftypes::U2FDeviceInfo); pub type SignResult = (AppId, Vec, Vec, u2ftypes::U2FDeviceInfo); -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Error { - Unknown = 1, - NotSupported = 2, - InvalidState = 3, - ConstraintError = 4, - NotAllowed = 5, -} - -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self) - } -} - -impl std::error::Error for Error {} +pub type Result = std::result::Result; #[derive(Debug, Clone)] pub enum StatusUpdate { @@ -130,6 +116,10 @@ pub enum StatusUpdate { Success { dev_info: u2ftypes::U2FDeviceInfo }, } +#[cfg(test)] +#[macro_use] +extern crate assert_matches; + #[cfg(fuzzing)] pub use consts::*; #[cfg(fuzzing)] diff --git a/src/linux/transaction.rs b/src/linux/transaction.rs index 26858510..e7cd00f1 100644 --- a/src/linux/transaction.rs +++ b/src/linux/transaction.rs @@ -2,6 +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 crate::errors; use crate::platform::monitor::Monitor; use crate::statecallback::StateCallback; use runloop::RunLoop; @@ -15,9 +16,9 @@ pub struct Transaction { impl Transaction { pub fn new( timeout: u64, - callback: StateCallback>, + callback: StateCallback>, new_device_cb: F, - ) -> Result + ) -> crate::Result where F: Fn(OsString, &dyn Fn() -> bool) + Sync + Send + 'static, T: 'static, @@ -29,14 +30,16 @@ impl Transaction { // Start polling for new devices. try_or!(monitor.run(alive), |_| callback - .call(Err(crate::Error::Unknown))); + .call(Err(errors::AuthenticatorError::Platform))); // Send an error, if the callback wasn't called already. - callback.call(Err(crate::Error::NotAllowed)); + callback.call(Err(errors::AuthenticatorError::U2FToken( + errors::U2FTokenError::NotAllowed, + ))); }, timeout, ) - .map_err(|_| crate::Error::Unknown)?; + .map_err(|_| errors::AuthenticatorError::Platform)?; Ok(Self { thread: Some(thread), diff --git a/src/macos/transaction.rs b/src/macos/transaction.rs index f523fc43..697730a4 100644 --- a/src/macos/transaction.rs +++ b/src/macos/transaction.rs @@ -4,6 +4,7 @@ extern crate libc; +use crate::errors; use crate::platform::iokit::{CFRunLoopEntryObserver, IOHIDDeviceRef, SendableRunLoop}; use crate::platform::monitor::Monitor; use crate::statecallback::StateCallback; @@ -24,9 +25,9 @@ pub struct Transaction { impl Transaction { pub fn new( timeout: u64, - callback: StateCallback>, + callback: StateCallback>, new_device_cb: F, - ) -> Result + ) -> crate::Result where F: Fn((IOHIDDeviceRef, Receiver>), &dyn Fn() -> bool) + Sync + Send + 'static, T: 'static, @@ -48,7 +49,7 @@ impl Transaction { // Create a new HID device monitor and start polling. let mut monitor = Monitor::new(new_device_cb); try_or!(monitor.start(), |_| callback - .call(Err(crate::Error::Unknown))); + .call(Err(errors::AuthenticatorError::Platform))); // This will block until completion, abortion, or timeout. unsafe { CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, 0) }; @@ -57,12 +58,16 @@ impl Transaction { monitor.stop(); // Send an error, if the callback wasn't called already. - callback.call(Err(crate::Error::NotAllowed)); + callback.call(Err(errors::AuthenticatorError::U2FToken( + errors::U2FTokenError::NotAllowed, + ))); }) - .map_err(|_| crate::Error::Unknown)?; + .map_err(|_| errors::AuthenticatorError::Platform)?; // Block until we enter the CFRunLoop. - let runloop = rx.recv().map_err(|_| crate::Error::Unknown)?; + let runloop = rx + .recv() + .map_err(|_| errors::AuthenticatorError::Platform)?; Ok(Self { runloop: Some(runloop), diff --git a/src/manager.rs b/src/manager.rs index 06c2351c..811323a6 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -8,6 +8,7 @@ use std::time::Duration; use crate::authenticatorservice::AuthenticatorTransport; use crate::consts::PARAMETER_SIZE; +use crate::errors::*; use crate::statecallback::StateCallback; use crate::statemachine::StateMachine; use runloop::RunLoop; @@ -20,7 +21,7 @@ enum QueueAction { application: crate::AppId, key_handles: Vec, status: Sender, - callback: StateCallback>, + callback: StateCallback>, }, Sign { flags: crate::SignFlags, @@ -29,7 +30,7 @@ enum QueueAction { app_ids: Vec, key_handles: Vec, status: Sender, - callback: StateCallback>, + callback: StateCallback>, }, Cancel, } @@ -118,15 +119,15 @@ impl AuthenticatorTransport for U2FManager { application: crate::AppId, key_handles: Vec, status: Sender, - callback: StateCallback>, - ) -> Result<(), crate::Error> { + callback: StateCallback>, + ) -> crate::Result<()> { if challenge.len() != PARAMETER_SIZE || application.len() != PARAMETER_SIZE { - return Err(crate::Error::Unknown); + return Err(AuthenticatorError::InvalidRelyingPartyInput); } for key_handle in &key_handles { if key_handle.credential.len() > 256 { - return Err(crate::Error::Unknown); + return Err(AuthenticatorError::InvalidRelyingPartyInput); } } @@ -139,7 +140,9 @@ impl AuthenticatorTransport for U2FManager { status, callback, }; - self.tx.send(action).map_err(|_| crate::Error::Unknown) + self.tx + .send(action) + .map_err(|e| AuthenticatorError::from(e)) } fn sign( @@ -150,25 +153,25 @@ impl AuthenticatorTransport for U2FManager { app_ids: Vec, key_handles: Vec, status: Sender, - callback: StateCallback>, - ) -> Result<(), crate::Error> { + callback: StateCallback>, + ) -> crate::Result<()> { if challenge.len() != PARAMETER_SIZE { - return Err(crate::Error::Unknown); + return Err(AuthenticatorError::InvalidRelyingPartyInput); } if app_ids.is_empty() { - return Err(crate::Error::Unknown); + return Err(AuthenticatorError::InvalidRelyingPartyInput); } for app_id in &app_ids { if app_id.len() != PARAMETER_SIZE { - return Err(crate::Error::Unknown); + return Err(AuthenticatorError::InvalidRelyingPartyInput); } } for key_handle in &key_handles { if key_handle.credential.len() > 256 { - return Err(crate::Error::Unknown); + return Err(AuthenticatorError::InvalidRelyingPartyInput); } } @@ -181,13 +184,11 @@ impl AuthenticatorTransport for U2FManager { status, callback, }; - self.tx.send(action).map_err(|_| crate::Error::Unknown) + Ok(self.tx.send(action)?) } - fn cancel(&mut self) -> Result<(), crate::Error> { - self.tx - .send(QueueAction::Cancel) - .map_err(|_| crate::Error::Unknown) + fn cancel(&mut self) -> crate::Result<()> { + Ok(self.tx.send(QueueAction::Cancel)?) } } diff --git a/src/netbsd/transaction.rs b/src/netbsd/transaction.rs index 648cc8d7..21ac2125 100644 --- a/src/netbsd/transaction.rs +++ b/src/netbsd/transaction.rs @@ -2,11 +2,11 @@ * 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 crate::statecallback::StateCallback; -use runloop::RunLoop; - +use crate::errors; use crate::platform::fd::Fd; use crate::platform::monitor::Monitor; +use crate::statecallback::StateCallback; +use runloop::RunLoop; pub struct Transaction { // Handle to the thread loop. @@ -16,9 +16,9 @@ pub struct Transaction { impl Transaction { pub fn new( timeout: u64, - callback: StateCallback>, + callback: StateCallback>, new_device_cb: F, - ) -> Result + ) -> crate::Result where F: Fn(Fd, &dyn Fn() -> bool) + Sync + Send + 'static, T: 'static, @@ -30,14 +30,16 @@ impl Transaction { // Start polling for new devices. try_or!(monitor.run(alive), |_| callback - .call(Err(crate::Error::Unknown))); + .call(Err(errors::AuthenticatorError::Platform))); // Send an error, if the callback wasn't called already. - callback.call(Err(crate::Error::NotAllowed)); + callback.call(Err(errors::AuthenticatorError::U2FToken( + errors::U2FTokenError::NotAllowed, + ))); }, timeout, ) - .map_err(|_| crate::Error::Unknown)?; + .map_err(|_| errors::AuthenticatorError::Platform)?; Ok(Self { thread: Some(thread), diff --git a/src/openbsd/transaction.rs b/src/openbsd/transaction.rs index 350a1283..4b85db27 100644 --- a/src/openbsd/transaction.rs +++ b/src/openbsd/transaction.rs @@ -2,6 +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 crate::errors; use crate::platform::monitor::{FidoDev, Monitor}; use crate::statecallback::StateCallback; use runloop::RunLoop; @@ -14,9 +15,9 @@ pub struct Transaction { impl Transaction { pub fn new( timeout: u64, - callback: StateCallback>, + callback: StateCallback>, new_device_cb: F, - ) -> Result + ) -> crate::Result where F: Fn(FidoDev, &dyn Fn() -> bool) + Sync + Send + 'static, T: 'static, @@ -28,14 +29,16 @@ impl Transaction { // Start polling for new devices. try_or!(monitor.run(alive), |_| callback - .call(Err(crate::Error::Unknown))); + .call(Err(errors::AuthenticatorError::Platform))); // Send an error, if the callback wasn't called already. - callback.call(Err(crate::Error::NotAllowed)); + callback.call(Err(errors::AuthenticatorError::U2FToken( + errors::U2FTokenError::NotAllowed, + ))); }, timeout, ) - .map_err(|_| crate::Error::Unknown)?; + .map_err(|_| errors::AuthenticatorError::Platform)?; Ok(Self { thread: Some(thread), diff --git a/src/statemachine.rs b/src/statemachine.rs index a8118ed7..ceaa367a 100644 --- a/src/statemachine.rs +++ b/src/statemachine.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::consts::PARAMETER_SIZE; +use crate::errors; use crate::platform::device::Device; use crate::platform::transaction::Transaction; use crate::statecallback::StateCallback; @@ -74,7 +75,7 @@ impl StateMachine { application: crate::AppId, key_handles: Vec, status: Sender, - callback: StateCallback>, + callback: StateCallback>, ) { // Abort any prior register/sign calls. self.cancel(); @@ -125,7 +126,9 @@ impl StateMachine { if excluded { let blank = vec![0u8; PARAMETER_SIZE]; if u2f_register(dev, &blank, &blank).is_ok() { - callback.call(Err(crate::Error::InvalidState)); + callback.call(Err(errors::AuthenticatorError::U2FToken( + errors::U2FTokenError::InvalidState, + ))); break; } } else if let Ok(bytes) = u2f_register(dev, &challenge, &application) { @@ -163,7 +166,7 @@ impl StateMachine { app_ids: Vec, key_handles: Vec, status: Sender, - callback: StateCallback>, + callback: StateCallback>, ) { // Abort any prior register/sign calls. self.cancel(); @@ -229,7 +232,9 @@ impl StateMachine { if valid_handles.is_empty() { let blank = vec![0u8; PARAMETER_SIZE]; if u2f_register(dev, &blank, &blank).is_ok() { - callback.call(Err(crate::Error::InvalidState)); + callback.call(Err(errors::AuthenticatorError::U2FToken( + errors::U2FTokenError::InvalidState, + ))); break; } } else { diff --git a/src/stub/transaction.rs b/src/stub/transaction.rs index 5febbe4a..bdf48ef5 100644 --- a/src/stub/transaction.rs +++ b/src/stub/transaction.rs @@ -2,6 +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 crate::errors; use crate::statecallback::StateCallback; pub struct Transaction {} @@ -9,14 +10,19 @@ pub struct Transaction {} impl Transaction { pub fn new( timeout: u64, - callback: StateCallback>, + callback: StateCallback>, new_device_cb: F, - ) -> Result + ) -> crate::Result where F: Fn(String, &dyn Fn() -> bool), { - callback.call(Err(crate::Error::NotSupported)); - Err(crate::Error::NotSupported) + callback.call(Err(errors::AuthenticatorError::U2FToken( + errors::U2FTokenError::NotSupported, + ))); + + Err(errors::AuthenticatorError::U2FToken( + errors::U2FTokenError::NotSupported, + )) } pub fn cancel(&mut self) { diff --git a/src/windows/transaction.rs b/src/windows/transaction.rs index de738134..74e856b6 100644 --- a/src/windows/transaction.rs +++ b/src/windows/transaction.rs @@ -2,6 +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 crate::errors; use crate::platform::monitor::Monitor; use crate::statecallback::StateCallback; use runloop::RunLoop; @@ -14,9 +15,9 @@ pub struct Transaction { impl Transaction { pub fn new( timeout: u64, - callback: StateCallback>, + callback: StateCallback>, new_device_cb: F, - ) -> Result + ) -> crate::Result where F: Fn(String, &dyn Fn() -> bool) + Sync + Send + 'static, T: 'static, @@ -28,14 +29,16 @@ impl Transaction { // Start polling for new devices. try_or!(monitor.run(alive), |_| callback - .call(Err(crate::Error::Unknown))); + .call(Err(errors::AuthenticatorError::Platform))); // Send an error, if the callback wasn't called already. - callback.call(Err(crate::Error::NotAllowed)); + callback.call(Err(errors::AuthenticatorError::U2FToken( + errors::U2FTokenError::NotAllowed, + ))); }, timeout, ) - .map_err(|_| crate::Error::Unknown)?; + .map_err(|_| errors::AuthenticatorError::Platform)?; Ok(Self { thread: Some(thread),