diff options
author | Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> | 2024-11-21 11:33:47 +0900 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2024-12-21 14:10:35 +0100 |
commit | 2b7b8106ecdab2f62f5239462b65ed6a2a12395e (patch) | |
tree | 5309f5a4201c4ef23d82dcdf8b9e13edf92e8bab | |
parent | 7ea66a327fd8d66350dbb931325104ad19097ec1 (diff) |
pdf: implement PDFEncryptor for PDF (2.0) encryption V5R6
PDFEncryptorR6 implements PDF 2.0 encryption (ver. 5 rev. 6) using
AESv3 (256 bit key length). The PDFEncryptorR6 is enabled when the
PDF output is PDF 2.0 (other versions have been deprecated, so it
is the only one available in PDF 2.0).
Change-Id: I27f270197910405b5c2378a3873d2495f52f3206
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176885
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178759
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Tested-by: Jenkins
-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: */ |