summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md3
-rw-r--r--TODO.md3
-rw-r--r--src/device.rs134
-rw-r--r--tests/device.rs38
4 files changed, 175 insertions, 3 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4e2efb1..7394cb0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,8 @@
# Unreleased
- Use `nitrokey-test` to select and execute the unit tests.
+- Add support for the hidden volumes on a Nitrokey Storage
+ (`enable_hidden_volume`, `disable_hidden_volume` and `create_hidden_volume`
+ methods for the `Storage` struct).
# v0.3.0 (2019-01-04)
- Add a `force` argument to `ConfigureOtp::set_time`.
diff --git a/TODO.md b/TODO.md
index 111105d..f839dc3 100644
--- a/TODO.md
+++ b/TODO.md
@@ -2,9 +2,6 @@
- `NK_set_unencrypted_volume_rorw_pin_type_user`
- `NK_is_AES_supported`
- `NK_send_startup`
- - `NK_unlock_hidden_volume`
- - `NK_lock_hidden_volume`
- - `NK_create_hidden_volume`
- `NK_set_unencrypted_read_only`
- `NK_set_unencrypted_read_only_admin`
- `NK_set_unencrypted_read_write`
diff --git a/src/device.rs b/src/device.rs
index 78d0d82..31e14a9 100644
--- a/src/device.rs
+++ b/src/device.rs
@@ -878,6 +878,140 @@ impl Storage {
unsafe { get_command_result(nitrokey_sys::NK_lock_encrypted_volume()) }
}
+ /// Enables a hidden storage volume.
+ ///
+ /// This function will only succeed if the encrypted storage ([`enable_encrypted_volume`][]) or
+ /// another hidden volume has been enabled previously. Once the hidden volume is enabled, it
+ /// is presented to the operating system as a block device and any previously opened encrypted
+ /// or hidden volumes are closed. The API does not provide any information on the name or path
+ /// of this block device.
+ ///
+ /// Note that the encrypted and the hidden volumes operate on the same storage area, so using
+ /// both at the same time might lead to data loss.
+ ///
+ /// The hidden volume to unlock is selected based on the provided password.
+ ///
+ /// # Errors
+ ///
+ /// - [`AesDecryptionFailed`][] if the encrypted storage has not been opened before calling
+ /// this method or the AES key has not been built
+ /// - [`InvalidString`][] if the provided password contains a null byte
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// # use nitrokey::CommandError;
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::Storage::connect()?;
+ /// device.enable_encrypted_volume("123445")?;
+ /// match device.enable_hidden_volume("hidden-pw") {
+ /// Ok(()) => println!("Enabled a hidden volume."),
+ /// Err(err) => println!("Could not enable the hidden volume: {}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`enable_encrypted_volume`]: #method.enable_encrypted_volume
+ /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed
+ /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString
+ pub fn enable_hidden_volume(&self, volume_password: &str) -> Result<(), CommandError> {
+ let volume_password = get_cstring(volume_password)?;
+ unsafe {
+ get_command_result(nitrokey_sys::NK_unlock_hidden_volume(
+ volume_password.as_ptr(),
+ ))
+ }
+ }
+
+ /// Disables a hidden storage volume.
+ ///
+ /// Once the volume is disabled, it can be no longer accessed as a block device. If no hidden
+ /// volume has been enabled, this method still returns a success.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// # use nitrokey::CommandError;
+ ///
+ /// fn use_volume() {}
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::Storage::connect()?;
+ /// device.enable_encrypted_volume("123445")?;
+ /// match device.enable_hidden_volume("hidden-pw") {
+ /// Ok(()) => {
+ /// println!("Enabled the hidden volume.");
+ /// use_volume();
+ /// match device.disable_hidden_volume() {
+ /// Ok(()) => println!("Disabled the hidden volume."),
+ /// Err(err) => {
+ /// println!("Could not disable the hidden volume: {}", err);
+ /// },
+ /// };
+ /// },
+ /// Err(err) => println!("Could not enable the hidden volume: {}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn disable_hidden_volume(&self) -> Result<(), CommandError> {
+ unsafe { get_command_result(nitrokey_sys::NK_lock_hidden_volume()) }
+ }
+
+ /// Creates a hidden volume.
+ ///
+ /// The volume is crated in the given slot and in the given range of the available memory,
+ /// where `start` is the start position as a percentage of the available memory, and `end` is
+ /// the end position as a percentage of the available memory. The volume will be protected by
+ /// the given password.
+ ///
+ /// Note that the encrypted and the hidden volumes operate on the same storage area, so using
+ /// both at the same time might lead to data loss.
+ ///
+ /// According to the libnitrokey documentation, this function only works if the encrypted
+ /// storage has been opened.
+ ///
+ /// # Errors
+ ///
+ /// - [`AesDecryptionFailed`][] if the encrypted storage has not been opened before calling
+ /// this method or the AES key has not been built
+ /// - [`InvalidString`][] if the provided password contains a null byte
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// # use nitrokey::CommandError;
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::Storage::connect()?;
+ /// device.enable_encrypted_volume("123445")?;
+ /// device.create_hidden_volume(0, 0, 100, "hidden-pw")?;
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed
+ /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString
+ pub fn create_hidden_volume(
+ &self,
+ slot: u8,
+ start: u8,
+ end: u8,
+ password: &str,
+ ) -> Result<(), CommandError> {
+ let password = get_cstring(password)?;
+ unsafe {
+ get_command_result(nitrokey_sys::NK_create_hidden_volume(
+ slot,
+ start,
+ end,
+ password.as_ptr(),
+ ))
+ }
+ }
+
/// Returns the status of the connected storage device.
///
/// # Example
diff --git a/tests/device.rs b/tests/device.rs
index d6ed0c4..f7d8df3 100644
--- a/tests/device.rs
+++ b/tests/device.rs
@@ -349,6 +349,44 @@ fn encrypted_volume(device: Storage) {
}
#[test_device]
+fn hidden_volume(device: Storage) {
+ assert_eq!(Ok(()), device.lock());
+
+ assert_eq!(1, count_nitrokey_block_devices());
+ assert_eq!(Ok(()), device.disable_hidden_volume());
+ assert_eq!(1, count_nitrokey_block_devices());
+
+ assert_eq!(Ok(()), device.enable_encrypted_volume(USER_PASSWORD));
+ assert_eq!(2, count_nitrokey_block_devices());
+
+ // TODO: why this error code?
+ assert_eq!(
+ Err(CommandError::WrongPassword),
+ device.create_hidden_volume(5, 0, 100, "hiddenpw")
+ );
+ assert_eq!(Ok(()), device.create_hidden_volume(0, 20, 21, "hidden-pw"));
+ assert_eq!(
+ Ok(()),
+ device.create_hidden_volume(0, 20, 21, "hiddenpassword")
+ );
+ assert_eq!(Ok(()), device.create_hidden_volume(1, 0, 1, "otherpw"));
+ // TODO: test invalid range (not handled by libnitrokey)
+ assert_eq!(2, count_nitrokey_block_devices());
+
+ assert_eq!(
+ Err(CommandError::WrongPassword),
+ device.enable_hidden_volume("blubb")
+ );
+ assert_eq!(Ok(()), device.enable_hidden_volume("hiddenpassword"));
+ assert_eq!(2, count_nitrokey_block_devices());
+ assert_eq!(Ok(()), device.enable_hidden_volume("otherpw"));
+ assert_eq!(2, count_nitrokey_block_devices());
+
+ assert_eq!(Ok(()), device.disable_hidden_volume());
+ assert_eq!(1, count_nitrokey_block_devices());
+}
+
+#[test_device]
fn lock(device: Storage) {
assert_eq!(Ok(()), device.enable_encrypted_volume(USER_PASSWORD));
assert_eq!(Ok(()), device.lock());