From 6b439950c3c856f1b0eb6df2c9e89387b1697608 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sat, 28 Dec 2019 23:34:58 +0100 Subject: Implement conversion traits for Model and NK_device_model A nitrokey_sys::NK_device_model (= u32) value may correspond to a nitrokey::Model, and vice versa. This patch adds the appropriate From and TryFrom implementations. --- src/device/mod.rs | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) (limited to 'src/device') diff --git a/src/device/mod.rs b/src/device/mod.rs index 5e15f08..a3a0255 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -5,6 +5,7 @@ mod pro; mod storage; mod wrapper; +use std::convert::TryFrom; use std::fmt; use libc; @@ -43,6 +44,30 @@ impl fmt::Display for Model { } } +impl From for nitrokey_sys::NK_device_model { + fn from(model: Model) -> Self { + match model { + Model::Storage => nitrokey_sys::NK_device_model_NK_STORAGE, + Model::Pro => nitrokey_sys::NK_device_model_NK_PRO, + } + } +} + +impl TryFrom for Model { + type Error = Error; + + fn try_from(model: nitrokey_sys::NK_device_model) -> Result { + match model { + nitrokey_sys::NK_device_model_NK_DISCONNECTED => { + Err(CommunicationError::NotConnected.into()) + } + nitrokey_sys::NK_device_model_NK_PRO => Ok(Model::Pro), + nitrokey_sys::NK_device_model_NK_STORAGE => Ok(Model::Storage), + _ => Err(Error::UnsupportedModelError), + } + } +} + /// A firmware version for a Nitrokey device. #[derive(Clone, Copy, Debug, PartialEq)] pub struct FirmwareVersion { @@ -428,12 +453,8 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt } } -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, - } +fn get_connected_model() -> Result { + Model::try_from(unsafe { nitrokey_sys::NK_get_device_model() }) } pub(crate) fn create_device_wrapper( @@ -449,16 +470,9 @@ pub(crate) fn 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()), - } + Ok(create_device_wrapper(manager, get_connected_model()?)) } 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 } + unsafe { nitrokey_sys::NK_login_enum(model.into()) == 1 } } -- cgit v1.2.1 From bcad061ed3e7777547c1b6fc9223dd65f752d94e Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sat, 28 Dec 2019 23:53:24 +0100 Subject: Add the DeviceInfo struct In the next patch, we will add support for the NK_list_devices functions that returns a list of NK_device_info structs with information about the connected devices. This patch introduces the DeviceInfo struct that holds the information returned by NK_list_devices and that can be created from a NK_device_info struct. --- src/device/mod.rs | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 2 deletions(-) (limited to 'src/device') diff --git a/src/device/mod.rs b/src/device/mod.rs index a3a0255..e015886 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -5,7 +5,9 @@ mod pro; mod storage; mod wrapper; -use std::convert::TryFrom; +use std::cmp; +use std::convert::{TryFrom, TryInto}; +use std::ffi; use std::fmt; use libc; @@ -17,7 +19,8 @@ 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, + get_command_result, get_cstring, get_last_error, owned_str_from_ptr, result_from_string, + result_or_error, }; pub use pro::Pro; @@ -68,6 +71,73 @@ impl TryFrom for Model { } } +/// Connection information for a Nitrokey device. +#[derive(Clone, Debug, PartialEq)] +pub struct DeviceInfo { + /// The model of the Nitrokey device, or `None` if the model is not supported by this crate. + pub model: Option, + /// The USB device path. + pub path: String, + /// The serial number as a 8-character hex string, or `None` if the device does not expose its + /// serial number. + pub serial_number: Option, +} + +impl TryFrom<&nitrokey_sys::NK_device_info> for DeviceInfo { + type Error = Error; + + fn try_from(device_info: &nitrokey_sys::NK_device_info) -> Result { + let model_result = device_info.model.try_into(); + let model_option = model_result.map(Some).or_else(|err| match err { + Error::UnsupportedModelError => Ok(None), + _ => Err(err), + })?; + let serial_number = unsafe { ffi::CStr::from_ptr(device_info.serial_number) } + .to_str() + .map_err(Error::from)?; + Ok(DeviceInfo { + model: model_option, + path: owned_str_from_ptr(device_info.path)?, + serial_number: get_hidapi_serial_number(serial_number), + }) + } +} + +impl fmt::Display for DeviceInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.model { + Some(model) => write!(f, "Nitrokey {}", model)?, + None => write!(f, "Unsupported Nitrokey model")?, + } + write!(f, " at {} with ", self.path)?; + match &self.serial_number { + Some(ref serial_number) => write!(f, "serial no. {}", serial_number), + None => write!(f, "an unknown serial number"), + } + } +} + +/// Parses a serial number returned by hidapi and transforms it to the Nitrokey format. +/// +/// If the serial number is all zero, this function returns `None`. Otherwise, all leading zeros +/// (except the last eight characters) are stripped and `Some(_)` is returned. If the serial +/// number has less than eight characters, leading zeros are added. +fn get_hidapi_serial_number(serial_number: &str) -> Option { + let mut iter = serial_number.char_indices().skip_while(|(_, c)| *c == '0'); + let first_non_null = iter.next(); + if let Some((i, _)) = first_non_null { + // keep at least the last 8 characters + let len = serial_number.len(); + let cut = cmp::min(len.saturating_sub(8), i); + let (_, suffix) = serial_number.split_at(cut); + // if necessary, add leading zeros to reach 8 characters + let fill = 8usize.saturating_sub(len); + Some("0".repeat(fill) + suffix) + } else { + None + } +} + /// A firmware version for a Nitrokey device. #[derive(Clone, Copy, Debug, PartialEq)] pub struct FirmwareVersion { @@ -476,3 +546,34 @@ pub(crate) fn get_connected_device( pub(crate) fn connect_enum(model: Model) -> bool { unsafe { nitrokey_sys::NK_login_enum(model.into()) == 1 } } + +#[cfg(test)] +mod tests { + use super::get_hidapi_serial_number; + + #[test] + fn hidapi_serial_number() { + assert_eq!(None, get_hidapi_serial_number("")); + assert_eq!(None, get_hidapi_serial_number("00000000000000000")); + assert_eq!( + Some("00001234".to_string()), + get_hidapi_serial_number("1234") + ); + assert_eq!( + Some("00001234".to_string()), + get_hidapi_serial_number("00001234") + ); + assert_eq!( + Some("00001234".to_string()), + get_hidapi_serial_number("000000001234") + ); + assert_eq!( + Some("100000001234".to_string()), + get_hidapi_serial_number("100000001234") + ); + assert_eq!( + Some("10000001234".to_string()), + get_hidapi_serial_number("010000001234") + ); + } +} -- cgit v1.2.1 From dbee55efa41496c8a683bfab96163facc93d6639 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sat, 11 Jan 2020 20:05:39 +0100 Subject: Add support for the GET_STATUS command This patch adds support for the GET_STATUS command that returns the status information common to all Nitrokey devices. It can be accessed using the Device::get_status function and is stored in a Status struct. Due to a bug in the Storage firmware [0], the GET_STATUS command returns wrong firmware versions and serial numbers. Until this is fixed in libnitrokey [1], we have to manually execute the GET_DEVICE_STATUS command to fix these values for the Nitrokey Storage. Also, this leads to a name clash with the existing Storage::get_status function, which will be renamed in an upcoming patch. [0] https://github.com/Nitrokey/nitrokey-storage-firmware/issues/96 [1] https://github.com/Nitrokey/libnitrokey/issues/166 --- src/device/mod.rs | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/device/pro.rs | 18 +++++++++++++++- src/device/storage.rs | 31 +++++++++++++++++++++++++-- src/device/wrapper.rs | 9 +++++++- 4 files changed, 112 insertions(+), 4 deletions(-) (limited to 'src/device') diff --git a/src/device/mod.rs b/src/device/mod.rs index e015886..668c230 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -153,6 +153,35 @@ impl fmt::Display for FirmwareVersion { } } +/// The status information common to all Nitrokey devices. +pub struct Status { + /// The firmware version of the device. + pub firmware_version: FirmwareVersion, + /// The serial number of the device. + pub serial_number: u32, + /// The configuration of the device. + pub config: Config, +} + +impl From for Status { + fn from(status: nitrokey_sys::NK_status) -> Self { + Self { + firmware_version: FirmwareVersion { + major: status.firmware_version_major, + minor: status.firmware_version_minor, + }, + serial_number: status.serial_number_smart_card, + config: RawConfig { + numlock: status.config_numlock, + capslock: status.config_capslock, + scrollock: status.config_scrolllock, + user_password: status.otp_user_password, + } + .into(), + } + } +} + /// A Nitrokey device. /// /// This trait provides the commands that can be executed without authentication and that are @@ -197,6 +226,35 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt /// # } fn get_model(&self) -> Model; + /// Returns the status of the Nitrokey device. + /// + /// This methods returns the status information common to all Nitrokey devices as a + /// [`Status`][] struct. Some models may provide more information, for example + /// [`Storage::get_status`][] returns the [`StorageStatus`][] struct. + /// + /// # Errors + /// + /// - [`NotConnected`][] if the Nitrokey device has been disconnected + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::Device; + /// + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; + /// let status = device.get_status()?; + /// println!("Firmware version: {}", status.firmware_version); + /// println!("Serial number: {:x}", status.serial_number); + /// # Ok::<(), nitrokey::Error>(()) + /// ``` + /// + /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected + /// [`Status`]: struct.Status.html + /// [`Storage::get_status`]: struct.Storage.html#method.get_status + /// [`StorageStatus`]: struct.StorageStatus.html + fn get_status(&self) -> Result; + /// Returns the serial number of the Nitrokey device. The serial number is the string /// representation of a hex number. /// diff --git a/src/device/pro.rs b/src/device/pro.rs index a65345e..591b730 100644 --- a/src/device/pro.rs +++ b/src/device/pro.rs @@ -3,8 +3,10 @@ use nitrokey_sys; -use crate::device::{Device, Model}; +use crate::device::{Device, Model, Status}; +use crate::error::Error; use crate::otp::GenerateOtp; +use crate::util::get_command_result; /// A Nitrokey Pro device without user or admin authentication. /// @@ -74,6 +76,20 @@ impl<'a> Device<'a> for Pro<'a> { fn get_model(&self) -> Model { Model::Pro } + + fn get_status(&self) -> Result { + let mut raw_status = nitrokey_sys::NK_status { + firmware_version_major: 0, + firmware_version_minor: 0, + serial_number_smart_card: 0, + config_numlock: 0, + config_capslock: 0, + config_scrolllock: 0, + otp_user_password: false, + }; + get_command_result(unsafe { nitrokey_sys::NK_get_status(&mut raw_status) })?; + Ok(raw_status.into()) + } } impl<'a> GenerateOtp for Pro<'a> {} diff --git a/src/device/storage.rs b/src/device/storage.rs index 370ce36..64e6848 100644 --- a/src/device/storage.rs +++ b/src/device/storage.rs @@ -1,11 +1,11 @@ -// Copyright (C) 2018-2019 Robin Krahl +// Copyright (C) 2019-2019 Robin Krahl // SPDX-License-Identifier: MIT use std::fmt; use nitrokey_sys; -use crate::device::{Device, FirmwareVersion, Model}; +use crate::device::{Device, FirmwareVersion, Model, Status}; use crate::error::Error; use crate::otp::GenerateOtp; use crate::util::{get_command_result, get_cstring}; @@ -678,6 +678,33 @@ impl<'a> Device<'a> for Storage<'a> { fn get_model(&self) -> Model { Model::Storage } + + fn get_status(&self) -> Result { + // Currently, the GET_STATUS command does not report the correct firmware version and + // serial number on the Nitrokey Storage, see [0]. Until this is fixed in libnitrokey, we + // have to manually execute the GET_DEVICE_STATUS command (Storage::get_status) and + // complete the missing data, see [1]. + // [0] https://github.com/Nitrokey/nitrokey-storage-firmware/issues/96 + // [1] https://github.com/Nitrokey/libnitrokey/issues/166 + + let mut raw_status = nitrokey_sys::NK_status { + firmware_version_major: 0, + firmware_version_minor: 0, + serial_number_smart_card: 0, + config_numlock: 0, + config_capslock: 0, + config_scrolllock: 0, + otp_user_password: false, + }; + get_command_result(unsafe { nitrokey_sys::NK_get_status(&mut raw_status) })?; + let mut status = Status::from(raw_status); + + let storage_status = Storage::get_status(&self)?; + status.firmware_version = storage_status.firmware_version; + status.serial_number = storage_status.serial_number_smart_card; + + Ok(status) + } } impl<'a> GenerateOtp for Storage<'a> {} diff --git a/src/device/wrapper.rs b/src/device/wrapper.rs index a3a18f9..adbb695 100644 --- a/src/device/wrapper.rs +++ b/src/device/wrapper.rs @@ -1,7 +1,7 @@ // Copyright (C) 2018-2019 Robin Krahl // SPDX-License-Identifier: MIT -use crate::device::{Device, Model, Pro, Storage}; +use crate::device::{Device, Model, Pro, Status, Storage}; use crate::error::Error; use crate::otp::GenerateOtp; @@ -131,4 +131,11 @@ impl<'a> Device<'a> for DeviceWrapper<'a> { DeviceWrapper::Storage(_) => Model::Storage, } } + + fn get_status(&self) -> Result { + match self { + DeviceWrapper::Pro(dev) => dev.get_status(), + DeviceWrapper::Storage(dev) => Device::get_status(dev), + } + } } -- cgit v1.2.1 From 6142752da1563c1ab873dc7069aeec72522cca99 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sat, 11 Jan 2020 20:13:22 +0100 Subject: Rename Status::get_status to get_storage_status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the last patch, we added the get_status function to the Device trait. This patch renames the Storage::get_status function to get_storage_status to resolve the name clash – though allowed by the compiler, it is rather confusing for the end user. --- src/device/mod.rs | 4 ++-- src/device/storage.rs | 10 +++++----- src/device/wrapper.rs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/device') diff --git a/src/device/mod.rs b/src/device/mod.rs index 668c230..ef9f018 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -230,7 +230,7 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt /// /// This methods returns the status information common to all Nitrokey devices as a /// [`Status`][] struct. Some models may provide more information, for example - /// [`Storage::get_status`][] returns the [`StorageStatus`][] struct. + /// [`get_storage_status`][] returns the [`StorageStatus`][] struct. /// /// # Errors /// @@ -249,9 +249,9 @@ pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt /// # Ok::<(), nitrokey::Error>(()) /// ``` /// + /// [`get_storage_status`]: struct.Storage.html#method.get_storage_status /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected /// [`Status`]: struct.Status.html - /// [`Storage::get_status`]: struct.Storage.html#method.get_status /// [`StorageStatus`]: struct.StorageStatus.html fn get_status(&self) -> Result; diff --git a/src/device/storage.rs b/src/device/storage.rs index 64e6848..08ec4e9 100644 --- a/src/device/storage.rs +++ b/src/device/storage.rs @@ -525,7 +525,7 @@ impl<'a> Storage<'a> { /// # fn try_main() -> Result<(), Error> { /// let mut manager = nitrokey::take()?; /// let device = manager.connect_storage()?; - /// match device.get_status() { + /// match device.get_storage_status() { /// Ok(status) => { /// println!("SD card ID: {:#x}", status.serial_number_sd_card); /// }, @@ -534,7 +534,7 @@ impl<'a> Storage<'a> { /// # Ok(()) /// # } /// ``` - pub fn get_status(&self) -> Result { + pub fn get_storage_status(&self) -> Result { let mut raw_status = nitrokey_sys::NK_storage_status { unencrypted_volume_read_only: false, unencrypted_volume_active: false, @@ -682,8 +682,8 @@ impl<'a> Device<'a> for Storage<'a> { fn get_status(&self) -> Result { // Currently, the GET_STATUS command does not report the correct firmware version and // serial number on the Nitrokey Storage, see [0]. Until this is fixed in libnitrokey, we - // have to manually execute the GET_DEVICE_STATUS command (Storage::get_status) and - // complete the missing data, see [1]. + // have to manually execute the GET_DEVICE_STATUS command (get_storage_status) and complete + // the missing data, see [1]. // [0] https://github.com/Nitrokey/nitrokey-storage-firmware/issues/96 // [1] https://github.com/Nitrokey/libnitrokey/issues/166 @@ -699,7 +699,7 @@ impl<'a> Device<'a> for Storage<'a> { get_command_result(unsafe { nitrokey_sys::NK_get_status(&mut raw_status) })?; let mut status = Status::from(raw_status); - let storage_status = Storage::get_status(&self)?; + let storage_status = self.get_storage_status()?; status.firmware_version = storage_status.firmware_version; status.serial_number = storage_status.serial_number_smart_card; diff --git a/src/device/wrapper.rs b/src/device/wrapper.rs index adbb695..69291ad 100644 --- a/src/device/wrapper.rs +++ b/src/device/wrapper.rs @@ -135,7 +135,7 @@ impl<'a> Device<'a> for DeviceWrapper<'a> { fn get_status(&self) -> Result { match self { DeviceWrapper::Pro(dev) => dev.get_status(), - DeviceWrapper::Storage(dev) => Device::get_status(dev), + DeviceWrapper::Storage(dev) => dev.get_status(), } } } -- cgit v1.2.1 From 73c8aebac338d3454d7e345ffd687324317077ec Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 14 Jan 2020 12:00:15 +0100 Subject: Add the get_sd_card_usage function to the Storage struct This patch adds support for the NK_get_SD_usage_data function. It returns a range of the SD card that has not been accessed during this power cycle. --- src/device/storage.rs | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) (limited to 'src/device') diff --git a/src/device/storage.rs b/src/device/storage.rs index 08ec4e9..a2c598b 100644 --- a/src/device/storage.rs +++ b/src/device/storage.rs @@ -1,7 +1,8 @@ -// Copyright (C) 2019-2019 Robin Krahl +// Copyright (C) 2019-2020 Robin Krahl // SPDX-License-Identifier: MIT use std::fmt; +use std::ops; use nitrokey_sys; @@ -635,6 +636,45 @@ impl<'a> Storage<'a> { }) } + /// Returns a range of the SD card that has not been used to during this power cycle. + /// + /// The Nitrokey Storage tracks read and write access to the SD card during a power cycle. + /// This method returns a range of the SD card that has not been accessed during this power + /// cycle. The range is relative to the total size of the SD card, so both values are less + /// than or equal to 100. This can be used as a guideline when creating a hidden volume. + /// + /// # Example + /// + /// ```no_run + /// let mut manager = nitrokey::take()?; + /// let storage = manager.connect_storage()?; + /// let usage = storage.get_sd_card_usage()?; + /// println!("SD card usage: {}..{}", usage.start, usage.end); + /// # Ok::<(), nitrokey::Error>(()) + /// ``` + pub fn get_sd_card_usage(&self) -> Result, Error> { + let mut usage_data = nitrokey_sys::NK_SD_usage_data { + write_level_min: 0, + write_level_max: 0, + }; + let result = unsafe { nitrokey_sys::NK_get_SD_usage_data(&mut usage_data) }; + match get_command_result(result) { + Ok(_) => { + if usage_data.write_level_min > usage_data.write_level_max + || usage_data.write_level_max > 100 + { + Err(Error::UnexpectedError) + } else { + Ok(ops::Range { + start: usage_data.write_level_min, + end: usage_data.write_level_max, + }) + } + } + Err(err) => Err(err), + } + } + /// 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() }) -- cgit v1.2.1 From eb04dafce313ff5349b1c93d8d87cb53ba320e7e Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 14 Jan 2020 16:18:02 +0100 Subject: Derive Clone, Copy, Debug, PartialEq for Status Somehow I forgot to derive the common traits for the new Status struct. This patch adds the missing derive attribute for Clone, Copy, Debug and PartialEq. --- src/device/mod.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/device') diff --git a/src/device/mod.rs b/src/device/mod.rs index ef9f018..8b7f1c6 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -154,6 +154,7 @@ impl fmt::Display for FirmwareVersion { } /// The status information common to all Nitrokey devices. +#[derive(Clone, Copy, Debug, PartialEq)] pub struct Status { /// The firmware version of the device. pub firmware_version: FirmwareVersion, -- cgit v1.2.1 From f266ea63039c87886f871b068ef3dcdf851a1eca Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 14 Jan 2020 13:48:56 +0100 Subject: Add the get_operation_status function to the Storage struct This patch adds support for the NK_get_progress_bar_value function: It adds the OperationStatus enum that stores the return value of this command and adds the get_operation_status function to the Storage struct that executes the command. --- src/device/mod.rs | 3 ++- src/device/storage.rs | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) (limited to 'src/device') diff --git a/src/device/mod.rs b/src/device/mod.rs index 8b7f1c6..16d6b11 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -25,7 +25,8 @@ use crate::util::{ pub use pro::Pro; pub use storage::{ - SdCardData, Storage, StorageProductionInfo, StorageStatus, VolumeMode, VolumeStatus, + OperationStatus, SdCardData, Storage, StorageProductionInfo, StorageStatus, VolumeMode, + VolumeStatus, }; pub use wrapper::DeviceWrapper; diff --git a/src/device/storage.rs b/src/device/storage.rs index a2c598b..8d45763 100644 --- a/src/device/storage.rs +++ b/src/device/storage.rs @@ -1,6 +1,7 @@ // Copyright (C) 2019-2020 Robin Krahl // SPDX-License-Identifier: MIT +use std::convert::TryFrom as _; use std::fmt; use std::ops; @@ -9,7 +10,7 @@ use nitrokey_sys; use crate::device::{Device, FirmwareVersion, Model, Status}; use crate::error::Error; use crate::otp::GenerateOtp; -use crate::util::{get_command_result, get_cstring}; +use crate::util::{get_command_result, get_cstring, get_last_error}; /// A Nitrokey Storage device without user or admin authentication. /// @@ -142,6 +143,20 @@ pub struct StorageStatus { pub stick_initialized: bool, } +/// The progress of a background operation on the Nitrokey. +/// +/// Some commands may start a background operation during which no other commands can be executed. +/// This enum stores the status of a background operation: Ongoing with a relative progress (up to +/// 100), or idle, i. e. no background operation has been started or the last one has been +/// finished. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum OperationStatus { + /// A background operation with its progress value (less than or equal to 100). + Ongoing(u8), + /// No backgrund operation. + Idle, +} + impl<'a> Storage<'a> { pub(crate) fn new(manager: &'a mut crate::Manager) -> Storage<'a> { Storage { @@ -680,6 +695,24 @@ impl<'a> Storage<'a> { get_command_result(unsafe { nitrokey_sys::NK_wink() }) } + /// Returns the status of an ongoing background operation on the Nitrokey Storage. + /// + /// Some commands may start a background operation during which no other commands can be + /// executed. This method can be used to check whether such an operation is ongoing. + /// + /// Currently, this is only used by the `fill_sd_card` method. + pub fn get_operation_status(&self) -> Result { + let status = unsafe { nitrokey_sys::NK_get_progress_bar_value() }; + match status { + 0..=100 => u8::try_from(status) + .map(OperationStatus::Ongoing) + .map_err(|_| Error::UnexpectedError), + -1 => Ok(OperationStatus::Idle), + -2 => Err(get_last_error()), + _ => Err(Error::UnexpectedError), + } + } + /// Exports the firmware to the unencrypted volume. /// /// This command requires the admin PIN. The unencrypted volume must be in read-write mode -- cgit v1.2.1 From 2e543445c3059fa9decdbef718caf84696bb8786 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 14 Jan 2020 16:15:40 +0100 Subject: Add the fill_sd_card function to Storage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for libnitrokey’s NK_fill_SD_card_with_random_data function. It is executed by the fill_sd_card function of the Storage struct. We also add a new test case that is set to ignore because it takes between 30 and 60 minutes to run. --- src/device/storage.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) (limited to 'src/device') diff --git a/src/device/storage.rs b/src/device/storage.rs index 8d45763..deb2844 100644 --- a/src/device/storage.rs +++ b/src/device/storage.rs @@ -8,7 +8,7 @@ use std::ops; use nitrokey_sys; use crate::device::{Device, FirmwareVersion, Model, Status}; -use crate::error::Error; +use crate::error::{CommandError, Error}; use crate::otp::GenerateOtp; use crate::util::{get_command_result, get_cstring, get_last_error}; @@ -700,7 +700,9 @@ impl<'a> Storage<'a> { /// Some commands may start a background operation during which no other commands can be /// executed. This method can be used to check whether such an operation is ongoing. /// - /// Currently, this is only used by the `fill_sd_card` method. + /// Currently, this is only used by the [`fill_sd_card`][] method. + /// + /// [`fill_sd_card`]: #method.fill_sd_card pub fn get_operation_status(&self) -> Result { let status = unsafe { nitrokey_sys::NK_get_progress_bar_value() }; match status { @@ -713,6 +715,53 @@ impl<'a> Storage<'a> { } } + /// Overwrites the SD card with random data. + /// + /// Ths method starts a background operation that overwrites the SD card with random data. + /// While this operation is ongoing, no other commands can be executed. Use the + /// [`get_operation_status`][] function to check the progress of the operation. + /// + /// # Errors + /// + /// - [`InvalidString`][] if one of the provided passwords contains a null byte + /// - [`WrongPassword`][] if the admin password is wrong + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::OperationStatus; + /// + /// let mut manager = nitrokey::take()?; + /// let mut storage = manager.connect_storage()?; + /// storage.fill_sd_card("12345678")?; + /// loop { + /// match storage.get_operation_status()? { + /// OperationStatus::Ongoing(progress) => println!("{}/100", progress), + /// OperationStatus::Idle => { + /// println!("Done!"); + /// break; + /// } + /// } + /// } + /// # Ok::<(), nitrokey::Error>(()) + /// ``` + /// + /// [`get_operation_status`]: #method.get_operation_status + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString + /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword + pub fn fill_sd_card(&mut self, admin_pin: &str) -> Result<(), Error> { + let admin_pin_string = get_cstring(admin_pin)?; + get_command_result(unsafe { + nitrokey_sys::NK_fill_SD_card_with_random_data(admin_pin_string.as_ptr()) + }) + .or_else(|err| match err { + // libnitrokey’s C API returns a LongOperationInProgressException with the same error + // code as the WrongCrc command error, so we cannot distinguish them. + Error::CommandError(CommandError::WrongCrc) => Ok(()), + err => Err(err), + }) + } + /// Exports the firmware to the unencrypted volume. /// /// This command requires the admin PIN. The unencrypted volume must be in read-write mode -- cgit v1.2.1