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