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 { 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), 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, 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 {{ [...] }}") } }