Browse Source

Upgraded error handling by introducing the `conv_err` method.

Matthew Carr 2 years ago
parent
commit
d3f6f7e46c
2 changed files with 196 additions and 98 deletions
  1. 54 31
      crates/btnode/src/crypto/mod.rs
  2. 142 67
      crates/btnode/src/crypto/tpm.rs

+ 54 - 31
crates/btnode/src/crypto/mod.rs

@@ -20,7 +20,7 @@ use serde::{
     ser::{Serializer, SerializeStruct},
 };
 use std::{
-    str::FromStr,
+    str::FromStr, num::TryFromIntError,
 };
 use strum_macros::{EnumString, EnumDiscriminants, Display};
 
@@ -42,9 +42,15 @@ pub(crate) enum Error {
     KeyVariantUnsupported,
     BlockNotEncrypted,
     InvalidHashFormat,
-    Message(String),
     Serde(serde_block_tree::Error),
     Io(std::io::Error),
+    Custom(Box<dyn std::fmt::Debug>)
+}
+
+impl Error {
+    fn custom<E: std::fmt::Debug + 'static>(err: E) -> Self {
+        Error::Custom(Box::new(err))
+    }
 }
 
 impl Display for Error {
@@ -56,16 +62,16 @@ impl Display for Error {
             Error::KeyVariantUnsupported => write!(f, "unsupported key variant"),
             Error::BlockNotEncrypted => write!(f, "block was not encrypted"),
             Error::InvalidHashFormat => write!(f, "invalid format"),
-            Error::Message(message) => f.write_str(message.as_str()),
             Error::Serde(serde_err) => serde_err.fmt(f),
             Error::Io(io_err) => io_err.fmt(f),
+            Error::Custom(cus) => cus.fmt(f),
         }
     }
 }
 
 impl From<ErrorStack> for Error {
     fn from(error: ErrorStack) -> Error {
-        Error::Message(error.to_string())
+        Error::custom(error)
     }
 }
 
@@ -81,8 +87,25 @@ impl From<std::io::Error> for Error {
     }
 }
 
+impl From<TryFromIntError> for Error {
+    fn from(err: TryFromIntError) -> Self {
+        Error::custom(err)
+    }
+}
+
 pub(crate) type Result<T> = std::result::Result<T, Error>;
 
+trait ConvErr<T> {
+    fn conv_err(self) -> Result<T>;
+}
+
+impl<T, E: Into<Error>> ConvErr<T> for std::result::Result<T, E> {
+    /// Converts the error value in self to a `crypto::Error`.
+    fn conv_err(self) -> Result<T> {
+        self.map_err(|e| e.into())
+    }
+}
+
 /// A cryptographic hash.
 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Hashable, Clone, EnumDiscriminants)]
 #[strum_discriminants(derive(EnumString, Display))]
@@ -235,7 +258,7 @@ struct SymParams<'a> {
 /// Returns an array of the given length filled with cryptographically random data.
 fn rand_array<const LEN: usize>() -> Result<[u8; LEN]> {
     let mut array = [0; LEN];
-    rand_bytes(&mut array).map_err(Error::from)?;
+    rand_bytes(&mut array).conv_err()?;
     Ok(array)
 }
 
@@ -275,8 +298,8 @@ impl AsymKeyPub {
     pub(crate) fn new(kind: AsymKeyKind, der: &[u8]) -> Result<AsymKeyPub> {
         let pkey = match kind {
             AsymKeyKind::Rsa => {
-                let rsa = Rsa::public_key_from_der(der).map_err(Error::from)?;
-                PKey::from_rsa(rsa).map_err(Error::from)?
+                let rsa = Rsa::public_key_from_der(der).conv_err()?;
+                PKey::from_rsa(rsa).conv_err()?
             }
         };
         Ok(AsymKeyPub { kind, pkey })
@@ -301,7 +324,7 @@ impl AsymKeyPub {
     }
 
     fn to_der(&self) -> Result<Vec<u8>> {
-        self.pkey.public_key_to_der().map_err(Error::from)
+        self.pkey.public_key_to_der().conv_err()
     }
 }
 
@@ -326,7 +349,7 @@ impl<'de> Deserialize<'de> for AsymKeyPub {
                 let der: Vec<u8> = seq.next_element()?
                     .ok_or_else(|| de::Error::missing_field(FIELDS[1]))?;
                 AsymKeyPub::new(kind, der.as_slice())
-                    .map_err(|e| de::Error::custom(e.to_string()))
+                    .map_err(de::Error::custom)
             }
         }
 
