aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--TODO.md1
-rw-r--r--src/device.rs31
-rw-r--r--src/pws.rs6
-rw-r--r--tests/device.rs23
5 files changed, 61 insertions, 1 deletions
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
@@ -347,6 +347,29 @@ fn factory_reset() {
}
#[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() {
let device = Storage::connect().unwrap();