diff options
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | TODO.md | 2 | ||||
| -rw-r--r-- | src/device/storage.rs | 53 | ||||
| -rw-r--r-- | tests/device.rs | 39 | 
5 files changed, 95 insertions, 4 deletions
| diff --git a/CHANGELOG.md b/CHANGELOG.md index 419a942..06836d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ SPDX-License-Identifier: CC0-1.0  - Add the `get_sd_card_usage` function to the `Storage` struct.  - Add the `OperationStatus` enum and the `get_operation_status` function for    the `Storage` struct. +- Add the `fill_sd_card` function to the `Storage` struct.  # v0.4.0 (2020-01-02)  - Remove the `test-pro` and `test-storage` features. @@ -64,6 +64,10 @@ an AES key has been built.  Some tests will overwrite the data stored on the  Nitrokey device or perform a factory reset.  Never execute the tests if you  don’t want to destroy all data on any connected Nitrokey device! +The test suite contains some test that take very long to execute, for example +filling the SD card of a Nitrokey Storage with random data.  These tests are +ignored per default.  Use `cargo test -- --ignored` to execute the tests. +  ## Acknowledgments  Thanks to Nitrokey UG for providing two Nitrokey devices to support the @@ -3,8 +3,6 @@ Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org>  SPDX-License-Identifier: CC0-1.0  --> -- Add support for the currently unsupported commands: -    - `NK_fill_SD_card_with_random_data`  - Clear passwords from memory.  - Lock password safe in `PasswordSafe::drop()` (see [nitrokey-storage-firmware    issue 65][]). diff --git a/src/device/storage.rs b/src/device/storage.rs index 8d45763..deb2844 100644 --- a/src/device/storage.rs +++ b/src/device/storage.rs @@ -8,7 +8,7 @@ use std::ops;  use nitrokey_sys;  use crate::device::{Device, FirmwareVersion, Model, Status}; -use crate::error::Error; +use crate::error::{CommandError, Error};  use crate::otp::GenerateOtp;  use crate::util::{get_command_result, get_cstring, get_last_error}; @@ -700,7 +700,9 @@ impl<'a> Storage<'a> {      /// Some commands may start a background operation during which no other commands can be      /// executed.  This method can be used to check whether such an operation is ongoing.      /// -    /// Currently, this is only used by the `fill_sd_card` method. +    /// Currently, this is only used by the [`fill_sd_card`][] method. +    /// +    /// [`fill_sd_card`]: #method.fill_sd_card      pub fn get_operation_status(&self) -> Result<OperationStatus, Error> {          let status = unsafe { nitrokey_sys::NK_get_progress_bar_value() };          match status { @@ -713,6 +715,53 @@ impl<'a> Storage<'a> {          }      } +    /// Overwrites the SD card with random data. +    /// +    /// Ths method starts a background operation that overwrites the SD card with random data. +    /// While this operation is ongoing, no other commands can be executed.  Use the +    /// [`get_operation_status`][] function to check the progress of the operation. +    /// +    /// # Errors +    /// +    /// - [`InvalidString`][] if one of the provided passwords contains a null byte +    /// - [`WrongPassword`][] if the admin password is wrong +    /// +    /// # Example +    /// +    /// ```no_run +    /// use nitrokey::OperationStatus; +    /// +    /// let mut manager = nitrokey::take()?; +    /// let mut storage = manager.connect_storage()?; +    /// storage.fill_sd_card("12345678")?; +    /// loop { +    ///     match storage.get_operation_status()? { +    ///         OperationStatus::Ongoing(progress) => println!("{}/100", progress), +    ///         OperationStatus::Idle => { +    ///             println!("Done!"); +    ///             break; +    ///         } +    ///     } +    /// } +    /// # Ok::<(), nitrokey::Error>(()) +    /// ``` +    /// +    /// [`get_operation_status`]: #method.get_operation_status +    /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString +    /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword +    pub fn fill_sd_card(&mut self, admin_pin: &str) -> Result<(), Error> { +        let admin_pin_string = get_cstring(admin_pin)?; +        get_command_result(unsafe { +            nitrokey_sys::NK_fill_SD_card_with_random_data(admin_pin_string.as_ptr()) +        }) +        .or_else(|err| match err { +            // libnitrokey’s C API returns a LongOperationInProgressException with the same error +            // code as the WrongCrc command error, so we cannot distinguish them. +            Error::CommandError(CommandError::WrongCrc) => Ok(()), +            err => Err(err), +        }) +    } +      /// Exports the firmware to the unencrypted volume.      ///      /// This command requires the admin PIN.  The unencrypted volume must be in read-write mode diff --git a/tests/device.rs b/tests/device.rs index 7296372..a88c956 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -653,6 +653,45 @@ fn get_operation_status(device: Storage) {  }  #[test_device] +#[ignore] +fn fill_sd_card(device: Storage) { +    // This test takes up to 60 min to execute and is therefore ignored by default.  Use `cargo +    // test -- --ignored fill_sd_card` to run the test. + +    let mut device = device; +    assert_ok!((), device.factory_reset(DEFAULT_ADMIN_PIN)); +    thread::sleep(time::Duration::from_secs(3)); +    assert_ok!((), device.build_aes_key(DEFAULT_ADMIN_PIN)); + +    let status = unwrap_ok!(device.get_storage_status()); +    assert!(!status.filled_with_random); + +    assert_ok!((), device.fill_sd_card(DEFAULT_ADMIN_PIN)); +    assert_cmd_err!(CommandError::WrongCrc, device.get_status()); + +    let mut status = OperationStatus::Ongoing(0); +    let mut last_progress = 0u8; +    while status != OperationStatus::Idle { +        status = unwrap_ok!(device.get_operation_status()); +        if let OperationStatus::Ongoing(progress) = status { +            assert!(progress <= 100, "progress = {}", progress); +            assert!( +                progress >= last_progress, +                "progress = {}, last_progress = {}", +                progress, +                last_progress +            ); +            last_progress = progress; + +            thread::sleep(time::Duration::from_secs(10)); +        } +    } + +    let status = unwrap_ok!(device.get_storage_status()); +    assert!(status.filled_with_random); +} + +#[test_device]  fn export_firmware(device: Storage) {      let mut device = device;      assert_cmd_err!( | 
