Commit c8862b5fe7b28a530183237a48eae551ccea60d3
Committed by
GitHub
Merge pull request #1489 from m-holger/qpdf_encrypt
Modernize QPDF_encryption
Showing
13 changed files
with
755 additions
and
910 deletions
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& qpdf) | @@ -663,9 +673,10 @@ QPDFWriter::copyEncryptionParameters(QPDF& 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& qpdf) | @@ -673,72 +684,62 @@ QPDFWriter::copyEncryptionParameters(QPDF& 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& length) | @@ -989,7 +948,7 @@ QPDFWriter::adjustAESStreamLength(size_t& 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& U) | @@ -111,6 +127,25 @@ QPDF::EncryptionData::setU(std::string const& 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& password, Encrypti | @@ -381,290 +392,185 @@ QPDF::compute_encryption_key_from_password(std::string const& 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<EncryptionParameters> encp, QPDFObjGen og, | @@ -960,12 +856,7 @@ QPDF::getKeyForObject(std::shared_ptr<EncryptionParameters> 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<QPDFWriter::NewObject> | @@ -43,92 +43,4 @@ class QPDFWriter::NewObjTable: public ::ObjTable<QPDFWriter::NewObject> | ||
| 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
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 |