Skip to main content

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