1#![doc = include_str!("../README.md")]
2#![forbid(unsafe_code)]
28#![cfg_attr(docsrs, feature(doc_cfg))]
29#![warn(clippy::indexing_slicing)]
32
33mod create;
34mod handler;
35mod key;
36mod object;
37mod permissions;
38mod primitives;
39mod rc4;
40mod saslprep;
41mod standard;
42
43#[cfg(test)]
44mod test_fixtures;
45
46pub use create::{ENTROPY_LEN, KeyMaterial, standard_r6};
47pub use handler::{Error, SecurityHandler, is_signature_dict};
48pub use key::SmallKey;
49pub use object::{CryptClass, Iv};
50pub use permissions::Permissions;
51pub use primitives::{md5, sha1};
52pub use rc4::rc4;
53pub use standard::{Cipher, EncryptParams, PAD, PasswordEncoding, parse_encrypt_dict};
54
55#[cfg(test)]
56mod tests {
57 use super::{CryptClass, Error, Iv, Permissions, SecurityHandler, is_signature_dict};
58 use crate::standard::Cipher;
59 use crate::test_fixtures::{self, unhex};
60 use pdfrum_object::{Dict, Name, NoResolve, ObjRef, Object, PdfString, names};
61
62 fn cipher_of(dict: &Dict) -> Result<(Cipher, usize), Error> {
64 super::parse_encrypt_dict(dict, &NoResolve).map(|p| (p.cipher, p.key_len))
65 }
66
67 #[test]
70 fn aes_v2_fixture_promotes_a_byte_length_to_bits() {
71 let dict = test_fixtures::encrypted_pdf_dict();
72 assert_eq!(cipher_of(&dict), Ok((Cipher::Aes, 16)));
73 }
74
75 #[test]
76 fn aes_v2_fixture_opens_with_either_password() {
77 let dict = test_fixtures::encrypted_pdf_dict();
78 let id = unhex("1B0FD0F5E29AD84DBF67775E9E3B009F");
79
80 let user = SecurityHandler::from_encrypt_dict(&dict, &id, b"1234", &NoResolve)
81 .expect("the user password");
82 assert!(matches!(user, SecurityHandler::AesV4 { .. }));
83 assert!(!user.owner_unlocked());
84 assert_eq!(user.permission_word(false), 0xFFFF_F2C0);
85 assert_eq!(user.permission_word(true), 0xFFFF_F2C0);
86 assert_eq!(user.revision(), 4);
87
88 let owner = SecurityHandler::from_encrypt_dict(&dict, &id, b"5678", &NoResolve)
89 .expect("the owner password");
90 assert!(owner.owner_unlocked());
91 assert_eq!(owner.permission_word(true), 0xFFFF_FFFC);
92 assert_eq!(owner.permission_word(false), 0xFFFF_F2C0);
93 }
94
95 #[test]
96 fn aes_v2_fixture_rejects_the_wrong_password() {
97 let dict = test_fixtures::encrypted_pdf_dict();
98 let id = unhex("1B0FD0F5E29AD84DBF67775E9E3B009F");
99 for password in [&b""[..], b"tiger"] {
100 assert_eq!(
101 SecurityHandler::from_encrypt_dict(&dict, &id, password, &NoResolve).unwrap_err(),
102 Error::WrongPassword,
103 "password {password:?}"
104 );
105 }
106 }
107
108 #[test]
111 fn revision_5_fixture_opens_with_all_four_spellings() {
112 let dict = test_fixtures::r5_dict();
113 let id = unhex("7ca64129d20fc9745f1bfc0e4166590a");
114
115 let owner_keys: Vec<_> = [&b"\xe2ge"[..], b"\xc3\xa2ge"]
116 .iter()
117 .map(|password| {
118 let handler = SecurityHandler::from_encrypt_dict(&dict, &id, password, &NoResolve)
119 .unwrap_or_else(|_| panic!("owner {password:?}"));
120 assert!(handler.owner_unlocked());
121 assert_eq!(handler.revision(), 5);
122 match &handler {
123 SecurityHandler::AesV5 { key, .. } => **key,
124 _ => panic!("expected AesV5"),
125 }
126 })
127 .collect();
128 assert_eq!(owner_keys.first(), owner_keys.last());
130
131 for password in [&b"h\xf4tel"[..], b"h\xc3\xb4tel"] {
132 let handler = SecurityHandler::from_encrypt_dict(&dict, &id, password, &NoResolve)
133 .unwrap_or_else(|_| panic!("user {password:?}"));
134 assert!(!handler.owner_unlocked());
135 }
136 }
137
138 #[test]
140 fn revision_5_ignores_the_file_id() {
141 let dict = test_fixtures::r5_dict();
142 assert!(SecurityHandler::from_encrypt_dict(&dict, &[], b"h\xf4tel", &NoResolve).is_ok());
143 assert!(SecurityHandler::from_encrypt_dict(&dict, &[], b"\xe2ge", &NoResolve).is_ok());
144 }
145
146 #[test]
148 fn a_tampered_perms_block_rejects_the_password() {
149 let mut dict = test_fixtures::r5_dict();
150 let mut perms = unhex("c954c264d796dfd131ddb784f5a8b1bf");
151 if let Some(byte) = perms.get_mut(9) {
152 *byte ^= 0xFF;
153 }
154 dict.push(
155 names::PERMS.clone(),
156 Object::Str(PdfString::literal(&perms)),
157 );
158 assert_eq!(
159 SecurityHandler::from_encrypt_dict(&dict, &[], b"h\xf4tel", &NoResolve).unwrap_err(),
160 Error::WrongPassword
161 );
162 }
163
164 #[test]
167 fn revision_6_fixture_opens_with_all_four_spellings() {
168 let dict = test_fixtures::r6_dict();
169 for password in [&b"\xe2ge"[..], b"\xc3\xa2ge"] {
170 let handler = SecurityHandler::from_encrypt_dict(&dict, &[], password, &NoResolve)
171 .unwrap_or_else(|_| panic!("owner {password:?}"));
172 assert!(handler.owner_unlocked());
173 assert_eq!(handler.revision(), 6);
174 }
175 for password in [&b"h\xf4tel"[..], b"h\xc3\xb4tel"] {
176 let handler = SecurityHandler::from_encrypt_dict(&dict, &[], password, &NoResolve)
177 .unwrap_or_else(|_| panic!("user {password:?}"));
178 assert!(!handler.owner_unlocked());
179 }
180 assert_eq!(
181 SecurityHandler::from_encrypt_dict(&dict, &[], b"tiger", &NoResolve).unwrap_err(),
182 Error::WrongPassword
183 );
184 }
185
186 #[test]
194 fn revision_5_alternate_fixture() {
195 let dict = test_fixtures::bug_644_dict();
196 let owner = SecurityHandler::from_encrypt_dict(&dict, &[], b"b", &NoResolve)
197 .expect("the owner password");
198 assert!(owner.owner_unlocked());
199 assert_eq!(owner.permission_word(true), 0xFFFF_FFFC);
200 assert_eq!(owner.permission_word(false), 0xFFFF_FFFC);
201
202 let user = SecurityHandler::from_encrypt_dict(&dict, &[], b"a", &NoResolve)
203 .expect("the user password");
204 assert!(!user.owner_unlocked());
205 assert_eq!(user.permission_word(false), 0xFFFF_FFFC);
206 assert_eq!(
208 format!("{:?}", (owner.revision(), user.revision())),
209 "(5, 5)"
210 );
211
212 for password in [&b""[..], b"tiger"] {
213 assert_eq!(
214 SecurityHandler::from_encrypt_dict(&dict, &[], password, &NoResolve).unwrap_err(),
215 Error::WrongPassword,
216 "password {password:?}"
217 );
218 }
219 assert_eq!(
220 owner.password_encoding(),
221 crate::PasswordEncoding::AsGiven,
222 "an ASCII password never converts"
223 );
224 }
225
226 type KeyLengthRow = (
231 i64,
232 Option<i64>,
233 Option<i64>,
234 Option<&'static str>,
235 Result<(Cipher, usize), Error>,
236 );
237
238 #[test]
239 fn key_length_resolution_table() {
240 use test_fixtures::encrypt_dict;
241 let cases: [KeyLengthRow; 14] = [
242 (1, None, None, None, Ok((Cipher::Rc4, 5))),
243 (1, Some(128), None, None, Ok((Cipher::Rc4, 5))),
245 (2, None, None, None, Ok((Cipher::Rc4, 5))),
246 (2, Some(40), None, None, Ok((Cipher::Rc4, 5))),
247 (2, Some(128), None, None, Ok((Cipher::Rc4, 16))),
248 (
249 2,
250 Some(256),
251 None,
252 None,
253 Err(Error::CipherKeyLength {
254 cipher: "RC4",
255 len: 32,
256 }),
257 ),
258 (
261 2,
262 Some(8),
263 None,
264 None,
265 Err(Error::CipherKeyLength {
266 cipher: "RC4",
267 len: 1,
268 }),
269 ),
270 (4, Some(128), None, Some("V2"), Ok((Cipher::Rc4, 16))),
271 (4, Some(128), Some(16), Some("AESV2"), Ok((Cipher::Aes, 16))),
272 (
273 4,
274 Some(128),
275 Some(128),
276 Some("AESV2"),
277 Ok((Cipher::Aes, 16)),
278 ),
279 (4, None, None, Some("AESV2"), Ok((Cipher::Aes, 16))),
280 (
281 4,
282 Some(128),
283 Some(40),
284 Some("AESV2"),
285 Err(Error::CipherKeyLength {
286 cipher: "AES",
287 len: 5,
288 }),
289 ),
290 (5, Some(256), Some(32), Some("AESV3"), Ok((Cipher::Aes, 32))),
291 (5, None, None, Some("AESV3"), Ok((Cipher::Aes, 32))),
292 ];
293 for (version, length, filter_length, method, expected) in cases {
294 let dict = encrypt_dict(version, length, filter_length, method);
295 assert_eq!(
296 cipher_of(&dict),
297 expected,
298 "/V {version} /Length {length:?} /CF Length {filter_length:?} /CFM {method:?}"
299 );
300 }
301 }
302
303 #[test]
304 fn a_negative_filter_length_is_malformed() {
305 let dict = test_fixtures::encrypt_dict(4, Some(128), Some(-8), Some("AESV2"));
306 assert!(matches!(
307 cipher_of(&dict),
308 Err(Error::MalformedEncryptDict(_))
309 ));
310 }
311
312 #[test]
314 fn an_identity_crypt_filter_yields_the_identity_handler() {
315 let dict = test_fixtures::identity_dict();
316 assert_eq!(cipher_of(&dict), Ok((Cipher::None, 0)));
317 let handler = SecurityHandler::from_encrypt_dict(&dict, &[], b"", &NoResolve)
318 .expect("identity needs no password");
319 assert!(matches!(handler, SecurityHandler::Identity));
320 assert_eq!(
321 handler.decrypt(ObjRef::new(3, 0), CryptClass::Stream, b"plain"),
322 b"plain"
323 );
324 }
325
326 #[test]
334 fn differing_stream_and_string_filters_open_rather_than_refusing() {
335 let mut dict = test_fixtures::encrypt_dict(4, Some(128), Some(16), Some("AESV2"));
336 dict.push(names::STR_F.clone(), Object::Name(Name::from("Other")));
337 assert_eq!(cipher_of(&dict), Ok((Cipher::Aes, 16)));
338 }
339
340 #[test]
345 fn an_absent_stream_filter_defaults_to_identity_beside_a_named_string_filter() {
346 let mut dict = test_fixtures::bare_v4_dict();
347 dict.push(names::STR_F.clone(), Object::Name(Name::from("StdCF")));
348 let params =
349 super::parse_encrypt_dict(&dict, &NoResolve).expect("a defaulted /StmF is conformant");
350 assert_eq!(params.cipher, Cipher::None, "/StmF defaults to /Identity");
351 assert_eq!(params.string_cipher, Cipher::Aes, "/StrF names StdCF");
352 }
353
354 #[test]
357 fn both_class_filters_absent_default_to_identity() {
358 let dict = test_fixtures::bare_v4_dict();
359 assert_eq!(cipher_of(&dict), Ok((Cipher::None, 0)));
360 let handler = SecurityHandler::from_encrypt_dict(&dict, &[], b"", &NoResolve)
361 .expect("a document with neither class filter opens");
362 assert!(matches!(handler, SecurityHandler::Identity));
363 }
364
365 #[test]
369 fn a_missing_crypt_filter_dictionary_defaults_to_identity() {
370 let dict = Dict::from_pairs([
371 (names::FILTER.clone(), Object::Name(names::STANDARD.clone())),
372 (names::V.clone(), Object::Int(4)),
373 (names::R.clone(), Object::Int(4)),
374 ]);
375 assert_eq!(cipher_of(&dict), Ok((Cipher::None, 0)));
376 }
377
378 #[test]
382 fn an_identity_string_filter_leaves_strings_plaintext_beside_an_enciphering_stream() {
383 let mut dict = test_fixtures::encrypt_dict(4, Some(128), Some(16), Some("AESV2"));
384 dict.push(names::STR_F.clone(), Object::Name(names::IDENTITY.clone()));
385 let params = super::parse_encrypt_dict(&dict, &NoResolve).expect("conformant per §7.6.5");
386 assert_eq!(params.cipher, Cipher::Aes);
387 assert_eq!(params.string_cipher, Cipher::None);
388 }
389
390 #[test]
393 fn a_non_standard_filter_is_unsupported() {
394 for spelling in ["Adobe.PubSec", "Nonesuch"] {
395 let dict =
396 Dict::from_pairs([(names::FILTER.clone(), Object::Name(Name::from(spelling)))]);
397 assert_eq!(
398 SecurityHandler::from_encrypt_dict(&dict, &[], b"", &NoResolve).unwrap_err(),
399 Error::UnsupportedHandler(spelling.as_bytes().into())
400 );
401 }
402 }
403
404 #[test]
407 fn a_string_valued_filter_is_not_the_standard_handler() {
408 let dict = Dict::from_pairs([(
409 names::FILTER.clone(),
410 Object::Str(PdfString::literal(b"Standard")),
411 )]);
412 assert_eq!(
413 SecurityHandler::from_encrypt_dict(&dict, &[], b"", &NoResolve).unwrap_err(),
414 Error::UnsupportedHandler(Box::default())
415 );
416 }
417
418 #[test]
421 fn encrypt_metadata_reads_only_a_boolean() {
422 let mut dict = test_fixtures::encrypt_dict(4, Some(128), Some(16), Some("AESV2"));
423 dict.push(names::ENCRYPT_METADATA.clone(), Object::Int(0));
424 let params = super::parse_encrypt_dict(&dict, &NoResolve).expect("parses");
425 assert!(params.encrypt_metadata, "an integer is not a boolean");
426
427 let mut dict = test_fixtures::encrypt_dict(4, Some(128), Some(16), Some("AESV2"));
428 dict.push(names::ENCRYPT_METADATA.clone(), Object::Bool(false));
429 let params = super::parse_encrypt_dict(&dict, &NoResolve).expect("parses");
430 assert!(!params.encrypt_metadata);
431 }
432
433 #[test]
434 fn identity_reports_no_restrictions_and_no_revision() {
435 let handler = SecurityHandler::Identity;
436 assert_eq!(handler.permission_word(false), 0xFFFF_FFFF);
437 assert_eq!(handler.permission_word(true), 0xFFFF_FFFF);
438 assert_eq!(handler.revision(), 0);
439 assert!(handler.encrypt_metadata());
440 assert!(!handler.owner_unlocked());
441 }
442
443 #[test]
444 fn the_permission_mask_clears_reserved_bits_and_forces_the_high_ones() {
445 let dict = test_fixtures::encrypted_pdf_dict();
446 let id = unhex("1B0FD0F5E29AD84DBF67775E9E3B009F");
447 let handler = SecurityHandler::from_encrypt_dict(&dict, &id, b"1234", &NoResolve)
448 .expect("the user password");
449 let reported = handler.permission_word(false);
450 assert_eq!(reported & 0b11, 0, "the two reserved bits are cleared");
451 assert_eq!(
452 reported & 0xFFFF_F0C0,
453 0xFFFF_F0C0,
454 "the forced bits are set"
455 );
456 }
457
458 #[test]
463 fn the_public_methods_decode_the_word_the_private_one_reports() {
464 let dict = test_fixtures::encrypted_pdf_dict();
465 let id = unhex("1B0FD0F5E29AD84DBF67775E9E3B009F");
466
467 let user = SecurityHandler::from_encrypt_dict(&dict, &id, b"1234", &NoResolve)
468 .expect("the user password");
469 let granted = user.permissions();
470 assert_eq!(granted, Permissions::from_bits(user.permission_word(false)));
471 assert_eq!(
475 granted,
476 Permissions {
477 extract: true,
478 ..Permissions::NONE
479 }
480 );
481 assert_eq!(user.owner_permissions(), granted);
484
485 let owner = SecurityHandler::from_encrypt_dict(&dict, &id, b"5678", &NoResolve)
486 .expect("the owner password");
487 assert_eq!(owner.owner_permissions(), Permissions::ALL);
488 assert_eq!(owner.permissions(), granted);
489 }
490
491 #[test]
494 fn a_short_user_entry_rejects_rather_than_reading_out_of_bounds() {
495 for len in 0..16usize {
496 let dict = test_fixtures::r3_dict_with_user_entry(&vec![0xCD; len]);
497 let id = unhex("9b744068bb5efbe920baaba6da63c2bf");
498 assert_eq!(
499 SecurityHandler::from_encrypt_dict(&dict, &id, b"h\xf4tel", &NoResolve)
500 .unwrap_err(),
501 Error::WrongPassword,
502 "/U of {len} bytes"
503 );
504 }
505 }
506
507 #[test]
510 fn a_partial_user_entry_is_zero_padded_and_still_compared() {
511 for len in 16..32usize {
512 let dict = test_fixtures::r3_dict_with_user_entry(&vec![0xCD; len]);
513 let id = unhex("9b744068bb5efbe920baaba6da63c2bf");
514 assert!(
516 SecurityHandler::from_encrypt_dict(&dict, &id, b"h\xf4tel", &NoResolve).is_err(),
517 "/U of {len} bytes"
518 );
519 }
520 }
521
522 #[test]
526 fn short_version_five_entries_reject_rather_than_panicking() {
527 for len in [0usize, 1, 31, 47] {
528 let short = vec![0xEFu8; len];
529 for key in [names::O, names::U, names::PERMS] {
531 let mut dict = test_fixtures::r5_dict();
532 dict.push(key.clone(), Object::Str(PdfString::literal(&short)));
533 for password in [&b"h\xf4tel"[..], b"\xe2ge"] {
534 assert!(
535 SecurityHandler::from_encrypt_dict(&dict, &[], password, &NoResolve)
536 .is_err(),
537 "{key:?} of {len} bytes with {password:?}"
538 );
539 }
540 }
541 for (key, password) in [(names::UE, &b"h\xf4tel"[..]), (names::OE, b"\xe2ge")] {
543 let mut dict = test_fixtures::r5_dict();
544 dict.push(key.clone(), Object::Str(PdfString::literal(&short)));
545 assert!(
546 SecurityHandler::from_encrypt_dict(&dict, &[], password, &NoResolve).is_err(),
547 "{key:?} of {len} bytes"
548 );
549 }
550 }
551 }
552
553 #[test]
554 fn an_empty_perms_entry_rejects() {
555 let mut dict = test_fixtures::r5_dict();
556 dict.push(names::PERMS.clone(), Object::Str(PdfString::literal(b"")));
557 assert_eq!(
558 SecurityHandler::from_encrypt_dict(&dict, &[], b"h\xf4tel", &NoResolve).unwrap_err(),
559 Error::WrongPassword
560 );
561 }
562
563 #[test]
566 fn a_truncated_owner_entry_fails_the_open() {
567 for revision in [2i64, 3] {
568 for len in [0usize, 1, 31] {
569 let dict = test_fixtures::rc4_dict_with_owner_entry(revision, &vec![0x11; len]);
570 assert_eq!(
571 SecurityHandler::from_encrypt_dict(&dict, &[], b"a", &NoResolve).unwrap_err(),
572 Error::WrongPassword,
573 "/R {revision} with /O of {len} bytes"
574 );
575 }
576 }
577 }
578
579 #[test]
582 fn signature_dictionaries_are_recognised_by_type_then_field_type() {
583 let sig_type = Dict::from_pairs([(names::TYPE.clone(), Object::Name(names::SIG.clone()))]);
584 assert!(is_signature_dict(&sig_type));
585
586 let sig_field = Dict::from_pairs([(names::FT.clone(), Object::Name(names::SIG.clone()))]);
587 assert!(is_signature_dict(&sig_field));
588
589 let annot = Dict::from_pairs([
591 (names::TYPE.clone(), Object::Name(Name::from("Annot"))),
592 (names::FT.clone(), Object::Name(names::SIG.clone())),
593 ]);
594 assert!(!is_signature_dict(&annot));
595
596 assert!(!is_signature_dict(&Dict::new()));
598 }
599
600 #[test]
603 fn a_string_valued_type_still_names_a_signature() {
604 let dict =
605 Dict::from_pairs([(names::TYPE.clone(), Object::Str(PdfString::literal(b"Sig")))]);
606 assert!(is_signature_dict(&dict));
607 }
608
609 fn opened_handlers() -> Vec<(&'static str, SecurityHandler)> {
613 let aes_v2_id = unhex("1B0FD0F5E29AD84DBF67775E9E3B009F");
614 let rc4_id = unhex("9b744068bb5efbe920baaba6da63c2bf");
615 vec![
616 (
617 "RC4 (/R 3)",
618 SecurityHandler::from_encrypt_dict(
619 &test_fixtures::r3_dict(),
620 &rc4_id,
621 b"h\xf4tel",
622 &NoResolve,
623 )
624 .expect("the r3 user password"),
625 ),
626 (
627 "AESV2 (/R 4)",
628 SecurityHandler::from_encrypt_dict(
629 &test_fixtures::encrypted_pdf_dict(),
630 &aes_v2_id,
631 b"1234",
632 &NoResolve,
633 )
634 .expect("the encrypted.pdf user password"),
635 ),
636 (
637 "AESV3 (/R 6)",
638 SecurityHandler::from_encrypt_dict(
639 &test_fixtures::r6_dict(),
640 &[],
641 b"h\xf4tel",
642 &NoResolve,
643 )
644 .expect("the r6 user password"),
645 ),
646 ]
647 }
648
649 #[test]
652 fn rc4_decrypt_round_trips_through_the_public_api() {
653 let rc4_id = unhex("9b744068bb5efbe920baaba6da63c2bf");
654 let handler = SecurityHandler::from_encrypt_dict(
655 &test_fixtures::r3_dict(),
656 &rc4_id,
657 b"h\xf4tel",
658 &NoResolve,
659 )
660 .expect("the r3 user password");
661 let obj = ObjRef::new(12, 3);
662 let payload = b"Hello, encrypted world.".to_vec();
663 for class in [CryptClass::Stream, CryptClass::String, CryptClass::Embedded] {
664 let once = handler.decrypt(obj, class, &payload);
665 assert_ne!(once, payload, "{class:?} actually enciphered");
666 assert_eq!(handler.decrypt(obj, class, &once), payload, "{class:?}");
667 }
668 }
669
670 #[test]
673 fn every_crypt_class_decrypts_the_same_way() {
674 for (name, handler) in opened_handlers() {
675 let obj = ObjRef::new(7, 0);
676 let payload: Vec<u8> = (0..64u8).collect();
677 let stream = handler.decrypt(obj, CryptClass::Stream, &payload);
678 assert_eq!(
679 handler.decrypt(obj, CryptClass::String, &payload),
680 stream,
681 "{name}"
682 );
683 assert_eq!(
684 handler.decrypt(obj, CryptClass::Embedded, &payload),
685 stream,
686 "{name}"
687 );
688 }
689 }
690
691 fn eff_dict(embedded_method: &str) -> Dict {
703 use pdfrum_object::Name;
704 let mut dict = test_fixtures::encrypted_pdf_dict();
705 let std_cf = Dict::from_pairs([
706 (names::CFM.clone(), Object::Name(Name::from("AESV2"))),
707 (names::LENGTH.clone(), Object::Int(16)),
708 ]);
709 let emb_cf = Dict::from_pairs([
710 (
711 names::CFM.clone(),
712 Object::Name(Name::from(embedded_method)),
713 ),
714 (names::LENGTH.clone(), Object::Int(16)),
715 ]);
716 dict.push(
717 names::CF.clone(),
718 Object::Dict(Dict::from_pairs([
719 (Name::from("StdCF"), Object::Dict(std_cf)),
720 (Name::from("EmbCF"), Object::Dict(emb_cf)),
721 ])),
722 );
723 dict.push(names::EFF.clone(), Object::Name(Name::from("EmbCF")));
724 dict
725 }
726
727 fn eff_handler(dict: &Dict) -> SecurityHandler {
729 SecurityHandler::from_encrypt_dict(
730 dict,
731 &unhex("1B0FD0F5E29AD84DBF67775E9E3B009F"),
732 b"1234",
733 &NoResolve,
734 )
735 .expect("the encrypted.pdf user password")
736 }
737
738 #[test]
742 fn an_eff_naming_another_filter_decrypts_embedded_files_with_it() {
743 let dict = eff_dict("V2");
744 let params = super::parse_encrypt_dict(&dict, &NoResolve).unwrap();
745 assert_eq!(params.cipher, Cipher::Aes);
746 assert_eq!(params.embedded_cipher, Some(Cipher::Rc4));
747
748 let handler = eff_handler(&dict);
749 assert_eq!(handler.embedded_cipher(), Some(Cipher::Rc4));
750
751 let obj = ObjRef::new(9, 0);
752 let payload: Vec<u8> = (0..48u8).collect();
753 let as_stream = handler.decrypt(obj, CryptClass::Stream, &payload);
756 let as_embedded = handler.decrypt(obj, CryptClass::Embedded, &payload);
757 assert_eq!(as_embedded.len(), payload.len());
758 assert_ne!(as_embedded, as_stream);
759 assert_eq!(
761 handler.decrypt(obj, CryptClass::String, &payload),
762 as_stream
763 );
764 }
765
766 #[test]
768 fn the_embedded_class_round_trips_under_its_own_cipher() {
769 let dict = eff_dict("V2");
770 let handler = eff_handler(&dict);
771 let obj = ObjRef::new(9, 0);
772 let payload = b"an attachment".to_vec();
773 let sealed = handler.encrypt(obj, CryptClass::Embedded, Iv([3; 16]), &payload);
774 assert_eq!(handler.decrypt(obj, CryptClass::Embedded, &sealed), payload);
775 assert_ne!(
777 sealed,
778 handler.encrypt(obj, CryptClass::Stream, Iv([3; 16]), &payload)
779 );
780 }
781
782 #[test]
785 fn an_absent_eff_leaves_every_class_on_the_stream_cipher() {
786 for (name, handler) in opened_handlers() {
787 assert_eq!(handler.embedded_cipher(), None, "{name}");
788 }
789 let dict = test_fixtures::encrypt_dict(4, Some(128), Some(16), Some("AESV2"));
790 let params = super::parse_encrypt_dict(&dict, &NoResolve).unwrap();
791 assert_eq!(params.embedded_cipher, None);
792 }
793
794 #[test]
797 fn an_eff_that_agrees_with_the_stream_filter_is_no_override() {
798 use pdfrum_object::Name;
799 let mut same = test_fixtures::encrypt_dict(4, Some(128), Some(16), Some("AESV2"));
800 same.push(names::EFF.clone(), Object::Name(Name::from("StdCF")));
801 assert_eq!(
802 super::parse_encrypt_dict(&same, &NoResolve)
803 .unwrap()
804 .embedded_cipher,
805 None
806 );
807 let agreeing = eff_dict("AESV3");
810 assert_eq!(
811 super::parse_encrypt_dict(&agreeing, &NoResolve)
812 .unwrap()
813 .embedded_cipher,
814 None
815 );
816 }
817
818 #[test]
821 fn an_identity_eff_leaves_embedded_files_unenciphered() {
822 let mut dict = test_fixtures::encrypted_pdf_dict();
823 dict.push(names::EFF.clone(), Object::Name(names::IDENTITY.clone()));
824 let handler = eff_handler(&dict);
825 assert_eq!(handler.embedded_cipher(), Some(Cipher::None));
826 let obj = ObjRef::new(9, 0);
827 let payload: Vec<u8> = (0..48u8).collect();
828 assert_eq!(
829 handler.decrypt(obj, CryptClass::Embedded, &payload),
830 payload
831 );
832 assert_ne!(handler.decrypt(obj, CryptClass::Stream, &payload), payload);
833 }
834
835 #[test]
839 fn an_eff_naming_a_missing_filter_falls_back_rather_than_failing() {
840 use pdfrum_object::Name;
841 let mut dict = test_fixtures::encrypt_dict(4, Some(128), Some(16), Some("AESV2"));
842 dict.push(names::EFF.clone(), Object::Name(Name::from("NoSuchCF")));
843 let params = super::parse_encrypt_dict(&dict, &NoResolve).expect("still opens");
844 assert_eq!(params.embedded_cipher, None);
845 }
846
847 #[test]
850 fn only_the_pre_version_five_handlers_key_by_object() {
851 let payload: Vec<u8> = (0..48u8).map(|i| i.wrapping_mul(5)).collect();
852 for (name, handler) in opened_handlers() {
853 let first = handler.decrypt(ObjRef::new(1, 0), CryptClass::Stream, &payload);
854 let second = handler.decrypt(ObjRef::new(2, 0), CryptClass::Stream, &payload);
855 if handler.revision() >= 5 {
856 assert_eq!(second, first, "{name} uses the file key verbatim");
857 } else {
858 assert_ne!(second, first, "{name} salts by object number");
859 }
860 }
861 }
862
863 #[test]
866 fn decrypt_never_panics_and_never_grows_a_payload() {
867 for (name, handler) in opened_handlers() {
868 for len in 0..80usize {
869 let payload = vec![0xA5u8; len];
870 let out = handler.decrypt(ObjRef::new(9, 1), CryptClass::Stream, &payload);
871 assert!(out.len() <= len, "{name} at {len} bytes");
872 }
873 }
874 for len in 0..40usize {
876 let payload = vec![0x5Au8; len];
877 assert_eq!(
878 SecurityHandler::Identity.decrypt(ObjRef::new(0, 0), CryptClass::String, &payload),
879 payload
880 );
881 }
882 }
883
884 fn every_revision() -> Vec<(&'static str, SecurityHandler)> {
892 let r2_id = unhex("2b778de1bcef1733b35e680882812409");
893 let mut all = vec![(
894 "RC4 (/R 2)",
895 SecurityHandler::from_encrypt_dict(
896 &test_fixtures::r2_dict(),
897 &r2_id,
898 b"h\xf4tel",
899 &NoResolve,
900 )
901 .expect("the r2 user password"),
902 )];
903 all.extend(opened_handlers());
904 all.push((
905 "AESV3 (/R 5)",
906 SecurityHandler::from_encrypt_dict(
907 &test_fixtures::r5_dict(),
908 &[],
909 b"h\xf4tel",
910 &NoResolve,
911 )
912 .expect("the r5 user password"),
913 ));
914 all
915 }
916
917 #[test]
921 fn every_revision_round_trips_encrypt_then_decrypt() {
922 for (name, handler) in every_revision() {
923 let obj = ObjRef::new(11, 0);
924 for len in [0usize, 1, 15, 16, 17, 31, 32, 33, 64, 127] {
925 let payload: Vec<u8> = (0..len)
926 .map(|i| u8::try_from(i % 253).unwrap_or(0))
927 .collect();
928 for class in [CryptClass::Stream, CryptClass::String, CryptClass::Embedded] {
929 let iv = Iv([u8::try_from(len % 256).unwrap_or(0); 16]);
930 let sealed = handler.encrypt(obj, class, iv, &payload);
931 assert_eq!(
932 handler.decrypt(obj, class, &sealed),
933 payload,
934 "{name} {class:?} at {len} bytes"
935 );
936 }
937 }
938 }
939 }
940
941 #[test]
944 fn an_encrypted_payload_is_not_its_own_plaintext() {
945 let payload = b"Hello, encrypted world.".to_vec();
946 for (name, handler) in every_revision() {
947 let sealed = handler.encrypt(
948 ObjRef::new(4, 0),
949 CryptClass::Stream,
950 Iv([0x5A; 16]),
951 &payload,
952 );
953 assert_ne!(sealed, payload, "{name}");
954 }
955 assert_eq!(
957 SecurityHandler::Identity.encrypt(
958 ObjRef::new(4, 0),
959 CryptClass::Stream,
960 Iv([0; 16]),
961 &payload
962 ),
963 payload
964 );
965 }
966
967 #[test]
971 fn only_the_pre_version_five_handlers_key_an_encryption_by_object() {
972 let payload = b"payload".to_vec();
973 for (name, handler) in every_revision() {
974 let iv = Iv([1; 16]);
975 let first = handler.encrypt(ObjRef::new(1, 0), CryptClass::Stream, iv, &payload);
976 let second = handler.encrypt(ObjRef::new(2, 0), CryptClass::Stream, iv, &payload);
977 if handler.revision() >= 5 {
978 assert_eq!(second, first, "{name} uses the file key verbatim");
979 } else {
980 assert_ne!(second, first, "{name} salts by object number");
981 }
982 }
983 }
984
985 #[test]
988 fn the_same_vector_produces_the_same_ciphertext() {
989 for (name, handler) in every_revision() {
990 let obj = ObjRef::new(6, 0);
991 let once = handler.encrypt(obj, CryptClass::Stream, Iv([2; 16]), b"stable");
992 let again = handler.encrypt(obj, CryptClass::Stream, Iv([2; 16]), b"stable");
993 assert_eq!(once, again, "{name}");
994 let other = handler.encrypt(obj, CryptClass::Stream, Iv([3; 16]), b"stable");
996 if handler.revision() >= 4 {
997 assert_ne!(other, once, "{name} mixes the vector in");
998 }
999 }
1000 }
1001
1002 #[test]
1004 fn encrypt_never_panics_and_grows_by_the_documented_amount() {
1005 for (name, handler) in every_revision() {
1006 for len in 0..80usize {
1007 let out = handler.encrypt(
1008 ObjRef::new(9, 1),
1009 CryptClass::Stream,
1010 Iv([0xC3; 16]),
1011 &vec![0xA5u8; len],
1012 );
1013 let expected = if len == 0 || matches!(handler, SecurityHandler::Rc4V2 { .. }) {
1014 len
1015 } else {
1016 32 + (len / 16) * 16
1017 };
1018 assert_eq!(out.len(), expected, "{name} at {len} bytes");
1019 }
1020 }
1021 }
1022
1023 #[test]
1027 fn an_empty_payload_stays_empty_at_every_revision() {
1028 for (name, handler) in every_revision() {
1029 for class in [CryptClass::Stream, CryptClass::String, CryptClass::Embedded] {
1030 assert!(
1031 handler
1032 .encrypt(ObjRef::new(2, 0), class, Iv([0xFF; 16]), b"")
1033 .is_empty(),
1034 "{name} {class:?}"
1035 );
1036 }
1037 }
1038 }
1039
1040 fn pseudo_random(seed: u64, len: usize) -> Vec<u8> {
1049 let mut state = seed.wrapping_mul(0x2545_F491_4F6C_DD1D) | 1;
1050 (0..len)
1051 .map(|_| {
1052 state ^= state << 13;
1053 state ^= state >> 7;
1054 state ^= state << 17;
1055 u8::try_from(state >> 56).unwrap_or(0)
1056 })
1057 .collect()
1058 }
1059
1060 #[test]
1061 fn arbitrary_encrypt_dictionaries_never_panic() {
1062 for seed in 0..8u64 {
1063 for version in [-1i64, 0, 1, 2, 3, 4, 5, 6, 99] {
1064 for revision in [0i64, 2, 3, 4, 5, 6, 7] {
1065 for length in [
1068 *pdfrum_object::INT_RANGE.start(),
1069 -8,
1070 0,
1071 8,
1072 40,
1073 128,
1074 256,
1075 4096,
1076 *pdfrum_object::INT_RANGE.end(),
1077 ] {
1078 let mut dict = test_fixtures::encrypt_dict(
1079 version,
1080 Some(length),
1081 Some(length),
1082 Some("AESV2"),
1083 );
1084 dict.push(names::R.clone(), Object::Int(revision));
1085 for key in [names::O, names::U, names::OE, names::UE, names::PERMS] {
1086 let entry = pseudo_random(
1087 seed.wrapping_add(key.as_bytes().len() as u64),
1088 usize::try_from(seed % 60).unwrap_or(0),
1089 );
1090 dict.push(key.clone(), Object::Str(PdfString::literal(entry)));
1091 }
1092 let password = pseudo_random(seed, usize::try_from(seed % 9).unwrap_or(0));
1093 let file_id =
1094 pseudo_random(seed + 1, usize::try_from(seed % 20).unwrap_or(0));
1095 let _ = SecurityHandler::from_encrypt_dict(
1097 &dict, &file_id, &password, &NoResolve,
1098 );
1099 }
1100 }
1101 }
1102 }
1103 }
1104
1105 #[test]
1109 fn arbitrary_version_five_entries_terminate() {
1110 for seed in 0..4u64 {
1111 let mut dict = test_fixtures::r6_dict();
1112 for key in [names::O, names::U, names::OE, names::UE, names::PERMS] {
1113 let entry = pseudo_random(seed, 48);
1114 dict.push(key.clone(), Object::Str(PdfString::literal(entry)));
1115 }
1116 let password = pseudo_random(seed, 64);
1119 let _ = SecurityHandler::from_encrypt_dict(&dict, &[], &password, &NoResolve);
1120 }
1121 }
1122
1123 #[test]
1126 fn every_password_entry_length_is_survivable() {
1127 for len in 0..64usize {
1128 let entry = pseudo_random(len as u64, len);
1129 for base in [
1132 test_fixtures::r2_dict(),
1133 test_fixtures::r3_dict(),
1134 test_fixtures::encrypted_pdf_dict(),
1135 test_fixtures::r5_dict(),
1136 ] {
1137 for key in [names::O, names::U, names::OE, names::UE, names::PERMS] {
1138 let mut dict = base.clone();
1139 dict.push(key.clone(), Object::Str(PdfString::literal(&entry)));
1140 let _ = SecurityHandler::from_encrypt_dict(&dict, &[], b"pw", &NoResolve);
1141 }
1142 }
1143 }
1144 }
1145
1146 #[test]
1147 fn handlers_are_send_and_sync() {
1148 const fn assert_send_sync<T: Send + Sync>() {}
1149 assert_send_sync::<SecurityHandler>();
1150 assert_send_sync::<Error>();
1151 assert_send_sync::<crate::EncryptParams>();
1152 }
1153
1154 #[test]
1155 fn a_handler_debug_dump_never_shows_key_material() {
1156 let dict = test_fixtures::encrypted_pdf_dict();
1157 let id = unhex("1B0FD0F5E29AD84DBF67775E9E3B009F");
1158 let handler = SecurityHandler::from_encrypt_dict(&dict, &id, b"1234", &NoResolve)
1159 .expect("the user password");
1160 let dump = format!("{handler:?}");
1161 assert!(dump.contains("redacted"), "{dump}");
1162 }
1163}