116 lines
2.9 KiB
Rust
116 lines
2.9 KiB
Rust
use crate::canonicalization::{canonicalize, join_canonicalized_str};
|
|
use crate::error::Result;
|
|
use crate::kdf::derive_key;
|
|
use crate::{storage, InputKeyMaterialList};
|
|
use chacha20poly1305::aead::{Aead, KeyInit, Payload};
|
|
use chacha20poly1305::{Key, XChaCha20Poly1305, XNonce};
|
|
|
|
pub(crate) type EncryptionFunction = dyn Fn(&[u8], &[u8], &str) -> Result<EncryptedData>;
|
|
|
|
pub(crate) struct EncryptedData {
|
|
pub(crate) nonce: Vec<u8>,
|
|
pub(crate) ciphertext: Vec<u8>,
|
|
}
|
|
|
|
pub fn encrypt(
|
|
ikml: &InputKeyMaterialList,
|
|
key_context: &[&str],
|
|
data: impl AsRef<[u8]>,
|
|
data_context: &[impl AsRef<[u8]>],
|
|
) -> Result<String> {
|
|
// Derive the key
|
|
let ikm = ikml.get_latest_ikm()?;
|
|
let key = derive_key(ikm, key_context);
|
|
|
|
// Generate the AAD
|
|
let key_context_canon = canonicalize(key_context);
|
|
let data_context_canon = canonicalize(data_context);
|
|
let aad = join_canonicalized_str(&key_context_canon, &data_context_canon);
|
|
|
|
// Encrypt
|
|
let encryption_function = ikm.scheme.get_encryption();
|
|
let encrypted_data = encryption_function(&key, data.as_ref(), &aad)?;
|
|
|
|
// Encode
|
|
Ok(storage::encode(ikm.id, &encrypted_data))
|
|
}
|
|
|
|
pub(crate) fn xchacha20poly1305_encrypt(
|
|
key: &[u8],
|
|
data: &[u8],
|
|
aad: &str,
|
|
) -> Result<EncryptedData> {
|
|
// Adapt the key
|
|
let key = Key::from_slice(key);
|
|
|
|
// Generate a nonce
|
|
let mut nonce: [u8; 24] = [0; 24];
|
|
getrandom::getrandom(&mut nonce)?;
|
|
let nonce = XNonce::from_slice(&nonce);
|
|
|
|
// Prepare the payload
|
|
let payload = Payload {
|
|
msg: data,
|
|
aad: aad.as_bytes(),
|
|
};
|
|
|
|
// Encrypt the payload
|
|
let cipher = XChaCha20Poly1305::new(key);
|
|
let ciphertext = cipher.encrypt(nonce, payload)?;
|
|
|
|
// Return the result
|
|
Ok(EncryptedData {
|
|
nonce: nonce.to_vec(),
|
|
ciphertext,
|
|
})
|
|
}
|
|
|
|
pub fn decrypt(
|
|
ikml: &InputKeyMaterialList,
|
|
key_context: &[&str],
|
|
data: impl AsRef<[u8]>,
|
|
data_context: &[impl AsRef<[u8]>],
|
|
) -> Result<Vec<u8>> {
|
|
unimplemented!("decrypt");
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
const TEST_DATA: &[u8] = b"Lorem ipsum dolor sit amet.";
|
|
const TEST_KEY_CTX: &[&str] = &["db_name", "table_name", "column_name"];
|
|
const TEST_DATA_CTX: &[&str] = &["018db876-3d9d-79af-9460-55d17da991d8"];
|
|
const EMPTY_DATA_CTX: &[[u8; 0]] = &[];
|
|
|
|
fn get_ikm_lst() -> InputKeyMaterialList {
|
|
InputKeyMaterialList::import(
|
|
"AQAAAAEAAAABAAAANGFtbdYEN0s7dzCfMm7dYeQWD64GdmuKsYSiKwppAhmkz81lAAAAACQDr2cAAAAAAA",
|
|
)
|
|
.unwrap()
|
|
}
|
|
|
|
#[test]
|
|
fn encrypt_decrypt_no_context() {
|
|
let lst = get_ikm_lst();
|
|
let res = encrypt(&lst, &[], TEST_DATA, EMPTY_DATA_CTX);
|
|
assert!(res.is_ok());
|
|
let ciphertext = res.unwrap();
|
|
assert!(ciphertext.starts_with("AQAAAA:"));
|
|
assert_eq!(ciphertext.len(), 98);
|
|
|
|
// TODO: decrypt
|
|
}
|
|
|
|
#[test]
|
|
fn encrypt_decrypt_with_context() {
|
|
let lst = get_ikm_lst();
|
|
let res = encrypt(&lst, TEST_KEY_CTX, TEST_DATA, TEST_DATA_CTX);
|
|
assert!(res.is_ok());
|
|
let ciphertext = res.unwrap();
|
|
assert!(ciphertext.starts_with("AQAAAA:"));
|
|
assert_eq!(ciphertext.len(), 98);
|
|
|
|
// TODO: decrypt
|
|
}
|
|
}
|