diff options
| -rw-r--r-- | CHANGELOG.md | 4 | ||||
| -rw-r--r-- | TODO.md | 1 | ||||
| -rw-r--r-- | src/auth.rs | 183 | ||||
| -rw-r--r-- | src/device.rs | 42 | ||||
| -rw-r--r-- | src/lib.rs | 4 | ||||
| -rw-r--r-- | src/otp.rs | 16 | ||||
| -rw-r--r-- | src/pws.rs | 24 | ||||
| -rw-r--r-- | tests/device.rs | 151 | ||||
| -rw-r--r-- | tests/otp.rs | 62 | ||||
| -rw-r--r-- | tests/util/mod.rs | 12 | 
10 files changed, 301 insertions, 198 deletions
| diff --git a/CHANGELOG.md b/CHANGELOG.md index 9227510..e98e857 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,10 +38,6 @@ SPDX-License-Identifier: MIT    - Implement `DerefMut` for `User<T>` and `Admin<T>`.    - Add `device_mut` method to `DeviceWrapper`.    - Require a mutable `Device` reference if a method changes the device state. -- Let `Admin` and `User` store a mutable reference to the `Device` instead of -  the `Device` value. -- Let `PasswordStore` store a mutable reference to the `Device` instead of a -  non-mutable reference.  # v0.3.4 (2019-01-20)  - Fix authentication methods that assumed that `char` is signed. @@ -13,6 +13,7 @@ SPDX-License-Identifier: MIT  - Clear passwords from memory.  - Lock password safe in `PasswordSafe::drop()` (see [nitrokey-storage-firmware    issue 65][]). +- Disable creation of multiple password safes at the same time.  - Check timing in Storage tests.  - Consider restructuring `device::StorageStatus`. diff --git a/src/auth.rs b/src/auth.rs index 573fed3..f9f50fa 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -42,10 +42,16 @@ pub trait Authenticate {      /// fn perform_other_task(device: &DeviceWrapper) {}      ///      /// # fn try_main() -> Result<(), Error> { -    /// let mut device = nitrokey::connect()?; -    /// match device.authenticate_user("123456") { -    ///     Ok(user) => perform_user_task(&user), -    ///     Err(err) => eprintln!("Could not authenticate as user: {}", err), +    /// let device = nitrokey::connect()?; +    /// let device = match device.authenticate_user("123456") { +    ///     Ok(user) => { +    ///         perform_user_task(&user); +    ///         user.device() +    ///     }, +    ///     Err((device, err)) => { +    ///         eprintln!("Could not authenticate as user: {}", err); +    ///         device +    ///     },      /// };      /// perform_other_task(&device);      /// #     Ok(()) @@ -55,9 +61,9 @@ pub trait Authenticate {      /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`RngError`]: enum.CommandError.html#variant.RngError      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword -    fn authenticate_user(&mut self, password: &str) -> Result<User<'_, Self>, Error> +    fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)>      where -        Self: Device + std::marker::Sized; +        Self: Device + Sized;      /// Performs admin authentication.  This method consumes the device.  If successful, an      /// authenticated device is returned.  Otherwise, the current unauthenticated device and the @@ -82,10 +88,16 @@ pub trait Authenticate {      /// fn perform_other_task(device: &DeviceWrapper) {}      ///      /// # fn try_main() -> Result<(), Error> { -    /// let mut device = nitrokey::connect()?; -    /// match device.authenticate_admin("123456") { -    ///     Ok(admin) => perform_admin_task(&admin), -    ///     Err(err) => eprintln!("Could not authenticate as admin: {}", err), +    /// let device = nitrokey::connect()?; +    /// let device = match device.authenticate_admin("123456") { +    ///     Ok(admin) => { +    ///         perform_admin_task(&admin); +    ///         admin.device() +    ///     }, +    ///     Err((device, err)) => { +    ///         eprintln!("Could not authenticate as admin: {}", err); +    ///         device +    ///     },      /// };      /// perform_other_task(&device);      /// #     Ok(()) @@ -95,13 +107,13 @@ pub trait Authenticate {      /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`RngError`]: enum.CommandError.html#variant.RngError      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword -    fn authenticate_admin(&mut self, password: &str) -> Result<Admin<'_, Self>, Error> +    fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)>      where -        Self: Device + std::marker::Sized; +        Self: Device + Sized;  } -trait AuthenticatedDevice<'a, T> { -    fn new(device: &'a mut T, temp_password: Vec<u8>) -> Self; +trait AuthenticatedDevice<T> { +    fn new(device: T, temp_password: Vec<u8>) -> Self;      fn temp_password_ptr(&self) -> *const c_char;  } @@ -116,8 +128,8 @@ trait AuthenticatedDevice<'a, T> {  /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin  /// [`device`]: #method.device  #[derive(Debug)] -pub struct User<'a, T: Device> { -    device: &'a mut T, +pub struct User<T: Device> { +    device: T,      temp_password: Vec<u8>,  } @@ -131,42 +143,89 @@ pub struct User<'a, T: Device> {  /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin  /// [`device`]: #method.device  #[derive(Debug)] -pub struct Admin<'a, T: Device> { -    device: &'a mut T, +pub struct Admin<T: Device> { +    device: T,      temp_password: Vec<u8>,  } -fn authenticate<'a, D, A, T>(device: &'a mut D, password: &str, callback: T) -> Result<A, Error> +fn authenticate<D, A, T>(device: D, password: &str, callback: T) -> Result<A, (D, Error)>  where      D: Device, -    A: AuthenticatedDevice<'a, D>, +    A: AuthenticatedDevice<D>,      T: Fn(*const c_char, *const c_char) -> c_int,  { -    let temp_password = generate_password(TEMPORARY_PASSWORD_LENGTH)?; -    let password = get_cstring(password)?; +    let temp_password = match generate_password(TEMPORARY_PASSWORD_LENGTH) { +        Ok(temp_password) => temp_password, +        Err(err) => return Err((device, err)), +    }; +    let password = match get_cstring(password) { +        Ok(password) => password, +        Err(err) => return Err((device, err)), +    };      let password_ptr = password.as_ptr();      let temp_password_ptr = temp_password.as_ptr() as *const c_char;      match callback(password_ptr, temp_password_ptr) {          0 => Ok(A::new(device, temp_password)), -        rv => Err(Error::from(rv)), +        rv => Err((device, Error::from(rv))),      }  } -impl<'a, T: Device> ops::Deref for User<'a, T> { +fn authenticate_user_wrapper<T, C>( +    device: T, +    constructor: C, +    password: &str, +) -> Result<User<DeviceWrapper>, (DeviceWrapper, Error)> +where +    T: Device, +    C: Fn(T) -> DeviceWrapper, +{ +    let result = device.authenticate_user(password); +    match result { +        Ok(user) => Ok(User::new(constructor(user.device), user.temp_password)), +        Err((device, err)) => Err((constructor(device), err)), +    } +} + +fn authenticate_admin_wrapper<T, C>( +    device: T, +    constructor: C, +    password: &str, +) -> Result<Admin<DeviceWrapper>, (DeviceWrapper, Error)> +where +    T: Device, +    C: Fn(T) -> DeviceWrapper, +{ +    let result = device.authenticate_admin(password); +    match result { +        Ok(user) => Ok(Admin::new(constructor(user.device), user.temp_password)), +        Err((device, err)) => Err((constructor(device), err)), +    } +} + +impl<T: Device> User<T> { +    /// Forgets the user authentication and returns an unauthenticated device.  This method +    /// consumes the authenticated device.  It does not perform any actual commands on the +    /// Nitrokey. +    pub fn device(self) -> T { +        self.device +    } +} + +impl<T: Device> ops::Deref for User<T> {      type Target = T;      fn deref(&self) -> &Self::Target { -        self.device +        &self.device      }  } -impl<'a, T: Device> ops::DerefMut for User<'a, T> { +impl<T: Device> ops::DerefMut for User<T> {      fn deref_mut(&mut self) -> &mut T { -        self.device +        &mut self.device      }  } -impl<'a, T: Device> GenerateOtp for User<'a, T> { +impl<T: Device> GenerateOtp for User<T> {      fn get_hotp_code(&mut self, slot: u8) -> Result<String, Error> {          result_from_string(unsafe {              nitrokey_sys::NK_get_hotp_code_PIN(slot, self.temp_password_ptr()) @@ -180,8 +239,8 @@ impl<'a, T: Device> GenerateOtp for User<'a, T> {      }  } -impl<'a, T: Device> AuthenticatedDevice<'a, T> for User<'a, T> { -    fn new(device: &'a mut T, temp_password: Vec<u8>) -> Self { +impl<T: Device> AuthenticatedDevice<T> for User<T> { +    fn new(device: T, temp_password: Vec<u8>) -> Self {          User {              device,              temp_password, @@ -193,21 +252,28 @@ impl<'a, T: Device> AuthenticatedDevice<'a, T> for User<'a, T> {      }  } -impl<'a, T: Device> ops::Deref for Admin<'a, T> { +impl<T: Device> ops::Deref for Admin<T> {      type Target = T;      fn deref(&self) -> &Self::Target { -        self.device +        &self.device      }  } -impl<'a, T: Device> ops::DerefMut for Admin<'a, T> { +impl<T: Device> ops::DerefMut for Admin<T> {      fn deref_mut(&mut self) -> &mut T { -        self.device +        &mut self.device      }  } -impl<'a, T: Device> Admin<'a, T> { +impl<T: Device> Admin<T> { +    /// Forgets the user authentication and returns an unauthenticated device.  This method +    /// consumes the authenticated device.  It does not perform any actual commands on the +    /// Nitrokey. +    pub fn device(self) -> T { +        self.device +    } +      /// Writes the given configuration to the Nitrokey device.      ///      /// # Errors @@ -221,11 +287,14 @@ impl<'a, T: Device> Admin<'a, T> {      /// # use nitrokey::Error;      ///      /// # fn try_main() -> Result<(), Error> { -    /// let mut device = nitrokey::connect()?; +    /// let device = nitrokey::connect()?;      /// let config = Config::new(None, None, None, false);      /// match device.authenticate_admin("12345678") { -    ///     Ok(mut admin) => admin.write_config(config)?, -    ///     Err(err) => eprintln!("Could not authenticate as admin: {}", err), +    ///     Ok(mut admin) => { +    ///         admin.write_config(config); +    ///         () +    ///     }, +    ///     Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err),      /// };      /// #     Ok(())      /// # } @@ -247,7 +316,7 @@ impl<'a, T: Device> Admin<'a, T> {      }  } -impl<'a, T: Device> ConfigureOtp for Admin<'a, T> { +impl<T: Device> ConfigureOtp for Admin<T> {      fn write_hotp_slot(&mut self, data: OtpSlotData, counter: u64) -> Result<(), Error> {          let raw_data = RawOtpSlotData::new(data)?;          get_command_result(unsafe { @@ -295,8 +364,8 @@ impl<'a, T: Device> ConfigureOtp for Admin<'a, T> {      }  } -impl<'a, T: Device> AuthenticatedDevice<'a, T> for Admin<'a, T> { -    fn new(device: &'a mut T, temp_password: Vec<u8>) -> Self { +impl<T: Device> AuthenticatedDevice<T> for Admin<T> { +    fn new(device: T, temp_password: Vec<u8>) -> Self {          Admin {              device,              temp_password, @@ -309,27 +378,35 @@ impl<'a, T: Device> AuthenticatedDevice<'a, T> for Admin<'a, T> {  }  impl Authenticate for DeviceWrapper { -    fn authenticate_user(&mut self, password: &str) -> Result<User<'_, Self>, Error> { -        authenticate(self, password, |password_ptr, temp_password_ptr| unsafe { -            nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr) -        }) +    fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)> { +        match self { +            DeviceWrapper::Storage(storage) => { +                authenticate_user_wrapper(storage, DeviceWrapper::Storage, password) +            } +            DeviceWrapper::Pro(pro) => authenticate_user_wrapper(pro, DeviceWrapper::Pro, password), +        }      } -    fn authenticate_admin(&mut self, password: &str) -> Result<Admin<'_, 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, Error)> { +        match self { +            DeviceWrapper::Storage(storage) => { +                authenticate_admin_wrapper(storage, DeviceWrapper::Storage, password) +            } +            DeviceWrapper::Pro(pro) => { +                authenticate_admin_wrapper(pro, DeviceWrapper::Pro, password) +            } +        }      }  }  impl Authenticate for Pro { -    fn authenticate_user(&mut self, password: &str) -> Result<User<'_, Self>, Error> { +    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(&mut self, password: &str) -> Result<Admin<'_, Self>, Error> { +    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)          }) @@ -337,13 +414,13 @@ impl Authenticate for Pro {  }  impl Authenticate for Storage { -    fn authenticate_user(&mut self, password: &str) -> Result<User<'_, Self>, Error> { +    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(&mut self, password: &str) -> Result<Admin<'_, Self>, Error> { +    fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)> {          authenticate(self, password, |password_ptr, temp_password_ptr| unsafe {              nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr)          }) diff --git a/src/device.rs b/src/device.rs index a0df30e..f6492cd 100644 --- a/src/device.rs +++ b/src/device.rs @@ -71,10 +71,16 @@ impl fmt::Display for VolumeMode {  /// fn perform_other_task(device: &DeviceWrapper) {}  ///  /// # fn try_main() -> Result<(), Error> { -/// let mut device = nitrokey::connect()?; -/// match device.authenticate_user("123456") { -///     Ok(user) => perform_user_task(&user), -///     Err(err) => eprintln!("Could not authenticate as user: {}", err), +/// let device = nitrokey::connect()?; +/// let device = match device.authenticate_user("123456") { +///     Ok(user) => { +///         perform_user_task(&user); +///         user.device() +///     }, +///     Err((device, err)) => { +///         eprintln!("Could not authenticate as user: {}", err); +///         device +///     },  /// };  /// perform_other_task(&device);  /// #     Ok(()) @@ -129,10 +135,16 @@ pub enum DeviceWrapper {  /// fn perform_other_task(device: &Pro) {}  ///  /// # fn try_main() -> Result<(), Error> { -/// let mut device = nitrokey::Pro::connect()?; -/// match device.authenticate_user("123456") { -///     Ok(user) => perform_user_task(&user), -///     Err(err) => eprintln!("Could not authenticate as user: {}", err), +/// let device = nitrokey::Pro::connect()?; +/// let device = match device.authenticate_user("123456") { +///     Ok(user) => { +///         perform_user_task(&user); +///         user.device() +///     }, +///     Err((device, err)) => { +///         eprintln!("Could not authenticate as user: {}", err); +///         device +///     },  /// };  /// perform_other_task(&device);  /// #     Ok(()) @@ -169,10 +181,16 @@ pub struct Pro {  /// fn perform_other_task(device: &Storage) {}  ///  /// # fn try_main() -> Result<(), Error> { -/// let mut device = nitrokey::Storage::connect()?; -/// match device.authenticate_user("123456") { -///     Ok(user) => perform_user_task(&user), -///     Err(err) => eprintln!("Could not authenticate as user: {}", err), +/// let device = nitrokey::Storage::connect()?; +/// let device = match device.authenticate_user("123456") { +///     Ok(user) => { +///         perform_user_task(&user); +///         user.device() +///     }, +///     Err((device, err)) => { +///         eprintln!("Could not authenticate as user: {}", err); +///         device +///     },  /// };  /// perform_other_task(&device);  /// #     Ok(()) @@ -44,7 +44,7 @@  //! # use nitrokey::Error;  //!  //! # fn try_main() -> Result<(), Error> { -//! let mut device = nitrokey::connect()?; +//! let device = nitrokey::connect()?;  //! let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::SixDigits);  //! match device.authenticate_admin("12345678") {  //!     Ok(mut admin) => { @@ -53,7 +53,7 @@  //!             Err(err) => eprintln!("Could not write slot: {}", err),  //!         }  //!     }, -//!     Err(err) => eprintln!("Could not authenticate as admin: {}", err), +//!     Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err),  //! }  //! #     Ok(())  //! # } @@ -35,7 +35,7 @@ pub trait ConfigureOtp {      /// # use nitrokey::Error;      ///      /// # fn try_main() -> Result<(), Error> { -    /// let mut device = nitrokey::connect()?; +    /// let device = nitrokey::connect()?;      /// let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::SixDigits);      /// match device.authenticate_admin("12345678") {      ///     Ok(mut admin) => { @@ -44,7 +44,7 @@ pub trait ConfigureOtp {      ///             Err(err) => eprintln!("Could not write slot: {}", err),      ///         }      ///     }, -    ///     Err(err) => eprintln!("Could not authenticate as admin: {}", err), +    ///     Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err),      /// }      /// #     Ok(())      /// # } @@ -71,7 +71,7 @@ pub trait ConfigureOtp {      /// # use nitrokey::Error;      ///      /// # fn try_main() -> Result<(), Error> { -    /// let mut device = nitrokey::connect()?; +    /// let device = nitrokey::connect()?;      /// let slot_data = OtpSlotData::new(1, "test", "01234567890123456689", OtpMode::EightDigits);      /// match device.authenticate_admin("12345678") {      ///     Ok(mut admin) => { @@ -80,7 +80,7 @@ pub trait ConfigureOtp {      ///             Err(err) => eprintln!("Could not write slot: {}", err),      ///         }      ///     }, -    ///     Err(err) => eprintln!("Could not authenticate as admin: {}", err), +    ///     Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err),      /// }      /// #     Ok(())      /// # } @@ -104,7 +104,7 @@ pub trait ConfigureOtp {      /// # use nitrokey::Error;      ///      /// # fn try_main() -> Result<(), Error> { -    /// let mut device = nitrokey::connect()?; +    /// let device = nitrokey::connect()?;      /// match device.authenticate_admin("12345678") {      ///     Ok(mut admin) => {      ///         match admin.erase_hotp_slot(1) { @@ -112,7 +112,7 @@ pub trait ConfigureOtp {      ///             Err(err) => eprintln!("Could not erase slot: {}", err),      ///         }      ///     }, -    ///     Err(err) => eprintln!("Could not authenticate as admin: {}", err), +    ///     Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err),      /// }      /// #     Ok(())      /// # } @@ -134,7 +134,7 @@ pub trait ConfigureOtp {      /// # use nitrokey::Error;      ///      /// # fn try_main() -> Result<(), Error> { -    /// let mut device = nitrokey::connect()?; +    /// let device = nitrokey::connect()?;      /// match device.authenticate_admin("12345678") {      ///     Ok(mut admin) => {      ///         match admin.erase_totp_slot(1) { @@ -142,7 +142,7 @@ pub trait ConfigureOtp {      ///             Err(err) => eprintln!("Could not erase slot: {}", err),      ///         }      ///     }, -    ///     Err(err) => eprintln!("Could not authenticate as admin: {}", err), +    ///     Err((_, err)) => eprintln!("Could not authenticate as admin: {}", err),      /// }      /// #     Ok(())      /// # } @@ -18,7 +18,8 @@ pub const SLOT_COUNT: u8 = 16;  /// The password safe stores a tuple consisting of a name, a login and a password on a slot.  The  /// number of available slots is [`SLOT_COUNT`][].  The slots are addressed starting with zero.  To  /// retrieve a password safe from a Nitrokey device, use the [`get_password_safe`][] method from -/// the [`GetPasswordSafe`][] trait. +/// the [`GetPasswordSafe`][] trait.  Note that the device must live at least as long as the +/// password safe.  ///  /// Once the password safe has been unlocked, it can be accessed without a password.  Therefore it  /// is mandatory to call [`lock`][] on the corresponding device after the password store is used. @@ -57,17 +58,21 @@ pub const SLOT_COUNT: u8 = 16;  /// [`GetPasswordSafe`]: trait.GetPasswordSafe.html  #[derive(Debug)]  pub struct PasswordSafe<'a> { -    device: &'a mut dyn Device, +    _device: &'a dyn Device,  }  /// Provides access to a [`PasswordSafe`][].  /// +/// The device that implements this trait must always live at least as long as a password safe +/// retrieved from it. +///  /// [`PasswordSafe`]: struct.PasswordSafe.html  pub trait GetPasswordSafe {      /// Enables and returns the password safe.      /// -    /// It is mandatory to lock the underlying device using [`lock`][] after the password safe has -    /// been used.  Otherwise, other applications can access the password store without +    /// The underlying device must always live at least as long as a password safe retrieved from +    /// it.  It is mandatory to lock the underlying device using [`lock`][] after the password safe +    /// has been used.  Otherwise, other applications can access the password store without      /// authentication.      ///      /// If this method returns an `AesDecryptionFailed` (Nitrokey Pro) or `Unknown` (Nitrokey @@ -116,17 +121,12 @@ pub trait GetPasswordSafe {  }  fn get_password_safe<'a>( -    device: &'a mut dyn Device, +    device: &'a dyn Device,      user_pin: &str,  ) -> Result<PasswordSafe<'a>, Error> {      let user_pin_string = get_cstring(user_pin)?; -    let result = get_command_result(unsafe { -        nitrokey_sys::NK_enable_password_safe(user_pin_string.as_ptr()) -    }); -    match result { -        Ok(()) => Ok(PasswordSafe { device }), -        Err(err) => Err(err), -    } +    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, Error> { diff --git a/tests/device.rs b/tests/device.rs index ecc3cfa..5c52024 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -97,34 +97,41 @@ fn get_firmware_version(device: Pro) {      assert!(version.minor > 0);  } -fn admin_retry<T: Authenticate + Device>(device: &mut T, suffix: &str, count: u8) { -    assert_any_ok!(device.authenticate_admin(&(DEFAULT_ADMIN_PIN.to_owned() + suffix))); +fn admin_retry<T: Authenticate + Device>(device: T, suffix: &str, count: u8) -> T { +    let result = device.authenticate_admin(&(DEFAULT_ADMIN_PIN.to_owned() + suffix)); +    let device = match result { +        Ok(admin) => admin.device(), +        Err((device, _)) => device, +    };      assert_ok!(count, device.get_admin_retry_count()); +    return device;  } -fn user_retry<T: Authenticate + Device>(device: &mut T, suffix: &str, count: u8) { -    assert_any_ok!(device.authenticate_user(&(DEFAULT_USER_PIN.to_owned() + suffix))); +fn user_retry<T: Authenticate + Device>(device: T, suffix: &str, count: u8) -> T { +    let result = device.authenticate_user(&(DEFAULT_USER_PIN.to_owned() + suffix)); +    let device = match result { +        Ok(admin) => admin.device(), +        Err((device, _)) => device, +    };      assert_ok!(count, device.get_user_retry_count()); +    return device;  }  #[test_device]  fn get_retry_count(device: DeviceWrapper) { -    let mut device = device; - -    admin_retry(&mut device, "", 3); -    admin_retry(&mut device, "123", 2); -    admin_retry(&mut device, "456", 1); -    admin_retry(&mut device, "", 3); - -    user_retry(&mut device, "", 3); -    user_retry(&mut device, "123", 2); -    user_retry(&mut device, "456", 1); -    user_retry(&mut device, "", 3); +    let device = admin_retry(device, "", 3); +    let device = admin_retry(device, "123", 2); +    let device = admin_retry(device, "456", 1); +    let device = admin_retry(device, "", 3); + +    let device = user_retry(device, "", 3); +    let device = user_retry(device, "123", 2); +    let device = user_retry(device, "456", 1); +    user_retry(device, "", 3);  }  #[test_device]  fn config(device: DeviceWrapper) { -    let mut device = device;      let mut admin = unwrap_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN));      let config = Config::new(None, None, None, true); @@ -145,24 +152,22 @@ fn config(device: DeviceWrapper) {  #[test_device]  fn change_user_pin(device: DeviceWrapper) { -    let mut device = device; -    assert_any_ok!(device.authenticate_user(DEFAULT_USER_PIN)); -    assert_cmd_err!( -        CommandError::WrongPassword, -        device.authenticate_user(USER_NEW_PASSWORD) -    ); +    let device = device.authenticate_user(DEFAULT_USER_PIN).unwrap().device(); +    let device = device.authenticate_user(USER_NEW_PASSWORD).unwrap_err().0; +    let mut device = device;      assert_ok!(          (),          device.change_user_pin(DEFAULT_USER_PIN, USER_NEW_PASSWORD)      ); -    assert_cmd_err!( -        CommandError::WrongPassword, -        device.authenticate_user(DEFAULT_USER_PIN) -    ); -    assert_any_ok!(device.authenticate_user(USER_NEW_PASSWORD)); +    let device = device.authenticate_user(DEFAULT_USER_PIN).unwrap_err().0; +    let device = device +        .authenticate_user(USER_NEW_PASSWORD) +        .unwrap() +        .device(); +    let mut device = device;      let result = device.change_user_pin(DEFAULT_USER_PIN, DEFAULT_USER_PIN);      assert_cmd_err!(CommandError::WrongPassword, result); @@ -171,32 +176,28 @@ fn change_user_pin(device: DeviceWrapper) {          device.change_user_pin(USER_NEW_PASSWORD, DEFAULT_USER_PIN)      ); -    assert_any_ok!(device.authenticate_user(DEFAULT_USER_PIN)); -    assert_cmd_err!( -        CommandError::WrongPassword, -        device.authenticate_user(USER_NEW_PASSWORD) -    ); +    let device = device.authenticate_user(DEFAULT_USER_PIN).unwrap().device(); +    assert!(device.authenticate_user(USER_NEW_PASSWORD).is_err());  }  #[test_device]  fn change_admin_pin(device: DeviceWrapper) { -    let mut device = device; -    assert_any_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)); -    assert_cmd_err!( -        CommandError::WrongPassword, -        device.authenticate_admin(ADMIN_NEW_PASSWORD) -    ); +    let device = device +        .authenticate_admin(DEFAULT_ADMIN_PIN) +        .unwrap() +        .device(); +    let mut device = device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err().0;      assert_ok!(          (),          device.change_admin_pin(DEFAULT_ADMIN_PIN, ADMIN_NEW_PASSWORD)      ); -    assert_cmd_err!( -        CommandError::WrongPassword, -        device.authenticate_admin(DEFAULT_ADMIN_PIN) -    ); -    assert_any_ok!(device.authenticate_admin(ADMIN_NEW_PASSWORD)); +    let device = device.authenticate_admin(DEFAULT_ADMIN_PIN).unwrap_err().0; +    let mut device = device +        .authenticate_admin(ADMIN_NEW_PASSWORD) +        .unwrap() +        .device();      assert_cmd_err!(          CommandError::WrongPassword, @@ -208,24 +209,31 @@ fn change_admin_pin(device: DeviceWrapper) {          device.change_admin_pin(ADMIN_NEW_PASSWORD, DEFAULT_ADMIN_PIN)      ); -    assert_any_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)); -    assert_cmd_err!( -        CommandError::WrongPassword, -        device.authenticate_admin(ADMIN_NEW_PASSWORD) -    ); +    let device = device +        .authenticate_admin(DEFAULT_ADMIN_PIN) +        .unwrap() +        .device(); +    device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err();  } -fn require_failed_user_login<D: Authenticate + Device>(device: &mut D, password: &str) { -    assert_cmd_err!( -        CommandError::WrongPassword, -        device.authenticate_user(password) -    ); +fn require_failed_user_login<D>(device: D, password: &str, error: CommandError) -> D +where +    D: Device + Authenticate, +    nitrokey::User<D>: std::fmt::Debug, +{ +    let result = device.authenticate_user(password); +    assert!(result.is_err()); +    let err = result.unwrap_err(); +    match err.1 { +        Error::CommandError(err) => assert_eq!(error, err), +        _ => assert!(false), +    }; +    err.0  }  #[test_device]  fn unlock_user_pin(device: DeviceWrapper) { -    let mut device = device; -    assert_any_ok!(device.authenticate_user(DEFAULT_USER_PIN)); +    let mut device = device.authenticate_user(DEFAULT_USER_PIN).unwrap().device();      assert_ok!(          (),          device.unlock_user_pin(DEFAULT_ADMIN_PIN, DEFAULT_USER_PIN) @@ -237,10 +245,11 @@ fn unlock_user_pin(device: DeviceWrapper) {      // block user PIN      let wrong_password = DEFAULT_USER_PIN.to_owned() + "foo"; -    require_failed_user_login(&mut device, &wrong_password); -    require_failed_user_login(&mut device, &wrong_password); -    require_failed_user_login(&mut device, &wrong_password); -    require_failed_user_login(&mut device, DEFAULT_USER_PIN); +    let device = require_failed_user_login(device, &wrong_password, CommandError::WrongPassword); +    let device = require_failed_user_login(device, &wrong_password, CommandError::WrongPassword); +    let device = require_failed_user_login(device, &wrong_password, CommandError::WrongPassword); +    let mut device = +        require_failed_user_login(device, DEFAULT_USER_PIN, CommandError::WrongPassword);      // unblock with current PIN      assert_cmd_err!( @@ -251,13 +260,14 @@ fn unlock_user_pin(device: DeviceWrapper) {          (),          device.unlock_user_pin(DEFAULT_ADMIN_PIN, DEFAULT_USER_PIN)      ); -    assert_any_ok!(device.authenticate_user(DEFAULT_USER_PIN)); +    let device = device.authenticate_user(DEFAULT_USER_PIN).unwrap().device();      // block user PIN -    require_failed_user_login(&mut device, &wrong_password); -    require_failed_user_login(&mut device, &wrong_password); -    require_failed_user_login(&mut device, &wrong_password); -    require_failed_user_login(&mut device, DEFAULT_USER_PIN); +    let device = require_failed_user_login(device, &wrong_password, CommandError::WrongPassword); +    let device = require_failed_user_login(device, &wrong_password, CommandError::WrongPassword); +    let device = require_failed_user_login(device, &wrong_password, CommandError::WrongPassword); +    let mut device = +        require_failed_user_login(device, DEFAULT_USER_PIN, CommandError::WrongPassword);      // unblock with new PIN      assert_cmd_err!( @@ -286,11 +296,11 @@ fn assert_utf8_err_or_ne(left: &str, right: Result<String, Error>) {  #[test_device]  fn factory_reset(device: DeviceWrapper) { -    let mut device = device;      let mut admin = unwrap_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN));      let otp_data = OtpSlotData::new(1, "test", "0123468790", OtpMode::SixDigits);      assert_ok!((), admin.write_totp_slot(otp_data, 30)); +    let mut device = admin.device();      let mut pws = unwrap_ok!(device.get_password_safe(DEFAULT_USER_PIN));      assert_ok!((), pws.write_slot(0, "test", "testlogin", "testpw"));      drop(pws); @@ -314,11 +324,15 @@ fn factory_reset(device: DeviceWrapper) {      );      assert_ok!((), device.factory_reset(ADMIN_NEW_PASSWORD)); -    assert_any_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)); +    let device = device +        .authenticate_admin(DEFAULT_ADMIN_PIN) +        .unwrap() +        .device();      let user = unwrap_ok!(device.authenticate_user(DEFAULT_USER_PIN));      assert_cmd_err!(CommandError::SlotNotProgrammed, user.get_totp_slot_name(1)); +    let mut device = user.device();      let pws = unwrap_ok!(device.get_password_safe(DEFAULT_USER_PIN));      assert_utf8_err_or_ne("test", pws.get_slot_name(0));      assert_utf8_err_or_ne("testlogin", pws.get_slot_login(0)); @@ -341,7 +355,10 @@ fn build_aes_key(device: DeviceWrapper) {      );      assert_ok!((), device.build_aes_key(DEFAULT_ADMIN_PIN)); -    assert_any_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)); +    let mut device = device +        .authenticate_admin(DEFAULT_ADMIN_PIN) +        .unwrap() +        .device();      let pws = unwrap_ok!(device.get_password_safe(DEFAULT_USER_PIN));      assert_utf8_err_or_ne("test", pws.get_slot_name(0)); diff --git a/tests/otp.rs b/tests/otp.rs index d55d54a..c0bbecf 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -3,6 +3,7 @@  mod util; +use std::fmt::Debug;  use std::ops::DerefMut;  use nitrokey::{ @@ -35,9 +36,10 @@ enum TotpTimestampSize {      U64,  } -fn make_admin_test_device<'a, T>(device: &'a mut T) -> Admin<'a, T> +fn make_admin_test_device<T>(device: T) -> Admin<T>  where      T: Device, +    (T, nitrokey::Error): Debug,  {      unwrap_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN))  } @@ -66,8 +68,7 @@ fn set_time(device: DeviceWrapper) {  #[test_device]  fn hotp_no_pin(device: DeviceWrapper) { -    let mut device = device; -    let mut admin = make_admin_test_device(&mut device); +    let mut admin = make_admin_test_device(device);      let config = Config::new(None, None, None, false);      assert_ok!((), admin.write_config(config)); @@ -78,38 +79,36 @@ fn hotp_no_pin(device: DeviceWrapper) {      check_hotp_codes(admin.deref_mut(), 5);      configure_hotp(&mut admin, 0); -    check_hotp_codes(&mut device, 0); +    check_hotp_codes(&mut admin.device(), 0);  }  #[test_device]  fn hotp_pin(device: DeviceWrapper) { -    let mut device = device; -    let mut admin = make_admin_test_device(&mut device); +    let mut admin = make_admin_test_device(device);      let config = Config::new(None, None, None, true);      assert_ok!((), admin.write_config(config));      configure_hotp(&mut admin, 0); -    let mut user = unwrap_ok!(device.authenticate_user(DEFAULT_USER_PIN)); +    let mut user = unwrap_ok!(admin.device().authenticate_user(DEFAULT_USER_PIN));      check_hotp_codes(&mut user, 0); -    assert_cmd_err!(CommandError::NotAuthorized, user.get_hotp_code(1)); +    assert_cmd_err!(CommandError::NotAuthorized, user.device().get_hotp_code(1));  }  #[test_device]  fn hotp_slot_name(device: DeviceWrapper) { -    let mut device = device; -    let mut admin = make_admin_test_device(&mut device); +    let mut admin = make_admin_test_device(device);      let slot_data = OtpSlotData::new(1, "test-hotp", HOTP_SECRET, OtpMode::SixDigits);      assert_ok!((), admin.write_hotp_slot(slot_data, 0)); +    let device = admin.device();      assert_ok!("test-hotp".to_string(), device.get_hotp_slot_name(1));      assert_lib_err!(LibraryError::InvalidSlot, device.get_hotp_slot_name(4));  }  #[test_device]  fn hotp_error(device: DeviceWrapper) { -    let mut device = device; -    let mut admin = make_admin_test_device(&mut device); +    let mut admin = make_admin_test_device(device);      let slot_data = OtpSlotData::new(1, "", HOTP_SECRET, OtpMode::SixDigits);      assert_cmd_err!(CommandError::NoName, admin.write_hotp_slot(slot_data, 0));      let slot_data = OtpSlotData::new(4, "test", HOTP_SECRET, OtpMode::SixDigits); @@ -128,8 +127,7 @@ fn hotp_error(device: DeviceWrapper) {  #[test_device]  fn hotp_erase(device: DeviceWrapper) { -    let mut device = device; -    let mut admin = make_admin_test_device(&mut device); +    let mut admin = make_admin_test_device(device);      let config = Config::new(None, None, None, false);      assert_ok!((), admin.write_config(config));      let slot_data = OtpSlotData::new(1, "test1", HOTP_SECRET, OtpMode::SixDigits); @@ -139,6 +137,7 @@ fn hotp_erase(device: DeviceWrapper) {      assert_ok!((), admin.erase_hotp_slot(1)); +    let mut device = admin.device();      let result = device.get_hotp_slot_name(1);      assert_cmd_err!(CommandError::SlotNotProgrammed, result);      let result = device.get_hotp_code(1); @@ -175,8 +174,7 @@ fn check_totp_codes(device: &mut GenerateOtp, factor: u64, timestamp_size: TotpT  #[test_device]  fn totp_no_pin(device: DeviceWrapper) { -    let mut device = device; -    let mut admin = make_admin_test_device(&mut device); +    let mut admin = make_admin_test_device(device);      let config = Config::new(None, None, None, false);      assert_ok!((), admin.write_config(config)); @@ -187,15 +185,14 @@ fn totp_no_pin(device: DeviceWrapper) {      check_totp_codes(admin.deref_mut(), 2, TotpTimestampSize::U32);      configure_totp(&mut admin, 1); -    check_totp_codes(&mut device, 1, TotpTimestampSize::U32); +    check_totp_codes(&mut admin.device(), 1, TotpTimestampSize::U32);  }  #[test_device]  // Nitrokey Storage does only support timestamps that fit in a 32-bit  // unsigned integer, so don't test with it.  fn totp_no_pin_64(device: Pro) { -    let mut device = device; -    let mut admin = make_admin_test_device(&mut device); +    let mut admin = make_admin_test_device(device);      let config = Config::new(None, None, None, false);      assert_ok!((), admin.write_config(config)); @@ -206,45 +203,43 @@ fn totp_no_pin_64(device: Pro) {      check_totp_codes(admin.deref_mut(), 2, TotpTimestampSize::U64);      configure_totp(&mut admin, 1); -    check_totp_codes(&mut device, 1, TotpTimestampSize::U64); +    check_totp_codes(&mut admin.device(), 1, TotpTimestampSize::U64);  }  #[test_device]  fn totp_pin(device: DeviceWrapper) { -    let mut device = device; -    let mut admin = make_admin_test_device(&mut device); +    let mut admin = make_admin_test_device(device);      let config = Config::new(None, None, None, true);      assert_ok!((), admin.write_config(config));      configure_totp(&mut admin, 1); -    let mut user = unwrap_ok!(device.authenticate_user(DEFAULT_USER_PIN)); +    let mut user = unwrap_ok!(admin.device().authenticate_user(DEFAULT_USER_PIN));      check_totp_codes(&mut user, 1, TotpTimestampSize::U32); -    assert_cmd_err!(CommandError::NotAuthorized, user.get_totp_code(1)); +    assert_cmd_err!(CommandError::NotAuthorized, user.device().get_totp_code(1));  }  #[test_device]  // See comment for totp_no_pin_64.  fn totp_pin_64(device: Pro) { -    let mut device = device; -    let mut admin = make_admin_test_device(&mut device); +    let mut admin = make_admin_test_device(device);      let config = Config::new(None, None, None, true);      assert_ok!((), admin.write_config(config));      configure_totp(&mut admin, 1); -    let mut user = unwrap_ok!(admin.authenticate_user(DEFAULT_USER_PIN)); +    let mut user = unwrap_ok!(admin.device().authenticate_user(DEFAULT_USER_PIN));      check_totp_codes(&mut user, 1, TotpTimestampSize::U64); -    assert_cmd_err!(CommandError::NotAuthorized, device.get_totp_code(1)); +    assert_cmd_err!(CommandError::NotAuthorized, user.device().get_totp_code(1));  }  #[test_device]  fn totp_slot_name(device: DeviceWrapper) { -    let mut device = device; -    let mut admin = make_admin_test_device(&mut device); +    let mut admin = make_admin_test_device(device);      let slot_data = OtpSlotData::new(1, "test-totp", TOTP_SECRET, OtpMode::EightDigits);      assert_ok!((), admin.write_totp_slot(slot_data, 0)); +    let device = admin.device();      let result = device.get_totp_slot_name(1);      assert_ok!("test-totp", result);      let result = device.get_totp_slot_name(16); @@ -253,8 +248,7 @@ fn totp_slot_name(device: DeviceWrapper) {  #[test_device]  fn totp_error(device: DeviceWrapper) { -    let mut device = device; -    let mut admin = make_admin_test_device(&mut device); +    let mut admin = make_admin_test_device(device);      let slot_data = OtpSlotData::new(1, "", TOTP_SECRET, OtpMode::SixDigits);      assert_cmd_err!(CommandError::NoName, admin.write_totp_slot(slot_data, 0));      let slot_data = OtpSlotData::new(20, "test", TOTP_SECRET, OtpMode::SixDigits); @@ -273,8 +267,7 @@ fn totp_error(device: DeviceWrapper) {  #[test_device]  fn totp_erase(device: DeviceWrapper) { -    let mut device = device; -    let mut admin = make_admin_test_device(&mut device); +    let mut admin = make_admin_test_device(device);      let config = Config::new(None, None, None, false);      assert_ok!((), admin.write_config(config));      let slot_data = OtpSlotData::new(1, "test1", TOTP_SECRET, OtpMode::SixDigits); @@ -284,6 +277,7 @@ fn totp_erase(device: DeviceWrapper) {      assert_ok!((), admin.erase_totp_slot(1)); +    let device = admin.device();      let result = device.get_totp_slot_name(1);      assert_cmd_err!(CommandError::SlotNotProgrammed, result);      let result = device.get_totp_code(1); diff --git a/tests/util/mod.rs b/tests/util/mod.rs index 5bd19d1..f2b20ec 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -3,7 +3,7 @@  #[macro_export]  macro_rules! unwrap_ok { -    ($val:expr) => { +    ($val:expr) => {{          match $val {              Ok(val) => val,              Err(err) => panic!( @@ -13,12 +13,12 @@ macro_rules! unwrap_ok {                  err              ),          } -    }; +    }};  }  #[macro_export]  macro_rules! assert_any_ok { -    ($val:expr) => { +    ($val:expr) => {{          match &$val {              Ok(_) => {}              Err(err) => panic!( @@ -28,12 +28,12 @@ macro_rules! assert_any_ok {                  err              ),          } -    }; +    }};  }  #[macro_export]  macro_rules! assert_ok { -    ($left:expr, $right:expr) => { +    ($left:expr, $right:expr) => {{          match &$right {              Ok(right) => match &$left {                  left => { @@ -54,7 +54,7 @@ macro_rules! assert_ok {                  $left, right_err              ),          } -    }; +    }};  }  #[macro_export] | 
