aboutsummaryrefslogtreecommitdiff
path: root/nitrocli/src/main.rs
diff options
context:
space:
mode:
authorDaniel Mueller <deso@posteo.net>2017-03-30 21:25:59 -0700
committerDaniel Mueller <deso@posteo.net>2017-03-30 21:25:59 -0700
commit02d31ed3dc89528f5b46201abb0c03b6ef29cc16 (patch)
tree09db53fefb0729ae633c5b9ecada8ecfba965a69 /nitrocli/src/main.rs
parentc3b61ebe688e6e353b0fcd737f76851f78b8c74f (diff)
downloadnitrocli-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.rs88
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();
+ }
+}