Skip to content

Commit

Permalink
feat: Implement storing externally encrypted content in Metadata (#340
Browse files Browse the repository at this point in the history
)

* feat: Implement storing externally encrypted content in `Metadata`

* Add `file_variants` to example README.md

* Remove debug println

* feat: Also implement `get_metadata_mut`(-`_rc`) and `open_file_mut` on the public side

* refactor: Implement and use `as_metadata_value`
  • Loading branch information
matheus23 authored Sep 4, 2023
1 parent f39a800 commit 2d15fbd
Show file tree
Hide file tree
Showing 10 changed files with 510 additions and 236 deletions.
22 changes: 21 additions & 1 deletion wnfs-common/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
use anyhow::{bail, Result};
use chrono::{DateTime, TimeZone, Utc};
use libipld::Ipld;
use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer};
use serde::{
de::{DeserializeOwned, Error as DeError},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::{collections::BTreeMap, convert::TryInto};

//--------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -164,6 +167,23 @@ impl Metadata {
self.0.insert(key.into(), value)
}

/// Returns metadata value behind given key.
pub fn get(&self, key: &str) -> Option<&Ipld> {
self.0.get(key)
}

/// Serializes and inserts given value at given key in metadata.
pub fn put_serializable(&mut self, key: &str, value: impl Serialize) -> Result<Option<Ipld>> {
let serialized = libipld::serde::to_ipld(value)?;
Ok(self.put(key, serialized))
}

/// Returns deserialized metadata value behind given key.
pub fn get_deserializable<D: DeserializeOwned>(&self, key: &str) -> Option<Result<D>> {
self.get(key)
.map(|ipld| Ok(libipld::serde::from_ipld(ipld.clone())?))
}

/// Deletes a key from the metadata.
///
/// # Examples
Expand Down
4 changes: 4 additions & 0 deletions wnfs/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@
```bash
cargo run --example mnemonic_based --release
```

```bash
cargo run --example file_variants
```
65 changes: 65 additions & 0 deletions wnfs/examples/file_variants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//! This example shows how to store multiple byte arrays per file, by storing
//! additional data in a file's metadata, which links out to externally encrypted data.
use anyhow::Result;
use chrono::Utc;
use rand::thread_rng;
use std::rc::Rc;
use wnfs::private::{
forest::{hamt::HamtForest, traits::PrivateForest},
PrivateFile, PrivateForestContent,
};
use wnfs_common::MemoryBlockStore;

#[async_std::main]
async fn main() -> Result<()> {
// The usual in-memory testing setup for WNFS
let store = &MemoryBlockStore::default();
let rng = &mut thread_rng();
let forest = &mut HamtForest::new_rsa_2048(rng);

// Create a new file (detached from any directory)
let mut file = Rc::new(
PrivateFile::with_content(
&forest.empty_name(),
Utc::now(),
b"main content".to_vec(),
forest,
store,
rng,
)
.await?,
);

// Create some content that's stored encrypted in the private forest.
// The PrivateForestContent struct holds the keys and pointers to look it back up.
// We use the file's name as the "path" for this content. This means anyone
// who had write access to the file will have write access to the external content.
let content = PrivateForestContent::new(
file.header.get_name(),
b"secondary content".to_vec(),
forest,
store,
rng,
)
.await?;

// We store the content in the file metadata.
// This will update the `file: Rc<PrivateFile>` for us with a new reference.
file.get_metadata_mut_rc()?
.put("thumbnail", content.as_metadata_value()?);

// We store the new reference in the forest.
file.as_node().store(forest, store, rng).await?;

// When can look up the private forest content again.
let content_ipld = file.get_metadata().get("thumbnail").unwrap();
let content = PrivateForestContent::from_metadata_value(content_ipld)?;

assert_eq!(
content.get_content(forest, store).await?,
b"secondary content".to_vec()
);

Ok(())
}
2 changes: 1 addition & 1 deletion wnfs/examples/private.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! This example shows how to add a directory to a private forest (a HAMT) where encrypted ciphertexts are stored.
//! It also shows how to retrieve encrypted nodes from the forest using `PrivateRef`s.
//! It also shows how to retrieve encrypted nodes from the forest using `AccessKey`s.
use anyhow::Result;
use chrono::Utc;
Expand Down
2 changes: 1 addition & 1 deletion wnfs/examples/write_proofs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ async fn alice_actions(store: &impl BlockStore) -> Result<(Cid, AccessKey, NameA

let access_key = root_dir.as_node().store(forest, store, rng).await?;
let cid = forest.store(store).await?;
let allowed_name = forest.get_accumulated_name(&root_dir.header.get_name());
let allowed_name = forest.get_accumulated_name(root_dir.header.get_name());

Ok((cid, access_key, allowed_name))
}
Expand Down
Loading

0 comments on commit 2d15fbd

Please sign in to comment.