From 23a36f14a4ac53720dc681d3439ef4f6cba86d30 Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Sun, 18 Aug 2019 16:03:41 +0200 Subject: [PATCH] Fix decryption of objects with number > 511, better use of zlib library --- PdfForQtLib/PdfForQtLib.pro | 10 ++++ .../sources/pdfpagecontentprocessor.cpp | 12 +++- PdfForQtLib/sources/pdfsecurityhandler.cpp | 4 +- PdfForQtLib/sources/pdfstreamfilters.cpp | 60 ++++++++++++++++--- PdfForQtLib/sources/pdfstreamfilters.h | 3 + 5 files changed, 76 insertions(+), 13 deletions(-) diff --git a/PdfForQtLib/PdfForQtLib.pro b/PdfForQtLib/PdfForQtLib.pro index 92a792b..1eb7c63 100644 --- a/PdfForQtLib/PdfForQtLib.pro +++ b/PdfForQtLib/PdfForQtLib.pro @@ -135,6 +135,16 @@ openssl_lib.files = $$PDFFORQT_DEPENDENCIES_PATH/OpenSSL/libcrypto-3.dll $$PDFFO openssl_lib.path = $$DESTDIR/install INSTALLS += openssl_lib +# Link zlib +LIBS += -L$$PDFFORQT_DEPENDENCIES_PATH/zlib/bin/ -lzlib +INCLUDEPATH += $$PDFFORQT_DEPENDENCIES_PATH/zlib/include +DEPENDPATH += $$PDFFORQT_DEPENDENCIES_PATH/zlib/include + +# Add zlib to installations +zlib.files = $$PDFFORQT_DEPENDENCIES_PATH/zlib/bin/zlib.dll +zlib.path = $$DESTDIR/install +INSTALLS += zlib + # ensure debug info even for RELEASE build CONFIG += force_debug_info diff --git a/PdfForQtLib/sources/pdfpagecontentprocessor.cpp b/PdfForQtLib/sources/pdfpagecontentprocessor.cpp index 24621cf..42e4200 100644 --- a/PdfForQtLib/sources/pdfpagecontentprocessor.cpp +++ b/PdfForQtLib/sources/pdfpagecontentprocessor.cpp @@ -518,9 +518,17 @@ void PDFPageContentProcessor::processContent(const QByteArray& content) void PDFPageContentProcessor::processContentStream(const PDFStream* stream) { - QByteArray content = m_document->getDecodedStream(stream); + try + { + QByteArray content = m_document->getDecodedStream(stream); - processContent(content); + processContent(content); + } + catch (PDFParserException exception) + { + m_operands.clear(); + m_errorList.append(PDFRenderError(RenderErrorType::Error, exception.getMessage())); + } } void PDFPageContentProcessor::processForm(const QMatrix& matrix, const QRectF& boundingBox, const PDFObject& resources, const QByteArray& content) diff --git a/PdfForQtLib/sources/pdfsecurityhandler.cpp b/PdfForQtLib/sources/pdfsecurityhandler.cpp index d54e033..a774c2a 100644 --- a/PdfForQtLib/sources/pdfsecurityhandler.cpp +++ b/PdfForQtLib/sources/pdfsecurityhandler.cpp @@ -660,7 +660,7 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data std::vector inputKeyData = convertByteArrayToVector(m_authorizationData.fileEncryptionKey); uint32_t objectNumber = qToLittleEndian(static_cast(reference.objectNumber)); uint32_t generation = qToLittleEndian(static_cast(reference.generation)); - inputKeyData.insert(inputKeyData.cend(), { uint8_t(objectNumber & 0xFF), uint8_t((objectNumber >> 8) && 0xFF), uint8_t((objectNumber >> 16) && 0xFF), uint8_t(generation & 0xFF), uint8_t((generation >> 8) && 0xFF), }); + inputKeyData.insert(inputKeyData.cend(), { uint8_t(objectNumber & 0xFF), uint8_t((objectNumber >> 8) & 0xFF), uint8_t((objectNumber >> 16) & 0xFF), uint8_t(generation & 0xFF), uint8_t((generation >> 8) & 0xFF) }); std::vector objectEncryptionKey(MD5_DIGEST_LENGTH, uint8_t(0)); MD5(inputKeyData.data(), inputKeyData.size(), objectEncryptionKey.data()); @@ -682,7 +682,7 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data std::vector inputKeyData = convertByteArrayToVector(m_authorizationData.fileEncryptionKey); uint32_t objectNumber = qToLittleEndian(static_cast(reference.objectNumber)); uint32_t generation = qToLittleEndian(static_cast(reference.generation)); - inputKeyData.insert(inputKeyData.cend(), { uint8_t(objectNumber & 0xFF), uint8_t((objectNumber >> 8) && 0xFF), uint8_t((objectNumber >> 16) && 0xFF), uint8_t(generation & 0xFF), uint8_t((generation >> 8) && 0xFF), 0x73, 0x41, 0x6C, 0x54 }); + inputKeyData.insert(inputKeyData.cend(), { uint8_t(objectNumber & 0xFF), uint8_t((objectNumber >> 8) & 0xFF), uint8_t((objectNumber >> 16) & 0xFF), uint8_t(generation & 0xFF), uint8_t((generation >> 8) & 0xFF), 0x73, 0x41, 0x6C, 0x54 }); std::vector objectEncryptionKey(MD5_DIGEST_LENGTH, uint8_t(0)); MD5(inputKeyData.data(), inputKeyData.size(), objectEncryptionKey.data()); diff --git a/PdfForQtLib/sources/pdfstreamfilters.cpp b/PdfForQtLib/sources/pdfstreamfilters.cpp index 0c20e2f..529163c 100644 --- a/PdfForQtLib/sources/pdfstreamfilters.cpp +++ b/PdfForQtLib/sources/pdfstreamfilters.cpp @@ -20,6 +20,9 @@ #include "pdfconstants.h" #include "pdfparser.h" #include "pdfsecurityhandler.h" +#include "pdfutils.h" + +#include #include @@ -395,16 +398,55 @@ QByteArray PDFFlateDecodeFilter::apply(const QByteArray& data, } } - uint32_t size = data.size(); - - QByteArray dataToUncompress; - dataToUncompress.resize(sizeof(decltype(size)) + data.size()); - - qToBigEndian(size, dataToUncompress.data()); - std::copy(data.cbegin(), data.cend(), std::next(dataToUncompress.begin(), sizeof(decltype(size)))); - PDFStreamPredictor predictor = PDFStreamPredictor::createPredictor(objectFetcher, parameters); - return predictor.apply(qUncompress(dataToUncompress)); + return predictor.apply(uncompress(data)); +} + +QByteArray PDFFlateDecodeFilter::uncompress(const QByteArray& data) +{ + QByteArray result; + + z_stream stream = { }; + stream.next_in = const_cast(convertByteArrayToUcharPtr(data)); + stream.avail_in = data.size(); + + std::array outputBuffer = { }; + + int error = inflateInit(&stream); + if (error != Z_OK) + { + throw PDFParserException(PDFTranslationContext::tr("Failed to initialize flate decompression stream.")); + } + + do + { + stream.next_out = outputBuffer.data(); + stream.avail_out = static_cast(outputBuffer.size()); + + error = inflate(&stream, Z_NO_FLUSH); + + int bytesWritten = int(outputBuffer.size()) - stream.avail_out; + result.append(reinterpret_cast(outputBuffer.data()), bytesWritten); + } while (error == Z_OK); + + QString errorMessage; + if (stream.msg) + { + errorMessage = QString::fromLatin1(stream.msg); + } + + inflateEnd(&stream); + + switch (error) + { + case Z_STREAM_END: + break; // No error, normal behaviour + + default: + throw PDFParserException(PDFTranslationContext::tr("Error decompressing by flate method: %1").arg(errorMessage)); + } + + return result; } QByteArray PDFRunLengthDecodeFilter::apply(const QByteArray& data, diff --git a/PdfForQtLib/sources/pdfstreamfilters.h b/PdfForQtLib/sources/pdfstreamfilters.h index 7a21a89..96ca22a 100644 --- a/PdfForQtLib/sources/pdfstreamfilters.h +++ b/PdfForQtLib/sources/pdfstreamfilters.h @@ -184,6 +184,9 @@ public: const PDFObjectFetcher& objectFetcher, const PDFObject& parameters, const PDFSecurityHandler* securityHandler) const override; + +private: + static QByteArray uncompress(const QByteArray& data); }; class PDFFORQTLIBSHARED_EXPORT PDFRunLengthDecodeFilter : public PDFStreamFilter