From 02d31ed3dc89528f5b46201abb0c03b6ef29cc16 Mon Sep 17 00:00:00 2001 From: Daniel Mueller Date: Thu, 30 Mar 2017 21:25:59 -0700 Subject: Receive command responses By just sending a command to the nitrokey alone we have no idea of what actually happened on the side of the nitrokey. A command could simply be invalid in the current context or the stick could be busy or in a failure state. In order to determine the success of an operation, this change adds the logic to retrieve the response from the nitrokey as well. --- nitrocli/src/main.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 2 deletions(-) (limited to 'nitrocli/src/main.rs') diff --git a/nitrocli/src/main.rs b/nitrocli/src/main.rs index 700204d..a1f026e 100644 --- a/nitrocli/src/main.rs +++ b/nitrocli/src/main.rs @@ -34,11 +34,35 @@ mod pinentry; use error::Error; use std::process; use std::result; +use std::thread; +use std::time; type Result = result::Result; type NitroFunc = Fn(&mut libhid::Handle) -> Result<()>; +const SEND_RECV_DELAY_MS: u64 = 200; + + +/// Send a HID feature report to the device represented by the given handle. +fn send

(handle: &mut libhid::Handle, report: &nitrokey::Report

) -> Result<()> + where P: AsRef<[u8]>, +{ + handle.feature().send_to(0, report.as_ref())?; + return Ok(()); +} + + +/// Receive a HID feature report from the device represented by the given handle. +fn receive

(handle: &mut libhid::Handle) -> Result> + where P: AsRef<[u8]> + Default, +{ + let mut report = nitrokey::Report::

::new(); + handle.feature().get_from(0, report.as_mut())?; + return Ok(report); +} + + /// Find and open the nitrokey device and execute a function on it. fn nitrokey_do(function: &NitroFunc) -> Result<()> { let hid = libhid::init()?; @@ -60,7 +84,12 @@ fn open() -> Result<()> { let payload = nitrokey::EnableEncryptedVolumeCommand::new(&passphrase); let report = nitrokey::Report::from(payload); - handle.feature().send_to(0, report.as_ref())?; + 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::(handle)?; return Ok(()); }); } @@ -72,7 +101,7 @@ fn close() -> Result<()> { let payload = nitrokey::DisableEncryptedVolumeCommand::new(); let report = nitrokey::Report::from(payload); - handle.feature().send_to(0, report.as_ref())?; + send(handle, &report)?; return Ok(()); }); } @@ -114,3 +143,58 @@ fn run() -> i32 { fn main() { process::exit(run()); } + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn wrong_crc() { + 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; + send(handle, &report).unwrap(); + thread::sleep(time::Duration::from_millis(SEND_RECV_DELAY_MS)); + + let new_report = receive::(handle).unwrap(); + assert!(new_report.is_valid()); + + let response: &nitrokey::Response = new_report.data + .as_ref(); + 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() { + nitrokey_do(&|handle| { + let payload = nitrokey::DeviceStatusCommand::new(); + let report = nitrokey::Report::from(payload); + + send(handle, &report).unwrap(); + thread::sleep(time::Duration::from_millis(SEND_RECV_DELAY_MS)); + + let new_report = receive::(handle).unwrap(); + assert!(new_report.is_valid()); + + let response: &nitrokey::Response = new_report.data.as_ref(); + + 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(); + } +} -- cgit v1.2.1