vcl/Library_vcl.mk                           |   12 
 vcl/inc/pdf/EncryptionHashTransporter.hxx    |   56 +++
 vcl/inc/pdf/PDFEncryptor.hxx                 |   85 +++++
 vcl/inc/pdf/pdfwriter_impl.hxx               |   62 ----
 vcl/source/gdi/pdfwriter_impl.cxx            |  296 ++++++--------------
 vcl/source/gdi/pdfwriter_impl2.cxx           |  348 -----------------------
 vcl/source/pdf/EncryptionHashTransporter.cxx |   55 +++
 vcl/source/pdf/PDFEncryptor.cxx              |  393 +++++++++++++++++++++++++++
 8 files changed, 701 insertions(+), 606 deletions(-)

New commits:
commit 2143961a14c39be485f3a99dfe82b0c0a215b41e
Author:     Tomaž Vajngerl <[email protected]>
AuthorDate: Mon Nov 4 16:45:25 2024 +0100
Commit:     Tomaž Vajngerl <[email protected]>
CommitDate: Mon Nov 4 21:51:57 2024 +0100

    pdf: move encryption methods into PDFEncryptor files
    
    Moving more PDF encryption implementation into common encryption
    files. This will make it easier to add new encryption later on as
    the code will be in one place.
    
    Change-Id: Id40c2f876a2e92bb8db27024a0e251befc5059e5
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176030
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <[email protected]>

diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index f42741fe6bf3..13d5a0076aca 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -501,6 +501,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
     vcl/source/pdf/EncryptionHashTransporter \
     vcl/source/pdf/ExternalPDFStreams \
     vcl/source/pdf/PDFiumTools \
+    vcl/source/pdf/PDFEncryptor \
     vcl/source/pdf/PdfConfig \
     vcl/source/pdf/ResourceDict \
     vcl/source/pdf/Matrix3 \
diff --git a/vcl/inc/pdf/EncryptionHashTransporter.hxx 
b/vcl/inc/pdf/EncryptionHashTransporter.hxx
index 0da0db3d8654..3ed1aa375561 100644
--- a/vcl/inc/pdf/EncryptionHashTransporter.hxx
+++ b/vcl/inc/pdf/EncryptionHashTransporter.hxx
@@ -24,7 +24,7 @@ namespace vcl::pdf
    clear text passwords down till they can be used in PDFWriter. Unfortunately
    the MD5 sum of the password (which is needed to create the PDF encryption 
key)
    is not sufficient, since an MD5 digest cannot be created in an arbitrary 
state
-   which would be needed in PDFWriterImpl::computeEncryptionKey.
+   which would be needed in computeEncryptionKey.
 */
 class EncryptionHashTransporter : public 