@@ -379,8 +402,8 @@ pub(crate) struct RsaPriv {
 
 impl RsaPriv {
     pub(crate) fn new(der: &[u8]) -> Result<RsaPriv> {
-        let rsa = Rsa::private_key_from_der(der).map_err(Error::from)?;
-        let pkey = PKey::from_rsa(rsa).map_err(Error::from)?;
+        let rsa = Rsa::private_key_from_der(der).conv_err()?;
+        let pkey = PKey::from_rsa(rsa).conv_err()?;
         Ok(RsaPriv { pkey })
     }
 
@@ -395,10 +418,10 @@ impl RsaPriv {
 
 impl Decrypter for RsaPriv {
     fn decrypt(&self, slice: &[u8]) -> Result<Vec<u8>> {
-        let decrypter = OsslDecrypter::new(&self.pkey).map_err(Error::from)?;
-        let buffer_len = decrypter.decrypt_len(slice).map_err(Error::from)?;
+        let decrypter = OsslDecrypter::new(&self.pkey).conv_err()?;
+        let buffer_len = decrypter.decrypt_len(slice).conv_err()?;
         let mut plaintext = vec![0; buffer_len];
-        let plaintext_len = decrypter.decrypt(slice, &mut plaintext).map_err(Error::from)?;
+        let plaintext_len = decrypter.decrypt(slice, &mut plaintext).conv_err()?;
         plaintext.truncate(plaintext_len);
         Ok(plaintext)
     }
@@ -409,12 +432,12 @@ impl Signer for RsaPriv {
         let digest = RsaPriv::digest();
         let mut signature = RsaPriv::signature_buf();
 
-        let mut signer = OsslSigner::new(digest, &self.pkey).map_err(Error::from)?;
+        let mut signer = OsslSigner::new(digest, &self.pkey).conv_err()?;
         for part in parts {
-            signer.update(part).map_err(Error::from)?;
+            signer.update(part).conv_err()?;
         }
         let buf = signature.as_mut_slice();
-        signer.sign(buf).map_err(Error::from)?;
+        signer.sign(buf).conv_err()?;
         Ok(signature)
     }
 }
@@ -435,11 +458,11 @@ impl<T: CredsPriv> ConcreteCreds<T> {
 
 impl ConcreteCreds<RsaPriv> {
     pub(crate) fn generate() -> Result<ConcreteCreds<RsaPriv>> {
-        let key_bits = 8 * u32::try_from(RSA_KEY_BYTES).map_err(|e| Error::Message(e.to_string()))?;
+        let key_bits = 8 * u32::try_from(RSA_KEY_BYTES).conv_err()?;
         let key = Rsa::generate(key_bits)?;
         // TODO: Separating the keys this way seems inefficient. Investigate alternatives.
-        let public_der = key.public_key_to_der().map_err(Error::from)?;
-        let private_der = key.private_key_to_der().map_err(Error::from)?;
+        let public_der = key.public_key_to_der().conv_err()?;
+        let private_der = key.private_key_to_der().conv_err()?;
         Ok(ConcreteCreds {
             public: AsymKeyPub::new(AsymKeyKind::Rsa, public_der.as_slice())?,
             private: RsaPriv::new(private_der.as_slice())?,
@@ -494,20 +517,20 @@ pub(crate) trait Encrypter {
 impl Encrypter for SymKey {
     fn encrypt(&self, slice: &[u8]) -> Result<Vec<u8>> {
         let SymParams { cipher, key, iv } = self.params();
-        openssl_encrypt(cipher, key, iv, slice).map_err(Error::from)
+        openssl_encrypt(cipher, key, iv, slice).conv_err()
     }
 }
 
 impl Encrypter for AsymKeyPub {
     fn encrypt(&self, slice: &[u8]) -> Result<Vec<u8>> {
-        let mut encrypter = OsslEncrypter::new(&self.pkey).map_err(Error::from)?;
+        let mut encrypter = OsslEncrypter::new(&self.pkey).conv_err()?;
         if let Some(padding) = self.padding() {
-            encrypter.set_rsa_padding(padding).map_err(Error::from)?;
+            encrypter.set_rsa_padding(padding).conv_err()?;
         }
-        let buffer_len = encrypter.encrypt_len(slice).map_err(Error::from)?;
+        let buffer_len = encrypter.encrypt_len(slice).conv_err()?;
         let mut ciphertext = vec![0; buffer_len];
         let ciphertext_len = encrypter.encrypt(slice, &mut ciphertext)
-            .map_err(Error::from)?;
+            .conv_err()?;
         ciphertext.truncate(ciphertext_len);
         Ok(ciphertext)
     }
@@ -520,7 +543,7 @@ pub(crate) trait Decrypter {
 impl Decrypter for SymKey {
     fn decrypt(&self, slice: &[u8]) -> Result<Vec<u8>> {
         let SymParams { cipher, key, iv } = self.params();
-        openssl_decrypt(cipher, key, iv, slice).map_err(Error::from)
+        openssl_decrypt(cipher, key, iv, slice).conv_err()
     }
 }
 
@@ -534,11 +557,11 @@ pub(crate) trait Verifier {
 
 impl Verifier for AsymKeyPub {
     fn verify<'a, I: Iterator<Item=&'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<bool> {
-        let mut verifier = OsslVerifier::new(self.digest(), &self.pkey).map_err(Error::from)?;
+        let mut verifier = OsslVerifier::new(self.digest(), &self.pkey).conv_err()?;
         for part in parts {
-            verifier.update(part).map_err(Error::from)?;
+            verifier.update(part).conv_err()?;
         }
-        verifier.verify(signature).map_err(Error::from)
+        verifier.verify(signature).conv_err()
     }
 }
 
@@ -601,7 +624,7 @@ pub(crate) fn decrypt_block<K: CredsPriv>(
 }
 
 fn encrypt<T: Serialize, K: Encrypter>(value: &T, key: &K) -> Result<Cryptotext<T>> {
-    let data = to_vec(value).map_err(Error::from)?;
+    let data = to_vec(value).conv_err()?;
     let vec = key.encrypt(&data);
     Ok(Cryptotext::Cipher(vec?))
 }
@@ -614,7 +637,7 @@ fn decrypt<T: Serialize + DeserializeOwned, K: CredsPriv>(
         Cryptotext::Cipher(data) => data
     };
     let vec = key.decrypt(&data);
-    from_vec(&vec?).map_err(Error::from)
+    from_vec(&vec?).conv_err()
 }
 
 #[derive(Serialize)]

+ 142 - 67
crates/btnode/src/crypto/tpm.rs

@@ -10,25 +10,34 @@ use std::{
     path::Path,
     fs::{File, OpenOptions},
     sync::{Arc, Mutex},
+    mem::size_of,
 };
 use openssl::{
     bn::BigNum,
     hash::Hasher,
     nid::Nid,
 };
-use tss_esapi_sys::{TSS2_RC, TPMT_TK_HASHCHECK };
+use tss_esapi_sys::{
+    TSS2_RC,
+    TPMT_TK_HASHCHECK,
+    TPM2_HANDLE,
+    TPM2_MAX_CAP_BUFFER,
+    TPM2_CAP
+};
 use tss_esapi::{
     Context,
     constants::{
         session_type::SessionType,
         response_code::Tss2ResponseCode,
-        tss::TPM2_RH_NULL,
+        tss::{TPM2_RH_NULL, TPM2_PERSISTENT_FIRST},
+        CapabilityType,
     },
     tcti_ldr::{TctiNameConf, TabrmdConfig},
     interface_types::{
         resource_handles::Hierarchy,
         algorithm::HashingAlgorithm,
         key_bits::RsaKeyBits,
+        dynamic_handles::Persistent,
     },
     structures::{
         Digest,
@@ -45,13 +54,75 @@ use tss_esapi::{
         SignatureScheme,
         RsaDecryptionScheme,
         Data,
+        CapabilityData,
     },
     attributes::{
         object::ObjectAttributes,
     },
-    handles::KeyHandle,
+    handles::{KeyHandle, PersistentTpmHandle},
 };
 
+impl From<tss_esapi::Error> for Error {
+    fn from(err: tss_esapi::Error) -> Self {
+        match err {
+            tss_esapi::Error::WrapperError(err) => Error::custom(err),
+            tss_esapi::Error::Tss2Error(err) => {
+               let rc = err.tss2_rc();
+               let text = tss2_rc_decode(err);
+               let string = format!("response code: {}, response text: {}", rc, text);
+               Error::custom(string)
+            }
+        }
+    }
+}
+
+impl<Guard> From<std::sync::PoisonError<Guard>> for Error {
+    fn from(error: std::sync::PoisonError<Guard>) -> Self {
+        Error::custom(error.to_string())
+    }
+}
+
+trait ContextExt {
+    fn persistent_handles(&mut self) -> Result<Vec<PersistentTpmHandle>>;
+
+    /// Returns the first free persistent handle available in the TPM.
+    fn first_free_persistent(&mut self) -> Result<Persistent> {
+        let mut handles = self.persistent_handles()?;
+        let handle = handles.pop()
+            .map_or_else(
+                || PersistentTpmHandle::try_from(TPM2_PERSISTENT_FIRST),
+                |v| PersistentTpmHandle::new(TPM2_HANDLE::from(v)+ 1))
+            .conv_err()?;
+        Ok(Persistent::Persistent(handle))
+    }
+}
+
+impl ContextExt for Context {
+    fn persistent_handles(&mut self) -> Result<Vec<PersistentTpmHandle>> {
+        let capability= CapabilityType::Handles;
+        let property = TPM2_PERSISTENT_FIRST;
+        let max = usize::try_from(TPM2_MAX_CAP_BUFFER).unwrap();
+        let count = (max - size_of::<TPM2_CAP>() - size_of::<u32>()) / size_of::<TPM2_HANDLE>();
+        let property_count = u32::try_from(count).unwrap();
+        let mut all_handles = Vec::new();
+        loop {
+            let (handles, more) = self.get_capability(capability, property, property_count)
+                .unwrap();
+            let list = match handles {
+                CapabilityData::Handles(list) => list.into_inner(),
+                _ => panic!("Unexpected capability type returned by TPM: {:?}", handles),
+            };
+            for handle in list {
+                all_handles.push(PersistentTpmHandle::try_from(handle).conv_err()?);
+            }
+            if !more {
+                break;
+            }
+        }
+        Ok(all_handles)
+    }
+}
+
 const COOKIE_LEN: usize = RSA_KEY_BYTES;
 
 struct Cookie([u8; COOKIE_LEN]);
@@ -76,7 +147,7 @@ impl Cookie {
                 }
                 let cookie = Cookie::random()?;
                 let mut file = OpenOptions::new().write(true).create_new(true).open(&cookie_path)
-                    .map_err(Error::from)?;
+                    .conv_err()?;
                 cookie.save(&mut file)?; 
                 cookie
             }
@@ -93,18 +164,18 @@ impl Cookie {
     }
 
     fn save(&self, to: &mut File) -> Result<()> {
-        to.write_all(self.as_slice()).map_err(Error::from)?;
+        to.write_all(self.as_slice()).conv_err()?;
         // Only allow read access from the current user.
-        let metadata = to.metadata().map_err(Error::from)?;
+        let metadata = to.metadata().conv_err()?;
         let mut permissions = metadata.permissions();
         permissions.set_mode(0o400);
-        to.set_permissions(permissions).map_err(Error::from)?;
+        to.set_permissions(permissions).conv_err()?;
         Ok(())
     }
 
     fn load(from: &mut File) -> Result<Cookie> {
         let mut cookie = Cookie::empty();
-        from.read_exact(cookie.as_mut_slice()).map_err(Error::from)?;
+        from.read_exact(cookie.as_mut_slice()).conv_err()?;
         Ok(cookie)
     }
 }
@@ -132,8 +203,8 @@ impl TpmCredStore {
             SymmetricDefinition::AES_256_CFB,
             HashingAlgorithm::Sha256,
         )
-        .map_err(Error::from)?
-        .ok_or_else(|| Error::Message("Received invalid session handle".to_string()))?;
+        .conv_err()?
+        .ok_or_else(|| Error::custom("Received invalid session handle"))?;
         context.set_sessions((Some(session), None, None));
         let context = Arc::new(Mutex::new(context));
         Ok(TpmCredStore { context, cookie })
@@ -150,18 +221,18 @@ impl TpmCredStore {
                 .with_sign_encrypt(true)
                 .with_restricted(false)
                 .build()
-                .map_err(Error::from)?;
+                .conv_err()?;
             let name_hashing_algorithm = HashingAlgorithm::Sha256;
             let empty = [0u8; 0];
-            let auth_policy = Digest::try_from(empty.as_slice()).map_err(Error::from)?;
+            let auth_policy = Digest::try_from(empty.as_slice()).conv_err()?;
             let parameters = PublicRsaParameters::new(
                 SymmetricDefinitionObject::Null,
                 RsaScheme::Null,
                 TpmCredStore::RSA_KEY_BITS,
-                RsaExponent::try_from(TpmCredStore::RSA_EXPONENT).map_err(Error::from)?, 
+                RsaExponent::try_from(TpmCredStore::RSA_EXPONENT).conv_err()?, 
             );
             let unique = PublicKeyRsa::try_from(self.cookie.as_slice())
-                .map_err(|e| Error::Message(e.to_string()))?;
+                .conv_err()?;
             Public::Rsa {
                 object_attributes,
                 name_hashing_algorithm,
@@ -171,8 +242,8 @@ impl TpmCredStore {
             }
         };
         let result = {
-            let mut lock = self.context.lock().map_err(|e| Error::Message(e.to_string()))?;
-            lock.create_primary(
+            let mut context = self.context.lock().conv_err()?;
+            context.create_primary(
                 Hierarchy::Endorsement,
                 template,
                 None,
@@ -180,21 +251,29 @@ impl TpmCredStore {
                 None,
                 None,
             )
-            .map_err(Error::from)?
+            .conv_err()?
         };
-        let public = match result.out_public {
+        let public = AsymKeyPub::try_from(result.out_public)?;
+
+        Ok(TpmCreds { public, handle: result.key_handle , context: self.context.clone() })
+    }
+}
+
+impl TryFrom<Public> for AsymKeyPub {
+    type Error = Error;
+
+    fn try_from(public: Public) -> Result<AsymKeyPub> {
+        match public {
             Public::Rsa { parameters, unique, .. } => {
                 let exponent_value = parameters.exponent().value();
-                let exponent = BigNum::from_u32(exponent_value).map_err(Error::from)?;
-                let modulus = BigNum::from_slice(unique.as_slice()).map_err(Error::from)?;
-                let rsa = Rsa::from_public_components(modulus, exponent).map_err(Error::from)?;
-                let pkey = PKey::from_rsa(rsa).map_err(Error::from)?;
-                AsymKeyPub { pkey, kind: AsymKeyKind::Rsa }
+                let exponent = BigNum::from_u32(exponent_value).conv_err()?;
+                let modulus = BigNum::from_slice(unique.as_slice()).conv_err()?;
+                let rsa = Rsa::from_public_components(modulus, exponent).conv_err()?;
+                let pkey = PKey::from_rsa(rsa).conv_err()?;
+                Ok(AsymKeyPub { pkey, kind: AsymKeyKind::Rsa })
             },
-            _ => return Err(Error::Message("Unexpected key type returned by TPM".to_string())),
-        };
-
-        Ok(TpmCreds { public, handle: result.key_handle , context: self.context.clone() })
+            _ => Err(Error::custom("Unsupported key type returned by TPM")),
+        }
     }
 }
 
@@ -247,7 +326,7 @@ impl MessageDigestExt for MessageDigest {
                 HashingAlgorithm::Sha3_512
             }
             else {
-                return Err(Error::Message(
+                return Err(Error::custom(
                     format!("Unsupported hash algorithm with NID: {:?}", nid)));
             };
         Ok(algo)
@@ -301,29 +380,29 @@ impl Signer for TpmCreds {
     fn sign<'a, I: Iterator<Item=&'a [u8]>>(&self, parts: I) -> Result<Signature> {
         let msg_digest = self.public.digest();
         let digest = {
-            let mut hasher = Hasher::new(msg_digest).map_err(Error::from)?;
+            let mut hasher = Hasher::new(msg_digest).conv_err()?;
             for part in parts {
-                hasher.update(part).map_err(Error::from)?;
+                hasher.update(part).conv_err()?;
             }
-            let bytes = hasher.finish().map_err(Error::from)?;
+            let bytes = hasher.finish().conv_err()?;
             let slice: &[u8] = &bytes;
-            Digest::try_from(slice).map_err(|e| Error::Message(e.to_string()))?
+            Digest::try_from(slice).conv_err()?
         };
         let validation = HashcheckTicket::null();
         let scheme = SignatureScheme::RsaSsa { hash_scheme: msg_digest.hash_scheme()? };
         let sig = {
-            let mut lock = self.context.lock().map_err(|e| Error::Message(e.to_string()))?;
-            lock.sign(self.handle, digest, scheme, validation)
-                .map_err(|e| Error::Message(e.to_string()))?
+            let mut context = self.context.lock().conv_err()?;
+            context.sign(self.handle, digest, scheme, validation)
+                .conv_err()?
         };
         let buf = match sig {
             tss_esapi::structures::Signature::RsaSsa(inner) => {
                 let mut buf = [0u8; RSA_KEY_BYTES];
                 let slice: &[u8] = inner.signature();
-                buf.as_mut_slice().write_all(slice).map_err(Error::from)?;
+                buf.as_mut_slice().write_all(slice).conv_err()?;
                 buf
             },
-            _ => return Err(Error::Message(format!("Unexpected signature type: {:?}", sig))),
+            _ => return Err(Error::custom(format!("Unexpected signature type: {:?}", sig))),
         };
 
         Ok(Signature::Rsa(buf))
@@ -332,12 +411,12 @@ impl Signer for TpmCreds {
 
 impl Decrypter for TpmCreds {
     fn decrypt(&self, slice: &[u8]) -> Result<Vec<u8>> {
-        let cipher_text = PublicKeyRsa::try_from(slice).map_err(|e| Error::Message(e.to_string()))?;
+        let cipher_text = PublicKeyRsa::try_from(slice).conv_err()?;
         let in_scheme = RsaDecryptionScheme::RsaEs;
         let empty = [0u8; 0];
-        let label = Data::try_from(empty.as_slice()).map_err(|e| Error::Message(e.to_string()))?;
+        let label = Data::try_from(empty.as_slice()).conv_err()?;
         let plain_text = {
-            let mut lock = self.context.lock().map_err(|e| Error::Message(e.to_string()))?;
+            let mut lock = self.context.lock().conv_err()?;
             lock.rsa_decrypt(self.handle, cipher_text, in_scheme, label)?
         };
         Ok(Vec::from(plain_text.value()))
@@ -401,26 +480,14 @@ impl HasResponseCode for TSS2_RC {
     }
 }
 
-impl From<tss_esapi::Error> for Error {
-    fn from(err: tss_esapi::Error) -> Self {
-        let string = match err {
-            tss_esapi::Error::WrapperError(err) => err.to_string(),
-            tss_esapi::Error::Tss2Error(err) => {
-               let rc = err.tss2_rc();
-               let text = tss2_rc_decode(err);
-               format!("response code: {}, response text: {}", rc, text)
-            }
-        };
-        Error::Message(string)
-    }
-}
-
 #[cfg(test)]
 mod test {
     use super::*;
 
     use tempdir::TempDir;
-    use std::fs::File;
+    use std::{
+        fs::File,
+    };
     use tss_esapi::{
         interface_types::{
             ecc::EccCurve,
@@ -430,18 +497,18 @@ mod test {
             EccScheme,
             KeyDerivationFunctionScheme,
             PublicEccParameters,
-        },
+        }
     };
     use ctor::ctor;
 
-    trait ContextExt {
+    trait TestContextExt {
         fn for_test() -> Result<Context>;
     }
 
-    impl ContextExt for Context {
+    impl TestContextExt for Context {
         fn for_test() -> Result<Context> {
-            let config = TabrmdConfig::from_str("bus_type=session").map_err(Error::from)?;
-            Context::new(TctiNameConf::Tabrmd(config)).map_err(Error::from)
+            let config = TabrmdConfig::from_str("bus_type=session").conv_err()?;
+            Context::new(TctiNameConf::Tabrmd(config)).conv_err()
         }
     }
 
@@ -505,7 +572,7 @@ mod test {
             SymmetricDefinition::AES_256_CFB,
             HashingAlgorithm::Sha256,
         )
-        .map_err(Error::from)
+        .conv_err()
         .expect("Failed to create session")
         .expect("Received invalid handle");
 
@@ -518,7 +585,7 @@ mod test {
                 None,
                 None,
             )
-            .map_err(Error::from)
+            .conv_err()
             .expect("create_primary failed")
             .key_handle;
 
@@ -529,11 +596,11 @@ mod test {
     /// Tests that a TPM Credential Store can be created when a cookie does not already exist.
     #[test]
     fn tpm_cred_store_new() -> Result<()> {
-        let dir = TempDir::new("btnode").map_err(Error::from)?;
+        let dir = TempDir::new("btnode").conv_err()?;
         let cookie_path = dir.path().join("cookie.bin");
         let store = TpmCredStore::new(Context::for_test()?, &cookie_path)?;
-        let cookie = File::open(&cookie_path).map_err(Error::from)?;
-        let metadata = cookie.metadata().map_err(Error::from)?;
+        let cookie = File::open(&cookie_path).conv_err()?;
+        let metadata = cookie.metadata().conv_err()?;
         let actual = metadata.permissions().mode();
         // Assert that the cookie can only be read by its owner.
         assert_eq!(0o400, 0o777 & actual);
@@ -544,7 +611,7 @@ mod test {
 
     #[test]
     fn gen_creds() -> Result<()> {
-        let dir = TempDir::new("btnode").map_err(Error::from)?;
+        let dir = TempDir::new("btnode").conv_err()?;
         let cookie_path = dir.path().join("cookie.bin");
         let store = TpmCredStore::new(Context::for_test()?, &cookie_path)?;
         store.gen_node_creds()?;
@@ -584,7 +651,7 @@ mod test {
     }
 
     fn test_store() -> Result<TpmCredStore> {
-        let dir = TempDir::new("btnode").map_err(Error::from)?;
+        let dir = TempDir::new("btnode").conv_err()?;
         let cookie_path = dir.path().join("cookie.bin");
         let store = TpmCredStore::new(Context::for_test()?, &cookie_path)?;       
         dir.close()?;
@@ -630,4 +697,12 @@ mod test {
         };
         assert_eq!(RSA_KEY_BYTES, bytes)
     }
+
+    fn list_persistent_handles() {
+        let mut context = Context::for_test().unwrap();
+        let all_handles = context.persistent_handles().unwrap();
+        for handle in all_handles.iter() {
+            println!("{:?}", handle);
+        }
+    }
 }