Commit c8862b5fe7b28a530183237a48eae551ccea60d3

Authored by m-holger
Committed by GitHub
2 parents 34f52682 04cc9df2

Merge pull request #1489 from m-holger/qpdf_encrypt

Modernize QPDF_encryption
include/qpdf/QPDF.hh
... ... @@ -23,6 +23,7 @@
23 23 #include <qpdf/DLL.h>
24 24 #include <qpdf/Types.h>
25 25  
  26 +#include <bitset>
26 27 #include <cstdio>
27 28 #include <functional>
28 29 #include <iostream>
... ... @@ -465,7 +466,7 @@ class QPDF
465 466 V(V),
466 467 R(R),
467 468 Length_bytes(Length_bytes),
468   - P(P),
  469 + P(static_cast<unsigned long long>(P)),
469 470 O(O),
470 471 U(U),
471 472 OE(OE),
... ... @@ -475,11 +476,20 @@ class QPDF
475 476 encrypt_metadata(encrypt_metadata)
476 477 {
477 478 }
  479 + EncryptionData(int V, int R, int Length_bytes, bool encrypt_metadata) :
  480 + V(V),
  481 + R(R),
  482 + Length_bytes(Length_bytes),
  483 + encrypt_metadata(encrypt_metadata)
  484 + {
  485 + }
478 486  
479 487 int getV() const;
480 488 int getR() const;
481 489 int getLengthBytes() const;
482 490 int getP() const;
  491 + // Bits in P are numbered from 1 as in the PDF spec.
  492 + bool getP(size_t bit) const;
483 493 std::string const& getO() const;
484 494 std::string const& getU() const;
485 495 std::string const& getOE() const;
... ... @@ -487,9 +497,12 @@ class QPDF
487 497 std::string const& getPerms() const;
488 498 std::string const& getId1() const;
489 499 bool getEncryptMetadata() const;
490   -
  500 + // Bits in P are numbered from 1 as in the PDF spec.
  501 + void setP(size_t bit, bool val);
  502 + void setP(unsigned long val);
491 503 void setO(std::string const&);
492 504 void setU(std::string const&);
  505 + void setId1(std::string const& val);
493 506 void setV5EncryptionParameters(
494 507 std::string const& O,
495 508 std::string const& OE,
... ... @@ -497,14 +510,51 @@ class QPDF
497 510 std::string const& UE,
498 511 std::string const& Perms);
499 512  
  513 + std::string compute_encryption_key(std::string const& password) const;
  514 +
  515 + bool
  516 + check_owner_password(std::string& user_password, std::string const& owner_password) const;
  517 +
  518 + bool check_user_password(std::string const& user_password) const;
  519 +
  520 + std::string
  521 + recover_encryption_key_with_password(std::string const& password, bool& perms_valid) const;
  522 +
  523 + void compute_encryption_O_U(char const* user_password, char const* owner_password);
  524 +
  525 + std::string
  526 + compute_encryption_parameters_V5(char const* user_password, char const* owner_password);
  527 +
  528 + std::string compute_parameters(char const* user_password, char const* owner_password);
  529 +
500 530 private:
  531 + static constexpr unsigned int OU_key_bytes_V4 = 16; // ( == sizeof(MD5::Digest)
  532 +
501 533 EncryptionData(EncryptionData const&) = delete;
502 534 EncryptionData& operator=(EncryptionData const&) = delete;
503 535  
  536 + std::string hash_V5(
  537 + std::string const& password, std::string const& salt, std::string const& udata) const;
  538 + std::string
  539 + compute_O_value(std::string const& user_password, std::string const& owner_password) const;
  540 + std::string compute_U_value(std::string const& user_password) const;
  541 + std::string compute_encryption_key_from_password(std::string const& password) const;
  542 + std::string recover_encryption_key_with_password(std::string const& password) const;
  543 + bool check_owner_password_V4(
  544 + std::string& user_password, std::string const& owner_password) const;
  545 + bool check_owner_password_V5(std::string const& owner_passworda) const;
  546 + std::string compute_Perms_value_V5_clear() const;
  547 + std::string compute_O_rc4_key(
  548 + std::string const& user_password, std::string const& owner_password) const;
  549 + std::string compute_U_value_R2(std::string const& user_password) const;
  550 + std::string compute_U_value_R3(std::string const& user_password) const;
  551 + bool check_user_password_V4(std::string const& user_password) const;
  552 + bool check_user_password_V5(std::string const& user_password) const;
  553 +
504 554 int V;
505 555 int R;
506 556 int Length_bytes;
507   - int P;
  557 + std::bitset<32> P{0xfffffffc}; // Specification always requires bits 1 and 2 to be cleared.
