diff options
author | Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> | 2024-11-20 18:33:40 +0900 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2024-12-21 05:59:17 +0100 |
commit | 0c09e5fb6eca9d3128e69779a654c4c6f01d9c02 (patch) | |
tree | db958f540f4e9fe10aca12317fcf2d693fd4a7a5 | |
parent | 41cd0fc6d9cbbf49acf4b13dcdcb0dd3332f9c52 (diff) |
pdf: add /Perm encrypted access permission algorithm
+ add test
Change-Id: Iba54dab6738c9707b37e434bab23ae286675436d
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176882
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178756
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Tested-by: Tomaž Vajngerl <quikee@gmail.com>
-rw-r--r-- | vcl/inc/pdf/PDFEncryptorR6.hxx | 25 | ||||
-rw-r--r-- | vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx | 64 | ||||
-rw-r--r-- | vcl/source/pdf/PDFEncryptorR6.cxx | 42 |
3 files changed, 131 insertions, 0 deletions
diff --git a/vcl/inc/pdf/PDFEncryptorR6.hxx b/vcl/inc/pdf/PDFEncryptorR6.hxx index e6efdeb5768d..70f7a3422f3a 100644 --- a/vcl/inc/pdf/PDFEncryptorR6.hxx +++ b/vcl/inc/pdf/PDFEncryptorR6.hxx @@ -76,6 +76,31 @@ VCL_DLLPUBLIC std::vector<sal_uInt8> decryptKey(const sal_uInt8* pUserPass, size std::vector<sal_uInt8>& U, std::vector<sal_uInt8>& UE); +/** Algorithm 13: Validating the permissions (Security handlers of revision 6) + * + * Described in ISO 32000-2:2020(E) - 7.6.4.4.12 + */ +VCL_DLLPUBLIC std::vector<sal_uInt8> decryptPerms(std::vector<sal_uInt8>& rPermsEncrypted, + std::vector<sal_uInt8>& rFileEncryptionKey); + +/** Algorithm 10 step f) + * + * Computing the encryption dictionary’s Perms (permissions) value (Security handlers of revision 6) + * + * Described in ISO 32000-2:2020(E) - 7.6.4.4.9 + */ +VCL_DLLPUBLIC std::vector<sal_uInt8> encryptPerms(std::vector<sal_uInt8>& rPerms, + std::vector<sal_uInt8>& rFileEncryptionKey); + +/** Algorithm 10 steps a) - e) + * + * Computing the encryption dictionary’s Perms (permissions) value (Security handlers of revision 6) + * + * Described in ISO 32000-2:2020(E) - 7.6.4.4.9 + */ +VCL_DLLPUBLIC std::vector<sal_uInt8> createPerms(sal_Int32 nAccessPermissions, + bool bEncryptMetadata); + } // end vcl::pdf /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx b/vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx index 6f1e868564e6..408735d12ca2 100644 --- a/vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx +++ b/vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx @@ -180,6 +180,70 @@ CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testGenerateOandOE) CPPUNIT_ASSERT_EQUAL( true, vcl::pdf::validateOwnerPassword(aOwnerPass.data(), aOwnerPass.size(), U, O)); } + +CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testPermsEncryption) +{ + // Encrypts file permissions for /Perms entry + + // We use a existing encrypted /Perm to validate the decryption and re-encryption + // algorithm works correctly. + + const sal_uInt8 pUserPass[] = { 'T', 'e', 's', 't' }; + + // U and UE taken from an PDF that was encrypted with "Test" as password + std::vector<sal_uInt8> U = parseHex("7BD210807A0277FECC52C261C442F02E1AD62C1A23553348B8F8AF7320" + "DC9978FAB7E65E1BF4CA76F4BE5E6D2AA8C7D5"); + std::vector<sal_uInt8> UE + = parseHex("67022D91A6BDF3179F488DC9658E54B78A0AD05C6A9C419DCD17A6941C151197"); + + // We decrypt the key, which is needed to decrypt and encrypt the /Perms content + std::vector<sal_uInt8> aKey = vcl::pdf::decryptKey(pUserPass, 4, U, UE); + + // Known encrypted /Perms content taken from the PDF + std::vector<sal_uInt8> aPermEncrypted = parseHex("6a2306c6e5e71a5bbd8404b07abec38f"); + + // Decrypt + std::vector<sal_uInt8> aPermsDecrpyted = vcl::pdf::decryptPerms(aPermEncrypted, aKey); + + // Encrypt again + std::vector<sal_uInt8> aPermsReencrypted = vcl::pdf::encryptPerms(aPermsDecrpyted, aKey); + + // Original encrypted /Perm content should be equal to decrypted and encrypted again + CPPUNIT_ASSERT_EQUAL(std::string("6a2306c6e5e71a5bbd8404b07abec38f"), + comphelper::hashToString(aPermsReencrypted)); + + // Always should be a,b,d + CPPUNIT_ASSERT_EQUAL(sal_uInt8('a'), aPermsDecrpyted[9]); + CPPUNIT_ASSERT_EQUAL(sal_uInt8('d'), aPermsDecrpyted[10]); + CPPUNIT_ASSERT_EQUAL(sal_uInt8('b'), aPermsDecrpyted[11]); + + // Metadata encrypted? - T or F + CPPUNIT_ASSERT_EQUAL(sal_uInt8('T'), aPermsDecrpyted[8]); + + // Decrypting the access permissions + sal_Int32 aAccessPermissions + = sal_Int32(aPermsDecrpyted[0]) + sal_Int32(aPermsDecrpyted[1] << 8) + + sal_Int32(aPermsDecrpyted[2] << 16) + sal_Int32(aPermsDecrpyted[3] << 24); + + // Taken from the PDF (/P entry) + sal_Int32 nExpectedAccessPermisssions = -4; + CPPUNIT_ASSERT_EQUAL(nExpectedAccessPermisssions, aAccessPermissions); + + // the whole decrypted /Perms content + CPPUNIT_ASSERT_EQUAL(std::string("fcffffffffffffff54616462bb609a8a"), + comphelper::hashToString(aPermsDecrpyted)); + + // Check the creating /Perm content from access permissions works correctly + std::vector<sal_uInt8> aPermsCreated = vcl::pdf::createPerms(nExpectedAccessPermisssions, true); + + // Last 12 bytes are random, so we shouldn't check those + std::vector<sal_uInt8> aPermsWithoutRandomBytes(aPermsCreated.begin(), + aPermsCreated.begin() + 12); + + // Should match the decrypted content + CPPUNIT_ASSERT_EQUAL(std::string("fcffffffffffffff54616462"), + comphelper::hashToString(aPermsWithoutRandomBytes)); +} } // end anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/vcl/source/pdf/PDFEncryptorR6.cxx b/vcl/source/pdf/PDFEncryptorR6.cxx index 9f0670f1fe1f..b3850a5929f8 100644 --- a/vcl/source/pdf/PDFEncryptorR6.cxx +++ b/vcl/source/pdf/PDFEncryptorR6.cxx @@ -129,6 +129,48 @@ std::vector<sal_uInt8> decryptKey(const sal_uInt8* pPass, size_t nLength, std::v return aFileEncryptionKey; } +/** Algorithm 13: Validating the permissions */ +std::vector<sal_uInt8> decryptPerms(std::vector<sal_uInt8>& rPermsEncrypted, + std::vector<sal_uInt8>& rFileEncryptionKey) +{ + std::vector<sal_uInt8> aPermsDecrpyted(rPermsEncrypted.size()); + std::vector<sal_uInt8> iv(IV_SIZE, 0); + comphelper::Decrypt aDecryptor(rFileEncryptionKey, iv, comphelper::CryptoType::AES_256_ECB); + aDecryptor.update(aPermsDecrpyted, rPermsEncrypted); + return aPermsDecrpyted; +} + +/** Algorithm 10 step f) */ +std::vector<sal_uInt8> encryptPerms(std::vector<sal_uInt8>& rPerms, + std::vector<sal_uInt8>& rFileEncryptionKey) +{ + std::vector<sal_uInt8> aPermsEncrypted(rPerms.size()); + std::vector<sal_uInt8> iv(IV_SIZE, 0); + comphelper::Encrypt aEncryptor(rFileEncryptionKey, iv, comphelper::CryptoType::AES_256_ECB); + aEncryptor.update(aPermsEncrypted, rPerms); + return aPermsEncrypted; +} + +/** Algorithm 10 steps a) - e) */ +std::vector<sal_uInt8> createPerms(sal_Int32 nAccessPermissions, bool bEncryptMetadata) +{ + std::vector<sal_uInt8> aPermsCreated; + generateBytes(aPermsCreated, 16); + aPermsCreated[0] = sal_uInt8(nAccessPermissions); + aPermsCreated[1] = sal_uInt8(nAccessPermissions >> 8); + aPermsCreated[2] = sal_uInt8(nAccessPermissions >> 16); + aPermsCreated[3] = sal_uInt8(nAccessPermissions >> 24); + aPermsCreated[4] = sal_uInt8(0xff); + aPermsCreated[5] = sal_uInt8(0xff); + aPermsCreated[6] = sal_uInt8(0xff); + aPermsCreated[7] = sal_uInt8(0xff); + aPermsCreated[8] = bEncryptMetadata ? 'T' : 'F'; // Encrypt metadata + aPermsCreated[9] = 'a'; + aPermsCreated[10] = 'd'; + aPermsCreated[11] = 'b'; + return aPermsCreated; +} + /** Algorithm 2.B: Computing a hash (revision 6 and later) */ std::vector<sal_uInt8> computeHashR6(const sal_uInt8* pPassword, size_t nPasswordLength, std::vector<sal_uInt8> const& rValidationSalt, |