diff options
| author | Robin Krahl <robin.krahl@ireas.org> | 2019-07-09 08:09:02 +0000 | 
|---|---|---|
| committer | Robin Krahl <robin.krahl@ireas.org> | 2019-07-09 08:27:55 +0000 | 
| commit | 12fa62483cf45d868099d5d4020333af492eebde (patch) | |
| tree | 2ad466dbfeafb21365a3625f0beb8e2c6c392b2f | |
| parent | fe2f39826ade5a156945dabb8c8ab725378a15c1 (diff) | |
| download | nitrokey-rs-12fa62483cf45d868099d5d4020333af492eebde.tar.gz nitrokey-rs-12fa62483cf45d868099d5d4020333af492eebde.tar.bz2 | |
Introduce into_manager for Device
To enable applications like nitrokey-test to go back to a manager
instance from a Device instance, we add the into_manager function to the
Device trait.  To do that, we have to keep track of the Manager’s
lifetime by adding a lifetime to Device (and then to some other traits
that use Device).
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | src/auth.rs | 69 | ||||
| -rw-r--r-- | src/device.rs | 64 | ||||
| -rw-r--r-- | src/error.rs | 2 | ||||
| -rw-r--r-- | src/pws.rs | 30 | ||||
| -rw-r--r-- | tests/device.rs | 16 | ||||
| -rw-r--r-- | tests/otp.rs | 4 | ||||
| -rw-r--r-- | tests/pws.rs | 4 | 
8 files changed, 123 insertions, 67 deletions
| diff --git a/CHANGELOG.md b/CHANGELOG.md index 88e68dd..06769bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ SPDX-License-Identifier: MIT    - Add `ConcurrentAccessError` and `PoisonError` `Error` variants.    - Add the `Manager` struct that manages connections to Nitrokey devices.    - Remove `connect`, `connect_model`, `Pro::connect` and `Storage::connect`. +  - Add the `into_manager` function to the `Device` trait.  # v0.3.4 (2019-01-20)  - Fix authentication methods that assumed that `char` is signed. diff --git a/src/auth.rs b/src/auth.rs index 2ed7bfc..829d083 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,6 +1,7 @@  // Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>  // SPDX-License-Identifier: MIT +use std::marker;  use std::ops;  use std::os::raw::c_char;  use std::os::raw::c_int; @@ -18,7 +19,7 @@ static TEMPORARY_PASSWORD_LENGTH: usize = 25;  /// Provides methods to authenticate as a user or as an admin using a PIN.  The authenticated  /// methods will consume the current device instance.  On success, they return the authenticated  /// device.  Otherwise, they return the current unauthenticated device and the error code. -pub trait Authenticate { +pub trait Authenticate<'a> {      /// Performs user authentication.  This method consumes the device.  If successful, an      /// authenticated device is returned.  Otherwise, the current unauthenticated device and the      /// error are returned. @@ -61,9 +62,9 @@ pub trait Authenticate {      /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`RngError`]: enum.CommandError.html#variant.RngError      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword -    fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)> +    fn authenticate_user(self, password: &str) -> Result<User<'a, Self>, (Self, Error)>      where -        Self: Device + Sized; +        Self: Device<'a> + Sized;      /// Performs admin authentication.  This method consumes the device.  If successful, an      /// authenticated device is returned.  Otherwise, the current unauthenticated device and the @@ -107,9 +108,9 @@ pub trait Authenticate {      /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`RngError`]: enum.CommandError.html#variant.RngError      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword -    fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)> +    fn authenticate_admin(self, password: &str) -> Result<Admin<'a, Self>, (Self, Error)>      where -        Self: Device + Sized; +        Self: Device<'a> + Sized;  }  trait AuthenticatedDevice<T> { @@ -128,9 +129,10 @@ trait AuthenticatedDevice<T> {  /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin  /// [`device`]: #method.device  #[derive(Debug)] -pub struct User<T: Device> { +pub struct User<'a, T: Device<'a>> {      device: T,      temp_password: Vec<u8>, +    marker: marker::PhantomData<&'a T>,  }  /// A Nitrokey device with admin authentication. @@ -143,14 +145,15 @@ pub struct User<T: Device> {  /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin  /// [`device`]: #method.device  #[derive(Debug)] -pub struct Admin<T: Device> { +pub struct Admin<'a, T: Device<'a>> {      device: T,      temp_password: Vec<u8>, +    marker: marker::PhantomData<&'a T>,  } -fn authenticate<D, A, T>(device: D, password: &str, callback: T) -> Result<A, (D, Error)> +fn authenticate<'a, D, A, T>(device: D, password: &str, callback: T) -> Result<A, (D, Error)>  where -    D: Device, +    D: Device<'a>,      A: AuthenticatedDevice<D>,      T: Fn(*const c_char, *const c_char) -> c_int,  { @@ -174,9 +177,9 @@ fn authenticate_user_wrapper<'a, T, C>(      device: T,      constructor: C,      password: &str, -) -> Result<User<DeviceWrapper<'a>>, (DeviceWrapper<'a>, Error)> +) -> Result<User<'a, DeviceWrapper<'a>>, (DeviceWrapper<'a>, Error)>  where -    T: Device, +    T: Device<'a> + 'a,      C: Fn(T) -> DeviceWrapper<'a>,  {      let result = device.authenticate_user(password); @@ -190,9 +193,9 @@ fn authenticate_admin_wrapper<'a, T, C>(      device: T,      constructor: C,      password: &str, -) -> Result<Admin<DeviceWrapper<'a>>, (DeviceWrapper<'a>, Error)> +) -> Result<Admin<'a, DeviceWrapper<'a>>, (DeviceWrapper<'a>, Error)>  where -    T: Device, +    T: Device<'a> + 'a,      C: Fn(T) -> DeviceWrapper<'a>,  {      let result = device.authenticate_admin(password); @@ -202,7 +205,7 @@ where      }  } -impl<T: Device> User<T> { +impl<'a, T: Device<'a>> User<'a, T> {      /// Forgets the user authentication and returns an unauthenticated device.  This method      /// consumes the authenticated device.  It does not perform any actual commands on the      /// Nitrokey. @@ -211,7 +214,7 @@ impl<T: Device> User<T> {      }  } -impl<T: Device> ops::Deref for User<T> { +impl<'a, T: Device<'a>> ops::Deref for User<'a, T> {      type Target = T;      fn deref(&self) -> &Self::Target { @@ -219,13 +222,13 @@ impl<T: Device> ops::Deref for User<T> {      }  } -impl<T: Device> ops::DerefMut for User<T> { +impl<'a, T: Device<'a>> ops::DerefMut for User<'a, T> {      fn deref_mut(&mut self) -> &mut T {          &mut self.device      }  } -impl<T: Device> GenerateOtp for User<T> { +impl<'a, T: Device<'a>> GenerateOtp for User<'a, 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()) @@ -239,11 +242,12 @@ impl<T: Device> GenerateOtp for User<T> {      }  } -impl<T: Device> AuthenticatedDevice<T> for User<T> { +impl<'a, T: Device<'a>> AuthenticatedDevice<T> for User<'a, T> {      fn new(device: T, temp_password: Vec<u8>) -> Self {          User {              device,              temp_password, +            marker: marker::PhantomData,          }      } @@ -252,7 +256,7 @@ impl<T: Device> AuthenticatedDevice<T> for User<T> {      }  } -impl<T: Device> ops::Deref for Admin<T> { +impl<'a, T: Device<'a>> ops::Deref for Admin<'a, T> {      type Target = T;      fn deref(&self) -> &Self::Target { @@ -260,13 +264,13 @@ impl<T: Device> ops::Deref for Admin<T> {      }  } -impl<T: Device> ops::DerefMut for Admin<T> { +impl<'a, T: Device<'a>> ops::DerefMut for Admin<'a, T> {      fn deref_mut(&mut self) -> &mut T {          &mut self.device      }  } -impl<T: Device> Admin<T> { +impl<'a, T: Device<'a>> Admin<'a, T> {      /// Forgets the user authentication and returns an unauthenticated device.  This method      /// consumes the authenticated device.  It does not perform any actual commands on the      /// Nitrokey. @@ -316,7 +320,7 @@ impl<T: Device> Admin<T> {      }  } -impl<T: Device> ConfigureOtp for Admin<T> { +impl<'a, T: Device<'a>> ConfigureOtp for Admin<'a, T> {      fn write_hotp_slot(&mut self, data: OtpSlotData, counter: u64) -> Result<(), Error> {          let raw_data = RawOtpSlotData::new(data)?;          get_command_result(unsafe { @@ -364,11 +368,12 @@ impl<T: Device> ConfigureOtp for Admin<T> {      }  } -impl<T: Device> AuthenticatedDevice<T> for Admin<T> { +impl<'a, T: Device<'a>> AuthenticatedDevice<T> for Admin<'a, T> {      fn new(device: T, temp_password: Vec<u8>) -> Self {          Admin {              device,              temp_password, +            marker: marker::PhantomData,          }      } @@ -377,8 +382,8 @@ impl<T: Device> AuthenticatedDevice<T> for Admin<T> {      }  } -impl<'a> Authenticate for DeviceWrapper<'a> { -    fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)> { +impl<'a> Authenticate<'a> for DeviceWrapper<'a> { +    fn authenticate_user(self, password: &str) -> Result<User<'a, Self>, (Self, Error)> {          match self {              DeviceWrapper::Storage(storage) => {                  authenticate_user_wrapper(storage, DeviceWrapper::Storage, password) @@ -387,7 +392,7 @@ impl<'a> Authenticate for DeviceWrapper<'a> {          }      } -    fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)> { +    fn authenticate_admin(self, password: &str) -> Result<Admin<'a, Self>, (Self, Error)> {          match self {              DeviceWrapper::Storage(storage) => {                  authenticate_admin_wrapper(storage, DeviceWrapper::Storage, password) @@ -399,28 +404,28 @@ impl<'a> Authenticate for DeviceWrapper<'a> {      }  } -impl<'a> Authenticate for Pro<'a> { -    fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)> { +impl<'a> Authenticate<'a> for Pro<'a> { +    fn authenticate_user(self, password: &str) -> Result<User<'a, 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, Error)> { +    fn authenticate_admin(self, password: &str) -> Result<Admin<'a, Self>, (Self, Error)> {          authenticate(self, password, |password_ptr, temp_password_ptr| unsafe {              nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr)          })      }  } -impl<'a> Authenticate for Storage<'a> { -    fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)> { +impl<'a> Authenticate<'a> for Storage<'a> { +    fn authenticate_user(self, password: &str) -> Result<User<'a, 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, Error)> { +    fn authenticate_admin(self, password: &str) -> Result<Admin<'a, 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 50ff071..e1a71fa 100644 --- a/src/device.rs +++ b/src/device.rs @@ -156,7 +156,7 @@ pub enum DeviceWrapper<'a> {  /// [`Pro::connect`]: #method.connect  #[derive(Debug)]  pub struct Pro<'a> { -    manager: &'a mut crate::Manager, +    manager: Option<&'a mut crate::Manager>,  }  /// A Nitrokey Storage device without user or admin authentication. @@ -200,7 +200,7 @@ pub struct Pro<'a> {  /// [`Storage::connect`]: #method.connect  #[derive(Debug)]  pub struct Storage<'a> { -    manager: &'a mut crate::Manager, +    manager: Option<&'a mut crate::Manager>,  }  /// The status of a volume on a Nitrokey Storage device. @@ -291,7 +291,32 @@ pub struct StorageStatus {  ///  /// This trait provides the commands that can be executed without authentication and that are  /// present on all supported Nitrokey devices. -pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { +pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt::Debug { +    /// Returns the [`Manager`][] instance that has been used to connect to this device. +    /// +    /// # Example +    /// +    /// ``` +    /// use nitrokey::{Device, DeviceWrapper}; +    /// +    /// fn do_something(device: DeviceWrapper) { +    ///     // reconnect to any device +    ///     let manager = device.into_manager(); +    ///     let device = manager.connect(); +    ///     // do something with the device +    ///     // ... +    /// } +    /// +    /// # fn main() -> Result<(), nitrokey::Error> { +    /// match nitrokey::take()?.connect() { +    ///     Ok(device) => do_something(device), +    ///     Err(err) => println!("Could not connect to a Nitrokey: {}", err), +    /// } +    /// #     Ok(()) +    /// # } +    /// ``` +    fn into_manager(self) -> &'a mut crate::Manager; +      /// Returns the model of the connected Nitrokey device.      ///      /// # Example @@ -657,14 +682,14 @@ pub(crate) fn connect_enum(model: Model) -> bool {  }  impl<'a> DeviceWrapper<'a> { -    fn device(&self) -> &dyn Device { +    fn device(&self) -> &dyn Device<'a> {          match *self {              DeviceWrapper::Storage(ref storage) => storage,              DeviceWrapper::Pro(ref pro) => pro,          }      } -    fn device_mut(&mut self) -> &mut dyn Device { +    fn device_mut(&mut self) -> &mut dyn Device<'a> {          match *self {              DeviceWrapper::Storage(ref mut storage) => storage,              DeviceWrapper::Pro(ref mut pro) => pro, @@ -702,7 +727,14 @@ impl<'a> GenerateOtp for DeviceWrapper<'a> {      }  } -impl<'a> Device for DeviceWrapper<'a> { +impl<'a> Device<'a> for DeviceWrapper<'a> { +    fn into_manager(self) -> &'a mut crate::Manager { +        match self { +            DeviceWrapper::Pro(dev) => dev.into_manager(), +            DeviceWrapper::Storage(dev) => dev.into_manager(), +        } +    } +      fn get_model(&self) -> Model {          match *self {              DeviceWrapper::Pro(_) => Model::Pro, @@ -713,7 +745,9 @@ impl<'a> Device for DeviceWrapper<'a> {  impl<'a> Pro<'a> {      pub(crate) fn new(manager: &'a mut crate::Manager) -> Pro<'a> { -        Pro { manager } +        Pro { +            manager: Some(manager), +        }      }  } @@ -725,7 +759,11 @@ impl<'a> Drop for Pro<'a> {      }  } -impl<'a> Device for Pro<'a> { +impl<'a> Device<'a> for Pro<'a> { +    fn into_manager(mut self) -> &'a mut crate::Manager { +        self.manager.take().unwrap() +    } +      fn get_model(&self) -> Model {          Model::Pro      } @@ -735,7 +773,9 @@ impl<'a> GenerateOtp for Pro<'a> {}  impl<'a> Storage<'a> {      pub(crate) fn new(manager: &'a mut crate::Manager) -> Storage<'a> { -        Storage { manager } +        Storage { +            manager: Some(manager), +        }      }      /// Changes the update PIN. @@ -1248,7 +1288,11 @@ impl<'a> Drop for Storage<'a> {      }  } -impl<'a> Device for Storage<'a> { +impl<'a> Device<'a> for Storage<'a> { +    fn into_manager(mut self) -> &'a mut crate::Manager { +        self.manager.take().unwrap() +    } +      fn get_model(&self) -> Model {          Model::Storage      } diff --git a/src/error.rs b/src/error.rs index b84f5eb..9e6adc0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -85,7 +85,7 @@ impl From<sync::TryLockError<sync::MutexGuard<'static, crate::Manager>>> for Err      }  } -impl<T: device::Device> From<(T, Error)> for Error { +impl<'a, T: device::Device<'a>> From<(T, Error)> for Error {      fn from((_, err): (T, Error)) -> Self {          err      } @@ -57,8 +57,8 @@ pub const SLOT_COUNT: u8 = 16;  /// [`lock`]: trait.Device.html#method.lock  /// [`GetPasswordSafe`]: trait.GetPasswordSafe.html  #[derive(Debug)] -pub struct PasswordSafe<'a> { -    _device: &'a dyn Device, +pub struct PasswordSafe<'a, 'b> { +    _device: &'a dyn Device<'b>,  }  /// Provides access to a [`PasswordSafe`][]. @@ -67,7 +67,7 @@ pub struct PasswordSafe<'a> {  /// retrieved from it.  ///  /// [`PasswordSafe`]: struct.PasswordSafe.html -pub trait GetPasswordSafe { +pub trait GetPasswordSafe<'a> {      /// Enables and returns the password safe.      ///      /// The underlying device must always live at least as long as a password safe retrieved from @@ -117,13 +117,13 @@ pub trait GetPasswordSafe {      /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString      /// [`Unknown`]: enum.CommandError.html#variant.Unknown      /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword -    fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_>, Error>; +    fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_, 'a>, Error>;  } -fn get_password_safe<'a>( -    device: &'a dyn Device, +fn get_password_safe<'a, 'b>( +    device: &'a dyn Device<'b>,      user_pin: &str, -) -> Result<PasswordSafe<'a>, Error> { +) -> Result<PasswordSafe<'a, 'b>, Error> {      let user_pin_string = get_cstring(user_pin)?;      get_command_result(unsafe { nitrokey_sys::NK_enable_password_safe(user_pin_string.as_ptr()) })          .map(|_| PasswordSafe { _device: device }) @@ -137,7 +137,7 @@ fn get_pws_result(s: String) -> Result<String, Error> {      }  } -impl<'a> PasswordSafe<'a> { +impl<'a, 'b> PasswordSafe<'a, 'b> {      /// Returns the status of all password slots.      ///      /// The status indicates whether a slot is programmed or not. @@ -357,27 +357,27 @@ impl<'a> PasswordSafe<'a> {      }  } -impl<'a> Drop for PasswordSafe<'a> { +impl<'a, 'b> Drop for PasswordSafe<'a, 'b> {      fn drop(&mut self) {          // TODO: disable the password safe -- NK_lock_device has side effects on the Nitrokey          // Storage, see https://github.com/Nitrokey/nitrokey-storage-firmware/issues/65      }  } -impl<'a> GetPasswordSafe for Pro<'a> { -    fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> { +impl<'a> GetPasswordSafe<'a> for Pro<'a> { +    fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_, 'a>, Error> {          get_password_safe(self, user_pin)      }  } -impl<'a> GetPasswordSafe for Storage<'a> { -    fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> { +impl<'a> GetPasswordSafe<'a> for Storage<'a> { +    fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_, 'a>, Error> {          get_password_safe(self, user_pin)      }  } -impl<'a> GetPasswordSafe for DeviceWrapper<'a> { -    fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> { +impl<'a> GetPasswordSafe<'a> for DeviceWrapper<'a> { +    fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_, 'a>, Error> {          get_password_safe(self, user_pin)      }  } diff --git a/tests/device.rs b/tests/device.rs index b377f2e..76f38e6 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -101,7 +101,10 @@ fn get_firmware_version(device: Pro) {      assert!(version.minor > 0);  } -fn admin_retry<T: Authenticate + Device>(device: T, suffix: &str, count: u8) -> T { +fn admin_retry<'a, T>(device: T, suffix: &str, count: u8) -> T +where +    T: Authenticate<'a> + Device<'a> + 'a, +{      let result = device.authenticate_admin(&(DEFAULT_ADMIN_PIN.to_owned() + suffix));      let device = match result {          Ok(admin) => admin.device(), @@ -111,7 +114,10 @@ fn admin_retry<T: Authenticate + Device>(device: T, suffix: &str, count: u8) ->      return device;  } -fn user_retry<T: Authenticate + Device>(device: T, suffix: &str, count: u8) -> T { +fn user_retry<'a, T>(device: T, suffix: &str, count: u8) -> T +where +    T: Authenticate<'a> + Device<'a> + 'a, +{      let result = device.authenticate_user(&(DEFAULT_USER_PIN.to_owned() + suffix));      let device = match result {          Ok(admin) => admin.device(), @@ -220,10 +226,10 @@ fn change_admin_pin(device: DeviceWrapper) {      device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err();  } -fn require_failed_user_login<D>(device: D, password: &str, error: CommandError) -> D +fn require_failed_user_login<'a, D>(device: D, password: &str, error: CommandError) -> D  where -    D: Device + Authenticate, -    nitrokey::User<D>: std::fmt::Debug, +    D: Device<'a> + Authenticate<'a> + 'a, +    nitrokey::User<'a, D>: std::fmt::Debug,  {      let result = device.authenticate_user(password);      assert!(result.is_err()); diff --git a/tests/otp.rs b/tests/otp.rs index c0bbecf..aafda59 100644 --- a/tests/otp.rs +++ b/tests/otp.rs @@ -36,9 +36,9 @@ enum TotpTimestampSize {      U64,  } -fn make_admin_test_device<T>(device: T) -> Admin<T> +fn make_admin_test_device<'a, T>(device: T) -> Admin<'a, T>  where -    T: Device, +    T: Device<'a>,      (T, nitrokey::Error): Debug,  {      unwrap_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)) diff --git a/tests/pws.rs b/tests/pws.rs index b0e5abe..7169695 100644 --- a/tests/pws.rs +++ b/tests/pws.rs @@ -32,9 +32,9 @@ fn get_slot_name_direct(slot: u8) -> Result<String, Error> {      }  } -fn get_pws<T>(device: &mut T) -> PasswordSafe +fn get_pws<'a, T>(device: &mut T) -> PasswordSafe<'_, 'a>  where -    T: Device, +    T: Device<'a>,  {      unwrap_ok!(device.get_password_safe(DEFAULT_USER_PIN))  } | 