508 558 std::string O;
509 559 std::string U;
510 560 std::string OE;
... ... @@ -906,18 +956,10 @@ class QPDF
906 956 void insertPageobjToPage(QPDFObjectHandle const& obj, int pos, bool check_duplicate);
907 957  
908 958 // methods to support encryption -- implemented in QPDF_encryption.cc
909   - static encryption_method_e
910   - interpretCF(std::shared_ptr<EncryptionParameters> encp, QPDFObjectHandle);
911 959 void initializeEncryption();
912 960 static std::string
913 961 getKeyForObject(std::shared_ptr<EncryptionParameters> encp, QPDFObjGen og, bool use_aes);
914 962 void decryptString(std::string&, QPDFObjGen og);
915   - static std::string
916   - compute_encryption_key_from_password(std::string const& password, EncryptionData const& data);
917   - static std::string
918   - recover_encryption_key_with_password(std::string const& password, EncryptionData const& data);
919   - static std::string recover_encryption_key_with_password(
920   - std::string const& password, EncryptionData const& data, bool& perms_valid);
921 963 static void decryptStream(
922 964 std::shared_ptr<EncryptionParameters> encp,
923 965 std::shared_ptr<InputSource> file,
... ...
include/qpdf/QPDFWriter.hh
... ... @@ -23,6 +23,7 @@
23 23 #include <qpdf/DLL.h>
24 24 #include <qpdf/Types.h>
25 25  
  26 +#include <bitset>
26 27 #include <cstdio>
27 28 #include <functional>
28 29 #include <list>
... ... @@ -510,9 +511,6 @@ class QPDFWriter
510 511 std::string getOriginalID1();
511 512 void generateID();
512 513 void interpretR3EncryptionParameters(
513   - std::set<int>& bits_to_clear,
514   - char const* user_password,
515   - char const* owner_password,
516 514 bool allow_accessibility,
517 515 bool allow_extract,
518 516 bool allow_assemble,
... ... @@ -524,26 +522,8 @@ class QPDFWriter
524 522 void disableIncompatibleEncryption(int major, int minor, int extension_level);
525 523 void parseVersion(std::string const& version, int& major, int& minor) const;
526 524 int compareVersions(int major1, int minor1, int major2, int minor2) const;
527   - void setEncryptionParameters(
528   - char const* user_password,
529   - char const* owner_password,
530   - int V,
531   - int R,
532   - int key_len,
533   - std::set<int>& bits_to_clear);
534   - void setEncryptionParametersInternal(
535   - int V,
536   - int R,
537   - int key_len,
538   - int P,
539   - std::string const& O,
540   - std::string const& U,
541   - std::string const& OE,
542   - std::string const& UE,
543   - std::string const& Perms,
544   - std::string const& id1,
545   - std::string const& user_password,
546   - std::string const& encryption_key);
  525 + void setEncryptionParameters(char const* user_password, char const* owner_password);
  526 + void setEncryptionMinimumVersion();
547 527 void setDataKey(int objid);
548 528 int openObject(int objid = 0);
549 529 void closeObject(int objid);
... ...
libqpdf/MD5.cc
... ... @@ -46,12 +46,6 @@ MD5::appendString(char const* input_string)
46 46 }
47 47  
48 48 void
49   -MD5::encodeDataIncrementally(char const* data, size_t len)
50   -{
51   - crypto->MD5_update(QUtil::unsigned_char_pointer(data), len);
52   -}
53   -
54   -void
55 49 MD5::encodeFile(char const* filename, qpdf_offset_t up_to_offset)
56 50 {
57 51 char buffer[1024];
... ... @@ -94,6 +88,24 @@ MD5::digest(Digest result)
94 88 crypto->MD5_digest(result);
95 89 }
96 90  
  91 +std::string
  92 +MD5::digest()
  93 +{
  94 + Digest digest_val;
  95 + digest(digest_val);
  96 + return {reinterpret_cast<char*>(digest_val), 16};
  97 +}
  98 +
  99 +std::string
  100 +MD5::digest(std::string_view data)
  101 +{
  102 + MD5 m;
  103 + m.encodeDataIncrementally(data.data(), data.size());
  104 + Digest digest_val;
  105 + m.digest(digest_val);
  106 + return {reinterpret_cast<char*>(digest_val), 16};
  107 +}
  108 +
97 109 void
98 110 MD5::print()
99 111 {
... ...
libqpdf/QPDFWriter.cc
... ... @@ -50,6 +50,90 @@ QPDFWriter::FunctionProgressReporter::reportProgress(int progress)
50 50 this->handler(progress);
51 51 }
52 52  
  53 +class QPDFWriter::Members
  54 +{
  55 + friend class QPDFWriter;
  56 +
  57 + public:
  58 + ~Members();
  59 +
  60 + private:
  61 + Members(QPDF& pdf);
  62 + Members(Members const&) = delete;
  63 +
  64 + QPDF& pdf;
  65 + QPDFObjGen root_og{-1, 0};
  66 + char const* filename{"unspecified"};
  67 + FILE* file{nullptr};
  68 + bool close_file{false};
  69 + Pl_Buffer* buffer_pipeline{nullptr};
  70 + Buffer* output_buffer{nullptr};
  71 + bool normalize_content_set{false};
  72 + bool normalize_content{false};
  73 + bool compress_streams{true};
  74 + bool compress_streams_set{false};
  75 + qpdf_stream_decode_level_e stream_decode_level{qpdf_dl_generalized};
  76 + bool stream_decode_level_set{false};
  77 + bool recompress_flate{false};
  78 + bool qdf_mode{false};
  79 + bool preserve_unreferenced_objects{false};
  80 + bool newline_before_endstream{false};
  81 + bool static_id{false};
  82 + bool suppress_original_object_ids{false};
  83 + bool direct_stream_lengths{true};
  84 + bool preserve_encryption{true};
  85 + bool linearized{false};
  86 + bool pclm{false};
  87 + qpdf_object_stream_e object_stream_mode{qpdf_o_preserve};
  88 +
  89 + std::unique_ptr<QPDF::EncryptionData> encryption;
  90 + std::string encryption_key;
  91 + bool encrypt_use_aes{false};
  92 +
  93 + std::string id1; // for /ID key of
  94 + std::string id2; // trailer dictionary
  95 + std::string final_pdf_version;
  96 + int final_extension_level{0};
  97 + std::string min_pdf_version;
  98 + int min_extension_level{0};
  99 + std::string forced_pdf_version;
  100 + int forced_extension_level{0};
  101 + std::string extra_header_text;
  102 + int encryption_dict_objid{0};
  103 + std::string cur_data_key;
  104 + std::list<std::shared_ptr<Pipeline>> to_delete;
  105 + qpdf::pl::Count* pipeline{nullptr};
  106 + std::vector<QPDFObjectHandle> object_queue;
  107 + size_t object_queue_front{0};
  108 + QPDFWriter::ObjTable obj;
  109 + QPDFWriter::NewObjTable new_obj;
  110 + int next_objid{1};
  111 + int cur_stream_length_id{0};
  112 + size_t cur_stream_length{0};
  113 + bool added_newline{false};
  114 + size_t max_ostream_index{0};
  115 + std::set<QPDFObjGen> normalized_streams;
  116 + std::map<QPDFObjGen, int> page_object_to_seq;
  117 + std::map<QPDFObjGen, int> contents_to_page_seq;
  118 + std::map<int, std::vector<QPDFObjGen>> object_stream_to_objects;
  119 + std::vector<Pipeline*> pipeline_stack;
  120 + unsigned long next_stack_id{2};
  121 + std::string count_buffer;
  122 + bool deterministic_id{false};
  123 + Pl_MD5* md5_pipeline{nullptr};
  124 + std::string deterministic_id_data;
  125 + bool did_write_setup{false};
  126 +
  127 + // For linearization only
  128 + std::string lin_pass1_filename;
  129 +
  130 + // For progress reporting
  131 + std::shared_ptr<QPDFWriter::ProgressReporter> progress_reporter;
  132 + int events_expected{0};
  133 + int events_seen{0};
  134 + int next_progress_report{0};
  135 +};
  136 +
53 137 QPDFWriter::Members::Members(QPDF& pdf) :
54 138 pdf(pdf),
55 139 root_og(pdf.getRoot().getObjGen().isIndirect() ? pdf.getRoot().getObjGen() : QPDFObjGen(-1, 0))
... ... @@ -344,21 +428,20 @@ QPDFWriter::setR2EncryptionParametersInsecure(
344 428 bool allow_extract,
345 429 bool allow_annotate)
346 430 {
347   - std::set<int> clear;
  431 + m->encryption = std::make_unique<QPDF::EncryptionData>(1, 2, 5, true);
348 432 if (!allow_print) {
349   - clear.insert(3);
  433 + m->encryption->setP(3, false);
350 434 }
351 435 if (!allow_modify) {
352   - clear.insert(4);
  436 + m->encryption->setP(4, false);
353 437 }
354 438 if (!allow_extract) {
355   - clear.insert(5);
  439 + m->encryption->setP(5, false);
356 440 }
357 441 if (!allow_annotate) {
358   - clear.insert(6);
  442 + m->encryption->setP(6, false);
359 443 }
360   -
361   - setEncryptionParameters(user_password, owner_password, 1, 2, 5, clear);
  444 + setEncryptionParameters(user_password, owner_password);
362 445 }
363 446  
364 447 void
... ... @@ -373,11 +456,8 @@ QPDFWriter::setR3EncryptionParametersInsecure(
373 456 bool allow_modify_other,
374 457 qpdf_r3_print_e print)
375 458 {
376   - std::set<int> clear;
  459 + m->encryption = std::make_unique<QPDF::EncryptionData>(2, 3, 16, true);
377 460 interpretR3EncryptionParameters(
378   - clear,
379   - user_password,
380   - owner_password,
381 461 allow_accessibility,
382 462 allow_extract,
383 463 allow_assemble,
... ... @@ -386,7 +466,7 @@ QPDFWriter::setR3EncryptionParametersInsecure(
386 466 allow_modify_other,
387 467 print,
388 468 qpdf_r3m_all);
389   - setEncryptionParameters(user_password, owner_password, 2, 3, 16, clear);
  469 + setEncryptionParameters(user_password, owner_password);
390 470 }
391 471  
392 472 void
... ... @@ -403,11 +483,9 @@ QPDFWriter::setR4EncryptionParametersInsecure(
403 483 bool encrypt_metadata,
404 484 bool use_aes)
405 485 {
406   - std::set<int> clear;
  486 + m->encryption = std::make_unique<QPDF::EncryptionData>(4, 4, 16, encrypt_metadata);
  487 + m->encrypt_use_aes = use_aes;
407 488 interpretR3EncryptionParameters(
408   - clear,
409   - user_password,
410   - owner_password,
411 489 allow_accessibility,
412 490 allow_extract,
413 491 allow_assemble,
... ... @@ -416,9 +494,7 @@ QPDFWriter::setR4EncryptionParametersInsecure(
416 494 allow_modify_other,
417 495 print,
418 496 qpdf_r3m_all);
419   - m->encrypt_use_aes = use_aes;
420   - m->encrypt_metadata = encrypt_metadata;
421   - setEncryptionParameters(user_password, owner_password, 4, 4, 16, clear);
  497 + setEncryptionParameters(user_password, owner_password);
422 498 }
423 499  
424 500 void
... ... @@ -434,11 +510,9 @@ QPDFWriter::setR5EncryptionParameters(
434 510 qpdf_r3_print_e print,
435 511 bool encrypt_metadata)
436 512 {
437   - std::set<int> clear;
  513 + m->encryption = std::make_unique<QPDF::EncryptionData>(5, 5, 32, encrypt_metadata);
  514 + m->encrypt_use_aes = true;
438 515 interpretR3EncryptionParameters(
439   - clear,
440   - user_password,
441   - owner_password,
442 516 allow_accessibility,
443 517 allow_extract,
444 518 allow_assemble,
... ... @@ -447,9 +521,7 @@ QPDFWriter::setR5EncryptionParameters(
447 521 allow_modify_other,
448 522 print,
449 523 qpdf_r3m_all);
450   - m->encrypt_use_aes = true;
451   - m->encrypt_metadata = encrypt_metadata;
452   - setEncryptionParameters(user_password, owner_password, 5, 5, 32, clear);
  524 + setEncryptionParameters(user_password, owner_password);
453 525 }
454 526  
455 527 void
... ... @@ -465,11 +537,8 @@ QPDFWriter::setR6EncryptionParameters(
465 537 qpdf_r3_print_e print,
466 538 bool encrypt_metadata)
467 539 {
468   - std::set<int> clear;
  540 + m->encryption = std::make_unique<QPDF::EncryptionData>(5, 6, 32, encrypt_metadata);
469 541 interpretR3EncryptionParameters(
470   - clear,
471   - user_password,
472   - owner_password,
473 542 allow_accessibility,
474 543 allow_extract,
475 544 allow_assemble,
... ... @@ -479,15 +548,11 @@ QPDFWriter::setR6EncryptionParameters(
479 548 print,
480 549 qpdf_r3m_all);
481 550 m->encrypt_use_aes = true;
482   - m->encrypt_metadata = encrypt_metadata;
483   - setEncryptionParameters(user_password, owner_password, 5, 6, 32, clear);
  551 + setEncryptionParameters(user_password, owner_password);
484 552 }
485 553  
486 554 void
487 555 QPDFWriter::interpretR3EncryptionParameters(
488   - std::set<int>& clear,
489   - char const* user_password,
490   - char const* owner_password,
491 556 bool allow_accessibility,
492 557 bool allow_extract,
493 558 bool allow_assemble,
... ... @@ -526,27 +591,24 @@ QPDFWriter::interpretR3EncryptionParameters(
526 591 // 10: accessibility; ignored by readers, should always be set
527 592 // 11: document assembly even if 4 is clear
528 593 // 12: high-resolution printing
529   -
530   - if (!allow_accessibility) {
531   - // setEncryptionParameters sets this if R > 3
532   - clear.insert(10);
  594 + if (!allow_accessibility && m->encryption->getR() <= 3) {
  595 + // Bit 10 is deprecated and should always be set. This used to mean accessibility. There
  596 + // is no way to disable accessibility with R > 3.
  597 + m->encryption->setP(10, false);
533 598 }
534 599 if (!allow_extract) {
535   - clear.insert(5);
  600 + m->encryption->setP(5, false);
536 601 }
537 602  
538   - // Note: these switch statements all "fall through" (no break statements). Each option clears
539   - // successively more access bits.
540 603 switch (print) {
541 604 case qpdf_r3p_none:
542   - clear.insert(3); // any printing
543   -
  605 + m->encryption->setP(3, false); // any printing
  606 + [[fallthrough]];
544 607 case qpdf_r3p_low:
545   - clear.insert(12); // high resolution printing
546   -
  608 + m->encryption->setP(12, false); // high resolution printing
  609 + [[fallthrough]];
547 610 case qpdf_r3p_full:
548 611 break;
549   -
550 612 // no default so gcc warns for missing cases
551 613 }
552 614  
... ... @@ -557,96 +619,44 @@ QPDFWriter::interpretR3EncryptionParameters(
557 619 // NOT EXERCISED IN TEST SUITE
558 620 switch (modify) {
559 621 case qpdf_r3m_none:
560   - clear.insert(11); // document assembly
561   -
  622 + m->encryption->setP(11, false); // document assembly
  623 + [[fallthrough]];
562 624 case qpdf_r3m_assembly:
563   - clear.insert(9); // filling in form fields
564   -
  625 + m->encryption->setP(9, false); // filling in form fields
  626 + [[fallthrough]];
565 627 case qpdf_r3m_form:
566   - clear.insert(6); // modify annotations, fill in form fields
567   -
  628 + m->encryption->setP(6, false); // modify annotations, fill in form fields
  629 + [[fallthrough]];
568 630 case qpdf_r3m_annotate:
569   - clear.insert(4); // other modifications
570   -
  631 + m->encryption->setP(4, false); // other modifications
  632 + [[fallthrough]];
571 633 case qpdf_r3m_all:
572 634 break;
573   -
574 635 // no default so gcc warns for missing cases
575 636 }
576 637 // END NOT EXERCISED IN TEST SUITE
577 638  
578 639 if (!allow_assemble) {
579   - clear.insert(11);
  640 + m->encryption->setP(11, false);
580 641 }
581 642 if (!allow_annotate_and_form) {
582   - clear.insert(6);
  643 + m->encryption->setP(6, false);
583 644 }
584 645 if (!allow_form_filling) {
585   - clear.insert(9);
  646 + m->encryption->setP(9, false);
586 647 }
587 648 if (!allow_modify_other) {
588   - clear.insert(4);
  649 + m->encryption->setP(4, false);
589 650 }
590 651 }
591 652  
592 653 void
593   -QPDFWriter::setEncryptionParameters(
594   - char const* user_password,
595   - char const* owner_password,
596   - int V,
597   - int R,
598   - int key_len,
599   - std::set<int>& bits_to_clear)
  654 +QPDFWriter::setEncryptionParameters(char const* user_password, char const* owner_password)
600 655 {
601   - // PDF specification refers to bits with the low bit numbered 1.
602   - // We have to convert this into a bit field.
603   -
604   - // Specification always requires bits 1 and 2 to be cleared.
605   - bits_to_clear.insert(1);
606   - bits_to_clear.insert(2);
607   -
608   - if (R > 3) {
609   - // Bit 10 is deprecated and should always be set. This used to mean accessibility. There
610   - // is no way to disable accessibility with R > 3.
611   - bits_to_clear.erase(10);
612   - }
613   -
614   - int P = 0;
615   - // Create the complement of P, then invert.
616   - for (int b: bits_to_clear) {
617   - P |= (1 << (b - 1));
618   - }
619   - P = ~P;
620   -
621 656 generateID();
622   - std::string O;
623   - std::string U;
624   - std::string OE;
625   - std::string UE;
626   - std::string Perms;
627   - std::string encryption_key;
628   - if (V < 5) {
629   - QPDF::compute_encryption_O_U(
630   - user_password, owner_password, V, R, key_len, P, m->encrypt_metadata, m->id1, O, U);
631   - } else {
632   - QPDF::compute_encryption_parameters_V5(
633   - user_password,
634   - owner_password,
635   - V,
636   - R,
637   - key_len,
638   - P,
639   - m->encrypt_metadata,
640   - m->id1,
641   - encryption_key,
642   - O,
643   - U,
644   - OE,
645   - UE,
646   - Perms);
647   - }
648   - setEncryptionParametersInternal(
649   - V, R, key_len, P, O, U, OE, UE, Perms, m->id1, user_password, encryption_key);
  657 + m->encryption->setId1(m->id1);
  658 + m->encryption_key = m->encryption->compute_parameters(user_password, owner_password);
  659 + setEncryptionMinimumVersion();
650 660 }
651 661  
652 662 void
... ... @@ -663,9 +673,10 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf)
663 673 if (V > 1) {
664 674 key_len = encrypt.getKey("/Length").getIntValueAsInt() / 8;
665 675 }
666   - if (encrypt.hasKey("/EncryptMetadata") && encrypt.getKey("/EncryptMetadata").isBool()) {
667   - m->encrypt_metadata = encrypt.getKey("/EncryptMetadata").getBoolValue();
668   - }
  676 + const bool encrypt_metadata =
  677 + encrypt.hasKey("/EncryptMetadata") && encrypt.getKey("/EncryptMetadata").isBool()
  678 + ? encrypt.getKey("/EncryptMetadata").getBoolValue()
  679 + : true;
669 680 if (V >= 4) {
670 681 // When copying encryption parameters, use AES even if the original file did not.
671 682 // Acrobat doesn't create files with V >= 4 that don't use AES, and the logic of
... ... @@ -673,72 +684,62 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf)
673 684 // all potentially having different values.
674 685 m->encrypt_use_aes = true;
675 686 }
676   - QTC::TC("qpdf", "QPDFWriter copy encrypt metadata", m->encrypt_metadata ? 0 : 1);
  687 + QTC::TC("qpdf", "QPDFWriter copy encrypt metadata", encrypt_metadata ? 0 : 1);
677 688 QTC::TC("qpdf", "QPDFWriter copy use_aes", m->encrypt_use_aes ? 0 : 1);
678   - std::string OE;
679   - std::string UE;
680   - std::string Perms;
681   - std::string encryption_key;
682   - if (V >= 5) {
683   - QTC::TC("qpdf", "QPDFWriter copy V5");
684   - OE = encrypt.getKey("/OE").getStringValue();
685   - UE = encrypt.getKey("/UE").getStringValue();
686   - Perms = encrypt.getKey("/Perms").getStringValue();
687   - encryption_key = qpdf.getEncryptionKey();
688   - }
689   -
690   - setEncryptionParametersInternal(
  689 +
  690 + m->encryption = std::make_unique<QPDF::EncryptionData>(
691 691 V,
692 692 encrypt.getKey("/R").getIntValueAsInt(),
693 693 key_len,
694 694 static_cast<int>(encrypt.getKey("/P").getIntValue()),
695 695 encrypt.getKey("/O").getStringValue(),
696 696 encrypt.getKey("/U").getStringValue(),
697   - OE,
698   - UE,
699   - Perms,
  697 + V < 5 ? "" : encrypt.getKey("/OE").getStringValue(),
  698 + V < 5 ? "" : encrypt.getKey("/UE").getStringValue(),
  699 + V < 5 ? "" : encrypt.getKey("/Perms").getStringValue(),
700 700 m->id1, // m->id1 == the other file's id1
701   - qpdf.getPaddedUserPassword(),
702   - encryption_key);
  701 + encrypt_metadata);
  702 + m->encryption_key = V >= 5
  703 + ? qpdf.getEncryptionKey()
  704 + : m->encryption->compute_encryption_key(qpdf.getPaddedUserPassword());
  705 + setEncryptionMinimumVersion();
703 706 }
704 707 }
705 708  
706 709 void
707 710 QPDFWriter::disableIncompatibleEncryption(int major, int minor, int extension_level)
708 711 {
709   - if (!m->encrypted) {
  712 + if (!m->encryption) {
710 713 return;
711 714 }
712   -
713   - bool disable = false;
714 715 if (compareVersions(major, minor, 1, 3) < 0) {
715   - disable = true;
716   - } else {
717   - int V = QUtil::string_to_int(m->encryption_dictionary["/V"].c_str());
718   - int R = QUtil::string_to_int(m->encryption_dictionary["/R"].c_str());
719   - if (compareVersions(major, minor, 1, 4) < 0) {
720   - if ((V > 1) || (R > 2)) {
721   - disable = true;
722   - }
723   - } else if (compareVersions(major, minor, 1, 5) < 0) {
724   - if ((V > 2) || (R > 3)) {
725   - disable = true;
726   - }
727   - } else if (compareVersions(major, minor, 1, 6) < 0) {
728   - if (m->encrypt_use_aes) {
729   - disable = true;
730   - }
731   - } else if (
732   - (compareVersions(major, minor, 1, 7) < 0) ||
733   - ((compareVersions(major, minor, 1, 7) == 0) && extension_level < 3)) {
734   - if ((V >= 5) || (R >= 5)) {
735   - disable = true;
736   - }
  716 + m->encryption = nullptr;
  717 + return;
  718 + }
  719 + int V = m->encryption->getV();
  720 + int R = m->encryption->getR();
  721 + if (compareVersions(major, minor, 1, 4) < 0) {
  722 + if (V > 1 || R > 2) {
  723 + m->encryption = nullptr;
  724 + }
  725 + } else if (compareVersions(major, minor, 1, 5) < 0) {
  726 + if (V > 2 || R > 3) {
  727 + m->encryption = nullptr;
  728 + }
  729 + } else if (compareVersions(major, minor, 1, 6) < 0) {
  730 + if (m->encrypt_use_aes) {
  731 + m->encryption = nullptr;
  732 + }
  733 + } else if (
  734 + (compareVersions(major, minor, 1, 7) < 0) ||
  735 + ((compareVersions(major, minor, 1, 7) == 0) && extension_level < 3)) {
  736 + if (V >= 5 || R >= 5) {
  737 + m->encryption = nullptr;
737 738 }
738 739 }
739   - if (disable) {
  740 +
  741 + if (!m->encryption) {
740 742 QTC::TC("qpdf", "QPDFWriter forced version disabled encryption");
741   - m->encrypted = false;
742 743 }
743 744 }
744 745  
... ... @@ -776,34 +777,9 @@ QPDFWriter::compareVersions(int major1, int minor1, int major2, int minor2) cons
776 777 }
777 778  
778 779 void
779   -QPDFWriter::setEncryptionParametersInternal(
780   - int V,
781   - int R,
782   - int key_len,
783   - int P,
784   - std::string const& O,
785   - std::string const& U,
786   - std::string const& OE,
787   - std::string const& UE,
788   - std::string const& Perms,
789   - std::string const& id1,
790   - std::string const& user_password,
791   - std::string const& encryption_key)
792   -{
793   - m->encryption_V = V;
794   - m->encryption_R = R;
795   - m->encryption_dictionary["/Filter"] = "/Standard";
796   - m->encryption_dictionary["/V"] = std::to_string(V);
797   - m->encryption_dictionary["/Length"] = std::to_string(key_len * 8);
798   - m->encryption_dictionary["/R"] = std::to_string(R);
799   - m->encryption_dictionary["/P"] = std::to_string(P);
800   - m->encryption_dictionary["/O"] = QPDF_String(O).unparse(true);
801   - m->encryption_dictionary["/U"] = QPDF_String(U).unparse(true);
802   - if (V >= 5) {
803   - m->encryption_dictionary["/OE"] = QPDF_String(OE).unparse(true);
804   - m->encryption_dictionary["/UE"] = QPDF_String(UE).unparse(true);
805   - m->encryption_dictionary["/Perms"] = QPDF_String(Perms).unparse(true);
806   - }
  780 +QPDFWriter::setEncryptionMinimumVersion()
  781 +{
  782 + auto const R = m->encryption->getR();
807 783 if (R >= 6) {
808 784 setMinimumPDFVersion("1.7", 8);
809 785 } else if (R == 5) {
... ... @@ -815,37 +791,20 @@ QPDFWriter::setEncryptionParametersInternal(
815 791 } else {
816 792 setMinimumPDFVersion("1.3");
817 793 }
818   -
819   - if ((R >= 4) && (!m->encrypt_metadata)) {
820   - m->encryption_dictionary["/EncryptMetadata"] = "false";
821   - }
822   - if ((V == 4) || (V == 5)) {
823   - // The spec says the value for the crypt filter key can be anything, and xpdf seems to
824   - // agree. However, Adobe Reader won't open our files unless we use /StdCF.
825   - m->encryption_dictionary["/StmF"] = "/StdCF";
826   - m->encryption_dictionary["/StrF"] = "/StdCF";
827   - std::string method = (m->encrypt_use_aes ? ((V < 5) ? "/AESV2" : "/AESV3") : "/V2");
828   - // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of
829   - // MacOS won't open encrypted files without it.
830   - m->encryption_dictionary["/CF"] = "<< /StdCF << /AuthEvent /DocOpen /CFM " + method +
831   - " /Length " + std::string((V < 5) ? "16" : "32") + " >> >>";
832   - }
833   -
834   - m->encrypted = true;
835   - QPDF::EncryptionData encryption_data(
836   - V, R, key_len, P, O, U, OE, UE, Perms, id1, m->encrypt_metadata);
837   - if (V < 5) {
838   - m->encryption_key = QPDF::compute_encryption_key(user_password, encryption_data);
839   - } else {
840   - m->encryption_key = encryption_key;
841   - }
842 794 }
843 795  
844 796 void
845 797 QPDFWriter::setDataKey(int objid)
846 798 {
847   - m->cur_data_key = QPDF::compute_data_key(
848   - m->encryption_key, objid, 0, m->encrypt_use_aes, m->encryption_V, m->encryption_R);
  799 + if (m->encryption) {
  800 + m->cur_data_key = QPDF::compute_data_key(
  801 + m->encryption_key,
  802 + objid,
  803 + 0,
  804 + m->encrypt_use_aes,
  805 + m->encryption->getV(),
  806 + m->encryption->getR());
  807 + }
849 808 }
850 809  
851 810 unsigned int
... ... @@ -979,7 +938,7 @@ QPDFWriter::PipelinePopper::~PipelinePopper()
979 938 void
980 939 QPDFWriter::adjustAESStreamLength(size_t& length)
981 940 {
982   - if (m->encrypted && (!m->cur_data_key.empty()) && m->encrypt_use_aes) {
  941 + if (m->encryption && !m->cur_data_key.empty() && m->encrypt_use_aes) {
983 942 // Stream length will be padded with 1 to 16 bytes to end up as a multiple of 16. It will
984 943 // also be prepended by 16 bits of random data.
985 944 length += 32 - (length & 0xf);
... ... @@ -989,7 +948,7 @@ QPDFWriter::adjustAESStreamLength(size_t&amp; length)
989 948 void
990 949 QPDFWriter::pushEncryptionFilter(PipelinePopper& pp)
991 950 {
992   - if (m->encrypted && (!m->cur_data_key.empty())) {
  951 + if (m->encryption && !m->cur_data_key.empty()) {
993 952 Pipeline* p = nullptr;
994 953 if (m->encrypt_use_aes) {
995 954 p = new Pl_AES_PDF(
... ... @@ -1232,7 +1191,7 @@ QPDFWriter::writeTrailer(
1232 1191  
1233 1192 if (which != t_lin_second) {
1234 1193 // Write reference to encryption dictionary
1235   - if (m->encrypted) {
  1194 + if (m->encryption) {
1236 1195 writeString(" /Encrypt ");
1237 1196 writeString(std::to_string(m->encryption_dict_objid));
1238 1197 writeString(" 0 R");
... ... @@ -1280,7 +1239,8 @@ QPDFWriter::willFilterStream(
1280 1239 }
1281 1240 bool normalize = false;
1282 1241 bool uncompress = false;
1283   - if (filter_on_write && is_root_metadata && (!m->encrypted || !m->encrypt_metadata)) {
  1242 + if (filter_on_write && is_root_metadata &&
  1243 + (!m->encryption || !m->encryption->getEncryptMetadata())) {
1284 1244 QTC::TC("qpdf", "QPDFWriter not compressing metadata");
1285 1245 filter = true;
1286 1246 compress_stream = false;
... ... @@ -1568,7 +1528,7 @@ QPDFWriter::unparseObject(
1568 1528 QPDFObjectHandle stream_dict = object.getDict();
1569 1529  
1570 1530 m->cur_stream_length = stream_data.size();
1571   - if (is_metadata && m->encrypted && (!m->encrypt_metadata)) {
  1531 + if (is_metadata && m->encryption && !m->encryption->getEncryptMetadata()) {
1572 1532 // Don't encrypt stream data for the metadata stream
1573 1533 m->cur_data_key.clear();
1574 1534 }
... ... @@ -1582,17 +1542,16 @@ QPDFWriter::unparseObject(
1582 1542 writeString(stream_data);
1583 1543 }
1584 1544  
1585   - if (m->newline_before_endstream || (m->qdf_mode && last_char != '\n')) {
1586   - writeString("\n");
1587   - m->added_newline = true;
  1545 + if ((m->added_newline =
  1546 + m->newline_before_endstream || (m->qdf_mode && last_char != '\n'))) {
  1547 + writeString("\nendstream");
1588 1548 } else {
1589   - m->added_newline = false;
  1549 + writeString("endstream");
1590 1550 }
1591   - writeString("endstream");
1592 1551 } else if (tc == ::ot_string) {
1593 1552 std::string val;
1594   - if (m->encrypted && (!(flags & f_in_ostream)) && (!(flags & f_no_encryption)) &&
1595   - (!m->cur_data_key.empty())) {
  1553 + if (m->encryption && !(flags & f_in_ostream) && !(flags & f_no_encryption) &&
  1554 + !m->cur_data_key.empty()) {
1596 1555 val = object.getStringValue();
1597 1556 if (m->encrypt_use_aes) {
1598 1557 Pl_Buffer bufpl("encrypted string");
... ... @@ -1775,7 +1734,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1775 1734 writeStringQDF("\n");
1776 1735 writeStringNoQDF(" ");
1777 1736 writeString(">>\nstream\n");
1778   - if (m->encrypted) {
  1737 + if (m->encryption) {
1779 1738 QTC::TC("qpdf", "QPDFWriter encrypt object stream");
1780 1739 }
1781 1740 {
... ... @@ -2132,7 +2091,7 @@ QPDFWriter::doWriteSetup()
2132 2091 if (m->pclm) {
2133 2092 m->stream_decode_level = qpdf_dl_none;
2134 2093 m->compress_streams = false;
2135   - m->encrypted = false;
  2094 + m->encryption = nullptr;
2136 2095 }
2137 2096  
2138 2097 if (m->qdf_mode) {
... ... @@ -2147,7 +2106,7 @@ QPDFWriter::doWriteSetup()
2147 2106 }
2148 2107 }
2149 2108  
2150   - if (m->encrypted) {
  2109 + if (m->encryption) {
2151 2110 // Encryption has been explicitly set
2152 2111 m->preserve_encryption = false;
2153 2112 } else if (m->normalize_content || !m->compress_streams || m->pclm || m->qdf_mode) {
... ... @@ -2211,7 +2170,7 @@ QPDFWriter::doWriteSetup()
2211 2170 }
2212 2171 }
2213 2172  
2214   - if (m->linearized || m->encrypted) {
  2173 + if (m->linearized || m->encryption) {
2215 2174 // The document catalog is not allowed to be compressed in linearized files either. It
2216 2175 // also appears that Adobe Reader 8.0.0 has a bug that prevents it from being able to
2217 2176 // handle encrypted files with compressed document catalogs, so we disable them in that
... ... @@ -2312,13 +2271,48 @@ void
2312 2271 QPDFWriter::writeEncryptionDictionary()
2313 2272 {
2314 2273 m->encryption_dict_objid = openObject(m->encryption_dict_objid);
  2274 + auto& enc = *m->encryption;
  2275 + auto const V = enc.getV();
  2276 +
2315 2277 writeString("<<");
2316   - for (auto const& iter: m->encryption_dictionary) {
2317   - writeString(" ");
2318   - writeString(iter.first);
2319   - writeString(" ");
2320   - writeString(iter.second);
  2278 + if (V >= 4) {
  2279 + writeString(" /CF << /StdCF << /AuthEvent /DocOpen /CFM ");
  2280 + writeString(m->encrypt_use_aes ? ((V < 5) ? "/AESV2" : "/AESV3") : "/V2");
  2281 + // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of
  2282 + // MacOS won't open encrypted files without it.
  2283 + writeString((V < 5) ? " /Length 16 >> >>" : " /Length 32 >> >>");
  2284 + if (!m->encryption->getEncryptMetadata()) {
  2285 + writeString(" /EncryptMetadata false");
  2286 + }
  2287 + }
  2288 + writeString(" /Filter /Standard /Length ");
  2289 + writeString(std::to_string(enc.getLengthBytes() * 8));
  2290 + writeString(" /O ");
  2291 + writeString(QPDF_String(enc.getO()).unparse(true));
  2292 + if (V >= 4) {
  2293 + writeString(" /OE ");
  2294 + writeString(QPDF_String(enc.getOE()).unparse(true));
  2295 + }
  2296 + writeString(" /P ");
  2297 + writeString(std::to_string(enc.getP()));
  2298 + if (V >= 5) {
  2299 + writeString(" /Perms ");
  2300 + writeString(QPDF_String(enc.getPerms()).unparse(true));
  2301 + }
  2302 + writeString(" /R ");
  2303 + writeString(std::to_string(enc.getR()));
  2304 +
  2305 + if (V >= 4) {
  2306 + writeString(" /StmF /StdCF /StrF /StdCF");
  2307 + }
  2308 + writeString(" /U ");
  2309 + writeString(QPDF_String(enc.getU()).unparse(true));
  2310 + if (V >= 4) {
  2311 + writeString(" /UE ");
  2312 + writeString(QPDF_String(enc.getUE()).unparse(true));
2321 2313 }
  2314 + writeString(" /V ");
  2315 + writeString(std::to_string(enc.getV()));
2322 2316 writeString(" >>");
2323 2317 closeObject(m->encryption_dict_objid);
2324 2318 }
... ... @@ -2380,7 +2374,7 @@ QPDFWriter::writeHintStream(int hint_id)
2380 2374 writeString(std::to_string(hlen));
2381 2375 writeString(" >>\nstream\n");
2382 2376  
2383   - if (m->encrypted) {
  2377 + if (m->encryption) {
2384 2378 QTC::TC("qpdf", "QPDFWriter encrypted hint stream");
2385 2379 }
2386 2380 char last_char = hint_buffer.empty() ? '\0' : hint_buffer.back();
... ... @@ -2651,7 +2645,7 @@ QPDFWriter::writeLinearized()
2651 2645 int part4_first_obj = m->next_objid;
2652 2646 m->next_objid += QIntC::to_int(part4.size());
2653 2647 int after_part4 = m->next_objid;
2654   - if (m->encrypted) {
  2648 + if (m->encryption) {
2655 2649 m->encryption_dict_objid = m->next_objid++;
2656 2650 }
2657 2651 int hint_id = m->next_objid++;
... ... @@ -2836,7 +2830,7 @@ QPDFWriter::writeLinearized()
2836 2830 }
2837 2831 writeObject(cur_object);
2838 2832 if (cur_object.getObjectID() == part4_end_marker) {
2839   - if (m->encrypted) {
  2833 + if (m->encryption) {
2840 2834 writeEncryptionDictionary();
2841 2835 }
2842 2836 if (pass == 1) {
... ... @@ -3060,7 +3054,7 @@ QPDFWriter::writeStandard()
3060 3054 }
3061 3055  
3062 3056 // Write out the encryption dictionary, if any
3063   - if (m->encrypted) {
  3057 + if (m->encryption) {
3064 3058 writeEncryptionDictionary();
3065 3059 }
3066 3060  
... ...
libqpdf/QPDF_encryption.cc
... ... @@ -12,21 +12,23 @@
12 12 #include <qpdf/Pl_Buffer.hh>
13 13 #include <qpdf/Pl_RC4.hh>
14 14 #include <qpdf/Pl_SHA2.hh>
  15 +#include <qpdf/QPDFObjectHandle_private.hh>
15 16 #include <qpdf/QTC.hh>
16 17 #include <qpdf/QUtil.hh>
17 18 #include <qpdf/RC4.hh>
  19 +#include <qpdf/Util.hh>
18 20  
19 21 #include <algorithm>
20 22 #include <cstring>
21 23  
22   -static unsigned char const padding_string[] = {
23   - 0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08,
24   - 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a};
  24 +using namespace qpdf;
  25 +using namespace std::literals;
25 26  
26   -static unsigned int const key_bytes = 32;
  27 +static std::string padding_string =
  28 + "\x28\xbf\x4e\x5e\x4e\x75\x8a\x41\x64\x00\x4e\x56\xff\xfa\x01\x08"
  29 + "\x2e\x2e\x00\xb6\xd0\x68\x3e\x80\x2f\x0c\xa9\xfe\x64\x53\x69\x7a"s;
27 30  
28   -// V4 key lengths apply to V <= 4
29   -static unsigned int const OU_key_bytes_V4 = sizeof(MD5::Digest);
  31 +static unsigned int const key_bytes = 32;
30 32  
31 33 static unsigned int const OU_key_bytes_V5 = 48;
32 34 static unsigned int const OUE_key_bytes_V5 = 32;
... ... @@ -53,7 +55,21 @@ QPDF::EncryptionData::getLengthBytes() const
53 55 int
54 56 QPDF::EncryptionData::getP() const
55 57 {
56   - return this->P;
  58 + return static_cast<int>(P.to_ulong());
  59 +}
  60 +
  61 +bool
  62 +QPDF::EncryptionData::getP(size_t bit) const
  63 +{
  64 + qpdf_assert_debug(bit);
  65 + return P.test(bit - 1);
  66 +}
  67 +
  68 +bool
  69 +QPDF::EncryptionParameters::P(size_t bit) const
  70 +{
  71 + qpdf_assert_debug(bit);
  72 + return P_.test(bit - 1);
57 73 }
58 74  
59 75 std::string const&
... ... @@ -111,6 +127,25 @@ QPDF::EncryptionData::setU(std::string const&amp; U)
111 127 }
112 128  
113 129 void
  130 +QPDF::EncryptionData::setP(size_t bit, bool val)
  131 +{
  132 + qpdf_assert_debug(bit);
  133 + P.set(bit - 1, val);
  134 +}
  135 +
  136 +void
  137 +QPDF::EncryptionData::setP(unsigned long val)
  138 +{
  139 + P = std::bitset<32>(val);
  140 +}
  141 +
  142 +void
  143 +QPDF::EncryptionData::setId1(std::string const& val)
  144 +{
  145 + id1 = val;
  146 +}
  147 +
  148 +void
114 149 QPDF::EncryptionData::setV5EncryptionParameters(
115 150 std::string const& O,
116 151 std::string const& OE,
... ... @@ -125,84 +160,62 @@ QPDF::EncryptionData::setV5EncryptionParameters(
125 160 this->Perms = Perms;
126 161 }
127 162  
128   -static void
129   -pad_or_truncate_password_V4(std::string const& password, char k1[key_bytes])
130   -{
131   - size_t password_bytes = std::min(QIntC::to_size(key_bytes), password.length());
132   - size_t pad_bytes = key_bytes - password_bytes;
133   - memcpy(k1, password.c_str(), password_bytes);
134   - memcpy(k1 + password_bytes, padding_string, pad_bytes);
135   -}
136   -
137 163 void
138 164 QPDF::trim_user_password(std::string& user_password)
139 165 {
140 166 // Although unnecessary, this routine trims the padding string from the end of a user password.
141 167 // Its only purpose is for recovery of user passwords which is done in the test suite.
142   - char const* cstr = user_password.c_str();
143   - size_t len = user_password.length();
144   - if (len < key_bytes) {
  168 + if (user_password.size() < key_bytes) {
145 169 return;
146 170 }
147 171  
148   - char const* p1 = cstr;
149   - char const* p2 = nullptr;
150   - while ((p2 = strchr(p1, '\x28')) != nullptr) {
151   - size_t idx = toS(p2 - cstr);
152   - if (memcmp(p2, padding_string, len - idx) == 0) {
153   - user_password = user_password.substr(0, idx);
  172 + auto idx = user_password.find('\x28');
  173 +
  174 + while (idx != user_password.npos) {
  175 + if (padding_string.starts_with(user_password.substr(idx))) {
  176 + user_password.resize(idx);
154 177 return;
155   - } else {
156   - QTC::TC("qpdf", "QPDF_encryption skip 0x28");
157   - p1 = p2 + 1;
158 178 }
  179 + QTC::TC("qpdf", "QPDF_encryption skip 0x28");
  180 + idx = user_password.find('\x28', ++idx);
159 181 }
160 182 }
161 183  
162 184 static std::string
163   -pad_or_truncate_password_V4(std::string const& password)
  185 +pad_or_truncate_password_V4(std::string password)
164 186 {
165   - char k1[key_bytes];
166   - pad_or_truncate_password_V4(password, k1);
167   - return {k1, key_bytes};
  187 + if (password.size() < key_bytes) {
  188 + password.append(padding_string);
  189 + }
  190 + password.resize(key_bytes);
  191 + return password;
168 192 }
169 193  
170 194 static std::string
171   -truncate_password_V5(std::string const& password)
172   -{
173   - return password.substr(0, std::min(static_cast<size_t>(127), password.length()));
174   -}
175   -
176   -static void
177   -iterate_md5_digest(MD5& md5, MD5::Digest& digest, int iterations, int key_len)
  195 +iterate_md5_digest(MD5& md5, int iterations, int key_len)
178 196 {
  197 + MD5::Digest digest;
179 198 md5.digest(digest);
180   -
  199 + auto len = std::min(QIntC::to_size(key_len), sizeof(digest));
181 200 for (int i = 0; i < iterations; ++i) {
182 201 MD5 m;
183   - m.encodeDataIncrementally(reinterpret_cast<char*>(digest), QIntC::to_size(key_len));
  202 + m.encodeDataIncrementally(reinterpret_cast<char*>(digest), len);
184 203 m.digest(digest);
185 204 }
  205 + return {reinterpret_cast<char*>(digest), len};
186 206 }
187 207  
188 208 static void
189   -iterate_rc4(
190   - unsigned char* data,
191   - size_t data_len,
192   - unsigned char* okey,
193   - int key_len,
194   - int iterations,
195   - bool reverse)
  209 +iterate_rc4(std::string& data, std::string_view okey, int iterations, bool reverse)
196 210 {
197   - auto key_ph = std::make_unique<unsigned char[]>(QIntC::to_size(key_len));
198   - unsigned char* key = key_ph.get();
  211 + auto len = okey.size();
  212 + std::string key(len, '\0');
199 213 for (int i = 0; i < iterations; ++i) {
200 214 int const xor_value = (reverse ? iterations - 1 - i : i);
201   - for (int j = 0; j < key_len; ++j) {
202   - key[j] = static_cast<unsigned char>(okey[j] ^ xor_value);
  215 + for (size_t j = 0; j < len; ++j) {
  216 + key[j] = static_cast<char>(okey[j] ^ xor_value);
203 217 }
204   - RC4 rc4(key, QIntC::to_int(key_len));
205   - rc4.process(data, data_len, data);
  218 + RC4::process(key, data);
206 219 }
207 220 }
208 221  
... ... @@ -236,12 +249,9 @@ process_with_aes(
236 249 }
237 250 }
238 251  
239   -static std::string
240   -hash_V5(
241   - std::string const& password,
242   - std::string const& salt,
243   - std::string const& udata,
244   - QPDF::EncryptionData const& data)
  252 +std::string
  253 +QPDF::EncryptionData::hash_V5(
  254 + std::string const& password, std::string const& salt, std::string const& udata) const
245 255 {
246 256 Pl_SHA2 hash(256);
247 257 hash.writeString(password);
... ... @@ -251,7 +261,7 @@ hash_V5(
251 261 std::string K = hash.getRawDigest();
252 262  
253 263 std::string result;
254   - if (data.getR() < 6) {
  264 + if (getR() < 6) {
255 265 result = K;
256 266 } else {
257 267 // Algorithm 2.B from ISO 32000-1 chapter 7: Computing a hash
... ... @@ -348,30 +358,31 @@ QPDF::compute_data_key(
348 358 if (use_aes) {
349 359 result += "sAlT";
350 360 }
351   -
352   - MD5 md5;
353   - md5.encodeDataIncrementally(result.c_str(), result.length());
354   - MD5::Digest digest;
355   - md5.digest(digest);
356   - return {reinterpret_cast<char*>(digest), std::min(result.length(), toS(16))};
  361 + return MD5::digest(result).substr(0, result.size());
357 362 }
358 363  
359 364 std::string
360 365 QPDF::compute_encryption_key(std::string const& password, EncryptionData const& data)
361 366 {
362   - if (data.getV() >= 5) {
  367 + return data.compute_encryption_key(password);
  368 +}
  369 +
  370 +std::string
  371 +QPDF::EncryptionData::compute_encryption_key(std::string const& password) const
  372 +{
  373 + if (getV() >= 5) {
363 374 // For V >= 5, the encryption key is generated and stored in the file, encrypted separately
364 375 // with both user and owner passwords.
365   - return recover_encryption_key_with_password(password, data);
  376 + return recover_encryption_key_with_password(password);
366 377 } else {
367 378 // For V < 5, the encryption key is derived from the user
368 379 // password.
369   - return compute_encryption_key_from_password(password, data);
  380 + return compute_encryption_key_from_password(password);
370 381 }
371 382 }
372 383  
373 384 std::string
374   -QPDF::compute_encryption_key_from_password(std::string const& password, EncryptionData const& data)
  385 +QPDF::EncryptionData::compute_encryption_key_from_password(std::string const& password) const
375 386 {
376 387 // Algorithm 3.2 from the PDF 1.7 Reference Manual
377 388  
... ... @@ -381,290 +392,185 @@ QPDF::compute_encryption_key_from_password(std::string const&amp; password, Encrypti
381 392 // password to be presented in its final form.
382 393  
383 394 MD5 md5;
384   - md5.encodeDataIncrementally(pad_or_truncate_password_V4(password).c_str(), key_bytes);
385   - md5.encodeDataIncrementally(data.getO().c_str(), key_bytes);
  395 + md5.encodeDataIncrementally(pad_or_truncate_password_V4(password));
  396 + md5.encodeDataIncrementally(getO());
386 397 char pbytes[4];
387   - int P = data.getP();
388   - pbytes[0] = static_cast<char>(P & 0xff);
389   - pbytes[1] = static_cast<char>((P >> 8) & 0xff);
390   - pbytes[2] = static_cast<char>((P >> 16) & 0xff);
391   - pbytes[3] = static_cast<char>((P >> 24) & 0xff);
  398 + int p = getP();
  399 + pbytes[0] = static_cast<char>(p & 0xff);
  400 + pbytes[1] = static_cast<char>((p >> 8) & 0xff);
  401 + pbytes[2] = static_cast<char>((p >> 16) & 0xff);
  402 + pbytes[3] = static_cast<char>((p >> 24) & 0xff);
392 403 md5.encodeDataIncrementally(pbytes, 4);
393   - md5.encodeDataIncrementally(data.getId1().c_str(), data.getId1().length());
394   - if ((data.getR() >= 4) && (!data.getEncryptMetadata())) {
395   - char bytes[4];
396   - memset(bytes, 0xff, 4);
397   - md5.encodeDataIncrementally(bytes, 4);
  404 + md5.encodeDataIncrementally(getId1());
  405 + if (getR() >= 4 && !getEncryptMetadata()) {
  406 + md5.encodeDataIncrementally("\xff\xff\xff\xff");
398 407 }
399   - MD5::Digest digest;
400   - int key_len = std::min(toI(sizeof(digest)), data.getLengthBytes());
401   - iterate_md5_digest(md5, digest, ((data.getR() >= 3) ? 50 : 0), key_len);
402   - return {reinterpret_cast<char*>(digest), toS(key_len)};
  408 + return iterate_md5_digest(md5, (getR() >= 3 ? 50 : 0), getLengthBytes());
403 409 }
404 410  
405   -static void
406   -compute_O_rc4_key(
407   - std::string const& user_password,
408   - std::string const& owner_password,
409   - QPDF::EncryptionData const& data,
410   - unsigned char key[OU_key_bytes_V4])
  411 +std::string
  412 +QPDF::EncryptionData::compute_O_rc4_key(
  413 + std::string const& user_password, std::string const& owner_password) const
411 414 {
412   - if (data.getV() >= 5) {
  415 + if (getV() >= 5) {
413 416 throw std::logic_error("compute_O_rc4_key called for file with V >= 5");
414 417 }
415   - std::string password = owner_password;
416   - if (password.empty()) {
417   - password = user_password;
418   - }
  418 + std::string password = owner_password.empty() ? user_password : owner_password;
419 419 MD5 md5;
420   - md5.encodeDataIncrementally(pad_or_truncate_password_V4(password).c_str(), key_bytes);
421   - MD5::Digest digest;
422   - int key_len = std::min(QIntC::to_int(sizeof(digest)), data.getLengthBytes());
423   - iterate_md5_digest(md5, digest, ((data.getR() >= 3) ? 50 : 0), key_len);
424   - memcpy(key, digest, OU_key_bytes_V4);
  420 + md5.encodeDataIncrementally(pad_or_truncate_password_V4(password));
  421 + return iterate_md5_digest(md5, (getR() >= 3 ? 50 : 0), getLengthBytes());
425 422 }
426 423  
427   -static std::string
428   -compute_O_value(
429   - std::string const& user_password,
430   - std::string const& owner_password,
431   - QPDF::EncryptionData const& data)
  424 +std::string
  425 +QPDF::EncryptionData::compute_O_value(
  426 + std::string const& user_password, std::string const& owner_password) const
432 427 {
433 428 // Algorithm 3.3 from the PDF 1.7 Reference Manual
434 429  
435   - unsigned char O_key[OU_key_bytes_V4];
436   - compute_O_rc4_key(user_password, owner_password, data, O_key);
437   -
438   - char upass[key_bytes];
439   - pad_or_truncate_password_V4(user_password, upass);
440   - std::string k1(reinterpret_cast<char*>(O_key), OU_key_bytes_V4);
441   - pad_short_parameter(k1, QIntC::to_size(data.getLengthBytes()));
442   - iterate_rc4(
443   - QUtil::unsigned_char_pointer(upass),
444   - key_bytes,
445   - O_key,
446   - data.getLengthBytes(),
447   - (data.getR() >= 3) ? 20 : 1,
448   - false);
449   - return {upass, key_bytes};
  430 + auto upass = pad_or_truncate_password_V4(user_password);
  431 + std::string O_key = compute_O_rc4_key(user_password, owner_password);
  432 + pad_short_parameter(O_key, QIntC::to_size(getLengthBytes()));
  433 + iterate_rc4(upass, O_key, getR() >= 3 ? 20 : 1, false);
  434 + return upass;
450 435 }
451 436  
452   -static std::string
453   -compute_U_value_R2(std::string const& user_password, QPDF::EncryptionData const& data)
  437 +std::string
  438 +QPDF::EncryptionData::compute_U_value_R2(std::string const& user_password) const
454 439 {
455 440 // Algorithm 3.4 from the PDF 1.7 Reference Manual
456 441  
457   - std::string k1 = QPDF::compute_encryption_key(user_password, data);
458   - char udata[key_bytes];
459   - pad_or_truncate_password_V4("", udata);
460   - pad_short_parameter(k1, QIntC::to_size(data.getLengthBytes()));
461   - iterate_rc4(
462   - QUtil::unsigned_char_pointer(udata),
463   - key_bytes,
464   - QUtil::unsigned_char_pointer(k1),
465   - data.getLengthBytes(),
466   - 1,
467   - false);
468   - return {udata, key_bytes};
  442 + std::string k1 = compute_encryption_key(user_password);
  443 + auto udata = padding_string;
  444 + pad_short_parameter(k1, QIntC::to_size(getLengthBytes()));
  445 + iterate_rc4(udata, k1, 1, false);
  446 + return udata;
469 447 }
470 448  
471   -static std::string
472   -compute_U_value_R3(std::string const& user_password, QPDF::EncryptionData const& data)
  449 +std::string
  450 +QPDF::EncryptionData::compute_U_value_R3(std::string const& user_password) const
473 451 {
474 452 // Algorithm 3.5 from the PDF 1.7 Reference Manual
475 453  
476   - std::string k1 = QPDF::compute_encryption_key(user_password, data);
  454 + std::string k1 = compute_encryption_key(user_password);
477 455 MD5 md5;
478   - md5.encodeDataIncrementally(pad_or_truncate_password_V4("").c_str(), key_bytes);
479   - md5.encodeDataIncrementally(data.getId1().c_str(), data.getId1().length());
480   - MD5::Digest digest;
481   - md5.digest(digest);
482   - pad_short_parameter(k1, QIntC::to_size(data.getLengthBytes()));
483   - iterate_rc4(
484   - digest,
485   - sizeof(MD5::Digest),
486   - QUtil::unsigned_char_pointer(k1),
487   - data.getLengthBytes(),
488   - 20,
489   - false);
490   - char result[key_bytes];
491   - memcpy(result, digest, sizeof(MD5::Digest));
492   - // pad with arbitrary data -- make it consistent for the sake of
493   - // testing
494   - for (unsigned int i = sizeof(MD5::Digest); i < key_bytes; ++i) {
495   - result[i] = static_cast<char>((i * i) % 0xff);
496   - }
497   - return {result, key_bytes};
  456 + md5.encodeDataIncrementally(padding_string);
  457 + md5.encodeDataIncrementally(getId1());
  458 + auto result = md5.digest();
  459 + pad_short_parameter(k1, QIntC::to_size(getLengthBytes()));
  460 + iterate_rc4(result, k1, 20, false);
  461 + // pad with arbitrary data -- make it consistent for the sake of testing
  462 + result += "\x0\x21\x44\x69\x90\xb9\xe4\x11\x40\x71\xa4\xd9\x10\x49\x84\xc1"s;
  463 + return result;
498 464 }
499 465  
500   -static std::string
501   -compute_U_value(std::string const& user_password, QPDF::EncryptionData const& data)
  466 +std::string
  467 +QPDF::EncryptionData::compute_U_value(std::string const& user_password) const
502 468 {
503   - if (data.getR() >= 3) {
504   - return compute_U_value_R3(user_password, data);
  469 + if (getR() >= 3) {
  470 + return compute_U_value_R3(user_password);
505 471 }
506 472  
507   - return compute_U_value_R2(user_password, data);
  473 + return compute_U_value_R2(user_password);
508 474 }
509 475  
510   -static bool
511   -check_user_password_V4(std::string const& user_password, QPDF::EncryptionData const& data)
  476 +bool
  477 +QPDF::EncryptionData::check_user_password_V4(std::string const& user_password) const
512 478 {
513 479 // Algorithm 3.6 from the PDF 1.7 Reference Manual
514 480  
515   - std::string u_value = compute_U_value(user_password, data);
516   - size_t to_compare = ((data.getR() >= 3) ? sizeof(MD5::Digest) : key_bytes);
517   - return (memcmp(data.getU().c_str(), u_value.c_str(), to_compare) == 0);
  481 + std::string u_value = compute_U_value(user_password);
  482 + size_t to_compare = (getR() >= 3 ? sizeof(MD5::Digest) : key_bytes);
  483 + return memcmp(getU().c_str(), u_value.c_str(), to_compare) == 0;
518 484 }
519 485  
520   -static bool
521   -check_user_password_V5(std::string const& user_password, QPDF::EncryptionData const& data)
  486 +bool
  487 +QPDF::EncryptionData::check_user_password_V5(std::string const& user_password) const
522 488 {
523 489 // Algorithm 3.11 from the PDF 1.7 extension level 3
524 490  
525   - std::string user_data = data.getU().substr(0, 32);
526   - std::string validation_salt = data.getU().substr(32, 8);
527   - std::string password = truncate_password_V5(user_password);
528   - return (hash_V5(password, validation_salt, "", data) == user_data);
  491 + std::string user_data = getU().substr(0, 32);
  492 + std::string validation_salt = getU().substr(32, 8);
  493 + std::string password = user_password.substr(0, 127);
  494 + return hash_V5(user_password.substr(0, 127), validation_salt, "") == user_data;
529 495 }
530 496  
531   -static bool
532   -check_user_password(std::string const& user_password, QPDF::EncryptionData const& data)
  497 +bool
  498 +QPDF::EncryptionData::check_user_password(std::string const& user_password) const
533 499 {
534   - if (data.getV() < 5) {
535   - return check_user_password_V4(user_password, data);
  500 + if (getV() < 5) {
  501 + return check_user_password_V4(user_password);
536 502 } else {
537   - return check_user_password_V5(user_password, data);
  503 + return check_user_password_V5(user_password);
538 504 }
539 505 }
540 506  
541   -static bool
542   -check_owner_password_V4(
543   - std::string& user_password, std::string const& owner_password, QPDF::EncryptionData const& data)
  507 +bool
  508 +QPDF::EncryptionData::check_owner_password_V4(
  509 + std::string& user_password, std::string const& owner_password) const
544 510 {
545 511 // Algorithm 3.7 from the PDF 1.7 Reference Manual
546 512  
547   - unsigned char key[OU_key_bytes_V4];
548   - compute_O_rc4_key(user_password, owner_password, data, key);
549   - unsigned char O_data[key_bytes];
550   - memcpy(O_data, QUtil::unsigned_char_pointer(data.getO()), key_bytes);
551   - std::string k1(reinterpret_cast<char*>(key), OU_key_bytes_V4);
552   - pad_short_parameter(k1, QIntC::to_size(data.getLengthBytes()));
553   - iterate_rc4(
554   - O_data,
555   - key_bytes,
556   - QUtil::unsigned_char_pointer(k1),
557   - data.getLengthBytes(),
558   - (data.getR() >= 3) ? 20 : 1,
559   - true);
560   - std::string new_user_password = std::string(reinterpret_cast<char*>(O_data), key_bytes);
561   - bool result = false;
562   - if (check_user_password(new_user_password, data)) {
563   - result = true;
  513 + auto key = compute_O_rc4_key(user_password, owner_password);
  514 + pad_short_parameter(key, QIntC::to_size(getLengthBytes()));
  515 + auto new_user_password = O.substr(0, key_bytes);
  516 + iterate_rc4(new_user_password, key, (getR() >= 3) ? 20 : 1, true);
  517 + if (check_user_password(new_user_password)) {
564 518 user_password = new_user_password;
  519 + return true;
565 520 }
566   - return result;
  521 + return false;
567 522 }
568 523  
569   -static bool
570   -check_owner_password_V5(std::string const& owner_password, QPDF::EncryptionData const& data)
  524 +bool
  525 +QPDF::EncryptionData::check_owner_password_V5(std::string const& owner_password) const
571 526 {
572 527 // Algorithm 3.12 from the PDF 1.7 extension level 3
573 528  
574   - std::string user_data = data.getU().substr(0, 48);
575   - std::string owner_data = data.getO().substr(0, 32);
576   - std::string validation_salt = data.getO().substr(32, 8);
577   - std::string password = truncate_password_V5(owner_password);
578   - return (hash_V5(password, validation_salt, user_data, data) == owner_data);
  529 + std::string user_data = getU().substr(0, 48);
  530 + std::string owner_data = getO().substr(0, 32);
  531 + std::string validation_salt = getO().substr(32, 8);
  532 + return hash_V5(owner_password.substr(0, 127), validation_salt, user_data) == owner_data;
579 533 }
580 534  
581   -static bool
582   -check_owner_password(
583   - std::string& user_password, std::string const& owner_password, QPDF::EncryptionData const& data)
  535 +bool
  536 +QPDF::EncryptionData::check_owner_password(
  537 + std::string& user_password, std::string const& owner_password) const
584 538 {
585   - if (data.getV() < 5) {
586   - return check_owner_password_V4(user_password, owner_password, data);
  539 + if (getV() < 5) {
  540 + return check_owner_password_V4(user_password, owner_password);
587 541 } else {
588   - return check_owner_password_V5(owner_password, data);
  542 + return check_owner_password_V5(owner_password);
589 543 }
590 544 }
591 545  
592 546 std::string
593   -QPDF::recover_encryption_key_with_password(std::string const& password, EncryptionData const& data)
  547 +QPDF::EncryptionData::recover_encryption_key_with_password(std::string const& password) const
594 548 {
595 549 // Disregard whether Perms is valid.
596 550 bool disregard;
597   - return recover_encryption_key_with_password(password, data, disregard);
  551 + return recover_encryption_key_with_password(password, disregard);
598 552 }
599 553  
600   -static void
601   -compute_U_UE_value_V5(
602   - std::string const& user_password,
603   - std::string const& encryption_key,
604   - QPDF::EncryptionData const& data,
605   - std::string& U,
606   - std::string& UE)
607   -{
608   - // Algorithm 3.8 from the PDF 1.7 extension level 3
609   - char k[16];
610   - QUtil::initializeWithRandomBytes(reinterpret_cast<unsigned char*>(k), sizeof(k));
611   - std::string validation_salt(k, 8);
612   - std::string key_salt(k + 8, 8);
613   - U = hash_V5(user_password, validation_salt, "", data) + validation_salt + key_salt;
614   - std::string intermediate_key = hash_V5(user_password, key_salt, "", data);
615   - UE = process_with_aes(intermediate_key, true, encryption_key);
616   -}
617   -
618   -static void
619   -compute_O_OE_value_V5(
620   - std::string const& owner_password,
621   - std::string const& encryption_key,
622   - QPDF::EncryptionData const& data,
623   - std::string const& U,
624   - std::string& O,
625   - std::string& OE)
626   -{
627   - // Algorithm 3.9 from the PDF 1.7 extension level 3
628   - char k[16];
629   - QUtil::initializeWithRandomBytes(reinterpret_cast<unsigned char*>(k), sizeof(k));
630   - std::string validation_salt(k, 8);
631   - std::string key_salt(k + 8, 8);
632   - O = hash_V5(owner_password, validation_salt, U, data) + validation_salt + key_salt;
633   - std::string intermediate_key = hash_V5(owner_password, key_salt, U, data);
634   - OE = process_with_aes(intermediate_key, true, encryption_key);
635   -}
636   -
637   -void
638   -compute_Perms_value_V5_clear(
639   - std::string const& encryption_key, QPDF::EncryptionData const& data, unsigned char k[16])
  554 +std::string
  555 +QPDF::EncryptionData::compute_Perms_value_V5_clear() const
640 556 {
641 557 // From algorithm 3.10 from the PDF 1.7 extension level 3
642   - unsigned long long extended_perms =
643   - 0xffffffff00000000LL | static_cast<unsigned long long>(data.getP());
644   - for (int i = 0; i < 8; ++i) {
645   - k[i] = static_cast<unsigned char>(extended_perms & 0xff);
646   - extended_perms >>= 8;
  558 + std::string k = " \xff\xff\xff\xffTadb ";
  559 + int perms = getP();
  560 + for (size_t i = 0; i < 4; ++i) {
  561 + k[i] = static_cast<char>(perms & 0xff);
  562 + perms >>= 8;
647 563 }
648   - k[8] = data.getEncryptMetadata() ? 'T' : 'F';
649   - k[9] = 'a';
650   - k[10] = 'd';
651   - k[11] = 'b';
652   - QUtil::initializeWithRandomBytes(k + 12, 4);
653   -}
654   -
655   -static std::string
656   -compute_Perms_value_V5(std::string const& encryption_key, QPDF::EncryptionData const& data)
657   -{
658   - // Algorithm 3.10 from the PDF 1.7 extension level 3
659   - unsigned char k[16];
660   - compute_Perms_value_V5_clear(encryption_key, data, k);
661   - return process_with_aes(
662   - encryption_key, true, std::string(reinterpret_cast<char*>(k), sizeof(k)));
  564 + if (!getEncryptMetadata()) {
  565 + k[8] = 'F';
  566 + }
  567 + QUtil::initializeWithRandomBytes(reinterpret_cast<unsigned char*>(&k[12]), 4);
  568 + return k;
663 569 }
664 570  
665 571 std::string
666   -QPDF::recover_encryption_key_with_password(
667   - std::string const& password, EncryptionData const& data, bool& perms_valid)
  572 +QPDF::EncryptionData::recover_encryption_key_with_password(
  573 + std::string const& password, bool& perms_valid) const
668 574 {
669 575 // Algorithm 3.2a from the PDF 1.7 extension level 3
670 576  
... ... @@ -673,129 +579,136 @@ QPDF::recover_encryption_key_with_password(
673 579 // profile of stringprep (RFC 3454) and then convert the result to UTF-8.
674 580  
675 581 perms_valid = false;
676   - std::string key_password = truncate_password_V5(password);
  582 + std::string key_password = password.substr(0, 127);
677 583 std::string key_salt;
678 584 std::string user_data;
679 585 std::string encrypted_file_key;
680   - if (check_owner_password_V5(key_password, data)) {
681   - key_salt = data.getO().substr(40, 8);
682   - user_data = data.getU().substr(0, 48);
683   - encrypted_file_key = data.getOE().substr(0, 32);
684   - } else if (check_user_password_V5(key_password, data)) {
685   - key_salt = data.getU().substr(40, 8);
686   - encrypted_file_key = data.getUE().substr(0, 32);
  586 + if (check_owner_password_V5(key_password)) {
  587 + key_salt = getO().substr(40, 8);
  588 + user_data = getU().substr(0, 48);
  589 + encrypted_file_key = getOE().substr(0, 32);
  590 + } else if (check_user_password_V5(key_password)) {
  591 + key_salt = getU().substr(40, 8);
  592 + encrypted_file_key = getUE().substr(0, 32);
687 593 }
688   - std::string intermediate_key = hash_V5(key_password, key_salt, user_data, data);
  594 + std::string intermediate_key = hash_V5(key_password, key_salt, user_data);
689 595 std::string file_key = process_with_aes(intermediate_key, false, encrypted_file_key);
690 596  
691 597 // Decrypt Perms and check against expected value
692   - std::string perms_check = process_with_aes(file_key, false, data.getPerms(), 12);
693   - unsigned char k[16];
694   - compute_Perms_value_V5_clear(file_key, data, k);
695   - perms_valid = (memcmp(perms_check.c_str(), k, 12) == 0);
696   -
  598 + auto perms_check = process_with_aes(file_key, false, getPerms()).substr(0, 12);
  599 + perms_valid = compute_Perms_value_V5_clear().substr(0, 12) == perms_check;
697 600 return file_key;
698 601 }
699 602  
700 603 QPDF::encryption_method_e
701   -QPDF::interpretCF(std::shared_ptr<EncryptionParameters> encp, QPDFObjectHandle cf)
  604 +QPDF::EncryptionParameters::interpretCF(QPDFObjectHandle const& cf) const
702 605 {
703   - if (cf.isName()) {
704   - std::string filter = cf.getName();
705   - auto it = encp->crypt_filters.find(filter);
706   - if (it != encp->crypt_filters.end()) {
707   - return it->second;
708   - }
709   - if (filter == "/Identity") {
710   - return e_none;
711   - }
712   - return e_unknown;
  606 + if (!cf.isName()) {
  607 + // Default: /Identity
  608 + return e_none;
  609 + }
  610 + std::string filter = cf.getName();
  611 + auto it = crypt_filters.find(filter);
  612 + if (it != crypt_filters.end()) {
  613 + return it->second;
  614 + }
  615 + if (filter == "/Identity") {
  616 + return e_none;
713 617 }
714   - // Default: /Identity
715   - return e_none;
  618 + return e_unknown;
716 619 }
717 620  
718 621 void
719 622 QPDF::initializeEncryption()
720 623 {
721   - if (m->encp->encryption_initialized) {
  624 + m->encp->initialize(*this);
  625 +}
  626 +
  627 +void
  628 +QPDF::EncryptionParameters::initialize(QPDF& qpdf)
  629 +{
  630 + if (encryption_initialized) {
722 631 return;
723 632 }
724   - m->encp->encryption_initialized = true;
  633 + encryption_initialized = true;
  634 +
  635 + auto& qm = *qpdf.m;
  636 + auto& trailer = qm.trailer;
  637 + auto& file = qm.file;
  638 +
  639 + auto warn_damaged_pdf = [&qpdf](std::string const& msg) {
  640 + qpdf.warn(qpdf.damagedPDF("encryption dictionary", msg));
  641 + };
  642 + auto throw_damaged_pdf = [&qpdf](std::string const& msg) {
  643 + throw qpdf.damagedPDF("encryption dictionary", msg);
  644 + };
  645 + auto unsupported = [&file](std::string const& msg) -> QPDFExc {
  646 + return {
  647 + qpdf_e_unsupported,
  648 + file->getName(),
  649 + "encryption dictionary",
  650 + file->getLastOffset(),
  651 + msg};
  652 + };
725 653  
726 654 // After we initialize encryption parameters, we must use stored key information and never look
727 655 // at /Encrypt again. Otherwise, things could go wrong if someone mutates the encryption
728 656 // dictionary.
729 657  
730   - if (!m->trailer.hasKey("/Encrypt")) {
  658 + if (!trailer.hasKey("/Encrypt")) {
731 659 return;
732 660 }
733 661  
734 662 // Go ahead and set m->encrypted here. That way, isEncrypted will return true even if there
735 663 // were errors reading the encryption dictionary.
736   - m->encp->encrypted = true;
  664 + encrypted = true;
737 665  
738 666 std::string id1;
739   - QPDFObjectHandle id_obj = m->trailer.getKey("/ID");
740   - if ((id_obj.isArray() && (id_obj.getArrayNItems() == 2) && id_obj.getArrayItem(0).isString())) {
741   - id1 = id_obj.getArrayItem(0).getStringValue();
742   - } else {
  667 + auto id_obj = trailer.getKey("/ID");
  668 + if (!id_obj.isArray() || id_obj.getArrayNItems() != 2 || !id_obj.getArrayItem(0).isString()) {
743 669 // Treating a missing ID as the empty string enables qpdf to decrypt some invalid encrypted
744 670 // files with no /ID that poppler can read but Adobe Reader can't.
745   - warn(damagedPDF("trailer", "invalid /ID in trailer dictionary"));
  671 + qpdf.warn(qpdf.damagedPDF("trailer", "invalid /ID in trailer dictionary"));
  672 + } else {
  673 + id1 = id_obj.getArrayItem(0).getStringValue();
746 674 }
747 675  
748   - QPDFObjectHandle encryption_dict = m->trailer.getKey("/Encrypt");
  676 + auto encryption_dict = trailer.getKey("/Encrypt");
749 677 if (!encryption_dict.isDictionary()) {
750   - throw damagedPDF("/Encrypt in trailer dictionary is not a dictionary");
  678 + throw qpdf.damagedPDF("/Encrypt in trailer dictionary is not a dictionary");
751 679 }
752 680  
753 681 if (!(encryption_dict.getKey("/Filter").isName() &&
754 682 (encryption_dict.getKey("/Filter").getName() == "/Standard"))) {
755   - throw QPDFExc(
756   - qpdf_e_unsupported,
757   - m->file->getName(),
758   - "encryption dictionary",
759   - m->file->getLastOffset(),
760   - "unsupported encryption filter");
  683 + throw unsupported("unsupported encryption filter");
761 684 }
762 685 if (!encryption_dict.getKey("/SubFilter").isNull()) {
763   - warn(
764   - qpdf_e_unsupported,
765   - "encryption dictionary",
766   - m->file->getLastOffset(),
767   - "file uses encryption SubFilters, which qpdf does not support");
  686 + qpdf.warn(unsupported("file uses encryption SubFilters, which qpdf does not support"));
768 687 }
769 688  
770 689 if (!(encryption_dict.getKey("/V").isInteger() && encryption_dict.getKey("/R").isInteger() &&
771 690 encryption_dict.getKey("/O").isString() && encryption_dict.getKey("/U").isString() &&
772 691 encryption_dict.getKey("/P").isInteger())) {
773   - throw damagedPDF(
774   - "encryption dictionary",
775   - "some encryption dictionary parameters are missing or the wrong "
776   - "type");
  692 + throw_damaged_pdf("some encryption dictionary parameters are missing or the wrong type");
777 693 }
778 694  
779 695 int V = encryption_dict.getKey("/V").getIntValueAsInt();
780 696 int R = encryption_dict.getKey("/R").getIntValueAsInt();
781 697 std::string O = encryption_dict.getKey("/O").getStringValue();
782 698 std::string U = encryption_dict.getKey("/U").getStringValue();
783   - int P = static_cast<int>(encryption_dict.getKey("/P").getIntValue());
  699 + int p = static_cast<int>(encryption_dict.getKey("/P").getIntValue());
784 700  
785 701 // If supporting new encryption R/V values, remember to update error message inside this if
786 702 // statement.
787   - if (!(((R >= 2) && (R <= 6)) && ((V == 1) || (V == 2) || (V == 4) || (V == 5)))) {
788   - throw QPDFExc(
789   - qpdf_e_unsupported,
790   - m->file->getName(),
791   - "encryption dictionary",
792   - m->file->getLastOffset(),
  703 + if (!(2 <= R && R <= 6 && (V == 1 || V == 2 || V == 4 || V == 5))) {
  704 + throw unsupported(
793 705 "Unsupported /R or /V in encryption dictionary; R = " + std::to_string(R) +
794   - " (max 6), V = " + std::to_string(V) + " (max 5)");
  706 + " (max 6), V = " + std::to_string(V) + " (max 5)");
795 707 }
796 708  
797   - m->encp->encryption_V = V;
798   - m->encp->encryption_R = R;
  709 + P_ = std::bitset<32>(static_cast<unsigned long long>(p));
  710 + encryption_V = V;
  711 + R_ = R;
799 712  
800 713 // OE, UE, and Perms are only present if V >= 5.
801 714 std::string OE;
... ... @@ -806,19 +719,15 @@ QPDF::initializeEncryption()
806 719 // These must be exactly the right number of bytes.
807 720 pad_short_parameter(O, key_bytes);
808 721 pad_short_parameter(U, key_bytes);
809   - if (!((O.length() == key_bytes) && (U.length() == key_bytes))) {
810   - throw damagedPDF(
811   - "encryption dictionary",
812   - "incorrect length for /O and/or /U in encryption dictionary");
  722 + if (!(O.length() == key_bytes && U.length() == key_bytes)) {
  723 + throw_damaged_pdf("incorrect length for /O and/or /U in encryption dictionary");
813 724 }
814 725 } else {
815 726 if (!(encryption_dict.getKey("/OE").isString() &&
816 727 encryption_dict.getKey("/UE").isString() &&
817 728 encryption_dict.getKey("/Perms").isString())) {
818   - throw damagedPDF(
819   - "encryption dictionary",
820   - "some V=5 encryption dictionary parameters are missing or the "
821   - "wrong type");
  729 + throw_damaged_pdf(
  730 + "some V=5 encryption dictionary parameters are missing or the wrong type");
822 731 }
823 732 OE = encryption_dict.getKey("/OE").getStringValue();
824 733 UE = encryption_dict.getKey("/UE").getStringValue();
... ... @@ -832,7 +741,7 @@ QPDF::initializeEncryption()
832 741 pad_short_parameter(Perms, Perms_key_bytes_V5);
833 742 }
834 743  
835   - int Length = 0;
  744 + int Length = 128; // Just take a guess.
836 745 if (V <= 1) {
837 746 Length = 40;
838 747 } else if (V == 4) {
... ... @@ -842,25 +751,20 @@ QPDF::initializeEncryption()
842 751 } else {
843 752 if (encryption_dict.getKey("/Length").isInteger()) {
844 753 Length = encryption_dict.getKey("/Length").getIntValueAsInt();
845   - if ((Length % 8) || (Length < 40) || (Length > 128)) {
846   - Length = 0;
  754 + if (Length % 8 || Length < 40 || Length > 128) {
  755 + Length = 128; // Just take a guess.
847 756 }
848 757 }
849 758 }
850   - if (Length == 0) {
851   - // Still no Length? Just take a guess.
852   - Length = 128;
853   - }
854 759  
855   - m->encp->encrypt_metadata = true;
856   - if ((V >= 4) && (encryption_dict.getKey("/EncryptMetadata").isBool())) {
857   - m->encp->encrypt_metadata = encryption_dict.getKey("/EncryptMetadata").getBoolValue();
  760 + encrypt_metadata = true;
  761 + if (V >= 4 && encryption_dict.getKey("/EncryptMetadata").isBool()) {
  762 + encrypt_metadata = encryption_dict.getKey("/EncryptMetadata").getBoolValue();
858 763 }
859 764  
860   - if ((V == 4) || (V == 5)) {
861   - QPDFObjectHandle CF = encryption_dict.getKey("/CF");
862   - for (auto const& filter: CF.getKeys()) {
863   - QPDFObjectHandle cdict = CF.getKey(filter);
  765 + if (V == 4 || V == 5) {
  766 + auto CF = encryption_dict.getKey("/CF");
  767 + for (auto const& [filter, cdict]: CF.as_dictionary()) {
864 768 if (cdict.isDictionary()) {
865 769 encryption_method_e method = e_none;
866 770 if (cdict.getKey("/CFM").isName()) {
... ... @@ -879,16 +783,13 @@ QPDF::initializeEncryption()
879 783 method = e_unknown;
880 784 }
881 785 }
882   - m->encp->crypt_filters[filter] = method;
  786 + crypt_filters[filter] = method;
883 787 }
884 788 }
885 789  
886   - QPDFObjectHandle StmF = encryption_dict.getKey("/StmF");
887   - QPDFObjectHandle StrF = encryption_dict.getKey("/StrF");
888   - QPDFObjectHandle EFF = encryption_dict.getKey("/EFF");
889   - m->encp->cf_stream = interpretCF(m->encp, StmF);
890   - m->encp->cf_string = interpretCF(m->encp, StrF);
891   - if (EFF.isName()) {
  790 + cf_stream = interpretCF(encryption_dict.getKey("/StmF"));
  791 + cf_string = interpretCF(encryption_dict.getKey("/StrF"));
  792 + if (auto EFF = encryption_dict.getKey("/EFF"); EFF.isName()) {
892 793 // qpdf does not use this for anything other than informational purposes. This is
893 794 // intended to instruct conforming writers on which crypt filter should be used when new
894 795 // file attachments are added to a PDF file, but qpdf never generates encrypted files
... ... @@ -898,55 +799,50 @@ QPDF::initializeEncryption()
898 799 // the way I was imagining. Still, providing this information could be useful when
899 800 // looking at a file generated by something else, such as Acrobat when specifying that
900 801 // only attachments should be encrypted.
901   - m->encp->cf_file = interpretCF(m->encp, EFF);
  802 + cf_file = interpretCF(EFF);
902 803 } else {
903   - m->encp->cf_file = m->encp->cf_stream;
  804 + cf_file = cf_stream;
904 805 }
905 806 }
906 807  
907   - EncryptionData data(V, R, Length / 8, P, O, U, OE, UE, Perms, id1, m->encp->encrypt_metadata);
908   - if (m->provided_password_is_hex_key) {
  808 + EncryptionData data(V, R, Length / 8, p, O, U, OE, UE, Perms, id1, encrypt_metadata);
  809 + if (qm.provided_password_is_hex_key) {
909 810 // ignore passwords in file
910   - } else {
911   - m->encp->owner_password_matched =
912   - check_owner_password(m->encp->user_password, m->encp->provided_password, data);
913   - if (m->encp->owner_password_matched && (V < 5)) {
914   - // password supplied was owner password; user_password has been initialized for V < 5
915   - if (getTrimmedUserPassword() == m->encp->provided_password) {
916   - m->encp->user_password_matched = true;
917   - QTC::TC("qpdf", "QPDF_encryption user matches owner V < 5");
918   - }
919   - } else {
920   - m->encp->user_password_matched = check_user_password(m->encp->provided_password, data);
921   - if (m->encp->user_password_matched) {
922   - m->encp->user_password = m->encp->provided_password;
923   - }
924   - }
925   - if (m->encp->user_password_matched && m->encp->owner_password_matched) {
926   - QTC::TC("qpdf", "QPDF_encryption same password", (V < 5) ? 0 : 1);
  811 + encryption_key = QUtil::hex_decode(provided_password);
  812 + return;
  813 + }
  814 +
  815 + owner_password_matched = data.check_owner_password(user_password, provided_password);
  816 + if (owner_password_matched && V < 5) {
  817 + // password supplied was owner password; user_password has been initialized for V < 5
  818 + if (qpdf.getTrimmedUserPassword() == provided_password) {
  819 + user_password_matched = true;
  820 + QTC::TC("qpdf", "QPDF_encryption user matches owner V < 5");
927 821 }
928   - if (!(m->encp->owner_password_matched || m->encp->user_password_matched)) {
929   - throw QPDFExc(qpdf_e_password, m->file->getName(), "", 0, "invalid password");
  822 + } else {
  823 + user_password_matched = data.check_user_password(provided_password);
  824 + if (user_password_matched) {
  825 + user_password = provided_password;
930 826 }
931 827 }
  828 + if (user_password_matched && owner_password_matched) {
  829 + QTC::TC("qpdf", "QPDF_encryption same password", (V < 5) ? 0 : 1);
  830 + }
  831 + if (!(owner_password_matched || user_password_matched)) {
  832 + throw QPDFExc(qpdf_e_password, file->getName(), "", 0, "invalid password");
  833 + }
932 834  
933   - if (m->provided_password_is_hex_key) {
934   - m->encp->encryption_key = QUtil::hex_decode(m->encp->provided_password);
935   - } else if (V < 5) {
  835 + if (V < 5) {
936 836 // For V < 5, the user password is encrypted with the owner password, and the user password
937 837 // is always used for computing the encryption key.
938   - m->encp->encryption_key = compute_encryption_key(m->encp->user_password, data);
  838 + encryption_key = data.compute_encryption_key(user_password);
939 839 } else {
940 840 // For V >= 5, either password can be used independently to compute the encryption key, and
941 841 // neither password can be used to recover the other.
942 842 bool perms_valid;
943   - m->encp->encryption_key =
944   - recover_encryption_key_with_password(m->encp->provided_password, data, perms_valid);
  843 + encryption_key = data.recover_encryption_key_with_password(provided_password, perms_valid);
945 844 if (!perms_valid) {
946   - warn(damagedPDF(
947   - "encryption dictionary",
948   - "/Perms field in encryption dictionary doesn't match expected "
949   - "value"));
  845 + warn_damaged_pdf("/Perms field in encryption dictionary doesn't match expected value");
950 846 }
951 847 }
952 848 }
... ... @@ -960,12 +856,7 @@ QPDF::getKeyForObject(std::shared_ptr&lt;EncryptionParameters&gt; encp, QPDFObjGen og,
960 856  
961 857 if (og != encp->cached_key_og) {
962 858 encp->cached_object_encryption_key = compute_data_key(
963   - encp->encryption_key,
964   - og.getObj(),
965   - og.getGen(),
966   - use_aes,
967   - encp->encryption_V,
968   - encp->encryption_R);
  859 + encp->encryption_key, og.getObj(), og.getGen(), use_aes, encp->encryption_V, encp->R());
969 860 encp->cached_key_og = og;
970 861 }
971 862  
... ... @@ -1070,7 +961,7 @@ QPDF::decryptStream(
1070 961 QPDFObjectHandle decode_parms = stream_dict.getKey("/DecodeParms");
1071 962 if (decode_parms.isDictionaryOfType("/CryptFilterDecodeParms")) {
1072 963 QTC::TC("qpdf", "QPDF_encryption stream crypt filter");
1073   - method = interpretCF(encp, decode_parms.getKey("/Name"));
  964 + method = encp->interpretCF(decode_parms.getKey("/Name"));
1074 965 method_source = "stream's Crypt decode parameters";
1075 966 }
1076 967 } else if (
... ... @@ -1085,7 +976,7 @@ QPDF::decryptStream(
1085 976 if (crypt_params.isDictionary() &&
1086 977 crypt_params.getKey("/Name").isName()) {
1087 978 QTC::TC("qpdf", "QPDF_encrypt crypt array");
1088   - method = interpretCF(encp, crypt_params.getKey("/Name"));
  979 + method = encp->interpretCF(crypt_params.getKey("/Name"));
1089 980 method_source = "stream's Crypt decode parameters (array)";
1090 981 }
1091 982 }
... ... @@ -1164,17 +1055,23 @@ QPDF::compute_encryption_O_U(
1164 1055 int P,
1165 1056 bool encrypt_metadata,
1166 1057 std::string const& id1,
1167   - std::string& O,
1168   - std::string& U)
  1058 + std::string& out_O,
  1059 + std::string& out_U)
  1060 +{
  1061 + EncryptionData data(V, R, key_len, P, "", "", "", "", "", id1, encrypt_metadata);
  1062 + data.compute_encryption_O_U(user_password, owner_password);
  1063 + out_O = data.getO();
  1064 + out_U = data.getU();
  1065 +}
  1066 +
  1067 +void
  1068 +QPDF::EncryptionData::compute_encryption_O_U(char const* user_password, char const* owner_password)
1169 1069 {
1170 1070 if (V >= 5) {
1171 1071 throw std::logic_error("compute_encryption_O_U called for file with V >= 5");
1172 1072 }
1173   - EncryptionData data(V, R, key_len, P, "", "", "", "", "", id1, encrypt_metadata);
1174   - data.setO(compute_O_value(user_password, owner_password, data));
1175   - O = data.getO();
1176   - data.setU(compute_U_value(user_password, data));
1177   - U = data.getU();
  1073 + O = compute_O_value(user_password, owner_password);
  1074 + U = compute_U_value(user_password);
1178 1075 }
1179 1076  
1180 1077 void
... ... @@ -1188,20 +1085,53 @@ QPDF::compute_encryption_parameters_V5(
1188 1085 bool encrypt_metadata,
1189 1086 std::string const& id1,
1190 1087 std::string& encryption_key,
1191   - std::string& O,
1192   - std::string& U,
1193   - std::string& OE,
1194   - std::string& UE,
1195   - std::string& Perms)
  1088 + std::string& out_O,
  1089 + std::string& out_U,
  1090 + std::string& out_OE,
  1091 + std::string& out_UE,
  1092 + std::string& out_Perms)
1196 1093 {
1197 1094 EncryptionData data(V, R, key_len, P, "", "", "", "", "", id1, encrypt_metadata);
1198   - unsigned char k[key_bytes];
1199   - QUtil::initializeWithRandomBytes(k, key_bytes);
1200   - encryption_key = std::string(reinterpret_cast<char*>(k), key_bytes);
1201   - compute_U_UE_value_V5(user_password, encryption_key, data, U, UE);
1202   - compute_O_OE_value_V5(owner_password, encryption_key, data, U, O, OE);
1203   - Perms = compute_Perms_value_V5(encryption_key, data);
1204   - data.setV5EncryptionParameters(O, OE, U, UE, Perms);
  1095 + encryption_key = data.compute_encryption_parameters_V5(user_password, owner_password);
  1096 +
  1097 + out_O = data.getO();
  1098 + out_U = data.getU();
  1099 + out_OE = data.getOE();
  1100 + out_UE = data.getUE();
  1101 + out_Perms = data.getPerms();
  1102 +}
  1103 +
  1104 +std::string
  1105 +QPDF::EncryptionData::compute_encryption_parameters_V5(
  1106 + char const* user_password, char const* owner_password)
  1107 +{
  1108 + auto out_encryption_key = util::random_string(key_bytes);
  1109 + // Algorithm 8 from the PDF 2.0
  1110 + auto validation_salt = util::random_string(8);
  1111 + auto key_salt = util::random_string(8);
  1112 + U = hash_V5(user_password, validation_salt, "").append(validation_salt).append(key_salt);
  1113 + auto intermediate_key = hash_V5(user_password, key_salt, "");
  1114 + UE = process_with_aes(intermediate_key, true, out_encryption_key);
  1115 + // Algorithm 9 from the PDF 2.0
  1116 + validation_salt = util::random_string(8);
  1117 + key_salt = util::random_string(8);
  1118 + O = hash_V5(owner_password, validation_salt, U) + validation_salt + key_salt;
  1119 + intermediate_key = hash_V5(owner_password, key_salt, U);
  1120 + OE = process_with_aes(intermediate_key, true, out_encryption_key);
  1121 + // Algorithm 10 from the PDF 2.0
  1122 + Perms = process_with_aes(out_encryption_key, true, compute_Perms_value_V5_clear());
  1123 + return out_encryption_key;
  1124 +}
  1125 +
  1126 +std::string
  1127 +QPDF::EncryptionData::compute_parameters(char const* user_password, char const* owner_password)
  1128 +{
  1129 + if (V < 5) {
  1130 + compute_encryption_O_U(user_password, owner_password);
  1131 + return compute_encryption_key(user_password);
  1132 + } else {
  1133 + return compute_encryption_parameters_V5(user_password, owner_password);
  1134 + }
1205 1135 }
1206 1136  
1207 1137 std::string const&
... ... @@ -1233,9 +1163,12 @@ QPDF::isEncrypted() const
1233 1163 bool
1234 1164 QPDF::isEncrypted(int& R, int& P)
1235 1165 {
1236   - int V;
1237   - encryption_method_e stream, string, file;
1238   - return isEncrypted(R, P, V, stream, string, file);
  1166 + if (!m->encp->encrypted) {
  1167 + return false;
  1168 + }
  1169 + P = m->encp->P();
  1170 + R = m->encp->R();
  1171 + return true;
1239 1172 }
1240 1173  
1241 1174 bool
... ... @@ -1247,22 +1180,16 @@ QPDF::isEncrypted(
1247 1180 encryption_method_e& string_method,
1248 1181 encryption_method_e& file_method)
1249 1182 {
1250   - if (m->encp->encrypted) {
1251   - QPDFObjectHandle trailer = getTrailer();
1252   - QPDFObjectHandle encrypt = trailer.getKey("/Encrypt");
1253   - QPDFObjectHandle Pkey = encrypt.getKey("/P");
1254   - QPDFObjectHandle Rkey = encrypt.getKey("/R");
1255   - QPDFObjectHandle Vkey = encrypt.getKey("/V");
1256   - P = static_cast<int>(Pkey.getIntValue());
1257   - R = Rkey.getIntValueAsInt();
1258   - V = Vkey.getIntValueAsInt();
1259   - stream_method = m->encp->cf_stream;
1260   - string_method = m->encp->cf_string;
1261   - file_method = m->encp->cf_file;
1262   - return true;
1263   - } else {
  1183 + if (!m->encp->encrypted) {
1264 1184 return false;
1265 1185 }
  1186 + P = m->encp->P();
  1187 + R = m->encp->R();
  1188 + V = m->encp->encryption_V;
  1189 + stream_method = m->encp->cf_stream;
  1190 + string_method = m->encp->cf_string;
  1191 + file_method = m->encp->cf_file;
  1192 + return true;
1266 1193 }
1267 1194  
1268 1195 bool
... ... @@ -1277,135 +1204,57 @@ QPDF::userPasswordMatched() const
1277 1204 return m->encp->user_password_matched;
1278 1205 }
1279 1206  
1280   -static bool
1281   -is_bit_set(int P, int bit)
1282   -{
1283   - // Bits in P are numbered from 1 in the spec
1284   - return ((P & (1 << (bit - 1))) != 0);
1285   -}
1286   -
1287 1207 bool
1288 1208 QPDF::allowAccessibility()
1289 1209 {
1290   - int R = 0;
1291   - int P = 0;
1292   - bool status = true;
1293   - if (isEncrypted(R, P)) {
1294   - if (R < 3) {
1295   - status = is_bit_set(P, 5);
1296   - } else {
1297   - status = is_bit_set(P, 10);
1298   - }
1299   - }
1300   - return status;
  1210 + return m->encp->R() < 3 ? m->encp->P(5) : m->encp->P(10);
1301 1211 }
1302 1212  
1303 1213 bool
1304 1214 QPDF::allowExtractAll()
1305 1215 {
1306   - int R = 0;
1307   - int P = 0;
1308   - bool status = true;
1309   - if (isEncrypted(R, P)) {
1310   - status = is_bit_set(P, 5);
1311   - }
1312   - return status;
  1216 + return m->encp->P(5);
1313 1217 }
1314 1218  
1315 1219 bool
1316 1220 QPDF::allowPrintLowRes()
1317 1221 {
1318   - int R = 0;
1319   - int P = 0;
1320   - bool status = true;
1321   - if (isEncrypted(R, P)) {
1322   - status = is_bit_set(P, 3);
1323   - }
1324   - return status;
  1222 + return m->encp->P(3);
1325 1223 }
1326 1224  
1327 1225 bool
1328 1226 QPDF::allowPrintHighRes()
1329 1227 {
1330   - int R = 0;
1331   - int P = 0;
1332   - bool status = true;
1333   - if (isEncrypted(R, P)) {
1334   - status = is_bit_set(P, 3);
1335   - if ((R >= 3) && (!is_bit_set(P, 12))) {
1336   - status = false;
1337   - }
1338   - }
1339   - return status;
  1228 + return allowPrintLowRes() && (m->encp->R() < 3 ? true : m->encp->P(12));
1340 1229 }
1341 1230  
1342 1231 bool
1343 1232 QPDF::allowModifyAssembly()
1344 1233 {
1345   - int R = 0;
1346   - int P = 0;
1347   - bool status = true;
1348   - if (isEncrypted(R, P)) {
1349   - if (R < 3) {
1350   - status = is_bit_set(P, 4);
1351   - } else {
1352   - status = is_bit_set(P, 11);
1353   - }
1354   - }
1355   - return status;
  1234 + return m->encp->R() < 3 ? m->encp->P(4) : m->encp->P(11);
1356 1235 }
1357 1236  
1358 1237 bool
1359 1238 QPDF::allowModifyForm()
1360 1239 {
1361   - int R = 0;
1362   - int P = 0;
1363   - bool status = true;
1364   - if (isEncrypted(R, P)) {
1365   - if (R < 3) {
1366   - status = is_bit_set(P, 6);
1367   - } else {
1368   - status = is_bit_set(P, 9);
1369   - }
1370   - }
1371   - return status;
  1240 + return m->encp->R() < 3 ? m->encp->P(6) : m->encp->P(9);
1372 1241 }
1373 1242  
1374 1243 bool
1375 1244 QPDF::allowModifyAnnotation()
1376 1245 {
1377   - int R = 0;
1378   - int P = 0;
1379   - bool status = true;
1380   - if (isEncrypted(R, P)) {
1381   - status = is_bit_set(P, 6);
1382   - }
1383   - return status;
  1246 + return m->encp->P(6);
1384 1247 }
1385 1248  
1386 1249 bool
1387 1250 QPDF::allowModifyOther()
1388 1251 {
1389   - int R = 0;
1390   - int P = 0;
1391   - bool status = true;
1392   - if (isEncrypted(R, P)) {
1393   - status = is_bit_set(P, 4);
1394   - }
1395   - return status;
  1252 + return m->encp->P(4);
1396 1253 }
1397 1254  
1398 1255 bool
1399 1256 QPDF::allowModifyAll()
1400 1257 {
1401   - int R = 0;
1402   - int P = 0;
1403   - bool status = true;
1404   - if (isEncrypted(R, P)) {
1405   - status = (is_bit_set(P, 4) && is_bit_set(P, 6));
1406   - if (R >= 3) {
1407   - status = status && (is_bit_set(P, 9) && is_bit_set(P, 11));
1408   - }
1409   - }
1410   - return status;
  1258 + return allowModifyAnnotation() && allowModifyOther() &&
  1259 + (m->encp->R() < 3 ? true : allowModifyForm() && allowModifyAssembly());
1411 1260 }
... ...
libqpdf/QUtil.cc
... ... @@ -2,6 +2,7 @@
2 2 #include <qpdf/qpdf-config.h>
3 3  
4 4 #include <qpdf/QUtil.hh>
  5 +#include <qpdf/Util.hh>
5 6  
6 7 #include <qpdf/CryptoRandomDataProvider.hh>
7 8 #include <qpdf/Pipeline.hh>
... ... @@ -1131,6 +1132,14 @@ QUtil::initializeWithRandomBytes(unsigned char* data, size_t len)
1131 1132 getRandomDataProvider()->provideRandomData(data, len);
1132 1133 }
1133 1134  
  1135 +std::string
  1136 +util::random_string(size_t len)
  1137 +{
  1138 + std::string result(len, '\0');
  1139 + QUtil::initializeWithRandomBytes(reinterpret_cast<unsigned char*>(result.data()), len);
  1140 + return result;
  1141 +}
  1142 +
1134 1143 long
1135 1144 QUtil::random()
1136 1145 {
... ...
libqpdf/RC4.cc
... ... @@ -13,3 +13,13 @@ RC4::process(unsigned char const* in_data, size_t len, unsigned char* out_data)
13 13 {
14 14 crypto->RC4_process(in_data, len, out_data);
15 15 }
  16 +
  17 +void
  18 +RC4::process(std::string_view key, std::string& data)
  19 +{
  20 + RC4 rc4(reinterpret_cast<unsigned char const*>(key.data()), static_cast<int>(key.size()));
  21 + rc4.process(
  22 + reinterpret_cast<unsigned char const*>(data.data()),
  23 + data.size(),
  24 + reinterpret_cast<unsigned char*>(data.data()));
  25 +}
... ...
libqpdf/qpdf/MD5.hh
... ... @@ -24,10 +24,25 @@ class MD5
24 24 void appendString(char const* input_string);
25 25  
26 26 // appends arbitrary data to current md5 object
27   - void encodeDataIncrementally(char const* input_data, size_t len);
  27 + void
  28 + encodeDataIncrementally(char const* input_data, size_t len)
  29 + {
  30 + crypto->MD5_update(reinterpret_cast<unsigned char*>(const_cast<char*>(input_data)), len);
  31 + }
  32 +
  33 + // appends arbitrary data to current md5 object
  34 + void
  35 + encodeDataIncrementally(std::string_view input_data)
  36 + {
  37 + crypto->MD5_update(
  38 + reinterpret_cast<unsigned char*>(const_cast<char*>(input_data.data())),
  39 + input_data.size());
  40 + }
28 41  
29 42 // computes a raw digest
30 43 void digest(Digest);
  44 + std::string digest();
  45 + static std::string digest(std::string_view data);
31 46  
32 47 // prints the digest to stdout terminated with \r\n (primarily for testing)
33 48 void print();
... ...
libqpdf/qpdf/QPDFWriter_private.hh
... ... @@ -43,92 +43,4 @@ class QPDFWriter::NewObjTable: public ::ObjTable&lt;QPDFWriter::NewObject&gt;
43 43 friend class QPDFWriter;
44 44 };
45 45  
46   -class QPDFWriter::Members
47   -{
48   - friend class QPDFWriter;
49   -
50   - public:
51   - QPDF_DLL
52   - ~Members();
53   -
54   - private:
55   - Members(QPDF& pdf);
56   - Members(Members const&) = delete;
57   -
58   - QPDF& pdf;
59   - QPDFObjGen root_og{-1, 0};
60   - char const* filename{"unspecified"};
61   - FILE* file{nullptr};
62   - bool close_file{false};
63   - Pl_Buffer* buffer_pipeline{nullptr};
64   - Buffer* output_buffer{nullptr};
65   - bool normalize_content_set{false};
66   - bool normalize_content{false};
67   - bool compress_streams{true};
68   - bool compress_streams_set{false};
69   - qpdf_stream_decode_level_e stream_decode_level{qpdf_dl_generalized};
70   - bool stream_decode_level_set{false};
71   - bool recompress_flate{false};
72   - bool qdf_mode{false};
73   - bool preserve_unreferenced_objects{false};
74   - bool newline_before_endstream{false};
75   - bool static_id{false};
76   - bool suppress_original_object_ids{false};
77   - bool direct_stream_lengths{true};
78   - bool encrypted{false};
79   - bool preserve_encryption{true};
80   - bool linearized{false};
81   - bool pclm{false};
82   - qpdf_object_stream_e object_stream_mode{qpdf_o_preserve};
83   - std::string encryption_key;
84   - bool encrypt_metadata{true};
85   - bool encrypt_use_aes{false};
86   - std::map<std::string, std::string> encryption_dictionary;
87   - int encryption_V{0};
88   - int encryption_R{0};
89   -
90   - std::string id1; // for /ID key of
91   - std::string id2; // trailer dictionary
92   - std::string final_pdf_version;
93   - int final_extension_level{0};
94   - std::string min_pdf_version;
95   - int min_extension_level{0};
96   - std::string forced_pdf_version;
97   - int forced_extension_level{0};
98   - std::string extra_header_text;
99   - int encryption_dict_objid{0};
100   - std::string cur_data_key;
101   - std::list<std::shared_ptr<Pipeline>> to_delete;
102   - qpdf::pl::Count* pipeline{nullptr};
103   - std::vector<QPDFObjectHandle> object_queue;
104   - size_t object_queue_front{0};
105   - QPDFWriter::ObjTable obj;
106   - QPDFWriter::NewObjTable new_obj;
107   - int next_objid{1};
108   - int cur_stream_length_id{0};
109   - size_t cur_stream_length{0};
110   - bool added_newline{false};
111   - size_t max_ostream_index{0};
112   - std::set<QPDFObjGen> normalized_streams;
113   - std::map<QPDFObjGen, int> page_object_to_seq;
114   - std::map<QPDFObjGen, int> contents_to_page_seq;
115   - std::map<int, std::vector<QPDFObjGen>> object_stream_to_objects;
116   - std::vector<Pipeline*> pipeline_stack;
117   - unsigned long next_stack_id{2};
118   - std::string count_buffer;
119   - bool deterministic_id{false};
120   - Pl_MD5* md5_pipeline{nullptr};
121   - std::string deterministic_id_data;
122   - bool did_write_setup{false};
123   -
124   - // For linearization only
125   - std::string lin_pass1_filename;
126   -
127   - // For progress reporting
128   - std::shared_ptr<QPDFWriter::ProgressReporter> progress_reporter;
129   - int events_expected{0};
130   - int events_seen{0};
131   - int next_progress_report{0};
132   -};
133   -
134 46 #endif // QPDFWRITER_PRIVATE_HH
... ...
libqpdf/qpdf/QPDF_private.hh
... ... @@ -187,11 +187,30 @@ class QPDF::EncryptionParameters
187 187 public:
188 188 EncryptionParameters() = default;
189 189  
  190 + int
  191 + P() const
  192 + {
  193 + return static_cast<int>(P_.to_ullong());
  194 + }
  195 +
  196 + // Bits in P are numbered from 1 as in the PDF spec.
  197 + bool P(size_t bit) const;
  198 +
  199 + int
  200 + R()
  201 + {
  202 + return R_;
  203 + }
  204 +
  205 + void initialize(QPDF& qpdf);
  206 + encryption_method_e interpretCF(QPDFObjectHandle const& cf) const;
  207 +
190 208 private:
191 209 bool encrypted{false};
192 210 bool encryption_initialized{false};
  211 + std::bitset<32> P_{0xfffffffc}; // Specification always requires bits 1 and 2 to be cleared.
193 212 int encryption_V{0};
194   - int encryption_R{0};
  213 + int R_{0};
195 214 bool encrypt_metadata{true};
196 215 std::map<std::string, encryption_method_e> crypt_filters;
197 216 encryption_method_e cf_stream{e_none};
... ...
libqpdf/qpdf/RC4.hh
... ... @@ -14,6 +14,8 @@ class RC4
14 14 // It is safe to pass the same pointer to in_data and out_data to encrypt/decrypt in place
15 15 void process(unsigned char const* in_data, size_t len, unsigned char* out_data);
16 16  
  17 + static void process(std::string_view key, std::string& data);
  18 +
17 19 private:
18 20 std::shared_ptr<QPDFCryptoImpl> crypto;
19 21 };
... ...
libqpdf/qpdf/Util.hh
... ... @@ -59,6 +59,8 @@ namespace qpdf::util
59 59 s.insert(0, 1, '1');
60 60 }
61 61  
  62 + std::string random_string(size_t len);
  63 +
62 64 } // namespace qpdf::util
63 65  
64 66 #endif // UTIL_HH
... ...
qpdf/qpdf.testcov
... ... @@ -228,7 +228,6 @@ QPDFWriter extra header text no newline 0
228 228 QPDFWriter extra header text add newline 0
229 229 QPDF bogus 0 offset 0
230 230 QPDF global offset 0
231   -QPDFWriter copy V5 0
232 231 QPDFWriter increasing extension level 0
233 232 QPDFWriter make Extensions direct 0
234 233 QPDFWriter make ADBE direct 1
... ...