use super::key::SecureKey;
pub const NONCE_SIZE: usize = reddb_crypto::NONCE_SIZE;
pub const TAG_SIZE: usize = reddb_crypto::TAG_SIZE;
pub const OVERHEAD: usize = reddb_crypto::PAGE_ENVELOPE_OVERHEAD;
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> {
reddb_crypto::encrypt_page(self.key_bytes(), page_id, plaintext)
.expect("page envelope encryption failed (CSPRNG)")
}
pub fn decrypt(&self, page_id: u32, encrypted_data: &[u8]) -> Result<Vec<u8>, String> {
reddb_crypto::decrypt_page(self.key_bytes(), page_id, encrypted_data)
.map_err(|e| e.to_string())
}
fn key_bytes(&self) -> &[u8; 32] {
self.key
.as_bytes()
.try_into()
.expect("Key must be 32 bytes")
}
}
#[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());
}
}