Encryption settings dialog, authorization as owner

This commit is contained in:
Jakub Melka
2021-05-24 19:29:02 +02:00
parent 759d5c7793
commit 936fe2fbe7
9 changed files with 342 additions and 27 deletions

View File

@@ -449,11 +449,6 @@ public:
/// header.
QByteArray getVersion() const;
private:
friend class PDFDocumentReader;
friend class PDFDocumentBuilder;
friend class PDFOptimizer;
explicit PDFDocument(PDFObjectStorage&& storage, PDFVersion version) :
m_pdfObjectStorage(std::move(storage))
{
@@ -462,6 +457,11 @@ private:
m_info.version = version;
}
private:
friend class PDFDocumentReader;
friend class PDFDocumentBuilder;
friend class PDFOptimizer;
/// Initialize data based on object in the storage.
/// Can throw exception if error is detected.
void init();

View File

@@ -508,6 +508,11 @@ PDFSecurityHandlerPointer PDFSecurityHandler::createSecurityHandler(const PDFObj
return PDFSecurityHandlerPointer(new PDFStandardSecurityHandler(qMove(handler)));
}
PDFSecurityHandler* PDFStandardSecurityHandler::clone() const
{
return new PDFStandardSecurityHandler(*this);
}
PDFSecurityHandler::AuthorizationResult PDFStandardSecurityHandler::authenticate(const std::function<QString(bool*)>& getPasswordCallback, bool authorizeOwnerOnly)
{
QByteArray password;
@@ -664,7 +669,7 @@ PDFSecurityHandler::AuthorizationResult PDFStandardSecurityHandler::authenticate
return AuthorizationResult::Failed;
}
password = adjustPassword(getPasswordCallback(&passwordObtained));
password = adjustPassword(getPasswordCallback(&passwordObtained), m_R);
}
return AuthorizationResult::Cancelled;
@@ -1375,11 +1380,11 @@ PDFStandardSecurityHandler::UserOwnerData_r6 PDFStandardSecurityHandler::parsePa
return result;
}
QByteArray PDFStandardSecurityHandler::adjustPassword(const QString& password)
QByteArray PDFStandardSecurityHandler::adjustPassword(const QString& password, int revision)
{
QByteArray result;
switch (m_R)
switch (revision)
{
case 2:
case 3:
@@ -1486,4 +1491,84 @@ bool PDFStandardSecurityHandler::isUnicodeMappedToNothing(ushort unicode)
}
}
PDFSecurityHandlerPointer PDFSecurityHandlerFactory::createSecurityHandler(const PDFSecurityHandlerFactory::SecuritySettings& settings)
{
return nullptr;
}
int PDFSecurityHandlerFactory::getPasswordOptimalEntropy()
{
return 128;
}
int PDFSecurityHandlerFactory::getPasswordEntropy(const QString& password, Algorithm algorithm)
{
if (algorithm == None)
{
return 0;
}
QByteArray adjustedPassword = PDFStandardSecurityHandler::adjustPassword(password, getRevisionFromAlgorithm(algorithm));
if (adjustedPassword.isEmpty())
{
return 0;
}
const int length = adjustedPassword.length();
std::sort(adjustedPassword.begin(), adjustedPassword.end());
int charCount = 0;
char lastChar = adjustedPassword.front();
PDFReal entropy = 0.0;
for (int i = 0; i < length; ++i)
{
const char currentChar = adjustedPassword[i];
if (currentChar == lastChar)
{
++charCount;
}
else
{
const PDFReal probability = PDFReal(charCount) / PDFReal(length);
entropy += -probability * std::log2(probability);
charCount = 1;
lastChar = currentChar;
}
}
// Jakub Melka: last character
const PDFReal probability = PDFReal(charCount) / PDFReal(length);
entropy += -probability * std::log2(probability);
return entropy * length;
}
int PDFSecurityHandlerFactory::getRevisionFromAlgorithm(Algorithm algorithm)
{
switch (algorithm)
{
case None:
return 0;
case RC4:
return 3;
case AES_128:
return 4;
case AES_256:
return 6;
default:
Q_ASSERT(false);
break;
}
return 0;
}
} // namespace pdf

View File

@@ -109,6 +109,9 @@ public:
/// Retrieve encryption mode (none/standard encryption/custom)
virtual EncryptionMode getMode() const = 0;
/// Creates a clone of this object
virtual PDFSecurityHandler* clone() const = 0;
/// Performs authentication of the document content access. First, algorithm should check,
/// if empty password allows document access (so, for example, only owner password is provided).
/// If this fails, function \p getPasswordCallback is called to retrieve user entered password.
@@ -214,6 +217,7 @@ class PDFNoneSecurityHandler : public PDFSecurityHandler
{
public:
virtual EncryptionMode getMode() const override { return EncryptionMode::None; }
virtual PDFSecurityHandler* clone() const override { return new PDFNoneSecurityHandler(); }
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; }
@@ -231,6 +235,7 @@ class PDFStandardSecurityHandler : public PDFSecurityHandler
{
public:
virtual EncryptionMode getMode() const override { return EncryptionMode::Standard; }
virtual PDFSecurityHandler* clone() const 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 decryptByFilter(const QByteArray& data, const QByteArray& filterName, PDFObjectReference reference) const override;
@@ -249,6 +254,9 @@ public:
QByteArray fileEncryptionKey;
};
/// Adjusts the password according to the PDF specification
static QByteArray adjustPassword(const QString& password, int revision);
private:
friend PDFSecurityHandlerPointer PDFSecurityHandler::createSecurityHandler(const PDFObject& encryptionDictionaryObject, const QByteArray& id);
@@ -288,8 +296,6 @@ private:
/// Parses parts of the user/owner data (U/O values of the encryption dictionary)
UserOwnerData_r6 parseParts(const QByteArray& data) const;
/// Adjusts the password according to the PDF specification
QByteArray adjustPassword(const QString& password);
/// Decrypts data using specified filter. This function can be called only, if authorization was successfull.
/// \param data Data to be decrypted
@@ -356,6 +362,57 @@ private:
AuthorizationData m_authorizationData;
};
/// Factory, which creates security handler based on settings.
class Pdf4QtLIBSHARED_EXPORT PDFSecurityHandlerFactory
{
public:
enum Algorithm
{
None,
RC4,
AES_128,
AES_256
};
enum EncryptContents
{
All,
AllExceptMetadata,
EmbeddedFiles
};
struct SecuritySettings
{
Algorithm algorithm = None;
EncryptContents encryptContents = All;
QString userPassword;
QString ownerPassword;
uint32_t permissions = 0;
};
/// Creates security handler based on given settings. If security handler cannot
/// be created, then nullptr is returned.
/// \param settings Security handler settings
static PDFSecurityHandlerPointer createSecurityHandler(const SecuritySettings& settings);
/// Returns optimal number of bits (entropy) for strong password
static int getPasswordOptimalEntropy();
/// Calculates password entropy (number of bits), can be used
/// for ranking the password security. Encryption algorithm must be also
/// considered, because password is adjusted according to the specification
/// before it's entropy is being computed.
/// \param password Password to be scored
/// \param algorithm Encryption algorithm
static int getPasswordEntropy(const QString& password, Algorithm algorithm);
/// Returns revision number of standard security handler for a given
/// algorithm. If algorithm is invalid or None, zero is returned.
/// \param algorithm Algorithm
static int getRevisionFromAlgorithm(Algorithm algorithm);
};
} // namespace pdf