diff --git a/Cargo.lock b/Cargo.lock index 0649d84..d6b289e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,7 +10,7 @@ checksum = "495ee669413bfbe9e8cace80f4d3d78e6d8c8d99579f97fb93bde351b185f2d4" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.1.5", "ctr", "opaque-debug", ] @@ -26,19 +26,20 @@ dependencies = [ [[package]] name = "argon2" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d60f5f3113c903294dc81dd8cf0012963ed4dda8bc931c864e12e175356ff98b" +checksum = "d805bb12b532be9ce066df7913311f43716b41d8d780e9322113e8a6ae7c41ab" dependencies = [ + "base64ct", "blake2", "password-hash", ] [[package]] name = "assert_cmd" -version = "1.0.6" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2dfc8228c6260bf620fc5a341afa8e27edcde388b19ffc5732320bfe657eb2" +checksum = "54f002ce7d0c5e809ebb02be78fd503aeed4a511fd0fcaff6e6914cbdabbfa33" dependencies = [ "bstr", "doc-comment", @@ -101,13 +102,13 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chacha20" -version = "0.7.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee7ad89dc1128635074c268ee661f90c3f7e83d9fd12910608c36b47d6c3412" +checksum = "01b72a433d0cf2aef113ba70f62634c56fddb0f244e6377185c56a7cadbd8f91" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.1", ] [[package]] @@ -143,6 +144,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + [[package]] name = "crypto-mac" version = "0.8.0" @@ -184,10 +194,10 @@ dependencies = [ ] [[package]] -name = "difference" -version = "2.0.0" +name = "difflib" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" [[package]] name = "digest" @@ -208,7 +218,7 @@ dependencies = [ "blake2", "chacha20", "clap", - "cpufeatures", + "cpufeatures 0.2.1", "hkdf", "hmac", "num_enum", @@ -224,6 +234,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "generic-array" version = "0.14.4" @@ -274,6 +290,15 @@ dependencies = [ "digest", ] +[[package]] +name = "itertools" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" +dependencies = [ + "either", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -282,9 +307,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.97" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" +checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" [[package]] name = "memchr" @@ -322,9 +347,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "password-hash" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1a5d4e9c205d2c1ae73b84aab6240e98218c0e72e63b50422cfb2d1ca952282" +checksum = "7ad7268ef9bc463fddde8361d358fbfae1aeeb1fb62eca111cd8c763bf1c5891" dependencies = [ "base64ct", "rand_core", @@ -339,11 +364,12 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "predicates" -version = "1.0.8" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df" +checksum = "c143348f141cc87aab5b950021bac6145d0e5ae754b0591de23244cee42c9308" dependencies = [ - "difference", + "difflib", + "itertools", "predicates-core", ] diff --git a/Cargo.toml b/Cargo.toml index 4fad60e..c66794e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,16 +18,16 @@ opt-level = 3 clap = "2.33" rand = "0.8" num_enum = "0.5" -cpufeatures = "0.1" +cpufeatures = "0.2" aes = { version = "0.7", features = ["ctr"] } -chacha20 = "0.7" +chacha20 = "0.8" hmac = "0.11" blake2 = "0.9" hkdf = "0.11" -argon2 = "0.2" +argon2 = "0.3" rpassword = "5.0" zeroize = "1.3" [dev-dependencies] -assert_cmd = "1.0" +assert_cmd = "2.0" tempfile = "3.0" \ No newline at end of file diff --git a/src/crypto.rs b/src/crypto.rs index 9406b02..109ac7b 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -1,8 +1,4 @@ -use std::{ - convert::TryFrom, - fmt::{self, Display, Formatter}, - io::{self, Read, Write} -}; +use std::{convert::{TryFrom, TryInto}, fmt::{self, Display, Formatter}, io::{self, Read, Write}}; use num_enum::TryFromPrimitive; use chacha20::XChaCha20; use aes::{Aes256Ctr, cipher::{NewCipher, StreamCipher}}; @@ -19,13 +15,20 @@ const XCHACHA20_NONCE_LEN: usize = 24; pub const HASH_LEN: usize = 64; const KEY_LEN: usize = 32; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct ArgonParams { pub t_cost: u32, pub m_cost: u32, pub parallelism: u8, } +impl TryFrom for argon2::Params { + type Error = argon2::Error; + fn try_from(params: ArgonParams) -> Result { + argon2::Params::new(params.m_cost, params.t_cost, params.parallelism.into(), None) + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)] #[repr(u8)] pub enum CipherAlgorithm { @@ -59,9 +62,7 @@ pub struct EncryptionParams { } impl EncryptionParams { - pub fn get_params_len(&self) -> usize { - SALT_LEN + 4*2 + 2 - } + pub const LEN: usize = SALT_LEN + 4*2 + 2; pub fn new(argon2_params: ArgonParams, cipher: CipherAlgorithm) -> EncryptionParams { let mut salt = [0; SALT_LEN]; @@ -72,6 +73,7 @@ impl EncryptionParams { cipher, } } + pub fn write(&self, writer: &mut W) -> io::Result<()> { writer.write_all(&self.salt)?; writer.write_all(&self.argon2.t_cost.to_be_bytes())?; @@ -80,6 +82,7 @@ impl EncryptionParams { writer.write_all(&(self.cipher as u8).to_be_bytes())?; Ok(()) } + pub fn read(reader: &mut R) -> io::Result> { let mut salt = [0; SALT_LEN]; reader.read_exact(&mut salt)?; @@ -129,11 +132,12 @@ pub struct DobyCipher { impl DobyCipher { pub fn new(mut password: Password, params: &EncryptionParams) -> Result { - match Argon2::new(None, params.argon2.t_cost, params.argon2.m_cost, params.argon2.parallelism.into(), Version::V0x13) { - Ok(argon2) => { + match params.argon2.try_into() { + Ok(argon2_params) => { + let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, argon2_params); let mut master_key = [0; KEY_LEN]; let password = password.unwrap_or_ask(); - argon2.hash_password_into(Algorithm::Argon2id, password.as_bytes(), ¶ms.salt, &[], &mut master_key).zeroize(password)?; + argon2.hash_password_into(password.as_bytes(), ¶ms.salt, &mut master_key).zeroize(password)?; let hkdf = Hkdf::::new(Some(¶ms.salt), &master_key); let mut nonce = vec![0; params.cipher.get_nonce_size()]; hkdf.expand(b"doby_nonce", &mut nonce).unwrap(); @@ -143,7 +147,7 @@ impl DobyCipher { hkdf.expand(b"doby_authentication_key", &mut authentication_key).unwrap(); master_key.zeroize(); - let mut encoded_params = Vec::with_capacity(params.get_params_len()); + let mut encoded_params = Vec::with_capacity(EncryptionParams::LEN); params.write(&mut encoded_params).unwrap(); let mut hmac = Hmac::new_from_slice(&authentication_key).unwrap(); authentication_key.zeroize(); @@ -214,7 +218,7 @@ mod tests { parallelism: 1, }, CipherAlgorithm::XChaCha20); - assert_eq!(params.get_params_len(), 74); + assert_eq!(EncryptionParams::LEN, 74); let mut buff = Vec::with_capacity(74); params.write(&mut buff).unwrap(); diff --git a/tests/authentication.rs b/tests/authentication.rs index dc95aa8..9385914 100644 --- a/tests/authentication.rs +++ b/tests/authentication.rs @@ -46,7 +46,7 @@ fn authentication() { let decrypter = DobyCipher::new(PASSWORD.into(), ¶ms).unwrap(); let mut decrypted = Vec::with_capacity(PLAINTEXT.len()); - let verified = decrypt(&mut &ciphertext[4+params.get_params_len()..], &mut decrypted, decrypter, BLOCK_SIZE).unwrap(); + let verified = decrypt(&mut &ciphertext[4+EncryptionParams::LEN..], &mut decrypted, decrypter, BLOCK_SIZE).unwrap(); assert_eq!(decrypted, PLAINTEXT); assert_eq!(verified, true); } \ No newline at end of file diff --git a/tests/cli.rs b/tests/cli.rs index cb120d5..575aa8c 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -132,7 +132,7 @@ fn aes_cipher() -> io::Result<()> { fn argon2_params() -> io::Result<()> { Command::cargo_bin("doby").unwrap().arg("-i").arg("0").assert().failure().stderr("Invalid argon2 params: time cost is too small\n"); Command::cargo_bin("doby").unwrap().arg("-m").arg("0").assert().failure().stderr("Invalid argon2 params: memory cost is too small\n"); - Command::cargo_bin("doby").unwrap().arg("-t").arg("0").assert().failure().stderr("Invalid argon2 params: too few lanes\n"); + Command::cargo_bin("doby").unwrap().arg("-t").arg("0").assert().failure().stderr("Invalid argon2 params: not enough threads\n"); let ciphertext = doby_cmd().unwrap().arg("-i").arg("8").arg("-m").arg("2048").arg("-t").arg("8").assert().success().stderr("").get_output().stdout.clone(); assert_eq!(u32::from_be_bytes(ciphertext[4+SALT_LEN..4+SALT_LEN+4].try_into().unwrap()), 8); //time cost