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(owner_hash, user_hash, permissions, None);
300
301 assert_eq!(enc_dict.filter, "Standard");
302 assert_eq!(enc_dict.v, 2);
303 assert_eq!(enc_dict.length, Some(16));
304 assert_eq!(enc_dict.r, 3);
305 }
306
307 #[test]
308 fn test_encryption_dict_to_pdf() {
309 let enc_dict =
310 EncryptionDictionary::rc4_40bit(vec![0u8; 32], vec![1u8; 32], Permissions::new(), None);
311
312 let pdf_dict = enc_dict.to_dict();
313 assert_eq!(
314 pdf_dict.get("Filter"),
315 Some(&Object::Name("Standard".to_string()))
316 );
317 assert_eq!(pdf_dict.get("V"), Some(&Object::Integer(1)));
318 assert_eq!(pdf_dict.get("Length"), Some(&Object::Integer(40))); assert_eq!(pdf_dict.get("R"), Some(&Object::Integer(2)));
320 assert!(pdf_dict.get("O").is_some());
321 assert!(pdf_dict.get("U").is_some());
322 assert!(pdf_dict.get("P").is_some());
323 }
324
325 #[test]
326 fn test_stream_filter_names() {
327 let identity = StreamFilter::Identity;
328 let std_cf = StreamFilter::StdCF;
329 let custom = StreamFilter::Custom("MyFilter".to_string());
330
331 let _identity_clone = identity;
333 let _std_cf_clone = std_cf;
334 let _custom_clone = custom;
335 }
336
337 #[test]
338 fn test_string_filter_names() {
339 let identity = StringFilter::Identity;
340 let std_cf = StringFilter::StdCF;
341 let custom = StringFilter::Custom("MyStringFilter".to_string());
342
343 let _identity_clone = identity;
345 let _std_cf_clone = std_cf;
346 let _custom_clone = custom;
347 }
348
349 #[test]
350 fn test_encryption_algorithm_variants() {
351 assert_eq!(EncryptionAlgorithm::RC4, EncryptionAlgorithm::RC4);
352 assert_eq!(EncryptionAlgorithm::AES128, EncryptionAlgorithm::AES128);
353 assert_eq!(EncryptionAlgorithm::AES256, EncryptionAlgorithm::AES256);
354 assert_ne!(EncryptionAlgorithm::RC4, EncryptionAlgorithm::AES128);
355
356 let _ = format!("{:?}", EncryptionAlgorithm::RC4);
358 let _ = format!("{:?}", EncryptionAlgorithm::AES128);
359 let _ = format!("{:?}", EncryptionAlgorithm::AES256);
360 }
361
362 #[test]
363 fn test_crypt_filter_method_variants() {
364 assert_eq!(CryptFilterMethod::None, CryptFilterMethod::None);
365 assert_eq!(CryptFilterMethod::V2, CryptFilterMethod::V2);
366 assert_eq!(CryptFilterMethod::AESV2, CryptFilterMethod::AESV2);
367 assert_eq!(CryptFilterMethod::AESV3, CryptFilterMethod::AESV3);
368 assert_ne!(CryptFilterMethod::None, CryptFilterMethod::V2);
369
370 let _ = format!("{:?}", CryptFilterMethod::None);
372 let _ = format!("{:?}", CryptFilterMethod::V2);
373 let _ = format!("{:?}", CryptFilterMethod::AESV2);
374 let _ = format!("{:?}", CryptFilterMethod::AESV3);
375 }
376
377 #[test]
378 fn test_crypt_filter_custom() {
379 let filter = CryptFilter {
380 name: "CustomFilter".to_string(),
381 method: CryptFilterMethod::AESV2,
382 length: Some(32),
383 };
384
385 let dict = filter.to_dict();
386 assert_eq!(dict.get("CFM"), Some(&Object::Name("AESV2".to_string())));
387 assert_eq!(dict.get("Length"), Some(&Object::Integer(32)));
388 }
389
390 #[test]
391 fn test_crypt_filter_no_optional_fields() {
392 let filter = CryptFilter {
393 name: "MinimalFilter".to_string(),
394 method: CryptFilterMethod::V2,
395 length: None,
396 };
397
398 let dict = filter.to_dict();
399 assert_eq!(dict.get("CFM"), Some(&Object::Name("V2".to_string())));
400 assert!(dict.get("Length").is_none());
401 }
402
403 #[test]
404 fn test_encryption_dict_with_file_id() {
405 let owner_hash = vec![0u8; 32];
406 let user_hash = vec![1u8; 32];
407 let permissions = Permissions::new();
408 let file_id = vec![42u8; 16];
409
410 let enc_dict =
411 EncryptionDictionary::rc4_40bit(owner_hash, user_hash, permissions, Some(file_id));
412
413 assert_eq!(enc_dict.filter, "Standard");
415 assert_eq!(enc_dict.v, 1);
416 }
417
418 #[test]
419 fn test_encryption_dict_rc4_128bit_with_metadata() {
420 let owner_hash = vec![0u8; 32];
421 let user_hash = vec![1u8; 32];
422 let permissions = Permissions::all();
423
424 let enc_dict = EncryptionDictionary::rc4_128bit(owner_hash, user_hash, permissions, None);
425
426 assert_eq!(enc_dict.v, 2);
427 assert_eq!(enc_dict.length, Some(16));
428 assert_eq!(enc_dict.r, 3);
429 assert!(enc_dict.encrypt_metadata);
430 }
431
432 #[test]
433 fn test_encryption_dict_to_pdf_with_metadata_false() {
434 let mut enc_dict = EncryptionDictionary::rc4_128bit(
435 vec![0u8; 32],
436 vec![1u8; 32],
437 Permissions::new(),
438 None,
439 );
440 enc_dict.encrypt_metadata = false;
441 enc_dict.v = 4; let pdf_dict = enc_dict.to_dict();
444 assert_eq!(
445 pdf_dict.get("EncryptMetadata"),
446 Some(&Object::Boolean(false))
447 );
448 }
449
450 #[test]
451 fn test_encryption_dict_with_crypt_filters() {
452 let mut enc_dict = EncryptionDictionary::rc4_128bit(
453 vec![0u8; 32],
454 vec![1u8; 32],
455 Permissions::new(),
456 None,
457 );
458
459 let filter = CryptFilter::standard(CryptFilterMethod::AESV2);
460 enc_dict.cf = Some(vec![filter]);
461 enc_dict.stm_f = Some(StreamFilter::StdCF);
462 enc_dict.str_f = Some(StringFilter::StdCF);
463
464 let pdf_dict = enc_dict.to_dict();
465 assert!(pdf_dict.get("CF").is_some());
466 assert_eq!(
467 pdf_dict.get("StmF"),
468 Some(&Object::Name("StdCF".to_string()))
469 );
470 assert_eq!(
471 pdf_dict.get("StrF"),
472 Some(&Object::Name("StdCF".to_string()))
473 );
474 }
475
476 #[test]
477 fn test_encryption_dict_with_identity_filters() {
478 let mut enc_dict = EncryptionDictionary::rc4_128bit(
479 vec![0u8; 32],
480 vec![1u8; 32],
481 Permissions::new(),
482 None,
483 );
484
485 enc_dict.stm_f = Some(StreamFilter::Identity);
486 enc_dict.str_f = Some(StringFilter::Identity);
487
488 let pdf_dict = enc_dict.to_dict();
489 assert_eq!(
490 pdf_dict.get("StmF"),
491 Some(&Object::Name("Identity".to_string()))
492 );
493 assert_eq!(
494 pdf_dict.get("StrF"),
495 Some(&Object::Name("Identity".to_string()))
496 );
497 }
498
499 #[test]
500 fn test_encryption_dict_with_custom_filters() {
501 let mut enc_dict = EncryptionDictionary::rc4_128bit(
502 vec![0u8; 32],
503 vec![1u8; 32],
504 Permissions::new(),
505 None,
506 );
507
508 enc_dict.stm_f = Some(StreamFilter::Custom("MyStreamFilter".to_string()));
509 enc_dict.str_f = Some(StringFilter::Custom("MyStringFilter".to_string()));
510
511 let pdf_dict = enc_dict.to_dict();
512 assert_eq!(
513 pdf_dict.get("StmF"),
514 Some(&Object::Name("MyStreamFilter".to_string()))
515 );
516 assert_eq!(
517 pdf_dict.get("StrF"),
518 Some(&Object::Name("MyStringFilter".to_string()))
519 );
520 }
521
522 #[test]
523 fn test_multiple_crypt_filters() {
524 let mut enc_dict = EncryptionDictionary::rc4_128bit(
525 vec![0u8; 32],
526 vec![1u8; 32],
527 Permissions::new(),
528 None,
529 );
530
531 let filter1 = CryptFilter::standard(CryptFilterMethod::V2);
532 let filter2 = CryptFilter {
533 name: "AESFilter".to_string(),
534 method: CryptFilterMethod::AESV2,
535 length: Some(16),
536 };
537
538 enc_dict.cf = Some(vec![filter1, filter2]);
539
540 let pdf_dict = enc_dict.to_dict();
541 if let Some(Object::Dictionary(cf_dict)) = pdf_dict.get("CF") {
542 assert!(cf_dict.get("StdCF").is_some());
543 assert!(cf_dict.get("AESFilter").is_some());
544 } else {
545 panic!("CF should be a dictionary");
546 }
547 }
548}