diff options
-rw-r--r-- | include/vcl/pdfwriter.hxx | 6 | ||||
-rw-r--r-- | vcl/inc/pdf/EncryptionHashTransporter.hxx | 29 | ||||
-rw-r--r-- | vcl/inc/pdf/IPDFEncryptor.hxx | 6 | ||||
-rw-r--r-- | vcl/inc/pdf/PDFEncryptorR6.hxx | 49 | ||||
-rw-r--r-- | vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx | 136 | ||||
-rw-r--r-- | vcl/qa/cppunit/pdfexport/pdfexport2.cxx | 21 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.cxx | 88 | ||||
-rw-r--r-- | vcl/source/pdf/PDFEncryptionInitialization.cxx | 1 | ||||
-rw-r--r-- | 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<sal_uInt8> OValue; + std::vector<sal_uInt8> OE; // needed by R6 algorithm + std::vector<sal_uInt8> UValue; + std::vector<sal_uInt8> UE; // needed by R6 algorithm + std::vector<sal_uInt8> EncryptionKey; std::vector<sal_uInt8> DocumentIdentifier; @@ -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<css::beans::XMaterialHolder> { + // V2R3 std::unique_ptr<comphelper::Hash> m_pDigest; std::vector<sal_uInt8> maOValue; + + // V5R6 + std::vector<sal_uInt8> mU; + std::vector<sal_uInt8> mUE; + std::vector<sal_uInt8> mO; + std::vector<sal_uInt8> mOE; + std::vector<sal_uInt8> maEncryptionKey; + + // ID sal_IntPtr maID; public: @@ -43,6 +52,24 @@ public: void invalidate() { m_pDigest.reset(); } + std::vector<sal_uInt8> getU() { return mU; } + void setU(std::vector<sal_uInt8> const& rU) { mU = rU; } + + std::vector<sal_uInt8> getUE() { return mUE; } + void setUE(std::vector<sal_uInt8> const& rUE) { mUE = rUE; } + + std::vector<sal_uInt8> getO() { return mO; } + void setO(std::vector<sal_uInt8> const& rO) { mO = rO; } + + std::vector<sal_uInt8> getOE() { return mOE; } + void setOE(std::vector<sal_uInt8> const& rOE) { mOE = rOE; } + + std::vector<sal_uInt8> getEncryptionKey() { return maEncryptionKey; } + void setEncryptionKey(std::vector<sal_uInt8> const& rEncryptionKey) + { + maEncryptionKey = rEncryptionKey; + } + // XMaterialHolder virtual css::uno::Any SAL_CALL getMaterial() override { return css::uno::Any(sal_Int64(maID)); } diff --git a/vcl/inc/pdf/IPDFEncryptor.hxx b/vcl/inc/pdf/IPDFEncryptor.hxx index 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<sal_uInt8> getEncryptedAccessPermissions() + virtual std::vector<sal_uInt8> getEncryptedAccessPermissions(std::vector<sal_uInt8>& /*rKey*/) { return std::vector<sal_uInt8>(); } @@ -79,6 +78,9 @@ public: /** Setup before we start encrypting - remembers the key */ virtual void setupEncryption(std::vector<sal_uInt8>& rEncryptionKey, sal_Int32 nObject) = 0; + /** Calculate the size of the output (by default the same as input) */ + virtual sal_uInt64 calculateSizeIncludingHeader(sal_uInt64 nSize) { return nSize; } + /** Encrypts the input and stores into the output */ virtual void encrypt(const void* pInput, sal_uInt64 nInputSize, std::vector<sal_uInt8>& rOutput, sal_uInt64 nOutputSize) diff --git a/vcl/inc/pdf/PDFEncryptorR6.hxx b/vcl/inc/pdf/PDFEncryptorR6.hxx index 219796ccf0a5..a8812fefcd21 100644 --- a/vcl/inc/pdf/PDFEncryptorR6.hxx +++ b/vcl/inc/pdf/PDFEncryptorR6.hxx @@ -13,9 +13,12 @@ #include <string_view> #include <vector> #include <vcl/dllapi.h> +#include <pdf/IPDFEncryptor.hxx> namespace vcl::pdf { +class EncryptionHashTransporter; + /** Algorithm 2.B: Computing a hash (revision 6 and later) * * Described in ISO 32000-2:2020(E) - 7.6.4.3.4 @@ -107,6 +110,52 @@ VCL_DLLPUBLIC std::vector<sal_uInt8> createPerms(sal_Int32 nAccessPermissions, */ VCL_DLLPUBLIC size_t addPaddingToVector(std::vector<sal_uInt8>& rVector, size_t nBlockSize); +class EncryptionContext; + +/** IPDFEncryptor implementation of PDF encryption version 5 revision 6 added in PDF 2.0 + * + * The complete algorithm is defined in PDF 2.0 specification ISO 32000-2:2020(E) + */ +class VCL_DLLPUBLIC PDFEncryptorR6 : public IPDFEncryptor +{ + std::unique_ptr<EncryptionContext> m_pEncryptionContext; + sal_Int32 m_nAccessPermissions = 0; + +public: + PDFEncryptorR6(); + ~PDFEncryptorR6(); + + sal_Int32 getVersion() override { return 5; } + sal_Int32 getRevision() override { return 6; } + sal_Int32 getAccessPermissions() override { return m_nAccessPermissions; } + /** Key length - AES 256 bit */ + sal_Int32 getKeyLength() override { return 256 / 8; } + + std::vector<sal_uInt8> getEncryptedAccessPermissions(std::vector<sal_uInt8>& rKey) override; + + static void initEncryption(EncryptionHashTransporter& rEncryptionHashTransporter, + const OUString& i_rOwnerPassword, const OUString& i_rUserPassword); + + bool prepareEncryption( + const css::uno::Reference<css::beans::XMaterialHolder>& xEncryptionMaterialHolder, + PDFEncryptionProperties& rProperties) override; + + void setupKeysAndCheck(PDFEncryptionProperties& rProperties) override; + + sal_uInt64 calculateSizeIncludingHeader(sal_uInt64 nSize) override; + + void setupEncryption(std::vector<sal_uInt8>& rEncryptionKey, sal_Int32 nObject) override; + + void setupEncryptionWithIV(std::vector<sal_uInt8>& rInitvector, std::vector<sal_uInt8>& rIV); + + /** Encrypts using Algorithm 1.A: Encryption of data using the AES algorithms + * + * Described in ISO 32000-2:2020(E) - 7.6.3.3 + */ + void encrypt(const void* pInput, sal_uInt64 nInputSize, std::vector<sal_uInt8>& rOutput, + sal_uInt64 nOutputsSize) override; +}; + } // end vcl::pdf /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx b/vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx index 40a4eb94c1b4..f4eeb2a9fd05 100644 --- a/vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx +++ b/vcl/qa/cppunit/pdfexport/PDFEncryptionTest.cxx @@ -10,21 +10,25 @@ #include <sal/config.h> #include <config_oox.h> -#include <algorithm> -#include <memory> -#include <string_view> - #include <test/unoapi_test.hxx> #include <o3tl/string_view.hxx> -#include <vcl/filter/PDFiumLibrary.hxx> -#include <vcl/pdfread.hxx> -#include <comphelper/propertyvalue.hxx> -#include <cmath> - +#include <unotools/mediadescriptor.hxx> #include <comphelper/crypto/Crypto.hxx> #include <comphelper/hash.hxx> #include <comphelper/random.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/propertyvalue.hxx> + +#include <vcl/filter/PDFiumLibrary.hxx> +#include <vcl/pdfread.hxx> + +#include <com/sun/star/frame/XStorable.hpp> + +#include <algorithm> +#include <memory> +#include <string_view> +#include <cmath> #include <pdf/PDFEncryptorR6.hxx> @@ -38,6 +42,9 @@ namespace { class PDFEncryptionTest : public UnoApiTest { +protected: + utl::MediaDescriptor maMediaDescriptor; + public: PDFEncryptionTest() : UnoApiTest("/vcl/qa/cppunit/pdfexport/data/") @@ -79,6 +86,48 @@ std::vector<sal_uInt8> parseHex(std::string_view rString) return aResult; } +CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testEncryptionRoundtrip_PDF_1_7) +{ + loadFromFile(u"BrownFoxLazyDog.odt"); + + // Save PDF + uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY); + maMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export"); + uno::Sequence<beans::PropertyValue> aFilterData = comphelper::InitPropertySequence( + { { "SelectPdfVersion", uno::Any(sal_Int32(17)) }, + { "EncryptFile", uno::Any(true) }, + { "DocumentOpenPassword", uno::Any(u"secret"_ustr) } }); + maMediaDescriptor["FilterData"] <<= aFilterData; + xStorable->storeToURL(maTempFile.GetURL(), maMediaDescriptor.getAsConstPropertyValueList()); + + // Load the exported result in PDFium + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport("secret"_ostr); + CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount()); + int nFileVersion = pPdfDocument->getFileVersion(); + CPPUNIT_ASSERT_EQUAL(17, nFileVersion); +} + +CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testEncryptionRoundtrip_PDF_2_0) +{ + loadFromFile(u"BrownFoxLazyDog.odt"); + + // Save PDF + uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY); + maMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export"); + uno::Sequence<beans::PropertyValue> aFilterData = comphelper::InitPropertySequence( + { { "SelectPdfVersion", uno::Any(sal_Int32(20)) }, + { "EncryptFile", uno::Any(true) }, + { "DocumentOpenPassword", uno::Any(u"secret"_ustr) } }); + maMediaDescriptor["FilterData"] <<= aFilterData; + xStorable->storeToURL(maTempFile.GetURL(), maMediaDescriptor.getAsConstPropertyValueList()); + + // Load the exported result in PDFium + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport("secret"_ostr); + CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount()); + int nFileVersion = pPdfDocument->getFileVersion(); + CPPUNIT_ASSERT_EQUAL(20, nFileVersion); +} + CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testComputeHashForR6) { const sal_uInt8 pOwnerPass[] = { 'T', 'e', 's', 't' }; @@ -257,6 +306,75 @@ CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testPadding) CPPUNIT_ASSERT_EQUAL(sal_uInt8(0x0B), aVector[i]); } +CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testFileDecryption) +{ + std::vector<sal_uInt8> aData + = parseHex("d07efca5cce3c18fd8e344d45d826886d1774c5e1e310c971f8578924f848fc6"); + + std::vector<sal_uInt8> iv(aData.begin(), aData.begin() + 16); + + CPPUNIT_ASSERT_EQUAL(std::string("d07efca5cce3c18fd8e344d45d826886"), + comphelper::hashToString(iv)); + + std::vector<sal_uInt8> aEncryptedString(aData.begin() + 16, aData.end()); + + std::vector<sal_uInt8> U = parseHex("7BD210807A0277FECC52C261C442F02E1AD62C1A23553348B8F8AF7320" + "DC9978FAB7E65E1BF4CA76F4BE5E6D2AA8C7D5"); + + std::vector<sal_uInt8> UE + = parseHex("67022D91A6BDF3179F488DC9658E54B78A0AD05C6A9C419DCD17A6941C151197"); + + const sal_uInt8 pUserPass[] = { 'T', 'e', 's', 't' }; + + CPPUNIT_ASSERT_EQUAL(true, vcl::pdf::validateUserPassword(pUserPass, 4, U)); + + std::vector<sal_uInt8> aDecryptedKey = vcl::pdf::decryptKey(pUserPass, 4, U, UE); + + CPPUNIT_ASSERT_EQUAL( + std::string("90e657b78c0315610f3f421bd396ff635fa8fe3cf2ea399e7e1ae23e6185b4fc"), + comphelper::hashToString(aDecryptedKey)); + + comphelper::Decrypt aDecrypt(aDecryptedKey, iv, comphelper::CryptoType::AES_256_CBC); + + std::vector<sal_uInt8> aOutput(aEncryptedString.size(), 0); + + aDecrypt.update(aOutput, aEncryptedString); + + CPPUNIT_ASSERT_EQUAL( + std::string("656e2d47420b0b0b0b0b0b0b0b0b0b0b"), // 'en-GB' + padding 0x0B = 11 chars + comphelper::hashToString(aOutput)); +} + +CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testFileEncryption) +{ + std::vector<sal_uInt8> aKey + = parseHex("90e657b78c0315610f3f421bd396ff635fa8fe3cf2ea399e7e1ae23e6185b4fc"); + std::vector<sal_uInt8> aIV = parseHex("d07efca5cce3c18fd8e344d45d826886"); + + static constexpr const auto aData = std::to_array<sal_uInt8>({ 'e', 'n', '-', 'G', 'B' }); + + std::vector<sal_uInt8> aEncryptedBuffer; + + vcl::pdf::PDFEncryptorR6 aEncryptor; + aEncryptor.setupEncryptionWithIV(aKey, aIV); + aEncryptor.encrypt(aData.data(), aData.size(), aEncryptedBuffer, aData.size()); + + CPPUNIT_ASSERT_EQUAL( + std::string("d07efca5cce3c18fd8e344d45d826886d1774c5e1e310c971f8578924f848fc6"), + comphelper::hashToString(aEncryptedBuffer)); + + // Decrypt + std::vector<sal_uInt8> aEncryptedIV(aEncryptedBuffer.begin(), aEncryptedBuffer.begin() + 16); + std::vector<sal_uInt8> aEncryptedString(aEncryptedBuffer.begin() + 16, aEncryptedBuffer.end()); + comphelper::Decrypt aDecrypt(aKey, aEncryptedIV, comphelper::CryptoType::AES_256_CBC); + std::vector<sal_uInt8> aOutputString(aEncryptedString.size(), 0); + aDecrypt.update(aOutputString, aEncryptedString); + + CPPUNIT_ASSERT_EQUAL( + std::string("656e2d47420b0b0b0b0b0b0b0b0b0b0b"), // 'en-GB' + padding 0x0B = 11 chars + comphelper::hashToString(aOutputString)); +} + } // end anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx index 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<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY); - aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export"); - uno::Sequence<beans::PropertyValue> aFilterData = comphelper::InitPropertySequence( - { { "SelectPdfVersion", uno::Any(sal_Int32(20)) }, - { "EncryptFile", uno::Any(true) }, - { "DocumentOpenPassword", uno::Any(u"secret"_ustr) } }); - aMediaDescriptor["FilterData"] <<= aFilterData; - xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList()); - - // Load the exported result in PDFium - std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport("secret"_ostr); - CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount()); - int nFileVersion = pPdfDocument->getFileVersion(); - CPPUNIT_ASSERT_EQUAL(20, nFileVersion); -} - // Check round-trip of importing and exporting the PDF with PDFium filter, // which imports the PDF document as multiple PDFs as graphic object. // Each page in the document has one PDF graphic object which content is diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index aa841973df17..56311e7f2791 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -96,7 +96,7 @@ #include <pdf/objectcopier.hxx> #include <pdf/pdfwriter_impl.hxx> #include <pdf/PdfConfig.hxx> -#include <pdf/IPDFEncryptor.hxx> +#include <pdf/PDFEncryptorR6.hxx> #include <pdf/PDFEncryptor.hxx> #include <o3tl/sorted_vector.hxx> #include <frozen/bits/defines.h> @@ -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<sal_uInt8>( aUnChar & 255 ); } //encrypt in place - m_pPDFEncryptor->encrypt(m_vEncryptionBuffer.data(), nChars, m_vEncryptionBuffer, nChars); + std::vector<sal_uInt8> aNewBuffer(nChars); + m_pPDFEncryptor->encrypt(m_vEncryptionBuffer.data(), nChars, aNewBuffer, nChars); //now append, hexadecimal (appendHex), the encrypted result - for(int i = 0; i < nChars; i++) - appendHex( m_vEncryptionBuffer[i], rOutBuffer ); + appendHexArray(aNewBuffer.data(), aNewBuffer.size(), rOutBuffer); } else PDFWriter::AppendUnicodeTextString(rInString, rOutBuffer); @@ -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<char*>(m_vEncryptionBuffer.data()), nChars, rOutBuffer ); + appendLiteralString(reinterpret_cast<char*>(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<const sal_uInt8*>(pBuffer), static_cast<sal_uLong>(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<unsigned char const*>(pWriteBuffer), static_cast<sal_uInt32>(nBytes)); + m_DocDigest.update(static_cast<unsigned char const*>(pWriteBuffer), sal_uInt32(nActualSize)); - if (m_aFile.write(pWriteBuffer, nBytes, nWritten) != osl::File::E_None) + + if (m_aFile.write(pWriteBuffer, nActualSize, nWritten) != osl::File::E_None) nWritten = 0; - if( nWritten != nBytes ) + if (nWritten != nActualSize) { m_aFile.close(); m_bOpen = false; + return false; } + return true; } - return nWritten == nBytes; + return nWritten == nActualSize; } void PDFWriterImpl::newPage( double nPageWidth, double nPageHeight, PDFWriter::Orientation eOrientation ) @@ -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<sal_uInt8> aEncryptedPermissions = m_pPDFEncryptor->getEncryptedAccessPermissions(rProperties.EncryptionKey); + aWriter.writeHexArray("/Perms", aEncryptedPermissions.data(), aEncryptedPermissions.size()); + + // Write content filter stuff - to select we want AESv3 256bit + aWriter.write("/CF", "<</StdCF <</CFM /AESV3 /Length 256>>>>"); + aWriter.write("/StmF", "/StdCF"); + aWriter.write("/StrF", "/StdCF"); + aWriter.write("/EncryptMetadata", " false "); + } + else + { + // emit the owner password, must not be encrypted + aWriter.writeHexArray("/U", rProperties.UValue.data(), rProperties.UValue.size()); + aWriter.writeHexArray("/O", rProperties.OValue.data(), rProperties.OValue.size()); + } aWriter.write("/P", m_pPDFEncryptor->getAccessPermissions()); aWriter.endDict(); aWriter.endObject(); @@ -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<sal_uInt8> aOutputBuffer(nChar); + m_pPDFEncryptor->encrypt(m_vEncryptionBuffer.data(), nChar, aOutputBuffer, nChar); //now queue the data for output - nChar = 0; - for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) - { - appendHex(m_vEncryptionBuffer[nChar++], aLine ); - appendHex(m_vEncryptionBuffer[nChar++], aLine ); - appendHex(m_vEncryptionBuffer[nChar++], aLine ); - } + appendHexArray(aOutputBuffer.data(), aOutputBuffer.size(), aLine); } else //no encryption requested (PDF/A-1a program flow drops here) { diff --git a/vcl/source/pdf/PDFEncryptionInitialization.cxx b/vcl/source/pdf/PDFEncryptionInitialization.cxx index df06968fc290..c58898329eac 100644 --- a/vcl/source/pdf/PDFEncryptionInitialization.cxx +++ b/vcl/source/pdf/PDFEncryptionInitialization.cxx @@ -24,6 +24,7 @@ css::uno::Reference<css::beans::XMaterialHolder> initEncryption(const OUString& { rtl::Reference<EncryptionHashTransporter> pTransporter = new EncryptionHashTransporter; PDFEncryptor::initEncryption(*pTransporter, i_rOwnerPassword, i_rUserPassword); + PDFEncryptorR6::initEncryption(*pTransporter, i_rOwnerPassword, i_rUserPassword); return pTransporter; } diff --git a/vcl/source/pdf/PDFEncryptorR6.cxx b/vcl/source/pdf/PDFEncryptorR6.cxx index b3e6f9e3059e..c16d7cb1a276 100644 --- a/vcl/source/pdf/PDFEncryptorR6.cxx +++ b/vcl/source/pdf/PDFEncryptorR6.cxx @@ -14,11 +14,14 @@ #include <comphelper/hash.hxx> #include <comphelper/random.hxx> +using namespace css; + namespace vcl::pdf { namespace { constexpr size_t IV_SIZE = 16; +constexpr size_t BLOCK_SIZE = 16; constexpr size_t KEY_SIZE = 32; constexpr size_t SALT_SIZE = 8; @@ -155,7 +158,7 @@ std::vector<sal_uInt8> encryptPerms(std::vector<sal_uInt8>& rPerms, std::vector<sal_uInt8> createPerms(sal_Int32 nAccessPermissions, bool bEncryptMetadata) { std::vector<sal_uInt8> aPermsCreated; - generateBytes(aPermsCreated, 16); + generateBytes(aPermsCreated, 16); // fill with random data - mainly for last 4 bytes aPermsCreated[0] = sal_uInt8(nAccessPermissions); aPermsCreated[1] = sal_uInt8(nAccessPermissions >> 8); aPermsCreated[2] = sal_uInt8(nAccessPermissions >> 16); @@ -249,6 +252,128 @@ size_t addPaddingToVector(std::vector<sal_uInt8>& rVector, size_t nBlockSize) return nPaddedSize; } +class VCL_DLLPUBLIC EncryptionContext +{ +private: + std::vector<sal_uInt8> maKey; + std::vector<sal_uInt8> maInitVector; + +public: + EncryptionContext(std::vector<sal_uInt8> const& rKey, std::vector<sal_uInt8> const& rIV) + : maKey(rKey) + , maInitVector(rIV) + { + } + + /** Algorithm 1.A: Encryption of data using the AES algorithms + * + **/ + void encrypt(const void* pInput, sal_uInt64 nInputSize, std::vector<sal_uInt8>& rOutput) + { + comphelper::Encrypt aEncrypt(maKey, maInitVector, comphelper::CryptoType::AES_256_CBC); + const sal_uInt8* pInputBytes = static_cast<const sal_uInt8*>(pInput); + std::vector<sal_uInt8> aInput(pInputBytes, pInputBytes + nInputSize); + size_t nPaddedSize = addPaddingToVector(aInput, BLOCK_SIZE); + std::vector<sal_uInt8> aOutput(nPaddedSize); + aEncrypt.update(aOutput, aInput); + rOutput.resize(nPaddedSize + IV_SIZE); + std::copy(maInitVector.begin(), maInitVector.end(), rOutput.begin()); + std::copy(aOutput.begin(), aOutput.end(), rOutput.begin() + IV_SIZE); + } +}; + +PDFEncryptorR6::PDFEncryptorR6() = default; +PDFEncryptorR6::~PDFEncryptorR6() = default; + +std::vector<sal_uInt8> PDFEncryptorR6::getEncryptedAccessPermissions(std::vector<sal_uInt8>& rKey) +{ + std::vector<sal_uInt8> aPerms = createPerms(m_nAccessPermissions, false); + return encryptPerms(aPerms, rKey); +} + +void PDFEncryptorR6::initEncryption(EncryptionHashTransporter& rEncryptionHashTransporter, + const OUString& rOwnerPassword, const OUString& rUserPassword) +{ + if (rUserPassword.isEmpty()) + return; + + std::vector<sal_uInt8> aEncryptionKey = vcl::pdf::generateKey(); + rEncryptionHashTransporter.setEncryptionKey(aEncryptionKey); + + std::vector<sal_uInt8> U; + std::vector<sal_uInt8> UE; + + OString pUserPasswordUtf8 = OUStringToOString(rUserPassword, RTL_TEXTENCODING_UTF8); + vcl::pdf::generateUandUE(reinterpret_cast<const sal_uInt8*>(pUserPasswordUtf8.getStr()), + pUserPasswordUtf8.getLength(), aEncryptionKey, U, UE); + + rEncryptionHashTransporter.setU(U); + rEncryptionHashTransporter.setUE(UE); + + OUString aOwnerPasswordToUse = rOwnerPassword.isEmpty() ? rUserPassword : rOwnerPassword; + + std::vector<sal_uInt8> O; + std::vector<sal_uInt8> OE; + + OString pOwnerPasswordUtf8 = OUStringToOString(aOwnerPasswordToUse, RTL_TEXTENCODING_UTF8); + vcl::pdf::generateOandOE(reinterpret_cast<const sal_uInt8*>(pOwnerPasswordUtf8.getStr()), + pOwnerPasswordUtf8.getLength(), aEncryptionKey, U, O, OE); + + rEncryptionHashTransporter.setO(O); + rEncryptionHashTransporter.setOE(OE); +} + +bool PDFEncryptorR6::prepareEncryption( + const uno::Reference<beans::XMaterialHolder>& xEncryptionMaterialHolder, + vcl::PDFEncryptionProperties& rProperties) +{ + auto* pTransporter + = EncryptionHashTransporter::getEncHashTransporter(xEncryptionMaterialHolder); + + if (!pTransporter) + { + rProperties.clear(); + return false; + } + + rProperties.UValue = pTransporter->getU(); + rProperties.UE = pTransporter->getUE(); + rProperties.OValue = pTransporter->getO(); + rProperties.OE = pTransporter->getOE(); + rProperties.EncryptionKey = pTransporter->getEncryptionKey(); + + return true; +} + +void PDFEncryptorR6::setupKeysAndCheck(vcl::PDFEncryptionProperties& rProperties) +{ + m_nAccessPermissions = rProperties.getAccessPermissions(); +} + +sal_uInt64 PDFEncryptorR6::calculateSizeIncludingHeader(sal_uInt64 nSize) +{ + return IV_SIZE + comphelper::roundUp<sal_uInt64>(nSize, BLOCK_SIZE); +} + +void PDFEncryptorR6::setupEncryption(std::vector<sal_uInt8>& rEncryptionKey, sal_Int32 /*nObject*/) +{ + std::vector<sal_uInt8> aInitVector; + generateBytes(aInitVector, IV_SIZE); + m_pEncryptionContext = std::make_unique<EncryptionContext>(rEncryptionKey, aInitVector); +} + +void PDFEncryptorR6::setupEncryptionWithIV(std::vector<sal_uInt8>& rEncryptionKey, + std::vector<sal_uInt8>& rInitvector) +{ + m_pEncryptionContext = std::make_unique<EncryptionContext>(rEncryptionKey, rInitvector); +} + +void PDFEncryptorR6::encrypt(const void* pInput, sal_uInt64 nInputSize, + std::vector<sal_uInt8>& rOutput, sal_uInt64 /*nOutputSize*/) +{ + m_pEncryptionContext->encrypt(pInput, nInputSize, rOutput); +} + } // end vcl::pdf /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |