Skip to main content

oxidize_pdf/encryption/
object_encryption.rs

1//! Object encryption/decryption for PDF documents
2//!
3//! This module implements encryption and decryption of PDF objects
4//! with integration into the parser and writer according to ISO 32000-1:2008.
5
6use crate::encryption::{
7    CryptFilterManager, EmbeddedFileEncryption, EncryptionDictionary, EncryptionKey,
8    SecurityHandler, StandardSecurityHandler,
9};
10use crate::error::{PdfError, Result};
11use crate::objects::{Dictionary, Object, ObjectId, Stream};
12use std::sync::Arc;
13
14/// Object Encryptor for encrypting PDF objects
15pub struct ObjectEncryptor {
16    /// Crypt filter manager
17    filter_manager: Arc<CryptFilterManager>,
18    /// Encryption key
19    encryption_key: EncryptionKey,
20    /// Encrypt metadata flag
21    encrypt_metadata: bool,
22    /// Embedded file encryption handler
23    embedded_file_handler: Option<EmbeddedFileEncryption>,
24}
25
26impl ObjectEncryptor {
27    /// Create new object encryptor
28    pub fn new(
29        filter_manager: Arc<CryptFilterManager>,
30        encryption_key: EncryptionKey,
31        encrypt_metadata: bool,
32    ) -> Self {
33        Self {
34            filter_manager,
35            encryption_key,
36            encrypt_metadata,
37            embedded_file_handler: None,
38        }
39    }
40
41    /// Create new object encryptor with embedded file support
42    pub fn with_embedded_files(
43        filter_manager: Arc<CryptFilterManager>,
44        encryption_key: EncryptionKey,
45        encrypt_metadata: bool,
46        eff_filter: Option<String>,
47    ) -> Self {
48        let embedded_file_handler = Some(EmbeddedFileEncryption::new(
49            eff_filter,
50            encrypt_metadata,
51            filter_manager.clone(),
52        ));
53
54        Self {
55            filter_manager,
56            encryption_key,
57            encrypt_metadata,
58            embedded_file_handler,
59        }
60    }
61
62    /// Encrypt an object
63    pub fn encrypt_object(&self, object: &mut Object, obj_id: &ObjectId) -> Result<()> {
64        match object {
65            Object::String(s) => {
66                // Check if this is a metadata stream
67                if !self.should_encrypt_string(s) {
68                    return Ok(());
69                }
70
71                let encrypted = self.filter_manager.encrypt_string(
72                    s.as_bytes(),
73                    obj_id,
74                    None,
75                    &self.encryption_key,
76                )?;
77
78                // Encrypted data is binary — store as ByteString for hex output
79                *object = Object::ByteString(encrypted);
80            }
81            Object::ByteString(bytes) => {
82                let encrypted = self.filter_manager.encrypt_string(
83                    bytes,
84                    obj_id,
85                    None,
86                    &self.encryption_key,
87                )?;
88                *bytes = encrypted;
89            }
90            Object::Stream(dict, data) => {
91                // Create a temporary Stream object
92                let mut stream = Stream::with_dictionary(dict.clone(), data.clone());
93                self.encrypt_stream(&mut stream, obj_id)?;
94
95                // Update the Object with encrypted data
96                *object = Object::Stream(stream.dictionary().clone(), stream.data().to_vec());
97            }
98            Object::Dictionary(dict) => {
99                self.encrypt_dictionary(dict, obj_id)?;
100            }
101            Object::Array(array) => {
102                for item in array.iter_mut() {
103                    self.encrypt_object(item, obj_id)?;
104                }
105            }
106            Object::Reference(_) => {
107                // References are not encrypted
108            }
109            _ => {
110                // Other object types are not encrypted
111            }
112        }
113
114        Ok(())
115    }
116
117    /// Decrypt an object
118    pub fn decrypt_object(&self, object: &mut Object, obj_id: &ObjectId) -> Result<()> {
119        match object {
120            Object::String(s) => {
121                // Check if this is a metadata stream
122                if !self.should_encrypt_string(s) {
123                    return Ok(());
124                }
125
126                let decrypted = self.filter_manager.decrypt_string(
127                    s.as_bytes(),
128                    obj_id,
129                    None,
130                    &self.encryption_key,
131                )?;
132
133                *s = String::from_utf8_lossy(&decrypted).to_string();
134            }
135            Object::ByteString(bytes) => {
136                let decrypted = self.filter_manager.decrypt_string(
137                    bytes,
138                    obj_id,
139                    None,
140                    &self.encryption_key,
141                )?;
142                // Decrypted binary data may be valid UTF-8 text — restore as String
143                *object = Object::String(String::from_utf8_lossy(&decrypted).to_string());
144            }
145            Object::Stream(dict, data) => {
146                // Create a temporary Stream object
147                let mut stream = Stream::with_dictionary(dict.clone(), data.clone());
148                self.decrypt_stream(&mut stream, obj_id)?;
149
150                // Update the Object with decrypted data
151                *object = Object::Stream(stream.dictionary().clone(), stream.data().to_vec());
152            }
153            Object::Dictionary(dict) => {
154                self.decrypt_dictionary(dict, obj_id)?;
155            }
156            Object::Array(array) => {
157                for item in array.iter_mut() {
158                    self.decrypt_object(item, obj_id)?;
159                }
160            }
161            Object::Reference(_) => {
162                // References are not decrypted
163            }
164            _ => {
165                // Other object types are not decrypted
166            }
167        }
168
169        Ok(())
170    }
171
172    /// Encrypt a stream
173    fn encrypt_stream(&self, stream: &mut Stream, obj_id: &ObjectId) -> Result<()> {
174        // Check if stream should be encrypted
175        if !self.should_encrypt_stream(stream) {
176            return Ok(());
177        }
178
179        let encrypted_data = if let Some(ref handler) = self.embedded_file_handler {
180            // Use embedded file handler for special stream types
181            handler.process_stream_encryption(
182                stream.dictionary(),
183                stream.data(),
184                obj_id,
185                &self.encryption_key,
186                true, // encrypt
187            )?
188        } else {
189            // Use standard encryption
190            self.filter_manager.encrypt_stream(
191                stream.data(),
192                obj_id,
193                stream.dictionary(),
194                &self.encryption_key,
195            )?
196        };
197
198        *stream.data_mut() = encrypted_data;
199
200        // Update stream dictionary if needed
201        if !stream.dictionary().contains_key("Filter") {
202            stream
203                .dictionary_mut()
204                .set("Filter", Object::Name("Crypt".to_string()));
205        } else if let Some(Object::Array(filters)) = stream.dictionary_mut().get_mut("Filter") {
206            // Add Crypt filter to existing filters
207            filters.push(Object::Name("Crypt".to_string()));
208        }
209
210        Ok(())
211    }
212
213    /// Decrypt a stream
214    fn decrypt_stream(&self, stream: &mut Stream, obj_id: &ObjectId) -> Result<()> {
215        // Check if stream should be decrypted
216        if !self.should_encrypt_stream(stream) {
217            return Ok(());
218        }
219
220        let decrypted_data = if let Some(ref handler) = self.embedded_file_handler {
221            // Use embedded file handler for special stream types
222            handler.process_stream_encryption(
223                stream.dictionary(),
224                stream.data(),
225                obj_id,
226                &self.encryption_key,
227                false, // decrypt
228            )?
229        } else {
230            // Use standard decryption
231            self.filter_manager.decrypt_stream(
232                stream.data(),
233                obj_id,
234                stream.dictionary(),
235                &self.encryption_key,
236            )?
237        };
238
239        *stream.data_mut() = decrypted_data;
240
241        // Remove Crypt filter from dictionary if present
242        if let Some(Object::Array(filters)) = stream.dictionary_mut().get_mut("Filter") {
243            filters.retain(|f| {
244                if let Object::Name(name) = f {
245                    name != "Crypt"
246                } else {
247                    true
248                }
249            });
250
251            // If only Crypt filter was present, remove Filter entry
252            if filters.is_empty() {
253                stream.dictionary_mut().remove("Filter");
254            }
255        } else if let Some(Object::Name(name)) = stream.dictionary().get("Filter") {
256            if name == "Crypt" {
257                stream.dictionary_mut().remove("Filter");
258            }
259        }
260
261        Ok(())
262    }
263
264    /// Encrypt a dictionary
265    fn encrypt_dictionary(&self, dict: &mut Dictionary, obj_id: &ObjectId) -> Result<()> {
266        // Get all keys to avoid borrowing issues
267        let keys: Vec<String> = dict.keys().cloned().collect();
268
269        for key in keys {
270            // Skip certain dictionary entries
271            if self.should_skip_dictionary_key(&key) {
272                continue;
273            }
274
275            if let Some(value) = dict.get_mut(&key) {
276                self.encrypt_object(value, obj_id)?;
277            }
278        }
279
280        Ok(())
281    }
282
283    /// Decrypt a dictionary
284    fn decrypt_dictionary(&self, dict: &mut Dictionary, obj_id: &ObjectId) -> Result<()> {
285        // Get all keys to avoid borrowing issues
286        let keys: Vec<String> = dict.keys().cloned().collect();
287
288        for key in keys {
289            // Skip certain dictionary entries
290            if self.should_skip_dictionary_key(&key) {
291                continue;
292            }
293
294            if let Some(value) = dict.get_mut(&key) {
295                self.decrypt_object(value, obj_id)?;
296            }
297        }
298
299        Ok(())
300    }
301
302    /// Check if a string should be encrypted
303    fn should_encrypt_string(&self, _s: &str) -> bool {
304        // All strings are encrypted except in special cases
305        true
306    }
307
308    /// Check if a stream should be encrypted
309    fn should_encrypt_stream(&self, stream: &Stream) -> bool {
310        // Check if this is a metadata stream
311        if !self.encrypt_metadata {
312            if let Some(Object::Name(type_name)) = stream.dictionary().get("Type") {
313                if type_name == "Metadata" {
314                    return false;
315                }
316            }
317        }
318
319        // Check if stream is already encrypted
320        if let Some(filter) = stream.dictionary().get("Filter") {
321            match filter {
322                Object::Name(name) => {
323                    if name == "Crypt" {
324                        return false;
325                    }
326                }
327                Object::Array(filters) => {
328                    for f in filters {
329                        if let Object::Name(name) = f {
330                            if name == "Crypt" {
331                                return false;
332                            }
333                        }
334                    }
335                }
336                _ => {}
337            }
338        }
339
340        true
341    }
342
343    /// Check if a dictionary key should be skipped during encryption
344    fn should_skip_dictionary_key(&self, key: &str) -> bool {
345        // These keys should never be encrypted
346        matches!(
347            key,
348            "Length" | "Filter" | "DecodeParms" | "Encrypt" | "ID" | "O" | "U" | "P" | "Perms"
349        )
350    }
351}
352
353/// Integration with Document for encryption
354pub struct DocumentEncryption {
355    /// Encryption dictionary
356    pub encryption_dict: EncryptionDictionary,
357    /// Object encryptor
358    pub encryptor: ObjectEncryptor,
359}
360
361impl DocumentEncryption {
362    /// Create from encryption dictionary and password
363    pub fn new(
364        encryption_dict: EncryptionDictionary,
365        user_password: &str,
366        file_id: Option<&[u8]>,
367    ) -> Result<Self> {
368        // Create security handler based on revision
369        let handler: Box<dyn SecurityHandler> = match encryption_dict.r {
370            2 | 3 => Box::new(StandardSecurityHandler::rc4_128bit()),
371            4 => Box::new(StandardSecurityHandler {
372                revision: crate::encryption::SecurityHandlerRevision::R4,
373                key_length: encryption_dict.length.unwrap_or(16) as usize,
374            }),
375            5 => Box::new(StandardSecurityHandler::aes_256_r5()),
376            6 => Box::new(StandardSecurityHandler::aes_256_r6()),
377            _ => {
378                return Err(PdfError::EncryptionError(format!(
379                    "Unsupported encryption revision: {}",
380                    encryption_dict.r
381                )));
382            }
383        };
384
385        // Compute encryption key
386        let user_pwd = crate::encryption::UserPassword(user_password.to_string());
387        let encryption_key = if encryption_dict.r <= 4 {
388            StandardSecurityHandler {
389                revision: match encryption_dict.r {
390                    2 => crate::encryption::SecurityHandlerRevision::R2,
391                    3 => crate::encryption::SecurityHandlerRevision::R3,
392                    4 => crate::encryption::SecurityHandlerRevision::R4,
393                    _ => unreachable!(),
394                },
395                key_length: encryption_dict.length.unwrap_or(16) as usize,
396            }
397            .compute_encryption_key(
398                &user_pwd,
399                &encryption_dict.o,
400                encryption_dict.p,
401                file_id,
402            )?
403        } else {
404            // R5/R6 (AES-256) recover the file key from the UE/OE entries via
405            // `StandardSecurityHandler::recover_r5_encryption_key` /
406            // `recover_r6_encryption_key`, not from a password-derived key. This
407            // legacy constructor never implemented that, and returning a zero key
408            // would silently decrypt every object to garbage. Fail loudly instead
409            // (issue #380). The production reader path is `EncryptionHandler` in
410            // `src/parser/encryption_handler.rs`.
411            return Err(PdfError::EncryptionError(format!(
412                "DocumentEncryption::new does not support AES-256 (R{}); \
413                 use the reader's EncryptionHandler for R5/R6 key recovery",
414                encryption_dict.r
415            )));
416        };
417
418        // Create crypt filter manager
419        let mut filter_manager = CryptFilterManager::new(
420            handler,
421            encryption_dict
422                .stm_f
423                .as_ref()
424                .map(|f| match f {
425                    crate::encryption::StreamFilter::StdCF => "StdCF".to_string(),
426                    crate::encryption::StreamFilter::Identity => "Identity".to_string(),
427                    crate::encryption::StreamFilter::Custom(name) => name.clone(),
428                })
429                .unwrap_or_else(|| "StdCF".to_string()),
430            encryption_dict
431                .str_f
432                .as_ref()
433                .map(|f| match f {
434                    crate::encryption::StringFilter::StdCF => "StdCF".to_string(),
435                    crate::encryption::StringFilter::Identity => "Identity".to_string(),
436                    crate::encryption::StringFilter::Custom(name) => name.clone(),
437                })
438                .unwrap_or_else(|| "StdCF".to_string()),
439        );
440
441        // Add crypt filters from dictionary
442        if let Some(ref filters) = encryption_dict.cf {
443            for filter in filters {
444                filter_manager.add_filter(crate::encryption::FunctionalCryptFilter {
445                    name: filter.name.clone(),
446                    method: filter.method,
447                    length: filter.length,
448                    auth_event: crate::encryption::AuthEvent::DocOpen,
449                    recipients: None,
450                });
451            }
452        }
453
454        let encryptor = ObjectEncryptor::new(
455            Arc::new(filter_manager),
456            encryption_key,
457            encryption_dict.encrypt_metadata,
458        );
459
460        Ok(Self {
461            encryption_dict,
462            encryptor,
463        })
464    }
465
466    /// Encrypt all objects in a document
467    pub fn encrypt_objects(&self, objects: &mut [(ObjectId, Object)]) -> Result<()> {
468        for (obj_id, obj) in objects.iter_mut() {
469            // Skip encryption dictionary object
470            if self.is_encryption_dict_object(obj) {
471                continue;
472            }
473
474            self.encryptor.encrypt_object(obj, obj_id)?;
475        }
476
477        Ok(())
478    }
479
480    /// Decrypt all objects in a document
481    pub fn decrypt_objects(&self, objects: &mut [(ObjectId, Object)]) -> Result<()> {
482        for (obj_id, obj) in objects.iter_mut() {
483            // Skip encryption dictionary object
484            if self.is_encryption_dict_object(obj) {
485                continue;
486            }
487
488            self.encryptor.decrypt_object(obj, obj_id)?;
489        }
490
491        Ok(())
492    }
493
494    /// Check if object is the encryption dictionary
495    fn is_encryption_dict_object(&self, obj: &Object) -> bool {
496        if let Object::Dictionary(dict) = obj {
497            // Check if this is an encryption dictionary
498            if let Some(Object::Name(filter)) = dict.get("Filter") {
499                return filter == "Standard";
500            }
501        }
502        false
503    }
504}
505
506#[cfg(test)]
507mod tests {
508    use super::*;
509    use crate::encryption::Permissions;
510
511    fn create_test_encryptor() -> ObjectEncryptor {
512        let handler = Box::new(StandardSecurityHandler::rc4_128bit());
513        let mut filter_manager =
514            CryptFilterManager::new(handler, "StdCF".to_string(), "StdCF".to_string());
515
516        // Add the StdCF filter
517        filter_manager.add_filter(crate::encryption::FunctionalCryptFilter {
518            name: "StdCF".to_string(),
519            method: crate::encryption::CryptFilterMethod::V2,
520            length: Some(16),
521            auth_event: crate::encryption::AuthEvent::DocOpen,
522            recipients: None,
523        });
524
525        let encryption_key = EncryptionKey::new(vec![0u8; 16]);
526
527        ObjectEncryptor::new(Arc::new(filter_manager), encryption_key, true)
528    }
529
530    #[test]
531    fn test_encrypt_string_object() {
532        let encryptor = create_test_encryptor();
533        let obj_id = ObjectId::new(1, 0);
534
535        let mut obj = Object::String("Test string".to_string());
536        let original = obj.clone();
537
538        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
539
540        // String should be different after encryption
541        assert_ne!(obj, original);
542    }
543
544    #[test]
545    fn test_encrypt_array_object() {
546        let encryptor = create_test_encryptor();
547        let obj_id = ObjectId::new(1, 0);
548
549        let mut obj = Object::Array(vec![
550            Object::String("String 1".to_string()),
551            Object::Integer(42),
552            Object::String("String 2".to_string()),
553        ]);
554
555        let original = obj.clone();
556        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
557
558        // Array should be different (strings encrypted)
559        assert_ne!(obj, original);
560
561        // Check that integers are not encrypted
562        if let Object::Array(array) = &obj {
563            assert_eq!(array[1], Object::Integer(42));
564        }
565    }
566
567    #[test]
568    fn test_encrypt_dictionary_object() {
569        let encryptor = create_test_encryptor();
570        let obj_id = ObjectId::new(1, 0);
571
572        let mut dict = Dictionary::new();
573        dict.set("Title", Object::String("Test Title".to_string()));
574        dict.set("Length", Object::Integer(100)); // Should be skipped
575        dict.set("Filter", Object::Name("FlateDecode".to_string())); // Should be skipped
576
577        let mut obj = Object::Dictionary(dict);
578        let original = obj.clone();
579
580        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
581
582        // Dictionary should be different
583        assert_ne!(obj, original);
584
585        // Check that skipped keys are not encrypted
586        if let Object::Dictionary(dict) = &obj {
587            assert_eq!(dict.get("Length"), Some(&Object::Integer(100)));
588            assert_eq!(
589                dict.get("Filter"),
590                Some(&Object::Name("FlateDecode".to_string()))
591            );
592        }
593    }
594
595    #[test]
596    fn test_encrypt_stream_object() {
597        let encryptor = create_test_encryptor();
598        let obj_id = ObjectId::new(1, 0);
599
600        let dict = Dictionary::new();
601        let data = b"Stream data content".to_vec();
602        let original_data = data.clone();
603
604        let mut obj = Object::Stream(dict, data);
605        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
606
607        if let Object::Stream(dict, data) = &obj {
608            // Data should be encrypted
609            assert_ne!(data, &original_data);
610
611            // Filter should be added
612            assert_eq!(dict.get("Filter"), Some(&Object::Name("Crypt".to_string())));
613        }
614    }
615
616    #[test]
617    fn test_skip_metadata_stream() {
618        let handler = Box::new(StandardSecurityHandler::rc4_128bit());
619        let filter_manager =
620            CryptFilterManager::new(handler, "StdCF".to_string(), "StdCF".to_string());
621
622        let encryption_key = EncryptionKey::new(vec![0u8; 16]);
623
624        let encryptor = ObjectEncryptor::new(
625            Arc::new(filter_manager),
626            encryption_key,
627            false, // Don't encrypt metadata
628        );
629
630        let obj_id = ObjectId::new(1, 0);
631
632        let mut dict = Dictionary::new();
633        dict.set("Type", Object::Name("Metadata".to_string()));
634        let data = b"Metadata content".to_vec();
635        let original_data = data.clone();
636
637        let mut obj = Object::Stream(dict, data);
638
639        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
640
641        if let Object::Stream(_, data) = &obj {
642            // Metadata should not be encrypted
643            assert_eq!(data, &original_data);
644        }
645    }
646
647    #[test]
648    fn test_should_skip_dictionary_key() {
649        let encryptor = create_test_encryptor();
650
651        assert!(encryptor.should_skip_dictionary_key("Length"));
652        assert!(encryptor.should_skip_dictionary_key("Filter"));
653        assert!(encryptor.should_skip_dictionary_key("DecodeParms"));
654        assert!(encryptor.should_skip_dictionary_key("Encrypt"));
655        assert!(encryptor.should_skip_dictionary_key("ID"));
656        assert!(encryptor.should_skip_dictionary_key("O"));
657        assert!(encryptor.should_skip_dictionary_key("U"));
658        assert!(encryptor.should_skip_dictionary_key("P"));
659        assert!(encryptor.should_skip_dictionary_key("Perms"));
660
661        assert!(!encryptor.should_skip_dictionary_key("Title"));
662        assert!(!encryptor.should_skip_dictionary_key("Author"));
663        assert!(!encryptor.should_skip_dictionary_key("Subject"));
664    }
665
666    #[test]
667    fn test_decrypt_object_reverses_encryption() {
668        let encryptor = create_test_encryptor();
669        let obj_id = ObjectId::new(1, 0);
670
671        let original_string = "Test content for encryption";
672        let mut obj = Object::String(original_string.to_string());
673        let original = obj.clone();
674
675        // Encrypt
676        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
677
678        // Verify it's encrypted (different from original)
679        assert_ne!(obj, original);
680
681        // Decrypt
682        encryptor.decrypt_object(&mut obj, &obj_id).unwrap();
683
684        // Due to String::from_utf8_lossy, we may not get exact original back
685        // In production, PDF strings should handle binary data properly
686        // For now, just verify that encrypt/decrypt complete without errors
687        if let Object::String(s) = &obj {
688            // At minimum, verify it's a valid string
689            assert!(!s.is_empty());
690        }
691    }
692
693    #[test]
694    fn test_reference_object_not_encrypted() {
695        let encryptor = create_test_encryptor();
696        let obj_id = ObjectId::new(1, 0);
697
698        let mut obj = Object::Reference(ObjectId::new(5, 0));
699        let original = obj.clone();
700
701        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
702
703        // Reference should remain unchanged
704        assert_eq!(obj, original);
705    }
706
707    #[test]
708    fn test_already_encrypted_stream_skipped() {
709        let encryptor = create_test_encryptor();
710        let obj_id = ObjectId::new(1, 0);
711
712        let mut dict = Dictionary::new();
713        dict.set("Filter", Object::Name("Crypt".to_string()));
714        let data = b"Already encrypted data".to_vec();
715        let original_data = data.clone();
716
717        let mut obj = Object::Stream(dict, data);
718
719        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
720
721        if let Object::Stream(_, data) = &obj {
722            // Data should remain unchanged
723            assert_eq!(data, &original_data);
724        }
725    }
726
727    #[test]
728    fn test_document_encryption_creation() {
729        let encryption_dict = crate::encryption::EncryptionDictionary::rc4_128bit(
730            vec![0u8; 32],
731            vec![1u8; 32],
732            Permissions::all(),
733            None,
734        );
735
736        let doc_encryption =
737            DocumentEncryption::new(encryption_dict, "user_password", Some(b"file_id"));
738
739        assert!(doc_encryption.is_ok());
740    }
741
742    #[test]
743    fn test_is_encryption_dict_object() {
744        let encryption_dict = crate::encryption::EncryptionDictionary::rc4_128bit(
745            vec![0u8; 32],
746            vec![1u8; 32],
747            Permissions::all(),
748            None,
749        );
750
751        let doc_encryption =
752            DocumentEncryption::new(encryption_dict, "user_password", Some(b"file_id")).unwrap();
753
754        let mut dict = Dictionary::new();
755        dict.set("Filter", Object::Name("Standard".to_string()));
756        let obj = Object::Dictionary(dict);
757
758        assert!(doc_encryption.is_encryption_dict_object(&obj));
759
760        let normal_obj = Object::String("Not an encryption dict".to_string());
761        assert!(!doc_encryption.is_encryption_dict_object(&normal_obj));
762    }
763
764    #[test]
765    fn test_with_embedded_files_constructor() {
766        let handler = Box::new(StandardSecurityHandler::rc4_128bit());
767        let filter_manager =
768            CryptFilterManager::new(handler, "StdCF".to_string(), "StdCF".to_string());
769
770        let encryption_key = EncryptionKey::new(vec![0u8; 16]);
771
772        let encryptor = ObjectEncryptor::with_embedded_files(
773            Arc::new(filter_manager),
774            encryption_key,
775            true,
776            Some("StdCF".to_string()),
777        );
778
779        // Verify embedded file handler was created
780        assert!(encryptor.embedded_file_handler.is_some());
781    }
782
783    #[test]
784    fn test_with_embedded_files_no_filter() {
785        let handler = Box::new(StandardSecurityHandler::rc4_128bit());
786        let filter_manager =
787            CryptFilterManager::new(handler, "StdCF".to_string(), "StdCF".to_string());
788
789        let encryption_key = EncryptionKey::new(vec![0u8; 16]);
790
791        let encryptor = ObjectEncryptor::with_embedded_files(
792            Arc::new(filter_manager),
793            encryption_key,
794            false,
795            None,
796        );
797
798        // Handler should still be created even without explicit filter
799        assert!(encryptor.embedded_file_handler.is_some());
800    }
801
802    #[test]
803    fn test_decrypt_string_object() {
804        let encryptor = create_test_encryptor();
805        let obj_id = ObjectId::new(1, 0);
806
807        // First encrypt a string
808        let mut obj = Object::String("Test string for decryption".to_string());
809        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
810
811        // Then decrypt it
812        encryptor.decrypt_object(&mut obj, &obj_id).unwrap();
813
814        // Verify it's a valid string after round-trip
815        if let Object::String(s) = &obj {
816            assert!(!s.is_empty());
817        } else {
818            panic!("Expected String object");
819        }
820    }
821
822    #[test]
823    fn test_decrypt_array_object() {
824        let encryptor = create_test_encryptor();
825        let obj_id = ObjectId::new(1, 0);
826
827        let mut obj = Object::Array(vec![
828            Object::String("Test 1".to_string()),
829            Object::Integer(123),
830            Object::String("Test 2".to_string()),
831        ]);
832
833        // Encrypt
834        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
835
836        // Decrypt
837        encryptor.decrypt_object(&mut obj, &obj_id).unwrap();
838
839        // Verify structure is preserved
840        if let Object::Array(array) = &obj {
841            assert_eq!(array.len(), 3);
842            assert_eq!(array[1], Object::Integer(123));
843        } else {
844            panic!("Expected Array object");
845        }
846    }
847
848    #[test]
849    fn test_decrypt_dictionary_object() {
850        let encryptor = create_test_encryptor();
851        let obj_id = ObjectId::new(1, 0);
852
853        let mut dict = Dictionary::new();
854        dict.set("Title", Object::String("Test Title".to_string()));
855        dict.set("Count", Object::Integer(42));
856        dict.set("Length", Object::Integer(100)); // Should be skipped
857
858        let mut obj = Object::Dictionary(dict);
859
860        // Encrypt
861        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
862
863        // Decrypt
864        encryptor.decrypt_object(&mut obj, &obj_id).unwrap();
865
866        // Verify skipped keys are unchanged
867        if let Object::Dictionary(dict) = &obj {
868            assert_eq!(dict.get("Count"), Some(&Object::Integer(42)));
869            assert_eq!(dict.get("Length"), Some(&Object::Integer(100)));
870        } else {
871            panic!("Expected Dictionary object");
872        }
873    }
874
875    #[test]
876    fn test_decrypt_reference_not_changed() {
877        let encryptor = create_test_encryptor();
878        let obj_id = ObjectId::new(1, 0);
879
880        let mut obj = Object::Reference(ObjectId::new(10, 0));
881        let original = obj.clone();
882
883        encryptor.decrypt_object(&mut obj, &obj_id).unwrap();
884
885        // Reference should remain unchanged
886        assert_eq!(obj, original);
887    }
888
889    #[test]
890    fn test_decrypt_other_types_not_changed() {
891        let encryptor = create_test_encryptor();
892        let obj_id = ObjectId::new(1, 0);
893
894        // Test Integer
895        let mut obj = Object::Integer(42);
896        let original = obj.clone();
897        encryptor.decrypt_object(&mut obj, &obj_id).unwrap();
898        assert_eq!(obj, original);
899
900        // Test Real
901        let mut obj = Object::Real(3.14);
902        let original = obj.clone();
903        encryptor.decrypt_object(&mut obj, &obj_id).unwrap();
904        assert_eq!(obj, original);
905
906        // Test Boolean
907        let mut obj = Object::Boolean(true);
908        let original = obj.clone();
909        encryptor.decrypt_object(&mut obj, &obj_id).unwrap();
910        assert_eq!(obj, original);
911
912        // Test Null
913        let mut obj = Object::Null;
914        let original = obj.clone();
915        encryptor.decrypt_object(&mut obj, &obj_id).unwrap();
916        assert_eq!(obj, original);
917
918        // Test Name
919        let mut obj = Object::Name("TestName".to_string());
920        let original = obj.clone();
921        encryptor.decrypt_object(&mut obj, &obj_id).unwrap();
922        assert_eq!(obj, original);
923    }
924
925    #[test]
926    fn test_encrypt_other_types_not_changed() {
927        let encryptor = create_test_encryptor();
928        let obj_id = ObjectId::new(1, 0);
929
930        // Test Integer
931        let mut obj = Object::Integer(999);
932        let original = obj.clone();
933        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
934        assert_eq!(obj, original);
935
936        // Test Real
937        let mut obj = Object::Real(2.718);
938        let original = obj.clone();
939        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
940        assert_eq!(obj, original);
941
942        // Test Boolean
943        let mut obj = Object::Boolean(false);
944        let original = obj.clone();
945        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
946        assert_eq!(obj, original);
947
948        // Test Null
949        let mut obj = Object::Null;
950        let original = obj.clone();
951        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
952        assert_eq!(obj, original);
953
954        // Test Name
955        let mut obj = Object::Name("AnotherName".to_string());
956        let original = obj.clone();
957        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
958        assert_eq!(obj, original);
959    }
960
961    #[test]
962    fn test_should_encrypt_stream_with_array_filter_containing_crypt() {
963        let encryptor = create_test_encryptor();
964
965        let mut dict = Dictionary::new();
966        dict.set(
967            "Filter",
968            Object::Array(vec![
969                Object::Name("FlateDecode".to_string()),
970                Object::Name("Crypt".to_string()),
971            ]),
972        );
973
974        let stream = Stream::with_dictionary(dict, vec![1, 2, 3]);
975
976        // Should return false because Crypt is in the filter array
977        assert!(!encryptor.should_encrypt_stream(&stream));
978    }
979
980    #[test]
981    fn test_should_encrypt_stream_with_array_filter_without_crypt() {
982        let encryptor = create_test_encryptor();
983
984        let mut dict = Dictionary::new();
985        dict.set(
986            "Filter",
987            Object::Array(vec![
988                Object::Name("FlateDecode".to_string()),
989                Object::Name("ASCII85Decode".to_string()),
990            ]),
991        );
992
993        let stream = Stream::with_dictionary(dict, vec![1, 2, 3]);
994
995        // Should return true because no Crypt filter
996        assert!(encryptor.should_encrypt_stream(&stream));
997    }
998
999    #[test]
1000    fn test_should_encrypt_stream_with_non_name_filter() {
1001        let encryptor = create_test_encryptor();
1002
1003        let mut dict = Dictionary::new();
1004        // Invalid filter type (Integer instead of Name or Array)
1005        dict.set("Filter", Object::Integer(123));
1006
1007        let stream = Stream::with_dictionary(dict, vec![1, 2, 3]);
1008
1009        // Should return true because no valid Crypt filter found
1010        assert!(encryptor.should_encrypt_stream(&stream));
1011    }
1012
1013    #[test]
1014    fn test_should_encrypt_string_always_true() {
1015        let encryptor = create_test_encryptor();
1016
1017        assert!(encryptor.should_encrypt_string("any string"));
1018        assert!(encryptor.should_encrypt_string(""));
1019        assert!(encryptor.should_encrypt_string("special chars: !@#$%"));
1020    }
1021
1022    #[test]
1023    fn test_encrypt_objects_skips_encryption_dict() {
1024        let mut encryption_dict = crate::encryption::EncryptionDictionary::rc4_128bit(
1025            vec![0u8; 32],
1026            vec![1u8; 32],
1027            Permissions::all(),
1028            None,
1029        );
1030
1031        // Add the crypt filter that will be needed
1032        encryption_dict.cf = Some(vec![crate::encryption::CryptFilter {
1033            name: "StdCF".to_string(),
1034            method: crate::encryption::CryptFilterMethod::V2,
1035            length: Some(16),
1036        }]);
1037
1038        let doc_encryption =
1039            DocumentEncryption::new(encryption_dict, "user_password", Some(b"file_id")).unwrap();
1040
1041        // Create an encryption dictionary object
1042        let mut encrypt_dict = Dictionary::new();
1043        encrypt_dict.set("Filter", Object::Name("Standard".to_string()));
1044        encrypt_dict.set("V", Object::Integer(4));
1045
1046        let mut objects = vec![
1047            (
1048                ObjectId::new(1, 0),
1049                Object::Dictionary(encrypt_dict.clone()),
1050            ),
1051            (
1052                ObjectId::new(2, 0),
1053                Object::String("Normal string".to_string()),
1054            ),
1055        ];
1056
1057        let original_encrypt_dict = objects[0].1.clone();
1058
1059        doc_encryption.encrypt_objects(&mut objects).unwrap();
1060
1061        // Encryption dict should be unchanged
1062        assert_eq!(objects[0].1, original_encrypt_dict);
1063
1064        // Normal string should be encrypted (different from original)
1065        assert_ne!(objects[1].1, Object::String("Normal string".to_string()));
1066    }
1067
1068    #[test]
1069    fn test_decrypt_objects_skips_encryption_dict() {
1070        let mut encryption_dict = crate::encryption::EncryptionDictionary::rc4_128bit(
1071            vec![0u8; 32],
1072            vec![1u8; 32],
1073            Permissions::all(),
1074            None,
1075        );
1076
1077        // Add the crypt filter that will be needed
1078        encryption_dict.cf = Some(vec![crate::encryption::CryptFilter {
1079            name: "StdCF".to_string(),
1080            method: crate::encryption::CryptFilterMethod::V2,
1081            length: Some(16),
1082        }]);
1083
1084        let doc_encryption =
1085            DocumentEncryption::new(encryption_dict, "user_password", Some(b"file_id")).unwrap();
1086
1087        // Create an encryption dictionary object
1088        let mut encrypt_dict = Dictionary::new();
1089        encrypt_dict.set("Filter", Object::Name("Standard".to_string()));
1090        encrypt_dict.set("V", Object::Integer(4));
1091
1092        let mut objects = vec![
1093            (
1094                ObjectId::new(1, 0),
1095                Object::Dictionary(encrypt_dict.clone()),
1096            ),
1097            (
1098                ObjectId::new(2, 0),
1099                Object::String("encrypted content".to_string()),
1100            ),
1101        ];
1102
1103        let original_encrypt_dict = objects[0].1.clone();
1104
1105        doc_encryption.decrypt_objects(&mut objects).unwrap();
1106
1107        // Encryption dict should be unchanged
1108        assert_eq!(objects[0].1, original_encrypt_dict);
1109    }
1110
1111    #[test]
1112    fn test_document_encryption_unsupported_revision() {
1113        let mut encryption_dict = crate::encryption::EncryptionDictionary::rc4_128bit(
1114            vec![0u8; 32],
1115            vec![1u8; 32],
1116            Permissions::all(),
1117            None,
1118        );
1119        encryption_dict.r = 99; // Unsupported revision
1120
1121        let result = DocumentEncryption::new(encryption_dict, "user_password", Some(b"file_id"));
1122
1123        assert!(result.is_err());
1124        if let Err(PdfError::EncryptionError(msg)) = result {
1125            assert!(msg.contains("Unsupported encryption revision"));
1126        }
1127    }
1128
1129    #[test]
1130    fn test_document_encryption_r2() {
1131        let mut encryption_dict = crate::encryption::EncryptionDictionary::rc4_128bit(
1132            vec![0u8; 32],
1133            vec![1u8; 32],
1134            Permissions::all(),
1135            None,
1136        );
1137        encryption_dict.r = 2;
1138
1139        let result = DocumentEncryption::new(encryption_dict, "user_password", Some(b"file_id"));
1140        assert!(result.is_ok());
1141    }
1142
1143    #[test]
1144    fn test_document_encryption_r4() {
1145        let mut encryption_dict = crate::encryption::EncryptionDictionary::rc4_128bit(
1146            vec![0u8; 32],
1147            vec![1u8; 32],
1148            Permissions::all(),
1149            None,
1150        );
1151        encryption_dict.r = 4;
1152        encryption_dict.length = Some(16);
1153
1154        let result = DocumentEncryption::new(encryption_dict, "user_password", Some(b"file_id"));
1155        assert!(result.is_ok());
1156    }
1157
1158    #[test]
1159    fn test_document_encryption_r5_rejects_aes256() {
1160        // This legacy constructor never implemented R5/R6 key recovery and used
1161        // to return a zero key, silently decrypting every object to garbage.
1162        // It now fails loudly (issue #380).
1163        let mut encryption_dict = crate::encryption::EncryptionDictionary::rc4_128bit(
1164            vec![0u8; 32],
1165            vec![1u8; 32],
1166            Permissions::all(),
1167            None,
1168        );
1169        encryption_dict.r = 5;
1170
1171        let result = DocumentEncryption::new(encryption_dict, "user_password", Some(b"file_id"));
1172        assert!(result.is_err(), "R5 must be rejected, not zero-keyed");
1173    }
1174
1175    #[test]
1176    fn test_document_encryption_r6_rejects_aes256() {
1177        let mut encryption_dict = crate::encryption::EncryptionDictionary::rc4_128bit(
1178            vec![0u8; 32],
1179            vec![1u8; 32],
1180            Permissions::all(),
1181            None,
1182        );
1183        encryption_dict.r = 6;
1184
1185        let result = DocumentEncryption::new(encryption_dict, "user_password", Some(b"file_id"));
1186        assert!(result.is_err(), "R6 must be rejected, not zero-keyed");
1187    }
1188
1189    #[test]
1190    fn test_is_encryption_dict_object_not_dict() {
1191        let encryption_dict = crate::encryption::EncryptionDictionary::rc4_128bit(
1192            vec![0u8; 32],
1193            vec![1u8; 32],
1194            Permissions::all(),
1195            None,
1196        );
1197
1198        let doc_encryption =
1199            DocumentEncryption::new(encryption_dict, "user_password", Some(b"file_id")).unwrap();
1200
1201        // Non-dictionary objects should return false
1202        assert!(!doc_encryption.is_encryption_dict_object(&Object::Integer(42)));
1203        assert!(!doc_encryption.is_encryption_dict_object(&Object::Null));
1204        assert!(!doc_encryption.is_encryption_dict_object(&Object::Array(vec![Object::Integer(1)])));
1205    }
1206
1207    #[test]
1208    fn test_is_encryption_dict_object_dict_without_filter() {
1209        let encryption_dict = crate::encryption::EncryptionDictionary::rc4_128bit(
1210            vec![0u8; 32],
1211            vec![1u8; 32],
1212            Permissions::all(),
1213            None,
1214        );
1215
1216        let doc_encryption =
1217            DocumentEncryption::new(encryption_dict, "user_password", Some(b"file_id")).unwrap();
1218
1219        // Dictionary without Filter key should return false
1220        let mut dict = Dictionary::new();
1221        dict.set("Type", Object::Name("Catalog".to_string()));
1222        let obj = Object::Dictionary(dict);
1223
1224        assert!(!doc_encryption.is_encryption_dict_object(&obj));
1225    }
1226
1227    #[test]
1228    fn test_is_encryption_dict_object_dict_with_different_filter() {
1229        let encryption_dict = crate::encryption::EncryptionDictionary::rc4_128bit(
1230            vec![0u8; 32],
1231            vec![1u8; 32],
1232            Permissions::all(),
1233            None,
1234        );
1235
1236        let doc_encryption =
1237            DocumentEncryption::new(encryption_dict, "user_password", Some(b"file_id")).unwrap();
1238
1239        // Dictionary with non-Standard Filter should return false
1240        let mut dict = Dictionary::new();
1241        dict.set("Filter", Object::Name("FlateDecode".to_string()));
1242        let obj = Object::Dictionary(dict);
1243
1244        assert!(!doc_encryption.is_encryption_dict_object(&obj));
1245    }
1246
1247    #[test]
1248    fn test_nested_array_encryption() {
1249        let encryptor = create_test_encryptor();
1250        let obj_id = ObjectId::new(1, 0);
1251
1252        // Create nested arrays with strings
1253        let mut obj = Object::Array(vec![
1254            Object::Array(vec![
1255                Object::String("Nested 1".to_string()),
1256                Object::String("Nested 2".to_string()),
1257            ]),
1258            Object::String("Outer".to_string()),
1259        ]);
1260
1261        let original = obj.clone();
1262
1263        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
1264
1265        // Should be different (strings encrypted)
1266        assert_ne!(obj, original);
1267
1268        // Verify structure preserved
1269        if let Object::Array(outer) = &obj {
1270            assert_eq!(outer.len(), 2);
1271            if let Object::Array(inner) = &outer[0] {
1272                assert_eq!(inner.len(), 2);
1273            } else {
1274                panic!("Expected nested array");
1275            }
1276        }
1277    }
1278
1279    #[test]
1280    fn test_nested_dictionary_encryption() {
1281        let encryptor = create_test_encryptor();
1282        let obj_id = ObjectId::new(1, 0);
1283
1284        let mut inner_dict = Dictionary::new();
1285        inner_dict.set("InnerTitle", Object::String("Inner value".to_string()));
1286
1287        let mut outer_dict = Dictionary::new();
1288        outer_dict.set("OuterTitle", Object::String("Outer value".to_string()));
1289        outer_dict.set("Nested", Object::Dictionary(inner_dict));
1290
1291        let mut obj = Object::Dictionary(outer_dict);
1292        let original = obj.clone();
1293
1294        encryptor.encrypt_object(&mut obj, &obj_id).unwrap();
1295
1296        // Should be different (strings encrypted)
1297        assert_ne!(obj, original);
1298
1299        // Verify structure preserved
1300        if let Object::Dictionary(dict) = &obj {
1301            assert!(dict.contains_key("OuterTitle"));
1302            assert!(dict.contains_key("Nested"));
1303            if let Some(Object::Dictionary(nested)) = dict.get("Nested") {
1304                assert!(nested.contains_key("InnerTitle"));
1305            } else {
1306                panic!("Expected nested dictionary");
1307            }
1308        }
1309    }
1310
1311    #[test]
1312    fn test_document_encryption_with_crypt_filters() {
1313        let mut encryption_dict = crate::encryption::EncryptionDictionary::rc4_128bit(
1314            vec![0u8; 32],
1315            vec![1u8; 32],
1316            Permissions::all(),
1317            None,
1318        );
1319
1320        // Add crypt filters
1321        encryption_dict.cf = Some(vec![crate::encryption::CryptFilter {
1322            name: "StdCF".to_string(),
1323            method: crate::encryption::CryptFilterMethod::V2,
1324            length: Some(16),
1325        }]);
1326
1327        let result = DocumentEncryption::new(encryption_dict, "user_password", Some(b"file_id"));
1328        assert!(result.is_ok());
1329    }
1330
1331    #[test]
1332    fn test_document_encryption_with_custom_stm_str_filters() {
1333        let mut encryption_dict = crate::encryption::EncryptionDictionary::rc4_128bit(
1334            vec![0u8; 32],
1335            vec![1u8; 32],
1336            Permissions::all(),
1337            None,
1338        );
1339
1340        encryption_dict.stm_f = Some(crate::encryption::StreamFilter::Identity);
1341        encryption_dict.str_f = Some(crate::encryption::StringFilter::Identity);
1342
1343        let result = DocumentEncryption::new(encryption_dict, "user_password", Some(b"file_id"));
1344        assert!(result.is_ok());
1345    }
1346
1347    #[test]
1348    fn test_document_encryption_with_custom_filter_names() {
1349        let mut encryption_dict = crate::encryption::EncryptionDictionary::rc4_128bit(
1350            vec![0u8; 32],
1351            vec![1u8; 32],
1352            Permissions::all(),
1353            None,
1354        );
1355
1356        encryption_dict.stm_f = Some(crate::encryption::StreamFilter::Custom(
1357            "CustomStm".to_string(),
1358        ));
1359        encryption_dict.str_f = Some(crate::encryption::StringFilter::Custom(
1360            "CustomStr".to_string(),
1361        ));
1362
1363        let result = DocumentEncryption::new(encryption_dict, "user_password", Some(b"file_id"));
1364        assert!(result.is_ok());
1365    }
1366}