1pub mod envs;
2
3use std::fs;
4use std::io::Write;
5use std::path::PathBuf;
6
7use anyhow::{Result, bail, format_err};
8use argon2::password_hash::SaltString;
9use argon2::{Argon2, Params};
10use rand::Rng;
11use rand::rngs::OsRng;
12use ring::aead::Nonce;
13pub use ring::aead::{Aad, LessSafeKey, NONCE_LEN, UnboundKey};
14
15use crate::envs::FM_TEST_FAST_WEAK_CRYPTO_ENV;
16
17pub fn get_random_nonce() -> ring::aead::Nonce {
19 Nonce::assume_unique_for_key(OsRng.r#gen())
20}
21
22pub fn encrypt(mut plaintext: Vec<u8>, key: &LessSafeKey) -> Result<Vec<u8>> {
26 let nonce = get_random_nonce();
27 let mut ciphertext: Vec<u8> = nonce.as_ref().to_vec();
29
30 key.seal_in_place_append_tag(nonce, Aad::empty(), &mut plaintext)
31 .map_err(|_| anyhow::format_err!("Encryption failed due to unspecified aead error"))?;
32
33 ciphertext.append(&mut plaintext);
34
35 Ok(ciphertext)
36}
37
38pub fn decrypt<'c>(ciphertext: &'c mut [u8], key: &LessSafeKey) -> Result<&'c [u8]> {
42 if ciphertext.len() < NONCE_LEN {
43 bail!("Ciphertext too short: {}", ciphertext.len());
44 }
45
46 let (nonce_bytes, encrypted_bytes) = ciphertext.split_at_mut(NONCE_LEN);
47
48 key.open_in_place(
49 Nonce::assume_unique_for_key(nonce_bytes.try_into().expect("nonce size known")),
50 Aad::empty(),
51 encrypted_bytes,
52 )
53 .map_err(|_| format_err!("Decryption failed due to unspecified aead error"))?;
54
55 Ok(&encrypted_bytes[..encrypted_bytes.len() - key.algorithm().tag_len()])
56}
57
58pub fn encrypted_write(data: Vec<u8>, key: &LessSafeKey, file: PathBuf) -> Result<()> {
61 Ok(fs::File::options()
62 .write(true)
63 .create_new(true)
64 .open(file)?
65 .write_all(hex::encode(encrypt(data, key)?).as_bytes())?)
66}
67
68pub fn encrypted_read(key: &LessSafeKey, file: PathBuf) -> Result<Vec<u8>> {
70 let hex = fs::read_to_string(file)?;
71 let mut bytes = hex::decode(hex)?;
72
73 Ok(decrypt(&mut bytes, key)?.to_vec())
74}
75
76pub fn get_encryption_key(password: &str, salt: &str) -> Result<LessSafeKey> {
95 let mut key = [0u8; ring::digest::SHA256_OUTPUT_LEN];
96
97 argon2()
98 .hash_password_into(password.as_bytes(), salt.as_bytes(), &mut key)
99 .map_err(|e| format_err!("could not hash password").context(e))?;
100 let key = UnboundKey::new(&ring::aead::CHACHA20_POLY1305, &key)
101 .map_err(|_| anyhow::Error::msg("Unable to create key"))?;
102 Ok(LessSafeKey::new(key))
103}
104
105pub fn random_salt() -> String {
107 SaltString::generate(OsRng).to_string()
108}
109
110fn argon2() -> Argon2<'static> {
113 let mut params = argon2::ParamsBuilder::default();
114 if let Ok("1") = std::env::var(FM_TEST_FAST_WEAK_CRYPTO_ENV).as_deref() {
115 params.m_cost(Params::MIN_M_COST);
116 }
117 Argon2::from(params.build().expect("valid params"))
118}
119
120#[cfg(test)]
121mod tests;