diff --git a/Cargo.lock b/Cargo.lock index 52a0be76f3087d7891868b5a1142c70058af2d97..0a0b3fbe5f79254f3e4fe837be805d64b0b42d8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,6 +6,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" +[[package]] +name = "aead" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf01b9b56e767bb57b94ebf91a58b338002963785cdd7013e21c0d4679471e4" +dependencies = [ + "generic-array", +] + [[package]] name = "aes" version = "0.3.2" @@ -288,6 +297,18 @@ dependencies = [ "subtle 1.0.0", ] +[[package]] +name = "crypto_box" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9bbe0776bec03ad00a033d6bf9a18458781a90df99486e558e78f0fd6b15c23" +dependencies = [ + "rand_core", + "salsa20", + "x25519-dalek", + "xsalsa20poly1305", +] + [[package]] name = "csv" version = "1.1.3" @@ -343,6 +364,7 @@ dependencies = [ "bs58", "byteorder", "criterion", + "crypto_box", "curve25519-dalek", "ring", "scrypt", @@ -586,6 +608,15 @@ dependencies = [ "web-sys", ] +[[package]] +name = "poly1305" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5829f50f48e9ddb79f3f7c3097029d0caee30f8286accb241416df603b080b8" +dependencies = [ + "universal-hash", +] + [[package]] name = "proc-macro2" version = "1.0.8" @@ -703,6 +734,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" +[[package]] +name = "salsa20" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "becffecaad76c7ff25f0ba16387721b48e7cfe0b04cd290478d338f4203eff3c" +dependencies = [ + "stream-cipher", + "zeroize", +] + [[package]] name = "same-file" version = "1.0.6" @@ -812,6 +853,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "stream-cipher" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" +dependencies = [ + "generic-array", +] + [[package]] name = "subtle" version = "1.0.0" @@ -928,6 +978,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +[[package]] +name = "universal-hash" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0c900f2f9b4116803415878ff48b63da9edb268668e08cf9292d7503114a01" +dependencies = [ + "generic-array", + "subtle 2.2.2", +] + [[package]] name = "untrusted" version = "0.7.0" @@ -1092,6 +1152,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "x25519-dalek" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637ff90c9540fa3073bb577e65033069e4bae7c79d49d74aa3ffdf5342a53217" +dependencies = [ + "curve25519-dalek", + "rand_core", + "zeroize", +] + [[package]] name = "xattr" version = "0.2.2" @@ -1101,6 +1172,18 @@ dependencies = [ "libc", ] +[[package]] +name = "xsalsa20poly1305" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e183e60a53b16cb71c90200f6dc3b9fdeef410a613d1e28c3fbcbda8180e82d4" +dependencies = [ + "aead", + "poly1305", + "salsa20", + "zeroize", +] + [[package]] name = "zeroize" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 8cbf95ba7048ad63590d2183fb4edb0ff7effe3f..58a5a14298938811f0ca0fbd058d634e59bde995 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ arrayvec = { version = "0.5.1", features = ["array-sizes-33-128", "array-sizes-1 base64 = "0.11.0" bs58 = "0.3.0" byteorder = "1.3.2" +crypto_box = { version = "0.1.0", optional = true } curve25519-dalek = "2.0.0" ring = "0.16.9" scrypt = { version = "0.2", default-features = false } @@ -43,7 +44,8 @@ default = ["dewif", "private_message", "rand", "ser"] aes256 = ["aes"] dewif = ["aes256", "arrayvec"] -x25519 = [] +legacy_box_nacl = ["crypto_box"] private_message = ["arrayvec", "x25519", "rand"] rand = [] ser = ["serde"] +x25519 = [] diff --git a/src/keys/x25519.rs b/src/keys/x25519.rs index 00d4d2244ff378c26a116977f7f360389f9e2dd6..f6de9326d0544454b461ef88223c7a789043c48b 100644 --- a/src/keys/x25519.rs +++ b/src/keys/x25519.rs @@ -29,6 +29,12 @@ use zeroize::Zeroize; #[derive(Clone, Copy, Debug)] pub(crate) struct X25519PublicKey(MontgomeryPoint); +impl AsRef<[u8]> for X25519PublicKey { + fn as_ref(&self) -> &[u8] { + self.0.as_bytes() + } +} + impl From<&PublicKey> for X25519PublicKey { fn from(ed25519_public_key: &PublicKey) -> Self { let compressed_edwards_y = CompressedEdwardsY::from_slice(ed25519_public_key.as_ref()); @@ -43,6 +49,12 @@ impl From<&PublicKey> for X25519PublicKey { #[zeroize(drop)] pub(crate) struct X25519SecretKey([u8; 32]); +impl AsRef<[u8]> for X25519SecretKey { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + impl From<&Seed32> for X25519SecretKey { fn from(seed: &Seed32) -> Self { let mut hash = [0u8; 64]; diff --git a/src/private_message.rs b/src/private_message.rs index 6161df9429a8937a4677697d8385c1268c5d5a75..b1e820e1b8b1cfb30cd93ba8107e0ed2d020120d 100644 --- a/src/private_message.rs +++ b/src/private_message.rs @@ -129,6 +129,8 @@ //! mod authentication; +#[cfg(feature = "legacy_box_nacl")] +mod legacy_box_nacl; pub use self::authentication::AuthenticationPolicy; pub use ring::aead::Aad; diff --git a/src/private_message/legacy_box_nacl.rs b/src/private_message/legacy_box_nacl.rs new file mode 100644 index 0000000000000000000000000000000000000000..ef49a1ebd0ee42cf912232c5492ee6e66cbc6940 --- /dev/null +++ b/src/private_message/legacy_box_nacl.rs @@ -0,0 +1,96 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Legacy crypto_box used by NaCl + +use crate::keys::ed25519::{Ed25519KeyPair, PublicKey as Ed25519PublicKey}; +use crate::keys::x25519::{X25519PublicKey, X25519SecretKey}; +use crypto_box::aead::generic_array::{typenum::U24, GenericArray}; +use crypto_box::{ + aead::{Aead, Buffer}, + Box, PublicKey, SecretKey, +}; +use ring::error::Unspecified; + +pub struct Nonce(GenericArray<u8, U24>); + +impl AsRef<[u8]> for Nonce { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +/// Encrypt message +pub fn encrypt_in_place<M>( + sender_key_pair: &Ed25519KeyPair, + receiver_public_key: &Ed25519PublicKey, + message: &mut M, +) -> Result<Nonce, Unspecified> +where + M: Buffer, +{ + let nonce = generate_nonce(&ring::rand::SystemRandom::new())?; + + let x25519_secret_key = X25519SecretKey::from(sender_key_pair.seed()); + let mut sender_secret_key = [0u8; 32]; + sender_secret_key.copy_from_slice(x25519_secret_key.as_ref()); + let sender_secret_key = SecretKey::from(sender_secret_key); + + let x25519_public_key = X25519PublicKey::from(receiver_public_key); + let mut receiver_public_key = [0u8; 32]; + receiver_public_key.copy_from_slice(x25519_public_key.as_ref()); + let receiver_public_key = PublicKey::from(receiver_public_key); + + let salsa_box = Box::new(&receiver_public_key, &sender_secret_key); + + salsa_box + .encrypt_in_place(&nonce, &[], message) + .map_err(|_| Unspecified)?; + + Ok(Nonce(nonce)) +} + +fn generate_nonce<T>(csprng: &T) -> Result<GenericArray<u8, U24>, Unspecified> +where + T: ring::rand::SecureRandom, +{ + let mut nonce = GenericArray::default(); + csprng.fill(&mut nonce)?; + Ok(nonce) +} + +/// Decrypt message +pub fn decrypt_in_place<M: Buffer>( + receiver_key_pair: &Ed25519KeyPair, + sender_public_key: &Ed25519PublicKey, + message: &mut M, + nonce: Nonce, +) -> Result<(), Unspecified> { + let x25519_secret_key = X25519SecretKey::from(receiver_key_pair.seed()); + let mut receiver_secret_key = [0u8; 32]; + receiver_secret_key.copy_from_slice(x25519_secret_key.as_ref()); + let receiver_secret_key = SecretKey::from(receiver_secret_key); + + let x25519_public_key = X25519PublicKey::from(sender_public_key); + let mut sender_public_key = [0u8; 32]; + sender_public_key.copy_from_slice(x25519_public_key.as_ref()); + let sender_public_key = PublicKey::from(sender_public_key); + + let salsa_box = Box::new(&sender_public_key, &receiver_secret_key); + + salsa_box + .decrypt_in_place(&nonce.0, &[], message) + .map_err(|_| Unspecified) +}