1use anyhow::{bail, ensure, Context};
6use orion::aead::SecretKey;
7use rand::RngCore;
8
9pub fn encrypt(plaintext: impl AsRef<[u8]>, password: impl AsRef<str>) -> anyhow::Result<Vec<u8>> {
25 use orion::hazardous::{
26 aead::xchacha20poly1305::{seal, Nonce, SecretKey as XSecretKey},
27 mac::poly1305::POLY1305_OUTSIZE,
28 stream::xchacha20::XCHACHA_NONCESIZE,
29 };
30 let plaintext = plaintext.as_ref();
32 let password = password.as_ref();
33 let mut nonce = [0u8; 24];
34 rand::thread_rng().fill_bytes(&mut nonce);
35 let nonce = nonce.as_ref();
36 let key = get_key_from_password(password, nonce)?;
38 let key =
40 XSecretKey::from_slice(key.unprotected_as_bytes()).with_context(|| "Key is invalid")?;
41
42 let nonce = Nonce::from_slice(nonce).with_context(|| "Nonce is too short")?;
44
45 let output_len = match plaintext
47 .len()
48 .checked_add(XCHACHA_NONCESIZE + POLY1305_OUTSIZE)
49 {
50 Some(min_output_len) => min_output_len,
51 None => bail!("Plaintext is too long"),
52 };
53
54 let mut output = vec![0u8; output_len];
56 output[..XCHACHA_NONCESIZE].copy_from_slice(nonce.as_ref());
57
58 seal(
60 &key,
61 &nonce,
62 plaintext,
63 None,
64 &mut output[XCHACHA_NONCESIZE..],
65 )
66 .with_context(|| "Could not convert key")?;
67
68 Ok(output)
69}
70
71pub fn decrypt(ciphertext: impl AsRef<[u8]>, password: impl AsRef<str>) -> anyhow::Result<Vec<u8>> {
80 use orion::aead::open;
81 use orion::hazardous::stream::xchacha20::XCHACHA_NONCESIZE;
82
83 let ciphertext = ciphertext.as_ref();
84 let password = password.as_ref();
85
86 ensure!(
87 ciphertext.len() > XCHACHA_NONCESIZE,
88 "Ciphertext is too short"
89 );
90
91 let key = get_key_from_password(password, &ciphertext[..XCHACHA_NONCESIZE])?;
93 open(&key, ciphertext).with_context(|| "Ciphertext was tampered with")
94}
95
96fn get_key_from_password(password: &str, salt: &[u8]) -> anyhow::Result<SecretKey> {
102 use orion::hazardous::stream::chacha20::CHACHA_KEYSIZE;
103 use orion::kdf::{derive_key, Password, Salt};
104 let password = Password::from_slice(password.as_bytes()).with_context(|| "Password error")?;
105 let salt = Salt::from_slice(salt).with_context(|| "Salt is too short")?;
106 let kdf_key = derive_key(&password, &salt, 15, 1024, CHACHA_KEYSIZE as u32)
107 .with_context(|| "Could not derive key from password")?;
108 let key = SecretKey::from_slice(kdf_key.unprotected_as_bytes())
109 .with_context(|| "Could not convert key")?;
110 Ok(key)
111}