Skip to content

Commit

Permalink
Rename RatchetKey to RevisionKey, encrypt RevisionKey within `P…
Browse files Browse the repository at this point in the history
…rivateRef` (#83)

* Encrypt the ratchet key

* Rename `RatchetKey` to `RevisionKey`

* Fix wasm crate

* Rename `*Serde` into `*Serializable`

* Rename `_serde` variables into `_serializable`
  • Loading branch information
matheus23 authored Nov 4, 2022
1 parent ce7d988 commit 80dbe82
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 55 deletions.
6 changes: 3 additions & 3 deletions wnfs-wasm/src/fs/private/forest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use js_sys::{Error, Promise};
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
use wasm_bindgen_futures::future_to_promise;
use wnfs::{
private::{Key, PrivateForest as WnfsPrivateForest, PrivateRef, RatchetKey, KEY_BYTE_SIZE},
private::{Key, PrivateForest as WnfsPrivateForest, PrivateRef, RevisionKey, KEY_BYTE_SIZE},
HASH_BYTE_SIZE,
};

Expand Down Expand Up @@ -50,9 +50,9 @@ impl PrivateForest {

let key_bytes = expect_bytes::<KEY_BYTE_SIZE>(revision_key)?;
let key = Key::new(key_bytes);
let ratchet_key = RatchetKey(key);
let revision_key = RevisionKey(key);

let private_ref = PrivateRef::from_ratchet_key(saturated_name_hash, ratchet_key);
let private_ref = PrivateRef::from_revision_key(saturated_name_hash, revision_key);

Ok(future_to_promise(async move {
let node_option = forest
Expand Down
37 changes: 27 additions & 10 deletions wnfs/src/private/directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use serde::{de::Error as DeError, ser::Error as SerError, Deserialize, Deseriali

use super::{
namefilter::Namefilter, Key, PrivateFile, PrivateForest, PrivateNode, PrivateNodeHeader,
PrivateRef, RatchetKey,
PrivateRef, PrivateRefSerializable, RevisionKey,
};

use crate::{
Expand Down Expand Up @@ -49,12 +49,12 @@ pub struct PrivateDirectory {
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct PrivateDirectorySerde {
struct PrivateDirectorySerializable {
pub r#type: NodeType,
pub version: Version,
pub header: Vec<u8>,
pub metadata: Metadata,
pub entries: BTreeMap<String, PrivateRef>,
pub entries: BTreeMap<String, PrivateRefSerializable>,
}

/// The result of an operation applied to a directory.
Expand Down Expand Up @@ -1150,9 +1150,18 @@ impl PrivateDirectory {
.header
.get_private_ref()
.map_err(SerError::custom)?
.ratchet_key;
.revision_key;

let mut entries = BTreeMap::new();

(PrivateDirectorySerde {
for (name, private_ref) in self.entries.iter() {
let private_ref_serializable = private_ref
.to_serializable(&key, rng)
.map_err(SerError::custom)?;
entries.insert(name.clone(), private_ref_serializable);
}

(PrivateDirectorySerializable {
r#type: NodeType::PrivateDirectory,
version: self.version.clone(),
header: {
Expand All @@ -1162,23 +1171,31 @@ impl PrivateDirectory {
.map_err(SerError::custom)?
},
metadata: self.metadata.clone(),
entries: self.entries.clone(),
entries,
})
.serialize(serializer)
}

/// Deserializes the directory with provided Serde deserializer and key.
pub(crate) fn deserialize<'de, D>(deserializer: D, key: &RatchetKey) -> Result<Self, D::Error>
pub(crate) fn deserialize<'de, D>(deserializer: D, key: &RevisionKey) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let PrivateDirectorySerde {
let PrivateDirectorySerializable {
version,
metadata,
header,
entries,
entries: entries_encrypted,
..
} = PrivateDirectorySerde::deserialize(deserializer)?;
} = PrivateDirectorySerializable::deserialize(deserializer)?;

let mut entries = BTreeMap::new();

for (name, private_ref_serializable) in entries_encrypted {
let private_ref = PrivateRef::from_serializable(private_ref_serializable, key)
.map_err(DeError::custom)?;
entries.insert(name, private_ref);
}

Ok(Self {
version,
Expand Down
14 changes: 7 additions & 7 deletions wnfs/src/private/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use serde::{de::Error as DeError, ser::Error as SerError, Deserialize, Deseriali

use crate::{dagcbor, Id, Metadata, NodeType};

use super::{namefilter::Namefilter, Key, PrivateNodeHeader, RatchetKey};
use super::{namefilter::Namefilter, Key, PrivateNodeHeader, RevisionKey};

//--------------------------------------------------------------------------------------------------
// Type Definitions
Expand Down Expand Up @@ -39,7 +39,7 @@ pub struct PrivateFile {
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct PrivateFileSerde {
struct PrivateFileSerializable {
pub r#type: NodeType,
pub version: Version,
pub header: Vec<u8>,
Expand Down Expand Up @@ -98,9 +98,9 @@ impl PrivateFile {
.header
.get_private_ref()
.map_err(SerError::custom)?
.ratchet_key;
.revision_key;

(PrivateFileSerde {
(PrivateFileSerializable {
r#type: NodeType::PrivateFile,
version: self.version.clone(),
header: {
Expand All @@ -116,17 +116,17 @@ impl PrivateFile {
}

/// Deserializes the file with provided Serde deserializer and key.
pub(crate) fn deserialize<'de, D>(deserializer: D, key: &RatchetKey) -> Result<Self, D::Error>
pub(crate) fn deserialize<'de, D>(deserializer: D, key: &RevisionKey) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let PrivateFileSerde {
let PrivateFileSerializable {
version,
metadata,
header,
content,
..
} = PrivateFileSerde::deserialize(deserializer)?;
} = PrivateFileSerializable::deserialize(deserializer)?;

Ok(Self {
version,
Expand Down
2 changes: 1 addition & 1 deletion wnfs/src/private/forest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ impl PrivateForest {
// Deserialize bytes.
Ok(Some(PrivateNode::deserialize_from_cbor(
&cbor_bytes,
&private_ref.ratchet_key,
&private_ref.revision_key,
)?))
}

Expand Down
2 changes: 1 addition & 1 deletion wnfs/src/private/hamt/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ impl<K, V, H: Hasher> Node<K, V, H> {
Ok(len)
}

// TODO(appcypher): Do we really need this? Why not use PublicDirectorySerde style instead.
// TODO(appcypher): Do we really need this? Why not use PublicDirectorySerializable style instead.
/// Converts a Node to an IPLD object.
pub async fn to_ipld<B: BlockStore + ?Sized>(&self, store: &mut B) -> Result<Ipld>
where
Expand Down
124 changes: 99 additions & 25 deletions wnfs/src/private/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use libipld::{
serde as ipld_serde, Ipld,
};
use rand_core::RngCore;
use serde::{Deserialize, Serialize};
use serde::{de::Error as DeError, ser::Error as SerError, Deserialize, Serialize};
use sha3::Sha3_256;
use skip_ratchet::{seek::JumpSize, Ratchet, RatchetSeeker};

Expand Down Expand Up @@ -58,7 +58,7 @@ pub struct ContentKey(pub Key);

/// The key used to encrypt the header section of a node.
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct RatchetKey(pub Key);
pub struct RevisionKey(pub Key);

/// This is the header of a private node. It contains secret information about the node which includes
/// the inumber, the ratchet, and the namefilter.
Expand Down Expand Up @@ -91,14 +91,24 @@ pub struct PrivateNodeHeader {
}

/// PrivateRef holds the information to fetch associated node from a HAMT and decrypt it if it is present.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PrivateRef {
/// Sha3-256 hash of saturated namefilter.
pub(crate) saturated_name_hash: HashOutput,
/// Sha3-256 hash of the ratchet key.
pub(crate) content_key: ContentKey,
/// Skip-ratchet-derived key.
pub(crate) ratchet_key: RatchetKey,
pub(crate) revision_key: RevisionKey,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct PrivateRefSerializable {
#[serde(rename = "name")]
pub(crate) saturated_name_hash: HashOutput,
#[serde(rename = "contentKey")]
pub(crate) content_key: ContentKey,
#[serde(rename = "revisionKey")]
pub(crate) revision_key: Vec<u8>,
}

//--------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -408,7 +418,7 @@ impl PrivateNode {
}
}

/// Serializes the node with provided Serde serialilzer.
/// Serializes the node with provided Serde serializer.
pub(crate) fn serialize<S, R: RngCore>(
&self,
serializer: S,
Expand All @@ -432,16 +442,16 @@ impl PrivateNode {
}

/// Deserializes the node from dag-cbor bytes.
pub(crate) fn deserialize_from_cbor(bytes: &[u8], key: &RatchetKey) -> Result<Self> {
pub(crate) fn deserialize_from_cbor(bytes: &[u8], key: &RevisionKey) -> Result<Self> {
let ipld = Ipld::decode(DagCborCodec, &mut Cursor::new(bytes))?;
(ipld, key).try_into()
}
}

impl TryFrom<(Ipld, &RatchetKey)> for PrivateNode {
impl TryFrom<(Ipld, &RevisionKey)> for PrivateNode {
type Error = anyhow::Error;

fn try_from(pair: (Ipld, &RatchetKey)) -> Result<Self> {
fn try_from(pair: (Ipld, &RevisionKey)) -> Result<Self> {
match pair {
(Ipld::Map(map), key) => {
let r#type: NodeType = map
Expand Down Expand Up @@ -541,20 +551,20 @@ impl PrivateNodeHeader {
/// println!("Private ref: {:?}", private_ref);
/// ```
pub fn get_private_ref(&self) -> Result<PrivateRef> {
let ratchet_key = Key::new(self.ratchet.derive_key());
let saturated_name_hash = Sha3_256::hash(&self.get_saturated_name_with_key(&ratchet_key));
let revision_key = Key::new(self.ratchet.derive_key());
let saturated_name_hash = Sha3_256::hash(&self.get_saturated_name_with_key(&revision_key));

Ok(PrivateRef {
saturated_name_hash,
content_key: Key::new(Sha3_256::hash(&ratchet_key.as_bytes())).into(),
ratchet_key: ratchet_key.into(),
content_key: Key::new(Sha3_256::hash(&revision_key.as_bytes())).into(),
revision_key: revision_key.into(),
})
}

/// Gets the saturated namefilter for this node using the provided ratchet key.
pub(crate) fn get_saturated_name_with_key(&self, ratchet_key: &Key) -> Namefilter {
pub(crate) fn get_saturated_name_with_key(&self, revision_key: &Key) -> Namefilter {
let mut name = self.bare_name.clone();
name.add(&ratchet_key.as_bytes());
name.add(&revision_key.as_bytes());
name.saturate();
name
}
Expand Down Expand Up @@ -582,19 +592,19 @@ impl PrivateNodeHeader {
/// ```
#[inline]
pub fn get_saturated_name(&self) -> Namefilter {
let ratchet_key = Key::new(self.ratchet.derive_key());
self.get_saturated_name_with_key(&ratchet_key)
let revision_key = Key::new(self.ratchet.derive_key());
self.get_saturated_name_with_key(&revision_key)
}
}

impl From<Key> for RatchetKey {
impl From<Key> for RevisionKey {
fn from(key: Key) -> Self {
Self(key)
}
}

impl From<RatchetKey> for Key {
fn from(key: RatchetKey) -> Self {
impl From<RevisionKey> for Key {
fn from(key: RevisionKey) -> Self {
key.0
}
}
Expand All @@ -612,18 +622,82 @@ impl From<ContentKey> for Key {
}

impl PrivateRef {
pub fn from_ratchet_key(saturated_name_hash: HashOutput, ratchet_key: RatchetKey) -> Self {
pub fn from_revision_key(saturated_name_hash: HashOutput, revision_key: RevisionKey) -> Self {
Self {
saturated_name_hash,
content_key: ratchet_key.derive_content_key(),
ratchet_key,
content_key: revision_key.derive_content_key(),
revision_key,
}
}

pub(crate) fn to_serializable(
&self,
revision_key: &RevisionKey,
rng: &mut impl RngCore,
) -> Result<PrivateRefSerializable> {
// encrypt ratchet key
let revision_key = revision_key
.0
.encrypt(&Key::generate_nonce(rng), self.revision_key.0.as_bytes())?;
Ok(PrivateRefSerializable {
saturated_name_hash: self.saturated_name_hash,
content_key: self.content_key.clone(),
revision_key,
})
}

pub(crate) fn from_serializable(
private_ref: PrivateRefSerializable,
revision_key: &RevisionKey,
) -> Result<Self> {
let revision_key = RevisionKey(Key::new(
revision_key
.0
.decrypt(&private_ref.revision_key)?
.try_into()
.map_err(|e: Vec<u8>| {
FsError::InvalidDeserialization(format!(
"Expected 32 bytes for ratchet key, but got {}",
e.len()
))
})?,
));
Ok(Self {
saturated_name_hash: private_ref.saturated_name_hash,
content_key: private_ref.content_key,
revision_key,
})
}

pub fn serialize<S>(
&self,
serializer: S,
revision_key: &RevisionKey,
rng: &mut impl RngCore,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.to_serializable(revision_key, rng)
.map_err(SerError::custom)?
.serialize(serializer)
}

pub fn deserialize<'de, D>(
deserializer: D,
revision_key: &RevisionKey,
) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let private_ref = PrivateRefSerializable::deserialize(deserializer)?;
PrivateRef::from_serializable(private_ref, revision_key).map_err(DeError::custom)
}
}

impl RatchetKey {
impl RevisionKey {
pub fn derive_content_key(&self) -> ContentKey {
let RatchetKey(key) = self;
let RevisionKey(key) = self;
ContentKey(Key::new(Sha3_256::hash(&key.as_bytes())))
}
}
Expand Down Expand Up @@ -651,7 +725,7 @@ mod private_node_tests {

let bytes = original_file.serialize_to_cbor(rng).unwrap();
let deserialized_node =
PrivateNode::deserialize_from_cbor(&bytes, &private_ref.ratchet_key).unwrap();
PrivateNode::deserialize_from_cbor(&bytes, &private_ref.revision_key).unwrap();

assert_eq!(original_file, deserialized_node);
}
Expand Down
Loading

0 comments on commit 80dbe82

Please sign in to comment.