cppu::WeakImplHelper<css::beans::XMaterialHolder>
 {
diff --git a/vcl/inc/pdf/PDFEncryptor.hxx b/vcl/inc/pdf/PDFEncryptor.hxx
index c93fc0bec9e9..e1602f11723f 100644
--- a/vcl/inc/pdf/PDFEncryptor.hxx
+++ b/vcl/inc/pdf/PDFEncryptor.hxx
@@ -11,11 +11,50 @@
 #pragma once
 
 #include <rtl/cipher.h>
+#include <string_view>
+#include <vcl/pdfwriter.hxx>
 
 namespace vcl::pdf
 {
+class EncryptionHashTransporter;
+
 constexpr sal_Int32 ENCRYPTED_PWD_SIZE = 32;
 
+// the maximum password length
+constexpr sal_Int32 MD5_DIGEST_SIZE = 16;
+
+// security 128 bit
+constexpr sal_Int32 SECUR_128BIT_KEY = 16;
+
+// maximum length of MD5 digest input, in step 2 of algorithm 3.1
+// PDF spec ver. 1.4: see there for details
+constexpr sal_Int32 MAXIMUM_RC4_KEY_LENGTH = SECUR_128BIT_KEY + 3 + 2;
+
+void padPassword(std::u16string_view i_rPassword, sal_uInt8* o_pPaddedPW);
+
+/* algorithm 3.2: compute an encryption key */
+bool computeEncryptionKey(vcl::pdf::EncryptionHashTransporter*,
+                          vcl::PDFWriter::PDFEncryptionProperties& 
io_rProperties,
+                          sal_Int32 i_nAccessPermissions);
+
+/* algorithm 3.3: computing the encryption dictionary'ss owner password value 
( /O ) */
+bool computeODictionaryValue(const sal_uInt8* i_pPaddedOwnerPassword,
+                             const sal_uInt8* i_pPaddedUserPassword,
+                             std::vector<sal_uInt8>& io_rOValue, sal_Int32 
i_nKeyLength);
+
+/* algorithm 3.4 or 3.5: computing the encryption dictionary's user password 
value ( /U ) revision 2 or 3 of the standard security handler */
+bool computeUDictionaryValue(vcl::pdf::EncryptionHashTransporter* 
i_pTransporter,
+                             vcl::PDFWriter::PDFEncryptionProperties& 
io_rProperties,
+                             sal_Int32 i_nKeyLength, sal_Int32 
i_nAccessPermissions);
+
+void computeDocumentIdentifier(std::vector<sal_uInt8>& o_rIdentifier,
+                               const vcl::PDFWriter::PDFDocInfo& i_rDocInfo,
+                               const OString& i_rCString1,
+                               const css::util::DateTime& rCreationMetaDate, 
OString& o_rCString2);
+
+sal_Int32 computeAccessPermissions(const 
vcl::PDFWriter::PDFEncryptionProperties& i_rProperties,
+                                   sal_Int32& o_rKeyLength, sal_Int32& 
o_rRC4KeyLength);
+
 class PDFEncryptor
 {
 public:
diff --git a/vcl/inc/pdf/pdfwriter_impl.hxx b/vcl/inc/pdf/pdfwriter_impl.hxx
index 68c49841baf6..92e5452e0c70 100644
--- a/vcl/inc/pdf/pdfwriter_impl.hxx
+++ b/vcl/inc/pdf/pdfwriter_impl.hxx
@@ -67,18 +67,9 @@ class FontSubsetInfo;
 class ZCodec;
 struct BitStreamState;
 namespace vcl::font { class PhysicalFontFace; }
-namespace vcl::pdf { class EncryptionHashTransporter; }
 class SvStream;
 class SvMemoryStream;
 
-// the maximum password length
-constexpr sal_Int32 MD5_DIGEST_SIZE = 16;
-// security 128 bit
-constexpr sal_Int32 SECUR_128BIT_KEY = 16;
-// maximum length of MD5 digest input, in step 2 of algorithm 3.1
-// PDF spec ver. 1.4: see there for details
-constexpr sal_Int32 MAXIMUM_RC4_KEY_LENGTH = SECUR_128BIT_KEY + 3 + 2;
-
 namespace vcl::pdf
 {
 
@@ -1096,32 +1087,6 @@ private:
     methods for PDF security
 
     pad a password according  algorithm 3.2, step 1 */
-    static void padPassword( std::u16string_view i_rPassword, sal_uInt8* 
o_pPaddedPW );
-    /* algorithm 3.2: compute an encryption key */
-    static bool computeEncryptionKey( vcl::pdf::EncryptionHashTransporter*,
-                                      vcl::PDFWriter::PDFEncryptionProperties& 
io_rProperties,
-                                      sal_Int32 i_nAccessPermissions
-                                     );
-    /* algorithm 3.3: computing the encryption dictionary'ss owner password 
value ( /O ) */
-    static bool computeODictionaryValue( const sal_uInt8* 
i_pPaddedOwnerPassword, const sal_uInt8* i_pPaddedUserPassword,
-                                         std::vector< sal_uInt8 >& io_rOValue,
-                                         sal_Int32 i_nKeyLength
-                                        );
-    /* algorithm 3.4 or 3.5: computing the encryption dictionary's user 
password value ( /U ) revision 2 or 3 of the standard security handler */
-    static bool computeUDictionaryValue( vcl::pdf::EncryptionHashTransporter* 
i_pTransporter,
-                                         
vcl::PDFWriter::PDFEncryptionProperties& io_rProperties,
-                                         sal_Int32 i_nKeyLength,
-                                         sal_Int32 i_nAccessPermissions
-                                        );
-
-    static void computeDocumentIdentifier( std::vector< sal_uInt8 >& 
o_rIdentifier,
-                                           const vcl::PDFWriter::PDFDocInfo& 
i_rDocInfo,
-                                           const OString& i_rCString1,
-                                           const css::util::DateTime& 
rCreationMetaDate,
-                                           OString& o_rCString2
-                                          );
-    static sal_Int32 computeAccessPermissions( const 
vcl::PDFWriter::PDFEncryptionProperties& i_rProperties,
-                                               sal_Int32& o_rKeyLength, 
sal_Int32& o_rRC4KeyLength );
     void setupDocInfo();
     bool prepareEncryption( const css::uno::Reference< 
css::beans::XMaterialHolder >& );
 
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx 
b/vcl/source/gdi/pdfwriter_impl.cxx
index 906fc6ba9d31..06b596a1df41 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -1492,108 +1492,6 @@ OString PDFWriter::GetDateTime()
     return aRet.makeStringAndClear();
 }
 
-void PDFWriterImpl::computeDocumentIdentifier( std::vector< sal_uInt8 >& 
o_rIdentifier,
-                                               const 
vcl::PDFWriter::PDFDocInfo& i_rDocInfo,
-                                               const OString& i_rCString1,
-                                               const css::util::DateTime& 
rCreationMetaDate,
-                                               OString& o_rCString2
-                                               )
-{
-    o_rIdentifier.clear();
-
-    //build the document id
-    OString aInfoValuesOut;
-    OStringBuffer aID( 1024 );
-    if( !i_rDocInfo.Title.isEmpty() )
-        PDFWriter::AppendUnicodeTextString(i_rDocInfo.Title, aID);
-    if( !i_rDocInfo.Author.isEmpty() )
-        PDFWriter::AppendUnicodeTextString(i_rDocInfo.Author, aID);
-    if( !i_rDocInfo.Subject.isEmpty() )
-        PDFWriter::AppendUnicodeTextString(i_rDocInfo.Subject, aID);
-    if( !i_rDocInfo.Keywords.isEmpty() )
-        PDFWriter::AppendUnicodeTextString(i_rDocInfo.Keywords, aID);
-    if( !i_rDocInfo.Creator.isEmpty() )
-        PDFWriter::AppendUnicodeTextString(i_rDocInfo.Creator, aID);
-    if( !i_rDocInfo.Producer.isEmpty() )
-        PDFWriter::AppendUnicodeTextString(i_rDocInfo.Producer, aID);
-
-    TimeValue aTVal, aGMT;
-    oslDateTime aDT;
-    aDT.NanoSeconds = rCreationMetaDate.NanoSeconds;
-    aDT.Seconds = rCreationMetaDate.Seconds;
-    aDT.Minutes = rCreationMetaDate.Minutes;
-    aDT.Hours = rCreationMetaDate.Hours;
-    aDT.Day = rCreationMetaDate.Day;
-    aDT.Month = rCreationMetaDate.Month;
-    aDT.Year = rCreationMetaDate.Year;
-
-    osl_getSystemTime( &aGMT );
-    osl_getLocalTimeFromSystemTime( &aGMT, &aTVal );
-    OStringBuffer aCreationMetaDateString(64);
-
-    // i59651: we fill the Metadata date string as well, if PDF/A is requested
-    // according to ISO 19005-1:2005 6.7.3 the date is corrected for
-    // local time zone offset UTC only, whereas Acrobat 8 seems
-    // to use the localtime notation only
-    // according to a recommendation in XMP Specification (Jan 2004, page 75)
-    // the Acrobat way seems the right approach
-    aCreationMetaDateString.append(
-        OStringChar(static_cast<char>('0' + ((aDT.Year/1000)%10)) )
-        + OStringChar(static_cast<char>('0' + ((aDT.Year/100)%10)) )
-        + OStringChar(static_cast<char>('0' + ((aDT.Year/10)%10)) )
-        + OStringChar(static_cast<char>('0' + ((aDT.Year)%10)) )
-        + "-"
-        + OStringChar(static_cast<char>('0' + ((aDT.Month/10)%10)) )
-        + OStringChar(static_cast<char>('0' + ((aDT.Month)%10)) )
-        + "-"
-        + OStringChar(static_cast<char>('0' + ((aDT.Day/10)%10)) )
-        + OStringChar(static_cast<char>('0' + ((aDT.Day)%10)) )
-        + "T"
-        + OStringChar(static_cast<char>('0' + ((aDT.Hours/10)%10)) )
-        + OStringChar(static_cast<char>('0' + ((aDT.Hours)%10)) )
-        + ":"
-        + OStringChar(static_cast<char>('0' + ((aDT.Minutes/10)%10)) )
-        + OStringChar(static_cast<char>('0' + ((aDT.Minutes)%10)) )
-        + ":"
-        + OStringChar(static_cast<char>('0' + ((aDT.Seconds/10)%10)) )
-        + OStringChar(static_cast<char>('0' + ((aDT.Seconds)%10)) ));
-
-    sal_uInt32 nDelta = 0;
-    if( aGMT.Seconds > aTVal.Seconds )
-    {
-        nDelta = aGMT.Seconds-aTVal.Seconds;
-        aCreationMetaDateString.append( "-" );
-    }
-    else if( aGMT.Seconds < aTVal.Seconds )
-    {
-        nDelta = aTVal.Seconds-aGMT.Seconds;
-        aCreationMetaDateString.append( "+" );
-    }
-    else
-    {
-        aCreationMetaDateString.append( "Z" );
-
-    }
-    if( nDelta )
-    {
-        aCreationMetaDateString.append(
-            OStringChar(static_cast<char>('0' + ((nDelta/36000)%10)) )
-            + OStringChar(static_cast<char>('0' + ((nDelta/3600)%10)) )
-            + ":"
-            + OStringChar(static_cast<char>('0' + ((nDelta/600)%6)) )
-            + OStringChar(static_cast<char>('0' + ((nDelta/60)%10)) ));
-    }
-    aID.append( i_rCString1.getStr(), i_rCString1.getLength() );
-
-    aInfoValuesOut = aID.makeStringAndClear();
-    o_rCString2 = aCreationMetaDateString.makeStringAndClear();
-
-    ::comphelper::Hash aDigest(::comphelper::HashType::MD5);
-    aDigest.update(reinterpret_cast<unsigned char const*>(&aGMT), 
sizeof(aGMT));
-    aDigest.update(reinterpret_cast<unsigned char 
const*>(aInfoValuesOut.getStr()), aInfoValuesOut.getLength());
-    //the binary form of the doc id is needed for encryption stuff
-    o_rIdentifier = aDigest.finalize();
-}
 
 /* i12626 methods */
 /*
diff --git a/vcl/source/gdi/pdfwriter_impl2.cxx 
b/vcl/source/gdi/pdfwriter_impl2.cxx
index 496676be5c74..95f8f6f72c0e 100644
--- a/vcl/source/gdi/pdfwriter_impl2.cxx
+++ b/vcl/source/gdi/pdfwriter_impl2.cxx
@@ -16,7 +16,6 @@
  *   except in compliance with the License. You may obtain a copy of
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
-
 #include <pdf/pdfwriter_impl.hxx>
 #include <pdf/EncryptionHashTransporter.hxx>
 
@@ -43,7 +42,6 @@
 #include <com/sun/star/graphic/XGraphicProvider.hpp>
 #include <com/sun/star/beans/XMaterialHolder.hpp>
 
-
 #include <o3tl/unit_conversion.hxx>
 #include <osl/diagnose.h>
 #include <vcl/skia/SkiaHelper.hxx>
@@ -1171,270 +1169,6 @@ bool PDFWriterImpl::prepareEncryption( const 
uno::Reference< beans::XMaterialHol
     return bSuccess;
 }
 
-sal_Int32 PDFWriterImpl::computeAccessPermissions( const 
vcl::PDFWriter::PDFEncryptionProperties& i_rProperties,
-                                                   sal_Int32& o_rKeyLength, 
sal_Int32& o_rRC4KeyLength )
-{
-    /*
-    2) compute the access permissions, in numerical form
-
-    the default value depends on the revision 2 (40 bit) or 3 (128 bit 
security):
-    - for 40 bit security the unused bit must be set to 1, since they are not 
used
-    - for 128 bit security the same bit must be preset to 0 and set later if 
needed
-    according to the table 3.15, pdf v 1.4 */
-    sal_Int32 nAccessPermissions = 0xfffff0c0;
-
-    o_rKeyLength = SECUR_128BIT_KEY;
-    o_rRC4KeyLength = 16; // for this value see PDF spec v 1.4, algorithm 3.1 
step 4, where n is 16,
-                          // thus maximum permitted value is 16
-
-    nAccessPermissions |= ( i_rProperties.CanPrintTheDocument ) ?  1 << 2 : 0;
-    nAccessPermissions |= ( i_rProperties.CanModifyTheContent ) ? 1 << 3 : 0;
-    nAccessPermissions |= ( i_rProperties.CanCopyOrExtract ) ?   1 << 4 : 0;
-    nAccessPermissions |= ( i_rProperties.CanAddOrModify ) ? 1 << 5 : 0;
-    nAccessPermissions |= ( i_rProperties.CanFillInteractive ) ?         1 << 
8 : 0;
-    nAccessPermissions |= ( i_rProperties.CanExtractForAccessibility ) ? 1 << 
9 : 0;
-    nAccessPermissions |= ( i_rProperties.CanAssemble ) ?                1 << 
10 : 0;
-    nAccessPermissions |= ( i_rProperties.CanPrintFull ) ?               1 << 
11 : 0;
-    return nAccessPermissions;
-}
-
-/*************************************************************
-begin i12626 methods
-
-Implements Algorithm 3.2, step 1 only
-*/
-void PDFWriterImpl::padPassword( std::u16string_view i_rPassword, sal_uInt8* 
o_pPaddedPW )
-{
-    // get ansi-1252 version of the password string CHECKIT ! i12626
-    OString aString( OUStringToOString( i_rPassword, RTL_TEXTENCODING_MS_1252 
) );
-
-    //copy the string to the target
-    sal_Int32 nToCopy = ( aString.getLength() < ENCRYPTED_PWD_SIZE ) ? 
aString.getLength() : ENCRYPTED_PWD_SIZE;
-    sal_Int32 nCurrentChar;
-
-    for( nCurrentChar = 0; nCurrentChar < nToCopy; nCurrentChar++ )
-        o_pPaddedPW[nCurrentChar] = static_cast<sal_uInt8>( 
aString[nCurrentChar] );
-
-    //pad it with standard byte string
-    sal_Int32 i,y;
-    for( i = nCurrentChar, y = 0 ; i < ENCRYPTED_PWD_SIZE; i++, y++ )
-        o_pPaddedPW[i] = PDFEncryptor::s_nPadString[y];
-}
-
-/**********************************
-Algorithm 3.2  Compute the encryption key used
-
-step 1 should already be done before calling, the paThePaddedPassword 
parameter should contain
-the padded password and must be 32 byte long, the encryption key is returned 
into the paEncryptionKey parameter,
-it will be 16 byte long for 128 bit security; for 40 bit security only the 
first 5 bytes are used
-
-TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. 
See spec.
-
-*/
-bool PDFWriterImpl::computeEncryptionKey( EncryptionHashTransporter* 
i_pTransporter, vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, 
sal_Int32 i_nAccessPermissions )
-{
-    bool bSuccess = true;
-    ::std::vector<unsigned char> nMD5Sum;
-
-    // transporter contains an MD5 digest with the padded user password already
-    ::comphelper::Hash *const pDigest = i_pTransporter->getUDigest();
-    if (pDigest)
-    {
-        //step 3
-        if( ! io_rProperties.OValue.empty() )
-            pDigest->update(io_rProperties.OValue.data(), 
io_rProperties.OValue.size());
-        else
-            bSuccess = false;
-        //Step 4
-        sal_uInt8 nPerm[4];
-
-        nPerm[0] = static_cast<sal_uInt8>(i_nAccessPermissions);
-        nPerm[1] = static_cast<sal_uInt8>( i_nAccessPermissions >> 8 );
-        nPerm[2] = static_cast<sal_uInt8>( i_nAccessPermissions >> 16 );
-        nPerm[3] = static_cast<sal_uInt8>( i_nAccessPermissions >> 24 );
-
-        pDigest->update(nPerm, sizeof(nPerm));
-
-        //step 5, get the document ID, binary form
-        pDigest->update(io_rProperties.DocumentIdentifier.data(), 
io_rProperties.DocumentIdentifier.size());
-        //get the digest
-        nMD5Sum = pDigest->finalize();
-
-        //step 6, only if 128 bit
-        for (sal_Int32 i = 0; i < 50; i++)
-        {
-            nMD5Sum = ::comphelper::Hash::calculateHash(nMD5Sum.data(), 
nMD5Sum.size(), ::comphelper::HashType::MD5);
-        }
-    }
-    else
-        bSuccess = false;
-
-    i_pTransporter->invalidate();
-
-    //Step 7
-    if( bSuccess )
-    {
-        io_rProperties.EncryptionKey.resize( MAXIMUM_RC4_KEY_LENGTH );
-        for( sal_Int32 i = 0; i < MD5_DIGEST_SIZE; i++ )
-            io_rProperties.EncryptionKey[i] = nMD5Sum[i];
-    }
-    else
-        io_rProperties.EncryptionKey.clear();
-
-    return bSuccess;
-}
-
-/**********************************
-Algorithm 3.3  Compute the encryption dictionary /O value, save into the class 
data member
-the step numbers down here correspond to the ones in PDF v.1.4 specification
-*/
-bool PDFWriterImpl::computeODictionaryValue( const sal_uInt8* 
i_pPaddedOwnerPassword,
-                                             const sal_uInt8* 
i_pPaddedUserPassword,
-                                             std::vector< sal_uInt8 >& 
io_rOValue,
-                                             sal_Int32 i_nKeyLength
-                                             )
-{
-    bool bSuccess = true;
-
-    io_rOValue.resize( ENCRYPTED_PWD_SIZE );
-
-    rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
-    if (aCipher)
-    {
-        //step 1 already done, data is in i_pPaddedOwnerPassword
-        //step 2
-
-        ::std::vector<unsigned char> nMD5Sum(::comphelper::Hash::calculateHash(
-            i_pPaddedOwnerPassword, ENCRYPTED_PWD_SIZE, 
::comphelper::HashType::MD5));
-        //step 3, only if 128 bit
-        if (i_nKeyLength == SECUR_128BIT_KEY)
-        {
-            sal_Int32 i;
-            for (i = 0; i < 50; i++)
-            {
-                nMD5Sum = ::comphelper::Hash::calculateHash(nMD5Sum.data(), 
nMD5Sum.size(), ::comphelper::HashType::MD5);
-            }
-        }
-        //Step 4, the key is in nMD5Sum
-        //step 5 already done, data is in i_pPaddedUserPassword
-        //step 6
-        if (rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
-                                    nMD5Sum.data(), i_nKeyLength , nullptr, 0 )
-            == rtl_Cipher_E_None)
-        {
-            // encrypt the user password using the key set above
-            rtl_cipher_encodeARCFOUR( aCipher, i_pPaddedUserPassword, 
ENCRYPTED_PWD_SIZE, // the data to be encrypted
-                                      io_rOValue.data(), 
sal_Int32(io_rOValue.size()) ); //encrypted data
-            //Step 7, only if 128 bit
-            if( i_nKeyLength == SECUR_128BIT_KEY )
-            {
-                sal_uInt32 i;
-                size_t y;
-                sal_uInt8 nLocalKey[ SECUR_128BIT_KEY ]; // 16 = 128 bit key
-
-                for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1
-                {
-                    for( y = 0; y < sizeof( nLocalKey ); y++ )
-                        nLocalKey[y] = static_cast<sal_uInt8>( nMD5Sum[y] ^ i 
);
-
-                    if (rtl_cipher_initARCFOUR( aCipher, 
rtl_Cipher_DirectionEncode,
-                                                nLocalKey, SECUR_128BIT_KEY, 
nullptr, 0 ) //destination data area, on init can be NULL
-                        != rtl_Cipher_E_None)
-                    {
-                        bSuccess = false;
-                        break;
-                    }
-                    rtl_cipher_encodeARCFOUR( aCipher, io_rOValue.data(), 
sal_Int32(io_rOValue.size()), // the data to be encrypted
-                                              io_rOValue.data(), 
sal_Int32(io_rOValue.size()) ); // encrypted data, can be the same as the 
input, encrypt "in place"
-                    //step 8, store in class data member
-                }
-            }
-        }
-        else
-            bSuccess = false;
-    }
-    else
-        bSuccess = false;
-
-    if( aCipher )
-        rtl_cipher_destroyARCFOUR( aCipher );
-
-    if( ! bSuccess )
-        io_rOValue.clear();
-    return bSuccess;
-}
-
-/**********************************
-Algorithms 3.4 and 3.5  Compute the encryption dictionary /U value, save into 
the class data member, revision 2 (40 bit) or 3 (128 bit)
-*/
-bool PDFWriterImpl::computeUDictionaryValue( EncryptionHashTransporter* 
i_pTransporter,
-                                             
vcl::PDFWriter::PDFEncryptionProperties& io_rProperties,
-                                             sal_Int32 i_nKeyLength,
-                                             sal_Int32 i_nAccessPermissions
-                                             )
-{
-    bool bSuccess = true;
-
-    io_rProperties.UValue.resize( ENCRYPTED_PWD_SIZE );
-
-    ::comphelper::Hash aDigest(::comphelper::HashType::MD5);
-    rtlCipher aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
-    if (aCipher)
-    {
-        //step 1, common to both 3.4 and 3.5
-        if( computeEncryptionKey( i_pTransporter, io_rProperties, 
i_nAccessPermissions ) )
-        {
-            // prepare encryption key for object
-            for( sal_Int32 i = i_nKeyLength, y = 0; y < 5 ; y++ )
-                io_rProperties.EncryptionKey[i++] = 0;
-
-            //or 3.5, for 128 bit security
-            //step6, initialize the last 16 bytes of the encrypted user 
password to 0
-            for(sal_uInt32 i = MD5_DIGEST_SIZE; i < 
sal_uInt32(io_rProperties.UValue.size()); i++)
-                io_rProperties.UValue[i] = 0;
-            //steps 2 and 3
-            aDigest.update(PDFEncryptor::s_nPadString, 
sizeof(PDFEncryptor::s_nPadString));
-            aDigest.update(io_rProperties.DocumentIdentifier.data(), 
io_rProperties.DocumentIdentifier.size());
-
-            ::std::vector<unsigned char> const nMD5Sum(aDigest.finalize());
-            //Step 4
-            rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
-                                    io_rProperties.EncryptionKey.data(), 
SECUR_128BIT_KEY, nullptr, 0 ); //destination data area
-            rtl_cipher_encodeARCFOUR( aCipher, nMD5Sum.data(), nMD5Sum.size(), 
// the data to be encrypted
-                                      io_rProperties.UValue.data(), 
SECUR_128BIT_KEY ); //encrypted data, stored in class data member
-            //step 5
-            sal_uInt32 i;
-            size_t y;
-            sal_uInt8 nLocalKey[SECUR_128BIT_KEY];
-
-            for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1
-            {
-                for( y = 0; y < sizeof( nLocalKey ) ; y++ )
-                    nLocalKey[y] = static_cast<sal_uInt8>( 
io_rProperties.EncryptionKey[y] ^ i );
-
-                rtl_cipher_initARCFOUR( aCipher, rtl_Cipher_DirectionEncode,
-                                        nLocalKey, SECUR_128BIT_KEY, // key 
and key length
-                                        nullptr, 0 ); //destination data area, 
on init can be NULL
-                rtl_cipher_encodeARCFOUR( aCipher, 
io_rProperties.UValue.data(), SECUR_128BIT_KEY, // the data to be encrypted
-                                          io_rProperties.UValue.data(), 
SECUR_128BIT_KEY ); // encrypted data, can be the same as the input, encrypt 
"in place"
-            }
-        }
-        else
-            bSuccess = false;
-    }
-    else
-        bSuccess = false;
-
-    if( aCipher )
-        rtl_cipher_destroyARCFOUR( aCipher );
-
-    if( ! bSuccess )
-        io_rProperties.UValue.clear();
-    return bSuccess;
-}
-
-/* end i12626 methods */
-
 const tools::Long unsetRun[256] =
 {
     8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */
diff --git a/vcl/source/pdf/PDFEncryptor.cxx b/vcl/source/pdf/PDFEncryptor.cxx
new file mode 100644
index 000000000000..dc309fbbe59e
--- /dev/null
+++ b/vcl/source/pdf/PDFEncryptor.cxx
@@ -0,0 +1,393 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * 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/.
+ *
+ */
+
+#include <pdf/PDFEncryptor.hxx>
+#include <pdf/EncryptionHashTransporter.hxx>
+#include <pdf/pdfwriter_impl.hxx>
+#include <comphelper/hash.hxx>
+
+namespace vcl::pdf
+{
+/*************************************************************
+begin i12626 methods
+
+Implements Algorithm 3.2, step 1 only
+*/
+void padPassword(std::u16string_view i_rPassword, sal_uInt8* o_pPaddedPW)
+{
+    // get ansi-1252 version of the password string CHECKIT ! i12626
+    OString aString(OUStringToOString(i_rPassword, RTL_TEXTENCODING_MS_1252));
+
+    //copy the string to the target
+    sal_Int32 nToCopy
+        = (aString.getLength() < ENCRYPTED_PWD_SIZE) ? aString.getLength() : 
ENCRYPTED_PWD_SIZE;
+    sal_Int32 nCurrentChar;
+
+    for (nCurrentChar = 0; nCurrentChar < nToCopy; nCurrentChar++)
+        o_pPaddedPW[nCurrentChar] = 
static_cast<sal_uInt8>(aString[nCurrentChar]);
+
+    //pad it with standard byte string
+    sal_Int32 i, y;
+    for (i = nCurrentChar, y = 0; i < ENCRYPTED_PWD_SIZE; i++, y++)
+        o_pPaddedPW[i] = PDFEncryptor::s_nPadString[y];
+}
+
+/**********************************
+Algorithm 3.2  Compute the encryption key used
+
+step 1 should already be done before calling, the paThePaddedPassword 
parameter should contain
+the padded password and must be 32 byte long, the encryption key is returned 
into the paEncryptionKey parameter,
+it will be 16 byte long for 128 bit security; for 40 bit security only the 
first 5 bytes are used
+
+TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. 
See spec.
+
+*/
+bool computeEncryptionKey(EncryptionHashTransporter* i_pTransporter,
+                          vcl::PDFWriter::PDFEncryptionProperties& 
io_rProperties,
+                          sal_Int32 i_nAccessPermissions)
+{
+    bool bSuccess = true;
+    ::std::vector<unsigned char> nMD5Sum;
+
+    // transporter contains an MD5 digest with the padded user password already
+    ::comphelper::Hash* const pDigest = i_pTransporter->getUDigest();
+    if (pDigest)
+    {
+        //step 3
+        if (!io_rProperties.OValue.empty())
+            pDigest->update(io_rProperties.OValue.data(), 
io_rProperties.OValue.size());
+        else
+            bSuccess = false;
+        //Step 4
+        sal_uInt8 nPerm[4];
+
+        nPerm[0] = static_cast<sal_uInt8>(i_nAccessPermissions);
+        nPerm[1] = static_cast<sal_uInt8>(i_nAccessPermissions >> 8);
+        nPerm[2] = static_cast<sal_uInt8>(i_nAccessPermissions >> 16);
+        nPerm[3] = static_cast<sal_uInt8>(i_nAccessPermissions >> 24);
+
+        pDigest->update(nPerm, sizeof(nPerm));
+
+        //step 5, get the document ID, binary form
+        pDigest->update(io_rProperties.DocumentIdentifier.data(),
+                        io_rProperties.DocumentIdentifier.size());
+        //get the digest
+        nMD5Sum = pDigest->finalize();
+
+        //step 6, only if 128 bit
+        for (sal_Int32 i = 0; i < 50; i++)
+        {
+            nMD5Sum = ::comphelper::Hash::calculateHash(nMD5Sum.data(), 
nMD5Sum.size(),
+                                                        
::comphelper::HashType::MD5);
+        }
+    }
+    else
+        bSuccess = false;
+
+    i_pTransporter->invalidate();
+
+    //Step 7
+    if (bSuccess)
+    {
+        io_rProperties.EncryptionKey.resize(MAXIMUM_RC4_KEY_LENGTH);
+        for (sal_Int32 i = 0; i < MD5_DIGEST_SIZE; i++)
+            io_rProperties.EncryptionKey[i] = nMD5Sum[i];
+    }
+    else
+        io_rProperties.EncryptionKey.clear();
+
+    return bSuccess;
+}
+
+/**********************************
+Algorithm 3.3  Compute the encryption dictionary /O value, save into the class 
data member
+the step numbers down here correspond to the ones in PDF v.1.4 specification
+*/
+bool computeODictionaryValue(const sal_uInt8* i_pPaddedOwnerPassword,
+                             const sal_uInt8* i_pPaddedUserPassword,
+                             std::vector<sal_uInt8>& io_rOValue, sal_Int32 
i_nKeyLength)
+{
+    bool bSuccess = true;
+
+    io_rOValue.resize(ENCRYPTED_PWD_SIZE);
+
+    rtlCipher aCipher = rtl_cipher_createARCFOUR(rtl_Cipher_ModeStream);
+    if (aCipher)
+    {
+        //step 1 already done, data is in i_pPaddedOwnerPassword
+        //step 2
+
+        ::std::vector<unsigned char> nMD5Sum(::comphelper::Hash::calculateHash(
+            i_pPaddedOwnerPassword, ENCRYPTED_PWD_SIZE, 
::comphelper::HashType::MD5));
+        //step 3, only if 128 bit
+        if (i_nKeyLength == SECUR_128BIT_KEY)
+        {
+            sal_Int32 i;
+            for (i = 0; i < 50; i++)
+            {
+                nMD5Sum = ::comphelper::Hash::calculateHash(nMD5Sum.data(), 
nMD5Sum.size(),
+                                                            
::comphelper::HashType::MD5);
+            }
+        }
+        //Step 4, the key is in nMD5Sum
+        //step 5 already done, data is in i_pPaddedUserPassword
+        //step 6
+        if (rtl_cipher_initARCFOUR(aCipher, rtl_Cipher_DirectionEncode, 
nMD5Sum.data(),
+                                   i_nKeyLength, nullptr, 0)
+            == rtl_Cipher_E_None)
+        {
+            // encrypt the user password using the key set above
+            rtl_cipher_encodeARCFOUR(
+                aCipher, i_pPaddedUserPassword, ENCRYPTED_PWD_SIZE, // the 
data to be encrypted
+                io_rOValue.data(), sal_Int32(io_rOValue.size())); //encrypted 
data
+            //Step 7, only if 128 bit
+            if (i_nKeyLength == SECUR_128BIT_KEY)
+            {
+                sal_uInt32 i;
+                size_t y;
+                sal_uInt8 nLocalKey[SECUR_128BIT_KEY]; // 16 = 128 bit key
+
+                for (i = 1; i <= 19; i++) // do it 19 times, start with 1
+                {
+                    for (y = 0; y < sizeof(nLocalKey); y++)
+                        nLocalKey[y] = static_cast<sal_uInt8>(nMD5Sum[y] ^ i);
+
+                    if (rtl_cipher_initARCFOUR(aCipher, 
rtl_Cipher_DirectionEncode, nLocalKey,
+                                               SECUR_128BIT_KEY, nullptr,
+                                               0) //destination data area, on 
init can be NULL
+                        != rtl_Cipher_E_None)
+                    {
+                        bSuccess = false;
+                        break;
+                    }
+                    rtl_cipher_encodeARCFOUR(
+                        aCipher, io_rOValue.data(),
+                        sal_Int32(io_rOValue.size()), // the data to be 
encrypted
+                        io_rOValue.data(),
+                        sal_Int32(
+                            io_rOValue
+                                .size())); // encrypted data, can be the same 
as the input, encrypt "in place"
+                    //step 8, store in class data member
+                }
+            }
+        }
+        else
+            bSuccess = false;
+    }
+    else
+        bSuccess = false;
+
+    if (aCipher)
+        rtl_cipher_destroyARCFOUR(aCipher);
+
+    if (!bSuccess)
+        io_rOValue.clear();
+    return bSuccess;
+}
+
+/**********************************
+Algorithms 3.4 and 3.5  Compute the encryption dictionary /U value, save into 
the class data member, revision 2 (40 bit) or 3 (128 bit)
+*/
+bool computeUDictionaryValue(EncryptionHashTransporter* i_pTransporter,
+                             vcl::PDFWriter::PDFEncryptionProperties& 
io_rProperties,
+                             sal_Int32 i_nKeyLength, sal_Int32 
i_nAccessPermissions)
+{
+    bool bSuccess = true;
+
+    io_rProperties.UValue.resize(ENCRYPTED_PWD_SIZE);
+
+    ::comphelper::Hash aDigest(::comphelper::HashType::MD5);
+    rtlCipher aCipher = rtl_cipher_createARCFOUR(rtl_Cipher_ModeStream);
+    if (aCipher)
+    {
+        //step 1, common to both 3.4 and 3.5
+        if (computeEncryptionKey(i_pTransporter, io_rProperties, 
i_nAccessPermissions))
+        {
+            // prepare encryption key for object
+            for (sal_Int32 i = i_nKeyLength, y = 0; y < 5; y++)
+                io_rProperties.EncryptionKey[i++] = 0;
+
+            //or 3.5, for 128 bit security
+            //step6, initialize the last 16 bytes of the encrypted user 
password to 0
+            for (sal_uInt32 i = MD5_DIGEST_SIZE; i < 
sal_uInt32(io_rProperties.UValue.size()); i++)
+                io_rProperties.UValue[i] = 0;
+            //steps 2 and 3
+            aDigest.update(PDFEncryptor::s_nPadString, 
sizeof(PDFEncryptor::s_nPadString));
+            aDigest.update(io_rProperties.DocumentIdentifier.data(),
+                           io_rProperties.DocumentIdentifier.size());
+
+            ::std::vector<unsigned char> const nMD5Sum(aDigest.finalize());
+            //Step 4
+            rtl_cipher_initARCFOUR(aCipher, rtl_Cipher_DirectionEncode,
+                                   io_rProperties.EncryptionKey.data(), 
SECUR_128BIT_KEY, nullptr,
+                                   0); //destination data area
+            rtl_cipher_encodeARCFOUR(
+                aCipher, nMD5Sum.data(), nMD5Sum.size(), // the data to be 
encrypted
+                io_rProperties.UValue.data(),
+                SECUR_128BIT_KEY); //encrypted data, stored in class data 
member
+            //step 5
+            sal_uInt32 i;
+            size_t y;
+            sal_uInt8 nLocalKey[SECUR_128BIT_KEY];
+
+            for (i = 1; i <= 19; i++) // do it 19 times, start with 1
+            {
+                for (y = 0; y < sizeof(nLocalKey); y++)
+                    nLocalKey[y] = 
static_cast<sal_uInt8>(io_rProperties.EncryptionKey[y] ^ i);
+
+                rtl_cipher_initARCFOUR(aCipher, rtl_Cipher_DirectionEncode, 
nLocalKey,
+                                       SECUR_128BIT_KEY, // key and key length
+                                       nullptr, 0); //destination data area, 
on init can be NULL
+                rtl_cipher_encodeARCFOUR(
+                    aCipher, io_rProperties.UValue.data(),
+                    SECUR_128BIT_KEY, // the data to be encrypted
+                    io_rProperties.UValue.data(),
+                    SECUR_128BIT_KEY); // encrypted data, can be the same as 
the input, encrypt "in place"
+            }
+        }
+        else
+            bSuccess = false;
+    }
+    else
+        bSuccess = false;
+
+    if (aCipher)
+        rtl_cipher_destroyARCFOUR(aCipher);
+
+    if (!bSuccess)
+        io_rProperties.UValue.clear();
+    return bSuccess;
+}
+
+void computeDocumentIdentifier(std::vector<sal_uInt8>& o_rIdentifier,
+                               const vcl::PDFWriter::PDFDocInfo& i_rDocInfo,
+                               const OString& i_rCString1,
+                               const css::util::DateTime& rCreationMetaDate, 
OString& o_rCString2)
+{
+    o_rIdentifier.clear();
+
+    //build the document id
+    OString aInfoValuesOut;
+    OStringBuffer aID(1024);
+    if (!i_rDocInfo.Title.isEmpty())
+        PDFWriter::AppendUnicodeTextString(i_rDocInfo.Title, aID);
+    if (!i_rDocInfo.Author.isEmpty())
+        PDFWriter::AppendUnicodeTextString(i_rDocInfo.Author, aID);
+    if (!i_rDocInfo.Subject.isEmpty())
+        PDFWriter::AppendUnicodeTextString(i_rDocInfo.Subject, aID);
+    if (!i_rDocInfo.Keywords.isEmpty())
+        PDFWriter::AppendUnicodeTextString(i_rDocInfo.Keywords, aID);
+    if (!i_rDocInfo.Creator.isEmpty())
+        PDFWriter::AppendUnicodeTextString(i_rDocInfo.Creator, aID);
+    if (!i_rDocInfo.Producer.isEmpty())
+        PDFWriter::AppendUnicodeTextString(i_rDocInfo.Producer, aID);
+
+    TimeValue aTVal, aGMT;
+    oslDateTime aDT;
+    aDT.NanoSeconds = rCreationMetaDate.NanoSeconds;
+    aDT.Seconds = rCreationMetaDate.Seconds;
+    aDT.Minutes = rCreationMetaDate.Minutes;
+    aDT.Hours = rCreationMetaDate.Hours;
+    aDT.Day = rCreationMetaDate.Day;
+    aDT.Month = rCreationMetaDate.Month;
+    aDT.Year = rCreationMetaDate.Year;
+
+    osl_getSystemTime(&aGMT);
+    osl_getLocalTimeFromSystemTime(&aGMT, &aTVal);
+    OStringBuffer aCreationMetaDateString(64);
+
+    // i59651: we fill the Metadata date string as well, if PDF/A is requested
+    // according to ISO 19005-1:2005 6.7.3 the date is corrected for
+    // local time zone offset UTC only, whereas Acrobat 8 seems
+    // to use the localtime notation only
+    // according to a recommendation in XMP Specification (Jan 2004, page 75)
+    // the Acrobat way seems the right approach
+    aCreationMetaDateString.append(OStringChar(static_cast<char>('0' + 
((aDT.Year / 1000) % 10)))
+                                   + OStringChar(static_cast<char>('0' + 
((aDT.Year / 100) % 10)))
+                                   + OStringChar(static_cast<char>('0' + 
((aDT.Year / 10) % 10)))
+                                   + OStringChar(static_cast<char>('0' + 
((aDT.Year) % 10))) + "-"
+                                   + OStringChar(static_cast<char>('0' + 
((aDT.Month / 10) % 10)))
+                                   + OStringChar(static_cast<char>('0' + 
((aDT.Month) % 10))) + "-"
+                                   + OStringChar(static_cast<char>('0' + 
((aDT.Day / 10) % 10)))
+                                   + OStringChar(static_cast<char>('0' + 
((aDT.Day) % 10))) + "T"
+                                   + OStringChar(static_cast<char>('0' + 
((aDT.Hours / 10) % 10)))
+                                   + OStringChar(static_cast<char>('0' + 
((aDT.Hours) % 10))) + ":"
+                                   + OStringChar(static_cast<char>('0' + 
((aDT.Minutes / 10) % 10)))
+                                   + OStringChar(static_cast<char>('0' + 
((aDT.Minutes) % 10)))
+                                   + ":"
+                                   + OStringChar(static_cast<char>('0' + 
((aDT.Seconds / 10) % 10)))
+                                   + OStringChar(static_cast<char>('0' + 
((aDT.Seconds) % 10))));
+
+    sal_uInt32 nDelta = 0;
+    if (aGMT.Seconds > aTVal.Seconds)
+    {
+        nDelta = aGMT.Seconds - aTVal.Seconds;
+        aCreationMetaDateString.append("-");
+    }
+    else if (aGMT.Seconds < aTVal.Seconds)
+    {
+        nDelta = aTVal.Seconds - aGMT.Seconds;
+        aCreationMetaDateString.append("+");
+    }
+    else
+    {
+        aCreationMetaDateString.append("Z");
+    }
+    if (nDelta)
+    {
+        aCreationMetaDateString.append(
+            OStringChar(static_cast<char>('0' + ((nDelta / 36000) % 10)))
+            + OStringChar(static_cast<char>('0' + ((nDelta / 3600) % 10))) + 
":"
+            + OStringChar(static_cast<char>('0' + ((nDelta / 600) % 6)))
+            + OStringChar(static_cast<char>('0' + ((nDelta / 60) % 10))));
+    }
+    aID.append(i_rCString1.getStr(), i_rCString1.getLength());
+
+    aInfoValuesOut = aID.makeStringAndClear();
+    o_rCString2 = aCreationMetaDateString.makeStringAndClear();
+
+    ::comphelper::Hash aDigest(::comphelper::HashType::MD5);
+    aDigest.update(reinterpret_cast<unsigned char const*>(&aGMT), 
sizeof(aGMT));
+    aDigest.update(reinterpret_cast<unsigned char 
const*>(aInfoValuesOut.getStr()),
+                   aInfoValuesOut.getLength());
+    //the binary form of the doc id is needed for encryption stuff
+    o_rIdentifier = aDigest.finalize();
+}
+
+sal_Int32 computeAccessPermissions(const 
vcl::PDFWriter::PDFEncryptionProperties& i_rProperties,
+                                   sal_Int32& o_rKeyLength, sal_Int32& 
o_rRC4KeyLength)
+{
+    /*
+    2) compute the access permissions, in numerical form
+
+    the default value depends on the revision 2 (40 bit) or 3 (128 bit 
security):
+    - for 40 bit security the unused bit must be set to 1, since they are not 
used
+    - for 128 bit security the same bit must be preset to 0 and set later if 
needed
+    according to the table 3.15, pdf v 1.4 */
+    sal_Int32 nAccessPermissions = 0xfffff0c0;
+
+    o_rKeyLength = SECUR_128BIT_KEY;
+    o_rRC4KeyLength = 16; // for this value see PDF spec v 1.4, algorithm 3.1 
step 4, where n is 16,
+        // thus maximum permitted value is 16
+
+    nAccessPermissions |= (i_rProperties.CanPrintTheDocument) ? 1 << 2 : 0;
+    nAccessPermissions |= (i_rProperties.CanModifyTheContent) ? 1 << 3 : 0;
+    nAccessPermissions |= (i_rProperties.CanCopyOrExtract) ? 1 << 4 : 0;
+    nAccessPermissions |= (i_rProperties.CanAddOrModify) ? 1 << 5 : 0;
+    nAccessPermissions |= (i_rProperties.CanFillInteractive) ? 1 << 8 : 0;
+    nAccessPermissions |= (i_rProperties.CanExtractForAccessibility) ? 1 << 9 
: 0;
+    nAccessPermissions |= (i_rProperties.CanAssemble) ? 1 << 10 : 0;
+    nAccessPermissions |= (i_rProperties.CanPrintFull) ? 1 << 11 : 0;
+    return nAccessPermissions;
+}
+
+} // end vcl::pdf
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 4866d7f5ec7495dd00fa1da53189d0427c309e75
Author:     Tomaž Vajngerl <[email protected]>
AuthorDate: Mon Nov 4 15:37:56 2024 +0100
Commit:     Tomaž Vajngerl <[email protected]>
CommitDate: Mon Nov 4 21:51:49 2024 +0100

    pdf: move EncHashTransporter into own files
    
    Also rename to EncryptionHashTransporter.
    
    Change-Id: I20f984af4428e1182c77dbce4343d69c106063a4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176029
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <[email protected]>

diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index a59285a26dfb..f42741fe6bf3 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -309,11 +309,6 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
     vcl/source/gdi/scrptrun \
     vcl/source/gdi/CommonSalLayout \
     vcl/source/gdi/TypeSerializer \
-    vcl/source/pdf/PdfConfig \
-    vcl/source/pdf/ResourceDict \
-    vcl/source/pdf/Matrix3 \
-    vcl/source/pdf/XmpMetadata \
-    vcl/source/pdf/ExternalPDFStreams \
     vcl/source/graphic/BinaryDataContainer \
     vcl/source/graphic/BinaryDataContainerTools \
     vcl/source/graphic/GraphicID \
@@ -503,7 +498,13 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
     vcl/source/fontsubset/sft \
     vcl/source/fontsubset/ttcr \
     vcl/source/fontsubset/xlat \
+    vcl/source/pdf/EncryptionHashTransporter \
+    vcl/source/pdf/ExternalPDFStreams \
     vcl/source/pdf/PDFiumTools \
+    vcl/source/pdf/PdfConfig \
+    vcl/source/pdf/ResourceDict \
+    vcl/source/pdf/Matrix3 \
+    vcl/source/pdf/XmpMetadata \
     vcl/source/uitest/logger \
     vcl/source/uitest/uiobject \
     vcl/source/uitest/uitest \
diff --git a/vcl/inc/pdf/EncryptionHashTransporter.hxx 
b/vcl/inc/pdf/EncryptionHashTransporter.hxx
new file mode 100644
index 000000000000..0da0db3d8654
--- /dev/null
+++ b/vcl/inc/pdf/EncryptionHashTransporter.hxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * 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
+
+#include <com/sun/star/beans/XMaterialHolder.hpp>
+#include <comphelper/hash.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <sal/log.hxx>
+
+#include <map>
+
+namespace vcl::pdf
+{
+/* a crutch to transport a ::comphelper::Hash safely though UNO API
+   this is needed for the PDF export dialog, which otherwise would have to pass
+   clear text passwords down till they can be used in PDFWriter. Unfortunately
+   the MD5 sum of the password (which is needed to create the PDF encryption 
key)
+   is not sufficient, since an MD5 digest cannot be created in an arbitrary 
state
+   which would be needed in PDFWriterImpl::computeEncryptionKey.
+*/
+class EncryptionHashTransporter : public 
cppu::WeakImplHelper<css::beans::XMaterialHolder>
+{
+    ::std::unique_ptr<::comphelper::Hash> m_pDigest;
+    sal_IntPtr maID;
+    std::vector<sal_uInt8> maOValue;
+
+    static std::map<sal_IntPtr, EncryptionHashTransporter*> sTransporters;
+
+public:
+    EncryptionHashTransporter();
+
+    virtual ~EncryptionHashTransporter() override;
+
+    comphelper::Hash* getUDigest() { return m_pDigest.get(); };
+
+    std::vector<sal_uInt8>& getOValue() { return maOValue; }
+
+    void invalidate() { m_pDigest.reset(); }
+
+    // XMaterialHolder
+    virtual css::uno::Any SAL_CALL getMaterial() override { return 
css::uno::Any(sal_Int64(maID)); }
+
+    static EncryptionHashTransporter*
+    getEncHashTransporter(const 
css::uno::Reference<css::beans::XMaterialHolder>& xReference);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/pdf/pdfwriter_impl.hxx b/vcl/inc/pdf/pdfwriter_impl.hxx
index 3a4386581b27..68c49841baf6 100644
--- a/vcl/inc/pdf/pdfwriter_impl.hxx
+++ b/vcl/inc/pdf/pdfwriter_impl.hxx
@@ -65,9 +65,9 @@
 
 class FontSubsetInfo;
 class ZCodec;
-class EncHashTransporter;
 struct BitStreamState;
 namespace vcl::font { class PhysicalFontFace; }
+namespace vcl::pdf { class EncryptionHashTransporter; }
 class SvStream;
 class SvMemoryStream;
 
@@ -1098,7 +1098,7 @@ private:
     pad a password according  algorithm 3.2, step 1 */
     static void padPassword( std::u16string_view i_rPassword, sal_uInt8* 
o_pPaddedPW );
     /* algorithm 3.2: compute an encryption key */
-    static bool computeEncryptionKey( EncHashTransporter*,
+    static bool computeEncryptionKey( vcl::pdf::EncryptionHashTransporter*,
                                       vcl::PDFWriter::PDFEncryptionProperties& 
io_rProperties,
                                       sal_Int32 i_nAccessPermissions
                                      );
@@ -1108,7 +1108,7 @@ private:
                                          sal_Int32 i_nKeyLength
                                         );
     /* algorithm 3.4 or 3.5: computing the encryption dictionary's user 
password value ( /U ) revision 2 or 3 of the standard security handler */
-    static bool computeUDictionaryValue( EncHashTransporter* i_pTransporter,
+    static bool computeUDictionaryValue( vcl::pdf::EncryptionHashTransporter* 
i_pTransporter,
                                          
vcl::PDFWriter::PDFEncryptionProperties& io_rProperties,
                                          sal_Int32 i_nKeyLength,
                                          sal_Int32 i_nAccessPermissions
diff --git a/vcl/source/gdi/pdfwriter_impl2.cxx 
b/vcl/source/gdi/pdfwriter_impl2.cxx
index 84381bbf35c0..496676be5c74 100644
--- a/vcl/source/gdi/pdfwriter_impl2.cxx
+++ b/vcl/source/gdi/pdfwriter_impl2.cxx
@@ -18,6 +18,7 @@
  */
 
 #include <pdf/pdfwriter_impl.hxx>
+#include <pdf/EncryptionHashTransporter.hxx>
 
 #include <vcl/pdfextoutdevdata.hxx>
 #include <vcl/virdev.hxx>
@@ -33,7 +34,6 @@
 #include <tools/stream.hxx>
 
 #include <comphelper/fileformat.h>
-#include <comphelper/hash.hxx>
 #include <comphelper/processfactory.hxx>
 #include <comphelper/propertyvalue.hxx>
 
@@ -43,7 +43,7 @@
 #include <com/sun/star/graphic/XGraphicProvider.hpp>
 #include <com/sun/star/beans/XMaterialHolder.hpp>
 
-#include <cppuhelper/implbase.hxx>
+
 #include <o3tl/unit_conversion.hxx>
 #include <osl/diagnose.h>
 #include <vcl/skia/SkiaHelper.hxx>
@@ -1079,72 +1079,6 @@ void PDFWriterImpl::playMetafile( const GDIMetaFile& 
i_rMtf, vcl::PDFExtOutDevDa
 
 // Encryption methods
 
-/* a crutch to transport a ::comphelper::Hash safely though UNO API
-   this is needed for the PDF export dialog, which otherwise would have to pass
-   clear text passwords down till they can be used in PDFWriter. Unfortunately
-   the MD5 sum of the password (which is needed to create the PDF encryption 
key)
-   is not sufficient, since an MD5 digest cannot be created in an arbitrary 
state
-   which would be needed in PDFWriterImpl::computeEncryptionKey.
-*/
-class EncHashTransporter : public cppu::WeakImplHelper < 
css::beans::XMaterialHolder >
-{
-    ::std::unique_ptr<::comphelper::Hash> m_pDigest;
-    sal_IntPtr                  maID;
-    std::vector< sal_uInt8 >    maOValue;
-
-    static std::map< sal_IntPtr, EncHashTransporter* >      sTransporters;
-public:
-    EncHashTransporter()
-        : m_pDigest(new ::comphelper::Hash(::comphelper::HashType::MD5))
-    {
-        maID = reinterpret_cast< sal_IntPtr >(this);
-        while( sTransporters.find( maID ) != sTransporters.end() ) // paranoia 
mode
-            maID++;
-        sTransporters[ maID ] = this;
-    }
-
-    virtual ~EncHashTransporter() override
-    {
-        sTransporters.erase( maID );
-        SAL_INFO( "vcl", "EncHashTransporter freed" );
-    }
-
-    ::comphelper::Hash* getUDigest() { return m_pDigest.get(); };
-    std::vector< sal_uInt8 >& getOValue() { return maOValue; }
-    void invalidate()
-    {
-        m_pDigest.reset();
-    }
-
-    // XMaterialHolder
-    virtual uno::Any SAL_CALL getMaterial() override
-    {
-        return uno::Any( sal_Int64(maID) );
-    }
-
-    static EncHashTransporter* getEncHashTransporter( const uno::Reference< 
beans::XMaterialHolder >& );
-
-};
-
-std::map< sal_IntPtr, EncHashTransporter* > EncHashTransporter::sTransporters;
-
-EncHashTransporter* EncHashTransporter::getEncHashTransporter( const 
uno::Reference< beans::XMaterialHolder >& xRef )
-{
-    EncHashTransporter* pResult = nullptr;
-    if( xRef.is() )
-    {
-        uno::Any aMat( xRef->getMaterial() );
-        sal_Int64 nMat = 0;
-        if( aMat >>= nMat )
-        {
-            std::map< sal_IntPtr, EncHashTransporter* >::iterator it = 
sTransporters.find( static_cast<sal_IntPtr>(nMat) );
-            if( it != sTransporters.end() )
-                pResult = it->second;
-        }
-    }
-    return pResult;
-}
-
 void PDFWriterImpl::checkAndEnableStreamEncryption( sal_Int32 nObject )
 {
     if( !m_aContext.Encryption.Encrypt() )
@@ -1195,7 +1129,7 @@ uno::Reference< beans::XMaterialHolder > 
PDFWriterImpl::initEncryption( const OU
     uno::Reference< beans::XMaterialHolder > xResult;
     if( !i_rOwnerPassword.isEmpty() || !i_rUserPassword.isEmpty() )
     {
-        rtl::Reference<EncHashTransporter> pTransporter = new 
EncHashTransporter;
+        rtl::Reference<EncryptionHashTransporter> pTransporter = new 
EncryptionHashTransporter;
         xResult = pTransporter;
 
         // get padded passwords
@@ -1220,7 +1154,7 @@ uno::Reference< beans::XMaterialHolder > 
PDFWriterImpl::initEncryption( const OU
 bool PDFWriterImpl::prepareEncryption( const uno::Reference< 
beans::XMaterialHolder >& xEnc )
 {
     bool bSuccess = false;
-    EncHashTransporter* pTransporter = 
EncHashTransporter::getEncHashTransporter( xEnc );
+    EncryptionHashTransporter* pTransporter = 
EncryptionHashTransporter::getEncHashTransporter( xEnc );
     if( pTransporter )
     {
         sal_Int32 nKeyLength = 0, nRC4KeyLength = 0;
@@ -1297,7 +1231,7 @@ it will be 16 byte long for 128 bit security; for 40 bit 
security only the first
 TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. 
See spec.
 
 */
-bool PDFWriterImpl::computeEncryptionKey( EncHashTransporter* i_pTransporter, 
vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, sal_Int32 
i_nAccessPermissions )
+bool PDFWriterImpl::computeEncryptionKey( EncryptionHashTransporter* 
i_pTransporter, vcl::PDFWriter::PDFEncryptionProperties& io_rProperties, 
sal_Int32 i_nAccessPermissions )
 {
     bool bSuccess = true;
     ::std::vector<unsigned char> nMD5Sum;
@@ -1433,7 +1367,7 @@ bool PDFWriterImpl::computeODictionaryValue( const 
sal_uInt8* i_pPaddedOwnerPass
 /**********************************
 Algorithms 3.4 and 3.5  Compute the encryption dictionary /U value, save into 
the class data member, revision 2 (40 bit) or 3 (128 bit)
 */
-bool PDFWriterImpl::computeUDictionaryValue( EncHashTransporter* 
i_pTransporter,
+bool PDFWriterImpl::computeUDictionaryValue( EncryptionHashTransporter* 
i_pTransporter,
                                              
vcl::PDFWriter::PDFEncryptionProperties& io_rProperties,
                                              sal_Int32 i_nKeyLength,
                                              sal_Int32 i_nAccessPermissions
diff --git a/vcl/source/pdf/EncryptionHashTransporter.cxx 
b/vcl/source/pdf/EncryptionHashTransporter.cxx
new file mode 100644
index 000000000000..6ecd43396c3a
--- /dev/null
+++ b/vcl/source/pdf/EncryptionHashTransporter.cxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * 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/.
+ *
+ */
+
+#include <pdf/EncryptionHashTransporter.hxx>
+
+using namespace css;
+
+namespace vcl::pdf
+{
+EncryptionHashTransporter::EncryptionHashTransporter()
+    : m_pDigest(new ::comphelper::Hash(::comphelper::HashType::MD5))
+{
+    maID = reinterpret_cast<sal_IntPtr>(this);
+    while (sTransporters.find(maID) != sTransporters.end()) // paranoia mode
+        maID++;
+    sTransporters[maID] = this;
+}
+
+EncryptionHashTransporter::~EncryptionHashTransporter()
+{
+    sTransporters.erase(maID);
+    SAL_INFO("vcl", "EncryptionHashTransporter freed");
+}
+
+EncryptionHashTransporter* EncryptionHashTransporter::getEncHashTransporter(
+    const uno::Reference<beans::XMaterialHolder>& xReference)
+{
+    EncryptionHashTransporter* pResult = nullptr;
+    if (xReference.is())
+    {
+        uno::Any aMat(xReference->getMaterial());
+        sal_Int64 nMat = 0;
+        if (aMat >>= nMat)
+        {
+            std::map<sal_IntPtr, EncryptionHashTransporter*>::iterator it
+                = sTransporters.find(static_cast<sal_IntPtr>(nMat));
+            if (it != sTransporters.end())
+                pResult = it->second;
+        }
+    }
+    return pResult;
+}
+
+std::map<sal_IntPtr, EncryptionHashTransporter*> 
EncryptionHashTransporter::sTransporters;
+
+} // end vcl::pdf
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit a1d367dbad0e754f5977db231d0d709322e2b8c5
Author:     Tomaž Vajngerl <[email protected]>
AuthorDate: Mon Nov 4 13:18:42 2024 +0100
Commit:     Tomaž Vajngerl <[email protected]>
CommitDate: Mon Nov 4 21:51:42 2024 +0100

    pdf: move some encryption code into PDFEncryptor class
    
    Refactored the code to make encryption easier to change.
    
    Change-Id: I24b831781d4acd6329838dbf2468e9df6efad41e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176028
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <[email protected]>

diff --git a/vcl/inc/pdf/PDFEncryptor.hxx b/vcl/inc/pdf/PDFEncryptor.hxx
new file mode 100644
index 000000000000..c93fc0bec9e9
--- /dev/null
+++ b/vcl/inc/pdf/PDFEncryptor.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * 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
+
+#include <rtl/cipher.h>
+
+namespace vcl::pdf
+{
+constexpr sal_Int32 ENCRYPTED_PWD_SIZE = 32;
+
+class PDFEncryptor
+{
+public:
+    /* used to cipher the stream data and for password management */
+    rtlCipher m_aCipher = nullptr;
+
+    /* pad string used for password in Standard security handler */
+    static const sal_uInt8 s_nPadString[ENCRYPTED_PWD_SIZE];
+
+    /* set to true if the following stream must be encrypted, used inside 
writeBuffer() */
+    bool m_bEncryptThisStream = false;
+
+    /* The encryption key, formed with the user password according to 
algorithm 3.2,
+     * maximum length is 16 bytes + 3 + 2 for 128 bit security */
+    sal_Int32 m_nKeyLength = 0; // key length, 16 or 5
+    sal_Int32 m_nRC4KeyLength = 0; // key length, 16 or 10, to be input to the 
algorithm 3.1
+
+    PDFEncryptor()
+    {
+        /* prepare the cypher engine */
+        m_aCipher = rtl_cipher_createARCFOUR(rtl_Cipher_ModeStream);
+    }
+
+    ~PDFEncryptor() { rtl_cipher_destroyARCFOUR(m_aCipher); }
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/pdf/pdfwriter_impl.hxx b/vcl/inc/pdf/pdfwriter_impl.hxx
index 41e137481e9e..3a4386581b27 100644
--- a/vcl/inc/pdf/pdfwriter_impl.hxx
+++ b/vcl/inc/pdf/pdfwriter_impl.hxx
@@ -34,11 +34,11 @@
 #include <pdf/ResourceDict.hxx>
 #include <pdf/BitmapID.hxx>
 #include <pdf/Matrix3.hxx>
+#include <pdf/PDFEncryptor.hxx>
 
 #include <com/sun/star/lang/Locale.hpp>
 #include <com/sun/star/util/XURLTransformer.hpp>
 #include <osl/file.hxx>
-#include <rtl/cipher.h>
 #include <rtl/strbuf.hxx>
 #include <rtl/ustring.hxx>
 #include <tools/gen.hxx>
@@ -72,7 +72,6 @@ class SvStream;
 class SvMemoryStream;
 
 // the maximum password length
-constexpr sal_Int32 ENCRYPTED_PWD_SIZE = 32;
 constexpr sal_Int32 MD5_DIGEST_SIZE = 16;
 // security 128 bit
 constexpr sal_Int32 SECUR_128BIT_KEY = 16;
@@ -821,6 +820,8 @@ private:
 
     ExternalPDFStreams m_aExternalPDFStreams;
 
+    PDFEncryptor m_aPDFEncryptor;
+
     /* output redirection; e.g. to accumulate content streams for
        XObjects
      */
@@ -850,22 +851,6 @@ private:
         }
         return nPosition;
     }
-/*
-variables for PDF security
-i12626
-*/
-/* used to cipher the stream data and for password management */
-    rtlCipher                               m_aCipher;
-    /* pad string used for password in Standard security handler */
-    static const sal_uInt8                  s_nPadString[ENCRYPTED_PWD_SIZE];
-
-    /* the encryption key, formed with the user password according to 
algorithm 3.2, maximum length is 16 bytes + 3 + 2
-    for 128 bit security   */
-    sal_Int32                               m_nKeyLength; // key length, 16 or 
5
-    sal_Int32                               m_nRC4KeyLength; // key length, 16 
or 10, to be input to the algorithm 3.1
-
-    /* set to true if the following stream must be encrypted, used inside 
writeBuffer() */
-    bool                                    m_bEncryptThisStream;
 
     /* the numerical value of the access permissions, according to PDF spec, 
must be signed */
     sal_Int32                               m_nAccessPermissions;
@@ -881,17 +866,19 @@ i12626
     /* this function implements part of the PDF spec algorithm 3.1 in 
encryption, the rest (the actual encryption) is in PDFWriterImpl::writeBuffer */
     void checkAndEnableStreamEncryption( sal_Int32 nObject ) override;
 
-    void disableStreamEncryption() override { m_bEncryptThisStream = false; };
+    void disableStreamEncryption() override { 
m_aPDFEncryptor.m_bEncryptThisStream = false; };
 
     /* */
     void enableStringEncryption( sal_Int32 nObject );
 
-// test if the encryption is active, if yes than encrypt the unicode string  
and add to the OStringBuffer parameter
+public: // Temporary for PDFStructureWriter
+    // test if the encryption is active, if yes than encrypt the unicode 
string  and add to the OStringBuffer parameter
     void appendUnicodeTextStringEncrypt( const OUString& rInString, const 
sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer );
 
     void appendLiteralStringEncrypt( std::u16string_view rInString, const 
sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer, rtl_TextEncoding nEnc = 
RTL_TEXTENCODING_ASCII_US );
     void appendLiteralStringEncrypt( std::string_view rInString, const 
sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer );
 
+private:
     /* creates fonts and subsets that will be emitted later */
     void registerGlyph(const sal_GlyphId, const vcl::font::PhysicalFontFace*, 
const LogicalFontInstance* pFont, const std::vector<sal_Ucs>&, sal_Int32, 
sal_uInt8&, sal_Int32&);
     void registerSimpleGlyph(const sal_GlyphId, const 
vcl::font::PhysicalFontFace*, const std::vector<sal_Ucs>&, sal_Int32, 
sal_uInt8&, sal_Int32&);
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx 
b/vcl/source/gdi/pdfwriter_impl.cxx
index 632dbc7968a0..906fc6ba9d31 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -275,18 +275,52 @@ void appendDestinationName( const OUString& rString, 
OStringBuffer& rBuffer )
         }
     }
 }
+} // end anonymous namespace
+
+namespace vcl::pdf
+{
+const sal_uInt8 PDFEncryptor::s_nPadString[32] =
+{
+    0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 
0xFF, 0xFA, 0x01, 0x08,
+    0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 
0x64, 0x53, 0x69, 0x7A
+};
+}
+
+namespace vcl
+{
+
+namespace
+{
+
+template < class GEOMETRY >
+GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, 
OutputDevice* _pPixelConversion, const GEOMETRY& _rObject )
+{
+    GEOMETRY aPoint;
+    if ( MapUnit::MapPixel == _rSource.GetMapUnit() )
+    {
+        aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest );
+    }
+    else
+    {
+        aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest );
+    }
+    return aPoint;
+}
+
+void removePlaceholderSE(std::vector<PDFStructureElement> & rStructure, 
PDFStructureElement& rEle);
 
 /** Writes the PDF structure to the string buffer.
  *
  * Structure elements like: objects, IDs, dictionaries, key/values, ...
- *
  */
 class PDFStructureWriter
 {
     OStringBuffer maLine;
+    PDFWriterImpl& mrWriterImpl;
 public:
-    PDFStructureWriter()
+    PDFStructureWriter(PDFWriterImpl& rWriterImpl)
         : maLine(1024)
+        , mrWriterImpl(rWriterImpl)
     {
     }
 
@@ -335,37 +369,19 @@ public:
         appendLiteralString(pString, nSize, maLine);
         maLine.append(")");
     }
-};
-
-} // end anonymous namespace
-
-namespace vcl
-{
-const sal_uInt8 PDFWriterImpl::s_nPadString[32] =
-{
-    0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 
0xFF, 0xFA, 0x01, 0x08,
-    0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 
0x64, 0x53, 0x69, 0x7A
-};
 
-namespace
-{
-
-template < class GEOMETRY >
-GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, 
OutputDevice* _pPixelConversion, const GEOMETRY& _rObject )
-{
-    GEOMETRY aPoint;
-    if ( MapUnit::MapPixel == _rSource.GetMapUnit() )
+    void writeUnicodeEncrypt(std::string_view key, OUString const& rString, 
sal_Int32 nObject)
     {
-        aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest );
+        maLine.append(key);
+        mrWriterImpl.appendUnicodeTextStringEncrypt(rString, nObject, maLine);
     }
-    else
+
+    void writeLiteralEncrypt(std::string_view key, std::string_view value, 
sal_Int32 nObject)
     {
-        aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest );
+        maLine.append(key);
+        mrWriterImpl.appendLiteralStringEncrypt(value, nObject, maLine);
     }
-    return aPoint;
-}
-
-void removePlaceholderSE(std::vector<PDFStructureElement> & rStructure, 
PDFStructureElement& rEle);
+};
 
 } // end anonymous namespace
 
@@ -1311,10 +1327,6 @@ PDFWriterImpl::PDFWriterImpl( const 
PDFWriter::PDFWriterContext& rContext,
         m_aFile(m_aContext.URL),
         m_bOpen(false),
         m_DocDigest(::comphelper::HashType::MD5),
-        m_aCipher( nullptr ),
-        m_nKeyLength(0),
-        m_nRC4KeyLength(0),
-        m_bEncryptThisStream( false ),
         m_nAccessPermissions(0),
         m_rOuterFace( i_rOuterFace )
 {
@@ -1351,9 +1363,6 @@ PDFWriterImpl::PDFWriterImpl( const 
PDFWriter::PDFWriterContext& rContext,
     // setup DocInfo
     setupDocInfo();
 
-    /* prepare the cypher engine, can be done in CTOR, free in DTOR */
-    m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
-
     if( xEnc.is() )
         prepareEncryption( xEnc );
 
@@ -1372,7 +1381,7 @@ PDFWriterImpl::PDFWriterImpl( const 
PDFWriter::PDFWriterContext& rContext,
             OSL_ENSURE( false, "encryption data failed sanity check, 
encryption disabled" );
         }
         else // setup key lengths
-            m_nAccessPermissions = computeAccessPermissions( 
m_aContext.Encryption, m_nKeyLength, m_nRC4KeyLength );
+            m_nAccessPermissions = 
computeAccessPermissions(m_aContext.Encryption, m_aPDFEncryptor.m_nKeyLength, 
m_aPDFEncryptor.m_nRC4KeyLength);
     }
 
     // write header
@@ -1439,8 +1448,6 @@ PDFWriterImpl::~PDFWriterImpl()
 
 void PDFWriterImpl::dispose()
 {
-    if( m_aCipher )
-        rtl_cipher_destroyARCFOUR( m_aCipher );
     m_aPages.clear();
     VirtualDevice::dispose();
 }
@@ -1615,7 +1622,7 @@ inline void 
PDFWriterImpl::appendUnicodeTextStringEncrypt( const OUString& rInSt
             *pCopy++ = static_cast<sal_uInt8>( aUnChar & 255 );
         }
         //encrypt in place
-        rtl_cipher_encodeARCFOUR( m_aCipher, m_vEncryptionBuffer.data(), 
nChars, m_vEncryptionBuffer.data(), nChars );
+        rtl_cipher_encodeARCFOUR( m_aPDFEncryptor.m_aCipher, 
m_vEncryptionBuffer.data(), nChars, m_vEncryptionBuffer.data(), nChars);
         //now append, hexadecimal (appendHex), the encrypted result
         for(int i = 0; i < nChars; i++)
             appendHex( m_vEncryptionBuffer[i], rOutBuffer );
@@ -1635,7 +1642,7 @@ inline void PDFWriterImpl::appendLiteralStringEncrypt( 
std::string_view rInStrin
         m_vEncryptionBuffer.resize(nChars);
         //encrypt the string in a buffer, then append it
         enableStringEncryption( nInObjectNumber );
-        rtl_cipher_encodeARCFOUR( m_aCipher, rInString.data(), nChars, 
m_vEncryptionBuffer.data(), nChars );
+        rtl_cipher_encodeARCFOUR(m_aPDFEncryptor.m_aCipher, rInString.data(), 
nChars, m_vEncryptionBuffer.data(), nChars);
         appendLiteralString( 
reinterpret_cast<char*>(m_vEncryptionBuffer.data()), nChars, rOutBuffer );
     }
     else
@@ -1743,17 +1750,15 @@ bool PDFWriterImpl::writeBufferBytes( const void* 
pBuffer, sal_uInt64 nBytes )
     else
     {
         bool  buffOK = true;
-        if( m_bEncryptThisStream )
+        if (m_aPDFEncryptor.m_bEncryptThisStream)
         {
             /* implement the encryption part of the PDF spec encryption 
algorithm 3.1 */
             m_vEncryptionBuffer.resize(nBytes);
-            if( buffOK )
-                rtl_cipher_encodeARCFOUR( m_aCipher,
-                                          pBuffer, 
static_cast<sal_Size>(nBytes),
-                                          m_vEncryptionBuffer.data(), 
static_cast<sal_Size>(nBytes) );
+            if (buffOK)
+                rtl_cipher_encodeARCFOUR(m_aPDFEncryptor.m_aCipher, pBuffer, 
static_cast<sal_Size>(nBytes), m_vEncryptionBuffer.data(), 
static_cast<sal_Size>(nBytes));
         }
 
-        const void* pWriteBuffer = ( m_bEncryptThisStream && buffOK ) ? 
m_vEncryptionBuffer.data()  : pBuffer;
+        const void* pWriteBuffer = (m_aPDFEncryptor.m_bEncryptThisStream && 
buffOK) ? m_vEncryptionBuffer.data()  : pBuffer;
         m_DocDigest.update(static_cast<unsigned char const*>(pWriteBuffer), 
static_cast<sal_uInt32>(nBytes));
 
         if (m_aFile.write(pWriteBuffer, nBytes, nWritten) != osl::File::E_None)
@@ -5852,68 +5857,48 @@ sal_Int32 PDFWriterImpl::emitInfoDict( )
 {
     sal_Int32 nObject = createObject();
 
-    if( updateObject( nObject ) )
-    {
-        OStringBuffer aLine(1024);
-        appendObjectID(nObject, aLine);
-        aLine.append("<<");
+    if (!updateObject(nObject))
+        return 0;
 
-        // These entries are deprecated in PDF 2.0 (in favor of XMP metadata) 
and shouldn't be written.
-        // Exception: CreationDate and ModDate (which we don't write)
-        if (m_aContext.Version < PDFWriter::PDFVersion::PDF_2_0)
+    PDFStructureWriter aWriter(*this);
+    aWriter.startObject(nObject);
+    aWriter.startDict();
+
+    // These entries are deprecated in PDF 2.0 (in favor of XMP metadata) and 
shouldn't be written.
+    // Exception: CreationDate and ModDate (which we don't write)
+    if (m_aContext.Version < PDFWriter::PDFVersion::PDF_2_0)
+    {
+        if (!m_aContext.DocumentInfo.Title.isEmpty())
         {
-            if (!m_aContext.DocumentInfo.Title.isEmpty())
-            {
-                aLine.append("/Title" );
-                appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Title, 
nObject, aLine );
-                aLine.append("
" );
-            }
-            if( !m_aContext.DocumentInfo.Author.isEmpty() )
-            {
-                aLine.append( "/Author" );
-                appendUnicodeTextStringEncrypt( 
m_aContext.DocumentInfo.Author, nObject, aLine );
-                aLine.append( "
" );
-            }
-            if( !m_aContext.DocumentInfo.Subject.isEmpty() )
-            {
-                aLine.append( "/Subject" );
-                appendUnicodeTextStringEncrypt( 
m_aContext.DocumentInfo.Subject, nObject, aLine );
-                aLine.append( "
" );
-            }
-            if( !m_aContext.DocumentInfo.Keywords.isEmpty() )
-            {
-                aLine.append( "/Keywords" );
-                appendUnicodeTextStringEncrypt( 
m_aContext.DocumentInfo.Keywords, nObject, aLine );
-                aLine.append( "
" );
-            }
-            if( !m_aContext.DocumentInfo.Creator.isEmpty() )
-            {
-                aLine.append( "/Creator" );
-                appendUnicodeTextStringEncrypt( 
m_aContext.DocumentInfo.Creator, nObject, aLine );
-                aLine.append( "
" );
-            }
-            if( !m_aContext.DocumentInfo.Producer.isEmpty() )
-            {
-                aLine.append( "/Producer" );
-                appendUnicodeTextStringEncrypt( 
m_aContext.DocumentInfo.Producer, nObject, aLine );
-                aLine.append( "
" );
-            }
+            aWriter.writeUnicodeEncrypt("/Title", 
m_aContext.DocumentInfo.Title, nObject);
         }
-
-        // Allowed in PDF 2.0
+        if (!m_aContext.DocumentInfo.Author.isEmpty())
         {
-            aLine.append("/CreationDate");
-            appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine 
);
-            aLine.append( "
" );
+            aWriter.writeUnicodeEncrypt("/Author", 
m_aContext.DocumentInfo.Author, nObject);
+        }
+        if (!m_aContext.DocumentInfo.Subject.isEmpty())
+        {
+            aWriter.writeUnicodeEncrypt("/Subject", 
m_aContext.DocumentInfo.Subject, nObject);
+        }
+        if (!m_aContext.DocumentInfo.Keywords.isEmpty())
+        {
+            aWriter.writeUnicodeEncrypt("/Keywords", 
m_aContext.DocumentInfo.Keywords, nObject);
+        }
+        if (!m_aContext.DocumentInfo.Creator.isEmpty())
+        {
+            aWriter.writeUnicodeEncrypt("/Creator", 
m_aContext.DocumentInfo.Creator, nObject);
+        }
+        if (!m_aContext.DocumentInfo.Producer.isEmpty())
+        {
+            aWriter.writeUnicodeEncrypt("/Producer", 
m_aContext.DocumentInfo.Producer, nObject);
         }
-
-        aLine.append(">>
");
-        aLine.append("endobj

" );
-
-        if (!writeBuffer(aLine))
-            nObject = 0;
     }
-    else
+    // Allowed in PDF 2.0
+    aWriter.writeLiteralEncrypt("/CreationDate", m_aCreationDateString, 
nObject);
+    aWriter.endDict();
+    aWriter.endObject();
+
+    if (!writeBuffer(aWriter.getLine()))
         nObject = 0;
 
     return nObject;
@@ -6167,7 +6152,7 @@ sal_Int32 PDFWriterImpl::emitEncrypt()
 
     if (updateObject(nObject))
     {
-        PDFStructureWriter aWriter;
+        PDFStructureWriter aWriter(*this);
         aWriter.startObject(nObject);
         aWriter.startDict();
         aWriter.write("/Filter", "/Standard");
@@ -6272,6 +6257,7 @@ bool PDFWriterImpl::emitTrailer()
         }
         aLine.append( "> ]
" );
     }
+
     if( !aDocChecksum.isEmpty() )
     {
         aLine.append( "/DocChecksum /" );
@@ -9808,7 +9794,7 @@ bool PDFWriterImpl::writeBitmapObject( const BitmapEmit& 
rObject, bool bMask )
                     m_vEncryptionBuffer[nChar++] = rColor.GetBlue();
                 }
                 //encrypt the colorspace lookup table
-                rtl_cipher_encodeARCFOUR( m_aCipher, 
m_vEncryptionBuffer.data(), nChar, m_vEncryptionBuffer.data(), nChar );
+                rtl_cipher_encodeARCFOUR(m_aPDFEncryptor.m_aCipher, 
m_vEncryptionBuffer.data(), nChar, m_vEncryptionBuffer.data(), nChar);
                 //now queue the data for output
                 nChar = 0;
                 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); 
i++ )
diff --git a/vcl/source/gdi/pdfwriter_impl2.cxx 
b/vcl/source/gdi/pdfwriter_impl2.cxx
index a2ef1f30f4a3..84381bbf35c0 100644
--- a/vcl/source/gdi/pdfwriter_impl2.cxx
+++ b/vcl/source/gdi/pdfwriter_impl2.cxx
@@ -1150,8 +1150,8 @@ void PDFWriterImpl::checkAndEnableStreamEncryption( 
sal_Int32 nObject )
     if( !m_aContext.Encryption.Encrypt() )
         return;
 
-    m_bEncryptThisStream = true;
-    sal_Int32 i = m_nKeyLength;
+    m_aPDFEncryptor.m_bEncryptThisStream = true;
+    sal_Int32 i = m_aPDFEncryptor.m_nKeyLength;
     m_aContext.Encryption.EncryptionKey[i++] = static_cast<sal_uInt8>(nObject);
     m_aContext.Encryption.EncryptionKey[i++] = static_cast<sal_uInt8>( nObject 
>> 8 );
     m_aContext.Encryption.EncryptionKey[i++] = static_cast<sal_uInt8>( nObject 
>> 16 );
@@ -1162,7 +1162,7 @@ void PDFWriterImpl::checkAndEnableStreamEncryption( 
sal_Int32 nObject )
     // the i+2 to take into account the generation number, always zero
     // initialize the RC4 with the key
     // key length: see algorithm 3.1, step 4: (N+5) max 16
-    rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, 
nMD5Sum.data(), m_nRC4KeyLength, nullptr, 0 );
+    rtl_cipher_initARCFOUR(m_aPDFEncryptor.m_aCipher, 
rtl_Cipher_DirectionEncode, nMD5Sum.data(), m_aPDFEncryptor.m_nRC4KeyLength, 
nullptr, 0);
 }
 
 void PDFWriterImpl::enableStringEncryption( sal_Int32 nObject )
@@ -1170,7 +1170,7 @@ void PDFWriterImpl::enableStringEncryption( sal_Int32 
nObject )
     if( !m_aContext.Encryption.Encrypt() )
         return;
 
-    sal_Int32 i = m_nKeyLength;
+    sal_Int32 i = m_aPDFEncryptor.m_nKeyLength;
     m_aContext.Encryption.EncryptionKey[i++] = static_cast<sal_uInt8>(nObject);
     m_aContext.Encryption.EncryptionKey[i++] = static_cast<sal_uInt8>( nObject 
>> 8 );
     m_aContext.Encryption.EncryptionKey[i++] = static_cast<sal_uInt8>( nObject 
>> 16 );
@@ -1181,7 +1181,7 @@ void PDFWriterImpl::enableStringEncryption( sal_Int32 
nObject )
         m_aContext.Encryption.EncryptionKey.data(), i+2, 
::comphelper::HashType::MD5));
     // initialize the RC4 with the key
     // key length: see algorithm 3.1, step 4: (N+5) max 16
-    rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode, 
nMD5Sum.data(), m_nRC4KeyLength, nullptr, 0 );
+    rtl_cipher_initARCFOUR(m_aPDFEncryptor.m_aCipher, 
rtl_Cipher_DirectionEncode, nMD5Sum.data(), m_aPDFEncryptor.m_nRC4KeyLength, 
nullptr, 0);
 }
 
 /* init the encryption engine
@@ -1284,7 +1284,7 @@ void PDFWriterImpl::padPassword( std::u16string_view 
i_rPassword, sal_uInt8* o_p
     //pad it with standard byte string
     sal_Int32 i,y;
     for( i = nCurrentChar, y = 0 ; i < ENCRYPTED_PWD_SIZE; i++, y++ )
-        o_pPaddedPW[i] = s_nPadString[y];
+        o_pPaddedPW[i] = PDFEncryptor::s_nPadString[y];
 }
 
 /**********************************
@@ -1459,7 +1459,7 @@ bool PDFWriterImpl::computeUDictionaryValue( 
EncHashTransporter* i_pTransporter,
             for(sal_uInt32 i = MD5_DIGEST_SIZE; i < 
sal_uInt32(io_rProperties.UValue.size()); i++)
                 io_rProperties.UValue[i] = 0;
             //steps 2 and 3
-            aDigest.update(s_nPadString, sizeof(s_nPadString));
+            aDigest.update(PDFEncryptor::s_nPadString, 
sizeof(PDFEncryptor::s_nPadString));
             aDigest.update(io_rProperties.DocumentIdentifier.data(), 
io_rProperties.DocumentIdentifier.size());
 
             ::std::vector<unsigned char> const nMD5Sum(aDigest.finalize());

Reply via email to