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}
134
135impl EncryptionDictionary {
136 pub fn rc4_40bit(
138 owner_hash: Vec<u8>,
139 user_hash: Vec<u8>,
140 permissions: Permissions,
141 id: Option<Vec<u8>>,
142 ) -> Self {
143 Self {
144 filter: "Standard".to_string(),
145 sub_filter: None,
146 v: 1,
147 length: Some(5), cf: None,
149 stm_f: None,
150 str_f: None,
151 ef: None,
152 r: 2,
153 o: owner_hash,
154 u: user_hash,
155 p: permissions,
156 encrypt_metadata: true,
157 id,
158 }
159 }
160
161 pub fn rc4_128bit(
163 owner_hash: Vec<u8>,
164 user_hash: Vec<u8>,
165 permissions: Permissions,
166 id: Option<Vec<u8>>,
167 ) -> Self {
168 Self {
169 filter: "Standard".to_string(),
170 sub_filter: None,
171 v: 2,
172 length: Some(16), cf: None,
174 stm_f: None,
175 str_f: None,
176 ef: None,
177 r: 3,
178 o: owner_hash,
179 u: user_hash,
180 p: permissions,
181 encrypt_metadata: true,
182 id,
183 }
184 }
185
186 pub fn to_dict(&self) -> Dictionary {
188 let mut dict = Dictionary::new();
189
190 dict.set("Filter", Object::Name(self.filter.clone()));
191
192 if let Some(ref sub_filter) = self.sub_filter {
193 dict.set("SubFilter", Object::Name(sub_filter.clone()));
194 }
195
196 dict.set("V", Object::Integer(self.v as i64));
197
198 if let Some(length) = self.length {
199 dict.set("Length", Object::Integer((length * 8) as i64)); }
201
202 dict.set("R", Object::Integer(self.r as i64));
203 dict.set(
204 "O",
205 Object::String(String::from_utf8_lossy(&self.o).to_string()),
206 );
207 dict.set(
208 "U",
209 Object::String(String::from_utf8_lossy(&self.u).to_string()),
210 );
211 dict.set("P", Object::Integer(self.p.bits() as i32 as i64));
212
213 if !self.encrypt_metadata && self.v >= 4 {
214 dict.set("EncryptMetadata", Object::Boolean(false));
215 }
216
217 if let Some(ref cf_list) = self.cf {
219 let mut cf_dict = Dictionary::new();
220 for filter in cf_list {
221 cf_dict.set(&filter.name, Object::Dictionary(filter.to_dict()));
222 }
223 dict.set("CF", Object::Dictionary(cf_dict));
224 }
225
226 if let Some(ref stm_f) = self.stm_f {
228 match stm_f {
229 StreamFilter::Identity => dict.set("StmF", Object::Name("Identity".to_string())),
230 StreamFilter::StdCF => dict.set("StmF", Object::Name("StdCF".to_string())),
231 StreamFilter::Custom(name) => dict.set("StmF", Object::Name(name.clone())),
232 }
233 }
234
235 if let Some(ref str_f) = self.str_f {
237 match str_f {
238 StringFilter::Identity => dict.set("StrF", Object::Name("Identity".to_string())),
239 StringFilter::StdCF => dict.set("StrF", Object::Name("StdCF".to_string())),
240 StringFilter::Custom(name) => dict.set("StrF", Object::Name(name.clone())),
241 }
242 }
243
244 dict
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251
252 #[test]
253 fn test_crypt_filter_method() {
254 assert_eq!(CryptFilterMethod::None.pdf_name(), "None");
255 assert_eq!(CryptFilterMethod::V2.pdf_name(), "V2");
256 assert_eq!(CryptFilterMethod::AESV2.pdf_name(), "AESV2");
257 assert_eq!(CryptFilterMethod::AESV3.pdf_name(), "AESV3");
258 }
259
260 #[test]
261 fn test_crypt_filter() {
262 let filter = CryptFilter::standard(CryptFilterMethod::V2);
263 assert_eq!(filter.name, "StdCF");
264 assert_eq!(filter.method, CryptFilterMethod::V2);
265 assert_eq!(filter.length, Some(16));
266
267 let dict = filter.to_dict();
268 assert_eq!(dict.get("CFM"), Some(&Object::Name("V2".to_string())));
269 assert_eq!(dict.get("Length"), Some(&Object::Integer(16)));
270 }
271
272 #[test]
273 fn test_rc4_40bit_encryption_dict() {
274 let owner_hash = vec![0u8; 32];
275 let user_hash = vec![1u8; 32];
276 let permissions = Permissions::new();
277
278 let enc_dict = EncryptionDictionary::rc4_40bit(
279 owner_hash.clone(),
280 user_hash.clone(),
281 permissions,
282 None,
283 );
284
285 assert_eq!(enc_dict.filter, "Standard");
286 assert_eq!(enc_dict.v, 1);
287 assert_eq!(enc_dict.length, Some(5));
288 assert_eq!(enc_dict.r, 2);
289 assert_eq!(enc_dict.o, owner_hash);
290 assert_eq!(enc_dict.u, user_hash);
291 }
292
293 #[test]
294 fn test_rc4_128bit_encryption_dict() {
295 let owner_hash = vec![0u8; 32];
296 let user_hash = vec![1u8; 32];
297 let permissions = Permissions::all();
298
299 let enc_dict = EncryptionDictionary::rc4_128bit(
300 owner_hash.clone(),
301 user_hash.clone(),
302 permissions,
303 None,
304 );
305
306 assert_eq!(enc_dict.filter, "Standard");
307 assert_eq!(enc_dict.v, 2);
308 assert_eq!(enc_dict.length, Some(16));
309 assert_eq!(enc_dict.r, 3);
310 }
311
312 #[test]
313 fn test_encryption_dict_to_pdf() {
314 let enc_dict =
315 EncryptionDictionary::rc4_40bit(vec![0u8; 32], vec![1u8; 32], Permissions::new(), None);
316
317 let pdf_dict = enc_dict.to_dict();
318 assert_eq!(
319 pdf_dict.get("Filter"),
320 Some(&Object::Name("Standard".to_string()))
321 );
322 assert_eq!(pdf_dict.get("V"), Some(&Object::Integer(1)));
323 assert_eq!(pdf_dict.get("Length"), Some(&Object::Integer(40))); assert_eq!(pdf_dict.get("R"), Some(&Object::Integer(2)));
325 assert!(pdf_dict.get("O").is_some());
326 assert!(pdf_dict.get("U").is_some());
327 assert!(pdf_dict.get("P").is_some());
328 }
329
330 #[test]
331 fn test_stream_filter_names() {
332 let identity = StreamFilter::Identity;
333 let std_cf = StreamFilter::StdCF;
334 let custom = StreamFilter::Custom("MyFilter".to_string());
335
336 let _identity_clone = identity.clone();
338 let _std_cf_clone = std_cf.clone();
339 let _custom_clone = custom.clone();
340 }
341
342 #[test]
343 fn test_string_filter_names() {
344 let identity = StringFilter::Identity;
345 let std_cf = StringFilter::StdCF;
346 let custom = StringFilter::Custom("MyStringFilter".to_string());
347
348 let _identity_clone = identity.clone();
350 let _std_cf_clone = std_cf.clone();
351 let _custom_clone = custom.clone();
352 }
353
354 #[test]
355 fn test_encryption_algorithm_variants() {
356 assert_eq!(EncryptionAlgorithm::RC4, EncryptionAlgorithm::RC4);
357 assert_eq!(EncryptionAlgorithm::AES128, EncryptionAlgorithm::AES128);
358 assert_eq!(EncryptionAlgorithm::AES256, EncryptionAlgorithm::AES256);
359 assert_ne!(EncryptionAlgorithm::RC4, EncryptionAlgorithm::AES128);
360
361 let _ = format!("{:?}", EncryptionAlgorithm::RC4);
363 let _ = format!("{:?}", EncryptionAlgorithm::AES128);
364 let _ = format!("{:?}", EncryptionAlgorithm::AES256);
365 }
366
367 #[test]
368 fn test_crypt_filter_method_variants() {
369 assert_eq!(CryptFilterMethod::None, CryptFilterMethod::None);
370 assert_eq!(CryptFilterMethod::V2, CryptFilterMethod::V2);
371 assert_eq!(CryptFilterMethod::AESV2, CryptFilterMethod::AESV2);
372 assert_eq!(CryptFilterMethod::AESV3, CryptFilterMethod::AESV3);
373 assert_ne!(CryptFilterMethod::None, CryptFilterMethod::V2);
374
375 let _ = format!("{:?}", CryptFilterMethod::None);
377 let _ = format!("{:?}", CryptFilterMethod::V2);
378 let _ = format!("{:?}", CryptFilterMethod::AESV2);
379 let _ = format!("{:?}", CryptFilterMethod::AESV3);
380 }
381
382 #[test]
383 fn test_crypt_filter_custom() {
384 let filter = CryptFilter {
385 name: "CustomFilter".to_string(),
386 method: CryptFilterMethod::AESV2,
387 length: Some(32),
388 };
389
390 let dict = filter.to_dict();
391 assert_eq!(dict.get("CFM"), Some(&Object::Name("AESV2".to_string())));
392 assert_eq!(dict.get("Length"), Some(&Object::Integer(32)));
393 }
394
395 #[test]
396 fn test_crypt_filter_no_optional_fields() {
397 let filter = CryptFilter {
398 name: "MinimalFilter".to_string(),
399 method: CryptFilterMethod::V2,
400 length: None,
401 };
402
403 let dict = filter.to_dict();
404 assert_eq!(dict.get("CFM"), Some(&Object::Name("V2".to_string())));
405 assert!(dict.get("Length").is_none());
406 }
407
408 #[test]
409 fn test_encryption_dict_with_file_id() {
410 let owner_hash = vec![0u8; 32];
411 let user_hash = vec![1u8; 32];
412 let permissions = Permissions::new();
413 let file_id = vec![42u8; 16];
414
415 let enc_dict = EncryptionDictionary::rc4_40bit(
416 owner_hash.clone(),
417 user_hash.clone(),
418 permissions,
419 Some(file_id.clone()),
420 );
421
422 assert_eq!(enc_dict.filter, "Standard");
424 assert_eq!(enc_dict.v, 1);
425 }
426
427 #[test]
428 fn test_encryption_dict_rc4_128bit_with_metadata() {
429 let owner_hash = vec![0u8; 32];
430 let user_hash = vec![1u8; 32];
431 let permissions = Permissions::all();
432
433 let enc_dict = EncryptionDictionary::rc4_128bit(
434 owner_hash.clone(),
435 user_hash.clone(),
436 permissions,
437 None,
438 );
439
440 assert_eq!(enc_dict.v, 2);
441 assert_eq!(enc_dict.length, Some(16));
442 assert_eq!(enc_dict.r, 3);
443 assert!(enc_dict.encrypt_metadata);
444 }
445
446 #[test]
447 fn test_encryption_dict_to_pdf_with_metadata_false() {
448 let mut enc_dict = EncryptionDictionary::rc4_128bit(
449 vec![0u8; 32],
450 vec![1u8; 32],
451 Permissions::new(),
452 None,
453 );
454 enc_dict.encrypt_metadata = false;
455 enc_dict.v = 4; let pdf_dict = enc_dict.to_dict();
458 assert_eq!(
459 pdf_dict.get("EncryptMetadata"),
460 Some(&Object::Boolean(false))
461 );
462 }
463
464 #[test]
465 fn test_encryption_dict_with_crypt_filters() {
466 let mut enc_dict = EncryptionDictionary::rc4_128bit(
467 vec![0u8; 32],
468 vec![1u8; 32],
469 Permissions::new(),
470 None,
471 );
472
473 let filter = CryptFilter::standard(CryptFilterMethod::AESV2);
474 enc_dict.cf = Some(vec![filter]);
475 enc_dict.stm_f = Some(StreamFilter::StdCF);
476 enc_dict.str_f = Some(StringFilter::StdCF);
477
478 let pdf_dict = enc_dict.to_dict();
479 assert!(pdf_dict.get("CF").is_some());
480 assert_eq!(
481 pdf_dict.get("StmF"),
482 Some(&Object::Name("StdCF".to_string()))
483 );
484 assert_eq!(
485 pdf_dict.get("StrF"),
486 Some(&Object::Name("StdCF".to_string()))
487 );
488 }
489
490 #[test]
491 fn test_encryption_dict_with_identity_filters() {
492 let mut enc_dict = EncryptionDictionary::rc4_128bit(
493 vec![0u8; 32],
494 vec![1u8; 32],
495 Permissions::new(),
496 None,
497 );
498
499 enc_dict.stm_f = Some(StreamFilter::Identity);
500 enc_dict.str_f = Some(StringFilter::Identity);
501
502 let pdf_dict = enc_dict.to_dict();
503 assert_eq!(
504 pdf_dict.get("StmF"),
505 Some(&Object::Name("Identity".to_string()))
506 );
507 assert_eq!(
508 pdf_dict.get("StrF"),
509 Some(&Object::Name("Identity".to_string()))
510 );
511 }
512
513 #[test]
514 fn test_encryption_dict_with_custom_filters() {
515 let mut enc_dict = EncryptionDictionary::rc4_128bit(
516 vec![0u8; 32],
517 vec![1u8; 32],
518 Permissions::new(),
519 None,
520 );
521
522 enc_dict.stm_f = Some(StreamFilter::Custom("MyStreamFilter".to_string()));
523 enc_dict.str_f = Some(StringFilter::Custom("MyStringFilter".to_string()));
524
525 let pdf_dict = enc_dict.to_dict();
526 assert_eq!(
527 pdf_dict.get("StmF"),
528 Some(&Object::Name("MyStreamFilter".to_string()))
529 );
530 assert_eq!(
531 pdf_dict.get("StrF"),
532 Some(&Object::Name("MyStringFilter".to_string()))
533 );
534 }
535
536 #[test]
537 fn test_multiple_crypt_filters() {
538 let mut enc_dict = EncryptionDictionary::rc4_128bit(
539 vec![0u8; 32],
540 vec![1u8; 32],
541 Permissions::new(),
542 None,
543 );
544
545 let filter1 = CryptFilter::standard(CryptFilterMethod::V2);
546 let filter2 = CryptFilter {
547 name: "AESFilter".to_string(),
548 method: CryptFilterMethod::AESV2,
549 length: Some(16),
550 };
551
552 enc_dict.cf = Some(vec![filter1, filter2]);
553
554 let pdf_dict = enc_dict.to_dict();
555 if let Some(Object::Dictionary(cf_dict)) = pdf_dict.get("CF") {
556 assert!(cf_dict.get("StdCF").is_some());
557 assert!(cf_dict.get("AESFilter").is_some());
558 } else {
559 panic!("CF should be a dictionary");
560 }
561 }
562}