From 6c261241adf8e6827511ad64645a386c195b4967 Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Tue, 13 Aug 2019 15:48:01 +0200 Subject: [PATCH] Crypt filters & revision 5 encryption --- PdfForQtLib/sources/pdfdocument.cpp | 2 +- PdfForQtLib/sources/pdfdocumentreader.cpp | 4 +- PdfForQtLib/sources/pdfsecurityhandler.cpp | 103 ++++++++++++++------- PdfForQtLib/sources/pdfsecurityhandler.h | 29 ++++++ PdfForQtLib/sources/pdfstreamfilters.cpp | 74 +++++++++++++-- PdfForQtLib/sources/pdfstreamfilters.h | 50 ++++++++-- PdfForQtLib/sources/pdfxreftable.cpp | 2 +- UnitTests/tst_lexicalanalyzertest.cpp | 2 +- 8 files changed, 209 insertions(+), 57 deletions(-) diff --git a/PdfForQtLib/sources/pdfdocument.cpp b/PdfForQtLib/sources/pdfdocument.cpp index 10a001f..d1cea63 100644 --- a/PdfForQtLib/sources/pdfdocument.cpp +++ b/PdfForQtLib/sources/pdfdocument.cpp @@ -42,7 +42,7 @@ static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_TRAPPED_UNKNOWN = "Unknown" QByteArray PDFDocument::getDecodedStream(const PDFStream* stream) const { - return PDFStreamFilterStorage::getDecodedStream(stream, std::bind(&PDFDocument::getObject, this, std::placeholders::_1)); + return PDFStreamFilterStorage::getDecodedStream(stream, std::bind(&PDFDocument::getObject, this, std::placeholders::_1), m_pdfObjectStorage.getSecurityHandler()); } const PDFDictionary* PDFDocument::getTrailerDictionary() const diff --git a/PdfForQtLib/sources/pdfdocumentreader.cpp b/PdfForQtLib/sources/pdfdocumentreader.cpp index 187f7a8..3b63882 100644 --- a/PdfForQtLib/sources/pdfdocumentreader.cpp +++ b/PdfForQtLib/sources/pdfdocumentreader.cpp @@ -378,7 +378,7 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer) objectStreams.insert(entry.objectStream); } - auto processObjectStream = [this, &getObject, &objectFetcher, &objects, &objectStreamEntries] (const PDFObjectReference& objectStreamReference) + auto processObjectStream = [this, &getObject, &objectFetcher, &objects, &objectStreamEntries, &securityHandler] (const PDFObjectReference& objectStreamReference) { if (m_result != Result::OK) { @@ -419,7 +419,7 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer) const PDFInteger n = nObject.getInteger(); const PDFInteger first = firstObject.getInteger(); - QByteArray objectStreamData = PDFStreamFilterStorage::getDecodedStream(objectStream); + QByteArray objectStreamData = PDFStreamFilterStorage::getDecodedStream(objectStream, securityHandler.data()); PDFParsingContext::PDFParsingContextGuard guard(&context, objectStreamReference); PDFParser parser(objectStreamData, &context, PDFParser::AllowStreams); diff --git a/PdfForQtLib/sources/pdfsecurityhandler.cpp b/PdfForQtLib/sources/pdfsecurityhandler.cpp index 31b8d30..d54e033 100644 --- a/PdfForQtLib/sources/pdfsecurityhandler.cpp +++ b/PdfForQtLib/sources/pdfsecurityhandler.cpp @@ -162,9 +162,20 @@ void PDFDecryptObjectVisitor::visitStream(const PDFStream* stream) PDFObject dictionaryObject = m_objectStack.back(); m_objectStack.pop_back(); - // TODO: Handle Crypt filter + // We must also handle situation, that stream has specified Crypt filter. + // In this case, we must delegate decryption to the stream filters. PDFDictionary decryptedDictionary(*dictionaryObject.getDictionary()); - QByteArray decryptedData = m_securityHandler->decrypt(*stream->getContent(), m_reference, PDFSecurityHandler::EncryptionScope::Stream); + QByteArray decryptedData; + if (!decryptedDictionary.hasKey("Crypt")) + { + decryptedData = m_securityHandler->decrypt(*stream->getContent(), m_reference, PDFSecurityHandler::EncryptionScope::Stream); + } + else + { + decryptedData = *stream->getContent(); + decryptedDictionary.addEntry(PDFSecurityHandler::OBJECT_REFERENCE_DICTIONARY_NAME, PDFObject::createReference(m_reference)); + } + m_objectStack.push_back(PDFObject::createStream(std::make_shared(qMove(decryptedDictionary), qMove(decryptedData)))); } @@ -287,7 +298,7 @@ PDFSecurityHandlerPointer PDFSecurityHandler::createSecurityHandler(const PDFObj // Add "Identity" filter to the filters CryptFilter identityFilter; identityFilter.type = CryptFilterType::Identity; - handler.m_cryptFilters["Identity"] = identityFilter; + handler.m_cryptFilters[IDENTITY_FILTER_NAME] = identityFilter; if (V == 4 || V == 5) { @@ -365,8 +376,8 @@ PDFSecurityHandlerPointer PDFSecurityHandler::createSecurityHandler(const PDFObj return it->second; }; - handler.m_filterStreams = resolveFilter(getName(dictionary, "StmF", false, "Identity")); - handler.m_filterStrings = resolveFilter(getName(dictionary, "StrF", false, "Identity")); + handler.m_filterStreams = resolveFilter(getName(dictionary, "StmF", false, IDENTITY_FILTER_NAME)); + handler.m_filterStrings = resolveFilter(getName(dictionary, "StrF", false, IDENTITY_FILTER_NAME)); if (dictionary->hasKey("EFF")) { @@ -381,7 +392,7 @@ PDFSecurityHandlerPointer PDFSecurityHandler::createSecurityHandler(const PDFObj } int R = getInt(dictionary, "R", true); - if (R < 2 || R > 6 || R == 5) + if (R < 2 || R > 6) { throw PDFParserException(PDFTranslationContext::tr("Revision %1 of standard security handler is not supported.").arg(R)); } @@ -413,12 +424,12 @@ PDFSecurityHandlerPointer PDFSecurityHandler::createSecurityHandler(const PDFObj return result; }; - handler.m_O = readByteArray("O", (R != 6) ? 32 : 48); - handler.m_U = readByteArray("U", (R != 6) ? 32 : 48); + handler.m_O = readByteArray("O", (R != 6 && R != 5) ? 32 : 48); + handler.m_U = readByteArray("U", (R != 6 && R != 5) ? 32 : 48); handler.m_permissions = static_cast(static_cast(getInt(dictionary, "P", true))); - if (R == 6) + if (R == 6 || R == 5) { handler.m_OE = readByteArray("OE", 32); handler.m_UE = readByteArray("UE", 32); @@ -482,29 +493,42 @@ PDFSecurityHandler::AuthorizationResult PDFStandardSecurityHandler::authenticate break; } + case 5: case 6: { UserOwnerData_r6 userData = parseParts(m_U); UserOwnerData_r6 ownerData = parseParts(m_O); + auto createHash_r5 = [](const QByteArray& inputData) + { + QByteArray result(SHA256_DIGEST_LENGTH, char(0)); + SHA256(convertByteArrayToUcharPtr(inputData), inputData.size(), convertByteArrayToUcharPtr(result)); + return result; + }; + + auto createHash_r56 = [this, &createHash_r5](const QByteArray& input, const QByteArray& inputPassword, bool useUserKey) + { + return (m_R == 5) ? createHash_r5(input) : createHash_r6(input, inputPassword, useUserKey); + }; + // Try to authorize owner password { QByteArray inputData = password + ownerData.validationSalt + m_U; - QByteArray hash = createHash_r6(inputData, password, true); + QByteArray hash = createHash_r56(inputData, password, true); if (hash == ownerData.hash) { // We have authorized owner access. Now we must calculate the owner encryption key QByteArray fileEncryptionKeyInputData = password + ownerData.keySalt + m_U; - QByteArray fileEncryptionDecryptionKey = createHash_r6(fileEncryptionKeyInputData, password, true); + QByteArray fileEncryptionDecryptionKey = createHash_r56(fileEncryptionKeyInputData, password, true); Q_ASSERT(fileEncryptionDecryptionKey.size() == 32); AES_KEY key = { }; - AES_set_decrypt_key(reinterpret_cast(fileEncryptionDecryptionKey.data()), fileEncryptionDecryptionKey.size() * 8, &key); + AES_set_decrypt_key(convertByteArrayToUcharPtr(fileEncryptionDecryptionKey), fileEncryptionDecryptionKey.size() * 8, &key); unsigned char aesInitializationVector[AES_BLOCK_SIZE] = { }; m_authorizationData.fileEncryptionKey.resize(m_OE.size()); - AES_cbc_encrypt(reinterpret_cast(m_OE.data()), reinterpret_cast(m_authorizationData.fileEncryptionKey.data()), m_OE.size(), &key, aesInitializationVector, AES_DECRYPT); + AES_cbc_encrypt(convertByteArrayToUcharPtr(m_OE), convertByteArrayToUcharPtr(m_authorizationData.fileEncryptionKey), m_OE.size(), &key, aesInitializationVector, AES_DECRYPT); m_authorizationData.authorizationResult = AuthorizationResult::OwnerAuthorized; } @@ -514,19 +538,19 @@ PDFSecurityHandler::AuthorizationResult PDFStandardSecurityHandler::authenticate if (!m_authorizationData.isAuthorized()) { QByteArray inputData = password + userData.validationSalt; - QByteArray hash = createHash_r6(inputData, password, false); + QByteArray hash = createHash_r56(inputData, password, false); if (hash == userData.hash) { QByteArray fileEncryptionKeyInputData = password + userData.keySalt; - QByteArray fileEncryptionDecryptionKey = createHash_r6(fileEncryptionKeyInputData, password, false); + QByteArray fileEncryptionDecryptionKey = createHash_r56(fileEncryptionKeyInputData, password, false); Q_ASSERT(fileEncryptionDecryptionKey.size() == 32); AES_KEY key = { }; - AES_set_decrypt_key(reinterpret_cast(fileEncryptionDecryptionKey.data()), fileEncryptionDecryptionKey.size() * 8, &key); + AES_set_decrypt_key(convertByteArrayToUcharPtr(fileEncryptionDecryptionKey), fileEncryptionDecryptionKey.size() * 8, &key); unsigned char aesInitializationVector[AES_BLOCK_SIZE] = { }; m_authorizationData.fileEncryptionKey.resize(m_UE.size()); - AES_cbc_encrypt(reinterpret_cast(m_UE.data()), reinterpret_cast(m_authorizationData.fileEncryptionKey.data()), m_UE.size(), &key, aesInitializationVector, AES_DECRYPT); + AES_cbc_encrypt(convertByteArrayToUcharPtr(m_UE), convertByteArrayToUcharPtr(m_authorizationData.fileEncryptionKey), m_UE.size(), &key, aesInitializationVector, AES_DECRYPT); // We have authorized owner access m_authorizationData.authorizationResult = AuthorizationResult::UserAuthorized; @@ -539,9 +563,9 @@ PDFSecurityHandler::AuthorizationResult PDFStandardSecurityHandler::authenticate // According the PDF specification, we must also check, if flags are not manipulated. Q_ASSERT(m_Perms.size() == AES_BLOCK_SIZE); AES_KEY key = { }; - AES_set_decrypt_key(reinterpret_cast(m_authorizationData.fileEncryptionKey.data()), m_authorizationData.fileEncryptionKey.size() * 8, &key); + AES_set_decrypt_key(convertByteArrayToUcharPtr(m_authorizationData.fileEncryptionKey), m_authorizationData.fileEncryptionKey.size() * 8, &key); QByteArray decodedPerms(m_Perms.size(), char(0)); - AES_ecb_encrypt(reinterpret_cast(m_Perms.data()), reinterpret_cast(decodedPerms.data()), &key, AES_DECRYPT); + AES_ecb_encrypt(convertByteArrayToUcharPtr(m_Perms), convertByteArrayToUcharPtr(decodedPerms), &key, AES_DECRYPT); // 1) Checks, if bytes 9, 10, 11 are 'a', 'd', 'b' if (decodedPerms[9] != 'a' || decodedPerms[10] != 'd' || decodedPerms[11] != 'b') @@ -682,7 +706,7 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data { Q_ASSERT(m_authorizationData.fileEncryptionKey.size() == 32); AES_KEY key = { }; - AES_set_decrypt_key(convertByteArrayToUcharPtr(m_authorizationData.fileEncryptionKey.data()), static_cast(m_authorizationData.fileEncryptionKey.size()) * 8, &key); + AES_set_decrypt_key(convertByteArrayToUcharPtr(m_authorizationData.fileEncryptionKey), static_cast(m_authorizationData.fileEncryptionKey.size()) * 8, &key); AES_data aes_data = prepareAES_data(data); if (!aes_data.paddedData.isEmpty()) @@ -745,6 +769,17 @@ QByteArray PDFStandardSecurityHandler::decrypt(const QByteArray& data, PDFObject return decryptUsingFilter(data, filter, reference); } +QByteArray PDFStandardSecurityHandler::decryptByFilter(const QByteArray& data, const QByteArray& filterName, PDFObjectReference reference) const +{ + auto it = m_cryptFilters.find(filterName); + if (it == m_cryptFilters.cend()) + { + throw PDFParserException(PDFTranslationContext::tr("Crypt filter '%1' not found.").arg(QString::fromLatin1(filterName))); + } + + return decryptUsingFilter(data, it->second, reference); +} + QByteArray PDFStandardSecurityHandler::createFileEncryptionKey(const QByteArray& password) const { QByteArray result; @@ -796,9 +831,10 @@ QByteArray PDFStandardSecurityHandler::createFileEncryptionKey(const QByteArray& break; } + case 5: case 6: { - // This function must not be called with revision 6 + // This function must not be called with revision 5/6 Q_ASSERT(false); break; } @@ -821,10 +857,10 @@ QByteArray PDFStandardSecurityHandler::createEntryValueU_r234(const QByteArray& case 2: { RC4_KEY key = { }; - RC4_set_key(&key, fileEncryptionKey.size(), reinterpret_cast(fileEncryptionKey.data())); + RC4_set_key(&key, fileEncryptionKey.size(), convertByteArrayToUcharPtr(fileEncryptionKey)); result.resize(static_cast(PDFPasswordPadding.size())); - RC4(&key, PDFPasswordPadding.size(), PDFPasswordPadding.data(), reinterpret_cast(result.data())); + RC4(&key, PDFPasswordPadding.size(), PDFPasswordPadding.data(), convertByteArrayToUcharPtr(result)); break; } @@ -840,10 +876,10 @@ QByteArray PDFStandardSecurityHandler::createEntryValueU_r234(const QByteArray& MD5_Final(hash.data(), &context); RC4_KEY key = { }; - RC4_set_key(&key, fileEncryptionKey.size(), reinterpret_cast(fileEncryptionKey.data())); + RC4_set_key(&key, fileEncryptionKey.size(), convertByteArrayToUcharPtr(fileEncryptionKey)); std::array encryptedHash; - RC4(&key, hash.size(), hash.data(), reinterpret_cast(encryptedHash.data())); + RC4(&key, hash.size(), hash.data(), encryptedHash.data()); QByteArray transformedKey = fileEncryptionKey; for (int i = 1; i <= 19; ++i) @@ -853,8 +889,8 @@ QByteArray PDFStandardSecurityHandler::createEntryValueU_r234(const QByteArray& transformedKey[j] = static_cast(fileEncryptionKey[j]) ^ static_cast(i); } - RC4_set_key(&key, transformedKey.size(), reinterpret_cast(transformedKey.data())); - RC4(&key, encryptedHash.size(), encryptedHash.data(), reinterpret_cast(encryptedHash.data())); + RC4_set_key(&key, transformedKey.size(), convertByteArrayToUcharPtr(transformedKey)); + RC4(&key, encryptedHash.size(), encryptedHash.data(), encryptedHash.data()); } // We do a hack here. In the PDF's specification, it is written, that arbitrary 16 bytes @@ -909,9 +945,9 @@ QByteArray PDFStandardSecurityHandler::createUserPasswordFromOwnerPassword(const case 2: { RC4_KEY key = { }; - RC4_set_key(&key, keyByteLength, reinterpret_cast(hash.data())); + RC4_set_key(&key, keyByteLength, hash.data()); result.resize(m_O.size()); - RC4(&key, m_O.size(), reinterpret_cast(m_O.data()), reinterpret_cast(result.data())); + RC4(&key, m_O.size(), convertByteArrayToUcharPtr(m_O), convertByteArrayToUcharPtr(result)); break; } @@ -931,8 +967,8 @@ QByteArray PDFStandardSecurityHandler::createUserPasswordFromOwnerPassword(const } RC4_KEY key = { }; - RC4_set_key(&key, transformedKey.size(), reinterpret_cast(transformedKey.data())); - RC4(&key, buffer.size(), reinterpret_cast(buffer.data()), reinterpret_cast(buffer.data())); + RC4_set_key(&key, transformedKey.size(), convertByteArrayToUcharPtr(transformedKey)); + RC4(&key, buffer.size(), convertByteArrayToUcharPtr(buffer), convertByteArrayToUcharPtr(buffer)); } result = buffer; @@ -976,7 +1012,7 @@ QByteArray PDFStandardSecurityHandler::createHash_r6(const QByteArray& input, co // First compute sha-256 digest of the input std::array inputDigest = { }; - SHA256(reinterpret_cast(input.data()), input.size(), inputDigest.data()); + SHA256(convertByteArrayToUcharPtr(input), input.size(), inputDigest.data()); std::vector K(inputDigest.cbegin(), inputDigest.cend()); // Fill the user key, if we use it @@ -1099,7 +1135,7 @@ QByteArray PDFStandardSecurityHandler::createHash_r6(const QByteArray& input, co // Clamp result to 32 bytes result.resize(32); - std::copy_n(K.data(), 32, reinterpret_cast(result.data())); + std::copy_n(K.data(), 32, result.data()); return result; } @@ -1130,6 +1166,7 @@ QByteArray PDFStandardSecurityHandler::adjustPassword(const QString& password) break; } + case 5: case 6: { // According to the PDF specification, use SASLprep profile for stringprep RFC 4013, please see these websites: diff --git a/PdfForQtLib/sources/pdfsecurityhandler.h b/PdfForQtLib/sources/pdfsecurityhandler.h index 53a4309..ed743b2 100644 --- a/PdfForQtLib/sources/pdfsecurityhandler.h +++ b/PdfForQtLib/sources/pdfsecurityhandler.h @@ -75,6 +75,21 @@ public: explicit PDFSecurityHandler() = default; virtual ~PDFSecurityHandler() = default; + static constexpr const char* IDENTITY_FILTER_NAME = "Identity"; + static constexpr const char* OBJECT_REFERENCE_DICTIONARY_NAME = "PdfForQt_ObjectReference"; + + enum class Permission : uint32_t + { + PrintLowResolution = (1 << 2), + Modify = (1 << 3), + CopyContent = (1 << 4), + ModifyInteractiveItems = (1 << 5), + ModifyFormFields = (1 << 8), + Accessibility = (1 << 9), + Assemble = (1 << 10), + PrintHighResolution = (1 << 11) + }; + enum class AuthorizationResult { UserAuthorized, @@ -118,6 +133,16 @@ public: /// \returns Decrypted object data virtual QByteArray decrypt(const QByteArray& data, PDFObjectReference reference, EncryptionScope encryptionScope) const = 0; + /// Decrypts data using specified filter. Throws exception, if filter is not found. + /// \param data Data to be decrypted + /// \param filterName Filter name to be used to decrypt the data + /// \param reference Reference object + virtual QByteArray decryptByFilter(const QByteArray& data, const QByteArray& filterName, PDFObjectReference reference) const = 0; + + /// Returns true, if given permission is allowed in the current authorization context. + /// If owner is authorized, then this function allways returns true. + virtual bool isAllowed(Permission permission) const = 0; + /// Returns true, if metadata are encrypted virtual bool isMetadataEncrypted() const = 0; @@ -158,7 +183,9 @@ public: virtual EncryptionMode getMode() const { return EncryptionMode::None; } virtual AuthorizationResult authenticate(const std::function&) override { return AuthorizationResult::OwnerAuthorized; } virtual QByteArray decrypt(const QByteArray& data, PDFObjectReference, EncryptionScope) const override { return data; } + virtual QByteArray decryptByFilter(const QByteArray& data, const QByteArray&, PDFObjectReference) const override { return data; } virtual bool isMetadataEncrypted() const override { return true; } + virtual bool isAllowed(Permission) const { return true; } }; /// Specifies the security using standard security handler (see PDF specification @@ -169,7 +196,9 @@ public: virtual EncryptionMode getMode() const { return EncryptionMode::Standard; } virtual AuthorizationResult authenticate(const std::function& getPasswordCallback) override; virtual QByteArray decrypt(const QByteArray& data, PDFObjectReference reference, EncryptionScope encryptionScope) const override; + virtual QByteArray decryptByFilter(const QByteArray& data, const QByteArray& filterName, PDFObjectReference reference) const override; virtual bool isMetadataEncrypted() const override { return m_encryptMetadata; } + virtual bool isAllowed(Permission permission) const { return m_authorizationData.authorizationResult == AuthorizationResult::OwnerAuthorized || (m_permissions & static_cast(permission)); } struct AuthorizationData { diff --git a/PdfForQtLib/sources/pdfstreamfilters.cpp b/PdfForQtLib/sources/pdfstreamfilters.cpp index f8b3b4f..0c20e2f 100644 --- a/PdfForQtLib/sources/pdfstreamfilters.cpp +++ b/PdfForQtLib/sources/pdfstreamfilters.cpp @@ -19,16 +19,21 @@ #include "pdfexception.h" #include "pdfconstants.h" #include "pdfparser.h" +#include "pdfsecurityhandler.h" #include namespace pdf { -QByteArray PDFAsciiHexDecodeFilter::apply(const QByteArray& data, const PDFObjectFetcher& objectFetcher, const PDFObject& parameters) const +QByteArray PDFAsciiHexDecodeFilter::apply(const QByteArray& data, + const PDFObjectFetcher& objectFetcher, + const PDFObject& parameters, + const PDFSecurityHandler* securityHandler) const { Q_UNUSED(objectFetcher); Q_UNUSED(parameters); + Q_UNUSED(securityHandler); const int indexOfEnd = data.indexOf('>'); const int size = (indexOfEnd == -1) ? data.size() : indexOfEnd; @@ -50,10 +55,14 @@ QByteArray PDFAsciiHexDecodeFilter::apply(const QByteArray& data, const PDFObjec return QByteArray::fromHex(QByteArray::fromRawData(data.constData(), size)); } -QByteArray PDFAscii85DecodeFilter::apply(const QByteArray& data, const PDFObjectFetcher& objectFetcher, const PDFObject& parameters) const +QByteArray PDFAscii85DecodeFilter::apply(const QByteArray& data, + const PDFObjectFetcher& objectFetcher, + const PDFObject& parameters, + const PDFSecurityHandler* securityHandler) const { Q_UNUSED(objectFetcher); Q_UNUSED(parameters); + Q_UNUSED(securityHandler); const unsigned char* dataBegin = reinterpret_cast(data.constData()); const unsigned char* dataEnd = reinterpret_cast(data.constData() + data.size()); @@ -333,8 +342,13 @@ uint32_t PDFLzwStreamDecoder::getCode() return code; } -QByteArray PDFLzwDecodeFilter::apply(const QByteArray& data, const PDFObjectFetcher& objectFetcher, const PDFObject& parameters) const +QByteArray PDFLzwDecodeFilter::apply(const QByteArray& data, + const PDFObjectFetcher& objectFetcher, + const PDFObject& parameters, + const PDFSecurityHandler* securityHandler) const { + Q_UNUSED(securityHandler); + uint32_t early = 1; const PDFObject& dereferencedParameters = objectFetcher(parameters); @@ -361,8 +375,13 @@ QByteArray PDFLzwDecodeFilter::apply(const QByteArray& data, const PDFObjectFetc return predictor.apply(decoder.decompress()); } -QByteArray PDFFlateDecodeFilter::apply(const QByteArray& data, const PDFObjectFetcher& objectFetcher, const PDFObject& parameters) const +QByteArray PDFFlateDecodeFilter::apply(const QByteArray& data, + const PDFObjectFetcher& objectFetcher, + const PDFObject& parameters, + const PDFSecurityHandler* securityHandler) const { + Q_UNUSED(securityHandler); + const PDFObject& dereferencedParameters = objectFetcher(parameters); if (dereferencedParameters.isDictionary()) { @@ -388,10 +407,14 @@ QByteArray PDFFlateDecodeFilter::apply(const QByteArray& data, const PDFObjectFe return predictor.apply(qUncompress(dataToUncompress)); } -QByteArray PDFRunLengthDecodeFilter::apply(const QByteArray& data, const PDFObjectFetcher& objectFetcher, const PDFObject& parameters) const +QByteArray PDFRunLengthDecodeFilter::apply(const QByteArray& data, + const PDFObjectFetcher& objectFetcher, + const PDFObject& parameters, + const PDFSecurityHandler* securityHandler) const { Q_UNUSED(objectFetcher); Q_UNUSED(parameters); + Q_UNUSED(securityHandler); QByteArray result; result.reserve(data.size() * 2); @@ -442,7 +465,7 @@ const PDFStreamFilter* PDFStreamFilterStorage::getFilter(const QByteArray& filte return nullptr; } -QByteArray PDFStreamFilterStorage::getDecodedStream(const PDFStream* stream, const PDFObjectFetcher& objectFetcher) +QByteArray PDFStreamFilterStorage::getDecodedStream(const PDFStream* stream, const PDFObjectFetcher& objectFetcher, const PDFSecurityHandler* securityHandler) { const PDFDictionary* dictionary = stream->getDictionary(); @@ -525,16 +548,16 @@ QByteArray PDFStreamFilterStorage::getDecodedStream(const PDFStream* stream, con if (streamFilter) { - result = streamFilter->apply(result, objectFetcher, streamFilterParameters); + result = streamFilter->apply(result, objectFetcher, streamFilterParameters, securityHandler); } } return result; } -QByteArray PDFStreamFilterStorage::getDecodedStream(const PDFStream* stream) +QByteArray PDFStreamFilterStorage::getDecodedStream(const PDFStream* stream, const PDFSecurityHandler* securityHandler) { - return getDecodedStream(stream, [](const PDFObject& object) -> const PDFObject& { return object; }); + return getDecodedStream(stream, [](const PDFObject& object) -> const PDFObject& { return object; }, securityHandler); } PDFStreamFilterStorage::PDFStreamFilterStorage() @@ -545,6 +568,7 @@ PDFStreamFilterStorage::PDFStreamFilterStorage() m_filters["LZWDecode"] = std::make_unique(); m_filters["FlateDecode"] = std::make_unique(); m_filters["RunLengthDecode"] = std::make_unique(); + m_filters["Crypt"] = std::make_unique(); m_abbreviations["AHx"] = "ASCIIHexDecode"; m_abbreviations["A85"] = "ASCII85Decode"; @@ -739,4 +763,36 @@ QByteArray PDFStreamPredictor::applyTIFFPredictor(const QByteArray& data) const return QByteArray(); } +QByteArray PDFCryptFilter::apply(const QByteArray& data, + const PDFObjectFetcher& objectFetcher, + const PDFObject& parameters, + const PDFSecurityHandler* securityHandler) const +{ + if (!securityHandler) + { + throw PDFParserException(PDFTranslationContext::tr("Security handler required, but not provided.")); + } + + PDFObjectReference objectReference; + QByteArray cryptFilterName = PDFSecurityHandler::IDENTITY_FILTER_NAME; + const PDFObject& dereferencedParameters = objectFetcher(parameters); + if (dereferencedParameters.isDictionary()) + { + const PDFDictionary* dictionary = dereferencedParameters.getDictionary(); + const PDFObject& cryptFilterNameObject = objectFetcher(dictionary->get("Name")); + if (cryptFilterNameObject.isName()) + { + cryptFilterName = cryptFilterNameObject.getString(); + } + + const PDFObject& objectReferenceObject = dictionary->get(PDFSecurityHandler::OBJECT_REFERENCE_DICTIONARY_NAME); + if (objectReferenceObject.isReference()) + { + objectReference = objectReferenceObject.getReference(); + } + } + + return securityHandler->decryptByFilter(data, cryptFilterName, objectReference); +} + } // namespace pdf diff --git a/PdfForQtLib/sources/pdfstreamfilters.h b/PdfForQtLib/sources/pdfstreamfilters.h index 01c730a..7a21a89 100644 --- a/PdfForQtLib/sources/pdfstreamfilters.h +++ b/PdfForQtLib/sources/pdfstreamfilters.h @@ -28,6 +28,7 @@ namespace pdf { class PDFStreamFilter; +class PDFSecurityHandler; using PDFObjectFetcher = std::function; @@ -44,12 +45,14 @@ public: /// Returns decoded data from the stream /// \param stream Stream containing the data /// \param objectFetcher Function which retrieves objects (for example, reads objects from reference) - static QByteArray getDecodedStream(const PDFStream* stream, const PDFObjectFetcher& objectFetcher); + /// \param securityHandler Security handler for Crypt filters + static QByteArray getDecodedStream(const PDFStream* stream, const PDFObjectFetcher& objectFetcher, const PDFSecurityHandler* securityHandler); /// Returns decoded data from the stream, without object fetching /// \param stream Stream containing the data /// \param objectFetcher Function which retrieves objects (for example, reads objects from reference) - static QByteArray getDecodedStream(const PDFStream* stream); + /// \param securityHandler Security handler for Crypt filters + static QByteArray getDecodedStream(const PDFStream* stream, const PDFSecurityHandler* securityHandler); private: explicit PDFStreamFilterStorage(); @@ -124,14 +127,14 @@ public: /// \param data Stream data to be decoded /// \param objectFetcher Function which retrieves objects (for example, reads objects from reference) /// \param parameters Stream parameters - virtual QByteArray apply(const QByteArray& data, const PDFObjectFetcher& objectFetcher, const PDFObject& parameters) const = 0; + virtual QByteArray apply(const QByteArray& data, const PDFObjectFetcher& objectFetcher, const PDFObject& parameters, const PDFSecurityHandler* securityHandler) const = 0; /// Apply without object fetcher - it assumes no references exists in the streams dictionary /// \param data Stream data to be decoded /// \param parameters Stream parameters - inline QByteArray apply(const QByteArray& data, const PDFObject& parameters) const + inline QByteArray apply(const QByteArray& data, const PDFObject& parameters, const PDFSecurityHandler* securityHandler) const { - return apply(data, [](const PDFObject& object) -> const PDFObject& { return object; }, parameters); + return apply(data, [](const PDFObject& object) -> const PDFObject& { return object; }, parameters, securityHandler); } }; @@ -141,7 +144,10 @@ public: explicit PDFAsciiHexDecodeFilter() = default; virtual ~PDFAsciiHexDecodeFilter() override = default; - virtual QByteArray apply(const QByteArray& data, const PDFObjectFetcher& objectFetcher, const PDFObject& parameters) const override; + virtual QByteArray apply(const QByteArray& data, + const PDFObjectFetcher& objectFetcher, + const PDFObject& parameters, + const PDFSecurityHandler* securityHandler) const override; }; class PDFFORQTLIBSHARED_EXPORT PDFAscii85DecodeFilter : public PDFStreamFilter @@ -150,7 +156,10 @@ public: explicit PDFAscii85DecodeFilter() = default; virtual ~PDFAscii85DecodeFilter() override = default; - virtual QByteArray apply(const QByteArray& data, const PDFObjectFetcher& objectFetcher, const PDFObject& parameters) const override; + virtual QByteArray apply(const QByteArray& data, + const PDFObjectFetcher& objectFetcher, + const PDFObject& parameters, + const PDFSecurityHandler* securityHandler) const override; }; class PDFFORQTLIBSHARED_EXPORT PDFLzwDecodeFilter : public PDFStreamFilter @@ -159,7 +168,10 @@ public: explicit PDFLzwDecodeFilter() = default; virtual ~PDFLzwDecodeFilter() override = default; - virtual QByteArray apply(const QByteArray& data, const PDFObjectFetcher& objectFetcher, const PDFObject& parameters) const override; + virtual QByteArray apply(const QByteArray& data, + const PDFObjectFetcher& objectFetcher, + const PDFObject& parameters, + const PDFSecurityHandler* securityHandler) const override; }; class PDFFORQTLIBSHARED_EXPORT PDFFlateDecodeFilter : public PDFStreamFilter @@ -168,7 +180,10 @@ public: explicit PDFFlateDecodeFilter() = default; virtual ~PDFFlateDecodeFilter() override = default; - virtual QByteArray apply(const QByteArray& data, const PDFObjectFetcher& objectFetcher, const PDFObject& parameters) const override; + virtual QByteArray apply(const QByteArray& data, + const PDFObjectFetcher& objectFetcher, + const PDFObject& parameters, + const PDFSecurityHandler* securityHandler) const override; }; class PDFFORQTLIBSHARED_EXPORT PDFRunLengthDecodeFilter : public PDFStreamFilter @@ -177,7 +192,22 @@ public: explicit PDFRunLengthDecodeFilter() = default; virtual ~PDFRunLengthDecodeFilter() override = default; - virtual QByteArray apply(const QByteArray& data, const PDFObjectFetcher& objectFetcher, const PDFObject& parameters) const override; + virtual QByteArray apply(const QByteArray& data, + const PDFObjectFetcher& objectFetcher, + const PDFObject& parameters, + const PDFSecurityHandler* securityHandler) const override; +}; + +class PDFFORQTLIBSHARED_EXPORT PDFCryptFilter : public PDFStreamFilter +{ +public: + explicit PDFCryptFilter() = default; + virtual ~PDFCryptFilter() override = default; + + virtual QByteArray apply(const QByteArray& data, + const PDFObjectFetcher& objectFetcher, + const PDFObject& parameters, + const PDFSecurityHandler* securityHandler) const override; }; } // namespace pdf diff --git a/PdfForQtLib/sources/pdfxreftable.cpp b/PdfForQtLib/sources/pdfxreftable.cpp index deffe8a..365c1c9 100644 --- a/PdfForQtLib/sources/pdfxreftable.cpp +++ b/PdfForQtLib/sources/pdfxreftable.cpp @@ -248,7 +248,7 @@ void PDFXRefTable::readXRefTable(PDFParsingContext* context, const QByteArray& b const int columnGenerationNumberOrObjectIndexBytes = wArray[2]; const size_t blockCount = indexArray.size() / 2; - QByteArray data = PDFStreamFilterStorage::getDecodedStream(crossReferenceStream); + QByteArray data = PDFStreamFilterStorage::getDecodedStream(crossReferenceStream, nullptr); QDataStream dataStream(&data, QIODevice::ReadOnly); dataStream.setByteOrder(QDataStream::BigEndian); diff --git a/UnitTests/tst_lexicalanalyzertest.cpp b/UnitTests/tst_lexicalanalyzertest.cpp index 38cb767..dd3408e 100644 --- a/UnitTests/tst_lexicalanalyzertest.cpp +++ b/UnitTests/tst_lexicalanalyzertest.cpp @@ -309,7 +309,7 @@ void LexicalAnalyzerTest::test_lzw_filter() // This example is from PDF 1.7 Reference QByteArray byteArray = QByteArray::fromHex("800B6050220C0C8501"); pdf::PDFLzwDecodeFilter filter; - QByteArray decoded = filter.apply(byteArray, nullptr, pdf::PDFObject()); + QByteArray decoded = filter.apply(byteArray, nullptr, pdf::PDFObject(), nullptr); QByteArray valid = "-----A---B"; QCOMPARE(decoded, valid);