include/vcl/pdfwriter.hxx                      |    6 +
 vcl/inc/pdf/EncryptionHashTransporter.hxx      |   29 +++++
 vcl/inc/pdf/IPDFEncryptor.hxx                  |    6 -
 vcl/inc/pdf/PDFEncryptorR6.hxx                 |   49 +++++++++
 vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx |  136 +++++++++++++++++++++++--
 vcl/qa/cppunit/pdfexport/pdfexport2.cxx        |   21 ---
 vcl/source/gdi/pdfwriter_impl.cxx              |   93 +++++++++++------
 vcl/source/pdf/PDFEncryptionInitialization.cxx |    1 
 vcl/source/pdf/PDFEncryptorR6.cxx              |  130 +++++++++++++++++++++++
 9 files changed, 404 insertions(+), 67 deletions(-)

New commits:
commit 1908f487a9be9c5b8c7b84a5ff5487e9df0ec1e0
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Thu Nov 21 11:33:47 2024 +0900
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Dec 9 13:24:00 2024 +0100

    pdf: implement PDFEncryptor for PDF (2.0) encryption V5R6
    
    PDFEncryptorR6 implements PDF 2.0 encryption (ver. 5 rev. 6) using
    AESv3 (256 bit key length). The PDFEncryptorR6 is enabled when the
    PDF output is PDF 2.0 (other versions have been deprecated, so it
    is the only one available in PDF 2.0).
    
    Change-Id: I27f270197910405b5c2378a3873d2495f52f3206
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176885
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/include/vcl/pdfwriter.hxx b/include/vcl/pdfwriter.hxx
index 6b91ead8bd89..e9d4326eb593 100644
--- a/include/vcl/pdfwriter.hxx
+++ b/include/vcl/pdfwriter.hxx
@@ -96,7 +96,11 @@ struct PDFEncryptionProperties
     // PDFDocInfo, Owner password and User password used the InitEncryption 
method which
     // implements the algorithms described in the PDF reference chapter 3.5: 
Encryption
     std::vector<sal_uInt8> OValue;
+    std::vector<sal_uInt8> OE; // needed by R6 algorithm
+
     std::vector<sal_uInt8> UValue;
+    std::vector<sal_uInt8> UE; // needed by R6 algorithm
+
     std::vector<sal_uInt8> EncryptionKey;
     std::vector<sal_uInt8> DocumentIdentifier;
 
@@ -108,7 +112,9 @@ struct PDFEncryptionProperties
     void clear()
     {
         OValue.clear();
+        OE.clear();
         UValue.clear();
+        UE.clear();
         EncryptionKey.clear();
     }
 
diff --git a/vcl/inc/pdf/EncryptionHashTransporter.hxx 
b/vcl/inc/pdf/EncryptionHashTransporter.hxx
index 596b6490570a..9b523f3f90f0 100644
--- a/vcl/inc/pdf/EncryptionHashTransporter.hxx
+++ b/vcl/inc/pdf/EncryptionHashTransporter.hxx
@@ -5,7 +5,6 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
  */
 
 #pragma once
@@ -28,8 +27,18 @@ namespace vcl::pdf
 */
 class EncryptionHashTransporter : public 
