diff --git a/Pdf4QtLib/sources/pdfdocumentwriter.cpp b/Pdf4QtLib/sources/pdfdocumentwriter.cpp index 87ec9d6..9371075 100644 --- a/Pdf4QtLib/sources/pdfdocumentwriter.cpp +++ b/Pdf4QtLib/sources/pdfdocumentwriter.cpp @@ -316,10 +316,16 @@ PDFOperationResult PDFDocumentWriter::write(QIODevice* device, const PDFDocument device->write(entry.object.isNull() ? "f" : "n"); writeCRLF(device); } + + // Jakub Melka: Adjust trailer dictionary, to be really dictionary, not a stream + PDFDictionary trailerDictionary = *document->getTrailerDictionary(); + trailerDictionary.removeEntry("XRefStm"); + PDFObject trailerDictionaryObject = PDFObject::createDictionary(std::make_shared(qMove(trailerDictionary))); + device->write("trailer"); writeCRLF(device); PDFWriteObjectVisitor trailerVisitor(device); - storage.getTrailerDictionary().accept(&trailerVisitor); + trailerDictionaryObject.accept(&trailerVisitor); writeCRLF(device); device->write("startxref"); writeCRLF(device); @@ -339,7 +345,7 @@ void PDFDocumentWriter::writeCRLF(QIODevice* device) void PDFDocumentWriter::writeObjectHeader(QIODevice* device, PDFObjectReference reference) { - QString objectHeader = QString("%1 %2 obj").arg(QString::number(reference.objectNumber)).arg(QString::number(reference.generation)); + QString objectHeader = QString("%1 %2 obj").arg(QString::number(reference.objectNumber), QString::number(reference.generation)); device->write(objectHeader.toLatin1()); writeCRLF(device); } diff --git a/Pdf4QtLib/sources/pdfsecurityhandler.cpp b/Pdf4QtLib/sources/pdfsecurityhandler.cpp index e98f022..b3cff7b 100644 --- a/Pdf4QtLib/sources/pdfsecurityhandler.cpp +++ b/Pdf4QtLib/sources/pdfsecurityhandler.cpp @@ -708,7 +708,6 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data { QByteArray initializationVector; QByteArray paddedData; - int paddingRemainder = 0; }; auto prepareAES_data = [](const QByteArray& data) @@ -726,18 +725,31 @@ QByteArray PDFStandardSecurityHandler::decryptUsingFilter(const QByteArray& data result.paddedData = data.mid(AES_BLOCK_SIZE); - // Add padding remainder according to the specification - int size = result.paddedData.size(); - result.paddingRemainder = AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE); - - for (int i = 0; i < result.paddingRemainder; ++i) + // Remove errorneous data - we must have a data of multiple of AES_BLOCK_SIZE + const int remainder = result.paddedData.size() % AES_BLOCK_SIZE; + if (remainder != 0) { - result.paddedData.push_back(result.paddingRemainder); + result.paddedData = result.paddedData.left(result.paddedData.size() - remainder); } return result; }; + auto removeAES_padding = [](const QByteArray& data) + { + if (data.isEmpty()) + { + return data; + } + + // If padding doesnt fit from 1 to AES_BLOCK_SIZE, then it is + // an error, but just clamp the value. + const int padding = data.back(); + const int clampedPadding = qBound(1, padding, AES_BLOCK_SIZE); + + return data.left(data.size() - clampedPadding); + }; + switch (filter.type) { case CryptFilterType::None: // The application shall decrypt the data using the security handler @@ -774,7 +786,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_data.paddingRemainder); + decryptedData = removeAES_padding(decryptedData); } break; @@ -791,7 +803,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_data.paddingRemainder); + decryptedData = removeAES_padding(decryptedData); } break; @@ -817,7 +829,6 @@ QByteArray PDFStandardSecurityHandler::encryptUsingFilter(const QByteArray& data { QByteArray initializationVector; QByteArray paddedData; - int paddingRemainder = 0; }; auto prepareAES_data = [](const QByteArray& data) @@ -836,11 +847,12 @@ QByteArray PDFStandardSecurityHandler::encryptUsingFilter(const QByteArray& data // Add padding remainder according to the specification int size = data.size(); - result.paddingRemainder = AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE); + const int paddingRemainder = AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE); - for (int i = 0; i < result.paddingRemainder; ++i) + result.initializationVector.reserve(result.initializationVector.size() + paddingRemainder); + for (int i = 0; i < paddingRemainder; ++i) { - result.paddedData.push_back(result.paddingRemainder); + result.paddedData.push_back(paddingRemainder); } return result; @@ -881,10 +893,10 @@ QByteArray PDFStandardSecurityHandler::encryptUsingFilter(const QByteArray& data AES_data aes_data = prepareAES_data(data); if (!aes_data.paddedData.isEmpty()) { + QByteArray initializationVectorCopy = aes_data.initializationVector; 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); + encryptedData.prepend(initializationVectorCopy); } break; @@ -899,10 +911,10 @@ QByteArray PDFStandardSecurityHandler::encryptUsingFilter(const QByteArray& data AES_data aes_data = prepareAES_data(data); if (!aes_data.paddedData.isEmpty()) { + QByteArray initializationVectorCopy = aes_data.initializationVector; 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); + encryptedData.prepend(initializationVectorCopy); } break;