1use anyhow::Result;
2use sodiumoxide::crypto::secretbox;
3use sodiumoxide::crypto::pwhash::argon2id13;
4use zeroize::Zeroize;
5
6pub struct MasterKey {
7 key: secretbox::Key,
8}
9
10impl MasterKey {
11 pub fn from_password(password: &str, salt: &argon2id13::Salt) -> Result<Self> {
12 let mut key = secretbox::Key([0; secretbox::KEYBYTES]);
13
14 argon2id13::derive_key(
15 &mut key.0,
16 password.as_bytes(),
17 salt,
18 argon2id13::OPSLIMIT_INTERACTIVE,
19 argon2id13::MEMLIMIT_INTERACTIVE,
20 )
21 .map_err(|_| anyhow::anyhow!("Failed to derive key from password"))?;
22
23 Ok(Self { key })
24 }
25
26 pub fn encrypt(&self, data: &[u8]) -> (secretbox::Nonce, Vec<u8>) {
27 let nonce = secretbox::gen_nonce();
28 let ciphertext = secretbox::seal(data, &nonce, &self.key);
29 (nonce, ciphertext)
30 }
31
32 pub fn decrypt(&self, ciphertext: &[u8], nonce: &secretbox::Nonce) -> Result<Vec<u8>> {
33 secretbox::open(ciphertext, nonce, &self.key)
34 .map_err(|_| anyhow::anyhow!("Failed to decrypt data - invalid password or corrupted data"))
35 }
36
37 pub fn key(&self) -> &secretbox::Key {
38 &self.key
39 }
40}
41
42impl Drop for MasterKey {
43 fn drop(&mut self) {
44 self.key.0.zeroize();
45 }
46}
47
48pub fn generate_salt() -> argon2id13::Salt {
49 argon2id13::gen_salt()
50}