From d0f63513bb935d3d931c86a1ab7b68d6ed44bf27 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 01:53:08 +0000 Subject: Move util::CommandError to the new error module This prepares the refactoring of util::CommandError into multiple enums. --- src/error.rs | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/error.rs (limited to 'src/error.rs') diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..6aeeef8 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,114 @@ +use std::borrow; +use std::fmt; +use std::os::raw; + +/// 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, +} + +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 for CommandError { + fn from(value: raw::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 for CommandError { + fn from(_error: rand_core::Error) -> Self { + CommandError::RngError + } +} -- cgit v1.2.3 From 591c55ff294ee12812e4be1b90f03a093f83a4f5 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 01:58:42 +0000 Subject: Implement std::error::Error for error::CommandError --- CHANGELOG.md | 1 + src/error.rs | 3 +++ 2 files changed, 4 insertions(+) (limited to 'src/error.rs') diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c5ebf3..d2982a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - Remove the `test-pro` and `test-storage` features. - Implement `Display` for `Version`. - Introduce `DEFAULT_ADMIN_PIN` and `DEFAULT_USER_PIN` constants. +- Implement `std::error::Error` for `CommandError`. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/error.rs b/src/error.rs index 6aeeef8..dcaa4d2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,5 @@ use std::borrow; +use std::error; use std::fmt; use std::os::raw; @@ -79,6 +80,8 @@ impl CommandError { } } +impl error::Error for CommandError {} + impl fmt::Display for CommandError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.as_str()) -- cgit v1.2.3 From db198936be1a80f1735731d9e95eb6f4c48a5329 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 02:05:52 +0000 Subject: Add the Error enum and the Result typedef The Error enum is a wrapper for the possible error types (currently only CommandError). Result is defined as Result. --- CHANGELOG.md | 4 +++- src/error.rs | 35 ++++++++++++++++++++++++++++++++++- src/lib.rs | 2 +- 3 files changed, 38 insertions(+), 3 deletions(-) (limited to 'src/error.rs') diff --git a/CHANGELOG.md b/CHANGELOG.md index d2982a9..c69fd25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,9 @@ - Remove the `test-pro` and `test-storage` features. - Implement `Display` for `Version`. - Introduce `DEFAULT_ADMIN_PIN` and `DEFAULT_USER_PIN` constants. -- Implement `std::error::Error` for `CommandError`. +- Refactor the error handling code: + - Implement `std::error::Error` for `CommandError`. + - Add the `Error` enum and the `Result` typedef. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/error.rs b/src/error.rs index dcaa4d2..89c4c82 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,8 +2,41 @@ use std::borrow; use std::error; use std::fmt; use std::os::raw; +use std::result; -/// Error types returned by Nitrokey device or by the library. +/// 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), +} + +impl From for Error { + fn from(err: CommandError) -> Self { + Error::CommandError(err) + } +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + Error::CommandError(ref err) => Some(err), + } + } +} + +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), + } + } +} + +/// A result returned by the nitrokey crate. +pub type Result = result::Result; + +/// 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. diff --git a/src/lib.rs b/src/lib.rs index cec5db7..df11cc3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,7 +104,7 @@ pub use crate::device::{ connect, connect_model, Device, DeviceWrapper, Model, Pro, SdCardData, Storage, StorageProductionInfo, StorageStatus, VolumeMode, VolumeStatus, }; -pub use crate::error::CommandError; +pub use crate::error::{CommandError, Error, Result}; pub use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData}; pub use crate::pws::{GetPasswordSafe, PasswordSafe, SLOT_COUNT}; pub use crate::util::LogLevel; -- cgit v1.2.3 From 94390aadc8a3997d379bf5e4c0bc00c2a9669a34 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 20 Jan 2019 20:58:18 +0000 Subject: Return Error instead of CommandError This patch changes all public functions to return the Error enum instead of the CommandError enum. This breaks the tests which will be fixed with the next patch. This patch also adds a placeholder variant Error::CommandError and a placeholder enum CommandError to make the transition to a new nitrokey-test version easier. --- CHANGELOG.md | 1 + src/auth.rs | 54 ++++++++--------- src/config.rs | 8 +-- src/device.rs | 182 +++++++++++++++++++++++++++++----------------------------- src/error.rs | 11 ++++ src/lib.rs | 14 ++--- src/otp.rs | 62 ++++++++++---------- src/pws.rs | 62 ++++++++++---------- src/util.rs | 24 ++++---- 9 files changed, 215 insertions(+), 203 deletions(-) (limited to 'src/error.rs') diff --git a/CHANGELOG.md b/CHANGELOG.md index c69fd25..bbb3202 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Refactor the error handling code: - Implement `std::error::Error` for `CommandError`. - Add the `Error` enum and the `Result` typedef. + - Return `Error` instead of `CommandError` in all public functions. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/auth.rs b/src/auth.rs index e805e54..509d3aa 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -6,7 +6,7 @@ use nitrokey_sys; use crate::config::{Config, RawConfig}; use crate::device::{Device, DeviceWrapper, Pro, Storage}; -use crate::error::CommandError; +use crate::error::{CommandError, Error}; use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData, RawOtpSlotData}; use crate::util::{generate_password, get_command_result, get_cstring, result_from_string}; @@ -33,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) {} /// 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) => { @@ -58,7 +58,7 @@ pub trait Authenticate { /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString /// [`RngError`]: enum.CommandError.html#variant.RngError /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn authenticate_user(self, password: &str) -> Result, (Self, CommandError)> + fn authenticate_user(self, password: &str) -> Result, (Self, Error)> where Self: Device + Sized; @@ -79,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) {} /// 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) => { @@ -104,7 +104,7 @@ pub trait Authenticate { /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString /// [`RngError`]: enum.CommandError.html#variant.RngError /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn authenticate_admin(self, password: &str) -> Result, (Self, CommandError)> + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> where Self: Device + Sized; } @@ -143,7 +143,7 @@ pub struct Admin { temp_password: Vec, } -fn authenticate(device: D, password: &str, callback: T) -> Result +fn authenticate(device: D, password: &str, callback: T) -> Result where D: Device, A: AuthenticatedDevice, @@ -161,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, CommandError::from(rv).into())), }; } @@ -169,7 +169,7 @@ fn authenticate_user_wrapper( device: T, constructor: C, password: &str, -) -> Result, (DeviceWrapper, CommandError)> +) -> Result, (DeviceWrapper, Error)> where T: Device, C: Fn(T) -> DeviceWrapper, @@ -185,7 +185,7 @@ fn authenticate_admin_wrapper( device: T, constructor: C, password: &str, -) -> Result, (DeviceWrapper, CommandError)> +) -> Result, (DeviceWrapper, Error)> where T: Device, C: Fn(T) -> DeviceWrapper, @@ -215,14 +215,14 @@ impl Deref for User { } impl GenerateOtp for User { - fn get_hotp_code(&self, slot: u8) -> Result { + fn get_hotp_code(&self, slot: u8) -> Result { 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 { + fn get_totp_code(&self, slot: u8) -> Result { 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( @@ -271,9 +271,9 @@ impl Admin { /// /// ```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,7 +288,7 @@ impl Admin { /// ``` /// /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - pub fn write_config(&self, config: Config) -> Result<(), CommandError> { + 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( @@ -302,7 +302,7 @@ impl Admin { } } - fn write_otp_slot(&self, data: OtpSlotData, callback: C) -> Result<(), CommandError> + fn write_otp_slot(&self, data: OtpSlotData, callback: C) -> Result<(), Error> where C: Fn(RawOtpSlotData, *const c_char) -> c_int, { @@ -313,7 +313,7 @@ impl Admin { } impl ConfigureOtp for Admin { - 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, @@ -329,7 +329,7 @@ impl ConfigureOtp for Admin { }) } - 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, @@ -345,12 +345,12 @@ impl ConfigureOtp for Admin { }) } - 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)) } } @@ -366,7 +366,7 @@ impl AuthenticatedDevice for Admin { } impl Authenticate for DeviceWrapper { - fn authenticate_user(self, password: &str) -> Result, (Self, CommandError)> { + fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { match self { DeviceWrapper::Storage(storage) => { authenticate_user_wrapper(storage, DeviceWrapper::Storage, password) @@ -375,7 +375,7 @@ impl Authenticate for DeviceWrapper { } } - fn authenticate_admin(self, password: &str) -> Result, (Self, CommandError)> { + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { match self { DeviceWrapper::Storage(storage) => { authenticate_admin_wrapper(storage, DeviceWrapper::Storage, password) @@ -388,13 +388,13 @@ impl Authenticate for DeviceWrapper { } impl Authenticate for Pro { - fn authenticate_user(self, password: &str) -> Result, (Self, CommandError)> { + fn authenticate_user(self, password: &str) -> Result, (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, (Self, CommandError)> { + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr) }) @@ -402,13 +402,13 @@ impl Authenticate for Pro { } impl Authenticate for Storage { - fn authenticate_user(self, password: &str) -> Result, (Self, CommandError)> { + fn authenticate_user(self, password: &str) -> Result, (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, (Self, CommandError)> { + fn authenticate_admin(self, password: &str) -> Result, (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 277dc5e..741d67e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,4 @@ -use crate::error::CommandError; +use crate::error::{CommandError, Error}; /// The configuration for a Nitrokey. #[derive(Clone, Copy, Debug, PartialEq)] @@ -35,13 +35,13 @@ fn config_otp_slot_to_option(value: u8) -> Option { None } -fn option_to_config_otp_slot(value: Option) -> Result { +fn option_to_config_otp_slot(value: Option) -> Result { match value { Some(value) => { if value < 3 { Ok(value) } else { - Err(CommandError::InvalidSlot) + Err(CommandError::InvalidSlot.into()) } } None => Ok(255), @@ -66,7 +66,7 @@ impl Config { } impl RawConfig { - pub fn try_from(config: Config) -> Result { + pub fn try_from(config: Config) -> Result { 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 603a986..ccd0597 100644 --- a/src/device.rs +++ b/src/device.rs @@ -5,7 +5,7 @@ use nitrokey_sys; use crate::auth::Authenticate; use crate::config::{Config, RawConfig}; -use crate::error::CommandError; +use crate::error::{CommandError, Error}; use crate::otp::GenerateOtp; use crate::pws::GetPasswordSafe; use crate::util::{get_command_result, get_cstring, get_last_error, result_from_string}; @@ -63,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) {} /// 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) => { @@ -89,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 { @@ -127,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) {} /// 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) => { @@ -169,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) {} /// 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) => { @@ -293,9 +293,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!("Connected to a Nitrokey {}", device.get_model()); /// # Ok(()) @@ -309,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), @@ -320,7 +320,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # Ok(()) /// # } /// ``` - fn get_serial_number(&self) -> Result { + fn get_serial_number(&self) -> Result { unsafe { result_from_string(nitrokey_sys::NK_device_serial_number()) } } @@ -331,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); @@ -351,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); @@ -370,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: {}.{}", @@ -392,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: {}.{}", @@ -413,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); @@ -425,7 +425,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # Ok(()) /// # } /// ``` - fn get_config(&self) -> Result { + fn get_config(&self) -> Result { unsafe { let config_ptr = nitrokey_sys::NK_read_config(); if config_ptr.is_null() { @@ -449,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."), @@ -463,7 +463,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// [`InvalidString`]: enum.CommandError.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 { @@ -485,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."), @@ -499,7 +499,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// [`InvalidString`]: enum.CommandError.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 { @@ -521,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."), @@ -535,7 +535,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// [`InvalidString`]: enum.CommandError.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 { @@ -555,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."), @@ -566,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()) } } @@ -586,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."), @@ -599,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())) } } @@ -620,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."), @@ -633,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())) } } @@ -660,14 +660,14 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// ``` /// /// [`Undefined`]: enum.CommandError.html#variant.Undefined -pub fn connect() -> Result { +pub fn connect() -> Result { unsafe { match nitrokey_sys::NK_login_auto() { 1 => match get_connected_device() { Some(wrapper) => Ok(wrapper), - None => Err(CommandError::Undefined), + None => Err(CommandError::Undefined.into()), }, - _ => Err(CommandError::Undefined), + _ => Err(CommandError::Undefined.into()), } } } @@ -693,11 +693,11 @@ pub fn connect() -> Result { /// ``` /// /// [`Undefined`]: enum.CommandError.html#variant.Undefined -pub fn connect_model(model: Model) -> Result { +pub fn connect_model(model: Model) -> Result { if connect_enum(model) { Ok(create_device_wrapper(model)) } else { - Err(CommandError::Undefined) + Err(CommandError::Undefined.into()) } } @@ -740,19 +740,19 @@ impl DeviceWrapper { } impl GenerateOtp for DeviceWrapper { - fn get_hotp_slot_name(&self, slot: u8) -> Result { + fn get_hotp_slot_name(&self, slot: u8) -> Result { self.device().get_hotp_slot_name(slot) } - fn get_totp_slot_name(&self, slot: u8) -> Result { + fn get_totp_slot_name(&self, slot: u8) -> Result { self.device().get_totp_slot_name(slot) } - fn get_hotp_code(&self, slot: u8) -> Result { + fn get_hotp_code(&self, slot: u8) -> Result { self.device().get_hotp_code(slot) } - fn get_totp_code(&self, slot: u8) -> Result { + fn get_totp_code(&self, slot: u8) -> Result { self.device().get_totp_code(slot) } } @@ -787,11 +787,11 @@ impl Pro { /// ``` /// /// [`Undefined`]: enum.CommandError.html#variant.Undefined - pub fn connect() -> Result { + pub fn connect() -> Result { // TODO: maybe Option instead of Result? match connect_enum(Model::Pro) { true => Ok(Pro {}), - false => Err(CommandError::Undefined), + false => Err(CommandError::Undefined.into()), } } } @@ -833,11 +833,11 @@ impl Storage { /// ``` /// /// [`Undefined`]: enum.CommandError.html#variant.Undefined - pub fn connect() -> Result { + pub fn connect() -> Result { // TODO: maybe Option instead of Result? match connect_enum(Model::Storage) { true => Ok(Storage {}), - false => Err(CommandError::Undefined), + false => Err(CommandError::Undefined.into()), } } @@ -855,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."), @@ -869,7 +869,7 @@ impl Storage { /// /// [`InvalidString`]: enum.CommandError.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 { @@ -895,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."), @@ -909,7 +909,7 @@ impl Storage { /// /// [`InvalidString`]: enum.CommandError.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( @@ -931,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."), @@ -945,7 +945,7 @@ impl Storage { /// /// [`InvalidString`]: enum.CommandError.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())) } } @@ -958,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(()) => { @@ -980,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()) } } @@ -1006,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,7 +1022,7 @@ 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> { + 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( @@ -1039,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") { @@ -1062,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()) } } @@ -1088,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")?; @@ -1106,7 +1106,7 @@ impl Storage { 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( @@ -1132,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."), @@ -1151,7 +1151,7 @@ impl Storage { &self, admin_pin: &str, mode: VolumeMode, - ) -> Result<(), CommandError> { + ) -> Result<(), Error> { let admin_pin = get_cstring(admin_pin)?; let result = match mode { VolumeMode::ReadOnly => unsafe { @@ -1169,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) => { @@ -1184,7 +1184,7 @@ impl Storage { /// # Ok(()) /// # } /// ``` - pub fn get_status(&self) -> Result { + pub fn get_status(&self) -> Result { let mut raw_status = nitrokey_sys::NK_storage_status { unencrypted_volume_read_only: false, unencrypted_volume_active: false, @@ -1213,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) => { @@ -1229,7 +1229,7 @@ impl Storage { /// # Ok(()) /// # } /// ``` - pub fn get_production_info(&self) -> Result { + pub fn get_production_info(&self) -> Result { let mut raw_data = nitrokey_sys::NK_storage_ProductionTest { FirmwareVersion_au8: [0, 2], FirmwareVersionInternal_u8: 0, @@ -1264,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."), @@ -1278,7 +1278,7 @@ impl Storage { /// /// [`InvalidString`]: enum.CommandError.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()) @@ -1286,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() }) } @@ -1306,7 +1306,7 @@ impl Storage { /// /// [`InvalidString`]: enum.CommandError.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 index 89c4c82..3f60af2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,8 @@ use std::result; pub enum Error { /// An error reported by the Nitrokey device in the response packet. CommandError(CommandError), + /// Placeholder for testing. + CommunicationError(CommunicationError), } impl From for Error { @@ -21,6 +23,7 @@ impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match *self { Error::CommandError(ref err) => Some(err), + Error::CommunicationError(_) => None, } } } @@ -29,6 +32,7 @@ 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(_) => write!(f, "Placeholder"), } } } @@ -78,6 +82,13 @@ pub enum CommandError { RngError, } +/// Placeholder for testing. +#[derive(Debug)] +pub enum CommunicationError { + /// Placeholder for testing. + NotConnected, +} + impl CommandError { fn as_str(&self) -> borrow::Cow<'static, str> { match *self { diff --git a/src/lib.rs b/src/lib.rs index df11cc3..8522e83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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), @@ -104,7 +104,7 @@ pub use crate::device::{ connect, connect_model, Device, DeviceWrapper, Model, Pro, SdCardData, Storage, StorageProductionInfo, StorageStatus, VolumeMode, VolumeStatus, }; -pub use crate::error::{CommandError, Error, Result}; +pub use crate::error::{CommandError, CommunicationError, Error, Result}; pub use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData}; pub use crate::pws::{GetPasswordSafe, PasswordSafe, SLOT_COUNT}; pub use crate::util::LogLevel; diff --git a/src/otp.rs b/src/otp.rs index 784149a..5dfe8b1 100644 --- a/src/otp.rs +++ b/src/otp.rs @@ -2,7 +2,7 @@ use std::ffi::CString; use nitrokey_sys; -use crate::error::CommandError; +use crate::error::Error; use crate::util::{get_command_result, get_cstring, result_from_string}; /// Modes for one-time password generation. @@ -29,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") { @@ -50,7 +50,7 @@ pub trait ConfigureOtp { /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot /// [`InvalidString`]: enum.CommandError.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). @@ -65,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") { @@ -86,7 +86,7 @@ pub trait ConfigureOtp { /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot /// [`InvalidString`]: enum.CommandError.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. /// @@ -98,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) => { @@ -116,7 +116,7 @@ pub trait ConfigureOtp { /// ``` /// /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - fn erase_hotp_slot(&self, slot: u8) -> Result<(), CommandError>; + fn erase_hotp_slot(&self, slot: u8) -> Result<(), Error>; /// Erases a TOTP slot. /// @@ -128,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) => { @@ -146,7 +146,7 @@ pub trait ConfigureOtp { /// ``` /// /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - fn erase_totp_slot(&self, slot: u8) -> Result<(), CommandError>; + fn erase_totp_slot(&self, slot: u8) -> Result<(), Error>; } /// Provides methods to generate OTP codes and to query OTP slots on a Nitrokey @@ -165,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 { @@ -184,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 { @@ -203,13 +203,13 @@ 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(()) @@ -218,7 +218,7 @@ pub trait GenerateOtp { /// /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed - fn get_hotp_slot_name(&self, slot: u8) -> Result { + fn get_hotp_slot_name(&self, slot: u8) -> Result { unsafe { result_from_string(nitrokey_sys::NK_get_hotp_slot_name(slot)) } } @@ -232,13 +232,13 @@ 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(()) @@ -247,7 +247,7 @@ pub trait GenerateOtp { /// /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed - fn get_totp_slot_name(&self, slot: u8) -> Result { + fn get_totp_slot_name(&self, slot: u8) -> Result { unsafe { result_from_string(nitrokey_sys::NK_get_totp_slot_name(slot)) } } @@ -264,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); @@ -278,7 +278,7 @@ pub trait GenerateOtp { /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot /// [`NotAuthorized`]: enum.CommandError.html#variant.NotAuthorized /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed - fn get_hotp_code(&self, slot: u8) -> Result { + fn get_hotp_code(&self, slot: u8) -> Result { unsafe { return result_from_string(nitrokey_sys::NK_get_hotp_code(slot)); } @@ -301,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 { @@ -323,7 +323,7 @@ pub trait GenerateOtp { /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot /// [`NotAuthorized`]: enum.CommandError.html#variant.NotAuthorized /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed - fn get_totp_code(&self, slot: u8) -> Result { + fn get_totp_code(&self, slot: u8) -> Result { unsafe { return result_from_string(nitrokey_sys::NK_get_totp_code(slot, 0, 0, 0)); } @@ -396,7 +396,7 @@ impl OtpSlotData { } impl RawOtpSlotData { - pub fn new(data: OtpSlotData) -> Result { + pub fn new(data: OtpSlotData) -> Result { let name = get_cstring(data.name)?; let secret = get_cstring(data.secret)?; let use_token_id = data.token_id.is_some(); diff --git a/src/pws.rs b/src/pws.rs index 615e47c..e974737 100644 --- a/src/pws.rs +++ b/src/pws.rs @@ -2,7 +2,7 @@ use libc; use nitrokey_sys; use crate::device::{Device, DeviceWrapper, Pro, Storage}; -use crate::error::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`][]. @@ -29,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)?; @@ -39,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); @@ -88,11 +88,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) => { @@ -112,13 +112,13 @@ pub trait GetPasswordSafe { /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString /// [`Unknown`]: enum.CommandError.html#variant.Unknown /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn get_password_safe(&self, user_pin: &str) -> Result, CommandError>; + fn get_password_safe(&self, user_pin: &str) -> Result, Error>; } fn get_password_safe<'a>( device: &'a dyn Device, user_pin: &str, -) -> Result, CommandError> { +) -> Result, Error> { let user_pin_string = get_cstring(user_pin)?; let result = unsafe { get_command_result(nitrokey_sys::NK_enable_password_safe( @@ -128,9 +128,9 @@ fn get_password_safe<'a>( result.map(|()| PasswordSafe { _device: device }) } -fn get_pws_result(s: String) -> Result { +fn get_pws_result(s: String) -> Result { if s.is_empty() { - Err(CommandError::SlotNotProgrammed) + Err(CommandError::SlotNotProgrammed.into()) } else { Ok(s) } @@ -145,9 +145,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)| { @@ -160,7 +160,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()); @@ -190,9 +190,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) => { @@ -209,7 +209,7 @@ impl<'a> PasswordSafe<'a> { /// /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed - pub fn get_slot_name(&self, slot: u8) -> Result { + pub fn get_slot_name(&self, slot: u8) -> Result { unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_name(slot)) } .and_then(get_pws_result) } @@ -227,9 +227,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)?; @@ -242,7 +242,7 @@ impl<'a> PasswordSafe<'a> { /// /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed - pub fn get_slot_login(&self, slot: u8) -> Result { + pub fn get_slot_login(&self, slot: u8) -> Result { unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_login(slot)) } .and_then(get_pws_result) } @@ -260,9 +260,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)?; @@ -275,7 +275,7 @@ impl<'a> PasswordSafe<'a> { /// /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed - pub fn get_slot_password(&self, slot: u8) -> Result { + pub fn get_slot_password(&self, slot: u8) -> Result { unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_password(slot)) } .and_then(get_pws_result) } @@ -291,9 +291,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)?; @@ -312,7 +312,7 @@ impl<'a> PasswordSafe<'a> { 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)?; @@ -337,9 +337,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,7 +351,7 @@ impl<'a> PasswordSafe<'a> { /// ``` /// /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot - pub fn erase_slot(&self, slot: u8) -> Result<(), CommandError> { + pub fn erase_slot(&self, slot: u8) -> Result<(), Error> { unsafe { get_command_result(nitrokey_sys::NK_erase_password_safe_slot(slot)) } } } @@ -364,19 +364,19 @@ impl<'a> Drop for PasswordSafe<'a> { } impl GetPasswordSafe for Pro { - fn get_password_safe(&self, user_pin: &str) -> Result, CommandError> { + fn get_password_safe(&self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } impl GetPasswordSafe for Storage { - fn get_password_safe(&self, user_pin: &str) -> Result, CommandError> { + fn get_password_safe(&self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } impl GetPasswordSafe for DeviceWrapper { - fn get_password_safe(&self, user_pin: &str) -> Result, CommandError> { + fn get_password_safe(&self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } diff --git a/src/util.rs b/src/util.rs index 88a381c..8855275 100644 --- a/src/util.rs +++ b/src/util.rs @@ -5,7 +5,7 @@ use libc::{c_void, free}; use rand_core::RngCore; use rand_os::OsRng; -use crate::error::CommandError; +use crate::error::{CommandError, Error}; /// Log level for libnitrokey. /// @@ -34,9 +34,9 @@ pub fn owned_str_from_ptr(ptr: *const c_char) -> String { } } -pub fn result_from_string(ptr: *const c_char) -> Result { +pub fn result_from_string(ptr: *const c_char) -> Result { if ptr.is_null() { - return Err(CommandError::Undefined); + return Err(CommandError::Undefined.into()); } unsafe { let s = owned_str_from_ptr(ptr); @@ -51,34 +51,34 @@ pub fn result_from_string(ptr: *const c_char) -> Result { } } -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(CommandError::from(other).into()), } } -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(()) => CommandError::Undefined.into(), Err(err) => err, }; } -pub fn generate_password(length: usize) -> Result, CommandError> { - let mut rng = OsRng::new()?; +pub fn generate_password(length: usize) -> Result, Error> { + let mut rng = OsRng::new().map_err(CommandError::from)?; let mut data = vec![0u8; length]; rng.fill_bytes(&mut data[..]); Ok(data) } -pub fn get_cstring>>(s: T) -> Result { - CString::new(s).or(Err(CommandError::InvalidString)) +pub fn get_cstring>>(s: T) -> Result { + CString::new(s).or(Err(CommandError::InvalidString.into())) } impl Into for LogLevel { -- cgit v1.2.3 From cafc3a6f8cfb9f82343c1d3fe843c7f8d7ef1136 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 20 Jan 2019 21:05:58 +0000 Subject: Refactor CommandError::RngError into Error::RandError We reserve CommandError for errors returned by the Nitrokey device. Errors during random number generation should have their own type. --- CHANGELOG.md | 1 + src/error.rs | 19 ++++++++++--------- src/util.rs | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) (limited to 'src/error.rs') diff --git a/CHANGELOG.md b/CHANGELOG.md index bbb3202..5a78724 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Implement `std::error::Error` for `CommandError`. - Add the `Error` enum and the `Result` typedef. - Return `Error` instead of `CommandError` in all public functions. + - Move the `CommandError::RngError` variant to `Error::RandError`. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/error.rs b/src/error.rs index 3f60af2..dfe1680 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,6 +11,8 @@ pub enum Error { CommandError(CommandError), /// Placeholder for testing. CommunicationError(CommunicationError), + /// An error that occured during random number generation. + RandError(rand_core::Error), } impl From for Error { @@ -19,11 +21,18 @@ impl From for Error { } } +impl From 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(_) => None, + Error::RandError(ref err) => Some(err), } } } @@ -33,6 +42,7 @@ impl fmt::Display for Error { match *self { Error::CommandError(ref err) => write!(f, "Command error: {}", err), Error::CommunicationError(_) => write!(f, "Placeholder"), + Error::RandError(ref err) => write!(f, "RNG error: {}", err), } } } @@ -78,8 +88,6 @@ pub enum CommandError { InvalidHexString, /// The target buffer was smaller than the source. TargetBufferTooSmall, - /// An error occurred during random number generation. - RngError, } /// Placeholder for testing. @@ -119,7 +127,6 @@ impl CommandError { "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(), } } } @@ -153,9 +160,3 @@ impl From for CommandError { } } } - -impl From for CommandError { - fn from(_error: rand_core::Error) -> Self { - CommandError::RngError - } -} diff --git a/src/util.rs b/src/util.rs index 8855275..06bb854 100644 --- a/src/util.rs +++ b/src/util.rs @@ -71,7 +71,7 @@ pub fn get_last_error() -> Error { } pub fn generate_password(length: usize) -> Result, Error> { - let mut rng = OsRng::new().map_err(CommandError::from)?; + let mut rng = OsRng::new()?; let mut data = vec![0u8; length]; rng.fill_bytes(&mut data[..]); Ok(data) -- cgit v1.2.3 From 944e1fa0d51e547dde2a9368d2b8431b109f63c4 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 04:15:31 +0000 Subject: Move the CommandError::Unknown to Error An error code can not only indiciate a command error, but also a library or device communication error. Therefore, the variant for an unknown error code should be placed in the top-level Error enum instead of the CommandError enum. --- CHANGELOG.md | 3 ++- src/auth.rs | 4 ++-- src/error.rs | 61 +++++++++++++++++++++++++++++++++--------------------------- src/util.rs | 2 +- tests/pws.rs | 2 +- 5 files changed, 40 insertions(+), 32 deletions(-) (limited to 'src/error.rs') diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a78724..def5273 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ - Implement `std::error::Error` for `CommandError`. - Add the `Error` enum and the `Result` typedef. - Return `Error` instead of `CommandError` in all public functions. - - Move the `CommandError::RngError` variant to `Error::RandError`. + - Move the `CommandError::RngError` variant to `Error::RandError` and the + `CommandError::Unknown` variant to `Error::Unknown`. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/auth.rs b/src/auth.rs index 509d3aa..e05f6b3 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -6,7 +6,7 @@ use nitrokey_sys; use crate::config::{Config, RawConfig}; use crate::device::{Device, DeviceWrapper, Pro, Storage}; -use crate::error::{CommandError, Error}; +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}; @@ -161,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).into())), + rv => Err((device, Error::from(rv))), }; } diff --git a/src/error.rs b/src/error.rs index dfe1680..c5a975e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -13,6 +13,18 @@ pub enum Error { CommunicationError(CommunicationError), /// An error that occured during random number generation. RandError(rand_core::Error), + /// An unknown error returned by libnitrokey. + Unknown(i64), +} + +impl From for Error { + fn from(code: raw::c_int) -> Self { + if let Some(err) = CommandError::try_from(code) { + Error::CommandError(err) + } else { + Error::Unknown(code.into()) + } + } } impl From for Error { @@ -33,6 +45,7 @@ impl error::Error for Error { Error::CommandError(ref err) => Some(err), Error::CommunicationError(_) => None, Error::RandError(ref err) => Some(err), + Error::Unknown(_) => None, } } } @@ -43,6 +56,7 @@ impl fmt::Display for Error { Error::CommandError(ref err) => write!(f, "Command error: {}", err), Error::CommunicationError(_) => write!(f, "Placeholder"), Error::RandError(ref err) => write!(f, "RNG error: {}", err), + Error::Unknown(ref err) => write!(f, "Unknown error: {}", err), } } } @@ -74,8 +88,6 @@ pub enum CommandError { UnknownCommand, /// AES decryption failed. AesDecryptionFailed, - /// An unknown error occurred. - Unknown(i64), /// An unspecified error occurred. Undefined, /// You passed a string containing a null byte. @@ -98,6 +110,26 @@ pub enum CommunicationError { } impl CommandError { + fn try_from(value: raw::c_int) -> Option { + 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), + 200 => Some(CommandError::StringTooLong), + 201 => Some(CommandError::InvalidSlot), + 202 => Some(CommandError::InvalidHexString), + 203 => Some(CommandError::TargetBufferTooSmall), + _ => None, + } + } + fn as_str(&self) -> borrow::Cow<'static, str> { match *self { CommandError::WrongCrc => { @@ -116,9 +148,6 @@ impl CommandError { 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(), @@ -138,25 +167,3 @@ impl fmt::Display for CommandError { write!(f, "{}", self.as_str()) } } - -impl From for CommandError { - fn from(value: raw::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()), - } - } -} diff --git a/src/util.rs b/src/util.rs index 06bb854..3b9904f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -54,7 +54,7 @@ pub fn result_from_string(ptr: *const c_char) -> Result { pub fn get_command_result(value: c_int) -> Result<(), Error> { match value { 0 => Ok(()), - other => Err(CommandError::from(other).into()), + other => Err(Error::from(other)), } } diff --git a/tests/pws.rs b/tests/pws.rs index f12af48..a4647bd 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -21,7 +21,7 @@ fn get_slot_name_direct(slot: u8) -> Result { let error = unsafe { nitrokey_sys::NK_get_last_command_status() } as c_int; match error { 0 => Ok(s), - other => Err(CommandError::from(other).into()), + other => Err(Error::from(other)), } } false => Ok(s), -- cgit v1.2.3 From 5e258d26b55af6bed7c316b1c7ac12e20946702d Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 12:47:52 +0000 Subject: Refactor library errors into LibraryError enum Previously, library errors were part of the CommandError enum. As command errors and library errors are two different error types, they should be split into two enums. --- CHANGELOG.md | 2 ++ src/auth.rs | 6 ++--- src/config.rs | 4 +-- src/device.rs | 22 ++++++++--------- src/error.rs | 74 +++++++++++++++++++++++++++++++++++++++---------------- src/lib.rs | 2 +- src/otp.rs | 20 +++++++-------- src/pws.rs | 14 +++++------ src/util.rs | 4 +-- tests/device.rs | 4 +-- tests/otp.rs | 28 ++++++++++----------- tests/pws.rs | 16 ++++++------ tests/util/mod.rs | 7 ++++++ 13 files changed, 123 insertions(+), 80 deletions(-) (limited to 'src/error.rs') diff --git a/CHANGELOG.md b/CHANGELOG.md index def5273..c34175e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - 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`. - 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`. diff --git a/src/auth.rs b/src/auth.rs index e05f6b3..d1eb049 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -55,7 +55,7 @@ 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, (Self, Error)> @@ -101,7 +101,7 @@ 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, (Self, Error)> @@ -287,7 +287,7 @@ impl Admin { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot pub fn write_config(&self, config: Config) -> Result<(), Error> { let raw_config = RawConfig::try_from(config)?; unsafe { diff --git a/src/config.rs b/src/config.rs index 741d67e..6aa6d10 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,4 @@ -use crate::error::{CommandError, Error}; +use crate::error::{Error, LibraryError}; /// The configuration for a Nitrokey. #[derive(Clone, Copy, Debug, PartialEq)] @@ -41,7 +41,7 @@ fn option_to_config_otp_slot(value: Option) -> Result { if value < 3 { Ok(value) } else { - Err(CommandError::InvalidSlot.into()) + Err(LibraryError::InvalidSlot.into()) } } None => Ok(255), diff --git a/src/device.rs b/src/device.rs index ccd0597..5c4014b 100644 --- a/src/device.rs +++ b/src/device.rs @@ -461,7 +461,7 @@ 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<(), Error> { let current_string = get_cstring(current)?; @@ -497,7 +497,7 @@ 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<(), Error> { let current_string = get_cstring(current)?; @@ -533,7 +533,7 @@ 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<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; @@ -867,7 +867,7 @@ 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<(), Error> { let current_string = get_cstring(current)?; @@ -907,7 +907,7 @@ 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<(), Error> { let update_pin_string = get_cstring(update_pin)?; @@ -943,7 +943,7 @@ 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<(), Error> { let user_pin = get_cstring(user_pin)?; @@ -1021,7 +1021,7 @@ impl Storage { /// /// [`enable_encrypted_volume`]: #method.enable_encrypted_volume /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`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 { @@ -1099,7 +1099,7 @@ 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, @@ -1145,7 +1145,7 @@ 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, @@ -1276,7 +1276,7 @@ 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<(), Error> { let admin_pin = get_cstring(admin_pin)?; @@ -1304,7 +1304,7 @@ 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<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; diff --git a/src/error.rs b/src/error.rs index c5a975e..f40d07f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,6 +11,8 @@ pub enum Error { CommandError(CommandError), /// Placeholder for testing. CommunicationError(CommunicationError), + /// A library usage error. + LibraryError(LibraryError), /// An error that occured during random number generation. RandError(rand_core::Error), /// An unknown error returned by libnitrokey. @@ -21,6 +23,8 @@ impl From 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) = LibraryError::try_from(code) { + Error::LibraryError(err) } else { Error::Unknown(code.into()) } @@ -33,6 +37,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: LibraryError) -> Self { + Error::LibraryError(err) + } +} + impl From for Error { fn from(error: rand_core::Error) -> Self { Error::RandError(error) @@ -44,6 +54,7 @@ impl error::Error for Error { match *self { Error::CommandError(ref err) => Some(err), Error::CommunicationError(_) => None, + Error::LibraryError(ref err) => Some(err), Error::RandError(ref err) => Some(err), Error::Unknown(_) => None, } @@ -55,6 +66,7 @@ impl fmt::Display for Error { match *self { Error::CommandError(ref err) => write!(f, "Command error: {}", err), Error::CommunicationError(_) => write!(f, "Placeholder"), + Error::LibraryError(ref err) => write!(f, "Library error: {}", err), Error::RandError(ref err) => write!(f, "RNG error: {}", err), Error::Unknown(ref err) => write!(f, "Unknown error: {}", err), } @@ -90,16 +102,6 @@ pub enum CommandError { AesDecryptionFailed, /// 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, } /// Placeholder for testing. @@ -122,10 +124,6 @@ impl CommandError { 8 => Some(CommandError::NotSupported), 9 => Some(CommandError::UnknownCommand), 10 => Some(CommandError::AesDecryptionFailed), - 200 => Some(CommandError::StringTooLong), - 201 => Some(CommandError::InvalidSlot), - 202 => Some(CommandError::InvalidHexString), - 203 => Some(CommandError::TargetBufferTooSmall), _ => None, } } @@ -149,13 +147,6 @@ impl CommandError { CommandError::UnknownCommand => "This command is unknown".into(), CommandError::AesDecryptionFailed => "AES decryption failed".into(), 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(), } } } @@ -167,3 +158,44 @@ impl fmt::Display for CommandError { write!(f, "{}", self.as_str()) } } + +/// 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 { + 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", + }) + } +} diff --git a/src/lib.rs b/src/lib.rs index 8522e83..993ec92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,7 +104,7 @@ pub use crate::device::{ connect, connect_model, Device, DeviceWrapper, Model, Pro, SdCardData, Storage, StorageProductionInfo, StorageStatus, VolumeMode, VolumeStatus, }; -pub use crate::error::{CommandError, CommunicationError, Error, Result}; +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::LogLevel; diff --git a/src/otp.rs b/src/otp.rs index 5dfe8b1..7535a77 100644 --- a/src/otp.rs +++ b/src/otp.rs @@ -47,8 +47,8 @@ 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<(), Error>; @@ -83,8 +83,8 @@ 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<(), Error>; @@ -115,7 +115,7 @@ pub trait ConfigureOtp { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot fn erase_hotp_slot(&self, slot: u8) -> Result<(), Error>; /// Erases a TOTP slot. @@ -145,7 +145,7 @@ pub trait ConfigureOtp { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot fn erase_totp_slot(&self, slot: u8) -> Result<(), Error>; } @@ -216,7 +216,7 @@ pub trait GenerateOtp { /// # } /// ``` /// - /// [`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 { unsafe { result_from_string(nitrokey_sys::NK_get_hotp_slot_name(slot)) } @@ -245,7 +245,7 @@ pub trait GenerateOtp { /// # } /// ``` /// - /// [`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 { unsafe { result_from_string(nitrokey_sys::NK_get_totp_slot_name(slot)) } @@ -275,7 +275,7 @@ 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 { @@ -320,7 +320,7 @@ 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 { diff --git a/src/pws.rs b/src/pws.rs index e974737..47965d7 100644 --- a/src/pws.rs +++ b/src/pws.rs @@ -109,7 +109,7 @@ 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, Error>; @@ -207,7 +207,7 @@ 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 { unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_name(slot)) } @@ -240,7 +240,7 @@ 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 { unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_login(slot)) } @@ -273,7 +273,7 @@ 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 { unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_password(slot)) } @@ -304,8 +304,8 @@ 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, @@ -350,7 +350,7 @@ impl<'a> PasswordSafe<'a> { /// # } /// ``` /// - /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot + /// [`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)) } } diff --git a/src/util.rs b/src/util.rs index 3b9904f..2738fce 100644 --- a/src/util.rs +++ b/src/util.rs @@ -5,7 +5,7 @@ use libc::{c_void, free}; use rand_core::RngCore; use rand_os::OsRng; -use crate::error::{CommandError, Error}; +use crate::error::{CommandError, Error, LibraryError}; /// Log level for libnitrokey. /// @@ -78,7 +78,7 @@ pub fn generate_password(length: usize) -> Result, Error> { } pub fn get_cstring>>(s: T) -> Result { - CString::new(s).or(Err(CommandError::InvalidString.into())) + CString::new(s).or(Err(LibraryError::InvalidString.into())) } impl Into for LogLevel { diff --git a/tests/device.rs b/tests/device.rs index 174624f..ee5dae1 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -6,7 +6,7 @@ use std::{thread, time}; use nitrokey::{ Authenticate, CommandError, Config, ConfigureOtp, Device, Error, GenerateOtp, GetPasswordSafe, - OtpMode, OtpSlotData, Storage, VolumeMode, + LibraryError, OtpMode, OtpSlotData, Storage, VolumeMode, }; use nitrokey_test::test as test_device; @@ -130,7 +130,7 @@ fn config(device: DeviceWrapper) { assert_eq!(config, get_config); let config = Config::new(None, Some(9), None, true); - assert_cmd_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_ok!((), admin.write_config(config)); diff --git a/tests/otp.rs b/tests/otp.rs index d328351..51a6539 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; @@ -106,7 +106,7 @@ fn hotp_slot_name(device: DeviceWrapper) { let result = device.get_hotp_slot_name(1); assert_eq!("test-hotp", result.unwrap()); let result = device.get_hotp_slot_name(4); - assert_cmd_err!(CommandError::InvalidSlot, result); + assert_lib_err!(LibraryError::InvalidSlot, result); } #[test_device] @@ -115,17 +115,17 @@ fn hotp_error(device: DeviceWrapper) { let slot_data = OtpSlotData::new(1, "", HOTP_SECRET, OtpMode::SixDigits); assert_cmd_err!(CommandError::NoName, admin.write_hotp_slot(slot_data, 0)); let slot_data = OtpSlotData::new(4, "test", HOTP_SECRET, OtpMode::SixDigits); - assert_cmd_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_cmd_err!( - CommandError::InvalidHexString, + assert_lib_err!( + LibraryError::InvalidHexString, admin.write_hotp_slot(slot_data, 0) ); let code = admin.get_hotp_code(4); - assert_cmd_err!(CommandError::InvalidSlot, code); + assert_lib_err!(LibraryError::InvalidSlot, code); } #[test_device] @@ -249,7 +249,7 @@ fn totp_slot_name(device: DeviceWrapper) { assert!(result.is_ok()); assert_eq!("test-totp", result.unwrap()); let result = device.get_totp_slot_name(16); - assert_cmd_err!(CommandError::InvalidSlot, result); + assert_lib_err!(LibraryError::InvalidSlot, result); } #[test_device] @@ -258,17 +258,17 @@ fn totp_error(device: DeviceWrapper) { let slot_data = OtpSlotData::new(1, "", TOTP_SECRET, OtpMode::SixDigits); assert_cmd_err!(CommandError::NoName, admin.write_totp_slot(slot_data, 0)); let slot_data = OtpSlotData::new(20, "test", TOTP_SECRET, OtpMode::SixDigits); - assert_cmd_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_cmd_err!( - CommandError::InvalidHexString, + assert_lib_err!( + LibraryError::InvalidHexString, admin.write_totp_slot(slot_data, 0) ); let code = admin.get_totp_code(20); - assert_cmd_err!(CommandError::InvalidSlot, code); + assert_lib_err!(LibraryError::InvalidSlot, code); } #[test_device] diff --git a/tests/pws.rs b/tests/pws.rs index a4647bd..51e6189 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -3,7 +3,9 @@ mod util; use std::ffi::CStr; use libc::{c_int, c_void, free}; -use nitrokey::{CommandError, Device, Error, GetPasswordSafe, PasswordSafe, SLOT_COUNT}; +use nitrokey::{ + CommandError, Device, Error, GetPasswordSafe, LibraryError, PasswordSafe, SLOT_COUNT, +}; use nitrokey_sys; use nitrokey_test::test as test_device; @@ -104,17 +106,17 @@ fn get_data(device: DeviceWrapper) { assert_eq!(login, pws.get_slot_login(1).unwrap()); assert_eq!(password, pws.get_slot_password(1).unwrap()); - assert_cmd_err!(CommandError::InvalidSlot, pws.get_slot_name(SLOT_COUNT)); - assert_cmd_err!(CommandError::InvalidSlot, pws.get_slot_login(SLOT_COUNT)); - assert_cmd_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_cmd_err!( - CommandError::InvalidSlot, + assert_lib_err!( + LibraryError::InvalidSlot, pws.write_slot(SLOT_COUNT, "name", "login", "password") ); @@ -137,7 +139,7 @@ fn write(device: DeviceWrapper) { #[test_device] fn erase(device: DeviceWrapper) { let pws = get_pws(&device); - assert_cmd_err!(CommandError::InvalidSlot, pws.erase_slot(SLOT_COUNT)); + assert_lib_err!(LibraryError::InvalidSlot, pws.erase_slot(SLOT_COUNT)); assert_ok!((), pws.write_slot(0, "name", "login", "password")); assert_ok!((), pws.erase_slot(0)); diff --git a/tests/util/mod.rs b/tests/util/mod.rs index f0d0bb5..b1d3ea3 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -67,3 +67,10 @@ macro_rules! assert_cmd_err { assert_err!(::nitrokey::Error::CommandError, $left, $right); }; } + +#[macro_export] +macro_rules! assert_lib_err { + ($left:expr, $right:expr) => { + assert_err!(::nitrokey::Error::LibraryError, $left, $right); + }; +} -- cgit v1.2.3 From 27138c4b799248d2d39e9681337a620c89636557 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 13:10:01 +0000 Subject: Add the CommunicationError enum Communication errors returned by libnitrokey were previously not mapped to an error type in the nitrokey crate. We introduce the CommunicationError enum to represent these errors. --- CHANGELOG.md | 2 ++ src/error.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 51 insertions(+), 10 deletions(-) (limited to 'src/error.rs') diff --git a/CHANGELOG.md b/CHANGELOG.md index c34175e..413c626 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ - 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`. diff --git a/src/error.rs b/src/error.rs index f40d07f..a2b3848 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,7 +9,7 @@ use std::result; pub enum Error { /// An error reported by the Nitrokey device in the response packet. CommandError(CommandError), - /// Placeholder for testing. + /// A device communication. CommunicationError(CommunicationError), /// A library usage error. LibraryError(LibraryError), @@ -23,6 +23,8 @@ impl From 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 { @@ -37,6 +39,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: CommunicationError) -> Self { + Error::CommunicationError(err) + } +} + impl From for Error { fn from(err: LibraryError) -> Self { Error::LibraryError(err) @@ -53,7 +61,7 @@ impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match *self { Error::CommandError(ref err) => Some(err), - Error::CommunicationError(_) => None, + Error::CommunicationError(ref err) => Some(err), Error::LibraryError(ref err) => Some(err), Error::RandError(ref err) => Some(err), Error::Unknown(_) => None, @@ -65,7 +73,7 @@ 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(_) => write!(f, "Placeholder"), + 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::Unknown(ref err) => write!(f, "Unknown error: {}", err), @@ -104,13 +112,6 @@ pub enum CommandError { Undefined, } -/// Placeholder for testing. -#[derive(Debug)] -pub enum CommunicationError { - /// Placeholder for testing. - NotConnected, -} - impl CommandError { fn try_from(value: raw::c_int) -> Option { match value { @@ -159,6 +160,44 @@ impl fmt::Display for CommandError { } } +/// 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 { + 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 { -- cgit v1.2.3 From 391cfd03edafd6e857d6cdbee1347f38e7a02b3f Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 13:28:03 +0000 Subject: Remove CommandError::as_str method AsStr is automatically implementeded if Display is implemented, so having a manual as_str() method is not necessary. --- src/error.rs | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) (limited to 'src/error.rs') diff --git a/src/error.rs b/src/error.rs index a2b3848..1aaf21f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,3 @@ -use std::borrow; use std::error; use std::fmt; use std::os::raw; @@ -128,35 +127,28 @@ impl CommandError { _ => None, } } - - 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::Undefined => "An unspecified error occurred".into(), - } - } } impl error::Error for CommandError {} impl fmt::Display for CommandError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.as_str()) + f.write_str(match *self { + CommandError::WrongCrc => "A packet with a wrong checksum has been sent or received", + CommandError::WrongSlot => "The given OTP slot does not exist", + CommandError::SlotNotProgrammed => "The given OTP 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 OTP slot", + CommandError::NotSupported => "This command is not supported by this device", + CommandError::UnknownCommand => "This command is unknown", + CommandError::AesDecryptionFailed => "AES decryption failed", + CommandError::Undefined => "An unspecified error occurred", + }) } } -- cgit v1.2.3 From c3e551dd40142bcd2552972d549f31ad7483621d Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 13:30:20 +0000 Subject: Make CommandError messages more general For example, the WrongSlot error may also be returned for a PWS slot. --- src/error.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/error.rs') diff --git a/src/error.rs b/src/error.rs index 1aaf21f..ef9b149 100644 --- a/src/error.rs +++ b/src/error.rs @@ -135,15 +135,15 @@ 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 OTP slot does not exist", - CommandError::SlotNotProgrammed => "The given OTP slot is not programmed", + 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 OTP slot", + 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", -- cgit v1.2.3 From c191e875492ff8aeab1b4493b87486cd265f0edc Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 13:38:28 +0000 Subject: Introduce the Error::UnexpectedError variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The UnexpectedError variant is used when a libnitrokey function returns a value that violates the function’s contract, for example if a function returns a null pointer although it guarantees to never return null. Previously, we returned a CommandError::Unspecified in these cases. --- src/error.rs | 4 ++++ src/util.rs | 6 +++--- tests/pws.rs | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src/error.rs') diff --git a/src/error.rs b/src/error.rs index ef9b149..b27124c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -14,6 +14,8 @@ pub enum 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), } @@ -63,6 +65,7 @@ impl error::Error for Error { Error::CommunicationError(ref err) => Some(err), Error::LibraryError(ref err) => Some(err), Error::RandError(ref err) => Some(err), + Error::UnexpectedError => None, Error::Unknown(_) => None, } } @@ -75,6 +78,7 @@ impl fmt::Display for Error { 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), } } diff --git a/src/util.rs b/src/util.rs index 2738fce..79b8c34 100644 --- a/src/util.rs +++ b/src/util.rs @@ -5,7 +5,7 @@ use libc::{c_void, free}; use rand_core::RngCore; use rand_os::OsRng; -use crate::error::{CommandError, Error, LibraryError}; +use crate::error::{Error, LibraryError}; /// Log level for libnitrokey. /// @@ -36,7 +36,7 @@ pub fn owned_str_from_ptr(ptr: *const c_char) -> String { pub fn result_from_string(ptr: *const c_char) -> Result { if ptr.is_null() { - return Err(CommandError::Undefined.into()); + return Err(Error::UnexpectedError); } unsafe { let s = owned_str_from_ptr(ptr); @@ -65,7 +65,7 @@ pub fn get_last_result() -> Result<(), Error> { pub fn get_last_error() -> Error { return match get_last_result() { - Ok(()) => CommandError::Undefined.into(), + Ok(()) => Error::UnexpectedError, Err(err) => err, }; } diff --git a/tests/pws.rs b/tests/pws.rs index 51e6189..b89d7f6 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -14,7 +14,7 @@ use crate::util::{ADMIN_PASSWORD, USER_PASSWORD}; fn get_slot_name_direct(slot: u8) -> Result { let ptr = unsafe { nitrokey_sys::NK_get_password_safe_slot_name(slot) }; if ptr.is_null() { - return Err(CommandError::Undefined.into()); + return Err(Error::UnexpectedError); } let s = unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }; unsafe { free(ptr as *mut c_void) }; -- cgit v1.2.3 From 17f9c30a0ace070cba856e4e89fcccedcab5e8e6 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 14:00:54 +0000 Subject: Remove the unused CommandError::Undefined variant The CommandError::Undefined variant has been refactored into Error::UnexpectedError and CommunicationError::NotConnected and is therefore no longer needed. --- CHANGELOG.md | 1 + src/error.rs | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'src/error.rs') diff --git a/CHANGELOG.md b/CHANGELOG.md index 1856336..a4df5c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ `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. diff --git a/src/error.rs b/src/error.rs index b27124c..cde9a34 100644 --- a/src/error.rs +++ b/src/error.rs @@ -111,8 +111,6 @@ pub enum CommandError { UnknownCommand, /// AES decryption failed. AesDecryptionFailed, - /// An unspecified error occurred. - Undefined, } impl CommandError { @@ -151,7 +149,6 @@ impl fmt::Display for CommandError { CommandError::NotSupported => "This command is not supported by this device", CommandError::UnknownCommand => "This command is unknown", CommandError::AesDecryptionFailed => "AES decryption failed", - CommandError::Undefined => "An unspecified error occurred", }) } } -- cgit v1.2.3 From c79ddf8116659efd1aa7de42bb85337632f238dd Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 23 Jan 2019 04:16:59 +0000 Subject: Add Error::Utf8Error variant Previously, we just ignored UTF-8 errors. This patch prepares the Utf8Error variant so that we are able to return UTF-8 errors. --- CHANGELOG.md | 1 + src/error.rs | 11 +++++++++++ 2 files changed, 12 insertions(+) (limited to 'src/error.rs') diff --git a/CHANGELOG.md b/CHANGELOG.md index 6305ebe..c800521 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Prefer using the `Into` trait over numeric casting. - Add `Pro::new` and `Storage::new` functions. - Implement `From` and `From` for `DeviceWrapper`. +- Add `Error::Utf8Error` variant. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/error.rs b/src/error.rs index cde9a34..4b82c6e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,6 +2,7 @@ use std::error; use std::fmt; use std::os::raw; use std::result; +use std::str; /// An error returned by the nitrokey crate. #[derive(Debug)] @@ -18,6 +19,8 @@ pub enum Error { UnexpectedError, /// An unknown error returned by libnitrokey. Unknown(i64), + /// An error occurred when interpreting a UTF-8 string. + Utf8Error(str::Utf8Error), } impl From for Error { @@ -58,6 +61,12 @@ impl From for Error { } } +impl From for Error { + fn from(error: str::Utf8Error) -> Self { + Error::Utf8Error(error) + } +} + impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match *self { @@ -67,6 +76,7 @@ impl error::Error for Error { Error::RandError(ref err) => Some(err), Error::UnexpectedError => None, Error::Unknown(_) => None, + Error::Utf8Error(ref err) => Some(err), } } } @@ -80,6 +90,7 @@ impl fmt::Display for Error { 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), + Error::Utf8Error(ref err) => write!(f, "UTF-8 error: {}", err), } } } -- cgit v1.2.3 From b00bbaa5603504597729ed2ce0d1e8ff50ea078d Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 23 Jan 2019 04:36:57 +0000 Subject: Implement From<(T: Device, Error)> for Error Not all users of the authenticate methods want to use the device after an error, so implementing From<(T: Device, Error)> for Error makes it easier for them to discard the device. --- CHANGELOG.md | 1 + TODO.md | 1 - src/error.rs | 8 ++++++++ 3 files changed, 9 insertions(+), 1 deletion(-) (limited to 'src/error.rs') diff --git a/CHANGELOG.md b/CHANGELOG.md index 70bd7cb..c28c228 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - Add `Error::Utf8Error` variant. - Return `Result` instead of `Version` from `get_library_version`. - Return `Error::Utf8Error` if libnitrokey returns an invalid UTF-8 string. +- Implement `From<(T: Device, Error)>` for `Error`. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/TODO.md b/TODO.md index 0467f1d..49e4e08 100644 --- a/TODO.md +++ b/TODO.md @@ -8,7 +8,6 @@ - Fix timing issues with the `totp_no_pin` and `totp_pin` test cases. - Clear passwords from memory. - Find a nicer syntax for the `write_config` test. -- Consider implementing `Into` for `(Device, CommandError)` - Lock password safe in `PasswordSafe::drop()` (see [nitrokey-storage-firmware issue 65][]). - Disable creation of multiple password safes at the same time. diff --git a/src/error.rs b/src/error.rs index 4b82c6e..551dd0f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,6 +4,8 @@ use std::os::raw; use std::result; use std::str; +use crate::device; + /// An error returned by the nitrokey crate. #[derive(Debug)] pub enum Error { @@ -67,6 +69,12 @@ impl From for Error { } } +impl From<(T, Error)> for Error { + fn from((_, err): (T, Error)) -> Self { + err + } +} + impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match *self { -- cgit v1.2.3 From fdb7bac3063e62776bfc13f184cf786da19f42d1 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 23 Jan 2019 16:33:26 +0100 Subject: Add license and copyright information This patch adds license and copyright information to all files to make nitrokey-rs compliant with the REUSE practices [0]. [0] https://reuse.software/practices/2.0/ --- .builds/archlinux-use-system-lib.yaml | 2 ++ .builds/archlinux.yml | 2 ++ CHANGELOG.md | 5 +++++ Cargo.toml | 3 +++ LICENSE | 3 +++ README.md | 8 ++++++++ TODO.md | 5 +++++ src/auth.rs | 3 +++ src/config.rs | 3 +++ src/device.rs | 3 +++ src/error.rs | 3 +++ src/lib.rs | 3 +++ src/otp.rs | 3 +++ src/pws.rs | 3 +++ src/util.rs | 3 +++ tests/device.rs | 3 +++ tests/lib.rs | 3 +++ tests/otp.rs | 3 +++ tests/pws.rs | 3 +++ tests/util/mod.rs | 3 +++ 20 files changed, 67 insertions(+) (limited to 'src/error.rs') diff --git a/.builds/archlinux-use-system-lib.yaml b/.builds/archlinux-use-system-lib.yaml index 6fba33a..13f7581 100644 --- a/.builds/archlinux-use-system-lib.yaml +++ b/.builds/archlinux-use-system-lib.yaml @@ -1,3 +1,5 @@ +# Copyright (C) 2019 Robin Krahl +# SPDX-License-Identifier: MIT image: archlinux packages: - rust diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index 9d45386..7dab954 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -1,3 +1,5 @@ +# Copyright (C) 2019 Robin Krahl +# SPDX-License-Identifier: MIT image: archlinux packages: - rust diff --git a/CHANGELOG.md b/CHANGELOG.md index c28c228..49ff8fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ + + # Unreleased - Remove the `test-pro` and `test-storage` features. - Implement `Display` for `Version`. diff --git a/Cargo.toml b/Cargo.toml index ceaa57d..696adfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,6 @@ +# Copyright (C) 2019 Robin Krahl +# SPDX-License-Identifier: MIT + [package] name = "nitrokey" version = "0.3.4" diff --git a/LICENSE b/LICENSE index 1a3601d..6c67cd5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,6 @@ +Valid-License-Identifier: MIT +License-Text: + The MIT License (MIT) Copyright (c) 2018 Robin Krahl diff --git a/README.md b/README.md index 0819c9d..8e1d98e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ + + # nitrokey-rs A libnitrokey wrapper for Rust providing access to Nitrokey devices. @@ -73,6 +78,8 @@ mail to [nitrokey-rs-dev@ireas.org][]. This project is licensed under the [MIT License][]. `libnitrokey` is licensed under the [LGPL-3.0][]. +`nitrokey-rs` complies with [version 2.0 of the REUSE practices][reuse]. + [Documentation]: https://docs.rs/nitrokey [Nitrokey udev rules]: https://www.nitrokey.com/documentation/frequently-asked-questions-faq#openpgp-card-not-available [`libnitrokey`]: https://github.com/nitrokey/libnitrokey @@ -81,3 +88,4 @@ under the [LGPL-3.0][]. [pull request #114]: https://github.com/Nitrokey/libnitrokey/pull/114 [MIT license]: https://opensource.org/licenses/MIT [LGPL-3.0]: https://opensource.org/licenses/lgpl-3.0.html +[reuse]: https://reuse.software/practices/2.0/ diff --git a/TODO.md b/TODO.md index d3f0018..db45bb5 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,8 @@ + + - Add support for the currently unsupported commands: - `NK_send_startup` - `NK_fill_SD_card_with_random_data` diff --git a/src/auth.rs b/src/auth.rs index b97bee6..18b6572 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + use std::ops::Deref; use std::os::raw::c_char; use std::os::raw::c_int; diff --git a/src/config.rs b/src/config.rs index 329f7a6..c273792 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + use crate::error::{Error, LibraryError}; /// The configuration for a Nitrokey. diff --git a/src/device.rs b/src/device.rs index ad75a44..c4af8a8 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + use std::fmt; use std::marker; diff --git a/src/error.rs b/src/error.rs index 551dd0f..9cdb932 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2019 Robin Krahl +// SPDX-License-Identifier: MIT + use std::error; use std::fmt; use std::os::raw; diff --git a/src/lib.rs b/src/lib.rs index a1edb6b..9d15d03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + //! Provides access to a Nitrokey device using the native libnitrokey API. //! //! # Usage diff --git a/src/otp.rs b/src/otp.rs index 430b127..6e0379b 100644 --- a/src/otp.rs +++ b/src/otp.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + use std::ffi::CString; use nitrokey_sys; diff --git a/src/pws.rs b/src/pws.rs index c89b73f..fcf057b 100644 --- a/src/pws.rs +++ b/src/pws.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + use libc; use nitrokey_sys; diff --git a/src/util.rs b/src/util.rs index 64dde39..5f25655 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_int}; diff --git a/tests/device.rs b/tests/device.rs index 59c9348..c790049 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + mod util; use std::ffi::CStr; diff --git a/tests/lib.rs b/tests/lib.rs index d298048..697024d 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2019 Robin Krahl +// SPDX-License-Identifier: MIT + #[test] fn get_library_version() { let version = nitrokey::get_library_version().unwrap(); diff --git a/tests/otp.rs b/tests/otp.rs index 96da371..fb20768 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + mod util; use std::fmt::Debug; diff --git a/tests/pws.rs b/tests/pws.rs index 8bdf532..df99e1c 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + mod util; use std::ffi::CStr; diff --git a/tests/util/mod.rs b/tests/util/mod.rs index 4a00a66..49ec13e 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + pub static ADMIN_PASSWORD: &str = "12345678"; pub static USER_PASSWORD: &str = "123456"; -- cgit v1.2.3 From 809d31a4273505487febb2dd281376d2bb3766ab Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Fri, 25 Jan 2019 18:46:57 +0000 Subject: Remove rand_core::Error from public API rand_core does not have a stable release yet, and it is unlikely that there will be one soon. To be able to stabilize nitrokey without waiting for a stable rand_core version, we remove the rand_core::Error type from the public API and replace it with a Box. --- src/error.rs | 10 ++-------- src/util.rs | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) (limited to 'src/error.rs') diff --git a/src/error.rs b/src/error.rs index 9cdb932..1b36975 100644 --- a/src/error.rs +++ b/src/error.rs @@ -19,7 +19,7 @@ pub enum Error { /// A library usage error. LibraryError(LibraryError), /// An error that occured during random number generation. - RandError(rand_core::Error), + RandError(Box), /// An error that is caused by an unexpected value returned by libnitrokey. UnexpectedError, /// An unknown error returned by libnitrokey. @@ -60,12 +60,6 @@ impl From for Error { } } -impl From for Error { - fn from(error: rand_core::Error) -> Self { - Error::RandError(error) - } -} - impl From for Error { fn from(error: str::Utf8Error) -> Self { Error::Utf8Error(error) @@ -84,7 +78,7 @@ impl error::Error for Error { 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::RandError(ref err) => Some(err.as_ref()), Error::UnexpectedError => None, Error::Unknown(_) => None, Error::Utf8Error(ref err) => Some(err), diff --git a/src/util.rs b/src/util.rs index 5f25655..d87cd7c 100644 --- a/src/util.rs +++ b/src/util.rs @@ -73,7 +73,7 @@ pub fn get_last_error() -> Error { } pub fn generate_password(length: usize) -> Result, Error> { - let mut rng = OsRng::new()?; + let mut rng = OsRng::new().map_err(|err| Error::RandError(Box::new(err)))?; let mut data = vec![0u8; length]; rng.fill_bytes(&mut data[..]); Ok(data) -- cgit v1.2.3 From a30562638aed90d113739bb36dd6814f6cf7ace2 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 13:31:36 +0000 Subject: Remove the Result typedef Many of our functions do not return a Result<_, Error>, but for example a Result<_, (Device, Error)>. We only use the typedef in one function, but it makes the other functions more complicated as we have to use result::Result (if crate::Result is imported). Therefore, this patch removes the typedef. Applications or libraries can still redefine it if they want to. --- CHANGELOG.md | 2 +- src/error.rs | 4 ---- src/lib.rs | 4 ++-- 3 files changed, 3 insertions(+), 7 deletions(-) (limited to 'src/error.rs') diff --git a/CHANGELOG.md b/CHANGELOG.md index b9983ed..271230d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ SPDX-License-Identifier: MIT - 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 `Error` enum. - Add the `LibraryError` enum and move the library error variants from `CommandError` to `LibraryError`. - Add the `CommunicationError` enum and move the communication error variants diff --git a/src/error.rs b/src/error.rs index 1b36975..5c8c52e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,7 +4,6 @@ use std::error; use std::fmt; use std::os::raw; -use std::result; use std::str; use crate::device; @@ -100,9 +99,6 @@ impl fmt::Display for Error { } } -/// A result returned by the nitrokey crate. -pub type Result = result::Result; - /// An error reported by the Nitrokey device in the response packet. #[derive(Clone, Copy, Debug, PartialEq)] pub enum CommandError { diff --git a/src/lib.rs b/src/lib.rs index 9d15d03..18c86e2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,7 +107,7 @@ 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::error::{CommandError, CommunicationError, Error, LibraryError}; pub use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData}; pub use crate::pws::{GetPasswordSafe, PasswordSafe, SLOT_COUNT}; pub use crate::util::LogLevel; @@ -186,7 +186,7 @@ pub fn set_log_level(level: LogLevel) { /// ``` /// /// [`Utf8Error`]: enum.Error.html#variant.Utf8Error -pub fn get_library_version() -> Result { +pub fn get_library_version() -> Result { // NK_get_library_version returns a static string, so we don’t have to free the pointer. let git = unsafe { nitrokey_sys::NK_get_library_version() }; let git = if git.is_null() { -- cgit v1.2.3 From 33a65c1635e54ae51089ef3c37a749d67853be02 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 13:36:13 +0000 Subject: Rename Error::Unknown to Error::UnknownError For consistency with the other Error variants, we rename Unknown to UnknownError. --- CHANGELOG.md | 2 +- src/error.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/error.rs') diff --git a/CHANGELOG.md b/CHANGELOG.md index 271230d..24c79af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ SPDX-License-Identifier: MIT 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`. + `CommandError::Unknown` variant to `Error::UnknownError`. - Return `CommunicationError::NotConnected` instead of `CommandError::Undefined` from the connect functions. - Remove the `CommandError::Undefined` variant. diff --git a/src/error.rs b/src/error.rs index 5c8c52e..1730171 100644 --- a/src/error.rs +++ b/src/error.rs @@ -22,7 +22,7 @@ pub enum Error { /// An error that is caused by an unexpected value returned by libnitrokey. UnexpectedError, /// An unknown error returned by libnitrokey. - Unknown(i64), + UnknownError(i64), /// An error occurred when interpreting a UTF-8 string. Utf8Error(str::Utf8Error), } @@ -36,7 +36,7 @@ impl From for Error { } else if let Some(err) = LibraryError::try_from(code) { Error::LibraryError(err) } else { - Error::Unknown(code.into()) + Error::UnknownError(code.into()) } } } @@ -79,7 +79,7 @@ impl error::Error for Error { Error::LibraryError(ref err) => Some(err), Error::RandError(ref err) => Some(err.as_ref()), Error::UnexpectedError => None, - Error::Unknown(_) => None, + Error::UnknownError(_) => None, Error::Utf8Error(ref err) => Some(err), } } @@ -93,7 +93,7 @@ impl fmt::Display for Error { 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), + Error::UnknownError(ref err) => write!(f, "Unknown error: {}", err), Error::Utf8Error(ref err) => write!(f, "UTF-8 error: {}", err), } } -- cgit v1.2.3 From a52676d9577f587e0f4d8e47ddc71ba34f0b31ca Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 15:04:19 +0000 Subject: Add ConcurrentAccessError and PoisonError variants This patch prepares the refactoring of the connection methods by introducing the Error variants ConcurrentAccessError and PoisonError. ConcurrentAccessError indicates that the user tried to connect to obtain a token that is currently locked, and PoisonError indicates that a lock has been poisoned, i. e. a thread panicked while accessing using a token. --- CHANGELOG.md | 2 ++ src/error.rs | 28 ++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) (limited to 'src/error.rs') diff --git a/CHANGELOG.md b/CHANGELOG.md index 7623a33..046b609 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,8 @@ SPDX-License-Identifier: MIT - Update the `nitrokey-sys` dependency to version 3.5.0. - Update the `nitrokey-test` dependency to version 0.2.1 and add the `nitrokey-test-state` dependency in version 0.1.0. +- Refactor connection management: + - Add `ConcurrentAccessError` and `PoisonError` `Error` variants. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/error.rs b/src/error.rs index 1730171..c6b19db 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,6 +5,7 @@ use std::error; use std::fmt; use std::os::raw; use std::str; +use std::sync; use crate::device; @@ -13,11 +14,15 @@ use crate::device; pub enum Error { /// An error reported by the Nitrokey device in the response packet. CommandError(CommandError), - /// A device communication. + /// A device communication error. CommunicationError(CommunicationError), + /// An error occurred due to concurrent access to the Nitrokey device. + ConcurrentAccessError, /// A library usage error. LibraryError(LibraryError), - /// An error that occured during random number generation. + /// An error that occurred due to a poisoned lock. + PoisonError, + /// An error that occurred during random number generation. RandError(Box), /// An error that is caused by an unexpected value returned by libnitrokey. UnexpectedError, @@ -65,6 +70,21 @@ impl From for Error { } } +impl From> for Error { + fn from(_error: sync::PoisonError) -> Self { + Error::PoisonError + } +} + +impl From> for Error { + fn from(error: sync::TryLockError) -> Self { + match error { + sync::TryLockError::Poisoned(err) => err.into(), + sync::TryLockError::WouldBlock => Error::ConcurrentAccessError, + } + } +} + impl From<(T, Error)> for Error { fn from((_, err): (T, Error)) -> Self { err @@ -76,7 +96,9 @@ impl error::Error for Error { match *self { Error::CommandError(ref err) => Some(err), Error::CommunicationError(ref err) => Some(err), + Error::ConcurrentAccessError => None, Error::LibraryError(ref err) => Some(err), + Error::PoisonError => None, Error::RandError(ref err) => Some(err.as_ref()), Error::UnexpectedError => None, Error::UnknownError(_) => None, @@ -90,7 +112,9 @@ impl fmt::Display for Error { match *self { Error::CommandError(ref err) => write!(f, "Command error: {}", err), Error::CommunicationError(ref err) => write!(f, "Communication error: {}", err), + Error::ConcurrentAccessError => write!(f, "Internal error: concurrent access"), Error::LibraryError(ref err) => write!(f, "Library error: {}", err), + Error::PoisonError => write!(f, "Internal error: poisoned lock"), Error::RandError(ref err) => write!(f, "RNG error: {}", err), Error::UnexpectedError => write!(f, "An unexpected error occurred"), Error::UnknownError(ref err) => write!(f, "Unknown error: {}", err), -- cgit v1.2.3 From 588066f415e956fdcd2c6f6216c52b25911a3b1d Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 15:43:32 +0000 Subject: Add Manager struct to manage Nitrokey connections As part of the connection refactoring, we introduce the Manager struct that deals with connection management. To make sure there can be only once instance of the manager, we add a global static Mutex that holds the single Manager instance. We use the struct to ensure that the user can only connect to one device at a time. This also changes the Error::PoisonError variant to store the sync::PoisonError. This allows the user to call into_inner on the PoisonError to retrieve the MutexGuard and to ignore the error (for example useful during testing). --- CHANGELOG.md | 1 + Cargo.toml | 1 + src/error.rs | 16 +++++++-------- src/lib.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/lib.rs | 16 +++++++++++++++ 5 files changed, 92 insertions(+), 8 deletions(-) (limited to 'src/error.rs') diff --git a/CHANGELOG.md b/CHANGELOG.md index 046b609..e67fd81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ SPDX-License-Identifier: MIT `nitrokey-test-state` dependency in version 0.1.0. - Refactor connection management: - Add `ConcurrentAccessError` and `PoisonError` `Error` variants. + - Add the `Manager` struct that manages connections to Nitrokey devices. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/Cargo.toml b/Cargo.toml index d26bd88..7daadd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ license = "MIT" exclude = [".builds/*"] [dependencies] +lazy_static = "1.2.0" libc = "0.2" nitrokey-sys = "3.5" rand_core = {version = "0.3", default-features = false, features = ["std"] } diff --git a/src/error.rs b/src/error.rs index c6b19db..b84f5eb 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,7 +21,7 @@ pub enum Error { /// A library usage error. LibraryError(LibraryError), /// An error that occurred due to a poisoned lock. - PoisonError, + PoisonError(sync::PoisonError>), /// An error that occurred during random number generation. RandError(Box), /// An error that is caused by an unexpected value returned by libnitrokey. @@ -70,14 +70,14 @@ impl From for Error { } } -impl From> for Error { - fn from(_error: sync::PoisonError) -> Self { - Error::PoisonError +impl From>> for Error { + fn from(error: sync::PoisonError>) -> Self { + Error::PoisonError(error) } } -impl From> for Error { - fn from(error: sync::TryLockError) -> Self { +impl From>> for Error { + fn from(error: sync::TryLockError>) -> Self { match error { sync::TryLockError::Poisoned(err) => err.into(), sync::TryLockError::WouldBlock => Error::ConcurrentAccessError, @@ -98,7 +98,7 @@ impl error::Error for Error { Error::CommunicationError(ref err) => Some(err), Error::ConcurrentAccessError => None, Error::LibraryError(ref err) => Some(err), - Error::PoisonError => None, + Error::PoisonError(ref err) => Some(err), Error::RandError(ref err) => Some(err.as_ref()), Error::UnexpectedError => None, Error::UnknownError(_) => None, @@ -114,7 +114,7 @@ impl fmt::Display for Error { Error::CommunicationError(ref err) => write!(f, "Communication error: {}", err), Error::ConcurrentAccessError => write!(f, "Internal error: concurrent access"), Error::LibraryError(ref err) => write!(f, "Library error: {}", err), - Error::PoisonError => write!(f, "Internal error: poisoned lock"), + Error::PoisonError(_) => write!(f, "Internal error: poisoned lock"), Error::RandError(ref err) => write!(f, "RNG error: {}", err), Error::UnexpectedError => write!(f, "An unexpected error occurred"), Error::UnknownError(ref err) => write!(f, "Unknown error: {}", err), diff --git a/src/lib.rs b/src/lib.rs index c35829c..573f45f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,6 +89,9 @@ #![warn(missing_docs, rust_2018_compatibility, rust_2018_idioms, unused)] +#[macro_use(lazy_static)] +extern crate lazy_static; + mod auth; mod config; mod device; @@ -98,6 +101,8 @@ mod pws; mod util; use std::fmt; +use std::marker; +use std::sync; use nitrokey_sys; @@ -117,6 +122,10 @@ pub const DEFAULT_ADMIN_PIN: &str = "12345678"; /// The default user PIN for all Nitrokey devices. pub const DEFAULT_USER_PIN: &str = "123456"; +lazy_static! { + static ref MANAGER: sync::Mutex = sync::Mutex::new(Manager::new()); +} + /// A version of the libnitrokey library. /// /// Use the [`get_library_version`](fn.get_library_version.html) function to query the library @@ -147,6 +156,63 @@ impl fmt::Display for Version { } } +/// A manager for connections to Nitrokey devices. +/// +/// Currently, libnitrokey only provides access to one Nitrokey device at the same time. This +/// manager struct makes sure that `nitrokey-rs` does not try to connect to two devices at the same +/// time. +/// +/// To obtain an instance of this manager, use the [`take`][] function. +/// +/// [`take`]: fn.take.html +#[derive(Debug)] +pub struct Manager { + marker: marker::PhantomData<()>, +} + +impl Manager { + fn new() -> Self { + Manager { + marker: marker::PhantomData, + } + } +} + +/// Take an instance of the connection manager, blocking until an instance is available. +/// +/// There may only be one [`Manager`][] instance at the same time. If there already is an +/// instance, this method blocks. If you want a non-blocking version, use [`take`][]. +/// +/// # Errors +/// +/// - [`PoisonError`][] if the lock is poisoned +/// +/// [`take`]: fn.take.html +/// [`PoisonError`]: struct.Error.html#variant.PoisonError +/// [`Manager`]: struct.Manager.html +pub fn take_blocking() -> Result, Error> { + MANAGER.lock().map_err(Into::into) +} + +/// Try to take an instance of the connection manager. +/// +/// There may only be one [`Manager`][] instance at the same time. If there already is an +/// instance, a [`ConcurrentAccessError`][] is returned. If you want a blocking version, use +/// [`take_blocking`][]. +/// +/// # Errors +/// +/// - [`ConcurrentAccessError`][] if the token for the `Manager` instance cannot be locked +/// - [`PoisonError`][] if the lock is poisoned +/// +/// [`take_blocking`]: fn.take_blocking.html +/// [`ConcurrentAccessError`]: struct.Error.html#variant.ConcurrentAccessError +/// [`PoisonError`]: struct.Error.html#variant.PoisonError +/// [`Manager`]: struct.Manager.html +pub fn take() -> Result, Error> { + MANAGER.try_lock().map_err(Into::into) +} + /// Enables or disables debug output. Calling this method with `true` is equivalent to setting the /// log level to `Debug`; calling it with `false` is equivalent to the log level `Error` (see /// [`set_log_level`][]). diff --git a/tests/lib.rs b/tests/lib.rs index 8ab75f6..25aae0f 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -10,3 +10,19 @@ fn get_library_version() { assert!(version.git.is_empty() || version.git.starts_with("v")); assert!(version.major > 0); } + +#[test] +fn take_manager() { + assert!(nitrokey::take().is_ok()); + + let result = nitrokey::take(); + assert!(result.is_ok()); + let result2 = nitrokey::take(); + match result2 { + Ok(_) => panic!("Expected error, got Ok(_)!"), + Err(nitrokey::Error::ConcurrentAccessError) => {} + Err(err) => panic!("Expected ConcurrentAccessError, got {}", err), + } + drop(result); + assert!(nitrokey::take().is_ok()); +} -- cgit v1.2.3 From 12fa62483cf45d868099d5d4020333af492eebde Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 9 Jul 2019 08:09:02 +0000 Subject: Introduce into_manager for Device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To enable applications like nitrokey-test to go back to a manager instance from a Device instance, we add the into_manager function to the Device trait. To do that, we have to keep track of the Manager’s lifetime by adding a lifetime to Device (and then to some other traits that use Device). --- CHANGELOG.md | 1 + src/auth.rs | 69 +++++++++++++++++++++++++++++++-------------------------- src/device.rs | 64 +++++++++++++++++++++++++++++++++++++++++++--------- src/error.rs | 2 +- src/pws.rs | 30 ++++++++++++------------- tests/device.rs | 16 ++++++++----- tests/otp.rs | 4 ++-- tests/pws.rs | 4 ++-- 8 files changed, 123 insertions(+), 67 deletions(-) (limited to 'src/error.rs') diff --git a/CHANGELOG.md b/CHANGELOG.md index 88e68dd..06769bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ SPDX-License-Identifier: MIT - Add `ConcurrentAccessError` and `PoisonError` `Error` variants. - Add the `Manager` struct that manages connections to Nitrokey devices. - Remove `connect`, `connect_model`, `Pro::connect` and `Storage::connect`. + - Add the `into_manager` function to the `Device` trait. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/auth.rs b/src/auth.rs index 2ed7bfc..829d083 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,6 +1,7 @@ // Copyright (C) 2018-2019 Robin Krahl // SPDX-License-Identifier: MIT +use std::marker; use std::ops; use std::os::raw::c_char; use std::os::raw::c_int; @@ -18,7 +19,7 @@ static TEMPORARY_PASSWORD_LENGTH: usize = 25; /// Provides methods to authenticate as a user or as an admin using a PIN. The authenticated /// methods will consume the current device instance. On success, they return the authenticated /// device. Otherwise, they return the current unauthenticated device and the error code. -pub trait Authenticate { +pub trait Authenticate<'a> { /// Performs user authentication. This method consumes the device. If successful, an /// authenticated device is returned. Otherwise, the current unauthenticated device and the /// error are returned. @@ -61,9 +62,9 @@ pub trait Authenticate { /// [`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, (Self, Error)> + fn authenticate_user(self, password: &str) -> Result, (Self, Error)> where - Self: Device + Sized; + Self: Device<'a> + Sized; /// Performs admin authentication. This method consumes the device. If successful, an /// authenticated device is returned. Otherwise, the current unauthenticated device and the @@ -107,9 +108,9 @@ pub trait Authenticate { /// [`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, (Self, Error)> + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> where - Self: Device + Sized; + Self: Device<'a> + Sized; } trait AuthenticatedDevice { @@ -128,9 +129,10 @@ trait AuthenticatedDevice { /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin /// [`device`]: #method.device #[derive(Debug)] -pub struct User { +pub struct User<'a, T: Device<'a>> { device: T, temp_password: Vec, + marker: marker::PhantomData<&'a T>, } /// A Nitrokey device with admin authentication. @@ -143,14 +145,15 @@ pub struct User { /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin /// [`device`]: #method.device #[derive(Debug)] -pub struct Admin { +pub struct Admin<'a, T: Device<'a>> { device: T, temp_password: Vec, + marker: marker::PhantomData<&'a T>, } -fn authenticate(device: D, password: &str, callback: T) -> Result +fn authenticate<'a, D, A, T>(device: D, password: &str, callback: T) -> Result where - D: Device, + D: Device<'a>, A: AuthenticatedDevice, T: Fn(*const c_char, *const c_char) -> c_int, { @@ -174,9 +177,9 @@ fn authenticate_user_wrapper<'a, T, C>( device: T, constructor: C, password: &str, -) -> Result>, (DeviceWrapper<'a>, Error)> +) -> Result>, (DeviceWrapper<'a>, Error)> where - T: Device, + T: Device<'a> + 'a, C: Fn(T) -> DeviceWrapper<'a>, { let result = device.authenticate_user(password); @@ -190,9 +193,9 @@ fn authenticate_admin_wrapper<'a, T, C>( device: T, constructor: C, password: &str, -) -> Result>, (DeviceWrapper<'a>, Error)> +) -> Result>, (DeviceWrapper<'a>, Error)> where - T: Device, + T: Device<'a> + 'a, C: Fn(T) -> DeviceWrapper<'a>, { let result = device.authenticate_admin(password); @@ -202,7 +205,7 @@ where } } -impl User { +impl<'a, T: Device<'a>> User<'a, T> { /// Forgets the user authentication and returns an unauthenticated device. This method /// consumes the authenticated device. It does not perform any actual commands on the /// Nitrokey. @@ -211,7 +214,7 @@ impl User { } } -impl ops::Deref for User { +impl<'a, T: Device<'a>> ops::Deref for User<'a, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -219,13 +222,13 @@ impl ops::Deref for User { } } -impl ops::DerefMut for User { +impl<'a, T: Device<'a>> ops::DerefMut for User<'a, T> { fn deref_mut(&mut self) -> &mut T { &mut self.device } } -impl GenerateOtp for User { +impl<'a, T: Device<'a>> GenerateOtp for User<'a, T> { fn get_hotp_code(&mut self, slot: u8) -> Result { result_from_string(unsafe { nitrokey_sys::NK_get_hotp_code_PIN(slot, self.temp_password_ptr()) @@ -239,11 +242,12 @@ impl GenerateOtp for User { } } -impl AuthenticatedDevice for User { +impl<'a, T: Device<'a>> AuthenticatedDevice for User<'a, T> { fn new(device: T, temp_password: Vec) -> Self { User { device, temp_password, + marker: marker::PhantomData, } } @@ -252,7 +256,7 @@ impl AuthenticatedDevice for User { } } -impl ops::Deref for Admin { +impl<'a, T: Device<'a>> ops::Deref for Admin<'a, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -260,13 +264,13 @@ impl ops::Deref for Admin { } } -impl ops::DerefMut for Admin { +impl<'a, T: Device<'a>> ops::DerefMut for Admin<'a, T> { fn deref_mut(&mut self) -> &mut T { &mut self.device } } -impl Admin { +impl<'a, T: Device<'a>> Admin<'a, T> { /// Forgets the user authentication and returns an unauthenticated device. This method /// consumes the authenticated device. It does not perform any actual commands on the /// Nitrokey. @@ -316,7 +320,7 @@ impl Admin { } } -impl ConfigureOtp for Admin { +impl<'a, T: Device<'a>> ConfigureOtp for Admin<'a, T> { fn write_hotp_slot(&mut self, data: OtpSlotData, counter: u64) -> Result<(), Error> { let raw_data = RawOtpSlotData::new(data)?; get_command_result(unsafe { @@ -364,11 +368,12 @@ impl ConfigureOtp for Admin { } } -impl AuthenticatedDevice for Admin { +impl<'a, T: Device<'a>> AuthenticatedDevice for Admin<'a, T> { fn new(device: T, temp_password: Vec) -> Self { Admin { device, temp_password, + marker: marker::PhantomData, } } @@ -377,8 +382,8 @@ impl AuthenticatedDevice for Admin { } } -impl<'a> Authenticate for DeviceWrapper<'a> { - fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { +impl<'a> Authenticate<'a> for DeviceWrapper<'a> { + fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { match self { DeviceWrapper::Storage(storage) => { authenticate_user_wrapper(storage, DeviceWrapper::Storage, password) @@ -387,7 +392,7 @@ impl<'a> Authenticate for DeviceWrapper<'a> { } } - fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { match self { DeviceWrapper::Storage(storage) => { authenticate_admin_wrapper(storage, DeviceWrapper::Storage, password) @@ -399,28 +404,28 @@ impl<'a> Authenticate for DeviceWrapper<'a> { } } -impl<'a> Authenticate for Pro<'a> { - fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { +impl<'a> Authenticate<'a> for Pro<'a> { + fn authenticate_user(self, password: &str) -> Result, (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, (Self, Error)> { + fn authenticate_admin(self, password: &str) -> Result, (Self, Error)> { authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr) }) } } -impl<'a> Authenticate for Storage<'a> { - fn authenticate_user(self, password: &str) -> Result, (Self, Error)> { +impl<'a> Authenticate<'a> for Storage<'a> { + fn authenticate_user(self, password: &str) -> Result, (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, (Self, Error)> { + fn authenticate_admin(self, password: &str) -> Result, (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/device.rs b/src/device.rs index 50ff071..e1a71fa 100644 --- a/src/device.rs +++ b/src/device.rs @@ -156,7 +156,7 @@ pub enum DeviceWrapper<'a> { /// [`Pro::connect`]: #method.connect #[derive(Debug)] pub struct Pro<'a> { - manager: &'a mut crate::Manager, + manager: Option<&'a mut crate::Manager>, } /// A Nitrokey Storage device without user or admin authentication. @@ -200,7 +200,7 @@ pub struct Pro<'a> { /// [`Storage::connect`]: #method.connect #[derive(Debug)] pub struct Storage<'a> { - manager: &'a mut crate::Manager, + manager: Option<&'a mut crate::Manager>, } /// The status of a volume on a Nitrokey Storage device. @@ -291,7 +291,32 @@ 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 + fmt::Debug { +pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt::Debug { + /// Returns the [`Manager`][] instance that has been used to connect to this device. + /// + /// # Example + /// + /// ``` + /// use nitrokey::{Device, DeviceWrapper}; + /// + /// fn do_something(device: DeviceWrapper) { + /// // reconnect to any device + /// let manager = device.into_manager(); + /// let device = manager.connect(); + /// // do something with the device + /// // ... + /// } + /// + /// # fn main() -> Result<(), nitrokey::Error> { + /// match nitrokey::take()?.connect() { + /// Ok(device) => do_something(device), + /// Err(err) => println!("Could not connect to a Nitrokey: {}", err), + /// } + /// # Ok(()) + /// # } + /// ``` + fn into_manager(self) -> &'a mut crate::Manager; + /// Returns the model of the connected Nitrokey device. /// /// # Example @@ -657,14 +682,14 @@ pub(crate) fn connect_enum(model: Model) -> bool { } impl<'a> DeviceWrapper<'a> { - fn device(&self) -> &dyn Device { + fn device(&self) -> &dyn Device<'a> { match *self { DeviceWrapper::Storage(ref storage) => storage, DeviceWrapper::Pro(ref pro) => pro, } } - fn device_mut(&mut self) -> &mut dyn Device { + fn device_mut(&mut self) -> &mut dyn Device<'a> { match *self { DeviceWrapper::Storage(ref mut storage) => storage, DeviceWrapper::Pro(ref mut pro) => pro, @@ -702,7 +727,14 @@ impl<'a> GenerateOtp for DeviceWrapper<'a> { } } -impl<'a> Device for DeviceWrapper<'a> { +impl<'a> Device<'a> for DeviceWrapper<'a> { + fn into_manager(self) -> &'a mut crate::Manager { + match self { + DeviceWrapper::Pro(dev) => dev.into_manager(), + DeviceWrapper::Storage(dev) => dev.into_manager(), + } + } + fn get_model(&self) -> Model { match *self { DeviceWrapper::Pro(_) => Model::Pro, @@ -713,7 +745,9 @@ impl<'a> Device for DeviceWrapper<'a> { impl<'a> Pro<'a> { pub(crate) fn new(manager: &'a mut crate::Manager) -> Pro<'a> { - Pro { manager } + Pro { + manager: Some(manager), + } } } @@ -725,7 +759,11 @@ impl<'a> Drop for Pro<'a> { } } -impl<'a> Device for Pro<'a> { +impl<'a> Device<'a> for Pro<'a> { + fn into_manager(mut self) -> &'a mut crate::Manager { + self.manager.take().unwrap() + } + fn get_model(&self) -> Model { Model::Pro } @@ -735,7 +773,9 @@ impl<'a> GenerateOtp for Pro<'a> {} impl<'a> Storage<'a> { pub(crate) fn new(manager: &'a mut crate::Manager) -> Storage<'a> { - Storage { manager } + Storage { + manager: Some(manager), + } } /// Changes the update PIN. @@ -1248,7 +1288,11 @@ impl<'a> Drop for Storage<'a> { } } -impl<'a> Device for Storage<'a> { +impl<'a> Device<'a> for Storage<'a> { + fn into_manager(mut self) -> &'a mut crate::Manager { + self.manager.take().unwrap() + } + fn get_model(&self) -> Model { Model::Storage } diff --git a/src/error.rs b/src/error.rs index b84f5eb..9e6adc0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -85,7 +85,7 @@ impl From>> for Err } } -impl From<(T, Error)> for Error { +impl<'a, T: device::Device<'a>> From<(T, Error)> for Error { fn from((_, err): (T, Error)) -> Self { err } diff --git a/src/pws.rs b/src/pws.rs index cf2dd42..778765d 100644 --- a/src/pws.rs +++ b/src/pws.rs @@ -57,8 +57,8 @@ pub const SLOT_COUNT: u8 = 16; /// [`lock`]: trait.Device.html#method.lock /// [`GetPasswordSafe`]: trait.GetPasswordSafe.html #[derive(Debug)] -pub struct PasswordSafe<'a> { - _device: &'a dyn Device, +pub struct PasswordSafe<'a, 'b> { + _device: &'a dyn Device<'b>, } /// Provides access to a [`PasswordSafe`][]. @@ -67,7 +67,7 @@ pub struct PasswordSafe<'a> { /// retrieved from it. /// /// [`PasswordSafe`]: struct.PasswordSafe.html -pub trait GetPasswordSafe { +pub trait GetPasswordSafe<'a> { /// Enables and returns the password safe. /// /// The underlying device must always live at least as long as a password safe retrieved from @@ -117,13 +117,13 @@ pub trait GetPasswordSafe { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`Unknown`]: enum.CommandError.html#variant.Unknown /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn get_password_safe(&mut self, user_pin: &str) -> Result, Error>; + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error>; } -fn get_password_safe<'a>( - device: &'a dyn Device, +fn get_password_safe<'a, 'b>( + device: &'a dyn Device<'b>, user_pin: &str, -) -> Result, Error> { +) -> Result, Error> { let user_pin_string = get_cstring(user_pin)?; get_command_result(unsafe { nitrokey_sys::NK_enable_password_safe(user_pin_string.as_ptr()) }) .map(|_| PasswordSafe { _device: device }) @@ -137,7 +137,7 @@ fn get_pws_result(s: String) -> Result { } } -impl<'a> PasswordSafe<'a> { +impl<'a, 'b> PasswordSafe<'a, 'b> { /// Returns the status of all password slots. /// /// The status indicates whether a slot is programmed or not. @@ -357,27 +357,27 @@ impl<'a> PasswordSafe<'a> { } } -impl<'a> Drop for PasswordSafe<'a> { +impl<'a, 'b> Drop for PasswordSafe<'a, 'b> { fn drop(&mut self) { // TODO: disable the password safe -- NK_lock_device has side effects on the Nitrokey // Storage, see https://github.com/Nitrokey/nitrokey-storage-firmware/issues/65 } } -impl<'a> GetPasswordSafe for Pro<'a> { - fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { +impl<'a> GetPasswordSafe<'a> for Pro<'a> { + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } -impl<'a> GetPasswordSafe for Storage<'a> { - fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { +impl<'a> GetPasswordSafe<'a> for Storage<'a> { + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } -impl<'a> GetPasswordSafe for DeviceWrapper<'a> { - fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { +impl<'a> GetPasswordSafe<'a> for DeviceWrapper<'a> { + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } diff --git a/tests/device.rs b/tests/device.rs index b377f2e..76f38e6 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -101,7 +101,10 @@ fn get_firmware_version(device: Pro) { assert!(version.minor > 0); } -fn admin_retry(device: T, suffix: &str, count: u8) -> T { +fn admin_retry<'a, T>(device: T, suffix: &str, count: u8) -> T +where + T: Authenticate<'a> + Device<'a> + 'a, +{ let result = device.authenticate_admin(&(DEFAULT_ADMIN_PIN.to_owned() + suffix)); let device = match result { Ok(admin) => admin.device(), @@ -111,7 +114,10 @@ fn admin_retry(device: T, suffix: &str, count: u8) -> return device; } -fn user_retry(device: T, suffix: &str, count: u8) -> T { +fn user_retry<'a, T>(device: T, suffix: &str, count: u8) -> T +where + T: Authenticate<'a> + Device<'a> + 'a, +{ let result = device.authenticate_user(&(DEFAULT_USER_PIN.to_owned() + suffix)); let device = match result { Ok(admin) => admin.device(), @@ -220,10 +226,10 @@ fn change_admin_pin(device: DeviceWrapper) { device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err(); } -fn require_failed_user_login(device: D, password: &str, error: CommandError) -> D +fn require_failed_user_login<'a, D>(device: D, password: &str, error: CommandError) -> D where - D: Device + Authenticate, - nitrokey::User: std::fmt::Debug, + D: Device<'a> + Authenticate<'a> + 'a, + nitrokey::User<'a, D>: std::fmt::Debug, { let result = device.authenticate_user(password); assert!(result.is_err()); diff --git a/tests/otp.rs b/tests/otp.rs index c0bbecf..aafda59 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -36,9 +36,9 @@ enum TotpTimestampSize { U64, } -fn make_admin_test_device(device: T) -> Admin +fn make_admin_test_device<'a, T>(device: T) -> Admin<'a, T> where - T: Device, + T: Device<'a>, (T, nitrokey::Error): Debug, { unwrap_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)) diff --git a/tests/pws.rs b/tests/pws.rs index b0e5abe..7169695 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -32,9 +32,9 @@ fn get_slot_name_direct(slot: u8) -> Result { } } -fn get_pws(device: &mut T) -> PasswordSafe +fn get_pws<'a, T>(device: &mut T) -> PasswordSafe<'_, 'a> where - T: Device, + T: Device<'a>, { unwrap_ok!(device.get_password_safe(DEFAULT_USER_PIN)) } -- cgit v1.2.3