diff options
-rw-r--r-- | TODO.md | 1 | ||||
-rw-r--r-- | src/device.rs | 41 | ||||
-rw-r--r-- | src/tests/device.rs | 39 |
3 files changed, 80 insertions, 1 deletions
@@ -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(); +} |