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::PDFObjects& objects = storage.getObjects();
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.");
}
@ -238,6 +239,13 @@ PDFOperationResult PDFDocumentWriter::write(QIODevice* device, const PDFDocument
writeCRLF(device);
writeCRLF(device);
PDFObjectReference encryptObjectReference;
PDFObject encryptObject = document->getTrailerDictionary()->get("Encrypt");
if (encryptObject.isReference())
{
encryptObjectReference = encryptObject.getReference();
}
// Write objects
std::vector<PDFInteger> offsets(objectCount, -1);
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
offsets[i] = device->pos();
PDFWriteObjectVisitor visitor(device);
writeObjectHeader(device, PDFObjectReference(i, entry.generation));
entry.object.accept(&visitor);
writeObjectFooter(device);
if (isEncrypted)
{
PDFObjectReference reference(i, entry.generation);
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

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)
{
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
{
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
@ -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; });
}
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
{
Q_ASSERT(dynamic_cast<const PDFStream*>(other));

View File

@ -383,6 +383,11 @@ public:
/// \param key Key to be found in the dictionary
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.
/// \param key Key
/// \param value Value
@ -435,6 +440,11 @@ private:
/// \param key Key to be found
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,
/// then end iterator is returned.
/// \param key Key to be found

View File

@ -21,6 +21,8 @@
#include "pdfvisitor.h"
#include "pdfutils.h"
#include <QRandomGenerator>
#include <openssl/rc4.h>
#include <openssl/md5.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
};
class PDFDecryptObjectVisitor : public PDFAbstractVisitor
class PDFDecryptOrEncryptObjectVisitor : public PDFAbstractVisitor
{
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_reference(reference)
m_reference(reference),
m_mode(mode)
{
m_objectStack.reserve(32);
}
@ -60,45 +70,58 @@ public:
virtual void visitStream(const PDFStream* stream) override;
virtual void visitReference(const PDFObjectReference reference) override;
PDFObject getDecryptedObject();
PDFObject getProcessedObject();
private:
const PDFSecurityHandler* m_securityHandler;
const PDFSecurityHandler* m_securityHandler = nullptr;
std::vector<PDFObject> m_objectStack;
PDFObjectReference m_reference;
Mode m_mode = Mode::Decrypt;
};
void PDFDecryptObjectVisitor::visitNull()
void PDFDecryptOrEncryptObjectVisitor::visitNull()
{
m_objectStack.push_back(PDFObject::createNull());
}
void PDFDecryptObjectVisitor::visitBool(bool value)
void PDFDecryptOrEncryptObjectVisitor::visitBool(bool 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));
}
void PDFDecryptObjectVisitor::visitReal(PDFReal value)
void PDFDecryptOrEncryptObjectVisitor::visitReal(PDFReal 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));
}
void PDFDecryptObjectVisitor::visitArray(const PDFArray* array)
void PDFDecryptOrEncryptObjectVisitor::visitArray(const PDFArray* array)
{
acceptArray(array);
@ -112,12 +135,12 @@ void PDFDecryptObjectVisitor::visitArray(const PDFArray* array)
m_objectStack.push_back(object);
}
void PDFDecryptObjectVisitor::visitDictionary(const PDFDictionary* dictionary)
void PDFDecryptOrEncryptObjectVisitor::visitDictionary(const PDFDictionary* dictionary)
{
Q_ASSERT(dictionary);
// 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...
const PDFObject& typeObject = dictionary->get("Type");
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))));
}
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 PDFObject& typeObject = dictionary->get("Type");
@ -156,34 +179,66 @@ void PDFDecryptObjectVisitor::visitStream(const PDFStream* stream)
return;
}
// Decrypt the dictionary
// Decrypt/encrypt the dictionary
visitDictionary(dictionary);
PDFObject dictionaryObject = m_objectStack.back();
m_objectStack.pop_back();
// 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;
if (!decryptedDictionary.hasKey("Crypt"))
// In this case, we must delegate decryption/encryption to the stream filters.
PDFDictionary processedDictionary(*dictionaryObject.getDictionary());
QByteArray processedData;
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
{
decryptedData = *stream->getContent();
decryptedDictionary.addEntry(PDFInplaceOrMemoryString(PDFSecurityHandler::OBJECT_REFERENCE_DICTIONARY_NAME), PDFObject::createReference(m_reference));
switch (m_mode)
{
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));
}
PDFObject PDFDecryptObjectVisitor::getDecryptedObject()
PDFObject PDFDecryptOrEncryptObjectVisitor::getProcessedObject()
{
Q_ASSERT(m_objectStack.size() == 1);
return qMove(m_objectStack.back());
@ -191,9 +246,16 @@ PDFObject PDFDecryptObjectVisitor::getDecryptedObject()
PDFObject PDFSecurityHandler::decryptObject(const PDFObject& object, PDFObjectReference reference) const
{
PDFDecryptObjectVisitor visitor(this, reference);
PDFDecryptOrEncryptObjectVisitor visitor(this, reference, PDFDecryptOrEncryptObjectVisitor::Mode::Decrypt);
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)
@ -608,6 +670,34 @@ PDFSecurityHandler::AuthorizationResult PDFStandardSecurityHandler::authenticate
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 decryptedData;
@ -618,6 +708,7 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data
{
QByteArray initializationVector;
QByteArray paddedData;
int paddingRemainder = 0;
};
auto prepareAES_data = [](const QByteArray& data)
@ -637,11 +728,11 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data
// Add padding remainder according to the specification
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;
@ -659,17 +750,7 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data
case CryptFilterType::V2: // Use file encryption key for RC4 algorithm
{
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);
std::vector<uint8_t> objectEncryptionKey = createV2_ObjectEncryptionKey(reference, filter);
decryptedData.resize(data.size());
RC4_KEY key = { };
@ -681,12 +762,7 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data
case CryptFilterType::AESV2: // Use file encryption key for AES algorithm
{
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());
std::vector<uint8_t> objectEncryptionKey = createAESV2_ObjectEncryptionKey(reference);
// 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());
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;
@ -715,7 +791,7 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data
{
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);
decryptedData = decryptedData.left(data.length() - AES_BLOCK_SIZE);
decryptedData = decryptedData.left(data.length() - aes_data.paddingRemainder);
}
break;
@ -731,7 +807,135 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data
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;
@ -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);
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)));
}
return decryptUsingFilter(data, it->second, reference);
return encryptUsingFilter(data, it->second, reference);
}
QByteArray PDFStandardSecurityHandler::createFileEncryptionKey(const QByteArray& password) const

View File

@ -127,6 +127,13 @@ public:
/// \returns Decrypted object
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)
/// \p authenticate function returns user/owner authorization code.
/// \param data Data to be decrypted
@ -141,6 +148,20 @@ public:
/// \param reference Reference object
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.
/// If owner is authorized, then this function allways returns true.
virtual bool isAllowed(Permission permission) const = 0;
@ -148,6 +169,10 @@ public:
/// Returns true, if metadata are encrypted
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
virtual AuthorizationResult getAuthorizationResult() const = 0;
@ -192,8 +217,11 @@ public:
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 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 isAllowed(Permission) const override { return true; }
virtual bool isEncryptionAllowed() const override { return true; }
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 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 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 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; }
struct AuthorizationData
@ -267,6 +298,17 @@ private:
/// \returns Decrypted data
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
/// according the RFC 3454, section C.1.2
/// \param unicode Unicode code to be tested