aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO.md1
-rw-r--r--src/device.rs41
-rw-r--r--src/tests/device.rs39
3 files changed, 80 insertions, 1 deletions
diff --git a/TODO.md b/TODO.md
index dba39f8..c2096f7 100644
--- a/TODO.md
+++ b/TODO.md
@@ -3,7 +3,6 @@
- `NK_lock_device`
- `NK_factory_reset`
- `NK_build_aes_key`
- - `NK_unlock_user_password`
- `NK_is_AES_supported`
- `NK_send_startup`
- `NK_unlock_encrypted_volume`
diff --git a/src/device.rs b/src/device.rs
index 73952cc..d6b9780 100644
--- a/src/device.rs
+++ b/src/device.rs
@@ -367,6 +367,47 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp {
))
}
}
+
+ /// Unlocks the user PIN after three failed login attempts and sets it to the given value.
+ ///
+ /// # Errors
+ ///
+ /// - [`InvalidString`][] if one of the provided passwords contains a null byte
+ /// - [`WrongPassword`][] if the admin password is wrong
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use nitrokey::{CommandStatus, Device};
+ /// # use nitrokey::CommandError;
+ ///
+ /// # fn try_main() -> Result<(), CommandError> {
+ /// let device = nitrokey::connect()?;
+ /// match device.unlock_user_pin("12345678", "123456") {
+ /// CommandStatus::Success => println!("Unlocked user PIN."),
+ /// CommandStatus::Error(err) => println!("Failed to unlock user PIN: {:?}", err),
+ /// };
+ /// # Ok(())
+ /// # }
+ /// ```
+ ///
+ /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString
+ /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword
+ fn unlock_user_pin(&self, admin_pin: &str, user_pin: &str) -> CommandStatus {
+ let admin_pin_string = CString::new(admin_pin);
+ let user_pin_string = CString::new(user_pin);
+ if admin_pin_string.is_err() || user_pin_string.is_err() {
+ return CommandStatus::Error(CommandError::InvalidString);
+ }
+ let admin_pin_string = admin_pin_string.unwrap();
+ let user_pin_string = user_pin_string.unwrap();
+ unsafe {
+ CommandStatus::from(nitrokey_sys::NK_unlock_user_password(
+ admin_pin_string.as_ptr(),
+ user_pin_string.as_ptr(),
+ ))
+ }
+ }
}
/// Connects to a Nitrokey device. This method can be used to connect to any connected device,
diff --git a/src/tests/device.rs b/src/tests/device.rs
index 6c24025..6f88bf5 100644
--- a/src/tests/device.rs
+++ b/src/tests/device.rs
@@ -189,3 +189,42 @@ fn change_admin_pin() {
let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device();
device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err();
}
+
+fn require_failed_user_login(device: Target, password: &str, error: CommandError) -> Target {
+ let result = device.authenticate_user(password);
+ assert!(result.is_err());
+ let err = result.unwrap_err();
+ assert_eq!(error, err.1);
+ err.0
+}
+
+#[test]
+#[cfg_attr(not(any(feature = "test-pro", feature = "test-storage")), ignore)]
+fn unlock_user_pin() {
+ let device = Target::connect().unwrap();
+ let device = device.authenticate_user(USER_PASSWORD).unwrap().device();
+ assert_eq!(
+ CommandStatus::Success,
+ device.unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD)
+ );
+ assert_eq!(
+ CommandStatus::Error(CommandError::WrongPassword),
+ device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD)
+ );
+
+ let wrong_password = USER_PASSWORD.to_owned() + "foo";
+ let device = require_failed_user_login(device, &wrong_password, CommandError::WrongPassword);
+ let device = require_failed_user_login(device, &wrong_password, CommandError::WrongPassword);
+ let device = require_failed_user_login(device, &wrong_password, CommandError::WrongPassword);
+ let device = require_failed_user_login(device, USER_PASSWORD, CommandError::WrongPassword);
+
+ assert_eq!(
+ CommandStatus::Error(CommandError::WrongPassword),
+ device.unlock_user_pin(USER_PASSWORD, USER_PASSWORD)
+ );
+ assert_eq!(
+ CommandStatus::Success,
+ device.unlock_user_pin(ADMIN_PASSWORD, USER_PASSWORD)
+ );
+ device.authenticate_user(USER_PASSWORD).unwrap();
+}