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());  | 
