diff options
Diffstat (limited to 'nitrocli/src/main.rs')
-rw-r--r-- | nitrocli/src/main.rs | 237 |
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(); - } -} |