Skip to main content

oxidize_pdf/encryption/
standard_security.rs

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