117 lines
3.3 KiB
Rust

use std::fmt::{Debug, Formatter};
use argon2::{
Argon2,
Algorithm,
Version,
ParamsBuilder,
password_hash::rand_core::{RngCore, OsRng},
};
use chacha20poly1305::{
XChaCha20Poly1305,
XNonce,
aead::{
Aead,
AeadCore,
KeyInit,
generic_array::GenericArray,
},
};
use crate::errors::*;
#[derive(Clone)]
pub struct Crypto {
cipher: XChaCha20Poly1305,
}
impl Crypto {
/// Argon2 params rationale:
///
/// m_cost is measured in KiB, so 128 * 1024 gives us 128MiB.
/// This should roughly double the memory usage of the application
/// while deriving the key.
///
/// p_cost is irrelevant since (at present) there isn't any parallelism
/// implemented, so we leave it at 1.
///
/// With the above m_cost, t_cost = 8 results in about 800ms to derive
/// a key on my (somewhat older) CPU. This is probably overkill, but
/// given that it should only have to happen ~once a day for most
/// usage, it should be acceptable.
#[cfg(not(debug_assertions))]
const MEM_COST: u32 = 128 * 1024;
#[cfg(not(debug_assertions))]
const TIME_COST: u32 = 8;
/// But since this takes a million years without optimizations,
/// we turn it way down in debug builds.
#[cfg(debug_assertions)]
const MEM_COST: u32 = 48 * 1024;
#[cfg(debug_assertions)]
const TIME_COST: u32 = 1;
pub fn new(passphrase: &str, salt: &[u8]) -> argon2::Result<Crypto> {
let params = ParamsBuilder::new()
.m_cost(Self::MEM_COST)
.p_cost(1)
.t_cost(Self::TIME_COST)
.build()
.unwrap(); // only errors if the given params are invalid
let hasher = Argon2::new(
Algorithm::Argon2id,
Version::V0x13,
params,
);
let mut key = [0; 32];
hasher.hash_password_into(passphrase.as_bytes(), &salt, &mut key)?;
let cipher = XChaCha20Poly1305::new(GenericArray::from_slice(&key));
Ok(Crypto { cipher })
}
#[cfg(test)]
pub fn random() -> Crypto {
// salt and key are the same length, so we can just use this
let key = Crypto::salt();
let cipher = XChaCha20Poly1305::new(GenericArray::from_slice(&key));
Crypto { cipher }
}
#[cfg(test)]
pub fn fixed() -> Crypto {
let key = [
1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
];
let cipher = XChaCha20Poly1305::new(GenericArray::from_slice(&key));
Crypto { cipher }
}
pub fn salt() -> [u8; 32] {
let mut salt = [0; 32];
OsRng.fill_bytes(&mut salt);
salt
}
pub fn encrypt(&self, data: &[u8]) -> Result<(XNonce, Vec<u8>), CryptoError> {
let nonce = XChaCha20Poly1305::generate_nonce(&mut OsRng);
let ciphertext = self.cipher.encrypt(&nonce, data)?;
Ok((nonce, ciphertext))
}
pub fn decrypt(&self, nonce: &XNonce, data: &[u8]) -> Result<Vec<u8>, CryptoError> {
let plaintext = self.cipher.decrypt(nonce, data)?;
Ok(plaintext)
}
}
impl Debug for Crypto {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "Crypto {{ [...] }}")
}
}