cppu::WeakImplHelper<css::beans::XMaterialHolder>
 {
+    // V2R3
     std::unique_ptr<comphelper::Hash> m_pDigest;
     std::vector<sal_uInt8> maOValue;
+
+    // V5R6
+    std::vector<sal_uInt8> mU;
+    std::vector<sal_uInt8> mUE;
+    std::vector<sal_uInt8> mO;
+    std::vector<sal_uInt8> mOE;
+    std::vector<sal_uInt8> maEncryptionKey;
+
+    // ID
     sal_IntPtr maID;
 
 public:
@@ -43,6 +52,24 @@ public:
 
     void invalidate() { m_pDigest.reset(); }
 
+    std::vector<sal_uInt8> getU() { return mU; }
+    void setU(std::vector<sal_uInt8> const& rU) { mU = rU; }
+
+    std::vector<sal_uInt8> getUE() { return mUE; }
+    void setUE(std::vector<sal_uInt8> const& rUE) { mUE = rUE; }
+
+    std::vector<sal_uInt8> getO() { return mO; }
+    void setO(std::vector<sal_uInt8> const& rO) { mO = rO; }
+
+    std::vector<sal_uInt8> getOE() { return mOE; }
+    void setOE(std::vector<sal_uInt8> const& rOE) { mOE = rOE; }
+
+    std::vector<sal_uInt8> getEncryptionKey() { return maEncryptionKey; }
+    void setEncryptionKey(std::vector<sal_uInt8> const& rEncryptionKey)
+    {
+        maEncryptionKey = rEncryptionKey;
+    }
+
     // XMaterialHolder
     virtual css::uno::Any SAL_CALL getMaterial() override { return 
css::uno::Any(sal_Int64(maID)); }
 
diff --git a/vcl/inc/pdf/IPDFEncryptor.hxx b/vcl/inc/pdf/IPDFEncryptor.hxx
index 744c8bcac761..f37ece808362 100644
--- a/vcl/inc/pdf/IPDFEncryptor.hxx
+++ b/vcl/inc/pdf/IPDFEncryptor.hxx
@@ -5,7 +5,6 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
  */
 
 #pragma once
@@ -60,7 +59,7 @@ public:
      * Depending on the encryption revision this may not be available. In that
      * case we can expect empty content.
      */
-    virtual std::vector<sal_uInt8> getEncryptedAccessPermissions()
+    virtual std::vector<sal_uInt8> 
getEncryptedAccessPermissions(std::vector<sal_uInt8>& /*rKey*/)
     {
         return std::vector<sal_uInt8>();
     }
@@ -80,6 +79,9 @@ public:
     /** Setup before we start encrypting - remembers the key */
     virtual void setupEncryption(std::vector<sal_uInt8>& rEncryptionKey, 
sal_Int32 nObject) = 0;
 
+    /** Calculate the size of the output (by default the same as input) */
+    virtual sal_uInt64 calculateSizeIncludingHeader(sal_uInt64 nSize) { return 
nSize; }
+
     /** Encrypts the input and stores into the output */
     virtual void encrypt(const void* pInput, sal_uInt64 nInputSize, 
std::vector<sal_uInt8>& rOutput,
                          sal_uInt64 nOutputSize)
diff --git a/vcl/inc/pdf/PDFEncryptorR6.hxx b/vcl/inc/pdf/PDFEncryptorR6.hxx
index 219796ccf0a5..a8812fefcd21 100644
--- a/vcl/inc/pdf/PDFEncryptorR6.hxx
+++ b/vcl/inc/pdf/PDFEncryptorR6.hxx
@@ -13,9 +13,12 @@
 #include <string_view>
 #include <vector>
 #include <vcl/dllapi.h>
+#include <pdf/IPDFEncryptor.hxx>
 
 namespace vcl::pdf
 {
+class EncryptionHashTransporter;
+
 /** Algorithm 2.B: Computing a hash (revision 6 and later)
  *
  * Described in ISO 32000-2:2020(E) - 7.6.4.3.4
@@ -107,6 +110,52 @@ VCL_DLLPUBLIC std::vector<sal_uInt8> createPerms(sal_Int32 
nAccessPermissions,
  */
 VCL_DLLPUBLIC size_t addPaddingToVector(std::vector<sal_uInt8>& rVector, 
size_t nBlockSize);
 
+class EncryptionContext;
+
+/** IPDFEncryptor implementation of PDF encryption version 5 revision 6 added 
in PDF 2.0
+ *
+ * The complete algorithm is defined in PDF 2.0 specification ISO 
32000-2:2020(E)
+ */
+class VCL_DLLPUBLIC PDFEncryptorR6 : public IPDFEncryptor
+{
+    std::unique_ptr<EncryptionContext> m_pEncryptionContext;
+    sal_Int32 m_nAccessPermissions = 0;
+
+public:
+    PDFEncryptorR6();
+    ~PDFEncryptorR6();
+
+    sal_Int32 getVersion() override { return 5; }
+    sal_Int32 getRevision() override { return 6; }
+    sal_Int32 getAccessPermissions() override { return m_nAccessPermissions; }
+    /** Key length - AES 256 bit */
+    sal_Int32 getKeyLength() override { return 256 / 8; }
+
+    std::vector<sal_uInt8> 
getEncryptedAccessPermissions(std::vector<sal_uInt8>& rKey) override;
+
+    static void initEncryption(EncryptionHashTransporter& 
rEncryptionHashTransporter,
+                               const OUString& i_rOwnerPassword, const 
OUString& i_rUserPassword);
+
+    bool prepareEncryption(
+        const css::uno::Reference<css::beans::XMaterialHolder>& 
xEncryptionMaterialHolder,
+        PDFEncryptionProperties& rProperties) override;
+
+    void setupKeysAndCheck(PDFEncryptionProperties& rProperties) override;
+
+    sal_uInt64 calculateSizeIncludingHeader(sal_uInt64 nSize) override;
+
+    void setupEncryption(std::vector<sal_uInt8>& rEncryptionKey, sal_Int32 
nObject) override;
+
+    void setupEncryptionWithIV(std::vector<sal_uInt8>& rInitvector, 
std::vector<sal_uInt8>& rIV);
+
+    /** Encrypts using Algorithm 1.A: Encryption of data using the AES 
algorithms
+     *
+     * Described in ISO 32000-2:2020(E) - 7.6.3.3
+     */
+    void encrypt(const void* pInput, sal_uInt64 nInputSize, 
std::vector<sal_uInt8>& rOutput,
+                 sal_uInt64 nOutputsSize) override;
+};
+
 } // 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 40a4eb94c1b4..17e0f10b9afe 100644
--- a/vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx
+++ b/vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx
@@ -10,21 +10,25 @@
 #include <sal/config.h>
 #include <config_oox.h>
 
-#include <algorithm>
-#include <memory>
-#include <string_view>
-
 #include <test/unoapi_test.hxx>
 #include <o3tl/string_view.hxx>
 
-#include <vcl/filter/PDFiumLibrary.hxx>
-#include <vcl/pdfread.hxx>
-#include <comphelper/propertyvalue.hxx>
-#include <cmath>
-
+#include <unotools/mediadescriptor.hxx>
 #include <comphelper/crypto/Crypto.hxx>
 #include <comphelper/hash.hxx>
 #include <comphelper/random.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+#include <vcl/filter/PDFiumLibrary.hxx>
+#include <vcl/pdfread.hxx>
+
+#include <com/sun/star/frame/XStorable.hpp>
+
+#include <algorithm>
+#include <memory>
+#include <string_view>
+#include <cmath>
 
 #include <pdf/PDFEncryptorR6.hxx>
 
@@ -38,6 +42,9 @@ namespace
 {
 class PDFEncryptionTest : public UnoApiTest
 {
+protected:
+    utl::MediaDescriptor maMediaDescriptor;
+
 public:
     PDFEncryptionTest()
         : UnoApiTest("/vcl/qa/cppunit/pdfexport/data/")
@@ -79,6 +86,48 @@ std::vector<sal_uInt8> parseHex(std::string_view rString)
     return aResult;
 }
 
+CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testEncryptionRoundtrip_PDF_1_7)
+{
+    loadFromFile(u"BrownFoxLazyDog.odt");
+
+    // Save PDF
+    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+    maMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+    uno::Sequence<beans::PropertyValue> aFilterData = 
comphelper::InitPropertySequence(
+        { { "SelectPdfVersion", uno::Any(sal_Int32(17)) },
+          { "EncryptFile", uno::Any(true) },
+          { "DocumentOpenPassword", uno::Any(u"secret"_ustr) } });
+    maMediaDescriptor["FilterData"] <<= aFilterData;
+    xStorable->storeToURL(maTempFile.GetURL(), 
maMediaDescriptor.getAsConstPropertyValueList());
+
+    // Load the exported result in PDFium
+    std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = 
parsePDFExport("secret"_ostr);
+    CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+    int nFileVersion = pPdfDocument->getFileVersion();
+    CPPUNIT_ASSERT_EQUAL(17, nFileVersion);
+}
+
+CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testEncryptionRoundtrip_PDF_2_0)
+{
+    loadFromFile(u"BrownFoxLazyDog.odt");
+
+    // Save PDF
+    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+    maMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+    uno::Sequence<beans::PropertyValue> aFilterData = 
comphelper::InitPropertySequence(
+        { { "SelectPdfVersion", uno::Any(sal_Int32(20)) },
+          { "EncryptFile", uno::Any(true) },
+          { "DocumentOpenPassword", uno::Any(u"secret"_ustr) } });
+    maMediaDescriptor["FilterData"] <<= aFilterData;
+    xStorable->storeToURL(maTempFile.GetURL(), 
maMediaDescriptor.getAsConstPropertyValueList());
+
+    // Load the exported result in PDFium
+    std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = 
parsePDFExport("secret"_ostr);
+    CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+    int nFileVersion = pPdfDocument->getFileVersion();
+    CPPUNIT_ASSERT_EQUAL(20, nFileVersion);
+}
+
 CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testComputeHashForR6)
 {
     const sal_uInt8 pOwnerPass[] = { 'T', 'e', 's', 't' };
@@ -257,6 +306,75 @@ CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testPadding)
         CPPUNIT_ASSERT_EQUAL(sal_uInt8(0x0B), aVector[i]);
 }
 
+CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testFileDecryption)
+{
+    std::vector<sal_uInt8> aData
+        = 
parseHex("d07efca5cce3c18fd8e344d45d826886d1774c5e1e310c971f8578924f848fc6");
+
+    std::vector<sal_uInt8> iv(aData.begin(), aData.begin() + 16);
+
+    CPPUNIT_ASSERT_EQUAL(std::string("d07efca5cce3c18fd8e344d45d826886"),
+                         comphelper::hashToString(iv));
+
+    std::vector<sal_uInt8> aEncryptedString(aData.begin() + 16, aData.end());
+
+    std::vector<sal_uInt8> U = 
parseHex("7BD210807A0277FECC52C261C442F02E1AD62C1A23553348B8F8AF7320"
+                                        
"DC9978FAB7E65E1BF4CA76F4BE5E6D2AA8C7D5");
+
+    std::vector<sal_uInt8> UE
+        = 
parseHex("67022D91A6BDF3179F488DC9658E54B78A0AD05C6A9C419DCD17A6941C151197");
+
+    const sal_uInt8 pUserPass[] = { 'T', 'e', 's', 't' };
+
+    CPPUNIT_ASSERT_EQUAL(true, vcl::pdf::validateUserPassword(pUserPass, 4, 
U));
+
+    std::vector<sal_uInt8> aDecryptedKey = vcl::pdf::decryptKey(pUserPass, 4, 
U, UE);
+
+    CPPUNIT_ASSERT_EQUAL(
+        
std::string("90e657b78c0315610f3f421bd396ff635fa8fe3cf2ea399e7e1ae23e6185b4fc"),
+        comphelper::hashToString(aDecryptedKey));
+
+    comphelper::Decrypt aDecrypt(aDecryptedKey, iv, 
comphelper::CryptoType::AES_256_CBC);
+
+    std::vector<sal_uInt8> aOutput(aEncryptedString.size(), 0);
+
+    aDecrypt.update(aOutput, aEncryptedString);
+
+    CPPUNIT_ASSERT_EQUAL(
+        std::string("656e2d47420b0b0b0b0b0b0b0b0b0b0b"), // 'en-GB' + padding 
0x0B = 11 chars
+        comphelper::hashToString(aOutput));
+}
+
+CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testFileEncryption)
+{
+    std::vector<sal_uInt8> aKey
+        = 
parseHex("90e657b78c0315610f3f421bd396ff635fa8fe3cf2ea399e7e1ae23e6185b4fc");
+    std::vector<sal_uInt8> aIV = parseHex("d07efca5cce3c18fd8e344d45d826886");
+
+    constexpr const auto aData = std::to_array<sal_uInt8>({ 'e', 'n', '-', 
'G', 'B' });
+
+    std::vector<sal_uInt8> aEncryptedBuffer;
+
+    vcl::pdf::PDFEncryptorR6 aEncryptor;
+    aEncryptor.setupEncryptionWithIV(aKey, aIV);
+    aEncryptor.encrypt(aData.data(), aData.size(), aEncryptedBuffer, 
aData.size());
+
+    CPPUNIT_ASSERT_EQUAL(
+        
std::string("d07efca5cce3c18fd8e344d45d826886d1774c5e1e310c971f8578924f848fc6"),
+        comphelper::hashToString(aEncryptedBuffer));
+
+    // Decrypt
+    std::vector<sal_uInt8> aEncryptedIV(aEncryptedBuffer.begin(), 
aEncryptedBuffer.begin() + 16);
+    std::vector<sal_uInt8> aEncryptedString(aEncryptedBuffer.begin() + 16, 
aEncryptedBuffer.end());
+    comphelper::Decrypt aDecrypt(aKey, aEncryptedIV, 
comphelper::CryptoType::AES_256_CBC);
+    std::vector<sal_uInt8> aOutputString(aEncryptedString.size(), 0);
+    aDecrypt.update(aOutputString, aEncryptedString);
+
+    CPPUNIT_ASSERT_EQUAL(
+        std::string("656e2d47420b0b0b0b0b0b0b0b0b0b0b"), // 'en-GB' + padding 
0x0B = 11 chars
+        comphelper::hashToString(aOutputString));
+}
+
 } // end anonymous namespace
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx 
b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
index 837924d81224..bc23346a955d 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
@@ -687,27 +687,6 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testVersion20)
     CPPUNIT_ASSERT_EQUAL(20, nFileVersion);
 }
 
