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