summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomaž Vajngerl <tomaz.vajngerl@collabora.co.uk>2024-11-20 18:33:40 +0900
committerTomaž Vajngerl <quikee@gmail.com>2024-12-21 05:59:17 +0100
commit0c09e5fb6eca9d3128e69779a654c4c6f01d9c02 (patch)
treedb958f540f4e9fe10aca12317fcf2d693fd4a7a5
parent41cd0fc6d9cbbf49acf4b13dcdcb0dd3332f9c52 (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.hxx25
-rw-r--r--vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx64
-rw-r--r--vcl/source/pdf/PDFEncryptorR6.cxx42
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,