oxidize_pdf/parser/
encryption_handler.rs

1//! PDF encryption detection and password handling
2//!
3//! This module provides functionality to detect encrypted PDFs and handle password-based
4//! decryption according to ISO 32000-1 Chapter 7.6.
5
6use super::objects::PdfDictionary;
7use super::{ParseError, ParseResult};
8use crate::encryption::{EncryptionKey, Permissions, StandardSecurityHandler, UserPassword};
9use crate::objects::ObjectId;
10
11/// Encryption information extracted from PDF trailer
12#[derive(Debug, Clone)]
13pub struct EncryptionInfo {
14    /// Filter name (should be "Standard")
15    pub filter: String,
16    /// V entry (algorithm version)
17    pub v: i32,
18    /// R entry (revision)
19    pub r: i32,
20    /// O entry (owner password hash)
21    pub o: Vec<u8>,
22    /// U entry (user password hash)
23    pub u: Vec<u8>,
24    /// P entry (permissions)
25    pub p: i32,
26    /// Length entry (key length in bits)
27    pub length: Option<i32>,
28}
29
30/// PDF Encryption Handler
31pub struct EncryptionHandler {
32    /// Encryption information from trailer
33    encryption_info: EncryptionInfo,
34    /// Standard security handler
35    security_handler: StandardSecurityHandler,
36    /// Current encryption key (if unlocked)
37    encryption_key: Option<EncryptionKey>,
38    /// File ID from trailer
39    file_id: Option<Vec<u8>>,
40}
41
42impl EncryptionHandler {
43    /// Create encryption handler from encryption dictionary
44    pub fn new(encrypt_dict: &PdfDictionary, file_id: Option<Vec<u8>>) -> ParseResult<Self> {
45        let encryption_info = Self::parse_encryption_dict(encrypt_dict)?;
46
47        // Create appropriate security handler based on revision
48        let security_handler = match encryption_info.r {
49            2 => StandardSecurityHandler::rc4_40bit(),
50            3 | 4 => StandardSecurityHandler::rc4_128bit(),
51            5 => StandardSecurityHandler::aes_256_r5(),
52            6 => StandardSecurityHandler::aes_256_r6(),
53            _ => {
54                return Err(ParseError::SyntaxError {
55                    position: 0,
56                    message: format!("Encryption revision {} not supported", encryption_info.r),
57                });
58            }
59        };
60
61        Ok(Self {
62            encryption_info,
63            security_handler,
64            encryption_key: None,
65            file_id,
66        })
67    }
68
69    /// Parse encryption dictionary from PDF trailer
70    fn parse_encryption_dict(dict: &PdfDictionary) -> ParseResult<EncryptionInfo> {
71        // Get Filter (required)
72        let filter = dict
73            .get("Filter")
74            .and_then(|obj| obj.as_name())
75            .map(|name| name.0.as_str())
76            .ok_or_else(|| ParseError::MissingKey("Filter".to_string()))?;
77
78        if filter != "Standard" {
79            return Err(ParseError::SyntaxError {
80                position: 0,
81                message: format!("Encryption filter '{filter}' not supported"),
82            });
83        }
84
85        // Get V (algorithm version)
86        let v = dict
87            .get("V")
88            .and_then(|obj| obj.as_integer())
89            .map(|i| i as i32)
90            .unwrap_or(0);
91
92        // Get R (revision)
93        let r = dict
94            .get("R")
95            .and_then(|obj| obj.as_integer())
96            .map(|i| i as i32)
97            .ok_or_else(|| ParseError::MissingKey("R".to_string()))?;
98
99        // Get O (owner password hash)
100        let o = dict
101            .get("O")
102            .and_then(|obj| obj.as_string())
103            .ok_or_else(|| ParseError::MissingKey("O".to_string()))?
104            .as_bytes()
105            .to_vec();
106
107        // Get U (user password hash)
108        let u = dict
109            .get("U")
110            .and_then(|obj| obj.as_string())
111            .ok_or_else(|| ParseError::MissingKey("U".to_string()))?
112            .as_bytes()
113            .to_vec();
114
115        // Get P (permissions)
116        let p = dict
117            .get("P")
118            .and_then(|obj| obj.as_integer())
119            .map(|i| i as i32)
120            .ok_or_else(|| ParseError::MissingKey("P".to_string()))?;
121
122        // Get Length (optional, defaults based on revision)
123        let length = dict
124            .get("Length")
125            .and_then(|obj| obj.as_integer())
126            .map(|i| i as i32);
127
128        Ok(EncryptionInfo {
129            filter: filter.to_string(),
130            v,
131            r,
132            o,
133            u,
134            p,
135            length,
136        })
137    }
138
139    /// Check if PDF is encrypted by looking for Encrypt entry in trailer
140    pub fn detect_encryption(trailer: &PdfDictionary) -> bool {
141        trailer.contains_key("Encrypt")
142    }
143
144    /// Try to unlock PDF with user password
145    pub fn unlock_with_user_password(&mut self, password: &str) -> ParseResult<bool> {
146        let user_password = UserPassword(password.to_string());
147
148        // Compute what the U entry should be for this password
149        let permissions = Permissions::from_bits(self.encryption_info.p as u32);
150        let computed_u = self
151            .security_handler
152            .compute_user_hash(
153                &user_password,
154                &self.encryption_info.o,
155                permissions,
156                self.file_id.as_deref(),
157            )
158            .map_err(|e| ParseError::SyntaxError {
159                position: 0,
160                message: format!("Failed to compute user hash: {e}"),
161            })?;
162
163        // Compare with stored U entry (first 16 bytes for R3+)
164        let comparison_length = if self.encryption_info.r >= 3 { 16 } else { 32 };
165        let matches =
166            computed_u[..comparison_length] == self.encryption_info.u[..comparison_length];
167
168        if matches {
169            // Compute and store encryption key
170            let key = self
171                .security_handler
172                .compute_encryption_key(
173                    &user_password,
174                    &self.encryption_info.o,
175                    permissions,
176                    self.file_id.as_deref(),
177                )
178                .map_err(|e| ParseError::SyntaxError {
179                    position: 0,
180                    message: format!("Failed to compute encryption key: {e}"),
181                })?;
182            self.encryption_key = Some(key);
183        }
184
185        Ok(matches)
186    }
187
188    /// Try to unlock PDF with owner password
189    pub fn unlock_with_owner_password(&mut self, password: &str) -> ParseResult<bool> {
190        // For owner password, we need to derive the user password first
191        // This is a simplified implementation - full implementation would
192        // reverse the owner password algorithm
193
194        // For now, try the owner password as if it were a user password
195        self.unlock_with_user_password(password)
196    }
197
198    /// Try to unlock with empty password (common case)
199    pub fn try_empty_password(&mut self) -> ParseResult<bool> {
200        self.unlock_with_user_password("")
201    }
202
203    /// Check if the PDF is currently unlocked
204    pub fn is_unlocked(&self) -> bool {
205        self.encryption_key.is_some()
206    }
207
208    /// Get the current encryption key (if unlocked)
209    pub fn encryption_key(&self) -> Option<&EncryptionKey> {
210        self.encryption_key.as_ref()
211    }
212
213    /// Decrypt a string object
214    pub fn decrypt_string(&self, data: &[u8], obj_id: &ObjectId) -> ParseResult<Vec<u8>> {
215        match &self.encryption_key {
216            Some(key) => Ok(self.security_handler.decrypt_string(data, key, obj_id)),
217            None => Err(ParseError::EncryptionNotSupported),
218        }
219    }
220
221    /// Decrypt a stream object
222    pub fn decrypt_stream(&self, data: &[u8], obj_id: &ObjectId) -> ParseResult<Vec<u8>> {
223        match &self.encryption_key {
224            Some(key) => Ok(self.security_handler.decrypt_stream(data, key, obj_id)),
225            None => Err(ParseError::EncryptionNotSupported),
226        }
227    }
228
229    /// Get encryption algorithm information
230    pub fn algorithm_info(&self) -> String {
231        match (
232            self.encryption_info.r,
233            self.encryption_info.length.unwrap_or(40),
234        ) {
235            (2, _) => "RC4 40-bit".to_string(),
236            (3, len) => format!("RC4 {len}-bit"),
237            (4, len) => format!("RC4 {len}-bit with metadata control"),
238            (5, _) => "AES-256 (Revision 5)".to_string(),
239            (6, _) => "AES-256 (Revision 6, Unicode passwords)".to_string(),
240            (r, len) => format!("Unknown revision {r} with {len}-bit key"),
241        }
242    }
243
244    /// Get permissions information
245    pub fn permissions(&self) -> Permissions {
246        Permissions::from_bits(self.encryption_info.p as u32)
247    }
248
249    /// Check if strings should be encrypted
250    pub fn encrypt_strings(&self) -> bool {
251        // For standard security handler, strings are always encrypted
252        true
253    }
254
255    /// Check if streams should be encrypted  
256    pub fn encrypt_streams(&self) -> bool {
257        // For standard security handler, streams are always encrypted
258        true
259    }
260
261    /// Check if metadata should be encrypted (R4 only)
262    pub fn encrypt_metadata(&self) -> bool {
263        // For R4, this could be controlled by EncryptMetadata entry
264        // For now, assume metadata is encrypted
265        true
266    }
267}
268
269/// Password prompt result
270#[derive(Debug)]
271pub enum PasswordResult {
272    /// Password was accepted
273    Success,
274    /// Password was rejected
275    Rejected,
276    /// User cancelled password entry
277    Cancelled,
278}
279
280/// Trait for password prompting
281pub trait PasswordProvider {
282    /// Prompt for user password
283    fn prompt_user_password(&self) -> ParseResult<Option<String>>;
284
285    /// Prompt for owner password
286    fn prompt_owner_password(&self) -> ParseResult<Option<String>>;
287}
288
289/// Console-based password provider
290pub struct ConsolePasswordProvider;
291
292impl PasswordProvider for ConsolePasswordProvider {
293    fn prompt_user_password(&self) -> ParseResult<Option<String>> {
294        println!("PDF is encrypted. Enter user password (or press Enter for empty password):");
295
296        let mut input = String::new();
297        std::io::stdin()
298            .read_line(&mut input)
299            .map_err(|e| ParseError::SyntaxError {
300                position: 0,
301                message: format!("Failed to read password: {e}"),
302            })?;
303
304        // Remove trailing newline
305        input.truncate(input.trim_end().len());
306        Ok(Some(input))
307    }
308
309    fn prompt_owner_password(&self) -> ParseResult<Option<String>> {
310        println!("User password failed. Enter owner password:");
311
312        let mut input = String::new();
313        std::io::stdin()
314            .read_line(&mut input)
315            .map_err(|e| ParseError::SyntaxError {
316                position: 0,
317                message: format!("Failed to read password: {e}"),
318            })?;
319
320        // Remove trailing newline
321        input.truncate(input.trim_end().len());
322        Ok(Some(input))
323    }
324}
325
326/// Interactive decryption helper
327pub struct InteractiveDecryption<P: PasswordProvider> {
328    password_provider: P,
329}
330
331impl<P: PasswordProvider> InteractiveDecryption<P> {
332    /// Create new interactive decryption helper
333    pub fn new(password_provider: P) -> Self {
334        Self { password_provider }
335    }
336
337    /// Attempt to unlock PDF interactively
338    pub fn unlock_pdf(&self, handler: &mut EncryptionHandler) -> ParseResult<PasswordResult> {
339        // First try empty password
340        if handler.try_empty_password()? {
341            return Ok(PasswordResult::Success);
342        }
343
344        // Try user password
345        if let Some(password) = self.password_provider.prompt_user_password()? {
346            if handler.unlock_with_user_password(&password)? {
347                return Ok(PasswordResult::Success);
348            }
349        } else {
350            return Ok(PasswordResult::Cancelled);
351        }
352
353        // Try owner password
354        if let Some(password) = self.password_provider.prompt_owner_password()? {
355            if handler.unlock_with_owner_password(&password)? {
356                return Ok(PasswordResult::Success);
357            }
358        } else {
359            return Ok(PasswordResult::Cancelled);
360        }
361
362        Ok(PasswordResult::Rejected)
363    }
364}
365
366#[cfg(test)]
367mod tests {
368    use super::*;
369    use crate::parser::objects::{PdfDictionary, PdfName, PdfObject, PdfString};
370
371    fn create_test_encryption_dict() -> PdfDictionary {
372        let mut dict = PdfDictionary::new();
373        dict.insert(
374            "Filter".to_string(),
375            PdfObject::Name(PdfName("Standard".to_string())),
376        );
377        dict.insert("V".to_string(), PdfObject::Integer(1));
378        dict.insert("R".to_string(), PdfObject::Integer(2));
379        dict.insert(
380            "O".to_string(),
381            PdfObject::String(PdfString::new(vec![0u8; 32])),
382        );
383        dict.insert(
384            "U".to_string(),
385            PdfObject::String(PdfString::new(vec![0u8; 32])),
386        );
387        dict.insert("P".to_string(), PdfObject::Integer(-4));
388        dict
389    }
390
391    #[test]
392    fn test_encryption_detection() {
393        let mut trailer = PdfDictionary::new();
394        assert!(!EncryptionHandler::detect_encryption(&trailer));
395
396        trailer.insert("Encrypt".to_string(), PdfObject::Reference(1, 0));
397        assert!(EncryptionHandler::detect_encryption(&trailer));
398    }
399
400    #[test]
401    fn test_encryption_info_parsing() {
402        let dict = create_test_encryption_dict();
403        let info = EncryptionHandler::parse_encryption_dict(&dict).unwrap();
404
405        assert_eq!(info.filter, "Standard");
406        assert_eq!(info.v, 1);
407        assert_eq!(info.r, 2);
408        assert_eq!(info.o.len(), 32);
409        assert_eq!(info.u.len(), 32);
410        assert_eq!(info.p, -4);
411    }
412
413    #[test]
414    fn test_encryption_handler_creation() {
415        let dict = create_test_encryption_dict();
416        let handler = EncryptionHandler::new(&dict, None).unwrap();
417
418        assert_eq!(handler.encryption_info.r, 2);
419        assert!(!handler.is_unlocked());
420        assert_eq!(handler.algorithm_info(), "RC4 40-bit");
421    }
422
423    #[test]
424    fn test_empty_password_attempt() {
425        let dict = create_test_encryption_dict();
426        let mut handler = EncryptionHandler::new(&dict, None).unwrap();
427
428        // Empty password should not work with test data
429        let result = handler.try_empty_password().unwrap();
430        assert!(!result);
431        assert!(!handler.is_unlocked());
432    }
433
434    #[test]
435    fn test_permissions() {
436        let dict = create_test_encryption_dict();
437        let handler = EncryptionHandler::new(&dict, None).unwrap();
438
439        let permissions = handler.permissions();
440        // P value of -4 should result in specific permissions
441        assert!(permissions.bits() != 0);
442    }
443
444    #[test]
445    fn test_encryption_flags() {
446        let dict = create_test_encryption_dict();
447        let handler = EncryptionHandler::new(&dict, None).unwrap();
448
449        assert!(handler.encrypt_strings());
450        assert!(handler.encrypt_streams());
451        assert!(handler.encrypt_metadata());
452    }
453
454    #[test]
455    fn test_decrypt_without_key() {
456        let dict = create_test_encryption_dict();
457        let handler = EncryptionHandler::new(&dict, None).unwrap();
458
459        let obj_id = ObjectId::new(1, 0);
460        let result = handler.decrypt_string(b"test", &obj_id);
461        assert!(result.is_err());
462    }
463
464    #[test]
465    fn test_unsupported_filter() {
466        let mut dict = PdfDictionary::new();
467        dict.insert(
468            "Filter".to_string(),
469            PdfObject::Name(PdfName("UnsupportedFilter".to_string())),
470        );
471        dict.insert("R".to_string(), PdfObject::Integer(2));
472        dict.insert(
473            "O".to_string(),
474            PdfObject::String(PdfString::new(vec![0u8; 32])),
475        );
476        dict.insert(
477            "U".to_string(),
478            PdfObject::String(PdfString::new(vec![0u8; 32])),
479        );
480        dict.insert("P".to_string(), PdfObject::Integer(-4));
481
482        let result = EncryptionHandler::new(&dict, None);
483        assert!(result.is_err());
484    }
485
486    #[test]
487    fn test_unsupported_revision() {
488        let mut dict = create_test_encryption_dict();
489        dict.insert("R".to_string(), PdfObject::Integer(99)); // Unsupported revision
490
491        let result = EncryptionHandler::new(&dict, None);
492        assert!(result.is_err());
493    }
494
495    #[test]
496    fn test_missing_required_keys() {
497        let test_cases = vec![
498            ("Filter", PdfObject::Name(PdfName("Standard".to_string()))),
499            ("R", PdfObject::Integer(2)),
500            ("O", PdfObject::String(PdfString::new(vec![0u8; 32]))),
501            ("U", PdfObject::String(PdfString::new(vec![0u8; 32]))),
502            ("P", PdfObject::Integer(-4)),
503        ];
504
505        for (skip_key, _) in test_cases {
506            let mut dict = create_test_encryption_dict();
507            dict.0.remove(&PdfName(skip_key.to_string()));
508
509            let result = EncryptionHandler::parse_encryption_dict(&dict);
510            assert!(result.is_err(), "Should fail when {skip_key} is missing");
511        }
512    }
513
514    /// Mock password provider for testing
515    struct MockPasswordProvider {
516        user_password: Option<String>,
517        owner_password: Option<String>,
518    }
519
520    impl PasswordProvider for MockPasswordProvider {
521        fn prompt_user_password(&self) -> ParseResult<Option<String>> {
522            Ok(self.user_password.clone())
523        }
524
525        fn prompt_owner_password(&self) -> ParseResult<Option<String>> {
526            Ok(self.owner_password.clone())
527        }
528    }
529
530    #[test]
531    fn test_interactive_decryption_cancelled() {
532        let provider = MockPasswordProvider {
533            user_password: None,
534            owner_password: None,
535        };
536
537        let decryption = InteractiveDecryption::new(provider);
538        let dict = create_test_encryption_dict();
539        let mut handler = EncryptionHandler::new(&dict, None).unwrap();
540
541        let result = decryption.unlock_pdf(&mut handler).unwrap();
542        matches!(result, PasswordResult::Cancelled);
543    }
544
545    #[test]
546    fn test_interactive_decryption_rejected() {
547        let provider = MockPasswordProvider {
548            user_password: Some("wrong_password".to_string()),
549            owner_password: Some("also_wrong".to_string()),
550        };
551
552        let decryption = InteractiveDecryption::new(provider);
553        let dict = create_test_encryption_dict();
554        let mut handler = EncryptionHandler::new(&dict, None).unwrap();
555
556        let result = decryption.unlock_pdf(&mut handler).unwrap();
557        matches!(result, PasswordResult::Rejected);
558    }
559
560    // ===== ADVANCED EDGE CASE TESTS =====
561
562    #[test]
563    fn test_malformed_encryption_dictionary_invalid_types() {
564        // Test with wrong type for Filter
565        let mut dict = PdfDictionary::new();
566        dict.insert("Filter".to_string(), PdfObject::Integer(123)); // Should be Name
567        dict.insert("R".to_string(), PdfObject::Integer(2));
568        dict.insert(
569            "O".to_string(),
570            PdfObject::String(PdfString::new(vec![0u8; 32])),
571        );
572        dict.insert(
573            "U".to_string(),
574            PdfObject::String(PdfString::new(vec![0u8; 32])),
575        );
576        dict.insert("P".to_string(), PdfObject::Integer(-4));
577
578        let result = EncryptionHandler::parse_encryption_dict(&dict);
579        assert!(result.is_err());
580
581        // Test with wrong type for R
582        let mut dict = create_test_encryption_dict();
583        dict.insert(
584            "R".to_string(),
585            PdfObject::Name(PdfName("not_a_number".to_string())),
586        );
587        let result = EncryptionHandler::parse_encryption_dict(&dict);
588        assert!(result.is_err());
589    }
590
591    #[test]
592    fn test_encryption_dictionary_edge_values() {
593        // Test with extreme revision values
594        let mut dict = create_test_encryption_dict();
595        dict.insert("R".to_string(), PdfObject::Integer(0)); // Very low revision
596        let result = EncryptionHandler::new(&dict, None);
597        assert!(result.is_err());
598
599        // Test with negative revision
600        let mut dict = create_test_encryption_dict();
601        dict.insert("R".to_string(), PdfObject::Integer(-1));
602        let result = EncryptionHandler::new(&dict, None);
603        assert!(result.is_err());
604
605        // Test with very high revision
606        let mut dict = create_test_encryption_dict();
607        dict.insert("R".to_string(), PdfObject::Integer(1000));
608        let result = EncryptionHandler::new(&dict, None);
609        assert!(result.is_err());
610    }
611
612    #[test]
613    fn test_encryption_dictionary_invalid_hash_lengths() {
614        // Test with O hash too short
615        let mut dict = create_test_encryption_dict();
616        dict.insert(
617            "O".to_string(),
618            PdfObject::String(PdfString::new(vec![0u8; 16])),
619        ); // Should be 32
620        let result = EncryptionHandler::parse_encryption_dict(&dict);
621        // Should still work but be invalid data
622        assert!(result.is_ok());
623
624        // Test with U hash too long
625        let mut dict = create_test_encryption_dict();
626        dict.insert(
627            "U".to_string(),
628            PdfObject::String(PdfString::new(vec![0u8; 64])),
629        ); // Should be 32
630        let result = EncryptionHandler::parse_encryption_dict(&dict);
631        assert!(result.is_ok());
632
633        // Test with empty hashes
634        let mut dict = create_test_encryption_dict();
635        dict.insert("O".to_string(), PdfObject::String(PdfString::new(vec![])));
636        dict.insert("U".to_string(), PdfObject::String(PdfString::new(vec![])));
637        let result = EncryptionHandler::parse_encryption_dict(&dict);
638        assert!(result.is_ok());
639    }
640
641    #[test]
642    fn test_encryption_with_different_key_lengths() {
643        // Test Rev 2 (40-bit)
644        let mut dict = create_test_encryption_dict();
645        dict.insert("R".to_string(), PdfObject::Integer(2));
646        let handler = EncryptionHandler::new(&dict, None).unwrap();
647        assert_eq!(handler.algorithm_info(), "RC4 40-bit");
648
649        // Test Rev 3 (128-bit)
650        let mut dict = create_test_encryption_dict();
651        dict.insert("R".to_string(), PdfObject::Integer(3));
652        dict.insert("Length".to_string(), PdfObject::Integer(128));
653        let handler = EncryptionHandler::new(&dict, None).unwrap();
654        assert_eq!(handler.algorithm_info(), "RC4 128-bit");
655
656        // Test Rev 4 (128-bit with metadata control)
657        let mut dict = create_test_encryption_dict();
658        dict.insert("R".to_string(), PdfObject::Integer(4));
659        dict.insert("Length".to_string(), PdfObject::Integer(128));
660        let handler = EncryptionHandler::new(&dict, None).unwrap();
661        assert_eq!(
662            handler.algorithm_info(),
663            "RC4 128-bit with metadata control"
664        );
665
666        // Test Rev 5 (AES-256)
667        let mut dict = create_test_encryption_dict();
668        dict.insert("R".to_string(), PdfObject::Integer(5));
669        dict.insert("V".to_string(), PdfObject::Integer(5));
670        let handler = EncryptionHandler::new(&dict, None).unwrap();
671        assert_eq!(handler.algorithm_info(), "AES-256 (Revision 5)");
672
673        // Test Rev 6 (AES-256 with Unicode)
674        let mut dict = create_test_encryption_dict();
675        dict.insert("R".to_string(), PdfObject::Integer(6));
676        dict.insert("V".to_string(), PdfObject::Integer(5));
677        let handler = EncryptionHandler::new(&dict, None).unwrap();
678        assert_eq!(
679            handler.algorithm_info(),
680            "AES-256 (Revision 6, Unicode passwords)"
681        );
682    }
683
684    #[test]
685    fn test_file_id_handling() {
686        let dict = create_test_encryption_dict();
687
688        // Test with file ID
689        let file_id = Some(b"test_file_id_12345678".to_vec());
690        let handler = EncryptionHandler::new(&dict, file_id.clone()).unwrap();
691        // File ID should be stored
692        assert_eq!(handler.file_id, file_id);
693
694        // Test without file ID
695        let handler = EncryptionHandler::new(&dict, None).unwrap();
696        assert_eq!(handler.file_id, None);
697
698        // Test with empty file ID
699        let empty_file_id = Some(vec![]);
700        let handler = EncryptionHandler::new(&dict, empty_file_id.clone()).unwrap();
701        assert_eq!(handler.file_id, empty_file_id);
702    }
703
704    #[test]
705    fn test_permissions_edge_cases() {
706        // Test with different permission values
707        let permission_values = vec![0, -1, -4, -44, -100, i32::MAX, i32::MIN];
708
709        for p_value in permission_values {
710            let mut dict = create_test_encryption_dict();
711            dict.insert("P".to_string(), PdfObject::Integer(p_value as i64));
712            let handler = EncryptionHandler::new(&dict, None).unwrap();
713
714            let permissions = handler.permissions();
715            assert_eq!(permissions.bits(), p_value as u32);
716        }
717    }
718
719    #[test]
720    fn test_decrypt_with_different_object_ids() {
721        let dict = create_test_encryption_dict();
722        let handler = EncryptionHandler::new(&dict, None).unwrap();
723        let test_data = b"test data";
724
725        // Test with different object IDs (should all fail since not unlocked)
726        let object_ids = vec![
727            ObjectId::new(1, 0),
728            ObjectId::new(999, 0),
729            ObjectId::new(1, 999),
730            ObjectId::new(u32::MAX, u16::MAX),
731        ];
732
733        for obj_id in object_ids {
734            let result = handler.decrypt_string(test_data, &obj_id);
735            assert!(result.is_err());
736
737            let result = handler.decrypt_stream(test_data, &obj_id);
738            assert!(result.is_err());
739        }
740    }
741
742    #[test]
743    fn test_password_scenarios_comprehensive() {
744        let dict = create_test_encryption_dict();
745        let mut handler = EncryptionHandler::new(&dict, None).unwrap();
746
747        // Test various password scenarios (using String for uniformity)
748        let test_passwords = vec![
749            "".to_string(),                     // Empty
750            " ".to_string(),                    // Single space
751            "   ".to_string(),                  // Multiple spaces
752            "password".to_string(),             // Simple
753            "Password123!@#".to_string(),       // Complex
754            "a".repeat(32),                     // Exactly 32 chars
755            "a".repeat(50),                     // Over 32 chars
756            "unicode_ñáéíóú".to_string(),       // Unicode
757            "pass\nwith\nnewlines".to_string(), // Newlines
758            "pass\twith\ttabs".to_string(),     // Tabs
759            "pass with spaces".to_string(),     // Spaces
760            "🔐🗝️📄".to_string(),               // Emojis
761        ];
762
763        for password in test_passwords {
764            // All should fail with test data but not crash
765            let result = handler.unlock_with_user_password(&password);
766            assert!(result.is_ok());
767            assert!(!result.unwrap());
768
769            let result = handler.unlock_with_owner_password(&password);
770            assert!(result.is_ok());
771            assert!(!result.unwrap());
772        }
773    }
774
775    #[test]
776    fn test_encryption_handler_thread_safety_simulation() {
777        // Simulate what would happen in multi-threaded access
778        let dict = create_test_encryption_dict();
779        let handler = EncryptionHandler::new(&dict, None).unwrap();
780
781        // Test multiple read operations (safe)
782        for _ in 0..100 {
783            assert!(!handler.is_unlocked());
784            assert_eq!(handler.algorithm_info(), "RC4 40-bit");
785            assert!(handler.encrypt_strings());
786            assert!(handler.encrypt_streams());
787            assert!(handler.encrypt_metadata());
788        }
789    }
790
791    #[test]
792    fn test_encryption_state_transitions() {
793        let dict = create_test_encryption_dict();
794        let mut handler = EncryptionHandler::new(&dict, None).unwrap();
795
796        // Initial state
797        assert!(!handler.is_unlocked());
798
799        // Try unlock (should fail with test data)
800        let result = handler.try_empty_password().unwrap();
801        assert!(!result);
802        assert!(!handler.is_unlocked());
803
804        // Try user password (should fail with test data)
805        let result = handler.unlock_with_user_password("test").unwrap();
806        assert!(!result);
807        assert!(!handler.is_unlocked());
808
809        // Try owner password (should fail with test data)
810        let result = handler.unlock_with_owner_password("test").unwrap();
811        assert!(!result);
812        assert!(!handler.is_unlocked());
813
814        // State should remain consistent
815        assert!(!handler.is_unlocked());
816    }
817
818    #[test]
819    fn test_interactive_decryption_edge_cases() {
820        // Test provider that returns None for both passwords
821        let provider = MockPasswordProvider {
822            user_password: None,
823            owner_password: None,
824        };
825
826        let decryption = InteractiveDecryption::new(provider);
827        let dict = create_test_encryption_dict();
828        let mut handler = EncryptionHandler::new(&dict, None).unwrap();
829
830        let result = decryption.unlock_pdf(&mut handler).unwrap();
831        matches!(result, PasswordResult::Cancelled);
832
833        // Test provider that returns empty strings
834        let provider = MockPasswordProvider {
835            user_password: Some("".to_string()),
836            owner_password: Some("".to_string()),
837        };
838
839        let decryption = InteractiveDecryption::new(provider);
840        let mut handler = EncryptionHandler::new(&dict, None).unwrap();
841
842        let result = decryption.unlock_pdf(&mut handler).unwrap();
843        matches!(result, PasswordResult::Rejected);
844    }
845
846    /// Test custom MockPasswordProvider for edge cases
847    struct EdgeCasePasswordProvider {
848        call_count: std::cell::RefCell<usize>,
849        passwords: Vec<Option<String>>,
850    }
851
852    impl EdgeCasePasswordProvider {
853        fn new(passwords: Vec<Option<String>>) -> Self {
854            Self {
855                call_count: std::cell::RefCell::new(0),
856                passwords,
857            }
858        }
859    }
860
861    impl PasswordProvider for EdgeCasePasswordProvider {
862        fn prompt_user_password(&self) -> ParseResult<Option<String>> {
863            let mut count = self.call_count.borrow_mut();
864            if *count < self.passwords.len() {
865                let result = self.passwords[*count].clone();
866                *count += 1;
867                Ok(result)
868            } else {
869                Ok(None)
870            }
871        }
872
873        fn prompt_owner_password(&self) -> ParseResult<Option<String>> {
874            self.prompt_user_password()
875        }
876    }
877
878    #[test]
879    fn test_interactive_decryption_with_sequence() {
880        let passwords = vec![
881            Some("first_attempt".to_string()),
882            Some("second_attempt".to_string()),
883            None, // Cancelled
884        ];
885
886        let provider = EdgeCasePasswordProvider::new(passwords);
887        let decryption = InteractiveDecryption::new(provider);
888        let dict = create_test_encryption_dict();
889        let mut handler = EncryptionHandler::new(&dict, None).unwrap();
890
891        let result = decryption.unlock_pdf(&mut handler).unwrap();
892        matches!(result, PasswordResult::Cancelled);
893    }
894}