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