From 4003185b8bb3abe32d41be24c2754bba5a7d7651 Mon Sep 17 00:00:00 2001 From: Hardcore Sushi Date: Sun, 27 Jun 2021 20:35:23 +0200 Subject: [PATCH] XChaCha20 support --- Cargo.lock | 151 +++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 7 +- README.md | 14 ++-- src/bin/aes-ni.rs | 9 +++ src/bin/blocksize.rs | 8 +-- src/cli.rs | 39 +++++++++-- src/crypto.rs | 107 +++++++++++++++++++----------- src/lib.rs | 6 +- src/main.rs | 27 ++++---- 9 files changed, 302 insertions(+), 66 deletions(-) create mode 100644 src/bin/aes-ni.rs diff --git a/Cargo.lock b/Cargo.lock index 7ddb56d..b9e92de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,6 +15,15 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + [[package]] name = "argon2" version = "0.2.1" @@ -37,6 +46,17 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "base64ct" version = "1.0.0" @@ -93,6 +113,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chacha20" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee7ad89dc1128635074c268ee661f90c3f7e83d9fd12910608c36b47d6c3412" +dependencies = [ + "cfg-if 1.0.0", + "cipher", + "cpufeatures", +] + [[package]] name = "cipher" version = "0.3.0" @@ -108,9 +139,13 @@ version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ + "ansi_term", + "atty", "bitflags", + "strsim", "textwrap", "unicode-width", + "vec_map", ] [[package]] @@ -157,6 +192,17 @@ dependencies = [ "cipher", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "digest" version = "0.9.0" @@ -173,9 +219,12 @@ dependencies = [ "aes", "argon2", "blake3", + "chacha20", "clap", + "cpufeatures", "hkdf", "hmac", + "num_enum", "rand", "rpassword", "zeroize", @@ -202,6 +251,15 @@ dependencies = [ "wasi", ] +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hkdf" version = "0.11.0" @@ -228,6 +286,28 @@ version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" +[[package]] +name = "num_enum" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066" +dependencies = [ + "derivative", + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "opaque-debug" version = "0.3.0" @@ -251,6 +331,33 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rand" version = "0.8.4" @@ -301,12 +408,35 @@ dependencies = [ "winapi", ] +[[package]] +name = "serde" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "subtle" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" +[[package]] +name = "syn" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -316,6 +446,15 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + [[package]] name = "typenum" version = "1.13.0" @@ -328,6 +467,18 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.3" diff --git a/Cargo.toml b/Cargo.toml index 870de93..8714c14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,12 @@ codegen-units = 1 opt-level = 3 [dependencies] -clap = { version = "2.33", default-features = false } +clap = "2.33" rand = "0.8" -aes = { version = "0.7", features = ["ctr", "armv8"] } +num_enum = "0.5" +cpufeatures = "0.1" +aes = { version = "0.7", features = ["ctr"] } +chacha20 = "0.7" hmac = "0.11" blake3 = "0.3" hkdf = "0.11" diff --git a/README.md b/README.md index 8f7700b..bc92d72 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Secure symmetric encryption from the command line ## Features -* Fast: written in [rust](https://www.rust-lang.org), encrypts with [AES-256-CTR](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)) +* Fast: written in [rust](https://www.rust-lang.org), encrypts with [AES-256-CTR](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)) or [XChaCha20](https://en.wikipedia.org/wiki/Salsa20#XChaCha) * [HMAC](https://en.wikipedia.org/wiki/HMAC) ciphertext authentication * Password brute-force resistance with [Argon2](https://en.wikipedia.org/wiki/Argon2) * Encryption from STDIN/STDOUT or from files @@ -71,8 +71,9 @@ FLAGS: -V, --version Prints version information OPTIONS: - -p, --password + -p, --password Password used to derive encryption keys -b, --block-size Size of file chunk (in bytes) [default: 65536] + -c, --cipher Encryption cipher to use [possible values: aes, xchacha20] -m, --memory-cost Argon2 memory cost (in kilobytes) [default: 4096] -t, --threads Argon2 parallelism (between 1 and 255) [default: 4] -i, --iterations Argon2 time cost [default: 10] @@ -141,15 +142,16 @@ hmac.update(argon2_time_cost); hmac.update(argon2_memory_cost); hmac.update(argon2_parallelism); hmac.update(random_hkdf_salt); -hmac.update(random_nonce); //16 bytes random nonce used in AES-CTR cipher +hmac.update(cipher); //1-byte representation of the symmetric cipher used to encrypt (either AES-CTR or XChaCha20) +hmac.update(random_nonce); //random nonce used for encryption (16 bytes for AES-CTR, 24 for XChaCha20) ``` All this parameters are also written in plain text in the header of the doby output. -Now, doby initializes an AES-CTR cipher with `encryption_key` and `random_nonce` and starts the actual encryption. It reads chunks from the plaintext (according to the `--block-size` parameter), encrypts them with the cipher and updates the HMAC with the ciphertext. +Now, doby initializes a symmetric cipher with `encryption_key` and `random_nonce` (either AES-CTR or XChaCha20, based on the `--cipher` option) and starts the actual encryption. It reads chunks from the plaintext (according to the `--block-size` parameter), encrypts them with the cipher and updates the HMAC with the ciphertext. ```rust -let cipher = Aes256Ctr::new(encryption_key, random_nonce); +let cipher = Aes256Ctr::new(encryption_key, random_nonce); //example with AES-CTR let mut n = 1; let mut chunk: [u8; block_size] = [0; block_size]; while n != 0 { @@ -185,7 +187,7 @@ let master_key: [u8; 32] = argon2id( Then, doby starts decryption. ```rust -let cipher = Aes256Ctr::new(encryption_key, nonce_read_from_input); +let cipher = XChaCha20::new(encryption_key, nonce_read_from_input); //example with XChaCha20 let mut n = 1; let mut chunk: [u8; block_size] = [0; block_size]; while n != 0 { diff --git a/src/bin/aes-ni.rs b/src/bin/aes-ni.rs new file mode 100644 index 0000000..d501e2e --- /dev/null +++ b/src/bin/aes-ni.rs @@ -0,0 +1,9 @@ +cpufeatures::new!(aes_ni, "aes"); + +fn main() { + println!("AES-NI is {}supported on this device.", if aes_ni::get() { + "" + } else { + "NOT " + }); +} \ No newline at end of file diff --git a/src/bin/blocksize.rs b/src/bin/blocksize.rs index d7515cc..b26c2fd 100644 --- a/src/bin/blocksize.rs +++ b/src/bin/blocksize.rs @@ -6,7 +6,7 @@ use std::{ }; use doby::{ encrypt, decrypt, - crypto::{ArgonParams, EncryptionParams, Cipher} + crypto::{ArgonParams, EncryptionParams, CipherAlgorithm, DobyCipher} }; const MAX_BLOCK_SIZE: usize = 1_073_741_824; //1GB @@ -37,7 +37,7 @@ fn main() -> io::Result<()> { t_cost: 1, m_cost: 8, parallelism: 1, - }); + }, CipherAlgorithm::AesCtr); let mut best_encrypt_time = None; let mut best_encrypt_block_size = None; @@ -49,7 +49,7 @@ fn main() -> io::Result<()> { let mut reader = BufReader::with_capacity(block_size, &input); let mut writer = BufWriter::with_capacity(block_size, &output); - let cipher = Cipher::new(PASSWORD, ¶ms).unwrap(); + let cipher = DobyCipher::new(PASSWORD, ¶ms).unwrap(); let t_encrypt = Instant::now(); encrypt(&mut reader, &mut writer, ¶ms, cipher, block_size, None)?; writer.flush()?; @@ -59,7 +59,7 @@ fn main() -> io::Result<()> { reset(&mut reader)?; reset(&mut writer)?; - let cipher = Cipher::new(PASSWORD, ¶ms).unwrap(); + let cipher = DobyCipher::new(PASSWORD, ¶ms).unwrap(); let t_decrypt = Instant::now(); decrypt(&mut reader, &mut writer, cipher, block_size)?; writer.flush()?; diff --git a/src/cli.rs b/src/cli.rs index a7b3276..f2b0f10 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -4,13 +4,16 @@ use std::{ str::FromStr, io::{stdin, stdout, Read, Write}, }; -use clap::{crate_name, crate_version, App, Arg}; -use crate::crypto::ArgonParams; +use clap::{crate_name, crate_version, App, Arg, AppSettings}; +use crate::crypto::{ArgonParams, CipherAlgorithm}; + +cpufeatures::new!(aes_ni, "aes"); pub struct CliArgs { pub password: String, pub force_encrypt: bool, pub argon2_params: ArgonParams, + pub cipher: CipherAlgorithm, pub block_size: usize, pub reader: Box, pub writer: Box, @@ -19,6 +22,7 @@ pub struct CliArgs { pub fn parse() -> Option { let app = App::new(crate_name!()) .version(crate_version!()) + .setting(AppSettings::ColoredHelp) .arg(Arg::with_name("INPUT").help(" | \"-\" or empty for stdin")) .arg(Arg::with_name("OUTPUT").help(" | \"-\" or empty for stdout")) .arg( @@ -32,6 +36,7 @@ pub fn parse() -> Option { .short("p") .long("password") .value_name("password") + .help("Password used to derive encryption keys") ) .arg( Arg::with_name("t_cost") @@ -64,6 +69,16 @@ pub fn parse() -> Option { .help("Size of file chunk (in bytes)") .default_value("65536") ) + .arg( + Arg::with_name("cipher") + .short("c") + .long("cipher") + .value_name("cipher") + .help("Encryption cipher to use") + .long_help("Encryption cipher to use. By default, AES is selected if AES-NI is supported. Otherwise, XChaCha20 is used.") + .possible_values(&["aes", "xchacha20"]) + .case_insensitive(true) + ) .get_matches(); let params = { @@ -78,6 +93,21 @@ pub fn parse() -> Option { } }; + let cipher = app + .value_of("cipher") + .and_then(|s| Some(if s.to_lowercase() == "aes" { + CipherAlgorithm::AesCtr + } else { + CipherAlgorithm::XChaCha20 + }) + ) + .unwrap_or_else(|| if aes_ni::get() { + CipherAlgorithm::AesCtr + } else { + CipherAlgorithm::XChaCha20 + } + ); + let block_size = number(app.value_of("blocksize").unwrap())?; let input = app @@ -97,7 +127,7 @@ pub fn parse() -> Option { Some(Box::new(File::create(s).unwrap()) as Box) } }) - .unwrap_or_else(|| Some(Box::new(stdout()))); + .unwrap_or_else(|| Some(Box::new(stdout())))?; let password = match app.value_of("password") { Some(s) => s.to_string(), @@ -108,9 +138,10 @@ pub fn parse() -> Option { password, force_encrypt: app.is_present("force-encrypt"), argon2_params: params, + cipher, block_size, reader: input, - writer: output?, + writer: output, }) } diff --git a/src/crypto.rs b/src/crypto.rs index c68189b..524853c 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -1,4 +1,6 @@ -use std::io::{self, Read, Write}; +use std::{convert::TryFrom, io::{self, Read, Write}}; +use num_enum::TryFromPrimitive; +use chacha20::XChaCha20; use aes::{Aes256Ctr, cipher::{NewCipher, StreamCipher}}; use hmac::{Hmac, Mac, NewMac}; use rand::{Rng, rngs::OsRng}; @@ -7,7 +9,8 @@ use hkdf::Hkdf; use zeroize::Zeroize; const SALT_LEN: usize = 64; -const NONCE_LEN: usize = 16; +const AES_NONCE_LEN: usize = 16; +const XCHACHA20_NONCE_LEN: usize = 24; const HASH_LEN: usize = 32; const KEY_LEN: usize = HASH_LEN; @@ -17,28 +20,48 @@ pub struct ArgonParams { pub parallelism: u8, } +#[derive(Clone, Copy, Debug, TryFromPrimitive)] +#[repr(u8)] +pub enum CipherAlgorithm { + AesCtr = 0, + XChaCha20 = 1, +} + +impl CipherAlgorithm { + fn get_nonce_size(&self) -> usize { + match self { + CipherAlgorithm::AesCtr => AES_NONCE_LEN, + CipherAlgorithm::XChaCha20 => XCHACHA20_NONCE_LEN, + } + } +} + pub struct EncryptionParams { password_salt: [u8; SALT_LEN], argon2: ArgonParams, hkdf_salt: [u8; SALT_LEN], - nonce: [u8; NONCE_LEN], + nonce: Vec, + cipher: CipherAlgorithm, } impl EncryptionParams { - const PARAMS_LEN: usize = SALT_LEN*2 + 4*2 + 1 + NONCE_LEN; + fn get_params_len(&self) -> usize { + SALT_LEN*2 + 4*2 + 1 + self.cipher.get_nonce_size() + } - pub fn new(argon2_params: ArgonParams) -> EncryptionParams { + pub fn new(argon2_params: ArgonParams, cipher: CipherAlgorithm) -> EncryptionParams { let mut password_salt = [0; SALT_LEN]; OsRng.fill(&mut password_salt); let mut hkdf_salt = [0; SALT_LEN]; OsRng.fill(&mut hkdf_salt); - let mut nonce = [0; NONCE_LEN]; - OsRng.fill(&mut nonce); + let mut nonce = vec![0; cipher.get_nonce_size()]; + OsRng.fill(&mut nonce[..]); EncryptionParams { password_salt, argon2: argon2_params, hkdf_salt, nonce, + cipher, } } pub fn write(&self, writer: &mut W) -> io::Result<()> { @@ -47,45 +70,54 @@ impl EncryptionParams { writer.write_all(&self.argon2.m_cost.to_be_bytes())?; writer.write_all(&self.argon2.parallelism.to_be_bytes())?; writer.write_all(&self.hkdf_salt)?; + writer.write_all(&(self.cipher as u8).to_be_bytes())?; writer.write_all(&self.nonce)?; Ok(()) } - pub fn read(reader: &mut R) -> io::Result { + pub fn read(reader: &mut R) -> io::Result> { let mut password_salt = [0; SALT_LEN]; reader.read_exact(&mut password_salt)?; - let mut t_cost_buf = [0; 4]; - reader.read_exact(&mut t_cost_buf)?; - let mut m_cost_buf = [0; 4]; - reader.read_exact(&mut m_cost_buf)?; - let mut parallelism_buf = [0; 1]; - reader.read_exact(&mut parallelism_buf)?; + let mut t_cost = [0; 4]; + reader.read_exact(&mut t_cost)?; + let mut m_cost = [0; 4]; + reader.read_exact(&mut m_cost)?; + let mut parallelism = [0; 1]; + reader.read_exact(&mut parallelism)?; let mut hkdf_salt = [0; SALT_LEN]; reader.read_exact(&mut hkdf_salt)?; - let mut nonce = [0; NONCE_LEN]; - reader.read_exact(&mut nonce)?; - - let argon2_params = ArgonParams { - t_cost: u32::from_be_bytes(t_cost_buf), - m_cost: u32::from_be_bytes(m_cost_buf), - parallelism: u8::from_be_bytes(parallelism_buf), - }; - - Ok(EncryptionParams { - password_salt, - argon2: argon2_params, - hkdf_salt, - nonce, - }) + let mut cipher_buff = [0; 1]; + reader.read_exact(&mut cipher_buff)?; + match CipherAlgorithm::try_from(cipher_buff[0]) { + Ok(cipher) => { + let mut nonce = vec![0; cipher.get_nonce_size()]; + reader.read_exact(&mut nonce)?; + + let argon2_params = ArgonParams { + t_cost: u32::from_be_bytes(t_cost), + m_cost: u32::from_be_bytes(m_cost), + parallelism: u8::from_be_bytes(parallelism), + }; + + Ok(Some(EncryptionParams { + password_salt, + argon2: argon2_params, + hkdf_salt, + nonce, + cipher, + })) + } + Err(_) => Ok(None) + } } } -pub struct Cipher { - cipher: Aes256Ctr, +pub struct DobyCipher { + cipher: Box, hmac: Hmac, buffer: Vec, } -impl Cipher { +impl DobyCipher { pub fn new(password: &[u8], params: &EncryptionParams) -> Result { let argon = Argon2::new(None, params.argon2.t_cost, params.argon2.m_cost, params.argon2.parallelism.into(), Version::V0x13)?; @@ -99,13 +131,16 @@ impl Cipher { hkdf.expand(b"doby_authentication_key", &mut authentication_key).unwrap(); master_key.zeroize(); - let mut encoded_params = Vec::with_capacity(EncryptionParams::PARAMS_LEN); + let mut encoded_params = Vec::with_capacity(params.get_params_len()); params.write(&mut encoded_params).unwrap(); let mut hmac = Hmac::new_from_slice(&authentication_key).unwrap(); hmac.update(&encoded_params); - Ok(Cipher { - cipher: Aes256Ctr::new_from_slices(&encryption_key, ¶ms.nonce).unwrap(), + Ok(Self { + cipher: match params.cipher { + CipherAlgorithm::AesCtr => Box::new(Aes256Ctr::new_from_slices(&encryption_key, ¶ms.nonce).unwrap()), + CipherAlgorithm::XChaCha20 => Box::new(XChaCha20::new_from_slices(&encryption_key, ¶ms.nonce).unwrap()), + }, hmac, buffer: Vec::new(), }) @@ -127,8 +162,8 @@ impl Cipher { buff[..buffer_len].clone_from_slice(&self.buffer); let read = reader.read(&mut buff[buffer_len..])?; - self.buffer.clear(); let n = if buffer_len + read >= HASH_LEN { + self.buffer.clear(); buffer_len + read - HASH_LEN } else { 0 diff --git a/src/lib.rs b/src/lib.rs index b3087b3..85b5303 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,11 +2,11 @@ pub mod cli; pub mod crypto; use std::io::{self, Read, Write}; -use crypto::{Cipher, EncryptionParams}; +use crypto::{DobyCipher, EncryptionParams}; pub const MAGIC_BYTES: &[u8; 4] = b"DOBY"; -pub fn encrypt(reader: &mut R, writer: &mut W, params: &EncryptionParams, mut cipher: Cipher, block_size: usize, already_read: Option>) -> io::Result<()> { +pub fn encrypt(reader: &mut R, writer: &mut W, params: &EncryptionParams, mut cipher: DobyCipher, block_size: usize, already_read: Option>) -> io::Result<()> { writer.write_all(MAGIC_BYTES)?; params.write(writer)?; let mut buff = vec![0; block_size]; @@ -30,7 +30,7 @@ pub fn encrypt(reader: &mut R, writer: &mut W, params: &Encry Ok(()) } -pub fn decrypt(reader: &mut R, writer: &mut W, mut cipher: Cipher, block_size: usize) -> io::Result { +pub fn decrypt(reader: &mut R, writer: &mut W, mut cipher: DobyCipher, block_size: usize) -> io::Result { let mut buff = vec![0; block_size]; loop { let n = cipher.decrypt_chunk(reader, &mut buff)?; diff --git a/src/main.rs b/src/main.rs index 3dffe10..f451647 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use std::io::{BufWriter, BufReader, Read}; use zeroize::Zeroize; use doby::{ cli, - crypto::{EncryptionParams, Cipher}, + crypto::{EncryptionParams, DobyCipher}, MAGIC_BYTES, decrypt, encrypt, @@ -22,25 +22,30 @@ fn main() { if magic_bytes == MAGIC_BYTES && !cli_args.force_encrypt { //we probably want to decrypt match EncryptionParams::read(&mut reader) { Ok(params) => { - match Cipher::new(cli_args.password.as_bytes(), ¶ms) { - Ok(cipher) => { - match decrypt(&mut reader, &mut writer, cipher, cli_args.block_size) { - Ok(verified) => { - if !verified { - eprintln!("WARNING: HMAC verification failed !\nEither your password is incorrect or the file has been corrupted.\nOpen with caution, it could have been infected by an attacker."); + match params { + Some(params) => { + match DobyCipher::new(cli_args.password.as_bytes(), ¶ms) { + Ok(cipher) => { + match decrypt(&mut reader, &mut writer, cipher, cli_args.block_size) { + Ok(verified) => { + if !verified { + eprintln!("WARNING: HMAC verification failed !\nEither your password is incorrect or the file has been corrupted.\nOpen with caution, it could have been infected by an attacker."); + } + } + Err(e) => eprintln!("I/O error while decrypting: {}", e) } } - Err(e) => eprintln!("I/O error while decrypting: {}", e) + Err(e) => eprintln!("Invalid argon2 params: {}", e) } } - Err(e) => eprintln!("Invalid argon2 params: {}", e) + None => eprintln!("Invalid cipher") } } Err(e) => eprintln!("I/O error while reading headers: {}", e) } } else { //otherwise, encrypt - let params = EncryptionParams::new(cli_args.argon2_params); - match Cipher::new(cli_args.password.as_bytes(), ¶ms) { + let params = EncryptionParams::new(cli_args.argon2_params, cli_args.cipher); + match DobyCipher::new(cli_args.password.as_bytes(), ¶ms) { Ok(cipher) => { if let Err(e) = encrypt( &mut reader,