1use crate::encryption::Permissions;
4use crate::error::{PdfError, Result};
5use crate::objects::{Dictionary, Object};
6
7#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum EncryptionAlgorithm {
10 RC4,
12 AES128,
14 AES256,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq)]
20pub enum CryptFilterMethod {
21 None,
23 V2,
25 AESV2,
27 AESV3,
29}
30
31impl CryptFilterMethod {
32 pub fn pdf_name(&self) -> &'static str {
34 match self {
35 CryptFilterMethod::None => "None",
36 CryptFilterMethod::V2 => "V2",
37 CryptFilterMethod::AESV2 => "AESV2",
38 CryptFilterMethod::AESV3 => "AESV3",
39 }
40 }
41}
42
43#[derive(Debug, Clone)]
45pub enum StreamFilter {
46 Identity,
48 StdCF,
50 Custom(String),
52}
53
54#[derive(Debug, Clone)]
56pub enum StringFilter {
57 Identity,
59 StdCF,
61 Custom(String),
63}
64
65#[derive(Debug, Clone)]
67pub struct CryptFilter {
68 pub name: String,
70 pub method: CryptFilterMethod,
72 pub length: Option<u32>,
74}
75
76impl CryptFilter {
77 pub fn standard(method: CryptFilterMethod) -> Self {
79 Self {
80 name: "StdCF".to_string(),
81 method,
82 length: match method {
83 CryptFilterMethod::V2 => Some(16), _ => None,
85 },
86 }
87 }
88
89 pub fn to_dict(&self) -> Dictionary {
91 let mut dict = Dictionary::new();
92
93 dict.set("CFM", Object::Name(self.method.pdf_name().to_string()));
94
95 if let Some(length) = self.length {
96 dict.set("Length", Object::Integer(length as i64));
97 }
98
99 dict
100 }
101}
102
103#[derive(Debug, Clone)]
105pub struct EncryptionDictionary {
106 pub filter: String,
108 pub sub_filter: Option<String>,
110 pub v: u32,
112 pub length: Option<u32>,
114 pub cf: Option<Vec<CryptFilter>>,
116 pub stm_f: Option<StreamFilter>,
118 pub str_f: Option<StringFilter>,
120 pub ef: Option<String>,
122 pub r: u32,
124 pub o: Vec<u8>,
126 pub u: Vec<u8>,
128 pub p: Permissions,
130 pub encrypt_metadata: bool,
132 pub id: Option<Vec<u8>>,
134 pub ue: Option<Vec<u8>>,
136 pub oe: Option<Vec<u8>>,
138 pub perms: Option<Vec<u8>>,
140}
141
142impl EncryptionDictionary {
143 pub fn rc4_40bit(
145 owner_hash: Vec<u8>,
146 user_hash: Vec<u8>,
147 permissions: Permissions,
148 id: Option<Vec<u8>>,
149 ) -> Self {
150 Self {
151 filter: "Standard".to_string(),
152 sub_filter: None,
153 v: 1,
154 length: Some(5), cf: None,
156 stm_f: None,
157 str_f: None,
158 ef: None,
159 r: 2,
160 o: owner_hash,
161 u: user_hash,
162 p: permissions,
163 encrypt_metadata: true,
164 id,
165 ue: None,
166 oe: None,
167 perms: None,
168 }
169 }
170
171 pub fn rc4_128bit(
173 owner_hash: Vec<u8>,
174 user_hash: Vec<u8>,
175 permissions: Permissions,
176 id: Option<Vec<u8>>,
177 ) -> Self {
178 Self {
179 filter: "Standard".to_string(),
180 sub_filter: None,
181 v: 2,
182 length: Some(16), cf: None,
184 stm_f: None,
185 str_f: None,
186 ef: None,
187 r: 3,
188 o: owner_hash,
189 u: user_hash,
190 p: permissions,
191 encrypt_metadata: true,
192 id,
193 ue: None,
194 oe: None,
195 perms: None,
196 }
197 }
198
199 pub fn aes_128(
204 owner_hash: Vec<u8>,
205 user_hash: Vec<u8>,
206 permissions: Permissions,
207 id: Option<Vec<u8>>,
208 ) -> Self {
209 Self {
210 filter: "Standard".to_string(),
211 sub_filter: None,
212 v: 4,
213 length: Some(16), cf: Some(vec![CryptFilter::standard(CryptFilterMethod::AESV2)]),
215 stm_f: Some(StreamFilter::StdCF),
216 str_f: Some(StringFilter::StdCF),
217 ef: None,
218 r: 4,
219 o: owner_hash,
220 u: user_hash,
221 p: permissions,
222 encrypt_metadata: true,
223 id,
224 ue: None,
225 oe: None,
226 perms: None,
227 }
228 }
229
230 pub fn aes_256(
235 owner_hash: Vec<u8>,
236 user_hash: Vec<u8>,
237 permissions: Permissions,
238 id: Option<Vec<u8>>,
239 ) -> Self {
240 Self {
241 filter: "Standard".to_string(),
242 sub_filter: None,
243 v: 5,
244 length: Some(32), cf: Some(vec![CryptFilter::standard(CryptFilterMethod::AESV3)]),
246 stm_f: Some(StreamFilter::StdCF),
247 str_f: Some(StringFilter::StdCF),
248 ef: None,
249 r: 5,
250 o: owner_hash,
251 u: user_hash,
252 p: permissions,
253 encrypt_metadata: true,
254 id,
255 ue: None,
256 oe: None,
257 perms: None,
258 }
259 }
260
261 pub fn with_r5_entries(mut self, ue: Vec<u8>, oe: Vec<u8>) -> Self {
263 self.ue = Some(ue);
264 self.oe = Some(oe);
265 self
266 }
267
268 pub fn with_perms(mut self, perms: Vec<u8>) -> Result<Self> {
270 if perms.len() != 16 {
271 return Err(PdfError::EncryptionError(format!(
272 "V=5 Perms entry must be 16 bytes, got {}",
273 perms.len()
274 )));
275 }
276 self.perms = Some(perms);
277 Ok(self)
278 }
279
280 pub fn to_dict(&self) -> Dictionary {
282 let mut dict = Dictionary::new();
283
284 dict.set("Filter", Object::Name(self.filter.clone()));
285
286 if let Some(ref sub_filter) = self.sub_filter {
287 dict.set("SubFilter", Object::Name(sub_filter.clone()));
288 }
289
290 dict.set("V", Object::Integer(self.v as i64));
291
292 if let Some(length) = self.length {
293 dict.set("Length", Object::Integer((length * 8) as i64)); }
295
296 dict.set("R", Object::Integer(self.r as i64));
297 dict.set("O", Object::ByteString(self.o.clone()));
298 dict.set("U", Object::ByteString(self.u.clone()));
299 dict.set("P", Object::Integer(self.p.bits() as i32 as i64));
300
301 if !self.encrypt_metadata && self.v >= 4 {
302 dict.set("EncryptMetadata", Object::Boolean(false));
303 }
304
305 if let Some(ref cf_list) = self.cf {
307 let mut cf_dict = Dictionary::new();
308 for filter in cf_list {
309 cf_dict.set(&filter.name, Object::Dictionary(filter.to_dict()));
310 }
311 dict.set("CF", Object::Dictionary(cf_dict));
312 }
313
314 if let Some(ref stm_f) = self.stm_f {
316 match stm_f {
317 StreamFilter::Identity => dict.set("StmF", Object::Name("Identity".to_string())),
318 StreamFilter::StdCF => dict.set("StmF", Object::Name("StdCF".to_string())),
319 StreamFilter::Custom(name) => dict.set("StmF", Object::Name(name.clone())),
320 }
321 }
322
323 if let Some(ref str_f) = self.str_f {
325 match str_f {
326 StringFilter::Identity => dict.set("StrF", Object::Name("Identity".to_string())),
327 StringFilter::StdCF => dict.set("StrF", Object::Name("StdCF".to_string())),
328 StringFilter::Custom(name) => dict.set("StrF", Object::Name(name.clone())),
329 }
330 }
331
332 if let Some(ref ue) = self.ue {
334 dict.set("UE", Object::ByteString(ue.clone()));
335 }
336 if let Some(ref oe) = self.oe {
337 dict.set("OE", Object::ByteString(oe.clone()));
338 }
339 if let Some(ref perms) = self.perms {
340 dict.set("Perms", Object::ByteString(perms.clone()));
341 }
342
343 dict
344 }
345}
346
347#[cfg(test)]
348mod tests {
349 use super::*;
350
351 #[test]
352 fn test_crypt_filter_method() {
353 assert_eq!(CryptFilterMethod::None.pdf_name(), "None");
354 assert_eq!(CryptFilterMethod::V2.pdf_name(), "V2");
355 assert_eq!(CryptFilterMethod::AESV2.pdf_name(), "AESV2");
356 assert_eq!(CryptFilterMethod::AESV3.pdf_name(), "AESV3");
357 }
358
359 #[test]
360 fn test_crypt_filter() {
361 let filter = CryptFilter::standard(CryptFilterMethod::V2);
362 assert_eq!(filter.name, "StdCF");
363 assert_eq!(filter.method, CryptFilterMethod::V2);
364 assert_eq!(filter.length, Some(16));
365
366 let dict = filter.to_dict();
367 assert_eq!(dict.get("CFM"), Some(&Object::Name("V2".to_string())));
368 assert_eq!(dict.get("Length"), Some(&Object::Integer(16)));
369 }
370
371 #[test]
372 fn test_rc4_40bit_encryption_dict() {
373 let owner_hash = vec![0u8; 32];
374 let user_hash = vec![1u8; 32];
375 let permissions = Permissions::new();
376
377 let enc_dict = EncryptionDictionary::rc4_40bit(
378 owner_hash.clone(),
379 user_hash.clone(),
380 permissions,
381 None,
382 );
383
384 assert_eq!(enc_dict.filter, "Standard");
385 assert_eq!(enc_dict.v, 1);
386 assert_eq!(enc_dict.length, Some(5));
387 assert_eq!(enc_dict.r, 2);
388 assert_eq!(enc_dict.o, owner_hash);
389 assert_eq!(enc_dict.u, user_hash);
390 }
391
392 #[test]
393 fn test_rc4_128bit_encryption_dict() {
394 let owner_hash = vec![0u8; 32];
395 let user_hash = vec![1u8; 32];
396 let permissions = Permissions::all();
397
398 let enc_dict = EncryptionDictionary::rc4_128bit(owner_hash, user_hash, permissions, None);
399
400 assert_eq!(enc_dict.filter, "Standard");
401 assert_eq!(enc_dict.v, 2);
402 assert_eq!(enc_dict.length, Some(16));
403 assert_eq!(enc_dict.r, 3);
404 }
405
406 #[test]
407 fn test_encryption_dict_to_pdf() {
408 let enc_dict =
409 EncryptionDictionary::rc4_40bit(vec![0u8; 32], vec![1u8; 32], Permissions::new(), None);
410
411 let pdf_dict = enc_dict.to_dict();
412 assert_eq!(
413 pdf_dict.get("Filter"),
414 Some(&Object::Name("Standard".to_string()))
415 );
416 assert_eq!(pdf_dict.get("V"), Some(&Object::Integer(1)));
417 assert_eq!(pdf_dict.get("Length"), Some(&Object::Integer(40))); assert_eq!(pdf_dict.get("R"), Some(&Object::Integer(2)));
419 assert!(pdf_dict.get("O").is_some());
420 assert!(pdf_dict.get("U").is_some());
421 assert!(pdf_dict.get("P").is_some());
422 }
423
424 #[test]
425 fn test_stream_filter_names() {
426 let identity = StreamFilter::Identity;
427 let std_cf = StreamFilter::StdCF;
428 let custom = StreamFilter::Custom("MyFilter".to_string());
429
430 let _identity_clone = identity;
432 let _std_cf_clone = std_cf;
433 let _custom_clone = custom;
434 }
435
436 #[test]
437 fn test_string_filter_names() {
438 let identity = StringFilter::Identity;
439 let std_cf = StringFilter::StdCF;
440 let custom = StringFilter::Custom("MyStringFilter".to_string());
441
442 let _identity_clone = identity;
444 let _std_cf_clone = std_cf;
445 let _custom_clone = custom;
446 }
447
448 #[test]
449 fn test_encryption_algorithm_variants() {
450 assert_eq!(EncryptionAlgorithm::RC4, EncryptionAlgorithm::RC4);
451 assert_eq!(EncryptionAlgorithm::AES128, EncryptionAlgorithm::AES128);
452 assert_eq!(EncryptionAlgorithm::AES256, EncryptionAlgorithm::AES256);
453 assert_ne!(EncryptionAlgorithm::RC4, EncryptionAlgorithm::AES128);
454
455 let _ = format!("{:?}", EncryptionAlgorithm::RC4);
457 let _ = format!("{:?}", EncryptionAlgorithm::AES128);
458 let _ = format!("{:?}", EncryptionAlgorithm::AES256);
459 }
460
461 #[test]
462 fn test_crypt_filter_method_variants() {
463 assert_eq!(CryptFilterMethod::None, CryptFilterMethod::None);
464 assert_eq!(CryptFilterMethod::V2, CryptFilterMethod::V2);
465 assert_eq!(CryptFilterMethod::AESV2, CryptFilterMethod::AESV2);
466 assert_eq!(CryptFilterMethod::AESV3, CryptFilterMethod::AESV3);
467 assert_ne!(CryptFilterMethod::None, CryptFilterMethod::V2);
468
469 let _ = format!("{:?}", CryptFilterMethod::None);
471 let _ = format!("{:?}", CryptFilterMethod::V2);
472 let _ = format!("{:?}", CryptFilterMethod::AESV2);
473 let _ = format!("{:?}", CryptFilterMethod::AESV3);
474 }
475
476 #[test]
477 fn test_crypt_filter_custom() {
478 let filter = CryptFilter {
479 name: "CustomFilter".to_string(),
480 method: CryptFilterMethod::AESV2,
481 length: Some(32),
482 };
483
484 let dict = filter.to_dict();
485 assert_eq!(dict.get("CFM"), Some(&Object::Name("AESV2".to_string())));
486 assert_eq!(dict.get("Length"), Some(&Object::Integer(32)));
487 }
488
489 #[test]
490 fn test_crypt_filter_no_optional_fields() {
491 let filter = CryptFilter {
492 name: "MinimalFilter".to_string(),
493 method: CryptFilterMethod::V2,
494 length: None,
495 };
496
497 let dict = filter.to_dict();
498 assert_eq!(dict.get("CFM"), Some(&Object::Name("V2".to_string())));
499 assert!(dict.get("Length").is_none());
500 }
501
502 #[test]
503 fn test_encryption_dict_with_file_id() {
504 let owner_hash = vec![0u8; 32];
505 let user_hash = vec![1u8; 32];
506 let permissions = Permissions::new();
507 let file_id = vec![42u8; 16];
508
509 let enc_dict =
510 EncryptionDictionary::rc4_40bit(owner_hash, user_hash, permissions, Some(file_id));
511
512 assert_eq!(enc_dict.filter, "Standard");
514 assert_eq!(enc_dict.v, 1);
515 }
516
517 #[test]
518 fn test_encryption_dict_rc4_128bit_with_metadata() {
519 let owner_hash = vec![0u8; 32];
520 let user_hash = vec![1u8; 32];
521 let permissions = Permissions::all();
522
523 let enc_dict = EncryptionDictionary::rc4_128bit(owner_hash, user_hash, permissions, None);
524
525 assert_eq!(enc_dict.v, 2);
526 assert_eq!(enc_dict.length, Some(16));
527 assert_eq!(enc_dict.r, 3);
528 assert!(enc_dict.encrypt_metadata);
529 }
530
531 #[test]
532 fn test_encryption_dict_to_pdf_with_metadata_false() {
533 let mut enc_dict = EncryptionDictionary::rc4_128bit(
534 vec![0u8; 32],
535 vec![1u8; 32],
536 Permissions::new(),
537 None,
538 );
539 enc_dict.encrypt_metadata = false;
540 enc_dict.v = 4; let pdf_dict = enc_dict.to_dict();
543 assert_eq!(
544 pdf_dict.get("EncryptMetadata"),
545 Some(&Object::Boolean(false))
546 );
547 }
548
549 #[test]
550 fn test_encryption_dict_with_crypt_filters() {
551 let mut enc_dict = EncryptionDictionary::rc4_128bit(
552 vec![0u8; 32],
553 vec![1u8; 32],
554 Permissions::new(),
555 None,
556 );
557
558 let filter = CryptFilter::standard(CryptFilterMethod::AESV2);
559 enc_dict.cf = Some(vec![filter]);
560 enc_dict.stm_f = Some(StreamFilter::StdCF);
561 enc_dict.str_f = Some(StringFilter::StdCF);
562
563 let pdf_dict = enc_dict.to_dict();
564 assert!(pdf_dict.get("CF").is_some());
565 assert_eq!(
566 pdf_dict.get("StmF"),
567 Some(&Object::Name("StdCF".to_string()))
568 );
569 assert_eq!(
570 pdf_dict.get("StrF"),
571 Some(&Object::Name("StdCF".to_string()))
572 );
573 }
574
575 #[test]
576 fn test_encryption_dict_with_identity_filters() {
577 let mut enc_dict = EncryptionDictionary::rc4_128bit(
578 vec![0u8; 32],
579 vec![1u8; 32],
580 Permissions::new(),
581 None,
582 );
583
584 enc_dict.stm_f = Some(StreamFilter::Identity);
585 enc_dict.str_f = Some(StringFilter::Identity);
586
587 let pdf_dict = enc_dict.to_dict();
588 assert_eq!(
589 pdf_dict.get("StmF"),
590 Some(&Object::Name("Identity".to_string()))
591 );
592 assert_eq!(
593 pdf_dict.get("StrF"),
594 Some(&Object::Name("Identity".to_string()))
595 );
596 }
597
598 #[test]
599 fn test_encryption_dict_with_custom_filters() {
600 let mut enc_dict = EncryptionDictionary::rc4_128bit(
601 vec![0u8; 32],
602 vec![1u8; 32],
603 Permissions::new(),
604 None,
605 );
606
607 enc_dict.stm_f = Some(StreamFilter::Custom("MyStreamFilter".to_string()));
608 enc_dict.str_f = Some(StringFilter::Custom("MyStringFilter".to_string()));
609
610 let pdf_dict = enc_dict.to_dict();
611 assert_eq!(
612 pdf_dict.get("StmF"),
613 Some(&Object::Name("MyStreamFilter".to_string()))
614 );
615 assert_eq!(
616 pdf_dict.get("StrF"),
617 Some(&Object::Name("MyStringFilter".to_string()))
618 );
619 }
620
621 #[test]
622 fn test_multiple_crypt_filters() {
623 let mut enc_dict = EncryptionDictionary::rc4_128bit(
624 vec![0u8; 32],
625 vec![1u8; 32],
626 Permissions::new(),
627 None,
628 );
629
630 let filter1 = CryptFilter::standard(CryptFilterMethod::V2);
631 let filter2 = CryptFilter {
632 name: "AESFilter".to_string(),
633 method: CryptFilterMethod::AESV2,
634 length: Some(16),
635 };
636
637 enc_dict.cf = Some(vec![filter1, filter2]);
638
639 let pdf_dict = enc_dict.to_dict();
640 if let Some(Object::Dictionary(cf_dict)) = pdf_dict.get("CF") {
641 assert!(cf_dict.get("StdCF").is_some());
642 assert!(cf_dict.get("AESFilter").is_some());
643 } else {
644 panic!("CF should be a dictionary");
645 }
646 }
647}