From 4795d019be60b6cc76fc498a34c69b349b73ab97 Mon Sep 17 00:00:00 2001 From: Vera Gonzalez Date: Mon, 13 Nov 2023 14:13:28 -0500 Subject: [PATCH 1/7] feat: implemented SnapshotKey decryption on PrivateFiles --- wnfs/examples/private.rs | 2 +- wnfs/examples/tiered_blockstores.rs | 5 +- wnfs/src/private/directory.rs | 47 ++++++++--- wnfs/src/private/file.rs | 55 ++++++++++++- wnfs/src/private/node/header.rs | 36 ++++++++- wnfs/src/private/node/node.rs | 50 ++++++++++-- wnfs/src/private/previous.rs | 28 +++---- wnfs/src/private/share.rs | 117 ++++++++++++++++++++++++---- 8 files changed, 284 insertions(+), 56 deletions(-) diff --git a/wnfs/examples/private.rs b/wnfs/examples/private.rs index 048cb654..01fca392 100644 --- a/wnfs/examples/private.rs +++ b/wnfs/examples/private.rs @@ -62,7 +62,7 @@ async fn create_forest_and_add_directory( .unwrap(); // Private ref contains data and keys for fetching and decrypting the directory node in the private forest. - let private_ref = dir.store(forest, store, rng).await.unwrap(); + let private_ref = dir.store_temporal(forest, store, rng).await.unwrap(); // Persist encoded private forest to the block store. let forest_cid = store.put_async_serializable(forest).await.unwrap(); diff --git a/wnfs/examples/tiered_blockstores.rs b/wnfs/examples/tiered_blockstores.rs index 6c2f37ec..875fb49d 100644 --- a/wnfs/examples/tiered_blockstores.rs +++ b/wnfs/examples/tiered_blockstores.rs @@ -54,7 +54,10 @@ async fn main() { .unwrap(); // When storing the hierarchy data blocks, we use the `hot_store`: - let private_ref = directory.store(forest, &mut hot_store, rng).await.unwrap(); + let private_ref = directory + .store_temporal(forest, &mut hot_store, rng) + .await + .unwrap(); // Same thing for the forest. Doing this will give us a single root CID // for all of the data, but parts separated into `hot_store` and `cold_store`: diff --git a/wnfs/src/private/directory.rs b/wnfs/src/private/directory.rs index 926e5eb5..9d7b4528 100644 --- a/wnfs/src/private/directory.rs +++ b/wnfs/src/private/directory.rs @@ -1,7 +1,7 @@ use super::{ encrypted::Encrypted, link::PrivateLink, PrivateDirectoryContentSerializable, PrivateFile, PrivateForest, PrivateNode, PrivateNodeContentSerializable, PrivateNodeHeader, PrivateRef, - TemporalKey, + SnapshotKey, TemporalKey, }; use crate::{error::FsError, traits::Id, SearchResult, WNFS_VERSION}; use anyhow::{bail, ensure, Result}; @@ -139,7 +139,7 @@ impl PrivateDirectory { rng: &mut impl RngCore, ) -> Result> { let dir = Rc::new(Self::new(parent_bare_name, time, rng)); - dir.store(forest, store, rng).await?; + dir.store_temporal(forest, store, rng).await?; Ok(dir) } @@ -160,7 +160,7 @@ impl PrivateDirectory { ratchet_seed, inumber, )); - dir.store(forest, store, rng).await?; + dir.store_temporal(forest, store, rng).await?; Ok(dir) } @@ -1433,13 +1433,38 @@ impl PrivateDirectory { /// ); /// } /// ``` - pub async fn store( + pub async fn store_temporal( &self, forest: &mut Rc, store: &impl BlockStore, rng: &mut impl RngCore, ) -> Result { - let header_cid = self.header.store(store).await?; + let header_cid = self.header.store_temporal(store).await?; + let temporal_key = self.header.derive_temporal_key(); + let label = self.header.get_saturated_name(); + + let content_cid = self + .content + .store(header_cid, &temporal_key, forest, store, rng) + .await?; + + forest + .put_encrypted(label, [header_cid, content_cid], store) + .await?; + + Ok(self + .header + .derive_revision_ref() + .as_private_ref(content_cid)) + } + + pub async fn store_snapshot( + &self, + forest: &mut Rc, + store: &impl BlockStore, + rng: &mut impl RngCore, + ) -> Result { + let header_cid = self.header.store_snapshot(store, rng).await?; let temporal_key = self.header.derive_temporal_key(); let label = self.header.get_saturated_name(); @@ -1459,7 +1484,7 @@ impl PrivateDirectory { } /// Creates a new [`PrivateDirectory`] from a [`PrivateDirectoryContentSerializable`]. - pub(crate) async fn from_serializable( + pub(crate) async fn from_serializable_temporal( serializable: PrivateDirectoryContentSerializable, temporal_key: &TemporalKey, cid: Cid, @@ -1483,9 +1508,11 @@ impl PrivateDirectory { entries: entries_decrypted, }; - let header = PrivateNodeHeader::load(&serializable.header_cid, temporal_key, store).await?; + let header = + PrivateNodeHeader::load_temporal(&serializable.header_cid, temporal_key, store).await?; Ok(Self { header, content }) } + /// Wraps the directory in a [`PrivateNode`]. pub fn as_node(self: &Rc) -> PrivateNode { PrivateNode::Dir(Rc::clone(self)) @@ -1962,7 +1989,7 @@ mod tests { .await .unwrap(); - root_dir.store(forest, store, rng).await.unwrap(); + root_dir.store_temporal(forest, store, rng).await.unwrap(); let old_root = &Rc::clone(root_dir); @@ -1971,7 +1998,7 @@ mod tests { .await .unwrap(); - root_dir.store(forest, store, rng).await.unwrap(); + root_dir.store_temporal(forest, store, rng).await.unwrap(); let new_read = root_dir.read(&path, false, forest, store).await.unwrap(); @@ -2370,7 +2397,7 @@ mod tests { Utc::now(), rng, )); - old_dir.store(forest, store, rng).await.unwrap(); + old_dir.store_temporal(forest, store, rng).await.unwrap(); let new_dir = &mut Rc::clone(old_dir); new_dir diff --git a/wnfs/src/private/file.rs b/wnfs/src/private/file.rs index 66bc2c02..3404f296 100644 --- a/wnfs/src/private/file.rs +++ b/wnfs/src/private/file.rs @@ -765,13 +765,38 @@ impl PrivateFile { /// ); /// } /// ``` - pub async fn store( + pub async fn store_temporal( &self, forest: &mut Rc, store: &impl BlockStore, rng: &mut impl RngCore, ) -> Result { - let header_cid = self.header.store(store).await?; + let header_cid = self.header.store_temporal(store).await?; + let snapshot_key = self.header.derive_temporal_key().derive_snapshot_key(); + let label = self.header.get_saturated_name(); + + let content_cid = self + .content + .store(header_cid, &snapshot_key, store, rng) + .await?; + + forest + .put_encrypted(label, [header_cid, content_cid], store) + .await?; + + Ok(self + .header + .derive_revision_ref() + .as_private_ref(content_cid)) + } + + pub async fn store_snapshot( + &self, + forest: &mut Rc, + store: &impl BlockStore, + rng: &mut impl RngCore, + ) -> Result { + let header_cid = self.header.store_snapshot(store, rng).await?; let snapshot_key = self.header.derive_temporal_key().derive_snapshot_key(); let label = self.header.get_saturated_name(); @@ -808,7 +833,31 @@ impl PrivateFile { content: serializable.content, }; - let header = PrivateNodeHeader::load(&serializable.header_cid, temporal_key, store).await?; + let header = + PrivateNodeHeader::load_temporal(&serializable.header_cid, temporal_key, store).await?; + Ok(Self { header, content }) + } + + /// Creates a new [`PrivateFile`] from a [`PrivateFileContentSerializable`] but only a Snapshot. + pub(crate) async fn from_serializable_snapshot( + serializable: PrivateFileContentSerializable, + snapshot_key: &SnapshotKey, + cid: Cid, + store: &impl BlockStore, + ) -> Result { + if serializable.version.major != 0 || serializable.version.minor != 2 { + bail!(FsError::UnexpectedVersion(serializable.version)); + } + + let content = PrivateFileContent { + persisted_as: OnceCell::new_with(Some(cid)), + previous: serializable.previous.into_iter().collect(), + metadata: serializable.metadata, + content: serializable.content, + }; + + let header = + PrivateNodeHeader::load_snapshot(&serializable.header_cid, snapshot_key, store).await?; Ok(Self { header, content }) } diff --git a/wnfs/src/private/node/header.rs b/wnfs/src/private/node/header.rs index 9e69c9d8..b73dcfb0 100644 --- a/wnfs/src/private/node/header.rs +++ b/wnfs/src/private/node/header.rs @@ -1,4 +1,4 @@ -use super::TemporalKey; +use super::{SnapshotKey, TemporalKey}; use crate::private::RevisionRef; use anyhow::Result; use libipld::{Cid, IpldCodec}; @@ -9,7 +9,7 @@ use skip_ratchet::Ratchet; use std::fmt::Debug; use wnfs_common::{utils, BlockStore, HashOutput, HASH_BYTE_SIZE}; use wnfs_hamt::Hasher; -use wnfs_namefilter::Namefilter; +use wnfs_namefilter::{BloomFilter, Namefilter}; //-------------------------------------------------------------------------------------------------- // Type Definitions @@ -214,16 +214,28 @@ impl PrivateNodeHeader { /// Encrypts this private node header in an block, then stores that in the given /// BlockStore and returns its CID. - pub async fn store(&self, store: &impl BlockStore) -> Result { + pub async fn store_temporal(&self, store: &impl BlockStore) -> Result { let temporal_key = self.derive_temporal_key(); let cbor_bytes = serde_ipld_dagcbor::to_vec(self)?; let ciphertext = temporal_key.key_wrap_encrypt(&cbor_bytes)?; store.put_block(ciphertext, IpldCodec::Raw).await } + pub async fn store_snapshot( + &self, + store: &impl BlockStore, + rng: &mut impl RngCore, + ) -> Result { + let snapshot_key = self.derive_temporal_key().derive_snapshot_key(); + let tuple = (self.inumber, self.bare_name.clone()); + let cbor_bytes = serde_ipld_dagcbor::to_vec(&tuple)?; + // let ciphertext = snapshot_key.encrypt(&cbor_bytes, rng)?; + store.put_block(cbor_bytes, IpldCodec::Raw).await + } + /// Loads a private node header from a given CID linking to the ciphertext block /// to be decrypted with given key. - pub(crate) async fn load( + pub(crate) async fn load_temporal( cid: &Cid, temporal_key: &TemporalKey, store: &impl BlockStore, @@ -232,6 +244,22 @@ impl PrivateNodeHeader { let cbor_bytes = temporal_key.key_wrap_decrypt(&ciphertext)?; Ok(serde_ipld_dagcbor::from_slice(&cbor_bytes)?) } + + pub(crate) async fn load_snapshot( + cid: &Cid, + snapshot_key: &SnapshotKey, + store: &impl BlockStore, + ) -> Result { + let cbor_bytes = store.get_block(cid).await?; + // let cbor_bytes = snapshot_key.decrypt(&ciphertext)?; + let tuple: ([u8; 32], BloomFilter<256, 30>) = serde_ipld_dagcbor::from_slice(&cbor_bytes)?; + let header = PrivateNodeHeader { + inumber: tuple.0, + ratchet: Ratchet::default(), + bare_name: tuple.1, + }; + Ok(header) + } } impl Debug for PrivateNodeHeader { diff --git a/wnfs/src/private/node/node.rs b/wnfs/src/private/node/node.rs index 5732a9d8..ec2567ca 100644 --- a/wnfs/src/private/node/node.rs +++ b/wnfs/src/private/node/node.rs @@ -1,9 +1,9 @@ -use super::{PrivateNodeHeader, TemporalKey}; +use super::{PrivateNodeHeader, SnapshotKey, TemporalKey}; use crate::{ error::FsError, private::{ - encrypted::Encrypted, link::PrivateLink, PrivateDirectory, PrivateFile, PrivateForest, - PrivateNodeContentSerializable, PrivateRef, + encrypted::Encrypted, link::PrivateLink, share::SnapshotSharePointer, PrivateDirectory, + PrivateFile, PrivateForest, PrivateNodeContentSerializable, PrivateRef, }, traits::Id, }; @@ -481,9 +481,46 @@ impl PrivateNode { _ => return Err(FsError::NotFound.into()), }; + // let snapshot_key = private_ref.temporal_key.derive_snapshot_key(); Self::from_cid(cid, &private_ref.temporal_key, store).await } + /// A version of the load function designed to work when only a SnapshotKey is available + pub async fn load_from_snapshot( + snapshot: SnapshotSharePointer, + forest: &PrivateForest, + store: &impl BlockStore, + ) -> Result { + let cid = match forest.get_encrypted(&snapshot.label, store).await? { + Some(cids) if cids.contains(&snapshot.content_cid) => snapshot.content_cid, + _ => return Err(FsError::NotFound.into()), + }; + + Self::from_cid_snapshot(cid, &snapshot.snapshot_key, store).await + } + + pub(crate) async fn from_cid_snapshot( + cid: Cid, + snapshot_key: &SnapshotKey, + store: &impl BlockStore, + ) -> Result { + let encrypted_bytes = store.get_block(&cid).await?; + let bytes = snapshot_key.decrypt(&encrypted_bytes)?; + let node: PrivateNodeContentSerializable = serde_ipld_dagcbor::from_slice(&bytes)?; + let node = match node { + PrivateNodeContentSerializable::File(file) => { + let file = + PrivateFile::from_serializable_snapshot(file, snapshot_key, cid, store).await?; + PrivateNode::File(Rc::new(file)) + } + PrivateNodeContentSerializable::Dir(_) => { + bail!("cannot deserialize dir from snapshot yet"); + } + }; + + Ok(node) + } + pub(crate) async fn from_cid( cid: Cid, temporal_key: &TemporalKey, @@ -500,7 +537,8 @@ impl PrivateNode { } PrivateNodeContentSerializable::Dir(dir) => { let dir = - PrivateDirectory::from_serializable(dir, temporal_key, cid, store).await?; + PrivateDirectory::from_serializable_temporal(dir, temporal_key, cid, store) + .await?; PrivateNode::Dir(Rc::new(dir)) } }; @@ -515,8 +553,8 @@ impl PrivateNode { rng: &mut impl RngCore, ) -> Result { match self { - Self::File(file) => file.store(forest, store, rng).await, - Self::Dir(dir) => dir.store(forest, store, rng).await, + Self::File(file) => file.store_temporal(forest, store, rng).await, + Self::Dir(dir) => dir.store_temporal(forest, store, rng).await, } } diff --git a/wnfs/src/private/previous.rs b/wnfs/src/private/previous.rs index 2b42fbf2..e5047a69 100644 --- a/wnfs/src/private/previous.rs +++ b/wnfs/src/private/previous.rs @@ -550,7 +550,7 @@ mod tests { let rng = &mut rng; let store = &mut store; - root_dir.store(forest, store, rng).await.unwrap(); + root_dir.store_temporal(forest, store, rng).await.unwrap(); let past_dir = root_dir.clone(); @@ -567,14 +567,14 @@ mod tests { .await .unwrap(); - root_dir.store(forest, store, rng).await.unwrap(); + root_dir.store_temporal(forest, store, rng).await.unwrap(); root_dir .mkdir(&["docs".into()], true, Utc::now(), forest, store, rng) .await .unwrap(); - root_dir.store(forest, store, rng).await.unwrap(); + root_dir.store_temporal(forest, store, rng).await.unwrap(); let mut iterator = PrivateNodeOnPathHistory::of( root_dir, @@ -635,7 +635,7 @@ mod tests { let rng = &mut rng; let store = &mut store; - root_dir.store(forest, store, rng).await.unwrap(); + root_dir.store_temporal(forest, store, rng).await.unwrap(); let past_dir = root_dir.clone(); @@ -646,7 +646,7 @@ mod tests { .await .unwrap(); - root_dir.store(forest, store, rng).await.unwrap(); + root_dir.store_temporal(forest, store, rng).await.unwrap(); root_dir .write( @@ -661,7 +661,7 @@ mod tests { .await .unwrap(); - root_dir.store(forest, store, rng).await.unwrap(); + root_dir.store_temporal(forest, store, rng).await.unwrap(); let mut iterator = PrivateNodeOnPathHistory::of( root_dir, @@ -735,7 +735,7 @@ mod tests { let rng = &mut rng; let store = &mut store; - root_dir.store(forest, store, rng).await.unwrap(); + root_dir.store_temporal(forest, store, rng).await.unwrap(); let past_dir = root_dir.clone(); @@ -746,7 +746,7 @@ mod tests { .await .unwrap(); - root_dir.store(forest, store, rng).await.unwrap(); + root_dir.store_temporal(forest, store, rng).await.unwrap(); let docs_dir = root_dir .get_node(&["Docs".into()], true, forest, store) @@ -768,7 +768,7 @@ mod tests { .await .unwrap(); - docs_dir.store(forest, store, rng).await.unwrap(); + docs_dir.store_temporal(forest, store, rng).await.unwrap(); let mut iterator = PrivateNodeOnPathHistory::of( root_dir, @@ -857,7 +857,7 @@ mod tests { .await .unwrap(); - root_dir.store(forest, store, rng).await.unwrap(); + root_dir.store_temporal(forest, store, rng).await.unwrap(); let past_dir = root_dir.clone(); @@ -881,7 +881,7 @@ mod tests { .await .unwrap(); - docs_dir.store(forest, store, rng).await.unwrap(); + docs_dir.store_temporal(forest, store, rng).await.unwrap(); root_dir .write( @@ -896,7 +896,7 @@ mod tests { .await .unwrap(); - root_dir.store(forest, store, rng).await.unwrap(); + root_dir.store_temporal(forest, store, rng).await.unwrap(); let mut iterator = PrivateNodeOnPathHistory::of( root_dir, @@ -999,13 +999,13 @@ mod tests { .await .unwrap(); - root_dir.store(forest, store, rng).await.unwrap(); + root_dir.store_temporal(forest, store, rng).await.unwrap(); let past_dir = root_dir.clone(); let mut root_dir = Rc::new(root_dir.prepare_next_revision().unwrap().clone()); - root_dir.store(forest, store, rng).await.unwrap(); + root_dir.store_temporal(forest, store, rng).await.unwrap(); root_dir .write( diff --git a/wnfs/src/private/share.rs b/wnfs/src/private/share.rs index 292a0cfe..40b47609 100644 --- a/wnfs/src/private/share.rs +++ b/wnfs/src/private/share.rs @@ -38,7 +38,7 @@ pub struct Share<'a, K: ExchangeKey, S: BlockStore> { pub struct Sharer<'a, S: BlockStore> { pub root_did: String, pub forest: &'a mut Rc, - pub store: &'a mut S, + pub store: &'a S, } #[derive(Debug)] @@ -303,7 +303,7 @@ pub mod sharer { } pub mod recipient { - use super::{sharer, SharePayload, TemporalSharePointer}; + use super::{sharer, SharePayload, SnapshotSharePointer, TemporalSharePointer}; use crate::{ error::ShareError, private::{PrivateForest, PrivateKey, PrivateNode, PrivateRef}, @@ -369,19 +369,21 @@ pub mod recipient { let payload: SharePayload = serde_ipld_dagcbor::from_slice(&recipient_key.decrypt(&encrypted_payload).await?)?; - let SharePayload::Temporal(TemporalSharePointer { - label, - content_cid, - temporal_key, - }) = payload - else { - // TODO(appcypher): We currently need both TemporalKey to decrypt a node. - bail!(ShareError::UnsupportedSnapshotShareReceipt); - }; - - // Use decrypted payload to get cid to encrypted node in sharer's forest. - let private_ref = PrivateRef::with_temporal_key(label, temporal_key, content_cid); - PrivateNode::load(&private_ref, sharer_forest, store).await + match payload { + SharePayload::Temporal(TemporalSharePointer { + label, + content_cid, + temporal_key, + }) => { + // Use decrypted payload to get cid to encrypted node in sharer's forest. + let private_ref = PrivateRef::with_temporal_key(label, temporal_key, content_cid); + PrivateNode::load(&private_ref, sharer_forest, store).await + } + SharePayload::Snapshot(snapshot) => { + // Load from Snapshot + PrivateNode::load_from_snapshot(snapshot, sharer_forest, store).await + } + } } } @@ -467,9 +469,9 @@ mod tests { } #[async_std::test] - async fn can_share_and_recieve_share() { + async fn can_share_and_recieve_temporal_share() { let recipient_store = &mut MemoryBlockStore::default(); - let sharer_store = &mut MemoryBlockStore::default(); + let sharer_store = &MemoryBlockStore::default(); let sharer_forest = &mut Rc::new(PrivateForest::new()); let rng = &mut TestRng::deterministic_rng(RngAlgorithm::ChaCha); @@ -532,6 +534,87 @@ mod tests { assert_eq!(node.as_dir().unwrap(), sharer_dir); } + #[async_std::test] + async fn can_share_and_recieve_snapshot_share() { + let recipient_store = &mut MemoryBlockStore::default(); + let sharer_store = &MemoryBlockStore::default(); + let sharer_forest = &mut Rc::new(PrivateForest::new()); + let rng = &mut TestRng::deterministic_rng(RngAlgorithm::ChaCha); + + let sharer_root_did = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"; + // Create directory to share. + + let sharer_dir = helper::create_sharer_dir(sharer_forest, sharer_store, rng) + .await + .unwrap(); + + let sharer_file = sharer_dir + .get_node(&["text.txt".into()], true, sharer_forest, sharer_store) + .await + .unwrap() + .unwrap() + .as_file() + .unwrap(); + sharer_file + .store_snapshot(sharer_forest, sharer_store, rng) + .await + .unwrap(); + + // Establish recipient exchange root. + let (recipient_key, recipient_exchange_root) = + helper::create_recipient_exchange_root(recipient_store) + .await + .unwrap(); + + // Construct share payload from sharer's directory. + let sharer_payload = SharePayload::from_node( + &sharer_file.as_node(), + false, + sharer_forest, + sharer_store, + rng, + ) + .await + .unwrap(); + + // Share payload with recipient. + Share::::new(&sharer_payload, 0) + .by(Sharer { + root_did: sharer_root_did.into(), + store: sharer_store, + forest: sharer_forest, + }) + .to(Recipient { + exchange_root: PublicLink::new(PublicNode::Dir(recipient_exchange_root)), + store: recipient_store, + }) + .finish() + .await + .unwrap(); + + // Create share label. + let share_label = sharer::create_share_label( + 0, + sharer_root_did, + &recipient_key + .get_public_key() + .get_public_key_modulus() + .unwrap(), + ); + + // Grab node using share label. + let node = + recipient::receive_share(share_label, &recipient_key, sharer_forest, sharer_store) + .await + .unwrap(); + + // Assert payload is the same as the original. + let file = node.as_file().unwrap(); + let content = file.get_content(sharer_forest, sharer_store).await.unwrap(); + let content_string = String::from_utf8(content).unwrap(); + assert_eq!(content_string, "Hello World!".to_string()); + } + #[async_std::test] async fn serialized_share_payload_can_be_deserialized() { let store = &mut MemoryBlockStore::default(); From 66be8fc0c9fb495f78b195c4cbc8a1f3d198515e Mon Sep 17 00:00:00 2001 From: Vera Gonzalez Date: Mon, 13 Nov 2023 15:30:16 -0500 Subject: [PATCH 2/7] fix: docs tests --- wnfs/src/private/directory.rs | 4 ++-- wnfs/src/private/file.rs | 2 +- wnfs/src/private/forest.rs | 4 ++-- wnfs/src/private/node/node.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/wnfs/src/private/directory.rs b/wnfs/src/private/directory.rs index 9d7b4528..95b554ba 100644 --- a/wnfs/src/private/directory.rs +++ b/wnfs/src/private/directory.rs @@ -832,7 +832,7 @@ impl PrivateDirectory { /// .await /// .unwrap(); /// - /// dir_clone.store(forest, store, rng).await.unwrap(); + /// dir_clone.store_temporal(forest, store, rng).await.unwrap(); /// /// let latest_dir = init_dir.search_latest(forest, store).await.unwrap(); /// @@ -1423,7 +1423,7 @@ impl PrivateDirectory { /// rng, /// )); /// - /// let private_ref = dir.store(forest, store, rng).await.unwrap(); + /// let private_ref = dir.store_temporal(forest, store, rng).await.unwrap(); /// /// let node = PrivateNode::Dir(Rc::clone(&dir)); /// diff --git a/wnfs/src/private/file.rs b/wnfs/src/private/file.rs index 3404f296..d39b9faa 100644 --- a/wnfs/src/private/file.rs +++ b/wnfs/src/private/file.rs @@ -755,7 +755,7 @@ impl PrivateFile { /// rng, /// )); /// - /// let private_ref = file.store(forest, store, rng).await.unwrap(); + /// let private_ref = file.store_temporal(forest, store, rng).await.unwrap(); /// /// let node = PrivateNode::File(Rc::clone(&file)); /// diff --git a/wnfs/src/private/forest.rs b/wnfs/src/private/forest.rs index 420318c3..099e15de 100644 --- a/wnfs/src/private/forest.rs +++ b/wnfs/src/private/forest.rs @@ -231,7 +231,7 @@ where /// ratchet_seed, /// inumber /// )); - /// root_dir.store(main_forest, store, rng).await.unwrap(); + /// root_dir.store_temporal(main_forest, store, rng).await.unwrap(); /// /// let other_forest = &mut Rc::new(PrivateForest::new()); /// let root_dir = Rc::new(PrivateDirectory::with_seed( @@ -240,7 +240,7 @@ where /// ratchet_seed, /// inumber /// )); - /// root_dir.store(other_forest, store, rng).await.unwrap(); + /// root_dir.store_temporal(other_forest, store, rng).await.unwrap(); /// /// let merge_forest = main_forest.merge(other_forest, store).await.unwrap(); /// diff --git a/wnfs/src/private/node/node.rs b/wnfs/src/private/node/node.rs index ec2567ca..ae4c27c8 100644 --- a/wnfs/src/private/node/node.rs +++ b/wnfs/src/private/node/node.rs @@ -351,7 +351,7 @@ impl PrivateNode { /// .await /// .unwrap(); /// - /// dir_clone.store(forest, store, rng).await.unwrap(); + /// dir_clone.store_temporal(forest, store, rng).await.unwrap(); /// /// let latest_node = PrivateNode::Dir(init_dir).search_latest(forest, store).await.unwrap(); /// From 354cda12d6100429bf8c676d4e93d703c69c0db7 Mon Sep 17 00:00:00 2001 From: Vera Gonzalez Date: Tue, 14 Nov 2023 10:42:58 -0500 Subject: [PATCH 3/7] fix: checks --- wnfs/src/private/directory.rs | 2 +- wnfs/src/private/node/header.rs | 6 +++--- wnfs/src/private/share.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/wnfs/src/private/directory.rs b/wnfs/src/private/directory.rs index 95b554ba..030c9578 100644 --- a/wnfs/src/private/directory.rs +++ b/wnfs/src/private/directory.rs @@ -1,7 +1,7 @@ use super::{ encrypted::Encrypted, link::PrivateLink, PrivateDirectoryContentSerializable, PrivateFile, PrivateForest, PrivateNode, PrivateNodeContentSerializable, PrivateNodeHeader, PrivateRef, - SnapshotKey, TemporalKey, + TemporalKey, }; use crate::{error::FsError, traits::Id, SearchResult, WNFS_VERSION}; use anyhow::{bail, ensure, Result}; diff --git a/wnfs/src/private/node/header.rs b/wnfs/src/private/node/header.rs index b73dcfb0..d5a9a45b 100644 --- a/wnfs/src/private/node/header.rs +++ b/wnfs/src/private/node/header.rs @@ -224,9 +224,9 @@ impl PrivateNodeHeader { pub async fn store_snapshot( &self, store: &impl BlockStore, - rng: &mut impl RngCore, + _rng: &mut impl RngCore, ) -> Result { - let snapshot_key = self.derive_temporal_key().derive_snapshot_key(); + let _snapshot_key = self.derive_temporal_key().derive_snapshot_key(); let tuple = (self.inumber, self.bare_name.clone()); let cbor_bytes = serde_ipld_dagcbor::to_vec(&tuple)?; // let ciphertext = snapshot_key.encrypt(&cbor_bytes, rng)?; @@ -247,7 +247,7 @@ impl PrivateNodeHeader { pub(crate) async fn load_snapshot( cid: &Cid, - snapshot_key: &SnapshotKey, + _snapshot_key: &SnapshotKey, store: &impl BlockStore, ) -> Result { let cbor_bytes = store.get_block(cid).await?; diff --git a/wnfs/src/private/share.rs b/wnfs/src/private/share.rs index 40b47609..9733389c 100644 --- a/wnfs/src/private/share.rs +++ b/wnfs/src/private/share.rs @@ -303,12 +303,12 @@ pub mod sharer { } pub mod recipient { - use super::{sharer, SharePayload, SnapshotSharePointer, TemporalSharePointer}; + use super::{sharer, SharePayload, TemporalSharePointer}; use crate::{ error::ShareError, private::{PrivateForest, PrivateKey, PrivateNode, PrivateRef}, }; - use anyhow::{bail, Result}; + use anyhow::Result; use sha3::Sha3_256; use wnfs_common::BlockStore; use wnfs_hamt::Hasher; From b895c037aec99253503b0c2f207e48acfed50da7 Mon Sep 17 00:00:00 2001 From: Vera Gonzalez Date: Tue, 14 Nov 2023 10:46:22 -0500 Subject: [PATCH 4/7] fix: snapshot blocks are encrypted now --- wnfs/src/private/node/header.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/wnfs/src/private/node/header.rs b/wnfs/src/private/node/header.rs index d5a9a45b..62fdd5bd 100644 --- a/wnfs/src/private/node/header.rs +++ b/wnfs/src/private/node/header.rs @@ -226,11 +226,11 @@ impl PrivateNodeHeader { store: &impl BlockStore, _rng: &mut impl RngCore, ) -> Result { - let _snapshot_key = self.derive_temporal_key().derive_snapshot_key(); + let snapshot_key = self.derive_temporal_key().derive_snapshot_key(); let tuple = (self.inumber, self.bare_name.clone()); let cbor_bytes = serde_ipld_dagcbor::to_vec(&tuple)?; - // let ciphertext = snapshot_key.encrypt(&cbor_bytes, rng)?; - store.put_block(cbor_bytes, IpldCodec::Raw).await + let ciphertext = TemporalKey(snapshot_key.0).key_wrap_encrypt(&cbor_bytes)?; + store.put_block(ciphertext, IpldCodec::Raw).await } /// Loads a private node header from a given CID linking to the ciphertext block @@ -247,11 +247,11 @@ impl PrivateNodeHeader { pub(crate) async fn load_snapshot( cid: &Cid, - _snapshot_key: &SnapshotKey, + snapshot_key: &SnapshotKey, store: &impl BlockStore, ) -> Result { - let cbor_bytes = store.get_block(cid).await?; - // let cbor_bytes = snapshot_key.decrypt(&ciphertext)?; + let ciphertext = store.get_block(cid).await?; + let cbor_bytes = TemporalKey(snapshot_key.0.to_owned()).key_wrap_decrypt(&ciphertext)?; let tuple: ([u8; 32], BloomFilter<256, 30>) = serde_ipld_dagcbor::from_slice(&cbor_bytes)?; let header = PrivateNodeHeader { inumber: tuple.0, From fa536d8fab98c8c87c6a171d4b3ffa717c996710 Mon Sep 17 00:00:00 2001 From: Vera Gonzalez Date: Tue, 14 Nov 2023 13:21:21 -0500 Subject: [PATCH 5/7] fix: kept separate loading functions but unified store --- wnfs/examples/private.rs | 2 +- wnfs/examples/tiered_blockstores.rs | 5 +- wnfs/src/private/directory.rs | 43 +++-------- wnfs/src/private/file.rs | 31 +------- wnfs/src/private/forest.rs | 4 +- wnfs/src/private/node/header.rs | 112 +++++++++++++++++++++------- wnfs/src/private/node/node.rs | 6 +- wnfs/src/private/previous.rs | 28 +++---- wnfs/src/private/share.rs | 2 +- 9 files changed, 117 insertions(+), 116 deletions(-) diff --git a/wnfs/examples/private.rs b/wnfs/examples/private.rs index 01fca392..048cb654 100644 --- a/wnfs/examples/private.rs +++ b/wnfs/examples/private.rs @@ -62,7 +62,7 @@ async fn create_forest_and_add_directory( .unwrap(); // Private ref contains data and keys for fetching and decrypting the directory node in the private forest. - let private_ref = dir.store_temporal(forest, store, rng).await.unwrap(); + let private_ref = dir.store(forest, store, rng).await.unwrap(); // Persist encoded private forest to the block store. let forest_cid = store.put_async_serializable(forest).await.unwrap(); diff --git a/wnfs/examples/tiered_blockstores.rs b/wnfs/examples/tiered_blockstores.rs index 875fb49d..6c2f37ec 100644 --- a/wnfs/examples/tiered_blockstores.rs +++ b/wnfs/examples/tiered_blockstores.rs @@ -54,10 +54,7 @@ async fn main() { .unwrap(); // When storing the hierarchy data blocks, we use the `hot_store`: - let private_ref = directory - .store_temporal(forest, &mut hot_store, rng) - .await - .unwrap(); + let private_ref = directory.store(forest, &mut hot_store, rng).await.unwrap(); // Same thing for the forest. Doing this will give us a single root CID // for all of the data, but parts separated into `hot_store` and `cold_store`: diff --git a/wnfs/src/private/directory.rs b/wnfs/src/private/directory.rs index 030c9578..dd62beb4 100644 --- a/wnfs/src/private/directory.rs +++ b/wnfs/src/private/directory.rs @@ -139,7 +139,7 @@ impl PrivateDirectory { rng: &mut impl RngCore, ) -> Result> { let dir = Rc::new(Self::new(parent_bare_name, time, rng)); - dir.store_temporal(forest, store, rng).await?; + dir.store(forest, store, rng).await?; Ok(dir) } @@ -160,7 +160,7 @@ impl PrivateDirectory { ratchet_seed, inumber, )); - dir.store_temporal(forest, store, rng).await?; + dir.store(forest, store, rng).await?; Ok(dir) } @@ -832,7 +832,7 @@ impl PrivateDirectory { /// .await /// .unwrap(); /// - /// dir_clone.store_temporal(forest, store, rng).await.unwrap(); + /// dir_clone.store(forest, store, rng).await.unwrap(); /// /// let latest_dir = init_dir.search_latest(forest, store).await.unwrap(); /// @@ -1423,7 +1423,7 @@ impl PrivateDirectory { /// rng, /// )); /// - /// let private_ref = dir.store_temporal(forest, store, rng).await.unwrap(); + /// let private_ref = dir.store(forest, store, rng).await.unwrap(); /// /// let node = PrivateNode::Dir(Rc::clone(&dir)); /// @@ -1433,38 +1433,13 @@ impl PrivateDirectory { /// ); /// } /// ``` - pub async fn store_temporal( + pub async fn store( &self, forest: &mut Rc, store: &impl BlockStore, rng: &mut impl RngCore, ) -> Result { - let header_cid = self.header.store_temporal(store).await?; - let temporal_key = self.header.derive_temporal_key(); - let label = self.header.get_saturated_name(); - - let content_cid = self - .content - .store(header_cid, &temporal_key, forest, store, rng) - .await?; - - forest - .put_encrypted(label, [header_cid, content_cid], store) - .await?; - - Ok(self - .header - .derive_revision_ref() - .as_private_ref(content_cid)) - } - - pub async fn store_snapshot( - &self, - forest: &mut Rc, - store: &impl BlockStore, - rng: &mut impl RngCore, - ) -> Result { - let header_cid = self.header.store_snapshot(store, rng).await?; + let header_cid = self.header.store(store).await?; let temporal_key = self.header.derive_temporal_key(); let label = self.header.get_saturated_name(); @@ -1989,7 +1964,7 @@ mod tests { .await .unwrap(); - root_dir.store_temporal(forest, store, rng).await.unwrap(); + root_dir.store(forest, store, rng).await.unwrap(); let old_root = &Rc::clone(root_dir); @@ -1998,7 +1973,7 @@ mod tests { .await .unwrap(); - root_dir.store_temporal(forest, store, rng).await.unwrap(); + root_dir.store(forest, store, rng).await.unwrap(); let new_read = root_dir.read(&path, false, forest, store).await.unwrap(); @@ -2397,7 +2372,7 @@ mod tests { Utc::now(), rng, )); - old_dir.store_temporal(forest, store, rng).await.unwrap(); + old_dir.store(forest, store, rng).await.unwrap(); let new_dir = &mut Rc::clone(old_dir); new_dir diff --git a/wnfs/src/private/file.rs b/wnfs/src/private/file.rs index d39b9faa..ed1b9091 100644 --- a/wnfs/src/private/file.rs +++ b/wnfs/src/private/file.rs @@ -755,7 +755,7 @@ impl PrivateFile { /// rng, /// )); /// - /// let private_ref = file.store_temporal(forest, store, rng).await.unwrap(); + /// let private_ref = file.store(forest, store, rng).await.unwrap(); /// /// let node = PrivateNode::File(Rc::clone(&file)); /// @@ -765,38 +765,13 @@ impl PrivateFile { /// ); /// } /// ``` - pub async fn store_temporal( + pub async fn store( &self, forest: &mut Rc, store: &impl BlockStore, rng: &mut impl RngCore, ) -> Result { - let header_cid = self.header.store_temporal(store).await?; - let snapshot_key = self.header.derive_temporal_key().derive_snapshot_key(); - let label = self.header.get_saturated_name(); - - let content_cid = self - .content - .store(header_cid, &snapshot_key, store, rng) - .await?; - - forest - .put_encrypted(label, [header_cid, content_cid], store) - .await?; - - Ok(self - .header - .derive_revision_ref() - .as_private_ref(content_cid)) - } - - pub async fn store_snapshot( - &self, - forest: &mut Rc, - store: &impl BlockStore, - rng: &mut impl RngCore, - ) -> Result { - let header_cid = self.header.store_snapshot(store, rng).await?; + let header_cid = self.header.store(store).await?; let snapshot_key = self.header.derive_temporal_key().derive_snapshot_key(); let label = self.header.get_saturated_name(); diff --git a/wnfs/src/private/forest.rs b/wnfs/src/private/forest.rs index 099e15de..420318c3 100644 --- a/wnfs/src/private/forest.rs +++ b/wnfs/src/private/forest.rs @@ -231,7 +231,7 @@ where /// ratchet_seed, /// inumber /// )); - /// root_dir.store_temporal(main_forest, store, rng).await.unwrap(); + /// root_dir.store(main_forest, store, rng).await.unwrap(); /// /// let other_forest = &mut Rc::new(PrivateForest::new()); /// let root_dir = Rc::new(PrivateDirectory::with_seed( @@ -240,7 +240,7 @@ where /// ratchet_seed, /// inumber /// )); - /// root_dir.store_temporal(other_forest, store, rng).await.unwrap(); + /// root_dir.store(other_forest, store, rng).await.unwrap(); /// /// let merge_forest = main_forest.merge(other_forest, store).await.unwrap(); /// diff --git a/wnfs/src/private/node/header.rs b/wnfs/src/private/node/header.rs index 62fdd5bd..96aaed4a 100644 --- a/wnfs/src/private/node/header.rs +++ b/wnfs/src/private/node/header.rs @@ -1,15 +1,15 @@ use super::{SnapshotKey, TemporalKey}; use crate::private::RevisionRef; use anyhow::Result; -use libipld::{Cid, IpldCodec}; +use libipld::{Cid, Ipld, IpldCodec}; use rand_core::RngCore; use serde::{Deserialize, Serialize}; use sha3::Sha3_256; use skip_ratchet::Ratchet; -use std::fmt::Debug; +use std::{collections::BTreeMap, fmt::Debug}; use wnfs_common::{utils, BlockStore, HashOutput, HASH_BYTE_SIZE}; use wnfs_hamt::Hasher; -use wnfs_namefilter::{BloomFilter, Namefilter}; +use wnfs_namefilter::Namefilter; //-------------------------------------------------------------------------------------------------- // Type Definitions @@ -214,25 +214,34 @@ impl PrivateNodeHeader { /// Encrypts this private node header in an block, then stores that in the given /// BlockStore and returns its CID. - pub async fn store_temporal(&self, store: &impl BlockStore) -> Result { + pub async fn store(&self, store: &impl BlockStore) -> Result { let temporal_key = self.derive_temporal_key(); - let cbor_bytes = serde_ipld_dagcbor::to_vec(self)?; - let ciphertext = temporal_key.key_wrap_encrypt(&cbor_bytes)?; - store.put_block(ciphertext, IpldCodec::Raw).await - } + let snapshot_key = TemporalKey(temporal_key.derive_snapshot_key().0); - pub async fn store_snapshot( - &self, - store: &impl BlockStore, - _rng: &mut impl RngCore, - ) -> Result { - let snapshot_key = self.derive_temporal_key().derive_snapshot_key(); - let tuple = (self.inumber, self.bare_name.clone()); - let cbor_bytes = serde_ipld_dagcbor::to_vec(&tuple)?; - let ciphertext = TemporalKey(snapshot_key.0).key_wrap_encrypt(&cbor_bytes)?; - store.put_block(ciphertext, IpldCodec::Raw).await + let inumber_bytes = + snapshot_key.key_wrap_encrypt(&serde_ipld_dagcbor::to_vec(&self.inumber)?)?; + let ratchet_bytes = + temporal_key.key_wrap_encrypt(&serde_ipld_dagcbor::to_vec(&self.ratchet)?)?; + let bare_name_bytes = + snapshot_key.key_wrap_encrypt(&serde_ipld_dagcbor::to_vec(&self.bare_name)?)?; + + let inumber_cid = store.put_block(inumber_bytes, IpldCodec::Raw).await?; + let ratchet_cid = store.put_block(ratchet_bytes, IpldCodec::Raw).await?; + let bare_name_cid = store.put_block(bare_name_bytes, IpldCodec::Raw).await?; + + let mut map = >::new(); + map.insert("inumber".to_string(), Ipld::Link(inumber_cid)); + map.insert("ratchet".to_string(), Ipld::Link(ratchet_cid)); + map.insert("bare_name".to_string(), Ipld::Link(bare_name_cid)); + + let ipld_bytes = serde_ipld_dagcbor::to_vec(&Ipld::Map(map))?; + store.put_block(ipld_bytes, IpldCodec::Raw).await } + // async fn load_bytes(cid: &Cid, store: &impl BlockStore) -> Result<(Vec)> { + + // } + /// Loads a private node header from a given CID linking to the ciphertext block /// to be decrypted with given key. pub(crate) async fn load_temporal( @@ -240,9 +249,38 @@ impl PrivateNodeHeader { temporal_key: &TemporalKey, store: &impl BlockStore, ) -> Result { - let ciphertext = store.get_block(cid).await?; - let cbor_bytes = temporal_key.key_wrap_decrypt(&ciphertext)?; - Ok(serde_ipld_dagcbor::from_slice(&cbor_bytes)?) + let snapshot_key = temporal_key.derive_snapshot_key(); + + let ipld_bytes = store.get_block(cid).await?; + let Ipld::Map(map) = serde_ipld_dagcbor::from_slice(&ipld_bytes)? else { + return Err(anyhow::anyhow!("Unable to deserialize ipld map")); + }; + + let Some(Ipld::Link(inumber_cid)) = map.get("inumber") else { + return Err(anyhow::anyhow!("Missing inumber_cid")); + }; + let Some(Ipld::Link(ratchet_cid)) = map.get("ratchet") else { + return Err(anyhow::anyhow!("Missing ratchet_cid")); + }; + let Some(Ipld::Link(bare_name_cid)) = map.get("bare_name") else { + return Err(anyhow::anyhow!("Missing bare_name_cid")); + }; + + let inumber_bytes = TemporalKey(snapshot_key.0.to_owned()) + .key_wrap_decrypt(&store.get_block(inumber_cid).await?)?; + let ratchet_bytes = temporal_key.key_wrap_decrypt(&store.get_block(ratchet_cid).await?)?; + let bare_name_bytes = TemporalKey(snapshot_key.0.to_owned()) + .key_wrap_decrypt(&store.get_block(bare_name_cid).await?)?; + + let inumber: [u8; HASH_BYTE_SIZE] = serde_ipld_dagcbor::from_slice(&inumber_bytes)?; + let ratchet: Ratchet = serde_ipld_dagcbor::from_slice(&ratchet_bytes)?; + let bare_name: Namefilter = serde_ipld_dagcbor::from_slice(&bare_name_bytes)?; + + Ok(Self { + inumber, + ratchet, + bare_name, + }) } pub(crate) async fn load_snapshot( @@ -250,15 +288,31 @@ impl PrivateNodeHeader { snapshot_key: &SnapshotKey, store: &impl BlockStore, ) -> Result { - let ciphertext = store.get_block(cid).await?; - let cbor_bytes = TemporalKey(snapshot_key.0.to_owned()).key_wrap_decrypt(&ciphertext)?; - let tuple: ([u8; 32], BloomFilter<256, 30>) = serde_ipld_dagcbor::from_slice(&cbor_bytes)?; - let header = PrivateNodeHeader { - inumber: tuple.0, - ratchet: Ratchet::default(), - bare_name: tuple.1, + let ipld_bytes = store.get_block(cid).await?; + let Ipld::Map(map) = serde_ipld_dagcbor::from_slice(&ipld_bytes)? else { + return Err(anyhow::anyhow!("Unable to deserialize ipld map")); + }; + + let Some(Ipld::Link(inumber_cid)) = map.get("inumber") else { + return Err(anyhow::anyhow!("Missing inumber_cid")); }; - Ok(header) + let Some(Ipld::Link(bare_name_cid)) = map.get("bare_name") else { + return Err(anyhow::anyhow!("Missing bare_name_cid")); + }; + + let inumber_bytes = TemporalKey(snapshot_key.0.to_owned()) + .key_wrap_decrypt(&store.get_block(inumber_cid).await?)?; + let bare_name_bytes = TemporalKey(snapshot_key.0.to_owned()) + .key_wrap_decrypt(&store.get_block(bare_name_cid).await?)?; + + let inumber: [u8; HASH_BYTE_SIZE] = serde_ipld_dagcbor::from_slice(&inumber_bytes)?; + let bare_name: Namefilter = serde_ipld_dagcbor::from_slice(&bare_name_bytes)?; + + Ok(Self { + inumber, + ratchet: Ratchet::default(), + bare_name, + }) } } diff --git a/wnfs/src/private/node/node.rs b/wnfs/src/private/node/node.rs index ae4c27c8..20a5275d 100644 --- a/wnfs/src/private/node/node.rs +++ b/wnfs/src/private/node/node.rs @@ -351,7 +351,7 @@ impl PrivateNode { /// .await /// .unwrap(); /// - /// dir_clone.store_temporal(forest, store, rng).await.unwrap(); + /// dir_clone.store(forest, store, rng).await.unwrap(); /// /// let latest_node = PrivateNode::Dir(init_dir).search_latest(forest, store).await.unwrap(); /// @@ -553,8 +553,8 @@ impl PrivateNode { rng: &mut impl RngCore, ) -> Result { match self { - Self::File(file) => file.store_temporal(forest, store, rng).await, - Self::Dir(dir) => dir.store_temporal(forest, store, rng).await, + Self::File(file) => file.store(forest, store, rng).await, + Self::Dir(dir) => dir.store(forest, store, rng).await, } } diff --git a/wnfs/src/private/previous.rs b/wnfs/src/private/previous.rs index e5047a69..2b42fbf2 100644 --- a/wnfs/src/private/previous.rs +++ b/wnfs/src/private/previous.rs @@ -550,7 +550,7 @@ mod tests { let rng = &mut rng; let store = &mut store; - root_dir.store_temporal(forest, store, rng).await.unwrap(); + root_dir.store(forest, store, rng).await.unwrap(); let past_dir = root_dir.clone(); @@ -567,14 +567,14 @@ mod tests { .await .unwrap(); - root_dir.store_temporal(forest, store, rng).await.unwrap(); + root_dir.store(forest, store, rng).await.unwrap(); root_dir .mkdir(&["docs".into()], true, Utc::now(), forest, store, rng) .await .unwrap(); - root_dir.store_temporal(forest, store, rng).await.unwrap(); + root_dir.store(forest, store, rng).await.unwrap(); let mut iterator = PrivateNodeOnPathHistory::of( root_dir, @@ -635,7 +635,7 @@ mod tests { let rng = &mut rng; let store = &mut store; - root_dir.store_temporal(forest, store, rng).await.unwrap(); + root_dir.store(forest, store, rng).await.unwrap(); let past_dir = root_dir.clone(); @@ -646,7 +646,7 @@ mod tests { .await .unwrap(); - root_dir.store_temporal(forest, store, rng).await.unwrap(); + root_dir.store(forest, store, rng).await.unwrap(); root_dir .write( @@ -661,7 +661,7 @@ mod tests { .await .unwrap(); - root_dir.store_temporal(forest, store, rng).await.unwrap(); + root_dir.store(forest, store, rng).await.unwrap(); let mut iterator = PrivateNodeOnPathHistory::of( root_dir, @@ -735,7 +735,7 @@ mod tests { let rng = &mut rng; let store = &mut store; - root_dir.store_temporal(forest, store, rng).await.unwrap(); + root_dir.store(forest, store, rng).await.unwrap(); let past_dir = root_dir.clone(); @@ -746,7 +746,7 @@ mod tests { .await .unwrap(); - root_dir.store_temporal(forest, store, rng).await.unwrap(); + root_dir.store(forest, store, rng).await.unwrap(); let docs_dir = root_dir .get_node(&["Docs".into()], true, forest, store) @@ -768,7 +768,7 @@ mod tests { .await .unwrap(); - docs_dir.store_temporal(forest, store, rng).await.unwrap(); + docs_dir.store(forest, store, rng).await.unwrap(); let mut iterator = PrivateNodeOnPathHistory::of( root_dir, @@ -857,7 +857,7 @@ mod tests { .await .unwrap(); - root_dir.store_temporal(forest, store, rng).await.unwrap(); + root_dir.store(forest, store, rng).await.unwrap(); let past_dir = root_dir.clone(); @@ -881,7 +881,7 @@ mod tests { .await .unwrap(); - docs_dir.store_temporal(forest, store, rng).await.unwrap(); + docs_dir.store(forest, store, rng).await.unwrap(); root_dir .write( @@ -896,7 +896,7 @@ mod tests { .await .unwrap(); - root_dir.store_temporal(forest, store, rng).await.unwrap(); + root_dir.store(forest, store, rng).await.unwrap(); let mut iterator = PrivateNodeOnPathHistory::of( root_dir, @@ -999,13 +999,13 @@ mod tests { .await .unwrap(); - root_dir.store_temporal(forest, store, rng).await.unwrap(); + root_dir.store(forest, store, rng).await.unwrap(); let past_dir = root_dir.clone(); let mut root_dir = Rc::new(root_dir.prepare_next_revision().unwrap().clone()); - root_dir.store_temporal(forest, store, rng).await.unwrap(); + root_dir.store(forest, store, rng).await.unwrap(); root_dir .write( diff --git a/wnfs/src/private/share.rs b/wnfs/src/private/share.rs index 9733389c..b2c57d1a 100644 --- a/wnfs/src/private/share.rs +++ b/wnfs/src/private/share.rs @@ -556,7 +556,7 @@ mod tests { .as_file() .unwrap(); sharer_file - .store_snapshot(sharer_forest, sharer_store, rng) + .store(sharer_forest, sharer_store, rng) .await .unwrap(); From 9c8eea8901ec4f6714bfa2ffb6a3463917c19d0f Mon Sep 17 00:00:00 2001 From: Vera Gonzalez Date: Tue, 14 Nov 2023 13:36:45 -0500 Subject: [PATCH 6/7] feat: working on Directory reconstruction --- wnfs/src/private/directory.rs | 41 ++++++++++++++++++++++++++++++++--- wnfs/src/private/node/node.rs | 7 ++++-- wnfs/src/private/share.rs | 35 +++++++++++++++++++----------- 3 files changed, 65 insertions(+), 18 deletions(-) diff --git a/wnfs/src/private/directory.rs b/wnfs/src/private/directory.rs index dd62beb4..83555aba 100644 --- a/wnfs/src/private/directory.rs +++ b/wnfs/src/private/directory.rs @@ -1,7 +1,7 @@ use super::{ - encrypted::Encrypted, link::PrivateLink, PrivateDirectoryContentSerializable, PrivateFile, - PrivateForest, PrivateNode, PrivateNodeContentSerializable, PrivateNodeHeader, PrivateRef, - TemporalKey, + encrypted::Encrypted, link::PrivateLink, AesKey, PrivateDirectoryContentSerializable, + PrivateFile, PrivateForest, PrivateNode, PrivateNodeContentSerializable, PrivateNodeHeader, + PrivateRef, SnapshotKey, TemporalKey, KEY_BYTE_SIZE, }; use crate::{error::FsError, traits::Id, SearchResult, WNFS_VERSION}; use anyhow::{bail, ensure, Result}; @@ -1488,6 +1488,41 @@ impl PrivateDirectory { Ok(Self { header, content }) } + /// Creates a new [`PrivateDirectory`] from a [`PrivateDirectoryContentSerializable`]. + pub(crate) async fn from_serializable_snapshot( + serializable: PrivateDirectoryContentSerializable, + snapshot_key: &SnapshotKey, + cid: Cid, + store: &impl BlockStore, + ) -> Result { + if serializable.version.major != 0 || serializable.version.minor != 2 { + bail!(FsError::UnexpectedVersion(serializable.version)); + } + + let mut entries_decrypted = BTreeMap::new(); + // let temporal_key = TemporalKey(snapshot_key.0.to_owned()); + for (name, private_ref_serializable) in serializable.entries { + let private_ref = PrivateRef { + saturated_name_hash: private_ref_serializable.saturated_name_hash, + // What are we supposed to do here in the absence of a parent key? This node is not decryptable + temporal_key: TemporalKey(AesKey::new([0u8; KEY_BYTE_SIZE])), + content_cid: private_ref_serializable.content_cid, + }; + entries_decrypted.insert(name, PrivateLink::from_ref(private_ref)); + } + + let content = PrivateDirectoryContent { + persisted_as: OnceCell::new_with(Some(cid)), + metadata: serializable.metadata, + previous: serializable.previous.into_iter().collect(), + entries: entries_decrypted, + }; + + let header = + PrivateNodeHeader::load_snapshot(&serializable.header_cid, snapshot_key, store).await?; + Ok(Self { header, content }) + } + /// Wraps the directory in a [`PrivateNode`]. pub fn as_node(self: &Rc) -> PrivateNode { PrivateNode::Dir(Rc::clone(self)) diff --git a/wnfs/src/private/node/node.rs b/wnfs/src/private/node/node.rs index 20a5275d..906b6670 100644 --- a/wnfs/src/private/node/node.rs +++ b/wnfs/src/private/node/node.rs @@ -513,8 +513,11 @@ impl PrivateNode { PrivateFile::from_serializable_snapshot(file, snapshot_key, cid, store).await?; PrivateNode::File(Rc::new(file)) } - PrivateNodeContentSerializable::Dir(_) => { - bail!("cannot deserialize dir from snapshot yet"); + PrivateNodeContentSerializable::Dir(dir) => { + let dir = + PrivateDirectory::from_serializable_snapshot(dir, snapshot_key, cid, store) + .await?; + PrivateNode::Dir(Rc::new(dir)) } }; diff --git a/wnfs/src/private/share.rs b/wnfs/src/private/share.rs index b2c57d1a..38405f01 100644 --- a/wnfs/src/private/share.rs +++ b/wnfs/src/private/share.rs @@ -548,17 +548,17 @@ mod tests { .await .unwrap(); - let sharer_file = sharer_dir - .get_node(&["text.txt".into()], true, sharer_forest, sharer_store) - .await - .unwrap() - .unwrap() - .as_file() - .unwrap(); - sharer_file - .store(sharer_forest, sharer_store, rng) - .await - .unwrap(); + // let sharer_file = sharer_dir + // .get_node(&["text.txt".into()], true, sharer_forest, sharer_store) + // .await + // .unwrap() + // .unwrap() + // .as_file() + // .unwrap(); + // sharer_file + // .store(sharer_forest, sharer_store, rng) + // .await + // .unwrap(); // Establish recipient exchange root. let (recipient_key, recipient_exchange_root) = @@ -568,7 +568,7 @@ mod tests { // Construct share payload from sharer's directory. let sharer_payload = SharePayload::from_node( - &sharer_file.as_node(), + &sharer_dir.as_node(), false, sharer_forest, sharer_store, @@ -608,8 +608,17 @@ mod tests { .await .unwrap(); + let dir = node.as_dir().unwrap(); + // Assert payload is the same as the original. - let file = node.as_file().unwrap(); + let file = dir + .get_node(&["text.txt".into()], true, sharer_forest, sharer_store) + .await + .unwrap() + .unwrap() + .as_file() + .unwrap(); + let content = file.get_content(sharer_forest, sharer_store).await.unwrap(); let content_string = String::from_utf8(content).unwrap(); assert_eq!(content_string, "Hello World!".to_string()); From 829629a25d9a7b4f255b4a0f00618e0e16ccbe2d Mon Sep 17 00:00:00 2001 From: Vera Gonzalez Date: Thu, 16 Nov 2023 13:39:30 -0500 Subject: [PATCH 7/7] fix: re-disabled Dir snapshot decryption --- wnfs/src/private/directory.rs | 1 + wnfs/src/private/node/node.rs | 9 +++------ wnfs/src/private/share.rs | 35 +++++++++++++---------------------- 3 files changed, 17 insertions(+), 28 deletions(-) diff --git a/wnfs/src/private/directory.rs b/wnfs/src/private/directory.rs index 83555aba..58bda558 100644 --- a/wnfs/src/private/directory.rs +++ b/wnfs/src/private/directory.rs @@ -1488,6 +1488,7 @@ impl PrivateDirectory { Ok(Self { header, content }) } + #[allow(dead_code)] /// Creates a new [`PrivateDirectory`] from a [`PrivateDirectoryContentSerializable`]. pub(crate) async fn from_serializable_snapshot( serializable: PrivateDirectoryContentSerializable, diff --git a/wnfs/src/private/node/node.rs b/wnfs/src/private/node/node.rs index 906b6670..781af341 100644 --- a/wnfs/src/private/node/node.rs +++ b/wnfs/src/private/node/node.rs @@ -7,7 +7,7 @@ use crate::{ }, traits::Id, }; -use anyhow::{bail, Result}; +use anyhow::{anyhow, bail, Result}; use async_once_cell::OnceCell; use async_recursion::async_recursion; use chrono::{DateTime, Utc}; @@ -513,11 +513,8 @@ impl PrivateNode { PrivateFile::from_serializable_snapshot(file, snapshot_key, cid, store).await?; PrivateNode::File(Rc::new(file)) } - PrivateNodeContentSerializable::Dir(dir) => { - let dir = - PrivateDirectory::from_serializable_snapshot(dir, snapshot_key, cid, store) - .await?; - PrivateNode::Dir(Rc::new(dir)) + PrivateNodeContentSerializable::Dir(_) => { + return Err(anyhow!("Not yet able to deserialize Dir from snapshot")); } }; diff --git a/wnfs/src/private/share.rs b/wnfs/src/private/share.rs index 38405f01..d9b6646a 100644 --- a/wnfs/src/private/share.rs +++ b/wnfs/src/private/share.rs @@ -548,17 +548,17 @@ mod tests { .await .unwrap(); - // let sharer_file = sharer_dir - // .get_node(&["text.txt".into()], true, sharer_forest, sharer_store) - // .await - // .unwrap() - // .unwrap() - // .as_file() - // .unwrap(); - // sharer_file - // .store(sharer_forest, sharer_store, rng) - // .await - // .unwrap(); + let sharer_file = sharer_dir + .get_node(&["text.txt".into()], true, sharer_forest, sharer_store) + .await + .unwrap() + .unwrap() + .as_file() + .unwrap(); + sharer_file + .store(sharer_forest, sharer_store, rng) + .await + .unwrap(); // Establish recipient exchange root. let (recipient_key, recipient_exchange_root) = @@ -568,7 +568,7 @@ mod tests { // Construct share payload from sharer's directory. let sharer_payload = SharePayload::from_node( - &sharer_dir.as_node(), + &sharer_file.as_node(), false, sharer_forest, sharer_store, @@ -608,16 +608,7 @@ mod tests { .await .unwrap(); - let dir = node.as_dir().unwrap(); - - // Assert payload is the same as the original. - let file = dir - .get_node(&["text.txt".into()], true, sharer_forest, sharer_store) - .await - .unwrap() - .unwrap() - .as_file() - .unwrap(); + let file = node.as_file().unwrap(); let content = file.get_content(sharer_forest, sharer_store).await.unwrap(); let content_string = String::from_utf8(content).unwrap();