Skip to content

Commit

Permalink
feat: Switch from AES-GCM to XChaCha20-Poly1305 (#305)
Browse files Browse the repository at this point in the history
* Switch to XChaCha20-Poly1305

* Reduce code duplication & remove `AesKey` struct

* Fix wnfs-wasm
  • Loading branch information
matheus23 authored Jul 11, 2023
1 parent 2a9afd2 commit c17f6bb
Show file tree
Hide file tree
Showing 11 changed files with 80 additions and 180 deletions.
2 changes: 1 addition & 1 deletion wnfs-wasm/src/fs/private/access_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ impl AccessKey {

#[wasm_bindgen(js_name = "getTemporalKey")]
pub fn get_temporal_key(&self) -> Vec<u8> {
self.0.get_temporal_key().unwrap().0.as_bytes().to_vec()
self.0.get_temporal_key().unwrap().0.to_vec()
}

#[wasm_bindgen(js_name = "getContentCid")]
Expand Down
2 changes: 1 addition & 1 deletion wnfs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ homepage = "https://fission.codes"
authors = ["The Fission Authors"]

[dependencies]
aes-gcm = "0.10"
aes-kw = { version = "0.2", features = ["alloc"] }
anyhow = "1.0"
async-once-cell = "0.4"
async-recursion = "1.0"
async-stream = "0.3"
async-trait = "0.1"
bytes = "1.4.0"
chacha20poly1305 = "0.10"
chrono = { version = "0.4", default-features = false, features = ["clock", "std"] }
futures = "0.3"
libipld-core = { version = "0.16" }
Expand Down
8 changes: 4 additions & 4 deletions wnfs/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,14 @@ pub enum ShareError {
AccessKeyNotFound,
}

/// AES-GCM errors.
/// Symmetric encryption errors.
#[derive(Debug, Error)]
pub enum AesError {
pub enum CryptError {
#[error("Unable to encrypt data: {0}")]
UnableToEncrypt(String),
UnableToEncrypt(anyhow::Error),

#[error("Unable to decrypt data: {0}")]
UnableToDecrypt(String),
UnableToDecrypt(anyhow::Error),
}

/// RSA related errors
Expand Down
2 changes: 1 addition & 1 deletion wnfs/src/private/encrypted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize};
/// Any data wrapped like this **must not have low entropy**.
///
/// For anything that could potentially have low entropy,
/// please use AES-GCM instead via `SnapshotKey`.
/// please use XChaCha20-Poly1305 instead via `SnapshotKey`.
///
/// When serialized or deserialized this will only
/// ever emit or consume ciphertexts.
Expand Down
9 changes: 4 additions & 5 deletions wnfs/src/private/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ impl PrivateFile {
loop {
let mut current_block = vec![0u8; MAX_BLOCK_SIZE];
let nonce = SnapshotKey::generate_nonce(rng);
current_block[..NONCE_SIZE].copy_from_slice(&nonce);
current_block[..NONCE_SIZE].copy_from_slice(nonce.as_ref());

// read up to MAX_BLOCK_CONTENT_SIZE content

Expand All @@ -506,7 +506,7 @@ impl PrivateFile {
current_block.truncate(bytes_written + NONCE_SIZE);

let tag = key.encrypt_in_place(&nonce, &mut current_block[NONCE_SIZE..])?;
current_block.extend_from_slice(&tag);
current_block.extend_from_slice(tag.as_ref());

let content_cid = store.put_block(current_block, CODEC_RAW).await?;

Expand Down Expand Up @@ -582,15 +582,14 @@ impl PrivateFile {
}

fn create_revision_name(file_block_name: &Name, key: &SnapshotKey) -> Name {
let revision_segment =
NameSegment::from_digest(Sha3_256::new().chain_update(key.0.as_bytes()));
let revision_segment = NameSegment::from_digest(Sha3_256::new().chain_update(key.0));
file_block_name.with_segments_added(Some(revision_segment))
}

/// Creates the label for a block of a file.
fn create_block_label(key: &SnapshotKey, index: usize, file_revision_name: &Name) -> Name {
let key_hash = Sha3_256::new()
.chain_update(key.0.as_bytes())
.chain_update(key.0)
.chain_update(index.to_le_bytes());
let elem = NameSegment::from_digest(key_hash);

Expand Down
4 changes: 2 additions & 2 deletions wnfs/src/private/forest/traits.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
error::AesError,
error::CryptError,
private::{PrivateNode, TemporalKey},
};
use anyhow::Result;
Expand Down Expand Up @@ -133,7 +133,7 @@ pub trait PrivateForest {
for cid in cids {
match PrivateNode::from_cid(*cid, temporal_key, store, parent_name.clone(), setup).await {
Ok(node) => yield Ok(node),
Err(e) if e.downcast_ref::<AesError>().is_some() => {
Err(e) if e.downcast_ref::<CryptError>().is_some() => {
// we likely matched a PrivateNodeHeader instead of a PrivateNode.
// we skip it
}
Expand Down
84 changes: 0 additions & 84 deletions wnfs/src/private/keys/aes.rs

This file was deleted.

2 changes: 0 additions & 2 deletions wnfs/src/private/keys/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
mod access;
mod aes;
mod exchange;
mod privateref;

pub use self::exchange::*;
pub use access::*;
pub use aes::*;
pub(crate) use privateref::*;
31 changes: 11 additions & 20 deletions wnfs/src/private/keys/privateref.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use super::KEY_BYTE_SIZE;
use crate::{
error::{AesError, FsError},
private::{PrivateRefSerializable, TemporalKey},
error::FsError,
private::{PrivateRefSerializable, TemporalKey, KEY_BYTE_SIZE},
};
use aes_kw::KekAes256;
use anyhow::Result;
use libipld_core::cid::Cid;
use serde::{de::Error as DeError, ser::Error as SerError, Deserialize, Serialize};
Expand Down Expand Up @@ -62,11 +60,9 @@ impl PrivateRef {
parent_temporal_key: &TemporalKey,
) -> Result<PrivateRefSerializable> {
let snapshot_key = self.temporal_key.derive_snapshot_key();
// encrypt ratchet key
let temporal_key_as_kek = KekAes256::from(parent_temporal_key.0.clone().bytes());
let temporal_key_wrapped = temporal_key_as_kek
.wrap_with_padding_vec(self.temporal_key.0.as_bytes())
.map_err(|e| AesError::UnableToEncrypt(format!("{e}")))?;

// encrypt temporal key
let temporal_key_wrapped = parent_temporal_key.key_wrap_encrypt(&self.temporal_key.0)?;

Ok(PrivateRefSerializable {
revision_name_hash: self.revision_name_hash,
Expand All @@ -80,25 +76,20 @@ impl PrivateRef {
private_ref: PrivateRefSerializable,
parent_temporal_key: &TemporalKey,
) -> Result<Self> {
// TODO: Move key wrapping & unwrapping logic to impl TemporalKey
let temporal_key_as_kek = KekAes256::from(parent_temporal_key.0.clone().bytes());

let temporal_key_raw: [u8; KEY_BYTE_SIZE] = temporal_key_as_kek
.unwrap_with_padding_vec(&private_ref.temporal_key)
.map_err(|e| AesError::UnableToDecrypt(format!("{e}")))?
.try_into()
.map_err(|e: Vec<u8>| {
let temporal_key_decrypted =
parent_temporal_key.key_wrap_decrypt(&private_ref.temporal_key)?;

let temporal_key_raw: [u8; KEY_BYTE_SIZE] =
temporal_key_decrypted.try_into().map_err(|e: Vec<u8>| {
FsError::InvalidDeserialization(format!(
"Expected 32 bytes for ratchet key, but got {}",
e.len()
))
})?;

let temporal_key = temporal_key_raw.into();

Ok(Self {
revision_name_hash: private_ref.revision_name_hash,
temporal_key,
temporal_key: temporal_key_raw.into(),
content_cid: private_ref.content_cid,
})
}
Expand Down
2 changes: 1 addition & 1 deletion wnfs/src/private/node/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ impl PrivateNodeHeader {
/// ```
/// use std::rc::Rc;
/// use wnfs::private::{
/// PrivateFile, AesKey,
/// PrivateFile,
/// forest::{hamt::HamtForest, traits::PrivateForest},
/// };
/// use chrono::Utc;
Expand Down
Loading

0 comments on commit c17f6bb

Please sign in to comment.