-CPPUNIT_TEST_FIXTURE(PdfExportTest2, testEncryptionRoundtrip)
-{
-    mxComponent = loadFromDesktop("private:factory/swriter");
-
-    // Save PDF
-    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
-    aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-    uno::Sequence<beans::PropertyValue> aFilterData = 
comphelper::InitPropertySequence(
-        { { "SelectPdfVersion", uno::Any(sal_Int32(20)) },
-          { "EncryptFile", uno::Any(true) },
-          { "DocumentOpenPassword", uno::Any(u"secret"_ustr) } });
-    aMediaDescriptor["FilterData"] <<= aFilterData;
-    xStorable->storeToURL(maTempFile.GetURL(), 
aMediaDescriptor.getAsConstPropertyValueList());
-
-    // Load the exported result in PDFium
-    std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = 
parsePDFExport("secret"_ostr);
-    CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
-    int nFileVersion = pPdfDocument->getFileVersion();
-    CPPUNIT_ASSERT_EQUAL(20, nFileVersion);
-}
-
 // Check round-trip of importing and exporting the PDF with PDFium filter,
 // which imports the PDF document as multiple PDFs as graphic object.
 // Each page in the document has one PDF graphic object which content is
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx 
b/vcl/source/gdi/pdfwriter_impl.cxx
index 54f69a87166c..66edafd9bc4c 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -95,7 +95,7 @@
 #include <pdf/objectcopier.hxx>
 #include <pdf/pdfwriter_impl.hxx>
 #include <pdf/PdfConfig.hxx>
