Răsfoiți Sursa

Introduced new "operations" types for representing ongoing cryptographic
operations.

Matthew Carr 2 ani în urmă
părinte
comite
6d829f2622

+ 9 - 3
TODO.txt

@@ -23,7 +23,7 @@ Move crypto::{encrypt, decrypt} into corresponding {EncrypterExt, DecrypterExt}.
 - 6
 Create an enum to eliminate the use of Block trait objects?
 
-- 7
+!- 7, 2, mdcarr941@gmail.com, ?, mdcarr941@gmail.com, fd4356
 Add a ser_sign_into method to SignerExt which serializes a value into a provided Vec<u8> and returns
 a signature over this data. Update BlockStream::flush_integ to use this method.
 
@@ -34,7 +34,7 @@ Convert all sector sizes to u64 for portability.
 Create an extension trait for u64 with a method for adding an i64 to it. Use this in
 SecretStream::seek, Trailered::seek and SectoredBuf::seek.
 
-- 10
+!- 10, 5, mdcarr941@gmail.com, ?, mdcarr941@gmail.com, fd4356
 Create a struct which digests data written to it before passing it to an underlying Write.
 
 !- 11, 3, mdcarr941@gmail.com, bd6904, mdcarr941@gmail.com, bd6904
@@ -46,7 +46,7 @@ Create a struct for managing the directory used to store blocks in the file syst
 implement an API for creating, opening, moving, copying, deleting and linking blocks. This API must
 be codified by a trait to allow the implementation to be changed in the future.
 
-- 13
+!- 13, 5, mdcarr941@gmail.com, ?, mdcarr941@gmail.com, fd4356
 Change the Hash enum so it contains structs for each hash type. Unify these structs with the node
 structs used in the VecMerkleTree.
 
@@ -79,3 +79,9 @@ Integrate with tokio and add async methods to all of the stream types.
 - 20, 5, mdcarr941@gmail.com, ef1d43,
 Rewrite BlockPath to be more efficient by ensuring that all characters in a path are contiguous
 in memory.
+
+- 21, 13, mdcarr941@gmail.com, fd4356,
+Create a type to hold a pipeline of streams. New traits will need to be added for each of the
+components of the pipeline to implement. Require that the last pipeline component be a particular
+sub-type of this trait. This will allow me to get rid of the dyn Block objects, as they can be
+replaced by instances of this pipeline type.

+ 4 - 4
crates/btlib/src/block_path.rs

