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