Ask password just when needed

This commit is contained in:
Matéo Duparc 2021-07-04 16:24:44 +02:00
parent 007d96dedd
commit 6c8a4013cc
Signed by: hardcoresushi
GPG Key ID: 007F84120107191E
5 changed files with 85 additions and 42 deletions

View File

@ -10,7 +10,7 @@ use doby::{
};
const MAX_BLOCK_SIZE: usize = 1_073_741_824; //1GB
const PASSWORD: &[u8] = b"HARDCORE music is the best music of all time";
const PASSWORD: &str = "HARDCORE music is the best music of all time";
fn set_if_better(best_time: &mut Option<u128>, time: u128, best_block_size: &mut Option<usize>, block_size: usize) {
let mut better = true;
@ -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 = DobyCipher::new(PASSWORD, &params).unwrap();
let cipher = DobyCipher::new(PASSWORD.into(), &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 = DobyCipher::new(PASSWORD, &params).unwrap();
let cipher = DobyCipher::new(PASSWORD.into(), &params).unwrap();
let t_decrypt = Instant::now();
decrypt(&mut reader, &mut writer, cipher, block_size)?;
writer.flush()?;

View File

@ -5,12 +5,12 @@ use std::{
io::{stdin, stdout, Read, Write},
};
use clap::{crate_name, crate_version, App, Arg, AppSettings};
use crate::crypto::{ArgonParams, CipherAlgorithm};
use crate::{Password, crypto::{ArgonParams, CipherAlgorithm}};
cpufeatures::new!(aes_ni, "aes");
pub struct CliArgs {
pub password: String,
pub password: Password,
pub force_encrypt: bool,
pub argon2_params: ArgonParams,
pub cipher: CipherAlgorithm,
@ -130,13 +130,8 @@ pub fn parse() -> Option<CliArgs> {
})
.unwrap_or_else(|| Some(Box::new(stdout())))?;
let password = match app.value_of("1_password") {
Some(s) => s.to_string(),
None => rpassword::read_password_from_tty(Some("Password: ")).unwrap(),
};
Some(CliArgs {
password,
password: app.value_of("1_password").into(),
force_encrypt: app.is_present("force-encrypt"),
argon2_params: params,
cipher,

View File

@ -11,6 +11,7 @@ use rand::{Rng, rngs::OsRng};
use argon2::{Argon2, Version, Algorithm};
use hkdf::Hkdf;
use zeroize::Zeroize;
use crate::Password;
const SALT_LEN: usize = 64;
const AES_NONCE_LEN: usize = 16;
@ -126,6 +127,17 @@ impl EncryptionParams {
}
}
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
}
}
pub struct DobyCipher {
cipher: Box<dyn StreamCipher>,
hmac: Hmac<blake3::Hasher>,
@ -133,31 +145,42 @@ pub struct DobyCipher {
}
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)?;
let mut master_key = [0; KEY_LEN];
argon.hash_password_into(Algorithm::Argon2id, password, &params.password_salt, &[], &mut master_key)?;
pub fn new(mut password: Password, params: &EncryptionParams) -> Result<Self, argon2::Error> {
match Argon2::new(None, params.argon2.t_cost, params.argon2.m_cost, params.argon2.parallelism.into(), Version::V0x13) {
Ok(argon2) => {
let mut master_key = [0; KEY_LEN];
let password = password.unwrap_or_ask();
argon2.hash_password_into(Algorithm::Argon2id, password.as_bytes(), &params.password_salt, &[], &mut master_key).zeroize(password)?;
let hkdf = Hkdf::<blake3::Hasher>::new(Some(&params.hkdf_salt), &master_key);
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();
master_key.zeroize();
let hkdf = Hkdf::<blake3::Hasher>::new(Some(&params.hkdf_salt), &master_key);
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();
master_key.zeroize();
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();
authentication_key.zeroize();
hmac.update(&encoded_params);
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);
let cipher: Box<dyn StreamCipher> = 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()),
};
encryption_key.zeroize();
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(),
})
Ok(Self {
cipher,
hmac,
buffer: Vec::new(),
})
}
Err(e) => {
password.zeroize();
Err(e)
}
}
}
pub fn encrypt_chunk<W: Write>(&mut self, buff: &mut [u8], writer: &mut W) -> io::Result<()> {
@ -229,19 +252,19 @@ mod tests {
m_cost: 8,
parallelism: 1,
}, CipherAlgorithm::AesCtr);
let password = b"I like spaghetti";
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, &params).unwrap();
let mut enc_cipher = DobyCipher::new(password.into(), &params).unwrap();
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, &params).unwrap();
let mut dec_cipher = DobyCipher::new(password.into(), &params).unwrap();
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());

View File

@ -3,9 +3,36 @@ pub mod crypto;
use std::io::{self, Read, Write};
use crypto::{DobyCipher, EncryptionParams};
use zeroize::Zeroize;
pub const MAGIC_BYTES: &[u8; 4] = b"DOBY";
pub struct Password(Option<String>);
impl Password {
fn unwrap_or_ask(self) -> String {
self.0.unwrap_or_else(|| rpassword::read_password_from_tty(Some("Password: ")).unwrap())
}
}
impl From<Option<&str>> for Password {
fn from(s: Option<&str>) -> Self {
Self(s.map(|s| String::from(s)))
}
}
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 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)?;

View File

@ -1,5 +1,4 @@
use std::io::{BufWriter, BufReader, Read};
use zeroize::Zeroize;
use doby::{
cli,
crypto::{EncryptionParams, DobyCipher},
@ -9,10 +8,10 @@ use doby::{
};
fn main() {
if let Some(mut cli_args) = cli::parse() {
if let Some(cli_args) = cli::parse() {
let mut reader = BufReader::with_capacity(cli_args.block_size, cli_args.reader);
let mut writer = BufWriter::with_capacity(cli_args.block_size, cli_args.writer);
let mut magic_bytes = vec![0; MAGIC_BYTES.len()];
match reader.read(&mut magic_bytes) {
Ok(n) => {
@ -24,7 +23,7 @@ fn main() {
Ok(params) => {
match params {
Some(params) => {
match DobyCipher::new(cli_args.password.as_bytes(), &params) {
match DobyCipher::new(cli_args.password, &params) {
Ok(cipher) => {
match decrypt(&mut reader, &mut writer, cipher, cli_args.block_size) {
Ok(verified) => {
@ -45,7 +44,7 @@ fn main() {
}
} else { //otherwise, encrypt
let params = EncryptionParams::new(cli_args.argon2_params, cli_args.cipher);
match DobyCipher::new(cli_args.password.as_bytes(), &params) {
match DobyCipher::new(cli_args.password, &params) {
Ok(cipher) => {
if let Err(e) = encrypt(
&mut reader,
@ -64,6 +63,5 @@ fn main() {
}
Err(e) => eprintln!("I/O error while reading magic bytes: {}", e),
}
cli_args.password.zeroize();
}
}