use std::convert::TryFrom;
use aes_gcm::{
aead::{generic_array::GenericArray, Aead, NewAead},
Aes256Gcm, Error as DecryptionError,
};
use getrandom::getrandom;
use hmac::Hmac;
use olm_rs::PicklingMode;
use pbkdf2::pbkdf2;
use sha2::Sha256;
use zeroize::{Zeroize, Zeroizing};
use serde::{Deserialize, Serialize};
const KEY_SIZE: usize = 32;
const NONCE_SIZE: usize = 12;
const KDF_SALT_SIZE: usize = 32;
const KDF_ROUNDS: u32 = 10000;
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub enum KdfInfo {
Pbkdf2 {
rounds: u32,
},
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub enum CipherTextInfo {
Aes256Gcm {
nonce: Vec<u8>,
ciphertext: Vec<u8>,
},
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct EncryptedPickleKey {
pub kdf_info: KdfInfo,
pub ciphertext_info: CipherTextInfo,
kdf_salt: Vec<u8>,
}
#[derive(Debug, Zeroize, PartialEq)]
pub struct PickleKey {
aes256_key: Vec<u8>,
}
impl Default for PickleKey {
fn default() -> Self {
let mut key = vec![0u8; KEY_SIZE];
getrandom(&mut key).expect("Can't generate new pickle key");
Self { aes256_key: key }
}
}
impl TryFrom<Vec<u8>> for PickleKey {
type Error = ();
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
if value.len() != KEY_SIZE {
Err(())
} else {
Ok(Self { aes256_key: value })
}
}
}
impl PickleKey {
pub fn new() -> Self {
Default::default()
}
fn expand_key(passphrase: &str, salt: &[u8], rounds: u32) -> Zeroizing<Vec<u8>> {
let mut key = Zeroizing::from(vec![0u8; KEY_SIZE]);
pbkdf2::<Hmac<Sha256>>(passphrase.as_bytes(), &salt, rounds, &mut *key);
key
}
pub fn pickle_mode(&self) -> PicklingMode {
PicklingMode::Encrypted {
key: self.aes256_key.clone(),
}
}
pub fn key(&self) -> &[u8] {
&self.aes256_key
}
pub fn encrypt(&self, passphrase: &str) -> EncryptedPickleKey {
let mut salt = vec![0u8; KDF_SALT_SIZE];
getrandom(&mut salt).expect("Can't generate new random pickle key");
let key = PickleKey::expand_key(passphrase, &salt, KDF_ROUNDS);
let key = GenericArray::from_slice(key.as_ref());
let cipher = Aes256Gcm::new(&key);
let mut nonce = vec![0u8; NONCE_SIZE];
getrandom(&mut nonce).expect("Can't generate new random nonce for the pickle key");
let ciphertext = cipher
.encrypt(
&GenericArray::from_slice(nonce.as_ref()),
self.aes256_key.as_slice(),
)
.expect("Can't encrypt pickle key");
EncryptedPickleKey {
kdf_info: KdfInfo::Pbkdf2 { rounds: KDF_ROUNDS },
kdf_salt: salt,
ciphertext_info: CipherTextInfo::Aes256Gcm { nonce, ciphertext },
}
}
pub fn from_encrypted(
passphrase: &str,
encrypted: EncryptedPickleKey,
) -> Result<Self, DecryptionError> {
let key = match encrypted.kdf_info {
KdfInfo::Pbkdf2 { rounds } => Self::expand_key(passphrase, &encrypted.kdf_salt, rounds),
};
let key = GenericArray::from_slice(key.as_ref());
let decrypted = match encrypted.ciphertext_info {
CipherTextInfo::Aes256Gcm { nonce, ciphertext } => {
let cipher = Aes256Gcm::new(&key);
let nonce = GenericArray::from_slice(&nonce);
cipher.decrypt(nonce, ciphertext.as_ref())?
}
};
Ok(Self {
aes256_key: decrypted,
})
}
}
#[cfg(test)]
mod test {
use super::PickleKey;
#[test]
fn generating() {
PickleKey::new();
}
#[test]
fn encrypting() {
let passphrase = "it's a secret to everybody";
let pickle_key = PickleKey::new();
let encrypted = pickle_key.encrypt(passphrase);
let decrypted = PickleKey::from_encrypted(passphrase, encrypted).unwrap();
assert_eq!(pickle_key, decrypted);
}
}