From f49e61589e32217f97c94aa86d826f6b65170fba Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 28 Jan 2019 12:27:15 +0000 Subject: Require mutable reference if method changes device state Previously, all methods that access a Nitrokey device took a reference to the device as input. This method changes methods that change the device state to require a mutable reference instead. In most case, this is straightforward as the method writes data to the device (for example write_config or change_user_pin). But there are two edge cases: - Authenticating with a PIN changes the device state as it may decrease the PIN retry counter if the authentication fails. - Generating an HOTP code changes the device state as it increases the HOTP counter. --- src/pws.rs | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'src/pws.rs') diff --git a/src/pws.rs b/src/pws.rs index 1e27935..371de6e 100644 --- a/src/pws.rs +++ b/src/pws.rs @@ -43,9 +43,10 @@ pub const SLOT_COUNT: u8 = 16; /// } /// /// # fn try_main() -> Result<(), Error> { -/// let device = nitrokey::connect()?; +/// let mut device = nitrokey::connect()?; /// let pws = device.get_password_safe("123456")?; /// use_password_safe(&pws); +/// drop(pws); /// device.lock()?; /// # Ok(()) /// # } @@ -97,14 +98,14 @@ pub trait GetPasswordSafe { /// fn use_password_safe(pws: &PasswordSafe) {} /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.get_password_safe("123456") { /// Ok(pws) => { /// use_password_safe(&pws); - /// device.lock()?; /// }, /// Err(err) => eprintln!("Could not open the password safe: {}", err), /// }; + /// device.lock()?; /// # Ok(()) /// # } /// ``` @@ -116,7 +117,7 @@ pub trait GetPasswordSafe { /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString /// [`Unknown`]: enum.CommandError.html#variant.Unknown /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn get_password_safe(&self, user_pin: &str) -> Result, Error>; + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error>; } fn get_password_safe<'a>( @@ -148,7 +149,7 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// let pws = device.get_password_safe("123456")?; /// pws.get_slot_status()?.iter().enumerate().for_each(|(slot, programmed)| { /// let status = match *programmed { @@ -193,7 +194,7 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// match device.get_password_safe("123456") { /// Ok(pws) => { /// let name = pws.get_slot_name(0)?; @@ -230,7 +231,7 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// let pws = device.get_password_safe("123456")?; /// let name = pws.get_slot_name(0)?; /// let login = pws.get_slot_login(0)?; @@ -263,7 +264,7 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// let pws = device.get_password_safe("123456")?; /// let name = pws.get_slot_name(0)?; /// let login = pws.get_slot_login(0)?; @@ -294,7 +295,7 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; + /// let mut device = nitrokey::connect()?; /// let pws = device.get_password_safe("123456")?; /// let name = pws.get_slot_name(0)?; /// let login = pws.get_slot_login(0)?; @@ -307,7 +308,7 @@ impl<'a> PasswordSafe<'a> { /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString pub fn write_slot( - &self, + &mut self, slot: u8, name: &str, login: &str, @@ -340,8 +341,8 @@ impl<'a> PasswordSafe<'a> { /// # use nitrokey::Error; /// /// # fn try_main() -> Result<(), Error> { - /// let device = nitrokey::connect()?; - /// let pws = device.get_password_safe("123456")?; + /// let mut device = nitrokey::connect()?; + /// let mut pws = device.get_password_safe("123456")?; /// match pws.erase_slot(0) { /// Ok(()) => println!("Erased slot 0."), /// Err(err) => eprintln!("Could not erase slot 0: {}", err), @@ -351,7 +352,7 @@ impl<'a> PasswordSafe<'a> { /// ``` /// /// [`InvalidSlot`]: enum.LibraryError.html#variant.InvalidSlot - pub fn erase_slot(&self, slot: u8) -> Result<(), Error> { + pub fn erase_slot(&mut self, slot: u8) -> Result<(), Error> { get_command_result(unsafe { nitrokey_sys::NK_erase_password_safe_slot(slot) }) } } @@ -364,19 +365,19 @@ impl<'a> Drop for PasswordSafe<'a> { } impl GetPasswordSafe for Pro { - fn get_password_safe(&self, user_pin: &str) -> Result, Error> { + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } impl GetPasswordSafe for Storage { - fn get_password_safe(&self, user_pin: &str) -> Result, Error> { + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } impl GetPasswordSafe for DeviceWrapper { - fn get_password_safe(&self, user_pin: &str) -> Result, Error> { + fn get_password_safe(&mut self, user_pin: &str) -> Result, Error> { get_password_safe(self, user_pin) } } -- cgit v1.2.1 From 13006c00dcbd570cf8347d89557834e320427377 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 30 Jan 2019 18:40:11 +0000 Subject: Store mutable reference to Device in PasswordSafe The current implementation of PasswordSafe stored a normal reference to the Device. This patch changes the PasswordSafe struct to use a mutable reference instead. This allows the borrow checker to make sure that there is only one PasswordSafe instance at a time. While this is currently not needed, it will become important once we can lock the PWS on the Nitrokey when dropping the PasswordSafe instance. --- src/pws.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'src/pws.rs') diff --git a/src/pws.rs b/src/pws.rs index 371de6e..a5b9d33 100644 --- a/src/pws.rs +++ b/src/pws.rs @@ -18,8 +18,7 @@ pub const SLOT_COUNT: u8 = 16; /// The password safe stores a tuple consisting of a name, a login and a password on a slot. The /// number of available slots is [`SLOT_COUNT`][]. The slots are addressed starting with zero. To /// retrieve a password safe from a Nitrokey device, use the [`get_password_safe`][] method from -/// the [`GetPasswordSafe`][] trait. Note that the device must live at least as long as the -/// password safe. +/// the [`GetPasswordSafe`][] trait. /// /// Once the password safe has been unlocked, it can be accessed without a password. Therefore it /// is mandatory to call [`lock`][] on the corresponding device after the password store is used. @@ -58,21 +57,17 @@ pub const SLOT_COUNT: u8 = 16; /// [`GetPasswordSafe`]: trait.GetPasswordSafe.html #[derive(Debug)] pub struct PasswordSafe<'a> { - _device: &'a dyn Device, + device: &'a mut dyn Device, } /// Provides access to a [`PasswordSafe`][]. /// -/// The device that implements this trait must always live at least as long as a password safe -/// retrieved from it. -/// /// [`PasswordSafe`]: struct.PasswordSafe.html pub trait GetPasswordSafe { /// Enables and returns the password safe. /// - /// The underlying device must always live at least as long as a password safe retrieved from - /// it. It is mandatory to lock the underlying device using [`lock`][] after the password safe - /// has been used. Otherwise, other applications can access the password store without + /// It is mandatory to lock the underlying device using [`lock`][] after the password safe has + /// been used. Otherwise, other applications can access the password store without /// authentication. /// /// If this method returns an `AesDecryptionFailed` (Nitrokey Pro) or `Unknown` (Nitrokey @@ -121,12 +116,17 @@ pub trait GetPasswordSafe { } fn get_password_safe<'a>( - device: &'a dyn Device, + device: &'a mut dyn Device, user_pin: &str, ) -> Result, Error> { let user_pin_string = get_cstring(user_pin)?; - get_command_result(unsafe { nitrokey_sys::NK_enable_password_safe(user_pin_string.as_ptr()) }) - .map(|_| PasswordSafe { _device: device }) + let result = get_command_result(unsafe { + nitrokey_sys::NK_enable_password_safe(user_pin_string.as_ptr()) + }); + match result { + Ok(()) => Ok(PasswordSafe { device }), + Err(err) => Err(err), + } } fn get_pws_result(s: String) -> Result { -- cgit v1.2.1