Crypt filters & revision 5 encryption

This commit is contained in:
Jakub Melka 2019-08-13 15:48:01 +02:00
parent 2623ef6825
commit 6c261241ad
8 changed files with 209 additions and 57 deletions

View File

@ -42,7 +42,7 @@ static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_TRAPPED_UNKNOWN = "Unknown"
QByteArray PDFDocument::getDecodedStream(const PDFStream* stream) const 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 const PDFDictionary* PDFDocument::getTrailerDictionary() const

View File

@ -378,7 +378,7 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer)
objectStreams.insert(entry.objectStream); 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) if (m_result != Result::OK)
{ {
@ -419,7 +419,7 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer)
const PDFInteger n = nObject.getInteger(); const PDFInteger n = nObject.getInteger();
const PDFInteger first = firstObject.getInteger(); const PDFInteger first = firstObject.getInteger();
QByteArray objectStreamData = PDFStreamFilterStorage::getDecodedStream(objectStream); QByteArray objectStreamData = PDFStreamFilterStorage::getDecodedStream(objectStream, securityHandler.data());
PDFParsingContext::PDFParsingContextGuard guard(&context, objectStreamReference); PDFParsingContext::PDFParsingContextGuard guard(&context, objectStreamReference);
PDFParser parser(objectStreamData, &context, PDFParser::AllowStreams); PDFParser parser(objectStreamData, &context, PDFParser::AllowStreams);

View File

@ -162,9 +162,20 @@ void PDFDecryptObjectVisitor::visitStream(const PDFStream* stream)
PDFObject dictionaryObject = m_objectStack.back(); PDFObject dictionaryObject = m_objectStack.back();
m_objectStack.pop_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()); 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<PDFStream>(qMove(decryptedDictionary), qMove(decryptedData)))); m_objectStack.push_back(PDFObject::createStream(std::make_shared<PDFStream>(qMove(decryptedDictionary), qMove(decryptedData))));
} }
@ -287,7 +298,7 @@ PDFSecurityHandlerPointer PDFSecurityHandler::createSecurityHandler(const PDFObj
// Add "Identity" filter to the filters // Add "Identity" filter to the filters
CryptFilter identityFilter; CryptFilter identityFilter;
identityFilter.type = CryptFilterType::Identity; identityFilter.type = CryptFilterType::Identity;
handler.m_cryptFilters["Identity"] = identityFilter; handler.m_cryptFilters[IDENTITY_FILTER_NAME] = identityFilter;
if (V == 4 || V == 5) if (V == 4 || V == 5)
{ {
@ -365,8 +376,8 @@ PDFSecurityHandlerPointer PDFSecurityHandler::createSecurityHandler(const PDFObj
return it->second; return it->second;
}; };
handler.m_filterStreams = resolveFilter(getName(dictionary, "StmF", false, "Identity")); handler.m_filterStreams = resolveFilter(getName(dictionary, "StmF", false, IDENTITY_FILTER_NAME));
handler.m_filterStrings = resolveFilter(getName(dictionary, "StrF", false, "Identity")); handler.m_filterStrings = resolveFilter(getName(dictionary, "StrF", false, IDENTITY_FILTER_NAME));
if (dictionary->hasKey("EFF")) if (dictionary->hasKey("EFF"))
{ {
@ -381,7 +392,7 @@ PDFSecurityHandlerPointer PDFSecurityHandler::createSecurityHandler(const PDFObj
} }
int R = getInt(dictionary, "R", true); 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)); 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; return result;
}; };
handler.m_O = readByteArray("O", (R != 6) ? 32 : 48); handler.m_O = readByteArray("O", (R != 6 && R != 5) ? 32 : 48);
handler.m_U = readByteArray("U", (R != 6) ? 32 : 48); handler.m_U = readByteArray("U", (R != 6 && R != 5) ? 32 : 48);
handler.m_permissions = static_cast<uint32_t>(static_cast<int>(getInt(dictionary, "P", true))); handler.m_permissions = static_cast<uint32_t>(static_cast<int>(getInt(dictionary, "P", true)));
if (R == 6) if (R == 6 || R == 5)
{ {
handler.m_OE = readByteArray("OE", 32); handler.m_OE = readByteArray("OE", 32);
handler.m_UE = readByteArray("UE", 32); handler.m_UE = readByteArray("UE", 32);
@ -482,29 +493,42 @@ PDFSecurityHandler::AuthorizationResult PDFStandardSecurityHandler::authenticate
break; break;
} }
case 5:
case 6: case 6:
{ {
UserOwnerData_r6 userData = parseParts(m_U); UserOwnerData_r6 userData = parseParts(m_U);
UserOwnerData_r6 ownerData = parseParts(m_O); 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 // Try to authorize owner password
{ {
QByteArray inputData = password + ownerData.validationSalt + m_U; 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) if (hash == ownerData.hash)
{ {
// We have authorized owner access. Now we must calculate the owner encryption key // We have authorized owner access. Now we must calculate the owner encryption key
QByteArray fileEncryptionKeyInputData = password + ownerData.keySalt + m_U; 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); Q_ASSERT(fileEncryptionDecryptionKey.size() == 32);
AES_KEY key = { }; AES_KEY key = { };
AES_set_decrypt_key(reinterpret_cast<const unsigned char*>(fileEncryptionDecryptionKey.data()), fileEncryptionDecryptionKey.size() * 8, &key); AES_set_decrypt_key(convertByteArrayToUcharPtr(fileEncryptionDecryptionKey), fileEncryptionDecryptionKey.size() * 8, &key);
unsigned char aesInitializationVector[AES_BLOCK_SIZE] = { }; unsigned char aesInitializationVector[AES_BLOCK_SIZE] = { };
m_authorizationData.fileEncryptionKey.resize(m_OE.size()); m_authorizationData.fileEncryptionKey.resize(m_OE.size());
AES_cbc_encrypt(reinterpret_cast<const unsigned char*>(m_OE.data()), reinterpret_cast<unsigned char*>(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; m_authorizationData.authorizationResult = AuthorizationResult::OwnerAuthorized;
} }
@ -514,19 +538,19 @@ PDFSecurityHandler::AuthorizationResult PDFStandardSecurityHandler::authenticate
if (!m_authorizationData.isAuthorized()) if (!m_authorizationData.isAuthorized())
{ {
QByteArray inputData = password + userData.validationSalt; QByteArray inputData = password + userData.validationSalt;
QByteArray hash = createHash_r6(inputData, password, false); QByteArray hash = createHash_r56(inputData, password, false);
if (hash == userData.hash) if (hash == userData.hash)
{ {
QByteArray fileEncryptionKeyInputData = password + userData.keySalt; QByteArray fileEncryptionKeyInputData = password + userData.keySalt;
QByteArray fileEncryptionDecryptionKey = createHash_r6(fileEncryptionKeyInputData, password, false); QByteArray fileEncryptionDecryptionKey = createHash_r56(fileEncryptionKeyInputData, password, false);
Q_ASSERT(fileEncryptionDecryptionKey.size() == 32); Q_ASSERT(fileEncryptionDecryptionKey.size() == 32);
AES_KEY key = { }; AES_KEY key = { };
AES_set_decrypt_key(reinterpret_cast<const unsigned char*>(fileEncryptionDecryptionKey.data()), fileEncryptionDecryptionKey.size() * 8, &key); AES_set_decrypt_key(convertByteArrayToUcharPtr(fileEncryptionDecryptionKey), fileEncryptionDecryptionKey.size() * 8, &key);
unsigned char aesInitializationVector[AES_BLOCK_SIZE] = { }; unsigned char aesInitializationVector[AES_BLOCK_SIZE] = { };
m_authorizationData.fileEncryptionKey.resize(m_UE.size()); m_authorizationData.fileEncryptionKey.resize(m_UE.size());
AES_cbc_encrypt(reinterpret_cast<const unsigned char*>(m_UE.data()), reinterpret_cast<unsigned char*>(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 // We have authorized owner access
m_authorizationData.authorizationResult = AuthorizationResult::UserAuthorized; 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. // According the PDF specification, we must also check, if flags are not manipulated.
Q_ASSERT(m_Perms.size() == AES_BLOCK_SIZE); Q_ASSERT(m_Perms.size() == AES_BLOCK_SIZE);
AES_KEY key = { }; AES_KEY key = { };
AES_set_decrypt_key(reinterpret_cast<const unsigned char*>(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)); QByteArray decodedPerms(m_Perms.size(), char(0));
AES_ecb_encrypt(reinterpret_cast<const unsigned char*>(m_Perms.data()), reinterpret_cast<unsigned char*>(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' // 1) Checks, if bytes 9, 10, 11 are 'a', 'd', 'b'
if (decodedPerms[9] != 'a' || decodedPerms[10] != 'd' || decodedPerms[11] != '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); Q_ASSERT(m_authorizationData.fileEncryptionKey.size() == 32);
AES_KEY key = { }; AES_KEY key = { };
AES_set_decrypt_key(convertByteArrayToUcharPtr(m_authorizationData.fileEncryptionKey.data()), static_cast<int>(m_authorizationData.fileEncryptionKey.size()) * 8, &key); AES_set_decrypt_key(convertByteArrayToUcharPtr(m_authorizationData.fileEncryptionKey), static_cast<int>(m_authorizationData.fileEncryptionKey.size()) * 8, &key);
AES_data aes_data = prepareAES_data(data); AES_data aes_data = prepareAES_data(data);
if (!aes_data.paddedData.isEmpty()) if (!aes_data.paddedData.isEmpty())
@ -745,6 +769,17 @@ QByteArray PDFStandardSecurityHandler::decrypt(const QByteArray& data, PDFObject
return decryptUsingFilter(data, filter, reference); 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 PDFStandardSecurityHandler::createFileEncryptionKey(const QByteArray& password) const
{ {
QByteArray result; QByteArray result;
@ -796,9 +831,10 @@ QByteArray PDFStandardSecurityHandler::createFileEncryptionKey(const QByteArray&
break; break;
} }
case 5:
case 6: case 6:
{ {
// This function must not be called with revision 6 // This function must not be called with revision 5/6
Q_ASSERT(false); Q_ASSERT(false);
break; break;
} }
@ -821,10 +857,10 @@ QByteArray PDFStandardSecurityHandler::createEntryValueU_r234(const QByteArray&
case 2: case 2:
{ {
RC4_KEY key = { }; RC4_KEY key = { };
RC4_set_key(&key, fileEncryptionKey.size(), reinterpret_cast<const unsigned char*>(fileEncryptionKey.data())); RC4_set_key(&key, fileEncryptionKey.size(), convertByteArrayToUcharPtr(fileEncryptionKey));
result.resize(static_cast<int>(PDFPasswordPadding.size())); result.resize(static_cast<int>(PDFPasswordPadding.size()));
RC4(&key, PDFPasswordPadding.size(), PDFPasswordPadding.data(), reinterpret_cast<unsigned char*>(result.data())); RC4(&key, PDFPasswordPadding.size(), PDFPasswordPadding.data(), convertByteArrayToUcharPtr(result));
break; break;
} }
@ -840,10 +876,10 @@ QByteArray PDFStandardSecurityHandler::createEntryValueU_r234(const QByteArray&
MD5_Final(hash.data(), &context); MD5_Final(hash.data(), &context);
RC4_KEY key = { }; RC4_KEY key = { };
RC4_set_key(&key, fileEncryptionKey.size(), reinterpret_cast<const unsigned char*>(fileEncryptionKey.data())); RC4_set_key(&key, fileEncryptionKey.size(), convertByteArrayToUcharPtr(fileEncryptionKey));
std::array<uint8_t, MD5_DIGEST_LENGTH> encryptedHash; std::array<uint8_t, MD5_DIGEST_LENGTH> encryptedHash;
RC4(&key, hash.size(), hash.data(), reinterpret_cast<unsigned char*>(encryptedHash.data())); RC4(&key, hash.size(), hash.data(), encryptedHash.data());
QByteArray transformedKey = fileEncryptionKey; QByteArray transformedKey = fileEncryptionKey;
for (int i = 1; i <= 19; ++i) for (int i = 1; i <= 19; ++i)
@ -853,8 +889,8 @@ QByteArray PDFStandardSecurityHandler::createEntryValueU_r234(const QByteArray&
transformedKey[j] = static_cast<uint8_t>(fileEncryptionKey[j]) ^ static_cast<uint8_t>(i); transformedKey[j] = static_cast<uint8_t>(fileEncryptionKey[j]) ^ static_cast<uint8_t>(i);
} }
RC4_set_key(&key, transformedKey.size(), reinterpret_cast<const unsigned char*>(transformedKey.data())); RC4_set_key(&key, transformedKey.size(), convertByteArrayToUcharPtr(transformedKey));
RC4(&key, encryptedHash.size(), encryptedHash.data(), reinterpret_cast<unsigned char*>(encryptedHash.data())); 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 // 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: case 2:
{ {
RC4_KEY key = { }; RC4_KEY key = { };
RC4_set_key(&key, keyByteLength, reinterpret_cast<const unsigned char*>(hash.data())); RC4_set_key(&key, keyByteLength, hash.data());
result.resize(m_O.size()); result.resize(m_O.size());
RC4(&key, m_O.size(), reinterpret_cast<const unsigned char*>(m_O.data()), reinterpret_cast<unsigned char*>(result.data())); RC4(&key, m_O.size(), convertByteArrayToUcharPtr(m_O), convertByteArrayToUcharPtr(result));
break; break;
} }
@ -931,8 +967,8 @@ QByteArray PDFStandardSecurityHandler::createUserPasswordFromOwnerPassword(const
} }
RC4_KEY key = { }; RC4_KEY key = { };
RC4_set_key(&key, transformedKey.size(), reinterpret_cast<const unsigned char*>(transformedKey.data())); RC4_set_key(&key, transformedKey.size(), convertByteArrayToUcharPtr(transformedKey));
RC4(&key, buffer.size(), reinterpret_cast<const unsigned char*>(buffer.data()), reinterpret_cast<unsigned char*>(buffer.data())); RC4(&key, buffer.size(), convertByteArrayToUcharPtr(buffer), convertByteArrayToUcharPtr(buffer));
} }
result = buffer; result = buffer;
@ -976,7 +1012,7 @@ QByteArray PDFStandardSecurityHandler::createHash_r6(const QByteArray& input, co
// First compute sha-256 digest of the input // First compute sha-256 digest of the input
std::array<uint8_t, SHA256_DIGEST_LENGTH> inputDigest = { }; std::array<uint8_t, SHA256_DIGEST_LENGTH> inputDigest = { };
SHA256(reinterpret_cast<const unsigned char*>(input.data()), input.size(), inputDigest.data()); SHA256(convertByteArrayToUcharPtr(input), input.size(), inputDigest.data());
std::vector<uint8_t> K(inputDigest.cbegin(), inputDigest.cend()); std::vector<uint8_t> K(inputDigest.cbegin(), inputDigest.cend());
// Fill the user key, if we use it // 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 // Clamp result to 32 bytes
result.resize(32); result.resize(32);
std::copy_n(K.data(), 32, reinterpret_cast<unsigned char*>(result.data())); std::copy_n(K.data(), 32, result.data());
return result; return result;
} }
@ -1130,6 +1166,7 @@ QByteArray PDFStandardSecurityHandler::adjustPassword(const QString& password)
break; break;
} }
case 5:
case 6: case 6:
{ {
// According to the PDF specification, use SASLprep profile for stringprep RFC 4013, please see these websites: // According to the PDF specification, use SASLprep profile for stringprep RFC 4013, please see these websites:

View File

@ -75,6 +75,21 @@ public:
explicit PDFSecurityHandler() = default; explicit PDFSecurityHandler() = default;
virtual ~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 enum class AuthorizationResult
{ {
UserAuthorized, UserAuthorized,
@ -118,6 +133,16 @@ public:
/// \returns Decrypted object data /// \returns Decrypted object data
virtual QByteArray decrypt(const QByteArray& data, PDFObjectReference reference, EncryptionScope encryptionScope) const = 0; 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 /// Returns true, if metadata are encrypted
virtual bool isMetadataEncrypted() const = 0; virtual bool isMetadataEncrypted() const = 0;
@ -158,7 +183,9 @@ public:
virtual EncryptionMode getMode() const { return EncryptionMode::None; } virtual EncryptionMode getMode() const { return EncryptionMode::None; }
virtual AuthorizationResult authenticate(const std::function<QString(bool*)>&) override { return AuthorizationResult::OwnerAuthorized; } virtual AuthorizationResult authenticate(const std::function<QString(bool*)>&) override { return AuthorizationResult::OwnerAuthorized; }
virtual QByteArray decrypt(const QByteArray& data, PDFObjectReference, EncryptionScope) const override { return data; } 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 isMetadataEncrypted() const override { return true; }
virtual bool isAllowed(Permission) const { return true; }
}; };
/// Specifies the security using standard security handler (see PDF specification /// Specifies the security using standard security handler (see PDF specification
@ -169,7 +196,9 @@ public:
virtual EncryptionMode getMode() const { return EncryptionMode::Standard; } virtual EncryptionMode getMode() const { return EncryptionMode::Standard; }
virtual AuthorizationResult authenticate(const std::function<QString(bool*)>& getPasswordCallback) override; virtual AuthorizationResult authenticate(const std::function<QString(bool*)>& getPasswordCallback) override;
virtual QByteArray decrypt(const QByteArray& data, PDFObjectReference reference, EncryptionScope encryptionScope) const 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 isMetadataEncrypted() const override { return m_encryptMetadata; }
virtual bool isAllowed(Permission permission) const { return m_authorizationData.authorizationResult == AuthorizationResult::OwnerAuthorized || (m_permissions & static_cast<uint32_t>(permission)); }
struct AuthorizationData struct AuthorizationData
{ {

View File

@ -19,16 +19,21 @@
#include "pdfexception.h" #include "pdfexception.h"
#include "pdfconstants.h" #include "pdfconstants.h"
#include "pdfparser.h" #include "pdfparser.h"
#include "pdfsecurityhandler.h"
#include <QtEndian> #include <QtEndian>
namespace pdf 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(objectFetcher);
Q_UNUSED(parameters); Q_UNUSED(parameters);
Q_UNUSED(securityHandler);
const int indexOfEnd = data.indexOf('>'); const int indexOfEnd = data.indexOf('>');
const int size = (indexOfEnd == -1) ? data.size() : indexOfEnd; 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)); 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(objectFetcher);
Q_UNUSED(parameters); Q_UNUSED(parameters);
Q_UNUSED(securityHandler);
const unsigned char* dataBegin = reinterpret_cast<const unsigned char*>(data.constData()); const unsigned char* dataBegin = reinterpret_cast<const unsigned char*>(data.constData());
const unsigned char* dataEnd = reinterpret_cast<const unsigned char*>(data.constData() + data.size()); const unsigned char* dataEnd = reinterpret_cast<const unsigned char*>(data.constData() + data.size());
@ -333,8 +342,13 @@ uint32_t PDFLzwStreamDecoder::getCode()
return code; 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; uint32_t early = 1;
const PDFObject& dereferencedParameters = objectFetcher(parameters); const PDFObject& dereferencedParameters = objectFetcher(parameters);
@ -361,8 +375,13 @@ QByteArray PDFLzwDecodeFilter::apply(const QByteArray& data, const PDFObjectFetc
return predictor.apply(decoder.decompress()); 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); const PDFObject& dereferencedParameters = objectFetcher(parameters);
if (dereferencedParameters.isDictionary()) if (dereferencedParameters.isDictionary())
{ {
@ -388,10 +407,14 @@ QByteArray PDFFlateDecodeFilter::apply(const QByteArray& data, const PDFObjectFe
return predictor.apply(qUncompress(dataToUncompress)); 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(objectFetcher);
Q_UNUSED(parameters); Q_UNUSED(parameters);
Q_UNUSED(securityHandler);
QByteArray result; QByteArray result;
result.reserve(data.size() * 2); result.reserve(data.size() * 2);
@ -442,7 +465,7 @@ const PDFStreamFilter* PDFStreamFilterStorage::getFilter(const QByteArray& filte
return nullptr; 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(); const PDFDictionary* dictionary = stream->getDictionary();
@ -525,16 +548,16 @@ QByteArray PDFStreamFilterStorage::getDecodedStream(const PDFStream* stream, con
if (streamFilter) if (streamFilter)
{ {
result = streamFilter->apply(result, objectFetcher, streamFilterParameters); result = streamFilter->apply(result, objectFetcher, streamFilterParameters, securityHandler);
} }
} }
return result; 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() PDFStreamFilterStorage::PDFStreamFilterStorage()
@ -545,6 +568,7 @@ PDFStreamFilterStorage::PDFStreamFilterStorage()
m_filters["LZWDecode"] = std::make_unique<PDFLzwDecodeFilter>(); m_filters["LZWDecode"] = std::make_unique<PDFLzwDecodeFilter>();
m_filters["FlateDecode"] = std::make_unique<PDFFlateDecodeFilter>(); m_filters["FlateDecode"] = std::make_unique<PDFFlateDecodeFilter>();
m_filters["RunLengthDecode"] = std::make_unique<PDFRunLengthDecodeFilter>(); m_filters["RunLengthDecode"] = std::make_unique<PDFRunLengthDecodeFilter>();
m_filters["Crypt"] = std::make_unique<PDFCryptFilter>();
m_abbreviations["AHx"] = "ASCIIHexDecode"; m_abbreviations["AHx"] = "ASCIIHexDecode";
m_abbreviations["A85"] = "ASCII85Decode"; m_abbreviations["A85"] = "ASCII85Decode";
@ -739,4 +763,36 @@ QByteArray PDFStreamPredictor::applyTIFFPredictor(const QByteArray& data) const
return QByteArray(); 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 } // namespace pdf

View File

@ -28,6 +28,7 @@
namespace pdf namespace pdf
{ {
class PDFStreamFilter; class PDFStreamFilter;
class PDFSecurityHandler;
using PDFObjectFetcher = std::function<const PDFObject&(const PDFObject&)>; using PDFObjectFetcher = std::function<const PDFObject&(const PDFObject&)>;
@ -44,12 +45,14 @@ public:
/// Returns decoded data from the stream /// Returns decoded data from the stream
/// \param stream Stream containing the data /// \param stream Stream containing the data
/// \param objectFetcher Function which retrieves objects (for example, reads objects from reference) /// \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 /// Returns decoded data from the stream, without object fetching
/// \param stream Stream containing the data /// \param stream Stream containing the data
/// \param objectFetcher Function which retrieves objects (for example, reads objects from reference) /// \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: private:
explicit PDFStreamFilterStorage(); explicit PDFStreamFilterStorage();
@ -124,14 +127,14 @@ public:
/// \param data Stream data to be decoded /// \param data Stream data to be decoded
/// \param objectFetcher Function which retrieves objects (for example, reads objects from reference) /// \param objectFetcher Function which retrieves objects (for example, reads objects from reference)
/// \param parameters Stream parameters /// \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 /// Apply without object fetcher - it assumes no references exists in the streams dictionary
/// \param data Stream data to be decoded /// \param data Stream data to be decoded
/// \param parameters Stream parameters /// \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; explicit PDFAsciiHexDecodeFilter() = default;
virtual ~PDFAsciiHexDecodeFilter() override = 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 class PDFFORQTLIBSHARED_EXPORT PDFAscii85DecodeFilter : public PDFStreamFilter
@ -150,7 +156,10 @@ public:
explicit PDFAscii85DecodeFilter() = default; explicit PDFAscii85DecodeFilter() = default;
virtual ~PDFAscii85DecodeFilter() override = 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 class PDFFORQTLIBSHARED_EXPORT PDFLzwDecodeFilter : public PDFStreamFilter
@ -159,7 +168,10 @@ public:
explicit PDFLzwDecodeFilter() = default; explicit PDFLzwDecodeFilter() = default;
virtual ~PDFLzwDecodeFilter() override = 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 class PDFFORQTLIBSHARED_EXPORT PDFFlateDecodeFilter : public PDFStreamFilter
@ -168,7 +180,10 @@ public:
explicit PDFFlateDecodeFilter() = default; explicit PDFFlateDecodeFilter() = default;
virtual ~PDFFlateDecodeFilter() override = 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 class PDFFORQTLIBSHARED_EXPORT PDFRunLengthDecodeFilter : public PDFStreamFilter
@ -177,7 +192,22 @@ public:
explicit PDFRunLengthDecodeFilter() = default; explicit PDFRunLengthDecodeFilter() = default;
virtual ~PDFRunLengthDecodeFilter() override = 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 } // namespace pdf

View File

@ -248,7 +248,7 @@ void PDFXRefTable::readXRefTable(PDFParsingContext* context, const QByteArray& b
const int columnGenerationNumberOrObjectIndexBytes = wArray[2]; const int columnGenerationNumberOrObjectIndexBytes = wArray[2];
const size_t blockCount = indexArray.size() / 2; const size_t blockCount = indexArray.size() / 2;
QByteArray data = PDFStreamFilterStorage::getDecodedStream(crossReferenceStream); QByteArray data = PDFStreamFilterStorage::getDecodedStream(crossReferenceStream, nullptr);
QDataStream dataStream(&data, QIODevice::ReadOnly); QDataStream dataStream(&data, QIODevice::ReadOnly);
dataStream.setByteOrder(QDataStream::BigEndian); dataStream.setByteOrder(QDataStream::BigEndian);

View File

@ -309,7 +309,7 @@ void LexicalAnalyzerTest::test_lzw_filter()
// This example is from PDF 1.7 Reference // This example is from PDF 1.7 Reference
QByteArray byteArray = QByteArray::fromHex("800B6050220C0C8501"); QByteArray byteArray = QByteArray::fromHex("800B6050220C0C8501");
pdf::PDFLzwDecodeFilter filter; pdf::PDFLzwDecodeFilter filter;
QByteArray decoded = filter.apply(byteArray, nullptr, pdf::PDFObject()); QByteArray decoded = filter.apply(byteArray, nullptr, pdf::PDFObject(), nullptr);
QByteArray valid = "-----A---B"; QByteArray valid = "-----A---B";
QCOMPARE(decoded, valid); QCOMPARE(decoded, valid);