Skip to main content

oxidize_pdf/document/
encryption.rs

1//! Document encryption support
2
3use crate::encryption::{
4    EncryptionDictionary, EncryptionKey, OwnerPassword, Permissions, StandardSecurityHandler,
5    UserPassword,
6};
7use crate::error::Result;
8use crate::objects::ObjectId;
9
10/// Encryption settings for a document
11#[derive(Debug, Clone)]
12pub struct DocumentEncryption {
13    /// User password
14    pub user_password: UserPassword,
15    /// Owner password
16    pub owner_password: OwnerPassword,
17    /// Permissions
18    pub permissions: Permissions,
19    /// Encryption strength
20    pub strength: EncryptionStrength,
21}
22
23/// Encryption strength
24#[derive(Debug, Clone, Copy)]
25pub enum EncryptionStrength {
26    /// RC4 40-bit encryption
27    Rc4_40bit,
28    /// RC4 128-bit encryption
29    Rc4_128bit,
30    /// AES 128-bit encryption (V=4, R=4, AESV2 crypt filter)
31    Aes128,
32    /// AES 256-bit encryption (V=5, R=5, AESV3 crypt filter)
33    Aes256,
34}
35
36impl DocumentEncryption {
37    /// Create new encryption settings
38    pub fn new(
39        user_password: impl Into<String>,
40        owner_password: impl Into<String>,
41        permissions: Permissions,
42        strength: EncryptionStrength,
43    ) -> Self {
44        Self {
45            user_password: UserPassword(user_password.into()),
46            owner_password: OwnerPassword(owner_password.into()),
47            permissions,
48            strength,
49        }
50    }
51
52    /// Create with default permissions (all allowed)
53    pub fn with_passwords(
54        user_password: impl Into<String>,
55        owner_password: impl Into<String>,
56    ) -> Self {
57        Self::new(
58            user_password,
59            owner_password,
60            Permissions::all(),
61            EncryptionStrength::Rc4_128bit,
62        )
63    }
64
65    /// Get the security handler
66    pub fn handler(&self) -> StandardSecurityHandler {
67        match self.strength {
68            EncryptionStrength::Rc4_40bit => StandardSecurityHandler::rc4_40bit(),
69            EncryptionStrength::Rc4_128bit => StandardSecurityHandler::rc4_128bit(),
70            EncryptionStrength::Aes128 => StandardSecurityHandler::aes_128_r4(),
71            EncryptionStrength::Aes256 => StandardSecurityHandler::aes_256_r5(),
72        }
73    }
74
75    /// Create encryption dictionary
76    pub fn create_encryption_dict(&self, file_id: Option<&[u8]>) -> Result<EncryptionDictionary> {
77        let handler = self.handler();
78
79        // AES-256 (R5) uses a completely different key derivation — handle separately
80        if matches!(self.strength, EncryptionStrength::Aes256) {
81            return self.create_aes256_encryption_dict(&handler, file_id);
82        }
83
84        // RC4 and AES-128 use the legacy MD5-based key derivation
85        let owner_hash = handler.compute_owner_hash(&self.owner_password, &self.user_password);
86        let user_hash = handler.compute_user_hash(
87            &self.user_password,
88            &owner_hash,
89            self.permissions,
90            file_id,
91        )?;
92
93        let enc_dict = match self.strength {
94            EncryptionStrength::Rc4_40bit => EncryptionDictionary::rc4_40bit(
95                owner_hash,
96                user_hash,
97                self.permissions,
98                file_id.map(|id| id.to_vec()),
99            ),
100            EncryptionStrength::Rc4_128bit => EncryptionDictionary::rc4_128bit(
101                owner_hash,
102                user_hash,
103                self.permissions,
104                file_id.map(|id| id.to_vec()),
105            ),
106            EncryptionStrength::Aes128 => EncryptionDictionary::aes_128(
107                owner_hash,
108                user_hash,
109                self.permissions,
110                file_id.map(|id| id.to_vec()),
111            ),
112            EncryptionStrength::Aes256 => unreachable!("handled above"),
113        };
114
115        Ok(enc_dict)
116    }
117
118    /// Create AES-256 (R5) encryption dictionary with SHA-256 key derivation.
119    fn create_aes256_encryption_dict(
120        &self,
121        handler: &StandardSecurityHandler,
122        file_id: Option<&[u8]>,
123    ) -> Result<EncryptionDictionary> {
124        let u_entry = handler.compute_r5_user_hash(&self.user_password)?;
125        let o_entry = handler.compute_r5_owner_hash(&self.owner_password, &self.user_password)?;
126
127        // Generate a random 32-byte file encryption key
128        let mut encryption_key = vec![0u8; 32];
129        use rand::Rng;
130        rand::rng().fill_bytes(&mut encryption_key);
131        let enc_key_obj = EncryptionKey::new(encryption_key.clone());
132
133        // Compute UE and OE entries (encrypted copies of the encryption key)
134        let ue_entry = handler.compute_r5_ue_entry(&self.user_password, &u_entry, &enc_key_obj)?;
135        let oe_entry =
136            handler.compute_r5_oe_entry(&self.owner_password, &o_entry, &encryption_key)?;
137
138        Ok(EncryptionDictionary::aes_256(
139            o_entry,
140            u_entry,
141            self.permissions,
142            file_id.map(|id| id.to_vec()),
143        )
144        .with_r5_entries(ue_entry, oe_entry))
145    }
146
147    /// Get encryption key
148    pub fn get_encryption_key(
149        &self,
150        enc_dict: &EncryptionDictionary,
151        file_id: Option<&[u8]>,
152    ) -> Result<EncryptionKey> {
153        let handler = self.handler();
154        handler.compute_encryption_key(&self.user_password, &enc_dict.o, self.permissions, file_id)
155    }
156}
157
158/// Encryption context for encrypting objects
159#[allow(dead_code)]
160pub struct EncryptionContext {
161    /// Security handler
162    handler: StandardSecurityHandler,
163    /// Encryption key
164    key: EncryptionKey,
165}
166
167#[allow(dead_code)]
168impl EncryptionContext {
169    /// Create new encryption context
170    pub fn new(handler: StandardSecurityHandler, key: EncryptionKey) -> Self {
171        Self { handler, key }
172    }
173
174    /// Encrypt a string
175    pub fn encrypt_string(&self, data: &[u8], obj_id: &ObjectId) -> Vec<u8> {
176        self.handler.encrypt_string(data, &self.key, obj_id)
177    }
178
179    /// Decrypt a string
180    pub fn decrypt_string(&self, data: &[u8], obj_id: &ObjectId) -> Vec<u8> {
181        self.handler.decrypt_string(data, &self.key, obj_id)
182    }
183
184    /// Encrypt a stream
185    pub fn encrypt_stream(&self, data: &[u8], obj_id: &ObjectId) -> Vec<u8> {
186        self.handler.encrypt_stream(data, &self.key, obj_id)
187    }
188
189    /// Decrypt a stream
190    pub fn decrypt_stream(&self, data: &[u8], obj_id: &ObjectId) -> Vec<u8> {
191        self.handler.decrypt_stream(data, &self.key, obj_id)
192    }
193}
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198
199    #[test]
200    fn test_document_encryption_new() {
201        let enc = DocumentEncryption::new(
202            "user123",
203            "owner456",
204            Permissions::all(),
205            EncryptionStrength::Rc4_128bit,
206        );
207
208        assert_eq!(enc.user_password.0, "user123");
209        assert_eq!(enc.owner_password.0, "owner456");
210    }
211
212    #[test]
213    fn test_with_passwords() {
214        let enc = DocumentEncryption::with_passwords("user", "owner");
215        assert_eq!(enc.user_password.0, "user");
216        assert_eq!(enc.owner_password.0, "owner");
217        assert!(enc.permissions.can_print());
218        assert!(enc.permissions.can_modify_contents());
219    }
220
221    #[test]
222    fn test_encryption_dict_creation() {
223        let enc = DocumentEncryption::new(
224            "test",
225            "owner",
226            Permissions::new(),
227            EncryptionStrength::Rc4_40bit,
228        );
229
230        let enc_dict = enc.create_encryption_dict(None).unwrap();
231        assert_eq!(enc_dict.v, 1);
232        assert_eq!(enc_dict.r, 2);
233        assert_eq!(enc_dict.length, Some(5));
234    }
235
236    #[test]
237    fn test_encryption_context() {
238        let handler = StandardSecurityHandler::rc4_40bit();
239        let key = EncryptionKey::new(vec![1, 2, 3, 4, 5]);
240        let ctx = EncryptionContext::new(handler, key);
241
242        let obj_id = ObjectId::new(1, 0);
243        let plaintext = b"Hello, World!";
244
245        let encrypted = ctx.encrypt_string(plaintext, &obj_id);
246        assert_ne!(encrypted, plaintext);
247
248        let decrypted = ctx.decrypt_string(&encrypted, &obj_id);
249        assert_eq!(decrypted, plaintext);
250    }
251
252    #[test]
253    fn test_encryption_strength_variants() {
254        let enc_40 = DocumentEncryption::new(
255            "user",
256            "owner",
257            Permissions::new(),
258            EncryptionStrength::Rc4_40bit,
259        );
260
261        let enc_128 = DocumentEncryption::new(
262            "user",
263            "owner",
264            Permissions::new(),
265            EncryptionStrength::Rc4_128bit,
266        );
267
268        // Check handlers
269        let _handler_40 = enc_40.handler();
270        let _handler_128 = enc_128.handler();
271
272        // Verify different encryption dictionary versions
273        let dict_40 = enc_40.create_encryption_dict(None).unwrap();
274        let dict_128 = enc_128.create_encryption_dict(None).unwrap();
275
276        assert_eq!(dict_40.v, 1);
277        assert_eq!(dict_40.r, 2);
278        assert_eq!(dict_40.length, Some(5));
279
280        assert_eq!(dict_128.v, 2);
281        assert_eq!(dict_128.r, 3);
282        assert_eq!(dict_128.length, Some(16));
283    }
284
285    #[test]
286    fn test_empty_passwords() {
287        let enc =
288            DocumentEncryption::new("", "", Permissions::all(), EncryptionStrength::Rc4_128bit);
289
290        assert_eq!(enc.user_password.0, "");
291        assert_eq!(enc.owner_password.0, "");
292
293        // Should still create valid encryption dictionary
294        let dict = enc.create_encryption_dict(None);
295        assert!(dict.is_ok());
296    }
297
298    #[test]
299    fn test_long_passwords() {
300        let long_user = "a".repeat(100);
301        let long_owner = "b".repeat(100);
302
303        let enc = DocumentEncryption::new(
304            &long_user,
305            &long_owner,
306            Permissions::new(),
307            EncryptionStrength::Rc4_128bit,
308        );
309
310        assert_eq!(enc.user_password.0.len(), 100);
311        assert_eq!(enc.owner_password.0.len(), 100);
312
313        let dict = enc.create_encryption_dict(None);
314        assert!(dict.is_ok());
315    }
316
317    #[test]
318    fn test_unicode_passwords() {
319        let enc = DocumentEncryption::new(
320            "contraseña",
321            "密码",
322            Permissions::all(),
323            EncryptionStrength::Rc4_40bit,
324        );
325
326        assert_eq!(enc.user_password.0, "contraseña");
327        assert_eq!(enc.owner_password.0, "密码");
328
329        let dict = enc.create_encryption_dict(None);
330        assert!(dict.is_ok());
331    }
332
333    #[test]
334    fn test_encryption_with_file_id() {
335        let enc = DocumentEncryption::new(
336            "user",
337            "owner",
338            Permissions::new(),
339            EncryptionStrength::Rc4_128bit,
340        );
341
342        let file_id = b"test_file_id_12345";
343        let dict = enc.create_encryption_dict(Some(file_id)).unwrap();
344
345        // Should be able to get encryption key with same file ID
346        let key = enc.get_encryption_key(&dict, Some(file_id));
347        assert!(key.is_ok());
348    }
349
350    #[test]
351    fn test_different_permissions() {
352        let perms_none = Permissions::new();
353        let perms_all = Permissions::all();
354        let mut perms_custom = Permissions::new();
355        perms_custom.set_print(true);
356        perms_custom.set_modify_contents(false);
357
358        let enc1 =
359            DocumentEncryption::new("user", "owner", perms_none, EncryptionStrength::Rc4_128bit);
360
361        let enc2 =
362            DocumentEncryption::new("user", "owner", perms_all, EncryptionStrength::Rc4_128bit);
363
364        let enc3 = DocumentEncryption::new(
365            "user",
366            "owner",
367            perms_custom,
368            EncryptionStrength::Rc4_128bit,
369        );
370
371        // Create encryption dictionaries
372        let _dict1 = enc1.create_encryption_dict(None).unwrap();
373        let _dict2 = enc2.create_encryption_dict(None).unwrap();
374        let _dict3 = enc3.create_encryption_dict(None).unwrap();
375
376        // Permissions should be encoded differently
377        // Note: p field contains encoded permissions as i32
378        // Different permission sets should have different values
379    }
380
381    #[test]
382    fn test_encryption_context_stream() {
383        let handler = StandardSecurityHandler::rc4_128bit();
384        let key = EncryptionKey::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
385        let ctx = EncryptionContext::new(handler, key);
386
387        let obj_id = ObjectId::new(5, 0);
388        let stream_data = b"This is a PDF stream content that needs encryption";
389
390        let encrypted = ctx.encrypt_stream(stream_data, &obj_id);
391        assert_ne!(encrypted, stream_data);
392
393        let decrypted = ctx.decrypt_stream(&encrypted, &obj_id);
394        assert_eq!(decrypted, stream_data);
395    }
396
397    #[test]
398    fn test_encryption_context_different_objects() {
399        let handler = StandardSecurityHandler::rc4_40bit();
400        let key = EncryptionKey::new(vec![1, 2, 3, 4, 5]);
401        let ctx = EncryptionContext::new(handler, key);
402
403        let obj_id1 = ObjectId::new(1, 0);
404        let obj_id2 = ObjectId::new(2, 0);
405        let plaintext = b"Test data";
406
407        let encrypted1 = ctx.encrypt_string(plaintext, &obj_id1);
408        let encrypted2 = ctx.encrypt_string(plaintext, &obj_id2);
409
410        // Same plaintext encrypted with different object IDs should produce different ciphertext
411        assert_ne!(encrypted1, encrypted2);
412
413        // But both should decrypt to the same plaintext
414        assert_eq!(ctx.decrypt_string(&encrypted1, &obj_id1), plaintext);
415        assert_eq!(ctx.decrypt_string(&encrypted2, &obj_id2), plaintext);
416    }
417
418    #[test]
419    fn test_get_encryption_key_consistency() {
420        let enc = DocumentEncryption::new(
421            "user123",
422            "owner456",
423            Permissions::all(),
424            EncryptionStrength::Rc4_128bit,
425        );
426
427        let file_id = b"consistent_file_id";
428        let dict = enc.create_encryption_dict(Some(file_id)).unwrap();
429
430        // Getting key multiple times should produce consistent results
431        let key1 = enc.get_encryption_key(&dict, Some(file_id));
432        let key2 = enc.get_encryption_key(&dict, Some(file_id));
433
434        // Both should succeed
435        assert!(key1.is_ok());
436        assert!(key2.is_ok());
437    }
438
439    #[test]
440    fn test_handler_selection() {
441        let enc_40 = DocumentEncryption::new(
442            "test",
443            "test",
444            Permissions::new(),
445            EncryptionStrength::Rc4_40bit,
446        );
447
448        let enc_128 = DocumentEncryption::new(
449            "test",
450            "test",
451            Permissions::new(),
452            EncryptionStrength::Rc4_128bit,
453        );
454
455        // Handlers should be different for different strengths
456        let _handler_40 = enc_40.handler();
457        let _handler_128 = enc_128.handler();
458
459        // Create dictionaries to verify correct configuration
460        let dict_40 = enc_40.create_encryption_dict(None).unwrap();
461        let dict_128 = enc_128.create_encryption_dict(None).unwrap();
462
463        // 40-bit should have length 5, 128-bit should have length 16
464        assert_eq!(dict_40.length, Some(5));
465        assert_eq!(dict_128.length, Some(16));
466    }
467}