aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2019-07-09 08:09:02 +0000
committerRobin Krahl <robin.krahl@ireas.org>2019-07-09 08:27:55 +0000
commit12fa62483cf45d868099d5d4020333af492eebde (patch)
tree2ad466dbfeafb21365a3625f0beb8e2c6c392b2f
parentfe2f39826ade5a156945dabb8c8ab725378a15c1 (diff)
downloadnitrokey-rs-12fa62483cf45d868099d5d4020333af492eebde.tar.gz
nitrokey-rs-12fa62483cf45d868099d5d4020333af492eebde.tar.bz2
Introduce into_manager for Device
To enable applications like nitrokey-test to go back to a manager instance from a Device instance, we add the into_manager function to the Device trait. To do that, we have to keep track of the Manager’s lifetime by adding a lifetime to Device (and then to some other traits that use Device).
-rw-r--r--CHANGELOG.md1
-rw-r--r--src/auth.rs69
-rw-r--r--src/device.rs64
-rw-r--r--src/error.rs2
-rw-r--r--src/pws.rs30
-rw-r--r--tests/device.rs16
-rw-r--r--tests/otp.rs4
-rw-r--r--tests/pws.rs4
8 files changed, 123 insertions, 67 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 88e68dd..06769bd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -45,6 +45,7 @@ SPDX-License-Identifier: MIT
- Add `ConcurrentAccessError` and `PoisonError` `Error` variants.
- Add the `Manager` struct that manages connections to Nitrokey devices.
- Remove `connect`, `connect_model`, `Pro::connect` and `Storage::connect`.
+ - Add the `into_manager` function to the `Device` trait.
# v0.3.4 (2019-01-20)
- Fix authentication methods that assumed that `char` is signed.
diff --git a/src/auth.rs b/src/auth.rs
index 2ed7bfc..829d083 100644
--- a/src/auth.rs
+++ b/src/auth.rs
@@ -1,6 +1,7 @@
// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
// SPDX-License-Identifier: MIT
+use std::marker;
use std::ops;
use std::os::raw::c_char;
use std::os::raw::c_int;
@@ -18,7 +19,7 @@ static TEMPORARY_PASSWORD_LENGTH: usize = 25;
/// Provides methods to authenticate as a user or as an admin using a PIN. The authenticated
/// methods will consume the current device instance. On success, they return the authenticated
/// device. Otherwise, they return the current unauthenticated device and the error code.
-pub trait Authenticate {
+pub trait Authenticate<'a> {
/// Performs user authentication. This method consumes the device. If successful, an
/// authenticated device is returned. Otherwise, the current unauthenticated device and the
/// error are returned.
@@ -61,9 +62,9 @@ pub trait Authenticate {
/// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString
/// [`RngError`]: enum.CommandError.html#variant.RngError
/// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword
- fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)>
+ fn authenticate_user(self, password: &str) -> Result<User<'a, Self>, (Self, Error)>
where
- Self: Device + Sized;
+ Self: Device<'a> + Sized;
/// Performs admin authentication. This method consumes the device. If successful, an
/// authenticated device is returned. Otherwise, the current unauthenticated device and the
@@ -107,9 +108,9 @@ pub trait Authenticate {
/// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString
/// [`RngError`]: enum.CommandError.html#variant.RngError
/// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword
- fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)>
+ fn authenticate_admin(self, password: &str) -> Result<Admin<'a, Self>, (Self, Error)>
where
- Self: Device + Sized;
+ Self: Device<'a> + Sized;
}
trait AuthenticatedDevice<T> {
@@ -128,9 +129,10 @@ trait AuthenticatedDevice<T> {
/// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin
/// [`device`]: #method.device
#[derive(Debug)]
-pub struct User<T: Device> {
+pub struct User<'a, T: Device<'a>> {
device: T,
temp_password: Vec<u8>,
+ marker: marker::PhantomData<&'a T>,
}
/// A Nitrokey device with admin authentication.
@@ -143,14 +145,15 @@ pub struct User<T: Device> {
/// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin
/// [`device`]: #method.device
#[derive(Debug)]
-pub struct Admin<T: Device> {
+pub struct Admin<'a, T: Device<'a>> {
device: T,
temp_password: Vec<u8>,
+ marker: marker::PhantomData<&'a T>,
}
-fn authenticate<D, A, T>(device: D, password: &str, callback: T) -> Result<A, (D, Error)>
+fn authenticate<'a, D, A, T>(device: D, password: &str, callback: T) -> Result<A, (D, Error)>
where
- D: Device,
+ D: Device<'a>,
A: AuthenticatedDevice<D>,
T: Fn(*const c_char, *const c_char) -> c_int,
{
@@ -174,9 +177,9 @@ fn authenticate_user_wrapper<'a, T, C>(
device: T,
constructor: C,
password: &str,
-) -> Result<User<DeviceWrapper<'a>>, (DeviceWrapper<'a>, Error)>
+) -> Result<User<'a, DeviceWrapper<'a>>, (DeviceWrapper<'a>, Error)>
where
- T: Device,
+ T: Device<'a> + 'a,
C: Fn(T) -> DeviceWrapper<'a>,
{
let result = device.authenticate_user(password);
@@ -190,9 +193,9 @@ fn authenticate_admin_wrapper<'a, T, C>(
device: T,
constructor: C,
password: &str,
-) -> Result<Admin<DeviceWrapper<'a>>, (DeviceWrapper<'a>, Error)>
+) -> Result<Admin<'a, DeviceWrapper<'a>>, (DeviceWrapper<'a>, Error)>
where
- T: Device,
+ T: Device<'a> + 'a,
C: Fn(T) -> DeviceWrapper<'a>,
{
let result = device.authenticate_admin(password);
@@ -202,7 +205,7 @@ where
}
}
-impl<T: Device> User<T> {
+impl<'a, T: Device<'a>> User<'a, T> {
/// Forgets the user authentication and returns an unauthenticated device. This method
/// consumes the authenticated device. It does not perform any actual commands on the
/// Nitrokey.
@@ -211,7 +214,7 @@ impl<T: Device> User<T> {
}
}
-impl<T: Device> ops::Deref for User<T> {
+impl<'a, T: Device<'a>> ops::Deref for User<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
@@ -219,13 +222,13 @@ impl<T: Device> ops::Deref for User<T> {
}
}
-impl<T: Device> ops::DerefMut for User<T> {
+impl<'a, T: Device<'a>> ops::DerefMut for User<'a, T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.device
}
}
-impl<T: Device> GenerateOtp for User<T> {
+impl<'a, T: Device<'a>> GenerateOtp for User<'a, T> {
fn get_hotp_code(&mut self, slot: u8) -> Result<String, Error> {
result_from_string(unsafe {
nitrokey_sys::NK_get_hotp_code_PIN(slot, self.temp_password_ptr())
@@ -239,11 +242,12 @@ impl<T: Device> GenerateOtp for User<T> {
}
}
-impl<T: Device> AuthenticatedDevice<T> for User<T> {
+impl<'a, T: Device<'a>> AuthenticatedDevice<T> for User<'a, T> {
fn new(device: T, temp_password: Vec<u8>) -> Self {
User {
device,
temp_password,
+ marker: marker::PhantomData,
}
}
@@ -252,7 +256,7 @@ impl<T: Device> AuthenticatedDevice<T> for User<T> {
}
}
-impl<T: Device> ops::Deref for Admin<T> {
+impl<'a, T: Device<'a>> ops::Deref for Admin<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
@@ -260,13 +264,13 @@ impl<T: Device> ops::Deref for Admin<T> {
}
}
-impl<T: Device> ops::DerefMut for Admin<T> {
+impl<'a, T: Device<'a>> ops::DerefMut for Admin<'a, T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.device
}
}
-impl<T: Device> Admin<T> {
+impl<'a, T: Device<'a>> Admin<'a, T> {
/// Forgets the user authentication and returns an unauthenticated device. This method
/// consumes the authenticated device. It does not perform any actual commands on the
/// Nitrokey.
@@ -316,7 +320,7 @@ impl<T: Device> Admin<T> {
}
}
-impl<T: Device> ConfigureOtp for Admin<T> {
+impl<'a, T: Device<'a>> ConfigureOtp for Admin<'a, T> {
fn write_hotp_slot(&mut self, data: OtpSlotData, counter: u64) -> Result<(), Error> {
let raw_data = RawOtpSlotData::new(data)?;
get_command_result(unsafe {
@@ -364,11 +368,12 @@ impl<T: Device> ConfigureOtp for Admin<T> {
}
}
-impl<T: Device> AuthenticatedDevice<T> for Admin<T> {
+impl<'a, T: Device<'a>> AuthenticatedDevice<T> for Admin<'a, T> {
fn new(device: T, temp_password: Vec<u8>) -> Self {
Admin {
device,
temp_password,
+ marker: marker::PhantomData,
}
}
@@ -377,8 +382,8 @@ impl<T: Device> AuthenticatedDevice<T> for Admin<T> {
}
}
-impl<'a> Authenticate for DeviceWrapper<'a> {
- fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)> {
+impl<'a> Authenticate<'a> for DeviceWrapper<'a> {
+ fn authenticate_user(self, password: &str) -> Result<User<'a, Self>, (Self, Error)> {
match self {
DeviceWrapper::Storage(storage) => {
authenticate_user_wrapper(storage, DeviceWrapper::Storage, password)
@@ -387,7 +392,7 @@ impl<'a> Authenticate for DeviceWrapper<'a> {
}
}
- fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)> {
+ fn authenticate_admin(self, password: &str) -> Result<Admin<'a, Self>, (Self, Error)> {
match self {
DeviceWrapper::Storage(storage) => {
authenticate_admin_wrapper(storage, DeviceWrapper::Storage, password)
@@ -399,28 +404,28 @@ impl<'a> Authenticate for DeviceWrapper<'a> {
}
}
-impl<'a> Authenticate for Pro<'a> {
- fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)> {
+impl<'a> Authenticate<'a> for Pro<'a> {
+ fn authenticate_user(self, password: &str) -> Result<User<'a, Self>, (Self, Error)> {
authenticate(self, password, |password_ptr, temp_password_ptr| unsafe {
nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr)
})
}
- fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)> {
+ fn authenticate_admin(self, password: &str) -> Result<Admin<'a, Self>, (Self, Error)> {
authenticate(self, password, |password_ptr, temp_password_ptr| unsafe {
nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr)
})
}
}
-impl<'a> Authenticate for Storage<'a> {
- fn authenticate_user(self, password: &str) -> Result<User<Self>, (Self, Error)> {
+impl<'a> Authenticate<'a> for Storage<'a> {
+ fn authenticate_user(self, password: &str) -> Result<User<'a, Self>, (Self, Error)> {
authenticate(self, password, |password_ptr, temp_password_ptr| unsafe {
nitrokey_sys::NK_user_authenticate(password_ptr, temp_password_ptr)
})
}
- fn authenticate_admin(self, password: &str) -> Result<Admin<Self>, (Self, Error)> {
+ fn authenticate_admin(self, password: &str) -> Result<Admin<'a, Self>, (Self, Error)> {
authenticate(self, password, |password_ptr, temp_password_ptr| unsafe {
nitrokey_sys::NK_first_authenticate(password_ptr, temp_password_ptr)
})
diff --git a/src/device.rs b/src/device.rs
index 50ff071..e1a71fa 100644
--- a/src/device.rs
+++ b/src/device.rs
@@ -156,7 +156,7 @@ pub enum DeviceWrapper<'a> {
/// [`Pro::connect`]: #method.connect
#[derive(Debug)]
pub struct Pro<'a> {
- manager: &'a mut crate::Manager,
+ manager: Option<&'a mut crate::Manager>,
}
/// A Nitrokey Storage device without user or admin authentication.
@@ -200,7 +200,7 @@ pub struct Pro<'a> {
/// [`Storage::connect`]: #method.connect
#[derive(Debug)]
pub struct Storage<'a> {
- manager: &'a mut crate::Manager,
+ manager: Option<&'a mut crate::Manager>,
}
/// The status of a volume on a Nitrokey Storage device.
@@ -291,7 +291,32 @@ pub struct StorageStatus {
///
/// This trait provides the commands that can be executed without authentication and that are
/// present on all supported Nitrokey devices.
-pub trait Device: Authenticate + GetPasswordSafe + GenerateOtp + fmt::Debug {
+pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt::Debug {
+ /// Returns the [`Manager`][] instance that has been used to connect to this device.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use nitrokey::{Device, DeviceWrapper};
+ ///
+ /// fn do_something(device: DeviceWrapper) {
+ /// // reconnect to any device
+ /// let manager = device.into_manager();
+ /// let device = manager.connect();
+ /// // do something with the device
+ /// // ...
+ /// }
+ ///
+ /// # fn main() -> Result<(), nitrokey::Error> {
+ /// match nitrokey::take()?.connect() {
+ /// Ok(device) => do_something(device),
+ /// Err(err) => println!("Could not connect to a Nitrokey: {}", err),
+ /// }
+ /// # Ok(())
+ /// # }
+ /// ```
+ fn into_manager(self) -> &'a mut crate::Manager;
+
/// Returns the model of the connected Nitrokey device.
///
/// # Example
@@ -657,14 +682,14 @@ pub(crate) fn connect_enum(model: Model) -> bool {
}
impl<'a> DeviceWrapper<'a> {
- fn device(&self) -> &dyn Device {
+ fn device(&self) -> &dyn Device<'a> {
match *self {
DeviceWrapper::Storage(ref storage) => storage,
DeviceWrapper::Pro(ref pro) => pro,
}
}
- fn device_mut(&mut self) -> &mut dyn Device {
+ fn device_mut(&mut self) -> &mut dyn Device<'a> {
match *self {
DeviceWrapper::Storage(ref mut storage) => storage,
DeviceWrapper::Pro(ref mut pro) => pro,
@@ -702,7 +727,14 @@ impl<'a> GenerateOtp for DeviceWrapper<'a> {
}
}
-impl<'a> Device for DeviceWrapper<'a> {
+impl<'a> Device<'a> for DeviceWrapper<'a> {
+ fn into_manager(self) -> &'a mut crate::Manager {
+ match self {
+ DeviceWrapper::Pro(dev) => dev.into_manager(),
+ DeviceWrapper::Storage(dev) => dev.into_manager(),
+ }
+ }
+
fn get_model(&self) -> Model {
match *self {
DeviceWrapper::Pro(_) => Model::Pro,
@@ -713,7 +745,9 @@ impl<'a> Device for DeviceWrapper<'a> {
impl<'a> Pro<'a> {
pub(crate) fn new(manager: &'a mut crate::Manager) -> Pro<'a> {
- Pro { manager }
+ Pro {
+ manager: Some(manager),
+ }
}
}
@@ -725,7 +759,11 @@ impl<'a> Drop for Pro<'a> {
}
}
-impl<'a> Device for Pro<'a> {
+impl<'a> Device<'a> for Pro<'a> {
+ fn into_manager(mut self) -> &'a mut crate::Manager {
+ self.manager.take().unwrap()
+ }
+
fn get_model(&self) -> Model {
Model::Pro
}
@@ -735,7 +773,9 @@ impl<'a> GenerateOtp for Pro<'a> {}
impl<'a> Storage<'a> {
pub(crate) fn new(manager: &'a mut crate::Manager) -> Storage<'a> {
- Storage { manager }
+ Storage {
+ manager: Some(manager),
+ }
}
/// Changes the update PIN.
@@ -1248,7 +1288,11 @@ impl<'a> Drop for Storage<'a> {
}
}
-impl<'a> Device for Storage<'a> {
+impl<'a> Device<'a> for Storage<'a> {
+ fn into_manager(mut self) -> &'a mut crate::Manager {
+ self.manager.take().unwrap()
+ }
+
fn get_model(&self) -> Model {
Model::Storage
}
diff --git a/src/error.rs b/src/error.rs
index b84f5eb..9e6adc0 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -85,7 +85,7 @@ impl From<sync::TryLockError<sync::MutexGuard<'static, crate::Manager>>> for Err
}
}
-impl<T: device::Device> From<(T, Error)> for Error {
+impl<'a, T: device::Device<'a>> From<(T, Error)> for Error {
fn from((_, err): (T, Error)) -> Self {
err
}
diff --git a/src/pws.rs b/src/pws.rs
index cf2dd42..778765d 100644
--- a/src/pws.rs
+++ b/src/pws.rs
@@ -57,8 +57,8 @@ pub const SLOT_COUNT: u8 = 16;
/// [`lock`]: trait.Device.html#method.lock
/// [`GetPasswordSafe`]: trait.GetPasswordSafe.html
#[derive(Debug)]
-pub struct PasswordSafe<'a> {
- _device: &'a dyn Device,
+pub struct PasswordSafe<'a, 'b> {
+ _device: &'a dyn Device<'b>,
}
/// Provides access to a [`PasswordSafe`][].
@@ -67,7 +67,7 @@ pub struct PasswordSafe<'a> {
/// retrieved from it.
///
/// [`PasswordSafe`]: struct.PasswordSafe.html
-pub trait GetPasswordSafe {
+pub trait GetPasswordSafe<'a> {
/// Enables and returns the password safe.
///
/// The underlying device must always live at least as long as a password safe retrieved from
@@ -117,13 +117,13 @@ 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(&mut self, user_pin: &str) -> Result<PasswordSafe<'_>, Error>;
+ fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_, 'a>, Error>;
}
-fn get_password_safe<'a>(
- device: &'a dyn Device,
+fn get_password_safe<'a, 'b>(
+ device: &'a dyn Device<'b>,
user_pin: &str,
-) -> Result<PasswordSafe<'a>, Error> {
+) -> Result<PasswordSafe<'a, 'b>, 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 })
@@ -137,7 +137,7 @@ fn get_pws_result(s: String) -> Result<String, Error> {
}
}
-impl<'a> PasswordSafe<'a> {
+impl<'a, 'b> PasswordSafe<'a, 'b> {
/// Returns the status of all password slots.
///
/// The status indicates whether a slot is programmed or not.
@@ -357,27 +357,27 @@ impl<'a> PasswordSafe<'a> {
}
}
-impl<'a> Drop for PasswordSafe<'a> {
+impl<'a, 'b> Drop for PasswordSafe<'a, 'b> {
fn drop(&mut self) {
// TODO: disable the password safe -- NK_lock_device has side effects on the Nitrokey
// Storage, see https://github.com/Nitrokey/nitrokey-storage-firmware/issues/65
}
}
-impl<'a> GetPasswordSafe for Pro<'a> {
- fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> {
+impl<'a> GetPasswordSafe<'a> for Pro<'a> {
+ fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_, 'a>, Error> {
get_password_safe(self, user_pin)
}
}
-impl<'a> GetPasswordSafe for Storage<'a> {
- fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> {
+impl<'a> GetPasswordSafe<'a> for Storage<'a> {
+ fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_, 'a>, Error> {
get_password_safe(self, user_pin)
}
}
-impl<'a> GetPasswordSafe for DeviceWrapper<'a> {
- fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_>, Error> {
+impl<'a> GetPasswordSafe<'a> for DeviceWrapper<'a> {
+ fn get_password_safe(&mut self, user_pin: &str) -> Result<PasswordSafe<'_, 'a>, Error> {
get_password_safe(self, user_pin)
}
}
diff --git a/tests/device.rs b/tests/device.rs
index b377f2e..76f38e6 100644
--- a/tests/device.rs
+++ b/tests/device.rs
@@ -101,7 +101,10 @@ fn get_firmware_version(device: Pro) {
assert!(version.minor > 0);
}
-fn admin_retry<T: Authenticate + Device>(device: T, suffix: &str, count: u8) -> T {
+fn admin_retry<'a, T>(device: T, suffix: &str, count: u8) -> T
+where
+ T: Authenticate<'a> + Device<'a> + 'a,
+{
let result = device.authenticate_admin(&(DEFAULT_ADMIN_PIN.to_owned() + suffix));
let device = match result {
Ok(admin) => admin.device(),
@@ -111,7 +114,10 @@ fn admin_retry<T: Authenticate + Device>(device: T, suffix: &str, count: u8) ->
return device;
}
-fn user_retry<T: Authenticate + Device>(device: T, suffix: &str, count: u8) -> T {
+fn user_retry<'a, T>(device: T, suffix: &str, count: u8) -> T
+where
+ T: Authenticate<'a> + Device<'a> + 'a,
+{
let result = device.authenticate_user(&(DEFAULT_USER_PIN.to_owned() + suffix));
let device = match result {
Ok(admin) => admin.device(),
@@ -220,10 +226,10 @@ fn change_admin_pin(device: DeviceWrapper) {
device.authenticate_admin(ADMIN_NEW_PASSWORD).unwrap_err();
}
-fn require_failed_user_login<D>(device: D, password: &str, error: CommandError) -> D
+fn require_failed_user_login<'a, D>(device: D, password: &str, error: CommandError) -> D
where
- D: Device + Authenticate,
- nitrokey::User<D>: std::fmt::Debug,
+ D: Device<'a> + Authenticate<'a> + 'a,
+ nitrokey::User<'a, D>: std::fmt::Debug,
{
let result = device.authenticate_user(password);
assert!(result.is_err());
diff --git a/tests/otp.rs b/tests/otp.rs
index c0bbecf..aafda59 100644
--- a/tests/otp.rs
+++ b/tests/otp.rs
@@ -36,9 +36,9 @@ enum TotpTimestampSize {
U64,
}
-fn make_admin_test_device<T>(device: T) -> Admin<T>
+fn make_admin_test_device<'a, T>(device: T) -> Admin<'a, T>
where
- T: Device,
+ T: Device<'a>,
(T, nitrokey::Error): Debug,
{
unwrap_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN))
diff --git a/tests/pws.rs b/tests/pws.rs
index b0e5abe..7169695 100644
--- a/tests/pws.rs
+++ b/tests/pws.rs
@@ -32,9 +32,9 @@ fn get_slot_name_direct(slot: u8) -> Result<String, Error> {
}
}
-fn get_pws<T>(device: &mut T) -> PasswordSafe
+fn get_pws<'a, T>(device: &mut T) -> PasswordSafe<'_, 'a>
where
- T: Device,
+ T: Device<'a>,
{
unwrap_ok!(device.get_password_safe(DEFAULT_USER_PIN))
}