XChaCha20 support

This commit is contained in:
Matéo Duparc 2021-06-27 20:35:23 +02:00
parent 796a01376e
commit 4003185b8b
Signed by: hardcoresushi
GPG Key ID: 007F84120107191E
9 changed files with 302 additions and 66 deletions

151
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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 <password>
-p, --password <password> Password used to derive encryption keys
-b, --block-size <blocksize> Size of file chunk (in bytes) [default: 65536]
-c, --cipher <cipher> Encryption cipher to use [possible values: aes, xchacha20]
-m, --memory-cost <memory cost> Argon2 memory cost (in kilobytes) [default: 4096]
-t, --threads <threads> Argon2 parallelism (between 1 and 255) [default: 4]
-i, --iterations <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 {

9
src/bin/aes-ni.rs Normal file
View File

@ -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 "
});
}

View File

@ -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, &params).unwrap();
let cipher = DobyCipher::new(PASSWORD, &params).unwrap();
let t_encrypt = Instant::now();
encrypt(&mut reader, &mut writer, &params, 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, &params).unwrap();
let cipher = DobyCipher::new(PASSWORD, &params).unwrap();
let t_decrypt = Instant::now();
decrypt(&mut reader, &mut writer, cipher, block_size)?;
writer.flush()?;

View File

@ -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<dyn Read>,
pub writer: Box<dyn Write>,
@ -19,6 +22,7 @@ pub struct CliArgs {
pub fn parse() -> Option<CliArgs> {
let app = App::new(crate_name!())
.version(crate_version!())
.setting(AppSettings::ColoredHelp)
.arg(Arg::with_name("INPUT").help("<PATH> | \"-\" or empty for stdin"))
.arg(Arg::with_name("OUTPUT").help("<PATH> | \"-\" or empty for stdout"))
.arg(
@ -32,6 +36,7 @@ pub fn parse() -> Option<CliArgs> {
.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<CliArgs> {
.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<CliArgs> {
}
};
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<CliArgs> {
Some(Box::new(File::create(s).unwrap()) as Box<dyn Write>)
}
})
.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<CliArgs> {
password,
force_encrypt: app.is_present("force-encrypt"),
argon2_params: params,
cipher,
block_size,
reader: input,
writer: output?,
writer: output,
})
}

View File

@ -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<u8>,
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<W: 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<R: Read>(reader: &mut R) -> io::Result<Self> {
pub fn read<R: Read>(reader: &mut R) -> io::Result<Option<Self>> {
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<dyn StreamCipher>,
hmac: Hmac<blake3::Hasher>,
buffer: Vec<u8>,
}
impl Cipher {
impl DobyCipher {
pub fn new(password: &[u8], params: &EncryptionParams) -> Result<Self, argon2::Error> {
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, &params.nonce).unwrap(),
Ok(Self {
cipher: match params.cipher {
CipherAlgorithm::AesCtr => Box::new(Aes256Ctr::new_from_slices(&encryption_key, &params.nonce).unwrap()),
CipherAlgorithm::XChaCha20 => Box::new(XChaCha20::new_from_slices(&encryption_key, &params.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

View File

@ -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<R: Read, W: Write>(reader: &mut R, writer: &mut W, params: &EncryptionParams, mut cipher: Cipher, block_size: usize, already_read: Option<Vec<u8>>) -> io::Result<()> {
pub fn encrypt<R: Read, W: Write>(reader: &mut R, writer: &mut W, params: &EncryptionParams, mut cipher: DobyCipher, block_size: usize, already_read: Option<Vec<u8>>) -> io::Result<()> {
writer.write_all(MAGIC_BYTES)?;
params.write(writer)?;
let mut buff = vec![0; block_size];
@ -30,7 +30,7 @@ pub fn encrypt<R: Read, W: Write>(reader: &mut R, writer: &mut W, params: &Encry
Ok(())
}
pub fn decrypt<R: Read, W: Write>(reader: &mut R, writer: &mut W, mut cipher: Cipher, block_size: usize) -> io::Result<bool> {
pub fn decrypt<R: Read, W: Write>(reader: &mut R, writer: &mut W, mut cipher: DobyCipher, block_size: usize) -> io::Result<bool> {
let mut buff = vec![0; block_size];
loop {
let n = cipher.decrypt_chunk(reader, &mut buff)?;

View File

@ -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(), &params) {
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(), &params) {
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(), &params) {
let params = EncryptionParams::new(cli_args.argon2_params, cli_args.cipher);
match DobyCipher::new(cli_args.password.as_bytes(), &params) {
Ok(cipher) => {
if let Err(e) = encrypt(
&mut reader,