1use aes::cipher::{BlockEncrypt, KeyInit};
12use aes::{Aes128, Aes256};
13use cbc::cipher::{BlockEncryptMut, KeyIvInit};
14use md5::Md5;
15use sha2::Digest as _;
16use sha2::Sha256;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
24pub enum EncryptionAlgorithm {
25 #[default]
27 Rc4128,
28 Aes256,
30}
31
32#[derive(Debug, Clone, Copy)]
41pub struct PdfPermissions {
42 pub allow_print: bool,
44 pub allow_modify: bool,
46 pub allow_copy: bool,
48 pub allow_annotations: bool,
50 pub allow_fill_forms: bool,
52 pub allow_accessibility: bool,
54 pub allow_assemble: bool,
56 pub allow_print_high_quality: bool,
58}
59
60impl Default for PdfPermissions {
61 fn default() -> Self {
62 Self {
63 allow_print: true,
64 allow_modify: true,
65 allow_copy: true,
66 allow_annotations: true,
67 allow_fill_forms: true,
68 allow_accessibility: true,
69 allow_assemble: true,
70 allow_print_high_quality: true,
71 }
72 }
73}
74
75impl PdfPermissions {
76 pub fn to_p_value(&self) -> i32 {
81 let mut p: u32 = 0xFFFFF000; p |= 0b1100_0000;
85
86 if self.allow_print {
87 p |= 1 << 2; }
89 if self.allow_modify {
90 p |= 1 << 3; }
92 if self.allow_copy {
93 p |= 1 << 4; }
95 if self.allow_annotations {
96 p |= 1 << 5; }
98 if self.allow_fill_forms {
99 p |= 1 << 8; }
101 if self.allow_accessibility {
102 p |= 1 << 9; }
104 if self.allow_assemble {
105 p |= 1 << 10; }
107 if self.allow_print_high_quality {
108 p |= 1 << 11; }
110
111 p as i32
112 }
113}
114
115#[derive(Debug, Clone)]
121pub struct PdfSecurity {
122 pub owner_password: String,
124 pub user_password: String,
126 pub permissions: PdfPermissions,
128 pub key_length: u32,
130 pub algorithm: EncryptionAlgorithm,
132}
133
134impl PdfSecurity {
135 pub fn new(owner_password: &str, user_password: &str, permissions: PdfPermissions) -> Self {
137 Self {
138 owner_password: owner_password.to_string(),
139 user_password: user_password.to_string(),
140 permissions,
141 key_length: 128,
142 algorithm: EncryptionAlgorithm::Rc4128,
143 }
144 }
145
146 pub fn new_aes256(
148 owner_password: &str,
149 user_password: &str,
150 permissions: PdfPermissions,
151 ) -> Self {
152 Self {
153 owner_password: owner_password.to_string(),
154 user_password: user_password.to_string(),
155 permissions,
156 key_length: 256,
157 algorithm: EncryptionAlgorithm::Aes256,
158 }
159 }
160
161 pub fn compute_encryption_dict(&self, file_id: &[u8]) -> EncryptionDict {
165 match self.algorithm {
166 EncryptionAlgorithm::Rc4128 => self.compute_rc4_encryption_dict(file_id),
167 EncryptionAlgorithm::Aes256 => self.compute_aes256_encryption_dict(),
168 }
169 }
170
171 fn compute_rc4_encryption_dict(&self, file_id: &[u8]) -> EncryptionDict {
177 let p_value = self.permissions.to_p_value();
178 let revision = if self.key_length > 40 { 3 } else { 2 };
179 let version = if self.key_length > 40 { 2 } else { 1 };
180 let key_len_bytes = (self.key_length / 8) as usize;
181
182 let o_value = self.compute_o_value(revision, key_len_bytes);
184
185 let encryption_key =
187 self.compute_encryption_key(&o_value, p_value, file_id, revision, key_len_bytes);
188
189 let u_value = self.compute_u_value(&encryption_key, file_id, revision);
191
192 EncryptionDict {
193 o_value,
194 u_value,
195 oe_value: None,
196 ue_value: None,
197 perms_value: None,
198 p_value,
199 encryption_key,
200 key_length: self.key_length,
201 revision: revision as u32,
202 version: version as u32,
203 algorithm: EncryptionAlgorithm::Rc4128,
204 }
205 }
206
207 fn compute_o_value(&self, revision: usize, key_len_bytes: usize) -> Vec<u8> {
209 let owner_padded = pad_password(&self.owner_password);
211
212 let mut hash = md5_hash(&owner_padded);
214
215 if revision >= 3 {
217 for _ in 0..50 {
218 hash = md5_hash(&hash[..key_len_bytes]);
219 }
220 }
221
222 let key = &hash[..key_len_bytes];
224
225 let user_padded = pad_password(&self.user_password);
227
228 let mut result = rc4_encrypt(key, &user_padded);
230
231 if revision >= 3 {
233 for i in 1..=19 {
234 let derived_key: Vec<u8> = key.iter().map(|&b| b ^ (i as u8)).collect();
235 result = rc4_encrypt(&derived_key, &result);
236 }
237 }
238
239 result
240 }
241
242 fn compute_encryption_key(
244 &self,
245 o_value: &[u8],
246 p_value: i32,
247 file_id: &[u8],
248 revision: usize,
249 key_len_bytes: usize,
250 ) -> Vec<u8> {
251 let mut hasher = Md5::new();
252
253 let user_padded = pad_password(&self.user_password);
255 hasher.update(&user_padded);
256
257 hasher.update(o_value);
259
260 hasher.update((p_value as u32).to_le_bytes());
262
263 hasher.update(file_id);
265
266 let mut hash = hasher.finalize().to_vec();
267
268 if revision >= 3 {
270 for _ in 0..50 {
271 hash = md5_hash(&hash[..key_len_bytes]);
272 }
273 }
274
275 hash[..key_len_bytes].to_vec()
276 }
277
278 fn compute_u_value(&self, encryption_key: &[u8], file_id: &[u8], revision: usize) -> Vec<u8> {
280 if revision >= 3 {
281 let mut hasher = Md5::new();
283 hasher.update(PADDING);
284 hasher.update(file_id);
285 let hash = hasher.finalize().to_vec();
286
287 let mut result = rc4_encrypt(encryption_key, &hash);
288
289 for i in 1..=19 {
290 let derived_key: Vec<u8> = encryption_key.iter().map(|&b| b ^ (i as u8)).collect();
291 result = rc4_encrypt(&derived_key, &result);
292 }
293
294 result.resize(32, 0);
296 result
297 } else {
298 rc4_encrypt(encryption_key, &PADDING)
300 }
301 }
302
303 fn compute_aes256_encryption_dict(&self) -> EncryptionDict {
309 let p_value = self.permissions.to_p_value();
310
311 let user_validation_salt = derive_salt(&self.user_password, b"user_val");
314 let user_key_salt = derive_salt(&self.user_password, b"user_key");
315 let owner_validation_salt = derive_salt(&self.owner_password, b"owner_val");
316 let owner_key_salt = derive_salt(&self.owner_password, b"owner_key");
317
318 let u_hash = sha256_hash_parts(&[self.user_password.as_bytes(), &user_validation_salt]);
322 let mut u_value = u_hash.clone();
323 u_value.extend_from_slice(&user_validation_salt);
324 u_value.extend_from_slice(&user_key_salt);
325 let file_enc_key = derive_file_enc_key(&self.owner_password, &self.user_password);
329
330 let ue_key = sha256_hash_parts(&[self.user_password.as_bytes(), &user_key_salt]);
332 let ue_value = aes256_cbc_encrypt_no_iv(&file_enc_key, &ue_key);
333 let o_hash = sha256_hash_parts(&[
338 self.owner_password.as_bytes(),
339 &owner_validation_salt,
340 &u_value,
341 ]);
342 let mut o_value = o_hash;
343 o_value.extend_from_slice(&owner_validation_salt);
344 o_value.extend_from_slice(&owner_key_salt);
345 let oe_key =
349 sha256_hash_parts(&[self.owner_password.as_bytes(), &owner_key_salt, &u_value]);
350 let oe_value = aes256_cbc_encrypt_no_iv(&file_enc_key, &oe_key);
351
352 let mut perms_plain = [0u8; 16];
356 let p_bytes = (p_value as u32).to_le_bytes();
357 perms_plain[0..4].copy_from_slice(&p_bytes);
358 perms_plain[4] = 0xFF;
359 perms_plain[5] = 0xFF;
360 perms_plain[6] = 0xFF;
361 perms_plain[7] = 0xFF;
362 perms_plain[8] = b'T'; perms_plain[9] = b'a';
364 perms_plain[10] = b'd';
365 perms_plain[11] = b'b';
366 perms_plain[12] = file_enc_key[0];
368 perms_plain[13] = file_enc_key[1];
369 perms_plain[14] = file_enc_key[2];
370 perms_plain[15] = file_enc_key[3];
371
372 let perms_value = aes256_ecb_encrypt_block(&perms_plain, &file_enc_key);
373
374 EncryptionDict {
375 o_value,
376 u_value,
377 oe_value: Some(oe_value),
378 ue_value: Some(ue_value),
379 perms_value: Some(perms_value.to_vec()),
380 p_value,
381 encryption_key: file_enc_key,
382 key_length: 256,
383 revision: 6,
384 version: 5,
385 algorithm: EncryptionAlgorithm::Aes256,
386 }
387 }
388}
389
390#[derive(Debug, Clone)]
396pub struct EncryptionDict {
397 pub o_value: Vec<u8>,
399 pub u_value: Vec<u8>,
401 pub oe_value: Option<Vec<u8>>,
403 pub ue_value: Option<Vec<u8>>,
405 pub perms_value: Option<Vec<u8>>,
407 pub p_value: i32,
409 pub encryption_key: Vec<u8>,
411 pub key_length: u32,
413 pub revision: u32,
415 pub version: u32,
417 pub algorithm: EncryptionAlgorithm,
419}
420
421impl EncryptionDict {
422 pub fn encrypt_data(&self, data: &[u8], obj_num: u32, gen_num: u16) -> Vec<u8> {
427 match self.algorithm {
428 EncryptionAlgorithm::Rc4128 => {
429 let key = self.derive_object_key(obj_num, gen_num);
430 rc4_encrypt(&key, data)
431 }
432 EncryptionAlgorithm::Aes256 => {
433 encrypt_aes256_cbc(data, &self.encryption_key, obj_num)
435 }
436 }
437 }
438
439 fn derive_object_key(&self, obj_num: u32, gen_num: u16) -> Vec<u8> {
441 let mut hasher = Md5::new();
442 hasher.update(&self.encryption_key);
443 hasher.update(&obj_num.to_le_bytes()[..3]);
444 hasher.update(gen_num.to_le_bytes());
445 let hash = hasher.finalize();
446
447 let key_len = std::cmp::min(self.encryption_key.len() + 5, 16);
449 hash[..key_len].to_vec()
450 }
451
452 pub fn to_pdf_dict(&self, encrypt_obj_id: usize) -> String {
454 match self.algorithm {
455 EncryptionAlgorithm::Rc4128 => self.to_rc4_pdf_dict(encrypt_obj_id),
456 EncryptionAlgorithm::Aes256 => self.to_aes256_pdf_dict(encrypt_obj_id),
457 }
458 }
459
460 fn to_rc4_pdf_dict(&self, encrypt_obj_id: usize) -> String {
462 let mut dict = String::new();
463 dict.push_str(&format!("{} 0 obj\n", encrypt_obj_id));
464 dict.push_str("<<\n");
465 dict.push_str("/Filter /Standard\n");
466 dict.push_str(&format!("/V {}\n", self.version));
467 dict.push_str(&format!("/R {}\n", self.revision));
468 dict.push_str(&format!("/Length {}\n", self.key_length));
469 dict.push_str(&format!("/P {}\n", self.p_value));
470 dict.push_str(&format!("/O <{}>\n", hex_encode(&self.o_value)));
471 dict.push_str(&format!("/U <{}>\n", hex_encode(&self.u_value)));
472 dict.push_str(">>\n");
473 dict.push_str("endobj\n");
474 dict
475 }
476
477 fn to_aes256_pdf_dict(&self, encrypt_obj_id: usize) -> String {
479 let mut dict = String::new();
480 dict.push_str(&format!("{} 0 obj\n", encrypt_obj_id));
481 dict.push_str("<<\n");
482 dict.push_str("/Filter /Standard\n");
483 dict.push_str("/V 5\n");
484 dict.push_str("/R 6\n");
485 dict.push_str("/Length 256\n");
486 dict.push_str(&format!("/P {}\n", self.p_value));
487
488 dict.push_str(&format!("/O <{}>\n", hex_encode(&self.o_value)));
490 dict.push_str(&format!("/U <{}>\n", hex_encode(&self.u_value)));
491
492 if let Some(ref oe) = self.oe_value {
493 dict.push_str(&format!("/OE <{}>\n", hex_encode(oe)));
494 }
495 if let Some(ref ue) = self.ue_value {
496 dict.push_str(&format!("/UE <{}>\n", hex_encode(ue)));
497 }
498 if let Some(ref perms) = self.perms_value {
499 dict.push_str(&format!("/Perms <{}>\n", hex_encode(perms)));
500 }
501
502 dict.push_str("/CF <<\n");
504 dict.push_str(" /StdCF <<\n");
505 dict.push_str(" /AuthEvent /DocOpen\n");
506 dict.push_str(" /CFM /AESV3\n");
507 dict.push_str(" /Length 32\n");
508 dict.push_str(" >>\n");
509 dict.push_str(">>\n");
510 dict.push_str("/StmF /StdCF\n");
511 dict.push_str("/StrF /StdCF\n");
512 dict.push_str(">>\n");
513 dict.push_str("endobj\n");
514 dict
515 }
516
517 #[allow(dead_code)]
521 pub fn aes128_encrypt_block(&self, data: &[u8; 16]) -> [u8; 16] {
522 let key = aes::cipher::generic_array::GenericArray::from_slice(&self.encryption_key[..16]);
523 let cipher = Aes128::new(key);
524 let mut block = aes::cipher::generic_array::GenericArray::clone_from_slice(data);
525 cipher.encrypt_block(&mut block);
526 block.into()
527 }
528
529 #[allow(dead_code)]
534 pub fn encrypt_aes256(data: &[u8], key: &[u8]) -> Vec<u8> {
535 let iv = sha256_hash_parts(&[key, data])[..16].to_vec();
538 let mut iv_arr = [0u8; 16];
539 iv_arr.copy_from_slice(&iv);
540
541 let padded = pkcs7_pad(data, 16);
542 let key_arr = aes::cipher::generic_array::GenericArray::from_slice(&key[..32]);
543 let iv_ga = aes::cipher::generic_array::GenericArray::from_slice(&iv_arr);
544
545 type Aes256Cbc = cbc::Encryptor<Aes256>;
546 let cipher = Aes256Cbc::new(key_arr, iv_ga);
547 let ciphertext =
548 cipher.encrypt_padded_vec_mut::<cbc::cipher::block_padding::Pkcs7>(&padded);
549
550 let mut result = iv_arr.to_vec();
551 result.extend_from_slice(&ciphertext);
552 result
553 }
554}
555
556const PADDING: [u8; 32] = [
562 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
563 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A,
564];
565
566fn pad_password(password: &str) -> Vec<u8> {
572 let pwd_bytes = password.as_bytes();
573 let mut padded = Vec::with_capacity(32);
574
575 if pwd_bytes.len() >= 32 {
576 padded.extend_from_slice(&pwd_bytes[..32]);
577 } else {
578 padded.extend_from_slice(pwd_bytes);
579 let remaining = 32 - pwd_bytes.len();
580 padded.extend_from_slice(&PADDING[..remaining]);
581 }
582
583 padded
584}
585
586fn md5_hash(data: &[u8]) -> Vec<u8> {
588 let mut hasher = Md5::new();
589 hasher.update(data);
590 hasher.finalize().to_vec()
591}
592
593fn sha256_hash_parts(parts: &[&[u8]]) -> Vec<u8> {
595 let mut hasher = Sha256::new();
596 for part in parts {
597 hasher.update(part);
598 }
599 hasher.finalize().to_vec()
600}
601
602fn rc4_encrypt(key: &[u8], data: &[u8]) -> Vec<u8> {
604 let mut s: Vec<u8> = (0..=255).map(|i| i as u8).collect();
606 let mut j: usize = 0;
607 for i in 0..256 {
608 j = (j + s[i] as usize + key[i % key.len()] as usize) % 256;
609 s.swap(i, j);
610 }
611
612 let mut output = Vec::with_capacity(data.len());
614 let mut i: usize = 0;
615 j = 0;
616 for &byte in data {
617 i = (i + 1) % 256;
618 j = (j + s[i] as usize) % 256;
619 s.swap(i, j);
620 let k = s[(s[i] as usize + s[j] as usize) % 256];
621 output.push(byte ^ k);
622 }
623
624 output
625}
626
627fn encrypt_aes256_cbc(data: &[u8], key: &[u8], obj_num: u32) -> Vec<u8> {
631 let mut iv_src = [0u8; 36];
633 iv_src[..32].copy_from_slice(&key[..32]);
634 iv_src[32..36].copy_from_slice(&obj_num.to_le_bytes());
635 let iv_hash = sha256_hash_parts(&[&iv_src]);
636 let mut iv = [0u8; 16];
637 iv.copy_from_slice(&iv_hash[..16]);
638
639 let key_arr = aes::cipher::generic_array::GenericArray::from_slice(&key[..32]);
640 let iv_ga = aes::cipher::generic_array::GenericArray::from_slice(&iv);
641
642 type Aes256Cbc = cbc::Encryptor<Aes256>;
643 let cipher = Aes256Cbc::new(key_arr, iv_ga);
644 let ciphertext = cipher.encrypt_padded_vec_mut::<cbc::cipher::block_padding::Pkcs7>(data);
645
646 let mut result = iv.to_vec();
647 result.extend_from_slice(&ciphertext);
648 result
649}
650
651fn aes256_cbc_encrypt_no_iv(data: &[u8], key: &[u8]) -> Vec<u8> {
655 let iv = [0u8; 16];
656 let key_arr = aes::cipher::generic_array::GenericArray::from_slice(&key[..32]);
657 let iv_ga = aes::cipher::generic_array::GenericArray::from_slice(&iv);
658
659 type Aes256Cbc = cbc::Encryptor<Aes256>;
660 let cipher = Aes256Cbc::new(key_arr, iv_ga);
661 cipher.encrypt_padded_vec_mut::<cbc::cipher::block_padding::Pkcs7>(data)
662}
663
664fn aes256_ecb_encrypt_block(data: &[u8; 16], key: &[u8]) -> [u8; 16] {
666 let key_arr = aes::cipher::generic_array::GenericArray::from_slice(&key[..32]);
667 let cipher = Aes256::new(key_arr);
668 let mut block = aes::cipher::generic_array::GenericArray::clone_from_slice(data);
669 cipher.encrypt_block(&mut block);
670 block.into()
671}
672
673fn pkcs7_pad(data: &[u8], block_size: usize) -> Vec<u8> {
675 let pad_len = block_size - (data.len() % block_size);
676 let mut padded = data.to_vec();
677 for _ in 0..pad_len {
678 padded.push(pad_len as u8);
679 }
680 padded
681}
682
683fn derive_salt(password: &str, tag: &[u8]) -> [u8; 8] {
685 let hash = sha256_hash_parts(&[password.as_bytes(), tag]);
686 let mut salt = [0u8; 8];
687 salt.copy_from_slice(&hash[..8]);
688 salt
689}
690
691fn derive_file_enc_key(owner_pass: &str, user_pass: &str) -> Vec<u8> {
693 sha256_hash_parts(&[owner_pass.as_bytes(), user_pass.as_bytes(), b"file_enc_key"])
694}
695
696fn hex_encode(data: &[u8]) -> String {
698 data.iter().map(|b| format!("{:02X}", b)).collect()
699}
700
701pub fn generate_file_id(seed: &str) -> Vec<u8> {
707 md5_hash(seed.as_bytes())
708}
709
710#[cfg(test)]
715mod tests {
716 use super::*;
717
718 #[test]
719 fn test_pad_password_empty() {
720 let padded = pad_password("");
721 assert_eq!(padded.len(), 32);
722 assert_eq!(padded, PADDING.to_vec());
723 }
724
725 #[test]
726 fn test_pad_password_short() {
727 let padded = pad_password("test");
728 assert_eq!(padded.len(), 32);
729 assert_eq!(&padded[..4], b"test");
730 assert_eq!(&padded[4..], &PADDING[..28]);
731 }
732
733 #[test]
734 fn test_pad_password_long() {
735 let long_pwd = "a".repeat(40);
736 let padded = pad_password(&long_pwd);
737 assert_eq!(padded.len(), 32);
738 assert_eq!(padded, vec![b'a'; 32]);
739 }
740
741 #[test]
742 fn test_permissions_default_all_allowed() {
743 let perms = PdfPermissions::default();
744 let p = perms.to_p_value();
745 assert!(p & (1 << 2) != 0); assert!(p & (1 << 3) != 0); assert!(p & (1 << 4) != 0); assert!(p & (1 << 5) != 0); }
751
752 #[test]
753 fn test_permissions_restricted() {
754 let perms = PdfPermissions {
755 allow_print: false,
756 allow_copy: false,
757 allow_modify: false,
758 allow_annotations: false,
759 ..Default::default()
760 };
761 let p = perms.to_p_value();
762 assert!(p & (1 << 2) == 0); assert!(p & (1 << 3) == 0); assert!(p & (1 << 4) == 0); assert!(p & (1 << 5) == 0); }
767
768 #[test]
769 fn test_rc4_encrypt_decrypt_roundtrip() {
770 let key = b"testkey123456789";
771 let plaintext = b"Hello, World! This is a PDF encryption test.";
772 let encrypted = rc4_encrypt(key, plaintext);
773 let decrypted = rc4_encrypt(key, &encrypted);
774 assert_eq!(decrypted, plaintext);
775 }
776
777 #[test]
778 fn test_rc4_encryption_dict_computation() {
779 let security = PdfSecurity::new("owner", "user", PdfPermissions::default());
780 let file_id = generate_file_id("test-document");
781 let dict = security.compute_encryption_dict(&file_id);
782
783 assert_eq!(dict.o_value.len(), 32);
784 assert_eq!(dict.u_value.len(), 32);
785 assert_eq!(dict.key_length, 128);
786 assert_eq!(dict.revision, 3);
787 assert_eq!(dict.version, 2);
788 assert_eq!(dict.algorithm, EncryptionAlgorithm::Rc4128);
789 }
790
791 #[test]
792 fn test_rc4_object_encryption_roundtrip() {
793 let security = PdfSecurity::new("owner", "user", PdfPermissions::default());
794 let file_id = generate_file_id("test-doc");
795 let dict = security.compute_encryption_dict(&file_id);
796
797 let plaintext = b"This is a test stream content for PDF object.";
798 let encrypted = dict.encrypt_data(plaintext, 5, 0);
799 let decrypted = dict.encrypt_data(&encrypted, 5, 0);
800 assert_eq!(decrypted, plaintext);
801 }
802
803 #[test]
804 fn test_rc4_encryption_dict_pdf_output() {
805 let security = PdfSecurity::new("owner", "", PdfPermissions::default());
806 let file_id = generate_file_id("test");
807 let dict = security.compute_encryption_dict(&file_id);
808 let pdf_str = dict.to_pdf_dict(10);
809
810 assert!(pdf_str.contains("/Filter /Standard"));
811 assert!(pdf_str.contains("/V 2"));
812 assert!(pdf_str.contains("/R 3"));
813 assert!(pdf_str.contains("/Length 128"));
814 assert!(pdf_str.contains("/O <"));
815 assert!(pdf_str.contains("/U <"));
816 }
817
818 #[test]
819 fn test_aes256_encryption_dict_computation() {
820 let security =
821 PdfSecurity::new_aes256("owner_pass", "user_pass", PdfPermissions::default());
822 let dict = security.compute_encryption_dict(&[]);
823
824 assert_eq!(dict.o_value.len(), 48);
826 assert_eq!(dict.u_value.len(), 48);
827 assert_eq!(dict.key_length, 256);
828 assert_eq!(dict.revision, 6);
829 assert_eq!(dict.version, 5);
830 assert_eq!(dict.algorithm, EncryptionAlgorithm::Aes256);
831
832 assert!(dict.oe_value.is_some());
834 assert!(dict.ue_value.is_some());
835 assert!(dict.perms_value.is_some());
836
837 assert_eq!(
839 dict.perms_value
840 .as_ref()
841 .expect("test: should succeed")
842 .len(),
843 16
844 );
845 }
846
847 #[test]
848 fn test_aes256_encryption_dict_pdf_output() {
849 let security =
850 PdfSecurity::new_aes256("owner_pass", "user_pass", PdfPermissions::default());
851 let dict = security.compute_encryption_dict(&[]);
852 let pdf_str = dict.to_pdf_dict(10);
853
854 assert!(pdf_str.contains("/Filter /Standard"));
855 assert!(pdf_str.contains("/V 5"));
856 assert!(pdf_str.contains("/R 6"));
857 assert!(pdf_str.contains("/Length 256"));
858 assert!(pdf_str.contains("/O <"));
859 assert!(pdf_str.contains("/U <"));
860 assert!(pdf_str.contains("/OE <"));
861 assert!(pdf_str.contains("/UE <"));
862 assert!(pdf_str.contains("/Perms <"));
863 assert!(pdf_str.contains("/CFM /AESV3"));
864 assert!(pdf_str.contains("/StmF /StdCF"));
865 assert!(pdf_str.contains("/StrF /StdCF"));
866 }
867
868 #[test]
869 fn test_aes256_stream_encryption() {
870 let security =
871 PdfSecurity::new_aes256("owner_pass", "user_pass", PdfPermissions::default());
872 let dict = security.compute_encryption_dict(&[]);
873
874 let plaintext = b"Sensitive PDF stream content.";
875 let encrypted = dict.encrypt_data(plaintext, 10, 0);
876
877 assert!(encrypted.len() > plaintext.len());
879 assert_ne!(&encrypted[16..16 + plaintext.len()], plaintext.as_slice());
881 }
882
883 #[test]
884 fn test_aes256_deterministic() {
885 let security =
887 PdfSecurity::new_aes256("owner_pass", "user_pass", PdfPermissions::default());
888 let dict = security.compute_encryption_dict(&[]);
889
890 let plaintext = b"Test data for AES-256.";
891 let enc1 = dict.encrypt_data(plaintext, 5, 0);
892 let enc2 = dict.encrypt_data(plaintext, 5, 0);
893 assert_eq!(enc1, enc2);
894 }
895
896 #[test]
897 fn test_hex_encode() {
898 assert_eq!(hex_encode(&[0xFF, 0x00, 0xAB]), "FF00AB");
899 assert_eq!(hex_encode(&[]), "");
900 }
901
902 #[test]
903 fn test_file_id_generation() {
904 let id1 = generate_file_id("doc1");
905 let id2 = generate_file_id("doc2");
906 assert_eq!(id1.len(), 16);
907 assert_ne!(id1, id2);
908 }
909
910 #[test]
911 fn test_encryption_algorithm_default() {
912 let algo = EncryptionAlgorithm::default();
913 assert_eq!(algo, EncryptionAlgorithm::Rc4128);
914 }
915
916 #[test]
917 fn test_pkcs7_pad() {
918 let data = b"Hello, World!";
920 let padded = pkcs7_pad(data, 16);
921 assert_eq!(padded.len(), 16);
922 assert_eq!(&padded[13..], &[3u8, 3, 3]);
923 }
924}
925
926#[cfg(test)]
927mod tests_extended {
928 use super::*;
929
930 #[test]
931 fn test_permissions_all_denied() {
932 let perms = PdfPermissions {
933 allow_print: false,
934 allow_modify: false,
935 allow_copy: false,
936 allow_annotations: false,
937 allow_fill_forms: false,
938 allow_accessibility: false,
939 allow_assemble: false,
940 allow_print_high_quality: false,
941 };
942 let p = perms.to_p_value();
943 assert_eq!(p & (1 << 2), 0); assert_eq!(p & (1 << 3), 0); assert_eq!(p & (1 << 4), 0); assert_eq!(p & (1 << 5), 0); assert_eq!(p & (1 << 8), 0); assert_eq!(p & (1 << 9), 0); assert_eq!(p & (1 << 10), 0); assert_eq!(p & (1 << 11), 0); }
953
954 #[test]
955 fn test_permissions_only_print_allowed() {
956 let perms = PdfPermissions {
957 allow_print: true,
958 allow_modify: false,
959 allow_copy: false,
960 allow_annotations: false,
961 allow_fill_forms: false,
962 allow_accessibility: false,
963 allow_assemble: false,
964 allow_print_high_quality: false,
965 };
966 let p = perms.to_p_value();
967 assert_ne!(p & (1 << 2), 0); assert_eq!(p & (1 << 3), 0); assert_eq!(p & (1 << 4), 0); }
971
972 #[test]
973 fn test_pdf_security_new_stores_passwords() {
974 let perms = PdfPermissions::default();
975 let sec = PdfSecurity::new("owner_pw", "user_pw", perms);
976 assert_eq!(sec.owner_password, "owner_pw");
977 assert_eq!(sec.user_password, "user_pw");
978 assert_eq!(sec.key_length, 128);
979 assert_eq!(sec.algorithm, EncryptionAlgorithm::Rc4128);
980 }
981
982 #[test]
983 fn test_pdf_security_aes256_stores_correct_length() {
984 let perms = PdfPermissions::default();
985 let sec = PdfSecurity::new_aes256("owner", "user", perms);
986 assert_eq!(sec.key_length, 256);
987 assert_eq!(sec.algorithm, EncryptionAlgorithm::Aes256);
988 }
989
990 #[test]
991 fn test_rc4_different_objects_produce_different_ciphertext() {
992 let sec = PdfSecurity::new("owner", "user", PdfPermissions::default());
993 let file_id = generate_file_id("doc");
994 let dict = sec.compute_encryption_dict(&file_id);
995
996 let plaintext = b"same plaintext for both objects";
997 let enc_obj1 = dict.encrypt_data(plaintext, 1, 0);
998 let enc_obj2 = dict.encrypt_data(plaintext, 2, 0);
999 assert_ne!(enc_obj1, enc_obj2);
1001 }
1002
1003 #[test]
1004 fn test_aes256_different_objects_produce_different_ciphertext() {
1005 let sec = PdfSecurity::new_aes256("owner", "user", PdfPermissions::default());
1006 let dict = sec.compute_encryption_dict(&[]);
1007
1008 let plaintext = b"same plaintext";
1009 let enc_obj1 = dict.encrypt_data(plaintext, 1, 0);
1010 let enc_obj2 = dict.encrypt_data(plaintext, 2, 0);
1011 assert_ne!(enc_obj1, enc_obj2);
1012 }
1013
1014 #[test]
1015 fn test_rc4_empty_plaintext() {
1016 let sec = PdfSecurity::new("owner", "user", PdfPermissions::default());
1017 let file_id = generate_file_id("doc");
1018 let dict = sec.compute_encryption_dict(&file_id);
1019
1020 let encrypted = dict.encrypt_data(b"", 1, 0);
1021 assert!(encrypted.is_empty());
1023 }
1024
1025 #[test]
1026 fn test_generate_file_id_is_16_bytes() {
1027 let id = generate_file_id("test");
1028 assert_eq!(id.len(), 16);
1029 }
1030
1031 #[test]
1032 fn test_generate_file_id_different_seeds_differ() {
1033 let a = generate_file_id("seed_a");
1034 let b = generate_file_id("seed_b");
1035 assert_ne!(a, b);
1036 }
1037
1038 #[test]
1039 fn test_hex_encode_all_bytes() {
1040 let data: Vec<u8> = (0..=15).collect();
1041 let hex = hex_encode(&data);
1042 assert_eq!(hex, "000102030405060708090A0B0C0D0E0F");
1043 }
1044
1045 #[test]
1046 fn test_pkcs7_pad_exact_block() {
1047 let data = b"1234567890123456";
1049 let padded = pkcs7_pad(data, 16);
1050 assert_eq!(padded.len(), 32);
1051 assert_eq!(&padded[16..], &[16u8; 16]);
1052 }
1053
1054 #[test]
1055 fn test_encryption_dict_clone() {
1056 let sec = PdfSecurity::new("o", "u", PdfPermissions::default());
1057 let file_id = generate_file_id("clone");
1058 let dict = sec.compute_encryption_dict(&file_id);
1059 let dict2 = dict.clone();
1060 assert_eq!(dict.key_length, dict2.key_length);
1061 assert_eq!(dict.algorithm, dict2.algorithm);
1062 }
1063
1064 #[test]
1065 fn test_encryption_algorithm_debug() {
1066 let rc4 = EncryptionAlgorithm::Rc4128;
1067 let aes = EncryptionAlgorithm::Aes256;
1068 assert!(format!("{:?}", rc4).contains("Rc4"));
1069 assert!(format!("{:?}", aes).contains("Aes"));
1070 }
1071}