Skip to main content

reddb_server/storage/encryption/
header.rs

1//! Encrypted Database Header
2//!
3//! Stores encryption parameters and key verification data.
4//! This header is stored in the clear (in Page 0, potentially) or a separate header file?
5//! The `Pager` uses Page 0 for `DatabaseHeader`.
6//! Usually, encryption parameters are part of the `DatabaseHeader` or stored alongside it.
7//!
8//! Since `DatabaseHeader` in `pager.rs` is fixed structure (u32 fields), we might need to extend it
9//! or use a separate page (e.g. Page 1?) or just reserved bytes in Page 0?
10//! `HEADER_SIZE` in `page.rs` is 32 bytes.
11//! A standard 4KB page has plenty of room.
12//!
13//! We will implement serialization for this header so it can be embedded in Page 0 after the main header.
14
15use super::key::SecureKey;
16use super::page_encryptor::{PageEncryptor, NONCE_SIZE, TAG_SIZE};
17use crate::crypto::uuid::Uuid;
18
19pub const SALT_SIZE: usize = 32;
20pub const KEY_CHECK_LEN: usize = 32; // Length of known value to encrypt
21
22/// Header containing encryption parameters
23#[derive(Debug, Clone)]
24pub struct EncryptionHeader {
25    /// Salt used for Key Derivation (32 bytes)
26    pub salt: [u8; SALT_SIZE],
27
28    /// Key verification data
29    /// Layout: [Nonce (12)] [Ciphertext (32)] [Tag (16)]
30    /// Total: 12 + 32 + 16 = 60 bytes
31    pub key_check: Vec<u8>,
32}
33
34impl EncryptionHeader {
35    /// Create a new encryption header
36    pub fn new(key: &SecureKey) -> Self {
37        // Generate random salt
38        let uuid = Uuid::new_v4();
39        let mut salt = [0u8; SALT_SIZE];
40        // Fill salt with random data (using uuid chunks for now as we don't have rand)
41        let b = uuid.as_bytes();
42        salt[0..16].copy_from_slice(b);
43        let uuid2 = Uuid::new_v4();
44        salt[16..32].copy_from_slice(uuid2.as_bytes());
45
46        // Create key check
47        // Encrypt a known value (e.g., 32 bytes of 0xAA)
48        let known_value = [0xAAu8; KEY_CHECK_LEN];
49        let encryptor = PageEncryptor::new(key.clone());
50
51        // Use a dummy page ID for key check (e.g., u32::MAX)
52        let check_blob = encryptor.encrypt(u32::MAX, &known_value);
53
54        Self {
55            salt,
56            key_check: check_blob,
57        }
58    }
59
60    /// Validate the key against this header
61    pub fn validate(&self, key: &SecureKey) -> bool {
62        let encryptor = PageEncryptor::new(key.clone());
63
64        match encryptor.decrypt(u32::MAX, &self.key_check) {
65            Ok(plaintext) => {
66                let expected = [0xAAu8; KEY_CHECK_LEN];
67                plaintext == expected
68            }
69            Err(_) => false,
70        }
71    }
72
73    /// Serialize to bytes
74    pub fn to_bytes(&self) -> Vec<u8> {
75        let mut buf = Vec::new();
76        buf.extend_from_slice(&self.salt);
77        // Length of key check is dynamic or fixed?
78        // NONCE(12) + KEY_CHECK_LEN(32) + TAG(16) = 60 bytes.
79        // It's fixed.
80        buf.extend_from_slice(&self.key_check);
81        buf
82    }
83
84    /// Deserialize from bytes
85    pub fn from_bytes(data: &[u8]) -> Result<Self, String> {
86        let check_size = NONCE_SIZE + KEY_CHECK_LEN + TAG_SIZE;
87        let expected_len = SALT_SIZE + check_size;
88
89        if data.len() < expected_len {
90            return Err("Data too short for EncryptionHeader".to_string());
91        }
92
93        let mut salt = [0u8; SALT_SIZE];
94        salt.copy_from_slice(&data[0..SALT_SIZE]);
95
96        let key_check = data[SALT_SIZE..SALT_SIZE + check_size].to_vec();
97
98        Ok(Self { salt, key_check })
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_header_validation() {
108        let key = SecureKey::new(&[0x11u8; 32]);
109        let header = EncryptionHeader::new(&key);
110
111        assert!(header.validate(&key));
112
113        let wrong_key = SecureKey::new(&[0x22u8; 32]);
114        assert!(!header.validate(&wrong_key));
115    }
116
117    #[test]
118    fn test_header_serialization() {
119        let key = SecureKey::new(&[0x33u8; 32]);
120        let header = EncryptionHeader::new(&key);
121
122        let bytes = header.to_bytes();
123        let loaded = EncryptionHeader::from_bytes(&bytes).unwrap();
124
125        assert_eq!(header.salt, loaded.salt);
126        assert_eq!(header.key_check, loaded.key_check);
127        assert!(loaded.validate(&key));
128    }
129}