-#include <pdf/IPDFEncryptor.hxx>
+#include <pdf/PDFEncryptorR6.hxx>
 #include <pdf/PDFEncryptor.hxx>
 #include <o3tl/sorted_vector.hxx>
 #include <frozen/bits/defines.h>
@@ -142,6 +142,12 @@ void appendHex(sal_Int8 nInt, OStringBuffer& rBuffer)
     rBuffer.append( pHexDigits[ nInt & 15 ] );
 }
 
+void appendHexArray(sal_uInt8* pArray, size_t nSize, OStringBuffer& rBuffer)
+{
+    for (size_t i = 0; i < nSize; i++)
+        appendHex(pArray[i], rBuffer);
+}
+
 void appendName( std::u16string_view rStr, OStringBuffer& rBuffer )
 {
 // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, 
appendix C.1
@@ -1468,7 +1474,10 @@ PDFWriterImpl::PDFWriterImpl( const 
PDFWriter::PDFWriterContext& rContext,
 
     if (xEncryptionMaterialHolder.is())
     {
-        m_pPDFEncryptor.reset(new PDFEncryptor);
+        if (m_aContext.Version == PDFWriter::PDFVersion::PDF_2_0 || 
m_aContext.Version == PDFWriter::PDFVersion::PDF_A_4)
+            m_pPDFEncryptor.reset(new PDFEncryptorR6);
+        else
+            m_pPDFEncryptor.reset(new PDFEncryptor);
         m_pPDFEncryptor->prepareEncryption(xEncryptionMaterialHolder, 
m_aContext.Encryption);
     }
 
@@ -1627,10 +1636,10 @@ inline void 
PDFWriterImpl::appendUnicodeTextStringEncrypt( const OUString& rInSt
             *pCopy++ = static_cast<sal_uInt8>( aUnChar & 255 );
         }
         //encrypt in place
-        m_pPDFEncryptor->encrypt(m_vEncryptionBuffer.data(), nChars, 
m_vEncryptionBuffer, nChars);
+        std::vector<sal_uInt8> aNewBuffer(nChars);
+        m_pPDFEncryptor->encrypt(m_vEncryptionBuffer.data(), nChars, 
aNewBuffer, nChars);
         //now append, hexadecimal (appendHex), the encrypted result
-        for(int i = 0; i < nChars; i++)
-            appendHex( m_vEncryptionBuffer[i], rOutBuffer );
+        appendHexArray(aNewBuffer.data(), aNewBuffer.size(), rOutBuffer);
     }
     else
         PDFWriter::AppendUnicodeTextString(rInString, rOutBuffer);
@@ -1648,7 +1657,7 @@ inline void PDFWriterImpl::appendLiteralStringEncrypt( 
std::string_view rInStrin
         //encrypt the string in a buffer, then append it
         enableStringEncryption(nInObjectNumber);
         m_pPDFEncryptor->encrypt(rInString.data(), nChars, 
m_vEncryptionBuffer, nChars);
-        appendLiteralString( 
reinterpret_cast<char*>(m_vEncryptionBuffer.data()), nChars, rOutBuffer );
+        
appendLiteralString(reinterpret_cast<char*>(m_vEncryptionBuffer.data()), 
m_vEncryptionBuffer.size(), rOutBuffer);
     }
     else
         appendLiteralString( rInString.data(), nChars , rOutBuffer );
@@ -1747,38 +1756,41 @@ bool PDFWriterImpl::writeBufferBytes( const void* 
pBuffer, sal_uInt64 nBytes )
     }
 
     sal_uInt64 nWritten;