@@ -1,7 +1,7 @@
 pub use private::{BlockPath, BlockPathError};
 
 mod private {
-    use crate::{crypto::Hash, Principal};
+    use crate::{crypto::VarHash, Principal};
     use serde::{Deserialize, Serialize};
     use std::fmt::Display;
 
@@ -129,7 +129,7 @@ mod private {
             let leading = components
                 .get(0)
                 .ok_or(BlockPathError::InvalidLeadingComponent)?;
-            let hash = Hash::try_from(leading.as_str())
+            let hash = VarHash::try_from(leading.as_str())
                 .map_err(|_| BlockPathError::InvalidLeadingComponent)?;
             Ok(BlockPath {
                 root: Principal(hash),
@@ -189,7 +189,7 @@ mod private {
 #[cfg(test)]
 mod tests {
     use crate::{
-        crypto::Hash,
+        crypto::VarHash,
         test_helpers::{make_path, make_principal, PRINCIPAL2},
         Principal,
     };
@@ -295,7 +295,7 @@ mod tests {
     fn path_contains_false_different_owners() {
         let first = make_path(vec!["apps"]);
         let mut second = make_path(vec!["apps"]);
-        *second.mut_root() = Principal(Hash::Sha2_256(PRINCIPAL2));
+        *second.mut_root() = Principal(VarHash::Sha2_256(PRINCIPAL2.into()));
         assert!(!first.contains(&second));
     }
 }

+ 1 - 0
crates/btlib/src/blocktree.rs

@@ -70,6 +70,7 @@ mod private {
     impl<C: Creds + Clone + 'static> Blocktree<C> {
         /// Creates a new empty blocktree at the given path.
         pub fn new_empty(btdir: PathBuf, generation: u64, creds: C) -> Result<Blocktree<C>> {
+            // Initialize the superblock.
             let mut sb_block = Self::open_block(&btdir, SpecInodes::Sb.into(), creds.clone())?;
             let sb = Superblock {
                 generation,

+ 789 - 46
crates/btlib/src/crypto/mod.rs

@@ -11,11 +11,13 @@ use crate::{
 
 use btserde::{self, from_vec, to_vec, write_to};
 use foreign_types::ForeignType;
+use log::error;
 use openssl::{
     encrypt::{Decrypter as OsslDecrypter, Encrypter as OsslEncrypter},
     error::ErrorStack,
-    hash::{hash, Hasher, MessageDigest},
-    pkey::{HasPrivate, HasPublic, PKey},
+    hash::{hash, DigestBytes, Hasher, MessageDigest},
+    nid::Nid,
+    pkey::{HasPrivate, HasPublic, PKey, PKeyRef},
     rand::rand_bytes,
     rsa::{Padding as OpensslPadding, Rsa as OsslRsa},
     sign::{Signer as OsslSigner, Verifier as OsslVerifier},
@@ -25,7 +27,11 @@ use serde::{
     de::{self, DeserializeOwned, Deserializer, SeqAccess, Visitor},
     ser::{SerializeStruct, Serializer},
 };
-use std::{marker::PhantomData, num::TryFromIntError};
+use std::{
+    io::{Read, Write},
+    marker::PhantomData,
+    num::TryFromIntError,
+};
 use strum_macros::{Display, EnumDiscriminants, FromRepr};
 use zeroize::ZeroizeOnDrop;
 
@@ -206,7 +212,296 @@ pub fn rand_vec(len: usize) -> Result<Vec<u8>> {
     Ok(vec)
 }
 
+/// 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>;
+
+    /// 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>;
+}
+
+/// An ongoing hash hash operation.
+pub trait HashOp: Op {
+    /// The specific hash type which is returned by the finish method.
+    type Hash: Hash;
+
+    /// Returns the kind of hash this operation is computing.
+    fn kind(&self) -> HashKind;
+
+    /// Finish this operation and return a hash type containing the result.
+    fn finish(self) -> Result<Self::Hash>;
+}
+
+// A hash operation which uses OpenSSL.
+pub struct OsslHashOp<H> {
+    hasher: Hasher,
+    phantom: PhantomData<H>,
+    kind: HashKind,
+}
+
+impl<H> Op for OsslHashOp<H> {
+    type Arg = HashKind;
+
+    fn init(arg: Self::Arg) -> Result<Self> {
+        let hasher = Hasher::new(arg.into())?;
+        let phantom = PhantomData;
+        Ok(OsslHashOp {
+            hasher,
+            phantom,
+            kind: arg,
+        })
+    }
+
+    fn update(&mut self, data: &[u8]) -> Result<()> {
+        Ok(self.hasher.update(data)?)
+    }
+
+    fn finish_into(mut self, buf: &mut [u8]) -> Result<usize> {
+        if buf.len() < self.kind.len() {
+            return Err(Error::IncorrectSize {
+                expected: self.kind.len(),
+                actual: buf.len(),
+            });
+        }
+        let digest = self.hasher.finish()?;
+        let slice = digest.as_ref();
+        buf.copy_from_slice(slice);
+        Ok(slice.len())
+    }
+}
+
+impl<H: Hash + From<DigestBytes>> HashOp for OsslHashOp<H> {
+    type Hash = H;
+
+    fn kind(&self) -> HashKind {
+        self.kind
+    }
+
+    fn finish(mut self) -> Result<Self::Hash> {
+        let digest = self.hasher.finish()?;
+        Ok(H::from(digest))
+    }
+}
+
+/// A wrapper which updates a `HashOp` when data is read or written.
+pub struct HashStream<T, Op: HashOp> {
+    inner: T,
+    op: Op,
+    update_failed: bool,
+}
+
+impl<T, Op: HashOp> HashStream<T, Op> {
+    /// Create a new `HashWrap`.
+    pub fn new(inner: T, op: Op) -> HashStream<T, Op> {
+        HashStream {
+            inner,
+            op,
+            update_failed: false,
+        }
+    }
+
+    /// 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> {
+        if self.update_failed {
+            return Err(Error::custom(
+                "HashStream::finish_into can't produce result due to HashOp update failure",
+            ));
+        }
+        self.op.finish_into(buf)
+    }
+
+    /// Finish this hash operation and return the resulting hash.
+    pub fn finish(self) -> Result<Op::Hash> {
+        if self.update_failed {
+            return Err(Error::custom(
+                "HashStream::finish can't produce result due to HashOp update failure",
+            ));
+        }
+        self.op.finish()
+    }
+}
+
+impl<T: Read, Op: HashOp> Read for HashStream<T, Op> {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        if self.update_failed {
+            return Err(Error::custom(
+                "HashStream::read can't continue due to previous HashOp update failure",
+            )
+            .into());
+        }
+        let read = self.inner.read(buf)?;
+        if read > 0 {
+            if let Err(err) = self.op.update(&buf[..read]) {
+                self.update_failed = true;
+                error!("HashWrap::read failed to update HashOp: {}", err);
+            }
+        }
+        Ok(read)
+    }
+}
+
+impl<T: Write, Op: HashOp> Write for HashStream<T, Op> {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.op.update(buf)?;
+        self.inner.write(buf)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        self.inner.flush()
+    }
+}
+
 /// A cryptographic hash.
+pub trait Hash: AsRef<[u8]> + AsMut<[u8]> + Sized {
+    /// The hash operation associated with this `Hash`.
+    type Op: HashOp;
+    /// The type of the argument required by `new`.
+    type Arg;
+
+    /// Returns a new `Hash` instance.
+    fn new(arg: Self::Arg) -> Self;
+    /// Returns the `HashKind` of self.
+    fn kind(&self) -> HashKind;
+    /// Starts a new hash operation.
+    fn start_op(&self) -> Result<Self::Op>;
+}
+
+/// Trait for hash types which can be created with no arguments.
+pub trait DefaultHash: Hash {
+    fn default() -> Self;
+}
+
+impl<A: Default, T: Hash<Arg = A>> DefaultHash for T {
+    fn default() -> Self {
+        Self::new(A::default())
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hashable, Clone)]
+pub struct Sha2_256([u8; Self::LEN]);
+
+impl Sha2_256 {
+    pub const KIND: HashKind = HashKind::Sha2_256;
+    pub const LEN: usize = Self::KIND.len();
+}
+
+impl AsRef<[u8]> for Sha2_256 {
+    fn as_ref(&self) -> &[u8] {
+        self.0.as_slice()
+    }
+}
+
+impl AsMut<[u8]> for Sha2_256 {
+    fn as_mut(&mut self) -> &mut [u8] {
+        self.0.as_mut_slice()
+    }
+}
+
+impl From<DigestBytes> for Sha2_256 {
+    fn from(value: DigestBytes) -> Self {
+        let mut hash = Sha2_256::new(());
+        // TODO: It would be great if there was a way to avoid this copy.
+        hash.as_mut().copy_from_slice(value.as_ref());
+        hash
+    }
+}
+
+impl From<[u8; Self::LEN]> for Sha2_256 {
+    fn from(value: [u8; Self::LEN]) -> Self {
+        Sha2_256(value)
+    }
+}
+
+impl From<Sha2_256> for [u8; Sha2_256::LEN] {
+    fn from(value: Sha2_256) -> Self {
+        value.0
+    }
+}
+
+impl Hash for Sha2_256 {
+    type Op = OsslHashOp<Sha2_256>;
+    type Arg = ();
+
+    fn new(_: Self::Arg) -> Self {
+        Sha2_256([0u8; Self::KIND.len()])
+    }
+
+    fn kind(&self) -> HashKind {
+        Self::KIND
+    }
+
+    fn start_op(&self) -> Result<Self::Op> {
+        OsslHashOp::init(Self::KIND)
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hashable, Clone)]
+pub struct Sha2_512(#[serde(with = "BigArray")] [u8; Self::LEN]);
+
+impl Sha2_512 {
+    pub const KIND: HashKind = HashKind::Sha2_512;
+    pub const LEN: usize = Self::KIND.len();
+}
+
+impl AsRef<[u8]> for Sha2_512 {
+    fn as_ref(&self) -> &[u8] {
+        self.0.as_slice()
+    }
+}
+
+impl AsMut<[u8]> for Sha2_512 {
+    fn as_mut(&mut self) -> &mut [u8] {
+        self.0.as_mut_slice()
+    }
+}
+
+impl From<DigestBytes> for Sha2_512 {
+    fn from(value: DigestBytes) -> Self {
+        let mut hash = Sha2_512::new(());
+        hash.as_mut().copy_from_slice(value.as_ref());
+        hash
+    }
+}
+
+impl From<[u8; Self::LEN]> for Sha2_512 {
+    fn from(value: [u8; Self::LEN]) -> Self {
+        Self(value)
+    }
+}
+
+impl From<Sha2_512> for [u8; Sha2_512::LEN] {
+    fn from(value: Sha2_512) -> Self {
+        value.0
+    }
+}
+
+impl Hash for Sha2_512 {
+    type Op = OsslHashOp<Sha2_512>;
+    type Arg = ();
+
+    fn new(_: Self::Arg) -> Self {
+        Sha2_512([0u8; Self::LEN])
+    }
+
+    fn kind(&self) -> HashKind {
+        Self::KIND
+    }
+
+    fn start_op(&self) -> Result<Self::Op> {
+        OsslHashOp::init(Self::KIND)
+    }
+}
+
+/// One of several concrete hash types.
 #[derive(
     Debug,
     PartialEq,
@@ -221,10 +516,9 @@ pub fn rand_vec(len: usize) -> Result<Vec<u8>> {
 )]
 #[strum_discriminants(derive(FromRepr, Display, Serialize, Deserialize))]
 #[strum_discriminants(name(HashKind))]
-pub enum Hash {
-    Sha2_256([u8; HashKind::Sha2_256.len()]),
-    #[serde(with = "BigArray")]
-    Sha2_512([u8; HashKind::Sha2_512.len()]),
+pub enum VarHash {
+    Sha2_256(Sha2_256),
+    Sha2_512(Sha2_512),
 }
 
 impl Default for HashKind {
@@ -233,7 +527,7 @@ impl Default for HashKind {
     }
 }
 
-impl Default for Hash {
+impl Default for VarHash {
     fn default() -> Self {
         HashKind::default().into()
     }
@@ -265,6 +559,23 @@ impl HashKind {
     }
 }
 
+impl TryFrom<MessageDigest> for HashKind {
+    type Error = Error;
+    fn try_from(value: MessageDigest) -> Result<Self> {
+        let nid = value.type_();
+        if Nid::SHA256 == nid {
+            Ok(HashKind::Sha2_256)
+        } else if Nid::SHA512 == nid {
+            Ok(HashKind::Sha2_512)
+        } else {
+            Err(Error::custom(format!(
+                "Unsupported MessageDigest with NID: {:?}",
+                nid
+            )))
+        }
+    }
+}
+
 impl From<HashKind> for MessageDigest {
     fn from(kind: HashKind) -> Self {
         match kind {
@@ -274,46 +585,71 @@ impl From<HashKind> for MessageDigest {
     }
 }
 
-impl Hash {
+impl VarHash {
     /// The character that's used to separate a hash type from its value in its string
     /// representation.
     const HASH_SEP: char = '!';
 
     pub fn kind(&self) -> HashKind {
-        HashKind::from(self)
+        self.into()
     }
 }
 
-impl From<HashKind> for Hash {
-    fn from(discriminant: HashKind) -> Hash {
-        match discriminant {
-            HashKind::Sha2_256 => Hash::Sha2_256([0u8; HashKind::Sha2_256.len()]),
-            HashKind::Sha2_512 => Hash::Sha2_512([0u8; HashKind::Sha2_512.len()]),
+impl From<HashKind> for VarHash {
+    fn from(kind: HashKind) -> VarHash {
+        match kind {
+            HashKind::Sha2_256 => VarHash::Sha2_256(Sha2_256::default()),
+            HashKind::Sha2_512 => VarHash::Sha2_512(Sha2_512::default()),
         }
     }
 }
 
-impl AsRef<[u8]> for Hash {
+impl AsRef<[u8]> for VarHash {
     fn as_ref(&self) -> &[u8] {
         match self {
-            Hash::Sha2_256(arr) => arr,
-            Hash::Sha2_512(arr) => arr,
+            VarHash::Sha2_256(arr) => arr.as_ref(),
+            VarHash::Sha2_512(arr) => arr.as_ref(),
         }
     }
 }
 
-impl AsMut<[u8]> for Hash {
+impl AsMut<[u8]> for VarHash {
     fn as_mut(&mut self) -> &mut [u8] {
         match self {
-            Hash::Sha2_256(arr) => arr,
-            Hash::Sha2_512(arr) => arr,
+            VarHash::Sha2_256(arr) => arr.as_mut(),
+            VarHash::Sha2_512(arr) => arr.as_mut(),
         }
     }
 }
 
-impl TryFrom<&str> for Hash {
+impl TryFrom<MessageDigest> for VarHash {
+    type Error = Error;
+    fn try_from(value: MessageDigest) -> Result<Self> {
+        let kind: HashKind = value.try_into()?;
+        Ok(kind.into())
+    }
+}
+
+impl Hash for VarHash {
+    type Op = VarHashOp;
+    type Arg = HashKind;
+
+    fn new(arg: Self::Arg) -> Self {
+        arg.into()
+    }
+
+    fn kind(&self) -> HashKind {
+        self.kind()
+    }
+
+    fn start_op(&self) -> Result<Self::Op> {
+        VarHashOp::init(self.kind())
+    }
+}
+
+impl TryFrom<&str> for VarHash {
     type Error = Error;
-    fn try_from(string: &str) -> Result<Hash> {
+    fn try_from(string: &str) -> Result<VarHash> {
         let mut split: Vec<&str> = string.split(Self::HASH_SEP).collect();
         if split.len() != 2 {
             return Err(Error::InvalidHashFormat);
@@ -324,17 +660,63 @@ impl TryFrom<&str> for Hash {
             .ok_or(Error::InvalidHashFormat)?
             .parse::<usize>()
             .map_err(|_| Error::InvalidHashFormat)?;
-        let mut hash = Hash::from(HashKind::from_repr(first).ok_or(Error::InvalidHashFormat)?);
+        let mut hash = VarHash::from(HashKind::from_repr(first).ok_or(Error::InvalidHashFormat)?);
         base64_url::decode_to_slice(second, hash.as_mut()).map_err(|_| Error::InvalidHashFormat)?;
         Ok(hash)
     }
 }
 
-impl Display for Hash {
+impl Display for VarHash {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let hash_kind: HashKind = self.into();
         let hash_data = base64_url::encode(self.as_ref());
-        write!(f, "{}{}{}", hash_kind as u32, Hash::HASH_SEP, hash_data)
+        write!(f, "{}{}{}", hash_kind as u32, VarHash::HASH_SEP, hash_data)
+    }
+}
+
+pub struct VarHashOp {
+    kind: HashKind,
+    hasher: Hasher,
+}
+
+impl Op for VarHashOp {
+    type Arg = HashKind;
+
+    fn init(arg: Self::Arg) -> Result<Self> {
+        let hasher = Hasher::new(arg.into())?;
+        Ok(VarHashOp { kind: arg, hasher })
+    }
+
+    fn update(&mut self, data: &[u8]) -> Result<()> {
+        Ok(self.hasher.update(data)?)
+    }
+
+    fn finish_into(mut self, buf: &mut [u8]) -> Result<usize> {
+        if buf.len() < self.kind.len() {
+            return Err(Error::IncorrectSize {
+                expected: self.kind.len(),
+                actual: buf.len(),
+            });
+        }
+        let digest = self.hasher.finish()?;
+        let slice = digest.as_ref();
+        buf.copy_from_slice(slice);
+        Ok(slice.len())
+    }
+}
+
+impl HashOp for VarHashOp {
+    type Hash = VarHash;
+
+    fn kind(&self) -> HashKind {
+        self.kind
+    }
+
+    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());
+        Ok(hash)
     }
 }
 
@@ -606,17 +988,23 @@ impl KeyLen {
     }
 }
 
+/// A Cryptographic Scheme. This is a common type for operations such as encrypting, decrypting,
+/// signing and verifying.
 pub trait Scheme:
     for<'de> Deserialize<'de> + Serialize + Copy + std::fmt::Debug + PartialEq + Into<Self::Kind>
 {
     type Kind: Scheme;
     fn as_enum(self) -> SchemeKind;
-    fn message_digest(&self) -> MessageDigest;
+    fn hash_kind(&self) -> HashKind;
     fn padding(&self) -> Option<OpensslPadding>;
     fn public_from_der(self, der: &[u8]) -> Result<PKey<Public>>;
     fn private_from_der(self, der: &[u8]) -> Result<PKey<Private>>;
     fn generate(self) -> Result<AsymKeyPair<Self::Kind>>;
     fn key_len(self) -> KeyLen;
+
+    fn message_digest(&self) -> MessageDigest {
+        self.hash_kind().into()
+    }
 }
 
 pub enum SchemeKind {
@@ -636,9 +1024,9 @@ impl Scheme for Encrypt {
         SchemeKind::Encrypt(self)
     }
 
-    fn message_digest(&self) -> MessageDigest {
+    fn hash_kind(&self) -> HashKind {
         match self {
-            Encrypt::RsaEsOaep(inner) => inner.message_digest(),
+            Encrypt::RsaEsOaep(inner) => inner.hash_kind(),
         }
     }
 
@@ -703,9 +1091,9 @@ impl Scheme for Sign {
         SchemeKind::Sign(self)
     }
 
-    fn message_digest(&self) -> MessageDigest {
+    fn hash_kind(&self) -> HashKind {
         match self {
-            Sign::RsaSsaPss(inner) => inner.message_digest(),
+            Sign::RsaSsaPss(inner) => inner.hash_kind(),
         }
     }
 
@@ -786,7 +1174,7 @@ impl Scheme for RsaEsOaep {
         SchemeKind::Encrypt(self.into())
     }
 
-    fn message_digest(&self) -> MessageDigest {
+    fn hash_kind(&self) -> HashKind {
         self.hash_kind.into()
     }
 
@@ -830,7 +1218,7 @@ impl Scheme for RsaSsaPss {
         SchemeKind::Sign(self.into())
     }
 
-    fn message_digest(&self) -> MessageDigest {
+    fn hash_kind(&self) -> HashKind {
         self.hash_kind.into()
     }
 
@@ -989,7 +1377,7 @@ impl Principaled for AsymKey<Public, Sign> {
     fn principal_of_kind(&self, kind: HashKind) -> Principal {
         let der = self.pkey.public_key_to_der().unwrap();
         let bytes = hash(kind.into(), der.as_slice()).unwrap();
-        let mut hash_buf = Hash::from(kind);
+        let mut hash_buf = VarHash::from(kind);
         hash_buf.as_mut().copy_from_slice(&bytes);
         Principal(hash_buf)
     }
@@ -1032,6 +1420,12 @@ impl Decrypter for AsymKey<Private, Encrypt> {
 }
 
 impl Signer for AsymKey<Private, Sign> {
+    type Op<'s> = OsslSignOp<'s>;
+
+    fn init_sign<'s>(&'s self) -> Result<Self::Op<'s>> {
+        OsslSignOp::init((self.scheme, self.pkey.as_ref()))
+    }
+
     fn sign<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I) -> Result<Signature> {
         let mut signer = OsslSigner::new(self.scheme.message_digest(), &self.pkey)?;
         if let Some(padding) = self.scheme.padding() {
@@ -1047,6 +1441,12 @@ impl Signer for AsymKey<Private, Sign> {
 }
 
 impl Verifier for AsymKey<Public, Sign> {
+    type Op<'v> = OsslVerifyOp<'v>;
+
+    fn init_verify<'v>(&'v self) -> Result<Self::Op<'v>> {
+        OsslVerifyOp::init((self.scheme, self.pkey.as_ref()))
+    }
+
     fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
         let mut verifier = OsslVerifier::new(self.scheme.message_digest(), &self.pkey)?;
         if let Some(padding) = self.scheme.padding() {
@@ -1097,12 +1497,22 @@ impl Decrypter for AsymKeyPair<Encrypt> {
 }
 
 impl Signer for AsymKeyPair<Sign> {
+    type Op<'s> = <AsymKey<Private, Sign> as Signer>::Op<'s>;
+    fn init_sign<'s>(&'s self) -> Result<Self::Op<'s>> {
+        self.private.init_sign()
+    }
     fn sign<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I) -> Result<Signature> {
         self.private.sign(parts)
     }
 }
 
 impl Verifier for AsymKeyPair<Sign> {
+    type Op<'v> = OsslVerifyOp<'v>;
+
+    fn init_verify<'v>(&'v self) -> Result<Self::Op<'v>> {
+        self.public.init_verify()
+    }
+
     fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
         self.public.verify(parts, signature)
     }
@@ -1127,6 +1537,12 @@ impl Encrypter for PublicCreds {
 }
 
 impl Verifier for PublicCreds {
+    type Op<'v> = OsslVerifyOp<'v>;
+
+    fn init_verify<'v>(&'v self) -> Result<Self::Op<'v>> {
+        self.sign.init_verify()
+    }
+
     fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
         self.sign.verify(parts, signature)
     }
@@ -1173,6 +1589,12 @@ impl ConcreteCreds {
 }
 
 impl Verifier for ConcreteCreds {
+    type Op<'v> = OsslVerifyOp<'v>;
+
+    fn init_verify<'v>(&'v self) -> Result<Self::Op<'v>> {
+        self.sign.init_verify()
+    }
+
     fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
         self.sign.verify(parts, signature)
     }
@@ -1197,6 +1619,12 @@ impl CredsPub for ConcreteCreds {
 }
 
 impl Signer for ConcreteCreds {
+    type Op<'s> = <AsymKeyPair<Sign> as Signer>::Op<'s>;
+
+    fn init_sign<'s>(&'s self) -> Result<Self::Op<'s>> {
+        self.sign.init_sign()
+    }
+
     fn sign<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I) -> Result<Signature> {
         self.sign.sign(parts)
     }
@@ -1249,11 +1677,92 @@ pub trait DecrypterExt: Decrypter {
 
 impl<T: Decrypter + ?Sized> DecrypterExt for T {}
 
+/// Represents an ongoing signing operation.
+pub trait SignOp: Op {
+    /// Returns the signature scheme that this operation is using.
+    fn scheme(&self) -> Sign;
+
+    /// Finishes this signature operation and returns a new signature containing the result.
+    fn finish(self) -> Result<Signature> {
+        let scheme = self.scheme();
+        let mut sig = Signature::empty(scheme);
+        self.finish_into(sig.as_mut())?;
+        Ok(sig)
+    }
+}
+
+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> {
+        let scheme = arg.0;
+        let mut signer = OsslSigner::new(arg.0.message_digest(), arg.1)?;
+        if let Some(padding) = scheme.padding() {
+            signer.set_rsa_padding(padding)?;
+        }
+        Ok(OsslSignOp { signer, scheme })
+    }
+
+    fn update(&mut self, data: &[u8]) -> Result<()> {
+        Ok(self.signer.update(data)?)
+    }
+
+    fn finish_into(self, buf: &mut [u8]) -> Result<usize> {
+        Ok(self.signer.sign(buf)?)
+    }
+}
+
+impl<'a> SignOp for OsslSignOp<'a> {
+    fn scheme(&self) -> Sign {
+        self.scheme
+    }
+}
+
+/// A struct which computes a signature over data as it is written to it.
+pub struct SignWrite<T, Op> {
+    inner: T,
+    op: Op,
+}
+
+impl<T, Op: SignOp> SignWrite<T, Op> {
+    pub fn new(inner: T, op: Op) -> Self {
+        SignWrite { inner, op }
+    }
+
+    pub fn finish_into(self, buf: &mut [u8]) -> Result<(usize, T)> {
+        Ok((self.op.finish_into(buf)?, self.inner))
+    }
+
+    pub fn finish(self) -> Result<(Signature, T)> {
+        Ok((self.op.finish()?, self.inner))
+    }
+}
+
+impl<T: Write, Op: SignOp> Write for SignWrite<T, Op> {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.op.update(buf)?;
+        self.inner.write(buf)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        self.inner.flush()
+    }
+}
+
 pub trait Signer {
+    type Op<'s>: SignOp
+    where
+        Self: 's;
+
+    /// Starts a new signing operation and returns the struct representing it.
+    fn init_sign<'s>(&'s self) -> Result<Self::Op<'s>>;
     fn sign<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I) -> Result<Signature>;
-}
 
-pub trait SignerExt: Signer {
     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()))?;
@@ -1265,23 +1774,120 @@ pub trait SignerExt: Signer {
         writecap.signature = signed.sig;
         Ok(())
     }
+
+    fn ser_sign_into<T: Serialize>(&self, value: &T, buf: &mut Vec<u8>) -> Result<Signature> {
+        write_to(value, buf)?;
+        let sig = self.sign(std::iter::once(buf.as_slice()))?;
+        Ok(sig)
+    }
+}
+
+pub trait VerifyOp: Sized {
+    type Arg;
+
+    fn init(arg: Self::Arg) -> Result<Self>;
+
+    fn update(&mut self, data: &[u8]) -> Result<()>;
+
+    fn finish(self, sig: &[u8]) -> Result<()>;
+
+    fn scheme(&self) -> Sign;
+}
+
+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)?;
+        if let Some(padding) = scheme.padding() {
+            verifier.set_rsa_padding(padding)?;
+        }
+        Ok(OsslVerifyOp { verifier, scheme })
+    }
+
+    fn update(&mut self, data: &[u8]) -> Result<()> {
+        Ok(self.verifier.update(data)?)
+    }
+
+    fn finish(self, sig: &[u8]) -> Result<()> {
+        match self.verifier.verify(sig) {
+            Ok(true) => Ok(()),
+            Ok(false) => Err(Error::InvalidSignature),
+            Err(err) => Err(err.into()),
+        }
+    }
+
+    fn scheme(&self) -> Sign {
+        self.scheme
+    }
+}
+
+pub struct VerifyRead<T, Op> {
+    inner: T,
+    op: Op,
+    update_failed: bool,
+}
+
+impl<T: Read, Op: VerifyOp> VerifyRead<T, Op> {
+    pub fn new(inner: T, op: Op) -> Self {
+        VerifyRead {
+            inner,
+            op,
+            update_failed: false,
+        }
+    }
+
+    pub fn finish(self, sig: &[u8]) -> std::result::Result<T, (T, Error)> {
+        if self.update_failed {
+            return Err((
+                self.inner,
+                Error::custom("VerifyRead::finish: update_failed was true"),
+            ));
+        }
+        match self.op.finish(sig) {
+            Ok(_) => Ok(self.inner),
+            Err(err) => Err((self.inner, err)),
+        }
+    }
 }
 
-impl<T: Signer + ?Sized> SignerExt for T {}
+impl<T: Read, Op: VerifyOp> Read for VerifyRead<T, Op> {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        if self.update_failed {
+            return Err(Error::custom("VerifyRead::read update previously failed").into());
+        }
+        let read = self.inner.read(buf)?;
+        if read > 0 {
+            if let Err(err) = self.op.update(&buf[..read]) {
+                self.update_failed = true;
+                error!("VerifyRead::read failed to update VerifyOp: {err}");
+            }
+        }
+        Ok(read)
+    }
+}
 
 pub trait Verifier {
+    type Op<'v>: VerifyOp
+    where
+        Self: 'v;
+
+    fn init_verify<'v>(&'v self) -> Result<Self::Op<'v>>;
+
     fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()>;
-}
 
-pub trait VerifierExt: Verifier {
     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)
     }
 }
 
-impl<T: Verifier + ?Sized> VerifierExt for T {}
-
 /// 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.
@@ -1452,8 +2058,9 @@ impl Writecap {
 mod tests {
     use super::*;
     use crate::{
-        crypto::secret_stream::SecretStream, test_helpers::*, BrotliParams, Sectored, SectoredBuf,
-        TryCompose,
+        crypto::secret_stream::SecretStream,
+        test_helpers::{self, *},
+        BrotliParams, Sectored, SectoredBuf, TryCompose,
     };
     use std::{
         io::{Seek, SeekFrom},
@@ -1492,7 +2099,7 @@ mod tests {
     fn hash_to_string_round_trip() -> Result<()> {
         let expected = make_principal().0;
         let string = expected.to_string();
-        let actual = Hash::try_from(string.as_str())?;
+        let actual = VarHash::try_from(string.as_str())?;
         assert_eq!(expected, actual);
         Ok(())
     }
@@ -1551,7 +2158,7 @@ mod tests {
     #[test]
     fn verify_writecap_invalid_not_chained() -> Result<()> {
         let (mut root_writecap, root_key) = make_self_signed_writecap();
-        root_writecap.body.issued_to = Principal(Hash::Sha2_256([0; 32]));
+        root_writecap.body.issued_to = Principal(VarHash::from(HashKind::Sha2_256));
         root_key.sign_writecap(&mut root_writecap)?;
         let node_principal = NODE_CREDS.principal();
         let writecap = make_writecap_trusted_by(
@@ -1567,7 +2174,7 @@ mod tests {
     #[test]
     fn verify_writecap_invalid_root_doesnt_own_path() -> Result<()> {
         let (mut root_writecap, root_key) = make_self_signed_writecap();
-        let owner = Principal(Hash::Sha2_256([0; 32]));
+        let owner = Principal(VarHash::from(HashKind::Sha2_256));
         root_writecap.body.path = make_path_with_root(owner, vec![]);
         root_key.sign_writecap(&mut root_writecap)?;
         let node_principal = NODE_CREDS.principal();
@@ -1659,6 +2266,142 @@ mod tests {
         }
     }
 
+    fn ossl_hash_op_same_as_digest_test_case<H: Hash + From<DigestBytes>>(kind: HashKind) {
+        let parts = (0u8..32).map(|k| vec![k; kind.len()]).collect::<Vec<_>>();
+        let expected = {
+            let mut expected = vec![0u8; kind.len()];
+            kind.digest(expected.as_mut(), parts.iter().map(|a| a.as_slice()))
+                .unwrap();
+            expected
+        };
+        let mut op = OsslHashOp::<H>::init(kind).unwrap();
+
+        for part in parts.iter() {
+            op.update(part.as_slice()).unwrap();
+        }
+        let actual = op.finish().unwrap();
+
+        assert_eq!(expected.as_slice(), actual.as_ref());
+    }
+
+    /// Tests that the hash computed using an `OsslHashOp` is the same as the one returned by the
+    /// `HashKind::digest` method.
+    #[test]
+    fn ossl_hash_op_same_as_digest() {
+        ossl_hash_op_same_as_digest_test_case::<Sha2_256>(Sha2_256::KIND);
+        ossl_hash_op_same_as_digest_test_case::<Sha2_512>(Sha2_512::KIND);
+    }
+
+    /// Tests that a `HashWrap` instance calculates the same hash as a call to the `digest` method.
+    #[test]
+    fn hash_stream_agrees_with_digest_method() {
+        let cursor = BtCursor::new([0u8; 3 * 32]);
+        let parts = (1u8..4).map(|k| [k; Sha2_512::LEN]).collect::<Vec<_>>();
+        let expected = {
+            let mut expected = Sha2_512::default();
+            HashKind::Sha2_512
+                .digest(expected.as_mut(), parts.iter().map(|a| a.as_slice()))
+                .unwrap();
+            expected
+        };
+        let op = OsslHashOp::<Sha2_512>::init(Sha2_512::KIND).unwrap();
+        let mut wrap = HashStream::new(cursor, op);
+
+        for part in parts.iter() {
+            wrap.write(part.as_slice()).unwrap();
+        }
+        let actual = wrap.finish().unwrap();
+
+        assert_eq!(expected, actual);
+    }
+
+    /// Tests that the `VarHash` computed by `VarHashOp` is the same as the one returned by the
+    /// `digest` method.
+    #[test]
+    fn var_hash_op_agress_with_digest_method() {
+        let parts = (32..64u8).map(|k| [k; Sha2_512::LEN]).collect::<Vec<_>>();
+        let expected = {
+            let mut expected = VarHash::from(HashKind::Sha2_512);
+            HashKind::Sha2_512
+                .digest(expected.as_mut(), parts.iter().map(|a| a.as_slice()))
+                .unwrap();
+            expected
+        };
+        let mut op = VarHashOp::init(HashKind::Sha2_512).unwrap();
+
+        for part in parts.iter() {
+            op.update(part.as_slice()).unwrap();
+        }
+        let actual = op.finish().unwrap();
+
+        assert_eq!(expected, actual);
+    }
+
+    /// Tests that the signature produced by `OsslSignOp` can be verified.
+    #[test]
+    fn ossl_sign_op_sig_can_be_verified() {
+        let keys = &test_helpers::NODE_CREDS;
+        let part_values = (1..9u8).map(|k| [k; 32]).collect::<Vec<_>>();
+        let get_parts = || part_values.iter().map(|a| a.as_slice());
+        let mut sign_op = keys.init_sign().expect("init_sign failed");
+
+        for part in get_parts() {
+            sign_op.update(part).expect("update failed");
+        }
+        let sig = sign_op.finish().expect("finish failed");
+
+        keys.verify(get_parts(), sig.as_ref())
+            .expect("verify failed");
+    }
+
+    /// Tests that the signature produced by a `SignWrite` can be verified.
+    #[test]
+    fn sign_write_sig_can_be_verified() {
+        use crate::Decompose;
+        const LEN: usize = 512;
+
+        let cursor = BtCursor::new([0u8; LEN]);
+        let keys = &test_helpers::NODE_CREDS;
+        let sign_op = keys.sign.private.init_sign().expect("init_sign failed");
+        let mut sign_write = SignWrite::new(cursor, sign_op);
+
+        for part in (1..9u8).map(|k| [k; LEN / 8]) {
+            sign_write.write(part.as_slice()).expect("write failed");
+        }
+        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())
+            .expect("verify failed");
+    }
+
+    /// Tests that data signed using a `SignWrite` can later be verified using a `VerifyRead`.
+    #[test]
+    fn sign_write_then_verify_read() {
+        const LEN: usize = 512;
+        let cursor = BtCursor::new([0u8; LEN]);
+        let keys = &test_helpers::NODE_CREDS;
+        let sign_op = keys.sign.private.init_sign().expect("init_sign failed");
+        let mut sign_write = SignWrite::new(cursor, sign_op);
+
+        for part in (1..9u8).map(|k| [k; LEN / 8]) {
+            sign_write.write(part.as_slice()).expect("write failed");
+        }
+        let (sig, mut cursor) = sign_write.finish().expect("finish failed");
+        cursor.seek(SeekFrom::Start(0)).expect("seek failed");
+
+        let verify_op = keys.sign.public.init_verify().expect("init_verify failed");
+        let mut verify_read = VerifyRead::new(cursor, verify_op);
+        let mut buf = Vec::with_capacity(LEN);
+        verify_read
+            .read_to_end(&mut buf)
+            .expect("read_to_end failed");
+
+        verify_read
+            .finish(sig.as_ref())
+            .expect("failed to verify signature");
+    }
+
     /// Tests that validate the dependencies of this module.
     mod dependency_tests {
         use super::*;

+ 112 - 19
crates/btlib/src/crypto/tpm.rs

@@ -4,7 +4,6 @@ use btserde::read_from;
 use log::error;
 use openssl::{
     bn::{BigNum, BigNumRef},
-    hash::Hasher,
     nid::Nid,
     pkcs5::pbkdf2_hmac,
 };
@@ -1341,6 +1340,12 @@ impl Principaled for TpmCreds {
 }
 
 impl Verifier for TpmCreds {
+    type Op<'v> = OsslVerifyOp<'v>;
+
+    fn init_verify<'v>(&'v self) -> Result<Self::Op<'v>> {
+        self.sign.public.init_verify()
+    }
+
     fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()> {
         self.sign.public.verify(parts, signature)
     }
@@ -1358,26 +1363,36 @@ impl CredsPub for TpmCreds {
     }
 }
 
-impl Signer for TpmCreds {
-    fn sign<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I) -> Result<Signature> {
-        let digest = {
-            let mut hasher = Hasher::new(self.sign.public.scheme.message_digest())?;
-            for part in parts {
-                hasher.update(part)?;
-            }
-            let bytes = hasher.finish()?;
-            let slice: &[u8] = &bytes;
-            Digest::try_from(slice)?
-        };
+pub struct TpmSignOp<'a> {
+    op: VarHashOp,
+    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())?;
+        Ok(TpmSignOp { op, creds: arg })
+    }
+
+    fn update(&mut self, data: &[u8]) -> Result<()> {
+        self.op.update(data)
+    }
+
+    fn finish_into(self, buf: &mut [u8]) -> Result<usize> {
+        let hash = self.op.finish()?;
+        let digest = Digest::try_from(hash.as_ref())?;
+
         let validation = HashcheckTicket::null();
         // Null means the scheme used during key creation will be used.
         let scheme = SignatureScheme::Null;
 
         let sig = {
-            let mut guard = self.state.write()?;
+            let mut guard = self.creds.state.write()?;
             guard
                 .context
-                .sign(self.sign.private, digest, scheme, validation)?
+                .sign(self.creds.sign.private, digest, scheme, validation)?
         };
         let slice: &[u8] = match &sig {
             tss_esapi::structures::Signature::RsaSsa(inner) => inner.signature(),
@@ -1390,9 +1405,33 @@ impl Signer for TpmCreds {
             }
         };
 
-        let mut buf = Signature::empty(self.sign.public.scheme);
-        buf.as_mut_slice().write_all(slice)?;
-        Ok(buf)
+        if buf.len() < slice.len() {
+            return Err(Error::IncorrectSize { expected: slice.len(), actual: buf.len() });
+        }
+        buf.copy_from_slice(slice);
+        Ok(slice.len())
+    }
+}
+
+impl<'a> SignOp for TpmSignOp<'a> {
+    fn scheme(&self) -> Sign {
+        self.creds.sign.public.scheme
+    }
+}
+
+impl Signer for TpmCreds {
+    type Op<'s> = TpmSignOp<'s>;
+
+    fn init_sign<'s>(&'s self) -> Result<Self::Op<'s>> {
+        TpmSignOp::init(self)
+    }
+
+    fn sign<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I) -> Result<Signature> {
+        let mut op = self.init_sign()?;
+        for part in parts {
+            op.update(part)?;
+        }
+        op.finish()
     }
 }
 
@@ -1476,12 +1515,12 @@ impl HasResponseCode for TSS2_RC {
 
 #[cfg(test)]
 mod test {
-    use crate::test_helpers::SwtpmHarness;
+    use crate::test_helpers::{BtCursor, SwtpmHarness};
 
     use super::*;
 
     use ctor::ctor;
-    use std::fs::File;
+    use std::{fs::File, io::SeekFrom};
     use tss_esapi::{
         interface_types::ecc::EccCurve,
         structures::{EccPoint, EccScheme, KeyDerivationFunctionScheme, PublicEccParameters},
@@ -1633,6 +1672,7 @@ mod test {
         let data: [u8; 1024] = rand_array()?;
         let parts = [data.as_slice()];
         let sig = creds.sign(parts.into_iter())?;
+
         creds.verify(parts.into_iter(), sig.as_slice())
     }
 
@@ -1829,6 +1869,59 @@ mod test {
         assert_eq!(message.as_slice(), pt.as_slice());
     }
 
+    /// Tests that a signature computed using a `TpmSignOp` can be verified.
+    #[test]
+    fn sign_write_can_be_verified() {
+        const LEN: usize = 512;
+        let cursor = BtCursor::new([0u8; LEN]);
+        let (_harness, store) = test_store().unwrap();
+        let creds = store.gen_node_creds().expect("gen_node_creds failed");
+        let sign_op = creds.init_sign().expect("init_sign failed");
+        let mut sign_wrap = SignWrite::new(cursor, sign_op);
+        let part_values = (1..9u8).map(|k| [k; LEN / 8]).collect::<Vec<_>>();
+        let get_parts = || part_values.iter().map(|arr| arr.as_slice());
+
+        for part in get_parts() {
+            sign_wrap.write(part).expect("write failed");
+        }
+        let (sig, ..) = sign_wrap.finish().expect("finish failed");
+
+        creds
+            .verify(get_parts(), sig.as_ref())
+            .expect("verify failed");
+    }
+
+    /// Tests that data written into a `SignWrite` using a `TpmSignOp` and later read from a
+    /// `VerifyRead` using a `OsslVerifyOp` can be successfully verified.
+    #[test]
+    fn sign_write_then_verify_read() {
+        use std::io::Seek;
+        const LEN: usize = 512;
+        let cursor = BtCursor::new([0u8; LEN]);
+        let (_harness, store) = test_store().unwrap();
+        let creds = store.gen_node_creds().expect("gen_node_creds failed");
+        let sign_op = creds.init_sign().expect("init_sign failed");
+        let mut sign_wrap = SignWrite::new(cursor, sign_op);
+
+        for part in (1..9u8).map(|k| [k; LEN / 8]) {
+            sign_wrap.write(part.as_slice()).expect("write failed");
+        }
+        let (sig, mut cursor) = sign_wrap.finish().expect("finish failed");
+
+        cursor.seek(SeekFrom::Start(0)).expect("seek failed");
+
+        let verify_op = creds.init_verify().expect("init_verify failed");
+        let mut verify_read = VerifyRead::new(cursor, verify_op);
+        let mut buf = Vec::with_capacity(LEN);
+        verify_read
+            .read_to_end(&mut buf)
+            .expect("read_to_end failed");
+
+        verify_read
+            .finish(sig.as_ref())
+            .expect("failed to verify signature");
+    }
+
     /// This test is broken for unknown reasons. It passes if no inner encryption key is supplied.
     /// To work-around this issue I've chosen to wrap the data returned by `duplicate` with a
     /// symmetric key in software.

+ 16 - 19
crates/btlib/src/lib.rs

@@ -23,8 +23,8 @@ extern crate lazy_static;
 use brotli::{CompressorWriter, Decompressor};
 use btserde::{self, write_to};
 use crypto::{
-    AsymKeyPub, Ciphertext, Creds, Decrypter, DecrypterExt, Encrypter, EncrypterExt, Hash,
-    HashKind, MerkleStream, PublicCreds, SecretStream, Sign, Signature, Signer, SymKey, SymKeyKind,
+    AsymKeyPub, Ciphertext, Creds, Decrypter, DecrypterExt, Encrypter, EncrypterExt, HashKind,
+    MerkleStream, PublicCreds, SecretStream, Sign, Signature, Signer, SymKey, SymKeyKind, VarHash,
 };
 
 use fuse_backend_rs::file_traits::FileReadWriteVolatile;
@@ -238,7 +238,7 @@ pub struct BlockMetaBody {
     readcaps: BTreeMap<Principal, Ciphertext<SymKey>>,
     writecap: Option<Writecap>,
     /// A hash which provides integrity for the contents of the block body.
-    integrity: Option<Hash>,
+    integrity: Option<VarHash>,
     /// The public key that corresponds to the private key used to sign these metadata.
     signing_key: AsymKeyPub<Sign>,
 }
@@ -280,14 +280,14 @@ struct BlockStream<T, C> {
 
 impl<T: Read + Seek, C: Creds> BlockStream<T, C> {
     fn new(inner: T, creds: C) -> Result<BlockStream<T, C>> {
-        let (trailered, trailer) = Trailered::<_, BlockMeta>::new(inner)?;
-        let trailer = match trailer {
-            Some(mut trailer) => {
-                trailer.assert_valid()?;
+        let (trailered, meta) = Trailered::<_, BlockMeta>::new(inner)?;
+        let meta = match meta {
+            Some(mut meta) => {
+                meta.assert_valid()?;
                 // We need to use the writecap and signing_key provided by the current credentials.
-                trailer.body.writecap = creds.writecap().map(|e| e.to_owned());
-                trailer.body.signing_key = creds.public_sign().to_owned();
-                trailer
+                meta.body.writecap = creds.writecap().map(|e| e.to_owned());
+                meta.body.signing_key = creds.public_sign().to_owned();
+                meta
             }
             None => {
                 let mut meta = BlockMeta::new(&creds);
@@ -301,7 +301,7 @@ impl<T: Read + Seek, C: Creds> BlockStream<T, C> {
         };
         Ok(BlockStream {
             trailered,
-            meta: trailer,
+            meta,
             meta_body_buf: Vec::new(),
             creds,
         })
@@ -324,13 +324,10 @@ impl<T: Write + Seek, C: Signer> Write for BlockStream<T, C> {
 impl<T: Write + Seek, C: Signer> WriteInteg for BlockStream<T, C> {
     fn flush_integ(&mut self, integrity: &[u8]) -> io::Result<()> {
         let meta_body = &mut self.meta.body;
-        let integ = meta_body.integrity.get_or_insert_with(Hash::default);
+        let integ = meta_body.integrity.get_or_insert_with(VarHash::default);
         integ.as_mut().copy_from_slice(integrity);
         self.meta_body_buf.clear();
-        write_to(&meta_body, &mut self.meta_body_buf).box_err()?;
-        self.meta.sig = self
-            .creds
-            .sign(std::iter::once(self.meta_body_buf.as_slice()))?;
+        self.meta.sig = self.creds.ser_sign_into(&meta_body, &mut self.meta_body_buf)?;
         self.trailered.flush(&self.meta)?;
         Ok(())
     }
@@ -545,7 +542,7 @@ pub struct Readcap {
 }
 
 impl Readcap {
-    pub fn new(issued_to: Hash, key: Ciphertext<SymKey>) -> Readcap {
+    pub fn new(issued_to: VarHash, key: Ciphertext<SymKey>) -> Readcap {
         Readcap {
             issued_to: Principal(issued_to),
             key,
@@ -679,7 +676,7 @@ pub struct FragmentRecord {
 impl FragmentRecord {
     /// Creates a new `FragmentRecord` whose `serial` and `stored_by` fields are set to
     /// the given values.
-    pub fn new(serial: u32, stored_by: Hash) -> FragmentRecord {
+    pub fn new(serial: u32, stored_by: VarHash) -> FragmentRecord {
         FragmentRecord {
             serial: FragmentSerial(serial),
             stored_by: Principal(stored_by),
@@ -691,7 +688,7 @@ impl FragmentRecord {
 #[derive(
     Debug, PartialEq, Eq, Serialize, Deserialize, Hashable, Clone, Default, PartialOrd, Ord,
 )]
-pub struct Principal(Hash);
+pub struct Principal(VarHash);
 
 impl Principal {
     fn kind(&self) -> HashKind {

+ 9 - 3
crates/btlib/src/test_helpers.rs

@@ -114,7 +114,7 @@ fn convert_err<E: Display>(err: E) -> Error {
 }
 
 pub(crate) fn make_principal() -> Principal {
-    Principal(Hash::Sha2_256(PRINCIPAL))
+    Principal(VarHash::Sha2_256(PRINCIPAL.into()))
 }
 
 pub(crate) fn make_path_with_root(root: Principal, rel_components: Vec<&str>) -> BlockPath {
@@ -132,7 +132,7 @@ pub(crate) fn make_path(rel_components: Vec<&str>) -> BlockPath {
 
 pub(crate) fn make_writecap_and_creds(rel_components: Vec<&str>) -> (Writecap, impl Creds) {
     let (root_writecap, root_key) = make_self_signed_writecap();
-    let issued_to = Principal(Hash::Sha2_256(PRINCIPAL));
+    let issued_to = Principal(VarHash::Sha2_256(PRINCIPAL.into()));
     (
         make_writecap_trusted_by(root_writecap, &root_key, issued_to, rel_components),
         root_key,
@@ -217,7 +217,7 @@ pub(crate) fn make_block_with(readcap: Readcap) -> Box<dyn Block> {
         inherit: Some(INHERIT.clone()),
         readcaps,
         writecap: Some(writecap),
-        integrity: Some(Hash::Sha2_256([0u8; HashKind::Sha2_256.len()])),
+        integrity: Some(VarHash::from(HashKind::Sha2_256)),
         signing_key: creds.public_sign().to_owned(),
     };
     let sig = Signature::copy_from(Sign::RSA_PSS_3072_SHA_256, &SIGNATURE);
@@ -422,6 +422,12 @@ impl<T: FromVec> BtCursor<T> {
     }
 }
 
+impl<T: FromVec> Decompose<T> for BtCursor<T> {
+    fn into_inner(self) -> T {
+        self.cursor.into_inner().into_inner()
+    }
+}
+
 impl<T: FromVec + AsMut<[u8]>> BtCursor<T> {
     /// Access the data in this cursor as a mutable slice of bytes. The callback which access the
     /// bytes is expected to return the number of bytes to advance the cursor position by.