vcl/inc/pdf/PDFEncryptorR6.hxx                 |   25 +++++++++
 vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx |   64 +++++++++++++++++++++++++
 vcl/source/pdf/PDFEncryptorR6.cxx              |   42 ++++++++++++++++
 3 files changed, 131 insertions(+)

New commits:
commit 0c09e5fb6eca9d3128e69779a654c4c6f01d9c02
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Wed Nov 20 18:33:40 2024 +0900
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Sat Dec 21 05:59:17 2024 +0100

    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 <vmik...@collabora.com>
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178756
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>
    Tested-by: Tomaž Vajngerl <qui...@gmail.com>

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,

Reply via email to