From 1201e9621c3d8496325fa8c333bb54472213e638 Mon Sep 17 00:00:00 2001 From: Thomas Heck Date: Mon, 8 Mar 2021 22:46:34 +0100 Subject: [PATCH] feat: replace sodium secretbox cryptor with xchacha20poly1305 --- Cargo.toml | 2 +- .../Cargo.toml | 8 ++- .../src/lib.rs | 60 +++++++++++++------ examples/test/Cargo.toml | 2 +- examples/test/src/main.rs | 4 +- flake.nix | 1 - 6 files changed, 51 insertions(+), 26 deletions(-) rename {crdt-enc-sodium => crdt-enc-xchacha20poly1305}/Cargo.toml (68%) rename {crdt-enc-sodium => crdt-enc-xchacha20poly1305}/src/lib.rs (54%) diff --git a/Cargo.toml b/Cargo.toml index 05fb40e..c2eb058 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [ "crdt-enc", "crdt-enc-gpgme", - "crdt-enc-sodium", + "crdt-enc-xchacha20poly1305", "crdt-enc-tokio", "examples/*", ] diff --git a/crdt-enc-sodium/Cargo.toml b/crdt-enc-xchacha20poly1305/Cargo.toml similarity index 68% rename from crdt-enc-sodium/Cargo.toml rename to crdt-enc-xchacha20poly1305/Cargo.toml index add7eb9..86bc951 100644 --- a/crdt-enc-sodium/Cargo.toml +++ b/crdt-enc-xchacha20poly1305/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "crdt-enc-sodium" +name = "crdt-enc-xchacha20poly1305" version = "0.1.0" authors = ["Thomas Heck "] edition = "2018" [dependencies] crdts = "6.2" -sodiumoxide = "0.2" +rand = "0.8" anyhow = "1" serde = "1" rmp-serde = "0.15" @@ -16,3 +16,7 @@ async-trait = "0.1" [dependencies.crdt-enc] path = "../crdt-enc" + +[dependencies.chacha20poly1305] +version = "0.7" +features = ["std"] diff --git a/crdt-enc-sodium/src/lib.rs b/crdt-enc-xchacha20poly1305/src/lib.rs similarity index 54% rename from crdt-enc-sodium/src/lib.rs rename to crdt-enc-xchacha20poly1305/src/lib.rs index 5dba305..06a4f56 100644 --- a/crdt-enc-sodium/src/lib.rs +++ b/crdt-enc-xchacha20poly1305/src/lib.rs @@ -1,8 +1,12 @@ use ::anyhow::{Context, Error, Result}; use ::async_trait::async_trait; +use ::chacha20poly1305::{ + aead::{Aead, NewAead}, + Key, XChaCha20Poly1305, XNonce, +}; use ::crdt_enc::utils::{VersionBytes, VersionBytesRef}; +use ::rand::{thread_rng, RngCore}; use ::serde::{Deserialize, Serialize}; -use ::sodiumoxide::crypto::secretbox; use ::std::{borrow::Cow, fmt::Debug}; use ::uuid::Uuid; @@ -10,9 +14,8 @@ const DATA_VERSION: Uuid = Uuid::from_u128(0xc7f269be_0ff5_4a77_99c3_7c23c96d5cb const KEY_VERSION: Uuid = Uuid::from_u128(0x5df28591_439a_4cef_8ca6_8433276cc9ed); -pub fn init() { - sodiumoxide::init().expect("sodium init failed"); -} +const KEY_LEN: usize = 32; +const NONCE_LEN: usize = 24; #[derive(Debug)] pub struct EncHandler; @@ -26,20 +29,32 @@ impl EncHandler { #[async_trait] impl crdt_enc::cryptor::Cryptor for EncHandler { async fn gen_key(&self) -> Result { - let key = secretbox::gen_key(); - Ok(VersionBytes::new(KEY_VERSION, key.as_ref().into())) + let mut key = [0u8; KEY_LEN]; + thread_rng() + .try_fill_bytes(&mut key) + .context("Unable to get random data for secret key")?; + Ok(VersionBytes::new(KEY_VERSION, key.into())) } async fn encrypt(&self, key: VersionBytesRef<'_>, clear_text: &[u8]) -> Result> { key.ensure_version(KEY_VERSION) .context("not matching key version")?; - let key = secretbox::Key::from_slice(key.as_ref()).context("invalid key length")?; - - let nonce = secretbox::gen_nonce(); - let enc_data = secretbox::seal(clear_text, &nonce, &key); + if key.as_ref().len() != KEY_LEN { + return Err(Error::msg("Invalid key length")); + } + let key = Key::from_slice(key.as_ref()); + let aead = XChaCha20Poly1305::new(key); + let mut nonce = [0u8; NONCE_LEN]; + thread_rng() + .try_fill_bytes(&mut nonce) + .context("Unable to get random data for nonce")?; + let xnonce = XNonce::from_slice(&nonce); + let enc_data = aead + .encrypt(xnonce, clear_text) + .context("Encryption failed")?; let enc_box = EncBox { - nonce, - enc_data: enc_data.into(), + nonce: Cow::Borrowed(nonce.as_ref()), + enc_data: Cow::Owned(enc_data), }; let enc_box_bytes = rmp_serde::to_vec_named(&enc_box).context("failed to encode encryption box")?; @@ -52,25 +67,34 @@ impl crdt_enc::cryptor::Cryptor for EncHandler { async fn decrypt(&self, key: VersionBytesRef<'_>, enc_data: &[u8]) -> Result> { key.ensure_version(KEY_VERSION) .context("not matching key version")?; - let key = secretbox::Key::from_slice(key.as_ref()).context("invalid key length")?; - + if key.as_ref().len() != KEY_LEN { + return Err(Error::msg("Invalid key length")); + } let version_box: VersionBytesRef = rmp_serde::from_read_ref(enc_data).context("failed to parse version box")?; version_box .ensure_version(DATA_VERSION) .context("not matching version of encryption box")?; - let enc_box: EncBox = rmp_serde::from_read_ref(version_box.as_ref()) .context("failed to parse encryption box")?; - let clear_text = secretbox::open(&enc_box.enc_data, &enc_box.nonce, &key) - .map_err(|_| Error::msg("failed decrypting data"))?; + if enc_box.nonce.as_ref().len() != NONCE_LEN { + return Err(Error::msg("Invalid nonce length")); + } + let key = Key::from_slice(key.as_ref()); + let aead = XChaCha20Poly1305::new(key); + let xnonce = XNonce::from_slice(&enc_box.nonce); + let clear_text = aead + .decrypt(&xnonce, enc_box.enc_data.as_ref()) + .context("Decryption failed")?; Ok(clear_text) } } #[derive(Serialize, Deserialize, Debug)] struct EncBox<'a> { - nonce: secretbox::Nonce, + #[serde(borrow)] + #[serde(with = "serde_bytes")] + nonce: Cow<'a, [u8]>, #[serde(borrow)] #[serde(with = "serde_bytes")] diff --git a/examples/test/Cargo.toml b/examples/test/Cargo.toml index f6b19ce..b25b0e9 100644 --- a/examples/test/Cargo.toml +++ b/examples/test/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] crdt-enc = {path="../../crdt-enc"} crdt-enc-tokio = {path="../../crdt-enc-tokio"} -crdt-enc-sodium = {path="../../crdt-enc-sodium"} +crdt-enc-xchacha20poly1305 = {path="../../crdt-enc-xchacha20poly1305"} crdt-enc-gpgme = {path="../../crdt-enc-gpgme"} uuid = {version = "0.8", features = ["serde", "v4"]} crdts = "6.2" diff --git a/examples/test/src/main.rs b/examples/test/src/main.rs index 9693313..0d8d074 100644 --- a/examples/test/src/main.rs +++ b/examples/test/src/main.rs @@ -1,7 +1,7 @@ use ::anyhow::Result; use ::crdt_enc_gpgme::KeyHandler; -use ::crdt_enc_sodium::EncHandler; use ::crdt_enc_tokio::Storage; +use ::crdt_enc_xchacha20poly1305::EncHandler; use ::futures::task; use ::uuid::Uuid; @@ -23,8 +23,6 @@ impl task::Spawn for TokioSpawn { #[tokio::main(flavor = "current_thread")] async fn main() -> Result<()> { - crdt_enc_sodium::init(); - let data_dir = std::fs::canonicalize("./").unwrap().join("data"); let storage = Storage::new(data_dir.join("local"), data_dir.join("remote"))?; diff --git a/flake.nix b/flake.nix index c7051c7..e5969e5 100644 --- a/flake.nix +++ b/flake.nix @@ -15,7 +15,6 @@ name = "crdt-enc"; buildInputs = [ pkgs.gpgme - pkgs.libsodium pkgs.cargo pkgs.rustc