1use std::collections::HashMap;
12
13use rpdfium_core::{Name, PdfSource};
14
15use crate::crypto::{self, CryptoError};
16use crate::object::{Object, ObjectId};
17use crate::store::ObjectStore;
18
19const PASSWORD_PADDING: [u8; 32] = [
21 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
22 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A,
23];
24
25#[derive(Debug, thiserror::Error)]
27pub enum SecurityError {
28 #[error("invalid password")]
30 InvalidPassword,
31 #[error("unsupported encryption version: V={0}, R={1}")]
33 UnsupportedVersion(u32, u32),
34 #[error("missing encryption dictionary key: {0}")]
36 MissingKey(String),
37 #[error("crypto error: {0}")]
39 Crypto(#[from] CryptoError),
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum CryptFilterMethod {
45 None,
47 V2,
49 Aesv2,
51 Aesv3,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub struct Permissions(i32);
62
63impl Permissions {
64 pub fn from_bits(bits: i32) -> Self {
66 Self(bits)
67 }
68
69 pub fn bits(self) -> i32 {
71 self.0
72 }
73
74 pub fn modify_content(self) -> bool {
76 self.0 & (1 << 3) != 0
77 }
78
79 pub fn modify_annotation(self) -> bool {
81 self.0 & (1 << 5) != 0
82 }
83
84 pub fn fill_form(self) -> bool {
86 self.0 & (1 << 8) != 0
87 }
88
89 pub fn extract_for_accessibility(self) -> bool {
91 self.0 & (1 << 9) != 0
92 }
93}
94
95pub struct SecurityHandler {
98 revision: u32,
99 key_length_bytes: usize,
100 encryption_key: Vec<u8>,
101 permissions: Permissions,
102 encrypt_metadata: bool,
103 stream_cf: CryptFilterMethod,
104 string_cf: CryptFilterMethod,
105 o_value: Vec<u8>,
107 u_value: Vec<u8>,
109 encoded_password: Vec<u8>,
112}
113
114impl SecurityHandler {
115 pub fn from_encrypt_dict<S: PdfSource>(
121 encrypt_dict: &HashMap<Name, Object>,
122 store: &ObjectStore<S>,
123 password: &str,
124 file_id: &[u8],
125 ) -> Result<Self, SecurityError> {
126 let v = get_int(encrypt_dict, store, &Name::v())? as u32;
128 let r = get_int(encrypt_dict, store, &Name::r())? as u32;
129 let p = get_int(encrypt_dict, store, &Name::p())?;
130
131 let o_bytes = get_string_bytes(encrypt_dict, store, &Name::o())?;
132 let u_bytes = get_string_bytes(encrypt_dict, store, &Name::u())?;
133
134 let key_length_bits =
136 get_optional_int(encrypt_dict, store, &Name::length()).unwrap_or(40) as usize;
137 let key_length_bytes = key_length_bits / 8;
138
139 if r <= 4 && !(5..=16).contains(&key_length_bytes) {
142 return Err(SecurityError::UnsupportedVersion(v, r));
143 }
144
145 let encrypt_metadata =
147 get_optional_bool(encrypt_dict, store, &Name::encrypt_metadata()).unwrap_or(true);
148
149 let (stream_cf, string_cf) = if v >= 4 {
151 let stm = parse_single_crypt_filter(encrypt_dict, store, &Name::stm_f());
152 let str_ = parse_single_crypt_filter(encrypt_dict, store, &Name::str_f());
153 (stm, str_)
154 } else if v == 1 || v == 2 || v == 3 {
155 (CryptFilterMethod::V2, CryptFilterMethod::V2)
156 } else {
157 (CryptFilterMethod::None, CryptFilterMethod::None)
158 };
159
160 match (v, r) {
161 (1, 2) | (2, 3) | (3, 3) | (4, 4) => {
162 let pwd_bytes = encode_password_latin1(password);
164 let key = derive_key_r2_r4(
165 &pwd_bytes,
166 &o_bytes,
167 p as i32,
168 file_id,
169 r,
170 key_length_bytes,
171 encrypt_metadata,
172 );
173
174 if verify_user_password_r2_r4(&key, &u_bytes, r, file_id) {
176 return Ok(Self {
177 revision: r,
178 key_length_bytes,
179 encryption_key: key,
180 permissions: Permissions::from_bits(p as i32),
181 encrypt_metadata,
182 stream_cf,
183 string_cf,
184 o_value: o_bytes.clone(),
185 u_value: u_bytes.clone(),
186 encoded_password: pwd_bytes,
187 });
188 }
189
190 let user_password =
192 recover_user_password_from_owner(&pwd_bytes, &o_bytes, r, key_length_bytes);
193 let key = derive_key_r2_r4(
194 &user_password,
195 &o_bytes,
196 p as i32,
197 file_id,
198 r,
199 key_length_bytes,
200 encrypt_metadata,
201 );
202
203 if verify_user_password_r2_r4(&key, &u_bytes, r, file_id) {
204 Ok(Self {
205 revision: r,
206 key_length_bytes,
207 encryption_key: key,
208 permissions: Permissions::from_bits(p as i32),
209 encrypt_metadata,
210 stream_cf,
211 string_cf,
212 o_value: o_bytes,
213 u_value: u_bytes,
214 encoded_password: user_password,
215 })
216 } else {
217 Err(SecurityError::InvalidPassword)
218 }
219 }
220 (5, 5) | (5, 6) => {
221 let oe_bytes = get_string_bytes(encrypt_dict, store, &Name::oe())?;
223 let ue_bytes = get_string_bytes(encrypt_dict, store, &Name::ue())?;
224
225 let key = if r == 5 {
226 derive_key_r5(password, &u_bytes, &ue_bytes, &o_bytes, &oe_bytes)?
227 } else {
228 derive_key_r6(password, &u_bytes, &ue_bytes, &o_bytes, &oe_bytes)?
229 };
230
231 match key {
232 Some(k) => {
233 let perms_bytes = get_string_bytes(encrypt_dict, store, &Name::perms())?;
245 if perms_bytes.len() < 16 {
246 return Err(SecurityError::MissingKey(
247 "Perms (too short, need ≥16 bytes)".into(),
248 ));
249 }
250 let decrypted = crypto::aes256_ecb_decrypt_block(&k, &perms_bytes[..16])?;
251 if &decrypted[9..12] != b"adb" {
255 return Err(SecurityError::InvalidPassword);
256 }
257 let perms_p = i32::from_le_bytes([
259 decrypted[0],
260 decrypted[1],
261 decrypted[2],
262 decrypted[3],
263 ]);
264 if perms_p != p as i32 {
265 return Err(SecurityError::InvalidPassword);
266 }
267
268 Ok(Self {
269 revision: r,
270 key_length_bytes: 32,
271 encryption_key: k,
272 permissions: Permissions::from_bits(p as i32),
273 encrypt_metadata,
274 stream_cf: CryptFilterMethod::Aesv3,
275 string_cf: CryptFilterMethod::Aesv3,
276 o_value: o_bytes,
277 u_value: u_bytes,
278 encoded_password: password.as_bytes().to_vec(),
279 })
280 }
281 None => Err(SecurityError::InvalidPassword),
282 }
283 }
284 _ => Err(SecurityError::UnsupportedVersion(v, r)),
285 }
286 }
287
288 pub fn permissions(&self) -> Permissions {
290 self.permissions
291 }
292
293 #[inline]
297 pub fn get_permissions(&self) -> Permissions {
298 self.permissions()
299 }
300
301 pub fn encoded_password(&self) -> &[u8] {
309 &self.encoded_password
310 }
311
312 #[inline]
316 pub fn get_encoded_password(&self) -> &[u8] {
317 self.encoded_password()
318 }
319
320 pub fn decrypt_string(&self, data: &[u8], obj_id: ObjectId) -> Vec<u8> {
322 if self.string_cf == CryptFilterMethod::None {
323 return data.to_vec();
324 }
325
326 if self.revision >= 5 {
327 crypto::aes256_cbc_decrypt(&self.encryption_key, data).unwrap_or_else(|_| data.to_vec())
329 } else {
330 let object_key = self.compute_object_key(obj_id, self.string_cf);
331 match self.string_cf {
332 CryptFilterMethod::V2 => {
333 crypto::rc4_crypt(&object_key, data).unwrap_or_else(|_| data.to_vec())
334 }
335 CryptFilterMethod::Aesv2 => {
336 crypto::aes128_cbc_decrypt(&object_key, data).unwrap_or_else(|_| data.to_vec())
337 }
338 CryptFilterMethod::Aesv3 => crypto::aes256_cbc_decrypt(&self.encryption_key, data)
339 .unwrap_or_else(|_| data.to_vec()),
340 CryptFilterMethod::None => data.to_vec(),
341 }
342 }
343 }
344
345 pub fn decrypt_stream(&self, data: &[u8], obj_id: ObjectId) -> Vec<u8> {
347 if self.stream_cf == CryptFilterMethod::None {
348 return data.to_vec();
349 }
350
351 if self.revision >= 5 {
352 crypto::aes256_cbc_decrypt(&self.encryption_key, data).unwrap_or_else(|_| data.to_vec())
353 } else {
354 let object_key = self.compute_object_key(obj_id, self.stream_cf);
355 match self.stream_cf {
356 CryptFilterMethod::V2 => {
357 crypto::rc4_crypt(&object_key, data).unwrap_or_else(|_| data.to_vec())
358 }
359 CryptFilterMethod::Aesv2 => {
360 crypto::aes128_cbc_decrypt(&object_key, data).unwrap_or_else(|_| data.to_vec())
361 }
362 CryptFilterMethod::Aesv3 => crypto::aes256_cbc_decrypt(&self.encryption_key, data)
363 .unwrap_or_else(|_| data.to_vec()),
364 CryptFilterMethod::None => data.to_vec(),
365 }
366 }
367 }
368
369 pub fn is_metadata_encrypted(&self) -> bool {
373 self.encrypt_metadata
374 }
375
376 pub fn revision(&self) -> u32 {
378 self.revision
379 }
380
381 pub fn crypt_filter_method(&self) -> CryptFilterMethod {
383 self.stream_cf
384 }
385
386 pub fn key_length_bytes(&self) -> usize {
388 self.key_length_bytes
389 }
390
391 pub fn encryption_key(&self) -> &[u8] {
393 &self.encryption_key
394 }
395
396 pub fn stream_crypt_filter(&self) -> CryptFilterMethod {
398 self.stream_cf
399 }
400
401 pub fn string_crypt_filter(&self) -> CryptFilterMethod {
403 self.string_cf
404 }
405
406 pub fn o_value(&self) -> &[u8] {
408 &self.o_value
409 }
410
411 pub fn u_value(&self) -> &[u8] {
413 &self.u_value
414 }
415
416 pub fn version(&self) -> u32 {
420 match self.revision {
421 2 => 1,
422 3 | 4 => {
423 if self.stream_cf == CryptFilterMethod::V2
424 && self.string_cf == CryptFilterMethod::V2
425 {
426 if self.key_length_bytes == 5 { 1 } else { 2 }
427 } else {
428 4
429 }
430 }
431 5 | 6 => 5,
432 _ => 0,
433 }
434 }
435
436 pub fn encrypt_string(&self, data: &[u8], obj_id: ObjectId) -> Vec<u8> {
442 if self.string_cf == CryptFilterMethod::None {
443 return data.to_vec();
444 }
445 self.encrypt_with_method(data, obj_id, self.string_cf)
446 }
447
448 pub fn encrypt_stream(&self, data: &[u8], obj_id: ObjectId) -> Vec<u8> {
450 if self.stream_cf == CryptFilterMethod::None {
451 return data.to_vec();
452 }
453 self.encrypt_with_method(data, obj_id, self.stream_cf)
454 }
455
456 fn encrypt_with_method(&self, data: &[u8], obj_id: ObjectId, cf: CryptFilterMethod) -> Vec<u8> {
474 let iv = Self::generate_iv();
475
476 if self.revision >= 5 {
477 let encrypted = crypto::aes256_cbc_encrypt(&self.encryption_key, &iv, data)
478 .expect("AES-256 encryption failed: key size validated at construction");
479 let mut result = iv.to_vec();
480 result.extend_from_slice(&encrypted);
481 result
482 } else {
483 let object_key = self.compute_object_key(obj_id, cf);
484 match cf {
485 CryptFilterMethod::V2 => crypto::rc4_crypt(&object_key, data)
486 .expect("RC4 encryption failed: key size validated at construction"),
487 CryptFilterMethod::Aesv2 => {
488 let encrypted = crypto::aes128_cbc_encrypt(&object_key, &iv, data)
489 .expect("AES-128 encryption failed: key size validated at construction");
490 let mut result = iv.to_vec();
491 result.extend_from_slice(&encrypted);
492 result
493 }
494 CryptFilterMethod::Aesv3 => {
495 let encrypted = crypto::aes256_cbc_encrypt(&self.encryption_key, &iv, data)
496 .expect("AES-256 encryption failed: key size validated at construction");
497 let mut result = iv.to_vec();
498 result.extend_from_slice(&encrypted);
499 result
500 }
501 CryptFilterMethod::None => data.to_vec(),
502 }
503 }
504 }
505
506 fn generate_iv() -> [u8; 16] {
517 let mut iv = [0u8; 16];
518 getrandom::fill(&mut iv).expect("OS RNG unavailable");
519 iv
520 }
521
522 fn compute_object_key(&self, obj_id: ObjectId, cf: CryptFilterMethod) -> Vec<u8> {
530 let obj_num = obj_id.number;
531 let gen_num = obj_id.generation;
532
533 let mut buf = Vec::with_capacity(self.encryption_key.len() + 9);
534 buf.extend_from_slice(&self.encryption_key);
535 buf.push((obj_num & 0xFF) as u8);
537 buf.push(((obj_num >> 8) & 0xFF) as u8);
538 buf.push(((obj_num >> 16) & 0xFF) as u8);
539 buf.push((gen_num & 0xFF) as u8);
541 buf.push(((gen_num >> 8) & 0xFF) as u8);
542
543 if cf == CryptFilterMethod::Aesv2 {
544 buf.extend_from_slice(&[0x73, 0x41, 0x6C, 0x54]);
546 }
547
548 let hash = crypto::md5(&buf);
549 let n = std::cmp::min(self.key_length_bytes + 5, 16);
550 hash[..n].to_vec()
551 }
552}
553
554fn encode_password_latin1(password: &str) -> Vec<u8> {
561 password
562 .chars()
563 .map(|c| {
564 let code = c as u32;
565 if code <= 255 {
566 code as u8
567 } else {
568 0x3F }
570 })
571 .collect()
572}
573
574fn pad_password(password: &[u8]) -> [u8; 32] {
576 let mut padded = [0u8; 32];
577 let copy_len = std::cmp::min(password.len(), 32);
578 padded[..copy_len].copy_from_slice(&password[..copy_len]);
579 if copy_len < 32 {
580 padded[copy_len..].copy_from_slice(&PASSWORD_PADDING[..(32 - copy_len)]);
581 }
582 padded
583}
584
585fn derive_key_r2_r4(
587 password: &[u8],
588 o_value: &[u8],
589 permissions: i32,
590 file_id: &[u8],
591 revision: u32,
592 key_length_bytes: usize,
593 encrypt_metadata: bool,
594) -> Vec<u8> {
595 let padded = pad_password(password);
596
597 let mut buf = Vec::with_capacity(32 + o_value.len() + 4 + file_id.len() + 4);
598 buf.extend_from_slice(&padded);
599 buf.extend_from_slice(o_value);
600 buf.extend_from_slice(&(permissions as u32).to_le_bytes());
601 buf.extend_from_slice(file_id);
602
603 if revision >= 4 && !encrypt_metadata {
604 buf.extend_from_slice(&[0xFF, 0xFF, 0xFF, 0xFF]);
605 }
606
607 let mut hash = crypto::md5(&buf);
608
609 if revision >= 3 {
610 for _ in 0..50 {
611 hash = crypto::md5(&hash[..key_length_bytes]);
612 }
613 }
614
615 hash[..key_length_bytes].to_vec()
616}
617
618fn verify_user_password_r2(key: &[u8], u_value: &[u8]) -> bool {
624 let Ok(decrypted) = crypto::rc4_crypt(key, u_value) else {
625 return false;
626 };
627 let expected = crypto::md5(&PASSWORD_PADDING);
628 decrypted[..] == expected[..]
629}
630
631fn verify_user_password_r3_r4(key: &[u8], u_value: &[u8], file_id: &[u8]) -> bool {
637 let mut buf = Vec::with_capacity(32 + file_id.len());
639 buf.extend_from_slice(&PASSWORD_PADDING);
640 buf.extend_from_slice(file_id);
641 let hash = crypto::md5(&buf);
642
643 let Ok(mut result) = crypto::rc4_crypt(key, &hash) else {
645 return false;
646 };
647
648 for i in 1..=19u8 {
650 let modified_key: Vec<u8> = key.iter().map(|b| b ^ i).collect();
651 let Ok(next) = crypto::rc4_crypt(&modified_key, &result) else {
652 return false;
653 };
654 result = next;
655 }
656
657 u_value.len() >= 16 && result.len() >= 16 && result[..16] == u_value[..16]
659}
660
661fn verify_user_password_r2_r4(key: &[u8], u_value: &[u8], revision: u32, file_id: &[u8]) -> bool {
663 if revision == 2 {
664 verify_user_password_r2(key, u_value)
665 } else {
666 verify_user_password_r3_r4(key, u_value, file_id)
667 }
668}
669
670fn recover_user_password_from_owner(
676 owner_password: &[u8],
677 o_value: &[u8],
678 revision: u32,
679 key_length_bytes: usize,
680) -> Vec<u8> {
681 let padded = pad_password(owner_password);
682 let mut hash = crypto::md5(&padded);
683
684 if revision >= 3 {
685 for _ in 0..50 {
686 hash = crypto::md5(&hash[..key_length_bytes]);
687 }
688 }
689
690 let key = &hash[..key_length_bytes];
691
692 if revision == 2 {
693 crypto::rc4_crypt(key, o_value)
694 .expect("RC4 key size validated from MD5 hash (always 5-16 bytes)")
695 } else {
696 let mut result = o_value.to_vec();
697 for i in (0..=19u8).rev() {
698 let modified_key: Vec<u8> = key.iter().map(|b| b ^ i).collect();
699 result = crypto::rc4_crypt(&modified_key, &result)
700 .expect("RC4 key size validated from MD5 hash (always 5-16 bytes)");
701 }
702 result
703 }
704}
705
706fn derive_key_r5(
714 password: &str,
715 u_value: &[u8],
716 ue_value: &[u8],
717 o_value: &[u8],
718 oe_value: &[u8],
719) -> Result<Option<Vec<u8>>, CryptoError> {
720 let pwd = truncate_utf8_password(password);
721
722 if u_value.len() >= 48 && ue_value.len() >= 32 {
724 let validation_salt = &u_value[32..40];
725 let hash = compute_r5_hash(&pwd, validation_salt);
726 if hash[..] == u_value[..32] {
727 let key_salt = &u_value[40..48];
729 let key_hash = compute_r5_hash(&pwd, key_salt);
730 let file_key = crypto::aes256_cbc_decrypt_no_padding(&key_hash, &[0u8; 16], ue_value)?;
731 return Ok(Some(file_key));
732 }
733 }
734
735 if o_value.len() >= 48 && oe_value.len() >= 32 && u_value.len() >= 48 {
737 let validation_salt = &o_value[32..40];
738 let mut input = Vec::with_capacity(pwd.len() + 8 + 48);
739 input.extend_from_slice(&pwd);
740 input.extend_from_slice(validation_salt);
741 input.extend_from_slice(&u_value[..48]);
742 let hash = crypto::sha256(&input);
743 if hash[..] == o_value[..32] {
744 let key_salt = &o_value[40..48];
745 let mut key_input = Vec::with_capacity(pwd.len() + 8 + 48);
746 key_input.extend_from_slice(&pwd);
747 key_input.extend_from_slice(key_salt);
748 key_input.extend_from_slice(&u_value[..48]);
749 let key_hash = crypto::sha256(&key_input);
750 let file_key = crypto::aes256_cbc_decrypt_no_padding(&key_hash, &[0u8; 16], oe_value)?;
751 return Ok(Some(file_key));
752 }
753 }
754
755 Ok(None)
756}
757
758fn derive_key_r6(
762 password: &str,
763 u_value: &[u8],
764 ue_value: &[u8],
765 o_value: &[u8],
766 oe_value: &[u8],
767) -> Result<Option<Vec<u8>>, CryptoError> {
768 let pwd = truncate_utf8_password(password);
769
770 if u_value.len() >= 48 && ue_value.len() >= 32 {
772 let validation_salt = &u_value[32..40];
773 let hash = compute_r6_hash(&pwd, validation_salt, &[]);
774 if hash[..] == u_value[..32] {
775 let key_salt = &u_value[40..48];
777 let key_hash = compute_r6_hash(&pwd, key_salt, &[]);
778 let file_key = crypto::aes256_cbc_decrypt_no_padding(&key_hash, &[0u8; 16], ue_value)?;
779 return Ok(Some(file_key));
780 }
781 }
782
783 if o_value.len() >= 48 && oe_value.len() >= 32 && u_value.len() >= 48 {
785 let validation_salt = &o_value[32..40];
786 let hash = compute_r6_hash(&pwd, validation_salt, &u_value[..48]);
787 if hash[..] == o_value[..32] {
788 let key_salt = &o_value[40..48];
789 let key_hash = compute_r6_hash(&pwd, key_salt, &u_value[..48]);
790 let file_key = crypto::aes256_cbc_decrypt_no_padding(&key_hash, &[0u8; 16], oe_value)?;
791 return Ok(Some(file_key));
792 }
793 }
794
795 Ok(None)
796}
797
798fn big_endian_mod3(bytes: &[u8]) -> u32 {
800 bytes
801 .iter()
802 .take(16)
803 .fold(0u32, |acc, &b| (acc * 256 + b as u32) % 3)
804}
805
806fn compute_r6_hash(password: &[u8], salt: &[u8], u_value: &[u8]) -> [u8; 32] {
823 let mut input = Vec::with_capacity(password.len() + salt.len() + u_value.len());
825 input.extend_from_slice(password);
826 input.extend_from_slice(salt);
827 input.extend_from_slice(u_value);
828 let mut k: Vec<u8> = crypto::sha256(&input).to_vec();
830
831 let mut i: u32 = 0;
832 loop {
833 let seq_len = password.len() + k.len() + u_value.len();
836 let mut k1 = Vec::with_capacity(seq_len * 64);
837 for _ in 0..64 {
838 k1.extend_from_slice(password);
839 k1.extend_from_slice(&k);
840 k1.extend_from_slice(u_value);
841 }
842
843 let aes_key = &k[..16];
846 let aes_iv = &k[16..32];
847 let e = crypto::aes128_cbc_encrypt_no_padding(aes_key, aes_iv, &k1)
848 .expect("K1 is block-aligned; key and IV derived from prior hash are always valid");
849
850 let hash_select = big_endian_mod3(&e);
852
853 k = match hash_select {
855 0 => crypto::sha256(&e).to_vec(),
856 1 => crypto::sha384(&e).to_vec(),
857 _ => crypto::sha512(&e).to_vec(),
858 };
859
860 i += 1;
863 if i >= 64 && (*e.last().unwrap_or(&0) as u32) <= i - 32 {
864 break;
865 }
866 }
867
868 let mut result = [0u8; 32];
870 result.copy_from_slice(&k[..32]);
871 result
872}
873
874fn compute_r5_hash(password: &[u8], salt: &[u8]) -> [u8; 32] {
876 let mut input = Vec::with_capacity(password.len() + salt.len());
877 input.extend_from_slice(password);
878 input.extend_from_slice(salt);
879 crypto::sha256(&input)
880}
881
882fn truncate_utf8_password(password: &str) -> Vec<u8> {
884 let bytes = password.as_bytes();
885 if bytes.len() <= 127 {
886 bytes.to_vec()
887 } else {
888 bytes[..127].to_vec()
889 }
890}
891
892fn parse_single_crypt_filter<S: PdfSource>(
899 encrypt_dict: &HashMap<Name, Object>,
900 store: &ObjectStore<S>,
901 filter_key: &Name,
902) -> CryptFilterMethod {
903 let filter_name = encrypt_dict
904 .get(filter_key)
905 .and_then(|obj| store.deep_resolve(obj).ok())
906 .and_then(|obj| obj.as_name())
907 .cloned()
908 .unwrap_or_else(|| Name::from("StdCF"));
909
910 if filter_name == Name::from("Identity") {
911 return CryptFilterMethod::None;
912 }
913
914 let cf_dict = encrypt_dict
916 .get(&Name::cf())
917 .and_then(|obj| store.deep_resolve(obj).ok())
918 .and_then(|obj| obj.as_dict());
919
920 if let Some(cf) = cf_dict {
921 if let Some(filter_obj) = cf.get(&filter_name) {
922 if let Ok(filter_resolved) = store.deep_resolve(filter_obj) {
923 if let Some(filter_dict) = filter_resolved.as_dict() {
924 let method = filter_dict
925 .get(&Name::cfm())
926 .and_then(|obj| store.deep_resolve(obj).ok())
927 .and_then(|obj| obj.as_name())
928 .cloned();
929
930 if let Some(m) = method {
931 if m == Name::aesv2() {
932 return CryptFilterMethod::Aesv2;
933 } else if m == Name::aesv3() {
934 return CryptFilterMethod::Aesv3;
935 } else if m == Name::v2() {
936 return CryptFilterMethod::V2;
937 } else if m == Name::none() {
938 return CryptFilterMethod::None;
939 }
940 }
941 }
942 }
943 }
944 }
945
946 CryptFilterMethod::V2
948}
949
950fn get_int<S: PdfSource>(
955 dict: &HashMap<Name, Object>,
956 store: &ObjectStore<S>,
957 key: &Name,
958) -> Result<i64, SecurityError> {
959 dict.get(key)
960 .and_then(|obj| store.deep_resolve(obj).ok())
961 .and_then(|obj| obj.as_i64())
962 .ok_or_else(|| SecurityError::MissingKey(key.to_string()))
963}
964
965fn get_optional_int<S: PdfSource>(
966 dict: &HashMap<Name, Object>,
967 store: &ObjectStore<S>,
968 key: &Name,
969) -> Option<i64> {
970 dict.get(key)
971 .and_then(|obj| store.deep_resolve(obj).ok())
972 .and_then(|obj| obj.as_i64())
973}
974
975fn get_optional_bool<S: PdfSource>(
976 dict: &HashMap<Name, Object>,
977 store: &ObjectStore<S>,
978 key: &Name,
979) -> Option<bool> {
980 dict.get(key)
981 .and_then(|obj| store.deep_resolve(obj).ok())
982 .and_then(|obj| obj.as_bool())
983}
984
985fn get_string_bytes<S: PdfSource>(
986 dict: &HashMap<Name, Object>,
987 store: &ObjectStore<S>,
988 key: &Name,
989) -> Result<Vec<u8>, SecurityError> {
990 dict.get(key)
991 .and_then(|obj| store.deep_resolve(obj).ok())
992 .and_then(|obj| obj.as_string())
993 .map(|s| s.as_bytes().to_vec())
994 .ok_or_else(|| SecurityError::MissingKey(key.to_string()))
995}
996
997#[cfg(test)]
998mod tests {
999 use super::*;
1000
1001 #[test]
1002 fn test_password_padding_empty() {
1003 let padded = pad_password(b"");
1004 assert_eq!(padded, PASSWORD_PADDING);
1005 }
1006
1007 #[test]
1008 fn test_password_padding_short() {
1009 let padded = pad_password(b"test");
1010 assert_eq!(&padded[..4], b"test");
1011 assert_eq!(&padded[4..], &PASSWORD_PADDING[..28]);
1012 }
1013
1014 #[test]
1015 fn test_password_padding_exact_32() {
1016 let pwd = b"abcdefghijklmnopqrstuvwxyz012345"; let padded = pad_password(pwd);
1018 assert_eq!(&padded[..], &pwd[..]);
1019 }
1020
1021 #[test]
1022 fn test_password_padding_longer_than_32() {
1023 let pwd = b"abcdefghijklmnopqrstuvwxyz0123456789"; let padded = pad_password(pwd);
1025 assert_eq!(&padded[..], &pwd[..32]);
1026 }
1027
1028 #[test]
1029 fn test_key_derivation_r2_known() {
1030 let o_value = [0u8; 32];
1032 let file_id = b"test_file_id_123";
1033 let key = derive_key_r2_r4(b"", &o_value, -44, file_id, 2, 5, true);
1034 assert_eq!(key.len(), 5);
1035 let key2 = derive_key_r2_r4(b"", &o_value, -44, file_id, 2, 5, true);
1037 assert_eq!(key, key2);
1038 }
1039
1040 #[test]
1041 fn test_key_derivation_r3_known() {
1042 let o_value = [0u8; 32];
1043 let file_id = b"test_file_id_123";
1044 let key = derive_key_r2_r4(b"", &o_value, -44, file_id, 3, 16, true);
1045 assert_eq!(key.len(), 16);
1046 let key_r2 = derive_key_r2_r4(b"", &o_value, -44, file_id, 2, 5, true);
1048 assert_ne!(key[..5], key_r2[..]);
1049 }
1050
1051 #[test]
1052 fn test_user_password_verification_r2_round_trip() {
1053 let o_value = [0x42u8; 32];
1055 let file_id = b"abcdef0123456789";
1056 let key = derive_key_r2_r4(b"password", &o_value, -4, file_id, 2, 5, true);
1057
1058 let expected_u = crypto::rc4_crypt(&key, &crypto::md5(&PASSWORD_PADDING)).unwrap();
1060 assert!(verify_user_password_r2(&key, &expected_u));
1061 }
1062
1063 #[test]
1064 fn test_user_password_verification_r2_wrong_password() {
1065 let o_value = [0x42u8; 32];
1066 let file_id = b"abcdef0123456789";
1067 let key = derive_key_r2_r4(b"password", &o_value, -4, file_id, 2, 5, true);
1068 let expected_u = crypto::rc4_crypt(&key, &crypto::md5(&PASSWORD_PADDING)).unwrap();
1069
1070 let wrong_key = derive_key_r2_r4(b"wrong", &o_value, -4, file_id, 2, 5, true);
1072 assert!(!verify_user_password_r2(&wrong_key, &expected_u));
1073 }
1074
1075 #[test]
1076 fn test_user_password_verification_r3_round_trip() {
1077 let o_value = [0x42u8; 32];
1078 let file_id = b"abcdef0123456789";
1079 let key = derive_key_r2_r4(b"pass", &o_value, -4, file_id, 3, 16, true);
1080
1081 let mut buf = Vec::new();
1083 buf.extend_from_slice(&PASSWORD_PADDING);
1084 buf.extend_from_slice(file_id);
1085 let hash = crypto::md5(&buf);
1086
1087 let mut result = crypto::rc4_crypt(&key, &hash).unwrap();
1088 for i in 1..=19u8 {
1089 let modified_key: Vec<u8> = key.iter().map(|b| b ^ i).collect();
1090 result = crypto::rc4_crypt(&modified_key, &result).unwrap();
1091 }
1092 let mut u_value = result;
1094 u_value.resize(32, 0);
1095
1096 assert!(verify_user_password_r3_r4(&key, &u_value, file_id));
1097 }
1098
1099 #[test]
1100 fn test_object_key_computation() {
1101 let handler = SecurityHandler {
1102 revision: 3,
1103 key_length_bytes: 5,
1104 encryption_key: vec![0x01, 0x02, 0x03, 0x04, 0x05],
1105 permissions: Permissions::from_bits(-4),
1106 encrypt_metadata: true,
1107 stream_cf: CryptFilterMethod::V2,
1108 string_cf: CryptFilterMethod::V2,
1109 o_value: Vec::new(),
1110 u_value: Vec::new(),
1111 encoded_password: Vec::new(),
1112 };
1113
1114 let obj_id = ObjectId::new(10, 0);
1115 let key = handler.compute_object_key(obj_id, CryptFilterMethod::V2);
1116
1117 assert_eq!(key.len(), 10);
1119
1120 let key2 = handler.compute_object_key(obj_id, CryptFilterMethod::V2);
1122 assert_eq!(key, key2);
1123
1124 let key3 = handler.compute_object_key(ObjectId::new(11, 0), CryptFilterMethod::V2);
1126 assert_ne!(key, key3);
1127 }
1128
1129 #[test]
1130 fn test_object_key_aesv2_includes_salt() {
1131 let handler = SecurityHandler {
1132 revision: 4,
1133 key_length_bytes: 16,
1134 encryption_key: vec![0xAA; 16],
1135 permissions: Permissions::from_bits(-4),
1136 encrypt_metadata: true,
1137 stream_cf: CryptFilterMethod::Aesv2,
1138 string_cf: CryptFilterMethod::V2,
1139 o_value: Vec::new(),
1140 u_value: Vec::new(),
1141 encoded_password: Vec::new(),
1142 };
1143
1144 let obj_id = ObjectId::new(1, 0);
1145 let key_rc4 = handler.compute_object_key(obj_id, CryptFilterMethod::V2);
1146 let key_aes = handler.compute_object_key(obj_id, CryptFilterMethod::Aesv2);
1147
1148 assert_ne!(key_rc4, key_aes);
1150 }
1151
1152 #[test]
1153 fn test_decrypt_string_rc4_round_trip() {
1154 let handler = SecurityHandler {
1155 revision: 3,
1156 key_length_bytes: 5,
1157 encryption_key: vec![0x01, 0x02, 0x03, 0x04, 0x05],
1158 permissions: Permissions::from_bits(-4),
1159 encrypt_metadata: true,
1160 stream_cf: CryptFilterMethod::V2,
1161 string_cf: CryptFilterMethod::V2,
1162 o_value: Vec::new(),
1163 u_value: Vec::new(),
1164 encoded_password: Vec::new(),
1165 };
1166
1167 let obj_id = ObjectId::new(1, 0);
1168 let plaintext = b"Hello, encrypted world!";
1169
1170 let object_key = handler.compute_object_key(obj_id, handler.string_cf);
1172 let encrypted = crypto::rc4_crypt(&object_key, plaintext).unwrap();
1173
1174 let decrypted = handler.decrypt_string(&encrypted, obj_id);
1176 assert_eq!(decrypted, plaintext);
1177 }
1178
1179 #[test]
1180 fn test_crypt_filter_method_none_passthrough() {
1181 let handler = SecurityHandler {
1182 revision: 4,
1183 key_length_bytes: 16,
1184 encryption_key: vec![0; 16],
1185 permissions: Permissions::from_bits(-4),
1186 encrypt_metadata: true,
1187 stream_cf: CryptFilterMethod::None,
1188 string_cf: CryptFilterMethod::None,
1189 o_value: Vec::new(),
1190 u_value: Vec::new(),
1191 encoded_password: Vec::new(),
1192 };
1193
1194 let data = b"unencrypted data";
1195 let obj_id = ObjectId::new(1, 0);
1196 assert_eq!(handler.decrypt_string(data, obj_id), data);
1197 assert_eq!(handler.decrypt_stream(data, obj_id), data);
1198 }
1199
1200 #[test]
1201 fn test_truncate_utf8_password_short() {
1202 let pwd = truncate_utf8_password("hello");
1203 assert_eq!(pwd, b"hello");
1204 }
1205
1206 #[test]
1207 fn test_truncate_utf8_password_long() {
1208 let long_pwd = "a".repeat(200);
1209 let pwd = truncate_utf8_password(&long_pwd);
1210 assert_eq!(pwd.len(), 127);
1211 }
1212
1213 #[test]
1214 fn test_compute_r6_hash_basic() {
1215 let hash = compute_r6_hash(b"password", &[0u8; 8], &[]);
1217 assert_eq!(hash.len(), 32);
1218 let hash2 = compute_r6_hash(b"password", &[0u8; 8], &[]);
1220 assert_eq!(hash, hash2);
1221 }
1222
1223 #[test]
1224 fn test_compute_r6_hash_differs_from_simple_sha256() {
1225 let salt = [0x01u8; 8];
1227 let r6_hash = compute_r6_hash(b"test", &salt, &[]);
1228 let simple_hash = crypto::sha256(&{
1229 let mut v = b"test".to_vec();
1230 v.extend_from_slice(&salt);
1231 v
1232 });
1233 assert_ne!(r6_hash, simple_hash);
1236 }
1237
1238 #[test]
1239 fn test_compute_r6_hash_with_u_value() {
1240 let salt = [0x42u8; 8];
1242 let hash_no_u = compute_r6_hash(b"pwd", &salt, &[]);
1243 let hash_with_u = compute_r6_hash(b"pwd", &salt, &[0xAAu8; 48]);
1244 assert_ne!(hash_no_u, hash_with_u);
1245 }
1246
1247 #[test]
1248 fn test_r6_user_password_round_trip() {
1249 let password = b"test123";
1251 let validation_salt = [0x11u8; 8];
1252 let key_salt = [0x22u8; 8];
1253 let file_key = [0x42u8; 32]; let u_hash = compute_r6_hash(password, &validation_salt, &[]);
1257 let mut u_value = Vec::with_capacity(48);
1258 u_value.extend_from_slice(&u_hash);
1259 u_value.extend_from_slice(&validation_salt);
1260 u_value.extend_from_slice(&key_salt);
1261
1262 let key_hash = compute_r6_hash(password, &key_salt, &[]);
1265 let ue_value =
1266 crypto::aes256_cbc_encrypt_no_padding(&key_hash, &[0u8; 16], &file_key).unwrap();
1267
1268 let result = derive_key_r6(
1270 "test123", &u_value, &ue_value, &[0u8; 48], &[0u8; 32], )
1273 .unwrap();
1274
1275 assert!(result.is_some());
1276 assert_eq!(result.unwrap(), file_key);
1277 }
1278
1279 #[test]
1280 fn test_r6_wrong_password_returns_none() {
1281 let password = b"correct";
1282 let validation_salt = [0x33u8; 8];
1283 let key_salt = [0x44u8; 8];
1284
1285 let u_hash = compute_r6_hash(password, &validation_salt, &[]);
1286 let mut u_value = Vec::with_capacity(48);
1287 u_value.extend_from_slice(&u_hash);
1288 u_value.extend_from_slice(&validation_salt);
1289 u_value.extend_from_slice(&key_salt);
1290
1291 let key_hash = compute_r6_hash(password, &key_salt, &[]);
1292 let ue_value =
1293 crypto::aes256_cbc_encrypt_no_padding(&key_hash, &[0u8; 16], &[0u8; 32]).unwrap();
1294
1295 let result = derive_key_r6("wrong", &u_value, &ue_value, &[0u8; 48], &[0u8; 32]).unwrap();
1297
1298 assert!(result.is_none());
1299 }
1300
1301 #[test]
1318 fn test_compute_r6_hash_kat_pdfium_user_validation() {
1319 let password = "h\u{00f4}tel".as_bytes();
1321
1322 let u: [u8; 48] = [
1325 0x07, 0x98, 0xab, 0x4b, 0x1c, 0x93, 0xd3, 0x60, 0xf9, 0x6b, 0x8b, 0xa4, 0x1d, 0x1a, 0xdd, 0x5b, 0x7e, 0xaf, 0x4b, 0x11, 0x0f, 0x01, 0x4d, 0xe8, 0x8a, 0x57, 0x61, 0x5f, 0xdd, 0x6f, 0x67, 0x7c, 0x1a, 0x5e, 0x05, 0x9d, 0x15, 0xed, 0x6e, 0xed, 0x88, 0xd9, 0x4c, 0x03, 0x49, 0x58, 0x3f, 0x86, ];
1332
1333 let computed = compute_r6_hash(password, &u[32..40], &[]);
1334 assert_eq!(
1335 computed,
1336 u[..32],
1337 "Algorithm 2.B hash must match U[0..32] from PDFium-generated R6 PDF"
1338 );
1339 }
1340
1341 #[test]
1344 fn test_derive_key_r6_kat_pdfium_user_password() {
1345 let u: [u8; 48] = [
1347 0x07, 0x98, 0xab, 0x4b, 0x1c, 0x93, 0xd3, 0x60, 0xf9, 0x6b, 0x8b, 0xa4, 0x1d, 0x1a,
1348 0xdd, 0x5b, 0x7e, 0xaf, 0x4b, 0x11, 0x0f, 0x01, 0x4d, 0xe8, 0x8a, 0x57, 0x61, 0x5f,
1349 0xdd, 0x6f, 0x67, 0x7c, 0x1a, 0x5e, 0x05, 0x9d, 0x15, 0xed, 0x6e, 0xed, 0x88, 0xd9,
1350 0x4c, 0x03, 0x49, 0x58, 0x3f, 0x86,
1351 ];
1352 let ue: [u8; 32] = [
1353 0x9e, 0x30, 0x4c, 0x9f, 0xff, 0x64, 0x7b, 0x71, 0x53, 0x6d, 0xb3, 0x68, 0x4c, 0x72,
1354 0x91, 0x4c, 0xae, 0x38, 0x82, 0x88, 0x5e, 0xb8, 0xcf, 0x9c, 0xfb, 0xf3, 0x02, 0x6d,
1355 0xae, 0x2e, 0x1f, 0x35,
1356 ];
1357 let o: [u8; 48] = [
1358 0xd8, 0x0e, 0x61, 0x06, 0xfd, 0x39, 0x47, 0x8c, 0x88, 0x60, 0xc9, 0x14, 0x5e, 0x89,
1359 0x6f, 0x12, 0x6c, 0x3f, 0xa0, 0xc9, 0x12, 0x5a, 0xd2, 0x09, 0x6d, 0x24, 0x2c, 0x07,
1360 0x5f, 0xa6, 0x21, 0xac, 0x99, 0x22, 0x41, 0x60, 0x8c, 0xc3, 0xd3, 0x97, 0x13, 0x5a,
1361 0x2c, 0x0a, 0xec, 0x96, 0xdb, 0x3b,
1362 ];
1363 let oe: [u8; 32] = [
1364 0x90, 0x46, 0xa2, 0x2c, 0x32, 0xd3, 0x32, 0x86, 0x55, 0x95, 0x94, 0xea, 0xef, 0x09,
1365 0xb9, 0xcf, 0x49, 0x22, 0x8b, 0x58, 0xd0, 0x2b, 0xf5, 0xdd, 0xc3, 0x38, 0x3d, 0xf9,
1366 0x26, 0x32, 0x82, 0xe2,
1367 ];
1368
1369 let result = derive_key_r6("h\u{00f4}tel", &u, &ue, &o, &oe)
1371 .expect("derive_key_r6 must not error on valid R6 data");
1372 assert!(
1373 result.is_some(),
1374 "user password 'hôtel' must be accepted for PDFium-generated R6 PDF"
1375 );
1376 }
1377
1378 #[test]
1381 fn test_derive_key_r6_kat_pdfium_owner_password() {
1382 let u: [u8; 48] = [
1383 0x07, 0x98, 0xab, 0x4b, 0x1c, 0x93, 0xd3, 0x60, 0xf9, 0x6b, 0x8b, 0xa4, 0x1d, 0x1a,
1384 0xdd, 0x5b, 0x7e, 0xaf, 0x4b, 0x11, 0x0f, 0x01, 0x4d, 0xe8, 0x8a, 0x57, 0x61, 0x5f,
1385 0xdd, 0x6f, 0x67, 0x7c, 0x1a, 0x5e, 0x05, 0x9d, 0x15, 0xed, 0x6e, 0xed, 0x88, 0xd9,
1386 0x4c, 0x03, 0x49, 0x58, 0x3f, 0x86,
1387 ];
1388 let ue: [u8; 32] = [
1389 0x9e, 0x30, 0x4c, 0x9f, 0xff, 0x64, 0x7b, 0x71, 0x53, 0x6d, 0xb3, 0x68, 0x4c, 0x72,
1390 0x91, 0x4c, 0xae, 0x38, 0x82, 0x88, 0x5e, 0xb8, 0xcf, 0x9c, 0xfb, 0xf3, 0x02, 0x6d,
1391 0xae, 0x2e, 0x1f, 0x35,
1392 ];
1393 let o: [u8; 48] = [
1394 0xd8, 0x0e, 0x61, 0x06, 0xfd, 0x39, 0x47, 0x8c, 0x88, 0x60, 0xc9, 0x14, 0x5e, 0x89,
1395 0x6f, 0x12, 0x6c, 0x3f, 0xa0, 0xc9, 0x12, 0x5a, 0xd2, 0x09, 0x6d, 0x24, 0x2c, 0x07,
1396 0x5f, 0xa6, 0x21, 0xac, 0x99, 0x22, 0x41, 0x60, 0x8c, 0xc3, 0xd3, 0x97, 0x13, 0x5a,
1397 0x2c, 0x0a, 0xec, 0x96, 0xdb, 0x3b,
1398 ];
1399 let oe: [u8; 32] = [
1400 0x90, 0x46, 0xa2, 0x2c, 0x32, 0xd3, 0x32, 0x86, 0x55, 0x95, 0x94, 0xea, 0xef, 0x09,
1401 0xb9, 0xcf, 0x49, 0x22, 0x8b, 0x58, 0xd0, 0x2b, 0xf5, 0xdd, 0xc3, 0x38, 0x3d, 0xf9,
1402 0x26, 0x32, 0x82, 0xe2,
1403 ];
1404
1405 let result = derive_key_r6("\u{00e2}ge", &u, &ue, &o, &oe)
1407 .expect("derive_key_r6 must not error on valid R6 data");
1408 assert!(
1409 result.is_some(),
1410 "owner password 'âge' must be accepted for PDFium-generated R6 PDF"
1411 );
1412 }
1413
1414 #[test]
1415 fn test_owner_password_recovery_r2() {
1416 let user_pwd = b"user";
1418 let owner_pwd = b"owner";
1419 let key_len = 5;
1420
1421 let padded_owner = pad_password(owner_pwd);
1423 let hash = crypto::md5(&padded_owner);
1424 let owner_key = &hash[..key_len];
1425 let padded_user = pad_password(user_pwd);
1426 let o_value = crypto::rc4_crypt(owner_key, &padded_user).unwrap();
1427
1428 let recovered = recover_user_password_from_owner(owner_pwd, &o_value, 2, key_len);
1430 assert_eq!(&recovered[..], &padded_user[..]);
1431 }
1432
1433 #[test]
1438 fn test_password_padding_matches_pdf_spec() {
1439 let spec_padding: [u8; 32] = [
1441 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA,
1442 0x01, 0x08, 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE,
1443 0x64, 0x53, 0x69, 0x7A,
1444 ];
1445 assert_eq!(PASSWORD_PADDING, spec_padding);
1446 }
1447
1448 #[test]
1453 fn test_big_endian_mod3_basic() {
1454 assert_eq!(big_endian_mod3(&[0u8; 16]), 0);
1456 assert_eq!(big_endian_mod3(&[0xFF; 16]), big_endian_mod3(&[0xFF; 16]));
1458 }
1459
1460 #[test]
1461 fn test_big_endian_mod3_differs_from_sum_mod3() {
1462 let bytes = [0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
1487 assert_eq!(big_endian_mod3(&bytes), 1);
1488 let bytes = [0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
1489 assert_eq!(big_endian_mod3(&bytes), 2);
1490 let bytes = [0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3];
1491 assert_eq!(big_endian_mod3(&bytes), 0);
1492 }
1493
1494 #[test]
1499 fn test_separate_strf_stmf_decrypt() {
1500 let handler = SecurityHandler {
1502 revision: 4,
1503 key_length_bytes: 16,
1504 encryption_key: vec![0xBB; 16],
1505 permissions: Permissions::from_bits(-4),
1506 encrypt_metadata: true,
1507 stream_cf: CryptFilterMethod::Aesv2,
1508 string_cf: CryptFilterMethod::V2,
1509 o_value: Vec::new(),
1510 u_value: Vec::new(),
1511 encoded_password: Vec::new(),
1512 };
1513
1514 assert_eq!(handler.crypt_filter_method(), CryptFilterMethod::Aesv2);
1516
1517 let obj_id = ObjectId::new(1, 0);
1518
1519 let plaintext = b"hello";
1521 let obj_key_str = handler.compute_object_key(obj_id, CryptFilterMethod::V2);
1522 let encrypted_str = crypto::rc4_crypt(&obj_key_str, plaintext).unwrap();
1523 let decrypted = handler.decrypt_string(&encrypted_str, obj_id);
1524 assert_eq!(decrypted, plaintext);
1525
1526 let obj_key_stm = handler.compute_object_key(obj_id, CryptFilterMethod::Aesv2);
1528 assert_ne!(obj_key_str, obj_key_stm); }
1530
1531 #[test]
1536 fn test_encode_password_latin1_ascii() {
1537 let encoded = encode_password_latin1("hello");
1538 assert_eq!(encoded, b"hello");
1539 }
1540
1541 #[test]
1542 fn test_encode_password_latin1_latin_chars() {
1543 let encoded = encode_password_latin1("\u{00E9}");
1545 assert_eq!(encoded, vec![0xE9]);
1546 }
1547
1548 #[test]
1549 fn test_encode_password_latin1_non_latin() {
1550 let encoded = encode_password_latin1("\u{4E2D}");
1552 assert_eq!(encoded, vec![0x3F]);
1553 }
1554
1555 #[test]
1556 fn test_encode_password_latin1_mixed() {
1557 let encoded = encode_password_latin1("a\u{00FC}\u{4E2D}b");
1559 assert_eq!(encoded, vec![b'a', 0xFC, 0x3F, b'b']);
1560 }
1561
1562 #[test]
1567 fn test_encode_password_latin1_empty() {
1568 let encoded = encode_password_latin1("");
1569 assert!(encoded.is_empty());
1570 }
1571
1572 #[test]
1573 fn test_encode_password_latin1_ascii_alphanumeric() {
1574 let encoded = encode_password_latin1("ABC123");
1575 assert_eq!(encoded, b"ABC123");
1576 }
1577
1578 #[test]
1579 fn test_encode_password_latin1_0xff() {
1580 let encoded = encode_password_latin1("\u{00FF}");
1582 assert_eq!(encoded, vec![0xFF]);
1583 }
1584
1585 #[test]
1586 fn test_encode_password_latin1_over_range() {
1587 let encoded = encode_password_latin1("\u{0100}");
1589 assert_eq!(encoded, vec![0x3F]); }
1591
1592 #[test]
1593 fn test_truncate_utf8_password_empty() {
1594 let pwd = truncate_utf8_password("");
1595 assert!(pwd.is_empty());
1596 }
1597
1598 #[test]
1599 fn test_truncate_utf8_password_exact_127() {
1600 let input = "a".repeat(127);
1601 let pwd = truncate_utf8_password(&input);
1602 assert_eq!(pwd.len(), 127);
1603 assert_eq!(pwd, input.as_bytes());
1604 }
1605
1606 #[test]
1607 fn test_truncate_utf8_password_over_127() {
1608 let input = "a".repeat(128);
1609 let pwd = truncate_utf8_password(&input);
1610 assert_eq!(pwd.len(), 127);
1611 }
1612
1613 #[test]
1614 fn test_perms_validation_construct_and_verify() {
1615 let permissions: i32 = -4;
1620 let mut perms_plain = [0u8; 16];
1621 perms_plain[0..4].copy_from_slice(&permissions.to_le_bytes());
1622 perms_plain[8] = b'T'; perms_plain[9] = b'a';
1625 perms_plain[10] = b'd';
1626 perms_plain[11] = b'b';
1627
1628 let file_key = [0x42u8; 32];
1630 use aes::cipher::{BlockEncrypt, KeyInit};
1633 let cipher = aes::Aes256::new_from_slice(&file_key).unwrap();
1634 let mut block = aes::cipher::generic_array::GenericArray::clone_from_slice(&perms_plain);
1635 cipher.encrypt_block(&mut block);
1636 let encrypted_perms: [u8; 16] = block.into();
1637
1638 let decrypted = crypto::aes256_ecb_decrypt_block(&file_key, &encrypted_perms).unwrap();
1640
1641 assert_eq!(decrypted[8], b'T');
1643 assert_eq!(&decrypted[9..12], b"adb");
1644 let recovered_p =
1645 i32::from_le_bytes([decrypted[0], decrypted[1], decrypted[2], decrypted[3]]);
1646 assert_eq!(recovered_p, permissions);
1647 }
1648
1649 #[test]
1650 fn test_get_permissions_alias_delegates_to_permissions() {
1651 let handler = SecurityHandler {
1652 revision: 3,
1653 key_length_bytes: 5,
1654 encryption_key: vec![0x01; 5],
1655 permissions: Permissions::from_bits(-4),
1656 encrypt_metadata: true,
1657 stream_cf: CryptFilterMethod::V2,
1658 string_cf: CryptFilterMethod::V2,
1659 o_value: Vec::new(),
1660 u_value: Vec::new(),
1661 encoded_password: b"secret".to_vec(),
1662 };
1663 assert_eq!(handler.get_permissions(), handler.permissions());
1664 }
1665
1666 #[test]
1667 fn test_encoded_password_stored_and_accessible() {
1668 let handler = SecurityHandler {
1669 revision: 3,
1670 key_length_bytes: 5,
1671 encryption_key: vec![0x01; 5],
1672 permissions: Permissions::from_bits(-4),
1673 encrypt_metadata: true,
1674 stream_cf: CryptFilterMethod::V2,
1675 string_cf: CryptFilterMethod::V2,
1676 o_value: Vec::new(),
1677 u_value: Vec::new(),
1678 encoded_password: b"my_password".to_vec(),
1679 };
1680 assert_eq!(handler.encoded_password(), b"my_password");
1681 }
1682
1683 #[test]
1693 fn test_security_handler_stores_encoded_password_user_path() {
1694 let password = "test";
1696 let pwd_bytes = encode_password_latin1(password);
1697 let o_value = [0x42u8; 32];
1698 let file_id = b"abcdef0123456789";
1699 let key = derive_key_r2_r4(&pwd_bytes, &o_value, -4, file_id, 3, 5, true);
1700
1701 let mut buf = Vec::new();
1703 buf.extend_from_slice(&PASSWORD_PADDING);
1704 buf.extend_from_slice(file_id);
1705 let hash = crypto::md5(&buf);
1706 let mut result = crypto::rc4_crypt(&key, &hash).unwrap();
1707 for i in 1..=19u8 {
1708 let modified: Vec<u8> = key.iter().map(|b| b ^ i).collect();
1709 result = crypto::rc4_crypt(&modified, &result).unwrap();
1710 }
1711 let mut u_value = result;
1712 u_value.resize(32, 0);
1713
1714 let handler = SecurityHandler {
1716 revision: 3,
1717 key_length_bytes: 5,
1718 encryption_key: key,
1719 permissions: Permissions::from_bits(-4),
1720 encrypt_metadata: true,
1721 stream_cf: CryptFilterMethod::V2,
1722 string_cf: CryptFilterMethod::V2,
1723 o_value: o_value.to_vec(),
1724 u_value,
1725 encoded_password: pwd_bytes.clone(),
1726 };
1727
1728 assert_eq!(handler.encoded_password(), pwd_bytes.as_slice());
1729 assert_eq!(handler.encoded_password(), b"test");
1731 }
1732
1733 #[test]
1738 fn test_security_handler_get_permissions_alias_r3() {
1739 let handler = SecurityHandler {
1740 revision: 3,
1741 key_length_bytes: 5,
1742 encryption_key: vec![0x01; 5],
1743 permissions: Permissions::from_bits(-3904),
1744 encrypt_metadata: true,
1745 stream_cf: CryptFilterMethod::V2,
1746 string_cf: CryptFilterMethod::V2,
1747 o_value: Vec::new(),
1748 u_value: Vec::new(),
1749 encoded_password: b"owner".to_vec(),
1750 };
1751 assert_eq!(handler.get_permissions().bits(), -3904);
1752 assert_eq!(handler.get_permissions(), handler.permissions());
1753 }
1754}