-    if( m_pCodec )
+    sal_uInt64 nActualSize = nBytes;
+
+    // we are compressing the stream
+    if (m_pCodec)
     {
         m_pCodec->Write( *m_pMemStream, static_cast<const 
sal_uInt8*>(pBuffer), static_cast<sal_uLong>(nBytes) );
         nWritten = nBytes;
     }
     else
     {
-        bool  buffOK = true;
+        // is it encrypted?
         bool bStreamEncryption = m_pPDFEncryptor && 
m_pPDFEncryptor->isStreamEncryptionEnabled();
         if (bStreamEncryption)
         {
-            m_vEncryptionBuffer.resize(nBytes);
-            if (buffOK)
-            {
-                m_pPDFEncryptor->encrypt(pBuffer, nBytes, m_vEncryptionBuffer, 
nBytes);
-            }
+            nActualSize = 
m_pPDFEncryptor->calculateSizeIncludingHeader(nActualSize);
+            m_vEncryptionBuffer.resize(nActualSize);
+            m_pPDFEncryptor->encrypt(pBuffer, nBytes, m_vEncryptionBuffer, 
nActualSize);
         }
 
-        const void* pWriteBuffer = (bStreamEncryption && buffOK) ? 
m_vEncryptionBuffer.data() : pBuffer;
-        m_DocDigest.update(static_cast<unsigned char const*>(pWriteBuffer), 
static_cast<sal_uInt32>(nBytes));
+        const void* pWriteBuffer = bStreamEncryption ? 
m_vEncryptionBuffer.data() : pBuffer;
+        m_DocDigest.update(static_cast<unsigned char const*>(pWriteBuffer), 
sal_uInt32(nActualSize));
 
-        if (m_aFile.write(pWriteBuffer, nBytes, nWritten) != osl::File::E_None)
+        if (m_aFile.write(pWriteBuffer, nActualSize, nWritten) != 
osl::File::E_None)
             nWritten = 0;
 
-        if( nWritten != nBytes )
+        if (nWritten != nActualSize)
         {
             m_aFile.close();
             m_bOpen = false;
+            return false;
         }
+        return true;
     }
 
-    return nWritten == nBytes;
+    return nWritten == nActualSize;
 }
 
 void PDFWriterImpl::newPage( double nPageWidth, double nPageHeight, 
PDFWriter::Orientation eOrientation )
@@ -6111,12 +6123,34 @@ sal_Int32 PDFWriterImpl::emitEncrypt()
         aWriter.startObject(nObject);
         aWriter.startDict();
         aWriter.write("/Filter", "/Standard");
-        aWriter.write("/V", 2);
-        aWriter.write("/Length", 128);
-        aWriter.write("/R", 3);
-        // emit the owner password, must not be encrypted
-        aWriter.writeHexArray("/O", rProperties.OValue.data(), 
rProperties.OValue.size());
-        aWriter.writeHexArray("/U", rProperties.UValue.data(), 
rProperties.UValue.size());
+        aWriter.write("/V", m_pPDFEncryptor->getVersion());
+        aWriter.write("/Length", m_pPDFEncryptor->getKeyLength() * 8);
+        aWriter.write("/R", m_pPDFEncryptor->getRevision());
+
+        if (m_pPDFEncryptor->getVersion() == 5 && 
m_pPDFEncryptor->getRevision() == 6)
+        {
+            // emit the owner password, must not be encrypted
+            aWriter.writeHexArray("/U", rProperties.UValue.data(), 
rProperties.UValue.size());
+            aWriter.writeHexArray("/UE", rProperties.UE.data(), 
rProperties.UE.size());
+            aWriter.writeHexArray("/O", rProperties.OValue.data(), 
rProperties.OValue.size());
+            aWriter.writeHexArray("/OE", rProperties.OE.data(), 
rProperties.OE.size());
+
+            // Encrypted perms
+            std::vector<sal_uInt8> aEncryptedPermissions = 
m_pPDFEncryptor->getEncryptedAccessPermissions(rProperties.EncryptionKey);
+            aWriter.writeHexArray("/Perms", aEncryptedPermissions.data(), 
aEncryptedPermissions.size());
+
+            // Write content filter stuff - to select we want AESv3 256bit
+            aWriter.write("/CF", "<</StdCF <</CFM /AESV3 /Length 256>>>>");
+            aWriter.write("/StmF", "/StdCF");
+            aWriter.write("/StrF", "/StdCF");
+            aWriter.write("/EncryptMetadata", " false ");
+        }
+        else
+        {
+            // emit the owner password, must not be encrypted
+            aWriter.writeHexArray("/U", rProperties.UValue.data(), 
rProperties.UValue.size());
+            aWriter.writeHexArray("/O", rProperties.OValue.data(), 
rProperties.OValue.size());
+        }
         aWriter.write("/P", m_pPDFEncryptor->getAccessPermissions());
         aWriter.endDict();
         aWriter.endObject();
@@ -9698,15 +9732,10 @@ bool PDFWriterImpl::writeBitmapObject( const 
BitmapEmit& rObject, bool bMask )
                     m_vEncryptionBuffer[nChar++] = rColor.GetBlue();
                 }
                 //encrypt the colorspace lookup table
-                m_pPDFEncryptor->encrypt(m_vEncryptionBuffer.data(), nChar, 
m_vEncryptionBuffer, nChar);
+                std::vector<sal_uInt8> aOutputBuffer(nChar);
+                m_pPDFEncryptor->encrypt(m_vEncryptionBuffer.data(), nChar, 
aOutputBuffer, nChar);
                 //now queue the data for output
