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,6 +23,7 @@
23 #include <qpdf/DLL.h> 23 #include <qpdf/DLL.h>
24 #include <qpdf/Types.h> 24 #include <qpdf/Types.h>
25 25
  26 +#include <bitset>
26 #include <cstdio> 27 #include <cstdio>
27 #include <functional> 28 #include <functional>
28 #include <iostream> 29 #include <iostream>
@@ -465,7 +466,7 @@ class QPDF @@ -465,7 +466,7 @@ class QPDF
465 V(V), 466 V(V),
466 R(R), 467 R(R),
467 Length_bytes(Length_bytes), 468 Length_bytes(Length_bytes),
468 - P(P), 469 + P(static_cast<unsigned long long>(P)),
469 O(O), 470 O(O),
470 U(U), 471 U(U),
471 OE(OE), 472 OE(OE),
@@ -475,11 +476,20 @@ class QPDF @@ -475,11 +476,20 @@ class QPDF
475 encrypt_metadata(encrypt_metadata) 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 int getV() const; 487 int getV() const;
480 int getR() const; 488 int getR() const;
481 int getLengthBytes() const; 489 int getLengthBytes() const;
482 int getP() const; 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 std::string const& getO() const; 493 std::string const& getO() const;
484 std::string const& getU() const; 494 std::string const& getU() const;
485 std::string const& getOE() const; 495 std::string const& getOE() const;
@@ -487,9 +497,12 @@ class QPDF @@ -487,9 +497,12 @@ class QPDF
487 std::string const& getPerms() const; 497 std::string const& getPerms() const;
488 std::string const& getId1() const; 498 std::string const& getId1() const;
489 bool getEncryptMetadata() const; 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 void setO(std::string const&); 503 void setO(std::string const&);
492 void setU(std::string const&); 504 void setU(std::string const&);
  505 + void setId1(std::string const& val);
