use super::key::SecureKey;
use crate::crypto::aes_gcm::{aes256_gcm_decrypt, aes256_gcm_encrypt};
use crate::crypto::uuid::Uuid;
pub const NONCE_SIZE: usize = 12;
pub const TAG_SIZE: usize = 16;
pub const OVERHEAD: usize = NONCE_SIZE + TAG_SIZE;
pub struct PageEncryptor {
key: SecureKey,
}
impl PageEncryptor {
pub fn new(key: SecureKey) -> Self {
Self { key }
}
pub fn encrypt(&self, page_id: u32, plaintext: &[u8]) -> Vec<u8> {
let uuid = Uuid::new_v4();
let mut nonce = [0u8; NONCE_SIZE];
nonce.copy_from_slice(&uuid.as_bytes()[0..NONCE_SIZE]);
let aad = page_id.to_le_bytes();
let key: &[u8; 32] = self
.key
.as_bytes()
.try_into()
.expect("Key must be 32 bytes");
let ciphertext_with_tag = aes256_gcm_encrypt(key, &nonce, &aad, plaintext);
let mut result = Vec::with_capacity(NONCE_SIZE + ciphertext_with_tag.len());
result.extend_from_slice(&nonce);
result.extend_from_slice(&ciphertext_with_tag);
result
}
pub fn decrypt(&self, page_id: u32, encrypted_data: &[u8]) -> Result<Vec<u8>, String> {
if encrypted_data.len() < OVERHEAD {
return Err("Encrypted data too short".to_string());
}
let nonce = &encrypted_data[..NONCE_SIZE];
let ciphertext_with_tag = &encrypted_data[NONCE_SIZE..];
let mut nonce_arr = [0u8; NONCE_SIZE];
nonce_arr.copy_from_slice(nonce);
let aad = page_id.to_le_bytes();
let key: &[u8; 32] = self
.key
.as_bytes()
.try_into()
.expect("Key must be 32 bytes");
aes256_gcm_decrypt(key, &nonce_arr, &aad, ciphertext_with_tag)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_page_encryption_roundtrip() {
let key = SecureKey::new(&[0x42u8; 32]);
let encryptor = PageEncryptor::new(key);
let page_id = 123;
let plaintext = b"This is a secret page content.";
let encrypted = encryptor.encrypt(page_id, plaintext);
assert_eq!(encrypted.len(), plaintext.len() + OVERHEAD);
let decrypted = encryptor.decrypt(page_id, &encrypted).unwrap();
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_page_encryption_bad_page_id() {
let key = SecureKey::new(&[0x42u8; 32]);
let encryptor = PageEncryptor::new(key);
let plaintext = b"content";
let encrypted = encryptor.encrypt(100, plaintext);
let result = encryptor.decrypt(101, &encrypted);
assert!(result.is_err());
}
#[test]
fn test_page_encryption_tampering() {
let key = SecureKey::new(&[0x42u8; 32]);
let encryptor = PageEncryptor::new(key);
let plaintext = b"content";
let mut encrypted = encryptor.encrypt(100, plaintext);
let last = encrypted.len() - 1;
encrypted[last] ^= 1;
let result = encryptor.decrypt(100, &encrypted);
assert!(result.is_err());
}
}