瀏覽代碼

Modified btfsd to allow it to use a `FileCredStore` or a `TpmCredStore`.

Matthew Carr 1 年之前
父節點
當前提交
553c941f5b

+ 1 - 0
Cargo.lock

@@ -202,6 +202,7 @@ dependencies = [
 name = "btfsd"
 version = "0.1.0"
 dependencies = [
+ "anyhow",
  "btfproto",
  "btlib",
  "btlib-tests",

+ 10 - 10
crates/btfproto-tests/src/local_fs_tests.rs

@@ -1,15 +1,11 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
-use crate::root_creds;
-
-use super::node_creds;
-
 use btfproto::{
     local_fs::{LocalFs, ModeAuthorizer},
     msg::{GrantAccess, SpecInodes},
     server::FsProvider,
 };
 use btlib::{
-    crypto::{ConcreteCreds, CredsPriv, CredsPub},
+    crypto::{Creds, CredsPriv, CredsPub},
     AuthzAttrs, BlockError, BlockPath, IssuedProcRec,
 };
 use std::{
@@ -27,7 +23,7 @@ pub fn bind_path<C: CredsPriv>(creds: C) -> BlockPath {
     writecap.bind_path()
 }
 
-pub type ConcreteFs = LocalFs<&'static ConcreteCreds, ModeAuthorizer>;
+pub type ConcreteFs = LocalFs<ModeAuthorizer>;
 
 pub struct LocalFsTest {
     dir: TempDir,
@@ -35,6 +31,14 @@ pub struct LocalFsTest {
     node_bind_path: Arc<BlockPath>,
 }
 
+fn node_creds() -> Arc<dyn Creds> {
+    Arc::new(super::node_creds())
+}
+
+fn root_creds() -> Arc<dyn Creds> {
+    Arc::new(super::root_creds())
+}
+
 impl LocalFsTest {
     pub const NODE_UID: u32 = 1000;
     pub const NODE_GID: u32 = 1000;
@@ -109,10 +113,6 @@ impl LocalFsTest {
         &self.fs
     }
 
-    pub fn creds() -> ConcreteCreds {
-        node_creds().clone()
-    }
-
     pub fn from(&self) -> &Arc<BlockPath> {
         &self.node_bind_path
     }

+ 67 - 69
crates/btfproto/src/local_fs.rs

@@ -342,47 +342,50 @@ mod private {
         }
     }
 
-    impl<C, B: Deref<Target = InodeTableValue<C>>> Deref for BlockGuard<B> {
-        type Target = FileBlock<C>;
+    impl<B: Deref<Target = InodeTableValue>> Deref for BlockGuard<B> {
+        type Target = FileBlock<Arc<dyn Creds>>;
         fn deref(&self) -> &Self::Target {
             self.inner.block.get_ref()
         }
     }
 
-    impl<C, B: DerefMut<Target = InodeTableValue<C>>> DerefMut for BlockGuard<B> {
+    impl<B: DerefMut<Target = InodeTableValue>> DerefMut for BlockGuard<B> {
         fn deref_mut(&mut self) -> &mut Self::Target {
             self.inner.block.get_mut()
         }
     }
 
-    impl<C, B: Deref<Target = InodeTableValue<C>>> Size for BlockGuard<B> {
+    impl<B: Deref<Target = InodeTableValue>> Size for BlockGuard<B> {
         fn size(&self) -> io::Result<Option<u64>> {
             self.inner.block.size()
         }
     }
 
-    impl<C, B: Deref<Target = InodeTableValue<C>>> ReadAt for BlockGuard<B> {
+    impl<B: Deref<Target = InodeTableValue>> ReadAt for BlockGuard<B> {
         fn read_at(&self, pos: u64, buf: &mut [u8]) -> io::Result<usize> {
             self.inner.block.get_ref().read_at(pos, buf)
         }
     }
 
-    impl<C: 'static, B: Deref<Target = InodeTableValue<C>>> AsRef<BlockMeta> for BlockGuard<B> {
+    impl<B: Deref<Target = InodeTableValue>> AsRef<BlockMeta> for BlockGuard<B> {
         fn as_ref(&self) -> &BlockMeta {
             self.inner.block.as_ref()
         }
     }
 
-    pub struct InodeTableValue<C> {
-        block: Accessor<FileBlock<C>>,
+    pub struct InodeTableValue {
+        block: Accessor<FileBlock<Arc<dyn Creds>>>,
         handle_values: HashMap<Handle, HandleValue>,
         next_handle: Handle,
         lookup_counts: HashMap<Arc<BlockPath>, u64>,
         delete: bool,
     }
 
-    impl<C: Signer + Principaled + Decrypter> InodeTableValue<C> {
-        fn new(block: Accessor<FileBlock<C>>, opener: Arc<BlockPath>) -> InodeTableValue<C> {
+    impl InodeTableValue {
+        fn new(
+            block: Accessor<FileBlock<Arc<dyn Creds>>>,
+            opener: Arc<BlockPath>,
+        ) -> InodeTableValue {
             let mut lookup_counts = HashMap::with_capacity(1);
             lookup_counts.insert(opener, 1);
             Self {
@@ -404,11 +407,11 @@ mod private {
                 .ok_or_else(|| Self::invalid_handle_err(handle))
         }
 
-        fn block(&self) -> &FileBlock<C> {
+        fn block(&self) -> &FileBlock<Arc<dyn Creds>> {
             self.block.get_ref()
         }
 
-        fn block_mut(&mut self) -> &mut FileBlock<C> {
+        fn block_mut(&mut self) -> &mut FileBlock<Arc<dyn Creds>> {
             self.block.get_mut()
         }
 
@@ -427,7 +430,8 @@ mod private {
             &'a self,
             from: &BlockPath,
             handle: Handle,
-        ) -> Result<HandleGuard<&FileBlock<C>, MutexGuard<'a, EmptyAccessor>>> {
+        ) -> Result<HandleGuard<&FileBlock<Arc<dyn Creds>>, MutexGuard<'a, EmptyAccessor>>>
+        {
             let value = self.value(handle)?;
             let block = self.block();
             value.guard(from, block).await
@@ -450,7 +454,8 @@ mod private {
             &'a mut self,
             from: &BlockPath,
             handle: Handle,
-        ) -> Result<HandleGuard<&mut FileBlock<C>, MutexGuard<'a, EmptyAccessor>>> {
+        ) -> Result<HandleGuard<&mut FileBlock<Arc<dyn Creds>>, MutexGuard<'a, EmptyAccessor>>>
+        {
             let value = self
                 .handle_values
                 .get(&handle)
@@ -529,50 +534,41 @@ mod private {
         }
     }
 
-    type InodeTable<C> = HashMap<Inode, Arc<RwLock<InodeTableValue<C>>>>;
-    type OwnedTableLock<C> = OwnedRwLockReadGuard<InodeTable<C>>;
-    type TableLock<'a, C> = RwLockReadGuard<'a, InodeTable<C>>;
+    type InodeTable = HashMap<Inode, Arc<RwLock<InodeTableValue>>>;
+    type OwnedTableLock = OwnedRwLockReadGuard<InodeTable>;
+    type TableLock<'a> = RwLockReadGuard<'a, InodeTable>;
 
     struct TableGuard<G> {
         table_guard: G,
     }
 
-    impl<C> TableGuard<OwnedRwLockReadGuard<C>> {
-        async fn new_owned(table: Arc<RwLock<InodeTable<C>>>) -> TableGuard<OwnedTableLock<C>> {
+    impl TableGuard<OwnedTableLock> {
+        async fn new_owned(table: Arc<RwLock<InodeTable>>) -> TableGuard<OwnedTableLock> {
             let table_guard = table.read_owned().await;
             TableGuard { table_guard }
         }
     }
 
-    impl<'a, C> TableGuard<TableLock<'a, C>> {
-        async fn new(table: &'a RwLock<InodeTable<C>>) -> TableGuard<TableLock<'a, C>> {
+    impl<'a> TableGuard<TableLock<'a>> {
+        async fn new(table: &'a RwLock<InodeTable>) -> TableGuard<TableLock<'a>> {
             let table_guard = table.read().await;
             TableGuard { table_guard }
         }
     }
 
-    impl<C, G: Deref<Target = InodeTable<C>>> TableGuard<G> {
-        fn get_value(&self, inode: Inode) -> Result<&Arc<RwLock<InodeTableValue<C>>>> {
+    impl<G: Deref<Target = InodeTable>> TableGuard<G> {
+        fn get_value(&self, inode: Inode) -> Result<&Arc<RwLock<InodeTableValue>>> {
             self.table_guard
                 .get(&inode)
                 .ok_or_else(|| bterr!(Error::NotOpen(inode)))
         }
 
-        async fn read<'a>(&'a self, inode: Inode) -> Result<RwLockReadGuard<'a, InodeTableValue<C>>>
-        where
-            C: 'a,
-        {
+        async fn read(&self, inode: Inode) -> Result<RwLockReadGuard<'_, InodeTableValue>> {
             let value = self.get_value(inode)?;
             Ok(value.read().await)
         }
 
-        async fn write<'a>(
-            &'a self,
-            inode: Inode,
-        ) -> Result<RwLockWriteGuard<'a, InodeTableValue<C>>>
-        where
-            C: 'a,
-        {
+        async fn write(&self, inode: Inode) -> Result<RwLockWriteGuard<'_, InodeTableValue>> {
             let value = self.get_value(inode)?;
             Ok(value.write().await)
         }
@@ -594,33 +590,33 @@ mod private {
     }
 
     /// Structure for managing the part of a blocktree which is stored in the local filesystem.
-    pub struct LocalFs<C, A> {
+    pub struct LocalFs<A> {
         /// The path to the directory in the local filesystem where this blocktree is located.
         path: PathBuf,
         /// A map from inode numbers to their reference counts.
-        inodes: Arc<RwLock<InodeTable<C>>>,
+        inodes: Arc<RwLock<InodeTable>>,
         /// An in-memory copy of the superblock.
         sb: Superblock,
         /// The credentials this blocktree instance will use for all cryptographic operations.
-        creds: C,
+        creds: Arc<dyn Creds>,
         authorizer: A,
         root_principal: Principal,
     }
 
-    impl<C, A> LocalFs<C, A> {
+    impl<A> LocalFs<A> {
         /// The maximum number of directory entries that can be returned in any given call to
         /// `read_dir`.
         const READ_DIR_LIMIT: usize = 1024;
     }
 
-    impl<C: Creds + 'static, A: Authorizer> LocalFs<C, A> {
+    impl<A: Authorizer> LocalFs<A> {
         /// Creates a new empty blocktree at the given path.
         pub async fn new_empty(
             btdir: PathBuf,
             generation: u64,
-            creds: C,
+            creds: Arc<dyn Creds>,
             authorizer: A,
-        ) -> Result<LocalFs<C, A>> {
+        ) -> Result<LocalFs<A>> {
             let writecap = creds.writecap().ok_or(BlockError::MissingWritecap)?;
             let mut root_block_path = writecap.root_block_path();
             let root_principal = writecap.root_principal();
@@ -706,7 +702,11 @@ mod private {
         }
 
         /// Opens an existing blocktree stored at the given path.
-        pub fn new_existing(btdir: PathBuf, creds: C, authorizer: A) -> Result<LocalFs<C, A>> {
+        pub fn new_existing(
+            btdir: PathBuf,
+            creds: Arc<dyn Creds>,
+            authorizer: A,
+        ) -> Result<LocalFs<A>> {
             let writecap = creds.writecap().ok_or(BlockError::MissingWritecap)?;
             let root_block_path = writecap.root_block_path();
             let root_principal = writecap.root_principal();
@@ -741,12 +741,12 @@ mod private {
         fn new(
             btdir: PathBuf,
             sb: Superblock,
-            sb_block: Accessor<FileBlock<C>>,
-            root_block: Accessor<FileBlock<C>>,
-            creds: C,
+            sb_block: Accessor<FileBlock<Arc<dyn Creds>>>,
+            root_block: Accessor<FileBlock<Arc<dyn Creds>>>,
+            creds: Arc<dyn Creds>,
             authorizer: A,
             root_principal: Principal,
-        ) -> Result<LocalFs<C, A>> {
+        ) -> Result<LocalFs<A>> {
             let mut inodes = HashMap::with_capacity(1);
             let empty_path = Arc::new(BlockPath::default());
             inodes.insert(
@@ -802,11 +802,11 @@ mod private {
 
         fn open_superblock<P: AsRef<Path>>(
             btdir: P,
-            creds: C,
+            creds: Arc<dyn Creds>,
             block_path: BlockPath,
             root_principal: &Principal,
             create_new: bool,
-        ) -> Result<Accessor<FileBlock<C>>> {
+        ) -> Result<Accessor<FileBlock<Arc<dyn Creds>>>> {
             const HASH: HashKind = HashKind::Sha2_256;
             let mut buf = [0u8; HASH.len()];
             HASH.digest(
@@ -853,12 +853,12 @@ mod private {
         fn open_block<P: AsRef<Path>>(
             btdir: P,
             inode: Inode,
-            creds: C,
+            creds: Arc<dyn Creds>,
             block_path: BlockPath,
             parent_key: Option<SymKey>,
             inode_hash: HashKind,
             inode_key: &[u8],
-        ) -> Result<Accessor<FileBlock<C>>> {
+        ) -> Result<Accessor<FileBlock<Arc<dyn Creds>>>> {
             let path = Self::block_path(&btdir, inode, inode_hash, inode_key)?;
             Self::ensure_parent_created(&path)?;
             let file = std::fs::OpenOptions::new()
@@ -871,10 +871,10 @@ mod private {
 
         fn open_block_file(
             file: File,
-            creds: C,
+            creds: Arc<dyn Creds>,
             block_path: BlockPath,
             parent_key: Option<SymKey>,
-        ) -> Result<Accessor<FileBlock<C>>> {
+        ) -> Result<Accessor<FileBlock<Arc<dyn Creds>>>> {
             let block = BlockOpenOptions::new()
                 .with_creds(creds)
                 .with_encrypt(true)
@@ -885,7 +885,7 @@ mod private {
             Ok(block)
         }
 
-        async fn table_guard(&self) -> TableGuard<TableLock<'_, C>> {
+        async fn table_guard(&self) -> TableGuard<TableLock<'_>> {
             TableGuard::new(&self.inodes).await
         }
 
@@ -927,7 +927,7 @@ mod private {
             inode: Inode,
             block_path: BlockPath,
             parent_key: Option<SymKey>,
-        ) -> Result<TableGuard<OwnedTableLock<C>>> {
+        ) -> Result<TableGuard<OwnedTableLock>> {
             {
                 let table_guard = self.inodes.clone().read_owned().await;
                 if table_guard.contains_key(&inode) {
@@ -1095,7 +1095,7 @@ mod private {
         }
 
         async fn lookup_inode_in<'a>(
-            table_guard: &'a TableGuard<TableLock<'a, C>>,
+            table_guard: &'a TableGuard<TableLock<'a>>,
             parent: Inode,
             name: &str,
         ) -> Result<Inode> {
@@ -1109,7 +1109,7 @@ mod private {
         /// Returns a pair of inodes, where the first inode is the inode referred to by the given
         /// path, and the second is the parent inode.
         async fn lookup_inode<'a, 'b, I: Iterator<Item = &'a str>>(
-            table_guard: &'b TableGuard<TableLock<'b, C>>,
+            table_guard: &'b TableGuard<TableLock<'b>>,
             components: I,
         ) -> Result<(Inode, Option<Inode>)> {
             const ROOT: Inode = SpecInodes::RootDir as Inode;
@@ -1169,30 +1169,30 @@ mod private {
         }
     }
 
-    unsafe impl<C: Sync, A: Sync> Sync for LocalFs<C, A> {}
+    unsafe impl<A: Sync> Sync for LocalFs<A> {}
 
     /// An owned guard which allows read access to file data.
-    pub struct BufGuard<C> {
+    pub struct BufGuard {
         offset: u64,
         size: u64,
         // Note that handle must come before _table to ensure the guards are dropped in the correct
         // order.
         handle: HandleGuard<
-            BlockGuard<OwnedRwLockReadGuard<InodeTableValue<C>>>,
+            BlockGuard<OwnedRwLockReadGuard<InodeTableValue>>,
             OwnedMutexGuard<EmptyAccessor>,
         >,
-        _table: OwnedTableLock<C>,
+        _table: OwnedTableLock,
     }
 
-    impl<C: 'static + Principaled + Signer + Decrypter> BufGuard<C> {
+    impl BufGuard {
         async fn new(
-            table: Arc<RwLock<InodeTable<C>>>,
+            table: Arc<RwLock<InodeTable>>,
             from: &BlockPath,
             inode: Inode,
             handle: Handle,
             offset: u64,
             mut size: u64,
-        ) -> Result<BufGuard<C>> {
+        ) -> Result<BufGuard> {
             let table = table.read_owned().await;
             let entry = table.get(&inode).ok_or(Error::NotOpen(inode))?;
             let inode_guard = {
@@ -1242,16 +1242,14 @@ mod private {
         }
     }
 
-    impl<C: 'static + Principaled + Decrypter + Signer> Deref for BufGuard<C> {
+    impl Deref for BufGuard {
         type Target = [u8];
         fn deref(&self) -> &Self::Target {
             self.handle.get_buf(self.offset, self.size).unwrap()
         }
     }
 
-    impl<C: 'static + Creds + Clone + Send + Sync, A: 'static + Authorizer + Send + Sync> FsProvider
-        for LocalFs<C, A>
-    {
+    impl<A: 'static + Authorizer + Send + Sync> FsProvider for LocalFs<A> {
         type LookupFut<'c> = impl 'c + Send + Future<Output = Result<LookupReply>>;
         fn lookup<'c>(&'c self, from: &'c Arc<BlockPath>, msg: Lookup<'c>) -> Self::LookupFut<'c> {
             async move {
@@ -1455,7 +1453,7 @@ mod private {
             }
         }
 
-        type ReadGuard = BufGuard<C>;
+        type ReadGuard = BufGuard;
         type ReadFut<'c> = impl 'c + Send + Future<Output = Result<Self::ReadGuard>>;
         fn read<'c>(&'c self, from: &'c Arc<BlockPath>, msg: Read) -> Self::ReadFut<'c> {
             async move {
@@ -1728,7 +1726,7 @@ mod private {
                 } = msg;
                 debug!("write_meta: inode {inode}, handle {:?}", handle);
                 let authz_attrs = self.authz_attrs(from).await?;
-                let cb = |block: &mut FileBlock<C>| {
+                let cb = |block: &mut FileBlock<Arc<dyn Creds>>| {
                     self.authorizer.can_write(&AuthzContext::new(
                         from,
                         &authz_attrs,

+ 2 - 2
crates/btfproto/src/server.rs

@@ -292,8 +292,8 @@ pub fn new_fs_server<C, P>(
     provider: Arc<P>,
 ) -> Result<impl Receiver>
 where
-    C: 'static + Send + Sync + Creds,
-    P: 'static + Send + Sync + FsProvider,
+    C: 'static + Creds,
+    P: 'static + FsProvider,
 {
     receiver(ip_addr, creds, ServerCallback::new(provider))
 }

+ 1 - 0
crates/btfsd/Cargo.toml

@@ -13,6 +13,7 @@ tokio = { version = "1.24.2", features = ["rt", "rt-multi-thread", "time"] }
 log = "0.4.17"
 env_logger = "0.9.0"
 btlib-tests = { path = "../btlib-tests" }
+anyhow = { version = "1.0.66", features = ["std", "backtrace"] }
 
 [dev-dependencies]
 swtpm-harness = { path = "../swtpm-harness" }

+ 102 - 43
crates/btfsd/src/config.rs

@@ -1,16 +1,82 @@
+use btlib::bterr;
+
 // SPDX-License-Identifier: AGPL-3.0-or-later
 use super::DEFAULT_CONFIG;
-use std::{
-    net::IpAddr,
-    path::{Path, PathBuf},
-};
+use std::{net::IpAddr, path::PathBuf, str::FromStr};
+
+struct StrParser<'a>(&'a str);
+
+impl<'a> StrParser<'a> {
+    fn assert(&mut self, token: &str) -> btlib::Result<()> {
+        if self.0.starts_with(token) {
+            self.0 = &self.0[token.len()..];
+            Ok(())
+        } else {
+            Err(bterr!("string does not start with {token}"))
+        }
+    }
+
+    fn consume_up_to(&mut self, stop: char) -> btlib::Result<&str> {
+        let mut count = 0;
+        for c in self.0.chars() {
+            if c == stop {
+                break;
+            }
+            count += 1;
+        }
+        let output = &self.0[..count];
+        let new_start = self.0.len().min(count + 1);
+        self.0 = &self.0[new_start..];
+        Ok(output)
+    }
+
+    fn remaining(&self) -> &str {
+        self.0
+    }
+}
+
+pub enum CredStoreCfg {
+    File {
+        path: PathBuf,
+    },
+    Tpm {
+        tpm_state_path: PathBuf,
+        tabrmd: String,
+    },
+}
+
+impl FromStr for CredStoreCfg {
+    type Err = btlib::Error;
+    fn from_str(value: &str) -> Result<Self, Self::Err> {
+        let mut parser = StrParser(value);
+        parser.assert("kind=")?;
+        let kind = parser.consume_up_to(',')?;
+        match kind {
+            "file" => {
+                parser.assert("path=")?;
+                let path = PathBuf::from(parser.remaining());
+                Ok(CredStoreCfg::File { path })
+            }
+            "tpm" => {
+                parser.assert("tpm_state_path=")?;
+                let tpm_state_path = PathBuf::from(parser.consume_up_to(',')?);
+                let tabrmd = parser.remaining().to_string();
+                Ok(CredStoreCfg::Tpm {
+                    tpm_state_path,
+                    tabrmd,
+                })
+            }
+            _ => Err(bterr!(
+                "unrecognized CredStore kind (expected 'file' or 'tpm'): {kind}"
+            )),
+        }
+    }
+}
 
 pub struct Config {
+    pub cred_store_cfg: CredStoreCfg,
     pub ip_addr: IpAddr,
-    pub tabrmd: String,
-    pub tpm_state_path: PathBuf,
     pub block_dir: PathBuf,
-    pub use_swtpm: bool,
 }
 
 impl Config {
@@ -19,13 +85,21 @@ impl Config {
     }
 }
 
+trait OptionExt<'a> {
+    fn str_unwrap_or(self, default: &'a str) -> &'a str;
+}
+
+impl<'a> OptionExt<'a> for &'a Option<String> {
+    fn str_unwrap_or(self, default: &'a str) -> &'a str {
+        self.as_ref().map(|e| e.as_str()).unwrap_or(default)
+    }
+}
+
 #[derive(Default)]
 pub struct ConfigBuilder {
-    pub ip_addr: Option<IpAddr>,
-    pub tabrmd: Option<String>,
-    pub tpm_state_path: Option<PathBuf>,
-    pub block_dir: Option<PathBuf>,
-    pub use_swtpm: Option<bool>,
+    pub cred_store: Option<String>,
+    pub ip_addr: Option<String>,
+    pub block_dir: Option<String>,
 }
 
 impl ConfigBuilder {
@@ -33,58 +107,43 @@ impl ConfigBuilder {
         Self::default()
     }
 
-    pub fn with_ip_addr(mut self, ip_addr: Option<IpAddr>) -> Self {
-        self.ip_addr = ip_addr;
-        self
-    }
-
-    pub fn with_tabrmd(mut self, tabrmd: Option<String>) -> Self {
-        self.tabrmd = tabrmd;
+    pub fn with_cred_store(mut self, cred_store: Option<String>) -> Self {
+        self.cred_store = cred_store;
         self
     }
 
-    pub fn with_tpm_state_path(mut self, tpm_state_path: Option<PathBuf>) -> Self {
-        self.tpm_state_path = tpm_state_path;
+    pub fn with_ip_addr(mut self, ip_addr: Option<String>) -> Self {
+        self.ip_addr = ip_addr;
         self
     }
 
-    pub fn with_block_dir(mut self, block_dir: Option<PathBuf>) -> Self {
+    pub fn with_block_dir(mut self, block_dir: Option<String>) -> Self {
         self.block_dir = block_dir;
         self
     }
 
-    pub fn with_use_swtpm(mut self, use_swtpm: Option<bool>) -> Self {
-        self.use_swtpm = use_swtpm;
-        self
-    }
-
     pub fn build(self) -> Config {
+        let cred_store_cfg =
+            CredStoreCfg::from_str(self.cred_store.str_unwrap_or(DEFAULT_CONFIG.cred_store))
+                .unwrap();
+        let ip_addr = IpAddr::from_str(self.ip_addr.str_unwrap_or(DEFAULT_CONFIG.ip_addr)).unwrap();
+        let block_dir = PathBuf::from(self.block_dir.str_unwrap_or(DEFAULT_CONFIG.block_dir));
         Config {
-            ip_addr: self.ip_addr.unwrap_or(DEFAULT_CONFIG.ip_addr),
-            tabrmd: self.tabrmd.unwrap_or(DEFAULT_CONFIG.tabrmd.to_owned()),
-            tpm_state_path: self
-                .tpm_state_path
-                .unwrap_or(Path::new(DEFAULT_CONFIG.tpm_state_path).to_owned()),
-            block_dir: self
-                .block_dir
-                .unwrap_or(Path::new(DEFAULT_CONFIG.block_dir).to_owned()),
-            use_swtpm: self.use_swtpm.unwrap_or(true),
+            cred_store_cfg,
+            ip_addr,
+            block_dir,
         }
     }
 }
 
 pub struct ConfigRef<'a> {
-    pub ip_addr: IpAddr,
-    pub tabrmd: &'a str,
-    pub tpm_state_path: &'a str,
+    pub cred_store: &'a str,
+    pub ip_addr: &'a str,
     pub block_dir: &'a str,
-    pub use_swtpm: &'a str,
 }
 
 pub struct Envvars<'a> {
+    pub cred_store: &'a str,
     pub ip_addr: &'a str,
-    pub tabrmd: &'a str,
-    pub tpm_state_path: &'a str,
     pub block_dir: &'a str,
-    pub use_swtpm: &'a str,
 }

+ 106 - 85
crates/btfsd/src/main.rs

@@ -7,39 +7,26 @@ use btfproto::{
 };
 use btlib::{
     config_helpers::from_envvar,
-    crypto::{tpm::TpmCredStore, CredStore, Creds},
+    crypto::{file_cred_store::FileCredStore, tpm::TpmCredStore, CredStore, Creds},
     log::BuilderExt,
 };
-use btlib_tests::TpmCredStoreHarness;
 use btmsg::Receiver;
-use config::{Config, ConfigRef, Envvars};
-use std::{
-    net::{IpAddr, Ipv4Addr},
-    path::PathBuf,
-    str::FromStr,
-    sync::Arc, env::args,
-};
+use config::{Config, ConfigRef, CredStoreCfg, Envvars};
+use std::{path::PathBuf, sync::Arc};
 
 const ENVVARS: Envvars<'static> = Envvars {
+    cred_store: "BTFSD_CREDSTORE",
     ip_addr: "BTFSD_IPADDR",
-    tabrmd: "BTFSD_TABRMD",
-    tpm_state_path: "BTFSD_TPMSTATE",
     block_dir: "BTFSD_BLOCKDIR",
-    use_swtpm: "BTFSD_USESWTPM",
 };
 
 const DEFAULT_CONFIG: ConfigRef<'static> = ConfigRef {
-    ip_addr: IpAddr::V4(Ipv4Addr::LOCALHOST),
-    tabrmd: "bus_type=session",
-    tpm_state_path: "./state/tpm_state",
+    cred_store: "kind=tpm,tpm_state_file=./state/tpm_state,bus_type=session",
+    ip_addr: "127.0.0.1",
     block_dir: "./state/bt",
-    use_swtpm: "true",
 };
 
-async fn provider<C: 'static + Send + Sync + Creds>(
-    block_dir: PathBuf,
-    creds: C,
-) -> impl FsProvider {
+async fn provider(block_dir: PathBuf, creds: Arc<dyn Creds>) -> impl FsProvider {
     if block_dir.exists() {
         LocalFs::new_existing(block_dir, creds, ModeAuthorizer).unwrap()
     } else {
@@ -51,8 +38,19 @@ async fn provider<C: 'static + Send + Sync + Creds>(
 }
 
 async fn receiver(config: Config) -> impl Receiver {
-    let cred_store = TpmCredStore::from_tabrmd(&config.tabrmd, config.tpm_state_path).unwrap();
-    let node_creds = cred_store.node_creds().unwrap();
+    let node_creds: Arc<dyn Creds> = match config.cred_store_cfg {
+        CredStoreCfg::File { path } => {
+            let cred_store = FileCredStore::new(path).unwrap();
+            cred_store.node_creds().unwrap()
+        }
+        CredStoreCfg::Tpm {
+            tpm_state_path,
+            tabrmd,
+        } => {
+            let cred_store = TpmCredStore::from_tabrmd(&tabrmd, tpm_state_path).unwrap();
+            Arc::new(cred_store.node_creds().unwrap())
+        }
+    };
     let provider = Arc::new(provider(config.block_dir, node_creds.clone()).await);
     new_fs_server(config.ip_addr, Arc::new(node_creds), provider).unwrap()
 }
@@ -60,40 +58,11 @@ async fn receiver(config: Config) -> impl Receiver {
 #[tokio::main]
 async fn main() {
     env_logger::Builder::from_default_env().btformat().init();
-
-    let ip_addr = from_envvar(ENVVARS.ip_addr)
-        .unwrap()
-        .map(|txt| IpAddr::from_str(&txt).unwrap());
-    let tabrmd = from_envvar(ENVVARS.tabrmd).unwrap();
-    let tpm_state_path = from_envvar(ENVVARS.tpm_state_path)
-        .unwrap()
-        .map(PathBuf::from);
-    let block_dir = from_envvar(ENVVARS.block_dir).unwrap().map(PathBuf::from);
-    let use_swtpm = from_envvar(ENVVARS.use_swtpm)
-        .unwrap()
-        .map(|str| bool::from_str(&str).unwrap());
-    let mut config = Config::builder()
-        .with_ip_addr(ip_addr)
-        .with_tabrmd(tabrmd)
-        .with_tpm_state_path(tpm_state_path)
-        .with_block_dir(block_dir)
-        .with_use_swtpm(use_swtpm)
+    let config = Config::builder()
+        .with_cred_store(from_envvar(ENVVARS.cred_store).unwrap())
+        .with_ip_addr(from_envvar(ENVVARS.ip_addr).unwrap())
+        .with_block_dir(from_envvar(ENVVARS.block_dir).unwrap())
         .build();
-    let _swtpm = if config.use_swtpm {
-        log::debug!("starting swtpm");
-        let root_pw = if let Some(root_pw) = args().next() {
-            root_pw
-        } else {
-            panic!("when BTFSD_USESWTPM is true, the root password must be given as the first argument")
-        };
-        let swtpm = TpmCredStoreHarness::new(root_pw).unwrap(); 
-        config.tabrmd = swtpm.swtpm().tabrmd_config().to_owned();
-        config.tpm_state_path = swtpm.swtpm().state_path().to_owned();
-        Some(swtpm)
-    } else {
-        None
-    };
-
     let receiver = receiver(config).await;
     log::debug!("ready to accept connections");
     receiver.complete().unwrap().await.unwrap();
@@ -109,9 +78,10 @@ mod tests {
         log::BuilderExt,
         AuthzAttrs, BlockMetaSecrets, Epoch, IssuedProcRec, Principaled, ProcRec,
     };
-    use btlib_tests::TpmCredStoreHarness;
+    use btlib_tests::{CredStoreTestingExt, TpmCredStoreHarness};
     use btmsg::{BlockAddr, Transmitter};
     use btserde::from_slice;
+    use std::net::{IpAddr, Ipv4Addr};
     use std::{future::ready, net::Ipv6Addr, time::Duration};
     use swtpm_harness::SwtpmHarness;
     use tempdir::TempDir;
@@ -124,6 +94,45 @@ mod tests {
         env_logger::Builder::from_default_env().btformat().init();
     }
 
+    struct FileTestCase<R, T> {
+        client: FsClient<T>,
+        _rx: R,
+        _dir: TempDir,
+    }
+
+    async fn file_test_case(
+        dir: TempDir,
+        ip_addr: IpAddr,
+    ) -> FileTestCase<impl Receiver, impl Transmitter> {
+        let file_store_path = dir.path().join("cred_store");
+        let cred_store_cfg = CredStoreCfg::File {
+            path: file_store_path.clone(),
+        };
+
+        {
+            let cred_store = FileCredStore::new(file_store_path).unwrap();
+            cred_store.provision(ROOT_PASSWD).unwrap();
+        }
+        let config = Config {
+            cred_store_cfg,
+            ip_addr,
+            block_dir: dir.path().join(BT_DIR),
+        };
+        let rx = receiver(config).await;
+        let tx = rx.transmitter(rx.addr().clone()).await.unwrap();
+        let client = FsClient::new(tx);
+        FileTestCase {
+            _dir: dir,
+            _rx: rx,
+            client,
+        }
+    }
+
+    async fn new_file_test_case() -> FileTestCase<impl Receiver, impl Transmitter> {
+        let dir = TempDir::new("btfsd").unwrap();
+        file_test_case(dir, LOCALHOST).await
+    }
+
     struct TestCase<R, T> {
         client: FsClient<T>,
         rx: R,
@@ -135,17 +144,20 @@ mod tests {
     const LOCALHOST: IpAddr = IpAddr::V6(Ipv6Addr::LOCALHOST);
     const BT_DIR: &str = "bt";
 
-    async fn test_case(
+    async fn tpm_test_case(
         dir: TempDir,
         harness: TpmCredStoreHarness,
         ip_addr: IpAddr,
     ) -> TestCase<impl Receiver, impl Transmitter> {
+        let swtpm = harness.swtpm();
+        let cred_store_cfg = CredStoreCfg::Tpm {
+            tpm_state_path: swtpm.state_path().to_owned(),
+            tabrmd: swtpm.tabrmd_config().to_owned(),
+        };
         let config = Config {
+            cred_store_cfg,
             ip_addr,
-            tabrmd: harness.swtpm().tabrmd_config().to_owned(),
-            tpm_state_path: harness.swtpm().state_path().to_owned(),
             block_dir: dir.path().join(BT_DIR),
-            use_swtpm: false,
         };
         let rx = receiver(config).await;
         let tx = rx.transmitter(rx.addr().clone()).await.unwrap();
@@ -158,10 +170,10 @@ mod tests {
         }
     }
 
-    async fn new_case() -> TestCase<impl Receiver, impl Transmitter> {
+    async fn new_tpm_test_case() -> TestCase<impl Receiver, impl Transmitter> {
         let dir = TempDir::new("btfsd").unwrap();
         let harness = TpmCredStoreHarness::new(ROOT_PASSWD.to_owned()).unwrap();
-        test_case(dir, harness, LOCALHOST).await
+        tpm_test_case(dir, harness, LOCALHOST).await
     }
 
     async fn existing_case<R: Receiver, T: Transmitter>(
@@ -174,21 +186,18 @@ mod tests {
             harness: _harness,
             ..
         } = case;
-        test_case(_dir, _harness, IpAddr::V4(Ipv4Addr::LOCALHOST)).await
+        tpm_test_case(_dir, _harness, IpAddr::V4(Ipv4Addr::LOCALHOST)).await
     }
 
     #[allow(dead_code)]
     async fn manual_test() {
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         case.rx.complete().unwrap().await.unwrap();
     }
 
-    #[tokio::test]
-    async fn create_write_read() {
+    async fn create_write_read(client: FsClient<impl Transmitter>) {
         const FILENAME: &str = "file.txt";
         const EXPECTED: &[u8] = b"potato";
-        let case = new_case().await;
-        let client = case.client;
 
         let CreateReply { inode, handle, .. } = client
             .create(
@@ -214,11 +223,23 @@ mod tests {
         assert_eq!(EXPECTED, &actual);
     }
 
+    #[tokio::test]
+    async fn create_write_read_with_tpm() {
+        let case = new_tpm_test_case().await;
+        create_write_read(case.client).await;
+    }
+
+    #[tokio::test]
+    async fn create_write_read_with_file() {
+        let case = new_file_test_case().await;
+        create_write_read(case.client).await;
+    }
+
     #[tokio::test]
     async fn read_full_sector() {
         const FILENAME: &str = "file.txt";
         const EXPECTED: &[u8] = b"prawn crisps";
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = case.client;
 
         let CreateReply {
@@ -255,7 +276,7 @@ mod tests {
     async fn read_from_different_instance() {
         const FILENAME: &str = "file.txt";
         const EXPECTED: &[u8] = b"potato";
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = &case.client;
 
         let CreateReply { inode, handle, .. } = client
@@ -296,7 +317,7 @@ mod tests {
     #[tokio::test]
     async fn create_lookup() {
         const FILENAME: &str = "file.txt";
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = case.client;
 
         let CreateReply {
@@ -322,7 +343,7 @@ mod tests {
     #[tokio::test]
     async fn open_existing() {
         const FILENAME: &str = "file.txt";
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = case.client;
 
         let CreateReply { inode, .. } = client
@@ -344,7 +365,7 @@ mod tests {
     async fn write_flush_close_read() {
         const FILENAME: &str = "lyrics.txt";
         const EXPECTED: &[u8] = b"Fate, or something better";
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = &case.client;
 
         let CreateReply { inode, handle, .. } = client
@@ -379,7 +400,7 @@ mod tests {
     async fn link() {
         const FIRSTNAME: &str = "Jean-Luc";
         const LASTNAME: &str = "Picard";
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = &case.client;
 
         let CreateReply { inode, .. } = client
@@ -420,7 +441,7 @@ mod tests {
     async fn unlink() {
         const FIRSTNAME: &str = "Jean-Luc";
         const LASTNAME: &str = "Picard";
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = &case.client;
 
         let CreateReply { inode, .. } = client
@@ -464,7 +485,7 @@ mod tests {
     #[tokio::test]
     async fn delete() {
         const FILENAME: &str = "MANIFESTO.tex";
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = &case.client;
 
         client
@@ -504,7 +525,7 @@ mod tests {
     async fn read_meta() {
         const FILENAME: &str = "kibosh.txt";
         const EXPECTED: u32 = 0o600;
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = &case.client;
 
         let before_create = Epoch::now();
@@ -557,7 +578,7 @@ mod tests {
             ctime: 87239.into(),
             tags: Vec::new(),
         };
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = &case.client;
 
         let CreateReply { inode, handle, .. } = client
@@ -584,7 +605,7 @@ mod tests {
     async fn allocate_when_empty() {
         const FILENAME: &str = "output.dat";
         const EXPECTED: &[u8] = &[0u8; 8];
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = &case.client;
 
         let CreateReply { inode, handle, .. } = client
@@ -615,7 +636,7 @@ mod tests {
     async fn allocate_with_data_present() {
         const FILENAME: &str = "output.dat";
         const EXPECTED: &[u8] = &[1, 1, 1, 1, 0, 0, 0, 0];
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = &case.client;
 
         let CreateReply { inode, handle, .. } = client
@@ -646,7 +667,7 @@ mod tests {
     #[tokio::test]
     async fn forget() {
         const FILENAME: &str = "seed.dat";
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = &case.client;
 
         let CreateReply { inode, handle, .. } = client
@@ -668,7 +689,7 @@ mod tests {
     #[tokio::test]
     async fn add_readcap() {
         const FILENAME: &str = "net";
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = &case.client;
         let creds = ConcreteCreds::generate().unwrap();
 
@@ -690,7 +711,7 @@ mod tests {
 
     #[tokio::test]
     async fn grant_access_to_root() {
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = &case.client;
         let mut creds = ConcreteCreds::generate().unwrap();
         let root_creds = case.harness.root_creds().unwrap();
@@ -740,7 +761,7 @@ mod tests {
     #[tokio::test]
     async fn grant_access_to_non_root_dir() {
         const DIRNAME: &str = "var";
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = &case.client;
         let mut creds = ConcreteCreds::generate().unwrap();
         let root_creds = case.harness.root_creds().unwrap();
@@ -807,7 +828,7 @@ mod tests {
         const ROOT_FILE: &str = "root.txt";
         const USER_FILE: &str = "user.txt";
         let user_tpm = SwtpmHarness::new().unwrap();
-        let case = new_case().await;
+        let case = new_tpm_test_case().await;
         let client = &case.client;
         let root_creds = case.harness.root_creds().unwrap();
         let user_creds = {

+ 4 - 6
crates/btfuse/src/main.rs

@@ -62,10 +62,7 @@ fn node_creds(state_file: PathBuf, tabrmd_cfg: &str) -> Result<TpmCreds> {
     cred_store.node_creds()
 }
 
-async fn local_provider<C: 'static + Creds + Send + Sync>(
-    btdir: PathBuf,
-    node_creds: C,
-) -> Result<impl FsProvider> {
+async fn local_provider(btdir: PathBuf, node_creds: Arc<dyn Creds>) -> Result<impl FsProvider> {
     btdir.try_create_dir()?;
     let empty = fs::read_dir(&btdir)?.next().is_none();
     if empty {
@@ -89,8 +86,9 @@ async fn run_daemon(
     mounted_signal: Option<oneshot::Sender<()>>,
     stop_signal: Option<oneshot::Receiver<()>>,
 ) {
-    let node_creds =
-        node_creds(config.tpm_state_file, &config.tabrmd).expect("failed to get node creds");
+    let node_creds = Arc::new(
+        node_creds(config.tpm_state_file, &config.tabrmd).expect("failed to get node creds")
+    );
     let fallback_path = {
         let writecap = node_creds
             .writecap()

+ 20 - 0
crates/btlib-tests/src/cred_store_testing_ext.rs

@@ -0,0 +1,20 @@
+use std::time::Duration;
+
+use btlib::{
+    crypto::{CredStore, Creds},
+    Epoch, Principaled, Result,
+};
+
+pub trait CredStoreTestingExt: CredStore {
+    /// Generates new root credentials and issues the node credentials a writecap using them. The
+    /// given password is used to secure the root credentials.
+    fn provision(&self, root_password: &str) -> Result<()> {
+        let root_creds = self.gen_root_creds(root_password)?;
+        let mut node_creds = self.node_creds()?;
+        let expires = Epoch::now() + Duration::from_secs(3600);
+        let writecap = root_creds.issue_writecap(node_creds.principal(), vec![], expires)?;
+        self.assign_node_writecap(&mut node_creds, writecap)
+    }
+}
+
+impl<T: CredStore> CredStoreTestingExt for T {}

+ 3 - 0
crates/btlib-tests/src/lib.rs

@@ -3,3 +3,6 @@ mod tpm_cred_store_harness;
 pub use tpm_cred_store_harness::TpmCredStoreHarness;
 
 pub mod fs_queries;
+
+mod cred_store_testing_ext;
+pub use cred_store_testing_ext::CredStoreTestingExt;

+ 251 - 144
crates/btlib/src/crypto.rs

@@ -41,7 +41,7 @@ use std::{
     fmt::Display,
     io::{Read, Write},
     marker::PhantomData,
-    ops::Deref,
+    ops::{Deref, DerefMut},
     sync::Arc,
 };
 use strum_macros::{Display, EnumDiscriminants, FromRepr};
@@ -113,6 +113,8 @@ pub enum Error {
     SignatureMismatch(Box<SignatureMismatch>),
     /// This variant is used to convey errors that originated in an underlying library.
     Library(Box<dyn ::std::error::Error + Send + Sync + 'static>),
+    /// Occurs when an attempt is made to finish an [Op] that is already finished.
+    OpAlreadyFinished,
 }
 
 impl Error {
@@ -164,6 +166,7 @@ impl Display for Error {
                 )
             }
             Error::Library(err) => err.fmt(f),
+            Error::OpAlreadyFinished => write!(f, "operation is already finished"),
         }
     }
 }
@@ -197,19 +200,23 @@ pub fn rand_vec(len: usize) -> Result<Vec<u8>> {
 }
 
 /// An ongoing Init-Update-Finish operation.
-pub trait Op: Sized {
-    /// The type of the argument given to `init`.
-    type Arg;
-
-    /// Initialize a new operation.
-    fn init(arg: Self::Arg) -> Result<Self>;
-
+pub trait Op {
     /// Update this operation using the given data.
     fn update(&mut self, data: &[u8]) -> Result<()>;
 
     /// Finish this operation and write the result into the given buffer. If the given buffer is not
     /// large enough the implementation must return Error::IncorrectSize.
-    fn finish_into(self, buf: &mut [u8]) -> Result<usize>;
+    fn finish_into(&mut self, buf: &mut [u8]) -> Result<usize>;
+}
+
+impl<T: ?Sized + Op, P: DerefMut<Target = T>> Op for P {
+    fn update(&mut self, data: &[u8]) -> Result<()> {
+        self.deref_mut().update(data)
+    }
+
+    fn finish_into(&mut self, buf: &mut [u8]) -> Result<usize> {
+        self.deref_mut().finish_into(buf)
+    }
 }
 
 /// An ongoing hash hash operation.
@@ -221,7 +228,7 @@ pub trait HashOp: Op {
     fn kind(&self) -> HashKind;
 
     /// Finish this operation and return a hash type containing the result.
-    fn finish(self) -> Result<Self::Hash>;
+    fn finish(&mut self) -> Result<Self::Hash>;
 }
 
 // A hash operation which uses OpenSSL.
@@ -231,10 +238,8 @@ pub struct OsslHashOp<H> {
     kind: HashKind,
 }
 
-impl<H> Op for OsslHashOp<H> {
-    type Arg = HashKind;
-
-    fn init(arg: Self::Arg) -> Result<Self> {
+impl<H> OsslHashOp<H> {
+    fn new(arg: HashKind) -> Result<Self> {
         let hasher = Hasher::new(arg.into())?;
         let phantom = PhantomData;
         Ok(OsslHashOp {
@@ -243,12 +248,14 @@ impl<H> Op for OsslHashOp<H> {
             kind: arg,
         })
     }
+}
 
+impl<H> Op for OsslHashOp<H> {
     fn update(&mut self, data: &[u8]) -> Result<()> {
         Ok(self.hasher.update(data)?)
     }
 
-    fn finish_into(mut self, buf: &mut [u8]) -> Result<usize> {
+    fn finish_into(&mut self, buf: &mut [u8]) -> Result<usize> {
         if buf.len() < self.kind.len() {
             return Err(bterr!(Error::IncorrectSize {
                 expected: self.kind.len(),
@@ -269,7 +276,7 @@ impl<H: Hash + From<DigestBytes>> HashOp for OsslHashOp<H> {
         self.kind
     }
 
-    fn finish(mut self) -> Result<Self::Hash> {
+    fn finish(&mut self) -> Result<Self::Hash> {
         let digest = self.hasher.finish()?;
         Ok(H::from(digest))
     }
@@ -294,7 +301,7 @@ impl<T, Op: HashOp> HashStream<T, Op> {
 
     /// Finish this hash operation and write the result into the given buffer. The number of bytes
     /// written is returned.
-    pub fn finish_into(self, buf: &mut [u8]) -> Result<usize> {
+    pub fn finish_into(&mut self, buf: &mut [u8]) -> Result<usize> {
         if self.update_failed {
             return Err(bterr!(
                 "HashStream::finish_into can't produce result due to HashOp update failure",
@@ -304,7 +311,7 @@ impl<T, Op: HashOp> HashStream<T, Op> {
     }
 
     /// Finish this hash operation and return the resulting hash.
-    pub fn finish(self) -> Result<Op::Hash> {
+    pub fn finish(&mut self) -> Result<Op::Hash> {
         if self.update_failed {
             return Err(bterr!(
                 "HashStream::finish can't produce result due to HashOp update failure",
@@ -424,7 +431,7 @@ impl Hash for Sha2_256 {
     }
 
     fn start_op(&self) -> Result<Self::Op> {
-        OsslHashOp::init(Self::KIND)
+        OsslHashOp::new(Self::KIND)
     }
 }
 
@@ -481,7 +488,7 @@ impl Hash for Sha2_512 {
     }
 
     fn start_op(&self) -> Result<Self::Op> {
-        OsslHashOp::init(Self::KIND)
+        OsslHashOp::new(Self::KIND)
     }
 }
 
@@ -715,7 +722,7 @@ impl Hash for VarHash {
     }
 
     fn start_op(&self) -> Result<Self::Op> {
-        VarHashOp::init(self.kind())
+        VarHashOp::new(self.kind())
     }
 }
 
@@ -752,19 +759,19 @@ pub struct VarHashOp {
     hasher: Hasher,
 }
 
-impl Op for VarHashOp {
-    type Arg = HashKind;
-
-    fn init(arg: Self::Arg) -> Result<Self> {
+impl VarHashOp {
+    pub fn new(arg: HashKind) -> Result<Self> {
         let hasher = Hasher::new(arg.into())?;
         Ok(VarHashOp { kind: arg, hasher })
     }
+}
 
+impl Op for VarHashOp {
     fn update(&mut self, data: &[u8]) -> Result<()> {
         Ok(self.hasher.update(data)?)
     }
 
-    fn finish_into(mut self, buf: &mut [u8]) -> Result<usize> {
+    fn finish_into(&mut self, buf: &mut [u8]) -> Result<usize> {
         btensure!(
             buf.len() >= self.kind.len(),
             bterr!(Error::IncorrectSize {
@@ -786,7 +793,7 @@ impl HashOp for VarHashOp {
         self.kind
     }
 
-    fn finish(mut self) -> Result<Self::Hash> {
+    fn finish(&mut self) -> Result<Self::Hash> {
         let digest = self.hasher.finish()?;
         let mut hash: VarHash = self.kind.into();
         hash.as_mut().copy_from_slice(digest.as_ref());
@@ -1586,13 +1593,12 @@ impl Decrypter for AsymKey<Private, Encrypt> {
 }
 
 impl Signer for AsymKey<Private, Sign> {
-    type Op<'s> = OsslSignOp<'s>;
-
-    fn init_sign(&self) -> Result<Self::Op<'_>> {
-        OsslSignOp::init((self.scheme, self.pkey.as_ref()))
+    fn init_sign(&self) -> Result<Box<dyn '_ + SignOp>> {
+        let op = OsslSignOp::new((self.scheme, self.pkey.as_ref()))?;
+        Ok(Box::new(op))
     }
 
-    fn sign<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I) -> Result<Signature> {
+    fn sign(&self, parts: &mut dyn Iterator<Item = &[u8]>) -> Result<Signature> {
         let mut signer = OsslSigner::new(self.scheme.message_digest(), &self.pkey)?;
         if let Some(padding) = self.scheme.padding() {
             signer.set_rsa_padding(padding)?;
@@ -1611,13 +1617,12 @@ impl Signer for AsymKey<Private, Sign> {
 }
 
 impl Verifier for AsymKey<Public, Sign> {
-    type Op<'v> = OsslVerifyOp<'v>;
-
-    fn init_verify(&self) -> Result<Self::Op<'_>> {
-        OsslVerifyOp::init((self.scheme, self.pkey.as_ref()))
+    fn init_verify(&self) -> Result<Box<dyn '_ + VerifyOp>> {
+        let op = OsslVerifyOp::init(self.scheme, self.pkey.as_ref())?;
+        Ok(Box::new(op))
     }
 
-    fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
+    fn verify(&self, parts: &mut dyn Iterator<Item = &[u8]>, signature: &[u8]) -> Result<()> {
         let mut verifier = OsslVerifier::new(self.scheme.message_digest(), &self.pkey)?;
         if let Some(padding) = self.scheme.padding() {
             verifier.set_rsa_padding(padding)?;
@@ -1710,11 +1715,10 @@ impl Decrypter for AsymKeyPair<Encrypt> {
 }
 
 impl Signer for AsymKeyPair<Sign> {
-    type Op<'s> = <AsymKey<Private, Sign> as Signer>::Op<'s>;
-    fn init_sign(&self) -> Result<Self::Op<'_>> {
+    fn init_sign(&self) -> Result<Box<dyn '_ + SignOp>> {
         self.private.init_sign()
     }
-    fn sign<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I) -> Result<Signature> {
+    fn sign(&self, parts: &mut dyn Iterator<Item = &[u8]>) -> Result<Signature> {
         self.private.sign(parts)
     }
     fn kind(&self) -> Sign {
@@ -1723,13 +1727,11 @@ impl Signer for AsymKeyPair<Sign> {
 }
 
 impl Verifier for AsymKeyPair<Sign> {
-    type Op<'v> = OsslVerifyOp<'v>;
-
-    fn init_verify(&self) -> Result<Self::Op<'_>> {
+    fn init_verify(&self) -> Result<Box<dyn '_ + VerifyOp>> {
         self.public.init_verify()
     }
 
-    fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
+    fn verify(&self, parts: &mut dyn Iterator<Item = &[u8]>, signature: &[u8]) -> Result<()> {
         self.public.verify(parts, signature)
     }
 
@@ -1757,13 +1759,11 @@ impl Encrypter for ConcretePub {
 }
 
 impl Verifier for ConcretePub {
-    type Op<'v> = OsslVerifyOp<'v>;
-
-    fn init_verify(&self) -> Result<Self::Op<'_>> {
+    fn init_verify(&self) -> Result<Box<dyn '_ + VerifyOp>> {
         self.sign.init_verify()
     }
 
-    fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
+    fn verify(&self, parts: &mut dyn Iterator<Item = &[u8]>, signature: &[u8]) -> Result<()> {
         self.sign.verify(parts, signature)
     }
 
@@ -1828,13 +1828,11 @@ impl ConcreteCreds {
 }
 
 impl Verifier for ConcreteCreds {
-    type Op<'v> = OsslVerifyOp<'v>;
-
-    fn init_verify(&self) -> Result<Self::Op<'_>> {
+    fn init_verify(&self) -> Result<Box<dyn '_ + VerifyOp>> {
         self.sign.init_verify()
     }
 
-    fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
+    fn verify(&self, parts: &mut dyn Iterator<Item = &[u8]>, signature: &[u8]) -> Result<()> {
         self.sign.verify(parts, signature)
     }
 
@@ -1869,13 +1867,11 @@ impl CredsPub for ConcreteCreds {
 }
 
 impl Signer for ConcreteCreds {
-    type Op<'s> = <AsymKeyPair<Sign> as Signer>::Op<'s>;
-
-    fn init_sign(&self) -> Result<Self::Op<'_>> {
+    fn init_sign(&self) -> Result<Box<dyn '_ + SignOp>> {
         self.sign.init_sign()
     }
 
-    fn sign<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I) -> Result<Signature> {
+    fn sign(&self, parts: &mut dyn Iterator<Item = &[u8]>) -> Result<Signature> {
         self.sign.sign(parts)
     }
 
@@ -1900,7 +1896,7 @@ pub trait Encrypter {
     fn encrypt(&self, slice: &[u8]) -> Result<Vec<u8>>;
 }
 
-impl<T: Deref<Target = C>, C: Encrypter> Encrypter for T {
+impl<T: ?Sized + Encrypter, P: Deref<Target = T>> Encrypter for P {
     fn encrypt(&self, slice: &[u8]) -> Result<Vec<u8>> {
         self.deref().encrypt(slice)
     }
@@ -1922,7 +1918,7 @@ pub trait Decrypter {
     fn decrypt(&self, slice: &[u8]) -> Result<Vec<u8>>;
 }
 
-impl<T: Deref<Target = C>, C: Decrypter> Decrypter for T {
+impl<T: ?Sized + Decrypter, P: Deref<Target = T>> Decrypter for P {
     fn decrypt(&self, slice: &[u8]) -> Result<Vec<u8>> {
         self.deref().decrypt(slice)
     }
@@ -1943,7 +1939,7 @@ pub trait SignOp: Op {
     fn scheme(&self) -> Sign;
 
     /// Finishes this signature operation and returns a new signature containing the result.
-    fn finish(self) -> Result<Signature> {
+    fn finish(&mut self) -> Result<Signature> {
         let scheme = self.scheme();
         let mut sig = Signature::empty(scheme);
         self.finish_into(sig.as_mut())?;
@@ -1951,15 +1947,19 @@ pub trait SignOp: Op {
     }
 }
 
+impl<T: ?Sized + SignOp, P: DerefMut<Target = T>> SignOp for P {
+    fn scheme(&self) -> Sign {
+        self.deref().scheme()
+    }
+}
+
 pub struct OsslSignOp<'a> {
     signer: OsslSigner<'a>,
     scheme: Sign,
 }
 
-impl<'a> Op for OsslSignOp<'a> {
-    type Arg = (Sign, &'a PKeyRef<Private>);
-
-    fn init(arg: Self::Arg) -> Result<Self> {
+impl<'a> OsslSignOp<'a> {
+    pub fn new(arg: (Sign, &'a PKeyRef<Private>)) -> Result<Self> {
         let scheme = arg.0;
         let mut signer = OsslSigner::new(arg.0.message_digest(), arg.1)?;
         if let Some(padding) = scheme.padding() {
@@ -1967,12 +1967,14 @@ impl<'a> Op for OsslSignOp<'a> {
         }
         Ok(OsslSignOp { signer, scheme })
     }
+}
 
+impl<'a> Op for OsslSignOp<'a> {
     fn update(&mut self, data: &[u8]) -> Result<()> {
         Ok(self.signer.update(data)?)
     }
 
-    fn finish_into(self, buf: &mut [u8]) -> Result<usize> {
+    fn finish_into(&mut self, buf: &mut [u8]) -> Result<usize> {
         Ok(self.signer.sign(buf)?)
     }
 }
@@ -1991,14 +1993,14 @@ pub struct SignWrite<T, Op> {
 
 impl<T, Op: SignOp> SignWrite<T, Op> {
     pub fn new(inner: T, op: Op) -> Self {
-        SignWrite { inner, op }
+        SignWrite { inner, op, }
     }
 
-    pub fn finish_into(self, buf: &mut [u8]) -> Result<(usize, T)> {
+    pub fn finish_into(mut self, buf: &mut [u8]) -> Result<(usize, T)> {
         Ok((self.op.finish_into(buf)?, self.inner))
     }
 
-    pub fn finish(self) -> Result<(Signature, T)> {
+    pub fn finish(mut self) -> Result<(Signature, T)> {
         Ok((self.op.finish()?, self.inner))
     }
 }
@@ -2015,22 +2017,13 @@ impl<T: Write, Op: SignOp> Write for SignWrite<T, Op> {
 }
 
 pub trait Signer {
-    type Op<'s>: SignOp
-    where
-        Self: 's;
-
     /// Starts a new signing operation and returns the struct representing it.
-    fn init_sign(&self) -> Result<Self::Op<'_>>;
+    fn init_sign(&self) -> Result<Box<dyn '_ + SignOp>>;
+
     /// Returns a signature over the given parts. It's critical that subsequent invocations
     /// of this method on the same instance return a [Signature] with `data` fields of the same
     /// length.
-    fn sign<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I) -> Result<Signature>;
-
-    fn ser_sign<T: Serialize>(&self, value: &T) -> Result<Signed<T>> {
-        let data = to_vec(value)?;
-        let sig = self.sign(std::iter::once(data.as_slice()))?;
-        Ok(Signed::new(data, sig))
-    }
+    fn sign(&self, parts: &mut dyn Iterator<Item = &[u8]>) -> Result<Signature>;
 
     fn sign_writecap(&self, writecap: &mut Writecap) -> Result<()> {
         let signed = self.ser_sign(&writecap.body)?;
@@ -2038,22 +2031,15 @@ pub trait Signer {
         Ok(())
     }
 
-    fn ser_sign_into<T: Serialize>(&self, value: &T, buf: &mut Vec<u8>) -> Result<Signature> {
-        write_to(value, &mut *buf)?;
-        self.sign(std::iter::once(buf.as_slice()))
-    }
-
     fn kind(&self) -> Sign;
 }
 
-impl<T: Signer> Signer for &T {
-    type Op<'s> = T::Op<'s> where Self: 's;
-
-    fn init_sign(&self) -> Result<Self::Op<'_>> {
+impl<T: ?Sized + Signer> Signer for &T {
+    fn init_sign(&self) -> Result<Box<dyn '_ + SignOp>> {
         (*self).init_sign()
     }
 
-    fn sign<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I) -> Result<Signature> {
+    fn sign(&self, parts: &mut dyn Iterator<Item = &[u8]>) -> Result<Signature> {
         (*self).sign(parts)
     }
 
@@ -2062,14 +2048,12 @@ impl<T: Signer> Signer for &T {
     }
 }
 
-impl<T: Signer> Signer for Arc<T> {
-    type Op<'s> = T::Op<'s> where Self: 's;
-
-    fn init_sign(&self) -> Result<Self::Op<'_>> {
+impl<T: ?Sized + Signer> Signer for Arc<T> {
+    fn init_sign(&self) -> Result<Box<dyn '_ + SignOp>> {
         self.deref().init_sign()
     }
 
-    fn sign<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I) -> Result<Signature> {
+    fn sign(&self, parts: &mut dyn Iterator<Item = &[u8]>) -> Result<Signature> {
         self.deref().sign(parts)
     }
 
@@ -2078,40 +2062,64 @@ impl<T: Signer> Signer for Arc<T> {
     }
 }
 
-pub trait VerifyOp: Sized {
-    type Arg;
+pub trait SignerExt: Signer {
+    fn ser_sign<T: Serialize>(&self, value: &T) -> Result<Signed<T>> {
+        let data = to_vec(value)?;
+        let sig = self.sign(&mut std::iter::once(data.as_slice()))?;
+        Ok(Signed::new(data, sig))
+    }
+
+    fn ser_sign_into<T: Serialize>(&self, value: &T, buf: &mut Vec<u8>) -> Result<Signature> {
+        write_to(value, &mut *buf)?;
+        self.sign(&mut std::iter::once(buf.as_slice()))
+    }
+}
 
-    fn init(arg: Self::Arg) -> Result<Self>;
+impl<T: ?Sized + Signer> SignerExt for T {}
 
+pub trait VerifyOp {
     fn update(&mut self, data: &[u8]) -> Result<()>;
 
-    fn finish(self, sig: &[u8]) -> Result<()>;
+    fn finish(&mut self, sig: &[u8]) -> Result<()>;
 
     fn scheme(&self) -> Sign;
 }
 
+impl<T: ?Sized + VerifyOp, P: DerefMut<Target = T>> VerifyOp for P {
+    fn update(&mut self, data: &[u8]) -> Result<()> {
+        self.deref_mut().update(data)
+    }
+
+    fn finish(&mut self, sig: &[u8]) -> Result<()> {
+        self.deref_mut().finish(sig)
+    }
+
+    fn scheme(&self) -> Sign {
+        self.deref().scheme()
+    }
+}
+
 pub struct OsslVerifyOp<'a> {
     verifier: OsslVerifier<'a>,
     scheme: Sign,
 }
 
-impl<'a> VerifyOp for OsslVerifyOp<'a> {
-    type Arg = (Sign, &'a PKeyRef<Public>);
-
-    fn init(arg: Self::Arg) -> Result<Self> {
-        let scheme = arg.0;
-        let mut verifier = OsslVerifier::new(scheme.message_digest(), arg.1)?;
+impl<'a> OsslVerifyOp<'a> {
+    pub fn init(scheme: Sign, pkey: &'a PKeyRef<Public>) -> Result<Self> {
+        let mut verifier = OsslVerifier::new(scheme.message_digest(), pkey)?;
         if let Some(padding) = scheme.padding() {
             verifier.set_rsa_padding(padding)?;
         }
         Ok(OsslVerifyOp { verifier, scheme })
     }
+}
 
+impl<'a> VerifyOp for OsslVerifyOp<'a> {
     fn update(&mut self, data: &[u8]) -> Result<()> {
         Ok(self.verifier.update(data)?)
     }
 
-    fn finish(self, sig: &[u8]) -> Result<()> {
+    fn finish(&mut self, sig: &[u8]) -> Result<()> {
         match self.verifier.verify(sig) {
             Ok(true) => Ok(()),
             Ok(false) => Err(bterr!(Error::InvalidSignature)),
@@ -2139,7 +2147,7 @@ impl<T: Read, Op: VerifyOp> VerifyRead<T, Op> {
         }
     }
 
-    pub fn finish(self, sig: &[u8]) -> std::result::Result<T, (T, crate::Error)> {
+    pub fn finish(mut self, sig: &[u8]) -> std::result::Result<T, (T, crate::Error)> {
         if self.update_failed {
             return Err((
                 self.inner,
@@ -2170,46 +2178,33 @@ impl<T: Read, Op: VerifyOp> Read for VerifyRead<T, Op> {
 }
 
 pub trait Verifier {
-    type Op<'v>: VerifyOp
-    where
-        Self: 'v;
+    fn init_verify(&self) -> Result<Box<dyn '_ + VerifyOp>>;
 
-    fn init_verify(&self) -> Result<Self::Op<'_>>;
-
-    fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()>;
-
-    fn ser_verify<T: Serialize>(&self, value: &T, signature: &[u8]) -> Result<()> {
-        let data = to_vec(value)?;
-        self.verify(std::iter::once(data.as_slice()), signature)
-    }
+    fn verify(&self, parts: &mut dyn Iterator<Item = &[u8]>, signature: &[u8]) -> Result<()>;
 
     fn kind(&self) -> Sign;
 }
 
-impl<T: Verifier> Verifier for &T {
-    type Op<'v> = T::Op<'v> where Self: 'v;
-
-    fn init_verify(&self) -> Result<Self::Op<'_>> {
-        (*self).init_verify()
+impl<V: ?Sized + Verifier> Verifier for &V {
+    fn init_verify<'a>(&'a self) -> Result<Box<dyn 'a + VerifyOp>> {
+        self.deref().init_verify()
     }
 
-    fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
-        (*self).verify(parts, signature)
+    fn verify(&self, parts: &mut dyn Iterator<Item = &[u8]>, signature: &[u8]) -> Result<()> {
+        self.deref().verify(parts, signature)
     }
 
     fn kind(&self) -> Sign {
-        (*self).kind()
+        self.deref().kind()
     }
 }
 
-impl<T: Verifier> Verifier for Arc<T> {
-    type Op<'v> = T::Op<'v> where Self: 'v;
-
-    fn init_verify(&self) -> Result<Self::Op<'_>> {
+impl<V: ?Sized + Verifier> Verifier for Arc<V> {
+    fn init_verify<'a>(&'a self) -> Result<Box<dyn 'a + VerifyOp>> {
         self.deref().init_verify()
     }
 
-    fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
+    fn verify(&self, parts: &mut dyn Iterator<Item = &[u8]>, signature: &[u8]) -> Result<()> {
         self.deref().verify(parts, signature)
     }
 
@@ -2218,6 +2213,17 @@ impl<T: Verifier> Verifier for Arc<T> {
     }
 }
 
+pub trait VerifierExt: Verifier {
+    fn ser_verify<T: Serialize>(&self, value: &T, signature: &[u8]) -> Result<()>;
+}
+
+impl<V: ?Sized + Verifier> VerifierExt for V {
+    fn ser_verify<T: Serialize>(&self, value: &T, signature: &[u8]) -> Result<()> {
+        let data = to_vec(value)?;
+        self.verify(&mut std::iter::once(data.as_slice()), signature)
+    }
+}
+
 /// Trait for types which can be used as public credentials.
 pub trait CredsPub: Verifier + Encrypter + Principaled {
     /// Returns a reference to the public signing key which can be used to verify signatures.
@@ -2230,7 +2236,7 @@ pub trait CredsPub: Verifier + Encrypter + Principaled {
     }
 }
 
-impl<T: CredsPub> CredsPub for &T {
+impl<T: ?Sized + CredsPub> CredsPub for &T {
     fn public_sign(&self) -> &AsymKey<Public, Sign> {
         (*self).public_sign()
     }
@@ -2240,7 +2246,7 @@ impl<T: CredsPub> CredsPub for &T {
     }
 }
 
-impl<T: CredsPub> CredsPub for Arc<T> {
+impl<T: ?Sized + CredsPub> CredsPub for Arc<T> {
     fn public_sign(&self) -> &AsymKey<Public, Sign> {
         self.deref().public_sign()
     }
@@ -2271,20 +2277,20 @@ pub trait CredsPriv: Decrypter + Signer {
     }
 }
 
-impl<T: CredsPriv> CredsPriv for &T {
+impl<T: ?Sized + CredsPriv> CredsPriv for &T {
     fn writecap(&self) -> Option<&Writecap> {
         (*self).writecap()
     }
 }
 
-impl<T: CredsPriv> CredsPriv for Arc<T> {
+impl<T: ?Sized + CredsPriv> CredsPriv for Arc<T> {
     fn writecap(&self) -> Option<&Writecap> {
         self.deref().writecap()
     }
 }
 
 /// Trait for types which contain both public and private credentials.
-pub trait Creds: CredsPriv + CredsPub + Clone {
+pub trait Creds: CredsPriv + CredsPub + Send + Sync {
     fn issue_writecap(
         &self,
         issued_to: Principal,
@@ -2321,7 +2327,7 @@ pub trait Creds: CredsPriv + CredsPub + Clone {
     }
 }
 
-impl<C: CredsPriv + CredsPub + Clone> Creds for C {}
+impl<C: CredsPriv + CredsPub + Clone + Send + Sync> Creds for C {}
 
 /// A trait for types which store credentials.
 pub trait CredStore {
@@ -2366,6 +2372,53 @@ pub trait CredStore {
         -> Result<()>;
 }
 
+impl<T: ?Sized + CredStore, P: Deref<Target = T>> CredStore for P {
+    type CredHandle = T::CredHandle;
+    type ExportedCreds = T::ExportedCreds;
+
+    fn node_creds(&self) -> Result<Self::CredHandle> {
+        self.deref().node_creds()
+    }
+
+    fn root_creds(&self, password: &str) -> Result<Self::CredHandle> {
+        self.deref().root_creds(password)
+    }
+
+    fn gen_root_creds(&self, password: &str) -> Result<Self::CredHandle> {
+        self.deref().gen_root_creds(password)
+    }
+
+    fn storage_key(&self) -> Result<AsymKeyPub<Encrypt>> {
+        self.deref().storage_key()
+    }
+
+    fn export_root_creds(
+        &self,
+        root_creds: &Self::CredHandle,
+        password: &str,
+        new_parent: &AsymKeyPub<Encrypt>,
+    ) -> Result<Self::ExportedCreds> {
+        self.deref()
+            .export_root_creds(root_creds, password, new_parent)
+    }
+
+    fn import_root_creds(
+        &self,
+        password: &str,
+        exported: Self::ExportedCreds,
+    ) -> Result<Self::CredHandle> {
+        self.deref().import_root_creds(password, exported)
+    }
+
+    fn assign_node_writecap(
+        &self,
+        handle: &mut Self::CredHandle,
+        writecap: Writecap,
+    ) -> Result<()> {
+        self.deref().assign_node_writecap(handle, writecap)
+    }
+}
+
 impl BlockMeta {
     /// Validates that this metadata struct contains a valid writecap, that this writecap is
     /// permitted to write to the path of this block and that the signature in this metadata struct
@@ -2437,7 +2490,7 @@ impl Writecap {
             write_to(&writecap.body, &mut sig_input_buf)
                 .map_err(|e| bterr!(WritecapAuthzErr::Serde(e.to_string())))?;
             writecap.body.signing_key.verify(
-                std::iter::once(sig_input_buf.as_slice()),
+                &mut std::iter::once(sig_input_buf.as_slice()),
                 writecap.signature.as_slice(),
             )?;
             match &writecap.next {
@@ -2494,8 +2547,8 @@ mod tests {
         let key = make_key_pair();
         let header = b"About: lyrics".as_slice();
         let message = b"Everything that feels so good is bad bad bad.".as_slice();
-        let signature = key.sign([header, message].into_iter())?;
-        key.verify([header, message].into_iter(), signature.as_slice())
+        let signature = key.sign(&mut [header, message].into_iter())?;
+        key.verify(&mut [header, message].into_iter(), signature.as_slice())
     }
 
     #[test]
@@ -2659,7 +2712,7 @@ mod tests {
                 .unwrap();
             expected
         };
-        let mut op = OsslHashOp::<H>::init(kind).unwrap();
+        let mut op = OsslHashOp::<H>::new(kind).unwrap();
 
         for part in parts.iter() {
             op.update(part.as_slice()).unwrap();
@@ -2689,7 +2742,7 @@ mod tests {
                 .unwrap();
             expected
         };
-        let op = OsslHashOp::<Sha2_512>::init(Sha2_512::KIND).unwrap();
+        let op = OsslHashOp::<Sha2_512>::new(Sha2_512::KIND).unwrap();
         let mut wrap = HashStream::new(cursor, op);
 
         for part in parts.iter() {
@@ -2712,7 +2765,7 @@ mod tests {
                 .unwrap();
             expected
         };
-        let mut op = VarHashOp::init(HashKind::Sha2_512).unwrap();
+        let mut op = VarHashOp::new(HashKind::Sha2_512).unwrap();
 
         for part in parts.iter() {
             op.update(part.as_slice()).unwrap();
@@ -2735,7 +2788,7 @@ mod tests {
         }
         let sig = sign_op.finish().expect("finish failed");
 
-        keys.verify(get_parts(), sig.as_ref())
+        keys.verify(&mut get_parts(), sig.as_ref())
             .expect("verify failed");
     }
 
@@ -2756,7 +2809,7 @@ mod tests {
         let (sig, cursor) = sign_write.finish().expect("finish failed");
         let array = cursor.into_inner();
 
-        keys.verify(std::iter::once(array.as_slice()), sig.as_ref())
+        keys.verify(&mut std::iter::once(array.as_slice()), sig.as_ref())
             .expect("verify failed");
     }
 
@@ -2834,4 +2887,58 @@ mod tests {
             assert_eq!(48, private_len);
         }
     }
+
+    mod obj_safety {
+        use super::*;
+
+        #[test]
+        fn op_obj_safe() {
+            assert_obj_safe!(Op);
+        }
+
+        #[test]
+        fn sign_op_obj_safe() {
+            assert_obj_safe!(SignOp);
+        }
+
+        #[test]
+        fn verify_op_obj_safe() {
+            assert_obj_safe!(VerifyOp);
+        }
+
+        #[test]
+        fn encrypter_obj_safe() {
+            assert_obj_safe!(Encrypter);
+        }
+
+        #[test]
+        fn decrypter_obj_safe() {
+            assert_obj_safe!(Decrypter);
+        }
+
+        #[test]
+        fn verifier_obj_safe() {
+            assert_obj_safe!(Verifier);
+        }
+
+        #[test]
+        fn signer_obj_safe() {
+            assert_obj_safe!(Signer);
+        }
+
+        #[test]
+        fn creds_pub_obj_safe() {
+            assert_obj_safe!(CredsPub);
+        }
+
+        #[test]
+        fn creds_priv_obj_safe() {
+            assert_obj_safe!(CredsPriv);
+        }
+
+        #[test]
+        fn creds_obj_safe() {
+            assert_obj_safe!(Creds);
+        }
+    }
 }

+ 17 - 21
crates/btlib/src/crypto/tpm.rs

@@ -1320,13 +1320,11 @@ impl Principaled for TpmCreds {
 }
 
 impl Verifier for TpmCreds {
-    type Op<'v> = OsslVerifyOp<'v>;
-
-    fn init_verify(&self) -> Result<Self::Op<'_>> {
+    fn init_verify(&self) -> Result<Box<dyn '_ + VerifyOp>> {
         self.sign.public.init_verify()
     }
 
-    fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
+    fn verify(&self, parts: &mut dyn Iterator<Item = &[u8]>, signature: &[u8]) -> Result<()> {
         self.sign.public.verify(parts, signature)
     }
 
@@ -1359,19 +1357,19 @@ pub struct TpmSignOp<'a> {
     creds: &'a TpmCreds,
 }
 
-impl<'a> Op for TpmSignOp<'a> {
-    type Arg = &'a TpmCreds;
-
-    fn init(arg: Self::Arg) -> Result<Self> {
-        let op = VarHashOp::init(arg.sign.public.scheme.hash_kind())?;
+impl<'a> TpmSignOp<'a> {
+    pub fn new(arg: &'a TpmCreds) -> Result<Self> {
+        let op = VarHashOp::new(arg.sign.public.scheme.hash_kind())?;
         Ok(TpmSignOp { op, creds: arg })
     }
+}
 
+impl<'a> Op for TpmSignOp<'a> {
     fn update(&mut self, data: &[u8]) -> Result<()> {
         self.op.update(data)
     }
 
-    fn finish_into(self, buf: &mut [u8]) -> Result<usize> {
+    fn finish_into(&mut self, buf: &mut [u8]) -> Result<usize> {
         let hash = self.op.finish()?;
         let digest = Digest::try_from(hash.as_ref())?;
 
@@ -1409,13 +1407,11 @@ impl<'a> SignOp for TpmSignOp<'a> {
 }
 
 impl Signer for TpmCreds {
-    type Op<'s> = TpmSignOp<'s>;
-
-    fn init_sign(&self) -> Result<Self::Op<'_>> {
-        TpmSignOp::init(self)
+    fn init_sign(&self) -> Result<Box<dyn '_ + SignOp>> {
+        Ok(Box::new(TpmSignOp::new(self)?))
     }
 
-    fn sign<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I) -> Result<Signature> {
+    fn sign(&self, parts: &mut dyn Iterator<Item = &[u8]>) -> Result<Signature> {
         let mut op = self.init_sign()?;
         for part in parts {
             op.update(part)?;
@@ -1660,9 +1656,9 @@ mod test {
     fn sign_verify_test(creds: &TpmCreds) -> Result<()> {
         let data: [u8; 1024] = rand_array()?;
         let parts = [data.as_slice()];
-        let sig = creds.sign(parts.into_iter())?;
+        let sig = creds.sign(&mut parts.into_iter())?;
 
-        creds.verify(parts.into_iter(), sig.as_slice())
+        return creds.verify(&mut parts.into_iter(), sig.as_slice());
     }
 
     #[test]
@@ -1750,7 +1746,7 @@ mod test {
             .expect("failed to gen root creds");
         let data = [1u8; 32];
         creds
-            .sign(std::iter::once(data.as_slice()))
+            .sign(&mut std::iter::once(data.as_slice()))
             .expect("sign failed");
     }
 
@@ -1850,10 +1846,10 @@ mod test {
 
         let message = rand_vec(TpmCredStore::ENCRYPT_SCHEME.key_len() as usize / 2).unwrap();
         let sig = dest_root_creds
-            .sign([message.as_slice()].into_iter())
+            .sign(&mut [message.as_slice()].into_iter())
             .unwrap();
         src_root_creds
-            .verify([message.as_slice()].into_iter(), sig.as_slice())
+            .verify(&mut [message.as_slice()].into_iter(), sig.as_slice())
             .unwrap();
 
         let ct = src_root_creds.encrypt(message.as_slice()).unwrap();
@@ -1879,7 +1875,7 @@ mod test {
         let (sig, ..) = sign_wrap.finish().expect("finish failed");
 
         creds
-            .verify(get_parts(), sig.as_ref())
+            .verify(&mut get_parts(), sig.as_ref())
             .expect("verify failed");
     }
 

+ 3 - 2
crates/btlib/src/lib.rs

@@ -52,7 +52,8 @@ use accessor::Accessor;
 pub use block_path::{BlockPath, BlockPathError, RelBlockPath};
 use crypto::{
     AsymKeyPub, Ciphertext, ConcretePub, Creds, CredsPub, Decrypter, DecrypterExt, EncrypterExt,
-    HashKind, MerkleStream, SecretStream, Sign, Signature, Signer, SymKey, SymKeyKind, VarHash,
+    HashKind, MerkleStream, SecretStream, Sign, Signature, Signer, SignerExt, SymKey, SymKeyKind,
+    VarHash,
 };
 use error::{BoxInIoErr, BtErr};
 pub use error::{Error, Result};
@@ -1433,7 +1434,7 @@ pub trait Principaled {
     }
 }
 
-impl<T: Deref<Target = C>, C: Principaled> Principaled for T {
+impl<T: ?Sized + Principaled, P: Deref<Target = T>> Principaled for P {
     fn principal_of_kind(&self, kind: HashKind) -> Principal {
         self.deref().principal_of_kind(kind)
     }

+ 2 - 2
crates/btmsg/src/tls.rs

@@ -57,7 +57,7 @@ fn verify_tls13_signature(
 ) -> std::result::Result<(), rustls::Error> {
     let (_, subject_key) = Writecap::from_cert_chain(cert, &[]).map_err(to_cert_err)?;
     subject_key
-        .verify(std::iter::once(message), dss.signature())
+        .verify(&mut std::iter::once(message), dss.signature())
         .map_err(|_| rustls::Error::InvalidCertificateSignature)?;
     Ok(())
 }
@@ -253,7 +253,7 @@ impl<C: CredsPriv + Send + Sync + 'static> SigningKey for CredRef<C> {
 impl<C: CredsPriv + Send + Sync> rustls::sign::Signer for CredRef<C> {
     fn sign(&self, message: &[u8]) -> std::result::Result<Vec<u8>, rustls::Error> {
         self.creds
-            .sign(std::iter::once(message))
+            .sign(&mut std::iter::once(message))
             .map(|sig| sig.take_data())
             .map_err(|err| rustls::Error::General(err.to_string()))
     }