diff --git a/Pdf4QtLib/sources/pdfsecurityhandler.cpp b/Pdf4QtLib/sources/pdfsecurityhandler.cpp index f0bcc90..b34aef2 100644 --- a/Pdf4QtLib/sources/pdfsecurityhandler.cpp +++ b/Pdf4QtLib/sources/pdfsecurityhandler.cpp @@ -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(static_cast(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(decodedPerms.data())); + const uint32_t permissions = qFromLittleEndian(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(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(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; } diff --git a/Pdf4QtLib/sources/pdfsecurityhandler.h b/Pdf4QtLib/sources/pdfsecurityhandler.h index 3cc816d..8d1563f 100644 --- a/Pdf4QtLib/sources/pdfsecurityhandler.h +++ b/Pdf4QtLib/sources/pdfsecurityhandler.h @@ -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.