summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2018-06-07 00:22:45 +0200
committerRobin Krahl <robin.krahl@ireas.org>2018-06-07 00:22:45 +0200
commit22e378677d5b00a05c021dc6660651608b384e0d (patch)
tree1f079527e98c36f2d61d8cf2964a7256a0f48dce
parent2739188166445bb51091cfa33e6bd1ec0938de3f (diff)
downloadnitrokey-rs-22e378677d5b00a05c021dc6660651608b384e0d.tar.gz
nitrokey-rs-22e378677d5b00a05c021dc6660651608b384e0d.tar.bz2
Add support for encrypted volume
This patch adds support for the commands to enable or disable the encrypted volume on the Nitrokey Storage. To test these commands, the output of lsblk is parsed for the device model “Nitrokey Storage”. This is not perfect but seems to be the best solution for automated testing. As the effect of enabling and disabling volumes is not immediate, a delay of two seconds is added to the tests before checking lsblk. This is sufficient on my machine, yet it would be better to have a portable version of this check. This patch also adds a lock method to Device that executes the lock_device command. This command was previously only used to close the password safe. On the Nitrokey Storage, it also disables the encrypted and hidden volume.
-rw-r--r--TODO.md3
-rw-r--r--src/device.rs99
-rw-r--r--src/tests/device.rs50
3 files changed, 150 insertions, 2 deletions
diff --git a/TODO.md b/TODO.md
index 992b501..e2fb3fe 100644
--- a/TODO.md
+++ b/TODO.md
@@ -4,8 +4,6 @@
- `NK_build_aes_key`
- `NK_is_AES_supported`
- `NK_send_startup`
- - `NK_unlock_encrypted_volume`
- - `NK_lock_encrypted_volume`
- `NK_unlock_hidden_volume`
- `NK_lock_hidden_volume`
- `NK_create_hidden_volume`
@@ -39,3 +37,4 @@
- Consider implementing `Into<CommandError>` for `(Device, CommandError)`
- Check error handling in PasswordSafe::drop().
- Disable creation of multiple password safes at the same time.
+- Check timing in Storage tests.
diff --git a/src/device.rs b/src/device.rs
index d6b9780..f901306 100644
--- a/src/device.rs
+++ b/src/device.rs
@@ -408,6 +408,30 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
))
}
}
+
+ /// Locks the Nitrokey device.
+ ///
+ /// This disables the password store if it has been unlocked. On the Nitrokey Storage, this
+ /// also disables the volumes if they have been enabled.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::{CommandStatus, Device};
+ /// # use nitrokey::CommandError;
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::connect()?;
+ /// match device.lock() {
+ /// CommandStatus::Success => println!("Locked the Nitrokey device."),
+ /// CommandStatus::Error(err) => println!("Could not lock the Nitrokey device: {:?}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ fn lock(&self) -> CommandStatus {
+ unsafe { CommandStatus::from(nitrokey_sys::NK_lock_device()) }
+ }
}
/// Connects to a Nitrokey device. This method can be used to connect to any connected device,
@@ -509,6 +533,81 @@ impl Storage {
false => Err(CommandError::Unknown),
}
}
+
+ /// Enables the encrypted storage volume.
+ ///
+ /// Once the encrypted volume is enabled, it is presented to the operating system as a block
+ /// device. The API does not provide any information on the name or path of this block device.
+ ///
+ /// # Errors
+ ///
+ /// - [`InvalidString`][] if the provided password contains a null byte
+ /// - [`WrongPassword`][] if the provided user password is wrong
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::{CommandStatus};
+ /// # use nitrokey::CommandError;
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::Storage::connect()?;
+ /// match device.enable_encrypted_volume("123456") {
+ /// CommandStatus::Success => println!("Enabled the encrypted volume."),
+ /// CommandStatus::Error(err) => println!("Could not enable the encrypted volume: {:?}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString
+ /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword
+ pub fn enable_encrypted_volume(&self, user_pin: &str) -> CommandStatus {
+ let user_pin = CString::new(user_pin);
+ if user_pin.is_err() {
+ return CommandStatus::Error(CommandError::InvalidString);
+ }
+ let user_pin = user_pin.unwrap();
+ unsafe { CommandStatus::from(nitrokey_sys::NK_unlock_encrypted_volume(user_pin.as_ptr())) }
+ }
+
+ /// Disables the encrypted storage volume.
+ ///
+ /// Once the volume is disabled, it can be no longer accessed as a block device. If the
+ /// encrypted volume has not been enabled, this method still returns a success.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::{CommandStatus};
+ /// # use nitrokey::CommandError;
+ ///
+ /// fn use_volume() {}
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::Storage::connect()?;
+ /// match device.enable_encrypted_volume("123456") {
+ /// CommandStatus::Success => {
+ /// println!("Enabled the encrypted volume.");
+ /// use_volume();
+ /// match device.disable_encrypted_volume() {
+ /// CommandStatus::Success => println!("Disabled the encrypted volume."),
+ /// CommandStatus::Err(err) => {
+ /// println!("Could not disable the encrypted volume: {:?}", err);
+ /// },
+ /// };
+ /// },
+ /// CommandStatus::Error(err) => println!("Could not enable the encrypted volume: {:?}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString
+ /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword
+ pub fn disable_encrypted_volume(&self) -> CommandStatus {
+ unsafe { CommandStatus::from(nitrokey_sys::NK_lock_encrypted_volume()) }
+ }
}
impl Drop for Storage {
diff --git a/src/tests/device.rs b/src/tests/device.rs
index 68f1a39..7f7a819 100644
--- a/src/tests/device.rs
+++ b/src/tests/device.rs
@@ -1,10 +1,24 @@
use std::ffi::CStr;
+use std::process::Command;
+use std::{thread, time};
use tests::util::{Target, ADMIN_PASSWORD, USER_PASSWORD};
use {Authenticate, CommandError, CommandStatus, Config, Device};
static ADMIN_NEW_PASSWORD: &str = "1234567890";
static USER_NEW_PASSWORD: &str = "abcdefghij";
+fn count_nitrokey_block_devices() -> usize {
+ thread::sleep(time::Duration::from_secs(2));
+ let output = Command::new("lsblk")
+ .args(&["-o", "MODEL"])
+ .output()
+ .expect("Could not list block devices");
+ String::from_utf8_lossy(&output.stdout)
+ .split("\n")
+ .filter(|&s| s == "Nitrokey Storage")
+ .count()
+}
+
#[test]
#[cfg_attr(not(feature = "test-no-device"), ignore)]
fn connect_no_device() {
@@ -227,3 +241,39 @@ fn unlock_user_pin() {
);
device.authenticate_user(USER_PASSWORD).unwrap();
}
+
+#[test]
+#[cfg_attr(not(feature = "test-storage"), ignore)]
+fn encrypted_volume() {
+ let device = Target::connect().unwrap();
+ assert_eq!(CommandStatus::Success, device.lock());
+
+ assert_eq!(1, count_nitrokey_block_devices());
+ assert_eq!(CommandStatus::Success, device.disable_encrypted_volume());
+ assert_eq!(1, count_nitrokey_block_devices());
+ assert_eq!(
+ CommandStatus::Error(CommandError::WrongPassword),
+ device.enable_encrypted_volume("123")
+ );
+ assert_eq!(1, count_nitrokey_block_devices());
+ assert_eq!(
+ CommandStatus::Success,
+ device.enable_encrypted_volume(USER_PASSWORD)
+ );
+ assert_eq!(2, count_nitrokey_block_devices());
+ assert_eq!(CommandStatus::Success, device.disable_encrypted_volume());
+ assert_eq!(1, count_nitrokey_block_devices());
+}
+
+#[test]
+#[cfg_attr(not(feature = "test-storage"), ignore)]
+fn lock() {
+ let device = Target::connect().unwrap();
+
+ assert_eq!(
+ CommandStatus::Success,
+ device.enable_encrypted_volume(USER_PASSWORD)
+ );
+ assert_eq!(CommandStatus::Success, device.lock());
+ assert_eq!(1, count_nitrokey_block_devices());
+}