2024-02-17 20:26:45 +01:00
|
|
|
use crate::canonicalization::{canonicalize, join_canonicalized_str};
|
2024-02-17 20:47:07 +01:00
|
|
|
use crate::error::Result;
|
2024-03-02 14:53:38 +01:00
|
|
|
use crate::kdf::{derive_key, KeyContext};
|
2024-02-17 20:47:07 +01:00
|
|
|
use crate::{storage, InputKeyMaterialList};
|
2024-03-02 14:53:38 +01:00
|
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
2024-02-17 20:26:45 +01:00
|
|
|
|
2024-02-25 13:40:19 +01:00
|
|
|
pub(crate) type DecryptionFunction = dyn Fn(&[u8], &EncryptedData, &str) -> Result<Vec<u8>>;
|
2024-02-17 20:47:07 +01:00
|
|
|
pub(crate) type EncryptionFunction = dyn Fn(&[u8], &[u8], &str) -> Result<EncryptedData>;
|
2024-02-17 20:26:45 +01:00
|
|
|
|
2024-03-02 11:05:56 +01:00
|
|
|
#[derive(Debug)]
|
2024-02-17 20:26:45 +01:00
|
|
|
pub(crate) struct EncryptedData {
|
|
|
|
pub(crate) nonce: Vec<u8>,
|
|
|
|
pub(crate) ciphertext: Vec<u8>,
|
|
|
|
}
|
2024-02-15 18:47:36 +01:00
|
|
|
|
2024-02-25 13:40:19 +01:00
|
|
|
#[inline]
|
2024-03-02 14:53:38 +01:00
|
|
|
fn generate_aad(
|
|
|
|
key_context: &KeyContext,
|
|
|
|
data_context: &[impl AsRef<[u8]>],
|
|
|
|
ts: Option<u64>,
|
|
|
|
) -> String {
|
|
|
|
let key_context_canon = canonicalize(&key_context.get_value(ts));
|
2024-02-25 13:40:19 +01:00
|
|
|
let data_context_canon = canonicalize(data_context);
|
|
|
|
join_canonicalized_str(&key_context_canon, &data_context_canon)
|
|
|
|
}
|
|
|
|
|
2024-02-15 18:47:36 +01:00
|
|
|
pub fn encrypt(
|
|
|
|
ikml: &InputKeyMaterialList,
|
2024-03-02 14:53:38 +01:00
|
|
|
key_context: &KeyContext,
|
2024-02-15 18:47:36 +01:00
|
|
|
data: impl AsRef<[u8]>,
|
|
|
|
data_context: &[impl AsRef<[u8]>],
|
2024-02-17 20:47:07 +01:00
|
|
|
) -> Result<String> {
|
2024-03-02 14:53:38 +01:00
|
|
|
let ts = if key_context.is_periodic() {
|
|
|
|
Some(SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
2024-02-15 23:45:21 +01:00
|
|
|
let ikm = ikml.get_latest_ikm()?;
|
2024-03-02 14:53:38 +01:00
|
|
|
let key = derive_key(ikm, key_context, ts);
|
|
|
|
let aad = generate_aad(key_context, data_context, ts);
|
2024-02-17 20:26:45 +01:00
|
|
|
let encryption_function = ikm.scheme.get_encryption();
|
|
|
|
let encrypted_data = encryption_function(&key, data.as_ref(), &aad)?;
|
2024-03-02 14:53:38 +01:00
|
|
|
Ok(storage::encode_cipher(ikm.id, &encrypted_data, ts))
|
2024-02-17 20:26:45 +01:00
|
|
|
}
|
|
|
|
|
2024-02-15 18:47:36 +01:00
|
|
|
pub fn decrypt(
|
|
|
|
ikml: &InputKeyMaterialList,
|
2024-03-02 14:53:38 +01:00
|
|
|
key_context: &KeyContext,
|
2024-02-25 13:40:19 +01:00
|
|
|
stored_data: &str,
|
2024-02-15 18:47:36 +01:00
|
|
|
data_context: &[impl AsRef<[u8]>],
|
2024-02-17 20:47:07 +01:00
|
|
|
) -> Result<Vec<u8>> {
|
2024-03-02 14:53:38 +01:00
|
|
|
let (ikm_id, encrypted_data, ts) = storage::decode_cipher(stored_data)?;
|
2024-02-25 13:40:19 +01:00
|
|
|
let ikm = ikml.get_ikm_by_id(ikm_id)?;
|
2024-03-02 14:53:38 +01:00
|
|
|
let key = derive_key(ikm, key_context, ts);
|
|
|
|
let aad = generate_aad(key_context, data_context, ts);
|
2024-02-25 13:40:19 +01:00
|
|
|
let decryption_function = ikm.scheme.get_decryption();
|
|
|
|
decryption_function(&key, &encrypted_data, &aad)
|
|
|
|
}
|
|
|
|
|
2024-02-17 20:26:45 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2024-03-02 14:53:38 +01:00
|
|
|
use crate::KeyContext;
|
2024-02-17 20:26:45 +01:00
|
|
|
|
|
|
|
const TEST_DATA: &[u8] = b"Lorem ipsum dolor sit amet.";
|
2024-03-02 14:53:38 +01:00
|
|
|
const TEST_KEY_CTX: [&str; 3] = ["db_name", "table_name", "column_name"];
|
2024-02-17 20:26:45 +01:00
|
|
|
const TEST_DATA_CTX: &[&str] = &["018db876-3d9d-79af-9460-55d17da991d8"];
|
|
|
|
const EMPTY_DATA_CTX: &[[u8; 0]] = &[];
|
|
|
|
|
|
|
|
fn get_ikm_lst() -> InputKeyMaterialList {
|
|
|
|
InputKeyMaterialList::import(
|
2024-03-02 11:00:59 +01:00
|
|
|
"AQAAAA:AQAAAAEAAAC_vYEw1ujVG5i-CtoPYSzik_6xaAq59odjPm5ij01-e6zz4mUAAAAALJGBiwAAAAAA",
|
2024-02-17 20:26:45 +01:00
|
|
|
)
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn encrypt_decrypt_no_context() {
|
2024-03-02 14:53:38 +01:00
|
|
|
let ctx = KeyContext::from([]);
|
|
|
|
|
2024-02-25 13:40:19 +01:00
|
|
|
// Encrypt
|
2024-02-17 20:26:45 +01:00
|
|
|
let lst = get_ikm_lst();
|
2024-03-02 14:53:38 +01:00
|
|
|
let res = encrypt(&lst, &ctx, TEST_DATA, EMPTY_DATA_CTX);
|
2024-03-02 11:05:56 +01:00
|
|
|
assert!(res.is_ok(), "res: {res:?}");
|
2024-02-17 20:26:45 +01:00
|
|
|
let ciphertext = res.unwrap();
|
|
|
|
assert!(ciphertext.starts_with("AQAAAA:"));
|
|
|
|
assert_eq!(ciphertext.len(), 98);
|
|
|
|
|
2024-02-25 13:40:19 +01:00
|
|
|
// Decrypt
|
2024-03-02 14:53:38 +01:00
|
|
|
let res = decrypt(&lst, &ctx, &ciphertext, EMPTY_DATA_CTX);
|
2024-03-02 11:05:56 +01:00
|
|
|
assert!(res.is_ok(), "res: {res:?}");
|
2024-02-25 13:40:19 +01:00
|
|
|
let plaintext = res.unwrap();
|
|
|
|
assert_eq!(plaintext, TEST_DATA);
|
2024-02-17 20:26:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn encrypt_decrypt_with_context() {
|
2024-02-25 13:40:19 +01:00
|
|
|
// Encrypt
|
2024-02-17 20:26:45 +01:00
|
|
|
let lst = get_ikm_lst();
|
2024-03-02 14:53:38 +01:00
|
|
|
let res = encrypt(&lst, &TEST_KEY_CTX.into(), TEST_DATA, TEST_DATA_CTX);
|
2024-03-02 11:05:56 +01:00
|
|
|
assert!(res.is_ok(), "res: {res:?}");
|
2024-02-17 20:26:45 +01:00
|
|
|
let ciphertext = res.unwrap();
|
|
|
|
assert!(ciphertext.starts_with("AQAAAA:"));
|
|
|
|
assert_eq!(ciphertext.len(), 98);
|
|
|
|
|
2024-02-25 13:40:19 +01:00
|
|
|
// Decrypt
|
2024-03-02 14:53:38 +01:00
|
|
|
let res = decrypt(&lst, &TEST_KEY_CTX.into(), &ciphertext, TEST_DATA_CTX);
|
2024-03-02 11:05:56 +01:00
|
|
|
assert!(res.is_ok(), "res: {res:?}");
|
2024-02-25 13:40:19 +01:00
|
|
|
let plaintext = res.unwrap();
|
|
|
|
assert_eq!(plaintext, TEST_DATA);
|
2024-02-17 20:26:45 +01:00
|
|
|
}
|
|
|
|
}
|