diff options
-rw-r--r-- | CHANGELOG.md | 13 | ||||
-rw-r--r-- | Cargo.toml | 4 | ||||
-rw-r--r-- | TODO.md | 1 | ||||
-rw-r--r-- | src/auth.rs | 63 | ||||
-rw-r--r-- | src/config.rs | 8 | ||||
-rw-r--r-- | src/device.rs | 225 | ||||
-rw-r--r-- | src/error.rs | 233 | ||||
-rw-r--r-- | src/lib.rs | 16 | ||||
-rw-r--r-- | src/otp.rs | 83 | ||||
-rw-r--r-- | src/pws.rs | 80 | ||||
-rw-r--r-- | src/util.rs | 133 | ||||
-rw-r--r-- | tests/device.rs | 201 | ||||
-rw-r--r-- | tests/otp.rs | 117 | ||||
-rw-r--r-- | tests/pws.rs | 107 | ||||
-rw-r--r-- | tests/util/mod.rs | 81 |
15 files changed, 762 insertions, 603 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c5ebf3..a4df5c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ - Remove the `test-pro` and `test-storage` features. - Implement `Display` for `Version`. - Introduce `DEFAULT_ADMIN_PIN` and `DEFAULT_USER_PIN` constants. +- Refactor the error handling code: + - Implement `std::error::Error` for `CommandError`. + - Add the `Error` enum and the `Result` typedef. + - Add the `LibraryError` enum and move the library error variants from + `CommandError` to `LibraryError`. + - Add the `CommunicationError` enum and move the communication error variants + from `CommandError` to `CommunicationError`. + - Return `Error` instead of `CommandError` in all public functions. + - Move the `CommandError::RngError` variant to `Error::RandError` and the + `CommandError::Unknown` variant to `Error::Unknown`. + - Return `CommunicationError::NotConnected` instead of + `CommandError::Undefined` from the connect functions. + - Remove the `CommandError::Undefined` variant. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. @@ -15,8 +15,8 @@ license = "MIT" [dependencies] libc = "0.2" nitrokey-sys = "3.4" -rand_core = {version = "0.3", default-features = false} +rand_core = {version = "0.3", default-features = false, features = ["std"] } rand_os = {version = "0.1"} [dev-dependencies] -nitrokey-test = {version = "0.1"} +nitrokey-test = {version = "0.2"} @@ -9,7 +9,6 @@ - Clear passwords from memory. - Find a nicer syntax for the `write_config` test. - Prevent construction of internal types. -- More specific error checking in the tests. - Check integer conversions. - Consider implementing `Into<CommandError>` for `(Device, CommandError)` - Lock password safe in `PasswordSafe::drop()` (see [nitrokey-storage-firmware diff --git a/src/auth.rs b/src/auth.rs index 2d61d4b..d1eb049 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -6,10 +6,9 @@ use nitrokey_sys; use crate::config::{Config, RawConfig}; use crate::device::{Device, DeviceWrapper, Pro, Storage}; +use crate::error::Error; use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData, RawOtpSlotData}; -use crate::util::{ - generate_password, get_command_result, get_cstring, result_from_string, CommandError, -}; +use crate::util::{generate_password, get_command_result, get_cstring, result_from_string}; static TEMPORARY_PASSWORD_LENGTH: usize = 25; @@ -34,12 +33,12 @@ pub trait Authenticate { /// /// ```no_run /// use nitrokey::{Authenticate, DeviceWrapper, User}; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// /// fn perform_user_task(device: &User<DeviceWrapper>) {} /// fn perform_other_task(device: &DeviceWrapper) {} /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let device = match device.authenticate_user("123456") { /// Ok(user) => { @@ -56,10 +55,10 @@ pub trait Authenticate { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`RngError`]: enum.CommandError.html#variant.RngError /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, CommandError)> + fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)> where Self: Device + Sized; @@ -80,12 +79,12 @@ pub trait Authenticate { /// /// ```no_run /// use nitrokey::{Authenticate, Admin, DeviceWrapper}; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// /// fn perform_admin_task(device: &Admin<DeviceWrapper>) {} /// fn perform_other_task(device: &DeviceWrapper) {} /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let device = match device.authenticate_admin("123456") { /// Ok(admin) => { @@ -102,10 +101,10 @@ pub trait Authenticate { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`RngError`]: enum.CommandError.html#variant.RngError /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, CommandError)> + fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)> where Self: Device + Sized; } @@ -144,7 +143,7 @@ pub struct Admin<T: Device> { temp_password: Vec<u8>, } -fn authenticate<D, A, T>(device: D, password: &str, callback: T) -> Result<A, (D, CommandError)> +fn authenticate<D, A, T>(device: D, password: &str, callback: T) -> Result<A, (D, Error)> where D: Device, A: AuthenticatedDevice<D>, @@ -162,7 +161,7 @@ where let temp_password_ptr = temp_password.as_ptr() as *const c_char; return match callback(password_ptr, temp_password_ptr) { 0 => Ok(A::new(device, temp_password)), - rv => Err((device, CommandError::from(rv))), + rv => Err((device, Error::from(rv))), }; } @@ -170,7 +169,7 @@ fn authenticate_user_wrapper<T, C>( device: T, constructor: C, password: &str, -) -> Result<User<DeviceWrapper>, (DeviceWrapper, CommandError)> +) -> Result<User<DeviceWrapper>, (DeviceWrapper, Error)> where T: Device, C: Fn(T) -> DeviceWrapper, @@ -186,7 +185,7 @@ fn authenticate_admin_wrapper<T, C>( device: T, constructor: C, password: &str, -) -> Result<Admin<DeviceWrapper>, (DeviceWrapper, CommandError)> +) -> Result<Admin<DeviceWrapper>, (DeviceWrapper, Error)> where T: Device, C: Fn(T) -> DeviceWrapper, @@ -216,14 +215,14 @@ impl<T: Device> Deref for User<T> { } impl<T: Device> GenerateOtp for User<T> { - fn get_hotp_code(&self, slot: u8) -> Result<String, CommandError> { + fn get_hotp_code(&self, slot: u8) -> Result<String, Error> { unsafe { let temp_password_ptr = self.temp_password.as_ptr() as *const c_char; return result_from_string(nitrokey_sys::NK_get_hotp_code_PIN(slot, temp_password_ptr)); } } - fn get_totp_code(&self, slot: u8) -> Result<String, CommandError> { + fn get_totp_code(&self, slot: u8) -> Result<String, Error> { unsafe { let temp_password_ptr = self.temp_password.as_ptr() as *const c_char; return result_from_string(nitrokey_sys::NK_get_totp_code_PIN( @@ -272,9 +271,9 @@ impl<T: Device> Admin<T> { /// /// ```no_run /// use nitrokey::{Authenticate, Config}; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let config = Config::new(None, None, None, false); /// match device.authenticate_admin("12345678") { @@ -288,8 +287,8 @@ impl<T: Device> Admin<T> { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - pub fn write_config(&self, config: Config) -> Result<(), CommandError> { + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot + pub fn write_config(&self, config: Config) -> Result<(), Error> { let raw_config = RawConfig::try_from(config)?; unsafe { get_command_result(nitrokey_sys::NK_write_config( @@ -303,7 +302,7 @@ impl<T: Device> Admin<T> { } } - fn write_otp_slot<C>(&self, data: OtpSlotData, callback: C) -> Result<(), CommandError> + fn write_otp_slot<C>(&self, data: OtpSlotData, callback: C) -> Result<(), Error> where C: Fn(RawOtpSlotData, *const c_char) -> c_int, { @@ -314,7 +313,7 @@ impl<T: Device> Admin<T> { } impl<T: Device> ConfigureOtp for Admin<T> { - fn write_hotp_slot(&self, data: OtpSlotData, counter: u64) -> Result<(), CommandError> { + fn write_hotp_slot(&self, data: OtpSlotData, counter: u64) -> Result<(), Error> { self.write_otp_slot(data, |raw_data: RawOtpSlotData, temp_password_ptr| unsafe { nitrokey_sys::NK_write_hotp_slot( raw_data.number, @@ -330,7 +329,7 @@ impl<T: Device> ConfigureOtp for Admin<T> { }) } - fn write_totp_slot(&self, data: OtpSlotData, time_window: u16) -> Result<(), CommandError> { + fn write_totp_slot(&self, data: OtpSlotData, time_window: u16) -> Result<(), Error> { self.write_otp_slot(data, |raw_data: RawOtpSlotData, temp_password_ptr| unsafe { nitrokey_sys::NK_write_totp_slot( raw_data.number, @@ -346,12 +345,12 @@ impl<T: Device> ConfigureOtp for Admin<T> { }) } - fn erase_hotp_slot(&self, slot: u8) -> Result<(), CommandError> { + fn erase_hotp_slot(&self, slot: u8) -> Result<(), Error> { let temp_password_ptr = self.temp_password.as_ptr() as *const c_char; unsafe { get_command_result(nitrokey_sys::NK_erase_hotp_slot(slot, temp_password_ptr)) } } - fn erase_totp_slot(&self, slot: u8) -> Result<(), CommandError> { + fn erase_totp_slot(&self, slot: u8) -> Result<(), Error> { let temp_password_ptr = self.temp_password.as_ptr() as *const c_char; unsafe { get_command_result(nitrokey_sys::NK_erase_totp_slot(slot, temp_password_ptr)) } } @@ -367,7 +366,7 @@ impl<T: Device> AuthenticatedDevice<T> for Admin<T> { } impl Authenticate for DeviceWrapper { - fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, CommandError)> { + fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)> { match self { DeviceWrapper::Storage(storage) => { authenticate_user_wrapper(storage, DeviceWrapper::Storage, password) @@ -376,7 +375,7 @@ impl Authenticate for DeviceWrapper { } } - fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, CommandError)> { + fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)> { match self { DeviceWrapper::Storage(storage) => { authenticate_admin_wrapper(storage, DeviceWrapper::Storage, password) @@ -389,13 +388,13 @@ impl Authenticate for DeviceWrapper { } impl Authenticate for Pro { - fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, CommandError)> { + fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr) }) } - fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, CommandError)> { + fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr) }) @@ -403,13 +402,13 @@ impl Authenticate for Pro { } impl Authenticate for Storage { - fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, CommandError)> { + fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr) }) } - fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, CommandError)> { + fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr) }) diff --git a/src/config.rs b/src/config.rs index 2ce6f77..6aa6d10 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,4 @@ -use crate::util::CommandError; +use crate::error::{Error, LibraryError}; /// The configuration for a Nitrokey. #[derive(Clone, Copy, Debug, PartialEq)] @@ -35,13 +35,13 @@ fn config_otp_slot_to_option(value: u8) -> Option<u8> { None } -fn option_to_config_otp_slot(value: Option<u8>) -> Result<u8, CommandError> { +fn option_to_config_otp_slot(value: Option<u8>) -> Result<u8, Error> { match value { Some(value) => { if value < 3 { Ok(value) } else { - Err(CommandError::InvalidSlot) + Err(LibraryError::InvalidSlot.into()) } } None => Ok(255), @@ -66,7 +66,7 @@ impl Config { } impl RawConfig { - pub fn try_from(config: Config) -> Result<RawConfig, CommandError> { + pub fn try_from(config: Config) -> Result<RawConfig, Error> { Ok(RawConfig { numlock: option_to_config_otp_slot(config.numlock)?, capslock: option_to_config_otp_slot(config.capslock)?, diff --git a/src/device.rs b/src/device.rs index d794e1b..16064c3 100644 --- a/src/device.rs +++ b/src/device.rs @@ -5,11 +5,10 @@ use nitrokey_sys; use crate::auth::Authenticate; use crate::config::{Config, RawConfig}; +use crate::error::{CommunicationError, Error}; use crate::otp::GenerateOtp; use crate::pws::GetPasswordSafe; -use crate::util::{ - get_command_result, get_cstring, get_last_error, result_from_string, CommandError, -}; +use crate::util::{get_command_result, get_cstring, get_last_error, result_from_string}; /// Available Nitrokey models. #[derive(Clone, Copy, Debug, PartialEq)] @@ -64,12 +63,12 @@ impl fmt::Display for VolumeMode { /// /// ```no_run /// use nitrokey::{Authenticate, DeviceWrapper, User}; -/// # use nitrokey::CommandError; +/// # use nitrokey::Error; /// /// fn perform_user_task(device: &User<DeviceWrapper>) {} /// fn perform_other_task(device: &DeviceWrapper) {} /// -/// # fn try_main() -> Result<(), CommandError> { +/// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let device = match device.authenticate_user("123456") { /// Ok(user) => { @@ -90,12 +89,12 @@ impl fmt::Display for VolumeMode { /// /// ```no_run /// use nitrokey::{DeviceWrapper, Storage}; -/// # use nitrokey::CommandError; +/// # use nitrokey::Error; /// /// fn perform_common_task(device: &DeviceWrapper) {} /// fn perform_storage_task(device: &Storage) {} /// -/// # fn try_main() -> Result<(), CommandError> { +/// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// perform_common_task(&device); /// match device { @@ -128,12 +127,12 @@ pub enum DeviceWrapper { /// /// ```no_run /// use nitrokey::{Authenticate, User, Pro}; -/// # use nitrokey::CommandError; +/// # use nitrokey::Error; /// /// fn perform_user_task(device: &User<Pro>) {} /// fn perform_other_task(device: &Pro) {} /// -/// # fn try_main() -> Result<(), CommandError> { +/// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Pro::connect()?; /// let device = match device.authenticate_user("123456") { /// Ok(user) => { @@ -170,12 +169,12 @@ pub struct Pro {} /// /// ```no_run /// use nitrokey::{Authenticate, User, Storage}; -/// # use nitrokey::CommandError; +/// # use nitrokey::Error; /// /// fn perform_user_task(device: &User<Storage>) {} /// fn perform_other_task(device: &Storage) {} /// -/// # fn try_main() -> Result<(), CommandError> { +/// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// let device = match device.authenticate_user("123456") { /// Ok(user) => { @@ -287,16 +286,16 @@ pub struct StorageStatus { /// /// This trait provides the commands that can be executed without authentication and that are /// present on all supported Nitrokey devices. -pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { +pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// Returns the model of the connected Nitrokey device. /// /// # Example /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// println!("Connected to a Nitrokey {}", device.get_model()); /// # Ok(()) @@ -310,9 +309,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.get_serial_number() { /// Ok(number) => println!("serial no: {}", number), @@ -321,7 +320,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # Ok(()) /// # } /// ``` - fn get_serial_number(&self) -> Result<String, CommandError> { + fn get_serial_number(&self) -> Result<String, Error> { unsafe { result_from_string(nitrokey_sys::NK_device_serial_number()) } } @@ -332,9 +331,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let count = device.get_user_retry_count(); /// println!("{} remaining authentication attempts (user)", count); @@ -352,9 +351,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let count = device.get_admin_retry_count(); /// println!("{} remaining authentication attempts (admin)", count); @@ -371,9 +370,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// println!( /// "Firmware version: {}.{}", @@ -393,9 +392,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// println!( /// "Firmware version: {}.{}", @@ -414,9 +413,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let config = device.get_config()?; /// println!("numlock binding: {:?}", config.numlock); @@ -426,7 +425,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # Ok(()) /// # } /// ``` - fn get_config(&self) -> Result<Config, CommandError> { + fn get_config(&self) -> Result<Config, Error> { unsafe { let config_ptr = nitrokey_sys::NK_read_config(); if config_ptr.is_null() { @@ -450,9 +449,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.change_admin_pin("12345678", "12345679") { /// Ok(()) => println!("Updated admin PIN."), @@ -462,9 +461,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn change_admin_pin(&self, current: &str, new: &str) -> Result<(), CommandError> { + fn change_admin_pin(&self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; let new_string = get_cstring(new)?; unsafe { @@ -486,9 +485,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.change_user_pin("123456", "123457") { /// Ok(()) => println!("Updated admin PIN."), @@ -498,9 +497,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn change_user_pin(&self, current: &str, new: &str) -> Result<(), CommandError> { + fn change_user_pin(&self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; let new_string = get_cstring(new)?; unsafe { @@ -522,9 +521,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.unlock_user_pin("12345678", "123456") { /// Ok(()) => println!("Unlocked user PIN."), @@ -534,9 +533,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn unlock_user_pin(&self, admin_pin: &str, user_pin: &str) -> Result<(), CommandError> { + fn unlock_user_pin(&self, admin_pin: &str, user_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; let user_pin_string = get_cstring(user_pin)?; unsafe { @@ -556,9 +555,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.lock() { /// Ok(()) => println!("Locked the Nitrokey device."), @@ -567,7 +566,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # Ok(()) /// # } /// ``` - fn lock(&self) -> Result<(), CommandError> { + fn lock(&self) -> Result<(), Error> { unsafe { get_command_result(nitrokey_sys::NK_lock_device()) } } @@ -587,9 +586,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.factory_reset("12345678") { /// Ok(()) => println!("Performed a factory reset."), @@ -600,7 +599,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// ``` /// /// [`build_aes_key`]: #method.build_aes_key - fn factory_reset(&self, admin_pin: &str) -> Result<(), CommandError> { + fn factory_reset(&self, admin_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; unsafe { get_command_result(nitrokey_sys::NK_factory_reset(admin_pin_string.as_ptr())) } } @@ -621,9 +620,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.build_aes_key("12345678") { /// Ok(()) => println!("New AES keys have been built."), @@ -634,7 +633,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// ``` /// /// [`factory_reset`]: #method.factory_reset - fn build_aes_key(&self, admin_pin: &str) -> Result<(), CommandError> { + fn build_aes_key(&self, admin_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; unsafe { get_command_result(nitrokey_sys::NK_build_aes_key(admin_pin_string.as_ptr())) } } @@ -645,7 +644,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// # Errors /// -/// - [`Undefined`][] if no Nitrokey device is connected +/// - [`NotConnected`][] if no Nitrokey device is connected /// /// # Example /// @@ -660,15 +659,15 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// } /// ``` /// -/// [`Undefined`]: enum.CommandError.html#variant.Undefined -pub fn connect() -> Result<DeviceWrapper, CommandError> { +/// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected +pub fn connect() -> Result<DeviceWrapper, Error> { unsafe { match nitrokey_sys::NK_login_auto() { 1 => match get_connected_device() { Some(wrapper) => Ok(wrapper), - None => Err(CommandError::Undefined), + None => Err(CommunicationError::NotConnected.into()), }, - _ => Err(CommandError::Undefined), + _ => Err(CommunicationError::NotConnected.into()), } } } @@ -677,7 +676,7 @@ pub fn connect() -> Result<DeviceWrapper, CommandError> { /// /// # Errors /// -/// - [`Undefined`][] if no Nitrokey device of the given model is connected +/// - [`NotConnected`][] if no Nitrokey device of the given model is connected /// /// # Example /// @@ -693,12 +692,12 @@ pub fn connect() -> Result<DeviceWrapper, CommandError> { /// } /// ``` /// -/// [`Undefined`]: enum.CommandError.html#variant.Undefined -pub fn connect_model(model: Model) -> Result<DeviceWrapper, CommandError> { +/// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected +pub fn connect_model(model: Model) -> Result<DeviceWrapper, Error> { if connect_enum(model) { Ok(create_device_wrapper(model)) } else { - Err(CommandError::Undefined) + Err(CommunicationError::NotConnected.into()) } } @@ -741,19 +740,19 @@ impl DeviceWrapper { } impl GenerateOtp for DeviceWrapper { - fn get_hotp_slot_name(&self, slot: u8) -> Result<String, CommandError> { + fn get_hotp_slot_name(&self, slot: u8) -> Result<String, Error> { self.device().get_hotp_slot_name(slot) } - fn get_totp_slot_name(&self, slot: u8) -> Result<String, CommandError> { + fn get_totp_slot_name(&self, slot: u8) -> Result<String, Error> { self.device().get_totp_slot_name(slot) } - fn get_hotp_code(&self, slot: u8) -> Result<String, CommandError> { + fn get_hotp_code(&self, slot: u8) -> Result<String, Error> { self.device().get_hotp_code(slot) } - fn get_totp_code(&self, slot: u8) -> Result<String, CommandError> { + fn get_totp_code(&self, slot: u8) -> Result<String, Error> { self.device().get_totp_code(slot) } } @@ -772,7 +771,7 @@ impl Pro { /// /// # Errors /// - /// - [`Undefined`][] if no Nitrokey device of the given model is connected + /// - [`NotConnected`][] if no Nitrokey device of the given model is connected /// /// # Example /// @@ -787,12 +786,12 @@ impl Pro { /// } /// ``` /// - /// [`Undefined`]: enum.CommandError.html#variant.Undefined - pub fn connect() -> Result<Pro, CommandError> { + /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected + pub fn connect() -> Result<Pro, Error> { // TODO: maybe Option instead of Result? match connect_enum(Model::Pro) { true => Ok(Pro {}), - false => Err(CommandError::Undefined), + false => Err(CommunicationError::NotConnected.into()), } } } @@ -818,7 +817,7 @@ impl Storage { /// /// # Errors /// - /// - [`Undefined`][] if no Nitrokey device of the given model is connected + /// - [`NotConnected`][] if no Nitrokey device of the given model is connected /// /// # Example /// @@ -833,12 +832,12 @@ impl Storage { /// } /// ``` /// - /// [`Undefined`]: enum.CommandError.html#variant.Undefined - pub fn connect() -> Result<Storage, CommandError> { + /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected + pub fn connect() -> Result<Storage, Error> { // TODO: maybe Option instead of Result? match connect_enum(Model::Storage) { true => Ok(Storage {}), - false => Err(CommandError::Undefined), + false => Err(CommunicationError::NotConnected.into()), } } @@ -856,9 +855,9 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// match device.change_update_pin("12345678", "87654321") { /// Ok(()) => println!("Updated update PIN."), @@ -868,9 +867,9 @@ impl Storage { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn change_update_pin(&self, current: &str, new: &str) -> Result<(), CommandError> { + pub fn change_update_pin(&self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; let new_string = get_cstring(new)?; unsafe { @@ -896,9 +895,9 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// match device.enable_firmware_update("12345678") { /// Ok(()) => println!("Nitrokey entered update mode."), @@ -908,9 +907,9 @@ impl Storage { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn enable_firmware_update(&self, update_pin: &str) -> Result<(), CommandError> { + pub fn enable_firmware_update(&self, update_pin: &str) -> Result<(), Error> { let update_pin_string = get_cstring(update_pin)?; unsafe { get_command_result(nitrokey_sys::NK_enable_firmware_update( @@ -932,9 +931,9 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// match device.enable_encrypted_volume("123456") { /// Ok(()) => println!("Enabled the encrypted volume."), @@ -944,9 +943,9 @@ impl Storage { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn enable_encrypted_volume(&self, user_pin: &str) -> Result<(), CommandError> { + pub fn enable_encrypted_volume(&self, user_pin: &str) -> Result<(), Error> { let user_pin = get_cstring(user_pin)?; unsafe { get_command_result(nitrokey_sys::NK_unlock_encrypted_volume(user_pin.as_ptr())) } } @@ -959,11 +958,11 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// /// fn use_volume() {} /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// match device.enable_encrypted_volume("123456") { /// Ok(()) => { @@ -981,7 +980,7 @@ impl Storage { /// # Ok(()) /// # } /// ``` - pub fn disable_encrypted_volume(&self) -> Result<(), CommandError> { + pub fn disable_encrypted_volume(&self) -> Result<(), Error> { unsafe { get_command_result(nitrokey_sys::NK_lock_encrypted_volume()) } } @@ -1007,9 +1006,9 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// device.enable_encrypted_volume("123445")?; /// match device.enable_hidden_volume("hidden-pw") { @@ -1022,8 +1021,8 @@ impl Storage { /// /// [`enable_encrypted_volume`]: #method.enable_encrypted_volume /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString - pub fn enable_hidden_volume(&self, volume_password: &str) -> Result<(), CommandError> { + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString + pub fn enable_hidden_volume(&self, volume_password: &str) -> Result<(), Error> { let volume_password = get_cstring(volume_password)?; unsafe { get_command_result(nitrokey_sys::NK_unlock_hidden_volume( @@ -1040,11 +1039,11 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// /// fn use_volume() {} /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// device.enable_encrypted_volume("123445")?; /// match device.enable_hidden_volume("hidden-pw") { @@ -1063,7 +1062,7 @@ impl Storage { /// # Ok(()) /// # } /// ``` - pub fn disable_hidden_volume(&self) -> Result<(), CommandError> { + pub fn disable_hidden_volume(&self) -> Result<(), Error> { unsafe { get_command_result(nitrokey_sys::NK_lock_hidden_volume()) } } @@ -1089,9 +1088,9 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// device.enable_encrypted_volume("123445")?; /// device.create_hidden_volume(0, 0, 100, "hidden-pw")?; @@ -1100,14 +1099,14 @@ impl Storage { /// ``` /// /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString pub fn create_hidden_volume( &self, slot: u8, start: u8, end: u8, password: &str, - ) -> Result<(), CommandError> { + ) -> Result<(), Error> { let password = get_cstring(password)?; unsafe { get_command_result(nitrokey_sys::NK_create_hidden_volume( @@ -1133,10 +1132,10 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// use nitrokey::VolumeMode; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// match device.set_unencrypted_volume_mode("123456", VolumeMode::ReadWrite) { /// Ok(()) => println!("Set the unencrypted volume to read-write mode."), @@ -1146,13 +1145,13 @@ impl Storage { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn set_unencrypted_volume_mode( &self, admin_pin: &str, mode: VolumeMode, - ) -> Result<(), CommandError> { + ) -> Result<(), Error> { let admin_pin = get_cstring(admin_pin)?; let result = match mode { VolumeMode::ReadOnly => unsafe { @@ -1170,11 +1169,11 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// /// fn use_volume() {} /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// match device.get_status() { /// Ok(status) => { @@ -1185,7 +1184,7 @@ impl Storage { /// # Ok(()) /// # } /// ``` - pub fn get_status(&self) -> Result<StorageStatus, CommandError> { + pub fn get_status(&self) -> Result<StorageStatus, Error> { let mut raw_status = nitrokey_sys::NK_storage_status { unencrypted_volume_read_only: false, unencrypted_volume_active: false, @@ -1214,11 +1213,11 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// /// fn use_volume() {} /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// match device.get_production_info() { /// Ok(data) => { @@ -1230,7 +1229,7 @@ impl Storage { /// # Ok(()) /// # } /// ``` - pub fn get_production_info(&self) -> Result<StorageProductionInfo, CommandError> { + pub fn get_production_info(&self) -> Result<StorageProductionInfo, Error> { let mut raw_data = nitrokey_sys::NK_storage_ProductionTest { FirmwareVersion_au8: [0, 2], FirmwareVersionInternal_u8: 0, @@ -1265,9 +1264,9 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// match device.clear_new_sd_card_warning("12345678") { /// Ok(()) => println!("Cleared the new SD card warning."), @@ -1277,9 +1276,9 @@ impl Storage { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn clear_new_sd_card_warning(&self, admin_pin: &str) -> Result<(), CommandError> { + pub fn clear_new_sd_card_warning(&self, admin_pin: &str) -> Result<(), Error> { let admin_pin = get_cstring(admin_pin)?; get_command_result(unsafe { nitrokey_sys::NK_clear_new_sd_card_warning(admin_pin.as_ptr()) @@ -1287,7 +1286,7 @@ impl Storage { } /// Blinks the red and green LED alternatively and infinitely until the device is reconnected. - pub fn wink(&self) -> Result<(), CommandError> { + pub fn wink(&self) -> Result<(), Error> { get_command_result(unsafe { nitrokey_sys::NK_wink() }) } @@ -1305,9 +1304,9 @@ impl Storage { /// - [`InvalidString`][] if one of the provided passwords contains a null byte /// - [`WrongPassword`][] if the admin password is wrong /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn export_firmware(&self, admin_pin: &str) -> Result<(), CommandError> { + pub fn export_firmware(&self, admin_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; get_command_result(unsafe { nitrokey_sys::NK_export_firmware(admin_pin_string.as_ptr()) }) } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..cde9a34 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,233 @@ +use std::error; +use std::fmt; +use std::os::raw; +use std::result; + +/// An error returned by the nitrokey crate. +#[derive(Debug)] +pub enum Error { + /// An error reported by the Nitrokey device in the response packet. + CommandError(CommandError), + /// A device communication. + CommunicationError(CommunicationError), + /// A library usage error. + LibraryError(LibraryError), + /// An error that occured during random number generation. + RandError(rand_core::Error), + /// An error that is caused by an unexpected value returned by libnitrokey. + UnexpectedError, + /// An unknown error returned by libnitrokey. + Unknown(i64), +} + +impl From<raw::c_int> for Error { + fn from(code: raw::c_int) -> Self { + if let Some(err) = CommandError::try_from(code) { + Error::CommandError(err) + } else if let Some(err) = CommunicationError::try_from(256 - code) { + Error::CommunicationError(err) + } else if let Some(err) = LibraryError::try_from(code) { + Error::LibraryError(err) + } else { + Error::Unknown(code.into()) + } + } +} + +impl From<CommandError> for Error { + fn from(err: CommandError) -> Self { + Error::CommandError(err) + } +} + +impl From<CommunicationError> for Error { + fn from(err: CommunicationError) -> Self { + Error::CommunicationError(err) + } +} + +impl From<LibraryError> for Error { + fn from(err: LibraryError) -> Self { + Error::LibraryError(err) + } +} + +impl From<rand_core::Error> for Error { + fn from(error: rand_core::Error) -> Self { + Error::RandError(error) + } +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + Error::CommandError(ref err) => Some(err), + Error::CommunicationError(ref err) => Some(err), + Error::LibraryError(ref err) => Some(err), + Error::RandError(ref err) => Some(err), + Error::UnexpectedError => None, + Error::Unknown(_) => None, + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Error::CommandError(ref err) => write!(f, "Command error: {}", err), + Error::CommunicationError(ref err) => write!(f, "Communication error: {}", err), + Error::LibraryError(ref err) => write!(f, "Library error: {}", err), + Error::RandError(ref err) => write!(f, "RNG error: {}", err), + Error::UnexpectedError => write!(f, "An unexpected error occurred"), + Error::Unknown(ref err) => write!(f, "Unknown error: {}", err), + } + } +} + +/// A result returned by the nitrokey crate. +pub type Result<T> = result::Result<T, Error>; + +/// An error reported by the Nitrokey device in the response packet. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum CommandError { + /// A packet with a wrong checksum has been sent or received. + WrongCrc, + /// A command tried to access an OTP slot that does not exist. + WrongSlot, + /// A command tried to generate an OTP on a slot that is not configured. + SlotNotProgrammed, + /// The provided password is wrong. + WrongPassword, + /// You are not authorized for this command or provided a wrong temporary + /// password. + NotAuthorized, + /// An error occurred when getting or setting the time. + Timestamp, + /// You did not provide a name for the OTP slot. + NoName, + /// This command is not supported by this device. + NotSupported, + /// This command is unknown. + UnknownCommand, + /// AES decryption failed. + AesDecryptionFailed, +} + +impl CommandError { + fn try_from(value: raw::c_int) -> Option<Self> { + match value { + 1 => Some(CommandError::WrongCrc), + 2 => Some(CommandError::WrongSlot), + 3 => Some(CommandError::SlotNotProgrammed), + 4 => Some(CommandError::WrongPassword), + 5 => Some(CommandError::NotAuthorized), + 6 => Some(CommandError::Timestamp), + 7 => Some(CommandError::NoName), + 8 => Some(CommandError::NotSupported), + 9 => Some(CommandError::UnknownCommand), + 10 => Some(CommandError::AesDecryptionFailed), + _ => None, + } + } +} + +impl error::Error for CommandError {} + +impl fmt::Display for CommandError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match *self { + CommandError::WrongCrc => "A packet with a wrong checksum has been sent or received", + CommandError::WrongSlot => "The given slot does not exist", + CommandError::SlotNotProgrammed => "The given slot is not programmed", + CommandError::WrongPassword => "The given password is wrong", + CommandError::NotAuthorized => { + "You are not authorized for this command or provided a wrong temporary \ + password" + } + CommandError::Timestamp => "An error occurred when getting or setting the time", + CommandError::NoName => "You did not provide a name for the slot", + CommandError::NotSupported => "This command is not supported by this device", + CommandError::UnknownCommand => "This command is unknown", + CommandError::AesDecryptionFailed => "AES decryption failed", + }) + } +} + +/// A device communication error. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum CommunicationError { + /// Could not connect to a Nitrokey device. + NotConnected, + /// Sending a packet failed. + SendingFailure, + /// Receiving a packet failed. + ReceivingFailure, + /// A packet with a wrong checksum was received. + InvalidCrc, +} + +impl CommunicationError { + fn try_from(value: raw::c_int) -> Option<Self> { + match value { + 2 => Some(CommunicationError::NotConnected), + 3 => Some(CommunicationError::SendingFailure), + 4 => Some(CommunicationError::ReceivingFailure), + 5 => Some(CommunicationError::InvalidCrc), + _ => None, + } + } +} + +impl error::Error for CommunicationError {} + +impl fmt::Display for CommunicationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match *self { + CommunicationError::NotConnected => "Could not connect to a Nitrokey device", + CommunicationError::SendingFailure => "Sending a packet failed", + CommunicationError::ReceivingFailure => "Receiving a packet failed", + CommunicationError::InvalidCrc => "A packet with a wrong checksum was received", + }) + } +} + +/// A library usage error. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum LibraryError { + /// A supplied string exceeded a length limit. + StringTooLong, + /// You passed an invalid slot. + InvalidSlot, + /// The supplied string was not in hexadecimal format. + InvalidHexString, + /// The target buffer was smaller than the source. + TargetBufferTooSmall, + /// You passed a string containing a null byte. + InvalidString, +} + +impl LibraryError { + fn try_from(value: raw::c_int) -> Option<Self> { + match value { + 200 => Some(LibraryError::StringTooLong), + 201 => Some(LibraryError::InvalidSlot), + 202 => Some(LibraryError::InvalidHexString), + 203 => Some(LibraryError::TargetBufferTooSmall), + _ => None, + } + } +} + +impl error::Error for LibraryError {} + +impl fmt::Display for LibraryError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match *self { + LibraryError::StringTooLong => "The supplied string is too long", + LibraryError::InvalidSlot => "The given slot is invalid", + LibraryError::InvalidHexString => "The supplied string is not in hexadecimal format", + LibraryError::TargetBufferTooSmall => "The target buffer is too small", + LibraryError::InvalidString => "You passed a string containing a null byte", + }) + } +} @@ -25,9 +25,9 @@ //! //! ```no_run //! use nitrokey::Device; -//! # use nitrokey::CommandError; +//! # use nitrokey::Error; //! -//! # fn try_main() -> Result<(), CommandError> { +//! # fn try_main() -> Result<(), Error> { //! let device = nitrokey::connect()?; //! println!("{}", device.get_serial_number()?); //! # Ok(()) @@ -38,9 +38,9 @@ //! //! ```no_run //! use nitrokey::{Authenticate, ConfigureOtp, OtpMode, OtpSlotData}; -//! # use nitrokey::CommandError; +//! # use nitrokey::Error; //! -//! # fn try_main() -> Result<(), (CommandError)> { +//! # fn try_main() -> Result<(), Error> { //! let device = nitrokey::connect()?; //! let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::SixDigits); //! match device.authenticate_admin("12345678") { @@ -60,9 +60,9 @@ //! //! ```no_run //! use nitrokey::{Device, GenerateOtp}; -//! # use nitrokey::CommandError; +//! # use nitrokey::Error; //! -//! # fn try_main() -> Result<(), (CommandError)> { +//! # fn try_main() -> Result<(), Error> { //! let device = nitrokey::connect()?; //! match device.get_hotp_code(1) { //! Ok(code) => println!("Generated HOTP code: {}", code), @@ -89,6 +89,7 @@ mod auth; mod config; mod device; +mod error; mod otp; mod pws; mod util; @@ -103,9 +104,10 @@ pub use crate::device::{ connect, connect_model, Device, DeviceWrapper, Model, Pro, SdCardData, Storage, StorageProductionInfo, StorageStatus, VolumeMode, VolumeStatus, }; +pub use crate::error::{CommandError, CommunicationError, Error, LibraryError, Result}; pub use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData}; pub use crate::pws::{GetPasswordSafe, PasswordSafe, SLOT_COUNT}; -pub use crate::util::{CommandError, LogLevel}; +pub use crate::util::LogLevel; /// The default admin PIN for all Nitrokey devices. pub const DEFAULT_ADMIN_PIN: &'static str = "12345678"; @@ -2,7 +2,8 @@ use std::ffi::CString; use nitrokey_sys; -use crate::util::{get_command_result, get_cstring, result_from_string, CommandError}; +use crate::error::Error; +use crate::util::{get_command_result, get_cstring, result_from_string}; /// Modes for one-time password generation. #[derive(Clone, Copy, Debug, PartialEq)] @@ -28,9 +29,9 @@ pub trait ConfigureOtp { /// /// ```no_run /// use nitrokey::{Authenticate, ConfigureOtp, OtpMode, OtpSlotData}; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), (CommandError)> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::SixDigits); /// match device.authenticate_admin("12345678") { @@ -46,10 +47,10 @@ pub trait ConfigureOtp { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`NoName`]: enum.CommandError.html#variant.NoName - fn write_hotp_slot(&self, data: OtpSlotData, counter: u64) -> Result<(), CommandError>; + fn write_hotp_slot(&self, data: OtpSlotData, counter: u64) -> Result<(), Error>; /// Configure a TOTP slot with the given data and set the TOTP time window to the given value /// (default 30). @@ -64,9 +65,9 @@ pub trait ConfigureOtp { /// /// ```no_run /// use nitrokey::{Authenticate, ConfigureOtp, OtpMode, OtpSlotData}; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), (CommandError)> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::EightDigits); /// match device.authenticate_admin("12345678") { @@ -82,10 +83,10 @@ pub trait ConfigureOtp { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`NoName`]: enum.CommandError.html#variant.NoName - fn write_totp_slot(&self, data: OtpSlotData, time_window: u16) -> Result<(), CommandError>; + fn write_totp_slot(&self, data: OtpSlotData, time_window: u16) -> Result<(), Error>; /// Erases an HOTP slot. /// @@ -97,9 +98,9 @@ pub trait ConfigureOtp { /// /// ```no_run /// use nitrokey::{Authenticate, ConfigureOtp}; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), (CommandError)> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.authenticate_admin("12345678") { /// Ok(admin) => { @@ -114,8 +115,8 @@ pub trait ConfigureOtp { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - fn erase_hotp_slot(&self, slot: u8) -> Result<(), CommandError>; + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot + fn erase_hotp_slot(&self, slot: u8) -> Result<(), Error>; /// Erases a TOTP slot. /// @@ -127,9 +128,9 @@ pub trait ConfigureOtp { /// /// ```no_run /// use nitrokey::{Authenticate, ConfigureOtp}; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), (CommandError)> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.authenticate_admin("12345678") { /// Ok(admin) => { @@ -144,8 +145,8 @@ pub trait ConfigureOtp { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - fn erase_totp_slot(&self, slot: u8) -> Result<(), CommandError>; + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot + fn erase_totp_slot(&self, slot: u8) -> Result<(), Error>; } /// Provides methods to generate OTP codes and to query OTP slots on a Nitrokey @@ -164,9 +165,9 @@ pub trait GenerateOtp { /// ```no_run /// use std::time; /// use nitrokey::GenerateOtp; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH); /// match time { @@ -183,7 +184,7 @@ pub trait GenerateOtp { /// /// [`get_totp_code`]: #method.get_totp_code /// [`Timestamp`]: enum.CommandError.html#variant.Timestamp - fn set_time(&self, time: u64, force: bool) -> Result<(), CommandError> { + fn set_time(&self, time: u64, force: bool) -> Result<(), Error> { let result = if force { unsafe { nitrokey_sys::NK_totp_set_time(time) } } else { @@ -202,22 +203,22 @@ pub trait GenerateOtp { /// # Example /// /// ```no_run - /// use nitrokey::{CommandError, GenerateOtp}; + /// use nitrokey::{CommandError, Error, GenerateOtp}; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.get_hotp_slot_name(1) { /// Ok(name) => println!("HOTP slot 1: {}", name), - /// Err(CommandError::SlotNotProgrammed) => println!("HOTP slot 1 not programmed"), + /// Err(Error::CommandError(CommandError::SlotNotProgrammed)) => println!("HOTP slot 1 not programmed"), /// Err(err) => println!("Could not get slot name: {}", err), /// }; /// # Ok(()) /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed - fn get_hotp_slot_name(&self, slot: u8) -> Result<String, CommandError> { + fn get_hotp_slot_name(&self, slot: u8) -> Result<String, Error> { unsafe { result_from_string(nitrokey_sys::NK_get_hotp_slot_name(slot)) } } @@ -231,22 +232,22 @@ pub trait GenerateOtp { /// # Example /// /// ```no_run - /// use nitrokey::{CommandError, GenerateOtp}; + /// use nitrokey::{CommandError, Error, GenerateOtp}; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.get_totp_slot_name(1) { /// Ok(name) => println!("TOTP slot 1: {}", name), - /// Err(CommandError::SlotNotProgrammed) => println!("TOTP slot 1 not programmed"), + /// Err(Error::CommandError(CommandError::SlotNotProgrammed)) => println!("TOTP slot 1 not programmed"), /// Err(err) => println!("Could not get slot name: {}", err), /// }; /// # Ok(()) /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed - fn get_totp_slot_name(&self, slot: u8) -> Result<String, CommandError> { + fn get_totp_slot_name(&self, slot: u8) -> Result<String, Error> { unsafe { result_from_string(nitrokey_sys::NK_get_totp_slot_name(slot)) } } @@ -263,9 +264,9 @@ pub trait GenerateOtp { /// /// ```no_run /// use nitrokey::GenerateOtp; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let code = device.get_hotp_code(1)?; /// println!("Generated HOTP code on slot 1: {}", code); @@ -274,10 +275,10 @@ pub trait GenerateOtp { /// ``` /// /// [`get_config`]: trait.Device.html#method.get_config - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`NotAuthorized`]: enum.CommandError.html#variant.NotAuthorized /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed - fn get_hotp_code(&self, slot: u8) -> Result<String, CommandError> { + fn get_hotp_code(&self, slot: u8) -> Result<String, Error> { unsafe { return result_from_string(nitrokey_sys::NK_get_hotp_code(slot)); } @@ -300,9 +301,9 @@ pub trait GenerateOtp { /// ```no_run /// use std::time; /// use nitrokey::GenerateOtp; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH); /// match time { @@ -319,10 +320,10 @@ pub trait GenerateOtp { /// /// [`set_time`]: #method.set_time /// [`get_config`]: trait.Device.html#method.get_config - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`NotAuthorized`]: enum.CommandError.html#variant.NotAuthorized /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed - fn get_totp_code(&self, slot: u8) -> Result<String, CommandError> { + fn get_totp_code(&self, slot: u8) -> Result<String, Error> { unsafe { return result_from_string(nitrokey_sys::NK_get_totp_code(slot, 0, 0, 0)); } @@ -395,7 +396,7 @@ impl OtpSlotData { } impl RawOtpSlotData { - pub fn new(data: OtpSlotData) -> Result<RawOtpSlotData, CommandError> { + pub fn new(data: OtpSlotData) -> Result<RawOtpSlotData, Error> { let name = get_cstring(data.name)?; let secret = get_cstring(data.secret)?; let use_token_id = data.token_id.is_some(); @@ -2,9 +2,8 @@ use libc; use nitrokey_sys; use crate::device::{Device, DeviceWrapper, Pro, Storage}; -use crate::util::{ - get_command_result, get_cstring, get_last_error, result_from_string, CommandError, -}; +use crate::error::{CommandError, Error}; +use crate::util::{get_command_result, get_cstring, get_last_error, result_from_string}; /// The number of slots in a [`PasswordSafe`][]. /// @@ -30,9 +29,9 @@ pub const SLOT_COUNT: u8 = 16; /// /// ```no_run /// use nitrokey::{Device, GetPasswordSafe, PasswordSafe}; -/// # use nitrokey::CommandError; +/// # use nitrokey::Error; /// -/// fn use_password_safe(pws: &PasswordSafe) -> Result<(), CommandError> { +/// fn use_password_safe(pws: &PasswordSafe) -> Result<(), Error> { /// let name = pws.get_slot_name(0)?; /// let login = pws.get_slot_login(0)?; /// let password = pws.get_slot_login(0)?; @@ -40,7 +39,7 @@ pub const SLOT_COUNT: u8 = 16; /// Ok(()) /// } /// -/// # fn try_main() -> Result<(), CommandError> { +/// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let pws = device.get_password_safe("123456")?; /// use_password_safe(&pws); @@ -53,6 +52,7 @@ pub const SLOT_COUNT: u8 = 16; /// [`get_password_safe`]: trait.GetPasswordSafe.html#method.get_password_safe /// [`lock`]: trait.Device.html#method.lock /// [`GetPasswordSafe`]: trait.GetPasswordSafe.html +#[derive(Debug)] pub struct PasswordSafe<'a> { _device: &'a dyn Device, } @@ -89,11 +89,11 @@ pub trait GetPasswordSafe { /// /// ```no_run /// use nitrokey::{Device, GetPasswordSafe, PasswordSafe}; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// /// fn use_password_safe(pws: &PasswordSafe) {} /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.get_password_safe("123456") { /// Ok(pws) => { @@ -110,16 +110,16 @@ pub trait GetPasswordSafe { /// [`lock`]: trait.Device.html#method.lock /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed /// [`Device::build_aes_key`]: trait.Device.html#method.build_aes_key - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`Unknown`]: enum.CommandError.html#variant.Unknown /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, CommandError>; + fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, Error>; } fn get_password_safe<'a>( device: &'a dyn Device, user_pin: &str, -) -> Result<PasswordSafe<'a>, CommandError> { +) -> Result<PasswordSafe<'a>, Error> { let user_pin_string = get_cstring(user_pin)?; let result = unsafe { get_command_result(nitrokey_sys::NK_enable_password_safe( @@ -129,9 +129,9 @@ fn get_password_safe<'a>( result.map(|()| PasswordSafe { _device: device }) } -fn get_pws_result(s: String) -> Result<String, CommandError> { +fn get_pws_result(s: String) -> Result<String, Error> { if s.is_empty() { - Err(CommandError::SlotNotProgrammed) + Err(CommandError::SlotNotProgrammed.into()) } else { Ok(s) } @@ -146,9 +146,9 @@ impl<'a> PasswordSafe<'a> { /// /// ```no_run /// use nitrokey::{GetPasswordSafe, SLOT_COUNT}; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let pws = device.get_password_safe("123456")?; /// pws.get_slot_status()?.iter().enumerate().for_each(|(slot, programmed)| { @@ -161,7 +161,7 @@ impl<'a> PasswordSafe<'a> { /// # Ok(()) /// # } /// ``` - pub fn get_slot_status(&self) -> Result<[bool; SLOT_COUNT as usize], CommandError> { + pub fn get_slot_status(&self) -> Result<[bool; SLOT_COUNT as usize], Error> { let status_ptr = unsafe { nitrokey_sys::NK_get_password_safe_slot_status() }; if status_ptr.is_null() { return Err(get_last_error()); @@ -191,9 +191,9 @@ impl<'a> PasswordSafe<'a> { /// /// ```no_run /// use nitrokey::GetPasswordSafe; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.get_password_safe("123456") { /// Ok(pws) => { @@ -208,9 +208,9 @@ impl<'a> PasswordSafe<'a> { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed - pub fn get_slot_name(&self, slot: u8) -> Result<String, CommandError> { + pub fn get_slot_name(&self, slot: u8) -> Result<String, Error> { unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_name(slot)) } .and_then(get_pws_result) } @@ -228,9 +228,9 @@ impl<'a> PasswordSafe<'a> { /// /// ```no_run /// use nitrokey::GetPasswordSafe; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let pws = device.get_password_safe("123456")?; /// let name = pws.get_slot_name(0)?; @@ -241,9 +241,9 @@ impl<'a> PasswordSafe<'a> { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed - pub fn get_slot_login(&self, slot: u8) -> Result<String, CommandError> { + pub fn get_slot_login(&self, slot: u8) -> Result<String, Error> { unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_login(slot)) } .and_then(get_pws_result) } @@ -261,9 +261,9 @@ impl<'a> PasswordSafe<'a> { /// /// ```no_run /// use nitrokey::GetPasswordSafe; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let pws = device.get_password_safe("123456")?; /// let name = pws.get_slot_name(0)?; @@ -274,9 +274,9 @@ impl<'a> PasswordSafe<'a> { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed - pub fn get_slot_password(&self, slot: u8) -> Result<String, CommandError> { + pub fn get_slot_password(&self, slot: u8) -> Result<String, Error> { unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_password(slot)) } .and_then(get_pws_result) } @@ -292,9 +292,9 @@ impl<'a> PasswordSafe<'a> { /// /// ```no_run /// use nitrokey::GetPasswordSafe; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let pws = device.get_password_safe("123456")?; /// let name = pws.get_slot_name(0)?; @@ -305,15 +305,15 @@ impl<'a> PasswordSafe<'a> { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString pub fn write_slot( &self, slot: u8, name: &str, login: &str, password: &str, - ) -> Result<(), CommandError> { + ) -> Result<(), Error> { let name_string = get_cstring(name)?; let login_string = get_cstring(login)?; let password_string = get_cstring(password)?; @@ -338,9 +338,9 @@ impl<'a> PasswordSafe<'a> { /// /// ```no_run /// use nitrokey::GetPasswordSafe; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let pws = device.get_password_safe("123456")?; /// match pws.erase_slot(0) { @@ -351,8 +351,8 @@ impl<'a> PasswordSafe<'a> { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - pub fn erase_slot(&self, slot: u8) -> Result<(), CommandError> { + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot + pub fn erase_slot(&self, slot: u8) -> Result<(), Error> { unsafe { get_command_result(nitrokey_sys::NK_erase_password_safe_slot(slot)) } } } @@ -365,19 +365,19 @@ impl<'a> Drop for PasswordSafe<'a> { } impl GetPasswordSafe for Pro { - fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, CommandError> { + fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> { get_password_safe(self, user_pin) } } impl GetPasswordSafe for Storage { - fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, CommandError> { + fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> { get_password_safe(self, user_pin) } } impl GetPasswordSafe for DeviceWrapper { - fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, CommandError> { + fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> { get_password_safe(self, user_pin) } } diff --git a/src/util.rs b/src/util.rs index 567c478..79b8c34 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,53 +1,11 @@ -use std::borrow; use std::ffi::{CStr, CString}; -use std::fmt; use std::os::raw::{c_char, c_int}; use libc::{c_void, free}; use rand_core::RngCore; use rand_os::OsRng; -/// Error types returned by Nitrokey device or by the library. -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum CommandError { - /// A packet with a wrong checksum has been sent or received. - WrongCrc, - /// A command tried to access an OTP slot that does not exist. - WrongSlot, - /// A command tried to generate an OTP on a slot that is not configured. - SlotNotProgrammed, - /// The provided password is wrong. - WrongPassword, - /// You are not authorized for this command or provided a wrong temporary - /// password. - NotAuthorized, - /// An error occurred when getting or setting the time. - Timestamp, - /// You did not provide a name for the OTP slot. - NoName, - /// This command is not supported by this device. - NotSupported, - /// This command is unknown. - UnknownCommand, - /// AES decryption failed. - AesDecryptionFailed, - /// An unknown error occurred. - Unknown(i64), - /// An unspecified error occurred. - Undefined, - /// You passed a string containing a null byte. - InvalidString, - /// A supplied string exceeded a length limit. - StringTooLong, - /// You passed an invalid slot. - InvalidSlot, - /// The supplied string was not in hexadecimal format. - InvalidHexString, - /// The target buffer was smaller than the source. - TargetBufferTooSmall, - /// An error occurred during random number generation. - RngError, -} +use crate::error::{Error, LibraryError}; /// Log level for libnitrokey. /// @@ -76,9 +34,9 @@ pub fn owned_str_from_ptr(ptr: *const c_char) -> String { } } -pub fn result_from_string(ptr: *const c_char) -> Result<String, CommandError> { +pub fn result_from_string(ptr: *const c_char) -> Result<String, Error> { if ptr.is_null() { - return Err(CommandError::Undefined); + return Err(Error::UnexpectedError); } unsafe { let s = owned_str_from_ptr(ptr); @@ -93,103 +51,34 @@ pub fn result_from_string(ptr: *const c_char) -> Result<String, CommandError> { } } -pub fn get_command_result(value: c_int) -> Result<(), CommandError> { +pub fn get_command_result(value: c_int) -> Result<(), Error> { match value { 0 => Ok(()), - other => Err(CommandError::from(other)), + other => Err(Error::from(other)), } } -pub fn get_last_result() -> Result<(), CommandError> { +pub fn get_last_result() -> Result<(), Error> { let value = unsafe { nitrokey_sys::NK_get_last_command_status() } as c_int; get_command_result(value) } -pub fn get_last_error() -> CommandError { +pub fn get_last_error() -> Error { return match get_last_result() { - Ok(()) => CommandError::Undefined, + Ok(()) => Error::UnexpectedError, Err(err) => err, }; } -pub fn generate_password(length: usize) -> Result<Vec<u8>, CommandError> { +pub fn generate_password(length: usize) -> Result<Vec<u8>, Error> { let mut rng = OsRng::new()?; let mut data = vec![0u8; length]; rng.fill_bytes(&mut data[..]); Ok(data) } -pub fn get_cstring<T: Into<Vec<u8>>>(s: T) -> Result<CString, CommandError> { - CString::new(s).or(Err(CommandError::InvalidString)) -} - -impl CommandError { - fn as_str(&self) -> borrow::Cow<'static, str> { - match *self { - CommandError::WrongCrc => { - "A packet with a wrong checksum has been sent or received".into() - } - CommandError::WrongSlot => "The given OTP slot does not exist".into(), - CommandError::SlotNotProgrammed => "The given OTP slot is not programmed".into(), - CommandError::WrongPassword => "The given password is wrong".into(), - CommandError::NotAuthorized => { - "You are not authorized for this command or provided a wrong temporary \ - password" - .into() - } - CommandError::Timestamp => "An error occurred when getting or setting the time".into(), - CommandError::NoName => "You did not provide a name for the OTP slot".into(), - CommandError::NotSupported => "This command is not supported by this device".into(), - CommandError::UnknownCommand => "This command is unknown".into(), - CommandError::AesDecryptionFailed => "AES decryption failed".into(), - CommandError::Unknown(x) => { - borrow::Cow::from(format!("An unknown error occurred ({})", x)) - } - CommandError::Undefined => "An unspecified error occurred".into(), - CommandError::InvalidString => "You passed a string containing a null byte".into(), - CommandError::StringTooLong => "The supplied string is too long".into(), - CommandError::InvalidSlot => "The given slot is invalid".into(), - CommandError::InvalidHexString => { - "The supplied string is not in hexadecimal format".into() - } - CommandError::TargetBufferTooSmall => "The target buffer is too small".into(), - CommandError::RngError => "An error occurred during random number generation".into(), - } - } -} - -impl fmt::Display for CommandError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.as_str()) - } -} - -impl From<c_int> for CommandError { - fn from(value: c_int) -> Self { - match value { - 1 => CommandError::WrongCrc, - 2 => CommandError::WrongSlot, - 3 => CommandError::SlotNotProgrammed, - 4 => CommandError::WrongPassword, - 5 => CommandError::NotAuthorized, - 6 => CommandError::Timestamp, - 7 => CommandError::NoName, - 8 => CommandError::NotSupported, - 9 => CommandError::UnknownCommand, - 10 => CommandError::AesDecryptionFailed, - 200 => CommandError::StringTooLong, - 201 => CommandError::InvalidSlot, - 202 => CommandError::InvalidHexString, - 203 => CommandError::TargetBufferTooSmall, - x => CommandError::Unknown(x.into()), - } - } -} - -impl From<rand_core::Error> for CommandError { - fn from(_error: rand_core::Error) -> Self { - CommandError::RngError - } +pub fn get_cstring<T: Into<Vec<u8>>>(s: T) -> Result<CString, Error> { + CString::new(s).or(Err(LibraryError::InvalidString.into())) } impl Into<i32> for LogLevel { diff --git a/tests/device.rs b/tests/device.rs index abede67..c502945 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -5,8 +5,8 @@ use std::process::Command; use std::{thread, time}; use nitrokey::{ - Authenticate, CommandError, Config, ConfigureOtp, Device, GenerateOtp, GetPasswordSafe, - OtpMode, OtpSlotData, Storage, VolumeMode, + Authenticate, CommandError, CommunicationError, Config, ConfigureOtp, Device, Error, + GenerateOtp, GetPasswordSafe, LibraryError, OtpMode, OtpSlotData, Storage, VolumeMode, }; use nitrokey_test::test as test_device; @@ -31,11 +31,11 @@ fn count_nitrokey_block_devices() -> usize { #[test_device] fn connect_no_device() { - assert!(nitrokey::connect().is_err()); - assert!(nitrokey::connect_model(nitrokey::Model::Pro).is_err()); - assert!(nitrokey::connect_model(nitrokey::Model::Storage).is_err()); - assert!(nitrokey::Pro::connect().is_err()); - assert!(nitrokey::Storage::connect().is_err()); + assert_cmu_err!(CommunicationError::NotConnected, nitrokey::connect()); + assert_cmu_err!(CommunicationError::NotConnected, nitrokey::connect_model(nitrokey::Model::Pro)); + assert_cmu_err!(CommunicationError::NotConnected, nitrokey::connect_model(nitrokey::Model::Storage)); + assert_cmu_err!(CommunicationError::NotConnected, nitrokey::Pro::connect()); + assert_cmu_err!(CommunicationError::NotConnected, nitrokey::Storage::connect()); } #[test_device] @@ -125,20 +125,20 @@ fn get_retry_count(device: DeviceWrapper) { fn config(device: DeviceWrapper) { let admin = device.authenticate_admin(ADMIN_PASSWORD).unwrap(); let config = Config::new(None, None, None, true); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); let get_config = admin.get_config().unwrap(); assert_eq!(config, get_config); let config = Config::new(None, Some(9), None, true); - assert_eq!(Err(CommandError::InvalidSlot), admin.write_config(config)); + assert_lib_err!(LibraryError::InvalidSlot, admin.write_config(config)); let config = Config::new(Some(1), None, Some(0), false); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); let get_config = admin.get_config().unwrap(); assert_eq!(config, get_config); let config = Config::new(None, None, None, false); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); let get_config = admin.get_config().unwrap(); assert_eq!(config, get_config); } @@ -148,9 +148,7 @@ fn change_user_pin(device: DeviceWrapper) { let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); let device = device.authenticate_user(USER_NEW_PASSWORD).unwrap_err().0; - assert!(device - .change_user_pin(USER_PASSWORD, USER_NEW_PASSWORD) - .is_ok()); + assert_ok!((), device.change_user_pin(USER_PASSWORD, USER_NEW_PASSWORD)); let device = device.authenticate_user(USER_PASSWORD).unwrap_err().0; let device = device @@ -159,11 +157,9 @@ fn change_user_pin(device: DeviceWrapper) { .device(); let result = device.change_user_pin(USER_PASSWORD, USER_PASSWORD); - assert_eq!(Err(CommandError::WrongPassword), result); + assert_cmd_err!(CommandError::WrongPassword, result); - assert!(device - .change_user_pin(USER_NEW_PASSWORD, USER_PASSWORD) - .is_ok()); + assert_ok!((), device.change_user_pin(USER_NEW_PASSWORD, USER_PASSWORD)); let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); assert!(device.authenticate_user(USER_NEW_PASSWORD).is_err()); @@ -174,9 +170,7 @@ fn change_admin_pin(device: DeviceWrapper) { let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); let device = device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err().0; - assert!(device - .change_admin_pin(ADMIN_PASSWORD, ADMIN_NEW_PASSWORD) - .is_ok()); + assert_ok!((), device.change_admin_pin(ADMIN_PASSWORD, ADMIN_NEW_PASSWORD)); let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap_err().0; let device = device @@ -184,14 +178,12 @@ fn change_admin_pin(device: DeviceWrapper) { .unwrap() .device(); - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.change_admin_pin(ADMIN_PASSWORD, ADMIN_PASSWORD) ); - assert!(device - .change_admin_pin(ADMIN_NEW_PASSWORD, ADMIN_PASSWORD) - .is_ok()); + assert_ok!((), device.change_admin_pin(ADMIN_NEW_PASSWORD, ADMIN_PASSWORD)); let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err(); @@ -205,18 +197,19 @@ where let result = device.authenticate_user(password); assert!(result.is_err()); let err = result.unwrap_err(); - assert_eq!(error, err.1); + match err.1 { + Error::CommandError(err) => assert_eq!(error, err), + _ => assert!(false), + }; err.0 } #[test_device] fn unlock_user_pin(device: DeviceWrapper) { let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); - assert!(device - .unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD) - .is_ok()); - assert_eq!( - Err(CommandError::WrongPassword), + assert_ok!((), device.unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD)); + assert_cmd_err!( + CommandError::WrongPassword, device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD) ); @@ -228,13 +221,11 @@ fn unlock_user_pin(device: DeviceWrapper) { let device = require_failed_user_login(device, USER_PASSWORD, CommandError::WrongPassword); // unblock with current PIN - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD) ); - assert!(device - .unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD) - .is_ok()); + assert_ok!((), device.unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD)); let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); // block user PIN @@ -244,57 +235,47 @@ fn unlock_user_pin(device: DeviceWrapper) { let device = require_failed_user_login(device, USER_PASSWORD, CommandError::WrongPassword); // unblock with new PIN - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD) ); - assert!(device - .unlock_user_pin(ADMIN_PASSWORD, USER_NEW_PASSWORD) - .is_ok()); + assert_ok!((), device.unlock_user_pin(ADMIN_PASSWORD, USER_NEW_PASSWORD)); // reset user PIN - assert!(device - .change_user_pin(USER_NEW_PASSWORD, USER_PASSWORD) - .is_ok()); + assert_ok!((), device.change_user_pin(USER_NEW_PASSWORD, USER_PASSWORD)); } #[test_device] fn factory_reset(device: DeviceWrapper) { let admin = device.authenticate_admin(ADMIN_PASSWORD).unwrap(); let otp_data = OtpSlotData::new(1, "test", "0123468790", OtpMode::SixDigits); - assert_eq!(Ok(()), admin.write_totp_slot(otp_data, 30)); + assert_ok!((), admin.write_totp_slot(otp_data, 30)); let device = admin.device(); let pws = device.get_password_safe(USER_PASSWORD).unwrap(); - assert_eq!(Ok(()), pws.write_slot(0, "test", "testlogin", "testpw")); + assert_ok!((), pws.write_slot(0, "test", "testlogin", "testpw")); drop(pws); - assert_eq!( - Ok(()), - device.change_user_pin(USER_PASSWORD, USER_NEW_PASSWORD) - ); - assert_eq!( - Ok(()), + assert_ok!((), device.change_user_pin(USER_PASSWORD, USER_NEW_PASSWORD)); + assert_ok!( + (), device.change_admin_pin(ADMIN_PASSWORD, ADMIN_NEW_PASSWORD) ); - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.factory_reset(USER_NEW_PASSWORD) ); - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.factory_reset(ADMIN_PASSWORD) ); - assert_eq!(Ok(()), device.factory_reset(ADMIN_NEW_PASSWORD)); + assert_ok!((), device.factory_reset(ADMIN_NEW_PASSWORD)); let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); let user = device.authenticate_user(USER_PASSWORD).unwrap(); - assert_eq!( - Err(CommandError::SlotNotProgrammed), - user.get_totp_slot_name(1) - ); + assert_cmd_err!(CommandError::SlotNotProgrammed, user.get_totp_slot_name(1)); let device = user.device(); let pws = device.get_password_safe(USER_PASSWORD).unwrap(); @@ -302,20 +283,20 @@ fn factory_reset(device: DeviceWrapper) { assert_ne!("testlogin".to_string(), pws.get_slot_login(0).unwrap()); assert_ne!("testpw".to_string(), pws.get_slot_password(0).unwrap()); - assert_eq!(Ok(()), device.build_aes_key(ADMIN_PASSWORD)); + assert_ok!((), device.build_aes_key(ADMIN_PASSWORD)); } #[test_device] fn build_aes_key(device: DeviceWrapper) { let pws = device.get_password_safe(USER_PASSWORD).unwrap(); - assert_eq!(Ok(()), pws.write_slot(0, "test", "testlogin", "testpw")); + assert_ok!((), pws.write_slot(0, "test", "testlogin", "testpw")); drop(pws); - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.build_aes_key(USER_PASSWORD) ); - assert_eq!(Ok(()), device.build_aes_key(ADMIN_PASSWORD)); + assert_ok!((), device.build_aes_key(ADMIN_PASSWORD)); let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); @@ -327,74 +308,71 @@ fn build_aes_key(device: DeviceWrapper) { #[test_device] fn change_update_pin(device: Storage) { - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.change_update_pin(UPDATE_NEW_PIN, UPDATE_PIN) ); - assert_eq!(Ok(()), device.change_update_pin(UPDATE_PIN, UPDATE_NEW_PIN)); - assert_eq!(Ok(()), device.change_update_pin(UPDATE_NEW_PIN, UPDATE_PIN)); + assert_ok!((), device.change_update_pin(UPDATE_PIN, UPDATE_NEW_PIN)); + assert_ok!((), device.change_update_pin(UPDATE_NEW_PIN, UPDATE_PIN)); } #[test_device] fn encrypted_volume(device: Storage) { - assert_eq!(Ok(()), device.lock()); + assert_ok!((), device.lock()); assert_eq!(1, count_nitrokey_block_devices()); - assert_eq!(Ok(()), device.disable_encrypted_volume()); + assert_ok!((), device.disable_encrypted_volume()); assert_eq!(1, count_nitrokey_block_devices()); - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.enable_encrypted_volume("123") ); assert_eq!(1, count_nitrokey_block_devices()); - assert_eq!(Ok(()), device.enable_encrypted_volume(USER_PASSWORD)); + assert_ok!((), device.enable_encrypted_volume(USER_PASSWORD)); assert_eq!(2, count_nitrokey_block_devices()); - assert_eq!(Ok(()), device.disable_encrypted_volume()); + assert_ok!((), device.disable_encrypted_volume()); assert_eq!(1, count_nitrokey_block_devices()); } #[test_device] fn hidden_volume(device: Storage) { - assert_eq!(Ok(()), device.lock()); + assert_ok!((), device.lock()); assert_eq!(1, count_nitrokey_block_devices()); - assert_eq!(Ok(()), device.disable_hidden_volume()); + assert_ok!((), device.disable_hidden_volume()); assert_eq!(1, count_nitrokey_block_devices()); - assert_eq!(Ok(()), device.enable_encrypted_volume(USER_PASSWORD)); + assert_ok!((), device.enable_encrypted_volume(USER_PASSWORD)); assert_eq!(2, count_nitrokey_block_devices()); // TODO: why this error code? - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.create_hidden_volume(5, 0, 100, "hiddenpw") ); - assert_eq!(Ok(()), device.create_hidden_volume(0, 20, 21, "hidden-pw")); - assert_eq!( - Ok(()), - device.create_hidden_volume(0, 20, 21, "hiddenpassword") - ); - assert_eq!(Ok(()), device.create_hidden_volume(1, 0, 1, "otherpw")); + assert_ok!((), device.create_hidden_volume(0, 20, 21, "hidden-pw")); + assert_ok!((), device.create_hidden_volume(0, 20, 21, "hiddenpassword")); + assert_ok!((), device.create_hidden_volume(1, 0, 1, "otherpw")); // TODO: test invalid range (not handled by libnitrokey) assert_eq!(2, count_nitrokey_block_devices()); - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.enable_hidden_volume("blubb") ); - assert_eq!(Ok(()), device.enable_hidden_volume("hiddenpassword")); + assert_ok!((), device.enable_hidden_volume("hiddenpassword")); assert_eq!(2, count_nitrokey_block_devices()); - assert_eq!(Ok(()), device.enable_hidden_volume("otherpw")); + assert_ok!((), device.enable_hidden_volume("otherpw")); assert_eq!(2, count_nitrokey_block_devices()); - assert_eq!(Ok(()), device.disable_hidden_volume()); + assert_ok!((), device.disable_hidden_volume()); assert_eq!(1, count_nitrokey_block_devices()); } #[test_device] fn lock(device: Storage) { - assert_eq!(Ok(()), device.enable_encrypted_volume(USER_PASSWORD)); - assert_eq!(Ok(()), device.lock()); + assert_ok!((), device.enable_encrypted_volume(USER_PASSWORD)); + assert_ok!((), device.lock()); assert_eq!(1, count_nitrokey_block_devices()); } @@ -410,17 +388,14 @@ fn set_unencrypted_volume_mode(device: Storage) { } fn assert_success(device: &Storage, mode: VolumeMode) { - assert_eq!( - Ok(()), - device.set_unencrypted_volume_mode(ADMIN_PASSWORD, mode) - ); + assert_ok!((), device.set_unencrypted_volume_mode(ADMIN_PASSWORD, mode)); assert_mode(&device, mode); } assert_success(&device, VolumeMode::ReadOnly); - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.set_unencrypted_volume_mode(USER_PASSWORD, VolumeMode::ReadOnly) ); assert_mode(&device, VolumeMode::ReadOnly); @@ -460,17 +435,17 @@ fn get_production_info(device: Storage) { #[test_device] fn clear_new_sd_card_warning(device: Storage) { - assert_eq!(Ok(()), device.factory_reset(ADMIN_PASSWORD)); + assert_ok!((), device.factory_reset(ADMIN_PASSWORD)); thread::sleep(time::Duration::from_secs(3)); - assert_eq!(Ok(()), device.build_aes_key(ADMIN_PASSWORD)); + assert_ok!((), device.build_aes_key(ADMIN_PASSWORD)); // We have to perform an SD card operation to reset the new_sd_card_found field - assert_eq!(Ok(()), device.lock()); + assert_ok!((), device.lock()); let status = device.get_status().unwrap(); assert!(status.new_sd_card_found); - assert_eq!(Ok(()), device.clear_new_sd_card_warning(ADMIN_PASSWORD)); + assert_ok!((), device.clear_new_sd_card_warning(ADMIN_PASSWORD)); let status = device.get_status().unwrap(); assert!(!status.new_sd_card_found); @@ -478,18 +453,18 @@ fn clear_new_sd_card_warning(device: Storage) { #[test_device] fn export_firmware(device: Storage) { - assert_eq!( - Err(CommandError::WrongPassword), + assert_cmd_err!( + CommandError::WrongPassword, device.export_firmware("someadminpn") ); - assert_eq!(Ok(()), device.export_firmware(ADMIN_PASSWORD)); - assert_eq!( - Ok(()), + assert_ok!((), device.export_firmware(ADMIN_PASSWORD)); + assert_ok!( + (), device.set_unencrypted_volume_mode(ADMIN_PASSWORD, VolumeMode::ReadWrite) ); - assert_eq!(Ok(()), device.export_firmware(ADMIN_PASSWORD)); - assert_eq!( - Ok(()), + assert_ok!((), device.export_firmware(ADMIN_PASSWORD)); + assert_ok!( + (), device.set_unencrypted_volume_mode(ADMIN_PASSWORD, VolumeMode::ReadOnly) ); } diff --git a/tests/otp.rs b/tests/otp.rs index 712f7a2..96da371 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -4,8 +4,8 @@ use std::fmt::Debug; use std::ops::Deref; use nitrokey::{ - Admin, Authenticate, CommandError, Config, ConfigureOtp, Device, GenerateOtp, OtpMode, - OtpSlotData, + Admin, Authenticate, CommandError, Config, ConfigureOtp, Device, GenerateOtp, LibraryError, + OtpMode, OtpSlotData, }; use nitrokey_test::test as test_device; @@ -38,7 +38,7 @@ enum TotpTimestampSize { fn make_admin_test_device<T>(device: T) -> Admin<T> where T: Device, - (T, nitrokey::CommandError): Debug, + (T, nitrokey::Error): Debug, { device .authenticate_admin(ADMIN_PASSWORD) @@ -47,7 +47,7 @@ where fn configure_hotp(admin: &ConfigureOtp, counter: u8) { let slot_data = OtpSlotData::new(1, "test-hotp", HOTP_SECRET, OtpMode::SixDigits); - assert_eq!(Ok(()), admin.write_hotp_slot(slot_data, counter.into())); + assert_ok!((), admin.write_hotp_slot(slot_data, counter.into())); } fn check_hotp_codes(device: &GenerateOtp, offset: u8) { @@ -61,20 +61,17 @@ fn check_hotp_codes(device: &GenerateOtp, offset: u8) { #[test_device] fn set_time(device: DeviceWrapper) { - assert_eq!(Ok(()), device.set_time(1546385382, true)); - assert_eq!(Ok(()), device.set_time(1546385392, false)); - assert_eq!( - Err(CommandError::Timestamp), - device.set_time(1546385292, false) - ); - assert_eq!(Ok(()), device.set_time(1546385382, true)); + assert_ok!((), device.set_time(1546385382, true)); + assert_ok!((), device.set_time(1546385392, false)); + assert_cmd_err!(CommandError::Timestamp, device.set_time(1546385292, false)); + assert_ok!((), device.set_time(1546385382, true)); } #[test_device] fn hotp_no_pin(device: DeviceWrapper) { let admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); configure_hotp(&admin, 0); check_hotp_codes(admin.deref(), 0); @@ -90,67 +87,64 @@ fn hotp_no_pin(device: DeviceWrapper) { fn hotp_pin(device: DeviceWrapper) { let admin = make_admin_test_device(device); let config = Config::new(None, None, None, true); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); configure_hotp(&admin, 0); let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); check_hotp_codes(&user, 0); - assert!(user.device().get_hotp_code(1).is_err()); + assert_cmd_err!(CommandError::NotAuthorized, user.device().get_hotp_code(1)); } #[test_device] fn hotp_slot_name(device: DeviceWrapper) { let admin = make_admin_test_device(device); let slot_data = OtpSlotData::new(1, "test-hotp", HOTP_SECRET, OtpMode::SixDigits); - assert_eq!(Ok(()), admin.write_hotp_slot(slot_data, 0)); + assert_ok!((), admin.write_hotp_slot(slot_data, 0)); let device = admin.device(); let result = device.get_hotp_slot_name(1); assert_eq!("test-hotp", result.unwrap()); let result = device.get_hotp_slot_name(4); - assert_eq!(CommandError::InvalidSlot, result.unwrap_err()); + assert_lib_err!(LibraryError::InvalidSlot, result); } #[test_device] fn hotp_error(device: DeviceWrapper) { let admin = make_admin_test_device(device); let slot_data = OtpSlotData::new(1, "", HOTP_SECRET, OtpMode::SixDigits); - assert_eq!( - Err(CommandError::NoName), - admin.write_hotp_slot(slot_data, 0) - ); + assert_cmd_err!(CommandError::NoName, admin.write_hotp_slot(slot_data, 0)); let slot_data = OtpSlotData::new(4, "test", HOTP_SECRET, OtpMode::SixDigits); - assert_eq!( - Err(CommandError::InvalidSlot), + assert_lib_err!( + LibraryError::InvalidSlot, admin.write_hotp_slot(slot_data, 0) ); let slot_data = OtpSlotData::new(1, "test", "foobar", OtpMode::SixDigits); - assert_eq!( - Err(CommandError::InvalidHexString), + assert_lib_err!( + LibraryError::InvalidHexString, admin.write_hotp_slot(slot_data, 0) ); let code = admin.get_hotp_code(4); - assert_eq!(CommandError::InvalidSlot, code.unwrap_err()); + assert_lib_err!(LibraryError::InvalidSlot, code); } #[test_device] fn hotp_erase(device: DeviceWrapper) { let admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); let slot_data = OtpSlotData::new(1, "test1", HOTP_SECRET, OtpMode::SixDigits); - assert_eq!(Ok(()), admin.write_hotp_slot(slot_data, 0)); + assert_ok!((), admin.write_hotp_slot(slot_data, 0)); let slot_data = OtpSlotData::new(2, "test2", HOTP_SECRET, OtpMode::SixDigits); - assert_eq!(Ok(()), admin.write_hotp_slot(slot_data, 0)); + assert_ok!((), admin.write_hotp_slot(slot_data, 0)); - assert_eq!(Ok(()), admin.erase_hotp_slot(1)); + assert_ok!((), admin.erase_hotp_slot(1)); let device = admin.device(); let result = device.get_hotp_slot_name(1); - assert_eq!(CommandError::SlotNotProgrammed, result.unwrap_err()); + assert_cmd_err!(CommandError::SlotNotProgrammed, result); let result = device.get_hotp_code(1); - assert_eq!(CommandError::SlotNotProgrammed, result.unwrap_err()); + assert_cmd_err!(CommandError::SlotNotProgrammed, result); assert_eq!("test2", device.get_hotp_slot_name(2).unwrap()); } @@ -158,26 +152,19 @@ fn hotp_erase(device: DeviceWrapper) { fn configure_totp(admin: &ConfigureOtp, factor: u64) { let slot_data = OtpSlotData::new(1, "test-totp", TOTP_SECRET, OtpMode::EightDigits); let time_window = 30u64.checked_mul(factor).unwrap(); - assert_eq!(Ok(()), admin.write_totp_slot(slot_data, time_window as u16)); + assert_ok!((), admin.write_totp_slot(slot_data, time_window as u16)); } fn check_totp_codes(device: &GenerateOtp, factor: u64, timestamp_size: TotpTimestampSize) { - for (i, &(base_time, code)) in TOTP_CODES.iter().enumerate() { + for (base_time, code) in TOTP_CODES { let time = base_time.checked_mul(factor).unwrap(); let is_u64 = time > u32::max_value() as u64; if is_u64 != (timestamp_size == TotpTimestampSize::U64) { continue; } - assert_eq!(Ok(()), device.set_time(time, true)); - let result = device.get_totp_code(1); - assert!(result.is_ok()); - let result_code = result.unwrap(); - assert_eq!( - code, result_code, - "TOTP code {} should be {} but is {}", - i, code, result_code - ); + assert_ok!((), device.set_time(time, true)); + assert_ok!(code.to_string(), device.get_totp_code(1)); } } @@ -186,7 +173,7 @@ fn totp_no_pin(device: DeviceWrapper) { // TODO: this test may fail due to bad timing --> find solution let admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); configure_totp(&admin, 1); check_totp_codes(admin.deref(), 1, TotpTimestampSize::U32); @@ -204,7 +191,7 @@ fn totp_no_pin(device: DeviceWrapper) { fn totp_no_pin_64(device: Pro) { let admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); configure_totp(&admin, 1); check_totp_codes(admin.deref(), 1, TotpTimestampSize::U64); @@ -221,13 +208,13 @@ fn totp_pin(device: DeviceWrapper) { // TODO: this test may fail due to bad timing --> find solution let admin = make_admin_test_device(device); let config = Config::new(None, None, None, true); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); configure_totp(&admin, 1); let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); check_totp_codes(&user, 1, TotpTimestampSize::U32); - assert!(user.device().get_totp_code(1).is_err()); + assert_cmd_err!(CommandError::NotAuthorized, user.device().get_totp_code(1)); } #[test_device] @@ -235,68 +222,64 @@ fn totp_pin(device: DeviceWrapper) { fn totp_pin_64(device: Pro) { let admin = make_admin_test_device(device); let config = Config::new(None, None, None, true); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); configure_totp(&admin, 1); let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); check_totp_codes(&user, 1, TotpTimestampSize::U64); - assert!(user.device().get_totp_code(1).is_err()); + assert_cmd_err!(CommandError::NotAuthorized, user.device().get_totp_code(1)); } #[test_device] fn totp_slot_name(device: DeviceWrapper) { let admin = make_admin_test_device(device); let slot_data = OtpSlotData::new(1, "test-totp", TOTP_SECRET, OtpMode::EightDigits); - assert_eq!(Ok(()), admin.write_totp_slot(slot_data, 0)); + assert_ok!((), admin.write_totp_slot(slot_data, 0)); let device = admin.device(); let result = device.get_totp_slot_name(1); - assert!(result.is_ok()); - assert_eq!("test-totp", result.unwrap()); + assert_ok!("test-totp", result); let result = device.get_totp_slot_name(16); - assert_eq!(CommandError::InvalidSlot, result.unwrap_err()); + assert_lib_err!(LibraryError::InvalidSlot, result); } #[test_device] fn totp_error(device: DeviceWrapper) { let admin = make_admin_test_device(device); let slot_data = OtpSlotData::new(1, "", TOTP_SECRET, OtpMode::SixDigits); - assert_eq!( - Err(CommandError::NoName), - admin.write_totp_slot(slot_data, 0) - ); + assert_cmd_err!(CommandError::NoName, admin.write_totp_slot(slot_data, 0)); let slot_data = OtpSlotData::new(20, "test", TOTP_SECRET, OtpMode::SixDigits); - assert_eq!( - Err(CommandError::InvalidSlot), + assert_lib_err!( + LibraryError::InvalidSlot, admin.write_totp_slot(slot_data, 0) ); let slot_data = OtpSlotData::new(4, "test", "foobar", OtpMode::SixDigits); - assert_eq!( - Err(CommandError::InvalidHexString), + assert_lib_err!( + LibraryError::InvalidHexString, admin.write_totp_slot(slot_data, 0) ); let code = admin.get_totp_code(20); - assert_eq!(CommandError::InvalidSlot, code.unwrap_err()); + assert_lib_err!(LibraryError::InvalidSlot, code); } #[test_device] fn totp_erase(device: DeviceWrapper) { let admin = make_admin_test_device(device); let config = Config::new(None, None, None, false); - assert_eq!(Ok(()), admin.write_config(config)); + assert_ok!((), admin.write_config(config)); let slot_data = OtpSlotData::new(1, "test1", TOTP_SECRET, OtpMode::SixDigits); - assert_eq!(Ok(()), admin.write_totp_slot(slot_data, 0)); + assert_ok!((), admin.write_totp_slot(slot_data, 0)); let slot_data = OtpSlotData::new(2, "test2", TOTP_SECRET, OtpMode::SixDigits); - assert_eq!(Ok(()), admin.write_totp_slot(slot_data, 0)); + assert_ok!((), admin.write_totp_slot(slot_data, 0)); - assert_eq!(Ok(()), admin.erase_totp_slot(1)); + assert_ok!((), admin.erase_totp_slot(1)); let device = admin.device(); let result = device.get_totp_slot_name(1); - assert_eq!(CommandError::SlotNotProgrammed, result.unwrap_err()); + assert_cmd_err!(CommandError::SlotNotProgrammed, result); let result = device.get_totp_code(1); - assert_eq!(CommandError::SlotNotProgrammed, result.unwrap_err()); + assert_cmd_err!(CommandError::SlotNotProgrammed, result); assert_eq!("test2", device.get_totp_slot_name(2).unwrap()); } diff --git a/tests/pws.rs b/tests/pws.rs index fbcc0c1..7a97983 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -3,16 +3,18 @@ mod util; use std::ffi::CStr; use libc::{c_int, c_void, free}; -use nitrokey::{CommandError, Device, GetPasswordSafe, PasswordSafe, SLOT_COUNT}; +use nitrokey::{ + CommandError, Device, Error, GetPasswordSafe, LibraryError, PasswordSafe, SLOT_COUNT, +}; use nitrokey_sys; use nitrokey_test::test as test_device; use crate::util::{ADMIN_PASSWORD, USER_PASSWORD}; -fn get_slot_name_direct(slot: u8) -> Result<String, CommandError> { +fn get_slot_name_direct(slot: u8) -> Result<String, Error> { let ptr = unsafe { nitrokey_sys::NK_get_password_safe_slot_name(slot) }; if ptr.is_null() { - return Err(CommandError::Undefined); + return Err(Error::UnexpectedError); } let s = unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }; unsafe { free(ptr as *mut c_void) }; @@ -21,7 +23,7 @@ fn get_slot_name_direct(slot: u8) -> Result<String, CommandError> { let error = unsafe { nitrokey_sys::NK_get_last_command_status() } as c_int; match error { 0 => Ok(s), - other => Err(CommandError::from(other)), + other => Err(Error::from(other)), } } false => Ok(s), @@ -37,11 +39,9 @@ where #[test_device] fn enable(device: DeviceWrapper) { - assert!(device - .get_password_safe(&(USER_PASSWORD.to_owned() + "123")) - .is_err()); + assert_cmd_err!(CommandError::WrongPassword, device.get_password_safe(&(USER_PASSWORD.to_owned() + "123"))); assert!(device.get_password_safe(USER_PASSWORD).is_ok()); - assert!(device.get_password_safe(ADMIN_PASSWORD).is_err()); + assert_cmd_err!(CommandError::WrongPassword, device.get_password_safe(ADMIN_PASSWORD)); assert!(device.get_password_safe(USER_PASSWORD).is_ok()); } @@ -49,35 +49,35 @@ fn enable(device: DeviceWrapper) { fn drop(device: DeviceWrapper) { { let pws = get_pws(&device); - assert_eq!(Ok(()), pws.write_slot(1, "name", "login", "password")); + assert_ok!((), pws.write_slot(1, "name", "login", "password")); assert_eq!("name", pws.get_slot_name(1).unwrap()); let result = get_slot_name_direct(1); - assert_eq!(Ok(String::from("name")), result); + assert_ok!(String::from("name"), result); } let result = get_slot_name_direct(1); - assert_eq!(Ok(String::from("name")), result); - assert_eq!(Ok(()), device.lock()); + assert_ok!(String::from("name"), result); + assert_ok!((), device.lock()); let result = get_slot_name_direct(1); - assert_eq!(Err(CommandError::NotAuthorized), result); + assert_cmd_err!(CommandError::NotAuthorized, result); } #[test_device] fn get_status(device: DeviceWrapper) { let pws = get_pws(&device); for i in 0..SLOT_COUNT { - assert_eq!(Ok(()), pws.erase_slot(i), "Could not erase slot {}", i); + assert_ok!((), pws.erase_slot(i)); } let status = pws.get_slot_status().unwrap(); assert_eq!(status, [false; SLOT_COUNT as usize]); - assert_eq!(Ok(()), pws.write_slot(1, "name", "login", "password")); + assert_ok!((), pws.write_slot(1, "name", "login", "password")); let status = pws.get_slot_status().unwrap(); for i in 0..SLOT_COUNT { assert_eq!(i == 1, status[i as usize]); } for i in 0..SLOT_COUNT { - assert_eq!(Ok(()), pws.write_slot(i, "name", "login", "password")); + assert_ok!((), pws.write_slot(i, "name", "login", "password")); } let status = pws.get_slot_status().unwrap(); assert_eq!(status, [true; SLOT_COUNT as usize]); @@ -86,76 +86,61 @@ fn get_status(device: DeviceWrapper) { #[test_device] fn get_data(device: DeviceWrapper) { let pws = get_pws(&device); - assert_eq!(Ok(()), pws.write_slot(1, "name", "login", "password")); + assert_ok!((), pws.write_slot(1, "name", "login", "password")); assert_eq!("name", pws.get_slot_name(1).unwrap()); assert_eq!("login", pws.get_slot_login(1).unwrap()); assert_eq!("password", pws.get_slot_password(1).unwrap()); - assert_eq!(Ok(()), pws.erase_slot(1)); - assert_eq!(Err(CommandError::SlotNotProgrammed), pws.get_slot_name(1)); - assert_eq!(Err(CommandError::SlotNotProgrammed), pws.get_slot_login(1)); - assert_eq!( - Err(CommandError::SlotNotProgrammed), - pws.get_slot_password(1) - ); + assert_ok!((), pws.erase_slot(1)); + assert_cmd_err!(CommandError::SlotNotProgrammed, pws.get_slot_name(1)); + assert_cmd_err!(CommandError::SlotNotProgrammed, pws.get_slot_login(1)); + assert_cmd_err!(CommandError::SlotNotProgrammed, pws.get_slot_password(1)); let name = "with å"; let login = "pär@test.com"; let password = "'i3lJc[09?I:,[u7dWz9"; - assert_eq!(Ok(()), pws.write_slot(1, name, login, password)); + assert_ok!((), pws.write_slot(1, name, login, password)); assert_eq!(name, pws.get_slot_name(1).unwrap()); assert_eq!(login, pws.get_slot_login(1).unwrap()); assert_eq!(password, pws.get_slot_password(1).unwrap()); - assert_eq!( - Err(CommandError::InvalidSlot), - pws.get_slot_name(SLOT_COUNT) - ); - assert_eq!( - Err(CommandError::InvalidSlot), - pws.get_slot_login(SLOT_COUNT) - ); - assert_eq!( - Err(CommandError::InvalidSlot), - pws.get_slot_password(SLOT_COUNT) - ); + assert_lib_err!(LibraryError::InvalidSlot, pws.get_slot_name(SLOT_COUNT)); + assert_lib_err!(LibraryError::InvalidSlot, pws.get_slot_login(SLOT_COUNT)); + assert_lib_err!(LibraryError::InvalidSlot, pws.get_slot_password(SLOT_COUNT)); } #[test_device] fn write(device: DeviceWrapper) { let pws = get_pws(&device); - assert_eq!( - Err(CommandError::InvalidSlot), + assert_lib_err!( + LibraryError::InvalidSlot, pws.write_slot(SLOT_COUNT, "name", "login", "password") ); - assert_eq!(Ok(()), pws.write_slot(0, "", "login", "password")); - assert_eq!(Err(CommandError::SlotNotProgrammed), pws.get_slot_name(0)); - assert_eq!(Ok(String::from("login")), pws.get_slot_login(0)); - assert_eq!(Ok(String::from("password")), pws.get_slot_password(0)); - - assert_eq!(Ok(()), pws.write_slot(0, "name", "", "password")); - assert_eq!(Ok(String::from("name")), pws.get_slot_name(0)); - assert_eq!(Err(CommandError::SlotNotProgrammed), pws.get_slot_login(0)); - assert_eq!(Ok(String::from("password")), pws.get_slot_password(0)); - - assert_eq!(Ok(()), pws.write_slot(0, "name", "login", "")); - assert_eq!(Ok(String::from("name")), pws.get_slot_name(0)); - assert_eq!(Ok(String::from("login")), pws.get_slot_login(0)); - assert_eq!( - Err(CommandError::SlotNotProgrammed), - pws.get_slot_password(0) - ); + assert_ok!((), pws.write_slot(0, "", "login", "password")); + assert_cmd_err!(CommandError::SlotNotProgrammed, pws.get_slot_name(0)); + assert_ok!(String::from("login"), pws.get_slot_login(0)); + assert_ok!(String::from("password"), pws.get_slot_password(0)); + + assert_ok!((), pws.write_slot(0, "name", "", "password")); + assert_ok!(String::from("name"), pws.get_slot_name(0)); + assert_cmd_err!(CommandError::SlotNotProgrammed, pws.get_slot_login(0)); + assert_ok!(String::from("password"), pws.get_slot_password(0)); + + assert_ok!((), pws.write_slot(0, "name", "login", "")); + assert_ok!(String::from("name"), pws.get_slot_name(0)); + assert_ok!(String::from("login"), pws.get_slot_login(0)); + assert_cmd_err!(CommandError::SlotNotProgrammed, pws.get_slot_password(0)); } #[test_device] fn erase(device: DeviceWrapper) { let pws = get_pws(&device); - assert_eq!(Err(CommandError::InvalidSlot), pws.erase_slot(SLOT_COUNT)); + assert_lib_err!(LibraryError::InvalidSlot, pws.erase_slot(SLOT_COUNT)); - assert_eq!(Ok(()), pws.write_slot(0, "name", "login", "password")); - assert_eq!(Ok(()), pws.erase_slot(0)); - assert_eq!(Ok(()), pws.erase_slot(0)); - assert_eq!(Err(CommandError::SlotNotProgrammed), pws.get_slot_name(0)); + assert_ok!((), pws.write_slot(0, "name", "login", "password")); + assert_ok!((), pws.erase_slot(0)); + assert_ok!((), pws.erase_slot(0)); + assert_cmd_err!(CommandError::SlotNotProgrammed, pws.get_slot_name(0)); } diff --git a/tests/util/mod.rs b/tests/util/mod.rs index cbf6b93..4a00a66 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -1,2 +1,83 @@ pub static ADMIN_PASSWORD: &str = "12345678"; pub static USER_PASSWORD: &str = "123456"; + +#[macro_export] +macro_rules! assert_ok { + ($left:expr, $right:expr) => {{ + match &$right { + Ok(right) => match &$left { + left => { + if !(*left == *right) { + panic!( + r#"assertion failed: `(left == right)` + left: `{:?}`, + right: `{:?}`"#, + left, right + ) + } + } + }, + Err(right_err) => panic!( + r#"assertion failed: `(left == right)` + left: `Ok({:?})`, + right: `Err({:?})`"#, + $left, right_err + ), + } + }}; +} + +#[macro_export] +macro_rules! assert_err { + ($err:path, $left:expr, $right:expr) => { + match &$right { + Err($err(ref right_err)) => match &$left { + left_err => { + if !(*left_err == *right_err) { + panic!( + r#"assertion failed: `(left == right)` + left: `{:?}`, + right: `{:?}`"#, + left_err, right_err + ) + } + } + }, + Err(ref right_err) => panic!( + r#"assertion failed: `(left == right)` + left: `{:?}`, + right: `{:?}`"#, + $err($left), + right_err + ), + Ok(right_ok) => panic!( + r#"assertion failed: `(left == right)` + left: `Err({:?})`, + right: `Ok({:?})`"#, + $err($left), + right_ok + ), + } + }; +} + +#[macro_export] +macro_rules! assert_cmd_err { + ($left:expr, $right:expr) => { + assert_err!(::nitrokey::Error::CommandError, $left, $right); + }; +} + +#[macro_export] +macro_rules! assert_cmu_err { + ($left:expr, $right:expr) => { + assert_err!(::nitrokey::Error::CommunicationError, $left, $right); + }; +} + +#[macro_export] +macro_rules! assert_lib_err { + ($left:expr, $right:expr) => { + assert_err!(::nitrokey::Error::LibraryError, $left, $right); + }; +} |