aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2018-06-05 22:19:53 +0200
committerRobin Krahl <robin.krahl@ireas.org>2018-06-05 22:19:53 +0200
commit95d18c5b06399fa4109de808f70b1048d4494d39 (patch)
tree4b7cc832b85e9eeeb96a814dae7de598e16bd94c
parent7a89b3dbf2f8d8f882edb6298d011cdd3d7add3c (diff)
downloadnitrokey-rs-95d18c5b06399fa4109de808f70b1048d4494d39.tar.gz
nitrokey-rs-95d18c5b06399fa4109de808f70b1048d4494d39.tar.bz2
Use a Device reference in PasswordSafe
Instead of wrapping an owned Device instance, PasswordSafe now only requires a reference to a Device. The lifetime parameter makes sure that the device lives at least as long as the password safe. Using a reference instead of an owned device allows us to implement Drop on PasswordSafe to make sure that the password safe is disabled once it is destructed.
-rw-r--r--src/pws.rs189
-rw-r--r--src/tests/pws.rs44
2 files changed, 75 insertions, 158 deletions
diff --git a/src/pws.rs b/src/pws.rs
index fc4b516..87a71dd 100644
--- a/src/pws.rs
+++ b/src/pws.rs
@@ -12,55 +12,41 @@ pub const SLOT_COUNT: u8 = 16;
/// A password safe on a Nitrokey device.
///
/// 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.
-///
-/// The password safe struct wraps a device. To get back to the original device, use the
-/// [`device`][] method. To retrieve a password safe from a Nitrokey device, use the
-/// [`get_password_safe`][] method from the [`GetPasswordSafe`][] trait.
+/// 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.
///
/// # Examples
///
/// Open a password safe and access a password:
///
/// ```no_run
-/// use nitrokey::{Device, GetPasswordSafe};
+/// use nitrokey::GetPasswordSafe;
/// # use nitrokey::CommandError;
///
-/// fn use_device<T: Device>(device: &T) {}
-///
/// # fn try_main() -> Result<(), CommandError> {
/// let device = nitrokey::connect()?;
-/// let device = match device.get_password_safe("123456") {
-/// Ok(pws) => {
-/// let name = pws.get_slot_name(0)?;
-/// let login = pws.get_slot_login(0)?;
-/// let password = pws.get_slot_login(0)?;
-/// println!("Credentials for {}: login {}, password {}", name, login, password);
-/// pws.device()
-/// },
-/// Err((device, err)) => {
-/// println!("Could not open the password safe: {:?}", err);
-/// device
-/// },
-/// };
-/// use_device(&device);
+/// let pws = device.get_password_safe("123456")?;
+/// let name = pws.get_slot_name(0)?;
+/// let login = pws.get_slot_login(0)?;
+/// let password = pws.get_slot_login(0)?;
+/// println!("Credentials for {}: login {}, password {}", name, login, password);
/// # Ok(())
/// # }
/// ```
///
/// [`SLOT_COUNT`]: constant.SLOT_COUNT.html
-/// [`device`]: #method.device
/// [`get_password_safe`]: trait.GetPasswordSafe.html#method.get_password_safe
/// [`GetPasswordSafe`]: trait.GetPasswordSafe.html
-pub struct PasswordSafe<T: Device> {
- device: T,
+pub struct PasswordSafe<'a> {
+ _device: &'a Device,
}
/// Provides access to a [`PasswordSafe`][].
///
-/// When retrieving a password safe, the underlying device is consumed. On success, the returned
-/// password safe also containes the device. On error, the device is returned as part of the
-/// error.
+/// 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 {
@@ -76,25 +62,17 @@ pub trait GetPasswordSafe {
/// # Example
///
/// ```no_run
- /// use nitrokey::{Device, GetPasswordSafe, PasswordSafe};
+ /// use nitrokey::{GetPasswordSafe, PasswordSafe};
/// # use nitrokey::CommandError;
///
- /// fn use_password_safe<T: Device>(pws: &PasswordSafe<T>) {}
- /// fn use_device<T: Device>(device: &T) {}
+ /// fn use_password_safe(pws: &PasswordSafe) {}
///
/// # fn try_main() -> Result<(), CommandError> {
/// let device = nitrokey::connect()?;
- /// let device = match device.get_password_safe("123456") {
- /// Ok(pws) => {
- /// use_password_safe(&pws);
- /// pws.device()
- /// },
- /// Err((device, err)) => {
- /// println!("Could not open the password safe: {:?}", err);
- /// device
- /// },
+ /// match device.get_password_safe("123456") {
+ /// Ok(pws) => use_password_safe(&pws),
+ /// Err(err) => println!("Could not open the password safe: {:?}", err),
/// };
- /// use_device(&device);
/// # Ok(())
/// # }
/// ```
@@ -102,18 +80,16 @@ pub trait GetPasswordSafe {
/// [`device`]: struct.PasswordSafe.html#method.device
/// [`InvalidString`]: enum.CommandError.html#variant.InvalidString
/// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword
- fn get_password_safe(self, user_pin: &str) -> Result<PasswordSafe<Self>, (Self, CommandError)>
- where
- Self: Device + Sized;
+ fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe, CommandError>;
}
-fn get_password_safe<T: Device>(
- device: T,
+fn get_password_safe<'a>(
+ device: &'a Device,
user_pin: &str,
-) -> Result<PasswordSafe<T>, (T, CommandError)> {
+) -> Result<PasswordSafe<'a>, CommandError> {
let user_pin_string = CString::new(user_pin);
if user_pin_string.is_err() {
- return Err((device, CommandError::InvalidString));
+ return Err(CommandError::InvalidString);
}
let user_pin_string = user_pin_string.unwrap();
let status = unsafe {
@@ -122,36 +98,12 @@ fn get_password_safe<T: Device>(
))
};
match status {
- CommandStatus::Success => Ok(PasswordSafe { device }),
- CommandStatus::Error(err) => Err((device, err)),
+ CommandStatus::Success => Ok(PasswordSafe { _device: device }),
+ CommandStatus::Error(err) => Err(err),
}
}
-fn get_password_safe_wrapper<T, C>(
- device: T,
- constructor: C,
- user_pin: &str,
-) -> Result<PasswordSafe<DeviceWrapper>, (DeviceWrapper, CommandError)>
-where
- T: Device,
- C: Fn(T) -> DeviceWrapper,
-{
- let result = device.get_password_safe(user_pin);
- match result {
- Ok(pws) => Ok(PasswordSafe {
- device: constructor(pws.device),
- }),
- Err((device, err)) => Err((constructor(device), err)),
- }
-}
-
-impl<T: Device> PasswordSafe<T> {
- /// Forgets the password safe access and returns an unauthenticated device. This method
- /// consumes the password safe. It does not perform any actual commands on the Nitrokey.
- pub fn device(self) -> T {
- self.device
- }
-
+impl<'a> PasswordSafe<'a> {
/// Returns the status of all password slots.
///
/// The status indicates whether a slot is programmed or not.
@@ -164,18 +116,14 @@ impl<T: Device> PasswordSafe<T> {
///
/// # fn try_main() -> Result<(), CommandError> {
/// let device = nitrokey::connect()?;
- /// match device.get_password_safe("123456") {
- /// Ok(pws) => {
- /// pws.get_slot_status()?.iter().enumerate().for_each(|(slot, programmed)| {
- /// let status = match *programmed {
- /// true => "programmed",
- /// false => "not programmed",
- /// };
- /// println!("Slot {}: {}", slot, status);
- /// });
- /// },
- /// Err((_, err)) => println!("Could not open the password safe: {:?}", err),
- /// };
+ /// let pws = device.get_password_safe("123456")?;
+ /// pws.get_slot_status()?.iter().enumerate().for_each(|(slot, programmed)| {
+ /// let status = match *programmed {
+ /// true => "programmed",
+ /// false => "not programmed",
+ /// };
+ /// println!("Slot {}: {}", slot, status);
+ /// });
/// # Ok(())
/// # }
/// ```
@@ -218,7 +166,7 @@ impl<T: Device> PasswordSafe<T> {
/// let password = pws.get_slot_login(0)?;
/// println!("Credentials for {}: login {}, password {}", name, login, password);
/// },
- /// Err((_, err)) => println!("Could not open the password safe: {:?}", err),
+ /// Err(err) => println!("Could not open the password safe: {:?}", err),
/// };
/// # Ok(())
/// # }
@@ -245,15 +193,11 @@ impl<T: Device> PasswordSafe<T> {
///
/// # fn try_main() -> Result<(), CommandError> {
/// let device = nitrokey::connect()?;
- /// match device.get_password_safe("123456") {
- /// Ok(pws) => {
- /// let name = pws.get_slot_name(0)?;
- /// let login = pws.get_slot_login(0)?;
- /// let password = pws.get_slot_login(0)?;
- /// println!("Credentials for {}: login {}, password {}", name, login, password);
- /// },
- /// Err((_, err)) => println!("Could not open the password safe: {:?}", err),
- /// };
+ /// let pws = device.get_password_safe("123456")?;
+ /// let name = pws.get_slot_name(0)?;
+ /// let login = pws.get_slot_login(0)?;
+ /// let password = pws.get_slot_login(0)?;
+ /// println!("Credentials for {}: login {}, password {}", name, login, password);
/// # Ok(())
/// # }
/// ```
@@ -279,15 +223,11 @@ impl<T: Device> PasswordSafe<T> {
///
/// # fn try_main() -> Result<(), CommandError> {
/// let device = nitrokey::connect()?;
- /// match device.get_password_safe("123456") {
- /// Ok(pws) => {
- /// let name = pws.get_slot_name(0)?;
- /// let login = pws.get_slot_login(0)?;
- /// let password = pws.get_slot_login(0)?;
- /// println!("Credentials for {}: login {}, password {}", name, login, password);
- /// },
- /// Err((_, err)) => println!("Could not open the password safe: {:?}", err),
- /// };
+ /// let pws = device.get_password_safe("123456")?;
+ /// let name = pws.get_slot_name(0)?;
+ /// let login = pws.get_slot_login(0)?;
+ /// let password = pws.get_slot_login(0)?;
+ /// println!("Credentials for {}: login {}, password {}", name, login, password);
/// # Ok(())
/// # }
/// ```
@@ -308,18 +248,16 @@ impl<T: Device> PasswordSafe<T> {
/// # Example
///
/// ```no_run
- /// use nitrokey::{CommandStatus, GetPasswordSafe};
+ /// use nitrokey::GetPasswordSafe;
/// # use nitrokey::CommandError;
///
/// # fn try_main() -> Result<(), CommandError> {
/// let device = nitrokey::connect()?;
- /// match device.get_password_safe("123456") {
- /// Ok(pws) => match pws.write_slot(0, "GitHub", "johndoe", "passw0rd") {
- /// CommandStatus::Success => println!("Successfully wrote slot 0."),
- /// CommandStatus::Error(err) => println!("Could not write slot 0: {:?}", err),
- /// },
- /// Err((_, err)) => println!("Could not open the password safe: {:?}", err),
- /// };
+ /// let pws = device.get_password_safe("123456")?;
+ /// let name = pws.get_slot_name(0)?;
+ /// let login = pws.get_slot_login(0)?;
+ /// let password = pws.get_slot_login(0)?;
+ /// println!("Credentials for {}: login {}, password {}", name, login, password);
/// # Ok(())
/// # }
/// ```
@@ -362,12 +300,10 @@ impl<T: Device> PasswordSafe<T> {
///
/// # fn try_main() -> Result<(), CommandError> {
/// let device = nitrokey::connect()?;
- /// match device.get_password_safe("123456") {
- /// Ok(pws) => match pws.erase_slot(0) {
- /// CommandStatus::Success => println!("Successfully erased slot 0."),
- /// CommandStatus::Error(err) => println!("Could not erase slot 0: {:?}", err),
- /// },
- /// Err((_, err)) => println!("Could not open the password safe: {:?}", err),
+ /// let pws = device.get_password_safe("123456")?;
+ /// match pws.erase_slot(0) {
+ /// CommandStatus::Success => println!("Erased slot 0."),
+ /// CommandStatus::Error(err) => println!("Could not erase slot 0: {:?}", err),
/// };
/// # Ok(())
/// # }
@@ -380,24 +316,19 @@ impl<T: Device> PasswordSafe<T> {
}
impl GetPasswordSafe for Pro {
- fn get_password_safe(self, user_pin: &str) -> Result<PasswordSafe<Self>, (Self, CommandError)> {
+ fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe, CommandError> {
get_password_safe(self, user_pin)
}
}
impl GetPasswordSafe for Storage {
- fn get_password_safe(self, user_pin: &str) -> Result<PasswordSafe<Self>, (Self, CommandError)> {
+ fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe, CommandError> {
get_password_safe(self, user_pin)
}
}
impl GetPasswordSafe for DeviceWrapper {
- fn get_password_safe(self, user_pin: &str) -> Result<PasswordSafe<Self>, (Self, CommandError)> {
- match self {
- DeviceWrapper::Storage(storage) => {
- get_password_safe_wrapper(storage, DeviceWrapper::Storage, user_pin)
- }
- DeviceWrapper::Pro(pro) => get_password_safe_wrapper(pro, DeviceWrapper::Pro, user_pin),
- }
+ fn get_password_safe(&self, user_pin: &str) -> Result<PasswordSafe, CommandError> {
+ get_password_safe(self, user_pin)
}
}
diff --git a/src/tests/pws.rs b/src/tests/pws.rs
index f45a81a..30d6853 100644
--- a/src/tests/pws.rs
+++ b/src/tests/pws.rs
@@ -2,46 +2,29 @@ use pws::{GetPasswordSafe, PasswordSafe, SLOT_COUNT};
use tests::util::{Target, ADMIN_PASSWORD, USER_PASSWORD};
use util::{CommandError, CommandStatus};
-fn get_pws() -> PasswordSafe<Target> {
- Target::connect()
- .unwrap()
- .get_password_safe(USER_PASSWORD)
- .unwrap()
+fn get_pws(device: &Target) -> PasswordSafe {
+ device.get_password_safe(USER_PASSWORD).unwrap()
}
#[test]
#[cfg_attr(not(any(feature = "test-pro", feature = "test-storage")), ignore)]
fn enable() {
+ let device = Target::connect().unwrap();
assert!(
- Target::connect()
- .unwrap()
+ device
.get_password_safe(&(USER_PASSWORD.to_owned() + "123"))
.is_err()
);
- assert!(
- Target::connect()
- .unwrap()
- .get_password_safe(USER_PASSWORD)
- .is_ok()
- );
- assert!(
- Target::connect()
- .unwrap()
- .get_password_safe(ADMIN_PASSWORD)
- .is_err()
- );
- assert!(
- Target::connect()
- .unwrap()
- .get_password_safe(USER_PASSWORD)
- .is_ok()
- );
+ assert!(device.get_password_safe(USER_PASSWORD).is_ok());
+ assert!(device.get_password_safe(ADMIN_PASSWORD).is_err());
+ assert!(device.get_password_safe(USER_PASSWORD).is_ok());
}
#[test]
#[cfg_attr(not(any(feature = "test-pro", feature = "test-storage")), ignore)]
fn get_status() {
- let pws = get_pws();
+ let device = Target::connect().unwrap();
+ let pws = get_pws(&device);
for i in 0..SLOT_COUNT {
assert_eq!(
CommandStatus::Success,
@@ -75,7 +58,8 @@ fn get_status() {
#[test]
#[cfg_attr(not(any(feature = "test-pro", feature = "test-storage")), ignore)]
fn get_data() {
- let pws = get_pws();
+ let device = Target::connect().unwrap();
+ let pws = get_pws(&device);
assert_eq!(
CommandStatus::Success,
pws.write_slot(1, "name", "login", "password")
@@ -118,7 +102,8 @@ fn get_data() {
#[test]
#[cfg_attr(not(any(feature = "test-pro", feature = "test-storage")), ignore)]
fn write() {
- let pws = get_pws();
+ let device = Target::connect().unwrap();
+ let pws = get_pws(&device);
assert_eq!(
CommandStatus::Error(CommandError::InvalidSlot),
@@ -153,7 +138,8 @@ fn write() {
#[test]
#[cfg_attr(not(any(feature = "test-pro", feature = "test-storage")), ignore)]
fn erase() {
- let pws = get_pws();
+ let device = Target::connect().unwrap();
+ let pws = get_pws(&device);
assert_eq!(
CommandStatus::Error(CommandError::InvalidSlot),
pws.erase_slot(SLOT_COUNT)