From 2b7b8106ecdab2f62f5239462b65ed6a2a12395e Mon Sep 17 00:00:00 2001 From: Tomaž Vajngerl Date: Thu, 21 Nov 2024 11:33:47 +0900 Subject: pdf: implement PDFEncryptor for PDF (2.0) encryption V5R6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PDFEncryptorR6 implements PDF 2.0 encryption (ver. 5 rev. 6) using AESv3 (256 bit key length). The PDFEncryptorR6 is enabled when the PDF output is PDF 2.0 (other versions have been deprecated, so it is the only one available in PDF 2.0). Change-Id: I27f270197910405b5c2378a3873d2495f52f3206 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176885 Tested-by: Jenkins CollaboraOffice Reviewed-by: Miklos Vajna Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178759 Reviewed-by: Tomaž Vajngerl Tested-by: Jenkins --- include/vcl/pdfwriter.hxx | 6 ++ vcl/inc/pdf/EncryptionHashTransporter.hxx | 29 +++++- vcl/inc/pdf/IPDFEncryptor.hxx | 6 +- vcl/inc/pdf/PDFEncryptorR6.hxx | 49 +++++++++ vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx | 136 +++++++++++++++++++++++-- vcl/qa/cppunit/pdfexport/pdfexport2.cxx | 21 ---- vcl/source/gdi/pdfwriter_impl.cxx | 88 +++++++++++----- vcl/source/pdf/PDFEncryptionInitialization.cxx | 1 + vcl/source/pdf/PDFEncryptorR6.cxx | 127 ++++++++++++++++++++++- 9 files changed, 402 insertions(+), 61 deletions(-) diff --git a/include/vcl/pdfwriter.hxx b/include/vcl/pdfwriter.hxx index 9c6432203817..73ba0fcb9742 100644 --- a/include/vcl/pdfwriter.hxx +++ b/include/vcl/pdfwriter.hxx @@ -95,7 +95,11 @@ struct PDFEncryptionProperties // PDFDocInfo, Owner password and User password used the InitEncryption method which // implements the algorithms described in the PDF reference chapter 3.5: Encryption std::vector OValue; + std::vector OE; // needed by R6 algorithm + std::vector UValue; + std::vector UE; // needed by R6 algorithm + std::vector EncryptionKey; std::vector DocumentIdentifier; @@ -107,7 +111,9 @@ struct PDFEncryptionProperties void clear() { OValue.clear(); + OE.clear(); UValue.clear(); + UE.clear(); EncryptionKey.clear(); } diff --git a/vcl/inc/pdf/EncryptionHashTransporter.hxx b/vcl/inc/pdf/EncryptionHashTransporter.hxx index 596b6490570a..9b523f3f90f0 100644 --- a/vcl/inc/pdf/EncryptionHashTransporter.hxx +++ b/vcl/inc/pdf/EncryptionHashTransporter.hxx @@ -5,7 +5,6 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * */ #pragma once @@ -28,8 +27,18 @@ namespace vcl::pdf */ class EncryptionHashTransporter : public cppu::WeakImplHelper { + // V2R3 std::unique_ptr m_pDigest; std::vector maOValue; + + // V5R6 + std::vector mU; + std::vector mUE; + std::vector mO; + std::vector mOE; + std::vector maEncryptionKey; + + // ID sal_IntPtr maID; public: @@ -43,6 +52,24 @@ public: void invalidate() { m_pDigest.reset(); } + std::vector getU() { return mU; } + void setU(std::vector const& rU) { mU = rU; } + + std::vector getUE() { return mUE; } + void setUE(std::vector const& rUE) { mUE = rUE; } + + std::vector getO() { return mO; } + void setO(std::vector const& rO) { mO = rO; } + + std::vector getOE() { return mOE; } + void setOE(std::vector const& rOE) { mOE = rOE; } + + std::vector getEncryptionKey() { return maEncryptionKey; } + void setEncryptionKey(std::vector const& rEncryptionKey) + { + maEncryptionKey = rEncryptionKey; + } + // XMaterialHolder virtual css::uno::Any SAL_CALL getMaterial() override { return css::uno::Any(sal_Int64(maID)); } diff --git a/vcl/inc/pdf/IPDFEncryptor.hxx b/vcl/inc/pdf/IPDFEncryptor.hxx index 706eb82c71ed..61ef8676da46 100644 --- a/vcl/inc/pdf/IPDFEncryptor.hxx +++ b/vcl/inc/pdf/IPDFEncryptor.hxx @@ -5,7 +5,6 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * */ #pragma once @@ -59,7 +58,7 @@ public: * Depending on the encryption revision this may not be available. In that * case we can expect empty content. */ - virtual std::vector getEncryptedAccessPermissions() + virtual std::vector getEncryptedAccessPermissions(std::vector& /*rKey*/) { return std::vector(); } @@ -79,6 +78,9 @@ public: /** Setup before we start encrypting - remembers the key */ virtual void setupEncryption(std::vector& rEncryptionKey, sal_Int32 nObject) = 0; + /** Calculate the size of the output (by default the same as input) */ + virtual sal_uInt64 calculateSizeIncludingHeader(sal_uInt64 nSize) { return nSize; } + /** Encrypts the input and stores into the output */ virtual void encrypt(const void* pInput, sal_uInt64 nInputSize, std::vector& rOutput, sal_uInt64 nOutputSize) diff --git a/vcl/inc/pdf/PDFEncryptorR6.hxx b/vcl/inc/pdf/PDFEncryptorR6.hxx index 219796ccf0a5..a8812fefcd21 100644 --- a/vcl/inc/pdf/PDFEncryptorR6.hxx +++ b/vcl/inc/pdf/PDFEncryptorR6.hxx @@ -13,9 +13,12 @@ #include #include #include +#include namespace vcl::pdf { +class EncryptionHashTransporter; + /** Algorithm 2.B: Computing a hash (revision 6 and later) * * Described in ISO 32000-2:2020(E) - 7.6.4.3.4 @@ -107,6 +110,52 @@ VCL_DLLPUBLIC std::vector createPerms(sal_Int32 nAccessPermissions, */ VCL_DLLPUBLIC size_t addPaddingToVector(std::vector& rVector, size_t nBlockSize); +class EncryptionContext; + +/** IPDFEncryptor implementation of PDF encryption version 5 revision 6 added in PDF 2.0 + * + * The complete algorithm is defined in PDF 2.0 specification ISO 32000-2:2020(E) + */ +class VCL_DLLPUBLIC PDFEncryptorR6 : public IPDFEncryptor +{ + std::unique_ptr m_pEncryptionContext; + sal_Int32 m_nAccessPermissions = 0; + +public: + PDFEncryptorR6(); + ~PDFEncryptorR6(); + + sal_Int32 getVersion() override { return 5; } + sal_Int32 getRevision() override { return 6; } + sal_Int32 getAccessPermissions() override { return m_nAccessPermissions; } + /** Key length - AES 256 bit */ + sal_Int32 getKeyLength() override { return 256 / 8; } + + std::vector getEncryptedAccessPermissions(std::vector& rKey) override; + + static void initEncryption(EncryptionHashTransporter& rEncryptionHashTransporter, + const OUString& i_rOwnerPassword, const OUString& i_rUserPassword); + + bool prepareEncryption( + const css::uno::Reference& xEncryptionMaterialHolder, + PDFEncryptionProperties& rProperties) override; + + void setupKeysAndCheck(PDFEncryptionProperties& rProperties) override; + + sal_uInt64 calculateSizeIncludingHeader(sal_uInt64 nSize) override; + + void setupEncryption(std::vector& rEncryptionKey, sal_Int32 nObject) override; + + void setupEncryptionWithIV(std::vector& rInitvector, std::vector& rIV); + + /** Encrypts using Algorithm 1.A: Encryption of data using the AES algorithms + * + * Described in ISO 32000-2:2020(E) - 7.6.3.3 + */ + void encrypt(const void* pInput, sal_uInt64 nInputSize, std::vector& rOutput, + sal_uInt64 nOutputsSize) override; +}; + } // end vcl::pdf /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx b/vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx index 40a4eb94c1b4..f4eeb2a9fd05 100644 --- a/vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx +++ b/vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx @@ -10,21 +10,25 @@ #include #include -#include -#include -#include - #include #include -#include -#include -#include -#include - +#include #include #include #include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include #include @@ -38,6 +42,9 @@ namespace { class PDFEncryptionTest : public UnoApiTest { +protected: + utl::MediaDescriptor maMediaDescriptor; + public: PDFEncryptionTest() : UnoApiTest("/vcl/qa/cppunit/pdfexport/data/") @@ -79,6 +86,48 @@ std::vector parseHex(std::string_view rString) return aResult; } +CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testEncryptionRoundtrip_PDF_1_7) +{ + loadFromFile(u"BrownFoxLazyDog.odt"); + + // Save PDF + uno::Reference xStorable(mxComponent, uno::UNO_QUERY); + maMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export"); + uno::Sequence aFilterData = comphelper::InitPropertySequence( + { { "SelectPdfVersion", uno::Any(sal_Int32(17)) }, + { "EncryptFile", uno::Any(true) }, + { "DocumentOpenPassword", uno::Any(u"secret"_ustr) } }); + maMediaDescriptor["FilterData"] <<= aFilterData; + xStorable->storeToURL(maTempFile.GetURL(), maMediaDescriptor.getAsConstPropertyValueList()); + + // Load the exported result in PDFium + std::unique_ptr pPdfDocument = parsePDFExport("secret"_ostr); + CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount()); + int nFileVersion = pPdfDocument->getFileVersion(); + CPPUNIT_ASSERT_EQUAL(17, nFileVersion); +} + +CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testEncryptionRoundtrip_PDF_2_0) +{ + loadFromFile(u"BrownFoxLazyDog.odt"); + + // Save PDF + uno::Reference xStorable(mxComponent, uno::UNO_QUERY); + maMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export"); + uno::Sequence aFilterData = comphelper::InitPropertySequence( + { { "SelectPdfVersion", uno::Any(sal_Int32(20)) }, + { "EncryptFile", uno::Any(true) }, + { "DocumentOpenPassword", uno::Any(u"secret"_ustr) } }); + maMediaDescriptor["FilterData"] <<= aFilterData; + xStorable->storeToURL(maTempFile.GetURL(), maMediaDescriptor.getAsConstPropertyValueList()); + + // Load the exported result in PDFium + std::unique_ptr pPdfDocument = parsePDFExport("secret"_ostr); + CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount()); + int nFileVersion = pPdfDocument->getFileVersion(); + CPPUNIT_ASSERT_EQUAL(20, nFileVersion); +} + CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testComputeHashForR6) { const sal_uInt8 pOwnerPass[] = { 'T', 'e', 's', 't' }; @@ -257,6 +306,75 @@ CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testPadding) CPPUNIT_ASSERT_EQUAL(sal_uInt8(0x0B), aVector[i]); } +CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testFileDecryption) +{ + std::vector aData + = parseHex("d07efca5cce3c18fd8e344d45d826886d1774c5e1e310c971f8578924f848fc6"); + + std::vector iv(aData.begin(), aData.begin() + 16); + + CPPUNIT_ASSERT_EQUAL(std::string("d07efca5cce3c18fd8e344d45d826886"), + comphelper::hashToString(iv)); + + std::vector aEncryptedString(aData.begin() + 16, aData.end()); + + std::vector U = parseHex("7BD210807A0277FECC52C261C442F02E1AD62C1A23553348B8F8AF7320" + "DC9978FAB7E65E1BF4CA76F4BE5E6D2AA8C7D5"); + + std::vector UE + = parseHex("67022D91A6BDF3179F488DC9658E54B78A0AD05C6A9C419DCD17A6941C151197"); + + const sal_uInt8 pUserPass[] = { 'T', 'e', 's', 't' }; + + CPPUNIT_ASSERT_EQUAL(true, vcl::pdf::validateUserPassword(pUserPass, 4, U)); + + std::vector aDecryptedKey = vcl::pdf::decryptKey(pUserPass, 4, U, UE); + + CPPUNIT_ASSERT_EQUAL( + std::string("90e657b78c0315610f3f421bd396ff635fa8fe3cf2ea399e7e1ae23e6185b4fc"), + comphelper::hashToString(aDecryptedKey)); + + comphelper::Decrypt aDecrypt(aDecryptedKey, iv, comphelper::CryptoType::AES_256_CBC); + + std::vector aOutput(aEncryptedString.size(), 0); + + aDecrypt.update(aOutput, aEncryptedString); + + CPPUNIT_ASSERT_EQUAL( + std::string("656e2d47420b0b0b0b0b0b0b0b0b0b0b"), // 'en-GB' + padding 0x0B = 11 chars + comphelper::hashToString(aOutput)); +} + +CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testFileEncryption) +{ + std::vector aKey + = parseHex("90e657b78c0315610f3f421bd396ff635fa8fe3cf2ea399e7e1ae23e6185b4fc"); + std::vector aIV = parseHex("d07efca5cce3c18fd8e344d45d826886"); + + static constexpr const auto aData = std::to_array({ 'e', 'n', '-', 'G', 'B' }); + + std::vector aEncryptedBuffer; + + vcl::pdf::PDFEncryptorR6 aEncryptor; + aEncryptor.setupEncryptionWithIV(aKey, aIV); + aEncryptor.encrypt(aData.data(), aData.size(), aEncryptedBuffer, aData.size()); + + CPPUNIT_ASSERT_EQUAL( + std::string("d07efca5cce3c18fd8e344d45d826886d1774c5e1e310c971f8578924f848fc6"), + comphelper::hashToString(aEncryptedBuffer)); + + // Decrypt + std::vector aEncryptedIV(aEncryptedBuffer.begin(), aEncryptedBuffer.begin() + 16); + std::vector aEncryptedString(aEncryptedBuffer.begin() + 16, aEncryptedBuffer.end()); + comphelper::Decrypt aDecrypt(aKey, aEncryptedIV, comphelper::CryptoType::AES_256_CBC); + std::vector aOutputString(aEncryptedString.size(), 0); + aDecrypt.update(aOutputString, aEncryptedString); + + CPPUNIT_ASSERT_EQUAL( + std::string("656e2d47420b0b0b0b0b0b0b0b0b0b0b"), // 'en-GB' + padding 0x0B = 11 chars + comphelper::hashToString(aOutputString)); +} + } // end anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx index 20acdc856aeb..548036654222 100644 --- a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx +++ b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx @@ -750,27 +750,6 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testVersion20) CPPUNIT_ASSERT_EQUAL(20, nFileVersion); } -CPPUNIT_TEST_FIXTURE(PdfExportTest2, testEncryptionRoundtrip) -{ - mxComponent = loadFromDesktop("private:factory/swriter"); - - // Save PDF - uno::Reference xStorable(mxComponent, uno::UNO_QUERY); - aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export"); - uno::Sequence aFilterData = comphelper::InitPropertySequence( - { { "SelectPdfVersion", uno::Any(sal_Int32(20)) }, - { "EncryptFile", uno::Any(true) }, - { "DocumentOpenPassword", uno::Any(u"secret"_ustr) } }); - aMediaDescriptor["FilterData"] <<= aFilterData; - xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList()); - - // Load the exported result in PDFium - std::unique_ptr pPdfDocument = parsePDFExport("secret"_ostr); - CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount()); - int nFileVersion = pPdfDocument->getFileVersion(); - CPPUNIT_ASSERT_EQUAL(20, nFileVersion); -} - // Check round-trip of importing and exporting the PDF with PDFium filter, // which imports the PDF document as multiple PDFs as graphic object. // Each page in the document has one PDF graphic object which content is diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index aa841973df17..56311e7f2791 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -96,7 +96,7 @@ #include #include #include -#include +#include #include #include #include @@ -143,6 +143,12 @@ void appendHex(sal_Int8 nInt, OStringBuffer& rBuffer) rBuffer.append( pHexDigits[ nInt & 15 ] ); } +void appendHexArray(sal_uInt8* pArray, size_t nSize, OStringBuffer& rBuffer) +{ + for (size_t i = 0; i < nSize; i++) + appendHex(pArray[i], rBuffer); +} + void appendName( std::u16string_view rStr, OStringBuffer& rBuffer ) { // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1 @@ -1459,7 +1465,10 @@ PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext, if (xEncryptionMaterialHolder.is()) { - m_pPDFEncryptor.reset(new PDFEncryptor); + if (m_aContext.Version == PDFWriter::PDFVersion::PDF_2_0 || m_aContext.Version == PDFWriter::PDFVersion::PDF_A_4) + m_pPDFEncryptor.reset(new PDFEncryptorR6); + else + m_pPDFEncryptor.reset(new PDFEncryptor); m_pPDFEncryptor->prepareEncryption(xEncryptionMaterialHolder, m_aContext.Encryption); } @@ -1618,10 +1627,10 @@ inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const OUString& rInSt *pCopy++ = static_cast( aUnChar & 255 ); } //encrypt in place - m_pPDFEncryptor->encrypt(m_vEncryptionBuffer.data(), nChars, m_vEncryptionBuffer, nChars); + std::vector aNewBuffer(nChars); + m_pPDFEncryptor->encrypt(m_vEncryptionBuffer.data(), nChars, aNewBuffer, nChars); //now append, hexadecimal (appendHex), the encrypted result - for(int i = 0; i < nChars; i++) - appendHex( m_vEncryptionBuffer[i], rOutBuffer ); + appendHexArray(aNewBuffer.data(), aNewBuffer.size(), rOutBuffer); } else PDFWriter::AppendUnicodeTextString(rInString, rOutBuffer); @@ -1639,7 +1648,7 @@ inline void PDFWriterImpl::appendLiteralStringEncrypt( std::string_view rInStrin //encrypt the string in a buffer, then append it enableStringEncryption(nInObjectNumber); m_pPDFEncryptor->encrypt(rInString.data(), nChars, m_vEncryptionBuffer, nChars); - appendLiteralString( reinterpret_cast(m_vEncryptionBuffer.data()), nChars, rOutBuffer ); + appendLiteralString(reinterpret_cast(m_vEncryptionBuffer.data()), m_vEncryptionBuffer.size(), rOutBuffer); } else appendLiteralString( rInString.data(), nChars , rOutBuffer ); @@ -1738,34 +1747,42 @@ bool PDFWriterImpl::writeBufferBytes( const void* pBuffer, sal_uInt64 nBytes ) } sal_uInt64 nWritten; - if( m_pCodec ) + sal_uInt64 nActualSize = nBytes; + + // we are compressing the stream + if (m_pCodec) { m_pCodec->Write( *m_pMemStream, static_cast(pBuffer), static_cast(nBytes) ); nWritten = nBytes; } else { + // is it encrypted? bool bStreamEncryption = m_pPDFEncryptor && m_pPDFEncryptor->isStreamEncryptionEnabled(); if (bStreamEncryption) { - m_vEncryptionBuffer.resize(nBytes); - m_pPDFEncryptor->encrypt(pBuffer, nBytes, m_vEncryptionBuffer, nBytes); + nActualSize = m_pPDFEncryptor->calculateSizeIncludingHeader(nActualSize); + m_vEncryptionBuffer.resize(nActualSize); + m_pPDFEncryptor->encrypt(pBuffer, nBytes, m_vEncryptionBuffer, nActualSize); } const void* pWriteBuffer = bStreamEncryption ? m_vEncryptionBuffer.data() : pBuffer; - m_DocDigest.update(static_cast(pWriteBuffer), static_cast(nBytes)); + m_DocDigest.update(static_cast(pWriteBuffer), sal_uInt32(nActualSize)); - if (m_aFile.write(pWriteBuffer, nBytes, nWritten) != osl::File::E_None) + + if (m_aFile.write(pWriteBuffer, nActualSize, nWritten) != osl::File::E_None) nWritten = 0; - if( nWritten != nBytes ) + if (nWritten != nActualSize) { m_aFile.close(); m_bOpen = false; + return false; } + return true; } - return nWritten == nBytes; + return nWritten == nActualSize; } void PDFWriterImpl::newPage( double nPageWidth, double nPageHeight, PDFWriter::Orientation eOrientation ) @@ -6160,12 +6177,34 @@ sal_Int32 PDFWriterImpl::emitEncrypt() aWriter.startObject(nObject); aWriter.startDict(); aWriter.write("/Filter", "/Standard"); - aWriter.write("/V", 2); - aWriter.write("/Length", 128); - aWriter.write("/R", 3); - // emit the owner password, must not be encrypted - aWriter.writeHexArray("/O", rProperties.OValue.data(), rProperties.OValue.size()); - aWriter.writeHexArray("/U", rProperties.UValue.data(), rProperties.UValue.size()); + aWriter.write("/V", m_pPDFEncryptor->getVersion()); + aWriter.write("/Length", m_pPDFEncryptor->getKeyLength() * 8); + aWriter.write("/R", m_pPDFEncryptor->getRevision()); + + if (m_pPDFEncryptor->getVersion() == 5 && m_pPDFEncryptor->getRevision() == 6) + { + // emit the owner password, must not be encrypted + aWriter.writeHexArray("/U", rProperties.UValue.data(), rProperties.UValue.size()); + aWriter.writeHexArray("/UE", rProperties.UE.data(), rProperties.UE.size()); + aWriter.writeHexArray("/O", rProperties.OValue.data(), rProperties.OValue.size()); + aWriter.writeHexArray("/OE", rProperties.OE.data(), rProperties.OE.size()); + + // Encrypted perms + std::vector aEncryptedPermissions = m_pPDFEncryptor->getEncryptedAccessPermissions(rProperties.EncryptionKey); + aWriter.writeHexArray("/Perms", aEncryptedPermissions.data(), aEncryptedPermissions.size()); + + // Write content filter stuff - to select we want AESv3 256bit + aWriter.write("/CF", "<>>>"); + aWriter.write("/StmF", "/StdCF"); + aWriter.write("/StrF", "/StdCF"); + aWriter.write("/EncryptMetadata", " false "); + } + else + { + // emit the owner password, must not be encrypted + aWriter.writeHexArray("/U", rProperties.UValue.data(), rProperties.UValue.size()); + aWriter.writeHexArray("/O", rProperties.OValue.data(), rProperties.OValue.size()); + } aWriter.write("/P", m_pPDFEncryptor->getAccessPermissions()); aWriter.endDict(); aWriter.endObject(); @@ -9823,15 +9862,10 @@ bool PDFWriterImpl::writeBitmapObject( const BitmapEmit& rObject, bool bMask ) m_vEncryptionBuffer[nChar++] = rColor.GetBlue(); } //encrypt the colorspace lookup table - m_pPDFEncryptor->encrypt(m_vEncryptionBuffer.data(), nChar, m_vEncryptionBuffer, nChar); + std::vector aOutputBuffer(nChar); + m_pPDFEncryptor->encrypt(m_vEncryptionBuffer.data(), nChar, aOutputBuffer, nChar); //now queue the data for output - nChar = 0; - for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) - { - appendHex(m_vEncryptionBuffer[nChar++], aLine ); - appendHex(m_vEncryptionBuffer[nChar++], aLine ); - appendHex(m_vEncryptionBuffer[nChar++], aLine ); - } + appendHexArray(aOutputBuffer.data(), aOutputBuffer.size(), aLine); } else //no encryption requested (PDF/A-1a program flow drops here) { diff --git a/vcl/source/pdf/PDFEncryptionInitialization.cxx b/vcl/source/pdf/PDFEncryptionInitialization.cxx index df06968fc290..c58898329eac 100644 --- a/vcl/source/pdf/PDFEncryptionInitialization.cxx +++ b/vcl/source/pdf/PDFEncryptionInitialization.cxx @@ -24,6 +24,7 @@ css::uno::Reference initEncryption(const OUString& { rtl::Reference pTransporter = new EncryptionHashTransporter; PDFEncryptor::initEncryption(*pTransporter, i_rOwnerPassword, i_rUserPassword); + PDFEncryptorR6::initEncryption(*pTransporter, i_rOwnerPassword, i_rUserPassword); return pTransporter; } diff --git a/vcl/source/pdf/PDFEncryptorR6.cxx b/vcl/source/pdf/PDFEncryptorR6.cxx index b3e6f9e3059e..c16d7cb1a276 100644 --- a/vcl/source/pdf/PDFEncryptorR6.cxx +++ b/vcl/source/pdf/PDFEncryptorR6.cxx @@ -14,11 +14,14 @@ #include #include +using namespace css; + namespace vcl::pdf { namespace { constexpr size_t IV_SIZE = 16; +constexpr size_t BLOCK_SIZE = 16; constexpr size_t KEY_SIZE = 32; constexpr size_t SALT_SIZE = 8; @@ -155,7 +158,7 @@ std::vector encryptPerms(std::vector& rPerms, std::vector createPerms(sal_Int32 nAccessPermissions, bool bEncryptMetadata) { std::vector aPermsCreated; - generateBytes(aPermsCreated, 16); + generateBytes(aPermsCreated, 16); // fill with random data - mainly for last 4 bytes aPermsCreated[0] = sal_uInt8(nAccessPermissions); aPermsCreated[1] = sal_uInt8(nAccessPermissions >> 8); aPermsCreated[2] = sal_uInt8(nAccessPermissions >> 16); @@ -249,6 +252,128 @@ size_t addPaddingToVector(std::vector& rVector, size_t nBlockSize) return nPaddedSize; } +class VCL_DLLPUBLIC EncryptionContext +{ +private: + std::vector maKey; + std::vector maInitVector; + +public: + EncryptionContext(std::vector const& rKey, std::vector const& rIV) + : maKey(rKey) + , maInitVector(rIV) + { + } + + /** Algorithm 1.A: Encryption of data using the AES algorithms + * + **/ + void encrypt(const void* pInput, sal_uInt64 nInputSize, std::vector& rOutput) + { + comphelper::Encrypt aEncrypt(maKey, maInitVector, comphelper::CryptoType::AES_256_CBC); + const sal_uInt8* pInputBytes = static_cast(pInput); + std::vector aInput(pInputBytes, pInputBytes + nInputSize); + size_t nPaddedSize = addPaddingToVector(aInput, BLOCK_SIZE); + std::vector aOutput(nPaddedSize); + aEncrypt.update(aOutput, aInput); + rOutput.resize(nPaddedSize + IV_SIZE); + std::copy(maInitVector.begin(), maInitVector.end(), rOutput.begin()); + std::copy(aOutput.begin(), aOutput.end(), rOutput.begin() + IV_SIZE); + } +}; + +PDFEncryptorR6::PDFEncryptorR6() = default; +PDFEncryptorR6::~PDFEncryptorR6() = default; + +std::vector PDFEncryptorR6::getEncryptedAccessPermissions(std::vector& rKey) +{ + std::vector aPerms = createPerms(m_nAccessPermissions, false); + return encryptPerms(aPerms, rKey); +} + +void PDFEncryptorR6::initEncryption(EncryptionHashTransporter& rEncryptionHashTransporter, + const OUString& rOwnerPassword, const OUString& rUserPassword) +{ + if (rUserPassword.isEmpty()) + return; + + std::vector aEncryptionKey = vcl::pdf::generateKey(); + rEncryptionHashTransporter.setEncryptionKey(aEncryptionKey); + + std::vector U; + std::vector UE; + + OString pUserPasswordUtf8 = OUStringToOString(rUserPassword, RTL_TEXTENCODING_UTF8); + vcl::pdf::generateUandUE(reinterpret_cast(pUserPasswordUtf8.getStr()), + pUserPasswordUtf8.getLength(), aEncryptionKey, U, UE); + + rEncryptionHashTransporter.setU(U); + rEncryptionHashTransporter.setUE(UE); + + OUString aOwnerPasswordToUse = rOwnerPassword.isEmpty() ? rUserPassword : rOwnerPassword; + + std::vector O; + std::vector OE; + + OString pOwnerPasswordUtf8 = OUStringToOString(aOwnerPasswordToUse, RTL_TEXTENCODING_UTF8); + vcl::pdf::generateOandOE(reinterpret_cast(pOwnerPasswordUtf8.getStr()), + pOwnerPasswordUtf8.getLength(), aEncryptionKey, U, O, OE); + + rEncryptionHashTransporter.setO(O); + rEncryptionHashTransporter.setOE(OE); +} + +bool PDFEncryptorR6::prepareEncryption( + const uno::Reference& xEncryptionMaterialHolder, + vcl::PDFEncryptionProperties& rProperties) +{ + auto* pTransporter + = EncryptionHashTransporter::getEncHashTransporter(xEncryptionMaterialHolder); + + if (!pTransporter) + { + rProperties.clear(); + return false; + } + + rProperties.UValue = pTransporter->getU(); + rProperties.UE = pTransporter->getUE(); + rProperties.OValue = pTransporter->getO(); + rProperties.OE = pTransporter->getOE(); + rProperties.EncryptionKey = pTransporter->getEncryptionKey(); + + return true; +} + +void PDFEncryptorR6::setupKeysAndCheck(vcl::PDFEncryptionProperties& rProperties) +{ + m_nAccessPermissions = rProperties.getAccessPermissions(); +} + +sal_uInt64 PDFEncryptorR6::calculateSizeIncludingHeader(sal_uInt64 nSize) +{ + return IV_SIZE + comphelper::roundUp(nSize, BLOCK_SIZE); +} + +void PDFEncryptorR6::setupEncryption(std::vector& rEncryptionKey, sal_Int32 /*nObject*/) +{ + std::vector aInitVector; + generateBytes(aInitVector, IV_SIZE); + m_pEncryptionContext = std::make_unique(rEncryptionKey, aInitVector); +} + +void PDFEncryptorR6::setupEncryptionWithIV(std::vector& rEncryptionKey, + std::vector& rInitvector) +{ + m_pEncryptionContext = std::make_unique(rEncryptionKey, rInitvector); +} + +void PDFEncryptorR6::encrypt(const void* pInput, sal_uInt64 nInputSize, + std::vector& rOutput, sal_uInt64 /*nOutputSize*/) +{ + m_pEncryptionContext->encrypt(pInput, nInputSize, rOutput); +} + } // end vcl::pdf /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit