1use aes::cipher::{BlockCipherEncrypt, KeyInit};
12use aes::{Aes128, Aes256};
13use cbc::cipher::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 cipher = Aes128::new_from_slice(&self.encryption_key[..16])
523 .expect("AES-128 key length is 16 bytes");
524 let mut block = aes::Block::try_from(data.as_slice()).expect("block is exactly 16 bytes");
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 type Aes256Cbc = cbc::Encryptor<Aes256>;
542 let cipher =
543 Aes256Cbc::new_from_slices(&key[..32], &iv_arr).expect("AES-256 key/IV lengths valid");
544 let ciphertext = aes256_cbc_encrypt_with_pkcs7(cipher, data);
545
546 let mut result = iv_arr.to_vec();
547 result.extend_from_slice(&ciphertext);
548 result
549 }
550}
551
552const PADDING: [u8; 32] = [
558 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
559 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A,
560];
561
562fn pad_password(password: &str) -> Vec<u8> {
568 let pwd_bytes = password.as_bytes();
569 let mut padded = Vec::with_capacity(32);
570
571 if pwd_bytes.len() >= 32 {
572 padded.extend_from_slice(&pwd_bytes[..32]);
573 } else {
574 padded.extend_from_slice(pwd_bytes);
575 let remaining = 32 - pwd_bytes.len();
576 padded.extend_from_slice(&PADDING[..remaining]);
577 }
578
579 padded
580}
581
582fn md5_hash(data: &[u8]) -> Vec<u8> {
584 let mut hasher = Md5::new();
585 hasher.update(data);
586 hasher.finalize().to_vec()
587}
588
589fn sha256_hash_parts(parts: &[&[u8]]) -> Vec<u8> {
591 let mut hasher = Sha256::new();
592 for part in parts {
593 hasher.update(part);
594 }
595 hasher.finalize().to_vec()
596}
597
598fn rc4_encrypt(key: &[u8], data: &[u8]) -> Vec<u8> {
600 let mut s: Vec<u8> = (0..=255).map(|i| i as u8).collect();
602 let mut j: usize = 0;
603 for i in 0..256 {
604 j = (j + s[i] as usize + key[i % key.len()] as usize) % 256;
605 s.swap(i, j);
606 }
607
608 let mut output = Vec::with_capacity(data.len());
610 let mut i: usize = 0;
611 j = 0;
612 for &byte in data {
613 i = (i + 1) % 256;
614 j = (j + s[i] as usize) % 256;
615 s.swap(i, j);
616 let k = s[(s[i] as usize + s[j] as usize) % 256];
617 output.push(byte ^ k);
618 }
619
620 output
621}
622
623fn encrypt_aes256_cbc(data: &[u8], key: &[u8], obj_num: u32) -> Vec<u8> {
627 let mut iv_src = [0u8; 36];
629 iv_src[..32].copy_from_slice(&key[..32]);
630 iv_src[32..36].copy_from_slice(&obj_num.to_le_bytes());
631 let iv_hash = sha256_hash_parts(&[&iv_src]);
632 let mut iv = [0u8; 16];
633 iv.copy_from_slice(&iv_hash[..16]);
634
635 type Aes256Cbc = cbc::Encryptor<Aes256>;
636 let cipher = Aes256Cbc::new_from_slices(&key[..32], &iv).expect("AES-256 key/IV lengths valid");
637 let ciphertext = aes256_cbc_encrypt_with_pkcs7(cipher, data);
638
639 let mut result = iv.to_vec();
640 result.extend_from_slice(&ciphertext);
641 result
642}
643
644fn aes256_cbc_encrypt_no_iv(data: &[u8], key: &[u8]) -> Vec<u8> {
648 let iv = [0u8; 16];
649 type Aes256Cbc = cbc::Encryptor<Aes256>;
650 let cipher = Aes256Cbc::new_from_slices(&key[..32], &iv).expect("AES-256 key/IV lengths valid");
651 aes256_cbc_encrypt_with_pkcs7(cipher, data)
652}
653
654fn aes256_ecb_encrypt_block(data: &[u8; 16], key: &[u8]) -> [u8; 16] {
656 let cipher = Aes256::new_from_slice(&key[..32]).expect("AES-256 key length is 32 bytes");
657 let mut block = aes::Block::try_from(data.as_slice()).expect("block is exactly 16 bytes");
658 cipher.encrypt_block(&mut block);
659 block.into()
660}
661
662fn pkcs7_pad(data: &[u8], block_size: usize) -> Vec<u8> {
664 let pad_len = block_size - (data.len() % block_size);
665 let mut padded = data.to_vec();
666 for _ in 0..pad_len {
667 padded.push(pad_len as u8);
668 }
669 padded
670}
671
672fn aes256_cbc_encrypt_with_pkcs7(mut cipher: cbc::Encryptor<Aes256>, data: &[u8]) -> Vec<u8> {
677 use cbc::cipher::BlockModeEncrypt;
678 let padded = pkcs7_pad(data, 16);
679 let mut out = padded;
681 let block_count = out.len() / 16;
682 for i in 0..block_count {
683 let start = i * 16;
684 let end = start + 16;
685 let mut block = aes::Block::try_from(&out[start..end]).expect("block is exactly 16 bytes");
686 cipher.encrypt_block(&mut block);
687 out[start..end].copy_from_slice(&block);
688 }
689 out
690}
691
692fn derive_salt(password: &str, tag: &[u8]) -> [u8; 8] {
694 let hash = sha256_hash_parts(&[password.as_bytes(), tag]);
695 let mut salt = [0u8; 8];
696 salt.copy_from_slice(&hash[..8]);
697 salt
698}
699
700fn derive_file_enc_key(owner_pass: &str, user_pass: &str) -> Vec<u8> {
702 sha256_hash_parts(&[owner_pass.as_bytes(), user_pass.as_bytes(), b"file_enc_key"])
703}
704
705fn hex_encode(data: &[u8]) -> String {
707 data.iter().map(|b| format!("{:02X}", b)).collect()
708}
709
710pub fn generate_file_id(seed: &str) -> Vec<u8> {
716 md5_hash(seed.as_bytes())
717}
718
719#[cfg(test)]
724mod tests {
725 use super::*;
726
727 #[test]
728 fn test_pad_password_empty() {
729 let padded = pad_password("");
730 assert_eq!(padded.len(), 32);
731 assert_eq!(padded, PADDING.to_vec());
732 }
733
734 #[test]
735 fn test_pad_password_short() {
736 let padded = pad_password("test");
737 assert_eq!(padded.len(), 32);
738 assert_eq!(&padded[..4], b"test");
739 assert_eq!(&padded[4..], &PADDING[..28]);
740 }
741
742 #[test]
743 fn test_pad_password_long() {
744 let long_pwd = "a".repeat(40);
745 let padded = pad_password(&long_pwd);
746 assert_eq!(padded.len(), 32);
747 assert_eq!(padded, vec![b'a'; 32]);
748 }
749
750 #[test]
751 fn test_permissions_default_all_allowed() {
752 let perms = PdfPermissions::default();
753 let p = perms.to_p_value();
754 assert!(p & (1 << 2) != 0); assert!(p & (1 << 3) != 0); assert!(p & (1 << 4) != 0); assert!(p & (1 << 5) != 0); }
760
761 #[test]
762 fn test_permissions_restricted() {
763 let perms = PdfPermissions {
764 allow_print: false,
765 allow_copy: false,
766 allow_modify: false,
767 allow_annotations: false,
768 ..Default::default()
769 };
770 let p = perms.to_p_value();
771 assert!(p & (1 << 2) == 0); assert!(p & (1 << 3) == 0); assert!(p & (1 << 4) == 0); assert!(p & (1 << 5) == 0); }
776
777 #[test]
778 fn test_rc4_encrypt_decrypt_roundtrip() {
779 let key = b"testkey123456789";
780 let plaintext = b"Hello, World! This is a PDF encryption test.";
781 let encrypted = rc4_encrypt(key, plaintext);
782 let decrypted = rc4_encrypt(key, &encrypted);
783 assert_eq!(decrypted, plaintext);
784 }
785
786 #[test]
787 fn test_rc4_encryption_dict_computation() {
788 let security = PdfSecurity::new("owner", "user", PdfPermissions::default());
789 let file_id = generate_file_id("test-document");
790 let dict = security.compute_encryption_dict(&file_id);
791
792 assert_eq!(dict.o_value.len(), 32);
793 assert_eq!(dict.u_value.len(), 32);
794 assert_eq!(dict.key_length, 128);
795 assert_eq!(dict.revision, 3);
796 assert_eq!(dict.version, 2);
797 assert_eq!(dict.algorithm, EncryptionAlgorithm::Rc4128);
798 }
799
800 #[test]
801 fn test_rc4_object_encryption_roundtrip() {
802 let security = PdfSecurity::new("owner", "user", PdfPermissions::default());
803 let file_id = generate_file_id("test-doc");
804 let dict = security.compute_encryption_dict(&file_id);
805
806 let plaintext = b"This is a test stream content for PDF object.";
807 let encrypted = dict.encrypt_data(plaintext, 5, 0);
808 let decrypted = dict.encrypt_data(&encrypted, 5, 0);
809 assert_eq!(decrypted, plaintext);
810 }
811
812 #[test]
813 fn test_rc4_encryption_dict_pdf_output() {
814 let security = PdfSecurity::new("owner", "", PdfPermissions::default());
815 let file_id = generate_file_id("test");
816 let dict = security.compute_encryption_dict(&file_id);
817 let pdf_str = dict.to_pdf_dict(10);
818
819 assert!(pdf_str.contains("/Filter /Standard"));
820 assert!(pdf_str.contains("/V 2"));
821 assert!(pdf_str.contains("/R 3"));
822 assert!(pdf_str.contains("/Length 128"));
823 assert!(pdf_str.contains("/O <"));
824 assert!(pdf_str.contains("/U <"));
825 }
826
827 #[test]
828 fn test_aes256_encryption_dict_computation() {
829 let security =
830 PdfSecurity::new_aes256("owner_pass", "user_pass", PdfPermissions::default());
831 let dict = security.compute_encryption_dict(&[]);
832
833 assert_eq!(dict.o_value.len(), 48);
835 assert_eq!(dict.u_value.len(), 48);
836 assert_eq!(dict.key_length, 256);
837 assert_eq!(dict.revision, 6);
838 assert_eq!(dict.version, 5);
839 assert_eq!(dict.algorithm, EncryptionAlgorithm::Aes256);
840
841 assert!(dict.oe_value.is_some());
843 assert!(dict.ue_value.is_some());
844 assert!(dict.perms_value.is_some());
845
846 assert_eq!(
848 dict.perms_value
849 .as_ref()
850 .expect("test: should succeed")
851 .len(),
852 16
853 );
854 }
855
856 #[test]
857 fn test_aes256_encryption_dict_pdf_output() {
858 let security =
859 PdfSecurity::new_aes256("owner_pass", "user_pass", PdfPermissions::default());
860 let dict = security.compute_encryption_dict(&[]);
861 let pdf_str = dict.to_pdf_dict(10);
862
863 assert!(pdf_str.contains("/Filter /Standard"));
864 assert!(pdf_str.contains("/V 5"));
865 assert!(pdf_str.contains("/R 6"));
866 assert!(pdf_str.contains("/Length 256"));
867 assert!(pdf_str.contains("/O <"));
868 assert!(pdf_str.contains("/U <"));
869 assert!(pdf_str.contains("/OE <"));
870 assert!(pdf_str.contains("/UE <"));
871 assert!(pdf_str.contains("/Perms <"));
872 assert!(pdf_str.contains("/CFM /AESV3"));
873 assert!(pdf_str.contains("/StmF /StdCF"));
874 assert!(pdf_str.contains("/StrF /StdCF"));
875 }
876
877 #[test]
878 fn test_aes256_stream_encryption() {
879 let security =
880 PdfSecurity::new_aes256("owner_pass", "user_pass", PdfPermissions::default());
881 let dict = security.compute_encryption_dict(&[]);
882
883 let plaintext = b"Sensitive PDF stream content.";
884 let encrypted = dict.encrypt_data(plaintext, 10, 0);
885
886 assert!(encrypted.len() > plaintext.len());
888 assert_ne!(&encrypted[16..16 + plaintext.len()], plaintext.as_slice());
890 }
891
892 #[test]
893 fn test_aes256_deterministic() {
894 let security =
896 PdfSecurity::new_aes256("owner_pass", "user_pass", PdfPermissions::default());
897 let dict = security.compute_encryption_dict(&[]);
898
899 let plaintext = b"Test data for AES-256.";
900 let enc1 = dict.encrypt_data(plaintext, 5, 0);
901 let enc2 = dict.encrypt_data(plaintext, 5, 0);
902 assert_eq!(enc1, enc2);
903 }
904
905 #[test]
906 fn test_hex_encode() {
907 assert_eq!(hex_encode(&[0xFF, 0x00, 0xAB]), "FF00AB");
908 assert_eq!(hex_encode(&[]), "");
909 }
910
911 #[test]
912 fn test_file_id_generation() {
913 let id1 = generate_file_id("doc1");
914 let id2 = generate_file_id("doc2");
915 assert_eq!(id1.len(), 16);
916 assert_ne!(id1, id2);
917 }
918
919 #[test]
920 fn test_encryption_algorithm_default() {
921 let algo = EncryptionAlgorithm::default();
922 assert_eq!(algo, EncryptionAlgorithm::Rc4128);
923 }
924
925 #[test]
926 fn test_pkcs7_pad() {
927 let data = b"Hello, World!";
929 let padded = pkcs7_pad(data, 16);
930 assert_eq!(padded.len(), 16);
931 assert_eq!(&padded[13..], &[3u8, 3, 3]);
932 }
933}
934
935#[cfg(test)]
936mod tests_extended {
937 use super::*;
938
939 #[test]
940 fn test_permissions_all_denied() {
941 let perms = PdfPermissions {
942 allow_print: false,
943 allow_modify: false,
944 allow_copy: false,
945 allow_annotations: false,
946 allow_fill_forms: false,
947 allow_accessibility: false,
948 allow_assemble: false,
949 allow_print_high_quality: false,
950 };
951 let p = perms.to_p_value();
952 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); }
962
963 #[test]
964 fn test_permissions_only_print_allowed() {
965 let perms = PdfPermissions {
966 allow_print: true,
967 allow_modify: false,
968 allow_copy: false,
969 allow_annotations: false,
970 allow_fill_forms: false,
971 allow_accessibility: false,
972 allow_assemble: false,
973 allow_print_high_quality: false,
974 };
975 let p = perms.to_p_value();
976 assert_ne!(p & (1 << 2), 0); assert_eq!(p & (1 << 3), 0); assert_eq!(p & (1 << 4), 0); }
980
981 #[test]
982 fn test_pdf_security_new_stores_passwords() {
983 let perms = PdfPermissions::default();
984 let sec = PdfSecurity::new("owner_pw", "user_pw", perms);
985 assert_eq!(sec.owner_password, "owner_pw");
986 assert_eq!(sec.user_password, "user_pw");
987 assert_eq!(sec.key_length, 128);
988 assert_eq!(sec.algorithm, EncryptionAlgorithm::Rc4128);
989 }
990
991 #[test]
992 fn test_pdf_security_aes256_stores_correct_length() {
993 let perms = PdfPermissions::default();
994 let sec = PdfSecurity::new_aes256("owner", "user", perms);
995 assert_eq!(sec.key_length, 256);
996 assert_eq!(sec.algorithm, EncryptionAlgorithm::Aes256);
997 }
998
999 #[test]
1000 fn test_rc4_different_objects_produce_different_ciphertext() {
1001 let sec = PdfSecurity::new("owner", "user", PdfPermissions::default());
1002 let file_id = generate_file_id("doc");
1003 let dict = sec.compute_encryption_dict(&file_id);
1004
1005 let plaintext = b"same plaintext for both objects";
1006 let enc_obj1 = dict.encrypt_data(plaintext, 1, 0);
1007 let enc_obj2 = dict.encrypt_data(plaintext, 2, 0);
1008 assert_ne!(enc_obj1, enc_obj2);
1010 }
1011
1012 #[test]
1013 fn test_aes256_different_objects_produce_different_ciphertext() {
1014 let sec = PdfSecurity::new_aes256("owner", "user", PdfPermissions::default());
1015 let dict = sec.compute_encryption_dict(&[]);
1016
1017 let plaintext = b"same plaintext";
1018 let enc_obj1 = dict.encrypt_data(plaintext, 1, 0);
1019 let enc_obj2 = dict.encrypt_data(plaintext, 2, 0);
1020 assert_ne!(enc_obj1, enc_obj2);
1021 }
1022
1023 #[test]
1024 fn test_rc4_empty_plaintext() {
1025 let sec = PdfSecurity::new("owner", "user", PdfPermissions::default());
1026 let file_id = generate_file_id("doc");
1027 let dict = sec.compute_encryption_dict(&file_id);
1028
1029 let encrypted = dict.encrypt_data(b"", 1, 0);
1030 assert!(encrypted.is_empty());
1032 }
1033
1034 #[test]
1035 fn test_generate_file_id_is_16_bytes() {
1036 let id = generate_file_id("test");
1037 assert_eq!(id.len(), 16);
1038 }
1039
1040 #[test]
1041 fn test_generate_file_id_different_seeds_differ() {
1042 let a = generate_file_id("seed_a");
1043 let b = generate_file_id("seed_b");
1044 assert_ne!(a, b);
1045 }
1046
1047 #[test]
1048 fn test_hex_encode_all_bytes() {
1049 let data: Vec<u8> = (0..=15).collect();
1050 let hex = hex_encode(&data);
1051 assert_eq!(hex, "000102030405060708090A0B0C0D0E0F");
1052 }
1053
1054 #[test]
1055 fn test_pkcs7_pad_exact_block() {
1056 let data = b"1234567890123456";
1058 let padded = pkcs7_pad(data, 16);
1059 assert_eq!(padded.len(), 32);
1060 assert_eq!(&padded[16..], &[16u8; 16]);
1061 }
1062
1063 #[test]
1064 fn test_encryption_dict_clone() {
1065 let sec = PdfSecurity::new("o", "u", PdfPermissions::default());
1066 let file_id = generate_file_id("clone");
1067 let dict = sec.compute_encryption_dict(&file_id);
1068 let dict2 = dict.clone();
1069 assert_eq!(dict.key_length, dict2.key_length);
1070 assert_eq!(dict.algorithm, dict2.algorithm);
1071 }
1072
1073 #[test]
1074 fn test_encryption_algorithm_debug() {
1075 let rc4 = EncryptionAlgorithm::Rc4128;
1076 let aes = EncryptionAlgorithm::Aes256;
1077 assert!(format!("{:?}", rc4).contains("Rc4"));
1078 assert!(format!("{:?}", aes).contains("Aes"));
1079 }
1080}