Ask password just when needed
This commit is contained in:
parent
007d96dedd
commit
6c8a4013cc
|
@ -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, ¶ms).unwrap();
|
||||
let cipher = DobyCipher::new(PASSWORD.into(), ¶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 = DobyCipher::new(PASSWORD, ¶ms).unwrap();
|
||||
let cipher = DobyCipher::new(PASSWORD.into(), ¶ms).unwrap();
|
||||
let t_decrypt = Instant::now();
|
||||
decrypt(&mut reader, &mut writer, cipher, block_size)?;
|
||||
writer.flush()?;
|
||||
|
|
11
src/cli.rs
11
src/cli.rs
|
@ -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,
|
||||
|
|
|
@ -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, ¶ms.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(), ¶ms.password_salt, &[], &mut master_key).zeroize(password)?;
|
||||
let hkdf = Hkdf::<blake3::Hasher>::new(Some(¶ms.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(¶ms.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, ¶ms.nonce).unwrap()),
|
||||
CipherAlgorithm::XChaCha20 => Box::new(XChaCha20::new_from_slices(&encryption_key, ¶ms.nonce).unwrap()),
|
||||
};
|
||||
encryption_key.zeroize();
|
||||
|
||||
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(),
|
||||
})
|
||||
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, ¶ms).unwrap();
|
||||
let mut enc_cipher = DobyCipher::new(password.into(), ¶ms).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, ¶ms).unwrap();
|
||||
let mut dec_cipher = DobyCipher::new(password.into(), ¶ms).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());
|
||||
|
|
27
src/lib.rs
27
src/lib.rs
|
@ -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)?;
|
||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -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(), ¶ms) {
|
||||
match DobyCipher::new(cli_args.password, ¶ms) {
|
||||
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(), ¶ms) {
|
||||
match DobyCipher::new(cli_args.password, ¶ms) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue