Save document with encryption

This commit is contained in:
Jakub Melka 2021-05-15 19:08:45 +02:00
parent aa059ea1f6
commit 9539faf548
5 changed files with 364 additions and 62 deletions

View File

@ -223,7 +223,8 @@ PDFOperationResult PDFDocumentWriter::write(QIODevice* device, const PDFDocument
const PDFObjectStorage& storage = document->getStorage(); const PDFObjectStorage& storage = document->getStorage();
const PDFObjectStorage::PDFObjects& objects = storage.getObjects(); const PDFObjectStorage::PDFObjects& objects = storage.getObjects();
const size_t objectCount = objects.size(); const size_t objectCount = objects.size();
if (storage.getSecurityHandler()->getMode() != EncryptionMode::None) const bool isEncrypted = storage.getSecurityHandler()->getMode() != EncryptionMode::None;
if (!storage.getSecurityHandler()->isEncryptionAllowed())
{ {
return tr("Writing of encrypted documents is not supported."); return tr("Writing of encrypted documents is not supported.");
} }
@ -238,6 +239,13 @@ PDFOperationResult PDFDocumentWriter::write(QIODevice* device, const PDFDocument
writeCRLF(device); writeCRLF(device);
writeCRLF(device); writeCRLF(device);
PDFObjectReference encryptObjectReference;
PDFObject encryptObject = document->getTrailerDictionary()->get("Encrypt");
if (encryptObject.isReference())
{
encryptObjectReference = encryptObject.getReference();
}
// Write objects // Write objects
std::vector<PDFInteger> offsets(objectCount, -1); std::vector<PDFInteger> offsets(objectCount, -1);
for (size_t i = 0; i < objectCount; ++i) for (size_t i = 0; i < objectCount; ++i)
@ -251,10 +259,28 @@ PDFOperationResult PDFDocumentWriter::write(QIODevice* device, const PDFDocument
// Jakub Melka: we must mark actual position of object // Jakub Melka: we must mark actual position of object
offsets[i] = device->pos(); offsets[i] = device->pos();
PDFWriteObjectVisitor visitor(device); if (isEncrypted)
writeObjectHeader(device, PDFObjectReference(i, entry.generation)); {
entry.object.accept(&visitor); PDFObjectReference reference(i, entry.generation);
writeObjectFooter(device); PDFObject objectToWrite = entry.object;
if (reference != encryptObjectReference)
{
objectToWrite = storage.getSecurityHandler()->encryptObject(objectToWrite, reference);
}
PDFWriteObjectVisitor visitor(device);
writeObjectHeader(device, reference);
objectToWrite.accept(&visitor);
writeObjectFooter(device);
}
else
{
PDFWriteObjectVisitor visitor(device);
writeObjectHeader(device, PDFObjectReference(i, entry.generation));
entry.object.accept(&visitor);
writeObjectFooter(device);
}
} }
// Write cross-reference table // Write cross-reference table

View File

@ -291,6 +291,15 @@ const PDFObject& PDFDictionary::get(const PDFInplaceOrMemoryString& key) const
} }
} }
void PDFDictionary::removeEntry(const char* key)
{
auto it = find(key);
if (it != m_dictionary.end())
{
m_dictionary.erase(it);
}
}
void PDFDictionary::setEntry(const PDFInplaceOrMemoryString& key, PDFObject&& value) void PDFDictionary::setEntry(const PDFInplaceOrMemoryString& key, PDFObject&& value)
{ {
auto it = find(key); auto it = find(key);
@ -327,7 +336,7 @@ std::vector<PDFDictionary::DictionaryEntry>::iterator PDFDictionary::find(const
std::vector<PDFDictionary::DictionaryEntry>::const_iterator PDFDictionary::find(const char* key) const std::vector<PDFDictionary::DictionaryEntry>::const_iterator PDFDictionary::find(const char* key) const
{ {
return std::find_if(m_dictionary.cbegin(), m_dictionary.cend(), [&key](const DictionaryEntry& entry) { return entry.first == key; }); return std::find_if(m_dictionary.cbegin(), m_dictionary.cend(), [key](const DictionaryEntry& entry) { return entry.first == key; });
} }
std::vector<PDFDictionary::DictionaryEntry>::const_iterator PDFDictionary::find(const PDFInplaceOrMemoryString& key) const std::vector<PDFDictionary::DictionaryEntry>::const_iterator PDFDictionary::find(const PDFInplaceOrMemoryString& key) const
@ -340,6 +349,11 @@ std::vector<PDFDictionary::DictionaryEntry>::iterator PDFDictionary::find(const
return std::find_if(m_dictionary.begin(), m_dictionary.end(), [&key](const DictionaryEntry& entry) { return entry.first == key; }); return std::find_if(m_dictionary.begin(), m_dictionary.end(), [&key](const DictionaryEntry& entry) { return entry.first == key; });
} }
std::vector<PDFDictionary::DictionaryEntry>::iterator PDFDictionary::find(const char* key)
{
return std::find_if(m_dictionary.begin(), m_dictionary.end(), [key](const DictionaryEntry& entry) { return entry.first == key; });
}
bool PDFStream::equals(const PDFObjectContent* other) const bool PDFStream::equals(const PDFObjectContent* other) const
{ {
Q_ASSERT(dynamic_cast<const PDFStream*>(other)); Q_ASSERT(dynamic_cast<const PDFStream*>(other));

View File

@ -383,6 +383,11 @@ public:
/// \param key Key to be found in the dictionary /// \param key Key to be found in the dictionary
bool hasKey(const char* key) const { return find(key) != m_dictionary.cend(); } bool hasKey(const char* key) const { return find(key) != m_dictionary.cend(); }
/// Removes entry with given key. If entry with this key is not found,
/// nothing happens.
/// \param key Key to be removed
void removeEntry(const char* key);
/// Adds a new entry to the dictionary. /// Adds a new entry to the dictionary.
/// \param key Key /// \param key Key
/// \param value Value /// \param value Value
@ -435,6 +440,11 @@ private:
/// \param key Key to be found /// \param key Key to be found
std::vector<DictionaryEntry>::const_iterator find(const char* key) const; std::vector<DictionaryEntry>::const_iterator find(const char* key) const;
/// Finds an item in the dictionary array, if the item is not in the dictionary,
/// then end iterator is returned.
/// \param key Key to be found
std::vector<DictionaryEntry>::iterator find(const char* key);
/// Finds an item in the dictionary array, if the item is not in the dictionary, /// Finds an item in the dictionary array, if the item is not in the dictionary,
/// then end iterator is returned. /// then end iterator is returned.
/// \param key Key to be found /// \param key Key to be found

View File

@ -21,6 +21,8 @@
#include "pdfvisitor.h" #include "pdfvisitor.h"
#include "pdfutils.h" #include "pdfutils.h"
#include <QRandomGenerator>
#include <openssl/rc4.h> #include <openssl/rc4.h>
#include <openssl/md5.h> #include <openssl/md5.h>
#include <openssl/aes.h> #include <openssl/aes.h>
@ -39,12 +41,20 @@ static constexpr std::array<uint8_t, 32> PDFPasswordPadding = {
0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
}; };
class PDFDecryptObjectVisitor : public PDFAbstractVisitor class PDFDecryptOrEncryptObjectVisitor : public PDFAbstractVisitor
{ {
public: public:
explicit PDFDecryptObjectVisitor(const PDFSecurityHandler* securityHandler, PDFObjectReference reference) :
enum class Mode
{
Decrypt,
Encrypt
};
explicit PDFDecryptOrEncryptObjectVisitor(const PDFSecurityHandler* securityHandler, PDFObjectReference reference, Mode mode) :
m_securityHandler(securityHandler), m_securityHandler(securityHandler),
m_reference(reference) m_reference(reference),
m_mode(mode)
{ {
m_objectStack.reserve(32); m_objectStack.reserve(32);
} }
@ -60,45 +70,58 @@ public:
virtual void visitStream(const PDFStream* stream) override; virtual void visitStream(const PDFStream* stream) override;
virtual void visitReference(const PDFObjectReference reference) override; virtual void visitReference(const PDFObjectReference reference) override;
PDFObject getDecryptedObject(); PDFObject getProcessedObject();
private: private:
const PDFSecurityHandler* m_securityHandler; const PDFSecurityHandler* m_securityHandler = nullptr;
std::vector<PDFObject> m_objectStack; std::vector<PDFObject> m_objectStack;
PDFObjectReference m_reference; PDFObjectReference m_reference;
Mode m_mode = Mode::Decrypt;
}; };
void PDFDecryptObjectVisitor::visitNull() void PDFDecryptOrEncryptObjectVisitor::visitNull()
{ {
m_objectStack.push_back(PDFObject::createNull()); m_objectStack.push_back(PDFObject::createNull());
} }
void PDFDecryptObjectVisitor::visitBool(bool value) void PDFDecryptOrEncryptObjectVisitor::visitBool(bool value)
{ {
m_objectStack.push_back(PDFObject::createBool(value)); m_objectStack.push_back(PDFObject::createBool(value));
} }
void PDFDecryptObjectVisitor::visitInt(PDFInteger value) void PDFDecryptOrEncryptObjectVisitor::visitInt(PDFInteger value)
{ {
m_objectStack.push_back(PDFObject::createInteger(value)); m_objectStack.push_back(PDFObject::createInteger(value));
} }
void PDFDecryptObjectVisitor::visitReal(PDFReal value) void PDFDecryptOrEncryptObjectVisitor::visitReal(PDFReal value)
{ {
m_objectStack.push_back(PDFObject::createReal(value)); m_objectStack.push_back(PDFObject::createReal(value));
} }
void PDFDecryptObjectVisitor::visitString(PDFStringRef string) void PDFDecryptOrEncryptObjectVisitor::visitString(PDFStringRef string)
{ {
m_objectStack.push_back(PDFObject::createString(m_securityHandler->decrypt(string.getString(), m_reference, PDFSecurityHandler::EncryptionScope::String))); switch (m_mode)
{
case pdf::PDFDecryptOrEncryptObjectVisitor::Mode::Decrypt:
m_objectStack.push_back(PDFObject::createString(m_securityHandler->decrypt(string.getString(), m_reference, PDFSecurityHandler::EncryptionScope::String)));
break;
case pdf::PDFDecryptOrEncryptObjectVisitor::Mode::Encrypt:
m_objectStack.push_back(PDFObject::createString(m_securityHandler->encrypt(string.getString(), m_reference, PDFSecurityHandler::EncryptionScope::String)));
break;
default:
Q_ASSERT(false);
break;
}
} }
void PDFDecryptObjectVisitor::visitName(PDFStringRef name) void PDFDecryptOrEncryptObjectVisitor::visitName(PDFStringRef name)
{ {
m_objectStack.push_back(PDFObject::createName(name)); m_objectStack.push_back(PDFObject::createName(name));
} }
void PDFDecryptObjectVisitor::visitArray(const PDFArray* array) void PDFDecryptOrEncryptObjectVisitor::visitArray(const PDFArray* array)
{ {
acceptArray(array); acceptArray(array);
@ -112,12 +135,12 @@ void PDFDecryptObjectVisitor::visitArray(const PDFArray* array)
m_objectStack.push_back(object); m_objectStack.push_back(object);
} }
void PDFDecryptObjectVisitor::visitDictionary(const PDFDictionary* dictionary) void PDFDecryptOrEncryptObjectVisitor::visitDictionary(const PDFDictionary* dictionary)
{ {
Q_ASSERT(dictionary); Q_ASSERT(dictionary);
// We must check, if it is or isn't a signature dictionary. If it is, // We must check, if it is or isn't a signature dictionary. If it is,
// then don't decrypt the Content value. We also don't check, if signature // then don't decrypt/encrypt the Content value. We also don't check, if signature
// isn't indirectly referenced by reference. Hope it isn't... // isn't indirectly referenced by reference. Hope it isn't...
const PDFObject& typeObject = dictionary->get("Type"); const PDFObject& typeObject = dictionary->get("Type");
bool isSignatureObject = (typeObject.isName() && typeObject.getString() == "Sig"); bool isSignatureObject = (typeObject.isName() && typeObject.getString() == "Sig");
@ -142,9 +165,9 @@ void PDFDecryptObjectVisitor::visitDictionary(const PDFDictionary* dictionary)
m_objectStack.push_back(PDFObject::createDictionary(std::make_shared<PDFDictionary>(qMove(entries)))); m_objectStack.push_back(PDFObject::createDictionary(std::make_shared<PDFDictionary>(qMove(entries))));
} }
void PDFDecryptObjectVisitor::visitStream(const PDFStream* stream) void PDFDecryptOrEncryptObjectVisitor::visitStream(const PDFStream* stream)
{ {
// Don't decrypt, if it is a Metadata stream and Metadata encryption is turned off // Don't decrypt/encrypt, if it is a Metadata stream and Metadata encryption is turned off
const PDFDictionary* dictionary = stream->getDictionary(); const PDFDictionary* dictionary = stream->getDictionary();
const PDFObject& typeObject = dictionary->get("Type"); const PDFObject& typeObject = dictionary->get("Type");
@ -156,34 +179,66 @@ void PDFDecryptObjectVisitor::visitStream(const PDFStream* stream)
return; return;
} }
// Decrypt the dictionary // Decrypt/encrypt the dictionary
visitDictionary(dictionary); visitDictionary(dictionary);
PDFObject dictionaryObject = m_objectStack.back(); PDFObject dictionaryObject = m_objectStack.back();
m_objectStack.pop_back(); m_objectStack.pop_back();
// We must also handle situation, that stream has specified Crypt filter. // We must also handle situation, that stream has specified Crypt filter.
// In this case, we must delegate decryption to the stream filters. // In this case, we must delegate decryption/encryption to the stream filters.
PDFDictionary decryptedDictionary(*dictionaryObject.getDictionary()); PDFDictionary processedDictionary(*dictionaryObject.getDictionary());
QByteArray decryptedData; QByteArray processedData;
if (!decryptedDictionary.hasKey("Crypt")) if (!processedDictionary.hasKey("Crypt"))
{ {
decryptedData = m_securityHandler->decrypt(*stream->getContent(), m_reference, PDFSecurityHandler::EncryptionScope::Stream); switch (m_mode)
{
case pdf::PDFDecryptOrEncryptObjectVisitor::Mode::Decrypt:
processedData = m_securityHandler->decrypt(*stream->getContent(), m_reference, PDFSecurityHandler::EncryptionScope::Stream);
break;
case pdf::PDFDecryptOrEncryptObjectVisitor::Mode::Encrypt:
processedData = m_securityHandler->encrypt(*stream->getContent(), m_reference, PDFSecurityHandler::EncryptionScope::Stream);
break;
default:
Q_ASSERT(false);
break;
}
} }
else else
{ {
decryptedData = *stream->getContent(); switch (m_mode)
decryptedDictionary.addEntry(PDFInplaceOrMemoryString(PDFSecurityHandler::OBJECT_REFERENCE_DICTIONARY_NAME), PDFObject::createReference(m_reference)); {
case pdf::PDFDecryptOrEncryptObjectVisitor::Mode::Decrypt:
{
processedData = *stream->getContent();
processedDictionary.removeEntry(PDFSecurityHandler::OBJECT_REFERENCE_DICTIONARY_NAME);
processedDictionary.addEntry(PDFInplaceOrMemoryString(PDFSecurityHandler::OBJECT_REFERENCE_DICTIONARY_NAME), PDFObject::createReference(m_reference));
break;
}
case pdf::PDFDecryptOrEncryptObjectVisitor::Mode::Encrypt:
{
processedData = *stream->getContent();
processedDictionary.removeEntry(PDFSecurityHandler::OBJECT_REFERENCE_DICTIONARY_NAME);
break;
}
default:
Q_ASSERT(false);
break;
}
} }
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(processedDictionary), qMove(processedData))));
} }
void PDFDecryptObjectVisitor::visitReference(const PDFObjectReference reference) void PDFDecryptOrEncryptObjectVisitor::visitReference(const PDFObjectReference reference)
{ {
m_objectStack.push_back(PDFObject::createReference(reference)); m_objectStack.push_back(PDFObject::createReference(reference));
} }
PDFObject PDFDecryptObjectVisitor::getDecryptedObject() PDFObject PDFDecryptOrEncryptObjectVisitor::getProcessedObject()
{ {
Q_ASSERT(m_objectStack.size() == 1); Q_ASSERT(m_objectStack.size() == 1);
return qMove(m_objectStack.back()); return qMove(m_objectStack.back());
@ -191,9 +246,16 @@ PDFObject PDFDecryptObjectVisitor::getDecryptedObject()
PDFObject PDFSecurityHandler::decryptObject(const PDFObject& object, PDFObjectReference reference) const PDFObject PDFSecurityHandler::decryptObject(const PDFObject& object, PDFObjectReference reference) const
{ {
PDFDecryptObjectVisitor visitor(this, reference); PDFDecryptOrEncryptObjectVisitor visitor(this, reference, PDFDecryptOrEncryptObjectVisitor::Mode::Decrypt);
object.accept(&visitor); object.accept(&visitor);
return visitor.getDecryptedObject(); return visitor.getProcessedObject();
}
PDFObject PDFSecurityHandler::encryptObject(const PDFObject& object, PDFObjectReference reference) const
{
PDFDecryptOrEncryptObjectVisitor visitor(this, reference, PDFDecryptOrEncryptObjectVisitor::Mode::Encrypt);
object.accept(&visitor);
return visitor.getProcessedObject();
} }
PDFSecurityHandlerPointer PDFSecurityHandler::createSecurityHandler(const PDFObject& encryptionDictionaryObject, const QByteArray& id) PDFSecurityHandlerPointer PDFSecurityHandler::createSecurityHandler(const PDFObject& encryptionDictionaryObject, const QByteArray& id)
@ -608,6 +670,34 @@ PDFSecurityHandler::AuthorizationResult PDFStandardSecurityHandler::authenticate
return AuthorizationResult::Cancelled; return AuthorizationResult::Cancelled;
} }
std::vector<uint8_t> PDFStandardSecurityHandler::createV2_ObjectEncryptionKey(PDFObjectReference reference, CryptFilter filter) const
{
std::vector<uint8_t> inputKeyData = convertByteArrayToVector(m_authorizationData.fileEncryptionKey);
uint32_t objectNumber = qToLittleEndian(static_cast<uint32_t>(reference.objectNumber));
uint32_t generation = qToLittleEndian(static_cast<uint32_t>(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) });
std::vector<uint8_t> objectEncryptionKey(MD5_DIGEST_LENGTH, uint8_t(0));
MD5(inputKeyData.data(), inputKeyData.size(), objectEncryptionKey.data());
// Use up to (n + 5) bytes, maximally 16, from the digest as object encryption key
size_t objectEncryptionKeySize = qMin(filter.keyLength + 5, MD5_DIGEST_LENGTH);
objectEncryptionKey.resize(objectEncryptionKeySize);
return objectEncryptionKey;
}
std::vector<uint8_t> PDFStandardSecurityHandler::createAESV2_ObjectEncryptionKey(PDFObjectReference reference) const
{
std::vector<uint8_t> inputKeyData = convertByteArrayToVector(m_authorizationData.fileEncryptionKey);
uint32_t objectNumber = qToLittleEndian(static_cast<uint32_t>(reference.objectNumber));
uint32_t generation = qToLittleEndian(static_cast<uint32_t>(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 });
std::vector<uint8_t> objectEncryptionKey(MD5_DIGEST_LENGTH, uint8_t(0));
MD5(inputKeyData.data(), inputKeyData.size(), objectEncryptionKey.data());
return objectEncryptionKey;
}
QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data, CryptFilter filter, PDFObjectReference reference) const QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data, CryptFilter filter, PDFObjectReference reference) const
{ {
QByteArray decryptedData; QByteArray decryptedData;
@ -618,6 +708,7 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data
{ {
QByteArray initializationVector; QByteArray initializationVector;
QByteArray paddedData; QByteArray paddedData;
int paddingRemainder = 0;
}; };
auto prepareAES_data = [](const QByteArray& data) auto prepareAES_data = [](const QByteArray& data)
@ -637,11 +728,11 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data
// Add padding remainder according to the specification // Add padding remainder according to the specification
int size = result.paddedData.size(); int size = result.paddedData.size();
int paddingRemainder = AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE); result.paddingRemainder = AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE);
for (int i = 0; i < paddingRemainder; ++i) for (int i = 0; i < result.paddingRemainder; ++i)
{ {
result.paddedData.push_back(paddingRemainder); result.paddedData.push_back(result.paddingRemainder);
} }
return result; return result;
@ -659,17 +750,7 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data
case CryptFilterType::V2: // Use file encryption key for RC4 algorithm case CryptFilterType::V2: // Use file encryption key for RC4 algorithm
{ {
std::vector<uint8_t> inputKeyData = convertByteArrayToVector(m_authorizationData.fileEncryptionKey); std::vector<uint8_t> objectEncryptionKey = createV2_ObjectEncryptionKey(reference, filter);
uint32_t objectNumber = qToLittleEndian(static_cast<uint32_t>(reference.objectNumber));
uint32_t generation = qToLittleEndian(static_cast<uint32_t>(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) });
std::vector<uint8_t> objectEncryptionKey(MD5_DIGEST_LENGTH, uint8_t(0));
MD5(inputKeyData.data(), inputKeyData.size(), objectEncryptionKey.data());
// Use up to (n + 5) bytes, maximally 16, from the digest as object encryption key
size_t objectEncryptionKeySize = qMin(filter.keyLength + 5, MD5_DIGEST_LENGTH);
objectEncryptionKey.resize(objectEncryptionKeySize);
decryptedData.resize(data.size()); decryptedData.resize(data.size());
RC4_KEY key = { }; RC4_KEY key = { };
@ -681,12 +762,7 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data
case CryptFilterType::AESV2: // Use file encryption key for AES algorithm case CryptFilterType::AESV2: // Use file encryption key for AES algorithm
{ {
std::vector<uint8_t> inputKeyData = convertByteArrayToVector(m_authorizationData.fileEncryptionKey); std::vector<uint8_t> objectEncryptionKey = createAESV2_ObjectEncryptionKey(reference);
uint32_t objectNumber = qToLittleEndian(static_cast<uint32_t>(reference.objectNumber));
uint32_t generation = qToLittleEndian(static_cast<uint32_t>(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 });
std::vector<uint8_t> objectEncryptionKey(MD5_DIGEST_LENGTH, uint8_t(0));
MD5(inputKeyData.data(), inputKeyData.size(), objectEncryptionKey.data());
// For AES algorithm, always use 16 bytes key (128 bit encryption mode) // For AES algorithm, always use 16 bytes key (128 bit encryption mode)
@ -698,7 +774,7 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data
{ {
decryptedData.resize(aes_data.paddedData.size()); decryptedData.resize(aes_data.paddedData.size());
AES_cbc_encrypt(convertByteArrayToUcharPtr(aes_data.paddedData), convertByteArrayToUcharPtr(decryptedData), aes_data.paddedData.length(), &key, convertByteArrayToUcharPtr(aes_data.initializationVector), AES_DECRYPT); AES_cbc_encrypt(convertByteArrayToUcharPtr(aes_data.paddedData), convertByteArrayToUcharPtr(decryptedData), aes_data.paddedData.length(), &key, convertByteArrayToUcharPtr(aes_data.initializationVector), AES_DECRYPT);
decryptedData = decryptedData.left(data.length() - AES_BLOCK_SIZE); decryptedData = decryptedData.left(data.length() - aes_data.paddingRemainder);
} }
break; break;
@ -715,7 +791,7 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data
{ {
decryptedData.resize(aes_data.paddedData.size()); decryptedData.resize(aes_data.paddedData.size());
AES_cbc_encrypt(convertByteArrayToUcharPtr(aes_data.paddedData), convertByteArrayToUcharPtr(decryptedData), aes_data.paddedData.length(), &key, convertByteArrayToUcharPtr(aes_data.initializationVector), AES_DECRYPT); AES_cbc_encrypt(convertByteArrayToUcharPtr(aes_data.paddedData), convertByteArrayToUcharPtr(decryptedData), aes_data.paddedData.length(), &key, convertByteArrayToUcharPtr(aes_data.initializationVector), AES_DECRYPT);
decryptedData = decryptedData.left(data.length() - AES_BLOCK_SIZE); decryptedData = decryptedData.left(data.length() - aes_data.paddingRemainder);
} }
break; break;
@ -731,7 +807,135 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data
return decryptedData; return decryptedData;
} }
QByteArray PDFStandardSecurityHandler::decrypt(const QByteArray& data, PDFObjectReference reference, PDFSecurityHandler::EncryptionScope encryptionScope) const QByteArray PDFStandardSecurityHandler::encryptUsingFilter(const QByteArray& data, CryptFilter filter, PDFObjectReference reference) const
{
QByteArray encryptedData;
Q_ASSERT(m_authorizationData.isAuthorized());
struct AES_data
{
QByteArray initializationVector;
QByteArray paddedData;
int paddingRemainder = 0;
};
auto prepareAES_data = [](const QByteArray& data)
{
AES_data result;
QRandomGenerator randomNumberGenerator = QRandomGenerator::securelySeeded();
result.initializationVector.resize(AES_BLOCK_SIZE);
for (int i = 0; i < AES_BLOCK_SIZE; ++i)
{
result.initializationVector[i] = uint8_t(randomNumberGenerator.generate());
}
result.paddedData = data;
// Add padding remainder according to the specification
int size = data.size();
result.paddingRemainder = AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE);
for (int i = 0; i < result.paddingRemainder; ++i)
{
result.paddedData.push_back(result.paddingRemainder);
}
return result;
};
switch (filter.type)
{
case CryptFilterType::None: // The application shall encrypt the data using the security handler
{
// This shouldn't occur, because in case the used filter has None value, then default filter
// is used and default filter can't have this value.
Q_ASSERT(false);
break;
}
case CryptFilterType::V2: // Use file encryption key for RC4 algorithm
{
// This algorithm is same as the encrypt algorithm, because RC4 cipher is symmetrical
std::vector<uint8_t> objectEncryptionKey = createV2_ObjectEncryptionKey(reference, filter);
encryptedData.resize(data.size());
RC4_KEY key = { };
RC4_set_key(&key, static_cast<int>(objectEncryptionKey.size()), objectEncryptionKey.data());
RC4(&key, data.size(), convertByteArrayToUcharPtr(data), convertByteArrayToUcharPtr(encryptedData));
break;
}
case CryptFilterType::AESV2: // Use file encryption key for AES algorithm
{
std::vector<uint8_t> objectEncryptionKey = createAESV2_ObjectEncryptionKey(reference);
// For AES algorithm, always use 16 bytes key (128 bit encryption mode)
AES_KEY key = { };
AES_set_encrypt_key(objectEncryptionKey.data(), static_cast<int>(objectEncryptionKey.size()) * 8, &key);
AES_data aes_data = prepareAES_data(data);
if (!aes_data.paddedData.isEmpty())
{
encryptedData.resize(aes_data.paddedData.size());
AES_cbc_encrypt(convertByteArrayToUcharPtr(aes_data.paddedData), convertByteArrayToUcharPtr(encryptedData), aes_data.paddedData.length(), &key, convertByteArrayToUcharPtr(aes_data.initializationVector), AES_ENCRYPT);
encryptedData = encryptedData.left(data.length() - aes_data.paddingRemainder);
encryptedData.prepend(aes_data.initializationVector);
}
break;
}
case CryptFilterType::AESV3: // Use file encryption key for AES 256 bit algorithm
{
Q_ASSERT(m_authorizationData.fileEncryptionKey.size() == 32);
AES_KEY key = { };
AES_set_encrypt_key(convertByteArrayToUcharPtr(m_authorizationData.fileEncryptionKey), static_cast<int>(m_authorizationData.fileEncryptionKey.size()) * 8, &key);
AES_data aes_data = prepareAES_data(data);
if (!aes_data.paddedData.isEmpty())
{
encryptedData.resize(aes_data.paddedData.size());
AES_cbc_encrypt(convertByteArrayToUcharPtr(aes_data.paddedData), convertByteArrayToUcharPtr(encryptedData), aes_data.paddedData.length(), &key, convertByteArrayToUcharPtr(aes_data.initializationVector), AES_ENCRYPT);
encryptedData = encryptedData.left(data.length() - aes_data.paddingRemainder);
encryptedData.prepend(aes_data.initializationVector);
}
break;
}
case CryptFilterType::Identity: // Don't decrypt anything, use identity function
{
encryptedData = data;
break;
}
}
return encryptedData;
}
QByteArray PDFStandardSecurityHandler::decrypt(const QByteArray& data, PDFObjectReference reference, EncryptionScope encryptionScope) const
{
CryptFilter filter = getCryptFilter(encryptionScope);
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 PDFException(PDFTranslationContext::tr("Crypt filter '%1' not found.").arg(QString::fromLatin1(filterName)));
}
return decryptUsingFilter(data, it->second, reference);
}
CryptFilter PDFStandardSecurityHandler::getCryptFilter(EncryptionScope encryptionScope) const
{ {
CryptFilter filter = m_filterDefault; CryptFilter filter = m_filterDefault;
@ -768,10 +972,16 @@ QByteArray PDFStandardSecurityHandler::decrypt(const QByteArray& data, PDFObject
} }
} }
return decryptUsingFilter(data, filter, reference); return filter;
} }
QByteArray PDFStandardSecurityHandler::decryptByFilter(const QByteArray& data, const QByteArray& filterName, PDFObjectReference reference) const QByteArray PDFStandardSecurityHandler::encrypt(const QByteArray& data, PDFObjectReference reference, EncryptionScope encryptionScope) const
{
CryptFilter filter = getCryptFilter(encryptionScope);
return encryptUsingFilter(data, filter, reference);
}
QByteArray PDFStandardSecurityHandler::encryptByFilter(const QByteArray& data, const QByteArray& filterName, PDFObjectReference reference) const
{ {
auto it = m_cryptFilters.find(filterName); auto it = m_cryptFilters.find(filterName);
if (it == m_cryptFilters.cend()) if (it == m_cryptFilters.cend())
@ -779,7 +989,7 @@ QByteArray PDFStandardSecurityHandler::decryptByFilter(const QByteArray& data, c
throw PDFException(PDFTranslationContext::tr("Crypt filter '%1' not found.").arg(QString::fromLatin1(filterName))); throw PDFException(PDFTranslationContext::tr("Crypt filter '%1' not found.").arg(QString::fromLatin1(filterName)));
} }
return decryptUsingFilter(data, it->second, reference); return encryptUsingFilter(data, it->second, reference);
} }
QByteArray PDFStandardSecurityHandler::createFileEncryptionKey(const QByteArray& password) const QByteArray PDFStandardSecurityHandler::createFileEncryptionKey(const QByteArray& password) const

View File

@ -127,6 +127,13 @@ public:
/// \returns Decrypted object /// \returns Decrypted object
PDFObject decryptObject(const PDFObject& object, PDFObjectReference reference) const; PDFObject decryptObject(const PDFObject& object, PDFObjectReference reference) const;
/// Encrypts the PDF object. This function works properly only (and only if)
/// \p authenticate function returns user/owner authorization code.
/// \param object Object to be encrypted
/// \param reference Reference of indirect object (some algorithms require to generate key also from reference)
/// \returns Encrypted object
PDFObject encryptObject(const PDFObject& object, PDFObjectReference reference) const;
/// Decrypts the PDF object data. This function works properly only (and only if) /// Decrypts the PDF object data. This function works properly only (and only if)
/// \p authenticate function returns user/owner authorization code. /// \p authenticate function returns user/owner authorization code.
/// \param data Data to be decrypted /// \param data Data to be decrypted
@ -141,6 +148,20 @@ public:
/// \param reference Reference object /// \param reference Reference object
virtual QByteArray decryptByFilter(const QByteArray& data, const QByteArray& filterName, PDFObjectReference reference) const = 0; virtual QByteArray decryptByFilter(const QByteArray& data, const QByteArray& filterName, PDFObjectReference reference) const = 0;
/// Encrypts the PDF object data. This function works properly only (and only if)
/// \p authenticate function returns user/owner authorization code.
/// \param data Data to be encrypted
/// \param reference Reference of indirect object (some algorithms require to generate key also from reference)
/// \param encryptionScope Scope of the encryption (if it is string/stream/...)
/// \returns Encrypted object data
virtual QByteArray encrypt(const QByteArray& data, PDFObjectReference reference, EncryptionScope encryptionScope) const = 0;
/// Encrypts data using specified filter. Throws exception, if filter is not found.
/// \param data Data to be encrypted
/// \param filterName Filter name to be used to encrypt the data
/// \param reference Reference object
virtual QByteArray encryptByFilter(const QByteArray& data, const QByteArray& filterName, PDFObjectReference reference) const = 0;
/// Returns true, if given permission is allowed in the current authorization context. /// Returns true, if given permission is allowed in the current authorization context.
/// If owner is authorized, then this function allways returns true. /// If owner is authorized, then this function allways returns true.
virtual bool isAllowed(Permission permission) const = 0; virtual bool isAllowed(Permission permission) const = 0;
@ -148,6 +169,10 @@ public:
/// Returns true, if metadata are encrypted /// Returns true, if metadata are encrypted
virtual bool isMetadataEncrypted() const = 0; virtual bool isMetadataEncrypted() const = 0;
/// Returns true, if encryption is allowed (and document
/// can be encrypted, if it was decrypted)
virtual bool isEncryptionAllowed() const = 0;
/// Returns result of authorization process /// Returns result of authorization process
virtual AuthorizationResult getAuthorizationResult() const = 0; virtual AuthorizationResult getAuthorizationResult() const = 0;
@ -192,8 +217,11 @@ public:
virtual AuthorizationResult authenticate(const std::function<QString(bool*)>&, bool) override { return AuthorizationResult::OwnerAuthorized; } virtual AuthorizationResult authenticate(const std::function<QString(bool*)>&, 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 QByteArray decryptByFilter(const QByteArray& data, const QByteArray&, PDFObjectReference) const override { return data; }
virtual QByteArray encrypt(const QByteArray& data, PDFObjectReference, EncryptionScope) const override { return data; }
virtual QByteArray encryptByFilter(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 override { return true; } virtual bool isAllowed(Permission) const override { return true; }
virtual bool isEncryptionAllowed() const override { return true; }
virtual AuthorizationResult getAuthorizationResult() const override { return AuthorizationResult::NoAuthorizationRequired; } virtual AuthorizationResult getAuthorizationResult() const override { return AuthorizationResult::NoAuthorizationRequired; }
}; };
@ -206,8 +234,11 @@ public:
virtual AuthorizationResult authenticate(const std::function<QString(bool*)>& getPasswordCallback, bool authorizeOwnerOnly) override; virtual AuthorizationResult authenticate(const std::function<QString(bool*)>& getPasswordCallback, bool authorizeOwnerOnly) 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 QByteArray decryptByFilter(const QByteArray& data, const QByteArray& filterName, PDFObjectReference reference) const override;
virtual QByteArray encrypt(const QByteArray& data, PDFObjectReference reference, EncryptionScope encryptionScope) const override;
virtual QByteArray encryptByFilter(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 override { return m_authorizationData.authorizationResult == AuthorizationResult::OwnerAuthorized || (m_permissions & static_cast<uint32_t>(permission)); } virtual bool isAllowed(Permission permission) const override { return m_authorizationData.authorizationResult == AuthorizationResult::OwnerAuthorized || (m_permissions & static_cast<uint32_t>(permission)); }
virtual bool isEncryptionAllowed() const override { return m_authorizationData.isAuthorized(); }
virtual AuthorizationResult getAuthorizationResult() const override { return m_authorizationData.authorizationResult; } virtual AuthorizationResult getAuthorizationResult() const override { return m_authorizationData.authorizationResult; }
struct AuthorizationData struct AuthorizationData
@ -267,6 +298,17 @@ private:
/// \returns Decrypted data /// \returns Decrypted data
QByteArray decryptUsingFilter(const QByteArray& data, CryptFilter filter, PDFObjectReference reference) const; QByteArray decryptUsingFilter(const QByteArray& data, CryptFilter filter, PDFObjectReference reference) const;
/// Encrypts data using specified filter. This function can be called only, if authorization was successfull.
/// \param data Data to be encrypted
/// \param filter Filter to be used for encryption
/// \param reference Object reference for key generation
/// \returns Encrypted data
QByteArray encryptUsingFilter(const QByteArray& data, CryptFilter filter, PDFObjectReference reference) const;
std::vector<uint8_t> createV2_ObjectEncryptionKey(PDFObjectReference reference, CryptFilter filter) const;
std::vector<uint8_t> createAESV2_ObjectEncryptionKey(PDFObjectReference reference) const;
CryptFilter getCryptFilter(EncryptionScope encryptionScope) const;
/// Returns true, if character with unicode code is non-ascii space character /// Returns true, if character with unicode code is non-ascii space character
/// according the RFC 3454, section C.1.2 /// according the RFC 3454, section C.1.2
/// \param unicode Unicode code to be tested /// \param unicode Unicode code to be tested