From 57cab82dc3063cb6608aaf2ee937ad7af231c52a Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 21 May 2018 22:44:22 +0000 Subject: Add support for change_admin_pin and change_user_pin --- TODO.md | 2 -- src/lib.rs | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/tests/pro.rs | 48 +++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 3 deletions(-) diff --git a/TODO.md b/TODO.md index de33f8c..b9efdbd 100644 --- a/TODO.md +++ b/TODO.md @@ -5,8 +5,6 @@ - `NK_factory_reset` - `NK_build_aes_key` - `NK_unlock_user_password` - - `NK_change_admin_PIN` - - `NK_change_user_PIN` - `NK_enable_password_safe` - `NK_get_password_safe_slot_status` - `NK_get_password_safe_slot_name` diff --git a/src/lib.rs b/src/lib.rs index 8ffbf5a..817ee0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -211,7 +211,6 @@ struct RawConfig { pub user_password: bool, } -#[derive(Debug)] /// A Nitrokey device without user or admin authentication. /// /// Use [`connect`][] or [`connect_model`][] to obtain an instance. If you @@ -250,6 +249,7 @@ struct RawConfig { /// [`authenticate_user`]: #method.authenticate_user /// [`connect`]: fn.connect.html /// [`connect_model`]: fn.connect_model.html +#[derive(Debug)] pub struct UnauthenticatedDevice {} /// A Nitrokey device with user authentication. @@ -261,6 +261,7 @@ pub struct UnauthenticatedDevice {} /// [`authenticate_admin`]: struct.UnauthenticatedDevice#method.authenticate_admin /// [`device`]: #method.device /// [`UnauthenticatedDevice`]: struct.UnauthenticatedDevice.html +#[derive(Debug)] pub struct UserAuthenticatedDevice { device: UnauthenticatedDevice, temp_password: Vec, @@ -275,6 +276,7 @@ pub struct UserAuthenticatedDevice { /// [`authenticate_admin`]: struct.UnauthenticatedDevice#method.authenticate_admin /// [`device`]: #method.device /// [`UnauthenticatedDevice`]: struct.UnauthenticatedDevice.html +#[derive(Debug)] pub struct AdminAuthenticatedDevice { device: UnauthenticatedDevice, temp_password: Vec, @@ -634,6 +636,78 @@ pub trait Device { return result_from_string(nitrokey_sys::NK_get_totp_code(slot, 0, 0, 0)); } } + + /// Changes the administrator PIN. + /// + /// # Errors + /// + /// - [`InvalidString`][] if one of the provided passwords contains a null byte + /// - [`WrongPassword`][] if the current admin password is wrong + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::Device; + /// # use nitrokey::CommandError; + /// + /// # fn try_main() -> Result<(), CommandError> { + /// let device = nitrokey::connect()?; + /// match device.change_admin_pin("12345678", "12345679") { + /// CommandStatus::Success => println!("Updated admin PIN."), + /// CommandStatus::Error(err) => println!("Failed to update admin PIN: {:?}", err), + /// }; + /// # Ok(()) + /// # } + /// ``` + /// + /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword + fn change_admin_pin(&self, current: &str, new: &str) -> CommandStatus { + let current_string = CString::new(current); + let new_string = CString::new(new); + if current_string.is_err() || new_string.is_err() { + return CommandStatus::Error(CommandError::InvalidString); + } + let current_string = current_string.unwrap(); + let new_string = new_string.unwrap(); + unsafe { CommandStatus::from(nitrokey_sys::NK_change_admin_PIN(current_string.as_ptr(), new_string.as_ptr())) } + } + + /// Changes the user PIN. + /// + /// # Errors + /// + /// - [`InvalidString`][] if one of the provided passwords contains a null byte + /// - [`WrongPassword`][] if the current user password is wrong + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::Device; + /// # use nitrokey::CommandError; + /// + /// # fn try_main() -> Result<(), CommandError> { + /// let device = nitrokey::connect()?; + /// match device.change_user_pin("123456", "123457") { + /// CommandStatus::Success => println!("Updated admin PIN."), + /// CommandStatus::Error(err) => println!("Failed to update admin PIN: {:?}", err), + /// }; + /// # Ok(()) + /// # } + /// ``` + /// + /// [`InvalidString`]: enum.CommandError.html#variant.InvalidString + /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword + fn change_user_pin(&self, current: &str, new: &str) -> CommandStatus { + let current_string = CString::new(current); + let new_string = CString::new(new); + if current_string.is_err() || new_string.is_err() { + return CommandStatus::Error(CommandError::InvalidString); + } + let current_string = current_string.unwrap(); + let new_string = new_string.unwrap(); + unsafe { CommandStatus::from(nitrokey_sys::NK_change_user_PIN(current_string.as_ptr(), new_string.as_ptr())) } + } } trait AuthenticatedDevice { diff --git a/src/tests/pro.rs b/src/tests/pro.rs index a23d42a..d2132f8 100644 --- a/src/tests/pro.rs +++ b/src/tests/pro.rs @@ -4,7 +4,9 @@ use {set_debug, AdminAuthenticatedDevice, CommandError, CommandStatus, Config, D OtpMode, OtpSlotData, UnauthenticatedDevice}; static ADMIN_PASSWORD: &str = "12345678"; +static ADMIN_NEW_PASSWORD: &str = "1234567890"; static USER_PASSWORD: &str = "123456"; +static USER_NEW_PASSWORD: &str = "abcdefghij"; // test suite according to RFC 4226, Appendix D static HOTP_SECRET: &str = "3132333435363738393031323334353637383930"; @@ -330,3 +332,49 @@ fn read_write_config() { let get_config = admin.get_config().unwrap(); assert_eq!(config, get_config); } + +#[test] +#[cfg_attr(not(feature = "test-pro"), ignore)] +fn change_user_pin() { + let device = get_test_device(); + let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); + let device = device.authenticate_user(USER_NEW_PASSWORD).unwrap_err().0; + + let result = device.change_user_pin(USER_PASSWORD, USER_NEW_PASSWORD); + assert_eq!(CommandStatus::Success, result); + + let device = device.authenticate_user(USER_PASSWORD).unwrap_err().0; + let device = device.authenticate_user(USER_NEW_PASSWORD).unwrap().device(); + + let result = device.change_user_pin(USER_PASSWORD, USER_PASSWORD); + assert_eq!(CommandStatus::Error(CommandError::WrongPassword), result); + + let result = device.change_user_pin(USER_NEW_PASSWORD, USER_PASSWORD); + assert_eq!(CommandStatus::Success, result); + + let device = device.authenticate_user(USER_PASSWORD).unwrap().device(); + device.authenticate_user(USER_NEW_PASSWORD).unwrap_err(); +} + +#[test] +#[cfg_attr(not(feature = "test-pro"), ignore)] +fn change_admin_pin() { + let device = get_test_device(); + let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); + let device = device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err().0; + + let result = device.change_admin_pin(ADMIN_PASSWORD, ADMIN_NEW_PASSWORD); + assert_eq!(CommandStatus::Success, result); + + let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap_err().0; + let device = device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap().device(); + + let result = device.change_admin_pin(ADMIN_PASSWORD, ADMIN_PASSWORD); + assert_eq!(CommandStatus::Error(CommandError::WrongPassword), result); + + let result = device.change_admin_pin(ADMIN_NEW_PASSWORD, ADMIN_PASSWORD); + assert_eq!(CommandStatus::Success, result); + + let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); + device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err(); +} -- cgit v1.2.1