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());
