From 616f84c13a4e676d3e2f870533fb1b8778c5f614 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 3 Jan 2019 17:04:50 +0000 Subject: Add Device::build_aes_key method This patch adds the build_aes_key method to the Device trait that uses the NK_build_aes_key function to build new AES keys on the device. This effectively resets the password safe and the encrypted storage. It is unclear whether other data (e. g. the one-time passwords) are affected too. --- CHANGELOG.md | 1 + TODO.md | 1 - src/device.rs | 31 +++++++++++++++++++++++++++++++ src/pws.rs | 6 ++++++ tests/device.rs | 23 +++++++++++++++++++++++ 5 files changed, 61 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ecaef3..2551350 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Add the `Storage::change_update_pin` method that changes the firmware update PIN. - Add the `Device::factory_reset` method that performs a factory reset. +- Add the `Device::build_aes_key` method that builds a new AES key on the Nitrokey. # v0.2.3 (2018-12-31) diff --git a/TODO.md b/TODO.md index 5bcfa68..9555747 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,5 @@ - Add support for the currently unsupported commands: - `NK_set_unencrypted_volume_rorw_pin_type_user` - - `NK_build_aes_key` - `NK_is_AES_supported` - `NK_send_startup` - `NK_unlock_hidden_volume` diff --git a/src/device.rs b/src/device.rs index 8702405..bc48cd2 100644 --- a/src/device.rs +++ b/src/device.rs @@ -540,6 +540,37 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { let admin_pin_string = get_cstring(admin_pin)?; unsafe { get_command_result(nitrokey_sys::NK_factory_reset(admin_pin_string.as_ptr())) } } + + /// Builds a new AES key on the Nitrokey. + /// + /// The AES key is used to encrypt the password safe and the encrypted volume. You may need + /// to call this method after a factory reset using `gpg --card-edit`. You can also use it to + /// destory the data stored in the password safe or on the encrypted volume. + /// + /// # Errors + /// + /// - [`InvalidString`][] if the provided password contains a null byte + /// - [`WrongPassword`][] if the admin password is wrong + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::Device; + /// # use nitrokey::CommandError; + /// + /// # fn try_main() -> Result<(), CommandError> { + /// let device = nitrokey::connect()?; + /// match device.build_aes_key("12345678") { + /// Ok(()) => println!("New AES keys have been built."), + /// Err(err) => println!("Could not build new AES keys: {}", err), + /// }; + /// # Ok(()) + /// # } + /// ``` + fn build_aes_key(&self, admin_pin: &str) -> Result<(), CommandError> { + let admin_pin_string = get_cstring(admin_pin)?; + unsafe { get_command_result(nitrokey_sys::NK_build_aes_key(admin_pin_string.as_ptr())) } + } } /// Connects to a Nitrokey device. This method can be used to connect to any connected device, diff --git a/src/pws.rs b/src/pws.rs index c20ad1d..ebd5fcd 100644 --- a/src/pws.rs +++ b/src/pws.rs @@ -71,6 +71,11 @@ pub trait GetPasswordSafe { /// has been used. Otherwise, other applications can access the password store without /// authentication. /// + /// If this method returns an `AesDecryptionFailed` (Nitrokey Pro) or `Unknown` (Nitrokey + /// Storage) error, the AES data object on the smart card could not be accessed. This problem + /// occurs after a factory reset using `gpg --card-edit` and can be fixed using the + /// [`Device::build_aes_key`][] command. + /// /// # Errors /// /// - [`AesDecryptionFailed`][] if the secret for the password safe could not be decrypted @@ -104,6 +109,7 @@ pub trait GetPasswordSafe { /// [`device`]: struct.PasswordSafe.html#method.device /// [`lock`]: trait.Device.html#method.lock /// [`AesDecryptionFailed`]: enum.CommandError.html#variant.AesDecryptionFailed + /// [`Device::build_aes_key`]: trait.Device.html#method.build_aes_key /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString /// [`Unknown`]: enum.CommandError.html#variant.Unknown /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword diff --git a/tests/device.rs b/tests/device.rs index 363b8d8..06e014e 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -346,6 +346,29 @@ fn factory_reset() { assert_ne!("testpw".to_string(), pws.get_slot_password(0).unwrap()); } +#[test] +#[cfg_attr(not(any(feature = "test-pro", feature = "test-storage")), ignore)] +fn build_aes_key() { + let device = Target::connect().unwrap(); + + let pws = device.get_password_safe(USER_PASSWORD).unwrap(); + assert_eq!(Ok(()), pws.write_slot(0, "test", "testlogin", "testpw")); + drop(pws); + + assert_eq!( + Err(CommandError::WrongPassword), + device.build_aes_key(USER_PASSWORD) + ); + assert_eq!(Ok(()), device.build_aes_key(ADMIN_PASSWORD)); + + let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); + + let pws = device.get_password_safe(USER_PASSWORD).unwrap(); + assert_ne!("test".to_string(), pws.get_slot_name(0).unwrap()); + assert_ne!("testlogin".to_string(), pws.get_slot_login(0).unwrap()); + assert_ne!("testpw".to_string(), pws.get_slot_password(0).unwrap()); +} + #[test] #[cfg_attr(not(feature = "test-storage"), ignore)] fn change_update_pin() { -- cgit v1.2.1