feat: replace sodium secretbox cryptor with xchacha20poly1305

This commit is contained in:
2021-03-08 22:46:34 +01:00
parent 2b9d6fd1fc
commit 1201e9621c
6 changed files with 51 additions and 26 deletions

View File

@@ -3,7 +3,7 @@
members = [ members = [
"crdt-enc", "crdt-enc",
"crdt-enc-gpgme", "crdt-enc-gpgme",
"crdt-enc-sodium", "crdt-enc-xchacha20poly1305",
"crdt-enc-tokio", "crdt-enc-tokio",
"examples/*", "examples/*",
] ]

View File

@@ -1,12 +1,12 @@
[package] [package]
name = "crdt-enc-sodium" name = "crdt-enc-xchacha20poly1305"
version = "0.1.0" version = "0.1.0"
authors = ["Thomas Heck <t@b128.net>"] authors = ["Thomas Heck <t@b128.net>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
crdts = "6.2" crdts = "6.2"
sodiumoxide = "0.2" rand = "0.8"
anyhow = "1" anyhow = "1"
serde = "1" serde = "1"
rmp-serde = "0.15" rmp-serde = "0.15"
@@ -16,3 +16,7 @@ async-trait = "0.1"
[dependencies.crdt-enc] [dependencies.crdt-enc]
path = "../crdt-enc" path = "../crdt-enc"
[dependencies.chacha20poly1305]
version = "0.7"
features = ["std"]

View File

@@ -1,8 +1,12 @@
use ::anyhow::{Context, Error, Result}; use ::anyhow::{Context, Error, Result};
use ::async_trait::async_trait; use ::async_trait::async_trait;
use ::chacha20poly1305::{
aead::{Aead, NewAead},
Key, XChaCha20Poly1305, XNonce,
};
use ::crdt_enc::utils::{VersionBytes, VersionBytesRef}; use ::crdt_enc::utils::{VersionBytes, VersionBytesRef};
use ::rand::{thread_rng, RngCore};
use ::serde::{Deserialize, Serialize}; use ::serde::{Deserialize, Serialize};
use ::sodiumoxide::crypto::secretbox;
use ::std::{borrow::Cow, fmt::Debug}; use ::std::{borrow::Cow, fmt::Debug};
use ::uuid::Uuid; 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); const KEY_VERSION: Uuid = Uuid::from_u128(0x5df28591_439a_4cef_8ca6_8433276cc9ed);
pub fn init() { const KEY_LEN: usize = 32;
sodiumoxide::init().expect("sodium init failed"); const NONCE_LEN: usize = 24;
}
#[derive(Debug)] #[derive(Debug)]
pub struct EncHandler; pub struct EncHandler;
@@ -26,20 +29,32 @@ impl EncHandler {
#[async_trait] #[async_trait]
impl crdt_enc::cryptor::Cryptor for EncHandler { impl crdt_enc::cryptor::Cryptor for EncHandler {
async fn gen_key(&self) -> Result<VersionBytes> { async fn gen_key(&self) -> Result<VersionBytes> {
let key = secretbox::gen_key(); let mut key = [0u8; KEY_LEN];
Ok(VersionBytes::new(KEY_VERSION, key.as_ref().into())) 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<Vec<u8>> { async fn encrypt(&self, key: VersionBytesRef<'_>, clear_text: &[u8]) -> Result<Vec<u8>> {
key.ensure_version(KEY_VERSION) key.ensure_version(KEY_VERSION)
.context("not matching 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 nonce = secretbox::gen_nonce(); }
let enc_data = secretbox::seal(clear_text, &nonce, &key); 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 { let enc_box = EncBox {
nonce, nonce: Cow::Borrowed(nonce.as_ref()),
enc_data: enc_data.into(), enc_data: Cow::Owned(enc_data),
}; };
let enc_box_bytes = let enc_box_bytes =
rmp_serde::to_vec_named(&enc_box).context("failed to encode encryption box")?; 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<Vec<u8>> { async fn decrypt(&self, key: VersionBytesRef<'_>, enc_data: &[u8]) -> Result<Vec<u8>> {
key.ensure_version(KEY_VERSION) key.ensure_version(KEY_VERSION)
.context("not matching 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 = let version_box: VersionBytesRef =
rmp_serde::from_read_ref(enc_data).context("failed to parse version box")?; rmp_serde::from_read_ref(enc_data).context("failed to parse version box")?;
version_box version_box
.ensure_version(DATA_VERSION) .ensure_version(DATA_VERSION)
.context("not matching version of encryption box")?; .context("not matching version of encryption box")?;
let enc_box: EncBox = rmp_serde::from_read_ref(version_box.as_ref()) let enc_box: EncBox = rmp_serde::from_read_ref(version_box.as_ref())
.context("failed to parse encryption box")?; .context("failed to parse encryption box")?;
let clear_text = secretbox::open(&enc_box.enc_data, &enc_box.nonce, &key) if enc_box.nonce.as_ref().len() != NONCE_LEN {
.map_err(|_| Error::msg("failed decrypting data"))?; 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) Ok(clear_text)
} }
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
struct EncBox<'a> { struct EncBox<'a> {
nonce: secretbox::Nonce, #[serde(borrow)]
#[serde(with = "serde_bytes")]
nonce: Cow<'a, [u8]>,
#[serde(borrow)] #[serde(borrow)]
#[serde(with = "serde_bytes")] #[serde(with = "serde_bytes")]

View File

@@ -7,7 +7,7 @@ edition = "2018"
[dependencies] [dependencies]
crdt-enc = {path="../../crdt-enc"} crdt-enc = {path="../../crdt-enc"}
crdt-enc-tokio = {path="../../crdt-enc-tokio"} 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"} crdt-enc-gpgme = {path="../../crdt-enc-gpgme"}
uuid = {version = "0.8", features = ["serde", "v4"]} uuid = {version = "0.8", features = ["serde", "v4"]}
crdts = "6.2" crdts = "6.2"

View File

@@ -1,7 +1,7 @@
use ::anyhow::Result; use ::anyhow::Result;
use ::crdt_enc_gpgme::KeyHandler; use ::crdt_enc_gpgme::KeyHandler;
use ::crdt_enc_sodium::EncHandler;
use ::crdt_enc_tokio::Storage; use ::crdt_enc_tokio::Storage;
use ::crdt_enc_xchacha20poly1305::EncHandler;
use ::futures::task; use ::futures::task;
use ::uuid::Uuid; use ::uuid::Uuid;
@@ -23,8 +23,6 @@ impl task::Spawn for TokioSpawn {
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> { async fn main() -> Result<()> {
crdt_enc_sodium::init();
let data_dir = std::fs::canonicalize("./").unwrap().join("data"); let data_dir = std::fs::canonicalize("./").unwrap().join("data");
let storage = Storage::new(data_dir.join("local"), data_dir.join("remote"))?; let storage = Storage::new(data_dir.join("local"), data_dir.join("remote"))?;

View File

@@ -15,7 +15,6 @@
name = "crdt-enc"; name = "crdt-enc";
buildInputs = [ buildInputs = [
pkgs.gpgme pkgs.gpgme
pkgs.libsodium
pkgs.cargo pkgs.cargo
pkgs.rustc pkgs.rustc