Password confirm
This commit is contained in:
parent
382ce3c389
commit
2c8ab7e8ad
@ -6,7 +6,7 @@ use std::{
|
||||
};
|
||||
use doby::{
|
||||
encrypt, decrypt,
|
||||
crypto::{ArgonParams, EncryptionParams, CipherAlgorithm, DobyCipher}
|
||||
crypto::{EncryptionParams, CipherAlgorithm, DobyCipher}
|
||||
};
|
||||
|
||||
const MAX_BLOCK_SIZE: usize = 1_073_741_824; //1GB
|
||||
@ -33,11 +33,10 @@ fn main() -> io::Result<()> {
|
||||
let input = File::open(&args[1])?;
|
||||
let output = OpenOptions::new().create(true).truncate(true).write(true).open(&args[2])?;
|
||||
|
||||
let params = EncryptionParams::new(ArgonParams{
|
||||
t_cost: 1,
|
||||
m_cost: 8,
|
||||
parallelism: 1,
|
||||
}, CipherAlgorithm::AesCtr);
|
||||
let params = EncryptionParams::new(
|
||||
argon2::Params::new(8, 1, 1, None).unwrap(),
|
||||
CipherAlgorithm::AesCtr
|
||||
);
|
||||
|
||||
let mut best_encrypt_time = None;
|
||||
let mut best_encrypt_block_size = None;
|
||||
@ -49,7 +48,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 = DobyCipher::new(PASSWORD.into(), ¶ms).unwrap();
|
||||
let cipher = DobyCipher::new(PASSWORD.as_bytes(), ¶ms);
|
||||
let t_encrypt = Instant::now();
|
||||
encrypt(&mut reader, &mut writer, ¶ms, cipher, block_size, None)?;
|
||||
writer.flush()?;
|
||||
@ -59,7 +58,7 @@ fn main() -> io::Result<()> {
|
||||
reset(&mut reader)?;
|
||||
reset(&mut writer)?;
|
||||
|
||||
let cipher = DobyCipher::new(PASSWORD.into(), ¶ms).unwrap();
|
||||
let cipher = DobyCipher::new(PASSWORD.as_bytes(), ¶ms);
|
||||
let t_decrypt = Instant::now();
|
||||
decrypt(&mut reader, &mut writer, cipher, block_size)?;
|
||||
writer.flush()?;
|
||||
|
@ -10,12 +10,12 @@ fn main() -> io::Result<()> {
|
||||
if magic_bytes == MAGIC_BYTES {
|
||||
match EncryptionParams::read(&mut file)? {
|
||||
Some(params) => {
|
||||
println!("Argon2 time cost: {}", params.argon2.t_cost);
|
||||
println!("Argon2 memory cost: {}KB", params.argon2.m_cost);
|
||||
println!("Argon2 parallelism: {}", params.argon2.parallelism);
|
||||
println!("Argon2 time cost: {}", params.argon2.t_cost());
|
||||
println!("Argon2 memory cost: {}KB", params.argon2.m_cost());
|
||||
println!("Argon2 parallelism cost: {}", params.argon2.p_cost());
|
||||
println!("Encryption cihpher: {}", params.cipher);
|
||||
}
|
||||
None => eprintln!("Invalid cipher")
|
||||
None => eprintln!("Invalid parameters")
|
||||
}
|
||||
} else {
|
||||
eprintln!("Doby format not recognized.");
|
||||
|
37
src/cli.rs
37
src/cli.rs
@ -5,14 +5,14 @@ use std::{
|
||||
io::{stdin, stdout, Read},
|
||||
};
|
||||
use clap::{crate_name, crate_version, App, Arg, AppSettings};
|
||||
use crate::{LazyWriter, Password, crypto::{ArgonParams, CipherAlgorithm}};
|
||||
use crate::{LazyWriter, WrappedPassword, crypto::CipherAlgorithm};
|
||||
|
||||
cpufeatures::new!(aes_ni, "aes");
|
||||
|
||||
pub struct CliArgs {
|
||||
pub password: Password,
|
||||
pub password: WrappedPassword,
|
||||
pub force_encrypt: bool,
|
||||
pub argon2_params: ArgonParams,
|
||||
pub argon2_params: argon2::Params,
|
||||
pub cipher: CipherAlgorithm,
|
||||
pub block_size: usize,
|
||||
pub reader: Box<dyn Read>,
|
||||
@ -34,16 +34,15 @@ pub fn parse() -> Option<CliArgs> {
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("1_password")
|
||||
.short("p")
|
||||
.long("password")
|
||||
.value_name("password")
|
||||
.help("Password used to derive encryption keys")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("2_t_cost")
|
||||
.short("i")
|
||||
.long("iterations")
|
||||
.value_name("iterations")
|
||||
.short("t")
|
||||
.long("time-cost")
|
||||
.value_name("number of iterations")
|
||||
.help("Argon2 time cost")
|
||||
.default_value("10")
|
||||
)
|
||||
@ -56,11 +55,11 @@ pub fn parse() -> Option<CliArgs> {
|
||||
.default_value("4096")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("4_parallelism")
|
||||
.short("t")
|
||||
.long("threads")
|
||||
.value_name("threads")
|
||||
.help("Argon2 parallelism (between 1 and 255)")
|
||||
Arg::with_name("4_p_cost")
|
||||
.short("p")
|
||||
.long("parallelism")
|
||||
.value_name("degree of parallelism")
|
||||
.help("Argon2 parallelism cost")
|
||||
.default_value("4")
|
||||
)
|
||||
.arg(
|
||||
@ -85,14 +84,16 @@ pub fn parse() -> Option<CliArgs> {
|
||||
let params = {
|
||||
let t_cost = number(app.value_of("2_t_cost").unwrap())?;
|
||||
let m_cost = number(app.value_of("3_m_cost").unwrap())?;
|
||||
let parallelism = number(app.value_of("4_parallelism").unwrap())?;
|
||||
let p_cost = number(app.value_of("4_p_cost").unwrap())?;
|
||||
|
||||
ArgonParams {
|
||||
t_cost,
|
||||
m_cost,
|
||||
parallelism,
|
||||
match argon2::Params::new(m_cost, t_cost, p_cost, None) {
|
||||
Ok(params) => Some(params),
|
||||
Err(e) => {
|
||||
eprintln!("Invalid Argon2 parameters: {}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
}?;
|
||||
|
||||
let cipher = app
|
||||
.value_of("cipher")
|
||||
|
156
src/crypto.rs
156
src/crypto.rs
@ -1,4 +1,4 @@
|
||||
use std::{convert::{TryFrom, TryInto}, fmt::{self, Display, Formatter}, io::{self, Read, Write}};
|
||||
use std::{convert::TryFrom, fmt::{self, Display, Formatter}, io::{self, Read, Write}};
|
||||
use num_enum::TryFromPrimitive;
|
||||
use chacha20::XChaCha20;
|
||||
use aes::{Aes256Ctr, cipher::{NewCipher, StreamCipher}};
|
||||
@ -7,7 +7,6 @@ use rand::{Rng, rngs::OsRng};
|
||||
use argon2::{Argon2, Version, Algorithm};
|
||||
use hkdf::Hkdf;
|
||||
use zeroize::Zeroize;
|
||||
use crate::Password;
|
||||
|
||||
pub const SALT_LEN: usize = 64;
|
||||
const AES_NONCE_LEN: usize = 16;
|
||||
@ -15,20 +14,6 @@ const XCHACHA20_NONCE_LEN: usize = 24;
|
||||
pub const HASH_LEN: usize = 64;
|
||||
const KEY_LEN: usize = 32;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct ArgonParams {
|
||||
pub t_cost: u32,
|
||||
pub m_cost: u32,
|
||||
pub parallelism: u8,
|
||||
}
|
||||
|
||||
impl TryFrom<ArgonParams> for argon2::Params {
|
||||
type Error = argon2::Error;
|
||||
fn try_from(params: ArgonParams) -> Result<Self, Self::Error> {
|
||||
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 {
|
||||
@ -57,14 +42,14 @@ impl Display for CipherAlgorithm {
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct EncryptionParams {
|
||||
salt: [u8; SALT_LEN],
|
||||
pub argon2: ArgonParams,
|
||||
pub argon2: argon2::Params,
|
||||
pub cipher: CipherAlgorithm,
|
||||
}
|
||||
|
||||
impl EncryptionParams {
|
||||
pub const LEN: usize = SALT_LEN + 4*2 + 2;
|
||||
pub const LEN: usize = SALT_LEN + 4*3 + 1;
|
||||
|
||||
pub fn new(argon2_params: ArgonParams, cipher: CipherAlgorithm) -> EncryptionParams {
|
||||
pub fn new(argon2_params: argon2::Params, cipher: CipherAlgorithm) -> EncryptionParams {
|
||||
let mut salt = [0; SALT_LEN];
|
||||
OsRng.fill(&mut salt);
|
||||
EncryptionParams {
|
||||
@ -76,9 +61,9 @@ impl EncryptionParams {
|
||||
|
||||
pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||
writer.write_all(&self.salt)?;
|
||||
writer.write_all(&self.argon2.t_cost.to_be_bytes())?;
|
||||
writer.write_all(&self.argon2.m_cost.to_be_bytes())?;
|
||||
writer.write_all(&self.argon2.parallelism.to_be_bytes())?;
|
||||
writer.write_all(&self.argon2.t_cost().to_be_bytes())?;
|
||||
writer.write_all(&self.argon2.m_cost().to_be_bytes())?;
|
||||
writer.write_all(&self.argon2.p_cost().to_be_bytes())?;
|
||||
writer.write_all(&(self.cipher as u8).to_be_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
@ -90,37 +75,25 @@ impl EncryptionParams {
|
||||
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 p_cost = [0; 4];
|
||||
reader.read_exact(&mut p_cost)?;
|
||||
let mut cipher_buff = [0; 1];
|
||||
reader.read_exact(&mut cipher_buff)?;
|
||||
match CipherAlgorithm::try_from(cipher_buff[0]) {
|
||||
Ok(cipher) => {
|
||||
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 {
|
||||
if let Ok(cipher) = CipherAlgorithm::try_from(cipher_buff[0]) {
|
||||
if let Ok(argon2_params) = argon2::Params::new(
|
||||
u32::from_be_bytes(m_cost),
|
||||
u32::from_be_bytes(t_cost),
|
||||
u32::from_be_bytes(p_cost),
|
||||
None
|
||||
) {
|
||||
return Ok(Some(EncryptionParams {
|
||||
salt,
|
||||
argon2: argon2_params,
|
||||
cipher,
|
||||
}))
|
||||
}));
|
||||
}
|
||||
Err(_) => Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait ThenZeroize {
|
||||
fn zeroize<T: Zeroize>(self, v: T) -> Self;
|
||||
}
|
||||
|
||||
impl<S, E> ThenZeroize for Result<S, E> {
|
||||
fn zeroize<T: Zeroize>(self, mut v: T) -> Self {
|
||||
v.zeroize();
|
||||
self
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,44 +104,35 @@ pub struct DobyCipher {
|
||||
}
|
||||
|
||||
impl DobyCipher {
|
||||
pub fn new(mut password: Password, params: &EncryptionParams) -> Result<Self, argon2::Error> {
|
||||
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(password.as_bytes(), ¶ms.salt, &mut master_key).zeroize(password)?;
|
||||
let hkdf = Hkdf::<blake2::Blake2b>::new(Some(¶ms.salt), &master_key);
|
||||
master_key.zeroize();
|
||||
let mut nonce = vec![0; params.cipher.get_nonce_size()];
|
||||
hkdf.expand(b"doby_nonce", &mut nonce).unwrap();
|
||||
let mut encryption_key = [0; KEY_LEN];
|
||||
hkdf.expand(b"doby_encryption_key", &mut encryption_key).unwrap();
|
||||
let mut authentication_key = [0; KEY_LEN];
|
||||
hkdf.expand(b"doby_authentication_key", &mut authentication_key).unwrap();
|
||||
pub fn new(password: &[u8], params: &EncryptionParams) -> Self {
|
||||
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params.argon2.clone());
|
||||
let mut master_key = [0; KEY_LEN];
|
||||
argon2.hash_password_into(password, ¶ms.salt, &mut master_key).unwrap();
|
||||
let hkdf = Hkdf::<blake2::Blake2b>::new(Some(¶ms.salt), &master_key);
|
||||
master_key.zeroize();
|
||||
let mut nonce = vec![0; params.cipher.get_nonce_size()];
|
||||
hkdf.expand(b"doby_nonce", &mut nonce).unwrap();
|
||||
let mut encryption_key = [0; KEY_LEN];
|
||||
hkdf.expand(b"doby_encryption_key", &mut encryption_key).unwrap();
|
||||
let mut authentication_key = [0; KEY_LEN];
|
||||
hkdf.expand(b"doby_authentication_key", &mut authentication_key).unwrap();
|
||||
|
||||
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();
|
||||
hmac.update(&encoded_params);
|
||||
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();
|
||||
hmac.update(&encoded_params);
|
||||
|
||||
let cipher: Box<dyn StreamCipher> = match params.cipher {
|
||||
CipherAlgorithm::AesCtr => Box::new(Aes256Ctr::new_from_slices(&encryption_key, &nonce).unwrap()),
|
||||
CipherAlgorithm::XChaCha20 => Box::new(XChaCha20::new_from_slices(&encryption_key, &nonce).unwrap()),
|
||||
};
|
||||
encryption_key.zeroize();
|
||||
let cipher: Box<dyn StreamCipher> = match params.cipher {
|
||||
CipherAlgorithm::AesCtr => Box::new(Aes256Ctr::new_from_slices(&encryption_key, &nonce).unwrap()),
|
||||
CipherAlgorithm::XChaCha20 => Box::new(XChaCha20::new_from_slices(&encryption_key, &nonce).unwrap()),
|
||||
};
|
||||
encryption_key.zeroize();
|
||||
|
||||
Ok(Self {
|
||||
cipher,
|
||||
hmac,
|
||||
buffer: Vec::new(),
|
||||
})
|
||||
}
|
||||
Err(e) => {
|
||||
password.zeroize();
|
||||
Err(e)
|
||||
}
|
||||
Self {
|
||||
cipher,
|
||||
hmac,
|
||||
buffer: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,24 +173,23 @@ impl DobyCipher {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{ArgonParams, CipherAlgorithm, EncryptionParams, DobyCipher, HASH_LEN};
|
||||
use super::{CipherAlgorithm, EncryptionParams, DobyCipher, HASH_LEN};
|
||||
#[test]
|
||||
fn encryption_params() {
|
||||
let params = EncryptionParams::new(ArgonParams {
|
||||
t_cost: 1,
|
||||
m_cost: 8,
|
||||
parallelism: 1,
|
||||
}, CipherAlgorithm::XChaCha20);
|
||||
let params = EncryptionParams::new(
|
||||
argon2::Params::new(8, 1, 1, None).unwrap(),
|
||||
CipherAlgorithm::XChaCha20
|
||||
);
|
||||
|
||||
assert_eq!(EncryptionParams::LEN, 74);
|
||||
assert_eq!(EncryptionParams::LEN, 77);
|
||||
|
||||
let mut buff = Vec::with_capacity(74);
|
||||
params.write(&mut buff).unwrap();
|
||||
assert_eq!(buff[..64], params.salt);
|
||||
assert_eq!(buff[64..68], vec![0, 0, 0, 0x01]); //t_cost
|
||||
assert_eq!(buff[68..72], vec![0, 0, 0, 0x08]); //m_cost
|
||||
assert_eq!(buff[72], 0x01); //parallelism
|
||||
assert_eq!(buff[73], CipherAlgorithm::XChaCha20 as u8);
|
||||
assert_eq!(buff[72..76], vec![0, 0, 0, 0x01]); //p_cost
|
||||
assert_eq!(buff[76], CipherAlgorithm::XChaCha20 as u8);
|
||||
|
||||
let new_params = EncryptionParams::read(&mut buff.as_slice()).unwrap().unwrap();
|
||||
assert_eq!(new_params, params);
|
||||
@ -234,24 +197,23 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn doby_cipher() {
|
||||
let params = EncryptionParams::new(ArgonParams {
|
||||
t_cost: 1,
|
||||
m_cost: 8,
|
||||
parallelism: 1,
|
||||
}, CipherAlgorithm::AesCtr);
|
||||
let params = EncryptionParams::new(
|
||||
argon2::Params::new(8, 1, 1, None).unwrap(),
|
||||
CipherAlgorithm::AesCtr
|
||||
);
|
||||
let password = "I like spaghetti";
|
||||
let plaintext = b"but I love so much to listen to HARDCORE music on big subwoofer";
|
||||
let mut buff: [u8; 63] = *plaintext;
|
||||
let mut vec = Vec::with_capacity(buff.len()+HASH_LEN);
|
||||
|
||||
let mut enc_cipher = DobyCipher::new(password.into(), ¶ms).unwrap();
|
||||
let mut enc_cipher = DobyCipher::new(password.as_bytes(), ¶ms);
|
||||
enc_cipher.encrypt_chunk(&mut buff, &mut vec).unwrap();
|
||||
assert_ne!(buff, *plaintext);
|
||||
assert_eq!(buff, vec.as_slice());
|
||||
assert_eq!(enc_cipher.write_hmac(&mut vec).unwrap(), HASH_LEN);
|
||||
assert_eq!(vec.len(), buff.len()+HASH_LEN);
|
||||
|
||||
let mut dec_cipher = DobyCipher::new(password.into(), ¶ms).unwrap();
|
||||
let mut dec_cipher = DobyCipher::new(password.as_bytes(), ¶ms);
|
||||
let mut decrypted = vec![0; buff.len()+HASH_LEN];
|
||||
let mut n = dec_cipher.decrypt_chunk(&mut vec.as_slice(), &mut decrypted[..]).unwrap();
|
||||
assert_eq!(n, buff.len());
|
||||
|
38
src/lib.rs
38
src/lib.rs
@ -7,32 +7,36 @@ use zeroize::Zeroize;
|
||||
|
||||
pub const MAGIC_BYTES: &[u8; 4] = b"DOBY";
|
||||
|
||||
pub struct Password(Option<String>);
|
||||
pub struct WrappedPassword(Option<String>);
|
||||
|
||||
impl Password {
|
||||
fn unwrap_or_ask(self) -> String {
|
||||
self.0.unwrap_or_else(|| rpassword::read_password_from_tty(Some("Password: ")).unwrap())
|
||||
impl WrappedPassword {
|
||||
pub fn get(self, ask_confirm: bool) -> Option<String> {
|
||||
self.0.or_else(|| {
|
||||
let mut password = rpassword::read_password_from_tty(Some("Password: ")).ok()?;
|
||||
if ask_confirm {
|
||||
let mut password_confirm = rpassword::read_password_from_tty(Some("Password (confirm): ")).ok()?;
|
||||
if password == password_confirm {
|
||||
password_confirm.zeroize();
|
||||
Some(password)
|
||||
} else {
|
||||
password.zeroize();
|
||||
password_confirm.zeroize();
|
||||
eprintln!("Passwords don't match");
|
||||
None
|
||||
}
|
||||
} else {
|
||||
Some(password)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<&str>> for Password {
|
||||
impl From<Option<&str>> for WrappedPassword {
|
||||
fn from(s: Option<&str>) -> Self {
|
||||
Self(s.map(String::from))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Password {
|
||||
fn from(s: &str) -> Self {
|
||||
Some(s).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Zeroize for Password {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LazyWriter<P: AsRef<Path>> {
|
||||
path: Option<P>,
|
||||
writer: Option<Box<dyn Write>>,
|
||||
|
51
src/main.rs
51
src/main.rs
@ -6,6 +6,7 @@ use doby::{
|
||||
decrypt,
|
||||
encrypt,
|
||||
};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
fn run() -> bool {
|
||||
let mut success = false;
|
||||
@ -21,44 +22,42 @@ fn run() -> bool {
|
||||
Ok(params) => {
|
||||
match params {
|
||||
Some(params) => {
|
||||
match DobyCipher::new(cli_args.password, ¶ms) {
|
||||
Ok(cipher) => {
|
||||
match decrypt(&mut reader, &mut writer, cipher, cli_args.block_size) {
|
||||
Ok(verified) => {
|
||||
if verified {
|
||||
success = true
|
||||
} else {
|
||||
eprintln!("WARNING: HMAC verification failed !\nEither your password is incorrect or the ciphertext has been corrupted.\nBe careful, the data could have been altered by an attacker.");
|
||||
}
|
||||
if let Some(mut password) = cli_args.password.get(false) {
|
||||
let cipher = DobyCipher::new(password.as_bytes(), ¶ms);
|
||||
password.zeroize();
|
||||
match decrypt(&mut reader, &mut writer, cipher, cli_args.block_size) {
|
||||
Ok(verified) => {
|
||||
if verified {
|
||||
success = true
|
||||
} else {
|
||||
eprintln!("WARNING: HMAC verification failed !\nEither your password is incorrect or the ciphertext has been corrupted.\nBe careful, the data could have been altered 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)
|
||||
}
|
||||
}
|
||||
None => eprintln!("Invalid cipher")
|
||||
None => eprintln!("Invalid parameters")
|
||||
}
|
||||
}
|
||||
Err(e) => eprintln!("I/O error while reading headers: {}", e)
|
||||
}
|
||||
} else { //otherwise, encrypt
|
||||
let params = EncryptionParams::new(cli_args.argon2_params, cli_args.cipher);
|
||||
match DobyCipher::new(cli_args.password, ¶ms) {
|
||||
Ok(cipher) => {
|
||||
match encrypt(
|
||||
&mut reader,
|
||||
&mut writer,
|
||||
¶ms,
|
||||
cipher,
|
||||
cli_args.block_size,
|
||||
Some(&magic_bytes[..n])
|
||||
) {
|
||||
Ok(_) => success = true,
|
||||
Err(e) => eprintln!("I/O error while encrypting: {}", e)
|
||||
}
|
||||
if let Some(mut password) = cli_args.password.get(true) {
|
||||
let cipher = DobyCipher::new(password.as_bytes(), ¶ms);
|
||||
password.zeroize();
|
||||
match encrypt(
|
||||
&mut reader,
|
||||
&mut writer,
|
||||
¶ms,
|
||||
cipher,
|
||||
cli_args.block_size,
|
||||
Some(&magic_bytes[..n])
|
||||
) {
|
||||
Ok(_) => success = true,
|
||||
Err(e) => eprintln!("I/O error while encrypting: {}", e)
|
||||
}
|
||||
Err(e) => eprintln!("Invalid argon2 params: {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
use rand::Rng;
|
||||
use doby::{
|
||||
crypto::{
|
||||
ArgonParams,
|
||||
CipherAlgorithm,
|
||||
EncryptionParams,
|
||||
DobyCipher,
|
||||
@ -19,15 +18,14 @@ fn different_elements<T: Eq>(v1: &Vec<T>, v2: &Vec<T>) -> usize {
|
||||
fn authentication() {
|
||||
const BLOCK_SIZE: usize = 65536;
|
||||
const PLAINTEXT: &[u8; 13] = b"the plaintext";
|
||||
const CIPHERTEXT_SIZE: usize = PLAINTEXT.len()+142;
|
||||
const CIPHERTEXT_SIZE: usize = PLAINTEXT.len()+145;
|
||||
const PASSWORD: &str = "the password";
|
||||
let params = EncryptionParams::new(ArgonParams {
|
||||
t_cost: 1,
|
||||
m_cost: 8,
|
||||
parallelism: 1,
|
||||
}, CipherAlgorithm::AesCtr);
|
||||
let params = EncryptionParams::new(
|
||||
argon2::Params::new(8, 1, 1, None).unwrap(),
|
||||
CipherAlgorithm::AesCtr
|
||||
);
|
||||
|
||||
let encrypter = DobyCipher::new(PASSWORD.into(), ¶ms).unwrap();
|
||||
let encrypter = DobyCipher::new(PASSWORD.as_bytes(), ¶ms);
|
||||
let mut ciphertext = Vec::with_capacity(CIPHERTEXT_SIZE);
|
||||
encrypt(&mut &PLAINTEXT[..], &mut ciphertext, ¶ms, encrypter, BLOCK_SIZE, None).unwrap();
|
||||
assert_eq!(ciphertext.len(), CIPHERTEXT_SIZE);
|
||||
@ -38,13 +36,13 @@ fn authentication() {
|
||||
compromised[i] = rand::thread_rng().gen();
|
||||
}
|
||||
assert_eq!(different_elements(&compromised, &ciphertext), 1);
|
||||
let decrypter = DobyCipher::new(PASSWORD.into(), ¶ms).unwrap();
|
||||
let decrypter = DobyCipher::new(PASSWORD.as_bytes(), ¶ms);
|
||||
let mut decrypted = Vec::with_capacity(PLAINTEXT.len());
|
||||
let verified = decrypt(&mut &compromised[..], &mut decrypted, decrypter, BLOCK_SIZE).unwrap();
|
||||
assert_eq!(verified, false);
|
||||
}
|
||||
|
||||
let decrypter = DobyCipher::new(PASSWORD.into(), ¶ms).unwrap();
|
||||
let decrypter = DobyCipher::new(PASSWORD.as_bytes(), ¶ms);
|
||||
let mut decrypted = Vec::with_capacity(PLAINTEXT.len());
|
||||
let verified = decrypt(&mut &ciphertext[4+EncryptionParams::LEN..], &mut decrypted, decrypter, BLOCK_SIZE).unwrap();
|
||||
assert_eq!(decrypted, PLAINTEXT);
|
||||
|
23
tests/cli.rs
23
tests/cli.rs
@ -22,7 +22,7 @@ fn setup_files<'a>() -> io::Result<(PathBuf, PathBuf, PathBuf)> {
|
||||
|
||||
fn doby_cmd() -> Result<Command, CargoError> {
|
||||
let mut cmd = Command::cargo_bin("doby")?;
|
||||
cmd.arg("-p").arg(PASSWORD);
|
||||
cmd.arg("--password").arg(PASSWORD);
|
||||
Ok(cmd)
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ fn files() -> io::Result<()> {
|
||||
fn stdout() -> io::Result<()> {
|
||||
let (_, tmp_plaintext, tmp_ciphertext) = setup_files()?;
|
||||
|
||||
let shell_cmd = format!("{} -p \"{}\" {} > {}", cargo_bin("doby").to_str().unwrap(), PASSWORD, tmp_plaintext.to_str().unwrap(), tmp_ciphertext.to_str().unwrap());
|
||||
let shell_cmd = format!("{} --password \"{}\" {} > {}", cargo_bin("doby").to_str().unwrap(), PASSWORD, tmp_plaintext.to_str().unwrap(), tmp_ciphertext.to_str().unwrap());
|
||||
bash_cmd().arg(shell_cmd).assert().success().stdout("").stderr("");
|
||||
|
||||
doby_cmd().unwrap().arg(tmp_ciphertext).assert().success().stdout(PLAINTEXT);
|
||||
@ -64,10 +64,10 @@ fn stdout() -> io::Result<()> {
|
||||
fn stdin() -> io::Result<()> {
|
||||
let (_, tmp_plaintext, tmp_ciphertext) = setup_files()?;
|
||||
|
||||
let mut shell_cmd = format!("cat {} | {} -p \"{}\" - {}", tmp_plaintext.to_str().unwrap(), cargo_bin("doby").to_str().unwrap(), PASSWORD, tmp_ciphertext.to_str().unwrap());
|
||||
let mut shell_cmd = format!("cat {} | {} --password \"{}\" - {}", tmp_plaintext.to_str().unwrap(), cargo_bin("doby").to_str().unwrap(), PASSWORD, tmp_ciphertext.to_str().unwrap());
|
||||
bash_cmd().arg(shell_cmd).assert().success().stdout("").stderr("");
|
||||
|
||||
shell_cmd = format!("cat {} | {} -p \"{}\"", tmp_ciphertext.to_str().unwrap(), cargo_bin("doby").to_str().unwrap(), PASSWORD);
|
||||
shell_cmd = format!("cat {} | {} --password \"{}\"", tmp_ciphertext.to_str().unwrap(), cargo_bin("doby").to_str().unwrap(), PASSWORD);
|
||||
bash_cmd().arg(shell_cmd).assert().success().stdout(PLAINTEXT);
|
||||
|
||||
Ok(())
|
||||
@ -107,8 +107,8 @@ fn test_cipher(cipher_str: &str, cipher_algorithm: CipherAlgorithm) -> io::Resul
|
||||
doby_cmd().unwrap().arg("-c").arg(cipher_str).arg(tmp_plaintext).arg(&tmp_ciphertext).assert().success().stdout("").stderr("");
|
||||
|
||||
let ciphertext = fs::read(&tmp_ciphertext)?;
|
||||
assert_eq!(ciphertext[4+SALT_LEN+4*2+1], cipher_algorithm as u8);
|
||||
assert_eq!(ciphertext.len(), PLAINTEXT.len()+14+SALT_LEN+HASH_LEN);
|
||||
assert_eq!(ciphertext[4+SALT_LEN+4*3], cipher_algorithm as u8);
|
||||
assert_eq!(ciphertext.len(), PLAINTEXT.len()+17+SALT_LEN+HASH_LEN);
|
||||
|
||||
doby_cmd().unwrap().arg(tmp_ciphertext).assert().success().stdout(PLAINTEXT).stderr("");
|
||||
|
||||
@ -127,17 +127,16 @@ fn aes_cipher() -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
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: not enough threads\n");
|
||||
Command::cargo_bin("doby").unwrap().arg("-t").arg("0").assert().failure().stderr("Invalid Argon2 parameters: time cost is too small\n");
|
||||
Command::cargo_bin("doby").unwrap().arg("-m").arg("0").assert().failure().stderr("Invalid Argon2 parameters: memory cost is too small\n");
|
||||
Command::cargo_bin("doby").unwrap().arg("-p").arg("0").assert().failure().stderr("Invalid Argon2 parameters: 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();
|
||||
let ciphertext = doby_cmd().unwrap().arg("-t").arg("8").arg("-m").arg("2048").arg("-p").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
|
||||
assert_eq!(u32::from_be_bytes(ciphertext[4+SALT_LEN+4..4+SALT_LEN+8].try_into().unwrap()), 2048); //memory cost
|
||||
assert_eq!(u8::from_be_bytes([ciphertext[4+SALT_LEN+8]]), 8); //parallelism
|
||||
assert_eq!(u32::from_be_bytes(ciphertext[4+SALT_LEN+8..4+SALT_LEN+12].try_into().unwrap()), 8); //parallelism
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue
Block a user