portkey/
crypto.rs

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}