aboutsummaryrefslogtreecommitdiff
path: root/nitrocli/src/main.rs
diff options
context:
space:
mode:
authorRobin Krahl <me@robin-krahl.de>2018-12-11 23:51:01 +0100
committerDaniel Mueller <deso@posteo.net>2018-12-17 07:52:13 -0800
commit4d314264a897c474c12e626a2be36b75dc57f5c9 (patch)
tree11385b55f271a2b95d2d632ad1a2107180be5c3a /nitrocli/src/main.rs
parent986ad2f782cf944990e4eda8bf88ea1821233302 (diff)
downloadnitrocli-4d314264a897c474c12e626a2be36b75dc57f5c9.tar.gz
nitrocli-4d314264a897c474c12e626a2be36b75dc57f5c9.tar.bz2
Port the open and close commands to libnitrokey
This patch removes the raw hidapi implementations of the Enable Encrypted Volume and Disable Encrypted Volume commands and replaces them with the methods enable_encrypted_volume and disable_encrypted_volume of the Storage struct provided by the nitrokey trait. To provide some context to the error messages, the errors are wrapped using the map_err method of the Result enum and the get_error function that combines a nitrokey error code and a string into a nitrocli error. It would be more idiomatic to define a conversion from a nitrokey error to a nitrocli error, but then we would lose information about the context of the error.
Diffstat (limited to 'nitrocli/src/main.rs')
-rw-r--r--nitrocli/src/main.rs130
1 files changed, 49 insertions, 81 deletions
diff --git a/nitrocli/src/main.rs b/nitrocli/src/main.rs
index 23370ee..714c573 100644
--- a/nitrocli/src/main.rs
+++ b/nitrocli/src/main.rs
@@ -79,6 +79,8 @@ use std::result;
use std::thread;
use std::time;
+use libnitrokey;
+
use crate::error::Error;
type Result<T> = result::Result<T, Error>;
@@ -91,6 +93,19 @@ 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`.
+fn get_error(msg: &str, err: &libnitrokey::CommandError) -> Error {
+ Error::Error(format!("{}: {:?}", msg, err))
+}
+
+
+/// Connect to a Nitrokey Storage device and return it.
+fn get_storage_device() -> Result<libnitrokey::Storage> {
+ libnitrokey::Storage::connect()
+ .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]>,
@@ -272,64 +287,35 @@ fn status() -> Result<()> {
}
-/// Poll the nitrokey until it reports to no longer be busy.
-fn wait(handle: &mut libhid::Handle) -> Result<nitrokey::StorageStatus> {
- type Response = nitrokey::Response<nitrokey::StorageResponse>;
-
- loop {
- thread::sleep(time::Duration::from_millis(SEND_RECV_DELAY_MS));
-
- let report = receive::<nitrokey::EmptyPayload>(handle)?;
- let response = AsRef::<Response>::as_ref(&report.data);
- let status = response.data.storage_status;
-
- if status != nitrokey::StorageStatus::Busy &&
- status != nitrokey::StorageStatus::BusyProgressbar {
- return Ok(status);
- }
- }
-}
-
-
/// Open the encrypted volume on the nitrokey.
fn open() -> Result<()> {
- type Response = nitrokey::Response<nitrokey::StorageResponse>;
-
- nitrokey_do(&|handle| {
- let mut retry = 3;
- let mut error_msg: Option<&str> = None;
- loop {
- let passphrase = pinentry::inquire_passphrase(PIN_TYPE, error_msg)?;
- let payload = nitrokey::EnableEncryptedVolumeCommand::new(&passphrase);
- let report = nitrokey::Report::from(payload);
+ let device = get_storage_device()?;
- let report = transmit::<_, nitrokey::EmptyPayload>(handle, &report)?;
- let response = AsRef::<Response>::as_ref(&report.data);
- let mut status = response.data.storage_status;
-
- if status == nitrokey::StorageStatus::WrongPassword {
- pinentry::clear_passphrase(PIN_TYPE)?;
- retry -= 1;
+ let mut retry = 3;
+ let mut error_msg: Option<&str> = None;
+ loop {
+ // TODO: Rethink the usage of String::from_utf8_lossy here. We may
+ // not want to silently modify the password!
+ let passphrase = pinentry::inquire_passphrase(PIN_TYPE, error_msg)?;
+ let passphrase = String::from_utf8_lossy(&passphrase);
+ match device.enable_encrypted_volume(&passphrase) {
+ Ok(()) => return Ok(()),
+ Err(err) => match err {
+ libnitrokey::CommandError::WrongPassword => {
+ pinentry::clear_passphrase(PIN_TYPE)?;
+ retry -= 1;
- if retry > 0 {
- error_msg = Some("Wrong password, please reenter");
- continue;
- }
- let error = "Opening encrypted volume failed: Wrong password";
- return Err(Error::Error(error.to_string()));
- }
- if status == nitrokey::StorageStatus::Busy ||
- status == nitrokey::StorageStatus::BusyProgressbar {
- status = wait(handle)?;
- }
- if status != nitrokey::StorageStatus::Okay && status != nitrokey::StorageStatus::Idle {
- let status = format!("{:?}", status);
- let error = format!("Opening encrypted volume failed: {}", status);
- return Err(Error::Error(error));
- }
- return Ok(());
- }
- })
+ if retry > 0 {
+ error_msg = Some("Wrong password, please reenter");
+ continue;
+ }
+ let error = "Opening encrypted volume failed: Wrong password";
+ return Err(Error::Error(error.to_string()));
+ },
+ err => return Err(get_error("Opening encrypted volume failed", &err)),
+ },
+ };
+ }
}
@@ -340,33 +326,15 @@ extern "C" {
/// Close the previously opened encrypted volume.
fn close() -> Result<()> {
- type Response = nitrokey::Response<nitrokey::StorageResponse>;
-
- nitrokey_do(&|handle| {
- // Flush all filesystem caches to disk. We are mostly interested in
- // making sure that the encrypted volume on the nitrokey we are
- // about to close is not closed while not all data was written to
- // it.
- unsafe { sync() };
-
- let payload = nitrokey::DisableEncryptedVolumeCommand::new();
- let report = nitrokey::Report::from(payload);
-
- let report = transmit::<_, nitrokey::EmptyPayload>(handle, &report)?;
- let response = AsRef::<Response>::as_ref(&report.data);
- let mut status = response.data.storage_status;
-
- if status == nitrokey::StorageStatus::Busy ||
- status == nitrokey::StorageStatus::BusyProgressbar {
- status = wait(handle)?;
- }
- if status != nitrokey::StorageStatus::Okay && status != nitrokey::StorageStatus::Idle {
- let status = format!("{:?}", status);
- let error = format!("Closing encrypted volume failed: {}", status);
- return Err(Error::Error(error));
- }
- Ok(())
- })
+ // Flush all filesystem caches to disk. We are mostly interested in
+ // making sure that the encrypted volume on the nitrokey we are
+ // about to close is not closed while not all data was written to
+ // it.
+ unsafe { sync() };
+
+ get_storage_device()?
+ .disable_encrypted_volume()
+ .map_err(|err| get_error("Closing encrypted volume failed", &err))
}