aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2019-01-06 22:56:27 +0000
committerRobin Krahl <robin.krahl@ireas.org>2019-01-06 23:59:54 +0100
commit30264131262d9e926d3c14b0c92760fdc15ba5c8 (patch)
treee31417548153619ae96faaed746ac3e644eeea0b
parent83063599f4ab1bcbbd9be9166e738a13ae4e4cc6 (diff)
downloadnitrokey-rs-30264131262d9e926d3c14b0c92760fdc15ba5c8.tar.gz
nitrokey-rs-30264131262d9e926d3c14b0c92760fdc15ba5c8.tar.bz2
Add support for the hidden volumes on a Nitrokey Storage
This patch introduces the methods enable_hidden_volume, disable_hidden_volume and create_hidden_volume for the Storage struct to support the hidden volumes on the Nitrokey Storage. The enable and create methods require that the encrypted storage has been enabled. Contrary to authentication and password safe access, we do not enforce this requirement in the API as file system operations could have unwanted side effects and should not performed implicitly.
-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());