diff options
| author | Daniel Mueller <deso@posteo.net> | 2020-01-02 08:32:06 -0800 | 
|---|---|---|
| committer | Daniel Mueller <deso@posteo.net> | 2020-01-02 08:32:06 -0800 | 
| commit | fd091b04316db9dc5fafadbd6bdbe60b127408a9 (patch) | |
| tree | f202270f7ae5cedc513be03833a26148d9b5e219 /nitrokey/src | |
| parent | 8161cdb26f98e65b39c603ddf7a614cc87c77a1c (diff) | |
| download | nitrocli-fd091b04316db9dc5fafadbd6bdbe60b127408a9.tar.gz nitrocli-fd091b04316db9dc5fafadbd6bdbe60b127408a9.tar.bz2 | |
Update nitrokey crate to 0.4.0
This change finally updates the version of the nitrokey crate that we
consume to 0.4.0. Along with that we update rand_core, one of its
dependencies, to 0.5.1. Further more we add cfg-if in version 0.1.10 and
getrandom in version 0.1.13, both of which are now new (non-development)
dependencies.
Import subrepo nitrokey/:nitrokey at e81057037e9b4f370b64c0a030a725bc6bdfb870
Import subrepo cfg-if/:cfg-if at 4484a6faf816ff8058088ad857b0c6bb2f4b02b2
Import subrepo getrandom/:getrandom at d661aa7e1b8cc80b47dabe3d2135b3b47d2858af
Import subrepo rand/:rand at d877ed528248b52d947e0484364a4e1ae59ca502
Diffstat (limited to 'nitrokey/src')
| -rw-r--r-- | nitrokey/src/auth.rs | 1 | ||||
| -rw-r--r-- | nitrokey/src/config.rs | 8 | ||||
| -rw-r--r-- | nitrokey/src/device/mod.rs | 464 | ||||
| -rw-r--r-- | nitrokey/src/device/pro.rs | 79 | ||||
| -rw-r--r-- | nitrokey/src/device/storage.rs (renamed from nitrokey/src/device.rs) | 687 | ||||
| -rw-r--r-- | nitrokey/src/device/wrapper.rs | 134 | ||||
| -rw-r--r-- | nitrokey/src/lib.rs | 20 | ||||
| -rw-r--r-- | nitrokey/src/util.rs | 6 | 
8 files changed, 712 insertions, 687 deletions
| diff --git a/nitrokey/src/auth.rs b/nitrokey/src/auth.rs index 0b000f7..cab1021 100644 --- a/nitrokey/src/auth.rs +++ b/nitrokey/src/auth.rs @@ -1,6 +1,7 @@  // Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>  // SPDX-License-Identifier: MIT +use std::convert::TryFrom as _;  use std::marker;  use std::ops;  use std::os::raw::c_char; diff --git a/nitrokey/src/config.rs b/nitrokey/src/config.rs index c273792..cb678d7 100644 --- a/nitrokey/src/config.rs +++ b/nitrokey/src/config.rs @@ -1,6 +1,8 @@  // Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>  // SPDX-License-Identifier: MIT +use std::convert; +  use crate::error::{Error, LibraryError};  /// The configuration for a Nitrokey. @@ -68,8 +70,10 @@ impl Config {      }  } -impl RawConfig { -    pub fn try_from(config: Config) -> Result<RawConfig, Error> { +impl convert::TryFrom<Config> for RawConfig { +    type Error = Error; + +    fn try_from(config: Config) -> Result<RawConfig, Error> {          Ok(RawConfig {              numlock: option_to_config_otp_slot(config.numlock)?,              capslock: option_to_config_otp_slot(config.capslock)?, diff --git a/nitrokey/src/device/mod.rs b/nitrokey/src/device/mod.rs new file mode 100644 index 0000000..5e15f08 --- /dev/null +++ b/nitrokey/src/device/mod.rs @@ -0,0 +1,464 @@ +// 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 +    ///     // ... +    /// } +    /// +    /// match nitrokey::take()?.connect() { +    ///     Ok(device) => do_something(device), +    ///     Err(err) => println!("Could not connect to a Nitrokey: {}", err), +    /// } +    /// # Ok::<(), nitrokey::Error>(()) +    /// ``` +    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/nitrokey/src/device/pro.rs b/nitrokey/src/device/pro.rs new file mode 100644 index 0000000..a65345e --- /dev/null +++ b/nitrokey/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/nitrokey/src/device.rs b/nitrokey/src/device/storage.rs index 758d4c1..370ce36 100644 --- a/nitrokey/src/device.rs +++ b/nitrokey/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/nitrokey/src/device/wrapper.rs b/nitrokey/src/device/wrapper.rs new file mode 100644 index 0000000..a3a18f9 --- /dev/null +++ b/nitrokey/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, +        } +    } +} diff --git a/nitrokey/src/lib.rs b/nitrokey/src/lib.rs index a4402c5..059792d 100644 --- a/nitrokey/src/lib.rs +++ b/nitrokey/src/lib.rs @@ -243,14 +243,12 @@ impl Manager {      ///      /// fn do_something(device: DeviceWrapper) {}      /// -    /// # fn main() -> Result<(), nitrokey::Error> {      /// let mut manager = nitrokey::take()?;      /// match manager.connect() {      ///     Ok(device) => do_something(device),      ///     Err(err) => println!("Could not connect to a Nitrokey: {}", err),      /// } -    /// #     Ok(()) -    /// # } +    /// # Ok::<(), nitrokey::Error>(())      /// ```      ///      /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected @@ -276,13 +274,11 @@ impl Manager {      ///      /// fn do_something(device: DeviceWrapper) {}      /// -    /// # fn main() -> Result<(), nitrokey::Error> {      /// match nitrokey::take()?.connect_model(Model::Pro) {      ///     Ok(device) => do_something(device),      ///     Err(err) => println!("Could not connect to a Nitrokey Pro: {}", err),      /// } -    /// #     Ok(()) -    /// # } +    /// # Ok::<(), nitrokey::Error>(())      /// ```      ///      /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected @@ -307,13 +303,11 @@ impl Manager {      ///      /// fn use_pro(device: Pro) {}      /// -    /// # fn main() -> Result<(), nitrokey::Error> {      /// match nitrokey::take()?.connect_pro() {      ///     Ok(device) => use_pro(device),      ///     Err(err) => println!("Could not connect to the Nitrokey Pro: {}", err),      /// } -    /// #     Ok(()) -    /// # } +    /// # Ok::<(), nitrokey::Error>(())      /// ```      ///      /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected @@ -338,13 +332,11 @@ impl Manager {      ///      /// fn use_storage(device: Storage) {}      /// -    /// # fn main() -> Result<(), nitrokey::Error> {      /// match nitrokey::take()?.connect_storage() {      ///     Ok(device) => use_storage(device),      ///     Err(err) => println!("Could not connect to the Nitrokey Storage: {}", err),      /// } -    /// #     Ok(()) -    /// # } +    /// # Ok::<(), nitrokey::Error>(())      /// ```      ///      /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected @@ -453,11 +445,9 @@ pub fn set_log_level(level: LogLevel) {  /// # Example  ///  /// ``` -/// # fn main() -> Result<(), nitrokey::Error> {  /// let version = nitrokey::get_library_version()?;  /// println!("Using libnitrokey {}", version.git); -/// #    Ok(()) -/// # } +/// # Ok::<(), nitrokey::Error>(())  /// ```  ///  /// [`Utf8Error`]: enum.Error.html#variant.Utf8Error diff --git a/nitrokey/src/util.rs b/nitrokey/src/util.rs index fdb73c3..5a56c55 100644 --- a/nitrokey/src/util.rs +++ b/nitrokey/src/util.rs @@ -5,8 +5,7 @@ use std::ffi::{CStr, CString};  use std::os::raw::{c_char, c_int};  use libc::{c_void, free}; -use rand_core::RngCore; -use rand_os::OsRng; +use rand_core::{OsRng, RngCore};  use crate::error::{Error, LibraryError}; @@ -77,9 +76,8 @@ pub fn get_last_error() -> Error {  }  pub fn generate_password(length: usize) -> Result<Vec<u8>, Error> { -    let mut rng = OsRng::new().map_err(|err| Error::RandError(Box::new(err)))?;      let mut data = vec![0u8; length]; -    rng.fill_bytes(&mut data[..]); +    OsRng.fill_bytes(&mut data[..]);      Ok(data)  } | 
