Skip to main content

oxigdal_security/encryption/
mod.rs

1//! Encryption infrastructure for data at rest and in transit.
2
3pub mod at_rest;
4pub mod envelope;
5pub mod in_transit;
6pub mod key_management;
7
8// Re-export commonly used types
9pub use at_rest::{AtRestEncryptor, FieldEncryptor};
10pub use envelope::EnvelopeEncryptor;
11pub use in_transit::TlsConfigBuilder;
12pub use key_management::KeyManager;
13
14use crate::error::{Result, SecurityError};
15use serde::{Deserialize, Serialize};
16
17/// Encryption algorithm.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
19pub enum EncryptionAlgorithm {
20    /// AES-256-GCM (recommended for most use cases).
21    #[default]
22    Aes256Gcm,
23    /// ChaCha20-Poly1305 (faster on systems without AES hardware).
24    ChaCha20Poly1305,
25}
26
27/// Encryption metadata.
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct EncryptionMetadata {
30    /// Algorithm used for encryption.
31    pub algorithm: EncryptionAlgorithm,
32    /// Key ID (for key rotation).
33    pub key_id: String,
34    /// Initialization vector/nonce.
35    pub iv: Vec<u8>,
36    /// Additional authenticated data.
37    pub aad: Option<Vec<u8>>,
38    /// Timestamp when encrypted.
39    pub encrypted_at: chrono::DateTime<chrono::Utc>,
40}
41
42impl EncryptionMetadata {
43    /// Create new encryption metadata.
44    pub fn new(
45        algorithm: EncryptionAlgorithm,
46        key_id: String,
47        iv: Vec<u8>,
48        aad: Option<Vec<u8>>,
49    ) -> Self {
50        Self {
51            algorithm,
52            key_id,
53            iv,
54            aad,
55            encrypted_at: chrono::Utc::now(),
56        }
57    }
58}
59
60/// Encrypted data with metadata.
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct EncryptedData {
63    /// Encrypted ciphertext.
64    pub ciphertext: Vec<u8>,
65    /// Encryption metadata.
66    pub metadata: EncryptionMetadata,
67}
68
69impl EncryptedData {
70    /// Create new encrypted data.
71    pub fn new(ciphertext: Vec<u8>, metadata: EncryptionMetadata) -> Self {
72        Self {
73            ciphertext,
74            metadata,
75        }
76    }
77
78    /// Serialize to JSON bytes.
79    pub fn to_json_bytes(&self) -> Result<Vec<u8>> {
80        serde_json::to_vec(self).map_err(SecurityError::from)
81    }
82
83    /// Deserialize from JSON bytes.
84    pub fn from_json_bytes(bytes: &[u8]) -> Result<Self> {
85        serde_json::from_slice(bytes).map_err(SecurityError::from)
86    }
87}
88
89/// Key derivation function.
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
91pub enum KeyDerivationFunction {
92    /// PBKDF2 with SHA-256.
93    Pbkdf2Sha256,
94    /// Argon2id (recommended).
95    #[default]
96    Argon2id,
97}
98
99/// Key derivation parameters.
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct KeyDerivationParams {
102    /// Key derivation function.
103    pub kdf: KeyDerivationFunction,
104    /// Salt.
105    pub salt: Vec<u8>,
106    /// Iterations (for PBKDF2).
107    pub iterations: Option<u32>,
108    /// Memory cost (for Argon2).
109    pub memory_cost: Option<u32>,
110    /// Time cost (for Argon2).
111    pub time_cost: Option<u32>,
112    /// Parallelism (for Argon2).
113    pub parallelism: Option<u32>,
114}
115
116impl KeyDerivationParams {
117    /// Create PBKDF2 parameters with recommended settings.
118    pub fn pbkdf2_recommended(salt: Vec<u8>) -> Self {
119        Self {
120            kdf: KeyDerivationFunction::Pbkdf2Sha256,
121            salt,
122            iterations: Some(600000), // OWASP recommendation
123            memory_cost: None,
124            time_cost: None,
125            parallelism: None,
126        }
127    }
128
129    /// Create Argon2id parameters with recommended settings.
130    pub fn argon2_recommended(salt: Vec<u8>) -> Self {
131        Self {
132            kdf: KeyDerivationFunction::Argon2id,
133            salt,
134            iterations: None,
135            memory_cost: Some(19456), // 19 MiB
136            time_cost: Some(2),
137            parallelism: Some(1),
138        }
139    }
140}
141
142/// Derive a key from a password.
143pub fn derive_key(
144    password: &[u8],
145    params: &KeyDerivationParams,
146    key_length: usize,
147) -> Result<Vec<u8>> {
148    match params.kdf {
149        KeyDerivationFunction::Pbkdf2Sha256 => {
150            let iterations = params
151                .iterations
152                .ok_or_else(|| SecurityError::key_derivation("iterations required for PBKDF2"))?;
153
154            use ring::pbkdf2;
155            let mut key = vec![0u8; key_length];
156            pbkdf2::derive(
157                pbkdf2::PBKDF2_HMAC_SHA256,
158                std::num::NonZeroU32::new(iterations)
159                    .ok_or_else(|| SecurityError::key_derivation("invalid iterations"))?,
160                &params.salt,
161                password,
162                &mut key,
163            );
164            Ok(key)
165        }
166        KeyDerivationFunction::Argon2id => {
167            use argon2::{Algorithm, Argon2, Params, Version};
168
169            let memory_cost = params
170                .memory_cost
171                .ok_or_else(|| SecurityError::key_derivation("memory_cost required for Argon2"))?;
172            let time_cost = params
173                .time_cost
174                .ok_or_else(|| SecurityError::key_derivation("time_cost required for Argon2"))?;
175            let parallelism = params
176                .parallelism
177                .ok_or_else(|| SecurityError::key_derivation("parallelism required for Argon2"))?;
178
179            let argon2_params = Params::new(memory_cost, time_cost, parallelism, Some(key_length))
180                .map_err(|e| {
181                    SecurityError::key_derivation(format!("invalid Argon2 params: {}", e))
182                })?;
183
184            let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, argon2_params);
185
186            let mut key = vec![0u8; key_length];
187            argon2
188                .hash_password_into(password, &params.salt, &mut key)
189                .map_err(|e| SecurityError::key_derivation(format!("Argon2 error: {}", e)))?;
190
191            Ok(key)
192        }
193    }
194}
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199
200    #[test]
201    fn test_key_derivation_pbkdf2() {
202        let password = b"test_password";
203        let salt = b"test_salt_12345678";
204        let params = KeyDerivationParams::pbkdf2_recommended(salt.to_vec());
205
206        let key = derive_key(password, &params, 32).expect("key derivation failed");
207        assert_eq!(key.len(), 32);
208
209        // Same password and salt should produce same key
210        let key2 = derive_key(password, &params, 32).expect("key derivation failed");
211        assert_eq!(key, key2);
212
213        // Different password should produce different key
214        let key3 = derive_key(b"different", &params, 32).expect("key derivation failed");
215        assert_ne!(key, key3);
216    }
217
218    #[test]
219    fn test_key_derivation_argon2() {
220        let password = b"test_password";
221        let salt = b"test_salt_12345678";
222        let params = KeyDerivationParams::argon2_recommended(salt.to_vec());
223
224        let key = derive_key(password, &params, 32).expect("key derivation failed");
225        assert_eq!(key.len(), 32);
226
227        // Same password and salt should produce same key
228        let key2 = derive_key(password, &params, 32).expect("key derivation failed");
229        assert_eq!(key, key2);
230    }
231
232    #[test]
233    fn test_encryption_metadata_serialization() {
234        let metadata = EncryptionMetadata::new(
235            EncryptionAlgorithm::Aes256Gcm,
236            "key-001".to_string(),
237            vec![1, 2, 3, 4, 5],
238            Some(vec![6, 7, 8]),
239        );
240
241        let json = serde_json::to_string(&metadata).expect("serialization failed");
242        let deserialized: EncryptionMetadata =
243            serde_json::from_str(&json).expect("deserialization failed");
244
245        assert_eq!(deserialized.algorithm, metadata.algorithm);
246        assert_eq!(deserialized.key_id, metadata.key_id);
247        assert_eq!(deserialized.iv, metadata.iv);
248        assert_eq!(deserialized.aad, metadata.aad);
249    }
250}