diff options
author | Daniel Mueller <deso@posteo.net> | 2017-03-30 21:25:59 -0700 |
---|---|---|
committer | Daniel Mueller <deso@posteo.net> | 2017-03-30 21:25:59 -0700 |
commit | 02d31ed3dc89528f5b46201abb0c03b6ef29cc16 (patch) | |
tree | 09db53fefb0729ae633c5b9ecada8ecfba965a69 /nitrocli/src/main.rs | |
parent | c3b61ebe688e6e353b0fcd737f76851f78b8c74f (diff) | |
download | nitrocli-02d31ed3dc89528f5b46201abb0c03b6ef29cc16.tar.gz nitrocli-02d31ed3dc89528f5b46201abb0c03b6ef29cc16.tar.bz2 |
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.
Diffstat (limited to 'nitrocli/src/main.rs')
-rw-r--r-- | nitrocli/src/main.rs | 88 |
1 files changed, 86 insertions, 2 deletions
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<T> = result::Result<T, Error>; 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<P>(handle: &mut libhid::Handle, report: &nitrokey::Report<P>) -> 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<P>(handle: &mut libhid::Handle) -> Result<nitrokey::Report<P>> + where P: AsRef<[u8]> + Default, +{ + let mut report = nitrokey::Report::<P>::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::<nitrokey::EmptyPayload>(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::<nitrokey::EmptyPayload>(handle).unwrap(); + assert!(new_report.is_valid()); + + let response: &nitrokey::Response<nitrokey::DeviceStatusResponse> = 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::<nitrokey::EmptyPayload>(handle).unwrap(); + assert!(new_report.is_valid()); + + let response: &nitrokey::Response<nitrokey::DeviceStatusResponse> = 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(); + } +} |