493 void setV5EncryptionParameters( 506 void setV5EncryptionParameters(
494 std::string const& O, 507 std::string const& O,
495 std::string const& OE, 508 std::string const& OE,
@@ -497,14 +510,51 @@ class QPDF @@ -497,14 +510,51 @@ class QPDF
497 std::string const& UE, 510 std::string const& UE,
498 std::string const& Perms); 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 private: 530 private:
  531 + static constexpr unsigned int OU_key_bytes_V4 = 16; // ( == sizeof(MD5::Digest)
  532 +
501 EncryptionData(EncryptionData const&) = delete; 533 EncryptionData(EncryptionData const&) = delete;
502 EncryptionData& operator=(EncryptionData const&) = delete; 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 int V; 554 int V;
505 int R; 555 int R;
506 int Length_bytes; 556 int Length_bytes;
507 - int P; 557 + std::bitset<32> P{0xfffffffc}; // Specification always requires bits 1 and 2 to be cleared.
508 std::string O; 558 std::string O;
509 std::string U; 559 std::string U;
510 std::string OE; 560 std::string OE;
@@ -906,18 +956,10 @@ class QPDF @@ -906,18 +956,10 @@ class QPDF
906 void insertPageobjToPage(QPDFObjectHandle const& obj, int pos, bool check_duplicate); 956 void insertPageobjToPage(QPDFObjectHandle const& obj, int pos, bool check_duplicate);
907 957
908 // methods to support encryption -- implemented in QPDF_encryption.cc 958 // methods to support encryption -- implemented in QPDF_encryption.cc
909 - static encryption_method_e  
910 - interpretCF(std::shared_ptr<EncryptionParameters> encp, QPDFObjectHandle);  
911 void initializeEncryption(); 959 void initializeEncryption();
912 static std::string 960 static std::string
913 getKeyForObject(std::shared_ptr<EncryptionParameters> encp, QPDFObjGen og, bool use_aes); 961 getKeyForObject(std::shared_ptr<EncryptionParameters> encp, QPDFObjGen og, bool use_aes);
914 void decryptString(std::string&, QPDFObjGen og); 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 static void decryptStream( 963 static void decryptStream(
922 std::shared_ptr<EncryptionParameters> encp, 964 std::shared_ptr<EncryptionParameters> encp,
923 std::shared_ptr<InputSource> file, 965 std::shared_ptr<InputSource> file,
include/qpdf/QPDFWriter.hh
@@ -23,6 +23,7 @@ @@ -23,6 +23,7 @@
23 #include <qpdf/DLL.h> 23 #include <qpdf/DLL.h>
24 #include <qpdf/Types.h> 24 #include <qpdf/Types.h>
25 25
  26 +#include <bitset>
26 #include <cstdio> 27 #include <cstdio>
27 #include <functional> 28 #include <functional>
28 #include <list> 29 #include <list>
@@ -510,9 +511,6 @@ class QPDFWriter @@ -510,9 +511,6 @@ class QPDFWriter
510 std::string getOriginalID1(); 511 std::string getOriginalID1();
511 void generateID(); 512 void generateID();
512 void interpretR3EncryptionParameters( 513 void interpretR3EncryptionParameters(
513 - std::set<int>& bits_to_clear,  
514 - char const* user_password,  
515 - char const* owner_password,  
516 bool allow_accessibility, 514 bool allow_accessibility,
517 bool allow_extract, 515 bool allow_extract,
518 bool allow_assemble, 516 bool allow_assemble,
@@ -524,26 +522,8 @@ class QPDFWriter @@ -524,26 +522,8 @@ class QPDFWriter
524 void disableIncompatibleEncryption(int major, int minor, int extension_level); 522 void disableIncompatibleEncryption(int major, int minor, int extension_level);
525 void parseVersion(std::string const& version, int& major, int& minor) const; 523 void parseVersion(std::string const& version, int& major, int& minor) const;
526 int compareVersions(int major1, int minor1, int major2, int minor2) const; 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 void setDataKey(int objid); 527 void setDataKey(int objid);
548 int openObject(int objid = 0); 528 int openObject(int objid = 0);
549 void closeObject(int objid); 529 void closeObject(int objid);
libqpdf/MD5.cc
@@ -46,12 +46,6 @@ MD5::appendString(char const* input_string) @@ -46,12 +46,6 @@ MD5::appendString(char const* input_string)
46 } 46 }
47 47
48 void 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 MD5::encodeFile(char const* filename, qpdf_offset_t up_to_offset) 49 MD5::encodeFile(char const* filename, qpdf_offset_t up_to_offset)
56 { 50 {
57 char buffer[1024]; 51 char buffer[1024];
@@ -94,6 +88,24 @@ MD5::digest(Digest result) @@ -94,6 +88,24 @@ MD5::digest(Digest result)
94 crypto->MD5_digest(result); 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 void 109 void
98 MD5::print() 110 MD5::print()
99 { 111 {
libqpdf/QPDFWriter.cc
@@ -50,6 +50,90 @@ QPDFWriter::FunctionProgressReporter::reportProgress(int progress) @@ -50,6 +50,90 @@ QPDFWriter::FunctionProgressReporter::reportProgress(int progress)
50 this->handler(progress); 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 QPDFWriter::Members::Members(QPDF& pdf) : 137 QPDFWriter::Members::Members(QPDF& pdf) :
54 pdf(pdf), 138 pdf(pdf),
55 root_og(pdf.getRoot().getObjGen().isIndirect() ? pdf.getRoot().getObjGen() : QPDFObjGen(-1, 0)) 139 root_og(pdf.getRoot().getObjGen().isIndirect() ? pdf.getRoot().getObjGen() : QPDFObjGen(-1, 0))
@@ -344,21 +428,20 @@ QPDFWriter::setR2EncryptionParametersInsecure( @@ -344,21 +428,20 @@ QPDFWriter::setR2EncryptionParametersInsecure(
344 bool allow_extract, 428 bool allow_extract,
345 bool allow_annotate) 429 bool allow_annotate)
346 { 430 {
347 - std::set<int> clear; 431 + m->encryption = std::make_unique<QPDF::EncryptionData>(1, 2, 5, true);
348 if (!allow_print) { 432 if (!allow_print) {
349 - clear.insert(3); 433 + m->encryption->setP(3, false);
350 } 434 }
351 if (!allow_modify) { 435 if (!allow_modify) {
352 - clear.insert(4); 436 + m->encryption->setP(4, false);
353 } 437 }
354 if (!allow_extract) { 438 if (!allow_extract) {
355 - clear.insert(5); 439 + m->encryption->setP(5, false);
356 } 440 }
357 if (!allow_annotate) { 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 void 447 void
@@ -373,11 +456,8 @@ QPDFWriter::setR3EncryptionParametersInsecure( @@ -373,11 +456,8 @@ QPDFWriter::setR3EncryptionParametersInsecure(
373 bool allow_modify_other, 456 bool allow_modify_other,
374 qpdf_r3_print_e print) 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 interpretR3EncryptionParameters( 460 interpretR3EncryptionParameters(
378 - clear,  
379 - user_password,  
380 - owner_password,  
381 allow_accessibility, 461 allow_accessibility,
382 allow_extract, 462 allow_extract,
383 allow_assemble, 463 allow_assemble,
@@ -386,7 +466,7 @@ QPDFWriter::setR3EncryptionParametersInsecure( @@ -386,7 +466,7 @@ QPDFWriter::setR3EncryptionParametersInsecure(
386 allow_modify_other, 466 allow_modify_other,
387 print, 467 print,
388 qpdf_r3m_all); 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 void 472 void
@@ -403,11 +483,9 @@ QPDFWriter::setR4EncryptionParametersInsecure( @@ -403,11 +483,9 @@ QPDFWriter::setR4EncryptionParametersInsecure(
403 bool encrypt_metadata, 483 bool encrypt_metadata,
404 bool use_aes) 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 interpretR3EncryptionParameters( 488 interpretR3EncryptionParameters(
408 - clear,  
409 - user_password,  
410 - owner_password,  
411 allow_accessibility, 489 allow_accessibility,
412 allow_extract, 490 allow_extract,
413 allow_assemble, 491 allow_assemble,
@@ -416,9 +494,7 @@ QPDFWriter::setR4EncryptionParametersInsecure( @@ -416,9 +494,7 @@ QPDFWriter::setR4EncryptionParametersInsecure(
416 allow_modify_other, 494 allow_modify_other,
417 print, 495 print,
418 qpdf_r3m_all); 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 void 500 void
@@ -434,11 +510,9 @@ QPDFWriter::setR5EncryptionParameters( @@ -434,11 +510,9 @@ QPDFWriter::setR5EncryptionParameters(
434 qpdf_r3_print_e print, 510 qpdf_r3_print_e print,
435 bool encrypt_metadata) 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 interpretR3EncryptionParameters( 515 interpretR3EncryptionParameters(
439 - clear,  
440 - user_password,  
441 - owner_password,  
442 allow_accessibility, 516 allow_accessibility,
443 allow_extract, 517 allow_extract,
444 allow_assemble, 518 allow_assemble,
@@ -447,9 +521,7 @@ QPDFWriter::setR5EncryptionParameters( @@ -447,9 +521,7 @@ QPDFWriter::setR5EncryptionParameters(
447 allow_modify_other, 521 allow_modify_other,
448 print, 522 print,
449 qpdf_r3m_all); 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 void 527 void
@@ -465,11 +537,8 @@ QPDFWriter::setR6EncryptionParameters( @@ -465,11 +537,8 @@ QPDFWriter::setR6EncryptionParameters(
465 qpdf_r3_print_e print, 537 qpdf_r3_print_e print,
466 bool encrypt_metadata) 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 interpretR3EncryptionParameters( 541 interpretR3EncryptionParameters(
470 - clear,  
471 - user_password,  
472 - owner_password,  
473 allow_accessibility, 542 allow_accessibility,
474 allow_extract, 543 allow_extract,
475 allow_assemble, 544 allow_assemble,
@@ -479,15 +548,11 @@ QPDFWriter::setR6EncryptionParameters( @@ -479,15 +548,11 @@ QPDFWriter::setR6EncryptionParameters(
479 print, 548 print,
480 qpdf_r3m_all); 549 qpdf_r3m_all);
481 m->encrypt_use_aes = true; 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 void 554 void
487 QPDFWriter::interpretR3EncryptionParameters( 555 QPDFWriter::interpretR3EncryptionParameters(
488 - std::set<int>& clear,  
489 - char const* user_password,  
490 - char const* owner_password,  
491 bool allow_accessibility, 556 bool allow_accessibility,
492 bool allow_extract, 557 bool allow_extract,
493 bool allow_assemble, 558 bool allow_assemble,
@@ -526,27 +591,24 @@ QPDFWriter::interpretR3EncryptionParameters( @@ -526,27 +591,24 @@ QPDFWriter::interpretR3EncryptionParameters(
526 // 10: accessibility; ignored by readers, should always be set 591 // 10: accessibility; ignored by readers, should always be set
527 // 11: document assembly even if 4 is clear 592 // 11: document assembly even if 4 is clear
528 // 12: high-resolution printing 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 if (!allow_extract) { 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 switch (print) { 603 switch (print) {
541 case qpdf_r3p_none: 604 case qpdf_r3p_none:
542 - clear.insert(3); // any printing  
543 - 605 + m->encryption->setP(3, false); // any printing
  606 + [[fallthrough]];
544 case qpdf_r3p_low: 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 case qpdf_r3p_full: 610 case qpdf_r3p_full:
548 break; 611 break;
549 -  
550 // no default so gcc warns for missing cases 612 // no default so gcc warns for missing cases
551 } 613 }
552 614
@@ -557,96 +619,44 @@ QPDFWriter::interpretR3EncryptionParameters( @@ -557,96 +619,44 @@ QPDFWriter::interpretR3EncryptionParameters(
557 // NOT EXERCISED IN TEST SUITE 619 // NOT EXERCISED IN TEST SUITE
558 switch (modify) { 620 switch (modify) {
559 case qpdf_r3m_none: 621 case qpdf_r3m_none:
560 - clear.insert(11); // document assembly  
561 - 622 + m->encryption->setP(11, false); // document assembly
  623 + [[fallthrough]];
562 case qpdf_r3m_assembly: 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 case qpdf_r3m_form: 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 case qpdf_r3m_annotate: 630 case qpdf_r3m_annotate:
569 - clear.insert(4); // other modifications  
570 - 631 + m->encryption->setP(4, false); // other modifications
  632 + [[fallthrough]];
571 case qpdf_r3m_all: 633 case qpdf_r3m_all:
572 break; 634 break;
573 -  
574 // no default so gcc warns for missing cases 635 // no default so gcc warns for missing cases
575 } 636 }
576 // END NOT EXERCISED IN TEST SUITE 637 // END NOT EXERCISED IN TEST SUITE
577 638
578 if (!allow_assemble) { 639 if (!allow_assemble) {
579 - clear.insert(11); 640 + m->encryption->setP(11, false);
580 } 641 }
581 if (!allow_annotate_and_form) { 642 if (!allow_annotate_and_form) {
582 - clear.insert(6); 643 + m->encryption->setP(6, false);
583 } 644 }
584 if (!allow_form_filling) { 645 if (!allow_form_filling) {
585 - clear.insert(9); 646 + m->encryption->setP(9, false);
586 } 647 }
587 if (!allow_modify_other) { 648 if (!allow_modify_other) {
588 - clear.insert(4); 649 + m->encryption->setP(4, false);
589 } 650 }
590 } 651 }
591 652
592 void 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 generateID(); 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 void 662 void
@@ -663,9 +673,10 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf) @@ -663,9 +673,10 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf)
663 if (V > 1) { 673 if (V > 1) {
664 key_len = encrypt.getKey("/Length").getIntValueAsInt() / 8; 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 if (V >= 4) { 680 if (V >= 4) {
670 // When copying encryption parameters, use AES even if the original file did not. 681 // When copying encryption parameters, use AES even if the original file did not.
671 // Acrobat doesn't create files with V >= 4 that don't use AES, and the logic of 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,72 +684,62 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf)
673 // all potentially having different values. 684 // all potentially having different values.
674 m->encrypt_use_aes = true; 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 QTC::TC("qpdf", "QPDFWriter copy use_aes", m->encrypt_use_aes ? 0 : 1); 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 V, 691 V,
692 encrypt.getKey("/R").getIntValueAsInt(), 692 encrypt.getKey("/R").getIntValueAsInt(),
693 key_len, 693 key_len,
694 static_cast<int>(encrypt.getKey("/P").getIntValue()), 694 static_cast<int>(encrypt.getKey("/P").getIntValue()),
695 encrypt.getKey("/O").getStringValue(), 695 encrypt.getKey("/O").getStringValue(),
696 encrypt.getKey("/U").getStringValue(), 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 m->id1, // m->id1 == the other file's id1 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 void 709 void
707 QPDFWriter::disableIncompatibleEncryption(int major, int minor, int extension_level) 710 QPDFWriter::disableIncompatibleEncryption(int major, int minor, int extension_level)
708 { 711 {
709 - if (!m->encrypted) { 712 + if (!m->encryption) {
710 return; 713 return;
711 } 714 }
712 -  
713 - bool disable = false;  
714 if (compareVersions(major, minor, 1, 3) < 0) { 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 QTC::TC("qpdf", "QPDFWriter forced version disabled encryption"); 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,34 +777,9 @@ QPDFWriter::compareVersions(int major1, int minor1, int major2, int minor2) cons
776 } 777 }
777 778
778 void 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 if (R >= 6) { 783 if (R >= 6) {
808 setMinimumPDFVersion("1.7", 8); 784 setMinimumPDFVersion("1.7", 8);
809 } else if (R == 5) { 785 } else if (R == 5) {
@@ -815,37 +791,20 @@ QPDFWriter::setEncryptionParametersInternal( @@ -815,37 +791,20 @@ QPDFWriter::setEncryptionParametersInternal(
815 } else { 791 } else {
816 setMinimumPDFVersion("1.3"); 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 void 796 void
845 QPDFWriter::setDataKey(int objid) 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 unsigned int 810 unsigned int
@@ -979,7 +938,7 @@ QPDFWriter::PipelinePopper::~PipelinePopper() @@ -979,7 +938,7 @@ QPDFWriter::PipelinePopper::~PipelinePopper()
979 void 938 void
980 QPDFWriter::adjustAESStreamLength(size_t& length) 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 // Stream length will be padded with 1 to 16 bytes to end up as a multiple of 16. It will 942 // Stream length will be padded with 1 to 16 bytes to end up as a multiple of 16. It will
984 // also be prepended by 16 bits of random data. 943 // also be prepended by 16 bits of random data.
985 length += 32 - (length & 0xf); 944 length += 32 - (length & 0xf);
@@ -989,7 +948,7 @@ QPDFWriter::adjustAESStreamLength(size_t&amp; length) @@ -989,7 +948,7 @@ QPDFWriter::adjustAESStreamLength(size_t&amp; length)
989 void 948 void
990 QPDFWriter::pushEncryptionFilter(PipelinePopper& pp) 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 Pipeline* p = nullptr; 952 Pipeline* p = nullptr;
994 if (m->encrypt_use_aes) { 953 if (m->encrypt_use_aes) {
995 p = new Pl_AES_PDF( 954 p = new Pl_AES_PDF(
@@ -1232,7 +1191,7 @@ QPDFWriter::writeTrailer( @@ -1232,7 +1191,7 @@ QPDFWriter::writeTrailer(
1232 1191
1233 if (which != t_lin_second) { 1192 if (which != t_lin_second) {
1234 // Write reference to encryption dictionary 1193 // Write reference to encryption dictionary
1235 - if (m->encrypted) { 1194 + if (m->encryption) {
1236 writeString(" /Encrypt "); 1195 writeString(" /Encrypt ");
1237 writeString(std::to_string(m->encryption_dict_objid)); 1196 writeString(std::to_string(m->encryption_dict_objid));
1238 writeString(" 0 R"); 1197 writeString(" 0 R");
@@ -1280,7 +1239,8 @@ QPDFWriter::willFilterStream( @@ -1280,7 +1239,8 @@ QPDFWriter::willFilterStream(
1280 } 1239 }
1281 bool normalize = false; 1240 bool normalize = false;
1282 bool uncompress = false; 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 QTC::TC("qpdf", "QPDFWriter not compressing metadata"); 1244 QTC::TC("qpdf", "QPDFWriter not compressing metadata");
1285 filter = true; 1245 filter = true;
1286 compress_stream = false; 1246 compress_stream = false;
@@ -1568,7 +1528,7 @@ QPDFWriter::unparseObject( @@ -1568,7 +1528,7 @@ QPDFWriter::unparseObject(
1568 QPDFObjectHandle stream_dict = object.getDict(); 1528 QPDFObjectHandle stream_dict = object.getDict();
1569 1529
1570 m->cur_stream_length = stream_data.size(); 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 // Don't encrypt stream data for the metadata stream 1532 // Don't encrypt stream data for the metadata stream
1573 m->cur_data_key.clear(); 1533 m->cur_data_key.clear();
1574 } 1534 }
@@ -1582,17 +1542,16 @@ QPDFWriter::unparseObject( @@ -1582,17 +1542,16 @@ QPDFWriter::unparseObject(
1582 writeString(stream_data); 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 } else { 1548 } else {
1589 - m->added_newline = false; 1549 + writeString("endstream");
1590 } 1550 }
1591 - writeString("endstream");  
1592 } else if (tc == ::ot_string) { 1551 } else if (tc == ::ot_string) {
1593 std::string val; 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 val = object.getStringValue(); 1555 val = object.getStringValue();
1597 if (m->encrypt_use_aes) { 1556 if (m->encrypt_use_aes) {
1598 Pl_Buffer bufpl("encrypted string"); 1557 Pl_Buffer bufpl("encrypted string");
@@ -1775,7 +1734,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1775,7 +1734,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1775 writeStringQDF("\n"); 1734 writeStringQDF("\n");
1776 writeStringNoQDF(" "); 1735 writeStringNoQDF(" ");
1777 writeString(">>\nstream\n"); 1736 writeString(">>\nstream\n");
1778 - if (m->encrypted) { 1737 + if (m->encryption) {
1779 QTC::TC("qpdf", "QPDFWriter encrypt object stream"); 1738 QTC::TC("qpdf", "QPDFWriter encrypt object stream");
1780 } 1739 }
1781 { 1740 {
@@ -2132,7 +2091,7 @@ QPDFWriter::doWriteSetup() @@ -2132,7 +2091,7 @@ QPDFWriter::doWriteSetup()
2132 if (m->pclm) { 2091 if (m->pclm) {
2133 m->stream_decode_level = qpdf_dl_none; 2092 m->stream_decode_level = qpdf_dl_none;
2134 m->compress_streams = false; 2093 m->compress_streams = false;
2135 - m->encrypted = false; 2094 + m->encryption = nullptr;
2136 } 2095 }
2137 2096
2138 if (m->qdf_mode) { 2097 if (m->qdf_mode) {
@@ -2147,7 +2106,7 @@ QPDFWriter::doWriteSetup() @@ -2147,7 +2106,7 @@ QPDFWriter::doWriteSetup()
2147 } 2106 }
2148 } 2107 }
2149 2108
2150 - if (m->encrypted) { 2109 + if (m->encryption) {
2151 // Encryption has been explicitly set 2110 // Encryption has been explicitly set
2152 m->preserve_encryption = false; 2111 m->preserve_encryption = false;
2153 } else if (m->normalize_content || !m->compress_streams || m->pclm || m->qdf_mode) { 2112 } else if (m->normalize_content || !m->compress_streams || m->pclm || m->qdf_mode) {
@@ -2211,7 +2170,7 @@ QPDFWriter::doWriteSetup() @@ -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 // The document catalog is not allowed to be compressed in linearized files either. It 2174 // The document catalog is not allowed to be compressed in linearized files either. It
2216 // also appears that Adobe Reader 8.0.0 has a bug that prevents it from being able to 2175 // also appears that Adobe Reader 8.0.0 has a bug that prevents it from being able to
2217 // handle encrypted files with compressed document catalogs, so we disable them in that 2176 // handle encrypted files with compressed document catalogs, so we disable them in that
@@ -2312,13 +2271,48 @@ void @@ -2312,13 +2271,48 @@ void
2312 QPDFWriter::writeEncryptionDictionary() 2271 QPDFWriter::writeEncryptionDictionary()
2313 { 2272 {
2314 m->encryption_dict_objid = openObject(m->encryption_dict_objid); 2273 m->encryption_dict_objid = openObject(m->encryption_dict_objid);
  2274 + auto& enc = *m->encryption;
  2275 + auto const V = enc.getV();
  2276 +
2315 writeString("<<"); 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 writeString(" >>"); 2316 writeString(" >>");
2323 closeObject(m->encryption_dict_objid); 2317 closeObject(m->encryption_dict_objid);
2324 } 2318 }
@@ -2380,7 +2374,7 @@ QPDFWriter::writeHintStream(int hint_id) @@ -2380,7 +2374,7 @@ QPDFWriter::writeHintStream(int hint_id)
2380 writeString(std::to_string(hlen)); 2374 writeString(std::to_string(hlen));
2381 writeString(" >>\nstream\n"); 2375 writeString(" >>\nstream\n");
2382 2376
2383 - if (m->encrypted) { 2377 + if (m->encryption) {
2384 QTC::TC("qpdf", "QPDFWriter encrypted hint stream"); 2378 QTC::TC("qpdf", "QPDFWriter encrypted hint stream");
2385 } 2379 }
2386 char last_char = hint_buffer.empty() ? '\0' : hint_buffer.back(); 2380 char last_char = hint_buffer.empty() ? '\0' : hint_buffer.back();
@@ -2651,7 +2645,7 @@ QPDFWriter::writeLinearized() @@ -2651,7 +2645,7 @@ QPDFWriter::writeLinearized()
2651 int part4_first_obj = m->next_objid; 2645 int part4_first_obj = m->next_objid;
2652 m->next_objid += QIntC::to_int(part4.size()); 2646 m->next_objid += QIntC::to_int(part4.size());
2653 int after_part4 = m->next_objid; 2647 int after_part4 = m->next_objid;
2654 - if (m->encrypted) { 2648 + if (m->encryption) {
2655 m->encryption_dict_objid = m->next_objid++; 2649 m->encryption_dict_objid = m->next_objid++;
2656 } 2650 }
2657 int hint_id = m->next_objid++; 2651 int hint_id = m->next_objid++;
@@ -2836,7 +2830,7 @@ QPDFWriter::writeLinearized() @@ -2836,7 +2830,7 @@ QPDFWriter::writeLinearized()
2836 } 2830 }
2837 writeObject(cur_object); 2831 writeObject(cur_object);
2838 if (cur_object.getObjectID() == part4_end_marker) { 2832 if (cur_object.getObjectID() == part4_end_marker) {
2839 - if (m->encrypted) { 2833 + if (m->encryption) {
2840 writeEncryptionDictionary(); 2834 writeEncryptionDictionary();
2841 } 2835 }
2842 if (pass == 1) { 2836 if (pass == 1) {
@@ -3060,7 +3054,7 @@ QPDFWriter::writeStandard() @@ -3060,7 +3054,7 @@ QPDFWriter::writeStandard()
3060 } 3054 }
3061 3055
3062 // Write out the encryption dictionary, if any 3056 // Write out the encryption dictionary, if any
3063 - if (m->encrypted) { 3057 + if (m->encryption) {
3064 writeEncryptionDictionary(); 3058 writeEncryptionDictionary();
3065 } 3059 }
3066 3060
libqpdf/QPDF_encryption.cc
@@ -12,21 +12,23 @@ @@ -12,21 +12,23 @@
12 #include <qpdf/Pl_Buffer.hh> 12 #include <qpdf/Pl_Buffer.hh>
13 #include <qpdf/Pl_RC4.hh> 13 #include <qpdf/Pl_RC4.hh>
14 #include <qpdf/Pl_SHA2.hh> 14 #include <qpdf/Pl_SHA2.hh>
  15 +#include <qpdf/QPDFObjectHandle_private.hh>
15 #include <qpdf/QTC.hh> 16 #include <qpdf/QTC.hh>
16 #include <qpdf/QUtil.hh> 17 #include <qpdf/QUtil.hh>
17 #include <qpdf/RC4.hh> 18 #include <qpdf/RC4.hh>
  19 +#include <qpdf/Util.hh>
18 20
19 #include <algorithm> 21 #include <algorithm>
20 #include <cstring> 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 static unsigned int const OU_key_bytes_V5 = 48; 33 static unsigned int const OU_key_bytes_V5 = 48;
32 static unsigned int const OUE_key_bytes_V5 = 32; 34 static unsigned int const OUE_key_bytes_V5 = 32;
@@ -53,7 +55,21 @@ QPDF::EncryptionData::getLengthBytes() const @@ -53,7 +55,21 @@ QPDF::EncryptionData::getLengthBytes() const
53 int 55 int
54 QPDF::EncryptionData::getP() const 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 std::string const& 75 std::string const&
@@ -111,6 +127,25 @@ QPDF::EncryptionData::setU(std::string const&amp; U) @@ -111,6 +127,25 @@ QPDF::EncryptionData::setU(std::string const&amp; U)
111 } 127 }
112 128
113 void 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 QPDF::EncryptionData::setV5EncryptionParameters( 149 QPDF::EncryptionData::setV5EncryptionParameters(
115 std::string const& O, 150 std::string const& O,
116 std::string const& OE, 151 std::string const& OE,
@@ -125,84 +160,62 @@ QPDF::EncryptionData::setV5EncryptionParameters( @@ -125,84 +160,62 @@ QPDF::EncryptionData::setV5EncryptionParameters(
125 this->Perms = Perms; 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 void 163 void
138 QPDF::trim_user_password(std::string& user_password) 164 QPDF::trim_user_password(std::string& user_password)
139 { 165 {
140 // Although unnecessary, this routine trims the padding string from the end of a user password. 166 // Although unnecessary, this routine trims the padding string from the end of a user password.
141 // Its only purpose is for recovery of user passwords which is done in the test suite. 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 return; 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 return; 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 static std::string 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 static std::string 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 md5.digest(digest); 198 md5.digest(digest);
180 - 199 + auto len = std::min(QIntC::to_size(key_len), sizeof(digest));
181 for (int i = 0; i < iterations; ++i) { 200 for (int i = 0; i < iterations; ++i) {
182 MD5 m; 201 MD5 m;
183 - m.encodeDataIncrementally(reinterpret_cast<char*>(digest), QIntC::to_size(key_len)); 202 + m.encodeDataIncrementally(reinterpret_cast<char*>(digest), len);
184 m.digest(digest); 203 m.digest(digest);
185 } 204 }
  205 + return {reinterpret_cast<char*>(digest), len};
186 } 206 }
187 207
188 static void 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 for (int i = 0; i < iterations; ++i) { 213 for (int i = 0; i < iterations; ++i) {
200 int const xor_value = (reverse ? iterations - 1 - i : i); 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,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 Pl_SHA2 hash(256); 256 Pl_SHA2 hash(256);
247 hash.writeString(password); 257 hash.writeString(password);
@@ -251,7 +261,7 @@ hash_V5( @@ -251,7 +261,7 @@ hash_V5(
251 std::string K = hash.getRawDigest(); 261 std::string K = hash.getRawDigest();
252 262
253 std::string result; 263 std::string result;
254 - if (data.getR() < 6) { 264 + if (getR() < 6) {
255 result = K; 265 result = K;
256 } else { 266 } else {
257 // Algorithm 2.B from ISO 32000-1 chapter 7: Computing a hash 267 // Algorithm 2.B from ISO 32000-1 chapter 7: Computing a hash
@@ -348,30 +358,31 @@ QPDF::compute_data_key( @@ -348,30 +358,31 @@ QPDF::compute_data_key(
348 if (use_aes) { 358 if (use_aes) {
349 result += "sAlT"; 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 std::string 364 std::string
360 QPDF::compute_encryption_key(std::string const& password, EncryptionData const& data) 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 // For V >= 5, the encryption key is generated and stored in the file, encrypted separately 374 // For V >= 5, the encryption key is generated and stored in the file, encrypted separately
364 // with both user and owner passwords. 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 } else { 377 } else {
367 // For V < 5, the encryption key is derived from the user 378 // For V < 5, the encryption key is derived from the user
368 // password. 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 std::string 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 // Algorithm 3.2 from the PDF 1.7 Reference Manual 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,290 +392,185 @@ QPDF::compute_encryption_key_from_password(std::string const&amp; password, Encrypti
381 // password to be presented in its final form. 392 // password to be presented in its final form.
382 393
383 MD5 md5; 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 char pbytes[4]; 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 md5.encodeDataIncrementally(pbytes, 4); 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 throw std::logic_error("compute_O_rc4_key called for file with V >= 5"); 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 MD5 md5; 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 // Algorithm 3.3 from the PDF 1.7 Reference Manual 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 // Algorithm 3.4 from the PDF 1.7 Reference Manual 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 // Algorithm 3.5 from the PDF 1.7 Reference Manual 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 MD5 md5; 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 // Algorithm 3.6 from the PDF 1.7 Reference Manual 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 // Algorithm 3.11 from the PDF 1.7 extension level 3 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 } else { 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 // Algorithm 3.7 from the PDF 1.7 Reference Manual 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 user_password = new_user_password; 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 // Algorithm 3.12 from the PDF 1.7 extension level 3 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 } else { 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 std::string 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 // Disregard whether Perms is valid. 549 // Disregard whether Perms is valid.
596 bool disregard; 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 // From algorithm 3.10 from the PDF 1.7 extension level 3 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 std::string 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 // Algorithm 3.2a from the PDF 1.7 extension level 3 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,129 +579,136 @@ QPDF::recover_encryption_key_with_password(
673 // profile of stringprep (RFC 3454) and then convert the result to UTF-8. 579 // profile of stringprep (RFC 3454) and then convert the result to UTF-8.
674 580
675 perms_valid = false; 581 perms_valid = false;
676 - std::string key_password = truncate_password_V5(password); 582 + std::string key_password = password.substr(0, 127);
677 std::string key_salt; 583 std::string key_salt;
678 std::string user_data; 584 std::string user_data;
679 std::string encrypted_file_key; 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 std::string file_key = process_with_aes(intermediate_key, false, encrypted_file_key); 595 std::string file_key = process_with_aes(intermediate_key, false, encrypted_file_key);
690 596
691 // Decrypt Perms and check against expected value 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 return file_key; 600 return file_key;
698 } 601 }
699 602
700 QPDF::encryption_method_e 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 void 621 void
719 QPDF::initializeEncryption() 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 return; 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 // After we initialize encryption parameters, we must use stored key information and never look 654 // After we initialize encryption parameters, we must use stored key information and never look
727 // at /Encrypt again. Otherwise, things could go wrong if someone mutates the encryption 655 // at /Encrypt again. Otherwise, things could go wrong if someone mutates the encryption
728 // dictionary. 656 // dictionary.
729 657
730 - if (!m->trailer.hasKey("/Encrypt")) { 658 + if (!trailer.hasKey("/Encrypt")) {
731 return; 659 return;
732 } 660 }
733 661
734 // Go ahead and set m->encrypted here. That way, isEncrypted will return true even if there 662 // Go ahead and set m->encrypted here. That way, isEncrypted will return true even if there
735 // were errors reading the encryption dictionary. 663 // were errors reading the encryption dictionary.
736 - m->encp->encrypted = true; 664 + encrypted = true;
737 665
738 std::string id1; 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 // Treating a missing ID as the empty string enables qpdf to decrypt some invalid encrypted 669 // Treating a missing ID as the empty string enables qpdf to decrypt some invalid encrypted
744 // files with no /ID that poppler can read but Adobe Reader can't. 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 if (!encryption_dict.isDictionary()) { 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 if (!(encryption_dict.getKey("/Filter").isName() && 681 if (!(encryption_dict.getKey("/Filter").isName() &&
754 (encryption_dict.getKey("/Filter").getName() == "/Standard"))) { 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 if (!encryption_dict.getKey("/SubFilter").isNull()) { 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 if (!(encryption_dict.getKey("/V").isInteger() && encryption_dict.getKey("/R").isInteger() && 689 if (!(encryption_dict.getKey("/V").isInteger() && encryption_dict.getKey("/R").isInteger() &&
771 encryption_dict.getKey("/O").isString() && encryption_dict.getKey("/U").isString() && 690 encryption_dict.getKey("/O").isString() && encryption_dict.getKey("/U").isString() &&
772 encryption_dict.getKey("/P").isInteger())) { 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 int V = encryption_dict.getKey("/V").getIntValueAsInt(); 695 int V = encryption_dict.getKey("/V").getIntValueAsInt();
780 int R = encryption_dict.getKey("/R").getIntValueAsInt(); 696 int R = encryption_dict.getKey("/R").getIntValueAsInt();
781 std::string O = encryption_dict.getKey("/O").getStringValue(); 697 std::string O = encryption_dict.getKey("/O").getStringValue();
782 std::string U = encryption_dict.getKey("/U").getStringValue(); 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 // If supporting new encryption R/V values, remember to update error message inside this if 701 // If supporting new encryption R/V values, remember to update error message inside this if
786 // statement. 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 "Unsupported /R or /V in encryption dictionary; R = " + std::to_string(R) + 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 // OE, UE, and Perms are only present if V >= 5. 713 // OE, UE, and Perms are only present if V >= 5.
801 std::string OE; 714 std::string OE;
@@ -806,19 +719,15 @@ QPDF::initializeEncryption() @@ -806,19 +719,15 @@ QPDF::initializeEncryption()
806 // These must be exactly the right number of bytes. 719 // These must be exactly the right number of bytes.
807 pad_short_parameter(O, key_bytes); 720 pad_short_parameter(O, key_bytes);
808 pad_short_parameter(U, key_bytes); 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 } else { 725 } else {
815 if (!(encryption_dict.getKey("/OE").isString() && 726 if (!(encryption_dict.getKey("/OE").isString() &&
816 encryption_dict.getKey("/UE").isString() && 727 encryption_dict.getKey("/UE").isString() &&
817 encryption_dict.getKey("/Perms").isString())) { 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 OE = encryption_dict.getKey("/OE").getStringValue(); 732 OE = encryption_dict.getKey("/OE").getStringValue();
824 UE = encryption_dict.getKey("/UE").getStringValue(); 733 UE = encryption_dict.getKey("/UE").getStringValue();
@@ -832,7 +741,7 @@ QPDF::initializeEncryption() @@ -832,7 +741,7 @@ QPDF::initializeEncryption()
832 pad_short_parameter(Perms, Perms_key_bytes_V5); 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 if (V <= 1) { 745 if (V <= 1) {
837 Length = 40; 746 Length = 40;
838 } else if (V == 4) { 747 } else if (V == 4) {
@@ -842,25 +751,20 @@ QPDF::initializeEncryption() @@ -842,25 +751,20 @@ QPDF::initializeEncryption()
842 } else { 751 } else {
843 if (encryption_dict.getKey("/Length").isInteger()) { 752 if (encryption_dict.getKey("/Length").isInteger()) {
844 Length = encryption_dict.getKey("/Length").getIntValueAsInt(); 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 if (cdict.isDictionary()) { 768 if (cdict.isDictionary()) {
865 encryption_method_e method = e_none; 769 encryption_method_e method = e_none;
866 if (cdict.getKey("/CFM").isName()) { 770 if (cdict.getKey("/CFM").isName()) {
@@ -879,16 +783,13 @@ QPDF::initializeEncryption() @@ -879,16 +783,13 @@ QPDF::initializeEncryption()
879 method = e_unknown; 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 // qpdf does not use this for anything other than informational purposes. This is 793 // qpdf does not use this for anything other than informational purposes. This is
893 // intended to instruct conforming writers on which crypt filter should be used when new 794 // intended to instruct conforming writers on which crypt filter should be used when new
894 // file attachments are added to a PDF file, but qpdf never generates encrypted files 795 // file attachments are added to a PDF file, but qpdf never generates encrypted files
@@ -898,55 +799,50 @@ QPDF::initializeEncryption() @@ -898,55 +799,50 @@ QPDF::initializeEncryption()
898 // the way I was imagining. Still, providing this information could be useful when 799 // the way I was imagining. Still, providing this information could be useful when
899 // looking at a file generated by something else, such as Acrobat when specifying that 800 // looking at a file generated by something else, such as Acrobat when specifying that
900 // only attachments should be encrypted. 801 // only attachments should be encrypted.
901 - m->encp->cf_file = interpretCF(m->encp, EFF); 802 + cf_file = interpretCF(EFF);
902 } else { 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 // ignore passwords in file 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 // For V < 5, the user password is encrypted with the owner password, and the user password 836 // For V < 5, the user password is encrypted with the owner password, and the user password
937 // is always used for computing the encryption key. 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 } else { 839 } else {
940 // For V >= 5, either password can be used independently to compute the encryption key, and 840 // For V >= 5, either password can be used independently to compute the encryption key, and
941 // neither password can be used to recover the other. 841 // neither password can be used to recover the other.
942 bool perms_valid; 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 if (!perms_valid) { 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,12 +856,7 @@ QPDF::getKeyForObject(std::shared_ptr&lt;EncryptionParameters&gt; encp, QPDFObjGen og,
960 856
961 if (og != encp->cached_key_og) { 857 if (og != encp->cached_key_og) {
962 encp->cached_object_encryption_key = compute_data_key( 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 encp->cached_key_og = og; 860 encp->cached_key_og = og;
970 } 861 }
971 862
@@ -1070,7 +961,7 @@ QPDF::decryptStream( @@ -1070,7 +961,7 @@ QPDF::decryptStream(
1070 QPDFObjectHandle decode_parms = stream_dict.getKey("/DecodeParms"); 961 QPDFObjectHandle decode_parms = stream_dict.getKey("/DecodeParms");
1071 if (decode_parms.isDictionaryOfType("/CryptFilterDecodeParms")) { 962 if (decode_parms.isDictionaryOfType("/CryptFilterDecodeParms")) {
1072 QTC::TC("qpdf", "QPDF_encryption stream crypt filter"); 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 method_source = "stream's Crypt decode parameters"; 965 method_source = "stream's Crypt decode parameters";
1075 } 966 }
1076 } else if ( 967 } else if (
@@ -1085,7 +976,7 @@ QPDF::decryptStream( @@ -1085,7 +976,7 @@ QPDF::decryptStream(
1085 if (crypt_params.isDictionary() && 976 if (crypt_params.isDictionary() &&
1086 crypt_params.getKey("/Name").isName()) { 977 crypt_params.getKey("/Name").isName()) {
1087 QTC::TC("qpdf", "QPDF_encrypt crypt array"); 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 method_source = "stream's Crypt decode parameters (array)"; 980 method_source = "stream's Crypt decode parameters (array)";
1090 } 981 }
1091 } 982 }
@@ -1164,17 +1055,23 @@ QPDF::compute_encryption_O_U( @@ -1164,17 +1055,23 @@ QPDF::compute_encryption_O_U(
1164 int P, 1055 int P,
1165 bool encrypt_metadata, 1056 bool encrypt_metadata,
1166 std::string const& id1, 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 if (V >= 5) { 1070 if (V >= 5) {
1171 throw std::logic_error("compute_encryption_O_U called for file with V >= 5"); 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 void 1077 void
@@ -1188,20 +1085,53 @@ QPDF::compute_encryption_parameters_V5( @@ -1188,20 +1085,53 @@ QPDF::compute_encryption_parameters_V5(
1188 bool encrypt_metadata, 1085 bool encrypt_metadata,
1189 std::string const& id1, 1086 std::string const& id1,
1190 std::string& encryption_key, 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 EncryptionData data(V, R, key_len, P, "", "", "", "", "", id1, encrypt_metadata); 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 std::string const& 1137 std::string const&
@@ -1233,9 +1163,12 @@ QPDF::isEncrypted() const @@ -1233,9 +1163,12 @@ QPDF::isEncrypted() const
1233 bool 1163 bool
1234 QPDF::isEncrypted(int& R, int& P) 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 bool 1174 bool
@@ -1247,22 +1180,16 @@ QPDF::isEncrypted( @@ -1247,22 +1180,16 @@ QPDF::isEncrypted(
1247 encryption_method_e& string_method, 1180 encryption_method_e& string_method,
1248 encryption_method_e& file_method) 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 return false; 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 bool 1195 bool
@@ -1277,135 +1204,57 @@ QPDF::userPasswordMatched() const @@ -1277,135 +1204,57 @@ QPDF::userPasswordMatched() const
1277 return m->encp->user_password_matched; 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 bool 1207 bool
1288 QPDF::allowAccessibility() 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 bool 1213 bool
1304 QPDF::allowExtractAll() 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 bool 1219 bool
1316 QPDF::allowPrintLowRes() 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 bool 1225 bool
1328 QPDF::allowPrintHighRes() 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 bool 1231 bool
1343 QPDF::allowModifyAssembly() 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 bool 1237 bool
1359 QPDF::allowModifyForm() 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 bool 1243 bool
1375 QPDF::allowModifyAnnotation() 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 bool 1249 bool
1387 QPDF::allowModifyOther() 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 bool 1255 bool
1399 QPDF::allowModifyAll() 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,6 +2,7 @@
2 #include <qpdf/qpdf-config.h> 2 #include <qpdf/qpdf-config.h>
3 3
4 #include <qpdf/QUtil.hh> 4 #include <qpdf/QUtil.hh>
  5 +#include <qpdf/Util.hh>
5 6
6 #include <qpdf/CryptoRandomDataProvider.hh> 7 #include <qpdf/CryptoRandomDataProvider.hh>
7 #include <qpdf/Pipeline.hh> 8 #include <qpdf/Pipeline.hh>
@@ -1131,6 +1132,14 @@ QUtil::initializeWithRandomBytes(unsigned char* data, size_t len) @@ -1131,6 +1132,14 @@ QUtil::initializeWithRandomBytes(unsigned char* data, size_t len)
1131 getRandomDataProvider()->provideRandomData(data, len); 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 long 1143 long
1135 QUtil::random() 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,3 +13,13 @@ RC4::process(unsigned char const* in_data, size_t len, unsigned char* out_data)
13 { 13 {
14 crypto->RC4_process(in_data, len, out_data); 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,10 +24,25 @@ class MD5
24 void appendString(char const* input_string); 24 void appendString(char const* input_string);
25 25
26 // appends arbitrary data to current md5 object 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 // computes a raw digest 42 // computes a raw digest
30 void digest(Digest); 43 void digest(Digest);
  44 + std::string digest();
  45 + static std::string digest(std::string_view data);
31 46
32 // prints the digest to stdout terminated with \r\n (primarily for testing) 47 // prints the digest to stdout terminated with \r\n (primarily for testing)
33 void print(); 48 void print();
libqpdf/qpdf/QPDFWriter_private.hh
@@ -43,92 +43,4 @@ class QPDFWriter::NewObjTable: public ::ObjTable&lt;QPDFWriter::NewObject&gt; @@ -43,92 +43,4 @@ class QPDFWriter::NewObjTable: public ::ObjTable&lt;QPDFWriter::NewObject&gt;
43 friend class QPDFWriter; 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 #endif // QPDFWRITER_PRIVATE_HH 46 #endif // QPDFWRITER_PRIVATE_HH
libqpdf/qpdf/QPDF_private.hh
@@ -187,11 +187,30 @@ class QPDF::EncryptionParameters @@ -187,11 +187,30 @@ class QPDF::EncryptionParameters
187 public: 187 public:
188 EncryptionParameters() = default; 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 private: 208 private:
191 bool encrypted{false}; 209 bool encrypted{false};
192 bool encryption_initialized{false}; 210 bool encryption_initialized{false};
  211 + std::bitset<32> P_{0xfffffffc}; // Specification always requires bits 1 and 2 to be cleared.
193 int encryption_V{0}; 212 int encryption_V{0};
194 - int encryption_R{0}; 213 + int R_{0};
195 bool encrypt_metadata{true}; 214 bool encrypt_metadata{true};
196 std::map<std::string, encryption_method_e> crypt_filters; 215 std::map<std::string, encryption_method_e> crypt_filters;
197 encryption_method_e cf_stream{e_none}; 216 encryption_method_e cf_stream{e_none};
libqpdf/qpdf/RC4.hh
@@ -14,6 +14,8 @@ class RC4 @@ -14,6 +14,8 @@ class RC4
14 // It is safe to pass the same pointer to in_data and out_data to encrypt/decrypt in place 14 // It is safe to pass the same pointer to in_data and out_data to encrypt/decrypt in place
15 void process(unsigned char const* in_data, size_t len, unsigned char* out_data); 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 private: 19 private:
18 std::shared_ptr<QPDFCryptoImpl> crypto; 20 std::shared_ptr<QPDFCryptoImpl> crypto;
19 }; 21 };
libqpdf/qpdf/Util.hh
@@ -59,6 +59,8 @@ namespace qpdf::util @@ -59,6 +59,8 @@ namespace qpdf::util
59 s.insert(0, 1, '1'); 59 s.insert(0, 1, '1');
60 } 60 }
61 61
  62 + std::string random_string(size_t len);
  63 +
62 } // namespace qpdf::util 64 } // namespace qpdf::util
63 65
64 #endif // UTIL_HH 66 #endif // UTIL_HH
qpdf/qpdf.testcov
@@ -228,7 +228,6 @@ QPDFWriter extra header text no newline 0 @@ -228,7 +228,6 @@ QPDFWriter extra header text no newline 0
228 QPDFWriter extra header text add newline 0 228 QPDFWriter extra header text add newline 0
229 QPDF bogus 0 offset 0 229 QPDF bogus 0 offset 0
230 QPDF global offset 0 230 QPDF global offset 0
231 -QPDFWriter copy V5 0  
232 QPDFWriter increasing extension level 0 231 QPDFWriter increasing extension level 0
233 QPDFWriter make Extensions direct 0 232 QPDFWriter make Extensions direct 0
234 QPDFWriter make ADBE direct 1 233 QPDFWriter make ADBE direct 1