From 12fa62483cf45d868099d5d4020333af492eebde Mon Sep 17 00:00:00 2001
From: Robin Krahl <robin.krahl@ireas.org>
Date: Tue, 9 Jul 2019 08:09:02 +0000
Subject: Introduce into_manager for Device
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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).
---
 CHANGELOG.md    |  1 +
 src/auth.rs     | 69 +++++++++++++++++++++++++++++++--------------------------
 src/device.rs   | 64 +++++++++++++++++++++++++++++++++++++++++++---------
 src/error.rs    |  2 +-
 src/pws.rs      | 30 ++++++++++++-------------
 tests/device.rs | 16 ++++++++-----
 tests/otp.rs    |  4 ++--
 tests/pws.rs    |  4 ++--
 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))
 }
-- 
cgit v1.2.3