Skip to main content

oxidize_pdf/encryption/
standard_security.rs

1//! Standard Security Handler implementation according to ISO 32000-1/32000-2
2//!
3//! # Security Considerations
4//!
5//! This implementation includes several security hardening measures:
6//!
7//! - **Constant-time comparison**: Password validation uses `subtle::ConstantTimeEq`
8//!   to prevent timing side-channel attacks that could leak password information.
9//!
10//! - **Memory zeroization**: Sensitive data (`EncryptionKey`, `UserPassword`,
11//!   `OwnerPassword`) implements `Zeroize` to ensure secrets are cleared from
12//!   memory when dropped, preventing memory dump attacks.
13//!
14//! - **Cryptographically secure RNG**: Salt generation uses `rand::rng()` which
15//!   provides OS-level entropy suitable for cryptographic operations.
16
17#![allow(clippy::needless_range_loop)]
18
19use crate::encryption::{generate_iv, Aes, AesKey, Permissions, Rc4, Rc4Key};
20use crate::error::Result;
21use crate::objects::ObjectId;
22use rand::Rng;
23use sha2::{Digest, Sha256, Sha384, Sha512};
24use subtle::ConstantTimeEq;
25use zeroize::{Zeroize, ZeroizeOnDrop};
26
27/// Padding used in password processing
28const PADDING: [u8; 32] = [
29    0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
30    0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A,
31];
32
33/// User password
34///
35/// # Security
36/// Implements `Zeroize` and `ZeroizeOnDrop` to ensure password is cleared from memory.
37#[derive(Debug, Clone, Zeroize, ZeroizeOnDrop)]
38pub struct UserPassword(pub String);
39
40/// Owner password
41///
42/// # Security
43/// Implements `Zeroize` and `ZeroizeOnDrop` to ensure password is cleared from memory.
44#[derive(Debug, Clone, Zeroize, ZeroizeOnDrop)]
45pub struct OwnerPassword(pub String);
46
47/// Encryption key
48///
49/// # Security
50/// Implements `Zeroize` and `ZeroizeOnDrop` to ensure key bytes are cleared from memory.
51#[derive(Debug, Clone, Zeroize, ZeroizeOnDrop)]
52pub struct EncryptionKey {
53    /// Key bytes
54    pub key: Vec<u8>,
55}
56
57impl EncryptionKey {
58    /// Create from bytes
59    pub fn new(key: Vec<u8>) -> Self {
60        Self { key }
61    }
62
63    /// Get key length in bytes
64    pub fn len(&self) -> usize {
65        self.key.len()
66    }
67
68    /// Check if empty
69    pub fn is_empty(&self) -> bool {
70        self.key.is_empty()
71    }
72
73    /// Get key as bytes
74    pub fn as_bytes(&self) -> &[u8] {
75        &self.key
76    }
77}
78
79/// Security handler revision
80#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
81pub enum SecurityHandlerRevision {
82    /// Revision 2 (RC4 40-bit)
83    R2 = 2,
84    /// Revision 3 (RC4 128-bit)
85    R3 = 3,
86    /// Revision 4 (RC4 128-bit with metadata encryption control)
87    R4 = 4,
88    /// Revision 5 (AES-256 with improved password validation)
89    R5 = 5,
90    /// Revision 6 (AES-256 with Unicode password support)
91    R6 = 6,
92}
93
94/// Standard Security Handler
95pub struct StandardSecurityHandler {
96    /// Revision
97    pub revision: SecurityHandlerRevision,
98    /// Key length in bytes
99    pub key_length: usize,
100}
101
102impl StandardSecurityHandler {
103    /// Create handler for RC4 40-bit encryption
104    pub fn rc4_40bit() -> Self {
105        Self {
106            revision: SecurityHandlerRevision::R2,
107            key_length: 5,
108        }
109    }
110
111    /// Create handler for RC4 128-bit encryption
112    pub fn rc4_128bit() -> Self {
113        Self {
114            revision: SecurityHandlerRevision::R3,
115            key_length: 16,
116        }
117    }
118
119    /// Create handler for AES-128 encryption (Revision 4)
120    pub fn aes_128_r4() -> Self {
121        Self {
122            revision: SecurityHandlerRevision::R4,
123            key_length: 16,
124        }
125    }
126
127    /// Create handler for AES-256 encryption (Revision 5)
128    pub fn aes_256_r5() -> Self {
129        Self {
130            revision: SecurityHandlerRevision::R5,
131            key_length: 32,
132        }
133    }
134
135    /// Create handler for AES-256 encryption (Revision 6)
136    pub fn aes_256_r6() -> Self {
137        Self {
138            revision: SecurityHandlerRevision::R6,
139            key_length: 32,
140        }
141    }
142
143    /// Pad or truncate password to 32 bytes
144    fn pad_password(password: &str) -> [u8; 32] {
145        let mut padded = [0u8; 32];
146        let password_bytes = password.as_bytes();
147        let len = password_bytes.len().min(32);
148
149        // Copy password bytes
150        padded[..len].copy_from_slice(&password_bytes[..len]);
151
152        // Fill remaining with padding
153        if len < 32 {
154            padded[len..].copy_from_slice(&PADDING[..32 - len]);
155        }
156
157        padded
158    }
159
160    /// Compute owner password hash (O entry)
161    pub fn compute_owner_hash(
162        &self,
163        owner_password: &OwnerPassword,
164        user_password: &UserPassword,
165    ) -> Vec<u8> {
166        // Step 1: Pad passwords
167        let owner_pad = Self::pad_password(&owner_password.0);
168        let user_pad = Self::pad_password(&user_password.0);
169
170        // Step 2: Create MD5 hash of owner password
171        let mut hash = md5::compute(&owner_pad).to_vec();
172
173        // Step 3: For revision 3+, do 50 additional iterations
174        if self.revision >= SecurityHandlerRevision::R3 {
175            for _ in 0..50 {
176                hash = md5::compute(&hash).to_vec();
177            }
178        }
179
180        // Step 4: Create RC4 key from hash (truncated to key length)
181        let rc4_key = Rc4Key::from_slice(&hash[..self.key_length]);
182
183        // Step 5: Encrypt user password with RC4
184        let mut result = rc4_encrypt(&rc4_key, &user_pad);
185
186        // Step 6: For revision 3+, do 19 additional iterations
187        if self.revision >= SecurityHandlerRevision::R3 {
188            for i in 1..=19 {
189                let mut key_bytes = hash[..self.key_length].to_vec();
190                for j in 0..self.key_length {
191                    key_bytes[j] ^= i as u8;
192                }
193                let iter_key = Rc4Key::from_slice(&key_bytes);
194                result = rc4_encrypt(&iter_key, &result);
195            }
196        }
197
198        result
199    }
200
201    /// Compute user password hash (U entry)
202    pub fn compute_user_hash(
203        &self,
204        user_password: &UserPassword,
205        owner_hash: &[u8],
206        permissions: Permissions,
207        file_id: Option<&[u8]>,
208    ) -> Result<Vec<u8>> {
209        // Compute encryption key
210        let key = self.compute_encryption_key(user_password, owner_hash, permissions, file_id)?;
211
212        match self.revision {
213            SecurityHandlerRevision::R2 => {
214                // For R2, encrypt padding with key
215                let rc4_key = Rc4Key::from_slice(&key.key);
216                Ok(rc4_encrypt(&rc4_key, &PADDING))
217            }
218            SecurityHandlerRevision::R3 | SecurityHandlerRevision::R4 => {
219                // For R3/R4, compute MD5 hash including file ID
220                let mut data = Vec::new();
221                data.extend_from_slice(&PADDING);
222
223                if let Some(id) = file_id {
224                    data.extend_from_slice(id);
225                }
226
227                let hash = md5::compute(&data);
228
229                // Encrypt hash with RC4
230                let rc4_key = Rc4Key::from_slice(&key.key);
231                let mut result = rc4_encrypt(&rc4_key, hash.as_ref());
232
233                // Do 19 additional iterations
234                for i in 1..=19 {
235                    let mut key_bytes = key.key.clone();
236                    for j in 0..key_bytes.len() {
237                        key_bytes[j] ^= i as u8;
238                    }
239                    let iter_key = Rc4Key::from_slice(&key_bytes);
240                    result = rc4_encrypt(&iter_key, &result);
241                }
242
243                // Result is 32 bytes (16 bytes encrypted hash + 16 bytes arbitrary data)
244                result.resize(32, 0);
245                Ok(result)
246            }
247            SecurityHandlerRevision::R5 | SecurityHandlerRevision::R6 => {
248                // For R5/R6, use AES-based hash computation
249                let aes_key = self.compute_aes_encryption_key(
250                    user_password,
251                    owner_hash,
252                    permissions,
253                    file_id,
254                )?;
255                let hash = sha256(&aes_key.key);
256
257                // For AES revisions, return the hash directly (simplified)
258                Ok(hash)
259            }
260        }
261    }
262
263    /// Compute encryption key from user password
264    pub fn compute_encryption_key(
265        &self,
266        user_password: &UserPassword,
267        owner_hash: &[u8],
268        permissions: Permissions,
269        file_id: Option<&[u8]>,
270    ) -> Result<EncryptionKey> {
271        match self.revision {
272            SecurityHandlerRevision::R5 | SecurityHandlerRevision::R6 => {
273                // For AES revisions, use AES-specific key computation
274                self.compute_aes_encryption_key(user_password, owner_hash, permissions, file_id)
275            }
276            _ => {
277                // For RC4 revisions, use MD5-based key computation
278                // Step 1: Pad password
279                let padded = Self::pad_password(&user_password.0);
280
281                // Step 2: Create hash input
282                let mut data = Vec::new();
283                data.extend_from_slice(&padded);
284                data.extend_from_slice(owner_hash);
285                data.extend_from_slice(&permissions.bits().to_le_bytes());
286
287                if let Some(id) = file_id {
288                    data.extend_from_slice(id);
289                }
290
291                // For R4 with metadata not encrypted, add extra bytes
292                if self.revision == SecurityHandlerRevision::R4 {
293                    // In a full implementation, check EncryptMetadata flag
294                    // For now, assume metadata is encrypted
295                }
296
297                // Step 3: Create MD5 hash
298                let mut hash = md5::compute(&data).to_vec();
299
300                // Step 4: For revision 3+, do 50 additional iterations
301                if self.revision >= SecurityHandlerRevision::R3 {
302                    for _ in 0..50 {
303                        hash = md5::compute(&hash[..self.key_length]).to_vec();
304                    }
305                }
306
307                // Step 5: Truncate to key length
308                hash.truncate(self.key_length);
309
310                Ok(EncryptionKey::new(hash))
311            }
312        }
313    }
314
315    /// Encrypt a string
316    pub fn encrypt_string(&self, data: &[u8], key: &EncryptionKey, obj_id: &ObjectId) -> Vec<u8> {
317        match self.revision {
318            SecurityHandlerRevision::R4
319            | SecurityHandlerRevision::R5
320            | SecurityHandlerRevision::R6 => {
321                // AES path for R4 (AES-128) and R5/R6 (AES-256)
322                self.encrypt_aes(data, key, obj_id).unwrap_or_default()
323            }
324            _ => {
325                // RC4 for R2/R3
326                let obj_key = self.compute_object_key(key, obj_id);
327                let rc4_key = Rc4Key::from_slice(&obj_key);
328                rc4_encrypt(&rc4_key, data)
329            }
330        }
331    }
332
333    /// Decrypt a string, propagating any decryption error (issue #364).
334    ///
335    /// Prefer this over [`decrypt_string`](Self::decrypt_string) on read paths:
336    /// the latter swallows AES failures into an empty `Vec`, turning corruption
337    /// or a wrong key into *silent data loss* instead of a surfaced error.
338    ///
339    /// `pub(crate)`: an internal read-path helper, not part of the public API.
340    pub(crate) fn try_decrypt_string(
341        &self,
342        data: &[u8],
343        key: &EncryptionKey,
344        obj_id: &ObjectId,
345    ) -> Result<Vec<u8>> {
346        match self.revision {
347            SecurityHandlerRevision::R4
348            | SecurityHandlerRevision::R5
349            | SecurityHandlerRevision::R6 => self.decrypt_aes(data, key, obj_id),
350            // RC4 is symmetric and cannot fail (no padding/auth).
351            _ => Ok(self.encrypt_string(data, key, obj_id)),
352        }
353    }
354
355    /// Decrypt a stream, propagating any decryption error (issue #364).
356    ///
357    /// See [`try_decrypt_string`](Self::try_decrypt_string) for why read paths
358    /// should use this instead of [`decrypt_stream`](Self::decrypt_stream).
359    ///
360    /// `pub(crate)`: an internal read-path helper, not part of the public API.
361    pub(crate) fn try_decrypt_stream(
362        &self,
363        data: &[u8],
364        key: &EncryptionKey,
365        obj_id: &ObjectId,
366    ) -> Result<Vec<u8>> {
367        // Streams and strings share the same per-object cipher.
368        self.try_decrypt_string(data, key, obj_id)
369    }
370
371    /// Decrypt a string. Lenient: a decryption failure yields an empty `Vec`.
372    /// New code should use [`try_decrypt_string`](Self::try_decrypt_string).
373    pub fn decrypt_string(&self, data: &[u8], key: &EncryptionKey, obj_id: &ObjectId) -> Vec<u8> {
374        self.try_decrypt_string(data, key, obj_id)
375            .unwrap_or_default()
376    }
377
378    /// Encrypt a stream
379    pub fn encrypt_stream(&self, data: &[u8], key: &EncryptionKey, obj_id: &ObjectId) -> Vec<u8> {
380        self.encrypt_string(data, key, obj_id)
381    }
382
383    /// Decrypt a stream. Lenient: a decryption failure yields an empty `Vec`.
384    /// New code should use [`try_decrypt_stream`](Self::try_decrypt_stream).
385    pub fn decrypt_stream(&self, data: &[u8], key: &EncryptionKey, obj_id: &ObjectId) -> Vec<u8> {
386        self.try_decrypt_stream(data, key, obj_id)
387            .unwrap_or_default()
388    }
389
390    /// Encrypt data using AES.
391    ///
392    /// - **R4**: AES-128-CBC with MD5-based per-object key (ISO 32000-1 §7.6.2 Algorithm 1 + "sAlT")
393    /// - **R5/R6**: AES-256-CBC with SHA-256 key derivation
394    pub fn encrypt_aes(
395        &self,
396        data: &[u8],
397        key: &EncryptionKey,
398        obj_id: &ObjectId,
399    ) -> Result<Vec<u8>> {
400        let aes = match self.revision {
401            SecurityHandlerRevision::R4 => {
402                let obj_key = self.compute_r4_aes_object_key(key, obj_id);
403                Aes::new(AesKey::new_128(obj_key)?)
404            }
405            SecurityHandlerRevision::R5 | SecurityHandlerRevision::R6 => {
406                let obj_key = self.compute_aes_object_key(key, obj_id)?;
407                Aes::new(AesKey::new_256(obj_key)?)
408            }
409            _ => {
410                return Err(crate::error::PdfError::EncryptionError(
411                    "AES encryption requires Rev 4+ (use RC4 for Rev 2/3)".to_string(),
412                ));
413            }
414        };
415
416        let iv = generate_iv();
417        let mut result = Vec::with_capacity(16 + data.len() + 16);
418        result.extend_from_slice(&iv);
419
420        let encrypted = aes.encrypt_cbc(data, &iv).map_err(|e| {
421            crate::error::PdfError::EncryptionError(format!("AES encryption failed: {e}"))
422        })?;
423
424        result.extend_from_slice(&encrypted);
425        Ok(result)
426    }
427
428    /// Decrypt data using AES.
429    ///
430    /// - **R4**: AES-128-CBC with MD5-based per-object key
431    /// - **R5/R6**: AES-256-CBC with SHA-256 key derivation
432    pub fn decrypt_aes(
433        &self,
434        data: &[u8],
435        key: &EncryptionKey,
436        obj_id: &ObjectId,
437    ) -> Result<Vec<u8>> {
438        if data.len() < 16 {
439            return Err(crate::error::PdfError::EncryptionError(
440                "AES encrypted data must be at least 16 bytes (IV)".to_string(),
441            ));
442        }
443
444        let iv = &data[0..16];
445        let encrypted_data = &data[16..];
446
447        let aes = match self.revision {
448            SecurityHandlerRevision::R4 => {
449                let obj_key = self.compute_r4_aes_object_key(key, obj_id);
450                Aes::new(AesKey::new_128(obj_key)?)
451            }
452            SecurityHandlerRevision::R5 | SecurityHandlerRevision::R6 => {
453                let obj_key = self.compute_aes_object_key(key, obj_id)?;
454                Aes::new(AesKey::new_256(obj_key)?)
455            }
456            _ => {
457                return Err(crate::error::PdfError::EncryptionError(
458                    "AES decryption requires Rev 4+ (use RC4 for Rev 2/3)".to_string(),
459                ));
460            }
461        };
462
463        aes.decrypt_cbc(encrypted_data, iv).map_err(|e| {
464            crate::error::PdfError::EncryptionError(format!("AES decryption failed: {e}"))
465        })
466    }
467
468    /// Compute AES-128 per-object key for R4 (ISO 32000-1 §7.6.2 Algorithm 1 with "sAlT").
469    ///
470    /// key = MD5(file_key || obj_num[0..3] || gen_num[0..2] || "sAlT")[0..min(key_len+5, 16)]
471    /// For AES-128 (key_len=16), min(16+5, 16) = 16, so always 16 bytes.
472    fn compute_r4_aes_object_key(&self, key: &EncryptionKey, obj_id: &ObjectId) -> Vec<u8> {
473        let mut data = Vec::new();
474        data.extend_from_slice(&key.key);
475        data.extend_from_slice(&obj_id.number().to_le_bytes()[..3]);
476        data.extend_from_slice(&obj_id.generation().to_le_bytes()[..2]);
477        data.extend_from_slice(b"sAlT");
478
479        let hash = md5::compute(&data);
480        let key_len = (key.len() + 5).min(16);
481        hash[..key_len].to_vec()
482    }
483
484    /// Compute AES-256 object-specific encryption key for Rev 5/6 (SHA-256 based).
485    fn compute_aes_object_key(&self, key: &EncryptionKey, obj_id: &ObjectId) -> Result<Vec<u8>> {
486        if self.revision < SecurityHandlerRevision::R5 {
487            return Err(crate::error::PdfError::EncryptionError(
488                "SHA-256 AES key derivation only for Rev 5+".to_string(),
489            ));
490        }
491
492        let mut data = Vec::new();
493        data.extend_from_slice(&key.key);
494        data.extend_from_slice(&obj_id.number().to_le_bytes());
495        data.extend_from_slice(&obj_id.generation().to_le_bytes());
496        data.extend_from_slice(b"sAlT");
497
498        Ok(sha256(&data))
499    }
500
501    /// Compute encryption key for AES Rev 5/6
502    pub fn compute_aes_encryption_key(
503        &self,
504        user_password: &UserPassword,
505        owner_hash: &[u8],
506        permissions: Permissions,
507        file_id: Option<&[u8]>,
508    ) -> Result<EncryptionKey> {
509        if self.revision < SecurityHandlerRevision::R5 {
510            return Err(crate::error::PdfError::EncryptionError(
511                "AES key computation only for Rev 5+".to_string(),
512            ));
513        }
514
515        // For Rev 5/6, use more secure key derivation
516        let mut data = Vec::new();
517
518        // Use UTF-8 encoding for passwords in Rev 5/6
519        let password_bytes = user_password.0.as_bytes();
520        data.extend_from_slice(password_bytes);
521
522        // Add validation data
523        data.extend_from_slice(owner_hash);
524        data.extend_from_slice(&permissions.bits().to_le_bytes());
525
526        if let Some(id) = file_id {
527            data.extend_from_slice(id);
528        }
529
530        // Use SHA-256 for stronger hashing
531        let mut hash = sha256(&data);
532
533        // Perform additional iterations for Rev 5/6 (simplified)
534        for _ in 0..100 {
535            hash = sha256(&hash);
536        }
537
538        // AES-256 requires 32 bytes
539        hash.truncate(32);
540
541        Ok(EncryptionKey::new(hash))
542    }
543
544    /// Validate user password for AES Rev 5/6
545    pub fn validate_aes_user_password(
546        &self,
547        password: &UserPassword,
548        user_hash: &[u8],
549        permissions: Permissions,
550        file_id: Option<&[u8]>,
551    ) -> Result<bool> {
552        if self.revision < SecurityHandlerRevision::R5 {
553            return Err(crate::error::PdfError::EncryptionError(
554                "AES password validation only for Rev 5+".to_string(),
555            ));
556        }
557
558        let computed_key =
559            self.compute_aes_encryption_key(password, user_hash, permissions, file_id)?;
560
561        // Compare first 32 bytes of computed hash with stored hash
562        let computed_hash = sha256(&computed_key.key);
563
564        Ok(user_hash.len() >= 32 && computed_hash[..32] == user_hash[..32])
565    }
566
567    // ========================================================================
568    // R5/R6 Password Validation (ISO 32000-1 §7.6.4.3.4)
569    // ========================================================================
570
571    /// Compute R5 user password hash (U entry) - Algorithm 8
572    ///
573    /// Returns 48 bytes: hash(32) + validation_salt(8) + key_salt(8)
574    ///
575    /// # Algorithm
576    /// 1. Generate random validation_salt (8 bytes)
577    /// 2. Generate random key_salt (8 bytes)
578    /// 3. Compute hash: SHA-256(password + validation_salt)
579    /// 4. Apply 64 iterations of SHA-256
580    /// 5. Return hash[0..32] + validation_salt + key_salt
581    pub fn compute_r5_user_hash(&self, user_password: &UserPassword) -> Result<Vec<u8>> {
582        if self.revision != SecurityHandlerRevision::R5 {
583            return Err(crate::error::PdfError::EncryptionError(
584                "R5 user hash only for Revision 5".to_string(),
585            ));
586        }
587
588        // Generate cryptographically secure random salts
589        let validation_salt = generate_salt(R5_SALT_LENGTH);
590        let key_salt = generate_salt(R5_SALT_LENGTH);
591
592        // Compute hash: SHA-256(password + validation_salt)
593        let mut data = Vec::new();
594        data.extend_from_slice(user_password.0.as_bytes());
595        data.extend_from_slice(&validation_salt);
596
597        let mut hash = sha256(&data);
598
599        // Apply R5 iterations of SHA-256 (PDF spec §7.6.4.3.4)
600        for _ in 0..R5_HASH_ITERATIONS {
601            hash = sha256(&hash);
602        }
603
604        // Construct U entry: hash[0..32] + validation_salt + key_salt
605        let mut u_entry = Vec::with_capacity(48);
606        u_entry.extend_from_slice(&hash[..32]);
607        u_entry.extend_from_slice(&validation_salt);
608        u_entry.extend_from_slice(&key_salt);
609
610        debug_assert_eq!(u_entry.len(), 48);
611        Ok(u_entry)
612    }
613
614    /// Validate R5 user password - Algorithm 11
615    ///
616    /// Returns Ok(true) if password is correct, Ok(false) if incorrect.
617    ///
618    /// # Algorithm
619    /// 1. Extract validation_salt from U[32..40]
620    /// 2. Compute hash: SHA-256(password + validation_salt)
621    /// 3. Apply 64 iterations of SHA-256
622    /// 4. Compare result with U[0..32] using constant-time comparison
623    ///
624    /// # Security
625    /// Uses constant-time comparison (`subtle::ConstantTimeEq`) to prevent
626    /// timing side-channel attacks that could leak password information.
627    pub fn validate_r5_user_password(
628        &self,
629        password: &UserPassword,
630        u_entry: &[u8],
631    ) -> Result<bool> {
632        if u_entry.len() != U_ENTRY_LENGTH {
633            return Err(crate::error::PdfError::EncryptionError(format!(
634                "R5 U entry must be {} bytes, got {}",
635                U_ENTRY_LENGTH,
636                u_entry.len()
637            )));
638        }
639
640        // Extract validation_salt from U
641        let validation_salt = &u_entry[U_VALIDATION_SALT_START..U_VALIDATION_SALT_END];
642
643        // Compute hash: SHA-256(password + validation_salt)
644        let mut data = Vec::new();
645        data.extend_from_slice(password.0.as_bytes());
646        data.extend_from_slice(validation_salt);
647
648        let mut hash = sha256(&data);
649
650        // Apply same R5 iterations as compute
651        for _ in 0..R5_HASH_ITERATIONS {
652            hash = sha256(&hash);
653        }
654
655        // SECURITY: Constant-time comparison prevents timing attacks
656        let stored_hash = &u_entry[..U_HASH_LENGTH];
657        let computed_hash = &hash[..U_HASH_LENGTH];
658        Ok(bool::from(computed_hash.ct_eq(stored_hash)))
659    }
660
661    /// Compute R5 UE entry (encrypted encryption key)
662    ///
663    /// The UE entry stores the encryption key encrypted with a key derived
664    /// from the user password.
665    ///
666    /// # Algorithm
667    /// 1. Extract key_salt from U[40..48]
668    /// 2. Compute intermediate key: SHA-256(password + key_salt)
669    /// 3. Encrypt encryption_key with intermediate_key using AES-256-CBC (zero IV)
670    pub fn compute_r5_ue_entry(
671        &self,
672        user_password: &UserPassword,
673        u_entry: &[u8],
674        encryption_key: &EncryptionKey,
675    ) -> Result<Vec<u8>> {
676        if u_entry.len() != U_ENTRY_LENGTH {
677            return Err(crate::error::PdfError::EncryptionError(format!(
678                "U entry must be {} bytes",
679                U_ENTRY_LENGTH
680            )));
681        }
682        if encryption_key.len() != UE_ENTRY_LENGTH {
683            return Err(crate::error::PdfError::EncryptionError(format!(
684                "Encryption key must be {} bytes for R5",
685                UE_ENTRY_LENGTH
686            )));
687        }
688
689        // Extract key_salt from U
690        let key_salt = &u_entry[U_KEY_SALT_START..U_KEY_SALT_END];
691
692        // Compute intermediate key: SHA-256(password + key_salt)
693        let mut data = Vec::new();
694        data.extend_from_slice(user_password.0.as_bytes());
695        data.extend_from_slice(key_salt);
696
697        let intermediate_key = sha256(&data);
698
699        // Encrypt encryption_key with intermediate_key using AES-256-CBC
700        // Zero IV as per PDF spec, no padding since 32 bytes is block-aligned
701        let aes_key = AesKey::new_256(intermediate_key)?;
702        let aes = Aes::new(aes_key);
703        let iv = [0u8; 16];
704
705        let encrypted = aes
706            .encrypt_cbc_raw(encryption_key.as_bytes(), &iv)
707            .map_err(|e| {
708                crate::error::PdfError::EncryptionError(format!("UE encryption failed: {}", e))
709            })?;
710
711        // UE is exactly 32 bytes (no padding, 32 bytes = 2 AES blocks)
712        Ok(encrypted)
713    }
714
715    /// Recover encryption key from R5 UE entry
716    ///
717    /// # Algorithm
718    /// 1. Extract key_salt from U[40..48]
719    /// 2. Compute intermediate key: SHA-256(password + key_salt)
720    /// 3. Decrypt UE with intermediate_key using AES-256-CBC (zero IV)
721    pub fn recover_r5_encryption_key(
722        &self,
723        user_password: &UserPassword,
724        u_entry: &[u8],
725        ue_entry: &[u8],
726    ) -> Result<EncryptionKey> {
727        if ue_entry.len() != UE_ENTRY_LENGTH {
728            return Err(crate::error::PdfError::EncryptionError(format!(
729                "UE entry must be {} bytes, got {}",
730                UE_ENTRY_LENGTH,
731                ue_entry.len()
732            )));
733        }
734        if u_entry.len() != U_ENTRY_LENGTH {
735            return Err(crate::error::PdfError::EncryptionError(format!(
736                "U entry must be {} bytes",
737                U_ENTRY_LENGTH
738            )));
739        }
740
741        // Extract key_salt from U
742        let key_salt = &u_entry[U_KEY_SALT_START..U_KEY_SALT_END];
743
744        // Compute intermediate key: SHA-256(password + key_salt)
745        let mut data = Vec::new();
746        data.extend_from_slice(user_password.0.as_bytes());
747        data.extend_from_slice(key_salt);
748
749        let intermediate_key = sha256(&data);
750
751        // Decrypt UE to get encryption key
752        // UE is 32 bytes = 2 AES blocks, encrypted with CBC and zero IV
753        let aes_key = AesKey::new_256(intermediate_key)?;
754        let aes = Aes::new(aes_key);
755        let iv = [0u8; 16];
756
757        let decrypted = aes.decrypt_cbc_raw(ue_entry, &iv).map_err(|e| {
758            crate::error::PdfError::EncryptionError(format!("UE decryption failed: {}", e))
759        })?;
760
761        Ok(EncryptionKey::new(decrypted))
762    }
763
764    // ========================================================================
765    // R6 Password Validation (ISO 32000-2 §7.6.4.4)
766    // ========================================================================
767
768    /// Compute R6 user password hash (U entry) using SHA-512
769    ///
770    /// R6 uses SHA-512 (first 32 bytes) instead of SHA-256 for stronger security.
771    /// Returns 48 bytes: hash(32) + validation_salt(8) + key_salt(8)
772    ///
773    /// # Algorithm (ISO 32000-2)
774    /// 1. Generate random validation_salt (8 bytes)
775    /// 2. Generate random key_salt (8 bytes)
776    /// 3. Compute hash using Algorithm 2.B (ISO 32000-2:2020 §7.6.4.3.4)
777    /// 4. Return hash[0..32] + validation_salt + key_salt
778    pub fn compute_r6_user_hash(&self, user_password: &UserPassword) -> Result<Vec<u8>> {
779        if self.revision != SecurityHandlerRevision::R6 {
780            return Err(crate::error::PdfError::EncryptionError(
781                "R6 user hash only for Revision 6".to_string(),
782            ));
783        }
784
785        // Generate cryptographically secure random salts
786        let validation_salt = generate_salt(R6_SALT_LENGTH);
787        let key_salt = generate_salt(R6_SALT_LENGTH);
788
789        // Compute hash using Algorithm 2.B (ISO 32000-2:2020)
790        // For user password creation, u_entry is empty
791        let hash = compute_hash_r6_algorithm_2b(
792            user_password.0.as_bytes(),
793            &validation_salt,
794            &[], // No U entry for user password creation
795        )?;
796
797        // Construct U entry: hash[0..32] + validation_salt + key_salt
798        let mut u_entry = Vec::with_capacity(48);
799        u_entry.extend_from_slice(&hash[..32]);
800        u_entry.extend_from_slice(&validation_salt);
801        u_entry.extend_from_slice(&key_salt);
802
803        debug_assert_eq!(u_entry.len(), 48);
804        Ok(u_entry)
805    }
806
807    /// Validate R6 user password using Algorithm 2.B (ISO 32000-2:2020 §7.6.4.3.4)
808    ///
809    /// Returns Ok(true) if password is correct, Ok(false) if incorrect.
810    ///
811    /// # Algorithm
812    /// 1. Extract validation_salt from U[32..40]
813    /// 2. Compute hash using Algorithm 2.B with the validation_salt
814    /// 3. Compare result with U[0..32] using constant-time comparison
815    ///
816    /// # Security
817    /// Uses constant-time comparison (`subtle::ConstantTimeEq`) to prevent
818    /// timing side-channel attacks that could leak password information.
819    pub fn validate_r6_user_password(
820        &self,
821        password: &UserPassword,
822        u_entry: &[u8],
823    ) -> Result<bool> {
824        if u_entry.len() != U_ENTRY_LENGTH {
825            return Err(crate::error::PdfError::EncryptionError(format!(
826                "R6 U entry must be {} bytes, got {}",
827                U_ENTRY_LENGTH,
828                u_entry.len()
829            )));
830        }
831
832        // Extract validation_salt from U[32..40]
833        let validation_salt = &u_entry[U_VALIDATION_SALT_START..U_VALIDATION_SALT_END];
834
835        // Compute hash using Algorithm 2.B (ISO 32000-2:2020)
836        // For user password validation, u_entry is empty per spec
837        let hash = compute_hash_r6_algorithm_2b(password.0.as_bytes(), validation_salt, &[])?;
838
839        // SECURITY: Constant-time comparison prevents timing attacks
840        let stored_hash = &u_entry[..U_HASH_LENGTH];
841        let computed_hash = &hash[..U_HASH_LENGTH];
842        Ok(bool::from(computed_hash.ct_eq(stored_hash)))
843    }
844
845    /// Compute R6 UE entry (encrypted encryption key) using Algorithm 2.B (ISO 32000-2:2020 §7.6.4.3.4)
846    ///
847    /// # Algorithm
848    /// 1. Extract key_salt from U[40..48]
849    /// 2. Compute intermediate key using Algorithm 2.B(password, key_salt, u_entry)
850    /// 3. Encrypt encryption_key using AES-256-CBC with intermediate_key and IV = 0
851    pub fn compute_r6_ue_entry(
852        &self,
853        user_password: &UserPassword,
854        u_entry: &[u8],
855        encryption_key: &EncryptionKey,
856    ) -> Result<Vec<u8>> {
857        if u_entry.len() != U_ENTRY_LENGTH {
858            return Err(crate::error::PdfError::EncryptionError(format!(
859                "U entry must be {} bytes",
860                U_ENTRY_LENGTH
861            )));
862        }
863        if encryption_key.len() != UE_ENTRY_LENGTH {
864            return Err(crate::error::PdfError::EncryptionError(format!(
865                "Encryption key must be {} bytes for R6",
866                UE_ENTRY_LENGTH
867            )));
868        }
869
870        // Extract key_salt from U[40..48]
871        let key_salt = &u_entry[U_KEY_SALT_START..U_KEY_SALT_END];
872
873        // Compute intermediate key using Algorithm 2.B (ISO 32000-2:2020)
874        // For key derivation, we pass the full U entry as the third parameter
875        let hash = compute_hash_r6_algorithm_2b(user_password.0.as_bytes(), key_salt, u_entry)?;
876        let intermediate_key = hash[..U_HASH_LENGTH].to_vec();
877
878        // Encrypt encryption_key with intermediate_key using AES-256-CBC, IV = 0
879        let aes_key = AesKey::new_256(intermediate_key)?;
880        let aes = Aes::new(aes_key);
881        let iv = [0u8; 16];
882
883        let encrypted = aes
884            .encrypt_cbc_raw(encryption_key.as_bytes(), &iv)
885            .map_err(|e| {
886                crate::error::PdfError::EncryptionError(format!("UE encryption failed: {}", e))
887            })?;
888
889        Ok(encrypted)
890    }
891
892    /// Recover encryption key from R6 UE entry using Algorithm 2.B (ISO 32000-2:2020 §7.6.4.3.4)
893    ///
894    /// # Algorithm
895    /// 1. Extract key_salt from U[40..48]
896    /// 2. Compute intermediate key using Algorithm 2.B(password, key_salt, u_entry)
897    /// 3. Decrypt UE using AES-256-CBC with intermediate_key and IV = 0
898    pub fn recover_r6_encryption_key(
899        &self,
900        user_password: &UserPassword,
901        u_entry: &[u8],
902        ue_entry: &[u8],
903    ) -> Result<EncryptionKey> {
904        if ue_entry.len() != UE_ENTRY_LENGTH {
905            return Err(crate::error::PdfError::EncryptionError(format!(
906                "UE entry must be {} bytes, got {}",
907                UE_ENTRY_LENGTH,
908                ue_entry.len()
909            )));
910        }
911        if u_entry.len() != U_ENTRY_LENGTH {
912            return Err(crate::error::PdfError::EncryptionError(format!(
913                "U entry must be {} bytes",
914                U_ENTRY_LENGTH
915            )));
916        }
917
918        // Extract key_salt from U[40..48]
919        let key_salt = &u_entry[U_KEY_SALT_START..U_KEY_SALT_END];
920
921        // Compute intermediate key using Algorithm 2.B (ISO 32000-2:2020)
922        // For key derivation, we pass the full U entry as the third parameter
923        let hash = compute_hash_r6_algorithm_2b(user_password.0.as_bytes(), key_salt, u_entry)?;
924        let intermediate_key = hash[..U_HASH_LENGTH].to_vec();
925
926        // Decrypt UE to get encryption key using AES-256-CBC with IV = 0
927        let aes_key = AesKey::new_256(intermediate_key)?;
928        let aes = Aes::new(aes_key);
929        let iv = [0u8; 16];
930
931        let decrypted = aes.decrypt_cbc_raw(ue_entry, &iv).map_err(|e| {
932            crate::error::PdfError::EncryptionError(format!("UE decryption failed: {}", e))
933        })?;
934
935        Ok(EncryptionKey::new(decrypted))
936    }
937
938    // ========================================================================
939    // R6 Perms Entry (ISO 32000-2 Table 25)
940    // ========================================================================
941
942    /// Compute R6 Perms entry (encrypted permissions)
943    ///
944    /// The Perms entry is a 16-byte value that encrypts permissions using AES-256-ECB.
945    /// This allows verification that permissions haven't been tampered with.
946    ///
947    /// # Plaintext Structure (16 bytes)
948    /// - Bytes 0-3: Permissions (P value, little-endian)
949    /// - Bytes 4-7: 0xFFFFFFFF (fixed marker)
950    /// - Bytes 8-10: "adb" (literal verification string)
951    /// - Byte 11: 'T' or 'F' (EncryptMetadata flag)
952    /// - Bytes 12-15: 0x00 (padding)
953    pub fn compute_r6_perms_entry(
954        &self,
955        permissions: Permissions,
956        encryption_key: &EncryptionKey,
957        encrypt_metadata: bool,
958    ) -> Result<Vec<u8>> {
959        if self.revision != SecurityHandlerRevision::R6 {
960            return Err(crate::error::PdfError::EncryptionError(
961                "Perms entry only for Revision 6".to_string(),
962            ));
963        }
964        if encryption_key.len() != UE_ENTRY_LENGTH {
965            return Err(crate::error::PdfError::EncryptionError(format!(
966                "Encryption key must be {} bytes for R6 Perms",
967                UE_ENTRY_LENGTH
968            )));
969        }
970
971        // Construct plaintext: P + 0xFFFFFFFF + "adb" + T/F + padding
972        let mut plaintext = vec![0u8; PERMS_ENTRY_LENGTH];
973
974        // Permissions (4 bytes, little-endian)
975        let p_bytes = (permissions.bits() as u32).to_le_bytes();
976        plaintext[PERMS_P_START..PERMS_P_END].copy_from_slice(&p_bytes);
977
978        // Fixed marker bytes (0xFFFFFFFF)
979        plaintext[PERMS_MARKER_START..PERMS_MARKER_END].copy_from_slice(&PERMS_MARKER);
980
981        // Literal "adb" verification string
982        plaintext[PERMS_LITERAL_START..PERMS_LITERAL_END].copy_from_slice(PERMS_LITERAL);
983
984        // EncryptMetadata flag
985        plaintext[PERMS_ENCRYPT_META_BYTE] = if encrypt_metadata { b'T' } else { b'F' };
986
987        // Bytes 12-15 remain 0x00 (padding)
988
989        // Encrypt with AES-256-ECB
990        let aes_key = AesKey::new_256(encryption_key.key.clone())?;
991        let aes = Aes::new(aes_key);
992
993        let encrypted = aes.encrypt_ecb(&plaintext).map_err(|e| {
994            crate::error::PdfError::EncryptionError(format!("Perms encryption failed: {}", e))
995        })?;
996
997        Ok(encrypted)
998    }
999
1000    /// Validate R6 Perms entry by decrypting and checking structure
1001    ///
1002    /// Returns Ok(true) if the Perms entry is valid and matches expected permissions.
1003    /// Returns Ok(false) if decryption succeeds but structure/permissions don't match.
1004    /// Returns Err if decryption fails.
1005    ///
1006    /// # Security
1007    /// Uses constant-time comparison (`subtle::ConstantTimeEq`) for permissions
1008    /// comparison to prevent timing side-channel attacks.
1009    pub fn validate_r6_perms(
1010        &self,
1011        perms_entry: &[u8],
1012        encryption_key: &EncryptionKey,
1013        expected_permissions: Permissions,
1014    ) -> Result<bool> {
1015        if perms_entry.len() != PERMS_ENTRY_LENGTH {
1016            return Err(crate::error::PdfError::EncryptionError(format!(
1017                "Perms entry must be {} bytes, got {}",
1018                PERMS_ENTRY_LENGTH,
1019                perms_entry.len()
1020            )));
1021        }
1022        if encryption_key.len() != UE_ENTRY_LENGTH {
1023            return Err(crate::error::PdfError::EncryptionError(format!(
1024                "Encryption key must be {} bytes",
1025                UE_ENTRY_LENGTH
1026            )));
1027        }
1028
1029        // Decrypt with AES-256-ECB
1030        let aes_key = AesKey::new_256(encryption_key.key.clone())?;
1031        let aes = Aes::new(aes_key);
1032
1033        let decrypted = aes.decrypt_ecb(perms_entry).map_err(|e| {
1034            crate::error::PdfError::EncryptionError(format!("Perms decryption failed: {}", e))
1035        })?;
1036
1037        // Verify fixed marker
1038        if decrypted[PERMS_MARKER_START..PERMS_MARKER_END] != PERMS_MARKER {
1039            return Ok(false);
1040        }
1041
1042        // Verify literal "adb"
1043        if &decrypted[PERMS_LITERAL_START..PERMS_LITERAL_END] != PERMS_LITERAL {
1044            return Ok(false);
1045        }
1046
1047        // SECURITY: Constant-time comparison for permissions
1048        let expected_bytes = (expected_permissions.bits() as u32).to_le_bytes();
1049        let actual_bytes = &decrypted[PERMS_P_START..PERMS_P_END];
1050        Ok(bool::from(expected_bytes.ct_eq(actual_bytes)))
1051    }
1052
1053    /// Extract EncryptMetadata flag from decrypted Perms entry
1054    ///
1055    /// Returns Ok(Some(true)) if EncryptMetadata='T', Ok(Some(false)) if 'F',
1056    /// Ok(None) if Perms structure is invalid.
1057    pub fn extract_r6_encrypt_metadata(
1058        &self,
1059        perms_entry: &[u8],
1060        encryption_key: &EncryptionKey,
1061    ) -> Result<Option<bool>> {
1062        if perms_entry.len() != PERMS_ENTRY_LENGTH || encryption_key.len() != UE_ENTRY_LENGTH {
1063            return Ok(None);
1064        }
1065
1066        let aes_key = AesKey::new_256(encryption_key.key.clone())?;
1067        let aes = Aes::new(aes_key);
1068
1069        let decrypted = match aes.decrypt_ecb(perms_entry) {
1070            Ok(d) => d,
1071            Err(_) => return Ok(None),
1072        };
1073
1074        // Verify structure before extracting flag
1075        if decrypted[PERMS_MARKER_START..PERMS_MARKER_END] != PERMS_MARKER
1076            || &decrypted[PERMS_LITERAL_START..PERMS_LITERAL_END] != PERMS_LITERAL
1077        {
1078            return Ok(None);
1079        }
1080
1081        // Extract EncryptMetadata flag
1082        match decrypted[PERMS_ENCRYPT_META_BYTE] {
1083            b'T' => Ok(Some(true)),
1084            b'F' => Ok(Some(false)),
1085            _ => Ok(None), // Invalid flag value
1086        }
1087    }
1088
1089    // ========================================================================
1090    // R5/R6 Owner Password Support (ISO 32000-1 §7.6.4.3.3)
1091    // ========================================================================
1092
1093    /// Compute R5 owner password hash (O entry)
1094    ///
1095    /// Algorithm 9 (ISO 32000-1): Creates 48-byte O entry
1096    /// - Bytes 0-31: SHA-256(owner_password || validation_salt)
1097    /// - Bytes 32-39: validation_salt (8 random bytes)
1098    /// - Bytes 40-47: key_salt (8 random bytes)
1099    pub fn compute_r5_owner_hash(
1100        &self,
1101        owner_password: &OwnerPassword,
1102        _user_password: &UserPassword,
1103    ) -> Result<Vec<u8>> {
1104        if self.revision != SecurityHandlerRevision::R5 {
1105            return Err(crate::error::PdfError::EncryptionError(
1106                "R5 owner hash only for Revision 5".to_string(),
1107            ));
1108        }
1109
1110        // Generate random salts
1111        let validation_salt = generate_salt(R5_SALT_LENGTH);
1112        let key_salt = generate_salt(R5_SALT_LENGTH);
1113
1114        // Compute hash: SHA-256(owner_password || validation_salt)
1115        let mut data = Vec::new();
1116        data.extend_from_slice(owner_password.0.as_bytes());
1117        data.extend_from_slice(&validation_salt);
1118
1119        let hash = sha256(&data);
1120
1121        // Construct O entry: hash[0..32] + validation_salt + key_salt
1122        let mut o_entry = Vec::with_capacity(U_ENTRY_LENGTH);
1123        o_entry.extend_from_slice(&hash[..U_HASH_LENGTH]);
1124        o_entry.extend_from_slice(&validation_salt);
1125        o_entry.extend_from_slice(&key_salt);
1126
1127        debug_assert_eq!(o_entry.len(), U_ENTRY_LENGTH);
1128        Ok(o_entry)
1129    }
1130
1131    /// Validate R5 owner password
1132    ///
1133    /// Algorithm 12 (ISO 32000-1): Validates owner password against O entry
1134    pub fn validate_r5_owner_password(
1135        &self,
1136        owner_password: &OwnerPassword,
1137        o_entry: &[u8],
1138    ) -> Result<bool> {
1139        if o_entry.len() != U_ENTRY_LENGTH {
1140            return Err(crate::error::PdfError::EncryptionError(format!(
1141                "R5 O entry must be {} bytes, got {}",
1142                U_ENTRY_LENGTH,
1143                o_entry.len()
1144            )));
1145        }
1146
1147        // Extract validation_salt from O (bytes 32-39)
1148        let validation_salt = &o_entry[U_VALIDATION_SALT_START..U_VALIDATION_SALT_END];
1149
1150        // Compute hash: SHA-256(owner_password || validation_salt)
1151        let mut data = Vec::new();
1152        data.extend_from_slice(owner_password.0.as_bytes());
1153        data.extend_from_slice(validation_salt);
1154
1155        let hash = sha256(&data);
1156
1157        // SECURITY: Constant-time comparison prevents timing attacks
1158        let stored_hash = &o_entry[..U_HASH_LENGTH];
1159        Ok(bool::from(hash[..U_HASH_LENGTH].ct_eq(stored_hash)))
1160    }
1161
1162    /// Compute R5 OE entry (encrypted encryption key with owner password)
1163    ///
1164    /// OE = AES-256-CBC(encryption_key, key=intermediate_key, iv=zeros)
1165    /// where intermediate_key = SHA-256(owner_password || key_salt)
1166    pub fn compute_r5_oe_entry(
1167        &self,
1168        owner_password: &OwnerPassword,
1169        o_entry: &[u8],
1170        encryption_key: &[u8],
1171    ) -> Result<Vec<u8>> {
1172        if o_entry.len() != U_ENTRY_LENGTH {
1173            return Err(crate::error::PdfError::EncryptionError(format!(
1174                "O entry must be {} bytes",
1175                U_ENTRY_LENGTH
1176            )));
1177        }
1178        if encryption_key.len() != UE_ENTRY_LENGTH {
1179            return Err(crate::error::PdfError::EncryptionError(format!(
1180                "Encryption key must be {} bytes",
1181                UE_ENTRY_LENGTH
1182            )));
1183        }
1184
1185        // Extract key_salt from O (bytes 40-47)
1186        let key_salt = &o_entry[U_KEY_SALT_START..U_KEY_SALT_END];
1187
1188        // Compute intermediate key: SHA-256(owner_password || key_salt)
1189        let mut data = Vec::new();
1190        data.extend_from_slice(owner_password.0.as_bytes());
1191        data.extend_from_slice(key_salt);
1192
1193        let intermediate_key = sha256(&data);
1194
1195        // Encrypt encryption_key with intermediate_key using AES-256-CBC
1196        let aes = Aes::new(AesKey::new_256(intermediate_key)?);
1197        let iv = [0u8; 16];
1198
1199        let encrypted = aes.encrypt_cbc_raw(encryption_key, &iv).map_err(|e| {
1200            crate::error::PdfError::EncryptionError(format!("OE encryption failed: {}", e))
1201        })?;
1202
1203        // OE is first 32 bytes of encrypted output
1204        Ok(encrypted[..UE_ENTRY_LENGTH].to_vec())
1205    }
1206
1207    /// Recover encryption key from R5 OE entry using owner password
1208    pub fn recover_r5_owner_encryption_key(
1209        &self,
1210        owner_password: &OwnerPassword,
1211        o_entry: &[u8],
1212        oe_entry: &[u8],
1213    ) -> Result<Vec<u8>> {
1214        if o_entry.len() != U_ENTRY_LENGTH {
1215            return Err(crate::error::PdfError::EncryptionError(format!(
1216                "O entry must be {} bytes",
1217                U_ENTRY_LENGTH
1218            )));
1219        }
1220        if oe_entry.len() != UE_ENTRY_LENGTH {
1221            return Err(crate::error::PdfError::EncryptionError(format!(
1222                "OE entry must be {} bytes",
1223                UE_ENTRY_LENGTH
1224            )));
1225        }
1226
1227        // Extract key_salt from O (bytes 40-47)
1228        let key_salt = &o_entry[U_KEY_SALT_START..U_KEY_SALT_END];
1229
1230        // Compute intermediate key
1231        let mut data = Vec::new();
1232        data.extend_from_slice(owner_password.0.as_bytes());
1233        data.extend_from_slice(key_salt);
1234
1235        let intermediate_key = sha256(&data);
1236
1237        // Decrypt OE to get encryption key
1238        let aes = Aes::new(AesKey::new_256(intermediate_key)?);
1239        let iv = [0u8; 16];
1240
1241        let decrypted = aes.decrypt_cbc_raw(oe_entry, &iv).map_err(|e| {
1242            crate::error::PdfError::EncryptionError(format!("OE decryption failed: {}", e))
1243        })?;
1244
1245        Ok(decrypted)
1246    }
1247
1248    /// Compute R6 owner password hash (O entry)
1249    ///
1250    /// R6 uses Algorithm 2.B (complex hash) for owner password too
1251    pub fn compute_r6_owner_hash(
1252        &self,
1253        owner_password: &OwnerPassword,
1254        u_entry: &[u8],
1255    ) -> Result<Vec<u8>> {
1256        if self.revision != SecurityHandlerRevision::R6 {
1257            return Err(crate::error::PdfError::EncryptionError(
1258                "R6 owner hash only for Revision 6".to_string(),
1259            ));
1260        }
1261        if u_entry.len() != U_ENTRY_LENGTH {
1262            return Err(crate::error::PdfError::EncryptionError(format!(
1263                "U entry must be {} bytes for R6 O computation",
1264                U_ENTRY_LENGTH
1265            )));
1266        }
1267
1268        // Generate random salts
1269        let validation_salt = generate_salt(R6_SALT_LENGTH);
1270        let key_salt = generate_salt(R6_SALT_LENGTH);
1271
1272        // For R6, use Algorithm 2.B: hash = Alg2B(owner_password || validation_salt || U[0..48])
1273        let mut input = Vec::new();
1274        input.extend_from_slice(owner_password.0.as_bytes());
1275        input.extend_from_slice(&validation_salt);
1276        input.extend_from_slice(u_entry);
1277
1278        let hash = compute_hash_r6_algorithm_2b(&input, owner_password.0.as_bytes(), u_entry)?;
1279
1280        // Construct O entry: hash[0..32] + validation_salt + key_salt
1281        let mut o_entry = Vec::with_capacity(U_ENTRY_LENGTH);
1282        o_entry.extend_from_slice(&hash[..U_HASH_LENGTH]);
1283        o_entry.extend_from_slice(&validation_salt);
1284        o_entry.extend_from_slice(&key_salt);
1285
1286        debug_assert_eq!(o_entry.len(), U_ENTRY_LENGTH);
1287        Ok(o_entry)
1288    }
1289
1290    /// Validate R6 owner password
1291    ///
1292    /// Uses Algorithm 2.B to validate owner password
1293    pub fn validate_r6_owner_password(
1294        &self,
1295        owner_password: &OwnerPassword,
1296        o_entry: &[u8],
1297        u_entry: &[u8],
1298    ) -> Result<bool> {
1299        if o_entry.len() != U_ENTRY_LENGTH {
1300            return Err(crate::error::PdfError::EncryptionError(format!(
1301                "R6 O entry must be {} bytes",
1302                U_ENTRY_LENGTH
1303            )));
1304        }
1305        if u_entry.len() != U_ENTRY_LENGTH {
1306            return Err(crate::error::PdfError::EncryptionError(format!(
1307                "R6 U entry must be {} bytes",
1308                U_ENTRY_LENGTH
1309            )));
1310        }
1311
1312        // Extract validation_salt from O (bytes 32-39)
1313        let validation_salt = &o_entry[U_VALIDATION_SALT_START..U_VALIDATION_SALT_END];
1314
1315        // Compute hash using Algorithm 2.B
1316        let mut input = Vec::new();
1317        input.extend_from_slice(owner_password.0.as_bytes());
1318        input.extend_from_slice(validation_salt);
1319        input.extend_from_slice(u_entry);
1320
1321        let hash = compute_hash_r6_algorithm_2b(&input, owner_password.0.as_bytes(), u_entry)?;
1322
1323        // SECURITY: Constant-time comparison prevents timing attacks
1324        let stored_hash = &o_entry[..U_HASH_LENGTH];
1325        Ok(bool::from(hash[..U_HASH_LENGTH].ct_eq(stored_hash)))
1326    }
1327
1328    /// Compute R6 OE entry (encrypted encryption key with owner password)
1329    ///
1330    /// Uses Algorithm 2.B to derive intermediate key
1331    pub fn compute_r6_oe_entry(
1332        &self,
1333        owner_password: &OwnerPassword,
1334        o_entry: &[u8],
1335        u_entry: &[u8],
1336        encryption_key: &[u8],
1337    ) -> Result<Vec<u8>> {
1338        if o_entry.len() != U_ENTRY_LENGTH {
1339            return Err(crate::error::PdfError::EncryptionError(format!(
1340                "O entry must be {} bytes",
1341                U_ENTRY_LENGTH
1342            )));
1343        }
1344        if u_entry.len() != U_ENTRY_LENGTH {
1345            return Err(crate::error::PdfError::EncryptionError(format!(
1346                "U entry must be {} bytes",
1347                U_ENTRY_LENGTH
1348            )));
1349        }
1350        if encryption_key.len() != UE_ENTRY_LENGTH {
1351            return Err(crate::error::PdfError::EncryptionError(format!(
1352                "Encryption key must be {} bytes",
1353                UE_ENTRY_LENGTH
1354            )));
1355        }
1356
1357        // Extract key_salt from O (bytes 40-47)
1358        let key_salt = &o_entry[U_KEY_SALT_START..U_KEY_SALT_END];
1359
1360        // Compute intermediate key using Algorithm 2.B
1361        let mut input = Vec::new();
1362        input.extend_from_slice(owner_password.0.as_bytes());
1363        input.extend_from_slice(key_salt);
1364        input.extend_from_slice(u_entry);
1365
1366        let intermediate_key =
1367            compute_hash_r6_algorithm_2b(&input, owner_password.0.as_bytes(), u_entry)?;
1368
1369        // Encrypt encryption_key with intermediate_key using AES-256-CBC
1370        let aes = Aes::new(AesKey::new_256(intermediate_key[..32].to_vec())?);
1371        let iv = [0u8; 16];
1372
1373        let encrypted = aes.encrypt_cbc_raw(encryption_key, &iv).map_err(|e| {
1374            crate::error::PdfError::EncryptionError(format!("OE encryption failed: {}", e))
1375        })?;
1376
1377        Ok(encrypted[..UE_ENTRY_LENGTH].to_vec())
1378    }
1379
1380    /// Recover encryption key from R6 OE entry using owner password
1381    pub fn recover_r6_owner_encryption_key(
1382        &self,
1383        owner_password: &OwnerPassword,
1384        o_entry: &[u8],
1385        u_entry: &[u8],
1386        oe_entry: &[u8],
1387    ) -> Result<Vec<u8>> {
1388        if o_entry.len() != U_ENTRY_LENGTH {
1389            return Err(crate::error::PdfError::EncryptionError(format!(
1390                "O entry must be {} bytes",
1391                U_ENTRY_LENGTH
1392            )));
1393        }
1394        if u_entry.len() != U_ENTRY_LENGTH {
1395            return Err(crate::error::PdfError::EncryptionError(format!(
1396                "U entry must be {} bytes",
1397                U_ENTRY_LENGTH
1398            )));
1399        }
1400        if oe_entry.len() != UE_ENTRY_LENGTH {
1401            return Err(crate::error::PdfError::EncryptionError(format!(
1402                "OE entry must be {} bytes",
1403                UE_ENTRY_LENGTH
1404            )));
1405        }
1406
1407        // Extract key_salt from O (bytes 40-47)
1408        let key_salt = &o_entry[U_KEY_SALT_START..U_KEY_SALT_END];
1409
1410        // Compute intermediate key using Algorithm 2.B
1411        let mut input = Vec::new();
1412        input.extend_from_slice(owner_password.0.as_bytes());
1413        input.extend_from_slice(key_salt);
1414        input.extend_from_slice(u_entry);
1415
1416        let intermediate_key =
1417            compute_hash_r6_algorithm_2b(&input, owner_password.0.as_bytes(), u_entry)?;
1418
1419        // Decrypt OE to get encryption key
1420        let aes = Aes::new(AesKey::new_256(intermediate_key[..32].to_vec())?);
1421        let iv = [0u8; 16];
1422
1423        let decrypted = aes.decrypt_cbc_raw(oe_entry, &iv).map_err(|e| {
1424            crate::error::PdfError::EncryptionError(format!("OE decryption failed: {}", e))
1425        })?;
1426
1427        Ok(decrypted)
1428    }
1429
1430    /// Compute object-specific encryption key (Algorithm 1, ISO 32000-1 §7.6.2)
1431    pub fn compute_object_key(&self, key: &EncryptionKey, obj_id: &ObjectId) -> Vec<u8> {
1432        let mut data = Vec::new();
1433        data.extend_from_slice(&key.key);
1434        data.extend_from_slice(&obj_id.number().to_le_bytes()[..3]); // Low 3 bytes
1435        data.extend_from_slice(&obj_id.generation().to_le_bytes()[..2]); // Low 2 bytes
1436
1437        let hash = md5::compute(&data);
1438        let key_len = (key.len() + 5).min(16);
1439        hash[..key_len].to_vec()
1440    }
1441
1442    /// Validate user password (Algorithm 6, ISO 32000-1 §7.6.3.4)
1443    ///
1444    /// Returns Ok(true) if password is correct, Ok(false) if incorrect.
1445    /// Returns Err only on internal errors.
1446    pub fn validate_user_password(
1447        &self,
1448        password: &UserPassword,
1449        user_hash: &[u8],
1450        owner_hash: &[u8],
1451        permissions: Permissions,
1452        file_id: Option<&[u8]>,
1453    ) -> Result<bool> {
1454        // Compute encryption key from provided password
1455        let key = self.compute_encryption_key(password, owner_hash, permissions, file_id)?;
1456
1457        match self.revision {
1458            SecurityHandlerRevision::R2 => {
1459                // For R2: Encrypt padding with key and compare with U
1460                let rc4_key = Rc4Key::from_slice(&key.key);
1461                let encrypted_padding = rc4_encrypt(&rc4_key, &PADDING);
1462
1463                // Compare with stored user hash
1464                Ok(user_hash.len() >= 32 && encrypted_padding[..] == user_hash[..32])
1465            }
1466            SecurityHandlerRevision::R3 | SecurityHandlerRevision::R4 => {
1467                // For R3/R4: Compute MD5 hash including file ID
1468                let mut data = Vec::new();
1469                data.extend_from_slice(&PADDING);
1470
1471                if let Some(id) = file_id {
1472                    data.extend_from_slice(id);
1473                }
1474
1475                let hash = md5::compute(&data);
1476
1477                // Encrypt hash with RC4
1478                let rc4_key = Rc4Key::from_slice(&key.key);
1479                let mut encrypted = rc4_encrypt(&rc4_key, hash.as_ref());
1480
1481                // Do 19 additional iterations with modified keys
1482                for i in 1..=19 {
1483                    let mut key_bytes = key.key.clone();
1484                    for byte in &mut key_bytes {
1485                        *byte ^= i as u8;
1486                    }
1487                    let iter_key = Rc4Key::from_slice(&key_bytes);
1488                    encrypted = rc4_encrypt(&iter_key, &encrypted);
1489                }
1490
1491                // Compare first 16 bytes of result with first 16 bytes of U
1492                Ok(user_hash.len() >= 16 && encrypted[..16] == user_hash[..16])
1493            }
1494            SecurityHandlerRevision::R5 | SecurityHandlerRevision::R6 => {
1495                // For R5/R6, use AES-based validation
1496                self.validate_aes_user_password(password, user_hash, permissions, file_id)
1497            }
1498        }
1499    }
1500
1501    /// Validate owner password (Algorithm 7, ISO 32000-1 §7.6.3.4)
1502    ///
1503    /// Returns Ok(true) if password is correct, Ok(false) if incorrect.
1504    /// Returns Err only on internal errors.
1505    ///
1506    /// Note: For owner password validation, we first decrypt the user password
1507    /// from the owner hash, then validate that user password.
1508    ///
1509    /// # Parameters
1510    /// - `owner_password`: The owner password to validate
1511    /// - `owner_hash`: The O entry from the encryption dictionary
1512    /// - `_user_password`: Unused for R2-R4 (recovered from owner_hash), ignored for R5/R6
1513    /// - `_permissions`: Unused for R5/R6 (not part of validation)
1514    /// - `_file_id`: Unused for R5/R6 (not part of validation)
1515    /// - `u_entry`: Required for R6 (U entry needed for Algorithm 2.B), ignored for R2-R5
1516    pub fn validate_owner_password(
1517        &self,
1518        owner_password: &OwnerPassword,
1519        owner_hash: &[u8],
1520        _user_password: &UserPassword, // Will be recovered from owner_hash
1521        _permissions: Permissions,
1522        _file_id: Option<&[u8]>,
1523        u_entry: Option<&[u8]>,
1524    ) -> Result<bool> {
1525        match self.revision {
1526            SecurityHandlerRevision::R2
1527            | SecurityHandlerRevision::R3
1528            | SecurityHandlerRevision::R4 => {
1529                // Step 1: Pad owner password
1530                let owner_pad = Self::pad_password(&owner_password.0);
1531
1532                // Step 2: Create MD5 hash of owner password
1533                let mut hash = md5::compute(&owner_pad).to_vec();
1534
1535                // Step 3: For revision 3+, do 50 additional iterations
1536                if self.revision >= SecurityHandlerRevision::R3 {
1537                    for _ in 0..50 {
1538                        hash = md5::compute(&hash).to_vec();
1539                    }
1540                }
1541
1542                // Step 4: Create RC4 key from hash (truncated to key length)
1543                let rc4_key = Rc4Key::from_slice(&hash[..self.key_length]);
1544
1545                // Step 5: Decrypt owner hash to get user password
1546                let mut decrypted = owner_hash[..32].to_vec();
1547
1548                // For R3+, do 19 iterations in reverse
1549                if self.revision >= SecurityHandlerRevision::R3 {
1550                    for i in (0..20).rev() {
1551                        let mut key_bytes = hash[..self.key_length].to_vec();
1552                        for byte in &mut key_bytes {
1553                            *byte ^= i as u8;
1554                        }
1555                        let iter_key = Rc4Key::from_slice(&key_bytes);
1556                        decrypted = rc4_encrypt(&iter_key, &decrypted);
1557                    }
1558                } else {
1559                    // For R2, single decryption
1560                    decrypted = rc4_encrypt(&rc4_key, &decrypted);
1561                }
1562
1563                // Step 6: The decrypted data should be the padded user password
1564                // Try to validate by computing what the owner hash SHOULD be
1565                // with this owner password, and compare
1566
1567                // Extract potential user password (remove padding)
1568                let user_pwd_bytes = decrypted
1569                    .iter()
1570                    .take_while(|&&b| b != 0x28 || decrypted.starts_with(&PADDING))
1571                    .copied()
1572                    .collect::<Vec<u8>>();
1573
1574                let recovered_user =
1575                    UserPassword(String::from_utf8_lossy(&user_pwd_bytes).to_string());
1576
1577                // Compute what owner hash should be with this owner password
1578                let computed_owner = self.compute_owner_hash(owner_password, &recovered_user);
1579
1580                // Compare with stored owner hash
1581                Ok(computed_owner[..32] == owner_hash[..32])
1582            }
1583            SecurityHandlerRevision::R5 => {
1584                // R5 uses simple SHA-256 validation (Algorithm 12)
1585                // owner_hash is the O entry (48 bytes)
1586                self.validate_r5_owner_password(owner_password, owner_hash)
1587            }
1588            SecurityHandlerRevision::R6 => {
1589                // R6 uses Algorithm 2.B which requires U entry
1590                let u = u_entry.ok_or_else(|| {
1591                    crate::error::PdfError::EncryptionError(
1592                        "R6 owner password validation requires U entry".to_string(),
1593                    )
1594                })?;
1595                self.validate_r6_owner_password(owner_password, owner_hash, u)
1596            }
1597        }
1598    }
1599}
1600
1601/// Helper function for RC4 encryption
1602fn rc4_encrypt(key: &Rc4Key, data: &[u8]) -> Vec<u8> {
1603    let mut cipher = Rc4::new(key);
1604    cipher.process(data)
1605}
1606
1607// Use the md5 crate for actual MD5 hashing (required for PDF encryption)
1608
1609/// SHA-256 implementation using RustCrypto (production-grade)
1610///
1611/// Returns a 32-byte hash of the input data according to FIPS 180-4.
1612/// Used for R5 password validation and key derivation.
1613fn sha256(data: &[u8]) -> Vec<u8> {
1614    Sha256::digest(data).to_vec()
1615}
1616
1617/// SHA-384 implementation using RustCrypto (production-grade)
1618///
1619/// Returns a 48-byte hash of the input data according to FIPS 180-4.
1620/// Used for R6 Algorithm 2.B hash rotation.
1621fn sha384(data: &[u8]) -> Vec<u8> {
1622    Sha384::digest(data).to_vec()
1623}
1624
1625/// SHA-512 implementation using RustCrypto (production-grade)
1626///
1627/// Returns a 64-byte hash of the input data according to FIPS 180-4.
1628/// Used for R6 password validation and key derivation.
1629fn sha512(data: &[u8]) -> Vec<u8> {
1630    Sha512::digest(data).to_vec()
1631}
1632
1633// ============================================================================
1634// Algorithm 2.B - R6 Key Derivation (ISO 32000-2:2020 §7.6.4.3.4)
1635// ============================================================================
1636
1637/// Minimum number of rounds for Algorithm 2.B
1638const ALGORITHM_2B_MIN_ROUNDS: usize = 64;
1639
1640/// Maximum rounds (DoS protection, not in spec but common implementation)
1641const ALGORITHM_2B_MAX_ROUNDS: usize = 2048;
1642
1643/// Maximum password length (ISO 32000-2 §7.6.3.3.2 recommends 127 bytes)
1644/// This prevents DoS via massive allocation: 1MB password × 64 repetitions = 64MB/round
1645const ALGORITHM_2B_MAX_PASSWORD_LEN: usize = 127;
1646
1647/// Number of bytes used for hash function selection (spec: first 16 bytes as BigInteger mod 3)
1648const HASH_SELECTOR_BYTES: usize = 16;
1649
1650/// Compute R6 password hash using Algorithm 2.B (ISO 32000-2:2020 §7.6.4.3.4)
1651///
1652/// This is the correct R6 key derivation algorithm used by qpdf, Adobe Acrobat,
1653/// and other compliant PDF processors. It uses AES-128-CBC encryption within
1654/// the iteration loop and dynamically selects SHA-256/384/512 based on output.
1655///
1656/// # Algorithm Overview
1657/// 1. Initial hash: K = SHA-256(password + salt + U\[0..48\])
1658/// 2. Loop (minimum 64 rounds):
1659///    a. Construct k1 = (password + K + U\[0..48\]), repeat 64 times
1660///    b. E = AES-128-CBC-encrypt(k1, key=K\[0..16\], iv=K\[16..32\])
1661///    c. Select hash: SHA-256/384/512 based on sum(E\[0..16\]) mod 3
1662///    d. K = hash(E)
1663///    e. Check termination: round >= 64 AND E\[last\] <= (round - 32)
1664/// 3. Return K\[0..32\]
1665///
1666/// # Parameters
1667/// - `password`: User password bytes (UTF-8 encoded)
1668/// - `salt`: 8-byte salt (validation_salt or key_salt from U entry)
1669/// - `u_entry`: Full 48-byte U entry (or empty slice for initial computation)
1670///
1671/// # Returns
1672/// 32-byte derived key
1673///
1674/// # Security Notes
1675/// - Maximum 2048 rounds to prevent DoS attacks
1676/// - Variable iteration count makes brute-force harder
1677/// - AES encryption + hash rotation provides strong KDF
1678///
1679/// # References
1680/// - ISO 32000-2:2020 §7.6.4.3.4 "Algorithm 2.B: Computing a hash (R6)"
1681pub fn compute_hash_r6_algorithm_2b(
1682    password: &[u8],
1683    salt: &[u8],
1684    u_entry: &[u8],
1685) -> Result<Vec<u8>> {
1686    // Security: Validate password length to prevent DoS via massive allocations
1687    if password.len() > ALGORITHM_2B_MAX_PASSWORD_LEN {
1688        return Err(crate::error::PdfError::EncryptionError(format!(
1689            "Password too long ({} bytes, max {})",
1690            password.len(),
1691            ALGORITHM_2B_MAX_PASSWORD_LEN
1692        )));
1693    }
1694
1695    // Step 1: Initial hash K = SHA-256(password + salt + U[0..48])
1696    let mut input = Vec::with_capacity(password.len() + salt.len() + u_entry.len().min(48));
1697    input.extend_from_slice(password);
1698    input.extend_from_slice(salt);
1699    if !u_entry.is_empty() {
1700        input.extend_from_slice(&u_entry[..u_entry.len().min(48)]);
1701    }
1702
1703    let mut k = sha256(&input);
1704
1705    // Step 2: Iteration loop
1706    let mut round: usize = 0;
1707    loop {
1708        // 2a. Construct input sequence: password + K + U[0..48], repeated
1709        // The spec says to create a sequence that will be encrypted
1710        let mut k1_unit = Vec::new();
1711        k1_unit.extend_from_slice(password);
1712        k1_unit.extend_from_slice(&k);
1713        if !u_entry.is_empty() {
1714            k1_unit.extend_from_slice(&u_entry[..u_entry.len().min(48)]);
1715        }
1716
1717        // Repeat 64 times to create input for AES
1718        let mut k1 = Vec::with_capacity(k1_unit.len() * 64);
1719        for _ in 0..64 {
1720            k1.extend_from_slice(&k1_unit);
1721        }
1722
1723        // Zero-pad to AES block size (16 bytes) per ISO 32000-2 §7.6.4.3.4
1724        // NOTE: This is zero-padding, NOT PKCS#7 - the spec requires raw AES without padding removal
1725        while k1.len() % 16 != 0 {
1726            k1.push(0);
1727        }
1728
1729        // 2b. AES-128-CBC encryption
1730        // Key: first 16 bytes of K, IV: next 16 bytes of K
1731        if k.len() < 32 {
1732            // Extend K if needed (shouldn't happen with proper hashes)
1733            while k.len() < 32 {
1734                k.push(0);
1735            }
1736        }
1737
1738        let aes_key = AesKey::new_128(k[..16].to_vec()).map_err(|e| {
1739            crate::error::PdfError::EncryptionError(format!(
1740                "Algorithm 2.B: Failed to create AES key: {}",
1741                e
1742            ))
1743        })?;
1744        let aes = Aes::new(aes_key);
1745        let iv = &k[16..32];
1746
1747        let e = aes.encrypt_cbc_raw(&k1, iv).map_err(|e| {
1748            crate::error::PdfError::EncryptionError(format!(
1749                "Algorithm 2.B: AES encryption failed: {}",
1750                e
1751            ))
1752        })?;
1753
1754        // 2c. Select hash function based on first 16 bytes of E as BigInteger mod 3
1755        // Per iText/Adobe implementation: interpret E[0..HASH_SELECTOR_BYTES] as big-endian integer
1756        // Mathematical equivalence: sum(bytes) mod 3 == BigInteger(bytes) mod 3
1757        // because 256 mod 3 = 1, therefore 256^k mod 3 = 1 for all k
1758        let hash_selector = {
1759            let sum: u64 = e[..HASH_SELECTOR_BYTES.min(e.len())]
1760                .iter()
1761                .map(|&b| b as u64)
1762                .sum();
1763            (sum % 3) as u8
1764        };
1765
1766        k = match hash_selector {
1767            0 => sha256(&e),
1768            1 => sha384(&e),
1769            2 => sha512(&e),
1770            _ => unreachable!("Modulo 3 can only be 0, 1, or 2"),
1771        };
1772
1773        // 2d. Check termination condition
1774        // Terminate when: round >= 64 AND E[last] <= (round - 32)
1775        let last_byte = *e.last().unwrap_or(&0);
1776        round += 1;
1777
1778        if round >= ALGORITHM_2B_MIN_ROUNDS {
1779            // The termination condition from ISO spec:
1780            // "the last byte value of the last iteration is less than or equal to
1781            // the number of iterations minus 32"
1782            if (last_byte as usize) <= round.saturating_sub(32) {
1783                break;
1784            }
1785        }
1786
1787        // Safety: Prevent infinite loop (DoS protection)
1788        if round >= ALGORITHM_2B_MAX_ROUNDS {
1789            break;
1790        }
1791    }
1792
1793    // Step 3: Return first 32 bytes of final K
1794    // K might be > 32 bytes if last hash was SHA-384 or SHA-512
1795    Ok(k[..32.min(k.len())].to_vec())
1796}
1797
1798/// R5 salt length in bytes (PDF spec §7.6.4.3.4)
1799const R5_SALT_LENGTH: usize = 8;
1800
1801/// R5 SHA-256 iteration count (ISO 32000-2:2020 Algorithm 8/11)
1802/// NOTE: R5 does NOT use iterations - hash is simply SHA-256(password + salt)
1803/// The 64 iterations are only for R6 which uses Algorithm 2.B
1804const R5_HASH_ITERATIONS: usize = 0;
1805
1806/// R6 salt length in bytes (PDF spec ISO 32000-2)
1807const R6_SALT_LENGTH: usize = 8;
1808
1809// ============================================================================
1810// R5/R6 U Entry Structure Constants (48 bytes total)
1811// ============================================================================
1812
1813/// Length of the hash portion in U entry (SHA-256/SHA-512 truncated to 32 bytes)
1814const U_HASH_LENGTH: usize = 32;
1815
1816/// Start offset of validation salt in U entry
1817const U_VALIDATION_SALT_START: usize = 32;
1818
1819/// End offset of validation salt in U entry
1820const U_VALIDATION_SALT_END: usize = 40;
1821
1822/// Start offset of key salt in U entry
1823const U_KEY_SALT_START: usize = 40;
1824
1825/// End offset of key salt in U entry
1826const U_KEY_SALT_END: usize = 48;
1827
1828/// Total length of U entry for R5/R6
1829const U_ENTRY_LENGTH: usize = 48;
1830
1831/// Length of UE entry (encrypted encryption key)
1832const UE_ENTRY_LENGTH: usize = 32;
1833
1834// ============================================================================
1835// R6 Perms Entry Structure Constants (16 bytes total)
1836// ============================================================================
1837
1838/// Length of Perms entry
1839const PERMS_ENTRY_LENGTH: usize = 16;
1840
1841/// Start offset of permissions value in decrypted Perms (little-endian u32)
1842const PERMS_P_START: usize = 0;
1843
1844/// End offset of permissions value in decrypted Perms
1845const PERMS_P_END: usize = 4;
1846
1847/// Start offset of fixed marker (0xFFFFFFFF) in decrypted Perms
1848const PERMS_MARKER_START: usize = 4;
1849
1850/// End offset of fixed marker in decrypted Perms
1851const PERMS_MARKER_END: usize = 8;
1852
1853/// Start offset of "adb" literal in decrypted Perms
1854const PERMS_LITERAL_START: usize = 8;
1855
1856/// End offset of "adb" literal in decrypted Perms
1857const PERMS_LITERAL_END: usize = 11;
1858
1859/// Offset of EncryptMetadata flag byte ('T' or 'F') in decrypted Perms
1860const PERMS_ENCRYPT_META_BYTE: usize = 11;
1861
1862/// Fixed marker value in Perms entry
1863const PERMS_MARKER: [u8; 4] = [0xFF, 0xFF, 0xFF, 0xFF];
1864
1865/// Literal verification string in Perms entry
1866const PERMS_LITERAL: &[u8; 3] = b"adb";
1867
1868/// Generate cryptographically secure random salt using OS CSPRNG
1869///
1870/// Uses `rand::rng()` which provides a thread-local CSPRNG (ChaCha12) seeded
1871/// from the OS random number generator. This is suitable for PDF encryption salts.
1872///
1873/// # Security
1874/// - Uses ChaCha12 PRNG seeded from OS entropy (rand 0.9 implementation)
1875/// - Provides cryptographic-quality randomness for salt generation
1876/// - Each call produces independent random bytes
1877fn generate_salt(len: usize) -> Vec<u8> {
1878    let mut salt = vec![0u8; len];
1879    rand::rng().fill_bytes(&mut salt);
1880    salt
1881}
1882
1883#[cfg(test)]
1884mod tests {
1885    use super::*;
1886
1887    #[test]
1888    fn test_pad_password() {
1889        let padded = StandardSecurityHandler::pad_password("test");
1890        assert_eq!(padded.len(), 32);
1891        assert_eq!(&padded[..4], b"test");
1892        assert_eq!(&padded[4..8], &PADDING[..4]);
1893    }
1894
1895    #[test]
1896    fn test_pad_password_long() {
1897        let long_password = "a".repeat(40);
1898        let padded = StandardSecurityHandler::pad_password(&long_password);
1899        assert_eq!(padded.len(), 32);
1900        assert_eq!(&padded[..32], &long_password.as_bytes()[..32]);
1901    }
1902
1903    #[test]
1904    fn test_rc4_40bit_handler() {
1905        let handler = StandardSecurityHandler::rc4_40bit();
1906        assert_eq!(handler.revision, SecurityHandlerRevision::R2);
1907        assert_eq!(handler.key_length, 5);
1908    }
1909
1910    #[test]
1911    fn test_rc4_128bit_handler() {
1912        let handler = StandardSecurityHandler::rc4_128bit();
1913        assert_eq!(handler.revision, SecurityHandlerRevision::R3);
1914        assert_eq!(handler.key_length, 16);
1915    }
1916
1917    #[test]
1918    fn test_owner_hash_computation() {
1919        let handler = StandardSecurityHandler::rc4_40bit();
1920        let owner_pwd = OwnerPassword("owner".to_string());
1921        let user_pwd = UserPassword("user".to_string());
1922
1923        let hash = handler.compute_owner_hash(&owner_pwd, &user_pwd);
1924        assert_eq!(hash.len(), 32);
1925    }
1926
1927    #[test]
1928    fn test_encryption_key_computation() {
1929        let handler = StandardSecurityHandler::rc4_40bit();
1930        let user_pwd = UserPassword("user".to_string());
1931        let owner_hash = vec![0u8; 32];
1932        let permissions = Permissions::new();
1933
1934        let key = handler
1935            .compute_encryption_key(&user_pwd, &owner_hash, permissions, None)
1936            .unwrap();
1937
1938        assert_eq!(key.len(), 5);
1939    }
1940
1941    #[test]
1942    fn test_aes_256_r5_handler() {
1943        let handler = StandardSecurityHandler::aes_256_r5();
1944        assert_eq!(handler.revision, SecurityHandlerRevision::R5);
1945        assert_eq!(handler.key_length, 32);
1946    }
1947
1948    #[test]
1949    fn test_aes_256_r6_handler() {
1950        let handler = StandardSecurityHandler::aes_256_r6();
1951        assert_eq!(handler.revision, SecurityHandlerRevision::R6);
1952        assert_eq!(handler.key_length, 32);
1953    }
1954
1955    #[test]
1956    fn test_aes_encryption_key_computation() {
1957        let handler = StandardSecurityHandler::aes_256_r5();
1958        let user_pwd = UserPassword("testuser".to_string());
1959        let owner_hash = vec![0u8; 32];
1960        let permissions = Permissions::new();
1961
1962        let key = handler
1963            .compute_aes_encryption_key(&user_pwd, &owner_hash, permissions, None)
1964            .unwrap();
1965
1966        assert_eq!(key.len(), 32);
1967    }
1968
1969    #[test]
1970    fn test_aes_encrypt_decrypt() {
1971        let handler = StandardSecurityHandler::aes_256_r5();
1972        let key = EncryptionKey::new(vec![0u8; 32]);
1973        let obj_id = ObjectId::new(1, 0);
1974        let data = b"Hello AES encryption!";
1975
1976        let encrypted = handler.encrypt_aes(data, &key, &obj_id).unwrap();
1977        assert_ne!(encrypted.as_slice(), data);
1978        assert!(encrypted.len() > data.len()); // Should include IV
1979
1980        // Note: This simplified AES implementation is for demonstration only
1981        let _decrypted = handler.decrypt_aes(&encrypted, &key, &obj_id);
1982        // For now, just test that the operations complete without panicking
1983    }
1984
1985    #[test]
1986    fn test_aes_with_rc4_handler_fails() {
1987        let handler = StandardSecurityHandler::rc4_128bit();
1988        let key = EncryptionKey::new(vec![0u8; 16]);
1989        let obj_id = ObjectId::new(1, 0);
1990        let data = b"test data";
1991
1992        // Should fail because handler is not Rev 5+
1993        assert!(handler.encrypt_aes(data, &key, &obj_id).is_err());
1994        assert!(handler.decrypt_aes(data, &key, &obj_id).is_err());
1995    }
1996
1997    #[test]
1998    fn test_aes_decrypt_invalid_data() {
1999        let handler = StandardSecurityHandler::aes_256_r5();
2000        let key = EncryptionKey::new(vec![0u8; 32]);
2001        let obj_id = ObjectId::new(1, 0);
2002
2003        // Data too short (no IV)
2004        let short_data = vec![0u8; 10];
2005        assert!(handler.decrypt_aes(&short_data, &key, &obj_id).is_err());
2006    }
2007
2008    #[test]
2009    fn test_sha256_deterministic() {
2010        let data1 = b"test data";
2011        let data2 = b"test data";
2012        let data3 = b"different data";
2013
2014        let hash1 = sha256(data1);
2015        let hash2 = sha256(data2);
2016        let hash3 = sha256(data3);
2017
2018        assert_eq!(hash1.len(), 32);
2019        assert_eq!(hash2.len(), 32);
2020        assert_eq!(hash3.len(), 32);
2021
2022        assert_eq!(hash1, hash2); // Same input should give same output
2023        assert_ne!(hash1, hash3); // Different input should give different output
2024    }
2025
2026    #[test]
2027    fn test_security_handler_revision_ordering() {
2028        assert!(SecurityHandlerRevision::R2 < SecurityHandlerRevision::R3);
2029        assert!(SecurityHandlerRevision::R3 < SecurityHandlerRevision::R4);
2030        assert!(SecurityHandlerRevision::R4 < SecurityHandlerRevision::R5);
2031        assert!(SecurityHandlerRevision::R5 < SecurityHandlerRevision::R6);
2032    }
2033
2034    #[test]
2035    fn test_aes_password_validation() {
2036        let handler = StandardSecurityHandler::aes_256_r5();
2037        let password = UserPassword("testpassword".to_string());
2038        let user_hash = vec![0u8; 32]; // Simplified hash
2039        let permissions = Permissions::new();
2040
2041        // This is a basic test - in practice, the validation would be more complex
2042        let result = handler.validate_aes_user_password(&password, &user_hash, permissions, None);
2043        assert!(result.is_ok());
2044    }
2045
2046    // ===== Additional Comprehensive Tests =====
2047
2048    #[test]
2049    fn test_user_password_debug() {
2050        let pwd = UserPassword("debug_test".to_string());
2051        let debug_str = format!("{pwd:?}");
2052        assert!(debug_str.contains("UserPassword"));
2053        assert!(debug_str.contains("debug_test"));
2054    }
2055
2056    #[test]
2057    fn test_owner_password_debug() {
2058        let pwd = OwnerPassword("owner_debug".to_string());
2059        let debug_str = format!("{pwd:?}");
2060        assert!(debug_str.contains("OwnerPassword"));
2061        assert!(debug_str.contains("owner_debug"));
2062    }
2063
2064    #[test]
2065    fn test_encryption_key_debug() {
2066        let key = EncryptionKey::new(vec![0x01, 0x02, 0x03]);
2067        let debug_str = format!("{key:?}");
2068        assert!(debug_str.contains("EncryptionKey"));
2069    }
2070
2071    #[test]
2072    fn test_security_handler_revision_equality() {
2073        assert_eq!(SecurityHandlerRevision::R2, SecurityHandlerRevision::R2);
2074        assert_ne!(SecurityHandlerRevision::R2, SecurityHandlerRevision::R3);
2075    }
2076
2077    #[test]
2078    fn test_security_handler_revision_values() {
2079        assert_eq!(SecurityHandlerRevision::R2 as u8, 2);
2080        assert_eq!(SecurityHandlerRevision::R3 as u8, 3);
2081        assert_eq!(SecurityHandlerRevision::R4 as u8, 4);
2082        assert_eq!(SecurityHandlerRevision::R5 as u8, 5);
2083        assert_eq!(SecurityHandlerRevision::R6 as u8, 6);
2084    }
2085
2086    #[test]
2087    fn test_pad_password_various_lengths() {
2088        for len in 0..=40 {
2089            let password = "x".repeat(len);
2090            let padded = StandardSecurityHandler::pad_password(&password);
2091            assert_eq!(padded.len(), 32);
2092
2093            if len <= 32 {
2094                assert_eq!(&padded[..len], password.as_bytes());
2095            } else {
2096                assert_eq!(&padded[..], &password.as_bytes()[..32]);
2097            }
2098        }
2099    }
2100
2101    #[test]
2102    fn test_pad_password_unicode() {
2103        let padded = StandardSecurityHandler::pad_password("café");
2104        assert_eq!(padded.len(), 32);
2105        // UTF-8 encoding of "café" is 5 bytes
2106        assert_eq!(&padded[..5], "café".as_bytes());
2107    }
2108
2109    #[test]
2110    fn test_compute_owner_hash_different_users() {
2111        let handler = StandardSecurityHandler::rc4_128bit();
2112        let owner = OwnerPassword("owner".to_string());
2113        let user1 = UserPassword("user1".to_string());
2114        let user2 = UserPassword("user2".to_string());
2115
2116        let hash1 = handler.compute_owner_hash(&owner, &user1);
2117        let hash2 = handler.compute_owner_hash(&owner, &user2);
2118
2119        assert_ne!(hash1, hash2); // Different user passwords should produce different hashes
2120    }
2121
2122    #[test]
2123    fn test_compute_user_hash_r4() {
2124        let handler = StandardSecurityHandler {
2125            revision: SecurityHandlerRevision::R4,
2126            key_length: 16,
2127        };
2128        let user = UserPassword("r4test".to_string());
2129        let owner_hash = vec![0xAA; 32];
2130        let permissions = Permissions::new();
2131
2132        let hash = handler
2133            .compute_user_hash(&user, &owner_hash, permissions, None)
2134            .unwrap();
2135        assert_eq!(hash.len(), 32);
2136    }
2137
2138    #[test]
2139    fn test_compute_user_hash_r6() {
2140        let handler = StandardSecurityHandler::aes_256_r6();
2141        let user = UserPassword("r6test".to_string());
2142        let owner_hash = vec![0xBB; 32];
2143        let permissions = Permissions::all();
2144
2145        let hash = handler
2146            .compute_user_hash(&user, &owner_hash, permissions, None)
2147            .unwrap();
2148        assert_eq!(hash.len(), 32);
2149    }
2150
2151    #[test]
2152    fn test_encryption_key_with_file_id_affects_result() {
2153        let handler = StandardSecurityHandler::rc4_128bit();
2154        let user = UserPassword("test".to_string());
2155        let owner_hash = vec![0xFF; 32];
2156        let permissions = Permissions::new();
2157        let file_id = b"unique_file_id_12345";
2158
2159        let key_with_id = handler
2160            .compute_encryption_key(&user, &owner_hash, permissions, Some(file_id))
2161            .unwrap();
2162        let key_without_id = handler
2163            .compute_encryption_key(&user, &owner_hash, permissions, None)
2164            .unwrap();
2165
2166        assert_ne!(key_with_id.key, key_without_id.key);
2167    }
2168
2169    #[test]
2170    fn test_encrypt_string_empty() {
2171        let handler = StandardSecurityHandler::rc4_40bit();
2172        let key = EncryptionKey::new(vec![0x01, 0x02, 0x03, 0x04, 0x05]);
2173        let obj_id = ObjectId::new(1, 0);
2174
2175        let encrypted = handler.encrypt_string(b"", &key, &obj_id);
2176        assert_eq!(encrypted.len(), 0);
2177    }
2178
2179    #[test]
2180    fn test_encrypt_decrypt_large_data() {
2181        let handler = StandardSecurityHandler::rc4_128bit();
2182        let key = EncryptionKey::new(vec![0xAA; 16]);
2183        let obj_id = ObjectId::new(42, 0);
2184        let large_data = vec![0x55; 10000]; // 10KB
2185
2186        let encrypted = handler.encrypt_string(&large_data, &key, &obj_id);
2187        assert_eq!(encrypted.len(), large_data.len());
2188        assert_ne!(encrypted, large_data);
2189
2190        let decrypted = handler.decrypt_string(&encrypted, &key, &obj_id);
2191        assert_eq!(decrypted, large_data);
2192    }
2193
2194    #[test]
2195    fn test_stream_encryption_different_from_string() {
2196        // For current implementation they're the same, but test separately
2197        let handler = StandardSecurityHandler::rc4_128bit();
2198        let key = EncryptionKey::new(vec![0x11; 16]);
2199        let obj_id = ObjectId::new(5, 1);
2200        let data = b"Stream content test";
2201
2202        let encrypted_string = handler.encrypt_string(data, &key, &obj_id);
2203        let encrypted_stream = handler.encrypt_stream(data, &key, &obj_id);
2204
2205        assert_eq!(encrypted_string, encrypted_stream); // Currently same implementation
2206    }
2207
2208    #[test]
2209    fn test_aes_encryption_with_different_object_ids() {
2210        let handler = StandardSecurityHandler::aes_256_r5();
2211        let key = EncryptionKey::new(vec![0x77; 32]);
2212        let obj_id1 = ObjectId::new(10, 0);
2213        let obj_id2 = ObjectId::new(11, 0);
2214        let data = b"AES test data";
2215
2216        let encrypted1 = handler.encrypt_aes(data, &key, &obj_id1).unwrap();
2217        let encrypted2 = handler.encrypt_aes(data, &key, &obj_id2).unwrap();
2218
2219        // Different object IDs should produce different ciphertexts
2220        assert_ne!(encrypted1, encrypted2);
2221    }
2222
2223    #[test]
2224    fn test_aes_decrypt_invalid_iv_length() {
2225        let handler = StandardSecurityHandler::aes_256_r5();
2226        let key = EncryptionKey::new(vec![0x88; 32]);
2227        let obj_id = ObjectId::new(1, 0);
2228
2229        // Data too short to contain IV
2230        let short_data = vec![0u8; 10];
2231        assert!(handler.decrypt_aes(&short_data, &key, &obj_id).is_err());
2232
2233        // Exactly 16 bytes (only IV, no encrypted data)
2234        let iv_only = vec![0u8; 16];
2235        let result = handler.decrypt_aes(&iv_only, &key, &obj_id);
2236        // This might succeed with empty decrypted data or fail depending on implementation
2237        if let Ok(decrypted) = result {
2238            assert_eq!(decrypted.len(), 0);
2239        }
2240    }
2241
2242    #[test]
2243    fn test_aes_validate_password_wrong_hash_length() {
2244        let handler = StandardSecurityHandler::aes_256_r5();
2245        let password = UserPassword("test".to_string());
2246        let short_hash = vec![0u8; 16]; // Too short
2247        let permissions = Permissions::new();
2248
2249        let result = handler
2250            .validate_aes_user_password(&password, &short_hash, permissions, None)
2251            .unwrap();
2252        assert!(!result); // Should return false for invalid hash
2253    }
2254
2255    #[test]
2256    fn test_permissions_affect_encryption_key() {
2257        let handler = StandardSecurityHandler::rc4_128bit();
2258        let user = UserPassword("same_user".to_string());
2259        let owner_hash = vec![0xCC; 32];
2260
2261        let perms1 = Permissions::new();
2262        let perms2 = Permissions::all();
2263
2264        let key1 = handler
2265            .compute_encryption_key(&user, &owner_hash, perms1, None)
2266            .unwrap();
2267        let key2 = handler
2268            .compute_encryption_key(&user, &owner_hash, perms2, None)
2269            .unwrap();
2270
2271        assert_ne!(key1.key, key2.key); // Different permissions should affect the key
2272    }
2273
2274    #[test]
2275    fn test_different_handlers_produce_different_keys() {
2276        let user = UserPassword("test".to_string());
2277        let owner_hash = vec![0xDD; 32];
2278        let permissions = Permissions::new();
2279
2280        let handler_r2 = StandardSecurityHandler::rc4_40bit();
2281        let handler_r3 = StandardSecurityHandler::rc4_128bit();
2282
2283        let key_r2 = handler_r2
2284            .compute_encryption_key(&user, &owner_hash, permissions, None)
2285            .unwrap();
2286        let key_r3 = handler_r3
2287            .compute_encryption_key(&user, &owner_hash, permissions, None)
2288            .unwrap();
2289
2290        assert_ne!(key_r2.len(), key_r3.len()); // Different key lengths
2291        assert_eq!(key_r2.len(), 5);
2292        assert_eq!(key_r3.len(), 16);
2293    }
2294
2295    #[test]
2296    fn test_full_workflow_aes_r6() {
2297        let handler = StandardSecurityHandler::aes_256_r6();
2298        let user_pwd = UserPassword("user_r6".to_string());
2299        let permissions = Permissions::new();
2300        let file_id = b"test_file_r6";
2301
2302        // For AES R5/R6, owner hash computation is different - use a dummy hash
2303        let owner_hash = vec![0x42; 32]; // AES uses 32-byte hashes
2304
2305        // Compute user hash
2306        let user_hash = handler
2307            .compute_user_hash(&user_pwd, &owner_hash, permissions, Some(file_id))
2308            .unwrap();
2309        assert_eq!(user_hash.len(), 32);
2310
2311        // Compute encryption key
2312        let key = handler
2313            .compute_aes_encryption_key(&user_pwd, &owner_hash, permissions, Some(file_id))
2314            .unwrap();
2315        assert_eq!(key.len(), 32);
2316
2317        // Test string encryption (uses AES for R6)
2318        let obj_id = ObjectId::new(100, 5);
2319        let content = b"R6 AES encryption test";
2320        let encrypted = handler.encrypt_string(content, &key, &obj_id);
2321
2322        // With AES, encrypted should be empty on error or have data
2323        if !encrypted.is_empty() {
2324            assert_ne!(encrypted.as_slice(), content);
2325        }
2326    }
2327
2328    #[test]
2329    fn test_md5_compute_consistency() {
2330        let data = b"consistent data for md5";
2331        let hash1 = md5::compute(data);
2332        let hash2 = md5::compute(data);
2333
2334        assert_eq!(hash1, hash2);
2335        assert_eq!(hash1.len(), 16);
2336    }
2337
2338    #[test]
2339    fn test_sha256_consistency() {
2340        let data = b"consistent data for sha256";
2341        let hash1 = sha256(data);
2342        let hash2 = sha256(data);
2343
2344        assert_eq!(hash1, hash2);
2345        assert_eq!(hash1.len(), 32);
2346    }
2347
2348    #[test]
2349    fn test_rc4_encrypt_helper() {
2350        let key = Rc4Key::from_slice(&[0x01, 0x02, 0x03, 0x04, 0x05]);
2351        let data = b"test rc4 helper";
2352
2353        let encrypted = rc4_encrypt(&key, data);
2354        assert_ne!(encrypted.as_slice(), data);
2355
2356        // RC4 is symmetric
2357        let decrypted = rc4_encrypt(&key, &encrypted);
2358        assert_eq!(decrypted.as_slice(), data);
2359    }
2360
2361    #[test]
2362    fn test_edge_case_max_object_generation() {
2363        let handler = StandardSecurityHandler::rc4_128bit();
2364        let key = EncryptionKey::new(vec![0xEE; 16]);
2365        let obj_id = ObjectId::new(0xFFFFFF, 0xFFFF); // Max values
2366        let data = b"edge case";
2367
2368        let encrypted = handler.encrypt_string(data, &key, &obj_id);
2369        let decrypted = handler.decrypt_string(&encrypted, &key, &obj_id);
2370        assert_eq!(decrypted.as_slice(), data);
2371    }
2372
2373    // Issue #364: a failed AES decryption must surface as an error, not be
2374    // swallowed into empty content (silent data loss). The `try_*` variants
2375    // propagate the error; the legacy `Vec`-returning ones keep the lenient
2376    // behaviour for backward compatibility.
2377    #[test]
2378    fn test_try_decrypt_stream_surfaces_aes_error() {
2379        let handler = StandardSecurityHandler::aes_128_r4();
2380        let key = EncryptionKey::new(vec![0x11; 16]);
2381        let obj_id = ObjectId::new(1, 0);
2382
2383        // Too short to even contain the 16-byte AES IV → must error.
2384        let undecryptable = [0u8; 8];
2385
2386        let err = handler.try_decrypt_stream(&undecryptable, &key, &obj_id);
2387        assert!(
2388            err.is_err(),
2389            "try_decrypt_stream must return Err on undecryptable AES data, got {err:?}"
2390        );
2391
2392        // The legacy lenient API still swallows into empty Vec (documents the
2393        // behaviour the parser path no longer relies on).
2394        let lenient = handler.decrypt_stream(&undecryptable, &key, &obj_id);
2395        assert!(lenient.is_empty());
2396    }
2397
2398    #[test]
2399    fn test_try_decrypt_string_surfaces_aes_error() {
2400        let handler = StandardSecurityHandler::aes_128_r4();
2401        let key = EncryptionKey::new(vec![0x22; 16]);
2402        let obj_id = ObjectId::new(2, 0);
2403
2404        let undecryptable = [0u8; 4];
2405        assert!(
2406            handler
2407                .try_decrypt_string(&undecryptable, &key, &obj_id)
2408                .is_err(),
2409            "try_decrypt_string must return Err on undecryptable AES data"
2410        );
2411    }
2412
2413    // ===== SHA-256/512 NIST Vector Tests (Phase 1.3 - RustCrypto Integration) =====
2414
2415    #[test]
2416    fn test_sha256_nist_empty_string() {
2417        // NIST FIPS 180-4 test vector: SHA-256("")
2418        let hash = sha256(b"");
2419        let expected: [u8; 32] = [
2420            0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f,
2421            0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b,
2422            0x78, 0x52, 0xb8, 0x55,
2423        ];
2424        assert_eq!(
2425            hash.as_slice(),
2426            expected.as_slice(),
2427            "SHA-256('') must match NIST test vector"
2428        );
2429    }
2430
2431    #[test]
2432    fn test_sha256_nist_abc() {
2433        // NIST FIPS 180-4 test vector: SHA-256("abc")
2434        let hash = sha256(b"abc");
2435        let expected: [u8; 32] = [
2436            0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae,
2437            0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61,
2438            0xf2, 0x00, 0x15, 0xad,
2439        ];
2440        assert_eq!(
2441            hash.as_slice(),
2442            expected.as_slice(),
2443            "SHA-256('abc') must match NIST test vector"
2444        );
2445    }
2446
2447    #[test]
2448    fn test_sha512_nist_abc() {
2449        // NIST FIPS 180-4 test vector: SHA-512("abc")
2450        let hash = sha512(b"abc");
2451        let expected: [u8; 64] = [
2452            0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20,
2453            0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, 0xe6,
2454            0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba,
2455            0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
2456            0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f,
2457        ];
2458        assert_eq!(
2459            hash.as_slice(),
2460            expected.as_slice(),
2461            "SHA-512('abc') must match NIST test vector"
2462        );
2463    }
2464
2465    #[test]
2466    fn test_sha512_length() {
2467        let hash = sha512(b"test data");
2468        assert_eq!(hash.len(), 64, "SHA-512 must produce 64 bytes");
2469    }
2470
2471    #[test]
2472    fn test_sha512_deterministic() {
2473        let data1 = b"sha512 test data";
2474        let data2 = b"sha512 test data";
2475        let data3 = b"different data";
2476
2477        let hash1 = sha512(data1);
2478        let hash2 = sha512(data2);
2479        let hash3 = sha512(data3);
2480
2481        assert_eq!(hash1, hash2, "Same input must produce same SHA-512 hash");
2482        assert_ne!(hash1, hash3, "Different input must produce different hash");
2483    }
2484
2485    // ===== Phase 2.1: R5 User Password Tests (Algorithm 8 & 11) =====
2486
2487    #[test]
2488    fn test_r5_user_hash_computation() {
2489        let handler = StandardSecurityHandler::aes_256_r5();
2490        let password = UserPassword("test_password".to_string());
2491
2492        let u_entry = handler.compute_r5_user_hash(&password).unwrap();
2493
2494        // U entry must be exactly 48 bytes: hash(32) + validation_salt(8) + key_salt(8)
2495        assert_eq!(u_entry.len(), 48, "R5 U entry must be 48 bytes");
2496    }
2497
2498    #[test]
2499    fn test_r5_user_password_validation_correct() {
2500        let handler = StandardSecurityHandler::aes_256_r5();
2501        let password = UserPassword("correct_password".to_string());
2502
2503        // Compute U entry with the password
2504        let u_entry = handler.compute_r5_user_hash(&password).unwrap();
2505
2506        // Validate with same password should succeed
2507        let is_valid = handler
2508            .validate_r5_user_password(&password, &u_entry)
2509            .unwrap();
2510        assert!(is_valid, "Correct password must validate");
2511    }
2512
2513    #[test]
2514    fn test_r5_user_password_validation_incorrect() {
2515        let handler = StandardSecurityHandler::aes_256_r5();
2516        let correct_password = UserPassword("correct_password".to_string());
2517        let wrong_password = UserPassword("wrong_password".to_string());
2518
2519        // Compute U entry with correct password
2520        let u_entry = handler.compute_r5_user_hash(&correct_password).unwrap();
2521
2522        // Validate with wrong password should fail
2523        let is_valid = handler
2524            .validate_r5_user_password(&wrong_password, &u_entry)
2525            .unwrap();
2526        assert!(!is_valid, "Wrong password must not validate");
2527    }
2528
2529    #[test]
2530    fn test_r5_user_hash_random_salts() {
2531        let handler = StandardSecurityHandler::aes_256_r5();
2532        let password = UserPassword("same_password".to_string());
2533
2534        // Compute U entry twice - salts should be different
2535        let u_entry1 = handler.compute_r5_user_hash(&password).unwrap();
2536        let u_entry2 = handler.compute_r5_user_hash(&password).unwrap();
2537
2538        // Hash portion should be different (due to random salts)
2539        assert_ne!(
2540            &u_entry1[..32],
2541            &u_entry2[..32],
2542            "Different random salts should produce different hashes"
2543        );
2544
2545        // Validation salt should be different
2546        assert_ne!(
2547            &u_entry1[32..40],
2548            &u_entry2[32..40],
2549            "Validation salts must be random"
2550        );
2551
2552        // But both should validate with the same password
2553        assert!(handler
2554            .validate_r5_user_password(&password, &u_entry1)
2555            .unwrap());
2556        assert!(handler
2557            .validate_r5_user_password(&password, &u_entry2)
2558            .unwrap());
2559    }
2560
2561    #[test]
2562    fn test_r5_user_hash_invalid_entry_length() {
2563        let handler = StandardSecurityHandler::aes_256_r5();
2564        let password = UserPassword("test".to_string());
2565
2566        // Try to validate with wrong length U entry
2567        let short_entry = vec![0u8; 32]; // Too short
2568        let result = handler.validate_r5_user_password(&password, &short_entry);
2569        assert!(result.is_err(), "Short U entry must fail");
2570
2571        let long_entry = vec![0u8; 64]; // Too long
2572        let result = handler.validate_r5_user_password(&password, &long_entry);
2573        assert!(result.is_err(), "Long U entry must fail");
2574    }
2575
2576    #[test]
2577    fn test_r5_empty_password() {
2578        let handler = StandardSecurityHandler::aes_256_r5();
2579        let empty_password = UserPassword("".to_string());
2580
2581        // Empty password should work (common for user-only encryption)
2582        let u_entry = handler.compute_r5_user_hash(&empty_password).unwrap();
2583        assert_eq!(u_entry.len(), 48);
2584
2585        let is_valid = handler
2586            .validate_r5_user_password(&empty_password, &u_entry)
2587            .unwrap();
2588        assert!(is_valid, "Empty password must validate correctly");
2589
2590        // Non-empty password should fail
2591        let non_empty = UserPassword("not_empty".to_string());
2592        let is_valid = handler
2593            .validate_r5_user_password(&non_empty, &u_entry)
2594            .unwrap();
2595        assert!(!is_valid, "Non-empty password must not validate");
2596    }
2597
2598    // ===== Phase 2.2: R5 UE Entry Tests (Encryption Key Storage) =====
2599
2600    #[test]
2601    fn test_r5_ue_entry_computation() {
2602        let handler = StandardSecurityHandler::aes_256_r5();
2603        let password = UserPassword("ue_test_password".to_string());
2604        let encryption_key = EncryptionKey::new(vec![0xAB; 32]);
2605
2606        // Compute U entry first
2607        let u_entry = handler.compute_r5_user_hash(&password).unwrap();
2608
2609        // Compute UE entry
2610        let ue_entry = handler
2611            .compute_r5_ue_entry(&password, &u_entry, &encryption_key)
2612            .unwrap();
2613
2614        // UE entry must be exactly 32 bytes
2615        assert_eq!(ue_entry.len(), 32, "R5 UE entry must be 32 bytes");
2616
2617        // UE should be different from the original key (it's encrypted)
2618        assert_ne!(
2619            ue_entry.as_slice(),
2620            encryption_key.as_bytes(),
2621            "UE must be encrypted"
2622        );
2623    }
2624
2625    #[test]
2626    fn test_r5_encryption_key_recovery() {
2627        let handler = StandardSecurityHandler::aes_256_r5();
2628        let password = UserPassword("recovery_test".to_string());
2629        let original_key = EncryptionKey::new(vec![0x42; 32]);
2630
2631        // Compute U entry
2632        let u_entry = handler.compute_r5_user_hash(&password).unwrap();
2633
2634        // Compute UE entry
2635        let ue_entry = handler
2636            .compute_r5_ue_entry(&password, &u_entry, &original_key)
2637            .unwrap();
2638
2639        // Recover the key
2640        let recovered_key = handler
2641            .recover_r5_encryption_key(&password, &u_entry, &ue_entry)
2642            .unwrap();
2643
2644        // Recovered key must match original
2645        assert_eq!(
2646            recovered_key.as_bytes(),
2647            original_key.as_bytes(),
2648            "Recovered key must match original"
2649        );
2650    }
2651
2652    #[test]
2653    fn test_r5_ue_wrong_password_fails() {
2654        let handler = StandardSecurityHandler::aes_256_r5();
2655        let correct_password = UserPassword("correct".to_string());
2656        let wrong_password = UserPassword("wrong".to_string());
2657        let original_key = EncryptionKey::new(vec![0x99; 32]);
2658
2659        // Compute U and UE with correct password
2660        let u_entry = handler.compute_r5_user_hash(&correct_password).unwrap();
2661        let ue_entry = handler
2662            .compute_r5_ue_entry(&correct_password, &u_entry, &original_key)
2663            .unwrap();
2664
2665        // Try to recover with wrong password
2666        let recovered_key = handler
2667            .recover_r5_encryption_key(&wrong_password, &u_entry, &ue_entry)
2668            .unwrap();
2669
2670        // Key should be different (wrong decryption)
2671        assert_ne!(
2672            recovered_key.as_bytes(),
2673            original_key.as_bytes(),
2674            "Wrong password must produce wrong key"
2675        );
2676    }
2677
2678    #[test]
2679    fn test_r5_ue_invalid_length() {
2680        let handler = StandardSecurityHandler::aes_256_r5();
2681        let password = UserPassword("test".to_string());
2682        let u_entry = vec![0u8; 48]; // Valid U entry length
2683
2684        // Try to recover with wrong length UE entry
2685        let short_ue = vec![0u8; 16]; // Too short
2686        let result = handler.recover_r5_encryption_key(&password, &u_entry, &short_ue);
2687        assert!(result.is_err(), "Short UE entry must fail");
2688
2689        let long_ue = vec![0u8; 64]; // Too long
2690        let result = handler.recover_r5_encryption_key(&password, &u_entry, &long_ue);
2691        assert!(result.is_err(), "Long UE entry must fail");
2692    }
2693
2694    #[test]
2695    fn test_r5_ue_invalid_u_length() {
2696        let handler = StandardSecurityHandler::aes_256_r5();
2697        let password = UserPassword("test".to_string());
2698        let encryption_key = EncryptionKey::new(vec![0x11; 32]);
2699
2700        // Try to compute UE with wrong length U entry
2701        let short_u = vec![0u8; 32]; // Too short
2702        let result = handler.compute_r5_ue_entry(&password, &short_u, &encryption_key);
2703        assert!(
2704            result.is_err(),
2705            "Short U entry must fail for UE computation"
2706        );
2707    }
2708
2709    #[test]
2710    fn test_r5_full_workflow_u_ue() {
2711        let handler = StandardSecurityHandler::aes_256_r5();
2712        let password = UserPassword("full_workflow_test".to_string());
2713        let encryption_key = EncryptionKey::new((0..32).collect::<Vec<u8>>());
2714
2715        // Step 1: Compute U entry (password verification data)
2716        let u_entry = handler.compute_r5_user_hash(&password).unwrap();
2717        assert_eq!(u_entry.len(), 48);
2718
2719        // Step 2: Verify password validates
2720        assert!(handler
2721            .validate_r5_user_password(&password, &u_entry)
2722            .unwrap());
2723
2724        // Step 3: Compute UE entry (encrypted key storage)
2725        let ue_entry = handler
2726            .compute_r5_ue_entry(&password, &u_entry, &encryption_key)
2727            .unwrap();
2728        assert_eq!(ue_entry.len(), 32);
2729
2730        // Step 4: Recover key from UE
2731        let recovered = handler
2732            .recover_r5_encryption_key(&password, &u_entry, &ue_entry)
2733            .unwrap();
2734
2735        // Step 5: Verify recovered key matches original
2736        assert_eq!(
2737            recovered.as_bytes(),
2738            encryption_key.as_bytes(),
2739            "Full R5 workflow: recovered key must match original"
2740        );
2741    }
2742
2743    // ===== Phase 3.1: R6 User Password Tests (SHA-512 based) =====
2744
2745    #[test]
2746    fn test_r6_user_hash_computation() {
2747        let handler = StandardSecurityHandler::aes_256_r6();
2748        let password = UserPassword("r6_test_password".to_string());
2749
2750        let u_entry = handler.compute_r6_user_hash(&password).unwrap();
2751
2752        // U entry must be exactly 48 bytes: hash(32) + validation_salt(8) + key_salt(8)
2753        assert_eq!(u_entry.len(), 48, "R6 U entry must be 48 bytes");
2754    }
2755
2756    #[test]
2757    fn test_r6_user_password_validation_correct() {
2758        let handler = StandardSecurityHandler::aes_256_r6();
2759        let password = UserPassword("r6_correct_password".to_string());
2760
2761        // Compute U entry with the password
2762        let u_entry = handler.compute_r6_user_hash(&password).unwrap();
2763
2764        // Validate with same password should succeed
2765        let is_valid = handler
2766            .validate_r6_user_password(&password, &u_entry)
2767            .unwrap();
2768        assert!(is_valid, "Correct R6 password must validate");
2769    }
2770
2771    #[test]
2772    fn test_r6_user_password_validation_incorrect() {
2773        let handler = StandardSecurityHandler::aes_256_r6();
2774        let correct_password = UserPassword("r6_correct".to_string());
2775        let wrong_password = UserPassword("r6_wrong".to_string());
2776
2777        // Compute U entry with correct password
2778        let u_entry = handler.compute_r6_user_hash(&correct_password).unwrap();
2779
2780        // Validate with wrong password should fail
2781        let is_valid = handler
2782            .validate_r6_user_password(&wrong_password, &u_entry)
2783            .unwrap();
2784        assert!(!is_valid, "Wrong R6 password must not validate");
2785    }
2786
2787    #[test]
2788    fn test_r6_uses_sha512_not_sha256() {
2789        // Verify R6 produces different hash than R5 for same password
2790        let handler_r5 = StandardSecurityHandler::aes_256_r5();
2791        let handler_r6 = StandardSecurityHandler::aes_256_r6();
2792        let password = UserPassword("same_password_both_revisions".to_string());
2793
2794        let u_r5 = handler_r5.compute_r5_user_hash(&password).unwrap();
2795        let u_r6 = handler_r6.compute_r6_user_hash(&password).unwrap();
2796
2797        // Hash portions (first 32 bytes) should be different
2798        // Note: Salts are random, but even with same salt the hash algorithm differs
2799        assert_ne!(
2800            &u_r5[..32],
2801            &u_r6[..32],
2802            "R5 (SHA-256) and R6 (SHA-512) must produce different hashes"
2803        );
2804    }
2805
2806    #[test]
2807    fn test_r6_unicode_password() {
2808        let handler = StandardSecurityHandler::aes_256_r6();
2809        let unicode_password = UserPassword("café🔒日本語".to_string());
2810
2811        let u_entry = handler.compute_r6_user_hash(&unicode_password).unwrap();
2812        assert_eq!(u_entry.len(), 48);
2813
2814        // Validate with same Unicode password
2815        let is_valid = handler
2816            .validate_r6_user_password(&unicode_password, &u_entry)
2817            .unwrap();
2818        assert!(is_valid, "Unicode password must validate");
2819
2820        // Different Unicode password should fail
2821        let different_unicode = UserPassword("café🔓日本語".to_string()); // Different emoji
2822        let is_valid = handler
2823            .validate_r6_user_password(&different_unicode, &u_entry)
2824            .unwrap();
2825        assert!(!is_valid, "Different Unicode password must not validate");
2826    }
2827
2828    // ===== Phase 3.1: R6 UE Entry Tests =====
2829
2830    #[test]
2831    fn test_r6_ue_entry_computation() {
2832        let handler = StandardSecurityHandler::aes_256_r6();
2833        let password = UserPassword("r6_ue_test".to_string());
2834        let encryption_key = EncryptionKey::new(vec![0xCD; 32]);
2835
2836        let u_entry = handler.compute_r6_user_hash(&password).unwrap();
2837        let ue_entry = handler
2838            .compute_r6_ue_entry(&password, &u_entry, &encryption_key)
2839            .unwrap();
2840
2841        assert_eq!(ue_entry.len(), 32, "R6 UE entry must be 32 bytes");
2842    }
2843
2844    #[test]
2845    fn test_r6_encryption_key_recovery() {
2846        let handler = StandardSecurityHandler::aes_256_r6();
2847        let password = UserPassword("r6_recovery_test".to_string());
2848        let original_key = EncryptionKey::new(vec![0xEF; 32]);
2849
2850        let u_entry = handler.compute_r6_user_hash(&password).unwrap();
2851        let ue_entry = handler
2852            .compute_r6_ue_entry(&password, &u_entry, &original_key)
2853            .unwrap();
2854
2855        let recovered_key = handler
2856            .recover_r6_encryption_key(&password, &u_entry, &ue_entry)
2857            .unwrap();
2858
2859        assert_eq!(
2860            recovered_key.as_bytes(),
2861            original_key.as_bytes(),
2862            "R6: Recovered key must match original"
2863        );
2864    }
2865
2866    // ===== Phase 3.2: R6 Perms Entry Tests =====
2867
2868    #[test]
2869    fn test_r6_perms_entry_computation() {
2870        let handler = StandardSecurityHandler::aes_256_r6();
2871        let permissions = Permissions::all();
2872        let key = EncryptionKey::new(vec![0x42; 32]);
2873
2874        let perms = handler
2875            .compute_r6_perms_entry(permissions, &key, true)
2876            .unwrap();
2877
2878        assert_eq!(perms.len(), 16, "Perms entry must be 16 bytes");
2879    }
2880
2881    #[test]
2882    fn test_r6_perms_validation() {
2883        let handler = StandardSecurityHandler::aes_256_r6();
2884        let permissions = Permissions::new();
2885        let key = EncryptionKey::new(vec![0x55; 32]);
2886
2887        let perms = handler
2888            .compute_r6_perms_entry(permissions, &key, false)
2889            .unwrap();
2890
2891        let is_valid = handler
2892            .validate_r6_perms(&perms, &key, permissions)
2893            .unwrap();
2894        assert!(is_valid, "Perms validation must succeed with correct key");
2895    }
2896
2897    #[test]
2898    fn test_r6_perms_wrong_key_fails() {
2899        let handler = StandardSecurityHandler::aes_256_r6();
2900        let permissions = Permissions::all();
2901        let correct_key = EncryptionKey::new(vec![0xAA; 32]);
2902        let wrong_key = EncryptionKey::new(vec![0xBB; 32]);
2903
2904        let perms = handler
2905            .compute_r6_perms_entry(permissions, &correct_key, true)
2906            .unwrap();
2907
2908        // Validation with wrong key should fail (structure won't match)
2909        let result = handler.validate_r6_perms(&perms, &wrong_key, permissions);
2910        assert!(result.is_ok()); // No error
2911        assert!(!result.unwrap()); // But validation fails
2912    }
2913
2914    #[test]
2915    fn test_r6_perms_encrypt_metadata_flag() {
2916        let handler = StandardSecurityHandler::aes_256_r6();
2917        let permissions = Permissions::new();
2918        let key = EncryptionKey::new(vec![0x33; 32]);
2919
2920        let perms_true = handler
2921            .compute_r6_perms_entry(permissions, &key, true)
2922            .unwrap();
2923        let perms_false = handler
2924            .compute_r6_perms_entry(permissions, &key, false)
2925            .unwrap();
2926
2927        // Different encrypt_metadata flag should produce different Perms
2928        assert_ne!(
2929            perms_true, perms_false,
2930            "Different EncryptMetadata must produce different Perms"
2931        );
2932
2933        // Extract and verify flags
2934        let flag_true = handler
2935            .extract_r6_encrypt_metadata(&perms_true, &key)
2936            .unwrap();
2937        assert_eq!(flag_true, Some(true));
2938
2939        let flag_false = handler
2940            .extract_r6_encrypt_metadata(&perms_false, &key)
2941            .unwrap();
2942        assert_eq!(flag_false, Some(false));
2943    }
2944
2945    #[test]
2946    fn test_r6_perms_invalid_length() {
2947        let handler = StandardSecurityHandler::aes_256_r6();
2948        let key = EncryptionKey::new(vec![0x44; 32]);
2949        let permissions = Permissions::new();
2950
2951        let invalid_perms = vec![0u8; 12]; // Too short
2952        let result = handler.validate_r6_perms(&invalid_perms, &key, permissions);
2953        assert!(result.is_err(), "Short Perms entry must fail");
2954    }
2955
2956    #[test]
2957    fn test_r6_full_workflow_with_perms() {
2958        // Complete R6 integration test: U + UE + Perms
2959        let handler = StandardSecurityHandler::aes_256_r6();
2960        let password = UserPassword("r6_full_workflow".to_string());
2961        let permissions = Permissions::all();
2962        let encryption_key = EncryptionKey::new((0..32).map(|i| (i * 3) as u8).collect());
2963
2964        // Step 1: Compute U entry (password verification)
2965        let u_entry = handler.compute_r6_user_hash(&password).unwrap();
2966        assert_eq!(u_entry.len(), 48);
2967
2968        // Step 2: Validate password
2969        assert!(handler
2970            .validate_r6_user_password(&password, &u_entry)
2971            .unwrap());
2972
2973        // Step 3: Compute UE entry (encrypted key)
2974        let ue_entry = handler
2975            .compute_r6_ue_entry(&password, &u_entry, &encryption_key)
2976            .unwrap();
2977        assert_eq!(ue_entry.len(), 32);
2978
2979        // Step 4: Compute Perms entry (encrypted permissions)
2980        let perms = handler
2981            .compute_r6_perms_entry(permissions, &encryption_key, true)
2982            .unwrap();
2983        assert_eq!(perms.len(), 16);
2984
2985        // Step 5: Recover encryption key from UE
2986        let recovered_key = handler
2987            .recover_r6_encryption_key(&password, &u_entry, &ue_entry)
2988            .unwrap();
2989        assert_eq!(
2990            recovered_key.as_bytes(),
2991            encryption_key.as_bytes(),
2992            "Recovered key must match original"
2993        );
2994
2995        // Step 6: Validate Perms with recovered key
2996        let perms_valid = handler
2997            .validate_r6_perms(&perms, &recovered_key, permissions)
2998            .unwrap();
2999        assert!(perms_valid, "Perms must validate with recovered key");
3000
3001        // Step 7: Extract EncryptMetadata flag
3002        let encrypt_meta = handler
3003            .extract_r6_encrypt_metadata(&perms, &recovered_key)
3004            .unwrap();
3005        assert_eq!(encrypt_meta, Some(true), "EncryptMetadata must be true");
3006    }
3007
3008    // ===== AES-128 R4 Tests =====
3009
3010    #[test]
3011    fn test_r4_aes_object_key_is_16_bytes() {
3012        let handler = StandardSecurityHandler::aes_128_r4();
3013        let key = EncryptionKey::new(vec![0xAB; 16]);
3014        let obj_id = ObjectId::new(7, 0);
3015
3016        let obj_key = handler.compute_r4_aes_object_key(&key, &obj_id);
3017        assert_eq!(obj_key.len(), 16);
3018    }
3019
3020    #[test]
3021    fn test_r4_aes_object_key_includes_salt() {
3022        // R4 AES key differs from RC4 key because of "sAlT" suffix
3023        let handler_r4 = StandardSecurityHandler::aes_128_r4();
3024        let handler_rc4 = StandardSecurityHandler::rc4_128bit();
3025        let key = EncryptionKey::new(vec![0xCD; 16]);
3026        let obj_id = ObjectId::new(3, 0);
3027
3028        let aes_key = handler_r4.compute_r4_aes_object_key(&key, &obj_id);
3029        let rc4_key = handler_rc4.compute_object_key(&key, &obj_id);
3030
3031        assert_ne!(
3032            aes_key, rc4_key,
3033            "AES R4 key must differ from RC4 key due to sAlT"
3034        );
3035    }
3036
3037    #[test]
3038    fn test_r4_aes_object_key_deterministic() {
3039        let handler = StandardSecurityHandler::aes_128_r4();
3040        let key = EncryptionKey::new(vec![0x42; 16]);
3041        let obj_id = ObjectId::new(5, 2);
3042
3043        let key1 = handler.compute_r4_aes_object_key(&key, &obj_id);
3044        let key2 = handler.compute_r4_aes_object_key(&key, &obj_id);
3045        assert_eq!(key1, key2);
3046    }
3047
3048    #[test]
3049    fn test_r4_encrypt_decrypt_roundtrip() {
3050        let handler = StandardSecurityHandler::aes_128_r4();
3051        let key = EncryptionKey::new(vec![0x55; 16]);
3052        let obj_id = ObjectId::new(1, 0);
3053        let plaintext = b"Hello AES-128 R4 encryption!";
3054
3055        let encrypted = handler.encrypt_aes(plaintext, &key, &obj_id).unwrap();
3056        assert_ne!(&encrypted[16..], plaintext.as_slice()); // ciphertext != plaintext
3057        assert!(encrypted.len() > 16); // IV + ciphertext
3058
3059        let decrypted = handler.decrypt_aes(&encrypted, &key, &obj_id).unwrap();
3060        assert_eq!(decrypted, plaintext);
3061    }
3062
3063    #[test]
3064    fn test_r4_encrypt_output_has_iv_prefix() {
3065        let handler = StandardSecurityHandler::aes_128_r4();
3066        let key = EncryptionKey::new(vec![0x77; 16]);
3067        let obj_id = ObjectId::new(2, 0);
3068        let data = b"test";
3069
3070        let encrypted = handler.encrypt_aes(data, &key, &obj_id).unwrap();
3071        // Output = 16-byte IV + AES-CBC ciphertext (multiple of 16)
3072        assert!(encrypted.len() >= 32); // 16 IV + at least 16 ciphertext
3073        assert_eq!((encrypted.len() - 16) % 16, 0);
3074    }
3075
3076    #[test]
3077    fn test_r4_decrypt_rejects_short_data() {
3078        let handler = StandardSecurityHandler::aes_128_r4();
3079        let key = EncryptionKey::new(vec![0x99; 16]);
3080        let obj_id = ObjectId::new(1, 0);
3081
3082        let short = vec![0u8; 10];
3083        assert!(handler.decrypt_aes(&short, &key, &obj_id).is_err());
3084    }
3085
3086    #[test]
3087    fn test_r4_inherent_encrypt_string_uses_aes() {
3088        // Verify that the inherent encrypt_string routes R4 through AES, not RC4
3089        let handler = StandardSecurityHandler::aes_128_r4();
3090        let key = EncryptionKey::new(vec![0x33; 16]);
3091        let obj_id = ObjectId::new(1, 0);
3092        let data = b"R4 string encryption";
3093
3094        let encrypted = handler.encrypt_string(data, &key, &obj_id);
3095        // AES output = IV(16) + ciphertext(≥16), so always > input for small inputs
3096        assert!(encrypted.len() >= 32);
3097
3098        // Decrypt via inherent method must also work
3099        let decrypted = handler.decrypt_string(&encrypted, &key, &obj_id);
3100        assert_eq!(decrypted, data);
3101    }
3102
3103    #[test]
3104    fn test_r4_inherent_stream_uses_aes() {
3105        let handler = StandardSecurityHandler::aes_128_r4();
3106        let key = EncryptionKey::new(vec![0x44; 16]);
3107        let obj_id = ObjectId::new(3, 0);
3108        let data = b"R4 stream content";
3109
3110        let encrypted = handler.encrypt_stream(data, &key, &obj_id);
3111        assert!(encrypted.len() >= 32);
3112
3113        let decrypted = handler.decrypt_stream(&encrypted, &key, &obj_id);
3114        assert_eq!(decrypted, data);
3115    }
3116}