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