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    // R5/R6 Perms Entry (ISO 32000-2 Table 25)
1034    // ========================================================================
1035
1036    /// Compute R5/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    /// - Byte 8: 'T' or 'F' (EncryptMetadata flag)
1045    /// - Bytes 9-11: "adb" (literal verification string)
1046    /// - Bytes 12-15: random padding
1047    pub fn compute_perms_entry(
1048        &self,
1049        permissions: Permissions,
1050        encryption_key: &EncryptionKey,
1051        encrypt_metadata: bool,
1052    ) -> Result<Vec<u8>> {
1053        if !matches!(
1054            self.revision,
1055            SecurityHandlerRevision::R5 | SecurityHandlerRevision::R6
1056        ) {
1057            return Err(crate::error::PdfError::EncryptionError(
1058                "Perms entry only for Revision 5 or 6".to_string(),
1059            ));
1060        }
1061        if encryption_key.len() != UE_ENTRY_LENGTH {
1062            return Err(crate::error::PdfError::EncryptionError(format!(
1063                "Encryption key must be {} bytes for R5/R6 Perms",
1064                UE_ENTRY_LENGTH
1065            )));
1066        }
1067
1068        // Construct plaintext: P + 0xFFFFFFFF + T/F + "adb" + random padding
1069        let mut plaintext = vec![0u8; PERMS_ENTRY_LENGTH];
1070
1071        // Permissions (4 bytes, little-endian)
1072        let p_bytes = (permissions.bits() as u32).to_le_bytes();
1073        plaintext[PERMS_P_START..PERMS_P_END].copy_from_slice(&p_bytes);
1074
1075        // Fixed marker bytes (0xFFFFFFFF)
1076        plaintext[PERMS_MARKER_START..PERMS_MARKER_END].copy_from_slice(&PERMS_MARKER);
1077
1078        // EncryptMetadata flag
1079        plaintext[PERMS_ENCRYPT_META_BYTE] = if encrypt_metadata { b'T' } else { b'F' };
1080
1081        // Literal "adb" verification string
1082        plaintext[PERMS_LITERAL_START..PERMS_LITERAL_END].copy_from_slice(PERMS_LITERAL);
1083
1084        // The final four bytes are intentionally unpredictable.
1085        use rand::Rng;
1086        rand::rng().fill_bytes(&mut plaintext[PERMS_RANDOM_START..]);
1087
1088        // Encrypt with AES-256-ECB
1089        let aes_key = AesKey::new_256(encryption_key.key.clone())?;
1090        let aes = Aes::new(aes_key);
1091
1092        let encrypted = aes.encrypt_ecb(&plaintext).map_err(|e| {
1093            crate::error::PdfError::EncryptionError(format!("Perms encryption failed: {}", e))
1094        })?;
1095
1096        Ok(encrypted)
1097    }
1098
1099    /// Compatibility alias for [`Self::compute_perms_entry`].
1100    #[deprecated(
1101        since = "4.4.1",
1102        note = "use compute_perms_entry; the algorithm applies to both R5 and R6"
1103    )]
1104    pub fn compute_r6_perms_entry(
1105        &self,
1106        permissions: Permissions,
1107        encryption_key: &EncryptionKey,
1108        encrypt_metadata: bool,
1109    ) -> Result<Vec<u8>> {
1110        self.compute_perms_entry(permissions, encryption_key, encrypt_metadata)
1111    }
1112
1113    /// Validate R6 Perms entry by decrypting and checking structure
1114    ///
1115    /// Returns Ok(true) if the Perms entry is valid and matches expected permissions.
1116    /// Returns Ok(false) if decryption succeeds but structure/permissions don't match.
1117    /// Returns Err if decryption fails.
1118    ///
1119    /// # Security
1120    /// Uses constant-time comparison (`subtle::ConstantTimeEq`) for permissions
1121    /// comparison to prevent timing side-channel attacks.
1122    pub fn validate_r6_perms(
1123        &self,
1124        perms_entry: &[u8],
1125        encryption_key: &EncryptionKey,
1126        expected_permissions: Permissions,
1127    ) -> Result<bool> {
1128        if perms_entry.len() != PERMS_ENTRY_LENGTH {
1129            return Err(crate::error::PdfError::EncryptionError(format!(
1130                "Perms entry must be {} bytes, got {}",
1131                PERMS_ENTRY_LENGTH,
1132                perms_entry.len()
1133            )));
1134        }
1135        if encryption_key.len() != UE_ENTRY_LENGTH {
1136            return Err(crate::error::PdfError::EncryptionError(format!(
1137                "Encryption key must be {} bytes",
1138                UE_ENTRY_LENGTH
1139            )));
1140        }
1141
1142        // Decrypt with AES-256-ECB
1143        let aes_key = AesKey::new_256(encryption_key.key.clone())?;
1144        let aes = Aes::new(aes_key);
1145
1146        let decrypted = aes.decrypt_ecb(perms_entry).map_err(|e| {
1147            crate::error::PdfError::EncryptionError(format!("Perms decryption failed: {}", e))
1148        })?;
1149
1150        // Verify fixed marker
1151        if decrypted[PERMS_MARKER_START..PERMS_MARKER_END] != PERMS_MARKER {
1152            return Ok(false);
1153        }
1154
1155        // Verify literal "adb"
1156        if &decrypted[PERMS_LITERAL_START..PERMS_LITERAL_END] != PERMS_LITERAL {
1157            return Ok(false);
1158        }
1159
1160        // SECURITY: Constant-time comparison for permissions
1161        let expected_bytes = (expected_permissions.bits() as u32).to_le_bytes();
1162        let actual_bytes = &decrypted[PERMS_P_START..PERMS_P_END];
1163        Ok(bool::from(expected_bytes.ct_eq(actual_bytes)))
1164    }
1165
1166    /// Extract EncryptMetadata flag from decrypted Perms entry
1167    ///
1168    /// Returns Ok(Some(true)) if EncryptMetadata='T', Ok(Some(false)) if 'F',
1169    /// Ok(None) if Perms structure is invalid.
1170    pub fn extract_r6_encrypt_metadata(
1171        &self,
1172        perms_entry: &[u8],
1173        encryption_key: &EncryptionKey,
1174    ) -> Result<Option<bool>> {
1175        if perms_entry.len() != PERMS_ENTRY_LENGTH || encryption_key.len() != UE_ENTRY_LENGTH {
1176            return Ok(None);
1177        }
1178
1179        let aes_key = AesKey::new_256(encryption_key.key.clone())?;
1180        let aes = Aes::new(aes_key);
1181
1182        let decrypted = match aes.decrypt_ecb(perms_entry) {
1183            Ok(d) => d,
1184            Err(_) => return Ok(None),
1185        };
1186
1187        // Verify structure before extracting flag
1188        if decrypted[PERMS_MARKER_START..PERMS_MARKER_END] != PERMS_MARKER
1189            || &decrypted[PERMS_LITERAL_START..PERMS_LITERAL_END] != PERMS_LITERAL
1190        {
1191            return Ok(None);
1192        }
1193
1194        // Extract EncryptMetadata flag
1195        match decrypted[PERMS_ENCRYPT_META_BYTE] {
1196            b'T' => Ok(Some(true)),
1197            b'F' => Ok(Some(false)),
1198            _ => Ok(None), // Invalid flag value
1199        }
1200    }
1201
1202    // ========================================================================
1203    // R5/R6 Owner Password Support (ISO 32000-1 §7.6.4.3.3)
1204    // ========================================================================
1205
1206    /// Compute R5 owner password hash (O entry)
1207    ///
1208    /// Algorithm 9 (ISO 32000-1): Creates 48-byte O entry
1209    /// - Bytes 0-31: SHA-256(owner_password || validation_salt)
1210    /// - Bytes 32-39: validation_salt (8 random bytes)
1211    /// - Bytes 40-47: key_salt (8 random bytes)
1212    pub fn compute_r5_owner_hash(
1213        &self,
1214        owner_password: &OwnerPassword,
1215        u_entry: &[u8],
1216    ) -> Result<Vec<u8>> {
1217        if self.revision != SecurityHandlerRevision::R5 {
1218            return Err(crate::error::PdfError::EncryptionError(
1219                "R5 owner hash only for Revision 5".to_string(),
1220            ));
1221        }
1222        if u_entry.len() != U_ENTRY_LENGTH {
1223            return Err(crate::error::PdfError::EncryptionError(format!(
1224                "U entry must be {} bytes for R5 O computation, got {}",
1225                U_ENTRY_LENGTH,
1226                u_entry.len()
1227            )));
1228        }
1229
1230        // Generate random salts
1231        let validation_salt = generate_salt(R5_SALT_LENGTH);
1232        let key_salt = generate_salt(R5_SALT_LENGTH);
1233
1234        // Compute hash: SHA-256(owner_password || validation_salt || U[0..48]).
1235        // The R5 (Adobe SHA-256 extension level 3) owner hash appends the whole
1236        // 48-byte U entry; omitting it makes the O entry non-interoperable with
1237        // conforming readers (issue #380). The R5 *user* hash omits U by design.
1238        let mut data = Vec::new();
1239        data.extend_from_slice(owner_password.0.as_bytes());
1240        data.extend_from_slice(&validation_salt);
1241        data.extend_from_slice(u_entry);
1242
1243        let hash = sha256(&data);
1244
1245        // Construct O entry: hash[0..32] + validation_salt + key_salt
1246        let mut o_entry = Vec::with_capacity(U_ENTRY_LENGTH);
1247        o_entry.extend_from_slice(&hash[..U_HASH_LENGTH]);
1248        o_entry.extend_from_slice(&validation_salt);
1249        o_entry.extend_from_slice(&key_salt);
1250
1251        debug_assert_eq!(o_entry.len(), U_ENTRY_LENGTH);
1252        Ok(o_entry)
1253    }
1254
1255    /// Validate R5 owner password
1256    ///
1257    /// Algorithm 12 (ISO 32000-1): Validates owner password against O entry
1258    pub fn validate_r5_owner_password(
1259        &self,
1260        owner_password: &OwnerPassword,
1261        o_entry: &[u8],
1262        u_entry: &[u8],
1263    ) -> Result<bool> {
1264        let o_entry = defined_entry_prefix(o_entry, "R5 O")?;
1265        let u_entry = defined_entry_prefix(u_entry, "R5 U")?;
1266
1267        // Extract validation_salt from O (bytes 32-39)
1268        let validation_salt = &o_entry[U_VALIDATION_SALT_START..U_VALIDATION_SALT_END];
1269
1270        // Compute hash: SHA-256(owner_password || validation_salt || U[0..48]).
1271        // See `compute_r5_owner_hash` for why U is appended (issue #380).
1272        let mut data = Vec::new();
1273        data.extend_from_slice(owner_password.0.as_bytes());
1274        data.extend_from_slice(validation_salt);
1275        data.extend_from_slice(u_entry);
1276
1277        let hash = sha256(&data);
1278
1279        // SECURITY: Constant-time comparison prevents timing attacks
1280        let stored_hash = &o_entry[..U_HASH_LENGTH];
1281        Ok(bool::from(hash[..U_HASH_LENGTH].ct_eq(stored_hash)))
1282    }
1283
1284    /// Compute R5 OE entry (encrypted encryption key with owner password)
1285    ///
1286    /// OE = AES-256-CBC(encryption_key, key=intermediate_key, iv=zeros)
1287    /// where intermediate_key = SHA-256(owner_password || key_salt)
1288    pub fn compute_r5_oe_entry(
1289        &self,
1290        owner_password: &OwnerPassword,
1291        o_entry: &[u8],
1292        u_entry: &[u8],
1293        encryption_key: &[u8],
1294    ) -> Result<Vec<u8>> {
1295        if o_entry.len() != U_ENTRY_LENGTH {
1296            return Err(crate::error::PdfError::EncryptionError(format!(
1297                "O entry must be {} bytes",
1298                U_ENTRY_LENGTH
1299            )));
1300        }
1301        if u_entry.len() != U_ENTRY_LENGTH {
1302            return Err(crate::error::PdfError::EncryptionError(format!(
1303                "U entry must be {} bytes",
1304                U_ENTRY_LENGTH
1305            )));
1306        }
1307        if encryption_key.len() != UE_ENTRY_LENGTH {
1308            return Err(crate::error::PdfError::EncryptionError(format!(
1309                "Encryption key must be {} bytes",
1310                UE_ENTRY_LENGTH
1311            )));
1312        }
1313
1314        // Extract key_salt from O (bytes 40-47)
1315        let key_salt = &o_entry[U_KEY_SALT_START..U_KEY_SALT_END];
1316
1317        // Compute intermediate key: SHA-256(owner_password || key_salt || U[0..48]).
1318        // See `compute_r5_owner_hash` for why U is appended (issue #380).
1319        let mut data = Vec::new();
1320        data.extend_from_slice(owner_password.0.as_bytes());
1321        data.extend_from_slice(key_salt);
1322        data.extend_from_slice(u_entry);
1323
1324        let intermediate_key = sha256(&data);
1325
1326        // Encrypt encryption_key with intermediate_key using AES-256-CBC
1327        let aes = Aes::new(AesKey::new_256(intermediate_key)?);
1328        let iv = [0u8; 16];
1329
1330        let encrypted = aes.encrypt_cbc_raw(encryption_key, &iv).map_err(|e| {
1331            crate::error::PdfError::EncryptionError(format!("OE encryption failed: {}", e))
1332        })?;
1333
1334        // OE is first 32 bytes of encrypted output
1335        Ok(encrypted[..UE_ENTRY_LENGTH].to_vec())
1336    }
1337
1338    /// Recover encryption key from R5 OE entry using owner password
1339    pub fn recover_r5_owner_encryption_key(
1340        &self,
1341        owner_password: &OwnerPassword,
1342        o_entry: &[u8],
1343        u_entry: &[u8],
1344        oe_entry: &[u8],
1345    ) -> Result<Vec<u8>> {
1346        let o_entry = defined_entry_prefix(o_entry, "O")?;
1347        let u_entry = defined_entry_prefix(u_entry, "U")?;
1348        if oe_entry.len() != UE_ENTRY_LENGTH {
1349            return Err(crate::error::PdfError::EncryptionError(format!(
1350                "OE entry must be {} bytes",
1351                UE_ENTRY_LENGTH
1352            )));
1353        }
1354
1355        // Extract key_salt from O (bytes 40-47)
1356        let key_salt = &o_entry[U_KEY_SALT_START..U_KEY_SALT_END];
1357
1358        // Compute intermediate key: SHA-256(owner_password || key_salt || U[0..48]).
1359        // See `compute_r5_owner_hash` for why U is appended (issue #380).
1360        let mut data = Vec::new();
1361        data.extend_from_slice(owner_password.0.as_bytes());
1362        data.extend_from_slice(key_salt);
1363        data.extend_from_slice(u_entry);
1364
1365        let intermediate_key = sha256(&data);
1366
1367        // Decrypt OE to get encryption key
1368        let aes = Aes::new(AesKey::new_256(intermediate_key)?);
1369        let iv = [0u8; 16];
1370
1371        let decrypted = aes.decrypt_cbc_raw(oe_entry, &iv).map_err(|e| {
1372            crate::error::PdfError::EncryptionError(format!("OE decryption failed: {}", e))
1373        })?;
1374
1375        Ok(decrypted)
1376    }
1377
1378    /// Compute R6 owner password hash (O entry)
1379    ///
1380    /// R6 uses Algorithm 2.B (complex hash) for owner password too
1381    pub fn compute_r6_owner_hash(
1382        &self,
1383        owner_password: &OwnerPassword,
1384        u_entry: &[u8],
1385    ) -> Result<Vec<u8>> {
1386        if self.revision != SecurityHandlerRevision::R6 {
1387            return Err(crate::error::PdfError::EncryptionError(
1388                "R6 owner hash only for Revision 6".to_string(),
1389            ));
1390        }
1391        if u_entry.len() != U_ENTRY_LENGTH {
1392            return Err(crate::error::PdfError::EncryptionError(format!(
1393                "U entry must be {} bytes for R6 O computation",
1394                U_ENTRY_LENGTH
1395            )));
1396        }
1397
1398        // Generate random salts
1399        let validation_salt = generate_salt(R6_SALT_LENGTH);
1400        let key_salt = generate_salt(R6_SALT_LENGTH);
1401
1402        // For R6 the owner hash is Algorithm 2.B with the owner password, the
1403        // validation salt, and the 48-byte U entry as the additional input
1404        // (ISO 32000-2:2020 §7.6.4.3.4). `compute_hash_r6_algorithm_2b` builds
1405        // `password ‖ salt ‖ U` internally; passing a pre-concatenated blob as
1406        // the `password` argument double-includes the salt/U and is not
1407        // interoperable with conforming readers (issue #380).
1408        let hash =
1409            compute_hash_r6_algorithm_2b(owner_password.0.as_bytes(), &validation_salt, u_entry)?;
1410
1411        // Construct O entry: hash[0..32] + validation_salt + key_salt
1412        let mut o_entry = Vec::with_capacity(U_ENTRY_LENGTH);
1413        o_entry.extend_from_slice(&hash[..U_HASH_LENGTH]);
1414        o_entry.extend_from_slice(&validation_salt);
1415        o_entry.extend_from_slice(&key_salt);
1416
1417        debug_assert_eq!(o_entry.len(), U_ENTRY_LENGTH);
1418        Ok(o_entry)
1419    }
1420
1421    /// Validate R6 owner password
1422    ///
1423    /// Uses Algorithm 2.B to validate owner password
1424    pub fn validate_r6_owner_password(
1425        &self,
1426        owner_password: &OwnerPassword,
1427        o_entry: &[u8],
1428        u_entry: &[u8],
1429    ) -> Result<bool> {
1430        let o_entry = defined_entry_prefix(o_entry, "R6 O")?;
1431        let u_entry = defined_entry_prefix(u_entry, "R6 U")?;
1432
1433        // Extract validation_salt from O (bytes 32-39)
1434        let validation_salt = &o_entry[U_VALIDATION_SALT_START..U_VALIDATION_SALT_END];
1435
1436        // Compute hash using Algorithm 2.B: 2B(owner_pw, validation_salt, U).
1437        // See `compute_r6_owner_hash` for why the salt/U must be passed as
1438        // dedicated arguments rather than pre-concatenated (issue #380).
1439        let hash =
1440            compute_hash_r6_algorithm_2b(owner_password.0.as_bytes(), validation_salt, u_entry)?;
1441
1442        // SECURITY: Constant-time comparison prevents timing attacks
1443        let stored_hash = &o_entry[..U_HASH_LENGTH];
1444        Ok(bool::from(hash[..U_HASH_LENGTH].ct_eq(stored_hash)))
1445    }
1446
1447    /// Compute R6 OE entry (encrypted encryption key with owner password)
1448    ///
1449    /// Uses Algorithm 2.B to derive intermediate key
1450    pub fn compute_r6_oe_entry(
1451        &self,
1452        owner_password: &OwnerPassword,
1453        o_entry: &[u8],
1454        u_entry: &[u8],
1455        encryption_key: &[u8],
1456    ) -> Result<Vec<u8>> {
1457        if o_entry.len() != U_ENTRY_LENGTH {
1458            return Err(crate::error::PdfError::EncryptionError(format!(
1459                "O entry must be {} bytes",
1460                U_ENTRY_LENGTH
1461            )));
1462        }
1463        if u_entry.len() != U_ENTRY_LENGTH {
1464            return Err(crate::error::PdfError::EncryptionError(format!(
1465                "U entry must be {} bytes",
1466                U_ENTRY_LENGTH
1467            )));
1468        }
1469        if encryption_key.len() != UE_ENTRY_LENGTH {
1470            return Err(crate::error::PdfError::EncryptionError(format!(
1471                "Encryption key must be {} bytes",
1472                UE_ENTRY_LENGTH
1473            )));
1474        }
1475
1476        // Extract key_salt from O (bytes 40-47)
1477        let key_salt = &o_entry[U_KEY_SALT_START..U_KEY_SALT_END];
1478
1479        // Compute intermediate key using Algorithm 2.B: 2B(owner_pw, key_salt, U).
1480        // See `compute_r6_owner_hash` for the argument-order rationale (#380).
1481        let intermediate_key =
1482            compute_hash_r6_algorithm_2b(owner_password.0.as_bytes(), key_salt, u_entry)?;
1483
1484        // Encrypt encryption_key with intermediate_key using AES-256-CBC
1485        let aes = Aes::new(AesKey::new_256(intermediate_key[..32].to_vec())?);
1486        let iv = [0u8; 16];
1487
1488        let encrypted = aes.encrypt_cbc_raw(encryption_key, &iv).map_err(|e| {
1489            crate::error::PdfError::EncryptionError(format!("OE encryption failed: {}", e))
1490        })?;
1491
1492        Ok(encrypted[..UE_ENTRY_LENGTH].to_vec())
1493    }
1494
1495    /// Recover encryption key from R6 OE entry using owner password
1496    pub fn recover_r6_owner_encryption_key(
1497        &self,
1498        owner_password: &OwnerPassword,
1499        o_entry: &[u8],
1500        u_entry: &[u8],
1501        oe_entry: &[u8],
1502    ) -> Result<Vec<u8>> {
1503        let o_entry = defined_entry_prefix(o_entry, "O")?;
1504        let u_entry = defined_entry_prefix(u_entry, "U")?;
1505        if oe_entry.len() != UE_ENTRY_LENGTH {
1506            return Err(crate::error::PdfError::EncryptionError(format!(
1507                "OE entry must be {} bytes",
1508                UE_ENTRY_LENGTH
1509            )));
1510        }
1511
1512        // Extract key_salt from O (bytes 40-47)
1513        let key_salt = &o_entry[U_KEY_SALT_START..U_KEY_SALT_END];
1514
1515        // Compute intermediate key using Algorithm 2.B: 2B(owner_pw, key_salt, U).
1516        // See `compute_r6_owner_hash` for the argument-order rationale (#380).
1517        let intermediate_key =
1518            compute_hash_r6_algorithm_2b(owner_password.0.as_bytes(), key_salt, u_entry)?;
1519
1520        // Decrypt OE to get encryption key
1521        let aes = Aes::new(AesKey::new_256(intermediate_key[..32].to_vec())?);
1522        let iv = [0u8; 16];
1523
1524        let decrypted = aes.decrypt_cbc_raw(oe_entry, &iv).map_err(|e| {
1525            crate::error::PdfError::EncryptionError(format!("OE decryption failed: {}", e))
1526        })?;
1527
1528        Ok(decrypted)
1529    }
1530
1531    /// Compute object-specific encryption key (Algorithm 1, ISO 32000-1 §7.6.2)
1532    pub fn compute_object_key(&self, key: &EncryptionKey, obj_id: &ObjectId) -> Vec<u8> {
1533        let mut data = Vec::new();
1534        data.extend_from_slice(&key.key);
1535        data.extend_from_slice(&obj_id.number().to_le_bytes()[..3]); // Low 3 bytes
1536        data.extend_from_slice(&obj_id.generation().to_le_bytes()[..2]); // Low 2 bytes
1537
1538        let hash = md5::compute(&data);
1539        let key_len = (key.len() + 5).min(16);
1540        hash[..key_len].to_vec()
1541    }
1542
1543    /// Validate user password (Algorithm 6, ISO 32000-1 §7.6.3.4)
1544    ///
1545    /// Returns Ok(true) if password is correct, Ok(false) if incorrect.
1546    /// Returns Err only on internal errors.
1547    pub fn validate_user_password(
1548        &self,
1549        password: &UserPassword,
1550        user_hash: &[u8],
1551        owner_hash: &[u8],
1552        permissions: Permissions,
1553        file_id: Option<&[u8]>,
1554    ) -> Result<bool> {
1555        // Compute encryption key from provided password
1556        let key = self.compute_encryption_key(password, owner_hash, permissions, file_id)?;
1557
1558        match self.revision {
1559            SecurityHandlerRevision::R2 => {
1560                // For R2: Encrypt padding with key and compare with U
1561                let rc4_key = Rc4Key::from_slice(&key.key);
1562                let encrypted_padding = rc4_encrypt(&rc4_key, &PADDING);
1563
1564                // Compare with stored user hash
1565                Ok(user_hash.len() >= 32 && encrypted_padding[..] == user_hash[..32])
1566            }
1567            SecurityHandlerRevision::R3 | SecurityHandlerRevision::R4 => {
1568                // For R3/R4: Compute MD5 hash including file ID
1569                let mut data = Vec::new();
1570                data.extend_from_slice(&PADDING);
1571
1572                if let Some(id) = file_id {
1573                    data.extend_from_slice(id);
1574                }
1575
1576                let hash = md5::compute(&data);
1577
1578                // Encrypt hash with RC4
1579                let rc4_key = Rc4Key::from_slice(&key.key);
1580                let mut encrypted = rc4_encrypt(&rc4_key, hash.as_ref());
1581
1582                // Do 19 additional iterations with modified keys
1583                for i in 1..=19 {
1584                    let mut key_bytes = key.key.clone();
1585                    for byte in &mut key_bytes {
1586                        *byte ^= i as u8;
1587                    }
1588                    let iter_key = Rc4Key::from_slice(&key_bytes);
1589                    encrypted = rc4_encrypt(&iter_key, &encrypted);
1590                }
1591
1592                // Compare first 16 bytes of result with first 16 bytes of U
1593                Ok(user_hash.len() >= 16 && encrypted[..16] == user_hash[..16])
1594            }
1595            SecurityHandlerRevision::R5 | SecurityHandlerRevision::R6 => {
1596                // For R5/R6, use AES-based validation
1597                self.validate_aes_user_password(password, user_hash, permissions, file_id)
1598            }
1599        }
1600    }
1601
1602    /// Validate owner password (Algorithm 7, ISO 32000-1 §7.6.3.4)
1603    ///
1604    /// Returns Ok(true) if password is correct, Ok(false) if incorrect.
1605    /// Returns Err only on internal errors.
1606    ///
1607    /// Note: For owner password validation, we first decrypt the user password
1608    /// from the owner hash, then validate that user password.
1609    ///
1610    /// # Parameters
1611    /// - `owner_password`: The owner password to validate
1612    /// - `owner_hash`: The O entry from the encryption dictionary
1613    /// - `_user_password`: Unused for R2-R4 (recovered from owner_hash), ignored for R5/R6
1614    /// - `_permissions`: Unused for R5/R6 (not part of validation)
1615    /// - `_file_id`: Unused for R5/R6 (not part of validation)
1616    /// - `u_entry`: Required for R5 and R6 (the U entry is bound into the owner
1617    ///   hash — SHA-256 for R5, Algorithm 2.B for R6); ignored for R2-R4
1618    pub fn validate_owner_password(
1619        &self,
1620        owner_password: &OwnerPassword,
1621        owner_hash: &[u8],
1622        _user_password: &UserPassword, // Will be recovered from owner_hash
1623        _permissions: Permissions,
1624        _file_id: Option<&[u8]>,
1625        u_entry: Option<&[u8]>,
1626    ) -> Result<bool> {
1627        match self.revision {
1628            SecurityHandlerRevision::R2
1629            | SecurityHandlerRevision::R3
1630            | SecurityHandlerRevision::R4 => {
1631                // Step 1: Pad owner password
1632                let owner_pad = Self::pad_password(&owner_password.0);
1633
1634                // Step 2: Create MD5 hash of owner password
1635                let mut hash = md5::compute(&owner_pad).to_vec();
1636
1637                // Step 3: For revision 3+, do 50 additional iterations
1638                if self.revision >= SecurityHandlerRevision::R3 {
1639                    for _ in 0..50 {
1640                        hash = md5::compute(&hash).to_vec();
1641                    }
1642                }
1643
1644                // Step 4: Create RC4 key from hash (truncated to key length)
1645                let rc4_key = Rc4Key::from_slice(&hash[..self.key_length]);
1646
1647                // Step 5: Decrypt owner hash to get user password
1648                let mut decrypted = owner_hash[..32].to_vec();
1649
1650                // For R3+, do 19 iterations in reverse
1651                if self.revision >= SecurityHandlerRevision::R3 {
1652                    for i in (0..20).rev() {
1653                        let mut key_bytes = hash[..self.key_length].to_vec();
1654                        for byte in &mut key_bytes {
1655                            *byte ^= i as u8;
1656                        }
1657                        let iter_key = Rc4Key::from_slice(&key_bytes);
1658                        decrypted = rc4_encrypt(&iter_key, &decrypted);
1659                    }
1660                } else {
1661                    // For R2, single decryption
1662                    decrypted = rc4_encrypt(&rc4_key, &decrypted);
1663                }
1664
1665                // Step 6: The decrypted data should be the padded user password
1666                // Try to validate by computing what the owner hash SHOULD be
1667                // with this owner password, and compare
1668
1669                // Extract potential user password (remove padding)
1670                let user_pwd_bytes = decrypted
1671                    .iter()
1672                    .take_while(|&&b| b != 0x28 || decrypted.starts_with(&PADDING))
1673                    .copied()
1674                    .collect::<Vec<u8>>();
1675
1676                let recovered_user =
1677                    UserPassword(String::from_utf8_lossy(&user_pwd_bytes).to_string());
1678
1679                // Compute what owner hash should be with this owner password
1680                let computed_owner = self.compute_owner_hash(owner_password, &recovered_user);
1681
1682                // Compare with stored owner hash
1683                Ok(computed_owner[..32] == owner_hash[..32])
1684            }
1685            SecurityHandlerRevision::R5 => {
1686                // R5 owner validation is SHA-256(owner_pw ‖ salt ‖ U); it needs
1687                // the 48-byte U entry (issue #380).
1688                let u = u_entry.ok_or_else(|| {
1689                    crate::error::PdfError::EncryptionError(
1690                        "R5 owner password validation requires U entry".to_string(),
1691                    )
1692                })?;
1693                self.validate_r5_owner_password(owner_password, owner_hash, u)
1694            }
1695            SecurityHandlerRevision::R6 => {
1696                // R6 uses Algorithm 2.B which requires U entry
1697                let u = u_entry.ok_or_else(|| {
1698                    crate::error::PdfError::EncryptionError(
1699                        "R6 owner password validation requires U entry".to_string(),
1700                    )
1701                })?;
1702                self.validate_r6_owner_password(owner_password, owner_hash, u)
1703            }
1704        }
1705    }
1706}
1707
1708/// Helper function for RC4 encryption
1709fn rc4_encrypt(key: &Rc4Key, data: &[u8]) -> Vec<u8> {
1710    let mut cipher = Rc4::new(key);
1711    cipher.process(data)
1712}
1713
1714// Use the md5 crate for actual MD5 hashing (required for PDF encryption)
1715
1716/// SHA-256 implementation using RustCrypto (production-grade)
1717///
1718/// Returns a 32-byte hash of the input data according to FIPS 180-4.
1719/// Used for R5 password validation and key derivation.
1720fn sha256(data: &[u8]) -> Vec<u8> {
1721    Sha256::digest(data).to_vec()
1722}
1723
1724/// SHA-384 implementation using RustCrypto (production-grade)
1725///
1726/// Returns a 48-byte hash of the input data according to FIPS 180-4.
1727/// Used for R6 Algorithm 2.B hash rotation.
1728fn sha384(data: &[u8]) -> Vec<u8> {
1729    Sha384::digest(data).to_vec()
1730}
1731
1732/// SHA-512 implementation using RustCrypto (production-grade)
1733///
1734/// Returns a 64-byte hash of the input data according to FIPS 180-4.
1735/// Used for R6 password validation and key derivation.
1736fn sha512(data: &[u8]) -> Vec<u8> {
1737    Sha512::digest(data).to_vec()
1738}
1739
1740// ============================================================================
1741// Algorithm 2.B - R6 Key Derivation (ISO 32000-2:2020 §7.6.4.3.4)
1742// ============================================================================
1743
1744/// Minimum number of rounds for Algorithm 2.B
1745const ALGORITHM_2B_MIN_ROUNDS: usize = 64;
1746
1747/// Maximum rounds (DoS protection, not in spec but common implementation)
1748const ALGORITHM_2B_MAX_ROUNDS: usize = 2048;
1749
1750/// Maximum password length (ISO 32000-2 §7.6.3.3.2 recommends 127 bytes)
1751/// This prevents DoS via massive allocation: 1MB password × 64 repetitions = 64MB/round
1752const ALGORITHM_2B_MAX_PASSWORD_LEN: usize = 127;
1753
1754/// Number of bytes used for hash function selection (spec: first 16 bytes as BigInteger mod 3)
1755const HASH_SELECTOR_BYTES: usize = 16;
1756
1757/// Compute R6 password hash using Algorithm 2.B (ISO 32000-2:2020 §7.6.4.3.4)
1758///
1759/// This is the correct R6 key derivation algorithm used by qpdf, Adobe Acrobat,
1760/// and other compliant PDF processors. It uses AES-128-CBC encryption within
1761/// the iteration loop and dynamically selects SHA-256/384/512 based on output.
1762///
1763/// # Algorithm Overview
1764/// 1. Initial hash: K = SHA-256(password + salt + U\[0..48\])
1765/// 2. Loop (minimum 64 rounds):
1766///    a. Construct k1 = (password + K + U\[0..48\]), repeat 64 times
1767///    b. E = AES-128-CBC-encrypt(k1, key=K\[0..16\], iv=K\[16..32\])
1768///    c. Select hash: SHA-256/384/512 based on sum(E\[0..16\]) mod 3
1769///    d. K = hash(E)
1770///    e. Check termination: round >= 64 AND E\[last\] <= (round - 32)
1771/// 3. Return K\[0..32\]
1772///
1773/// # Parameters
1774/// - `password`: User password bytes (UTF-8 encoded)
1775/// - `salt`: 8-byte salt (validation_salt or key_salt from U entry)
1776/// - `u_entry`: Full 48-byte U entry (or empty slice for initial computation)
1777///
1778/// # Returns
1779/// 32-byte derived key
1780///
1781/// # Security Notes
1782/// - Maximum 2048 rounds to prevent DoS attacks
1783/// - Variable iteration count makes brute-force harder
1784/// - AES encryption + hash rotation provides strong KDF
1785///
1786/// # References
1787/// - ISO 32000-2:2020 §7.6.4.3.4 "Algorithm 2.B: Computing a hash (R6)"
1788pub fn compute_hash_r6_algorithm_2b(
1789    password: &[u8],
1790    salt: &[u8],
1791    u_entry: &[u8],
1792) -> Result<Vec<u8>> {
1793    // Security: Validate password length to prevent DoS via massive allocations
1794    if password.len() > ALGORITHM_2B_MAX_PASSWORD_LEN {
1795        return Err(crate::error::PdfError::EncryptionError(format!(
1796            "Password too long ({} bytes, max {})",
1797            password.len(),
1798            ALGORITHM_2B_MAX_PASSWORD_LEN
1799        )));
1800    }
1801
1802    // Step 1: Initial hash K = SHA-256(password + salt + U[0..48])
1803    let mut input = Vec::with_capacity(password.len() + salt.len() + u_entry.len().min(48));
1804    input.extend_from_slice(password);
1805    input.extend_from_slice(salt);
1806    if !u_entry.is_empty() {
1807        input.extend_from_slice(&u_entry[..u_entry.len().min(48)]);
1808    }
1809
1810    let mut k = sha256(&input);
1811
1812    // Step 2: Iteration loop
1813    let mut round: usize = 0;
1814    loop {
1815        // 2a. Construct input sequence: password + K + U[0..48], repeated
1816        // The spec says to create a sequence that will be encrypted
1817        let mut k1_unit = Vec::new();
1818        k1_unit.extend_from_slice(password);
1819        k1_unit.extend_from_slice(&k);
1820        if !u_entry.is_empty() {
1821            k1_unit.extend_from_slice(&u_entry[..u_entry.len().min(48)]);
1822        }
1823
1824        // Repeat 64 times to create input for AES
1825        let mut k1 = Vec::with_capacity(k1_unit.len() * 64);
1826        for _ in 0..64 {
1827            k1.extend_from_slice(&k1_unit);
1828        }
1829
1830        // Zero-pad to AES block size (16 bytes) per ISO 32000-2 §7.6.4.3.4
1831        // NOTE: This is zero-padding, NOT PKCS#7 - the spec requires raw AES without padding removal
1832        while k1.len() % 16 != 0 {
1833            k1.push(0);
1834        }
1835
1836        // 2b. AES-128-CBC encryption
1837        // Key: first 16 bytes of K, IV: next 16 bytes of K
1838        if k.len() < 32 {
1839            // Extend K if needed (shouldn't happen with proper hashes)
1840            while k.len() < 32 {
1841                k.push(0);
1842            }
1843        }
1844
1845        let aes_key = AesKey::new_128(k[..16].to_vec()).map_err(|e| {
1846            crate::error::PdfError::EncryptionError(format!(
1847                "Algorithm 2.B: Failed to create AES key: {}",
1848                e
1849            ))
1850        })?;
1851        let aes = Aes::new(aes_key);
1852        let iv = &k[16..32];
1853
1854        let e = aes.encrypt_cbc_raw(&k1, iv).map_err(|e| {
1855            crate::error::PdfError::EncryptionError(format!(
1856                "Algorithm 2.B: AES encryption failed: {}",
1857                e
1858            ))
1859        })?;
1860
1861        // 2c. Select hash function based on first 16 bytes of E as BigInteger mod 3
1862        // Per iText/Adobe implementation: interpret E[0..HASH_SELECTOR_BYTES] as big-endian integer
1863        // Mathematical equivalence: sum(bytes) mod 3 == BigInteger(bytes) mod 3
1864        // because 256 mod 3 = 1, therefore 256^k mod 3 = 1 for all k
1865        let hash_selector = {
1866            let sum: u64 = e[..HASH_SELECTOR_BYTES.min(e.len())]
1867                .iter()
1868                .map(|&b| b as u64)
1869                .sum();
1870            (sum % 3) as u8
1871        };
1872
1873        k = match hash_selector {
1874            0 => sha256(&e),
1875            1 => sha384(&e),
1876            2 => sha512(&e),
1877            _ => unreachable!("Modulo 3 can only be 0, 1, or 2"),
1878        };
1879
1880        // 2d. Check termination condition
1881        // Terminate when: round >= 64 AND E[last] <= (round - 32)
1882        let last_byte = *e.last().unwrap_or(&0);
1883        round += 1;
1884
1885        if round >= ALGORITHM_2B_MIN_ROUNDS {
1886            // The termination condition from ISO spec:
1887            // "the last byte value of the last iteration is less than or equal to
1888            // the number of iterations minus 32"
1889            if (last_byte as usize) <= round.saturating_sub(32) {
1890                break;
1891            }
1892        }
1893
1894        // Safety: Prevent infinite loop (DoS protection)
1895        if round >= ALGORITHM_2B_MAX_ROUNDS {
1896            break;
1897        }
1898    }
1899
1900    // Step 3: Return first 32 bytes of final K
1901    // K might be > 32 bytes if last hash was SHA-384 or SHA-512
1902    Ok(k[..32.min(k.len())].to_vec())
1903}
1904
1905/// R5 salt length in bytes (PDF spec §7.6.4.3.4)
1906const R5_SALT_LENGTH: usize = 8;
1907
1908/// R5 SHA-256 iteration count (ISO 32000-2:2020 Algorithm 8/11)
1909/// NOTE: R5 does NOT use iterations - hash is simply SHA-256(password + salt)
1910/// The 64 iterations are only for R6 which uses Algorithm 2.B
1911const R5_HASH_ITERATIONS: usize = 0;
1912
1913/// R6 salt length in bytes (PDF spec ISO 32000-2)
1914const R6_SALT_LENGTH: usize = 8;
1915
1916// ============================================================================
1917// R5/R6 U Entry Structure Constants (48 bytes total)
1918// ============================================================================
1919
1920/// Length of the hash portion in U entry (SHA-256/SHA-512 truncated to 32 bytes)
1921const U_HASH_LENGTH: usize = 32;
1922
1923/// Start offset of validation salt in U entry
1924const U_VALIDATION_SALT_START: usize = 32;
1925
1926/// End offset of validation salt in U entry
1927const U_VALIDATION_SALT_END: usize = 40;
1928
1929/// Start offset of key salt in U entry
1930const U_KEY_SALT_START: usize = 40;
1931
1932/// End offset of key salt in U entry
1933const U_KEY_SALT_END: usize = 48;
1934
1935/// Total length of U entry for R5/R6
1936const U_ENTRY_LENGTH: usize = 48;
1937
1938/// Narrows a `/U` or `/O` entry read from a document to the bytes ISO 32000-2
1939/// §7.6.4.3.3 defines for it: a 32-byte hash, an 8-byte validation salt and an
1940/// 8-byte key salt.
1941///
1942/// Acrobat writes those entries as 127-byte strings, zero-padding everything
1943/// past byte 48 — the length the pre-R5 revisions used for `/U` and `/O`. Such
1944/// documents open in every conforming reader, so trailing bytes are ignored
1945/// rather than treated as a malformed entry, which is what turned a correct
1946/// empty password into `WrongPassword` in issue #459. Anything shorter than 48
1947/// bytes is still an error: the salts would not fit.
1948///
1949/// This applies to entries parsed from a file. The `compute_*` functions build
1950/// our own entries and keep requiring exactly 48 bytes.
1951fn defined_entry_prefix<'a>(entry: &'a [u8], label: &str) -> Result<&'a [u8]> {
1952    if entry.len() < U_ENTRY_LENGTH {
1953        return Err(crate::error::PdfError::EncryptionError(format!(
1954            "{} entry must be at least {} bytes, got {}",
1955            label,
1956            U_ENTRY_LENGTH,
1957            entry.len()
1958        )));
1959    }
1960    Ok(&entry[..U_ENTRY_LENGTH])
1961}
1962
1963/// Length of UE entry (encrypted encryption key)
1964const UE_ENTRY_LENGTH: usize = 32;
1965
1966// ============================================================================
1967// R6 Perms Entry Structure Constants (16 bytes total)
1968// ============================================================================
1969
1970/// Length of Perms entry
1971const PERMS_ENTRY_LENGTH: usize = 16;
1972
1973/// Start offset of permissions value in decrypted Perms (little-endian u32)
1974const PERMS_P_START: usize = 0;
1975
1976/// End offset of permissions value in decrypted Perms
1977const PERMS_P_END: usize = 4;
1978
1979/// Start offset of fixed marker (0xFFFFFFFF) in decrypted Perms
1980const PERMS_MARKER_START: usize = 4;
1981
1982/// End offset of fixed marker in decrypted Perms
1983const PERMS_MARKER_END: usize = 8;
1984
1985/// Offset of EncryptMetadata flag byte ('T' or 'F') in decrypted Perms
1986const PERMS_ENCRYPT_META_BYTE: usize = 8;
1987
1988/// Start offset of "adb" literal in decrypted Perms
1989const PERMS_LITERAL_START: usize = 9;
1990
1991/// End offset of "adb" literal in decrypted Perms
1992const PERMS_LITERAL_END: usize = 12;
1993
1994/// Start offset of the four random bytes in decrypted Perms
1995const PERMS_RANDOM_START: usize = 12;
1996
1997/// Fixed marker value in Perms entry
1998const PERMS_MARKER: [u8; 4] = [0xFF, 0xFF, 0xFF, 0xFF];
1999
2000/// Literal verification string in Perms entry
2001const PERMS_LITERAL: &[u8; 3] = b"adb";
2002
2003/// Generate cryptographically secure random salt using OS CSPRNG
2004///
2005/// Uses `rand::rng()` which provides a thread-local CSPRNG (ChaCha12) seeded
2006/// from the OS random number generator. This is suitable for PDF encryption salts.
2007///
2008/// # Security
2009/// - Uses ChaCha12 PRNG seeded from OS entropy (rand 0.9 implementation)
2010/// - Provides cryptographic-quality randomness for salt generation
2011/// - Each call produces independent random bytes
2012fn generate_salt(len: usize) -> Vec<u8> {
2013    let mut salt = vec![0u8; len];
2014    rand::rng().fill_bytes(&mut salt);
2015    salt
2016}
2017
2018#[cfg(test)]
2019mod tests {
2020    use super::*;
2021
2022    #[test]
2023    fn test_pad_password() {
2024        let padded = StandardSecurityHandler::pad_password("test");
2025        assert_eq!(padded.len(), 32);
2026        assert_eq!(&padded[..4], b"test");
2027        assert_eq!(&padded[4..8], &PADDING[..4]);
2028    }
2029
2030    #[test]
2031    fn test_pad_password_long() {
2032        let long_password = "a".repeat(40);
2033        let padded = StandardSecurityHandler::pad_password(&long_password);
2034        assert_eq!(padded.len(), 32);
2035        assert_eq!(&padded[..32], &long_password.as_bytes()[..32]);
2036    }
2037
2038    #[test]
2039    fn test_rc4_40bit_handler() {
2040        let handler = StandardSecurityHandler::rc4_40bit();
2041        assert_eq!(handler.revision, SecurityHandlerRevision::R2);
2042        assert_eq!(handler.key_length, 5);
2043    }
2044
2045    #[test]
2046    fn test_rc4_128bit_handler() {
2047        let handler = StandardSecurityHandler::rc4_128bit();
2048        assert_eq!(handler.revision, SecurityHandlerRevision::R3);
2049        assert_eq!(handler.key_length, 16);
2050    }
2051
2052    #[test]
2053    fn test_owner_hash_computation() {
2054        let handler = StandardSecurityHandler::rc4_40bit();
2055        let owner_pwd = OwnerPassword("owner".to_string());
2056        let user_pwd = UserPassword("user".to_string());
2057
2058        let hash = handler.compute_owner_hash(&owner_pwd, &user_pwd);
2059        assert_eq!(hash.len(), 32);
2060    }
2061
2062    #[test]
2063    fn test_encryption_key_computation() {
2064        let handler = StandardSecurityHandler::rc4_40bit();
2065        let user_pwd = UserPassword("user".to_string());
2066        let owner_hash = vec![0u8; 32];
2067        let permissions = Permissions::new();
2068
2069        let key = handler
2070            .compute_encryption_key(&user_pwd, &owner_hash, permissions, None)
2071            .unwrap();
2072
2073        assert_eq!(key.len(), 5);
2074    }
2075
2076    #[test]
2077    fn test_aes_256_r5_handler() {
2078        let handler = StandardSecurityHandler::aes_256_r5();
2079        assert_eq!(handler.revision, SecurityHandlerRevision::R5);
2080        assert_eq!(handler.key_length, 32);
2081    }
2082
2083    #[test]
2084    fn test_aes_256_r6_handler() {
2085        let handler = StandardSecurityHandler::aes_256_r6();
2086        assert_eq!(handler.revision, SecurityHandlerRevision::R6);
2087        assert_eq!(handler.key_length, 32);
2088    }
2089
2090    #[test]
2091    fn test_aes_encryption_key_computation() {
2092        let handler = StandardSecurityHandler::aes_256_r5();
2093        let user_pwd = UserPassword("testuser".to_string());
2094        let owner_hash = vec![0u8; 32];
2095        let permissions = Permissions::new();
2096
2097        let key = handler
2098            .compute_aes_encryption_key(&user_pwd, &owner_hash, permissions, None)
2099            .unwrap();
2100
2101        assert_eq!(key.len(), 32);
2102    }
2103
2104    #[test]
2105    fn test_aes_encrypt_decrypt() {
2106        let handler = StandardSecurityHandler::aes_256_r5();
2107        let key = EncryptionKey::new(vec![0u8; 32]);
2108        let obj_id = ObjectId::new(1, 0);
2109        let data = b"Hello AES encryption!";
2110
2111        let encrypted = handler.encrypt_aes(data, &key, &obj_id).unwrap();
2112        assert_ne!(encrypted.as_slice(), data);
2113        assert!(encrypted.len() > data.len()); // Should include IV
2114
2115        // Note: This simplified AES implementation is for demonstration only
2116        let _decrypted = handler.decrypt_aes(&encrypted, &key, &obj_id);
2117        // For now, just test that the operations complete without panicking
2118    }
2119
2120    #[test]
2121    fn test_aes_with_rc4_handler_fails() {
2122        let handler = StandardSecurityHandler::rc4_128bit();
2123        let key = EncryptionKey::new(vec![0u8; 16]);
2124        let obj_id = ObjectId::new(1, 0);
2125        let data = b"test data";
2126
2127        // Should fail because handler is not Rev 5+
2128        assert!(handler.encrypt_aes(data, &key, &obj_id).is_err());
2129        assert!(handler.decrypt_aes(data, &key, &obj_id).is_err());
2130    }
2131
2132    #[test]
2133    fn test_aes_decrypt_invalid_data() {
2134        let handler = StandardSecurityHandler::aes_256_r5();
2135        let key = EncryptionKey::new(vec![0u8; 32]);
2136        let obj_id = ObjectId::new(1, 0);
2137
2138        // Data too short (no IV)
2139        let short_data = vec![0u8; 10];
2140        assert!(handler.decrypt_aes(&short_data, &key, &obj_id).is_err());
2141    }
2142
2143    #[test]
2144    fn test_sha256_deterministic() {
2145        let data1 = b"test data";
2146        let data2 = b"test data";
2147        let data3 = b"different data";
2148
2149        let hash1 = sha256(data1);
2150        let hash2 = sha256(data2);
2151        let hash3 = sha256(data3);
2152
2153        assert_eq!(hash1.len(), 32);
2154        assert_eq!(hash2.len(), 32);
2155        assert_eq!(hash3.len(), 32);
2156
2157        assert_eq!(hash1, hash2); // Same input should give same output
2158        assert_ne!(hash1, hash3); // Different input should give different output
2159    }
2160
2161    #[test]
2162    fn test_security_handler_revision_ordering() {
2163        assert!(SecurityHandlerRevision::R2 < SecurityHandlerRevision::R3);
2164        assert!(SecurityHandlerRevision::R3 < SecurityHandlerRevision::R4);
2165        assert!(SecurityHandlerRevision::R4 < SecurityHandlerRevision::R5);
2166        assert!(SecurityHandlerRevision::R5 < SecurityHandlerRevision::R6);
2167    }
2168
2169    #[test]
2170    fn test_aes_password_validation() {
2171        let handler = StandardSecurityHandler::aes_256_r5();
2172        let password = UserPassword("testpassword".to_string());
2173        let user_hash = vec![0u8; 32]; // Simplified hash
2174        let permissions = Permissions::new();
2175
2176        // This is a basic test - in practice, the validation would be more complex
2177        let result = handler.validate_aes_user_password(&password, &user_hash, permissions, None);
2178        assert!(result.is_ok());
2179    }
2180
2181    // ===== Additional Comprehensive Tests =====
2182
2183    #[test]
2184    fn test_user_password_debug() {
2185        let pwd = UserPassword("debug_test".to_string());
2186        let debug_str = format!("{pwd:?}");
2187        assert!(debug_str.contains("UserPassword"));
2188        assert!(debug_str.contains("debug_test"));
2189    }
2190
2191    #[test]
2192    fn test_owner_password_debug() {
2193        let pwd = OwnerPassword("owner_debug".to_string());
2194        let debug_str = format!("{pwd:?}");
2195        assert!(debug_str.contains("OwnerPassword"));
2196        assert!(debug_str.contains("owner_debug"));
2197    }
2198
2199    #[test]
2200    fn test_encryption_key_debug() {
2201        let key = EncryptionKey::new(vec![0x01, 0x02, 0x03]);
2202        let debug_str = format!("{key:?}");
2203        assert!(debug_str.contains("EncryptionKey"));
2204    }
2205
2206    #[test]
2207    fn test_security_handler_revision_equality() {
2208        assert_eq!(SecurityHandlerRevision::R2, SecurityHandlerRevision::R2);
2209        assert_ne!(SecurityHandlerRevision::R2, SecurityHandlerRevision::R3);
2210    }
2211
2212    #[test]
2213    fn test_security_handler_revision_values() {
2214        assert_eq!(SecurityHandlerRevision::R2 as u8, 2);
2215        assert_eq!(SecurityHandlerRevision::R3 as u8, 3);
2216        assert_eq!(SecurityHandlerRevision::R4 as u8, 4);
2217        assert_eq!(SecurityHandlerRevision::R5 as u8, 5);
2218        assert_eq!(SecurityHandlerRevision::R6 as u8, 6);
2219    }
2220
2221    #[test]
2222    fn test_pad_password_various_lengths() {
2223        for len in 0..=40 {
2224            let password = "x".repeat(len);
2225            let padded = StandardSecurityHandler::pad_password(&password);
2226            assert_eq!(padded.len(), 32);
2227
2228            if len <= 32 {
2229                assert_eq!(&padded[..len], password.as_bytes());
2230            } else {
2231                assert_eq!(&padded[..], &password.as_bytes()[..32]);
2232            }
2233        }
2234    }
2235
2236    #[test]
2237    fn test_pad_password_unicode() {
2238        let padded = StandardSecurityHandler::pad_password("café");
2239        assert_eq!(padded.len(), 32);
2240        // UTF-8 encoding of "café" is 5 bytes
2241        assert_eq!(&padded[..5], "café".as_bytes());
2242    }
2243
2244    #[test]
2245    fn test_compute_owner_hash_different_users() {
2246        let handler = StandardSecurityHandler::rc4_128bit();
2247        let owner = OwnerPassword("owner".to_string());
2248        let user1 = UserPassword("user1".to_string());
2249        let user2 = UserPassword("user2".to_string());
2250
2251        let hash1 = handler.compute_owner_hash(&owner, &user1);
2252        let hash2 = handler.compute_owner_hash(&owner, &user2);
2253
2254        assert_ne!(hash1, hash2); // Different user passwords should produce different hashes
2255    }
2256
2257    #[test]
2258    fn test_compute_user_hash_r4() {
2259        let handler = StandardSecurityHandler {
2260            revision: SecurityHandlerRevision::R4,
2261            key_length: 16,
2262        };
2263        let user = UserPassword("r4test".to_string());
2264        let owner_hash = vec![0xAA; 32];
2265        let permissions = Permissions::new();
2266
2267        let hash = handler
2268            .compute_user_hash(&user, &owner_hash, permissions, None)
2269            .unwrap();
2270        assert_eq!(hash.len(), 32);
2271    }
2272
2273    #[test]
2274    fn test_compute_user_hash_r6() {
2275        let handler = StandardSecurityHandler::aes_256_r6();
2276        let user = UserPassword("r6test".to_string());
2277        let owner_hash = vec![0xBB; 32];
2278        let permissions = Permissions::all();
2279
2280        let hash = handler
2281            .compute_user_hash(&user, &owner_hash, permissions, None)
2282            .unwrap();
2283        assert_eq!(hash.len(), 32);
2284    }
2285
2286    #[test]
2287    fn test_encryption_key_with_file_id_affects_result() {
2288        let handler = StandardSecurityHandler::rc4_128bit();
2289        let user = UserPassword("test".to_string());
2290        let owner_hash = vec![0xFF; 32];
2291        let permissions = Permissions::new();
2292        let file_id = b"unique_file_id_12345";
2293
2294        let key_with_id = handler
2295            .compute_encryption_key(&user, &owner_hash, permissions, Some(file_id))
2296            .unwrap();
2297        let key_without_id = handler
2298            .compute_encryption_key(&user, &owner_hash, permissions, None)
2299            .unwrap();
2300
2301        assert_ne!(key_with_id.key, key_without_id.key);
2302    }
2303
2304    #[test]
2305    fn test_encrypt_string_empty() {
2306        let handler = StandardSecurityHandler::rc4_40bit();
2307        let key = EncryptionKey::new(vec![0x01, 0x02, 0x03, 0x04, 0x05]);
2308        let obj_id = ObjectId::new(1, 0);
2309
2310        let encrypted = handler.encrypt_string(b"", &key, &obj_id);
2311        assert_eq!(encrypted.len(), 0);
2312    }
2313
2314    #[test]
2315    fn test_encrypt_decrypt_large_data() {
2316        let handler = StandardSecurityHandler::rc4_128bit();
2317        let key = EncryptionKey::new(vec![0xAA; 16]);
2318        let obj_id = ObjectId::new(42, 0);
2319        let large_data = vec![0x55; 10000]; // 10KB
2320
2321        let encrypted = handler.encrypt_string(&large_data, &key, &obj_id);
2322        assert_eq!(encrypted.len(), large_data.len());
2323        assert_ne!(encrypted, large_data);
2324
2325        let decrypted = handler.decrypt_string(&encrypted, &key, &obj_id);
2326        assert_eq!(decrypted, large_data);
2327    }
2328
2329    #[test]
2330    fn test_stream_encryption_different_from_string() {
2331        // For current implementation they're the same, but test separately
2332        let handler = StandardSecurityHandler::rc4_128bit();
2333        let key = EncryptionKey::new(vec![0x11; 16]);
2334        let obj_id = ObjectId::new(5, 1);
2335        let data = b"Stream content test";
2336
2337        let encrypted_string = handler.encrypt_string(data, &key, &obj_id);
2338        let encrypted_stream = handler.encrypt_stream(data, &key, &obj_id);
2339
2340        assert_eq!(encrypted_string, encrypted_stream); // Currently same implementation
2341    }
2342
2343    #[test]
2344    fn test_aes_encryption_with_different_object_ids() {
2345        let handler = StandardSecurityHandler::aes_256_r5();
2346        let key = EncryptionKey::new(vec![0x77; 32]);
2347        let obj_id1 = ObjectId::new(10, 0);
2348        let obj_id2 = ObjectId::new(11, 0);
2349        let data = b"AES test data";
2350
2351        let encrypted1 = handler.encrypt_aes(data, &key, &obj_id1).unwrap();
2352        let encrypted2 = handler.encrypt_aes(data, &key, &obj_id2).unwrap();
2353
2354        // Different object IDs should produce different ciphertexts
2355        assert_ne!(encrypted1, encrypted2);
2356    }
2357
2358    #[test]
2359    fn test_aes_decrypt_invalid_iv_length() {
2360        let handler = StandardSecurityHandler::aes_256_r5();
2361        let key = EncryptionKey::new(vec![0x88; 32]);
2362        let obj_id = ObjectId::new(1, 0);
2363
2364        // Data too short to contain IV
2365        let short_data = vec![0u8; 10];
2366        assert!(handler.decrypt_aes(&short_data, &key, &obj_id).is_err());
2367
2368        // Exactly 16 bytes (only IV, no encrypted data)
2369        let iv_only = vec![0u8; 16];
2370        let result = handler.decrypt_aes(&iv_only, &key, &obj_id);
2371        // This might succeed with empty decrypted data or fail depending on implementation
2372        if let Ok(decrypted) = result {
2373            assert_eq!(decrypted.len(), 0);
2374        }
2375    }
2376
2377    #[test]
2378    fn test_aes_validate_password_wrong_hash_length() {
2379        let handler = StandardSecurityHandler::aes_256_r5();
2380        let password = UserPassword("test".to_string());
2381        let short_hash = vec![0u8; 16]; // Too short
2382        let permissions = Permissions::new();
2383
2384        let result = handler
2385            .validate_aes_user_password(&password, &short_hash, permissions, None)
2386            .unwrap();
2387        assert!(!result); // Should return false for invalid hash
2388    }
2389
2390    #[test]
2391    fn test_permissions_affect_encryption_key() {
2392        let handler = StandardSecurityHandler::rc4_128bit();
2393        let user = UserPassword("same_user".to_string());
2394        let owner_hash = vec![0xCC; 32];
2395
2396        let perms1 = Permissions::new();
2397        let perms2 = Permissions::all();
2398
2399        let key1 = handler
2400            .compute_encryption_key(&user, &owner_hash, perms1, None)
2401            .unwrap();
2402        let key2 = handler
2403            .compute_encryption_key(&user, &owner_hash, perms2, None)
2404            .unwrap();
2405
2406        assert_ne!(key1.key, key2.key); // Different permissions should affect the key
2407    }
2408
2409    #[test]
2410    fn test_different_handlers_produce_different_keys() {
2411        let user = UserPassword("test".to_string());
2412        let owner_hash = vec![0xDD; 32];
2413        let permissions = Permissions::new();
2414
2415        let handler_r2 = StandardSecurityHandler::rc4_40bit();
2416        let handler_r3 = StandardSecurityHandler::rc4_128bit();
2417
2418        let key_r2 = handler_r2
2419            .compute_encryption_key(&user, &owner_hash, permissions, None)
2420            .unwrap();
2421        let key_r3 = handler_r3
2422            .compute_encryption_key(&user, &owner_hash, permissions, None)
2423            .unwrap();
2424
2425        assert_ne!(key_r2.len(), key_r3.len()); // Different key lengths
2426        assert_eq!(key_r2.len(), 5);
2427        assert_eq!(key_r3.len(), 16);
2428    }
2429
2430    #[test]
2431    fn test_full_workflow_aes_r6() {
2432        let handler = StandardSecurityHandler::aes_256_r6();
2433        let user_pwd = UserPassword("user_r6".to_string());
2434        let permissions = Permissions::new();
2435        let file_id = b"test_file_r6";
2436
2437        // For AES R5/R6, owner hash computation is different - use a dummy hash
2438        let owner_hash = vec![0x42; 32]; // AES uses 32-byte hashes
2439
2440        // Compute user hash
2441        let user_hash = handler
2442            .compute_user_hash(&user_pwd, &owner_hash, permissions, Some(file_id))
2443            .unwrap();
2444        assert_eq!(user_hash.len(), 32);
2445
2446        // Compute encryption key
2447        let key = handler
2448            .compute_aes_encryption_key(&user_pwd, &owner_hash, permissions, Some(file_id))
2449            .unwrap();
2450        assert_eq!(key.len(), 32);
2451
2452        // Test string encryption (uses AES for R6)
2453        let obj_id = ObjectId::new(100, 5);
2454        let content = b"R6 AES encryption test";
2455        let encrypted = handler.encrypt_string(content, &key, &obj_id);
2456
2457        // With AES, encrypted should be empty on error or have data
2458        if !encrypted.is_empty() {
2459            assert_ne!(encrypted.as_slice(), content);
2460        }
2461    }
2462
2463    #[test]
2464    fn test_md5_compute_consistency() {
2465        let data = b"consistent data for md5";
2466        let hash1 = md5::compute(data);
2467        let hash2 = md5::compute(data);
2468
2469        assert_eq!(hash1, hash2);
2470        assert_eq!(hash1.len(), 16);
2471    }
2472
2473    #[test]
2474    fn test_sha256_consistency() {
2475        let data = b"consistent data for sha256";
2476        let hash1 = sha256(data);
2477        let hash2 = sha256(data);
2478
2479        assert_eq!(hash1, hash2);
2480        assert_eq!(hash1.len(), 32);
2481    }
2482
2483    #[test]
2484    fn test_rc4_encrypt_helper() {
2485        let key = Rc4Key::from_slice(&[0x01, 0x02, 0x03, 0x04, 0x05]);
2486        let data = b"test rc4 helper";
2487
2488        let encrypted = rc4_encrypt(&key, data);
2489        assert_ne!(encrypted.as_slice(), data);
2490
2491        // RC4 is symmetric
2492        let decrypted = rc4_encrypt(&key, &encrypted);
2493        assert_eq!(decrypted.as_slice(), data);
2494    }
2495
2496    #[test]
2497    fn test_edge_case_max_object_generation() {
2498        let handler = StandardSecurityHandler::rc4_128bit();
2499        let key = EncryptionKey::new(vec![0xEE; 16]);
2500        let obj_id = ObjectId::new(0xFFFFFF, 0xFFFF); // Max values
2501        let data = b"edge case";
2502
2503        let encrypted = handler.encrypt_string(data, &key, &obj_id);
2504        let decrypted = handler.decrypt_string(&encrypted, &key, &obj_id);
2505        assert_eq!(decrypted.as_slice(), data);
2506    }
2507
2508    // Issue #364: a failed AES decryption must surface as an error, not be
2509    // swallowed into empty content (silent data loss). The `try_*` variants
2510    // propagate the error; the legacy `Vec`-returning ones keep the lenient
2511    // behaviour for backward compatibility.
2512    #[test]
2513    fn test_try_decrypt_stream_surfaces_aes_error() {
2514        let handler = StandardSecurityHandler::aes_128_r4();
2515        let key = EncryptionKey::new(vec![0x11; 16]);
2516        let obj_id = ObjectId::new(1, 0);
2517
2518        // Too short to even contain the 16-byte AES IV → must error.
2519        let undecryptable = [0u8; 8];
2520
2521        let err = handler.try_decrypt_stream(&undecryptable, &key, &obj_id);
2522        assert!(
2523            err.is_err(),
2524            "try_decrypt_stream must return Err on undecryptable AES data, got {err:?}"
2525        );
2526
2527        // The legacy lenient API still swallows into empty Vec (documents the
2528        // behaviour the parser path no longer relies on).
2529        let lenient = handler.decrypt_stream(&undecryptable, &key, &obj_id);
2530        assert!(lenient.is_empty());
2531    }
2532
2533    #[test]
2534    fn test_try_decrypt_string_surfaces_aes_error() {
2535        let handler = StandardSecurityHandler::aes_128_r4();
2536        let key = EncryptionKey::new(vec![0x22; 16]);
2537        let obj_id = ObjectId::new(2, 0);
2538
2539        let undecryptable = [0u8; 4];
2540        assert!(
2541            handler
2542                .try_decrypt_string(&undecryptable, &key, &obj_id)
2543                .is_err(),
2544            "try_decrypt_string must return Err on undecryptable AES data"
2545        );
2546    }
2547
2548    // ===== SHA-256/512 NIST Vector Tests (Phase 1.3 - RustCrypto Integration) =====
2549
2550    #[test]
2551    fn test_sha256_nist_empty_string() {
2552        // NIST FIPS 180-4 test vector: SHA-256("")
2553        let hash = sha256(b"");
2554        let expected: [u8; 32] = [
2555            0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f,
2556            0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b,
2557            0x78, 0x52, 0xb8, 0x55,
2558        ];
2559        assert_eq!(
2560            hash.as_slice(),
2561            expected.as_slice(),
2562            "SHA-256('') must match NIST test vector"
2563        );
2564    }
2565
2566    #[test]
2567    fn test_sha256_nist_abc() {
2568        // NIST FIPS 180-4 test vector: SHA-256("abc")
2569        let hash = sha256(b"abc");
2570        let expected: [u8; 32] = [
2571            0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae,
2572            0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61,
2573            0xf2, 0x00, 0x15, 0xad,
2574        ];
2575        assert_eq!(
2576            hash.as_slice(),
2577            expected.as_slice(),
2578            "SHA-256('abc') must match NIST test vector"
2579        );
2580    }
2581
2582    #[test]
2583    fn test_sha512_nist_abc() {
2584        // NIST FIPS 180-4 test vector: SHA-512("abc")
2585        let hash = sha512(b"abc");
2586        let expected: [u8; 64] = [
2587            0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20,
2588            0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, 0xe6,
2589            0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba,
2590            0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
2591            0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f,
2592        ];
2593        assert_eq!(
2594            hash.as_slice(),
2595            expected.as_slice(),
2596            "SHA-512('abc') must match NIST test vector"
2597        );
2598    }
2599
2600    #[test]
2601    fn test_sha512_length() {
2602        let hash = sha512(b"test data");
2603        assert_eq!(hash.len(), 64, "SHA-512 must produce 64 bytes");
2604    }
2605
2606    #[test]
2607    fn test_sha512_deterministic() {
2608        let data1 = b"sha512 test data";
2609        let data2 = b"sha512 test data";
2610        let data3 = b"different data";
2611
2612        let hash1 = sha512(data1);
2613        let hash2 = sha512(data2);
2614        let hash3 = sha512(data3);
2615
2616        assert_eq!(hash1, hash2, "Same input must produce same SHA-512 hash");
2617        assert_ne!(hash1, hash3, "Different input must produce different hash");
2618    }
2619
2620    // ===== Phase 2.1: R5 User Password Tests (Algorithm 8 & 11) =====
2621
2622    #[test]
2623    fn test_r5_user_hash_computation() {
2624        let handler = StandardSecurityHandler::aes_256_r5();
2625        let password = UserPassword("test_password".to_string());
2626
2627        let u_entry = handler.compute_r5_user_hash(&password).unwrap();
2628
2629        // U entry must be exactly 48 bytes: hash(32) + validation_salt(8) + key_salt(8)
2630        assert_eq!(u_entry.len(), 48, "R5 U entry must be 48 bytes");
2631    }
2632
2633    #[test]
2634    fn test_r5_user_password_validation_correct() {
2635        let handler = StandardSecurityHandler::aes_256_r5();
2636        let password = UserPassword("correct_password".to_string());
2637
2638        // Compute U entry with the password
2639        let u_entry = handler.compute_r5_user_hash(&password).unwrap();
2640
2641        // Validate with same password should succeed
2642        let is_valid = handler
2643            .validate_r5_user_password(&password, &u_entry)
2644            .unwrap();
2645        assert!(is_valid, "Correct password must validate");
2646    }
2647
2648    #[test]
2649    fn test_r5_user_password_validation_incorrect() {
2650        let handler = StandardSecurityHandler::aes_256_r5();
2651        let correct_password = UserPassword("correct_password".to_string());
2652        let wrong_password = UserPassword("wrong_password".to_string());
2653
2654        // Compute U entry with correct password
2655        let u_entry = handler.compute_r5_user_hash(&correct_password).unwrap();
2656
2657        // Validate with wrong password should fail
2658        let is_valid = handler
2659            .validate_r5_user_password(&wrong_password, &u_entry)
2660            .unwrap();
2661        assert!(!is_valid, "Wrong password must not validate");
2662    }
2663
2664    #[test]
2665    fn test_r5_user_hash_random_salts() {
2666        let handler = StandardSecurityHandler::aes_256_r5();
2667        let password = UserPassword("same_password".to_string());
2668
2669        // Compute U entry twice - salts should be different
2670        let u_entry1 = handler.compute_r5_user_hash(&password).unwrap();
2671        let u_entry2 = handler.compute_r5_user_hash(&password).unwrap();
2672
2673        // Hash portion should be different (due to random salts)
2674        assert_ne!(
2675            &u_entry1[..32],
2676            &u_entry2[..32],
2677            "Different random salts should produce different hashes"
2678        );
2679
2680        // Validation salt should be different
2681        assert_ne!(
2682            &u_entry1[32..40],
2683            &u_entry2[32..40],
2684            "Validation salts must be random"
2685        );
2686
2687        // But both should validate with the same password
2688        assert!(handler
2689            .validate_r5_user_password(&password, &u_entry1)
2690            .unwrap());
2691        assert!(handler
2692            .validate_r5_user_password(&password, &u_entry2)
2693            .unwrap());
2694    }
2695
2696    #[test]
2697    fn test_r5_user_hash_entry_shorter_than_the_salts_is_rejected() {
2698        let handler = StandardSecurityHandler::aes_256_r5();
2699        let password = UserPassword("test".to_string());
2700
2701        // 32 bytes hold the hash but neither salt.
2702        let short_entry = vec![0u8; 32];
2703        let result = handler.validate_r5_user_password(&password, &short_entry);
2704        assert!(result.is_err(), "Short U entry must fail");
2705
2706        // Longer than 48 is what Acrobat writes (127 bytes, zero-padded): the
2707        // entry is evaluated on its defined prefix rather than rejected on its
2708        // length (issue #459). An all-zero entry still fails to validate.
2709        let long_entry = vec![0u8; 64];
2710        assert!(
2711            !handler
2712                .validate_r5_user_password(&password, &long_entry)
2713                .expect("a longer entry is read, not refused"),
2714            "an all-zero entry must not authenticate any password"
2715        );
2716    }
2717
2718    #[test]
2719    fn test_r5_empty_password() {
2720        let handler = StandardSecurityHandler::aes_256_r5();
2721        let empty_password = UserPassword("".to_string());
2722
2723        // Empty password should work (common for user-only encryption)
2724        let u_entry = handler.compute_r5_user_hash(&empty_password).unwrap();
2725        assert_eq!(u_entry.len(), 48);
2726
2727        let is_valid = handler
2728            .validate_r5_user_password(&empty_password, &u_entry)
2729            .unwrap();
2730        assert!(is_valid, "Empty password must validate correctly");
2731
2732        // Non-empty password should fail
2733        let non_empty = UserPassword("not_empty".to_string());
2734        let is_valid = handler
2735            .validate_r5_user_password(&non_empty, &u_entry)
2736            .unwrap();
2737        assert!(!is_valid, "Non-empty password must not validate");
2738    }
2739
2740    // ===== Phase 2.2: R5 UE Entry Tests (Encryption Key Storage) =====
2741
2742    #[test]
2743    fn test_r5_ue_entry_computation() {
2744        let handler = StandardSecurityHandler::aes_256_r5();
2745        let password = UserPassword("ue_test_password".to_string());
2746        let encryption_key = EncryptionKey::new(vec![0xAB; 32]);
2747
2748        // Compute U entry first
2749        let u_entry = handler.compute_r5_user_hash(&password).unwrap();
2750
2751        // Compute UE entry
2752        let ue_entry = handler
2753            .compute_r5_ue_entry(&password, &u_entry, &encryption_key)
2754            .unwrap();
2755
2756        // UE entry must be exactly 32 bytes
2757        assert_eq!(ue_entry.len(), 32, "R5 UE entry must be 32 bytes");
2758
2759        // UE should be different from the original key (it's encrypted)
2760        assert_ne!(
2761            ue_entry.as_slice(),
2762            encryption_key.as_bytes(),
2763            "UE must be encrypted"
2764        );
2765    }
2766
2767    #[test]
2768    fn test_r5_encryption_key_recovery() {
2769        let handler = StandardSecurityHandler::aes_256_r5();
2770        let password = UserPassword("recovery_test".to_string());
2771        let original_key = EncryptionKey::new(vec![0x42; 32]);
2772
2773        // Compute U entry
2774        let u_entry = handler.compute_r5_user_hash(&password).unwrap();
2775
2776        // Compute UE entry
2777        let ue_entry = handler
2778            .compute_r5_ue_entry(&password, &u_entry, &original_key)
2779            .unwrap();
2780
2781        // Recover the key
2782        let recovered_key = handler
2783            .recover_r5_encryption_key(&password, &u_entry, &ue_entry)
2784            .unwrap();
2785
2786        // Recovered key must match original
2787        assert_eq!(
2788            recovered_key.as_bytes(),
2789            original_key.as_bytes(),
2790            "Recovered key must match original"
2791        );
2792    }
2793
2794    #[test]
2795    fn test_r5_ue_wrong_password_fails() {
2796        let handler = StandardSecurityHandler::aes_256_r5();
2797        let correct_password = UserPassword("correct".to_string());
2798        let wrong_password = UserPassword("wrong".to_string());
2799        let original_key = EncryptionKey::new(vec![0x99; 32]);
2800
2801        // Compute U and UE with correct password
2802        let u_entry = handler.compute_r5_user_hash(&correct_password).unwrap();
2803        let ue_entry = handler
2804            .compute_r5_ue_entry(&correct_password, &u_entry, &original_key)
2805            .unwrap();
2806
2807        // Try to recover with wrong password
2808        let recovered_key = handler
2809            .recover_r5_encryption_key(&wrong_password, &u_entry, &ue_entry)
2810            .unwrap();
2811
2812        // Key should be different (wrong decryption)
2813        assert_ne!(
2814            recovered_key.as_bytes(),
2815            original_key.as_bytes(),
2816            "Wrong password must produce wrong key"
2817        );
2818    }
2819
2820    #[test]
2821    fn test_r5_ue_invalid_length() {
2822        let handler = StandardSecurityHandler::aes_256_r5();
2823        let password = UserPassword("test".to_string());
2824        let u_entry = vec![0u8; 48]; // Valid U entry length
2825
2826        // Try to recover with wrong length UE entry
2827        let short_ue = vec![0u8; 16]; // Too short
2828        let result = handler.recover_r5_encryption_key(&password, &u_entry, &short_ue);
2829        assert!(result.is_err(), "Short UE entry must fail");
2830
2831        let long_ue = vec![0u8; 64]; // Too long
2832        let result = handler.recover_r5_encryption_key(&password, &u_entry, &long_ue);
2833        assert!(result.is_err(), "Long UE entry must fail");
2834    }
2835
2836    #[test]
2837    fn test_r5_ue_invalid_u_length() {
2838        let handler = StandardSecurityHandler::aes_256_r5();
2839        let password = UserPassword("test".to_string());
2840        let encryption_key = EncryptionKey::new(vec![0x11; 32]);
2841
2842        // Try to compute UE with wrong length U entry
2843        let short_u = vec![0u8; 32]; // Too short
2844        let result = handler.compute_r5_ue_entry(&password, &short_u, &encryption_key);
2845        assert!(
2846            result.is_err(),
2847            "Short U entry must fail for UE computation"
2848        );
2849    }
2850
2851    #[test]
2852    fn test_r5_full_workflow_u_ue() {
2853        let handler = StandardSecurityHandler::aes_256_r5();
2854        let password = UserPassword("full_workflow_test".to_string());
2855        let encryption_key = EncryptionKey::new((0..32).collect::<Vec<u8>>());
2856
2857        // Step 1: Compute U entry (password verification data)
2858        let u_entry = handler.compute_r5_user_hash(&password).unwrap();
2859        assert_eq!(u_entry.len(), 48);
2860
2861        // Step 2: Verify password validates
2862        assert!(handler
2863            .validate_r5_user_password(&password, &u_entry)
2864            .unwrap());
2865
2866        // Step 3: Compute UE entry (encrypted key storage)
2867        let ue_entry = handler
2868            .compute_r5_ue_entry(&password, &u_entry, &encryption_key)
2869            .unwrap();
2870        assert_eq!(ue_entry.len(), 32);
2871
2872        // Step 4: Recover key from UE
2873        let recovered = handler
2874            .recover_r5_encryption_key(&password, &u_entry, &ue_entry)
2875            .unwrap();
2876
2877        // Step 5: Verify recovered key matches original
2878        assert_eq!(
2879            recovered.as_bytes(),
2880            encryption_key.as_bytes(),
2881            "Full R5 workflow: recovered key must match original"
2882        );
2883    }
2884
2885    // ===== Phase 3.1: R6 User Password Tests (SHA-512 based) =====
2886
2887    #[test]
2888    fn test_r6_user_hash_computation() {
2889        let handler = StandardSecurityHandler::aes_256_r6();
2890        let password = UserPassword("r6_test_password".to_string());
2891
2892        let u_entry = handler.compute_r6_user_hash(&password).unwrap();
2893
2894        // U entry must be exactly 48 bytes: hash(32) + validation_salt(8) + key_salt(8)
2895        assert_eq!(u_entry.len(), 48, "R6 U entry must be 48 bytes");
2896    }
2897
2898    #[test]
2899    fn test_r6_user_password_validation_correct() {
2900        let handler = StandardSecurityHandler::aes_256_r6();
2901        let password = UserPassword("r6_correct_password".to_string());
2902
2903        // Compute U entry with the password
2904        let u_entry = handler.compute_r6_user_hash(&password).unwrap();
2905
2906        // Validate with same password should succeed
2907        let is_valid = handler
2908            .validate_r6_user_password(&password, &u_entry)
2909            .unwrap();
2910        assert!(is_valid, "Correct R6 password must validate");
2911    }
2912
2913    #[test]
2914    fn test_r6_user_password_validation_incorrect() {
2915        let handler = StandardSecurityHandler::aes_256_r6();
2916        let correct_password = UserPassword("r6_correct".to_string());
2917        let wrong_password = UserPassword("r6_wrong".to_string());
2918
2919        // Compute U entry with correct password
2920        let u_entry = handler.compute_r6_user_hash(&correct_password).unwrap();
2921
2922        // Validate with wrong password should fail
2923        let is_valid = handler
2924            .validate_r6_user_password(&wrong_password, &u_entry)
2925            .unwrap();
2926        assert!(!is_valid, "Wrong R6 password must not validate");
2927    }
2928
2929    #[test]
2930    fn test_r6_uses_sha512_not_sha256() {
2931        // Verify R6 produces different hash than R5 for same password
2932        let handler_r5 = StandardSecurityHandler::aes_256_r5();
2933        let handler_r6 = StandardSecurityHandler::aes_256_r6();
2934        let password = UserPassword("same_password_both_revisions".to_string());
2935
2936        let u_r5 = handler_r5.compute_r5_user_hash(&password).unwrap();
2937        let u_r6 = handler_r6.compute_r6_user_hash(&password).unwrap();
2938
2939        // Hash portions (first 32 bytes) should be different
2940        // Note: Salts are random, but even with same salt the hash algorithm differs
2941        assert_ne!(
2942            &u_r5[..32],
2943            &u_r6[..32],
2944            "R5 (SHA-256) and R6 (SHA-512) must produce different hashes"
2945        );
2946    }
2947
2948    #[test]
2949    fn test_r6_unicode_password() {
2950        let handler = StandardSecurityHandler::aes_256_r6();
2951        let unicode_password = UserPassword("café🔒日本語".to_string());
2952
2953        let u_entry = handler.compute_r6_user_hash(&unicode_password).unwrap();
2954        assert_eq!(u_entry.len(), 48);
2955
2956        // Validate with same Unicode password
2957        let is_valid = handler
2958            .validate_r6_user_password(&unicode_password, &u_entry)
2959            .unwrap();
2960        assert!(is_valid, "Unicode password must validate");
2961
2962        // Different Unicode password should fail
2963        let different_unicode = UserPassword("café🔓日本語".to_string()); // Different emoji
2964        let is_valid = handler
2965            .validate_r6_user_password(&different_unicode, &u_entry)
2966            .unwrap();
2967        assert!(!is_valid, "Different Unicode password must not validate");
2968    }
2969
2970    // ===== Phase 3.1: R6 UE Entry Tests =====
2971
2972    #[test]
2973    fn test_r6_ue_entry_computation() {
2974        let handler = StandardSecurityHandler::aes_256_r6();
2975        let password = UserPassword("r6_ue_test".to_string());
2976        let encryption_key = EncryptionKey::new(vec![0xCD; 32]);
2977
2978        let u_entry = handler.compute_r6_user_hash(&password).unwrap();
2979        let ue_entry = handler
2980            .compute_r6_ue_entry(&password, &u_entry, &encryption_key)
2981            .unwrap();
2982
2983        assert_eq!(ue_entry.len(), 32, "R6 UE entry must be 32 bytes");
2984    }
2985
2986    #[test]
2987    fn test_r6_encryption_key_recovery() {
2988        let handler = StandardSecurityHandler::aes_256_r6();
2989        let password = UserPassword("r6_recovery_test".to_string());
2990        let original_key = EncryptionKey::new(vec![0xEF; 32]);
2991
2992        let u_entry = handler.compute_r6_user_hash(&password).unwrap();
2993        let ue_entry = handler
2994            .compute_r6_ue_entry(&password, &u_entry, &original_key)
2995            .unwrap();
2996
2997        let recovered_key = handler
2998            .recover_r6_encryption_key(&password, &u_entry, &ue_entry)
2999            .unwrap();
3000
3001        assert_eq!(
3002            recovered_key.as_bytes(),
3003            original_key.as_bytes(),
3004            "R6: Recovered key must match original"
3005        );
3006    }
3007
3008    // ===== Phase 3.2: R6 Perms Entry Tests =====
3009
3010    #[test]
3011    fn test_r6_perms_entry_computation() {
3012        let handler = StandardSecurityHandler::aes_256_r6();
3013        let permissions = Permissions::all();
3014        let key = EncryptionKey::new(vec![0x42; 32]);
3015
3016        let perms = handler
3017            .compute_perms_entry(permissions, &key, true)
3018            .unwrap();
3019
3020        assert_eq!(perms.len(), 16, "Perms entry must be 16 bytes");
3021    }
3022
3023    #[test]
3024    fn test_r6_perms_validation() {
3025        let handler = StandardSecurityHandler::aes_256_r6();
3026        let permissions = Permissions::new();
3027        let key = EncryptionKey::new(vec![0x55; 32]);
3028
3029        let perms = handler
3030            .compute_perms_entry(permissions, &key, false)
3031            .unwrap();
3032
3033        let is_valid = handler
3034            .validate_r6_perms(&perms, &key, permissions)
3035            .unwrap();
3036        assert!(is_valid, "Perms validation must succeed with correct key");
3037    }
3038
3039    #[test]
3040    fn test_r6_perms_wrong_key_fails() {
3041        let handler = StandardSecurityHandler::aes_256_r6();
3042        let permissions = Permissions::all();
3043        let correct_key = EncryptionKey::new(vec![0xAA; 32]);
3044        let wrong_key = EncryptionKey::new(vec![0xBB; 32]);
3045
3046        let perms = handler
3047            .compute_perms_entry(permissions, &correct_key, true)
3048            .unwrap();
3049
3050        // Validation with wrong key should fail (structure won't match)
3051        let result = handler.validate_r6_perms(&perms, &wrong_key, permissions);
3052        assert!(result.is_ok()); // No error
3053        assert!(!result.unwrap()); // But validation fails
3054    }
3055
3056    #[test]
3057    fn test_r6_perms_encrypt_metadata_flag() {
3058        let handler = StandardSecurityHandler::aes_256_r6();
3059        let permissions = Permissions::new();
3060        let key = EncryptionKey::new(vec![0x33; 32]);
3061
3062        let perms_true = handler
3063            .compute_perms_entry(permissions, &key, true)
3064            .unwrap();
3065        let perms_false = handler
3066            .compute_perms_entry(permissions, &key, false)
3067            .unwrap();
3068
3069        // Different encrypt_metadata flag should produce different Perms
3070        assert_ne!(
3071            perms_true, perms_false,
3072            "Different EncryptMetadata must produce different Perms"
3073        );
3074
3075        // Extract and verify flags
3076        let flag_true = handler
3077            .extract_r6_encrypt_metadata(&perms_true, &key)
3078            .unwrap();
3079        assert_eq!(flag_true, Some(true));
3080
3081        let flag_false = handler
3082            .extract_r6_encrypt_metadata(&perms_false, &key)
3083            .unwrap();
3084        assert_eq!(flag_false, Some(false));
3085    }
3086
3087    #[test]
3088    fn test_r6_perms_invalid_length() {
3089        let handler = StandardSecurityHandler::aes_256_r6();
3090        let key = EncryptionKey::new(vec![0x44; 32]);
3091        let permissions = Permissions::new();
3092
3093        let invalid_perms = vec![0u8; 12]; // Too short
3094        let result = handler.validate_r6_perms(&invalid_perms, &key, permissions);
3095        assert!(result.is_err(), "Short Perms entry must fail");
3096    }
3097
3098    #[test]
3099    fn test_r6_full_workflow_with_perms() {
3100        // Complete R6 integration test: U + UE + Perms
3101        let handler = StandardSecurityHandler::aes_256_r6();
3102        let password = UserPassword("r6_full_workflow".to_string());
3103        let permissions = Permissions::all();
3104        let encryption_key = EncryptionKey::new((0..32).map(|i| (i * 3) as u8).collect());
3105
3106        // Step 1: Compute U entry (password verification)
3107        let u_entry = handler.compute_r6_user_hash(&password).unwrap();
3108        assert_eq!(u_entry.len(), 48);
3109
3110        // Step 2: Validate password
3111        assert!(handler
3112            .validate_r6_user_password(&password, &u_entry)
3113            .unwrap());
3114
3115        // Step 3: Compute UE entry (encrypted key)
3116        let ue_entry = handler
3117            .compute_r6_ue_entry(&password, &u_entry, &encryption_key)
3118            .unwrap();
3119        assert_eq!(ue_entry.len(), 32);
3120
3121        // Step 4: Compute Perms entry (encrypted permissions)
3122        let perms = handler
3123            .compute_perms_entry(permissions, &encryption_key, true)
3124            .unwrap();
3125        assert_eq!(perms.len(), 16);
3126
3127        // Step 5: Recover encryption key from UE
3128        let recovered_key = handler
3129            .recover_r6_encryption_key(&password, &u_entry, &ue_entry)
3130            .unwrap();
3131        assert_eq!(
3132            recovered_key.as_bytes(),
3133            encryption_key.as_bytes(),
3134            "Recovered key must match original"
3135        );
3136
3137        // Step 6: Validate Perms with recovered key
3138        let perms_valid = handler
3139            .validate_r6_perms(&perms, &recovered_key, permissions)
3140            .unwrap();
3141        assert!(perms_valid, "Perms must validate with recovered key");
3142
3143        // Step 7: Extract EncryptMetadata flag
3144        let encrypt_meta = handler
3145            .extract_r6_encrypt_metadata(&perms, &recovered_key)
3146            .unwrap();
3147        assert_eq!(encrypt_meta, Some(true), "EncryptMetadata must be true");
3148    }
3149
3150    // ===== AES-128 R4 Tests =====
3151
3152    #[test]
3153    fn test_r4_aes_object_key_is_16_bytes() {
3154        let handler = StandardSecurityHandler::aes_128_r4();
3155        let key = EncryptionKey::new(vec![0xAB; 16]);
3156        let obj_id = ObjectId::new(7, 0);
3157
3158        let obj_key = handler.compute_r4_aes_object_key(&key, &obj_id);
3159        assert_eq!(obj_key.len(), 16);
3160    }
3161
3162    #[test]
3163    fn test_r4_aes_object_key_includes_salt() {
3164        // R4 AES key differs from RC4 key because of "sAlT" suffix
3165        let handler_r4 = StandardSecurityHandler::aes_128_r4();
3166        let handler_rc4 = StandardSecurityHandler::rc4_128bit();
3167        let key = EncryptionKey::new(vec![0xCD; 16]);
3168        let obj_id = ObjectId::new(3, 0);
3169
3170        let aes_key = handler_r4.compute_r4_aes_object_key(&key, &obj_id);
3171        let rc4_key = handler_rc4.compute_object_key(&key, &obj_id);
3172
3173        assert_ne!(
3174            aes_key, rc4_key,
3175            "AES R4 key must differ from RC4 key due to sAlT"
3176        );
3177    }
3178
3179    #[test]
3180    fn test_r4_aes_object_key_deterministic() {
3181        let handler = StandardSecurityHandler::aes_128_r4();
3182        let key = EncryptionKey::new(vec![0x42; 16]);
3183        let obj_id = ObjectId::new(5, 2);
3184
3185        let key1 = handler.compute_r4_aes_object_key(&key, &obj_id);
3186        let key2 = handler.compute_r4_aes_object_key(&key, &obj_id);
3187        assert_eq!(key1, key2);
3188    }
3189
3190    #[test]
3191    fn test_r4_encrypt_decrypt_roundtrip() {
3192        let handler = StandardSecurityHandler::aes_128_r4();
3193        let key = EncryptionKey::new(vec![0x55; 16]);
3194        let obj_id = ObjectId::new(1, 0);
3195        let plaintext = b"Hello AES-128 R4 encryption!";
3196
3197        let encrypted = handler.encrypt_aes(plaintext, &key, &obj_id).unwrap();
3198        assert_ne!(&encrypted[16..], plaintext.as_slice()); // ciphertext != plaintext
3199        assert!(encrypted.len() > 16); // IV + ciphertext
3200
3201        let decrypted = handler.decrypt_aes(&encrypted, &key, &obj_id).unwrap();
3202        assert_eq!(decrypted, plaintext);
3203    }
3204
3205    #[test]
3206    fn test_r4_encrypt_output_has_iv_prefix() {
3207        let handler = StandardSecurityHandler::aes_128_r4();
3208        let key = EncryptionKey::new(vec![0x77; 16]);
3209        let obj_id = ObjectId::new(2, 0);
3210        let data = b"test";
3211
3212        let encrypted = handler.encrypt_aes(data, &key, &obj_id).unwrap();
3213        // Output = 16-byte IV + AES-CBC ciphertext (multiple of 16)
3214        assert!(encrypted.len() >= 32); // 16 IV + at least 16 ciphertext
3215        assert_eq!((encrypted.len() - 16) % 16, 0);
3216    }
3217
3218    #[test]
3219    fn test_r4_decrypt_rejects_short_data() {
3220        let handler = StandardSecurityHandler::aes_128_r4();
3221        let key = EncryptionKey::new(vec![0x99; 16]);
3222        let obj_id = ObjectId::new(1, 0);
3223
3224        let short = vec![0u8; 10];
3225        assert!(handler.decrypt_aes(&short, &key, &obj_id).is_err());
3226    }
3227
3228    #[test]
3229    fn test_r4_inherent_encrypt_string_uses_aes() {
3230        // Verify that the inherent encrypt_string routes R4 through AES, not RC4
3231        let handler = StandardSecurityHandler::aes_128_r4();
3232        let key = EncryptionKey::new(vec![0x33; 16]);
3233        let obj_id = ObjectId::new(1, 0);
3234        let data = b"R4 string encryption";
3235
3236        let encrypted = handler.encrypt_string(data, &key, &obj_id);
3237        // AES output = IV(16) + ciphertext(≥16), so always > input for small inputs
3238        assert!(encrypted.len() >= 32);
3239
3240        // Decrypt via inherent method must also work
3241        let decrypted = handler.decrypt_string(&encrypted, &key, &obj_id);
3242        assert_eq!(decrypted, data);
3243    }
3244
3245    #[test]
3246    fn test_r4_inherent_stream_uses_aes() {
3247        let handler = StandardSecurityHandler::aes_128_r4();
3248        let key = EncryptionKey::new(vec![0x44; 16]);
3249        let obj_id = ObjectId::new(3, 0);
3250        let data = b"R4 stream content";
3251
3252        let encrypted = handler.encrypt_stream(data, &key, &obj_id);
3253        assert!(encrypted.len() >= 32);
3254
3255        let decrypted = handler.decrypt_stream(&encrypted, &key, &obj_id);
3256        assert_eq!(decrypted, data);
3257    }
3258}