mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-24 10:21:15 +01:00
80 lines
2.5 KiB
Rust
80 lines
2.5 KiB
Rust
use crate::encryption::{decrypt_data, encrypt_data};
|
|
use crate::error::Error::GenericError;
|
|
use crate::error::Result;
|
|
use base32::Alphabet;
|
|
use chacha20poly1305::aead::{Key, KeyInit, OsRng};
|
|
use chacha20poly1305::XChaCha20Poly1305;
|
|
use keyring::{Entry, Error};
|
|
use log::info;
|
|
|
|
const HUMAN_PREFIX: &str = "YKM_";
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub(crate) struct MasterKey {
|
|
key: Key<XChaCha20Poly1305>,
|
|
}
|
|
|
|
impl MasterKey {
|
|
pub(crate) fn get_or_create(app_id: &str, user: &str) -> Result<Self> {
|
|
let id = format!("{app_id}.EncryptionKey");
|
|
let entry = Entry::new(&id, user)?;
|
|
|
|
let key = match entry.get_password() {
|
|
Ok(encoded) => {
|
|
let without_prefix = encoded.strip_prefix(HUMAN_PREFIX).unwrap_or(&encoded);
|
|
let key_bytes = base32::decode(Alphabet::Crockford {}, &without_prefix)
|
|
.ok_or(GenericError("Failed to decode master key".to_string()))?;
|
|
Key::<XChaCha20Poly1305>::clone_from_slice(key_bytes.as_slice())
|
|
}
|
|
Err(Error::NoEntry) => {
|
|
info!("Creating new master key");
|
|
let key = XChaCha20Poly1305::generate_key(OsRng);
|
|
let encoded = base32::encode(Alphabet::Crockford {}, key.as_slice());
|
|
let with_prefix = format!("{HUMAN_PREFIX}{encoded}");
|
|
entry.set_password(&with_prefix)?;
|
|
key
|
|
}
|
|
Err(e) => return Err(GenericError(e.to_string())),
|
|
};
|
|
|
|
Ok(Self { key })
|
|
}
|
|
|
|
pub(crate) fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
|
|
encrypt_data(data, &self.key)
|
|
}
|
|
|
|
pub(crate) fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
|
|
decrypt_data(data, &self.key)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub(crate) fn test_key() -> Self {
|
|
let key: Key<XChaCha20Poly1305> = Key::<XChaCha20Poly1305>::clone_from_slice(
|
|
"00000000000000000000000000000000".as_bytes(),
|
|
);
|
|
Self { key }
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::error::Result;
|
|
use crate::master_key::MasterKey;
|
|
|
|
#[test]
|
|
fn test_master_key() -> Result<()> {
|
|
// Test out the master key
|
|
let mkey = MasterKey::test_key();
|
|
let encrypted = mkey.encrypt("hello".as_bytes())?;
|
|
let decrypted = mkey.decrypt(encrypted.as_slice()).unwrap();
|
|
assert_eq!(decrypted, "hello".as_bytes().to_vec());
|
|
|
|
let mkey = MasterKey::test_key();
|
|
let decrypted = mkey.decrypt(encrypted.as_slice()).unwrap();
|
|
assert_eq!(decrypted, "hello".as_bytes().to_vec());
|
|
|
|
Ok(())
|
|
}
|
|
}
|