From fbbb0ee0167a9013c3a712c790a9772075aed2ad Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sun, 6 Jan 2019 10:34:52 -0500 Subject: Make a static version of QPDF::pipeStreamData This is in preparation of being able to pipe a stream's data without keeping a copy of its containing qpdf object. --- include/qpdf/QPDF.hh | 74 ++++++++++++++++++++++++++----------------- libqpdf/QPDF.cc | 63 +++++++++++++++++++++++++----------- libqpdf/QPDF_encryption.cc | 79 ++++++++++++++++++++++++++-------------------- qpdf/qpdf.testcov | 1 + 4 files changed, 134 insertions(+), 83 deletions(-) diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index 57e30383..38f3f7b3 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -692,6 +692,30 @@ class QPDF }; friend class ResolveRecorder; + class EncryptionParameters + { + friend class QPDF; + public: + EncryptionParameters(); + + private: + bool encrypted; + bool encryption_initialized; + int encryption_V; + int encryption_R; + bool encrypt_metadata; + std::map crypt_filters; + encryption_method_e cf_stream; + encryption_method_e cf_string; + encryption_method_e cf_file; + std::string provided_password; + std::string user_password; + std::string encryption_key; + std::string cached_object_encryption_key; + int cached_key_objid; + int cached_key_generation; + }; + void parse(char const* password); void warn(QPDFExc const& e); void setTrailer(QPDFObjectHandle obj); @@ -735,6 +759,16 @@ class QPDF Pipeline* pipeline, bool suppress_warnings, bool will_retry); + static bool pipeStreamData(PointerHolder encp, + PointerHolder file, + QPDF& qpdf_for_warning, + int objid, int generation, + qpdf_offset_t offset, size_t length, + QPDFObjectHandle dict, + bool is_attachment_stream, + Pipeline* pipeline, + bool suppress_warnings, + bool will_retry); // For QPDFWriter: @@ -776,9 +810,12 @@ class QPDF bool check_duplicate); // methods to support encryption -- implemented in QPDF_encryption.cc - encryption_method_e interpretCF(QPDFObjectHandle); + static encryption_method_e interpretCF( + PointerHolder encp, QPDFObjectHandle); void initializeEncryption(); - std::string getKeyForObject(int objid, int generation, bool use_aes); + static std::string getKeyForObject( + PointerHolder encp, + int objid, int generation, bool use_aes); void decryptString(std::string&, int objid, int generation); static std::string compute_encryption_key_from_password( std::string const& password, EncryptionData const& data); @@ -787,9 +824,12 @@ class QPDF static std::string recover_encryption_key_with_password( std::string const& password, EncryptionData const& data, bool& perms_valid); - void decryptStream( - Pipeline*& pipeline, int objid, int generation, - QPDFObjectHandle& stream_dict, + static void decryptStream( + PointerHolder encp, + PointerHolder file, + QPDF& qpdf_for_warning, Pipeline*& pipeline, + int objid, int generation, + QPDFObjectHandle& stream_dict, bool is_attachment_stream, std::vector >& heap); // Methods to support object copying @@ -1160,30 +1200,6 @@ class QPDF std::set& visited, bool top); void filterCompressedObjects(std::map const& object_stream_data); - class EncryptionParameters - { - friend class QPDF; - public: - EncryptionParameters(); - - private: - bool encrypted; - bool encryption_initialized; - int encryption_V; - int encryption_R; - bool encrypt_metadata; - std::map crypt_filters; - encryption_method_e cf_stream; - encryption_method_e cf_string; - encryption_method_e cf_file; - std::string provided_password; - std::string user_password; - std::string encryption_key; - std::string cached_object_encryption_key; - int cached_key_objid; - int cached_key_generation; - }; - class Members { friend class QPDF; diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 813775a2..f5267dea 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -2512,34 +2512,40 @@ QPDF::getCompressibleObjGens() } bool -QPDF::pipeStreamData(int objid, int generation, +QPDF::pipeStreamData(PointerHolder encp, + PointerHolder file, + QPDF& qpdf_for_warning, + int objid, int generation, qpdf_offset_t offset, size_t length, QPDFObjectHandle stream_dict, + bool is_attachment_stream, Pipeline* pipeline, bool suppress_warnings, bool will_retry) { - bool success = false; std::vector > to_delete; - if (this->m->encp->encrypted) + if (encp->encrypted) { - decryptStream(pipeline, objid, generation, stream_dict, to_delete); + decryptStream(encp, file, qpdf_for_warning, + pipeline, objid, generation, + stream_dict, is_attachment_stream, to_delete); } + bool success = false; try { - this->m->file->seek(offset, SEEK_SET); + file->seek(offset, SEEK_SET); char buf[10240]; while (length > 0) { size_t to_read = (sizeof(buf) < length ? sizeof(buf) : length); - size_t len = this->m->file->read(buf, to_read); + size_t len = file->read(buf, to_read); if (len == 0) { throw QPDFExc(qpdf_e_damaged_pdf, - this->m->file->getName(), - this->m->last_object_description, - this->m->file->getLastOffset(), + file->getName(), + "", + file->getLastOffset(), "unexpected EOF reading stream data"); } length -= len; @@ -2552,7 +2558,7 @@ QPDF::pipeStreamData(int objid, int generation, { if (! suppress_warnings) { - warn(e); + qpdf_for_warning.warn(e); } } catch (std::exception& e) @@ -2560,17 +2566,19 @@ QPDF::pipeStreamData(int objid, int generation, if (! suppress_warnings) { QTC::TC("qpdf", "QPDF decoding error warning"); - warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(), - "", this->m->file->getLastOffset(), - "error decoding stream data for object " + - QUtil::int_to_string(objid) + " " + - QUtil::int_to_string(generation) + ": " + e.what())); + qpdf_for_warning.warn( + QPDFExc(qpdf_e_damaged_pdf, file->getName(), + "", file->getLastOffset(), + "error decoding stream data for object " + + QUtil::int_to_string(objid) + " " + + QUtil::int_to_string(generation) + ": " + e.what())); if (will_retry) { - warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(), - "", this->m->file->getLastOffset(), - "stream will be re-processed without" - " filtering to avoid data loss")); + qpdf_for_warning.warn( + QPDFExc(qpdf_e_damaged_pdf, file->getName(), + "", file->getLastOffset(), + "stream will be re-processed without" + " filtering to avoid data loss")); } } } @@ -2588,6 +2596,23 @@ QPDF::pipeStreamData(int objid, int generation, return success; } +bool +QPDF::pipeStreamData(int objid, int generation, + qpdf_offset_t offset, size_t length, + QPDFObjectHandle stream_dict, + Pipeline* pipeline, + bool suppress_warnings, + bool will_retry) +{ + bool is_attachment_stream = this->m->attachment_streams.count( + QPDFObjGen(objid, generation)); + return pipeStreamData( + this->m->encp, this->m->file, *this, + objid, generation, offset, length, + stream_dict, is_attachment_stream, + pipeline, suppress_warnings, will_retry); +} + void QPDF::findAttachmentStreams() { diff --git a/libqpdf/QPDF_encryption.cc b/libqpdf/QPDF_encryption.cc index 53ecc622..c0778b6a 100644 --- a/libqpdf/QPDF_encryption.cc +++ b/libqpdf/QPDF_encryption.cc @@ -764,14 +764,15 @@ QPDF::recover_encryption_key_with_password( } QPDF::encryption_method_e -QPDF::interpretCF(QPDFObjectHandle cf) +QPDF::interpretCF( + PointerHolder encp, QPDFObjectHandle cf) { if (cf.isName()) { std::string filter = cf.getName(); - if (this->m->encp->crypt_filters.count(filter) != 0) + if (encp->crypt_filters.count(filter) != 0) { - return this->m->encp->crypt_filters[filter]; + return encp->crypt_filters[filter]; } else if (filter == "/Identity") { @@ -1000,11 +1001,11 @@ QPDF::initializeEncryption() QPDFObjectHandle StmF = encryption_dict.getKey("/StmF"); QPDFObjectHandle StrF = encryption_dict.getKey("/StrF"); QPDFObjectHandle EFF = encryption_dict.getKey("/EFF"); - this->m->encp->cf_stream = interpretCF(StmF); - this->m->encp->cf_string = interpretCF(StrF); + this->m->encp->cf_stream = interpretCF(this->m->encp, StmF); + this->m->encp->cf_string = interpretCF(this->m->encp, StrF); if (EFF.isName()) { - this->m->encp->cf_file = interpretCF(EFF); + this->m->encp->cf_file = interpretCF(this->m->encp, EFF); } else { @@ -1068,26 +1069,28 @@ QPDF::initializeEncryption() } std::string -QPDF::getKeyForObject(int objid, int generation, bool use_aes) +QPDF::getKeyForObject( + PointerHolder encp, + int objid, int generation, bool use_aes) { - if (! this->m->encp->encrypted) + if (! encp->encrypted) { throw std::logic_error( "request for encryption key in non-encrypted PDF"); } - if (! ((objid == this->m->encp->cached_key_objid) && - (generation == this->m->encp->cached_key_generation))) + if (! ((objid == encp->cached_key_objid) && + (generation == encp->cached_key_generation))) { - this->m->encp->cached_object_encryption_key = - compute_data_key(this->m->encp->encryption_key, objid, generation, - use_aes, this->m->encp->encryption_V, - this->m->encp->encryption_R); - this->m->encp->cached_key_objid = objid; - this->m->encp->cached_key_generation = generation; + encp->cached_object_encryption_key = + compute_data_key(encp->encryption_key, objid, generation, + use_aes, encp->encryption_V, + encp->encryption_R); + encp->cached_key_objid = objid; + encp->cached_key_generation = generation; } - return this->m->encp->cached_object_encryption_key; + return encp->cached_object_encryption_key; } void @@ -1131,7 +1134,8 @@ QPDF::decryptString(std::string& str, int objid, int generation) } } - std::string key = getKeyForObject(objid, generation, use_aes); + std::string key = getKeyForObject( + this->m->encp, objid, generation, use_aes); try { if (use_aes) @@ -1175,8 +1179,12 @@ QPDF::decryptString(std::string& str, int objid, int generation) } void -QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, +QPDF::decryptStream(PointerHolder encp, + PointerHolder file, + QPDF& qpdf_for_warning, Pipeline*& pipeline, + int objid, int generation, QPDFObjectHandle& stream_dict, + bool is_attachment_stream, std::vector >& heap) { std::string type; @@ -1190,7 +1198,7 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, return; } bool use_aes = false; - if (this->m->encp->encryption_V >= 4) + if (encp->encryption_V >= 4) { encryption_method_e method = e_unknown; std::string method_source = "/StmF from /Encrypt dictionary"; @@ -1206,7 +1214,7 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, "/CryptFilterDecodeParms")) { QTC::TC("qpdf", "QPDF_encryption stream crypt filter"); - method = interpretCF(decode_parms.getKey("/Name")); + method = interpretCF(encp, decode_parms.getKey("/Name")); method_source = "stream's Crypt decode parameters"; } } @@ -1229,7 +1237,7 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, { QTC::TC("qpdf", "QPDF_encrypt crypt array"); method = interpretCF( - crypt_params.getKey("/Name")); + encp, crypt_params.getKey("/Name")); method_source = "stream's Crypt " "decode parameters (array)"; } @@ -1241,21 +1249,21 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, if (method == e_unknown) { - if ((! this->m->encp->encrypt_metadata) && (type == "/Metadata")) + if ((! encp->encrypt_metadata) && (type == "/Metadata")) { QTC::TC("qpdf", "QPDF_encryption cleartext metadata"); method = e_none; } else { - if (this->m->attachment_streams.count( - QPDFObjGen(objid, generation)) > 0) + if (is_attachment_stream) { - method = this->m->encp->cf_file; + QTC::TC("qpdf", "QPDF_encryption attachment stream"); + method = encp->cf_file; } else { - method = this->m->encp->cf_stream; + method = encp->cf_stream; } } } @@ -1279,19 +1287,20 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, default: // filter local to this stream. - warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(), - this->m->last_object_description, - this->m->file->getLastOffset(), - "unknown encryption filter for streams" - " (check " + method_source + ");" - " streams may be decrypted improperly")); + qpdf_for_warning.warn( + QPDFExc(qpdf_e_damaged_pdf, file->getName(), + "", file->getLastOffset(), + "unknown encryption filter for streams" + " (check " + method_source + ");" + " streams may be decrypted improperly")); // To avoid repeated warnings, reset cf_stream. Assume // we'd want to use AES if V == 4. - this->m->encp->cf_stream = e_aes; + encp->cf_stream = e_aes; + use_aes = true; break; } } - std::string key = getKeyForObject(objid, generation, use_aes); + std::string key = getKeyForObject(encp, objid, generation, use_aes); if (use_aes) { QTC::TC("qpdf", "QPDF_encryption aes decode stream"); diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index 476a1704..8e1a62f1 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -406,3 +406,4 @@ qpdf image optimize no pipeline 0 qpdf image optimize no shrink 0 qpdf image optimize too small 0 QPDFFormFieldObjectHelper WinAnsi 0 +QPDF_encryption attachment stream 0 -- cgit v1.2.3-54-g00ecf