aboutsummaryrefslogtreecommitdiff
path: root/nitrocli/src/main.rs
diff options
context:
space:
mode:
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();
- }
-}