diff options
-rw-r--r-- | CHANGELOG.md | 3 | ||||
-rw-r--r-- | TODO.md | 3 | ||||
-rw-r--r-- | src/device.rs | 134 | ||||
-rw-r--r-- | tests/device.rs | 38 |
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`. @@ -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()); |