From 0a7a62c9af15b11e5dbfad1900ac89924457b272 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 3 Jan 2019 13:50:15 +0000 Subject: Add Device::factory_reset method This patch adds the factory_reset_method to the Device trait that uses the NK_factory_reset function to perform a factory reset. The tests verify that the user and admin PIN are reset and that the OTP storage and the password safe are deleted. --- CHANGELOG.md | 3 ++- TODO.md | 1 - src/device.rs | 36 +++++++++++++++++++++++++++++++--- tests/device.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 92 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1611414..8ecaef3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,9 @@ - Add `CommandError::Undefined` to represent errors without further information (e. g. a method returned `NULL` unexpectedly). - Add error code to `CommandError::Unknown`. -- Add function `Storage::change_update_pin` that changes the firmware update +- Add the `Storage::change_update_pin` method that changes the firmware update PIN. +- Add the `Device::factory_reset` method that performs a factory reset. # v0.2.3 (2018-12-31) diff --git a/TODO.md b/TODO.md index 0071dcc..5bcfa68 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_factory_reset` - `NK_build_aes_key` - `NK_is_AES_supported` - `NK_send_startup` diff --git a/src/device.rs b/src/device.rs index 4b8cc5c..8702405 100644 --- a/src/device.rs +++ b/src/device.rs @@ -510,6 +510,36 @@ pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp { fn lock(&self) -> Result<(), CommandError> { unsafe { get_command_result(nitrokey_sys::NK_lock_device()) } } + + /// Performs a factory reset on the Nitrokey device. + /// + /// This commands performs a factory reset on the smart card (like the factory reset via `gpg + /// --card-edit`) and then clears the flash memory (password safe, one-time passwords etc.). + /// + /// # 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.factory_reset("12345678") { + /// Ok(()) => println!("Performed a factory reset."), + /// Err(err) => println!("Could not perform a factory reset: {}", err), + /// }; + /// # Ok(()) + /// # } + /// ``` + fn factory_reset(&self, admin_pin: &str) -> Result<(), CommandError> { + let admin_pin_string = get_cstring(admin_pin)?; + unsafe { get_command_result(nitrokey_sys::NK_factory_reset(admin_pin_string.as_ptr())) } + } } /// Connects to a Nitrokey device. This method can be used to connect to any connected device, @@ -700,9 +730,9 @@ impl Storage { let new_string = get_cstring(new)?; unsafe { get_command_result(nitrokey_sys::NK_change_update_password( - current_string.as_ptr(), - new_string.as_ptr(), - )) + current_string.as_ptr(), + new_string.as_ptr(), + )) } } diff --git a/tests/device.rs b/tests/device.rs index a225d2d..363b8d8 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -4,9 +4,12 @@ use std::ffi::CStr; use std::process::Command; use std::{thread, time}; -use nitrokey::{Authenticate, CommandError, Config, Device, Storage}; +use nitrokey::{ + Authenticate, CommandError, Config, ConfigureOtp, Device, GenerateOtp, GetPasswordSafe, + OtpMode, OtpSlotData, Storage, +}; -use crate::util::{Target, ADMIN_PASSWORD, USER_PASSWORD, UPDATE_PIN}; +use crate::util::{Target, ADMIN_PASSWORD, UPDATE_PIN, USER_PASSWORD}; static ADMIN_NEW_PASSWORD: &str = "1234567890"; static UPDATE_NEW_PIN: &str = "87654321"; @@ -295,12 +298,63 @@ fn unlock_user_pin() { .is_ok()); } +#[test] +#[cfg_attr(not(any(feature = "test-pro", feature = "test-storage")), ignore)] +fn factory_reset() { + let device = Target::connect().unwrap(); + + assert_eq!( + Ok(()), + device.change_user_pin(USER_PASSWORD, USER_NEW_PASSWORD) + ); + assert_eq!( + Ok(()), + device.change_admin_pin(ADMIN_PASSWORD, ADMIN_NEW_PASSWORD) + ); + + let admin = device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap(); + let otp_data = OtpSlotData::new(1, "test", "0123468790", OtpMode::SixDigits); + assert_eq!(Ok(()), admin.write_totp_slot(otp_data, 30)); + + let device = admin.device(); + let pws = device.get_password_safe(USER_NEW_PASSWORD).unwrap(); + assert_eq!(Ok(()), pws.write_slot(0, "test", "testlogin", "testpw")); + drop(pws); + + assert_eq!( + Err(CommandError::WrongPassword), + device.factory_reset(USER_NEW_PASSWORD) + ); + assert_eq!( + Err(CommandError::WrongPassword), + device.factory_reset(ADMIN_PASSWORD) + ); + assert_eq!(Ok(()), device.factory_reset(ADMIN_NEW_PASSWORD)); + + let device = device.authenticate_admin(ADMIN_PASSWORD).unwrap().device(); + + let user = device.authenticate_user(USER_PASSWORD).unwrap(); + assert_eq!( + Err(CommandError::SlotNotProgrammed), + user.get_totp_slot_name(1) + ); + + let device = user.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(); - assert_eq!(Err(CommandError::WrongPassword), device.change_update_pin(UPDATE_NEW_PIN, UPDATE_PIN)); + assert_eq!( + Err(CommandError::WrongPassword), + device.change_update_pin(UPDATE_NEW_PIN, UPDATE_PIN) + ); assert_eq!(Ok(()), device.change_update_pin(UPDATE_PIN, UPDATE_NEW_PIN)); assert_eq!(Ok(()), device.change_update_pin(UPDATE_NEW_PIN, UPDATE_PIN)); } -- cgit v1.2.3