From d18cb04ff4d201fe4532cedd22b9753e08385a7f Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 16 Jan 2019 23:08:56 +0000 Subject: Introduce the FirmwareVersion struct The FirmwareVersion struct stores the major and minor firmware version of a Nitrokey device. We refactor the StorageProductionInfo and StorageStatus structs to use this new struct. --- src/device.rs | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 9813c50..d794e1b 100644 --- a/src/device.rs +++ b/src/device.rs @@ -225,13 +225,26 @@ pub struct SdCardData { pub manufacturer: u8, } -#[derive(Debug)] -/// Production information for a Storage device. -pub struct StorageProductionInfo { +/// A firmware version for a Nitrokey device. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct FirmwareVersion { /// The major firmware version, e. g. 0 in v0.40. - pub firmware_version_major: u8, + pub major: u8, /// The minor firmware version, e. g. 40 in v0.40. - pub firmware_version_minor: u8, + pub minor: u8, +} + +impl fmt::Display for FirmwareVersion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "v{}.{}", self.major, self.minor) + } +} + +/// Production information for a Storage device. +#[derive(Debug)] +pub struct StorageProductionInfo { + /// The firmware version. + pub firmware_version: FirmwareVersion, /// The internal firmware version. pub firmware_version_internal: u8, /// The serial number of the CPU. @@ -249,10 +262,8 @@ pub struct StorageStatus { pub encrypted_volume: VolumeStatus, /// The status of the hidden volume. pub hidden_volume: VolumeStatus, - /// The major firmware version, e. g. 0 in v0.40. - pub firmware_version_major: u8, - /// The minor firmware version, e. g. 40 in v0.40. - pub firmware_version_minor: u8, + /// The firmware version. + pub firmware_version: FirmwareVersion, /// Indicates whether the firmware is locked. pub firmware_locked: bool, /// The serial number of the SD card in the Storage stick. @@ -1321,8 +1332,10 @@ impl GenerateOtp for Storage {} impl From for StorageProductionInfo { fn from(data: nitrokey_sys::NK_storage_ProductionTest) -> Self { Self { - firmware_version_major: data.FirmwareVersion_au8[0], - firmware_version_minor: data.FirmwareVersion_au8[1], + firmware_version: FirmwareVersion { + major: data.FirmwareVersion_au8[0], + minor: data.FirmwareVersion_au8[1], + }, firmware_version_internal: data.FirmwareVersionInternal_u8, serial_number_cpu: data.CPU_CardID_u32, sd_card: SdCardData { @@ -1352,8 +1365,10 @@ impl From for StorageStatus { read_only: status.hidden_volume_read_only, active: status.hidden_volume_active, }, - firmware_version_major: status.firmware_version_major, - firmware_version_minor: status.firmware_version_minor, + firmware_version: FirmwareVersion { + major: status.firmware_version_major, + minor: status.firmware_version_minor, + }, firmware_locked: status.firmware_locked, serial_number_sd_card: status.serial_number_sd_card, serial_number_smart_card: status.serial_number_smart_card, -- cgit v1.2.1 From d0f63513bb935d3d931c86a1ab7b68d6ed44bf27 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 01:53:08 +0000 Subject: Move util::CommandError to the new error module This prepares the refactoring of util::CommandError into multiple enums. --- src/device.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index d794e1b..603a986 100644 --- a/src/device.rs +++ b/src/device.rs @@ -5,11 +5,10 @@ use nitrokey_sys; use crate::auth::Authenticate; use crate::config::{Config, RawConfig}; +use crate::error::CommandError; use crate::otp::GenerateOtp; use crate::pws::GetPasswordSafe; -use crate::util::{ - get_command_result, get_cstring, get_last_error, result_from_string, CommandError, -}; +use crate::util::{get_command_result, get_cstring, get_last_error, result_from_string}; /// Available Nitrokey models. #[derive(Clone, Copy, Debug, PartialEq)] -- cgit v1.2.1 From 94390aadc8a3997d379bf5e4c0bc00c2a9669a34 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 20 Jan 2019 20:58:18 +0000 Subject: Return Error instead of CommandError This patch changes all public functions to return the Error enum instead of the CommandError enum. This breaks the tests which will be fixed with the next patch. This patch also adds a placeholder variant Error::CommandError and a placeholder enum CommandError to make the transition to a new nitrokey-test version easier. --- src/device.rs | 182 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 91 insertions(+), 91 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 603a986..ccd0597 100644 --- a/src/device.rs +++ b/src/device.rs @@ -5,7 +5,7 @@ use nitrokey_sys; use crate::auth::Authenticate; use crate::config::{Config, RawConfig}; -use crate::error::CommandError; +use crate::error::{CommandError, Error}; use crate::otp::GenerateOtp; use crate::pws::GetPasswordSafe; use crate::util::{get_command_result, get_cstring, get_last_error, result_from_string}; @@ -63,12 +63,12 @@ impl fmt::Display for VolumeMode { /// /// ```no_run /// use nitrokey::{Authenticate, DeviceWrapper, User}; -/// # use nitrokey::CommandError; +/// # use nitrokey::Error; /// /// fn perform_user_task(device: &User) {} /// fn perform_other_task(device: &DeviceWrapper) {} /// -/// # fn try_main() -> Result<(), CommandError> { +/// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let device = match device.authenticate_user("123456") { /// Ok(user) => { @@ -89,12 +89,12 @@ impl fmt::Display for VolumeMode { /// /// ```no_run /// use nitrokey::{DeviceWrapper, Storage}; -/// # use nitrokey::CommandError; +/// # use nitrokey::Error; /// /// fn perform_common_task(device: &DeviceWrapper) {} /// fn perform_storage_task(device: &Storage) {} /// -/// # fn try_main() -> Result<(), CommandError> { +/// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// perform_common_task(&device); /// match device { @@ -127,12 +127,12 @@ pub enum DeviceWrapper { /// /// ```no_run /// use nitrokey::{Authenticate, User, Pro}; -/// # use nitrokey::CommandError; +/// # use nitrokey::Error; /// /// fn perform_user_task(device: &User) {} /// fn perform_other_task(device: &Pro) {} /// -/// # fn try_main() -> Result<(), CommandError> { +/// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Pro::connect()?; /// let device = match device.authenticate_user("123456") { /// Ok(user) => { @@ -169,12 +169,12 @@ pub struct Pro {} /// /// ```no_run /// use nitrokey::{Authenticate, User, Storage}; -/// # use nitrokey::CommandError; +/// # use nitrokey::Error; /// /// fn perform_user_task(device: &User) {} /// fn perform_other_task(device: &Storage) {} /// -/// # fn try_main() -> Result<(), CommandError> { +/// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// let device = match device.authenticate_user("123456") { /// Ok(user) => { @@ -293,9 +293,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// println!("Connected to a Nitrokey {}", device.get_model()); /// # Ok(()) @@ -309,9 +309,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.get_serial_number() { /// Ok(number) => println!("serial no: {}", number), @@ -320,7 +320,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # Ok(()) /// # } /// ``` - fn get_serial_number(&self) -> Result { + fn get_serial_number(&self) -> Result { unsafe { result_from_string(nitrokey_sys::NK_device_serial_number()) } } @@ -331,9 +331,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let count = device.get_user_retry_count(); /// println!("{} remaining authentication attempts (user)", count); @@ -351,9 +351,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let count = device.get_admin_retry_count(); /// println!("{} remaining authentication attempts (admin)", count); @@ -370,9 +370,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// println!( /// "Firmware version: {}.{}", @@ -392,9 +392,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// println!( /// "Firmware version: {}.{}", @@ -413,9 +413,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let config = device.get_config()?; /// println!("numlock binding: {:?}", config.numlock); @@ -425,7 +425,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # Ok(()) /// # } /// ``` - fn get_config(&self) -> Result { + fn get_config(&self) -> Result { unsafe { let config_ptr = nitrokey_sys::NK_read_config(); if config_ptr.is_null() { @@ -449,9 +449,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.change_admin_pin("12345678", "12345679") { /// Ok(()) => println!("Updated admin PIN."), @@ -463,7 +463,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn change_admin_pin(&self, current: &str, new: &str) -> Result<(), CommandError> { + fn change_admin_pin(&self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; let new_string = get_cstring(new)?; unsafe { @@ -485,9 +485,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.change_user_pin("123456", "123457") { /// Ok(()) => println!("Updated admin PIN."), @@ -499,7 +499,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn change_user_pin(&self, current: &str, new: &str) -> Result<(), CommandError> { + fn change_user_pin(&self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; let new_string = get_cstring(new)?; unsafe { @@ -521,9 +521,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.unlock_user_pin("12345678", "123456") { /// Ok(()) => println!("Unlocked user PIN."), @@ -535,7 +535,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn unlock_user_pin(&self, admin_pin: &str, user_pin: &str) -> Result<(), CommandError> { + fn unlock_user_pin(&self, admin_pin: &str, user_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; let user_pin_string = get_cstring(user_pin)?; unsafe { @@ -555,9 +555,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.lock() { /// Ok(()) => println!("Locked the Nitrokey device."), @@ -566,7 +566,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # Ok(()) /// # } /// ``` - fn lock(&self) -> Result<(), CommandError> { + fn lock(&self) -> Result<(), Error> { unsafe { get_command_result(nitrokey_sys::NK_lock_device()) } } @@ -586,9 +586,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.factory_reset("12345678") { /// Ok(()) => println!("Performed a factory reset."), @@ -599,7 +599,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// ``` /// /// [`build_aes_key`]: #method.build_aes_key - fn factory_reset(&self, admin_pin: &str) -> Result<(), CommandError> { + fn factory_reset(&self, admin_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; unsafe { get_command_result(nitrokey_sys::NK_factory_reset(admin_pin_string.as_ptr())) } } @@ -620,9 +620,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// ```no_run /// use nitrokey::Device; - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// match device.build_aes_key("12345678") { /// Ok(()) => println!("New AES keys have been built."), @@ -633,7 +633,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// ``` /// /// [`factory_reset`]: #method.factory_reset - fn build_aes_key(&self, admin_pin: &str) -> Result<(), CommandError> { + fn build_aes_key(&self, admin_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; unsafe { get_command_result(nitrokey_sys::NK_build_aes_key(admin_pin_string.as_ptr())) } } @@ -660,14 +660,14 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// ``` /// /// [`Undefined`]: enum.CommandError.html#variant.Undefined -pub fn connect() -> Result { +pub fn connect() -> Result { unsafe { match nitrokey_sys::NK_login_auto() { 1 => match get_connected_device() { Some(wrapper) => Ok(wrapper), - None => Err(CommandError::Undefined), + None => Err(CommandError::Undefined.into()), }, - _ => Err(CommandError::Undefined), + _ => Err(CommandError::Undefined.into()), } } } @@ -693,11 +693,11 @@ pub fn connect() -> Result { /// ``` /// /// [`Undefined`]: enum.CommandError.html#variant.Undefined -pub fn connect_model(model: Model) -> Result { +pub fn connect_model(model: Model) -> Result { if connect_enum(model) { Ok(create_device_wrapper(model)) } else { - Err(CommandError::Undefined) + Err(CommandError::Undefined.into()) } } @@ -740,19 +740,19 @@ impl DeviceWrapper { } impl GenerateOtp for DeviceWrapper { - fn get_hotp_slot_name(&self, slot: u8) -> Result { + fn get_hotp_slot_name(&self, slot: u8) -> Result { self.device().get_hotp_slot_name(slot) } - fn get_totp_slot_name(&self, slot: u8) -> Result { + fn get_totp_slot_name(&self, slot: u8) -> Result { self.device().get_totp_slot_name(slot) } - fn get_hotp_code(&self, slot: u8) -> Result { + fn get_hotp_code(&self, slot: u8) -> Result { self.device().get_hotp_code(slot) } - fn get_totp_code(&self, slot: u8) -> Result { + fn get_totp_code(&self, slot: u8) -> Result { self.device().get_totp_code(slot) } } @@ -787,11 +787,11 @@ impl Pro { /// ``` /// /// [`Undefined`]: enum.CommandError.html#variant.Undefined - pub fn connect() -> Result { + pub fn connect() -> Result { // TODO: maybe Option instead of Result? match connect_enum(Model::Pro) { true => Ok(Pro {}), - false => Err(CommandError::Undefined), + false => Err(CommandError::Undefined.into()), } } } @@ -833,11 +833,11 @@ impl Storage { /// ``` /// /// [`Undefined`]: enum.CommandError.html#variant.Undefined - pub fn connect() -> Result { + pub fn connect() -> Result { // TODO: maybe Option instead of Result? match connect_enum(Model::Storage) { true => Ok(Storage {}), - false => Err(CommandError::Undefined), + false => Err(CommandError::Undefined.into()), } } @@ -855,9 +855,9 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// match device.change_update_pin("12345678", "87654321") { /// Ok(()) => println!("Updated update PIN."), @@ -869,7 +869,7 @@ impl Storage { /// /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn change_update_pin(&self, current: &str, new: &str) -> Result<(), CommandError> { + pub fn change_update_pin(&self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; let new_string = get_cstring(new)?; unsafe { @@ -895,9 +895,9 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// match device.enable_firmware_update("12345678") { /// Ok(()) => println!("Nitrokey entered update mode."), @@ -909,7 +909,7 @@ impl Storage { /// /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn enable_firmware_update(&self, update_pin: &str) -> Result<(), CommandError> { + pub fn enable_firmware_update(&self, update_pin: &str) -> Result<(), Error> { let update_pin_string = get_cstring(update_pin)?; unsafe { get_command_result(nitrokey_sys::NK_enable_firmware_update( @@ -931,9 +931,9 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// match device.enable_encrypted_volume("123456") { /// Ok(()) => println!("Enabled the encrypted volume."), @@ -945,7 +945,7 @@ impl Storage { /// /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn enable_encrypted_volume(&self, user_pin: &str) -> Result<(), CommandError> { + pub fn enable_encrypted_volume(&self, user_pin: &str) -> Result<(), Error> { let user_pin = get_cstring(user_pin)?; unsafe { get_command_result(nitrokey_sys::NK_unlock_encrypted_volume(user_pin.as_ptr())) } } @@ -958,11 +958,11 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// /// fn use_volume() {} /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// match device.enable_encrypted_volume("123456") { /// Ok(()) => { @@ -980,7 +980,7 @@ impl Storage { /// # Ok(()) /// # } /// ``` - pub fn disable_encrypted_volume(&self) -> Result<(), CommandError> { + pub fn disable_encrypted_volume(&self) -> Result<(), Error> { unsafe { get_command_result(nitrokey_sys::NK_lock_encrypted_volume()) } } @@ -1006,9 +1006,9 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// device.enable_encrypted_volume("123445")?; /// match device.enable_hidden_volume("hidden-pw") { @@ -1022,7 +1022,7 @@ impl Storage { /// [`enable_encrypted_volume`]: #method.enable_encrypted_volume /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString - pub fn enable_hidden_volume(&self, volume_password: &str) -> Result<(), CommandError> { + pub fn enable_hidden_volume(&self, volume_password: &str) -> Result<(), Error> { let volume_password = get_cstring(volume_password)?; unsafe { get_command_result(nitrokey_sys::NK_unlock_hidden_volume( @@ -1039,11 +1039,11 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// /// fn use_volume() {} /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// device.enable_encrypted_volume("123445")?; /// match device.enable_hidden_volume("hidden-pw") { @@ -1062,7 +1062,7 @@ impl Storage { /// # Ok(()) /// # } /// ``` - pub fn disable_hidden_volume(&self) -> Result<(), CommandError> { + pub fn disable_hidden_volume(&self) -> Result<(), Error> { unsafe { get_command_result(nitrokey_sys::NK_lock_hidden_volume()) } } @@ -1088,9 +1088,9 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// device.enable_encrypted_volume("123445")?; /// device.create_hidden_volume(0, 0, 100, "hidden-pw")?; @@ -1106,7 +1106,7 @@ impl Storage { start: u8, end: u8, password: &str, - ) -> Result<(), CommandError> { + ) -> Result<(), Error> { let password = get_cstring(password)?; unsafe { get_command_result(nitrokey_sys::NK_create_hidden_volume( @@ -1132,10 +1132,10 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// use nitrokey::VolumeMode; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// match device.set_unencrypted_volume_mode("123456", VolumeMode::ReadWrite) { /// Ok(()) => println!("Set the unencrypted volume to read-write mode."), @@ -1151,7 +1151,7 @@ impl Storage { &self, admin_pin: &str, mode: VolumeMode, - ) -> Result<(), CommandError> { + ) -> Result<(), Error> { let admin_pin = get_cstring(admin_pin)?; let result = match mode { VolumeMode::ReadOnly => unsafe { @@ -1169,11 +1169,11 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// /// fn use_volume() {} /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// match device.get_status() { /// Ok(status) => { @@ -1184,7 +1184,7 @@ impl Storage { /// # Ok(()) /// # } /// ``` - pub fn get_status(&self) -> Result { + pub fn get_status(&self) -> Result { let mut raw_status = nitrokey_sys::NK_storage_status { unencrypted_volume_read_only: false, unencrypted_volume_active: false, @@ -1213,11 +1213,11 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// /// fn use_volume() {} /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// match device.get_production_info() { /// Ok(data) => { @@ -1229,7 +1229,7 @@ impl Storage { /// # Ok(()) /// # } /// ``` - pub fn get_production_info(&self) -> Result { + pub fn get_production_info(&self) -> Result { let mut raw_data = nitrokey_sys::NK_storage_ProductionTest { FirmwareVersion_au8: [0, 2], FirmwareVersionInternal_u8: 0, @@ -1264,9 +1264,9 @@ impl Storage { /// # Example /// /// ```no_run - /// # use nitrokey::CommandError; + /// # use nitrokey::Error; /// - /// # fn try_main() -> Result<(), CommandError> { + /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; /// match device.clear_new_sd_card_warning("12345678") { /// Ok(()) => println!("Cleared the new SD card warning."), @@ -1278,7 +1278,7 @@ impl Storage { /// /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn clear_new_sd_card_warning(&self, admin_pin: &str) -> Result<(), CommandError> { + pub fn clear_new_sd_card_warning(&self, admin_pin: &str) -> Result<(), Error> { let admin_pin = get_cstring(admin_pin)?; get_command_result(unsafe { nitrokey_sys::NK_clear_new_sd_card_warning(admin_pin.as_ptr()) @@ -1286,7 +1286,7 @@ impl Storage { } /// Blinks the red and green LED alternatively and infinitely until the device is reconnected. - pub fn wink(&self) -> Result<(), CommandError> { + pub fn wink(&self) -> Result<(), Error> { get_command_result(unsafe { nitrokey_sys::NK_wink() }) } @@ -1306,7 +1306,7 @@ impl Storage { /// /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn export_firmware(&self, admin_pin: &str) -> Result<(), CommandError> { + pub fn export_firmware(&self, admin_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; get_command_result(unsafe { nitrokey_sys::NK_export_firmware(admin_pin_string.as_ptr()) }) } -- cgit v1.2.1 From 5e258d26b55af6bed7c316b1c7ac12e20946702d Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 12:47:52 +0000 Subject: Refactor library errors into LibraryError enum Previously, library errors were part of the CommandError enum. As command errors and library errors are two different error types, they should be split into two enums. --- src/device.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index ccd0597..5c4014b 100644 --- a/src/device.rs +++ b/src/device.rs @@ -461,7 +461,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword fn change_admin_pin(&self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; @@ -497,7 +497,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword fn change_user_pin(&self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; @@ -533,7 +533,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword fn unlock_user_pin(&self, admin_pin: &str, user_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; @@ -867,7 +867,7 @@ impl Storage { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn change_update_pin(&self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; @@ -907,7 +907,7 @@ impl Storage { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn enable_firmware_update(&self, update_pin: &str) -> Result<(), Error> { let update_pin_string = get_cstring(update_pin)?; @@ -943,7 +943,7 @@ impl Storage { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn enable_encrypted_volume(&self, user_pin: &str) -> Result<(), Error> { let user_pin = get_cstring(user_pin)?; @@ -1021,7 +1021,7 @@ impl Storage { /// /// [`enable_encrypted_volume`]: #method.enable_encrypted_volume /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString pub fn enable_hidden_volume(&self, volume_password: &str) -> Result<(), Error> { let volume_password = get_cstring(volume_password)?; unsafe { @@ -1099,7 +1099,7 @@ impl Storage { /// ``` /// /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString pub fn create_hidden_volume( &self, slot: u8, @@ -1145,7 +1145,7 @@ impl Storage { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn set_unencrypted_volume_mode( &self, @@ -1276,7 +1276,7 @@ impl Storage { /// # } /// ``` /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn clear_new_sd_card_warning(&self, admin_pin: &str) -> Result<(), Error> { let admin_pin = get_cstring(admin_pin)?; @@ -1304,7 +1304,7 @@ impl Storage { /// - [`InvalidString`][] if one of the provided passwords contains a null byte /// - [`WrongPassword`][] if the admin password is wrong /// - /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn export_firmware(&self, admin_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; -- cgit v1.2.1 From 70e886d3ca487c306b8eced9f0e067a67ba9c1bb Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 13:53:15 +0000 Subject: Return CommunicationError::NotConnected from connect functions Previously, we returned a CommandError::Undefined if a connect function failed. A CommunicationError::NotConnected is a more specific and better fitting choice. Once the Try trait has been stabilized, we should return an Option<_> instead of a Result<_, Error> from the connect functions. --- src/device.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 5c4014b..1cf9da9 100644 --- a/src/device.rs +++ b/src/device.rs @@ -5,7 +5,7 @@ use nitrokey_sys; use crate::auth::Authenticate; use crate::config::{Config, RawConfig}; -use crate::error::{CommandError, Error}; +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}; @@ -644,7 +644,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// /// # Errors /// -/// - [`Undefined`][] if no Nitrokey device is connected +/// - [`NotConnected`][] if no Nitrokey device is connected /// /// # Example /// @@ -659,15 +659,15 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { /// } /// ``` /// -/// [`Undefined`]: enum.CommandError.html#variant.Undefined +/// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected pub fn connect() -> Result { unsafe { match nitrokey_sys::NK_login_auto() { 1 => match get_connected_device() { Some(wrapper) => Ok(wrapper), - None => Err(CommandError::Undefined.into()), + None => Err(CommunicationError::NotConnected.into()), }, - _ => Err(CommandError::Undefined.into()), + _ => Err(CommunicationError::NotConnected.into()), } } } @@ -676,7 +676,7 @@ pub fn connect() -> Result { /// /// # Errors /// -/// - [`Undefined`][] if no Nitrokey device of the given model is connected +/// - [`NotConnected`][] if no Nitrokey device of the given model is connected /// /// # Example /// @@ -692,12 +692,12 @@ pub fn connect() -> Result { /// } /// ``` /// -/// [`Undefined`]: enum.CommandError.html#variant.Undefined +/// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected pub fn connect_model(model: Model) -> Result { if connect_enum(model) { Ok(create_device_wrapper(model)) } else { - Err(CommandError::Undefined.into()) + Err(CommunicationError::NotConnected.into()) } } @@ -771,7 +771,7 @@ impl Pro { /// /// # Errors /// - /// - [`Undefined`][] if no Nitrokey device of the given model is connected + /// - [`NotConnected`][] if no Nitrokey device of the given model is connected /// /// # Example /// @@ -786,12 +786,12 @@ impl Pro { /// } /// ``` /// - /// [`Undefined`]: enum.CommandError.html#variant.Undefined + /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected pub fn connect() -> Result { // TODO: maybe Option instead of Result? match connect_enum(Model::Pro) { true => Ok(Pro {}), - false => Err(CommandError::Undefined.into()), + false => Err(CommunicationError::NotConnected.into()), } } } @@ -817,7 +817,7 @@ impl Storage { /// /// # Errors /// - /// - [`Undefined`][] if no Nitrokey device of the given model is connected + /// - [`NotConnected`][] if no Nitrokey device of the given model is connected /// /// # Example /// @@ -832,12 +832,12 @@ impl Storage { /// } /// ``` /// - /// [`Undefined`]: enum.CommandError.html#variant.Undefined + /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected pub fn connect() -> Result { // TODO: maybe Option instead of Result? match connect_enum(Model::Storage) { true => Ok(Storage {}), - false => Err(CommandError::Undefined.into()), + false => Err(CommunicationError::NotConnected.into()), } } -- cgit v1.2.1 From d87859975dc158919ecd5bf11a1111a2da5fcb30 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 17 Jan 2019 14:21:44 +0000 Subject: Check specific error codes in the tests If possible, check specific error codes instead of `is_err()`. This makes the code more readable and catches bugs resulting in the wrong error code. Also, using the assert_*_err and assert_ok macros yields error messages containing the expected and the actual value. To be able to use these macros with the `get_password_safe` method, we also have to implement `Debug` for `PasswordSafe` and `Device`. --- src/device.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 1cf9da9..16064c3 100644 --- a/src/device.rs +++ b/src/device.rs @@ -286,7 +286,7 @@ pub struct StorageStatus { /// /// This trait provides the commands that can be executed without authentication and that are /// present on all supported Nitrokey devices. -pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { +pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// Returns the model of the connected Nitrokey device. /// /// # Example -- cgit v1.2.1 From 57e3c6bf010d51842cbc86a9801fd9baee1b22eb Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 23 Jan 2019 02:29:00 +0000 Subject: Prevent direct instantiation of Pro and Storage The Pro and Storage structs may only be created using the connect functions. This patch adds a private PhantomData field to the structs to ensure that the compiler does not allow direct instantiation. --- src/device.rs | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 16064c3..40d6ba4 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,4 +1,5 @@ use std::fmt; +use std::marker; use libc; use nitrokey_sys; @@ -154,7 +155,11 @@ pub enum DeviceWrapper { /// [`connect`]: fn.connect.html /// [`Pro::connect`]: #method.connect #[derive(Debug)] -pub struct Pro {} +pub struct Pro { + // make sure that users cannot directly instantiate this type + #[doc(hidden)] + marker: marker::PhantomData<()>, +} /// A Nitrokey Storage device without user or admin authentication. /// @@ -196,7 +201,11 @@ pub struct Pro {} /// [`connect`]: fn.connect.html /// [`Storage::connect`]: #method.connect #[derive(Debug)] -pub struct Storage {} +pub struct Storage { + // make sure that users cannot directly instantiate this type + #[doc(hidden)] + marker: marker::PhantomData<()>, +} /// The status of a volume on a Nitrokey Storage device. #[derive(Debug)] @@ -713,8 +722,12 @@ fn get_connected_model() -> Option { fn create_device_wrapper(model: Model) -> DeviceWrapper { match model { - Model::Pro => DeviceWrapper::Pro(Pro {}), - Model::Storage => DeviceWrapper::Storage(Storage {}), + Model::Pro => DeviceWrapper::Pro(Pro { + marker: marker::PhantomData, + }), + Model::Storage => DeviceWrapper::Storage(Storage { + marker: marker::PhantomData, + }), } } @@ -790,7 +803,9 @@ impl Pro { pub fn connect() -> Result { // TODO: maybe Option instead of Result? match connect_enum(Model::Pro) { - true => Ok(Pro {}), + true => Ok(Pro { + marker: marker::PhantomData, + }), false => Err(CommunicationError::NotConnected.into()), } } @@ -836,7 +851,9 @@ impl Storage { pub fn connect() -> Result { // TODO: maybe Option instead of Result? match connect_enum(Model::Storage) { - true => Ok(Storage {}), + true => Ok(Storage { + marker: marker::PhantomData, + }), false => Err(CommunicationError::NotConnected.into()), } } -- cgit v1.2.1 From 5540ca5e76ffe5efe27d8819efb9e62066a10219 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 23 Jan 2019 03:46:09 +0000 Subject: Refactor and clean up all code This includes: - using idiomatic Rust - limiting the scope of unsafe blocks - simplifying code --- src/device.rs | 141 +++++++++++++++++++++++----------------------------------- 1 file changed, 56 insertions(+), 85 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 40d6ba4..287268b 100644 --- a/src/device.rs +++ b/src/device.rs @@ -22,14 +22,10 @@ pub enum Model { impl fmt::Display for Model { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match *self { - Model::Pro => "Pro", - Model::Storage => "Storage", - } - ) + f.write_str(match *self { + Model::Pro => "Pro", + Model::Storage => "Storage", + }) } } @@ -44,10 +40,10 @@ pub enum VolumeMode { impl fmt::Display for VolumeMode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - VolumeMode::ReadOnly => f.write_str("read-only"), - VolumeMode::ReadWrite => f.write_str("read-write"), - } + f.write_str(match *self { + VolumeMode::ReadOnly => "read-only", + VolumeMode::ReadWrite => "read-write", + }) } } @@ -330,7 +326,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # } /// ``` fn get_serial_number(&self) -> Result { - unsafe { result_from_string(nitrokey_sys::NK_device_serial_number()) } + result_from_string(unsafe { nitrokey_sys::NK_device_serial_number() }) } /// Returns the number of remaining authentication attempts for the user. The total number of @@ -435,16 +431,14 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # } /// ``` fn get_config(&self) -> Result { - unsafe { - let config_ptr = nitrokey_sys::NK_read_config(); - if config_ptr.is_null() { - return Err(get_last_error()); - } - let config_array_ptr = config_ptr as *const [u8; 5]; - let raw_config = RawConfig::from(*config_array_ptr); - libc::free(config_ptr as *mut libc::c_void); - return Ok(raw_config.into()); + 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. @@ -475,12 +469,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { fn change_admin_pin(&self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; let new_string = get_cstring(new)?; - unsafe { - get_command_result(nitrokey_sys::NK_change_admin_PIN( - current_string.as_ptr(), - new_string.as_ptr(), - )) - } + get_command_result(unsafe { + nitrokey_sys::NK_change_admin_PIN(current_string.as_ptr(), new_string.as_ptr()) + }) } /// Changes the user PIN. @@ -511,12 +502,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { fn change_user_pin(&self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; let new_string = get_cstring(new)?; - unsafe { - get_command_result(nitrokey_sys::NK_change_user_PIN( - current_string.as_ptr(), - new_string.as_ptr(), - )) - } + get_command_result(unsafe { + nitrokey_sys::NK_change_user_PIN(current_string.as_ptr(), new_string.as_ptr()) + }) } /// Unlocks the user PIN after three failed login attempts and sets it to the given value. @@ -547,12 +535,12 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { fn unlock_user_pin(&self, admin_pin: &str, user_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; let user_pin_string = get_cstring(user_pin)?; - unsafe { - get_command_result(nitrokey_sys::NK_unlock_user_password( + get_command_result(unsafe { + nitrokey_sys::NK_unlock_user_password( admin_pin_string.as_ptr(), user_pin_string.as_ptr(), - )) - } + ) + }) } /// Locks the Nitrokey device. @@ -576,7 +564,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # } /// ``` fn lock(&self) -> Result<(), Error> { - unsafe { get_command_result(nitrokey_sys::NK_lock_device()) } + get_command_result(unsafe { nitrokey_sys::NK_lock_device() }) } /// Performs a factory reset on the Nitrokey device. @@ -610,7 +598,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// [`build_aes_key`]: #method.build_aes_key fn factory_reset(&self, admin_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; - unsafe { get_command_result(nitrokey_sys::NK_factory_reset(admin_pin_string.as_ptr())) } + get_command_result(unsafe { nitrokey_sys::NK_factory_reset(admin_pin_string.as_ptr()) }) } /// Builds a new AES key on the Nitrokey. @@ -644,7 +632,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// [`factory_reset`]: #method.factory_reset fn build_aes_key(&self, admin_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; - unsafe { get_command_result(nitrokey_sys::NK_build_aes_key(admin_pin_string.as_ptr())) } + get_command_result(unsafe { nitrokey_sys::NK_build_aes_key(admin_pin_string.as_ptr()) }) } } @@ -670,14 +658,13 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected pub fn connect() -> Result { - unsafe { - match nitrokey_sys::NK_login_auto() { - 1 => match get_connected_device() { - Some(wrapper) => Ok(wrapper), - None => Err(CommunicationError::NotConnected.into()), - }, - _ => Err(CommunicationError::NotConnected.into()), + if unsafe { nitrokey_sys::NK_login_auto() } == 1 { + match get_connected_device() { + Some(wrapper) => Ok(wrapper), + None => Err(CommunicationError::NotConnected.into()), } + } else { + Err(CommunicationError::NotConnected.into()) } } @@ -711,12 +698,10 @@ pub fn connect_model(model: Model) -> Result { } fn get_connected_model() -> Option { - unsafe { - match nitrokey_sys::NK_get_device_model() { - nitrokey_sys::NK_device_model_NK_PRO => Some(Model::Pro), - nitrokey_sys::NK_device_model_NK_STORAGE => Some(Model::Storage), - _ => None, - } + match unsafe { nitrokey_sys::NK_get_device_model() } { + nitrokey_sys::NK_device_model_NK_PRO => Some(Model::Pro), + nitrokey_sys::NK_device_model_NK_STORAGE => Some(Model::Storage), + _ => None, } } @@ -889,12 +874,9 @@ impl Storage { pub fn change_update_pin(&self, current: &str, new: &str) -> Result<(), Error> { let current_string = get_cstring(current)?; let new_string = get_cstring(new)?; - unsafe { - get_command_result(nitrokey_sys::NK_change_update_password( - current_string.as_ptr(), - new_string.as_ptr(), - )) - } + get_command_result(unsafe { + nitrokey_sys::NK_change_update_password(current_string.as_ptr(), new_string.as_ptr()) + }) } /// Enables the firmware update mode. @@ -928,11 +910,9 @@ impl Storage { /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn enable_firmware_update(&self, update_pin: &str) -> Result<(), Error> { let update_pin_string = get_cstring(update_pin)?; - unsafe { - get_command_result(nitrokey_sys::NK_enable_firmware_update( - update_pin_string.as_ptr(), - )) - } + get_command_result(unsafe { + nitrokey_sys::NK_enable_firmware_update(update_pin_string.as_ptr()) + }) } /// Enables the encrypted storage volume. @@ -964,7 +944,7 @@ impl Storage { /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn enable_encrypted_volume(&self, user_pin: &str) -> Result<(), Error> { let user_pin = get_cstring(user_pin)?; - unsafe { get_command_result(nitrokey_sys::NK_unlock_encrypted_volume(user_pin.as_ptr())) } + get_command_result(unsafe { nitrokey_sys::NK_unlock_encrypted_volume(user_pin.as_ptr()) }) } /// Disables the encrypted storage volume. @@ -998,7 +978,7 @@ impl Storage { /// # } /// ``` pub fn disable_encrypted_volume(&self) -> Result<(), Error> { - unsafe { get_command_result(nitrokey_sys::NK_lock_encrypted_volume()) } + get_command_result(unsafe { nitrokey_sys::NK_lock_encrypted_volume() }) } /// Enables a hidden storage volume. @@ -1041,11 +1021,9 @@ impl Storage { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString pub fn enable_hidden_volume(&self, volume_password: &str) -> Result<(), Error> { let volume_password = get_cstring(volume_password)?; - unsafe { - get_command_result(nitrokey_sys::NK_unlock_hidden_volume( - volume_password.as_ptr(), - )) - } + get_command_result(unsafe { + nitrokey_sys::NK_unlock_hidden_volume(volume_password.as_ptr()) + }) } /// Disables a hidden storage volume. @@ -1080,7 +1058,7 @@ impl Storage { /// # } /// ``` pub fn disable_hidden_volume(&self) -> Result<(), Error> { - unsafe { get_command_result(nitrokey_sys::NK_lock_hidden_volume()) } + get_command_result(unsafe { nitrokey_sys::NK_lock_hidden_volume() }) } /// Creates a hidden volume. @@ -1125,14 +1103,9 @@ impl Storage { password: &str, ) -> Result<(), Error> { let password = get_cstring(password)?; - unsafe { - get_command_result(nitrokey_sys::NK_create_hidden_volume( - slot, - start, - end, - password.as_ptr(), - )) - } + get_command_result(unsafe { + nitrokey_sys::NK_create_hidden_volume(slot, start, end, password.as_ptr()) + }) } /// Sets the access mode of the unencrypted volume. @@ -1221,8 +1194,7 @@ impl Storage { stick_initialized: false, }; let raw_result = unsafe { nitrokey_sys::NK_get_status_storage(&mut raw_status) }; - let result = get_command_result(raw_result); - result.and(Ok(StorageStatus::from(raw_status))) + get_command_result(raw_result).map(|_| StorageStatus::from(raw_status)) } /// Returns the production information for the connected storage device. @@ -1263,8 +1235,7 @@ impl Storage { SD_Card_Manufacturer_u8: 0, }; let raw_result = unsafe { nitrokey_sys::NK_get_storage_production_info(&mut raw_data) }; - let result = get_command_result(raw_result); - result.and(Ok(StorageProductionInfo::from(raw_data))) + get_command_result(raw_result).map(|_| StorageProductionInfo::from(raw_data)) } /// Clears the warning for a new SD card. -- cgit v1.2.1 From 425010284341fcc745072dcd39b9fa398ae8db69 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 23 Jan 2019 04:02:51 +0000 Subject: Add Pro::new and Storage::new functions --- src/device.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 287268b..2abf801 100644 --- a/src/device.rs +++ b/src/device.rs @@ -788,12 +788,16 @@ impl Pro { pub fn connect() -> Result { // TODO: maybe Option instead of Result? match connect_enum(Model::Pro) { - true => Ok(Pro { - marker: marker::PhantomData, - }), + true => Ok(Pro::new()), false => Err(CommunicationError::NotConnected.into()), } } + + fn new() -> Pro { + Pro { + marker: marker::PhantomData, + } + } } impl Drop for Pro { @@ -836,13 +840,17 @@ impl Storage { pub fn connect() -> Result { // TODO: maybe Option instead of Result? match connect_enum(Model::Storage) { - true => Ok(Storage { - marker: marker::PhantomData, - }), + true => Ok(Storage::new()), false => Err(CommunicationError::NotConnected.into()), } } + fn new() -> Storage { + Storage { + marker: marker::PhantomData, + } + } + /// Changes the update PIN. /// /// The update PIN is used to enable firmware updates. Unlike the user and the admin PIN, the -- cgit v1.2.1 From 5ef83ad507d1e5f51152b20628314936b4fb833c Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 23 Jan 2019 04:09:26 +0000 Subject: Implement From and From for DeviceWrapper --- src/device.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 2abf801..ad75a44 100644 --- a/src/device.rs +++ b/src/device.rs @@ -707,12 +707,8 @@ fn get_connected_model() -> Option { fn create_device_wrapper(model: Model) -> DeviceWrapper { match model { - Model::Pro => DeviceWrapper::Pro(Pro { - marker: marker::PhantomData, - }), - Model::Storage => DeviceWrapper::Storage(Storage { - marker: marker::PhantomData, - }), + Model::Pro => Pro::new().into(), + Model::Storage => Storage::new().into(), } } @@ -737,6 +733,18 @@ impl DeviceWrapper { } } +impl From for DeviceWrapper { + fn from(device: Pro) -> Self { + DeviceWrapper::Pro(device) + } +} + +impl From for DeviceWrapper { + fn from(device: Storage) -> Self { + DeviceWrapper::Storage(device) + } +} + impl GenerateOtp for DeviceWrapper { fn get_hotp_slot_name(&self, slot: u8) -> Result { self.device().get_hotp_slot_name(slot) -- cgit v1.2.1 From fdb7bac3063e62776bfc13f184cf786da19f42d1 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 23 Jan 2019 16:33:26 +0100 Subject: Add license and copyright information This patch adds license and copyright information to all files to make nitrokey-rs compliant with the REUSE practices [0]. [0] https://reuse.software/practices/2.0/ --- src/device.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index ad75a44..c4af8a8 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,3 +1,6 @@ +// Copyright (C) 2018-2019 Robin Krahl +// SPDX-License-Identifier: MIT + use std::fmt; use std::marker; -- cgit v1.2.1 From 41a2303ad06f409cb932cf570ff6cc04dd6692fe Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 14:04:24 +0000 Subject: Use if instead of match for boolean expression --- src/device.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index c4af8a8..386ce94 100644 --- a/src/device.rs +++ b/src/device.rs @@ -798,9 +798,10 @@ impl Pro { /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected pub fn connect() -> Result { // TODO: maybe Option instead of Result? - match connect_enum(Model::Pro) { - true => Ok(Pro::new()), - false => Err(CommunicationError::NotConnected.into()), + if connect_enum(Model::Pro) { + Ok(Pro::new()) + } else { + Err(CommunicationError::NotConnected.into()) } } @@ -850,9 +851,10 @@ impl Storage { /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected pub fn connect() -> Result { // TODO: maybe Option instead of Result? - match connect_enum(Model::Storage) { - true => Ok(Storage::new()), - false => Err(CommunicationError::NotConnected.into()), + if connect_enum(Model::Storage) { + Ok(Storage::new()) + } else { + Err(CommunicationError::NotConnected.into()) } } -- cgit v1.2.1 From c30cbd35ba187cd6e5055d3beb8420b11fb030ec Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 23:23:00 +0000 Subject: Always return a Result when communicating with a device Previously, we sometimes returned a value without wrapping it in a result if the API method did not indicate errors in the return value. But we can detect errors using the NK_get_last_command_status function. This patch changes the return types of these methods to Result<_, Error> and adds error checks. --- src/device.rs | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 386ce94..4178922 100644 --- a/src/device.rs +++ b/src/device.rs @@ -12,7 +12,9 @@ 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}; +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)] @@ -343,13 +345,15 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; - /// let count = device.get_user_retry_count(); - /// println!("{} remaining authentication attempts (user)", count); + /// match device.get_user_retry_count() { + /// Ok(count) => println!("{} remaining authentication attempts (user)", count), + /// Err(err) => println!("Could not get user retry count: {}", err), + /// } /// # Ok(()) /// # } /// ``` - fn get_user_retry_count(&self) -> u8 { - unsafe { nitrokey_sys::NK_get_user_retry_count() } + fn get_user_retry_count(&self) -> Result { + 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 @@ -364,12 +368,15 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; /// let count = device.get_admin_retry_count(); - /// println!("{} remaining authentication attempts (admin)", count); + /// match device.get_admin_retry_count() { + /// Ok(count) => println!("{} remaining authentication attempts (admin)", count), + /// Err(err) => println!("Could not get admin retry count: {}", err), + /// } /// # Ok(()) /// # } /// ``` - fn get_admin_retry_count(&self) -> u8 { - unsafe { nitrokey_sys::NK_get_admin_retry_count() } + fn get_admin_retry_count(&self) -> Result { + result_or_error(unsafe { nitrokey_sys::NK_get_admin_retry_count() }) } /// Returns the major part of the firmware version (should be zero). @@ -384,14 +391,14 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// let device = nitrokey::connect()?; /// println!( /// "Firmware version: {}.{}", - /// device.get_major_firmware_version(), - /// device.get_minor_firmware_version(), + /// device.get_major_firmware_version().unwrap(), + /// device.get_minor_firmware_version().unwrap(), /// ); /// # Ok(()) /// # } /// ``` - fn get_major_firmware_version(&self) -> i32 { - unsafe { nitrokey_sys::NK_get_major_firmware_version() } + fn get_major_firmware_version(&self) -> Result { + result_or_error(unsafe { nitrokey_sys::NK_get_major_firmware_version() }) } /// Returns the minor part of the firmware version (for example 8 for version 0.8). @@ -406,13 +413,13 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// let device = nitrokey::connect()?; /// println!( /// "Firmware version: {}.{}", - /// device.get_major_firmware_version(), - /// device.get_minor_firmware_version(), + /// device.get_major_firmware_version().unwrap(), + /// device.get_minor_firmware_version().unwrap(), /// ); /// # Ok(()) /// # } - fn get_minor_firmware_version(&self) -> i32 { - unsafe { nitrokey_sys::NK_get_minor_firmware_version() } + fn get_minor_firmware_version(&self) -> Result { + result_or_error(unsafe { nitrokey_sys::NK_get_minor_firmware_version() }) } /// Returns the current configuration of the Nitrokey device. -- cgit v1.2.1 From 1d68e24db4078ad1a004afd7bec90a81e7d31ec8 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 23:34:04 +0000 Subject: Add get_firmware_version method This patch combines the get_{major,minor}_firmware_version methods into the new get_firmware_version method that returns a FirmwareVersion struct. Currently, this requires casting from i32 to u8. But this will be fixed with the next libnitrokey version as we change the return types for the firmware getters. --- src/device.rs | 45 ++++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 29 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 4178922..2fac4f2 100644 --- a/src/device.rs +++ b/src/device.rs @@ -379,7 +379,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { result_or_error(unsafe { nitrokey_sys::NK_get_admin_retry_count() }) } - /// Returns the major part of the firmware version (should be zero). + /// Returns the firmware version. /// /// # Example /// @@ -389,37 +389,24 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::connect()?; - /// println!( - /// "Firmware version: {}.{}", - /// device.get_major_firmware_version().unwrap(), - /// device.get_minor_firmware_version().unwrap(), - /// ); + /// match device.get_firmware_version() { + /// Ok(version) => println!("Firmware version: {}", version), + /// Err(err) => println!("Could not access firmware version: {}", err), + /// }; /// # Ok(()) /// # } /// ``` - fn get_major_firmware_version(&self) -> Result { - result_or_error(unsafe { nitrokey_sys::NK_get_major_firmware_version() }) - } - - /// Returns the minor part of the firmware version (for example 8 for version 0.8). - /// - /// # Example - /// - /// ```no_run - /// use nitrokey::Device; - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; - /// println!( - /// "Firmware version: {}.{}", - /// device.get_major_firmware_version().unwrap(), - /// device.get_minor_firmware_version().unwrap(), - /// ); - /// # Ok(()) - /// # } - fn get_minor_firmware_version(&self) -> Result { - result_or_error(unsafe { nitrokey_sys::NK_get_minor_firmware_version() }) + fn get_firmware_version(&self) -> Result { + 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() })?; + let max = i32::from(u8::max_value()); + if major < 0 || minor < 0 || major > max || minor > max { + return Err(Error::UnexpectedError); + } + Ok(FirmwareVersion { + major: major as u8, + minor: minor as u8, + }) } /// Returns the current configuration of the Nitrokey device. -- cgit v1.2.1 From 12b6a4d6c56cba4c2a87027d7c5f26ebe42d3315 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 28 Jan 2019 20:02:18 +0000 Subject: Prefer eprintln over println for error messages --- src/device.rs | 58 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 2fac4f2..d199d9a 100644 --- a/src/device.rs +++ b/src/device.rs @@ -78,7 +78,7 @@ impl fmt::Display for VolumeMode { /// user.device() /// }, /// Err((device, err)) => { -/// println!("Could not authenticate as user: {}", err); +/// eprintln!("Could not authenticate as user: {}", err); /// device /// }, /// }; @@ -142,7 +142,7 @@ pub enum DeviceWrapper { /// user.device() /// }, /// Err((device, err)) => { -/// println!("Could not authenticate as user: {}", err); +/// eprintln!("Could not authenticate as user: {}", err); /// device /// }, /// }; @@ -188,7 +188,7 @@ pub struct Pro { /// user.device() /// }, /// Err((device, err)) => { -/// println!("Could not authenticate as user: {}", err); +/// eprintln!("Could not authenticate as user: {}", err); /// device /// }, /// }; @@ -325,7 +325,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// let device = nitrokey::connect()?; /// match device.get_serial_number() { /// Ok(number) => println!("serial no: {}", number), - /// Err(err) => println!("Could not get serial number: {}", err), + /// Err(err) => eprintln!("Could not get serial number: {}", err), /// }; /// # Ok(()) /// # } @@ -347,7 +347,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// let device = nitrokey::connect()?; /// match device.get_user_retry_count() { /// Ok(count) => println!("{} remaining authentication attempts (user)", count), - /// Err(err) => println!("Could not get user retry count: {}", err), + /// Err(err) => eprintln!("Could not get user retry count: {}", err), /// } /// # Ok(()) /// # } @@ -370,7 +370,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// let count = device.get_admin_retry_count(); /// match device.get_admin_retry_count() { /// Ok(count) => println!("{} remaining authentication attempts (admin)", count), - /// Err(err) => println!("Could not get admin retry count: {}", err), + /// Err(err) => eprintln!("Could not get admin retry count: {}", err), /// } /// # Ok(()) /// # } @@ -391,7 +391,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// let device = nitrokey::connect()?; /// match device.get_firmware_version() { /// Ok(version) => println!("Firmware version: {}", version), - /// Err(err) => println!("Could not access firmware version: {}", err), + /// Err(err) => eprintln!("Could not access firmware version: {}", err), /// }; /// # Ok(()) /// # } @@ -455,7 +455,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// let device = nitrokey::connect()?; /// match device.change_admin_pin("12345678", "12345679") { /// Ok(()) => println!("Updated admin PIN."), - /// Err(err) => println!("Failed to update admin PIN: {}", err), + /// Err(err) => eprintln!("Failed to update admin PIN: {}", err), /// }; /// # Ok(()) /// # } @@ -488,7 +488,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// let device = nitrokey::connect()?; /// match device.change_user_pin("123456", "123457") { /// Ok(()) => println!("Updated admin PIN."), - /// Err(err) => println!("Failed to update admin PIN: {}", err), + /// Err(err) => eprintln!("Failed to update admin PIN: {}", err), /// }; /// # Ok(()) /// # } @@ -521,7 +521,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// let device = nitrokey::connect()?; /// match device.unlock_user_pin("12345678", "123456") { /// Ok(()) => println!("Unlocked user PIN."), - /// Err(err) => println!("Failed to unlock user PIN: {}", err), + /// Err(err) => eprintln!("Failed to unlock user PIN: {}", err), /// }; /// # Ok(()) /// # } @@ -555,7 +555,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// let device = nitrokey::connect()?; /// match device.lock() { /// Ok(()) => println!("Locked the Nitrokey device."), - /// Err(err) => println!("Could not lock the Nitrokey device: {}", err), + /// Err(err) => eprintln!("Could not lock the Nitrokey device: {}", err), /// }; /// # Ok(()) /// # } @@ -586,7 +586,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// let device = nitrokey::connect()?; /// match device.factory_reset("12345678") { /// Ok(()) => println!("Performed a factory reset."), - /// Err(err) => println!("Could not perform a factory reset: {}", err), + /// Err(err) => eprintln!("Could not perform a factory reset: {}", err), /// }; /// # Ok(()) /// # } @@ -620,7 +620,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// let device = nitrokey::connect()?; /// match device.build_aes_key("12345678") { /// Ok(()) => println!("New AES keys have been built."), - /// Err(err) => println!("Could not build new AES keys: {}", err), + /// Err(err) => eprintln!("Could not build new AES keys: {}", err), /// }; /// # Ok(()) /// # } @@ -649,7 +649,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// /// match nitrokey::connect() { /// Ok(device) => do_something(device), -/// Err(err) => println!("Could not connect to a Nitrokey: {}", err), +/// Err(err) => eprintln!("Could not connect to a Nitrokey: {}", err), /// } /// ``` /// @@ -681,7 +681,7 @@ pub fn connect() -> Result { /// /// match nitrokey::connect_model(Model::Pro) { /// Ok(device) => do_something(device), -/// Err(err) => println!("Could not connect to a Nitrokey Pro: {}", err), +/// Err(err) => eprintln!("Could not connect to a Nitrokey Pro: {}", err), /// } /// ``` /// @@ -785,7 +785,7 @@ impl Pro { /// /// match nitrokey::Pro::connect() { /// Ok(device) => use_pro(device), - /// Err(err) => println!("Could not connect to the Nitrokey Pro: {}", err), + /// Err(err) => eprintln!("Could not connect to the Nitrokey Pro: {}", err), /// } /// ``` /// @@ -838,7 +838,7 @@ impl Storage { /// /// match nitrokey::Storage::connect() { /// Ok(device) => use_storage(device), - /// Err(err) => println!("Could not connect to the Nitrokey Storage: {}", err), + /// Err(err) => eprintln!("Could not connect to the Nitrokey Storage: {}", err), /// } /// ``` /// @@ -878,7 +878,7 @@ impl Storage { /// let device = nitrokey::Storage::connect()?; /// match device.change_update_pin("12345678", "87654321") { /// Ok(()) => println!("Updated update PIN."), - /// Err(err) => println!("Failed to update update PIN: {}", err), + /// Err(err) => eprintln!("Failed to update update PIN: {}", err), /// }; /// # Ok(()) /// # } @@ -915,7 +915,7 @@ impl Storage { /// let device = nitrokey::Storage::connect()?; /// match device.enable_firmware_update("12345678") { /// Ok(()) => println!("Nitrokey entered update mode."), - /// Err(err) => println!("Could not enter update mode: {}", err), + /// Err(err) => eprintln!("Could not enter update mode: {}", err), /// }; /// # Ok(()) /// # } @@ -949,7 +949,7 @@ impl Storage { /// let device = nitrokey::Storage::connect()?; /// match device.enable_encrypted_volume("123456") { /// Ok(()) => println!("Enabled the encrypted volume."), - /// Err(err) => println!("Could not enable the encrypted volume: {}", err), + /// Err(err) => eprintln!("Could not enable the encrypted volume: {}", err), /// }; /// # Ok(()) /// # } @@ -983,11 +983,11 @@ impl Storage { /// match device.disable_encrypted_volume() { /// Ok(()) => println!("Disabled the encrypted volume."), /// Err(err) => { - /// println!("Could not disable the encrypted volume: {}", err); + /// eprintln!("Could not disable the encrypted volume: {}", err); /// }, /// }; /// }, - /// Err(err) => println!("Could not enable the encrypted volume: {}", err), + /// Err(err) => eprintln!("Could not enable the encrypted volume: {}", err), /// }; /// # Ok(()) /// # } @@ -1025,7 +1025,7 @@ impl Storage { /// device.enable_encrypted_volume("123445")?; /// match device.enable_hidden_volume("hidden-pw") { /// Ok(()) => println!("Enabled a hidden volume."), - /// Err(err) => println!("Could not enable the hidden volume: {}", err), + /// Err(err) => eprintln!("Could not enable the hidden volume: {}", err), /// }; /// # Ok(()) /// # } @@ -1063,11 +1063,11 @@ impl Storage { /// match device.disable_hidden_volume() { /// Ok(()) => println!("Disabled the hidden volume."), /// Err(err) => { - /// println!("Could not disable the hidden volume: {}", err); + /// eprintln!("Could not disable the hidden volume: {}", err); /// }, /// }; /// }, - /// Err(err) => println!("Could not enable the hidden volume: {}", err), + /// Err(err) => eprintln!("Could not enable the hidden volume: {}", err), /// }; /// # Ok(()) /// # } @@ -1144,7 +1144,7 @@ impl Storage { /// let device = nitrokey::Storage::connect()?; /// match device.set_unencrypted_volume_mode("123456", VolumeMode::ReadWrite) { /// Ok(()) => println!("Set the unencrypted volume to read-write mode."), - /// Err(err) => println!("Could not set the unencrypted volume to read-write mode: {}", err), + /// Err(err) => eprintln!("Could not set the unencrypted volume to read-write mode: {}", err), /// }; /// # Ok(()) /// # } @@ -1184,7 +1184,7 @@ impl Storage { /// Ok(status) => { /// println!("SD card ID: {:#x}", status.serial_number_sd_card); /// }, - /// Err(err) => println!("Could not get Storage status: {}", err), + /// Err(err) => eprintln!("Could not get Storage status: {}", err), /// }; /// # Ok(()) /// # } @@ -1228,7 +1228,7 @@ impl Storage { /// println!("SD card ID: {:#x}", data.sd_card.serial_number); /// println!("SD card size: {} GB", data.sd_card.size); /// }, - /// Err(err) => println!("Could not get Storage production info: {}", err), + /// Err(err) => eprintln!("Could not get Storage production info: {}", err), /// }; /// # Ok(()) /// # } @@ -1273,7 +1273,7 @@ impl Storage { /// let device = nitrokey::Storage::connect()?; /// match device.clear_new_sd_card_warning("12345678") { /// Ok(()) => println!("Cleared the new SD card warning."), - /// Err(err) => println!("Could not set the clear the new SD card warning: {}", err), + /// Err(err) => eprintln!("Could not set the clear the new SD card warning: {}", err), /// }; /// # Ok(()) /// # } -- cgit v1.2.1 From ad76653b3be57c0cfd31c8056a8d68537034324e Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 31 Jan 2019 11:07:50 +0000 Subject: Add set_encrypted_volume_mode method to Storage Previously, we considered this command as unsupported as it only was available with firmware version 0.49. But as discussed in nitrocli issue 80 [0], it will probably be re-enabled in future firmware versions. Therefore this patch adds the set_encrypted_volume_mode to Storage. [0] https://github.com/d-e-s-o/nitrocli/issues/80 --- src/device.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index d199d9a..c985802 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1142,7 +1142,7 @@ impl Storage { /// /// # fn try_main() -> Result<(), Error> { /// let device = nitrokey::Storage::connect()?; - /// match device.set_unencrypted_volume_mode("123456", VolumeMode::ReadWrite) { + /// match device.set_unencrypted_volume_mode("12345678", VolumeMode::ReadWrite) { /// Ok(()) => println!("Set the unencrypted volume to read-write mode."), /// Err(err) => eprintln!("Could not set the unencrypted volume to read-write mode: {}", err), /// }; @@ -1169,6 +1169,51 @@ impl Storage { get_command_result(result) } + /// Sets the access mode of the encrypted volume. + /// + /// This command will reconnect the encrypted volume so buffers should be flushed before + /// calling it. It is only available in firmware version 0.49. + /// + /// # Errors + /// + /// - [`InvalidString`][] if the provided password contains a null byte + /// - [`WrongPassword`][] if the provided admin password is wrong + /// + /// # Example + /// + /// ```no_run + /// # use nitrokey::Error; + /// use nitrokey::VolumeMode; + /// + /// # fn try_main() -> Result<(), Error> { + /// let device = nitrokey::Storage::connect()?; + /// match device.set_encrypted_volume_mode("12345678", VolumeMode::ReadWrite) { + /// Ok(()) => println!("Set the encrypted volume to read-write mode."), + /// Err(err) => eprintln!("Could not set the encrypted volume to read-write mode: {}", err), + /// }; + /// # Ok(()) + /// # } + /// ``` + /// + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString + /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword + pub fn set_encrypted_volume_mode( + &self, + admin_pin: &str, + mode: VolumeMode, + ) -> Result<(), Error> { + let admin_pin = get_cstring(admin_pin)?; + let result = match mode { + VolumeMode::ReadOnly => unsafe { + nitrokey_sys::NK_set_encrypted_read_only(admin_pin.as_ptr()) + }, + VolumeMode::ReadWrite => unsafe { + nitrokey_sys::NK_set_encrypted_read_write(admin_pin.as_ptr()) + }, + }; + get_command_result(result) + } + /// Returns the status of the connected storage device. /// /// # Example -- cgit v1.2.1 From eef2118717878f3543248ebf2d099aebbedceacf Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 30 Jan 2019 16:02:49 +0000 Subject: Add device_mut method to DeviceWrapper To prepare the mutability refactoring, we add a device_mut method to DeviceWrapper that can be used to obtain a mutable reference to the wrapped device. --- src/device.rs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index c985802..462e9dc 100644 --- a/src/device.rs +++ b/src/device.rs @@ -728,6 +728,13 @@ impl DeviceWrapper { DeviceWrapper::Pro(ref pro) => pro, } } + + fn device_mut(&mut self) -> &mut dyn Device { + match *self { + DeviceWrapper::Storage(ref mut storage) => storage, + DeviceWrapper::Pro(ref mut pro) => pro, + } + } } impl From for DeviceWrapper { -- cgit v1.2.1 From f49e61589e32217f97c94aa86d826f6b65170fba Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 28 Jan 2019 12:27:15 +0000 Subject: Require mutable reference if method changes device state Previously, all methods that access a Nitrokey device took a reference to the device as input. This method changes methods that change the device state to require a mutable reference instead. In most case, this is straightforward as the method writes data to the device (for example write_config or change_user_pin). But there are two edge cases: - Authenticating with a PIN changes the device state as it may decrease the PIN retry counter if the authentication fails. - Generating an HOTP code changes the device state as it increases the HOTP counter. --- src/device.rs | 72 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 36 insertions(+), 36 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 462e9dc..f6492cd 100644 --- a/src/device.rs +++ b/src/device.rs @@ -452,7 +452,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.change_admin_pin("12345678", "12345679") { /// Ok(()) => println!("Updated admin PIN."), /// Err(err) => eprintln!("Failed to update admin PIN: {}", err), @@ -463,7 +463,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn change_admin_pin(&self, current: &str, new: &str) -> Result<(), Error> { + 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 { @@ -485,7 +485,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.change_user_pin("123456", "123457") { /// Ok(()) => println!("Updated admin PIN."), /// Err(err) => eprintln!("Failed to update admin PIN: {}", err), @@ -496,7 +496,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn change_user_pin(&self, current: &str, new: &str) -> Result<(), Error> { + 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 { @@ -518,7 +518,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.unlock_user_pin("12345678", "123456") { /// Ok(()) => println!("Unlocked user PIN."), /// Err(err) => eprintln!("Failed to unlock user PIN: {}", err), @@ -529,7 +529,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn unlock_user_pin(&self, admin_pin: &str, user_pin: &str) -> Result<(), Error> { + 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 { @@ -552,7 +552,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.lock() { /// Ok(()) => println!("Locked the Nitrokey device."), /// Err(err) => eprintln!("Could not lock the Nitrokey device: {}", err), @@ -560,7 +560,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # Ok(()) /// # } /// ``` - fn lock(&self) -> Result<(), Error> { + fn lock(&mut self) -> Result<(), Error> { get_command_result(unsafe { nitrokey_sys::NK_lock_device() }) } @@ -583,7 +583,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.factory_reset("12345678") { /// Ok(()) => println!("Performed a factory reset."), /// Err(err) => eprintln!("Could not perform a factory reset: {}", err), @@ -593,7 +593,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// ``` /// /// [`build_aes_key`]: #method.build_aes_key - fn factory_reset(&self, admin_pin: &str) -> Result<(), Error> { + 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()) }) } @@ -617,7 +617,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::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), @@ -627,7 +627,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// ``` /// /// [`factory_reset`]: #method.factory_reset - fn build_aes_key(&self, admin_pin: &str) -> Result<(), Error> { + 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()) }) } @@ -758,8 +758,8 @@ impl GenerateOtp for DeviceWrapper { self.device().get_totp_slot_name(slot) } - fn get_hotp_code(&self, slot: u8) -> Result { - self.device().get_hotp_code(slot) + fn get_hotp_code(&mut self, slot: u8) -> Result { + self.device_mut().get_hotp_code(slot) } fn get_totp_code(&self, slot: u8) -> Result { @@ -882,7 +882,7 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// match device.change_update_pin("12345678", "87654321") { /// Ok(()) => println!("Updated update PIN."), /// Err(err) => eprintln!("Failed to update update PIN: {}", err), @@ -893,7 +893,7 @@ impl Storage { /// /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn change_update_pin(&self, current: &str, new: &str) -> Result<(), Error> { + pub fn change_update_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 { @@ -919,7 +919,7 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// match device.enable_firmware_update("12345678") { /// Ok(()) => println!("Nitrokey entered update mode."), /// Err(err) => eprintln!("Could not enter update mode: {}", err), @@ -930,7 +930,7 @@ impl Storage { /// /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn enable_firmware_update(&self, update_pin: &str) -> Result<(), Error> { + pub fn enable_firmware_update(&mut self, update_pin: &str) -> Result<(), Error> { let update_pin_string = get_cstring(update_pin)?; get_command_result(unsafe { nitrokey_sys::NK_enable_firmware_update(update_pin_string.as_ptr()) @@ -953,7 +953,7 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// match device.enable_encrypted_volume("123456") { /// Ok(()) => println!("Enabled the encrypted volume."), /// Err(err) => eprintln!("Could not enable the encrypted volume: {}", err), @@ -964,7 +964,7 @@ impl Storage { /// /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn enable_encrypted_volume(&self, user_pin: &str) -> Result<(), Error> { + pub fn enable_encrypted_volume(&mut self, user_pin: &str) -> Result<(), Error> { let user_pin = get_cstring(user_pin)?; get_command_result(unsafe { nitrokey_sys::NK_unlock_encrypted_volume(user_pin.as_ptr()) }) } @@ -982,7 +982,7 @@ impl Storage { /// fn use_volume() {} /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// match device.enable_encrypted_volume("123456") { /// Ok(()) => { /// println!("Enabled the encrypted volume."); @@ -999,7 +999,7 @@ impl Storage { /// # Ok(()) /// # } /// ``` - pub fn disable_encrypted_volume(&self) -> Result<(), Error> { + pub fn disable_encrypted_volume(&mut self) -> Result<(), Error> { get_command_result(unsafe { nitrokey_sys::NK_lock_encrypted_volume() }) } @@ -1028,7 +1028,7 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// device.enable_encrypted_volume("123445")?; /// match device.enable_hidden_volume("hidden-pw") { /// Ok(()) => println!("Enabled a hidden volume."), @@ -1041,7 +1041,7 @@ impl Storage { /// [`enable_encrypted_volume`]: #method.enable_encrypted_volume /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString - pub fn enable_hidden_volume(&self, volume_password: &str) -> Result<(), Error> { + pub fn enable_hidden_volume(&mut self, volume_password: &str) -> Result<(), Error> { let volume_password = get_cstring(volume_password)?; get_command_result(unsafe { nitrokey_sys::NK_unlock_hidden_volume(volume_password.as_ptr()) @@ -1061,7 +1061,7 @@ impl Storage { /// fn use_volume() {} /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// device.enable_encrypted_volume("123445")?; /// match device.enable_hidden_volume("hidden-pw") { /// Ok(()) => { @@ -1079,7 +1079,7 @@ impl Storage { /// # Ok(()) /// # } /// ``` - pub fn disable_hidden_volume(&self) -> Result<(), Error> { + pub fn disable_hidden_volume(&mut self) -> Result<(), Error> { get_command_result(unsafe { nitrokey_sys::NK_lock_hidden_volume() }) } @@ -1108,7 +1108,7 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// device.enable_encrypted_volume("123445")?; /// device.create_hidden_volume(0, 0, 100, "hidden-pw")?; /// # Ok(()) @@ -1118,7 +1118,7 @@ impl Storage { /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString pub fn create_hidden_volume( - &self, + &mut self, slot: u8, start: u8, end: u8, @@ -1148,7 +1148,7 @@ impl Storage { /// use nitrokey::VolumeMode; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// match device.set_unencrypted_volume_mode("12345678", VolumeMode::ReadWrite) { /// Ok(()) => println!("Set the unencrypted volume to read-write mode."), /// Err(err) => eprintln!("Could not set the unencrypted volume to read-write mode: {}", err), @@ -1160,7 +1160,7 @@ impl Storage { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn set_unencrypted_volume_mode( - &self, + &mut self, admin_pin: &str, mode: VolumeMode, ) -> Result<(), Error> { @@ -1193,7 +1193,7 @@ impl Storage { /// use nitrokey::VolumeMode; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// match device.set_encrypted_volume_mode("12345678", VolumeMode::ReadWrite) { /// Ok(()) => println!("Set the encrypted volume to read-write mode."), /// Err(err) => eprintln!("Could not set the encrypted volume to read-write mode: {}", err), @@ -1205,7 +1205,7 @@ impl Storage { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword pub fn set_encrypted_volume_mode( - &self, + &mut self, admin_pin: &str, mode: VolumeMode, ) -> Result<(), Error> { @@ -1322,7 +1322,7 @@ impl Storage { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut device = nitrokey::Storage::connect()?; /// match device.clear_new_sd_card_warning("12345678") { /// Ok(()) => println!("Cleared the new SD card warning."), /// Err(err) => eprintln!("Could not set the clear the new SD card warning: {}", err), @@ -1333,7 +1333,7 @@ impl Storage { /// /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn clear_new_sd_card_warning(&self, admin_pin: &str) -> Result<(), Error> { + pub fn clear_new_sd_card_warning(&mut self, admin_pin: &str) -> Result<(), Error> { let admin_pin = get_cstring(admin_pin)?; get_command_result(unsafe { nitrokey_sys::NK_clear_new_sd_card_warning(admin_pin.as_ptr()) @@ -1341,7 +1341,7 @@ impl Storage { } /// Blinks the red and green LED alternatively and infinitely until the device is reconnected. - pub fn wink(&self) -> Result<(), Error> { + pub fn wink(&mut self) -> Result<(), Error> { get_command_result(unsafe { nitrokey_sys::NK_wink() }) } @@ -1361,7 +1361,7 @@ impl Storage { /// /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn export_firmware(&self, admin_pin: &str) -> Result<(), Error> { + pub fn export_firmware(&mut self, admin_pin: &str) -> Result<(), Error> { let admin_pin_string = get_cstring(admin_pin)?; get_command_result(unsafe { nitrokey_sys::NK_export_firmware(admin_pin_string.as_ptr()) }) } -- cgit v1.2.1 From 0972bbe82623c3d9649b6023d8f50d304aa0cde6 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 28 Jan 2019 14:24:12 +0000 Subject: Refactor User and Admin to use a mutable reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the initial nitrokey-rs implementation, the Admin and the User struct take the Device by value to make sure that the user cannot initiate a second authentication while this first is still active (which would invalidate the temporary password). Now we realized that this is not necessary – taking a mutable reference has the same effect, but leads to a much cleaner API. This patch refactors the Admin and User structs – and all dependent code – to use a mutable reference instead of a Device value. --- src/device.rs | 42 ++++++++++++------------------------------ 1 file changed, 12 insertions(+), 30 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index f6492cd..a0df30e 100644 --- a/src/device.rs +++ b/src/device.rs @@ -71,16 +71,10 @@ impl fmt::Display for VolumeMode { /// fn perform_other_task(device: &DeviceWrapper) {} /// /// # fn try_main() -> Result<(), Error> { -/// let device = nitrokey::connect()?; -/// let device = match device.authenticate_user("123456") { -/// Ok(user) => { -/// perform_user_task(&user); -/// user.device() -/// }, -/// Err((device, err)) => { -/// eprintln!("Could not authenticate as user: {}", err); -/// device -/// }, +/// let mut device = nitrokey::connect()?; +/// match device.authenticate_user("123456") { +/// Ok(user) => perform_user_task(&user), +/// Err(err) => eprintln!("Could not authenticate as user: {}", err), /// }; /// perform_other_task(&device); /// # Ok(()) @@ -135,16 +129,10 @@ pub enum DeviceWrapper { /// fn perform_other_task(device: &Pro) {} /// /// # fn try_main() -> Result<(), Error> { -/// let device = nitrokey::Pro::connect()?; -/// let device = match device.authenticate_user("123456") { -/// Ok(user) => { -/// perform_user_task(&user); -/// user.device() -/// }, -/// Err((device, err)) => { -/// eprintln!("Could not authenticate as user: {}", err); -/// device -/// }, +/// let mut device = nitrokey::Pro::connect()?; +/// match device.authenticate_user("123456") { +/// Ok(user) => perform_user_task(&user), +/// Err(err) => eprintln!("Could not authenticate as user: {}", err), /// }; /// perform_other_task(&device); /// # Ok(()) @@ -181,16 +169,10 @@ pub struct Pro { /// fn perform_other_task(device: &Storage) {} /// /// # fn try_main() -> Result<(), Error> { -/// let device = nitrokey::Storage::connect()?; -/// let device = match device.authenticate_user("123456") { -/// Ok(user) => { -/// perform_user_task(&user); -/// user.device() -/// }, -/// Err((device, err)) => { -/// eprintln!("Could not authenticate as user: {}", err); -/// device -/// }, +/// let mut device = nitrokey::Storage::connect()?; +/// match device.authenticate_user("123456") { +/// Ok(user) => perform_user_task(&user), +/// Err(err) => eprintln!("Could not authenticate as user: {}", err), /// }; /// perform_other_task(&device); /// # Ok(()) -- cgit v1.2.1 From 83641ca0518e4c766c63e40d0787e4f0b436652a Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 5 Feb 2019 12:47:24 +0000 Subject: Revert "Refactor User and Admin to use a mutable reference" This reverts commit 0972bbe82623c3d9649b6023d8f50d304aa0cde6. --- src/device.rs | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index a0df30e..f6492cd 100644 --- a/src/device.rs +++ b/src/device.rs @@ -71,10 +71,16 @@ impl fmt::Display for VolumeMode { /// fn perform_other_task(device: &DeviceWrapper) {} /// /// # fn try_main() -> Result<(), Error> { -/// let mut device = nitrokey::connect()?; -/// match device.authenticate_user("123456") { -/// Ok(user) => perform_user_task(&user), -/// Err(err) => eprintln!("Could not authenticate as user: {}", err), +/// let device = nitrokey::connect()?; +/// let device = match device.authenticate_user("123456") { +/// Ok(user) => { +/// perform_user_task(&user); +/// user.device() +/// }, +/// Err((device, err)) => { +/// eprintln!("Could not authenticate as user: {}", err); +/// device +/// }, /// }; /// perform_other_task(&device); /// # Ok(()) @@ -129,10 +135,16 @@ pub enum DeviceWrapper { /// fn perform_other_task(device: &Pro) {} /// /// # fn try_main() -> Result<(), Error> { -/// let mut device = nitrokey::Pro::connect()?; -/// match device.authenticate_user("123456") { -/// Ok(user) => perform_user_task(&user), -/// Err(err) => eprintln!("Could not authenticate as user: {}", err), +/// let device = nitrokey::Pro::connect()?; +/// let device = match device.authenticate_user("123456") { +/// Ok(user) => { +/// perform_user_task(&user); +/// user.device() +/// }, +/// Err((device, err)) => { +/// eprintln!("Could not authenticate as user: {}", err); +/// device +/// }, /// }; /// perform_other_task(&device); /// # Ok(()) @@ -169,10 +181,16 @@ pub struct Pro { /// fn perform_other_task(device: &Storage) {} /// /// # fn try_main() -> Result<(), Error> { -/// let mut device = nitrokey::Storage::connect()?; -/// match device.authenticate_user("123456") { -/// Ok(user) => perform_user_task(&user), -/// Err(err) => eprintln!("Could not authenticate as user: {}", err), +/// let device = nitrokey::Storage::connect()?; +/// let device = match device.authenticate_user("123456") { +/// Ok(user) => { +/// perform_user_task(&user); +/// user.device() +/// }, +/// Err((device, err)) => { +/// eprintln!("Could not authenticate as user: {}", err); +/// device +/// }, /// }; /// perform_other_task(&device); /// # Ok(()) -- cgit v1.2.1 From e923c8b1ddaeafc9494ae86738bed9ad0e0e6e8f Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Fri, 5 Jul 2019 22:59:27 +0000 Subject: Update nitrokey-sys to version 3.5 As the return type of the NK_get_{major,minor}_firmware_version methods changed with libnitrokey 3.5, we also have to adapt our get_firmware_version function in device.rs. This patch also updates the changelog and the todo list with the changes caused by the new libnitrokey version. --- src/device.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index f6492cd..6597ba9 100644 --- a/src/device.rs +++ b/src/device.rs @@ -399,13 +399,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { fn get_firmware_version(&self) -> Result { 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() })?; - let max = i32::from(u8::max_value()); - if major < 0 || minor < 0 || major > max || minor > max { - return Err(Error::UnexpectedError); - } Ok(FirmwareVersion { - major: major as u8, - minor: minor as u8, + major, + minor, }) } -- cgit v1.2.1 From 34f7022ce078dc132734873a3efdefe3d6d4399a Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 7 Jul 2019 20:40:20 +0000 Subject: Fix formatting error in device.rs --- src/device.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 6597ba9..51551c2 100644 --- a/src/device.rs +++ b/src/device.rs @@ -399,10 +399,7 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { fn get_firmware_version(&self) -> Result { 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, - }) + Ok(FirmwareVersion { major, minor }) } /// Returns the current configuration of the Nitrokey device. -- cgit v1.2.1 From 54d23475aa3b712a539bad129fe37223173268f2 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 17:44:59 +0000 Subject: Move the connect function into Manager As part of the connection refactoring, we replace the connect function with the Manager::connect method. To maintain compatibility with nitrokey-test, the connect function is not removed but marked as deprecated. --- src/device.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 51551c2..653c5d1 100644 --- a/src/device.rs +++ b/src/device.rs @@ -647,15 +647,9 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { /// ``` /// /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected +#[deprecated(since = "0.4.0", note = "use `nitrokey::Manager::connect` instead")] pub fn connect() -> Result { - if unsafe { nitrokey_sys::NK_login_auto() } == 1 { - match get_connected_device() { - Some(wrapper) => Ok(wrapper), - None => Err(CommunicationError::NotConnected.into()), - } - } else { - Err(CommunicationError::NotConnected.into()) - } + crate::take()?.connect().map_err(Into::into) } /// Connects to a Nitrokey device of the given model. @@ -702,7 +696,7 @@ fn create_device_wrapper(model: Model) -> DeviceWrapper { } } -fn get_connected_device() -> Option { +pub(crate) fn get_connected_device() -> Option { get_connected_model().map(create_device_wrapper) } -- cgit v1.2.1 From bd7c7a5fdf0ae66a1ff2f00beb5ed4c2e6994ca1 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 18:07:59 +0000 Subject: Move the connect_model function into Manager As part of the connection refactoring, this patch moves the connect_model function to the Manager struct. As the connect_model function is not used by nitrokey-test, it is removed. --- src/device.rs | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 653c5d1..f28558d 100644 --- a/src/device.rs +++ b/src/device.rs @@ -652,35 +652,6 @@ pub fn connect() -> Result { crate::take()?.connect().map_err(Into::into) } -/// Connects to a Nitrokey device of the given model. -/// -/// # Errors -/// -/// - [`NotConnected`][] if no Nitrokey device of the given model is connected -/// -/// # Example -/// -/// ``` -/// use nitrokey::DeviceWrapper; -/// use nitrokey::Model; -/// -/// fn do_something(device: DeviceWrapper) {} -/// -/// match nitrokey::connect_model(Model::Pro) { -/// Ok(device) => do_something(device), -/// Err(err) => eprintln!("Could not connect to a Nitrokey Pro: {}", err), -/// } -/// ``` -/// -/// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected -pub fn connect_model(model: Model) -> Result { - if connect_enum(model) { - Ok(create_device_wrapper(model)) - } else { - Err(CommunicationError::NotConnected.into()) - } -} - fn get_connected_model() -> Option { match unsafe { nitrokey_sys::NK_get_device_model() } { nitrokey_sys::NK_device_model_NK_PRO => Some(Model::Pro), @@ -689,7 +660,7 @@ fn get_connected_model() -> Option { } } -fn create_device_wrapper(model: Model) -> DeviceWrapper { +pub(crate) fn create_device_wrapper(model: Model) -> DeviceWrapper { match model { Model::Pro => Pro::new().into(), Model::Storage => Storage::new().into(), @@ -700,7 +671,7 @@ pub(crate) fn get_connected_device() -> Option { get_connected_model().map(create_device_wrapper) } -fn connect_enum(model: Model) -> bool { +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, -- cgit v1.2.1 From 379bc798477a1de7ffda923c5d10ca63aebae25f Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 18:21:08 +0000 Subject: Move {Pro, Storage}::connect into Manager As part of the connection refactoring, this patch moves the connect methods of the Pro and Storage structs into the Manager struct. To maintain compatibility with nitrokey-test, the old methods are not removed but marked as deprecated. --- src/device.rs | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index f28558d..758691d 100644 --- a/src/device.rs +++ b/src/device.rs @@ -9,7 +9,7 @@ use nitrokey_sys; use crate::auth::Authenticate; use crate::config::{Config, RawConfig}; -use crate::error::{CommunicationError, Error}; +use crate::error::Error; use crate::otp::GenerateOtp; use crate::pws::GetPasswordSafe; use crate::util::{ @@ -755,16 +755,12 @@ impl Pro { /// ``` /// /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected + #[deprecated(since = "0.4.0", note = "use `nitrokey::Manager::connect_pro` instead")] pub fn connect() -> Result { - // TODO: maybe Option instead of Result? - if connect_enum(Model::Pro) { - Ok(Pro::new()) - } else { - Err(CommunicationError::NotConnected.into()) - } + crate::take()?.connect_pro().map_err(Into::into) } - fn new() -> Pro { + pub(crate) fn new() -> Pro { Pro { marker: marker::PhantomData, } @@ -808,16 +804,15 @@ impl Storage { /// ``` /// /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected + #[deprecated( + since = "0.4.0", + note = "use `nitrokey::Manager::connect_storage` instead" + )] pub fn connect() -> Result { - // TODO: maybe Option instead of Result? - if connect_enum(Model::Storage) { - Ok(Storage::new()) - } else { - Err(CommunicationError::NotConnected.into()) - } + crate::take()?.connect_storage().map_err(Into::into) } - fn new() -> Storage { + pub(crate) fn new() -> Storage { Storage { marker: marker::PhantomData, } -- cgit v1.2.1 From fe2f39826ade5a156945dabb8c8ab725378a15c1 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 18:42:14 +0000 Subject: Store mutable reference to Manager in Device In the last patches, we ensured that devices can only be obtained using the Manager struct. But we did not ensure that there is only one device at a time. This patch adds a mutable reference to the Manager instance to the Device implementations. The borrow checker makes sure that there is only one mutable reference at a time. In this patch, we have to remove the old connect, Pro::connect and Storage::connect functions as they do no longer compile. (They discard the MutexGuard which invalidates the reference to the Manager.) Therefore the tests do no longer compile. --- src/device.rs | 160 +++++++++++++++------------------------------------------- 1 file changed, 40 insertions(+), 120 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 758691d..50ff071 100644 --- a/src/device.rs +++ b/src/device.rs @@ -2,14 +2,13 @@ // SPDX-License-Identifier: MIT use std::fmt; -use std::marker; use libc; use nitrokey_sys; use crate::auth::Authenticate; use crate::config::{Config, RawConfig}; -use crate::error::Error; +use crate::error::{CommunicationError, Error}; use crate::otp::GenerateOtp; use crate::pws::GetPasswordSafe; use crate::util::{ @@ -109,11 +108,11 @@ impl fmt::Display for VolumeMode { /// /// [`connect`]: fn.connect.html #[derive(Debug)] -pub enum DeviceWrapper { +pub enum DeviceWrapper<'a> { /// A Nitrokey Storage device. - Storage(Storage), + Storage(Storage<'a>), /// A Nitrokey Pro device. - Pro(Pro), + Pro(Pro<'a>), } /// A Nitrokey Pro device without user or admin authentication. @@ -156,10 +155,8 @@ pub enum DeviceWrapper { /// [`connect`]: fn.connect.html /// [`Pro::connect`]: #method.connect #[derive(Debug)] -pub struct Pro { - // make sure that users cannot directly instantiate this type - #[doc(hidden)] - marker: marker::PhantomData<()>, +pub struct Pro<'a> { + manager: &'a mut crate::Manager, } /// A Nitrokey Storage device without user or admin authentication. @@ -202,10 +199,8 @@ pub struct Pro { /// [`connect`]: fn.connect.html /// [`Storage::connect`]: #method.connect #[derive(Debug)] -pub struct Storage { - // make sure that users cannot directly instantiate this type - #[doc(hidden)] - marker: marker::PhantomData<()>, +pub struct Storage<'a> { + manager: &'a mut crate::Manager, } /// The status of a volume on a Nitrokey Storage device. @@ -626,32 +621,6 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { } } -/// Connects to a Nitrokey device. This method can be used to connect to any connected device, -/// both a Nitrokey Pro and a Nitrokey Storage. -/// -/// # Errors -/// -/// - [`NotConnected`][] if no Nitrokey device is connected -/// -/// # Example -/// -/// ``` -/// use nitrokey::DeviceWrapper; -/// -/// fn do_something(device: DeviceWrapper) {} -/// -/// match nitrokey::connect() { -/// Ok(device) => do_something(device), -/// Err(err) => eprintln!("Could not connect to a Nitrokey: {}", err), -/// } -/// ``` -/// -/// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected -#[deprecated(since = "0.4.0", note = "use `nitrokey::Manager::connect` instead")] -pub fn connect() -> Result { - crate::take()?.connect().map_err(Into::into) -} - fn get_connected_model() -> Option { match unsafe { nitrokey_sys::NK_get_device_model() } { nitrokey_sys::NK_device_model_NK_PRO => Some(Model::Pro), @@ -660,15 +629,23 @@ fn get_connected_model() -> Option { } } -pub(crate) fn create_device_wrapper(model: Model) -> DeviceWrapper { +pub(crate) fn create_device_wrapper( + manager: &mut crate::Manager, + model: Model, +) -> DeviceWrapper<'_> { match model { - Model::Pro => Pro::new().into(), - Model::Storage => Storage::new().into(), + Model::Pro => Pro::new(manager).into(), + Model::Storage => Storage::new(manager).into(), } } -pub(crate) fn get_connected_device() -> Option { - get_connected_model().map(create_device_wrapper) +pub(crate) fn get_connected_device( + manager: &mut crate::Manager, +) -> Result, 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 { @@ -679,7 +656,7 @@ pub(crate) fn connect_enum(model: Model) -> bool { unsafe { nitrokey_sys::NK_login_enum(model) == 1 } } -impl DeviceWrapper { +impl<'a> DeviceWrapper<'a> { fn device(&self) -> &dyn Device { match *self { DeviceWrapper::Storage(ref storage) => storage, @@ -695,19 +672,19 @@ impl DeviceWrapper { } } -impl From for DeviceWrapper { - fn from(device: Pro) -> Self { +impl<'a> From> for DeviceWrapper<'a> { + fn from(device: Pro<'a>) -> Self { DeviceWrapper::Pro(device) } } -impl From for DeviceWrapper { - fn from(device: Storage) -> Self { +impl<'a> From> for DeviceWrapper<'a> { + fn from(device: Storage<'a>) -> Self { DeviceWrapper::Storage(device) } } -impl GenerateOtp for DeviceWrapper { +impl<'a> GenerateOtp for DeviceWrapper<'a> { fn get_hotp_slot_name(&self, slot: u8) -> Result { self.device().get_hotp_slot_name(slot) } @@ -725,7 +702,7 @@ impl GenerateOtp for DeviceWrapper { } } -impl Device for DeviceWrapper { +impl<'a> Device for DeviceWrapper<'a> { fn get_model(&self) -> Model { match *self { DeviceWrapper::Pro(_) => Model::Pro, @@ -734,40 +711,13 @@ impl Device for DeviceWrapper { } } -impl Pro { - /// Connects to a Nitrokey Pro. - /// - /// # Errors - /// - /// - [`NotConnected`][] if no Nitrokey device of the given model is connected - /// - /// # Example - /// - /// ``` - /// use nitrokey::Pro; - /// - /// fn use_pro(device: Pro) {} - /// - /// match nitrokey::Pro::connect() { - /// Ok(device) => use_pro(device), - /// Err(err) => eprintln!("Could not connect to the Nitrokey Pro: {}", err), - /// } - /// ``` - /// - /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected - #[deprecated(since = "0.4.0", note = "use `nitrokey::Manager::connect_pro` instead")] - pub fn connect() -> Result { - crate::take()?.connect_pro().map_err(Into::into) - } - - pub(crate) fn new() -> Pro { - Pro { - marker: marker::PhantomData, - } +impl<'a> Pro<'a> { + pub(crate) fn new(manager: &'a mut crate::Manager) -> Pro<'a> { + Pro { manager } } } -impl Drop for Pro { +impl<'a> Drop for Pro<'a> { fn drop(&mut self) { unsafe { nitrokey_sys::NK_logout(); @@ -775,47 +725,17 @@ impl Drop for Pro { } } -impl Device for Pro { +impl<'a> Device for Pro<'a> { fn get_model(&self) -> Model { Model::Pro } } -impl GenerateOtp for Pro {} +impl<'a> GenerateOtp for Pro<'a> {} -impl Storage { - /// Connects to a Nitrokey Storage. - /// - /// # Errors - /// - /// - [`NotConnected`][] if no Nitrokey device of the given model is connected - /// - /// # Example - /// - /// ``` - /// use nitrokey::Storage; - /// - /// fn use_storage(device: Storage) {} - /// - /// match nitrokey::Storage::connect() { - /// Ok(device) => use_storage(device), - /// Err(err) => eprintln!("Could not connect to the Nitrokey Storage: {}", err), - /// } - /// ``` - /// - /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected - #[deprecated( - since = "0.4.0", - note = "use `nitrokey::Manager::connect_storage` instead" - )] - pub fn connect() -> Result { - crate::take()?.connect_storage().map_err(Into::into) - } - - pub(crate) fn new() -> Storage { - Storage { - marker: marker::PhantomData, - } +impl<'a> Storage<'a> { + pub(crate) fn new(manager: &'a mut crate::Manager) -> Storage<'a> { + Storage { manager } } /// Changes the update PIN. @@ -1320,7 +1240,7 @@ impl Storage { } } -impl Drop for Storage { +impl<'a> Drop for Storage<'a> { fn drop(&mut self) { unsafe { nitrokey_sys::NK_logout(); @@ -1328,13 +1248,13 @@ impl Drop for Storage { } } -impl Device for Storage { +impl<'a> Device for Storage<'a> { fn get_model(&self) -> Model { Model::Storage } } -impl GenerateOtp for Storage {} +impl<'a> GenerateOtp for Storage<'a> {} impl From for StorageProductionInfo { fn from(data: nitrokey_sys::NK_storage_ProductionTest) -> Self { -- cgit v1.2.1 From 12fa62483cf45d868099d5d4020333af492eebde Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 9 Jul 2019 08:09:02 +0000 Subject: Introduce into_manager for Device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To enable applications like nitrokey-test to go back to a manager instance from a Device instance, we add the into_manager function to the Device trait. To do that, we have to keep track of the Manager’s lifetime by adding a lifetime to Device (and then to some other traits that use Device). --- src/device.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 10 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index 50ff071..e1a71fa 100644 --- a/src/device.rs +++ b/src/device.rs @@ -156,7 +156,7 @@ pub enum DeviceWrapper<'a> { /// [`Pro::connect`]: #method.connect #[derive(Debug)] pub struct Pro<'a> { - manager: &'a mut crate::Manager, + manager: Option<&'a mut crate::Manager>, } /// A Nitrokey Storage device without user or admin authentication. @@ -200,7 +200,7 @@ pub struct Pro<'a> { /// [`Storage::connect`]: #method.connect #[derive(Debug)] pub struct Storage<'a> { - manager: &'a mut crate::Manager, + manager: Option<&'a mut crate::Manager>, } /// The status of a volume on a Nitrokey Storage device. @@ -291,7 +291,32 @@ pub struct StorageStatus { /// /// This trait provides the commands that can be executed without authentication and that are /// present on all supported Nitrokey devices. -pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug { +pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt::Debug { + /// Returns the [`Manager`][] instance that has been used to connect to this device. + /// + /// # Example + /// + /// ``` + /// use nitrokey::{Device, DeviceWrapper}; + /// + /// fn do_something(device: DeviceWrapper) { + /// // reconnect to any device + /// let manager = device.into_manager(); + /// let device = manager.connect(); + /// // do something with the device + /// // ... + /// } + /// + /// # fn main() -> Result<(), nitrokey::Error> { + /// match nitrokey::take()?.connect() { + /// Ok(device) => do_something(device), + /// Err(err) => println!("Could not connect to a Nitrokey: {}", err), + /// } + /// # Ok(()) + /// # } + /// ``` + fn into_manager(self) -> &'a mut crate::Manager; + /// Returns the model of the connected Nitrokey device. /// /// # Example @@ -657,14 +682,14 @@ pub(crate) fn connect_enum(model: Model) -> bool { } impl<'a> DeviceWrapper<'a> { - fn device(&self) -> &dyn Device { + fn device(&self) -> &dyn Device<'a> { match *self { DeviceWrapper::Storage(ref storage) => storage, DeviceWrapper::Pro(ref pro) => pro, } } - fn device_mut(&mut self) -> &mut dyn Device { + fn device_mut(&mut self) -> &mut dyn Device<'a> { match *self { DeviceWrapper::Storage(ref mut storage) => storage, DeviceWrapper::Pro(ref mut pro) => pro, @@ -702,7 +727,14 @@ impl<'a> GenerateOtp for DeviceWrapper<'a> { } } -impl<'a> Device for DeviceWrapper<'a> { +impl<'a> Device<'a> for DeviceWrapper<'a> { + fn into_manager(self) -> &'a mut crate::Manager { + match self { + DeviceWrapper::Pro(dev) => dev.into_manager(), + DeviceWrapper::Storage(dev) => dev.into_manager(), + } + } + fn get_model(&self) -> Model { match *self { DeviceWrapper::Pro(_) => Model::Pro, @@ -713,7 +745,9 @@ impl<'a> Device for DeviceWrapper<'a> { impl<'a> Pro<'a> { pub(crate) fn new(manager: &'a mut crate::Manager) -> Pro<'a> { - Pro { manager } + Pro { + manager: Some(manager), + } } } @@ -725,7 +759,11 @@ impl<'a> Drop for Pro<'a> { } } -impl<'a> Device for Pro<'a> { +impl<'a> Device<'a> for Pro<'a> { + fn into_manager(mut self) -> &'a mut crate::Manager { + self.manager.take().unwrap() + } + fn get_model(&self) -> Model { Model::Pro } @@ -735,7 +773,9 @@ impl<'a> GenerateOtp for Pro<'a> {} impl<'a> Storage<'a> { pub(crate) fn new(manager: &'a mut crate::Manager) -> Storage<'a> { - Storage { manager } + Storage { + manager: Some(manager), + } } /// Changes the update PIN. @@ -1248,7 +1288,11 @@ impl<'a> Drop for Storage<'a> { } } -impl<'a> Device for Storage<'a> { +impl<'a> Device<'a> for Storage<'a> { + fn into_manager(mut self) -> &'a mut crate::Manager { + self.manager.take().unwrap() + } + fn get_model(&self) -> Model { Model::Storage } -- cgit v1.2.1 From 62e8ee8f5d02511d6eb5dc179b087b04e88c1b94 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 27 Jan 2019 19:52:53 +0000 Subject: Update documentation for Manager refactoring This patch updates the documentation to reflect the latest changes to connection handling. It also updates the doc tests to prefer the new methods over the old ones. --- src/device.rs | 117 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 45 deletions(-) (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs index e1a71fa..758d4c1 100644 --- a/src/device.rs +++ b/src/device.rs @@ -53,7 +53,7 @@ impl fmt::Display for VolumeMode { /// A wrapper for a Nitrokey device of unknown type. /// -/// Use the function [`connect`][] to obtain a wrapped instance. The wrapper implements all traits +/// 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. @@ -66,11 +66,12 @@ impl fmt::Display for VolumeMode { /// use nitrokey::{Authenticate, DeviceWrapper, User}; /// # use nitrokey::Error; /// -/// fn perform_user_task(device: &User) {} +/// fn perform_user_task<'a>(device: &User<'a, DeviceWrapper<'a>>) {} /// fn perform_other_task(device: &DeviceWrapper) {} /// /// # fn try_main() -> Result<(), Error> { -/// let device = nitrokey::connect()?; +/// let mut manager = nitrokey::take()?; +/// let device = manager.connect()?; /// let device = match device.authenticate_user("123456") { /// Ok(user) => { /// perform_user_task(&user); @@ -96,7 +97,8 @@ impl fmt::Display for VolumeMode { /// fn perform_storage_task(device: &Storage) {} /// /// # fn try_main() -> Result<(), Error> { -/// let device = nitrokey::connect()?; +/// let mut manager = nitrokey::take()?; +/// let device = manager.connect()?; /// perform_common_task(&device); /// match device { /// DeviceWrapper::Storage(storage) => perform_storage_task(&storage), @@ -106,7 +108,7 @@ impl fmt::Display for VolumeMode { /// # } /// ``` /// -/// [`connect`]: fn.connect.html +/// [`connect`]: struct.Manager.html#method.connect #[derive(Debug)] pub enum DeviceWrapper<'a> { /// A Nitrokey Storage device. @@ -117,10 +119,9 @@ pub enum DeviceWrapper<'a> { /// A Nitrokey Pro device without user or admin authentication. /// -/// Use the global function [`connect`][] to obtain an instance wrapper or the method -/// [`connect`][`Pro::connect`] to directly obtain an instance. If you want to execute a command -/// that requires user or admin authentication, use [`authenticate_admin`][] or -/// [`authenticate_user`][]. +/// 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 /// @@ -130,11 +131,12 @@ pub enum DeviceWrapper<'a> { /// use nitrokey::{Authenticate, User, Pro}; /// # use nitrokey::Error; /// -/// fn perform_user_task(device: &User) {} +/// fn perform_user_task<'a>(device: &User<'a, Pro<'a>>) {} /// fn perform_other_task(device: &Pro) {} /// /// # fn try_main() -> Result<(), Error> { -/// let device = nitrokey::Pro::connect()?; +/// let mut manager = nitrokey::take()?; +/// let device = manager.connect_pro()?; /// let device = match device.authenticate_user("123456") { /// Ok(user) => { /// perform_user_task(&user); @@ -152,8 +154,8 @@ pub enum DeviceWrapper<'a> { /// /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin /// [`authenticate_user`]: trait.Authenticate.html#method.authenticate_user -/// [`connect`]: fn.connect.html -/// [`Pro::connect`]: #method.connect +/// [`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>, @@ -161,10 +163,9 @@ pub struct Pro<'a> { /// A Nitrokey Storage device without user or admin authentication. /// -/// Use the global function [`connect`][] to obtain an instance wrapper or the method -/// [`connect`][`Storage::connect`] to directly obtain an instance. If you want to execute a -/// command that requires user or admin authentication, use [`authenticate_admin`][] or -/// [`authenticate_user`][]. +/// Use the [`connect`][] method to obtain an instance wrapper or the [`connect_storage`] 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 /// @@ -174,11 +175,12 @@ pub struct Pro<'a> { /// use nitrokey::{Authenticate, User, Storage}; /// # use nitrokey::Error; /// -/// fn perform_user_task(device: &User) {} +/// fn perform_user_task<'a>(device: &User<'a, Storage<'a>>) {} /// fn perform_other_task(device: &Storage) {} /// /// # fn try_main() -> Result<(), Error> { -/// let device = nitrokey::Storage::connect()?; +/// let mut manager = nitrokey::take()?; +/// let device = manager.connect_storage()?; /// let device = match device.authenticate_user("123456") { /// Ok(user) => { /// perform_user_task(&user); @@ -196,8 +198,8 @@ pub struct Pro<'a> { /// /// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin /// [`authenticate_user`]: trait.Authenticate.html#method.authenticate_user -/// [`connect`]: fn.connect.html -/// [`Storage::connect`]: #method.connect +/// [`connect`]: struct.Manager.html#method.connect +/// [`connect_storage`]: struct.Manager.html#method.connect_storage #[derive(Debug)] pub struct Storage<'a> { manager: Option<&'a mut crate::Manager>, @@ -326,7 +328,8 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; /// println!("Connected to a Nitrokey {}", device.get_model()); /// # Ok(()) /// # } @@ -342,7 +345,8 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// 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), @@ -364,7 +368,9 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// 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), @@ -386,7 +392,8 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// 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), @@ -408,7 +415,8 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// 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), @@ -431,7 +439,8 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; /// let config = device.get_config()?; /// println!("numlock binding: {:?}", config.numlock); /// println!("capslock binding: {:?}", config.capslock); @@ -465,7 +474,8 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// 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), @@ -498,7 +508,8 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// 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), @@ -531,7 +542,8 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// 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), @@ -565,7 +577,8 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// 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), @@ -596,7 +609,8 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// 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), @@ -630,7 +644,8 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::connect()?; + /// 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), @@ -795,7 +810,8 @@ impl<'a> Storage<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// match device.change_update_pin("12345678", "87654321") { /// Ok(()) => println!("Updated update PIN."), /// Err(err) => eprintln!("Failed to update update PIN: {}", err), @@ -832,7 +848,8 @@ impl<'a> Storage<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// match device.enable_firmware_update("12345678") { /// Ok(()) => println!("Nitrokey entered update mode."), /// Err(err) => eprintln!("Could not enter update mode: {}", err), @@ -866,7 +883,8 @@ impl<'a> Storage<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// match device.enable_encrypted_volume("123456") { /// Ok(()) => println!("Enabled the encrypted volume."), /// Err(err) => eprintln!("Could not enable the encrypted volume: {}", err), @@ -895,7 +913,8 @@ impl<'a> Storage<'a> { /// fn use_volume() {} /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// match device.enable_encrypted_volume("123456") { /// Ok(()) => { /// println!("Enabled the encrypted volume."); @@ -941,7 +960,8 @@ impl<'a> Storage<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// device.enable_encrypted_volume("123445")?; /// match device.enable_hidden_volume("hidden-pw") { /// Ok(()) => println!("Enabled a hidden volume."), @@ -974,7 +994,8 @@ impl<'a> Storage<'a> { /// fn use_volume() {} /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// device.enable_encrypted_volume("123445")?; /// match device.enable_hidden_volume("hidden-pw") { /// Ok(()) => { @@ -1021,7 +1042,8 @@ impl<'a> Storage<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// device.enable_encrypted_volume("123445")?; /// device.create_hidden_volume(0, 0, 100, "hidden-pw")?; /// # Ok(()) @@ -1061,7 +1083,8 @@ impl<'a> Storage<'a> { /// use nitrokey::VolumeMode; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// match device.set_unencrypted_volume_mode("12345678", VolumeMode::ReadWrite) { /// Ok(()) => println!("Set the unencrypted volume to read-write mode."), /// Err(err) => eprintln!("Could not set the unencrypted volume to read-write mode: {}", err), @@ -1106,7 +1129,8 @@ impl<'a> Storage<'a> { /// use nitrokey::VolumeMode; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// match device.set_encrypted_volume_mode("12345678", VolumeMode::ReadWrite) { /// Ok(()) => println!("Set the encrypted volume to read-write mode."), /// Err(err) => eprintln!("Could not set the encrypted volume to read-write mode: {}", err), @@ -1144,7 +1168,8 @@ impl<'a> Storage<'a> { /// fn use_volume() {} /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect_storage()?; /// match device.get_status() { /// Ok(status) => { /// println!("SD card ID: {:#x}", status.serial_number_sd_card); @@ -1187,7 +1212,8 @@ impl<'a> Storage<'a> { /// fn use_volume() {} /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect_storage()?; /// match device.get_production_info() { /// Ok(data) => { /// println!("SD card ID: {:#x}", data.sd_card.serial_number); @@ -1235,7 +1261,8 @@ impl<'a> Storage<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let mut device = nitrokey::Storage::connect()?; + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect_storage()?; /// match device.clear_new_sd_card_warning("12345678") { /// Ok(()) => println!("Cleared the new SD card warning."), /// Err(err) => eprintln!("Could not set the clear the new SD card warning: {}", err), -- cgit v1.2.1 From 678f0b700666a4ba86db2180078d79a730dc82e0 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 16 Jul 2019 09:14:36 +0000 Subject: Refactor the device module into submodules 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. --- src/device.rs | 1380 --------------------------------------------------------- 1 file changed, 1380 deletions(-) delete mode 100644 src/device.rs (limited to 'src/device.rs') diff --git a/src/device.rs b/src/device.rs deleted file mode 100644 index 758d4c1..0000000 --- a/src/device.rs +++ /dev/null @@ -1,1380 +0,0 @@ -// Copyright (C) 2018-2019 Robin Krahl -// SPDX-License-Identifier: MIT - -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, -}; - -/// 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>, -} - -/// A Nitrokey Storage device without user or admin authentication. -/// -/// Use the [`connect`][] method to obtain an instance wrapper or the [`connect_storage`] 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, Storage}; -/// # use nitrokey::Error; -/// -/// fn perform_user_task<'a>(device: &User<'a, Storage<'a>>) {} -/// fn perform_other_task(device: &Storage) {} -/// -/// # fn try_main() -> Result<(), Error> { -/// let mut manager = nitrokey::take()?; -/// let device = manager.connect_storage()?; -/// 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_storage`]: struct.Manager.html#method.connect_storage -#[derive(Debug)] -pub struct Storage<'a> { - manager: Option<&'a mut crate::Manager>, -} - -/// The status of a volume on a Nitrokey Storage device. -#[derive(Debug)] -pub struct VolumeStatus { - /// Indicates whether the volume is read-only. - pub read_only: bool, - /// Indicates whether the volume is active. - pub active: bool, -} - -/// Information about the SD card in a Storage device. -#[derive(Debug)] -pub struct SdCardData { - /// The serial number of the SD card. - pub serial_number: u32, - /// The size of the SD card in GB. - pub size: u8, - /// The year the card was manufactured, e. g. 17 for 2017. - pub manufacturing_year: u8, - /// The month the card was manufactured. - pub manufacturing_month: u8, - /// The OEM ID. - pub oem: u16, - /// The manufacturer ID. - 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 { - /// The firmware version. - pub firmware_version: FirmwareVersion, - /// The internal firmware version. - pub firmware_version_internal: u8, - /// The serial number of the CPU. - pub serial_number_cpu: u32, - /// Information about the SD card. - pub sd_card: SdCardData, -} - -/// The status of a Nitrokey Storage device. -#[derive(Debug)] -pub struct StorageStatus { - /// The status of the unencrypted volume. - pub unencrypted_volume: VolumeStatus, - /// The status of the encrypted volume. - pub encrypted_volume: VolumeStatus, - /// The status of the hidden volume. - pub hidden_volume: VolumeStatus, - /// The firmware version. - pub firmware_version: FirmwareVersion, - /// Indicates whether the firmware is locked. - pub firmware_locked: bool, - /// The serial number of the SD card in the Storage stick. - pub serial_number_sd_card: u32, - /// The serial number of the smart card in the Storage stick. - pub serial_number_smart_card: u32, - /// The number of remaining login attempts for the user PIN. - pub user_retry_count: u8, - /// The number of remaining login attempts for the admin PIN. - pub admin_retry_count: u8, - /// Indicates whether a new SD card was found. - pub new_sd_card_found: bool, - /// Indicates whether the SD card is filled with random characters. - pub filled_with_random: bool, - /// Indicates whether the stick has been initialized by generating - /// the AES keys. - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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, 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> for DeviceWrapper<'a> { - fn from(device: Pro<'a>) -> Self { - DeviceWrapper::Pro(device) - } -} - -impl<'a> From> 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 { - self.device().get_hotp_slot_name(slot) - } - - fn get_totp_slot_name(&self, slot: u8) -> Result { - self.device().get_totp_slot_name(slot) - } - - fn get_hotp_code(&mut self, slot: u8) -> Result { - self.device_mut().get_hotp_code(slot) - } - - fn get_totp_code(&self, slot: u8) -> Result { - 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 { - manager: Some(manager), - } - } - - /// Changes the update PIN. - /// - /// The update PIN is used to enable firmware updates. Unlike the user and the admin PIN, the - /// update PIN is not managed by the OpenPGP smart card but by the Nitrokey firmware. There is - /// no retry counter as with the other PIN types. - /// - /// # Errors - /// - /// - [`InvalidString`][] if one of the provided passwords contains a null byte - /// - [`WrongPassword`][] if the current update password is wrong - /// - /// # Example - /// - /// ```no_run - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let mut device = manager.connect_storage()?; - /// match device.change_update_pin("12345678", "87654321") { - /// Ok(()) => println!("Updated update PIN."), - /// Err(err) => eprintln!("Failed to update update PIN: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - /// - /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString - /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn change_update_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_update_password(current_string.as_ptr(), new_string.as_ptr()) - }) - } - - /// Enables the firmware update mode. - /// - /// During firmware update mode, the Nitrokey can no longer be accessed using HID commands. - /// To resume normal operation, run `dfu-programmer at32uc3a3256s launch`. In order to enter - /// the firmware update mode, you need the update password that can be changed using the - /// [`change_update_pin`][] method. - /// - /// # Errors - /// - /// - [`InvalidString`][] if one of the provided passwords contains a null byte - /// - [`WrongPassword`][] if the current update password is wrong - /// - /// # Example - /// - /// ```no_run - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let mut device = manager.connect_storage()?; - /// match device.enable_firmware_update("12345678") { - /// Ok(()) => println!("Nitrokey entered update mode."), - /// Err(err) => eprintln!("Could not enter update mode: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - /// - /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString - /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn enable_firmware_update(&mut self, update_pin: &str) -> Result<(), Error> { - let update_pin_string = get_cstring(update_pin)?; - get_command_result(unsafe { - nitrokey_sys::NK_enable_firmware_update(update_pin_string.as_ptr()) - }) - } - - /// Enables the encrypted storage volume. - /// - /// Once the encrypted volume is enabled, it is presented to the operating system as a block - /// device. The API does not provide any information on the name or path of this block device. - /// - /// # Errors - /// - /// - [`InvalidString`][] if the provided password contains a null byte - /// - [`WrongPassword`][] if the provided user password is wrong - /// - /// # Example - /// - /// ```no_run - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let mut device = manager.connect_storage()?; - /// match device.enable_encrypted_volume("123456") { - /// Ok(()) => println!("Enabled the encrypted volume."), - /// Err(err) => eprintln!("Could not enable the encrypted volume: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - /// - /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString - /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn enable_encrypted_volume(&mut self, user_pin: &str) -> Result<(), Error> { - let user_pin = get_cstring(user_pin)?; - get_command_result(unsafe { nitrokey_sys::NK_unlock_encrypted_volume(user_pin.as_ptr()) }) - } - - /// Disables the encrypted storage volume. - /// - /// Once the volume is disabled, it can be no longer accessed as a block device. If the - /// encrypted volume has not been enabled, this method still returns a success. - /// - /// # Example - /// - /// ```no_run - /// # use nitrokey::Error; - /// - /// fn use_volume() {} - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let mut device = manager.connect_storage()?; - /// match device.enable_encrypted_volume("123456") { - /// Ok(()) => { - /// println!("Enabled the encrypted volume."); - /// use_volume(); - /// match device.disable_encrypted_volume() { - /// Ok(()) => println!("Disabled the encrypted volume."), - /// Err(err) => { - /// eprintln!("Could not disable the encrypted volume: {}", err); - /// }, - /// }; - /// }, - /// Err(err) => eprintln!("Could not enable the encrypted volume: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - pub fn disable_encrypted_volume(&mut self) -> Result<(), Error> { - get_command_result(unsafe { nitrokey_sys::NK_lock_encrypted_volume() }) - } - - /// Enables a hidden storage volume. - /// - /// This function will only succeed if the encrypted storage ([`enable_encrypted_volume`][]) or - /// another hidden volume has been enabled previously. Once the hidden volume is enabled, it - /// is presented to the operating system as a block device and any previously opened encrypted - /// or hidden volumes are closed. The API does not provide any information on the name or path - /// of this block device. - /// - /// Note that the encrypted and the hidden volumes operate on the same storage area, so using - /// both at the same time might lead to data loss. - /// - /// The hidden volume to unlock is selected based on the provided password. - /// - /// # Errors - /// - /// - [`AesDecryptionFailed`][] if the encrypted storage has not been opened before calling - /// this method or the AES key has not been built - /// - [`InvalidString`][] if the provided password contains a null byte - /// - /// # Example - /// - /// ```no_run - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let mut device = manager.connect_storage()?; - /// device.enable_encrypted_volume("123445")?; - /// match device.enable_hidden_volume("hidden-pw") { - /// Ok(()) => println!("Enabled a hidden volume."), - /// Err(err) => eprintln!("Could not enable the hidden volume: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - /// - /// [`enable_encrypted_volume`]: #method.enable_encrypted_volume - /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed - /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString - pub fn enable_hidden_volume(&mut self, volume_password: &str) -> Result<(), Error> { - let volume_password = get_cstring(volume_password)?; - get_command_result(unsafe { - nitrokey_sys::NK_unlock_hidden_volume(volume_password.as_ptr()) - }) - } - - /// Disables a hidden storage volume. - /// - /// Once the volume is disabled, it can be no longer accessed as a block device. If no hidden - /// volume has been enabled, this method still returns a success. - /// - /// # Example - /// - /// ```no_run - /// # use nitrokey::Error; - /// - /// fn use_volume() {} - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let mut device = manager.connect_storage()?; - /// device.enable_encrypted_volume("123445")?; - /// match device.enable_hidden_volume("hidden-pw") { - /// Ok(()) => { - /// println!("Enabled the hidden volume."); - /// use_volume(); - /// match device.disable_hidden_volume() { - /// Ok(()) => println!("Disabled the hidden volume."), - /// Err(err) => { - /// eprintln!("Could not disable the hidden volume: {}", err); - /// }, - /// }; - /// }, - /// Err(err) => eprintln!("Could not enable the hidden volume: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - pub fn disable_hidden_volume(&mut self) -> Result<(), Error> { - get_command_result(unsafe { nitrokey_sys::NK_lock_hidden_volume() }) - } - - /// Creates a hidden volume. - /// - /// The volume is crated in the given slot and in the given range of the available memory, - /// where `start` is the start position as a percentage of the available memory, and `end` is - /// the end position as a percentage of the available memory. The volume will be protected by - /// the given password. - /// - /// Note that the encrypted and the hidden volumes operate on the same storage area, so using - /// both at the same time might lead to data loss. - /// - /// According to the libnitrokey documentation, this function only works if the encrypted - /// storage has been opened. - /// - /// # Errors - /// - /// - [`AesDecryptionFailed`][] if the encrypted storage has not been opened before calling - /// this method or the AES key has not been built - /// - [`InvalidString`][] if the provided password contains a null byte - /// - /// # Example - /// - /// ```no_run - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let mut device = manager.connect_storage()?; - /// device.enable_encrypted_volume("123445")?; - /// device.create_hidden_volume(0, 0, 100, "hidden-pw")?; - /// # Ok(()) - /// # } - /// ``` - /// - /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed - /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString - pub fn create_hidden_volume( - &mut self, - slot: u8, - start: u8, - end: u8, - password: &str, - ) -> Result<(), Error> { - let password = get_cstring(password)?; - get_command_result(unsafe { - nitrokey_sys::NK_create_hidden_volume(slot, start, end, password.as_ptr()) - }) - } - - /// Sets the access mode of the unencrypted volume. - /// - /// This command will reconnect the unencrypted volume so buffers should be flushed before - /// calling it. Since firmware version v0.51, this command requires the admin PIN. Older - /// firmware versions are not supported. - /// - /// # Errors - /// - /// - [`InvalidString`][] if the provided password contains a null byte - /// - [`WrongPassword`][] if the provided admin password is wrong - /// - /// # Example - /// - /// ```no_run - /// # use nitrokey::Error; - /// use nitrokey::VolumeMode; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let mut device = manager.connect_storage()?; - /// match device.set_unencrypted_volume_mode("12345678", VolumeMode::ReadWrite) { - /// Ok(()) => println!("Set the unencrypted volume to read-write mode."), - /// Err(err) => eprintln!("Could not set the unencrypted volume to read-write mode: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - /// - /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString - /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn set_unencrypted_volume_mode( - &mut self, - admin_pin: &str, - mode: VolumeMode, - ) -> Result<(), Error> { - let admin_pin = get_cstring(admin_pin)?; - let result = match mode { - VolumeMode::ReadOnly => unsafe { - nitrokey_sys::NK_set_unencrypted_read_only_admin(admin_pin.as_ptr()) - }, - VolumeMode::ReadWrite => unsafe { - nitrokey_sys::NK_set_unencrypted_read_write_admin(admin_pin.as_ptr()) - }, - }; - get_command_result(result) - } - - /// Sets the access mode of the encrypted volume. - /// - /// This command will reconnect the encrypted volume so buffers should be flushed before - /// calling it. It is only available in firmware version 0.49. - /// - /// # Errors - /// - /// - [`InvalidString`][] if the provided password contains a null byte - /// - [`WrongPassword`][] if the provided admin password is wrong - /// - /// # Example - /// - /// ```no_run - /// # use nitrokey::Error; - /// use nitrokey::VolumeMode; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let mut device = manager.connect_storage()?; - /// match device.set_encrypted_volume_mode("12345678", VolumeMode::ReadWrite) { - /// Ok(()) => println!("Set the encrypted volume to read-write mode."), - /// Err(err) => eprintln!("Could not set the encrypted volume to read-write mode: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - /// - /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString - /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn set_encrypted_volume_mode( - &mut self, - admin_pin: &str, - mode: VolumeMode, - ) -> Result<(), Error> { - let admin_pin = get_cstring(admin_pin)?; - let result = match mode { - VolumeMode::ReadOnly => unsafe { - nitrokey_sys::NK_set_encrypted_read_only(admin_pin.as_ptr()) - }, - VolumeMode::ReadWrite => unsafe { - nitrokey_sys::NK_set_encrypted_read_write(admin_pin.as_ptr()) - }, - }; - get_command_result(result) - } - - /// Returns the status of the connected storage device. - /// - /// # Example - /// - /// ```no_run - /// # use nitrokey::Error; - /// - /// fn use_volume() {} - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let device = manager.connect_storage()?; - /// match device.get_status() { - /// Ok(status) => { - /// println!("SD card ID: {:#x}", status.serial_number_sd_card); - /// }, - /// Err(err) => eprintln!("Could not get Storage status: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - pub fn get_status(&self) -> Result { - let mut raw_status = nitrokey_sys::NK_storage_status { - unencrypted_volume_read_only: false, - unencrypted_volume_active: false, - encrypted_volume_read_only: false, - encrypted_volume_active: false, - hidden_volume_read_only: false, - hidden_volume_active: false, - firmware_version_major: 0, - firmware_version_minor: 0, - firmware_locked: false, - serial_number_sd_card: 0, - serial_number_smart_card: 0, - user_retry_count: 0, - admin_retry_count: 0, - new_sd_card_found: false, - filled_with_random: false, - stick_initialized: false, - }; - let raw_result = unsafe { nitrokey_sys::NK_get_status_storage(&mut raw_status) }; - get_command_result(raw_result).map(|_| StorageStatus::from(raw_status)) - } - - /// Returns the production information for the connected storage device. - /// - /// # Example - /// - /// ```no_run - /// # use nitrokey::Error; - /// - /// fn use_volume() {} - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let device = manager.connect_storage()?; - /// match device.get_production_info() { - /// Ok(data) => { - /// println!("SD card ID: {:#x}", data.sd_card.serial_number); - /// println!("SD card size: {} GB", data.sd_card.size); - /// }, - /// Err(err) => eprintln!("Could not get Storage production info: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - pub fn get_production_info(&self) -> Result { - let mut raw_data = nitrokey_sys::NK_storage_ProductionTest { - FirmwareVersion_au8: [0, 2], - FirmwareVersionInternal_u8: 0, - SD_Card_Size_u8: 0, - CPU_CardID_u32: 0, - SmartCardID_u32: 0, - SD_CardID_u32: 0, - SC_UserPwRetryCount: 0, - SC_AdminPwRetryCount: 0, - SD_Card_ManufacturingYear_u8: 0, - SD_Card_ManufacturingMonth_u8: 0, - SD_Card_OEM_u16: 0, - SD_WriteSpeed_u16: 0, - SD_Card_Manufacturer_u8: 0, - }; - let raw_result = unsafe { nitrokey_sys::NK_get_storage_production_info(&mut raw_data) }; - get_command_result(raw_result).map(|_| StorageProductionInfo::from(raw_data)) - } - - /// Clears the warning for a new SD card. - /// - /// The Storage status contains a field for a new SD card warning. After a factory reset, the - /// field is set to true. After filling the SD card with random data, it is set to false. - /// This method can be used to set it to false without filling the SD card with random data. - /// - /// # Errors - /// - /// - [`InvalidString`][] if the provided password contains a null byte - /// - [`WrongPassword`][] if the provided admin password is wrong - /// - /// # Example - /// - /// ```no_run - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let mut device = manager.connect_storage()?; - /// match device.clear_new_sd_card_warning("12345678") { - /// Ok(()) => println!("Cleared the new SD card warning."), - /// Err(err) => eprintln!("Could not set the clear the new SD card warning: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - /// - /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString - /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn clear_new_sd_card_warning(&mut self, admin_pin: &str) -> Result<(), Error> { - let admin_pin = get_cstring(admin_pin)?; - get_command_result(unsafe { - nitrokey_sys::NK_clear_new_sd_card_warning(admin_pin.as_ptr()) - }) - } - - /// Blinks the red and green LED alternatively and infinitely until the device is reconnected. - pub fn wink(&mut self) -> Result<(), Error> { - get_command_result(unsafe { nitrokey_sys::NK_wink() }) - } - - /// Exports the firmware to the unencrypted volume. - /// - /// This command requires the admin PIN. The unencrypted volume must be in read-write mode - /// when this command is executed. Otherwise, it will still return `Ok` but not write the - /// firmware. - /// - /// This command unmounts the unencrypted volume if it has been mounted, so all buffers should - /// be flushed. The firmware is written to the `firmware.bin` file on the unencrypted volume. - /// - /// # Errors - /// - /// - [`InvalidString`][] if one of the provided passwords contains a null byte - /// - [`WrongPassword`][] if the admin password is wrong - /// - /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString - /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - pub fn export_firmware(&mut self, admin_pin: &str) -> Result<(), Error> { - let admin_pin_string = get_cstring(admin_pin)?; - get_command_result(unsafe { nitrokey_sys::NK_export_firmware(admin_pin_string.as_ptr()) }) - } -} - -impl<'a> Drop for Storage<'a> { - fn drop(&mut self) { - unsafe { - nitrokey_sys::NK_logout(); - } - } -} - -impl<'a> Device<'a> for Storage<'a> { - fn into_manager(mut self) -> &'a mut crate::Manager { - self.manager.take().unwrap() - } - - fn get_model(&self) -> Model { - Model::Storage - } -} - -impl<'a> GenerateOtp for Storage<'a> {} - -impl From for StorageProductionInfo { - fn from(data: nitrokey_sys::NK_storage_ProductionTest) -> Self { - Self { - firmware_version: FirmwareVersion { - major: data.FirmwareVersion_au8[0], - minor: data.FirmwareVersion_au8[1], - }, - firmware_version_internal: data.FirmwareVersionInternal_u8, - serial_number_cpu: data.CPU_CardID_u32, - sd_card: SdCardData { - serial_number: data.SD_CardID_u32, - size: data.SD_Card_Size_u8, - manufacturing_year: data.SD_Card_ManufacturingYear_u8, - manufacturing_month: data.SD_Card_ManufacturingMonth_u8, - oem: data.SD_Card_OEM_u16, - manufacturer: data.SD_Card_Manufacturer_u8, - }, - } - } -} - -impl From for StorageStatus { - fn from(status: nitrokey_sys::NK_storage_status) -> Self { - StorageStatus { - unencrypted_volume: VolumeStatus { - read_only: status.unencrypted_volume_read_only, - active: status.unencrypted_volume_active, - }, - encrypted_volume: VolumeStatus { - read_only: status.encrypted_volume_read_only, - active: status.encrypted_volume_active, - }, - hidden_volume: VolumeStatus { - read_only: status.hidden_volume_read_only, - active: status.hidden_volume_active, - }, - firmware_version: FirmwareVersion { - major: status.firmware_version_major, - minor: status.firmware_version_minor, - }, - firmware_locked: status.firmware_locked, - serial_number_sd_card: status.serial_number_sd_card, - serial_number_smart_card: status.serial_number_smart_card, - user_retry_count: status.user_retry_count, - admin_retry_count: status.admin_retry_count, - new_sd_card_found: status.new_sd_card_found, - filled_with_random: status.filled_with_random, - stick_initialized: status.stick_initialized, - } - } -} -- cgit v1.2.1