aboutsummaryrefslogtreecommitdiff
path: root/nitrocli/src/main.rs
diff options
context:
space:
mode:
authorRobin Krahl <me@robin-krahl.de>2018-12-11 23:51:29 +0100
committerDaniel Mueller <deso@posteo.net>2018-12-17 07:52:13 -0800
commit6737f748b8edb5266c5397591b92da4c88f95dff (patch)
treec732624e9103d243ad15a35b2625351f87501187 /nitrocli/src/main.rs
parent4d314264a897c474c12e626a2be36b75dc57f5c9 (diff)
downloadnitrocli-6737f748b8edb5266c5397591b92da4c88f95dff.tar.gz
nitrocli-6737f748b8edb5266c5397591b92da4c88f95dff.tar.bz2
Port the status command to libnitrokey
This patch removes the raw hidapi implementation of the status command and all utility methods that are no longer needed. With this patch, all device communication is performed using libnitrokey.
Diffstat (limited to 'nitrocli/src/main.rs')
-rw-r--r--nitrocli/src/main.rs237
1 files changed, 25 insertions, 212 deletions
diff --git a/nitrocli/src/main.rs b/nitrocli/src/main.rs
index 714c573..5ec68a2 100644
--- a/nitrocli/src/main.rs
+++ b/nitrocli/src/main.rs
@@ -68,29 +68,19 @@
//! Nitrocli is a program providing a command line interface to certain
//! commands of the Nitrokey Storage device.
-mod crc32;
mod error;
-mod nitrokey;
mod pinentry;
-use std::mem;
use std::process;
use std::result;
-use std::thread;
-use std::time;
use libnitrokey;
use crate::error::Error;
type Result<T> = result::Result<T, Error>;
-type NitroFunc = dyn Fn(&mut libhid::Handle) -> Result<()>;
-
const PIN_TYPE: pinentry::PinType = pinentry::PinType::User;
-const SEND_TRY_COUNT: i8 = 3;
-const RECV_TRY_COUNT: i8 = 40;
-const SEND_RECV_DELAY_MS: u64 = 200;
/// Create an `error::Error` with an error message of the format `msg: err`.
@@ -105,185 +95,58 @@ fn get_storage_device() -> Result<libnitrokey::Storage> {
.or_else(|_| Err(Error::Error("Nitrokey device not found".to_string())))
}
-
-/// Send a HID feature report to the device represented by the given handle.
-fn send<P>(handle: &mut libhid::Handle, report: &nitrokey::Report<P>) -> Result<()>
- where P: AsRef<[u8]>,
-{
- let mut retry = SEND_TRY_COUNT;
- loop {
- let result = handle.feature().send_to(0, report.as_ref());
- retry -= 1;
-
- match result {
- Ok(_) => {
- return Ok(());
- },
- Err(err) => {
- if retry > 0 {
- thread::sleep(time::Duration::from_millis(SEND_RECV_DELAY_MS));
- continue;
- } else {
- return Err(Error::HidError(err));
- }
- },
- }
- }
-}
-
-
-/// Receive a HID feature report from the device represented by the given handle.
-fn receive<P>(handle: &mut libhid::Handle) -> Result<nitrokey::Report<P>>
- where P: AsRef<[u8]> + Default,
-{
- let mut retry = RECV_TRY_COUNT;
- loop {
- let mut report = nitrokey::Report::<P>::new();
- let result = handle.feature().get_from(0, report.as_mut());
-
- retry -= 1;
-
- match result {
- Ok(size) => {
- if size < mem::size_of_val(&report) {
- if retry > 0 {
- continue;
- } else {
- return Err(Error::Error("Failed to receive complete report".to_string()));
- }
- }
-
- if !report.is_valid() {
- if retry > 0 {
- continue;
- } else {
- return Err(Error::Error("Failed to receive report: CRC mismatch".to_string()));
- }
- }
- return Ok(report);
- },
-
- Err(err) => {
- if retry > 0 {
- thread::sleep(time::Duration::from_millis(SEND_RECV_DELAY_MS));
- continue;
- } else {
- return Err(Error::HidError(err));
- }
- },
+/// Return a string representation of the given volume status.
+fn get_volume_status(status: &libnitrokey::VolumeStatus) -> &'static str {
+ if status.active {
+ if status.read_only {
+ "read-only"
+ } else {
+ "active"
}
+ } else {
+ "inactive"
}
}
-/// Transmit a HID feature report to the nitrokey and receive a response.
-fn transmit<PS, PR>(handle: &mut libhid::Handle,
- report: &nitrokey::Report<PS>)
- -> Result<nitrokey::Report<PR>>
- where PS: AsRef<[u8]>,
- PR: AsRef<[u8]> + Default,
-{
- send(handle, report)?;
-
- // We need to give the stick some time to handle the command. If we
- // don't, we might just receive stale data from before.
- thread::sleep(time::Duration::from_millis(SEND_RECV_DELAY_MS));
-
- receive::<PR>(handle)
-}
-
-
-/// Find and open the nitrokey device and execute a function on it.
-fn nitrokey_do(function: &NitroFunc) -> Result<()> {
- let hid = libhid::init()?;
- // The Manager::find method is plain stupid as it still returns an
- // iterable. Using it does not help in more concise error handling.
- for device in hid.devices() {
- if device.vendor_id() == nitrokey::VID && device.product_id() == nitrokey::PID {
- return function(&mut device.open()?);
- }
- }
- Err(Error::Error("Nitrokey device not found".to_string()))
-}
-
-
/// Pretty print the response of a status command.
-fn print_status(response: &nitrokey::DeviceStatusResponse) {
+fn print_status(status: &libnitrokey::StorageStatus) {
println!("Status:");
// We omit displaying information about the smartcard here as this
// program really is only about the SD card portion of the device.
- println!(" SD card ID: {:#x}", response.active_sdcard_id);
+ println!(" SD card ID: {:#x}", status.serial_number_sd_card);
println!(" firmware version: {}.{}",
- response.version_major,
- response.version_minor);
+ status.firmware_version_major,
+ status.firmware_version_minor);
println!(" firmware: {}",
- if response.firmware_locked != 0 {
+ if status.firmware_locked {
"locked".to_string()
} else {
"unlocked".to_string()
});
println!(" storage keys: {}",
- if response.storage_keys_missing == 0 {
+ if status.stick_initialized {
"created".to_string()
} else {
"not created".to_string()
});
- println!(" user retry count: {}",
- response.user_password_retry_count);
- println!(" admin retry count: {}",
- response.admin_password_retry_count);
+ println!(" user retry count: {}", status.user_retry_count);
+ println!(" admin retry count: {}", status.admin_retry_count);
println!(" volumes:");
- println!(" unencrypted: {}",
- if response.volume_active & nitrokey::VOLUME_ACTIVE_UNENCRYPTED == 0 {
- "inactive"
- } else if response.unencrypted_volume_read_only != 0 {
- "read-only"
- } else {
- "active"
- });
- println!(" encrypted: {}",
- if response.volume_active & nitrokey::VOLUME_ACTIVE_ENCRYPTED == 0 {
- "inactive"
- } else if response.encrypted_volume_read_only != 0 {
- "read-only"
- } else {
- "active"
- });
- println!(" hidden: {}",
- if response.volume_active & nitrokey::VOLUME_ACTIVE_HIDDEN == 0 {
- "inactive"
- } else if response.hidden_volume_read_only != 0 {
- "read-only"
- } else {
- "active"
- });
+ println!(" unencrypted: {}", get_volume_status(&status.unencrypted_volume));
+ println!(" encrypted: {}", get_volume_status(&status.encrypted_volume));
+ println!(" hidden: {}", get_volume_status(&status.hidden_volume));
}
/// Inquire the status of the nitrokey.
fn status() -> Result<()> {
- type Response = nitrokey::Response<nitrokey::DeviceStatusResponse>;
-
- nitrokey_do(&|handle| {
- let payload = nitrokey::DeviceStatusCommand::new();
- let report = nitrokey::Report::from(payload);
-
- let report = transmit::<_, nitrokey::EmptyPayload>(handle, &report)?;
- let response = &AsRef::<Response>::as_ref(&report.data).data;
-
- // TODO: We should probably check the success of the command as
- // well.
- if response.magic != nitrokey::MAGIC_NUMBER_STICK20_CONFIG {
- let error = format!("Status response contains invalid magic: {:#x} \
- (expected: {:#x})",
- response.magic,
- nitrokey::MAGIC_NUMBER_STICK20_CONFIG);
- return Err(Error::Error(error.to_string()));
- }
+ let status = get_storage_device()?
+ .get_status()
+ .map_err(|err| get_error("Getting Storage status failed", &err))?;
- print_status(response);
- Ok(())
- })
+ print_status(&status);
+ Ok(())
}
@@ -380,53 +243,3 @@ fn run() -> i32 {
fn main() {
process::exit(run());
}
-
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn wrong_crc() {
- type Response = nitrokey::Response<nitrokey::DeviceStatusResponse>;
-
- nitrokey_do(&|handle| {
- let payload = nitrokey::DeviceStatusCommand::new();
- let mut report = nitrokey::Report::from(payload);
-
- // We want to verify that we get the correct result (i.e., a
- // report of the CRC mismatch) repeatedly.
- for _ in 0..10 {
- report.crc += 1;
-
- let new_report = transmit::<_, nitrokey::EmptyPayload>(handle, &report)?;
- let response = AsRef::<Response>::as_ref(&new_report.data);
-
- assert_eq!(response.command, nitrokey::Command::GetDeviceStatus);
- assert_eq!(response.command_crc, report.crc);
- assert_eq!(response.command_status, nitrokey::CommandStatus::WrongCrc);
- }
- return Ok(());
- })
- .unwrap();
- }
-
- #[test]
- fn device_status() {
- type Response = nitrokey::Response<nitrokey::DeviceStatusResponse>;
-
- nitrokey_do(&|handle| {
- let payload = nitrokey::DeviceStatusCommand::new();
- let report = nitrokey::Report::from(payload);
-
- let report = transmit::<_, nitrokey::EmptyPayload>(handle, &report)?;
- let response = AsRef::<Response>::as_ref(&report.data);
-
- assert!(response.device_status == nitrokey::StorageStatus::Idle ||
- response.device_status == nitrokey::StorageStatus::Okay);
- assert_eq!(response.data.magic, nitrokey::MAGIC_NUMBER_STICK20_CONFIG);
- return Ok(());
- })
- .unwrap();
- }
-}