Parcourir la source

Removed the old Block type and renamed NewBlock to Block.

Matthew Carr il y a 2 ans
Parent
commit
2d805052df

+ 60 - 106
crates/btnode/src/crypto/mod.rs

@@ -2,7 +2,11 @@
 
 mod tpm;
 
-use crate::Decompose;
+use crate::{
+    Decompose,
+    SECTOR_SZ_DEFAULT,
+    SectoredBuf
+};
 
 use super::{
     Block,
@@ -17,7 +21,6 @@ use super::{
     Owned,
     Principal,
     Path,
-    HashMap,
     Epoch,
     io,
     Read,
@@ -1167,7 +1170,7 @@ fn exp2(x: isize) -> usize {
 }
 
 /// Trait for types which can be used as nodes in a `MerkleTree`.
-trait MerkleNode: Default + Serialize + for<'de> Deserialize<'de> {
+pub trait MerkleNode: Default + Serialize + for<'de> Deserialize<'de> {
     /// The kind of hash algorithm that this `HashData` uses.
     const KIND: HashKind;
 
@@ -1364,7 +1367,7 @@ impl BinTreeIndex {
 /// Each sector corresponds to an offset into the protected data, and in order to verify that a
 /// sector has not been modified, you must supply the offset of the sector.
 #[derive(Serialize, Deserialize)]
-struct MerkleTree<T> {
+pub struct MerkleTree<T> {
     nodes: Vec<T>,
     /// The size of the sectors of data that this tree will protect.
     sector_sz: usize,
@@ -1776,35 +1779,17 @@ impl<T: Seek> Seek for SecretStream<T> {
     }
 }
 
-pub(crate) fn encrypt_block<C: Encrypter + Decrypter>(
-    mut block: Block, principal: &Principal, creds: &C
-) -> Result<Block> {
-    let body = match block.body {
-        Cryptotext::Cipher(_) => return Ok(block),
-        Cryptotext::Plain(body) => body
-    };
-    let (principal_owned, read_cap) = block.readcaps.remove_entry(principal)
-        .ok_or(Error::NoReadCap)?;
-    let block_key = decrypt(read_cap, creds)?;
-    let new_body = block_key.encrypt(&body)?;
-    block.body = Cryptotext::Cipher(new_body);
-    block.readcaps.insert(principal_owned, encrypt(&block_key, creds)?);
-    Ok(block)
-}
-
-pub(crate) fn decrypt_block<C: Decrypter>(
-    mut block: Block, principal: &Principal, creds: &C
-) -> Result<Block> {
-    let body = match block.body {
-        Cryptotext::Plain(_) => return Ok(block),
-        Cryptotext::Cipher(body) => body
-    };
-    let (principal_owned, read_cap) = block.readcaps.remove_entry(principal)
+pub(crate) fn unveil<T: Read + Write + Seek, C: Encrypter + Decrypter>(
+    mut block: Block<T>, principal: &Principal, creds: &C
+) -> Result<Block<impl Read + Write + Seek>> {
+    let (principal_owned, read_cap) = block.header.readcaps.remove_entry(principal)
         .ok_or(Error::NoReadCap)?;
     let block_key = decrypt(read_cap, creds)?;
-    let new_body = block_key.decrypt(&body)?;
-    block.body = Cryptotext::Plain(new_body);
-    block.readcaps.insert(principal_owned, Cryptotext::Plain(block_key));
+    let mut block = block
+        .compose_body(MerkleStream::new(MerkleTree::<Sha2_256>::empty(SECTOR_SZ_DEFAULT)))
+        .try_compose_body(SecretStream::new(block_key.clone()))?
+        .try_compose_body(SectoredBuf::new())?;
+    block.header.readcaps.insert(principal_owned, encrypt(&block_key, creds)?);
     Ok(block)
 }
 
@@ -1825,51 +1810,22 @@ fn decrypt<T: Serialize + DeserializeOwned, K: Decrypter>(
     Ok(from_vec(&vec?)?)
 }
 
-#[derive(Serialize)]
-struct HeaderSigInput<'a> {
-    path: &'a Path,
-    readcaps: &'a HashMap<Principal, Cryptotext<SymKey>>,
-    writecap: &'a Writecap,
-}
-
-impl<'a> From<&'a Block> for HeaderSigInput<'a> {
-    fn from(block: &'a Block) -> HeaderSigInput<'a> {
-        HeaderSigInput {
-            path: &block.path,
-            readcaps: &block.readcaps,
-            writecap: &block.writecap,
-        }
-    }
-}
-
-fn get_body(block: &Block) -> Result<&[u8]> {
-    let body = match &block.body {
-        Cryptotext::Cipher(body) => body,
-        Cryptotext::Plain(_) => {
-            return Err(Error::BlockNotEncrypted);
-        }
-    };
-    Ok(body)
-}
-
-pub(crate) fn sign_block<K: Signer>(
-    block: &mut Block, priv_key: &K
+pub(crate) fn sign_block<T, K: Signer>(
+    block: &mut Block<T>, priv_key: &K
 ) -> Result<()> {
-    let body = get_body(block)?;
-    let sig_input = HeaderSigInput::from(&*block);
-    let header = to_vec(&sig_input)?;
-    let signature = priv_key.sign([header.as_slice(), body].into_iter())?;
-    block.signature = signature;
+    let header = to_vec(&block.header)?;
+    let signature = priv_key.sign(std::iter::once(header.as_slice()))?;
+    block.sig = signature;
     Ok(())
 }
 
-pub(crate) fn verify_block(block: &Block) -> Result<()> {
-    verify_writecap(&block.writecap, &block.path)?;
-    let body = get_body(block)?;
-    let sig_input = HeaderSigInput::from(block);
-    let header = to_vec(&sig_input)?;
-    let parts = [header.as_slice(), body].into_iter();
-    block.writecap.signing_key.verify(parts, block.signature.as_slice())
+pub(crate) fn verify_block<T>(block: &Block<T>) -> Result<()> {
+    verify_writecap(&block.header.writecap, &block.header.path)?;
+    let header_data = to_vec(&block.header)?;
+    block.header.writecap.signing_key.verify(
+        std::iter::once(header_data.as_slice()),
+        block.sig.as_slice()
+    )
 }
 
 #[derive(Serialize)]
@@ -1976,35 +1932,33 @@ mod tests {
         time::Duration, io::{Cursor, SeekFrom},
     };
 
-    fn encrypt_decrypt_block_test_case<C: Creds>(
-        mut actual: Block, principal: &Principal, creds: &C
-    ) -> Result<()> {
-        let expected = actual.clone();
-        actual = encrypt_block(actual, principal, creds)?;
-        let encrypted = match &actual.body {
-            Cryptotext::Cipher(_) => true,
-            Cryptotext::Plain(_) => false
+    fn encrypt_decrypt_block_test_case<T: Read + Write + Seek, C: Creds>(
+        mut block: Block<T>, principal: &Principal, creds: &C
+    ) {
+        let expected = {
+            let mut dest = Vec::new();
+            block.seek(SeekFrom::Start(0)).expect("seek failed");
+            block.body.read_to_end(&mut dest).expect("read_to_end failed");
+            block.seek(SeekFrom::Start(0)).expect("seek failed");
+            dest
+        };
+        let mut block = unveil(block, principal, creds).expect("unveil failed");
+        block.write_all(&expected).expect("write_all failed");
+        block.flush().expect("flush failed");
+        block.seek(SeekFrom::Start(0)).expect("seek failed");
+        let actual = {
+            let mut actual = Vec::new();
+            block.read_to_end(&mut actual).expect("failed to read actual block contents");
+            actual
         };
-        assert!(encrypted);
-        actual = decrypt_block(actual, principal, creds)?;
         assert_eq!(expected, actual);
-        Ok(())
-    }
-
-    #[test]
-    fn encrypt_decrypt_block_aes256cbc() -> Result<()> {
-        let readcap = make_readcap();
-        let principal = readcap.issued_to.clone();
-        let block = make_block_with(readcap)?;
-        let key = make_key_pair();
-        encrypt_decrypt_block_test_case(block, &principal, &key)
     }
 
     #[test]
-    fn encrypt_decrypt_block_aes256ctr() -> Result<()> {
+    fn encrypt_decrypt_block() {
         let readcap = make_readcap();
         let principal = readcap.issued_to.clone();
-        let block = make_block_with(readcap)?;
+        let block = make_block_with(readcap);
         let key = make_key_pair();
         encrypt_decrypt_block_test_case(block, &principal, &key)
     }
@@ -2022,9 +1976,9 @@ mod tests {
     fn sign_verify_block_rsa() -> Result<()> {
         let readcap = make_readcap();
         let principal = readcap.issued_to.clone();
-        let mut block = make_block_with(readcap)?;
+        let block = make_block_with(readcap);
         let key = make_key_pair();
-        block = encrypt_block(block, &principal, &key)?;
+        let mut block = unveil(block, &principal, &key).expect("unveil failed");
         sign_block(&mut block, &key)?;
         verify_block(&block)?;
         Ok(())
@@ -2047,14 +2001,14 @@ mod tests {
     }
 
     #[test]
-    fn verify_writecap_valid() -> Result<()> {
-        let writecap = make_writecap(vec!["apps", "verse"])?;
-        verify_writecap(&writecap, &writecap.path)
+    fn verify_writecap_valid() {
+        let writecap = make_writecap(vec!["apps", "verse"]);
+        verify_writecap(&writecap, &writecap.path).expect("failed to verify writecap");
     }
 
     #[test]
     fn verify_writecap_invalid_signature() -> Result<()> {
-        let mut writecap = make_writecap(vec!["apps", "verse"])?;
+        let mut writecap = make_writecap(vec!["apps", "verse"]);
         writecap.signature = Signature::empty(Sign::RSA_PSS_3072_SHA_256);
         let result = verify_writecap(&writecap, &writecap.path);
         if let Err(Error::InvalidSignature) = result {
@@ -2078,7 +2032,7 @@ mod tests {
 
     #[test]
     fn verify_writecap_invalid_path_not_contained() -> Result<()> {
-        let writecap = make_writecap(vec!["apps", "verse"])?;
+        let writecap = make_writecap(vec!["apps", "verse"]);
         let mut path = writecap.path.clone();
         path.components.pop();
         // `path` is now a superpath of `writecap.path`, thus the writecap is not authorized to
@@ -2089,7 +2043,7 @@ mod tests {
 
     #[test]
     fn verify_writecap_invalid_expired() -> Result<()> {
-        let mut writecap = make_writecap(vec!["apps", "verse"])?;
+        let mut writecap = make_writecap(vec!["apps", "verse"]);
         writecap.expires = Epoch::now() - Duration::from_secs(1);
         let result = verify_writecap(&writecap, &writecap.path);
         assert_authz_err(WritecapAuthzErr::Expired, result)
@@ -2097,27 +2051,27 @@ mod tests {
 
     #[test]
     fn verify_writecap_invalid_not_chained() -> Result<()> {
-        let (mut root_writecap, root_key) = make_self_signed_writecap()?;
+        let (mut root_writecap, root_key) = make_self_signed_writecap();
         root_writecap.issued_to = Principal(Hash::Sha2_256([0; 32]));
         sign_writecap(&mut root_writecap, &root_key)?;
         let node_key = AsymKeyPub::new(Sign::RSA_PSS_3072_SHA_256, NODE_PUBLIC_KEY.as_slice())?;
         let node_principal = node_key.owner();
         let writecap = make_writecap_trusted_by(
-            root_writecap, root_key, node_principal, vec!["apps", "contacts"])?;
+            root_writecap, root_key, node_principal, vec!["apps", "contacts"]);
         let result = verify_writecap(&writecap, &writecap.path);
         assert_authz_err(WritecapAuthzErr::NotChained, result)
     }
 
     #[test]
     fn verify_writecap_invalid_root_doesnt_own_path() -> Result<()> {
-        let (mut root_writecap, root_key) = make_self_signed_writecap()?;
+        let (mut root_writecap, root_key) = make_self_signed_writecap();
         let owner = Principal(Hash::Sha2_256([0; 32]));
         root_writecap.path = make_path_with_owner(owner, vec![]);
         sign_writecap(&mut root_writecap, &root_key)?;
         let node_key = AsymKeyPub::new(Sign::RSA_PSS_3072_SHA_256, NODE_PUBLIC_KEY.as_slice())?;
         let node_owner = node_key.owner();
         let writecap = make_writecap_trusted_by(
-            root_writecap, root_key, node_owner, vec!["apps", "contacts"])?;
+            root_writecap, root_key, node_owner, vec!["apps", "contacts"]);
         let result = verify_writecap(&writecap, &writecap.path);
         assert_authz_err(WritecapAuthzErr::RootDoesNotOwnPath, result)
     }

+ 30 - 80
crates/btnode/src/main.rs

@@ -95,9 +95,9 @@ type Result<T> = std::result::Result<T, Error>;
 /// A Block tagged with its version number. When a block of a previous version is received over
 /// the network or read from the filesystem, it is upgraded to the current version before being
 /// processed.
-#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
-enum VersionedBlock {
-    V0(Block)
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+enum VersionedBlock<T> {
+    V0(Block<T>)
 }
 
 const SECTOR_SZ_DEFAULT: usize = 4096;
@@ -118,7 +118,7 @@ trait Sectored {
     }
 }
 
-#[derive(Serialize, Deserialize, Debug, PartialEq)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
 pub struct Header {
     path: Path,
     readcaps: HashMap<Principal, Cryptotext<SymKey>>,
@@ -126,39 +126,50 @@ pub struct Header {
     merkle_root: Hash,
 }
 
-struct NewBlock<T> {
+/// A container which binds together ciphertext along with the metadata needed to identify,
+/// verify and decrypt it.
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+struct Block<T> {
     header: Header,
     sig: Signature,
     body: T,
 }
 
-impl<T> NewBlock<T> {
-    fn compose<E: Into<Error>, U: Decompose<T>, V: TryCompose<T, U, Error = E>>(
+impl<T> Block<T> {
+    fn try_compose_body<E: Into<Error>, U: Decompose<T>, V: TryCompose<T, U, Error = E>>(
         self, new_body: V
-    ) -> Result<NewBlock<U>> {
-        Ok(NewBlock {
+    ) -> Result<Block<U>> {
+        Ok(Block {
             header: self.header,
             sig: self.sig,
             body: new_body.try_compose(self.body).map_err(|err| err.into())?,
         })
     }
+
+    fn compose_body<U: Decompose<T>, V: Compose<T, U>>(self, new_body: V) -> Block<U> {
+        Block {
+            header: self.header,
+            sig: self.sig,
+            body: new_body.compose(self.body),
+        }
+    }
 }
 
-impl NewBlock<File> {
-    fn new<P: AsRef<std::path::Path>>(path: P) -> Result<NewBlock<FileBody>> {
+impl Block<File> {
+    fn new<P: AsRef<std::path::Path>>(path: P) -> Result<Block<File>> {
         let mut file = OpenOptions::new().read(true).write(true).open(path)?;
         let header: Header = read_from(&mut file)?;
         let sig: Signature = read_from(&mut file)?;
         crypto::verify_header(&header, &sig)?;
-        Ok(NewBlock {
+        Ok(Block {
             header,
             sig,
-            body: FileBody::new(file)?,
+            body: file,
         })
     }
 }
 
-impl<T: Write> Write for NewBlock<T> {
+impl<T: Write> Write for Block<T> {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
         self.body.write(buf)
     }
@@ -168,13 +179,13 @@ impl<T: Write> Write for NewBlock<T> {
     }
 }
 
-impl<T: Read> Read for NewBlock<T> {
+impl<T: Read> Read for Block<T> {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         self.body.read(buf)
     }
 }
 
-impl<T: Seek> Seek for NewBlock<T> {
+impl<T: Seek> Seek for Block<T> {
     fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
         self.body.seek(pos)
     }
@@ -201,50 +212,6 @@ impl<T, U: Decompose<T>, S: TryCompose<T, U, Error = Infallible>> Compose<T, U>
     }
 }
 
-struct FileBody {
-    body_offset: u64,
-    file: File,
-}
-
-impl FileBody {
-    /// Create a new `FileBody` which wraps the given `File`. The current position of the file
-    /// is used as the offset into the file where the body starts.
-    fn new(mut file: File) -> Result<FileBody> {
-        Ok(FileBody {
-            body_offset: file.stream_position()?,
-            file,
-        })
-    }
-}
-
-impl Write for FileBody {
-    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        self.file.write(buf)
-    }
-
-    fn flush(&mut self) -> io::Result<()> {
-        self.file.flush()
-    }
-}
-
-impl Read for FileBody {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        self.file.read(buf)
-    }
-}
-
-impl Seek for FileBody {
-    /// Seeks the file to the given position in the body. The offset given to this method is the
-    /// offset into the body, thus to get the offset into the file we add the offset of the body.
-    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
-        match pos {
-            SeekFrom::Start(pos) => self.file.seek(SeekFrom::Start(pos + self.body_offset)),
-            SeekFrom::Current(pos) => self.file.seek(SeekFrom::Current(pos)),
-            SeekFrom::End(_pos) => unimplemented!(),
-        }
-    }
-}
-
 /// Extensions to the `Read` trait.
 trait ReadExt: Read {
     /// Reads repeatedly until one of the following occur:
@@ -619,25 +586,6 @@ impl<T: Seek + Read + Write> Seek for SectoredBuf<T> {
     }
 }
 
-/// A container which binds together ciphertext along with the metadata needed to identify,
-/// verify and decrypt it.
-#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
-struct Block {
-    /// Identifies this block and defines its location in the tree.
-    path: Path,
-    /// This field contains a collection of `Readcap`s indexed by the principal who holds them.
-    /// `Readcap`s are envelopments of the key used to encrypt this block.
-    readcaps: HashMap<Principal, Cryptotext<SymKey>>,
-    /// This field is used to verify that the signer of this block had permission to write it.
-    /// It contains a certificate chain that must lead back to the root key for the tree this block
-    /// is part of.
-    writecap: Writecap,
-    /// The encrypted data contained in this block.
-    body: Cryptotext<Vec<u8>>,
-    /// The contents of the block are covered by a digital signature contained in this field.  
-    signature: Signature
-}
-
 /// An envelopment of a key, which is tagged with the principal who the key is meant for.
 #[derive(Debug, PartialEq, Serialize, Deserialize)]
 struct Readcap {
@@ -685,7 +633,9 @@ struct Fragment {
 impl Fragment {
     /// Create a new fragment with the given fields. If `path_str` cannot be parsed then a failed
     /// `Result` is returned containing a `PathError`.
-    fn new(path_str: &str, serial_num: u32, body: Vec<u8>) -> std::result::Result<Fragment, PathError> {
+    fn new(
+        path_str: &str, serial_num: u32, body: Vec<u8>
+    ) -> std::result::Result<Fragment, PathError> {
         let result = Path::try_from(path_str);
         Ok(Fragment {
             path: result?,

+ 14 - 17
crates/btnode/src/serde_tests.rs

@@ -44,28 +44,25 @@ fn roundtrip_fragment() -> Result<()> {
 }
 
 #[test]
-fn roundtrip_write_cap() -> Result<()> {
-    let expected = make_writecap(vec!["nodes", "home"])?;
-    let ser_result = to_vec(&expected);
-    let de_result = from_vec(&ser_result?);
-    assert_eq!(expected, de_result?);
-    Ok(())
+fn roundtrip_write_cap() {
+    let expected = make_writecap(vec!["nodes", "home"]);
+    let ser_result = to_vec(&expected).expect("to_vec failed");
+    let actual = from_vec(&ser_result).expect("from_vec failed");
+    assert_eq!(expected, actual);
 }
 
 #[test]
-fn roundtrip_read_cap() -> Result<()> {
+fn roundtrip_read_cap() {
     let expected = make_readcap();
-    let ser_result = to_vec(&expected);
-    let de_result = from_vec(&ser_result?);
-    assert_eq!(expected, de_result?);
-    Ok(())
+    let ser_result = to_vec(&expected).expect("to_vec failed");
+    let actual = from_vec(&ser_result).expect("from_vec failed");
+    assert_eq!(expected, actual);
 }
 
 #[test]
-fn roundtrip_versioned_block() -> Result<()> {
-    let expected = VersionedBlock::V0(make_block()?);
-    let ser_result = to_vec(&expected);
-    let de_result = from_vec(&ser_result?);
-    assert_eq!(expected, de_result?);
-    Ok(())
+fn roundtrip_versioned_block() {
+    let expected = VersionedBlock::V0(make_block());
+    let ser_result = to_vec(&expected).expect("to_vec failed");
+    let actual = from_vec(&ser_result).expect("from_vec failed");
+    assert_eq!(expected, actual);
 }

+ 134 - 28
crates/btnode/src/test_helpers.rs

@@ -3,7 +3,12 @@
 use super::*;
 use crypto::*;
 use serde_block_tree::{Error, Result};
-use std::{ fs::File, io::{Write, Cursor}, fmt::Write as FmtWrite };
+use std::{
+    fs::File,
+    io::{Write, Cursor},
+    fmt::Write as FmtWrite,
+    cell::RefCell
+};
 
 pub const PRINCIPAL: [u8; 32] = [
     0x75, 0x28, 0xA9, 0xE0, 0x9D, 0x24, 0xBA, 0xB3, 0x79, 0x56, 0x15, 0x68, 0xFD, 0xA4, 0xE2, 0xA4,
@@ -388,8 +393,8 @@ pub(crate) fn make_path(rel_components: Vec<&str>) -> Path {
     make_path_with_owner(make_principal(), rel_components)
 }
 
-pub(crate) fn make_writecap(rel_components: Vec<&str>) -> Result<Writecap> {
-    let (root_writecap, root_key) = make_self_signed_writecap()?;
+pub(crate) fn make_writecap(rel_components: Vec<&str>) -> Writecap {
+    let (root_writecap, root_key) = make_self_signed_writecap();
     let issued_to = Principal(Hash::Sha2_256(PRINCIPAL));
     make_writecap_trusted_by(
         root_writecap, root_key, issued_to, rel_components)
@@ -397,7 +402,7 @@ pub(crate) fn make_writecap(rel_components: Vec<&str>) -> Result<Writecap> {
 
 pub(crate) fn make_writecap_trusted_by<C: Creds>(
     next: Writecap, trusting_creds: C, issued_to: Principal, path_components: Vec<&str>
-) -> Result<Writecap> {
+) -> Writecap {
     let hour_hence = Epoch::now() + Duration::from_secs(3600);
     let mut writecap = Writecap {
         issued_to,
@@ -408,8 +413,9 @@ pub(crate) fn make_writecap_trusted_by<C: Creds>(
         next: Some(Box::from(next)),
     };
     crypto::sign_writecap(&mut writecap, &trusting_creds)
-        .map_err(convert_err)?;
-    Ok(writecap)
+        .map_err(convert_err)
+        .expect("failed to sign writecap");
+    writecap
 }
 
 pub(crate) fn make_key_pair() -> impl Creds {
@@ -423,12 +429,12 @@ pub(crate) fn make_key_pair() -> impl Creds {
     ConcreteCreds::new(sign, encrypt)
 }
 
-pub(crate) fn make_self_signed_writecap() -> Result<(Writecap, impl Creds)> {
+pub(crate) fn make_self_signed_writecap() -> (Writecap, impl Creds) {
     let key = make_key_pair();
-    Ok((make_self_signed_writecap_with(&key)?, key))
+    (make_self_signed_writecap_with(&key), key)
 }
 
-pub(crate) fn make_self_signed_writecap_with<C: Creds>(key: &C) -> Result<Writecap> {
+pub(crate) fn make_self_signed_writecap_with<C: Creds>(key: &C) -> Writecap {
     let root_principal = key.owner();
     let hour_hence = Epoch::now() + Duration::from_secs(3600);
     let mut writecap = Writecap {
@@ -440,8 +446,9 @@ pub(crate) fn make_self_signed_writecap_with<C: Creds>(key: &C) -> Result<Writec
         next: None,
     };
     crypto::sign_writecap(&mut writecap, key)
-        .map_err(convert_err)?;
-    Ok(writecap)
+        .map_err(convert_err)
+        .expect("failed to sign writecap");
+    writecap
 }
 
 pub(crate) fn make_readcap() -> Readcap {
@@ -450,24 +457,27 @@ pub(crate) fn make_readcap() -> Readcap {
     )
 }
 
-pub(crate) fn make_block() -> Result<Block> {
+pub(crate) fn make_block() -> Block<SerdeCursor<Vec<u8>>> {
     make_block_with(make_readcap())
 }
 
-pub(crate) fn make_block_with(readcap: Readcap) -> Result<Block> {
+pub(crate) fn make_block_with(readcap: Readcap) -> Block<SerdeCursor<Vec<u8>>> {
     let mut readcaps = HashMap::new();
     readcaps.insert(readcap.issued_to, readcap.key);
     // Notice that the writecap path contains the block path. If this were not the case, the block
     // would be invalid.
-    let writecap = make_writecap(vec!["apps"])?;
+    let writecap = make_writecap(vec!["apps"]);
     let root_writecap = writecap.next.as_ref().unwrap();
-    Ok(Block {
-        path: make_path_with_owner(root_writecap.issued_to.clone(), vec!["apps", "verse"]),
-        readcaps,
-        writecap,
-        body: Cryptotext::Plain(Vec::from(PAYLOAD)),
-        signature: Signature::copy_from(Sign::RSA_PSS_3072_SHA_256, &SIGNATURE)
-    })
+    Block {
+        header: Header {
+            path: make_path_with_owner(root_writecap.issued_to.clone(), vec!["apps", "verse"]),
+            readcaps,
+            writecap,
+            merkle_root: Hash::Sha2_256([0u8; HashKind::Sha2_256.len()]), 
+        },
+        sig: Signature::copy_from(Sign::RSA_PSS_3072_SHA_256, &SIGNATURE),
+        body: SerdeCursor::new(Vec::new()),
+    }
 }
 
 /// This function can be run as a test to write a new RSA key pair, as two Rust arrays,
@@ -586,18 +596,114 @@ pub fn read_check<R: Read>(mut read: R, sect_sz: usize, sect_ct: usize) {
     }
 }
 
-pub struct SectoredCursor<T> {
-    cursor: Cursor<T>,
+/// Trait for types which can be referenced as slices and which support conversion from `Vec<u8>`.
+pub trait FromVec: AsRef<[u8]> {
+    fn from_vec(vec: Vec<u8>) -> Self;
+}
+
+impl FromVec for Vec<u8> {
+    fn from_vec(vec: Vec<u8>) -> Self {
+        vec
+    }
+}
+
+impl<const N: usize> FromVec for [u8; N] {
+    fn from_vec(vec: Vec<u8>) -> Self {
+        assert_eq!(N, vec.len());
+        let mut buf = [0u8; N];
+        buf.copy_from_slice(&vec);
+        buf
+    }
+}
+
+/// Module containing functions for serializing and deserializing buffers in `SectoredCursor<T>`.
+mod serde_cursor {
+    use std::{
+        cell::RefCell,
+        io::{Cursor, Read},
+        result::Result,
+    };
+    use serde::{Serializer, Serialize, Deserializer, Deserialize};
+    use super::FromVec;
+
+    pub fn serialize<T: AsRef<[u8]>, S: Serializer>(
+        cursor: &RefCell<Cursor<T>>, ser: S
+    ) -> Result<S::Ok, S::Error> {
+        let mut cursor = cursor.borrow_mut();
+        let pos = cursor.position();
+        cursor.set_position(0);
+        let mut data = Vec::new();
+        cursor.read_to_end(&mut data).expect("reading from cursor failed");
+        cursor.set_position(pos);
+        data.serialize(ser)
+    }
+
+    pub fn deserialize<'de, T: FromVec, D: Deserializer<'de>>(
+        de: D
+    ) -> Result<RefCell<Cursor<T>>, D::Error> {
+        let data = Vec::<u8>::deserialize(de)?;
+        Ok(RefCell::new(Cursor::new(T::from_vec(data))))
+    }
+}
+
+/// A wrapper for `Cursor<T>` which implements `Serialize` and `Deserialize<'de>` for any `'de'.
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct SerdeCursor<T: FromVec> {
+    #[serde(with = "serde_cursor")]
+    cursor: RefCell<Cursor<T>>
+}
+
+impl<T: FromVec> SerdeCursor<T> {
+    fn new(inner: T) -> SerdeCursor<T> {
+        SerdeCursor { cursor: RefCell::new(Cursor::new(inner)) }
+    }
+}
+
+impl Write for SerdeCursor<Vec<u8>> {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.cursor.get_mut().write(buf)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        self.cursor.get_mut().flush()
+    }
+}
+
+impl<const N: usize> Write for SerdeCursor<[u8; N]> {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.cursor.get_mut().write(buf)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        self.cursor.get_mut().flush()
+    }
+}
+
+impl<T: FromVec> Read for SerdeCursor<T> {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        self.cursor.get_mut().read(buf)
+    }
+}
+
+impl<T: FromVec> Seek for SerdeCursor<T> {
+    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
+        self.cursor.get_mut().seek(pos)
+    }
+}
+
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct SectoredCursor<T: FromVec> {
+    cursor: SerdeCursor<T>,
     sect_sz: usize,
 }
 
-impl<T> SectoredCursor<T> {
+impl<T: FromVec> SectoredCursor<T> {
     pub fn new(inner: T, sect_sz: usize) -> SectoredCursor<T> {
-        SectoredCursor { cursor: Cursor::new(inner), sect_sz }
+        SectoredCursor { cursor: SerdeCursor::new(inner), sect_sz }
     }
 }
 
-impl<T> Sectored for SectoredCursor<T> {
+impl<T: FromVec> Sectored for SectoredCursor<T> {
     fn sector_sz(&self) -> usize {
         self.sect_sz
     }
@@ -625,14 +731,14 @@ impl<const N: usize> Write for SectoredCursor<[u8; N]> {
     }
 }
 
-impl<T: AsRef<[u8]>> Read for SectoredCursor<T> {
+impl<T: FromVec> Read for SectoredCursor<T> {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         self.assert_sector_sz(buf.len())?;
         self.cursor.read(buf)
     }
 }
 
-impl<T: AsRef<[u8]>> Seek for SectoredCursor<T> {
+impl<T: FromVec> Seek for SectoredCursor<T> {
     fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
         self.cursor.seek(pos)
     }