-                nChar = 0;
-                for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); 
i++ )
-                {
-                    appendHex(m_vEncryptionBuffer[nChar++], aLine );
-                    appendHex(m_vEncryptionBuffer[nChar++], aLine );
-                    appendHex(m_vEncryptionBuffer[nChar++], aLine );
-                }
+                appendHexArray(aOutputBuffer.data(), aOutputBuffer.size(), 
aLine);
             }
             else //no encryption requested (PDF/A-1a program flow drops here)
             {
diff --git a/vcl/source/pdf/PDFEncryptionInitialization.cxx 
b/vcl/source/pdf/PDFEncryptionInitialization.cxx
index df06968fc290..c58898329eac 100644
--- a/vcl/source/pdf/PDFEncryptionInitialization.cxx
+++ b/vcl/source/pdf/PDFEncryptionInitialization.cxx
@@ -24,6 +24,7 @@ css::uno::Reference<css::beans::XMaterialHolder> 
initEncryption(const OUString&
 {
     rtl::Reference<EncryptionHashTransporter> pTransporter = new 
EncryptionHashTransporter;
     PDFEncryptor::initEncryption(*pTransporter, i_rOwnerPassword, 
i_rUserPassword);
+    PDFEncryptorR6::initEncryption(*pTransporter, i_rOwnerPassword, 
i_rUserPassword);
     return pTransporter;
 }
 
diff --git a/vcl/source/pdf/PDFEncryptorR6.cxx 
b/vcl/source/pdf/PDFEncryptorR6.cxx
index d3e7faa0141c..c16d7cb1a276 100644
--- a/vcl/source/pdf/PDFEncryptorR6.cxx
+++ b/vcl/source/pdf/PDFEncryptorR6.cxx
@@ -14,11 +14,14 @@
 #include <comphelper/hash.hxx>
 #include <comphelper/random.hxx>
 
+using namespace css;
+
 namespace vcl::pdf
 {
 namespace
 {
 constexpr size_t IV_SIZE = 16;
+constexpr size_t BLOCK_SIZE = 16;
 constexpr size_t KEY_SIZE = 32;
 constexpr size_t SALT_SIZE = 8;
 
@@ -124,7 +127,8 @@ std::vector<sal_uInt8> decryptKey(const sal_uInt8* pPass, 
size_t nLength, std::v
     comphelper::Decrypt aDecryptCBC(aKeyHash, iv, 
comphelper::CryptoType::AES_256_CBC);
     std::vector<sal_uInt8> aFileEncryptionKey(aEncryptedKey.size());
     sal_uInt32 nDecrypted = aDecryptCBC.update(aFileEncryptionKey, 
aEncryptedKey);
-    assert(nDecrypted > 0);
+    if (nDecrypted == 0)
+        return std::vector<sal_uInt8>();
     return aFileEncryptionKey;
 }
 
@@ -154,7 +158,7 @@ std::vector<sal_uInt8> encryptPerms(std::vector<sal_uInt8>& 
rPerms,
 std::vector<sal_uInt8> createPerms(sal_Int32 nAccessPermissions, bool 
bEncryptMetadata)
 {
     std::vector<sal_uInt8> aPermsCreated;
-    generateBytes(aPermsCreated, 16);
+    generateBytes(aPermsCreated, 16); // fill with random data - mainly for 
last 4 bytes
     aPermsCreated[0] = sal_uInt8(nAccessPermissions);
     aPermsCreated[1] = sal_uInt8(nAccessPermissions >> 8);
     aPermsCreated[2] = sal_uInt8(nAccessPermissions >> 16);
@@ -248,6 +252,128 @@ size_t addPaddingToVector(std::vector<sal_uInt8>& 
rVector, size_t nBlockSize)
     return nPaddedSize;
 }
 
+class VCL_DLLPUBLIC EncryptionContext
+{
+private:
+    std::vector<sal_uInt8> maKey;
+    std::vector<sal_uInt8> maInitVector;
+
+public:
+    EncryptionContext(std::vector<sal_uInt8> const& rKey, 
std::vector<sal_uInt8> const& rIV)
+        : maKey(rKey)
+        , maInitVector(rIV)
+    {
+    }
+
+    /** Algorithm 1.A: Encryption of data using the AES algorithms
+     *
+     **/
+    void encrypt(const void* pInput, sal_uInt64 nInputSize, 
std::vector<sal_uInt8>& rOutput)
+    {
+        comphelper::Encrypt aEncrypt(maKey, maInitVector, 
comphelper::CryptoType::AES_256_CBC);
+        const sal_uInt8* pInputBytes = static_cast<const sal_uInt8*>(pInput);
+        std::vector<sal_uInt8> aInput(pInputBytes, pInputBytes + nInputSize);
+        size_t nPaddedSize = addPaddingToVector(aInput, BLOCK_SIZE);
+        std::vector<sal_uInt8> aOutput(nPaddedSize);
+        aEncrypt.update(aOutput, aInput);
+        rOutput.resize(nPaddedSize + IV_SIZE);
+        std::copy(maInitVector.begin(), maInitVector.end(), rOutput.begin());
+        std::copy(aOutput.begin(), aOutput.end(), rOutput.begin() + IV_SIZE);
+    }
+};
+
+PDFEncryptorR6::PDFEncryptorR6() = default;
+PDFEncryptorR6::~PDFEncryptorR6() = default;
+
+std::vector<sal_uInt8> 
PDFEncryptorR6::getEncryptedAccessPermissions(std::vector<sal_uInt8>& rKey)
+{
+    std::vector<sal_uInt8> aPerms = createPerms(m_nAccessPermissions, false);
+    return encryptPerms(aPerms, rKey);
+}
+
+void PDFEncryptorR6::initEncryption(EncryptionHashTransporter& 
rEncryptionHashTransporter,
+                                    const OUString& rOwnerPassword, const 
OUString& rUserPassword)
+{
+    if (rUserPassword.isEmpty())
+        return;
+
+    std::vector<sal_uInt8> aEncryptionKey = vcl::pdf::generateKey();
+    rEncryptionHashTransporter.setEncryptionKey(aEncryptionKey);
+
+    std::vector<sal_uInt8> U;
+    std::vector<sal_uInt8> UE;
+
+    OString pUserPasswordUtf8 = OUStringToOString(rUserPassword, 
RTL_TEXTENCODING_UTF8);
+    vcl::pdf::generateUandUE(reinterpret_cast<const 
sal_uInt8*>(pUserPasswordUtf8.getStr()),
+                             pUserPasswordUtf8.getLength(), aEncryptionKey, U, 
UE);
+
+    rEncryptionHashTransporter.setU(U);
+    rEncryptionHashTransporter.setUE(UE);
+
+    OUString aOwnerPasswordToUse = rOwnerPassword.isEmpty() ? rUserPassword : 
rOwnerPassword;
+
+    std::vector<sal_uInt8> O;
+    std::vector<sal_uInt8> OE;
+
+    OString pOwnerPasswordUtf8 = OUStringToOString(aOwnerPasswordToUse, 
RTL_TEXTENCODING_UTF8);
+    vcl::pdf::generateOandOE(reinterpret_cast<const 
sal_uInt8*>(pOwnerPasswordUtf8.getStr()),
+                             pOwnerPasswordUtf8.getLength(), aEncryptionKey, 
U, O, OE);
+
+    rEncryptionHashTransporter.setO(O);
+    rEncryptionHashTransporter.setOE(OE);
+}
+
+bool PDFEncryptorR6::prepareEncryption(
+    const uno::Reference<beans::XMaterialHolder>& xEncryptionMaterialHolder,
+    vcl::PDFEncryptionProperties& rProperties)
+{
+    auto* pTransporter
+        = 
EncryptionHashTransporter::getEncHashTransporter(xEncryptionMaterialHolder);
+
+    if (!pTransporter)
+    {
+        rProperties.clear();
+        return false;
+    }
+
+    rProperties.UValue = pTransporter->getU();
+    rProperties.UE = pTransporter->getUE();
+    rProperties.OValue = pTransporter->getO();
+    rProperties.OE = pTransporter->getOE();
+    rProperties.EncryptionKey = pTransporter->getEncryptionKey();
+
+    return true;
+}
+
+void PDFEncryptorR6::setupKeysAndCheck(vcl::PDFEncryptionProperties& 
rProperties)
+{
+    m_nAccessPermissions = rProperties.getAccessPermissions();
+}
+
+sal_uInt64 PDFEncryptorR6::calculateSizeIncludingHeader(sal_uInt64 nSize)
+{
+    return IV_SIZE + comphelper::roundUp<sal_uInt64>(nSize, BLOCK_SIZE);
+}
+
+void PDFEncryptorR6::setupEncryption(std::vector<sal_uInt8>& rEncryptionKey, 
sal_Int32 /*nObject*/)
+{
+    std::vector<sal_uInt8> aInitVector;
+    generateBytes(aInitVector, IV_SIZE);
+    m_pEncryptionContext = std::make_unique<EncryptionContext>(rEncryptionKey, 
aInitVector);
+}
+
+void PDFEncryptorR6::setupEncryptionWithIV(std::vector<sal_uInt8>& 
rEncryptionKey,
+                                           std::vector<sal_uInt8>& rInitvector)
+{
+    m_pEncryptionContext = std::make_unique<EncryptionContext>(rEncryptionKey, 
rInitvector);
+}
+
+void PDFEncryptorR6::encrypt(const void* pInput, sal_uInt64 nInputSize,
+                             std::vector<sal_uInt8>& rOutput, sal_uInt64 
/*nOutputSize*/)
+{
+    m_pEncryptionContext->encrypt(pInput, nInputSize, rOutput);
+}
+
 } // end vcl::pdf
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to