aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2019-07-16 09:14:36 +0000
committerRobin Krahl <robin.krahl@ireas.org>2019-07-16 11:15:35 +0200
commit678f0b700666a4ba86db2180078d79a730dc82e0 (patch)
tree47473abd762e34ffab66a01c614e0a0a488db789
parent6c138eaa850c745b97b7e48a201db0cbaad8e1e0 (diff)
downloadnitrokey-rs-next.tar.gz
nitrokey-rs-next.tar.bz2
Refactor the device module into submodulesnext
This patch splits the rather large device module into the submodules pro, storage and wrapper. This only changes the internal code structure and does not affect the public API.
-rw-r--r--CHANGELOG.md1
-rw-r--r--src/device/mod.rs466
-rw-r--r--src/device/pro.rs79
-rw-r--r--src/device/storage.rs (renamed from src/device.rs)687
-rw-r--r--src/device/wrapper.rs134
5 files changed, 701 insertions, 666 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 41e46a6..a5d049c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -51,6 +51,7 @@ SPDX-License-Identifier: MIT
- Add the `into_manager` function to the `Device` trait.
- Add the `force_take` function that ignores a `PoisonError` when accessing
the manager instance.
+- Internally refactor the `device` module into submodules.
# v0.3.4 (2019-01-20)
- Fix authentication methods that assumed that `char` is signed.
diff --git a/src/device/mod.rs b/src/device/mod.rs
new file mode 100644
index 0000000..af28ab5
--- /dev/null
+++ b/src/device/mod.rs
@@ -0,0 +1,466 @@
+// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
+mod pro;
+mod storage;
+mod wrapper;
+
+use std::fmt;
+
+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, result_or_error,
+};
+
+pub use pro::Pro;
+pub use storage::{
+ SdCardData, Storage, StorageProductionInfo, StorageStatus, VolumeMode, VolumeStatus,
+};
+pub use wrapper::DeviceWrapper;
+
+/// Available Nitrokey models.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum Model {
+ /// The Nitrokey Storage.
+ Storage,
+ /// The Nitrokey Pro.
+ Pro,
+}
+
+impl fmt::Display for Model {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(match *self {
+ Model::Pro => "Pro",
+ Model::Storage => "Storage",
+ })
+ }
+}
+
+/// 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 major: u8,
+ /// The minor firmware version, e. g. 40 in v0.40.
+ pub minor: u8,
+}
+
+impl fmt::Display for FirmwareVersion {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "v{}.{}", self.major, self.minor)
+ }
+}
+
+/// A Nitrokey device.
+///
+/// This trait provides the commands that can be executed without authentication and that are
+/// present on all supported Nitrokey devices.
+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
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::Error;
+ ///
+ /// # fn try_main() -> Result<(), Error> {
+ /// let mut manager = nitrokey::take()?;
+ /// let device = manager.connect()?;
+ /// println!("Connected to a Nitrokey {}", device.get_model());
+ /// # Ok(())
+ /// # }
+ fn get_model(&self) -> Model;
+
+ /// Returns the serial number of the Nitrokey device. The serial number is the string
+ /// representation of a hex number.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::Error;
+ ///
+ /// # fn try_main() -> Result<(), Error> {
+ /// let mut manager = nitrokey::take()?;
+ /// let device = manager.connect()?;
+ /// match device.get_serial_number() {
+ /// Ok(number) => println!("serial no: {}", number),
+ /// Err(err) => eprintln!("Could not get serial number: {}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ 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
+ /// available attempts is three.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::Error;
+ ///
+ /// # fn try_main() -> Result<(), Error> {
+ /// let mut manager = nitrokey::take()?;
+ /// let device = manager.connect()?;
+ /// let count = device.get_user_retry_count();
+ /// match device.get_user_retry_count() {
+ /// Ok(count) => println!("{} remaining authentication attempts (user)", count),
+ /// Err(err) => eprintln!("Could not get user retry count: {}", err),
+ /// }
+ /// # Ok(())
+ /// # }
+ /// ```
+ fn get_user_retry_count(&self) -> Result<u8, Error> {
+ result_or_error(unsafe { nitrokey_sys::NK_get_user_retry_count() })
+ }
+
+ /// Returns the number of remaining authentication attempts for the admin. The total number of
+ /// available attempts is three.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::Error;
+ ///
+ /// # fn try_main() -> Result<(), Error> {
+ /// let mut manager = nitrokey::take()?;
+ /// let device = manager.connect()?;
+ /// let count = device.get_admin_retry_count();
+ /// match device.get_admin_retry_count() {
+ /// Ok(count) => println!("{} remaining authentication attempts (admin)", count),
+ /// Err(err) => eprintln!("Could not get admin retry count: {}", err),
+ /// }
+ /// # Ok(())
+ /// # }
+ /// ```
+ fn get_admin_retry_count(&self) -> Result<u8, Error> {
+ result_or_error(unsafe { nitrokey_sys::NK_get_admin_retry_count() })
+ }
+
+ /// Returns the firmware version.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::Error;
+ ///
+ /// # fn try_main() -> Result<(), Error> {
+ /// let mut manager = nitrokey::take()?;
+ /// let device = manager.connect()?;
+ /// match device.get_firmware_version() {
+ /// Ok(version) => println!("Firmware version: {}", version),
+ /// Err(err) => eprintln!("Could not access firmware version: {}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ fn get_firmware_version(&self) -> Result<FirmwareVersion, Error> {
+ let major = result_or_error(unsafe { nitrokey_sys::NK_get_major_firmware_version() })?;
+ let minor = result_or_error(unsafe { nitrokey_sys::NK_get_minor_firmware_version() })?;
+ Ok(FirmwareVersion { major, minor })
+ }
+
+ /// Returns the current configuration of the Nitrokey device.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::Error;
+ ///
+ /// # fn try_main() -> Result<(), Error> {
+ /// let mut manager = nitrokey::take()?;
+ /// let device = manager.connect()?;
+ /// let config = device.get_config()?;
+ /// println!("numlock binding: {:?}", config.numlock);
+ /// println!("capslock binding: {:?}", config.capslock);
+ /// println!("scrollock binding: {:?}", config.scrollock);
+ /// println!("require password for OTP: {:?}", config.user_password);
+ /// # Ok(())
+ /// # }
+ /// ```
+ 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.
+ ///
+ /// # Errors
+ ///
+ /// - [`InvalidString`][] if one of the provided passwords contains a null byte
+ /// - [`WrongPassword`][] if the current admin password is wrong
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::Error;
+ ///
+ /// # fn try_main() -> Result<(), Error> {
+ /// let mut manager = nitrokey::take()?;
+ /// let mut device = manager.connect()?;
+ /// match device.change_admin_pin("12345678", "12345679") {
+ /// Ok(()) => println!("Updated admin PIN."),
+ /// Err(err) => eprintln!("Failed to update admin PIN: {}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString
+ /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword
+ fn change_admin_pin(&mut self, current: &str, new: &str) -> Result<(), Error> {
+ let current_string = get_cstring(current)?;
+ let new_string = get_cstring(new)?;
+ get_command_result(unsafe {
+ nitrokey_sys::NK_change_admin_PIN(current_string.as_ptr(), new_string.as_ptr())
+ })
+ }
+
+ /// Changes the user PIN.
+ ///
+ /// # Errors
+ ///
+ /// - [`InvalidString`][] if one of the provided passwords contains a null byte
+ /// - [`WrongPassword`][] if the current user password is wrong
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::Error;
+ ///
+ /// # fn try_main() -> Result<(), Error> {
+ /// let mut manager = nitrokey::take()?;
+ /// let mut device = manager.connect()?;
+ /// match device.change_user_pin("123456", "123457") {
+ /// Ok(()) => println!("Updated admin PIN."),
+ /// Err(err) => eprintln!("Failed to update admin PIN: {}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString
+ /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword
+ fn change_user_pin(&mut self, current: &str, new: &str) -> Result<(), Error> {
+ let current_string = get_cstring(current)?;
+ let new_string = get_cstring(new)?;
+ 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.
+ ///
+ /// # Errors
+ ///
+ /// - [`InvalidString`][] if one of the provided passwords contains a null byte
+ /// - [`WrongPassword`][] if the admin password is wrong
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::Error;
+ ///
+ /// # fn try_main() -> Result<(), Error> {
+ /// let mut manager = nitrokey::take()?;
+ /// let mut device = manager.connect()?;
+ /// match device.unlock_user_pin("12345678", "123456") {
+ /// Ok(()) => println!("Unlocked user PIN."),
+ /// Err(err) => eprintln!("Failed to unlock user PIN: {}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString
+ /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword
+ fn unlock_user_pin(&mut 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)?;
+ get_command_result(unsafe {
+ nitrokey_sys::NK_unlock_user_password(
+ admin_pin_string.as_ptr(),
+ user_pin_string.as_ptr(),
+ )
+ })
+ }
+
+ /// Locks the Nitrokey device.
+ ///
+ /// This disables the password store if it has been unlocked. On the Nitrokey Storage, this
+ /// also disables the volumes if they have been enabled.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::Error;
+ ///
+ /// # fn try_main() -> Result<(), Error> {
+ /// let mut manager = nitrokey::take()?;
+ /// let mut device = manager.connect()?;
+ /// match device.lock() {
+ /// Ok(()) => println!("Locked the Nitrokey device."),
+ /// Err(err) => eprintln!("Could not lock the Nitrokey device: {}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ fn lock(&mut self) -> Result<(), Error> {
+ get_command_result(unsafe { nitrokey_sys::NK_lock_device() })
+ }
+
+ /// Performs a factory reset on the Nitrokey device.
+ ///
+ /// This commands performs a factory reset on the smart card (like the factory reset via `gpg
+ /// --card-edit`) and then clears the flash memory (password safe, one-time passwords etc.).
+ /// After a factory reset, [`build_aes_key`][] has to be called before the password safe or the
+ /// encrypted volume can be used.
+ ///
+ /// # Errors
+ ///
+ /// - [`InvalidString`][] if the provided password contains a null byte
+ /// - [`WrongPassword`][] if the admin password is wrong
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::Error;
+ ///
+ /// # fn try_main() -> Result<(), Error> {
+ /// let mut manager = nitrokey::take()?;
+ /// let mut device = manager.connect()?;
+ /// match device.factory_reset("12345678") {
+ /// Ok(()) => println!("Performed a factory reset."),
+ /// Err(err) => eprintln!("Could not perform a factory reset: {}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`build_aes_key`]: #method.build_aes_key
+ fn factory_reset(&mut self, admin_pin: &str) -> Result<(), Error> {
+ let admin_pin_string = get_cstring(admin_pin)?;
+ get_command_result(unsafe { nitrokey_sys::NK_factory_reset(admin_pin_string.as_ptr()) })
+ }
+
+ /// Builds a new AES key on the Nitrokey.
+ ///
+ /// The AES key is used to encrypt the password safe and the encrypted volume. You may need
+ /// to call this method after a factory reset, either using [`factory_reset`][] or using `gpg
+ /// --card-edit`. You can also use it to destroy the data stored in the password safe or on
+ /// the encrypted volume.
+ ///
+ /// # Errors
+ ///
+ /// - [`InvalidString`][] if the provided password contains a null byte
+ /// - [`WrongPassword`][] if the admin password is wrong
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::Device;
+ /// # use nitrokey::Error;
+ ///
+ /// # fn try_main() -> Result<(), Error> {
+ /// let mut manager = nitrokey::take()?;
+ /// let mut device = manager.connect()?;
+ /// match device.build_aes_key("12345678") {
+ /// Ok(()) => println!("New AES keys have been built."),
+ /// Err(err) => eprintln!("Could not build new AES keys: {}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`factory_reset`]: #method.factory_reset
+ fn build_aes_key(&mut self, admin_pin: &str) -> Result<(), Error> {
+ let admin_pin_string = get_cstring(admin_pin)?;
+ get_command_result(unsafe { nitrokey_sys::NK_build_aes_key(admin_pin_string.as_ptr()) })
+ }
+}
+
+fn get_connected_model() -> Option<Model> {
+ 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,
+ }
+}
+
+pub(crate) fn create_device_wrapper(
+ manager: &mut crate::Manager,
+ model: Model,
+) -> DeviceWrapper<'_> {
+ match model {
+ Model::Pro => Pro::new(manager).into(),
+ Model::Storage => Storage::new(manager).into(),
+ }
+}
+
+pub(crate) fn get_connected_device(
+ manager: &mut crate::Manager,
+) -> Result<DeviceWrapper<'_>, Error> {
+ match get_connected_model() {
+ Some(model) => Ok(create_device_wrapper(manager, model)),
+ None => Err(CommunicationError::NotConnected.into()),
+ }
+}
+
+pub(crate) fn connect_enum(model: Model) -> bool {
+ let model = match model {
+ Model::Storage => nitrokey_sys::NK_device_model_NK_STORAGE,
+ Model::Pro => nitrokey_sys::NK_device_model_NK_PRO,
+ };
+ unsafe { nitrokey_sys::NK_login_enum(model) == 1 }
+}
diff --git a/src/device/pro.rs b/src/device/pro.rs
new file mode 100644
index 0000000..a65345e
--- /dev/null
+++ b/src/device/pro.rs
@@ -0,0 +1,79 @@
+// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
+use nitrokey_sys;
+
+use crate::device::{Device, Model};
+use crate::otp::GenerateOtp;
+
+/// A Nitrokey Pro device without user or admin authentication.
+///
+/// Use the [`connect`][] method to obtain an instance wrapper or the [`connect_pro`] method to
+/// directly obtain an instance. If you want to execute a command that requires user or admin
+/// authentication, use [`authenticate_admin`][] or [`authenticate_user`][].
+///
+/// # Examples
+///
+/// Authentication with error handling:
+///
+/// ```no_run
+/// use nitrokey::{Authenticate, User, Pro};
+/// # use nitrokey::Error;
+///
+/// fn perform_user_task<'a>(device: &User<'a, Pro<'a>>) {}
+/// fn perform_other_task(device: &Pro) {}
+///
+/// # fn try_main() -> Result<(), Error> {
+/// let mut manager = nitrokey::take()?;
+/// let device = manager.connect_pro()?;
+/// 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(())
+/// # }
+/// ```
+///
+/// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin
+/// [`authenticate_user`]: trait.Authenticate.html#method.authenticate_user
+/// [`connect`]: struct.Manager.html#method.connect
+/// [`connect_pro`]: struct.Manager.html#method.connect_pro
+#[derive(Debug)]
+pub struct Pro<'a> {
+ manager: Option<&'a mut crate::Manager>,
+}
+
+impl<'a> Pro<'a> {
+ pub(crate) fn new(manager: &'a mut crate::Manager) -> Pro<'a> {
+ Pro {
+ manager: Some(manager),
+ }
+ }
+}
+
+impl<'a> Drop for Pro<'a> {
+ fn drop(&mut self) {
+ unsafe {
+ nitrokey_sys::NK_logout();
+ }
+ }
+}
+
+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
+ }
+}
+
+impl<'a> GenerateOtp for Pro<'a> {}
diff --git a/src/device.rs b/src/device/storage.rs
index 758d4c1..370ce36 100644
--- a/src/device.rs
+++ b/src/device/storage.rs
@@ -3,163 +3,12 @@
use std::fmt;
-use libc;
use nitrokey_sys;
-use crate::auth::Authenticate;
-use crate::config::{Config, RawConfig};
-use crate::error::{CommunicationError, Error};
+use crate::device::{Device, FirmwareVersion, Model};
+use crate::error::Error;
use crate::otp::GenerateOtp;
-use crate::pws::GetPasswordSafe;
-use crate::util::{
- get_command_result, get_cstring, get_last_error, result_from_string, result_or_error,
-};
-
-/// Available Nitrokey models.
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum Model {
- /// The Nitrokey Storage.
- Storage,
- /// The Nitrokey Pro.
- Pro,
-}
-
-impl fmt::Display for Model {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str(match *self {
- Model::Pro => "Pro",
- Model::Storage => "Storage",
- })
- }
-}
-
-/// The access mode of a volume on the Nitrokey Storage.
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum VolumeMode {
- /// A read-only volume.
- ReadOnly,
- /// A read-write volume.
- ReadWrite,
-}
-
-impl fmt::Display for VolumeMode {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str(match *self {
- VolumeMode::ReadOnly => "read-only",
- VolumeMode::ReadWrite => "read-write",
- })
- }
-}
-
-/// A wrapper for a Nitrokey device of unknown type.
-///
-/// Use the [`connect`][] method to obtain a wrapped instance. The wrapper implements all traits
-/// that are shared between all Nitrokey devices so that the shared functionality can be used
-/// without knowing the type of the underlying device. If you want to use functionality that is
-/// not available for all devices, you have to extract the device.
-///
-/// # Examples
-///
-/// Authentication with error handling:
-///
-/// ```no_run
-/// use nitrokey::{Authenticate, DeviceWrapper, User};
-/// # use nitrokey::Error;
-///
-/// fn perform_user_task<'a>(device: &User<'a, DeviceWrapper<'a>>) {}
-/// fn perform_other_task(device: &DeviceWrapper) {}
-///
-/// # fn try_main() -> Result<(), Error> {
-/// let mut manager = nitrokey::take()?;
-/// let device = manager.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(())
-/// # }
-/// ```
-///
-/// Device-specific commands:
-///
-/// ```no_run
-/// use nitrokey::{DeviceWrapper, Storage};
-/// # use nitrokey::Error;
-///
-/// fn perform_common_task(device: &DeviceWrapper) {}
-/// fn perform_storage_task(device: &Storage) {}
-///
-/// # fn try_main() -> Result<(), Error> {
-/// let mut manager = nitrokey::take()?;
-/// let device = manager.connect()?;
-/// perform_common_task(&device);
-/// match device {
-/// DeviceWrapper::Storage(storage) => perform_storage_task(&storage),
-/// _ => (),
-/// };
-/// # Ok(())
-/// # }
-/// ```
-///
-/// [`connect`]: struct.Manager.html#method.connect
-#[derive(Debug)]
-pub enum DeviceWrapper<'a> {
- /// A Nitrokey Storage device.
- Storage(Storage<'a>),
- /// A Nitrokey Pro device.
- Pro(Pro<'a>),
-}
-
-/// A Nitrokey Pro device without user or admin authentication.
-///
-/// Use the [`connect`][] method to obtain an instance wrapper or the [`connect_pro`] method to
-/// directly obtain an instance. If you want to execute a command that requires user or admin
-/// authentication, use [`authenticate_admin`][] or [`authenticate_user`][].
-///
-/// # Examples
-///
-/// Authentication with error handling:
-///
-/// ```no_run
-/// use nitrokey::{Authenticate, User, Pro};
-/// # use nitrokey::Error;
-///
-/// fn perform_user_task<'a>(device: &User<'a, Pro<'a>>) {}
-/// fn perform_other_task(device: &Pro) {}
-///
-/// # fn try_main() -> Result<(), Error> {
-/// let mut manager = nitrokey::take()?;
-/// let device = manager.connect_pro()?;
-/// 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(())
-/// # }
-/// ```
-///
-/// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin
-/// [`authenticate_user`]: trait.Authenticate.html#method.authenticate_user
-/// [`connect`]: struct.Manager.html#method.connect
-/// [`connect_pro`]: struct.Manager.html#method.connect_pro
-#[derive(Debug)]
-pub struct Pro<'a> {
- manager: Option<&'a mut crate::Manager>,
-}
+use crate::util::{get_command_result, get_cstring};
/// A Nitrokey Storage device without user or admin authentication.
///
@@ -205,6 +54,24 @@ pub struct Storage<'a> {
manager: Option<&'a mut crate::Manager>,
}
+/// The access mode of a volume on the Nitrokey Storage.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum VolumeMode {
+ /// A read-only volume.
+ ReadOnly,
+ /// A read-write volume.
+ ReadWrite,
+}
+
+impl fmt::Display for VolumeMode {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(match *self {
+ VolumeMode::ReadOnly => "read-only",
+ VolumeMode::ReadWrite => "read-write",
+ })
+ }
+}
+
/// The status of a volume on a Nitrokey Storage device.
#[derive(Debug)]
pub struct VolumeStatus {
@@ -231,21 +98,6 @@ pub struct SdCardData {
pub manufacturer: u8,
}
-/// 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 major: u8,
- /// The minor firmware version, e. g. 40 in v0.40.
- 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 {
@@ -289,503 +141,6 @@ pub struct StorageStatus {
pub stick_initialized: bool,
}
-/// A Nitrokey device.
-///
-/// This trait provides the commands that can be executed without authentication and that are
-/// present on all supported Nitrokey devices.
-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
- ///
- /// ```no_run
- /// use nitrokey::Device;
- /// # use nitrokey::Error;
- ///
- /// # fn try_main() -> Result<(), Error> {
- /// let mut manager = nitrokey::take()?;
- /// let device = manager.connect()?;
- /// println!("Connected to a Nitrokey {}", device.get_model());
- /// # Ok(())
- /// # }
- fn get_model(&self) -> Model;
-
- /// Returns the serial number of the Nitrokey device. The serial number is the string
- /// representation of a hex number.
- ///
- /// # Example
- ///
- /// ```no_run
- /// use nitrokey::Device;
- /// # use nitrokey::Error;
- ///
- /// # fn try_main() -> Result<(), Error> {
- /// let mut manager = nitrokey::take()?;
- /// let device = manager.connect()?;
- /// match device.get_serial_number() {
- /// Ok(number) => println!("serial no: {}", number),
- /// Err(err) => eprintln!("Could not get serial number: {}", err),
- /// };
- /// # Ok(())
- /// # }
- /// ```
- 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
- /// available attempts is three.
- ///
- /// # Example
- ///
- /// ```no_run
- /// use nitrokey::Device;
- /// # use nitrokey::Error;
- ///
- /// # fn try_main() -> Result<(), Error> {
- /// let mut manager = nitrokey::take()?;
- /// let device = manager.connect()?;
- /// let count = device.get_user_retry_count();
- /// match device.get_user_retry_count() {
- /// Ok(count) => println!("{} remaining authentication attempts (user)", count),
- /// Err(err) => eprintln!("Could not get user retry count: {}", err),
- /// }
- /// # Ok(())
- /// # }
- /// ```
- fn get_user_retry_count(&self) -> Result<u8, Error> {
- result_or_error(unsafe { nitrokey_sys::NK_get_user_retry_count() })
- }
-
- /// Returns the number of remaining authentication attempts for the admin. The total number of
- /// available attempts is three.
- ///
- /// # Example
- ///
- /// ```no_run
- /// use nitrokey::Device;
- /// # use nitrokey::Error;
- ///
- /// # fn try_main() -> Result<(), Error> {
- /// let mut manager = nitrokey::take()?;
- /// let device = manager.connect()?;
- /// let count = device.get_admin_retry_count();
- /// match device.get_admin_retry_count() {
- /// Ok(count) => println!("{} remaining authentication attempts (admin)", count),
- /// Err(err) => eprintln!("Could not get admin retry count: {}", err),
- /// }
- /// # Ok(())
- /// # }
- /// ```
- fn get_admin_retry_count(&self) -> Result<u8, Error> {
- result_or_error(unsafe { nitrokey_sys::NK_get_admin_retry_count() })
- }
-
- /// Returns the firmware version.
- ///
- /// # Example
- ///
- /// ```no_run
- /// use nitrokey::Device;
- /// # use nitrokey::Error;
- ///
- /// # fn try_main() -> Result<(), Error> {
- /// let mut manager = nitrokey::take()?;
- /// let device = manager.connect()?;
- /// match device.get_firmware_version() {
- /// Ok(version) => println!("Firmware version: {}", version),
- /// Err(err) => eprintln!("Could not access firmware version: {}", err),
- /// };
- /// # Ok(())
- /// # }
- /// ```
- fn get_firmware_version(&self) -> Result<FirmwareVersion, Error> {
- let major = result_or_error(unsafe { nitrokey_sys::NK_get_major_firmware_version() })?;
- let minor = result_or_error(unsafe { nitrokey_sys::NK_get_minor_firmware_version() })?;
- Ok(FirmwareVersion { major, minor })
- }
-
- /// Returns the current configuration of the Nitrokey device.
- ///
- /// # Example
- ///
- /// ```no_run
- /// use nitrokey::Device;
- /// # use nitrokey::Error;
- ///
- /// # fn try_main() -> Result<(), Error> {
- /// let mut manager = nitrokey::take()?;
- /// let device = manager.connect()?;
- /// let config = device.get_config()?;
- /// println!("numlock binding: {:?}", config.numlock);
- /// println!("capslock binding: {:?}", config.capslock);
- /// println!("scrollock binding: {:?}", config.scrollock);
- /// println!("require password for OTP: {:?}", config.user_password);
- /// # Ok(())
- /// # }
- /// ```
- 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.
- ///
- /// # Errors
- ///
- /// - [`InvalidString`][] if one of the provided passwords contains a null byte
- /// - [`WrongPassword`][] if the current admin password is wrong
- ///
- /// # Example
- ///
- /// ```no_run
- /// use nitrokey::Device;
- /// # use nitrokey::Error;
- ///
- /// # fn try_main() -> Result<(), Error> {
- /// let mut manager = nitrokey::take()?;
- /// let mut device = manager.connect()?;
- /// match device.change_admin_pin("12345678", "12345679") {
- /// Ok(()) => println!("Updated admin PIN."),
- /// Err(err) => eprintln!("Failed to update admin PIN: {}", err),
- /// };
- /// # Ok(())
- /// # }
- /// ```
- ///
- /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString
- /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword
- fn change_admin_pin(&mut self, current: &str, new: &str) -> Result<(), Error> {
- let current_string = get_cstring(current)?;
- let new_string = get_cstring(new)?;
- get_command_result(unsafe {
- nitrokey_sys::NK_change_admin_PIN(current_string.as_ptr(), new_string.as_ptr())
- })
- }
-
- /// Changes the user PIN.
- ///
- /// # Errors
- ///
- /// - [`InvalidString`][] if one of the provided passwords contains a null byte
- /// - [`WrongPassword`][] if the current user password is wrong
- ///
- /// # Example
- ///
- /// ```no_run
- /// use nitrokey::Device;
- /// # use nitrokey::Error;
- ///
- /// # fn try_main() -> Result<(), Error> {
- /// let mut manager = nitrokey::take()?;
- /// let mut device = manager.connect()?;
- /// match device.change_user_pin("123456", "123457") {
- /// Ok(()) => println!("Updated admin PIN."),
- /// Err(err) => eprintln!("Failed to update admin PIN: {}", err),
- /// };
- /// # Ok(())
- /// # }
- /// ```
- ///
- /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString
- /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword
- fn change_user_pin(&mut self, current: &str, new: &str) -> Result<(), Error> {
- let current_string = get_cstring(current)?;
- let new_string = get_cstring(new)?;
- 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.
- ///
- /// # Errors
- ///
- /// - [`InvalidString`][] if one of the provided passwords contains a null byte
- /// - [`WrongPassword`][] if the admin password is wrong
- ///
- /// # Example
- ///
- /// ```no_run
- /// use nitrokey::Device;
- /// # use nitrokey::Error;
- ///
- /// # fn try_main() -> Result<(), Error> {
- /// let mut manager = nitrokey::take()?;
- /// let mut device = manager.connect()?;
- /// match device.unlock_user_pin("12345678", "123456") {
- /// Ok(()) => println!("Unlocked user PIN."),
- /// Err(err) => eprintln!("Failed to unlock user PIN: {}", err),
- /// };
- /// # Ok(())
- /// # }
- /// ```
- ///
- /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString
- /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword
- fn unlock_user_pin(&mut 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)?;
- get_command_result(unsafe {
- nitrokey_sys::NK_unlock_user_password(
- admin_pin_string.as_ptr(),
- user_pin_string.as_ptr(),
- )
- })
- }
-
- /// Locks the Nitrokey device.
- ///
- /// This disables the password store if it has been unlocked. On the Nitrokey Storage, this
- /// also disables the volumes if they have been enabled.
- ///
- /// # Example
- ///
- /// ```no_run
- /// use nitrokey::Device;
- /// # use nitrokey::Error;
- ///
- /// # fn try_main() -> Result<(), Error> {
- /// let mut manager = nitrokey::take()?;
- /// let mut device = manager.connect()?;
- /// match device.lock() {
- /// Ok(()) => println!("Locked the Nitrokey device."),
- /// Err(err) => eprintln!("Could not lock the Nitrokey device: {}", err),
- /// };
- /// # Ok(())
- /// # }
- /// ```
- fn lock(&mut self) -> Result<(), Error> {
- get_command_result(unsafe { nitrokey_sys::NK_lock_device() })
- }
-
- /// Performs a factory reset on the Nitrokey device.
- ///
- /// This commands performs a factory reset on the smart card (like the factory reset via `gpg
- /// --card-edit`) and then clears the flash memory (password safe, one-time passwords etc.).
- /// After a factory reset, [`build_aes_key`][] has to be called before the password safe or the
- /// encrypted volume can be used.
- ///
- /// # Errors
- ///
- /// - [`InvalidString`][] if the provided password contains a null byte
- /// - [`WrongPassword`][] if the admin password is wrong
- ///
- /// # Example
- ///
- /// ```no_run
- /// use nitrokey::Device;
- /// # use nitrokey::Error;
- ///
- /// # fn try_main() -> Result<(), Error> {
- /// let mut manager = nitrokey::take()?;
- /// let mut device = manager.connect()?;
- /// match device.factory_reset("12345678") {
- /// Ok(()) => println!("Performed a factory reset."),
- /// Err(err) => eprintln!("Could not perform a factory reset: {}", err),
- /// };
- /// # Ok(())
- /// # }
- /// ```
- ///
- /// [`build_aes_key`]: #method.build_aes_key
- fn factory_reset(&mut self, admin_pin: &str) -> Result<(), Error> {
- let admin_pin_string = get_cstring(admin_pin)?;
- get_command_result(unsafe { nitrokey_sys::NK_factory_reset(admin_pin_string.as_ptr()) })
- }
-
- /// Builds a new AES key on the Nitrokey.
- ///
- /// The AES key is used to encrypt the password safe and the encrypted volume. You may need
- /// to call this method after a factory reset, either using [`factory_reset`][] or using `gpg
- /// --card-edit`. You can also use it to destroy the data stored in the password safe or on
- /// the encrypted volume.
- ///
- /// # Errors
- ///
- /// - [`InvalidString`][] if the provided password contains a null byte
- /// - [`WrongPassword`][] if the admin password is wrong
- ///
- /// # Example
- ///
- /// ```no_run
- /// use nitrokey::Device;
- /// # use nitrokey::Error;
- ///
- /// # fn try_main() -> Result<(), Error> {
- /// let mut manager = nitrokey::take()?;
- /// let mut device = manager.connect()?;
- /// match device.build_aes_key("12345678") {
- /// Ok(()) => println!("New AES keys have been built."),
- /// Err(err) => eprintln!("Could not build new AES keys: {}", err),
- /// };
- /// # Ok(())
- /// # }
- /// ```
- ///
- /// [`factory_reset`]: #method.factory_reset
- fn build_aes_key(&mut self, admin_pin: &str) -> Result<(), Error> {
- let admin_pin_string = get_cstring(admin_pin)?;
- get_command_result(unsafe { nitrokey_sys::NK_build_aes_key(admin_pin_string.as_ptr()) })
- }
-}
-
-fn get_connected_model() -> Option<Model> {
- 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,
- }
-}
-
-pub(crate) fn create_device_wrapper(
- manager: &mut crate::Manager,
- model: Model,
-) -> DeviceWrapper<'_> {
- match model {
- Model::Pro => Pro::new(manager).into(),
- Model::Storage => Storage::new(manager).into(),
- }
-}
-
-pub(crate) fn get_connected_device(
- manager: &mut crate::Manager,
-) -> Result<DeviceWrapper<'_>, Error> {
- match get_connected_model() {
- Some(model) => Ok(create_device_wrapper(manager, model)),
- None => Err(CommunicationError::NotConnected.into()),
- }
-}
-
-pub(crate) fn connect_enum(model: Model) -> bool {
- let model = match model {
- Model::Storage => nitrokey_sys::NK_device_model_NK_STORAGE,
- Model::Pro => nitrokey_sys::NK_device_model_NK_PRO,
- };
- unsafe { nitrokey_sys::NK_login_enum(model) == 1 }
-}
-
-impl<'a> DeviceWrapper<'a> {
- 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<'a> {
- match *self {
- DeviceWrapper::Storage(ref mut storage) => storage,
- DeviceWrapper::Pro(ref mut pro) => pro,
- }
- }
-}
-
-impl<'a> From<Pro<'a>> for DeviceWrapper<'a> {
- fn from(device: Pro<'a>) -> Self {
- DeviceWrapper::Pro(device)
- }
-}
-
-impl<'a> From<Storage<'a>> for DeviceWrapper<'a> {
- fn from(device: Storage<'a>) -> Self {
- DeviceWrapper::Storage(device)
- }
-}
-
-impl<'a> GenerateOtp for DeviceWrapper<'a> {
- 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, Error> {
- self.device().get_totp_slot_name(slot)
- }
-
- fn get_hotp_code(&mut self, slot: u8) -> Result<String, Error> {
- self.device_mut().get_hotp_code(slot)
- }
-
- fn get_totp_code(&self, slot: u8) -> Result<String, Error> {
- self.device().get_totp_code(slot)
- }
-}
-
-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,
- DeviceWrapper::Storage(_) => Model::Storage,
- }
- }
-}
-
-impl<'a> Pro<'a> {
- pub(crate) fn new(manager: &'a mut crate::Manager) -> Pro<'a> {
- Pro {
- manager: Some(manager),
- }
- }
-}
-
-impl<'a> Drop for Pro<'a> {
- fn drop(&mut self) {
- unsafe {
- nitrokey_sys::NK_logout();
- }
- }
-}
-
-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
- }
-}
-
-impl<'a> GenerateOtp for Pro<'a> {}
-
impl<'a> Storage<'a> {
pub(crate) fn new(manager: &'a mut crate::Manager) -> Storage<'a> {
Storage {
diff --git a/src/device/wrapper.rs b/src/device/wrapper.rs
new file mode 100644
index 0000000..a3a18f9
--- /dev/null
+++ b/src/device/wrapper.rs
@@ -0,0 +1,134 @@
+// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
+use crate::device::{Device, Model, Pro, Storage};
+use crate::error::Error;
+use crate::otp::GenerateOtp;
+
+/// A wrapper for a Nitrokey device of unknown type.
+///
+/// Use the [`connect`][] method to obtain a wrapped instance. The wrapper implements all traits
+/// that are shared between all Nitrokey devices so that the shared functionality can be used
+/// without knowing the type of the underlying device. If you want to use functionality that is
+/// not available for all devices, you have to extract the device.
+///
+/// # Examples
+///
+/// Authentication with error handling:
+///
+/// ```no_run
+/// use nitrokey::{Authenticate, DeviceWrapper, User};
+/// # use nitrokey::Error;
+///
+/// fn perform_user_task<'a>(device: &User<'a, DeviceWrapper<'a>>) {}
+/// fn perform_other_task(device: &DeviceWrapper) {}
+///
+/// # fn try_main() -> Result<(), Error> {
+/// let mut manager = nitrokey::take()?;
+/// let device = manager.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(())
+/// # }
+/// ```
+///
+/// Device-specific commands:
+///
+/// ```no_run
+/// use nitrokey::{DeviceWrapper, Storage};
+/// # use nitrokey::Error;
+///
+/// fn perform_common_task(device: &DeviceWrapper) {}
+/// fn perform_storage_task(device: &Storage) {}
+///
+/// # fn try_main() -> Result<(), Error> {
+/// let mut manager = nitrokey::take()?;
+/// let device = manager.connect()?;
+/// perform_common_task(&device);
+/// match device {
+/// DeviceWrapper::Storage(storage) => perform_storage_task(&storage),
+/// _ => (),
+/// };
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [`connect`]: struct.Manager.html#method.connect
+#[derive(Debug)]
+pub enum DeviceWrapper<'a> {
+ /// A Nitrokey Storage device.
+ Storage(Storage<'a>),
+ /// A Nitrokey Pro device.
+ Pro(Pro<'a>),
+}
+
+impl<'a> DeviceWrapper<'a> {
+ 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<'a> {
+ match *self {
+ DeviceWrapper::Storage(ref mut storage) => storage,
+ DeviceWrapper::Pro(ref mut pro) => pro,
+ }
+ }
+}
+
+impl<'a> From<Pro<'a>> for DeviceWrapper<'a> {
+ fn from(device: Pro<'a>) -> Self {
+ DeviceWrapper::Pro(device)
+ }
+}
+
+impl<'a> From<Storage<'a>> for DeviceWrapper<'a> {
+ fn from(device: Storage<'a>) -> Self {
+ DeviceWrapper::Storage(device)
+ }
+}
+
+impl<'a> GenerateOtp for DeviceWrapper<'a> {
+ 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, Error> {
+ self.device().get_totp_slot_name(slot)
+ }
+
+ fn get_hotp_code(&mut self, slot: u8) -> Result<String, Error> {
+ self.device_mut().get_hotp_code(slot)
+ }
+
+ fn get_totp_code(&self, slot: u8) -> Result<String, Error> {
+ self.device().get_totp_code(slot)
+ }
+}
+
+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,
+ DeviceWrapper::Storage(_) => Model::Storage,
+ }
+ }
+}