mirror of https://github.com/JakubMelka/PDF4QT.git
Encryption bugfixing (RC4)
This commit is contained in:
parent
e3fecc0568
commit
e001adc65b
|
@ -43,6 +43,26 @@ bool PDFDocument::operator==(const PDFDocument& other) const
|
|||
return m_pdfObjectStorage == other.m_pdfObjectStorage;
|
||||
}
|
||||
|
||||
QByteArray PDFDocument::getIdPart(size_t index) const
|
||||
{
|
||||
QByteArray id;
|
||||
const PDFObject& idArrayObject = getTrailerDictionary()->get("ID");
|
||||
if (idArrayObject.isArray())
|
||||
{
|
||||
const PDFArray* idArray = idArrayObject.getArray();
|
||||
if (idArray->getCount() > index)
|
||||
{
|
||||
const PDFObject& idArrayItem = idArray->getItem(index);
|
||||
if (idArrayItem.isString())
|
||||
{
|
||||
id = idArrayItem.getString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
QByteArray PDFDocument::getDecodedStream(const PDFStream* stream) const
|
||||
{
|
||||
return m_pdfObjectStorage.getDecodedStream(stream);
|
||||
|
|
|
@ -419,6 +419,10 @@ public:
|
|||
/// Returns info about the document (title, author, etc.)
|
||||
const PDFDocumentInfo* getInfo() const { return &m_info; }
|
||||
|
||||
/// Returns document id part with given index. If index is invalid,
|
||||
/// then empty id is returned.
|
||||
QByteArray getIdPart(size_t index) const;
|
||||
|
||||
/// If object is reference, the dereference attempt is performed
|
||||
/// and object is returned. If it is not a reference, then self
|
||||
/// is returned. If dereference attempt fails, then null object
|
||||
|
|
|
@ -1143,6 +1143,31 @@ void PDFDocumentBuilder::copyAnnotation(PDFObjectReference pageReference, PDFObj
|
|||
|
||||
void PDFDocumentBuilder::setSecurityHandler(PDFSecurityHandlerPointer handler)
|
||||
{
|
||||
if (!handler)
|
||||
{
|
||||
handler.reset(new PDFNoneSecurityHandler());
|
||||
}
|
||||
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Encrypt");
|
||||
|
||||
PDFObject encryptionDictionaryObject = handler->createEncryptionDictionaryObject();
|
||||
Q_ASSERT(!encryptionDictionaryObject.isReference());
|
||||
|
||||
if (!encryptionDictionaryObject.isNull())
|
||||
{
|
||||
encryptionDictionaryObject = PDFObject::createReference(addObject(encryptionDictionaryObject));
|
||||
}
|
||||
|
||||
objectBuilder << encryptionDictionaryObject;
|
||||
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObject updatedTrailerDictionary = objectBuilder.takeObject();
|
||||
m_storage.updateTrailerDictionary(qMove(updatedTrailerDictionary));
|
||||
|
||||
m_storage.setSecurityHandler(qMove(handler));
|
||||
}
|
||||
|
||||
|
@ -4297,13 +4322,7 @@ void PDFDocumentBuilder::removeEncryption()
|
|||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Encrypt");
|
||||
objectBuilder << PDFObject();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObject updatedTrailerDictionary = objectBuilder.takeObject();
|
||||
m_storage.updateTrailerDictionary(qMove(updatedTrailerDictionary));
|
||||
setSecurityHandler(nullptr);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -72,6 +72,12 @@ struct WrapString
|
|||
|
||||
}
|
||||
|
||||
WrapString(const QByteArray& byteArray) :
|
||||
string(byteArray)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QByteArray string;
|
||||
};
|
||||
|
||||
|
@ -421,9 +427,10 @@ public:
|
|||
/// \param annotationReference Annotation reference
|
||||
void copyAnnotation(PDFObjectReference pageReference, PDFObjectReference annotationReference);
|
||||
|
||||
/// Sets security handler to the object storage. Trailer dictionary is not
|
||||
/// updated and so must be updated manually.
|
||||
/// \param handler New security handler
|
||||
/// Sets security handler to the object storage. Trailer dictionary is also
|
||||
/// updated, so it is not needed to update it. Pass nullptr as handler to remove
|
||||
/// security.
|
||||
/// \param handler New security handler, or nullptr
|
||||
void setSecurityHandler(PDFSecurityHandlerPointer handler);
|
||||
|
||||
/* START GENERATED CODE */
|
||||
|
|
|
@ -319,8 +319,18 @@ PDFOperationResult PDFDocumentWriter::write(QIODevice* device, const PDFDocument
|
|||
|
||||
// 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<PDFDictionary>(qMove(trailerDictionary)));
|
||||
PDFDictionary newTrailerDictionary;
|
||||
|
||||
for (const char* entry : { "Size", "Root", "Encrypt", "Info", "ID"})
|
||||
{
|
||||
PDFObject object = trailerDictionary.get(entry);
|
||||
if (!object.isNull())
|
||||
{
|
||||
newTrailerDictionary.addEntry(PDFInplaceOrMemoryString(entry), qMove(object));
|
||||
}
|
||||
}
|
||||
|
||||
PDFObject trailerDictionaryObject = PDFObject::createDictionary(std::make_shared<PDFDictionary>(qMove(newTrailerDictionary)));
|
||||
|
||||
device->write("trailer");
|
||||
writeCRLF(device);
|
||||
|
|
|
@ -509,7 +509,7 @@ PDFSecurityHandlerPointer PDFSecurityHandler::createSecurityHandler(const PDFObj
|
|||
return PDFSecurityHandlerPointer(new PDFStandardSecurityHandler(qMove(handler)));
|
||||
}
|
||||
|
||||
void PDFSecurityHandler::fillEncryptionDictionary(PDFObjectFactory& factory)
|
||||
void PDFSecurityHandler::fillEncryptionDictionary(PDFObjectFactory& factory) const
|
||||
{
|
||||
factory.beginDictionaryItem("V");
|
||||
factory << PDFInteger(m_V);
|
||||
|
@ -621,15 +621,15 @@ void PDFSecurityHandler::fillEncryptionDictionary(PDFObjectFactory& factory)
|
|||
|
||||
// Store StmF, StrF, EFF
|
||||
factory.beginDictionaryItem("StmF");
|
||||
factory << stmfName;
|
||||
factory << WrapName(stmfName);
|
||||
factory.endDictionaryItem();
|
||||
|
||||
factory.beginDictionaryItem("StrF");
|
||||
factory << strfName;
|
||||
factory << WrapName(strfName);
|
||||
factory.endDictionaryItem();
|
||||
|
||||
factory.beginDictionaryItem("EFF");
|
||||
factory << effName;
|
||||
factory << WrapName(effName);
|
||||
factory.endDictionaryItem();
|
||||
}
|
||||
}
|
||||
|
@ -1141,6 +1141,8 @@ PDFObject PDFStandardSecurityHandler::createEncryptionDictionaryObject() const
|
|||
|
||||
factory.beginDictionary();
|
||||
|
||||
fillEncryptionDictionary(factory);
|
||||
|
||||
factory.beginDictionaryItem("Filter");
|
||||
factory << WrapName("Standard");
|
||||
factory.endDictionaryItem();
|
||||
|
@ -1150,21 +1152,21 @@ PDFObject PDFStandardSecurityHandler::createEncryptionDictionaryObject() const
|
|||
factory.endDictionaryItem();
|
||||
|
||||
factory.beginDictionaryItem("O");
|
||||
factory << m_O;
|
||||
factory << WrapString(m_O);
|
||||
factory.endDictionaryItem();
|
||||
|
||||
factory.beginDictionaryItem("U");
|
||||
factory << m_U;
|
||||
factory << WrapString(m_U);
|
||||
factory.endDictionaryItem();
|
||||
|
||||
if (m_R == 6)
|
||||
{
|
||||
factory.beginDictionaryItem("OE");
|
||||
factory << m_OE;
|
||||
factory << WrapString(m_OE);
|
||||
factory.endDictionaryItem();
|
||||
|
||||
factory.beginDictionaryItem("UE");
|
||||
factory << m_UE;
|
||||
factory << WrapString(m_UE);
|
||||
factory.endDictionaryItem();
|
||||
}
|
||||
|
||||
|
@ -1175,7 +1177,7 @@ PDFObject PDFStandardSecurityHandler::createEncryptionDictionaryObject() const
|
|||
if (m_R == 6)
|
||||
{
|
||||
factory.beginDictionaryItem("Perms");
|
||||
factory << m_Perms;
|
||||
factory << WrapString(m_Perms);
|
||||
factory.endDictionaryItem();
|
||||
}
|
||||
|
||||
|
@ -1689,6 +1691,7 @@ PDFSecurityHandlerPointer PDFSecurityHandlerFactory::createSecurityHandler(const
|
|||
|
||||
// Jakub Melka: create standard security handler, with given settings
|
||||
PDFStandardSecurityHandler* handler = new PDFStandardSecurityHandler();
|
||||
handler->m_ID = settings.id;
|
||||
|
||||
const bool isEncryptingEmbeddedFilesOnly = settings.encryptContents == EncryptContents::EmbeddedFiles;
|
||||
|
||||
|
@ -1820,7 +1823,8 @@ PDFSecurityHandlerPointer PDFSecurityHandlerFactory::createSecurityHandler(const
|
|||
}
|
||||
}
|
||||
|
||||
handler->authenticate([&settings](bool* b) { *b = false; return settings.ownerPassword; }, true);
|
||||
bool firstTry = true;
|
||||
handler->authenticate([&settings, &firstTry](bool* b) { *b = firstTry; firstTry = false; return settings.ownerPassword; }, true);
|
||||
Q_ASSERT(handler->getAuthorizationResult() == PDFSecurityHandler::AuthorizationResult::OwnerAuthorized);
|
||||
return PDFSecurityHandlerPointer(handler);
|
||||
}
|
||||
|
@ -1884,7 +1888,7 @@ int PDFSecurityHandlerFactory::getRevisionFromAlgorithm(Algorithm algorithm)
|
|||
return 0;
|
||||
|
||||
case RC4:
|
||||
return 3;
|
||||
return 4;
|
||||
|
||||
case AES_128:
|
||||
return 4;
|
||||
|
|
|
@ -199,7 +199,7 @@ protected:
|
|||
|
||||
/// Fills encryption dictionary with basic data
|
||||
/// \param factory Factory
|
||||
void fillEncryptionDictionary(PDFObjectFactory& factory);
|
||||
void fillEncryptionDictionary(PDFObjectFactory& factory) const;
|
||||
|
||||
/// Version of the encryption, shall be a number from 1 to 5, according the
|
||||
/// PDF specification. Other values are invalid.
|
||||
|
@ -404,6 +404,7 @@ public:
|
|||
QString userPassword;
|
||||
QString ownerPassword;
|
||||
uint32_t permissions = 0;
|
||||
QByteArray id;
|
||||
};
|
||||
|
||||
/// Creates security handler based on given settings. If security handler cannot
|
||||
|
|
|
@ -24,10 +24,11 @@
|
|||
namespace pdfviewer
|
||||
{
|
||||
|
||||
PDFEncryptionSettingsDialog::PDFEncryptionSettingsDialog(QWidget* parent) :
|
||||
PDFEncryptionSettingsDialog::PDFEncryptionSettingsDialog(QByteArray documentId, QWidget* parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::PDFEncryptionSettingsDialog),
|
||||
m_isUpdatingUi(false)
|
||||
m_isUpdatingUi(false),
|
||||
m_documentId(documentId)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
|
@ -175,6 +176,7 @@ void PDFEncryptionSettingsDialog::accept()
|
|||
encryptContents = pdf::PDFSecurityHandlerFactory::EmbeddedFiles;
|
||||
}
|
||||
|
||||
settings.id = m_documentId;
|
||||
settings.algorithm = static_cast<const pdf::PDFSecurityHandlerFactory::Algorithm>(ui->algorithmComboBox->currentData().toInt());
|
||||
settings.encryptContents = encryptContents;
|
||||
settings.userPassword = ui->userPasswordEdit->text();
|
||||
|
|
|
@ -38,9 +38,11 @@ class PDFEncryptionSettingsDialog : public QDialog
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PDFEncryptionSettingsDialog(QWidget* parent);
|
||||
explicit PDFEncryptionSettingsDialog(QByteArray documentId, QWidget* parent);
|
||||
virtual ~PDFEncryptionSettingsDialog() override;
|
||||
|
||||
pdf::PDFSecurityHandlerPointer getUpdatedSecurityHandler() const { return m_updatedSecurityHandler; }
|
||||
|
||||
public slots:
|
||||
virtual void accept() override;
|
||||
|
||||
|
@ -53,6 +55,7 @@ private:
|
|||
bool m_isUpdatingUi;
|
||||
std::map<QCheckBox*, pdf::PDFSecurityHandler::Permission> m_checkBoxToPermission;
|
||||
pdf::PDFSecurityHandlerPointer m_updatedSecurityHandler;
|
||||
QByteArray m_documentId;
|
||||
};
|
||||
|
||||
} // namespace pdfviewer
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "pdfdrawspacecontroller.h"
|
||||
#include "pdfwidgetutils.h"
|
||||
#include "pdfconstants.h"
|
||||
#include "pdfdocumentbuilder.h"
|
||||
|
||||
#include "pdfviewersettings.h"
|
||||
#include "pdfundoredomanager.h"
|
||||
|
@ -1133,16 +1134,6 @@ void PDFProgramController::onActionOptimizeTriggered()
|
|||
|
||||
void PDFProgramController::onActionEncryptionTriggered()
|
||||
{
|
||||
// Check that we have owner acces to the document
|
||||
const pdf::PDFSecurityHandler* securityHandler = m_pdfDocument->getStorage().getSecurityHandler();
|
||||
pdf::PDFSecurityHandler::AuthorizationResult authorizationResult = securityHandler->getAuthorizationResult();
|
||||
if (authorizationResult != pdf::PDFSecurityHandler::AuthorizationResult::OwnerAuthorized &&
|
||||
authorizationResult != pdf::PDFSecurityHandler::AuthorizationResult::NoAuthorizationRequired)
|
||||
{
|
||||
// Jakub Melka: we must authorize as owner, otherwise we can't continue,
|
||||
// because we don't have sufficient permissions.
|
||||
pdf::PDFSecurityHandlerPointer clonedSecurityHandler(securityHandler->clone());
|
||||
|
||||
auto queryPassword = [this](bool* ok)
|
||||
{
|
||||
QString result;
|
||||
|
@ -1151,6 +1142,15 @@ void PDFProgramController::onActionEncryptionTriggered()
|
|||
return result;
|
||||
};
|
||||
|
||||
// Check that we have owner access to the document
|
||||
const pdf::PDFSecurityHandler* securityHandler = m_pdfDocument->getStorage().getSecurityHandler();
|
||||
pdf::PDFSecurityHandler::AuthorizationResult authorizationResult = securityHandler->getAuthorizationResult();
|
||||
if (authorizationResult != pdf::PDFSecurityHandler::AuthorizationResult::OwnerAuthorized &&
|
||||
authorizationResult != pdf::PDFSecurityHandler::AuthorizationResult::NoAuthorizationRequired)
|
||||
{
|
||||
// Jakub Melka: we must authorize as owner, otherwise we can't continue,
|
||||
// because we don't have sufficient permissions.
|
||||
pdf::PDFSecurityHandlerPointer clonedSecurityHandler(securityHandler->clone());
|
||||
authorizationResult = clonedSecurityHandler->authenticate(queryPassword, true);
|
||||
|
||||
if (authorizationResult != pdf::PDFSecurityHandler::AuthorizationResult::OwnerAuthorized)
|
||||
|
@ -1167,8 +1167,30 @@ void PDFProgramController::onActionEncryptionTriggered()
|
|||
onDocumentModified(qMove(document));
|
||||
}
|
||||
|
||||
PDFEncryptionSettingsDialog dialog(m_mainWindow);
|
||||
dialog.exec();
|
||||
PDFEncryptionSettingsDialog dialog(m_pdfDocument->getIdPart(0), m_mainWindow);
|
||||
if (dialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
pdf::PDFSecurityHandlerPointer updatedSecurityHandler = dialog.getUpdatedSecurityHandler();
|
||||
|
||||
// Jakub Melka: If we changed encryption (password), recheck, that user doesn't
|
||||
// forgot (or accidentally entered wrong) password. So, we require owner authentization
|
||||
// to continue.
|
||||
if (updatedSecurityHandler->getMode() != pdf::EncryptionMode::None)
|
||||
{
|
||||
if (updatedSecurityHandler->authenticate(queryPassword, true) != pdf::PDFSecurityHandler::AuthorizationResult::OwnerAuthorized)
|
||||
{
|
||||
QMessageBox::critical(m_mainWindow, QApplication::applicationDisplayName(), tr("Reauthorization is required to change document encryption."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pdf::PDFDocumentBuilder builder(m_pdfDocument.data());
|
||||
builder.setSecurityHandler(qMove(updatedSecurityHandler));
|
||||
|
||||
pdf::PDFDocumentPointer pointer(new pdf::PDFDocument(builder.build()));
|
||||
pdf::PDFModifiedDocument document(qMove(pointer), m_optionalContentActivity, pdf::PDFModifiedDocument::Reset);
|
||||
onDocumentModified(qMove(document));
|
||||
}
|
||||
}
|
||||
|
||||
void PDFProgramController::onActionFitPageTriggered()
|
||||
|
|
|
@ -8935,37 +8935,13 @@ return rootNodeReference;</property>
|
|||
<QObject class="codegen::GeneratedFunction">
|
||||
<property name="objectName"></property>
|
||||
<property name="items">
|
||||
<QObject class="codegen::GeneratedAction">
|
||||
<property name="objectName"></property>
|
||||
<property name="items">
|
||||
<QObject class="codegen::GeneratedPDFObject">
|
||||
<property name="objectName"></property>
|
||||
<property name="items">
|
||||
<QObject class="codegen::GeneratedPDFObject">
|
||||
<property name="objectName"></property>
|
||||
<property name="items"/>
|
||||
<property name="dictionaryItemName">Encrypt</property>
|
||||
<property name="objectType">DictionaryItemSimple</property>
|
||||
<property name="value">PDFObject()</property>
|
||||
</QObject>
|
||||
</property>
|
||||
<property name="dictionaryItemName"></property>
|
||||
<property name="objectType">Dictionary</property>
|
||||
<property name="value"></property>
|
||||
</QObject>
|
||||
</property>
|
||||
<property name="actionType">CreateObject</property>
|
||||
<property name="variableName">updatedTrailerDictionary</property>
|
||||
<property name="variableType">_PDFObject</property>
|
||||
<property name="code"></property>
|
||||
</QObject>
|
||||
<QObject class="codegen::GeneratedAction">
|
||||
<property name="objectName"></property>
|
||||
<property name="items"/>
|
||||
<property name="actionType">Code</property>
|
||||
<property name="variableName"></property>
|
||||
<property name="variableType">_void</property>
|
||||
<property name="code">m_storage.updateTrailerDictionary(qMove(updatedTrailerDictionary));</property>
|
||||
<property name="code">setSecurityHandler(nullptr);</property>
|
||||
</QObject>
|
||||
</property>
|
||||
<property name="functionType">Structure</property>
|
||||
|
|
Loading…
Reference in New Issue