Skip to main content

portkey/
crypto.rs

1use anyhow::Result;
2use sodiumoxide::crypto::pwhash::argon2id13;
3use sodiumoxide::crypto::secretbox;
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).map_err(|_| {
34            anyhow::anyhow!("Failed to decrypt data - invalid password or corrupted data")
35        })
36    }
37}
38
39impl Drop for MasterKey {
40    fn drop(&mut self) {
41        self.key.0.zeroize();
42    }
43}
44
45pub fn generate_salt() -> argon2id13::Salt {
46    argon2id13::gen_salt()
47}