Public key encryption: fixing bugs

This commit is contained in:
Jakub Melka 2022-06-18 17:18:07 +02:00
parent 44fc1e021c
commit 08697b902a
2 changed files with 116 additions and 2 deletions

View File

@ -460,6 +460,22 @@ PDFSecurityHandlerPointer PDFSecurityHandler::createSecurityHandler(const PDFObj
typedHandler->m_filterDefault = it->second;
}
}
QString name = parseName(dictionary, "SubFilter", true, nullptr);
if (name == "adbe.pkcs7.s3")
{
typedHandler->m_pkcs7Type = PDFPublicKeySecurityHandler::PKCS7_Type::PKCS7_S3;
}
if (name == "adbe.pkcs7.s4")
{
typedHandler->m_pkcs7Type = PDFPublicKeySecurityHandler::PKCS7_Type::PKCS7_S4;
}
if (name == "adbe.pkcs7.s5")
{
typedHandler->m_pkcs7Type = PDFPublicKeySecurityHandler::PKCS7_Type::PKCS7_S5;
}
typedHandler->m_permissions = static_cast<uint32_t>(static_cast<int>(parseInt(dictionary, "P", false, 0)));
break;
}
@ -619,6 +635,22 @@ QByteArray PDFSecurityHandler::parseName(const PDFDictionary* dictionary, const
return nameObject.getString();
}
bool PDFSecurityHandler::parseBool(const PDFDictionary* dictionary, const char* key, bool required, bool defaultValue)
{
const PDFObject& intObject = dictionary->get(key);
if (!intObject.isBool())
{
if (required)
{
throw PDFException(PDFTranslationContext::tr("Invalid value for entry '%1' in encryption dictionary. Boolean expected.").arg(QString::fromLatin1(key)));
}
return defaultValue;
}
return intObject.getBool();
}
PDFInteger PDFSecurityHandler::parseInt(const PDFDictionary* dictionary, const char* key, bool required, PDFInteger defaultValue)
{
const PDFObject& intObject = dictionary->get(key);
@ -686,6 +718,9 @@ CryptFilter PDFSecurityHandler::parseCryptFilter(PDFInteger length, const PDFObj
// Recipients
filter.recipients = parseRecipients(cryptFilterDictionary);
// Encrypt metadata
filter.encryptMetadata = parseBool(cryptFilterDictionary, "EncryptMetadata", false, true);
return filter;
}
@ -1310,7 +1345,7 @@ PDFSecurityHandler::AuthorizationResult PDFStandardSecurityHandler::authenticate
}
// 2) Verify, that bytes 0-3 are valid permissions entry
const uint32_t permissions = qFromLittleEndian(*reinterpret_cast<const uint32_t*>(decodedPerms.data()));
const uint32_t permissions = qFromLittleEndian<uint32_t>(decodedPerms.data());
if (permissions != m_permissions)
{
throw PDFException(PDFTranslationContext::tr("Security permissions are manipulated. Can't open the document."));
@ -2282,6 +2317,12 @@ PDFSecurityHandler::AuthorizationResult PDFPublicKeySecurityHandler::authenticat
EVP_DigestFinal_ex(context, convertByteArrayToUcharPtr(digestBuffer), &size);
EVP_MD_CTX_free(context);
if (decryptedData.size() == 20 + sizeof(uint32_t))
{
// We shall set permissions
m_permissions = qFromLittleEndian<uint32_t>(decryptedData.data() + 20);
}
m_authorizationData.fileEncryptionKey = digestBuffer.left(m_keyLength / 8);
m_authorizationData.authorizationResult = AuthorizationResult::UserAuthorized;
return AuthorizationResult::UserAuthorized;
@ -2299,11 +2340,64 @@ PDFSecurityHandler::AuthorizationResult PDFPublicKeySecurityHandler::authenticat
bool PDFPublicKeySecurityHandler::isMetadataEncrypted() const
{
return true;
return m_filterDefault.encryptMetadata;
}
bool PDFPublicKeySecurityHandler::isAllowed(Permission permission) const
{
if (m_pkcs7Type == PKCS7_Type::PKCS7_S3)
{
// Jakub Melka: for S3, default standard permissions applies
return m_permissions & static_cast<uint32_t>(permission);
}
enum PermissionFlag : uint32_t
{
PKSH_Owner = 1 << 1,
PKSH_PrintLowResolution = 1 << 2,
PKSH_Modify = 1 << 3,
PKSH_CopyContent = 1 << 4,
PKSH_ModifyAnnotationsFillFormFields = 1 << 5,
PKSH_FillFormFields = 1 << 8,
PKSH_Assemble = 1 << 10,
PKSH_PrintHighResolution = 1 << 11
};
if (m_permissions & PKSH_Owner)
{
return true;
}
switch (permission)
{
case Permission::PrintLowResolution:
return m_permissions & PKSH_PrintLowResolution;
case Permission::Modify:
return m_permissions & PKSH_Modify;
case Permission::CopyContent:
return m_permissions & PKSH_CopyContent;
case Permission::ModifyInteractiveItems:
return m_permissions & PKSH_ModifyAnnotationsFillFormFields;
case Permission::ModifyFormFields:
return m_permissions & PKSH_FillFormFields;
case Permission::Accessibility:
return m_permissions & PKSH_CopyContent;
case Permission::Assemble:
return m_permissions & PKSH_Assemble;
case Permission::PrintHighResolution:
return m_permissions & PKSH_PrintHighResolution;
default:
break;
}
return false;
}

View File

@ -70,6 +70,7 @@ struct CryptFilter
AuthEvent authEvent = AuthEvent::DocOpen;
int keyLength = 0; ///< Key length in bytes
QByteArrayList recipients; ///< Recipients for public key security handler
bool encryptMetadata = true; ///< Encrypt metadata (for public key encryption)
};
class PDFSecurityHandler;
@ -200,6 +201,7 @@ public:
static PDFSecurityHandlerPointer createSecurityHandler(const PDFObject& encryptionDictionaryObject, const QByteArray& id);
protected:
static bool parseBool(const PDFDictionary* dictionary, const char* key, bool required, bool defaultValue = true);
static QByteArray parseName(const PDFDictionary* dictionary, const char* key, bool required, const char* defaultValue = nullptr);
static PDFInteger parseInt(const PDFDictionary* dictionary, const char* key, bool required, PDFInteger defaultValue = -1);
static CryptFilter parseCryptFilter(PDFInteger length, const PDFObject& object);
@ -406,6 +408,24 @@ public:
virtual bool isMetadataEncrypted() const override;
virtual bool isAllowed(Permission permission) const override;
virtual PDFObject createEncryptionDictionaryObject() const override;
private:
friend class PDFSecurityHandler;
friend class PDFSecurityHandlerFactory;
enum class PKCS7_Type
{
Unknown,
PKCS7_S3,
PKCS7_S4,
PKCS7_S5
};
/// What operations shall be permitted, when document is opened with user access.
uint32_t m_permissions = 0;
/// Type of the PKCS7 subfilter
PKCS7_Type m_pkcs7Type = PKCS7_Type::Unknown;
};
/// Factory, which creates security handler based on settings.