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 +++++++- src/lib.rs | 4 ++-- 5 files changed, 114 insertions(+), 6 deletions(-) (limited to 'src') 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), + } + } } diff --git a/src/lib.rs b/src/lib.rs index 7e8b3ef..41c99c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -126,8 +126,8 @@ use nitrokey_sys; pub use crate::auth::{Admin, Authenticate, User}; pub use crate::config::Config; pub use crate::device::{ - Device, DeviceInfo, DeviceWrapper, Model, Pro, SdCardData, Storage, StorageProductionInfo, - StorageStatus, VolumeMode, VolumeStatus, + Device, DeviceInfo, DeviceWrapper, Model, Pro, SdCardData, Status, Storage, + StorageProductionInfo, StorageStatus, VolumeMode, VolumeStatus, }; pub use crate::error::{CommandError, CommunicationError, Error, LibraryError}; pub use crate::otp::{ConfigureOtp, GenerateOtp, OtpMode, OtpSlotData}; -- cgit v1.2.1