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::{PdfError, 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, &u_entry)?;
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 = handler.compute_r5_oe_entry(
136            &self.owner_password,
137            &o_entry,
138            &u_entry,
139            &encryption_key,
140        )?;
141        let perms_entry = handler.compute_perms_entry(self.permissions, &enc_key_obj, true)?;
142
143        EncryptionDictionary::aes_256(
144            o_entry,
145            u_entry,
146            self.permissions,
147            file_id.map(|id| id.to_vec()),
148        )
149        .with_r5_entries(ue_entry, oe_entry)
150        .with_perms(perms_entry)
151    }
152
153    /// Get the object encryption key used to encrypt streams and strings.
154    ///
155    /// For RC4/AES-128 the key is derived from the password (ISO 32000-1 Algorithm 2).
156    /// For AES-256 (R5) the object key is the random file key **sealed in `/UE`**, not a
157    /// password-derived key — `create_aes256_encryption_dict` generates it randomly and
158    /// the reader recovers it via `recover_r5_encryption_key`. Deriving a password-based
159    /// key here would not match what the reader recovers, so encrypted content would
160    /// decrypt to garbage (issue #364).
161    pub fn get_encryption_key(
162        &self,
163        enc_dict: &EncryptionDictionary,
164        file_id: Option<&[u8]>,
165    ) -> Result<EncryptionKey> {
166        let handler = self.handler();
167        if matches!(self.strength, EncryptionStrength::Aes256) {
168            let ue = enc_dict.ue.as_deref().ok_or_else(|| {
169                PdfError::EncryptionError("AES-256 encryption dict missing UE entry".to_string())
170            })?;
171            return handler.recover_r5_encryption_key(&self.user_password, &enc_dict.u, ue);
172        }
173        handler.compute_encryption_key(&self.user_password, &enc_dict.o, self.permissions, file_id)
174    }
175}
176
177/// Encryption context for encrypting objects
178#[allow(dead_code)]
179pub struct EncryptionContext {
180    /// Security handler
181    handler: StandardSecurityHandler,
182    /// Encryption key
183    key: EncryptionKey,
184}
185
186#[allow(dead_code)]
187impl EncryptionContext {
188    /// Create new encryption context
189    pub fn new(handler: StandardSecurityHandler, key: EncryptionKey) -> Self {
190        Self { handler, key }
191    }
192
193    /// Encrypt a string
194    pub fn encrypt_string(&self, data: &[u8], obj_id: &ObjectId) -> Vec<u8> {
195        self.handler.encrypt_string(data, &self.key, obj_id)
196    }
197
198    /// Decrypt a string
199    pub fn decrypt_string(&self, data: &[u8], obj_id: &ObjectId) -> Vec<u8> {
200        self.handler.decrypt_string(data, &self.key, obj_id)
201    }
202
203    /// Encrypt a stream
204    pub fn encrypt_stream(&self, data: &[u8], obj_id: &ObjectId) -> Vec<u8> {
205        self.handler.encrypt_stream(data, &self.key, obj_id)
206    }
207
208    /// Decrypt a stream
209    pub fn decrypt_stream(&self, data: &[u8], obj_id: &ObjectId) -> Vec<u8> {
210        self.handler.decrypt_stream(data, &self.key, obj_id)
211    }
212}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217
218    #[test]
219    fn test_document_encryption_new() {
220        let enc = DocumentEncryption::new(
221            "user123",
222            "owner456",
223            Permissions::all(),
224            EncryptionStrength::Rc4_128bit,
225        );
226
227        assert_eq!(enc.user_password.0, "user123");
228        assert_eq!(enc.owner_password.0, "owner456");
229    }
230
231    #[test]
232    fn test_with_passwords() {
233        let enc = DocumentEncryption::with_passwords("user", "owner");
234        assert_eq!(enc.user_password.0, "user");
235        assert_eq!(enc.owner_password.0, "owner");
236        assert!(enc.permissions.can_print());
237        assert!(enc.permissions.can_modify_contents());
238    }
239
240    #[test]
241    fn test_encryption_dict_creation() {
242        let enc = DocumentEncryption::new(
243            "test",
244            "owner",
245            Permissions::new(),
246            EncryptionStrength::Rc4_40bit,
247        );
248
249        let enc_dict = enc.create_encryption_dict(None).unwrap();
250        assert_eq!(enc_dict.v, 1);
251        assert_eq!(enc_dict.r, 2);
252        assert_eq!(enc_dict.length, Some(5));
253    }
254
255    #[test]
256    fn test_encryption_context() {
257        let handler = StandardSecurityHandler::rc4_40bit();
258        let key = EncryptionKey::new(vec![1, 2, 3, 4, 5]);
259        let ctx = EncryptionContext::new(handler, key);
260
261        let obj_id = ObjectId::new(1, 0);
262        let plaintext = b"Hello, World!";
263
264        let encrypted = ctx.encrypt_string(plaintext, &obj_id);
265        assert_ne!(encrypted, plaintext);
266
267        let decrypted = ctx.decrypt_string(&encrypted, &obj_id);
268        assert_eq!(decrypted, plaintext);
269    }
270
271    #[test]
272    fn test_encryption_strength_variants() {
273        let enc_40 = DocumentEncryption::new(
274            "user",
275            "owner",
276            Permissions::new(),
277            EncryptionStrength::Rc4_40bit,
278        );
279
280        let enc_128 = DocumentEncryption::new(
281            "user",
282            "owner",
283            Permissions::new(),
284            EncryptionStrength::Rc4_128bit,
285        );
286
287        // Check handlers
288        let _handler_40 = enc_40.handler();
289        let _handler_128 = enc_128.handler();
290
291        // Verify different encryption dictionary versions
292        let dict_40 = enc_40.create_encryption_dict(None).unwrap();
293        let dict_128 = enc_128.create_encryption_dict(None).unwrap();
294
295        assert_eq!(dict_40.v, 1);
296        assert_eq!(dict_40.r, 2);
297        assert_eq!(dict_40.length, Some(5));
298
299        assert_eq!(dict_128.v, 2);
300        assert_eq!(dict_128.r, 3);
301        assert_eq!(dict_128.length, Some(16));
302    }
303
304    #[test]
305    fn test_empty_passwords() {
306        let enc =
307            DocumentEncryption::new("", "", Permissions::all(), EncryptionStrength::Rc4_128bit);
308
309        assert_eq!(enc.user_password.0, "");
310        assert_eq!(enc.owner_password.0, "");
311
312        // Should still create valid encryption dictionary
313        let dict = enc.create_encryption_dict(None);
314        assert!(dict.is_ok());
315    }
316
317    #[test]
318    fn test_long_passwords() {
319        let long_user = "a".repeat(100);
320        let long_owner = "b".repeat(100);
321
322        let enc = DocumentEncryption::new(
323            &long_user,
324            &long_owner,
325            Permissions::new(),
326            EncryptionStrength::Rc4_128bit,
327        );
328
329        assert_eq!(enc.user_password.0.len(), 100);
330        assert_eq!(enc.owner_password.0.len(), 100);
331
332        let dict = enc.create_encryption_dict(None);
333        assert!(dict.is_ok());
334    }
335
336    #[test]
337    fn test_unicode_passwords() {
338        let enc = DocumentEncryption::new(
339            "contraseña",
340            "密码",
341            Permissions::all(),
342            EncryptionStrength::Rc4_40bit,
343        );
344
345        assert_eq!(enc.user_password.0, "contraseña");
346        assert_eq!(enc.owner_password.0, "密码");
347
348        let dict = enc.create_encryption_dict(None);
349        assert!(dict.is_ok());
350    }
351
352    #[test]
353    fn test_encryption_with_file_id() {
354        let enc = DocumentEncryption::new(
355            "user",
356            "owner",
357            Permissions::new(),
358            EncryptionStrength::Rc4_128bit,
359        );
360
361        let file_id = b"test_file_id_12345";
362        let dict = enc.create_encryption_dict(Some(file_id)).unwrap();
363
364        // Should be able to get encryption key with same file ID
365        let key = enc.get_encryption_key(&dict, Some(file_id));
366        assert!(key.is_ok());
367    }
368
369    #[test]
370    fn test_different_permissions() {
371        let perms_none = Permissions::new();
372        let perms_all = Permissions::all();
373        let mut perms_custom = Permissions::new();
374        perms_custom.set_print(true);
375        perms_custom.set_modify_contents(false);
376
377        let enc1 =
378            DocumentEncryption::new("user", "owner", perms_none, EncryptionStrength::Rc4_128bit);
379
380        let enc2 =
381            DocumentEncryption::new("user", "owner", perms_all, EncryptionStrength::Rc4_128bit);
382
383        let enc3 = DocumentEncryption::new(
384            "user",
385            "owner",
386            perms_custom,
387            EncryptionStrength::Rc4_128bit,
388        );
389
390        // Create encryption dictionaries
391        let _dict1 = enc1.create_encryption_dict(None).unwrap();
392        let _dict2 = enc2.create_encryption_dict(None).unwrap();
393        let _dict3 = enc3.create_encryption_dict(None).unwrap();
394
395        // Permissions should be encoded differently
396        // Note: p field contains encoded permissions as i32
397        // Different permission sets should have different values
398    }
399
400    #[test]
401    fn test_encryption_context_stream() {
402        let handler = StandardSecurityHandler::rc4_128bit();
403        let key = EncryptionKey::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
404        let ctx = EncryptionContext::new(handler, key);
405
406        let obj_id = ObjectId::new(5, 0);
407        let stream_data = b"This is a PDF stream content that needs encryption";
408
409        let encrypted = ctx.encrypt_stream(stream_data, &obj_id);
410        assert_ne!(encrypted, stream_data);
411
412        let decrypted = ctx.decrypt_stream(&encrypted, &obj_id);
413        assert_eq!(decrypted, stream_data);
414    }
415
416    #[test]
417    fn test_encryption_context_different_objects() {
418        let handler = StandardSecurityHandler::rc4_40bit();
419        let key = EncryptionKey::new(vec![1, 2, 3, 4, 5]);
420        let ctx = EncryptionContext::new(handler, key);
421
422        let obj_id1 = ObjectId::new(1, 0);
423        let obj_id2 = ObjectId::new(2, 0);
424        let plaintext = b"Test data";
425
426        let encrypted1 = ctx.encrypt_string(plaintext, &obj_id1);
427        let encrypted2 = ctx.encrypt_string(plaintext, &obj_id2);
428
429        // Same plaintext encrypted with different object IDs should produce different ciphertext
430        assert_ne!(encrypted1, encrypted2);
431
432        // But both should decrypt to the same plaintext
433        assert_eq!(ctx.decrypt_string(&encrypted1, &obj_id1), plaintext);
434        assert_eq!(ctx.decrypt_string(&encrypted2, &obj_id2), plaintext);
435    }
436
437    #[test]
438    fn test_get_encryption_key_consistency() {
439        let enc = DocumentEncryption::new(
440            "user123",
441            "owner456",
442            Permissions::all(),
443            EncryptionStrength::Rc4_128bit,
444        );
445
446        let file_id = b"consistent_file_id";
447        let dict = enc.create_encryption_dict(Some(file_id)).unwrap();
448
449        // Getting key multiple times should produce consistent results
450        let key1 = enc.get_encryption_key(&dict, Some(file_id));
451        let key2 = enc.get_encryption_key(&dict, Some(file_id));
452
453        // Both should succeed
454        assert!(key1.is_ok());
455        assert!(key2.is_ok());
456    }
457
458    #[test]
459    fn test_handler_selection() {
460        let enc_40 = DocumentEncryption::new(
461            "test",
462            "test",
463            Permissions::new(),
464            EncryptionStrength::Rc4_40bit,
465        );
466
467        let enc_128 = DocumentEncryption::new(
468            "test",
469            "test",
470            Permissions::new(),
471            EncryptionStrength::Rc4_128bit,
472        );
473
474        // Handlers should be different for different strengths
475        let _handler_40 = enc_40.handler();
476        let _handler_128 = enc_128.handler();
477
478        // Create dictionaries to verify correct configuration
479        let dict_40 = enc_40.create_encryption_dict(None).unwrap();
480        let dict_128 = enc_128.create_encryption_dict(None).unwrap();
481
482        // 40-bit should have length 5, 128-bit should have length 16
483        assert_eq!(dict_40.length, Some(5));
484        assert_eq!(dict_128.length, Some(16));
485    }
486}