diff options
Diffstat (limited to 'nitrokey/src')
| -rw-r--r-- | nitrokey/src/auth.rs | 137 | ||||
| -rw-r--r-- | nitrokey/src/config.rs | 29 | ||||
| -rw-r--r-- | nitrokey/src/device.rs | 457 | ||||
| -rw-r--r-- | nitrokey/src/error.rs | 245 | ||||
| -rw-r--r-- | nitrokey/src/lib.rs | 53 | ||||
| -rw-r--r-- | nitrokey/src/otp.rs | 98 | ||||
| -rw-r--r-- | nitrokey/src/pws.rs | 107 | ||||
| -rw-r--r-- | nitrokey/src/util.rs | 177 | 
8 files changed, 747 insertions, 556 deletions
diff --git a/nitrokey/src/auth.rs b/nitrokey/src/auth.rs index 2d61d4b..18b6572 100644 --- a/nitrokey/src/auth.rs +++ b/nitrokey/src/auth.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org> +// SPDX-License-Identifier: MIT +  use std::ops::Deref;  use std::os::raw::c_char;  use std::os::raw::c_int; @@ -6,10 +9,9 @@ use nitrokey_sys;  use crate::config::{Config, RawConfig};  use crate::device::{Device, DeviceWrapper, Pro, Storage}; +use crate::error::Error;  use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData, RawOtpSlotData}; -use crate::util::{ -    generate_password, get_command_result, get_cstring, result_from_string, CommandError, -}; +use crate::util::{generate_password, get_command_result, get_cstring, result_from_string};  static TEMPORARY_PASSWORD_LENGTH: usize = 25; @@ -34,12 +36,12 @@ pub trait Authenticate {      ///      /// ```no_run      /// use nitrokey::{Authenticate, DeviceWrapper, User}; -    /// # use nitrokey::CommandError; +    /// # use nitrokey::Error;      ///      /// fn perform_user_task(device: &User<DeviceWrapper>) {}      /// fn perform_other_task(device: &DeviceWrapper) {}      /// -    /// # fn try_main() -> Result<(), CommandError> { +    /// # fn try_main() -> Result<(), Error> {      /// let device = nitrokey::connect()?;      /// let device = match device.authenticate_user("123456") {      ///     Ok(user) => { @@ -56,10 +58,10 @@ pub trait Authenticate {      /// # }      /// ```      /// -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`RngError`]: enum.CommandError.html#variant.RngError      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword -    fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, CommandError)> +    fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)>      where          Self: Device + Sized; @@ -80,12 +82,12 @@ pub trait Authenticate {      ///      /// ```no_run      /// use nitrokey::{Authenticate, Admin, DeviceWrapper}; -    /// # use nitrokey::CommandError; +    /// # use nitrokey::Error;      ///      /// fn perform_admin_task(device: &Admin<DeviceWrapper>) {}      /// fn perform_other_task(device: &DeviceWrapper) {}      /// -    /// # fn try_main() -> Result<(), CommandError> { +    /// # fn try_main() -> Result<(), Error> {      /// let device = nitrokey::connect()?;      /// let device = match device.authenticate_admin("123456") {      ///     Ok(admin) => { @@ -102,16 +104,18 @@ pub trait Authenticate {      /// # }      /// ```      /// -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`RngError`]: enum.CommandError.html#variant.RngError      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword -    fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, CommandError)> +    fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)>      where          Self: Device + Sized;  }  trait AuthenticatedDevice<T> {      fn new(device: T, temp_password: Vec<u8>) -> Self; + +    fn temp_password_ptr(&self) -> *const c_char;  }  /// A Nitrokey device with user authentication. @@ -144,7 +148,7 @@ pub struct Admin<T: Device> {      temp_password: Vec<u8>,  } -fn authenticate<D, A, T>(device: D, password: &str, callback: T) -> Result<A, (D, CommandError)> +fn authenticate<D, A, T>(device: D, password: &str, callback: T) -> Result<A, (D, Error)>  where      D: Device,      A: AuthenticatedDevice<D>, @@ -160,17 +164,17 @@ where      };      let password_ptr = password.as_ptr();      let temp_password_ptr = temp_password.as_ptr() as *const c_char; -    return match callback(password_ptr, temp_password_ptr) { +    match callback(password_ptr, temp_password_ptr) {          0 => Ok(A::new(device, temp_password)), -        rv => Err((device, CommandError::from(rv))), -    }; +        rv => Err((device, Error::from(rv))), +    }  }  fn authenticate_user_wrapper<T, C>(      device: T,      constructor: C,      password: &str, -) -> Result<User<DeviceWrapper>, (DeviceWrapper, CommandError)> +) -> Result<User<DeviceWrapper>, (DeviceWrapper, Error)>  where      T: Device,      C: Fn(T) -> DeviceWrapper, @@ -186,7 +190,7 @@ fn authenticate_admin_wrapper<T, C>(      device: T,      constructor: C,      password: &str, -) -> Result<Admin<DeviceWrapper>, (DeviceWrapper, CommandError)> +) -> Result<Admin<DeviceWrapper>, (DeviceWrapper, Error)>  where      T: Device,      C: Fn(T) -> DeviceWrapper, @@ -216,24 +220,16 @@ impl<T: Device> Deref for User<T> {  }  impl<T: Device> GenerateOtp for User<T> { -    fn get_hotp_code(&self, slot: u8) -> Result<String, CommandError> { -        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_hotp_code(&self, slot: u8) -> Result<String, Error> { +        result_from_string(unsafe { +            nitrokey_sys::NK_get_hotp_code_PIN(slot, self.temp_password_ptr()) +        })      } -    fn get_totp_code(&self, slot: u8) -> Result<String, CommandError> { -        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( -                slot, -                0, -                0, -                0, -                temp_password_ptr, -            )); -        } +    fn get_totp_code(&self, slot: u8) -> Result<String, Error> { +        result_from_string(unsafe { +            nitrokey_sys::NK_get_totp_code_PIN(slot, 0, 0, 0, self.temp_password_ptr()) +        })      }  } @@ -244,6 +240,10 @@ impl<T: Device> AuthenticatedDevice<T> for User<T> {              temp_password,          }      } + +    fn temp_password_ptr(&self) -> *const c_char { +        self.temp_password.as_ptr() as *const c_char +    }  }  impl<T: Device> Deref for Admin<T> { @@ -272,9 +272,9 @@ impl<T: Device> Admin<T> {      ///      /// ```no_run      /// use nitrokey::{Authenticate, Config}; -    /// # use nitrokey::CommandError; +    /// # use nitrokey::Error;      /// -    /// # fn try_main() -> Result<(), CommandError> { +    /// # fn try_main() -> Result<(), Error> {      /// let device = nitrokey::connect()?;      /// let config = Config::new(None, None, None, false);      /// match device.authenticate_admin("12345678") { @@ -288,34 +288,26 @@ impl<T: Device> Admin<T> {      /// # }      /// ```      /// -    /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot -    pub fn write_config(&self, config: Config) -> Result<(), CommandError> { +    /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot +    pub fn write_config(&self, config: Config) -> Result<(), Error> {          let raw_config = RawConfig::try_from(config)?; -        unsafe { -            get_command_result(nitrokey_sys::NK_write_config( +        get_command_result(unsafe { +            nitrokey_sys::NK_write_config(                  raw_config.numlock,                  raw_config.capslock,                  raw_config.scrollock,                  raw_config.user_password,                  false, -                self.temp_password.as_ptr() as *const c_char, -            )) -        } -    } - -    fn write_otp_slot<C>(&self, data: OtpSlotData, callback: C) -> Result<(), CommandError> -    where -        C: Fn(RawOtpSlotData, *const c_char) -> c_int, -    { -        let raw_data = RawOtpSlotData::new(data)?; -        let temp_password_ptr = self.temp_password.as_ptr() as *const c_char; -        get_command_result(callback(raw_data, temp_password_ptr)) +                self.temp_password_ptr(), +            ) +        })      }  }  impl<T: Device> ConfigureOtp for Admin<T> { -    fn write_hotp_slot(&self, data: OtpSlotData, counter: u64) -> Result<(), CommandError> { -        self.write_otp_slot(data, |raw_data: RawOtpSlotData, temp_password_ptr| unsafe { +    fn write_hotp_slot(&self, data: OtpSlotData, counter: u64) -> Result<(), Error> { +        let raw_data = RawOtpSlotData::new(data)?; +        get_command_result(unsafe {              nitrokey_sys::NK_write_hotp_slot(                  raw_data.number,                  raw_data.name.as_ptr(), @@ -325,13 +317,14 @@ impl<T: Device> ConfigureOtp for Admin<T> {                  raw_data.use_enter,                  raw_data.use_token_id,                  raw_data.token_id.as_ptr(), -                temp_password_ptr, +                self.temp_password_ptr(),              )          })      } -    fn write_totp_slot(&self, data: OtpSlotData, time_window: u16) -> Result<(), CommandError> { -        self.write_otp_slot(data, |raw_data: RawOtpSlotData, temp_password_ptr| unsafe { +    fn write_totp_slot(&self, data: OtpSlotData, time_window: u16) -> Result<(), Error> { +        let raw_data = RawOtpSlotData::new(data)?; +        get_command_result(unsafe {              nitrokey_sys::NK_write_totp_slot(                  raw_data.number,                  raw_data.name.as_ptr(), @@ -341,19 +334,21 @@ impl<T: Device> ConfigureOtp for Admin<T> {                  raw_data.use_enter,                  raw_data.use_token_id,                  raw_data.token_id.as_ptr(), -                temp_password_ptr, +                self.temp_password_ptr(),              )          })      } -    fn erase_hotp_slot(&self, slot: u8) -> Result<(), CommandError> { -        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_hotp_slot(&self, slot: u8) -> Result<(), Error> { +        get_command_result(unsafe { +            nitrokey_sys::NK_erase_hotp_slot(slot, self.temp_password_ptr()) +        })      } -    fn erase_totp_slot(&self, slot: u8) -> Result<(), CommandError> { -        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)) } +    fn erase_totp_slot(&self, slot: u8) -> Result<(), Error> { +        get_command_result(unsafe { +            nitrokey_sys::NK_erase_totp_slot(slot, self.temp_password_ptr()) +        })      }  } @@ -364,10 +359,14 @@ impl<T: Device> AuthenticatedDevice<T> for Admin<T> {              temp_password,          }      } + +    fn temp_password_ptr(&self) -> *const c_char { +        self.temp_password.as_ptr() as *const c_char +    }  }  impl Authenticate for DeviceWrapper { -    fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, CommandError)> { +    fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)> {          match self {              DeviceWrapper::Storage(storage) => {                  authenticate_user_wrapper(storage, DeviceWrapper::Storage, password) @@ -376,7 +375,7 @@ impl Authenticate for DeviceWrapper {          }      } -    fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, CommandError)> { +    fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)> {          match self {              DeviceWrapper::Storage(storage) => {                  authenticate_admin_wrapper(storage, DeviceWrapper::Storage, password) @@ -389,13 +388,13 @@ impl Authenticate for DeviceWrapper {  }  impl Authenticate for Pro { -    fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, CommandError)> { +    fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)> {          authenticate(self, password, |password_ptr, temp_password_ptr| unsafe {              nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr)          })      } -    fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, CommandError)> { +    fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)> {          authenticate(self, password, |password_ptr, temp_password_ptr| unsafe {              nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr)          }) @@ -403,13 +402,13 @@ impl Authenticate for Pro {  }  impl Authenticate for Storage { -    fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, CommandError)> { +    fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)> {          authenticate(self, password, |password_ptr, temp_password_ptr| unsafe {              nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr)          })      } -    fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, CommandError)> { +    fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)> {          authenticate(self, password, |password_ptr, temp_password_ptr| unsafe {              nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr)          }) diff --git a/nitrokey/src/config.rs b/nitrokey/src/config.rs index 2ce6f77..c273792 100644 --- a/nitrokey/src/config.rs +++ b/nitrokey/src/config.rs @@ -1,4 +1,7 @@ -use crate::util::CommandError; +// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org> +// SPDX-License-Identifier: MIT + +use crate::error::{Error, LibraryError};  /// The configuration for a Nitrokey.  #[derive(Clone, Copy, Debug, PartialEq)] @@ -30,21 +33,21 @@ pub struct RawConfig {  fn config_otp_slot_to_option(value: u8) -> Option<u8> {      if value < 3 { -        return Some(value); +        Some(value) +    } else { +        None      } -    None  } -fn option_to_config_otp_slot(value: Option<u8>) -> Result<u8, CommandError> { -    match value { -        Some(value) => { -            if value < 3 { -                Ok(value) -            } else { -                Err(CommandError::InvalidSlot) -            } +fn option_to_config_otp_slot(value: Option<u8>) -> Result<u8, Error> { +    if let Some(value) = value { +        if value < 3 { +            Ok(value) +        } else { +            Err(LibraryError::InvalidSlot.into())          } -        None => Ok(255), +    } else { +        Ok(255)      }  } @@ -66,7 +69,7 @@ impl Config {  }  impl RawConfig { -    pub fn try_from(config: Config) -> Result<RawConfig, CommandError> { +    pub fn try_from(config: Config) -> Result<RawConfig, Error> {          Ok(RawConfig {              numlock: option_to_config_otp_slot(config.numlock)?,              capslock: option_to_config_otp_slot(config.capslock)?, diff --git a/nitrokey/src/device.rs b/nitrokey/src/device.rs index 9813c50..386ce94 100644 --- a/nitrokey/src/device.rs +++ b/nitrokey/src/device.rs @@ -1,15 +1,18 @@ +// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org> +// SPDX-License-Identifier: MIT +  use std::fmt; +use std::marker;  use libc;  use nitrokey_sys;  use crate::auth::Authenticate;  use crate::config::{Config, RawConfig}; +use crate::error::{CommunicationError, Error};  use crate::otp::GenerateOtp;  use crate::pws::GetPasswordSafe; -use crate::util::{ -    get_command_result, get_cstring, get_last_error, result_from_string, CommandError, -}; +use crate::util::{get_command_result, get_cstring, get_last_error, result_from_string};  /// Available Nitrokey models.  #[derive(Clone, Copy, Debug, PartialEq)] @@ -22,14 +25,10 @@ pub enum Model {  impl fmt::Display for Model {      fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -        write!( -            f, -            "{}", -            match *self { -                Model::Pro => "Pro", -                Model::Storage => "Storage", -            } -        ) +        f.write_str(match *self { +            Model::Pro => "Pro", +            Model::Storage => "Storage", +        })      }  } @@ -44,10 +43,10 @@ pub enum VolumeMode {  impl fmt::Display for VolumeMode {      fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -        match *self { -            VolumeMode::ReadOnly => f.write_str("read-only"), -            VolumeMode::ReadWrite => f.write_str("read-write"), -        } +        f.write_str(match *self { +            VolumeMode::ReadOnly => "read-only", +            VolumeMode::ReadWrite => "read-write", +        })      }  } @@ -64,12 +63,12 @@ impl fmt::Display for VolumeMode {  ///  /// ```no_run  /// use nitrokey::{Authenticate, DeviceWrapper, User}; -/// # use nitrokey::CommandError; +/// # use nitrokey::Error;  ///  /// fn perform_user_task(device: &User<DeviceWrapper>) {}  /// fn perform_other_task(device: &DeviceWrapper) {}  /// -/// # fn try_main() -> Result<(), CommandError> { +/// # fn try_main() -> Result<(), Error> {  /// let device = nitrokey::connect()?;  /// let device = match device.authenticate_user("123456") {  ///     Ok(user) => { @@ -90,12 +89,12 @@ impl fmt::Display for VolumeMode {  ///  /// ```no_run  /// use nitrokey::{DeviceWrapper, Storage}; -/// # use nitrokey::CommandError; +/// # use nitrokey::Error;  ///  /// fn perform_common_task(device: &DeviceWrapper) {}  /// fn perform_storage_task(device: &Storage) {}  /// -/// # fn try_main() -> Result<(), CommandError> { +/// # fn try_main() -> Result<(), Error> {  /// let device = nitrokey::connect()?;  /// perform_common_task(&device);  /// match device { @@ -128,12 +127,12 @@ pub enum DeviceWrapper {  ///  /// ```no_run  /// use nitrokey::{Authenticate, User, Pro}; -/// # use nitrokey::CommandError; +/// # use nitrokey::Error;  ///  /// fn perform_user_task(device: &User<Pro>) {}  /// fn perform_other_task(device: &Pro) {}  /// -/// # fn try_main() -> Result<(), CommandError> { +/// # fn try_main() -> Result<(), Error> {  /// let device = nitrokey::Pro::connect()?;  /// let device = match device.authenticate_user("123456") {  ///     Ok(user) => { @@ -155,7 +154,11 @@ pub enum DeviceWrapper {  /// [`connect`]: fn.connect.html  /// [`Pro::connect`]: #method.connect  #[derive(Debug)] -pub struct Pro {} +pub struct Pro { +    // make sure that users cannot directly instantiate this type +    #[doc(hidden)] +    marker: marker::PhantomData<()>, +}  /// A Nitrokey Storage device without user or admin authentication.  /// @@ -170,12 +173,12 @@ pub struct Pro {}  ///  /// ```no_run  /// use nitrokey::{Authenticate, User, Storage}; -/// # use nitrokey::CommandError; +/// # use nitrokey::Error;  ///  /// fn perform_user_task(device: &User<Storage>) {}  /// fn perform_other_task(device: &Storage) {}  /// -/// # fn try_main() -> Result<(), CommandError> { +/// # fn try_main() -> Result<(), Error> {  /// let device = nitrokey::Storage::connect()?;  /// let device = match device.authenticate_user("123456") {  ///     Ok(user) => { @@ -197,7 +200,11 @@ pub struct Pro {}  /// [`connect`]: fn.connect.html  /// [`Storage::connect`]: #method.connect  #[derive(Debug)] -pub struct Storage {} +pub struct Storage { +    // make sure that users cannot directly instantiate this type +    #[doc(hidden)] +    marker: marker::PhantomData<()>, +}  /// The status of a volume on a Nitrokey Storage device.  #[derive(Debug)] @@ -225,13 +232,26 @@ pub struct SdCardData {      pub manufacturer: u8,  } -#[derive(Debug)] -/// Production information for a Storage device. -pub struct StorageProductionInfo { +/// A firmware version for a Nitrokey device. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct FirmwareVersion {      /// The major firmware version, e. g. 0 in v0.40. -    pub firmware_version_major: u8, +    pub major: u8,      /// The minor firmware version, e. g. 40 in v0.40. -    pub firmware_version_minor: u8, +    pub minor: u8, +} + +impl fmt::Display for FirmwareVersion { +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +        write!(f, "v{}.{}", self.major, self.minor) +    } +} + +/// Production information for a Storage device. +#[derive(Debug)] +pub struct StorageProductionInfo { +    /// The firmware version. +    pub firmware_version: FirmwareVersion,      /// The internal firmware version.      pub firmware_version_internal: u8,      /// The serial number of the CPU. @@ -249,10 +269,8 @@ pub struct StorageStatus {      pub encrypted_volume: VolumeStatus,      /// The status of the hidden volume.      pub hidden_volume: VolumeStatus, -    /// The major firmware version, e. g. 0 in v0.40. -    pub firmware_version_major: u8, -    /// The minor firmware version, e. g. 40 in v0.40. -    pub firmware_version_minor: u8, +    /// The firmware version. +    pub firmware_version: FirmwareVersion,      /// Indicates whether the firmware is locked.      pub firmware_locked: bool,      /// The serial number of the SD card in the Storage stick. @@ -276,16 +294,16 @@ pub struct StorageStatus {  ///  /// This trait provides the commands that can be executed without authentication and that are  /// present on all supported Nitrokey devices. -pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { +pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug {      /// Returns the model of the connected Nitrokey device.      ///      /// # Example      ///      /// ```no_run      /// use nitrokey::Device; -    /// # use nitrokey::CommandError; +    /// # use nitrokey::Error;      /// -    /// # fn try_main() -> Result<(), CommandError> { +    /// # fn try_main() -> Result<(), Error> {      /// let device = nitrokey::connect()?;      /// println!("Connected to a Nitrokey {}", device.get_model());      /// #    Ok(()) @@ -299,9 +317,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), @@ -310,8 +328,8 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {      /// #     Ok(())      /// # }      /// ``` -    fn get_serial_number(&self) -> Result<String, CommandError> { -        unsafe { result_from_string(nitrokey_sys::NK_device_serial_number()) } +    fn get_serial_number(&self) -> Result<String, Error> { +        result_from_string(unsafe { nitrokey_sys::NK_device_serial_number() })      }      /// Returns the number of remaining authentication attempts for the user.  The total number of @@ -321,9 +339,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); @@ -341,9 +359,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); @@ -360,9 +378,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: {}.{}", @@ -382,9 +400,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: {}.{}", @@ -403,9 +421,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); @@ -415,17 +433,15 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {      /// #     Ok(())      /// # }      /// ``` -    fn get_config(&self) -> Result<Config, CommandError> { -        unsafe { -            let config_ptr = nitrokey_sys::NK_read_config(); -            if config_ptr.is_null() { -                return Err(get_last_error()); -            } -            let config_array_ptr = config_ptr as *const [u8; 5]; -            let raw_config = RawConfig::from(*config_array_ptr); -            libc::free(config_ptr as *mut libc::c_void); -            return Ok(raw_config.into()); +    fn get_config(&self) -> Result<Config, Error> { +        let config_ptr = unsafe { nitrokey_sys::NK_read_config() }; +        if config_ptr.is_null() { +            return Err(get_last_error());          } +        let config_array_ptr = config_ptr as *const [u8; 5]; +        let raw_config = unsafe { RawConfig::from(*config_array_ptr) }; +        unsafe { libc::free(config_ptr as *mut libc::c_void) }; +        Ok(raw_config.into())      }      /// Changes the administrator PIN. @@ -439,9 +455,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."), @@ -451,17 +467,14 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {      /// # }      /// ```      /// -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword -    fn change_admin_pin(&self, current: &str, new: &str) -> Result<(), CommandError> { +    fn change_admin_pin(&self, current: &str, new: &str) -> Result<(), Error> {          let current_string = get_cstring(current)?;          let new_string = get_cstring(new)?; -        unsafe { -            get_command_result(nitrokey_sys::NK_change_admin_PIN( -                current_string.as_ptr(), -                new_string.as_ptr(), -            )) -        } +        get_command_result(unsafe { +            nitrokey_sys::NK_change_admin_PIN(current_string.as_ptr(), new_string.as_ptr()) +        })      }      /// Changes the user PIN. @@ -475,9 +488,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."), @@ -487,17 +500,14 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {      /// # }      /// ```      /// -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword -    fn change_user_pin(&self, current: &str, new: &str) -> Result<(), CommandError> { +    fn change_user_pin(&self, current: &str, new: &str) -> Result<(), Error> {          let current_string = get_cstring(current)?;          let new_string = get_cstring(new)?; -        unsafe { -            get_command_result(nitrokey_sys::NK_change_user_PIN( -                current_string.as_ptr(), -                new_string.as_ptr(), -            )) -        } +        get_command_result(unsafe { +            nitrokey_sys::NK_change_user_PIN(current_string.as_ptr(), new_string.as_ptr()) +        })      }      /// Unlocks the user PIN after three failed login attempts and sets it to the given value. @@ -511,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."), @@ -523,17 +533,17 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {      /// # }      /// ```      /// -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword -    fn unlock_user_pin(&self, admin_pin: &str, user_pin: &str) -> Result<(), CommandError> { +    fn unlock_user_pin(&self, admin_pin: &str, user_pin: &str) -> Result<(), Error> {          let admin_pin_string = get_cstring(admin_pin)?;          let user_pin_string = get_cstring(user_pin)?; -        unsafe { -            get_command_result(nitrokey_sys::NK_unlock_user_password( +        get_command_result(unsafe { +            nitrokey_sys::NK_unlock_user_password(                  admin_pin_string.as_ptr(),                  user_pin_string.as_ptr(), -            )) -        } +            ) +        })      }      /// Locks the Nitrokey device. @@ -545,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."), @@ -556,8 +566,8 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {      /// #     Ok(())      /// # }      /// ``` -    fn lock(&self) -> Result<(), CommandError> { -        unsafe { get_command_result(nitrokey_sys::NK_lock_device()) } +    fn lock(&self) -> Result<(), Error> { +        get_command_result(unsafe { nitrokey_sys::NK_lock_device() })      }      /// Performs a factory reset on the Nitrokey device. @@ -576,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."), @@ -589,9 +599,9 @@ 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())) } +        get_command_result(unsafe { nitrokey_sys::NK_factory_reset(admin_pin_string.as_ptr()) })      }      /// Builds a new AES key on the Nitrokey. @@ -610,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."), @@ -623,9 +633,9 @@ 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())) } +        get_command_result(unsafe { nitrokey_sys::NK_build_aes_key(admin_pin_string.as_ptr()) })      }  } @@ -634,7 +644,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {  ///  /// # Errors  /// -/// - [`Undefined`][] if no Nitrokey device is connected +/// - [`NotConnected`][] if no Nitrokey device is connected  ///  /// # Example  /// @@ -649,16 +659,15 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {  /// }  /// ```  /// -/// [`Undefined`]: enum.CommandError.html#variant.Undefined -pub fn connect() -> Result<DeviceWrapper, CommandError> { -    unsafe { -        match nitrokey_sys::NK_login_auto() { -            1 => match get_connected_device() { -                Some(wrapper) => Ok(wrapper), -                None => Err(CommandError::Undefined), -            }, -            _ => Err(CommandError::Undefined), +/// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected +pub fn connect() -> Result<DeviceWrapper, Error> { +    if unsafe { nitrokey_sys::NK_login_auto() } == 1 { +        match get_connected_device() { +            Some(wrapper) => Ok(wrapper), +            None => Err(CommunicationError::NotConnected.into()),          } +    } else { +        Err(CommunicationError::NotConnected.into())      }  } @@ -666,7 +675,7 @@ pub fn connect() -> Result<DeviceWrapper, CommandError> {  ///  /// # Errors  /// -/// - [`Undefined`][] if no Nitrokey device of the given model is connected +/// - [`NotConnected`][] if no Nitrokey device of the given model is connected  ///  /// # Example  /// @@ -682,29 +691,27 @@ pub fn connect() -> Result<DeviceWrapper, CommandError> {  /// }  /// ```  /// -/// [`Undefined`]: enum.CommandError.html#variant.Undefined -pub fn connect_model(model: Model) -> Result<DeviceWrapper, CommandError> { +/// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected +pub fn connect_model(model: Model) -> Result<DeviceWrapper, Error> {      if connect_enum(model) {          Ok(create_device_wrapper(model))      } else { -        Err(CommandError::Undefined) +        Err(CommunicationError::NotConnected.into())      }  }  fn get_connected_model() -> Option<Model> { -    unsafe { -        match nitrokey_sys::NK_get_device_model() { -            nitrokey_sys::NK_device_model_NK_PRO => Some(Model::Pro), -            nitrokey_sys::NK_device_model_NK_STORAGE => Some(Model::Storage), -            _ => None, -        } +    match unsafe { nitrokey_sys::NK_get_device_model() } { +        nitrokey_sys::NK_device_model_NK_PRO => Some(Model::Pro), +        nitrokey_sys::NK_device_model_NK_STORAGE => Some(Model::Storage), +        _ => None,      }  }  fn create_device_wrapper(model: Model) -> DeviceWrapper {      match model { -        Model::Pro => DeviceWrapper::Pro(Pro {}), -        Model::Storage => DeviceWrapper::Storage(Storage {}), +        Model::Pro => Pro::new().into(), +        Model::Storage => Storage::new().into(),      }  } @@ -729,20 +736,32 @@ impl DeviceWrapper {      }  } +impl From<Pro> for DeviceWrapper { +    fn from(device: Pro) -> Self { +        DeviceWrapper::Pro(device) +    } +} + +impl From<Storage> for DeviceWrapper { +    fn from(device: Storage) -> Self { +        DeviceWrapper::Storage(device) +    } +} +  impl GenerateOtp for DeviceWrapper { -    fn get_hotp_slot_name(&self, slot: u8) -> Result<String, CommandError> { +    fn get_hotp_slot_name(&self, slot: u8) -> Result<String, Error> {          self.device().get_hotp_slot_name(slot)      } -    fn get_totp_slot_name(&self, slot: u8) -> Result<String, CommandError> { +    fn get_totp_slot_name(&self, slot: u8) -> Result<String, Error> {          self.device().get_totp_slot_name(slot)      } -    fn get_hotp_code(&self, slot: u8) -> Result<String, CommandError> { +    fn get_hotp_code(&self, slot: u8) -> Result<String, Error> {          self.device().get_hotp_code(slot)      } -    fn get_totp_code(&self, slot: u8) -> Result<String, CommandError> { +    fn get_totp_code(&self, slot: u8) -> Result<String, Error> {          self.device().get_totp_code(slot)      }  } @@ -761,7 +780,7 @@ impl Pro {      ///      /// # Errors      /// -    /// - [`Undefined`][] if no Nitrokey device of the given model is connected +    /// - [`NotConnected`][] if no Nitrokey device of the given model is connected      ///      /// # Example      /// @@ -776,12 +795,19 @@ impl Pro {      /// }      /// ```      /// -    /// [`Undefined`]: enum.CommandError.html#variant.Undefined -    pub fn connect() -> Result<Pro, CommandError> { +    /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected +    pub fn connect() -> Result<Pro, Error> {          // TODO: maybe Option instead of Result? -        match connect_enum(Model::Pro) { -            true => Ok(Pro {}), -            false => Err(CommandError::Undefined), +        if connect_enum(Model::Pro) { +            Ok(Pro::new()) +        } else { +            Err(CommunicationError::NotConnected.into()) +        } +    } + +    fn new() -> Pro { +        Pro { +            marker: marker::PhantomData,          }      }  } @@ -807,7 +833,7 @@ impl Storage {      ///      /// # Errors      /// -    /// - [`Undefined`][] if no Nitrokey device of the given model is connected +    /// - [`NotConnected`][] if no Nitrokey device of the given model is connected      ///      /// # Example      /// @@ -822,12 +848,19 @@ impl Storage {      /// }      /// ```      /// -    /// [`Undefined`]: enum.CommandError.html#variant.Undefined -    pub fn connect() -> Result<Storage, CommandError> { +    /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected +    pub fn connect() -> Result<Storage, Error> {          // TODO: maybe Option instead of Result? -        match connect_enum(Model::Storage) { -            true => Ok(Storage {}), -            false => Err(CommandError::Undefined), +        if connect_enum(Model::Storage) { +            Ok(Storage::new()) +        } else { +            Err(CommunicationError::NotConnected.into()) +        } +    } + +    fn new() -> Storage { +        Storage { +            marker: marker::PhantomData,          }      } @@ -845,9 +878,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."), @@ -857,17 +890,14 @@ impl Storage {      /// # }      /// ```      /// -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword -    pub fn change_update_pin(&self, current: &str, new: &str) -> Result<(), CommandError> { +    pub fn change_update_pin(&self, current: &str, new: &str) -> Result<(), Error> {          let current_string = get_cstring(current)?;          let new_string = get_cstring(new)?; -        unsafe { -            get_command_result(nitrokey_sys::NK_change_update_password( -                current_string.as_ptr(), -                new_string.as_ptr(), -            )) -        } +        get_command_result(unsafe { +            nitrokey_sys::NK_change_update_password(current_string.as_ptr(), new_string.as_ptr()) +        })      }      /// Enables the firmware update mode. @@ -885,9 +915,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."), @@ -897,15 +927,13 @@ impl Storage {      /// # }      /// ```      /// -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword -    pub fn enable_firmware_update(&self, update_pin: &str) -> Result<(), CommandError> { +    pub fn enable_firmware_update(&self, update_pin: &str) -> Result<(), Error> {          let update_pin_string = get_cstring(update_pin)?; -        unsafe { -            get_command_result(nitrokey_sys::NK_enable_firmware_update( -                update_pin_string.as_ptr(), -            )) -        } +        get_command_result(unsafe { +            nitrokey_sys::NK_enable_firmware_update(update_pin_string.as_ptr()) +        })      }      /// Enables the encrypted storage volume. @@ -921,9 +949,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."), @@ -933,11 +961,11 @@ impl Storage {      /// # }      /// ```      /// -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword -    pub fn enable_encrypted_volume(&self, user_pin: &str) -> Result<(), CommandError> { +    pub fn enable_encrypted_volume(&self, user_pin: &str) -> Result<(), Error> {          let user_pin = get_cstring(user_pin)?; -        unsafe { get_command_result(nitrokey_sys::NK_unlock_encrypted_volume(user_pin.as_ptr())) } +        get_command_result(unsafe { nitrokey_sys::NK_unlock_encrypted_volume(user_pin.as_ptr()) })      }      /// Disables the encrypted storage volume. @@ -948,11 +976,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(()) => { @@ -970,8 +998,8 @@ impl Storage {      /// #     Ok(())      /// # }      /// ``` -    pub fn disable_encrypted_volume(&self) -> Result<(), CommandError> { -        unsafe { get_command_result(nitrokey_sys::NK_lock_encrypted_volume()) } +    pub fn disable_encrypted_volume(&self) -> Result<(), Error> { +        get_command_result(unsafe { nitrokey_sys::NK_lock_encrypted_volume() })      }      /// Enables a hidden storage volume. @@ -996,9 +1024,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") { @@ -1011,14 +1039,12 @@ impl Storage {      ///      /// [`enable_encrypted_volume`]: #method.enable_encrypted_volume      /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString -    pub fn enable_hidden_volume(&self, volume_password: &str) -> Result<(), CommandError> { +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString +    pub fn enable_hidden_volume(&self, volume_password: &str) -> Result<(), Error> {          let volume_password = get_cstring(volume_password)?; -        unsafe { -            get_command_result(nitrokey_sys::NK_unlock_hidden_volume( -                volume_password.as_ptr(), -            )) -        } +        get_command_result(unsafe { +            nitrokey_sys::NK_unlock_hidden_volume(volume_password.as_ptr()) +        })      }      /// Disables a hidden storage volume. @@ -1029,11 +1055,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") { @@ -1052,8 +1078,8 @@ impl Storage {      /// #     Ok(())      /// # }      /// ``` -    pub fn disable_hidden_volume(&self) -> Result<(), CommandError> { -        unsafe { get_command_result(nitrokey_sys::NK_lock_hidden_volume()) } +    pub fn disable_hidden_volume(&self) -> Result<(), Error> { +        get_command_result(unsafe { nitrokey_sys::NK_lock_hidden_volume() })      }      /// Creates a hidden volume. @@ -1078,9 +1104,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")?; @@ -1089,23 +1115,18 @@ impl Storage {      /// ```      ///      /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      pub fn create_hidden_volume(          &self,          slot: u8,          start: u8,          end: u8,          password: &str, -    ) -> Result<(), CommandError> { +    ) -> Result<(), Error> {          let password = get_cstring(password)?; -        unsafe { -            get_command_result(nitrokey_sys::NK_create_hidden_volume( -                slot, -                start, -                end, -                password.as_ptr(), -            )) -        } +        get_command_result(unsafe { +            nitrokey_sys::NK_create_hidden_volume(slot, start, end, password.as_ptr()) +        })      }      /// Sets the access mode of the unencrypted volume. @@ -1122,10 +1143,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."), @@ -1135,13 +1156,13 @@ impl Storage {      /// # }      /// ```      /// -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword      pub fn set_unencrypted_volume_mode(          &self,          admin_pin: &str,          mode: VolumeMode, -    ) -> Result<(), CommandError> { +    ) -> Result<(), Error> {          let admin_pin = get_cstring(admin_pin)?;          let result = match mode {              VolumeMode::ReadOnly => unsafe { @@ -1159,11 +1180,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) => { @@ -1174,7 +1195,7 @@ impl Storage {      /// #     Ok(())      /// # }      /// ``` -    pub fn get_status(&self) -> Result<StorageStatus, CommandError> { +    pub fn get_status(&self) -> Result<StorageStatus, Error> {          let mut raw_status = nitrokey_sys::NK_storage_status {              unencrypted_volume_read_only: false,              unencrypted_volume_active: false, @@ -1194,8 +1215,7 @@ impl Storage {              stick_initialized: false,          };          let raw_result = unsafe { nitrokey_sys::NK_get_status_storage(&mut raw_status) }; -        let result = get_command_result(raw_result); -        result.and(Ok(StorageStatus::from(raw_status))) +        get_command_result(raw_result).map(|_| StorageStatus::from(raw_status))      }      /// Returns the production information for the connected storage device. @@ -1203,11 +1223,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) => { @@ -1219,7 +1239,7 @@ impl Storage {      /// #     Ok(())      /// # }      /// ``` -    pub fn get_production_info(&self) -> Result<StorageProductionInfo, CommandError> { +    pub fn get_production_info(&self) -> Result<StorageProductionInfo, Error> {          let mut raw_data = nitrokey_sys::NK_storage_ProductionTest {              FirmwareVersion_au8: [0, 2],              FirmwareVersionInternal_u8: 0, @@ -1236,8 +1256,7 @@ impl Storage {              SD_Card_Manufacturer_u8: 0,          };          let raw_result = unsafe { nitrokey_sys::NK_get_storage_production_info(&mut raw_data) }; -        let result = get_command_result(raw_result); -        result.and(Ok(StorageProductionInfo::from(raw_data))) +        get_command_result(raw_result).map(|_| StorageProductionInfo::from(raw_data))      }      /// Clears the warning for a new SD card. @@ -1254,9 +1273,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."), @@ -1266,9 +1285,9 @@ impl Storage {      /// # }      /// ```      /// -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword -    pub fn clear_new_sd_card_warning(&self, admin_pin: &str) -> Result<(), CommandError> { +    pub fn clear_new_sd_card_warning(&self, admin_pin: &str) -> Result<(), Error> {          let admin_pin = get_cstring(admin_pin)?;          get_command_result(unsafe {              nitrokey_sys::NK_clear_new_sd_card_warning(admin_pin.as_ptr()) @@ -1276,7 +1295,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() })      } @@ -1294,9 +1313,9 @@ impl Storage {      /// - [`InvalidString`][] if one of the provided passwords contains a null byte      /// - [`WrongPassword`][] if the admin password is wrong      /// -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword -    pub fn export_firmware(&self, admin_pin: &str) -> Result<(), CommandError> { +    pub fn export_firmware(&self, admin_pin: &str) -> Result<(), Error> {          let admin_pin_string = get_cstring(admin_pin)?;          get_command_result(unsafe { nitrokey_sys::NK_export_firmware(admin_pin_string.as_ptr()) })      } @@ -1321,8 +1340,10 @@ impl GenerateOtp for Storage {}  impl From<nitrokey_sys::NK_storage_ProductionTest> for StorageProductionInfo {      fn from(data: nitrokey_sys::NK_storage_ProductionTest) -> Self {          Self { -            firmware_version_major: data.FirmwareVersion_au8[0], -            firmware_version_minor: data.FirmwareVersion_au8[1], +            firmware_version: FirmwareVersion { +                major: data.FirmwareVersion_au8[0], +                minor: data.FirmwareVersion_au8[1], +            },              firmware_version_internal: data.FirmwareVersionInternal_u8,              serial_number_cpu: data.CPU_CardID_u32,              sd_card: SdCardData { @@ -1352,8 +1373,10 @@ impl From<nitrokey_sys::NK_storage_status> for StorageStatus {                  read_only: status.hidden_volume_read_only,                  active: status.hidden_volume_active,              }, -            firmware_version_major: status.firmware_version_major, -            firmware_version_minor: status.firmware_version_minor, +            firmware_version: FirmwareVersion { +                major: status.firmware_version_major, +                minor: status.firmware_version_minor, +            },              firmware_locked: status.firmware_locked,              serial_number_sd_card: status.serial_number_sd_card,              serial_number_smart_card: status.serial_number_smart_card, diff --git a/nitrokey/src/error.rs b/nitrokey/src/error.rs new file mode 100644 index 0000000..1730171 --- /dev/null +++ b/nitrokey/src/error.rs @@ -0,0 +1,245 @@ +// Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org> +// SPDX-License-Identifier: MIT + +use std::error; +use std::fmt; +use std::os::raw; +use std::str; + +use crate::device; + +/// An error returned by the nitrokey crate. +#[derive(Debug)] +pub enum Error { +    /// An error reported by the Nitrokey device in the response packet. +    CommandError(CommandError), +    /// A device communication. +    CommunicationError(CommunicationError), +    /// A library usage error. +    LibraryError(LibraryError), +    /// An error that occured during random number generation. +    RandError(Box<dyn error::Error>), +    /// An error that is caused by an unexpected value returned by libnitrokey. +    UnexpectedError, +    /// An unknown error returned by libnitrokey. +    UnknownError(i64), +    /// An error occurred when interpreting a UTF-8 string. +    Utf8Error(str::Utf8Error), +} + +impl From<raw::c_int> for Error { +    fn from(code: raw::c_int) -> Self { +        if let Some(err) = CommandError::try_from(code) { +            Error::CommandError(err) +        } else if let Some(err) = CommunicationError::try_from(256 - code) { +            Error::CommunicationError(err) +        } else if let Some(err) = LibraryError::try_from(code) { +            Error::LibraryError(err) +        } else { +            Error::UnknownError(code.into()) +        } +    } +} + +impl From<CommandError> for Error { +    fn from(err: CommandError) -> Self { +        Error::CommandError(err) +    } +} + +impl From<CommunicationError> for Error { +    fn from(err: CommunicationError) -> Self { +        Error::CommunicationError(err) +    } +} + +impl From<LibraryError> for Error { +    fn from(err: LibraryError) -> Self { +        Error::LibraryError(err) +    } +} + +impl From<str::Utf8Error> for Error { +    fn from(error: str::Utf8Error) -> Self { +        Error::Utf8Error(error) +    } +} + +impl<T: device::Device> 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 { +            Error::CommandError(ref err) => Some(err), +            Error::CommunicationError(ref err) => Some(err), +            Error::LibraryError(ref err) => Some(err), +            Error::RandError(ref err) => Some(err.as_ref()), +            Error::UnexpectedError => None, +            Error::UnknownError(_) => None, +            Error::Utf8Error(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), +            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::UnknownError(ref err) => write!(f, "Unknown error: {}", err), +            Error::Utf8Error(ref err) => write!(f, "UTF-8 error: {}", err), +        } +    } +} + +/// An error reported by the Nitrokey device in the response packet. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum CommandError { +    /// A packet with a wrong checksum has been sent or received. +    WrongCrc, +    /// A command tried to access an OTP slot that does not exist. +    WrongSlot, +    /// A command tried to generate an OTP on a slot that is not configured. +    SlotNotProgrammed, +    /// The provided password is wrong. +    WrongPassword, +    /// You are not authorized for this command or provided a wrong temporary +    /// password. +    NotAuthorized, +    /// An error occurred when getting or setting the time. +    Timestamp, +    /// You did not provide a name for the OTP slot. +    NoName, +    /// This command is not supported by this device. +    NotSupported, +    /// This command is unknown. +    UnknownCommand, +    /// AES decryption failed. +    AesDecryptionFailed, +} + +impl CommandError { +    fn try_from(value: raw::c_int) -> Option<Self> { +        match value { +            1 => Some(CommandError::WrongCrc), +            2 => Some(CommandError::WrongSlot), +            3 => Some(CommandError::SlotNotProgrammed), +            4 => Some(CommandError::WrongPassword), +            5 => Some(CommandError::NotAuthorized), +            6 => Some(CommandError::Timestamp), +            7 => Some(CommandError::NoName), +            8 => Some(CommandError::NotSupported), +            9 => Some(CommandError::UnknownCommand), +            10 => Some(CommandError::AesDecryptionFailed), +            _ => None, +        } +    } +} + +impl error::Error for CommandError {} + +impl fmt::Display for CommandError { +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +        f.write_str(match *self { +            CommandError::WrongCrc => "A packet with a wrong checksum has been sent or received", +            CommandError::WrongSlot => "The given slot does not exist", +            CommandError::SlotNotProgrammed => "The given slot is not programmed", +            CommandError::WrongPassword => "The given password is wrong", +            CommandError::NotAuthorized => { +                "You are not authorized for this command or provided a wrong temporary \ +                 password" +            } +            CommandError::Timestamp => "An error occurred when getting or setting the time", +            CommandError::NoName => "You did not provide a name for the slot", +            CommandError::NotSupported => "This command is not supported by this device", +            CommandError::UnknownCommand => "This command is unknown", +            CommandError::AesDecryptionFailed => "AES decryption failed", +        }) +    } +} + +/// A device communication error. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum CommunicationError { +    /// Could not connect to a Nitrokey device. +    NotConnected, +    /// Sending a packet failed. +    SendingFailure, +    /// Receiving a packet failed. +    ReceivingFailure, +    /// A packet with a wrong checksum was received. +    InvalidCrc, +} + +impl CommunicationError { +    fn try_from(value: raw::c_int) -> Option<Self> { +        match value { +            2 => Some(CommunicationError::NotConnected), +            3 => Some(CommunicationError::SendingFailure), +            4 => Some(CommunicationError::ReceivingFailure), +            5 => Some(CommunicationError::InvalidCrc), +            _ => None, +        } +    } +} + +impl error::Error for CommunicationError {} + +impl fmt::Display for CommunicationError { +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +        f.write_str(match *self { +            CommunicationError::NotConnected => "Could not connect to a Nitrokey device", +            CommunicationError::SendingFailure => "Sending a packet failed", +            CommunicationError::ReceivingFailure => "Receiving a packet failed", +            CommunicationError::InvalidCrc => "A packet with a wrong checksum was received", +        }) +    } +} + +/// A library usage error. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum LibraryError { +    /// A supplied string exceeded a length limit. +    StringTooLong, +    /// You passed an invalid slot. +    InvalidSlot, +    /// The supplied string was not in hexadecimal format. +    InvalidHexString, +    /// The target buffer was smaller than the source. +    TargetBufferTooSmall, +    /// You passed a string containing a null byte. +    InvalidString, +} + +impl LibraryError { +    fn try_from(value: raw::c_int) -> Option<Self> { +        match value { +            200 => Some(LibraryError::StringTooLong), +            201 => Some(LibraryError::InvalidSlot), +            202 => Some(LibraryError::InvalidHexString), +            203 => Some(LibraryError::TargetBufferTooSmall), +            _ => None, +        } +    } +} + +impl error::Error for LibraryError {} + +impl fmt::Display for LibraryError { +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +        f.write_str(match *self { +            LibraryError::StringTooLong => "The supplied string is too long", +            LibraryError::InvalidSlot => "The given slot is invalid", +            LibraryError::InvalidHexString => "The supplied string is not in hexadecimal format", +            LibraryError::TargetBufferTooSmall => "The target buffer is too small", +            LibraryError::InvalidString => "You passed a string containing a null byte", +        }) +    } +} diff --git a/nitrokey/src/lib.rs b/nitrokey/src/lib.rs index 02a622b..f2d524e 100644 --- a/nitrokey/src/lib.rs +++ b/nitrokey/src/lib.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org> +// SPDX-License-Identifier: MIT +  //! Provides access to a Nitrokey device using the native libnitrokey API.  //!  //! # Usage @@ -25,9 +28,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 +41,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 +63,9 @@  //!  //! ```no_run  //! use nitrokey::{Device, GenerateOtp}; -//! # use nitrokey::CommandError; +//! # use nitrokey::Error;  //! -//! # fn try_main() -> Result<(), (CommandError)> { +//! # fn try_main() -> Result<(), Error> {  //! let device = nitrokey::connect()?;  //! match device.get_hotp_code(1) {  //!     Ok(code) => println!("Generated HOTP code: {}", code), @@ -89,10 +92,13 @@  mod auth;  mod config;  mod device; +mod error;  mod otp;  mod pws;  mod util; +use std::fmt; +  use nitrokey_sys;  pub use crate::auth::{Admin, Authenticate, User}; @@ -101,9 +107,15 @@ 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};  pub use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData};  pub use crate::pws::{GetPasswordSafe, PasswordSafe, SLOT_COUNT}; -pub use crate::util::{CommandError, LogLevel}; +pub use crate::util::LogLevel; + +/// The default admin PIN for all Nitrokey devices. +pub const DEFAULT_ADMIN_PIN: &str = "12345678"; +/// The default user PIN for all Nitrokey devices. +pub const DEFAULT_USER_PIN: &str = "123456";  /// A version of the libnitrokey library.  /// @@ -125,6 +137,16 @@ pub struct Version {      pub minor: u32,  } +impl fmt::Display for Version { +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +        if self.git.is_empty() { +            write!(f, "v{}.{}", self.major, self.minor) +        } else { +            f.write_str(&self.git) +        } +    } +} +  /// 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`][]). @@ -149,21 +171,30 @@ pub fn set_log_level(level: LogLevel) {  /// Returns the libnitrokey library version.  /// +/// # Errors +/// +/// - [`Utf8Error`][] if libnitrokey returned an invalid UTF-8 string +///  /// # Example  ///  /// ``` -/// let version = nitrokey::get_library_version(); +/// # fn main() -> Result<(), nitrokey::Error> { +/// let version = nitrokey::get_library_version()?;  /// println!("Using libnitrokey {}", version.git); +/// #    Ok(()) +/// # }  /// ``` -pub fn get_library_version() -> Version { +/// +/// [`Utf8Error`]: enum.Error.html#variant.Utf8Error +pub fn get_library_version() -> Result<Version, Error> {      // 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() {          String::new()      } else { -        util::owned_str_from_ptr(git) +        util::owned_str_from_ptr(git)?      };      let major = unsafe { nitrokey_sys::NK_get_major_library_version() };      let minor = unsafe { nitrokey_sys::NK_get_minor_library_version() }; -    Version { git, major, minor } +    Ok(Version { git, major, minor })  } diff --git a/nitrokey/src/otp.rs b/nitrokey/src/otp.rs index 901bef9..6e0379b 100644 --- a/nitrokey/src/otp.rs +++ b/nitrokey/src/otp.rs @@ -1,8 +1,12 @@ +// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org> +// SPDX-License-Identifier: MIT +  use std::ffi::CString;  use nitrokey_sys; -use crate::util::{get_command_result, get_cstring, result_from_string, CommandError}; +use crate::error::Error; +use crate::util::{get_command_result, get_cstring, result_from_string};  /// Modes for one-time password generation.  #[derive(Clone, Copy, Debug, PartialEq)] @@ -28,9 +32,9 @@ pub trait ConfigureOtp {      ///      /// ```no_run      /// use nitrokey::{Authenticate, ConfigureOtp, OtpMode, OtpSlotData}; -    /// # use nitrokey::CommandError; +    /// # use nitrokey::Error;      /// -    /// # fn try_main() -> Result<(), (CommandError)> { +    /// # fn try_main() -> Result<(), Error> {      /// let device = nitrokey::connect()?;      /// let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::SixDigits);      /// match device.authenticate_admin("12345678") { @@ -46,10 +50,10 @@ pub trait ConfigureOtp {      /// # }      /// ```      /// -    /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString +    /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`NoName`]: enum.CommandError.html#variant.NoName -    fn write_hotp_slot(&self, data: OtpSlotData, counter: u64) -> Result<(), CommandError>; +    fn write_hotp_slot(&self, data: OtpSlotData, counter: u64) -> Result<(), Error>;      /// Configure a TOTP slot with the given data and set the TOTP time window to the given value      /// (default 30). @@ -64,9 +68,9 @@ pub trait ConfigureOtp {      ///      /// ```no_run      /// use nitrokey::{Authenticate, ConfigureOtp, OtpMode, OtpSlotData}; -    /// # use nitrokey::CommandError; +    /// # use nitrokey::Error;      /// -    /// # fn try_main() -> Result<(), (CommandError)> { +    /// # fn try_main() -> Result<(), Error> {      /// let device = nitrokey::connect()?;      /// let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::EightDigits);      /// match device.authenticate_admin("12345678") { @@ -82,10 +86,10 @@ pub trait ConfigureOtp {      /// # }      /// ```      /// -    /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString +    /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`NoName`]: enum.CommandError.html#variant.NoName -    fn write_totp_slot(&self, data: OtpSlotData, time_window: u16) -> Result<(), CommandError>; +    fn write_totp_slot(&self, data: OtpSlotData, time_window: u16) -> Result<(), Error>;      /// Erases an HOTP slot.      /// @@ -97,9 +101,9 @@ pub trait ConfigureOtp {      ///      /// ```no_run      /// use nitrokey::{Authenticate, ConfigureOtp}; -    /// # use nitrokey::CommandError; +    /// # use nitrokey::Error;      /// -    /// # fn try_main() -> Result<(), (CommandError)> { +    /// # fn try_main() -> Result<(), Error> {      /// let device = nitrokey::connect()?;      /// match device.authenticate_admin("12345678") {      ///     Ok(admin) => { @@ -114,8 +118,8 @@ pub trait ConfigureOtp {      /// # }      /// ```      /// -    /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot -    fn erase_hotp_slot(&self, slot: u8) -> Result<(), CommandError>; +    /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot +    fn erase_hotp_slot(&self, slot: u8) -> Result<(), Error>;      /// Erases a TOTP slot.      /// @@ -127,9 +131,9 @@ pub trait ConfigureOtp {      ///      /// ```no_run      /// use nitrokey::{Authenticate, ConfigureOtp}; -    /// # use nitrokey::CommandError; +    /// # use nitrokey::Error;      /// -    /// # fn try_main() -> Result<(), (CommandError)> { +    /// # fn try_main() -> Result<(), Error> {      /// let device = nitrokey::connect()?;      /// match device.authenticate_admin("12345678") {      ///     Ok(admin) => { @@ -144,8 +148,8 @@ pub trait ConfigureOtp {      /// # }      /// ```      /// -    /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot -    fn erase_totp_slot(&self, slot: u8) -> Result<(), CommandError>; +    /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot +    fn erase_totp_slot(&self, slot: u8) -> Result<(), Error>;  }  /// Provides methods to generate OTP codes and to query OTP slots on a Nitrokey @@ -164,9 +168,9 @@ pub trait GenerateOtp {      /// ```no_run      /// use std::time;      /// use nitrokey::GenerateOtp; -    /// # use nitrokey::CommandError; +    /// # use nitrokey::Error;      /// -    /// # fn try_main() -> Result<(), CommandError> { +    /// # fn try_main() -> Result<(), Error> {      /// let device = nitrokey::connect()?;      /// let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH);      /// match time { @@ -183,7 +187,7 @@ pub trait GenerateOtp {      ///      /// [`get_totp_code`]: #method.get_totp_code      /// [`Timestamp`]: enum.CommandError.html#variant.Timestamp -    fn set_time(&self, time: u64, force: bool) -> Result<(), CommandError> { +    fn set_time(&self, time: u64, force: bool) -> Result<(), Error> {          let result = if force {              unsafe { nitrokey_sys::NK_totp_set_time(time) }          } else { @@ -202,23 +206,23 @@ pub trait GenerateOtp {      /// # Example      ///      /// ```no_run -    /// use nitrokey::{CommandError, GenerateOtp}; +    /// use nitrokey::{CommandError, Error, GenerateOtp};      /// -    /// # fn try_main() -> Result<(), CommandError> { +    /// # fn try_main() -> Result<(), Error> {      /// let device = nitrokey::connect()?;      /// match device.get_hotp_slot_name(1) {      ///     Ok(name) => println!("HOTP slot 1: {}", name), -    ///     Err(CommandError::SlotNotProgrammed) => println!("HOTP slot 1 not programmed"), +    ///     Err(Error::CommandError(CommandError::SlotNotProgrammed)) => println!("HOTP slot 1 not programmed"),      ///     Err(err) => println!("Could not get slot name: {}", err),      /// };      /// #     Ok(())      /// # }      /// ```      /// -    /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot +    /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot      /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed -    fn get_hotp_slot_name(&self, slot: u8) -> Result<String, CommandError> { -        unsafe { result_from_string(nitrokey_sys::NK_get_hotp_slot_name(slot)) } +    fn get_hotp_slot_name(&self, slot: u8) -> Result<String, Error> { +        result_from_string(unsafe { nitrokey_sys::NK_get_hotp_slot_name(slot) })      }      /// Returns the name of the given TOTP slot. @@ -231,23 +235,23 @@ pub trait GenerateOtp {      /// # Example      ///      /// ```no_run -    /// use nitrokey::{CommandError, GenerateOtp}; +    /// use nitrokey::{CommandError, Error, GenerateOtp};      /// -    /// # fn try_main() -> Result<(), CommandError> { +    /// # fn try_main() -> Result<(), Error> {      /// let device = nitrokey::connect()?;      /// match device.get_totp_slot_name(1) {      ///     Ok(name) => println!("TOTP slot 1: {}", name), -    ///     Err(CommandError::SlotNotProgrammed) => println!("TOTP slot 1 not programmed"), +    ///     Err(Error::CommandError(CommandError::SlotNotProgrammed)) => println!("TOTP slot 1 not programmed"),      ///     Err(err) => println!("Could not get slot name: {}", err),      /// };      /// #     Ok(())      /// # }      /// ```      /// -    /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot +    /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot      /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed -    fn get_totp_slot_name(&self, slot: u8) -> Result<String, CommandError> { -        unsafe { result_from_string(nitrokey_sys::NK_get_totp_slot_name(slot)) } +    fn get_totp_slot_name(&self, slot: u8) -> Result<String, Error> { +        result_from_string(unsafe { nitrokey_sys::NK_get_totp_slot_name(slot) })      }      /// Generates an HOTP code on the given slot.  This operation may require user authorization, @@ -263,9 +267,9 @@ pub trait GenerateOtp {      ///      /// ```no_run      /// use nitrokey::GenerateOtp; -    /// # use nitrokey::CommandError; +    /// # use nitrokey::Error;      /// -    /// # fn try_main() -> Result<(), CommandError> { +    /// # fn try_main() -> Result<(), Error> {      /// let device = nitrokey::connect()?;      /// let code = device.get_hotp_code(1)?;      /// println!("Generated HOTP code on slot 1: {}", code); @@ -274,13 +278,11 @@ pub trait GenerateOtp {      /// ```      ///      /// [`get_config`]: trait.Device.html#method.get_config -    /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot +    /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot      /// [`NotAuthorized`]: enum.CommandError.html#variant.NotAuthorized      /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed -    fn get_hotp_code(&self, slot: u8) -> Result<String, CommandError> { -        unsafe { -            return result_from_string(nitrokey_sys::NK_get_hotp_code(slot)); -        } +    fn get_hotp_code(&self, slot: u8) -> Result<String, Error> { +        result_from_string(unsafe { nitrokey_sys::NK_get_hotp_code(slot) })      }      /// Generates a TOTP code on the given slot.  This operation may require user authorization, @@ -300,9 +302,9 @@ pub trait GenerateOtp {      /// ```no_run      /// use std::time;      /// use nitrokey::GenerateOtp; -    /// # use nitrokey::CommandError; +    /// # use nitrokey::Error;      /// -    /// # fn try_main() -> Result<(), CommandError> { +    /// # fn try_main() -> Result<(), Error> {      /// let device = nitrokey::connect()?;      /// let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH);      /// match time { @@ -319,13 +321,11 @@ pub trait GenerateOtp {      ///      /// [`set_time`]: #method.set_time      /// [`get_config`]: trait.Device.html#method.get_config -    /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot +    /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot      /// [`NotAuthorized`]: enum.CommandError.html#variant.NotAuthorized      /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed -    fn get_totp_code(&self, slot: u8) -> Result<String, CommandError> { -        unsafe { -            return result_from_string(nitrokey_sys::NK_get_totp_code(slot, 0, 0, 0)); -        } +    fn get_totp_code(&self, slot: u8) -> Result<String, Error> { +        result_from_string(unsafe { nitrokey_sys::NK_get_totp_code(slot, 0, 0, 0) })      }  } @@ -395,7 +395,7 @@ impl OtpSlotData {  }  impl RawOtpSlotData { -    pub fn new(data: OtpSlotData) -> Result<RawOtpSlotData, CommandError> { +    pub fn new(data: OtpSlotData) -> Result<RawOtpSlotData, Error> {          let name = get_cstring(data.name)?;          let secret = get_cstring(data.secret)?;          let use_token_id = data.token_id.is_some(); diff --git a/nitrokey/src/pws.rs b/nitrokey/src/pws.rs index 28f0681..fcf057b 100644 --- a/nitrokey/src/pws.rs +++ b/nitrokey/src/pws.rs @@ -1,10 +1,12 @@ +// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org> +// SPDX-License-Identifier: MIT +  use libc;  use nitrokey_sys;  use crate::device::{Device, DeviceWrapper, Pro, Storage}; -use crate::util::{ -    get_command_result, get_cstring, get_last_error, result_from_string, CommandError, -}; +use crate::error::{CommandError, Error}; +use crate::util::{get_command_result, get_cstring, get_last_error, result_from_string};  /// The number of slots in a [`PasswordSafe`][].  /// @@ -30,9 +32,9 @@ pub const SLOT_COUNT: u8 = 16;  ///  /// ```no_run  /// use nitrokey::{Device, GetPasswordSafe, PasswordSafe}; -/// # use nitrokey::CommandError; +/// # use nitrokey::Error;  /// -/// fn use_password_safe(pws: &PasswordSafe) -> Result<(), CommandError> { +/// fn use_password_safe(pws: &PasswordSafe) -> Result<(), Error> {  ///     let name = pws.get_slot_name(0)?;  ///     let login = pws.get_slot_login(0)?;  ///     let password = pws.get_slot_login(0)?; @@ -40,7 +42,7 @@ pub const SLOT_COUNT: u8 = 16;  ///     Ok(())  /// }  /// -/// # fn try_main() -> Result<(), CommandError> { +/// # fn try_main() -> Result<(), Error> {  /// let device = nitrokey::connect()?;  /// let pws = device.get_password_safe("123456")?;  /// use_password_safe(&pws); @@ -53,6 +55,7 @@ pub const SLOT_COUNT: u8 = 16;  /// [`get_password_safe`]: trait.GetPasswordSafe.html#method.get_password_safe  /// [`lock`]: trait.Device.html#method.lock  /// [`GetPasswordSafe`]: trait.GetPasswordSafe.html +#[derive(Debug)]  pub struct PasswordSafe<'a> {      _device: &'a dyn Device,  } @@ -89,11 +92,11 @@ pub trait GetPasswordSafe {      ///      /// ```no_run      /// use nitrokey::{Device, GetPasswordSafe, PasswordSafe}; -    /// # use nitrokey::CommandError; +    /// # use nitrokey::Error;      ///      /// fn use_password_safe(pws: &PasswordSafe) {}      /// -    /// # fn try_main() -> Result<(), CommandError> { +    /// # fn try_main() -> Result<(), Error> {      /// let device = nitrokey::connect()?;      /// match device.get_password_safe("123456") {      ///     Ok(pws) => { @@ -110,28 +113,24 @@ pub trait GetPasswordSafe {      /// [`lock`]: trait.Device.html#method.lock      /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed      /// [`Device::build_aes_key`]: trait.Device.html#method.build_aes_key -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`Unknown`]: enum.CommandError.html#variant.Unknown      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword -    fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, CommandError>; +    fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, Error>;  }  fn get_password_safe<'a>(      device: &'a dyn Device,      user_pin: &str, -) -> Result<PasswordSafe<'a>, CommandError> { +) -> Result<PasswordSafe<'a>, Error> {      let user_pin_string = get_cstring(user_pin)?; -    let result = unsafe { -        get_command_result(nitrokey_sys::NK_enable_password_safe( -            user_pin_string.as_ptr(), -        )) -    }; -    result.map(|()| PasswordSafe { _device: device }) +    get_command_result(unsafe { nitrokey_sys::NK_enable_password_safe(user_pin_string.as_ptr()) }) +        .map(|_| PasswordSafe { _device: device })  } -fn get_pws_result(s: String) -> Result<String, CommandError> { +fn get_pws_result(s: String) -> Result<String, Error> {      if s.is_empty() { -        Err(CommandError::SlotNotProgrammed) +        Err(CommandError::SlotNotProgrammed.into())      } else {          Ok(s)      } @@ -146,9 +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)| { @@ -161,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()); @@ -191,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) => { @@ -208,10 +207,10 @@ impl<'a> PasswordSafe<'a> {      /// # }      /// ```      /// -    /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot +    /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot      /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed -    pub fn get_slot_name(&self, slot: u8) -> Result<String, CommandError> { -        unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_name(slot)) } +    pub fn get_slot_name(&self, slot: u8) -> Result<String, Error> { +        result_from_string(unsafe { nitrokey_sys::NK_get_password_safe_slot_name(slot) })              .and_then(get_pws_result)      } @@ -228,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)?; @@ -241,10 +240,10 @@ impl<'a> PasswordSafe<'a> {      /// # }      /// ```      /// -    /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot +    /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot      /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed -    pub fn get_slot_login(&self, slot: u8) -> Result<String, CommandError> { -        unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_login(slot)) } +    pub fn get_slot_login(&self, slot: u8) -> Result<String, Error> { +        result_from_string(unsafe { nitrokey_sys::NK_get_password_safe_slot_login(slot) })              .and_then(get_pws_result)      } @@ -261,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)?; @@ -274,10 +273,10 @@ impl<'a> PasswordSafe<'a> {      /// # }      /// ```      /// -    /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot +    /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot      /// [`SlotNotProgrammed`]: enum.CommandError.html#variant.SlotNotProgrammed -    pub fn get_slot_password(&self, slot: u8) -> Result<String, CommandError> { -        unsafe { result_from_string(nitrokey_sys::NK_get_password_safe_slot_password(slot)) } +    pub fn get_slot_password(&self, slot: u8) -> Result<String, Error> { +        result_from_string(unsafe { nitrokey_sys::NK_get_password_safe_slot_password(slot) })              .and_then(get_pws_result)      } @@ -292,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)?; @@ -305,26 +304,26 @@ impl<'a> PasswordSafe<'a> {      /// # }      /// ```      /// -    /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot -    /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString +    /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      pub fn write_slot(          &self,          slot: u8,          name: &str,          login: &str,          password: &str, -    ) -> Result<(), CommandError> { +    ) -> Result<(), Error> {          let name_string = get_cstring(name)?;          let login_string = get_cstring(login)?;          let password_string = get_cstring(password)?; -        unsafe { -            get_command_result(nitrokey_sys::NK_write_password_safe_slot( +        get_command_result(unsafe { +            nitrokey_sys::NK_write_password_safe_slot(                  slot,                  name_string.as_ptr(),                  login_string.as_ptr(),                  password_string.as_ptr(), -            )) -        } +            ) +        })      }      /// Erases the given slot.  Erasing clears the stored name, login and password (if the slot was @@ -338,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,9 +350,9 @@ impl<'a> PasswordSafe<'a> {      /// # }      /// ```      /// -    /// [`InvalidSlot`]: enum.CommandError.html#variant.InvalidSlot -    pub fn erase_slot(&self, slot: u8) -> Result<(), CommandError> { -        unsafe { get_command_result(nitrokey_sys::NK_erase_password_safe_slot(slot)) } +    /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot +    pub fn erase_slot(&self, slot: u8) -> Result<(), Error> { +        get_command_result(unsafe { nitrokey_sys::NK_erase_password_safe_slot(slot) })      }  } @@ -365,19 +364,19 @@ impl<'a> Drop for PasswordSafe<'a> {  }  impl GetPasswordSafe for Pro { -    fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, CommandError> { +    fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> {          get_password_safe(self, user_pin)      }  }  impl GetPasswordSafe for Storage { -    fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, CommandError> { +    fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> {          get_password_safe(self, user_pin)      }  }  impl GetPasswordSafe for DeviceWrapper { -    fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, CommandError> { +    fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> {          get_password_safe(self, user_pin)      }  } diff --git a/nitrokey/src/util.rs b/nitrokey/src/util.rs index 567c478..b7e8cd3 100644 --- a/nitrokey/src/util.rs +++ b/nitrokey/src/util.rs @@ -1,53 +1,14 @@ -use std::borrow; +// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org> +// SPDX-License-Identifier: MIT +  use std::ffi::{CStr, CString}; -use std::fmt;  use std::os::raw::{c_char, c_int};  use libc::{c_void, free};  use rand_core::RngCore;  use rand_os::OsRng; -/// Error types returned by Nitrokey device or by the library. -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum CommandError { -    /// A packet with a wrong checksum has been sent or received. -    WrongCrc, -    /// A command tried to access an OTP slot that does not exist. -    WrongSlot, -    /// A command tried to generate an OTP on a slot that is not configured. -    SlotNotProgrammed, -    /// The provided password is wrong. -    WrongPassword, -    /// You are not authorized for this command or provided a wrong temporary -    /// password. -    NotAuthorized, -    /// An error occurred when getting or setting the time. -    Timestamp, -    /// You did not provide a name for the OTP slot. -    NoName, -    /// This command is not supported by this device. -    NotSupported, -    /// This command is unknown. -    UnknownCommand, -    /// AES decryption failed. -    AesDecryptionFailed, -    /// An unknown error occurred. -    Unknown(i64), -    /// An unspecified error occurred. -    Undefined, -    /// You passed a string containing a null byte. -    InvalidString, -    /// A supplied string exceeded a length limit. -    StringTooLong, -    /// You passed an invalid slot. -    InvalidSlot, -    /// The supplied string was not in hexadecimal format. -    InvalidHexString, -    /// The target buffer was smaller than the source. -    TargetBufferTooSmall, -    /// An error occurred during random number generation. -    RngError, -} +use crate::error::{Error, LibraryError};  /// Log level for libnitrokey.  /// @@ -70,126 +31,56 @@ pub enum LogLevel {      DebugL2,  } -pub fn owned_str_from_ptr(ptr: *const c_char) -> String { -    unsafe { -        return CStr::from_ptr(ptr).to_string_lossy().into_owned(); -    } +pub fn owned_str_from_ptr(ptr: *const c_char) -> Result<String, Error> { +    unsafe { CStr::from_ptr(ptr) } +        .to_str() +        .map(String::from) +        .map_err(Error::from)  } -pub fn result_from_string(ptr: *const c_char) -> Result<String, CommandError> { +pub fn result_from_string(ptr: *const c_char) -> Result<String, Error> {      if ptr.is_null() { -        return Err(CommandError::Undefined); +        return Err(Error::UnexpectedError);      } -    unsafe { -        let s = owned_str_from_ptr(ptr); -        free(ptr as *mut c_void); -        // An empty string can both indicate an error or be a valid return value.  In this case, we -        // have to check the last command status to decide what to return. -        if s.is_empty() { -            get_last_result().map(|_| s) -        } else { -            Ok(s) -        } +    let s = owned_str_from_ptr(ptr)?; +    unsafe { free(ptr as *mut c_void) }; +    // An empty string can both indicate an error or be a valid return value.  In this case, we +    // have to check the last command status to decide what to return. +    if s.is_empty() { +        get_last_result().map(|_| s) +    } else { +        Ok(s)      }  } -pub fn get_command_result(value: c_int) -> Result<(), CommandError> { -    match value { -        0 => Ok(()), -        other => Err(CommandError::from(other)), +pub fn get_command_result(value: c_int) -> Result<(), Error> { +    if value == 0 { +        Ok(()) +    } else { +        Err(Error::from(value))      }  } -pub fn get_last_result() -> Result<(), CommandError> { -    let value = unsafe { nitrokey_sys::NK_get_last_command_status() } as c_int; -    get_command_result(value) +pub fn get_last_result() -> Result<(), Error> { +    get_command_result(unsafe { nitrokey_sys::NK_get_last_command_status() }.into())  } -pub fn get_last_error() -> CommandError { -    return match get_last_result() { -        Ok(()) => CommandError::Undefined, +pub fn get_last_error() -> Error { +    match get_last_result() { +        Ok(()) => Error::UnexpectedError,          Err(err) => err, -    }; +    }  } -pub fn generate_password(length: usize) -> Result<Vec<u8>, CommandError> { -    let mut rng = OsRng::new()?; +pub fn generate_password(length: usize) -> Result<Vec<u8>, Error> { +    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)  } -pub fn get_cstring<T: Into<Vec<u8>>>(s: T) -> Result<CString, CommandError> { -    CString::new(s).or(Err(CommandError::InvalidString)) -} - -impl CommandError { -    fn as_str(&self) -> borrow::Cow<'static, str> { -        match *self { -            CommandError::WrongCrc => { -                "A packet with a wrong checksum has been sent or received".into() -            } -            CommandError::WrongSlot => "The given OTP slot does not exist".into(), -            CommandError::SlotNotProgrammed => "The given OTP slot is not programmed".into(), -            CommandError::WrongPassword => "The given password is wrong".into(), -            CommandError::NotAuthorized => { -                "You are not authorized for this command or provided a wrong temporary \ -                 password" -                    .into() -            } -            CommandError::Timestamp => "An error occurred when getting or setting the time".into(), -            CommandError::NoName => "You did not provide a name for the OTP slot".into(), -            CommandError::NotSupported => "This command is not supported by this device".into(), -            CommandError::UnknownCommand => "This command is unknown".into(), -            CommandError::AesDecryptionFailed => "AES decryption failed".into(), -            CommandError::Unknown(x) => { -                borrow::Cow::from(format!("An unknown error occurred ({})", x)) -            } -            CommandError::Undefined => "An unspecified error occurred".into(), -            CommandError::InvalidString => "You passed a string containing a null byte".into(), -            CommandError::StringTooLong => "The supplied string is too long".into(), -            CommandError::InvalidSlot => "The given slot is invalid".into(), -            CommandError::InvalidHexString => { -                "The supplied string is not in hexadecimal format".into() -            } -            CommandError::TargetBufferTooSmall => "The target buffer is too small".into(), -            CommandError::RngError => "An error occurred during random number generation".into(), -        } -    } -} - -impl fmt::Display for CommandError { -    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -        write!(f, "{}", self.as_str()) -    } -} - -impl From<c_int> for CommandError { -    fn from(value: c_int) -> Self { -        match value { -            1 => CommandError::WrongCrc, -            2 => CommandError::WrongSlot, -            3 => CommandError::SlotNotProgrammed, -            4 => CommandError::WrongPassword, -            5 => CommandError::NotAuthorized, -            6 => CommandError::Timestamp, -            7 => CommandError::NoName, -            8 => CommandError::NotSupported, -            9 => CommandError::UnknownCommand, -            10 => CommandError::AesDecryptionFailed, -            200 => CommandError::StringTooLong, -            201 => CommandError::InvalidSlot, -            202 => CommandError::InvalidHexString, -            203 => CommandError::TargetBufferTooSmall, -            x => CommandError::Unknown(x.into()), -        } -    } -} - -impl From<rand_core::Error> for CommandError { -    fn from(_error: rand_core::Error) -> Self { -        CommandError::RngError -    } +pub fn get_cstring<T: Into<Vec<u8>>>(s: T) -> Result<CString, Error> { +    CString::new(s).or_else(|_| Err(LibraryError::InvalidString.into()))  }  impl Into<i32> for LogLevel {  | 
