/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if USE_TLS_NSS #include #endif using namespace ::com::sun::star; namespace { class PDFEncryptionTest : public UnoApiTest { protected: comphelper::SequenceAsHashMap maMediaDescriptor; public: PDFEncryptionTest() : UnoApiTest("/vcl/qa/cppunit/pdfexport/data/") { } ~PDFEncryptionTest() { #if USE_TLS_NSS NSS_Shutdown(); #endif } }; std::vector parseHex(std::string_view rString) { std::vector aResult; aResult.reserve(rString.size() / 2); for (size_t i = 0; i < rString.size(); i += 2) { aResult.push_back(o3tl::convertToHex(rString[i], rString[i + 1])); } return aResult; } CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testEncryptionRoundtrip_PDF_1_7) { loadFromURL(u"private:factory/swriter"_ustr); // Save PDF uno::Sequence aFilterData = comphelper::InitPropertySequence({ { "SelectPdfVersion", uno::Any(sal_Int32(17)) } }); maMediaDescriptor["FilterData"] <<= aFilterData; save(TestFilter::PDF_WRITER, maMediaDescriptor.getAsConstPropertyValueList(), /*pPassword*/ "secret"); // Load the exported result in PDFium std::unique_ptr pPdfDocument = parsePDFExport(vcl::pdf::PDFiumLibrary::get(), "secret"_ostr); CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount()); int nFileVersion = pPdfDocument->getFileVersion(); CPPUNIT_ASSERT_EQUAL(17, nFileVersion); } // encryption used to cost a tagged file its object streams, and so most of its size CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testEncryptedTaggedUsesObjectStreams) { loadFromURL(u"private:factory/swriter"_ustr); auto xText = mxComponent.queryThrow()->getText(); xText->setString(u"Some tagged text"_ustr); // a footnote, because its element is the one this writer gives an /ID to auto xFactory = mxComponent.queryThrow(); auto xFootnote = xFactory->createInstance(u"com.sun.star.text.Footnote"_ustr) .queryThrow(); xText->insertTextContent(xText->getEnd(), xFootnote, false); xFootnote.queryThrow()->setString(u"The note this refers to"_ustr); uno::Sequence aFilterData{ comphelper::makePropertyValue(u"UseTaggedPDF"_ustr, true), comphelper::makePropertyValue(u"EncryptFile"_ustr, true), comphelper::makePropertyValue(u"DocumentOpenPassword"_ustr, u"secret"_ustr) }; mxComponent.queryThrow()->storeToURL( maTempFile.GetURL(), { comphelper::makePropertyValue(u"FilterName"_ustr, u"writer_pdf_Export"_ustr), comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData) }); std::unique_ptr pPdfDocument = parsePDFExport(vcl::pdf::PDFiumLibrary::get(), "secret"_ostr); // the structure tree went into object streams, which is what the encryption used to cost. // Only strings and stream data are encrypted, so the stream's own dictionary reads plainly const std::string_view aFile(static_cast(maMemory.GetData()), maMemory.GetSize()); // an unencrypted export has object streams anyway, so the rest tests nothing without this CPPUNIT_ASSERT(aFile.find("/Encrypt") != std::string_view::npos); // the dictionary entry, not a chance match in binary data const size_t nObjStm = aFile.find("/Type/ObjStm"); CPPUNIT_ASSERT(nObjStm != std::string_view::npos); // /Length has to count what the cipher adds, or a reader that trusts it stops mid-block. // pdfium does not: it scans for endstream, so only the byte count catches this // the writer puts exactly one newline here, and the cipher's own first byte may be another const size_t nData = aFile.find(">>\nstream\n", nObjStm); CPPUNIT_ASSERT(nData != std::string_view::npos); const size_t nLength = aFile.find("/Length ", nObjStm); CPPUNIT_ASSERT(nLength != std::string_view::npos); // inside this dictionary, so it is this stream's length and not a later object's CPPUNIT_ASSERT_LESS(nData, nLength); const sal_Int32 nDeclared = o3tl::toInt32(aFile.substr(nLength + 8, 12)); CPPUNIT_ASSERT(nData + 10 + nDeclared + 10 <= aFile.size()); CPPUNIT_ASSERT_EQUAL("\nendstream"_ostr, OString(aFile.substr(nData + 10 + nDeclared, 10))); // reading the footnote element's /ID back through the password distinguishes a plain // string from one encrypted a second time CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount()); auto pPage = pPdfDocument->openPage(0); CPPUNIT_ASSERT(pPage); auto pTree = pPage->getStructureTree(); CPPUNIT_ASSERT(pTree); CPPUNIT_ASSERT_GREATER(0, pTree->getNumberOfChildren()); OUString aID; const auto aFindID = [&aID](auto& rSelf, vcl::pdf::PDFiumStructureElement& rElement) -> void { if (aID.isEmpty()) aID = rElement.getID(); for (int i = 0; i < rElement.getNumberOfChildren() && aID.isEmpty(); ++i) { if (const auto pKid = rElement.getChild(i)) rSelf(rSelf, *pKid); } }; for (int i = 0; i < pTree->getNumberOfChildren() && aID.isEmpty(); ++i) { if (const auto pChild = pTree->getChild(i)) aFindID(aFindID, *pChild); } CPPUNIT_ASSERT(aID.startsWith("id")); } CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testEncryptionRoundtrip_PDF_2_0) { loadFromURL(u"private:factory/swriter"_ustr); // Save PDF uno::Sequence aFilterData = comphelper::InitPropertySequence({ { "SelectPdfVersion", uno::Any(sal_Int32(20)) } }); maMediaDescriptor["FilterData"] <<= aFilterData; save(TestFilter::PDF_WRITER, maMediaDescriptor.getAsConstPropertyValueList(), /*pPassword*/ "secret"); // Load the exported result in PDFium std::unique_ptr pPdfDocument = parsePDFExport(vcl::pdf::PDFiumLibrary::get(), "secret"_ostr); CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount()); int nFileVersion = pPdfDocument->getFileVersion(); CPPUNIT_ASSERT_EQUAL(20, nFileVersion); } CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testEncryptionRoundtrip_PDF_2_0_PaddingBlockBoundary) { loadFromURL(u"private:factory/swriter"_ustr); // 16 characters are exactly one cipher block const OUString aLinkURL = u"http://16.by/tes"_ustr; CPPUNIT_ASSERT_EQUAL(sal_Int32(16), aLinkURL.getLength()); uno::Reference xTextDocument(mxComponent, uno::UNO_QUERY_THROW); uno::Reference xText = xTextDocument->getText(); xText->setString(u"link"_ustr); uno::Reference xCursor = xText->createTextCursor(); xCursor->gotoStart(false); xCursor->gotoEnd(true); uno::Reference xCursorProperties(xCursor, uno::UNO_QUERY_THROW); xCursorProperties->setPropertyValue(u"HyperLinkURL"_ustr, uno::Any(aLinkURL)); // Save PDF uno::Reference xStorable(mxComponent, uno::UNO_QUERY); maMediaDescriptor[u"FilterName"_ustr] <<= u"writer_pdf_Export"_ustr; uno::Sequence aFilterData = comphelper::InitPropertySequence( { { "SelectPdfVersion", uno::Any(sal_Int32(20)) }, { "EncryptFile", uno::Any(true) }, { "DocumentOpenPassword", uno::Any(u"secret"_ustr) } }); maMediaDescriptor[u"FilterData"_ustr] <<= aFilterData; xStorable->storeToURL(maTempFile.GetURL(), maMediaDescriptor.getAsConstPropertyValueList()); // Load the exported result in PDFium and read the link target back std::unique_ptr pPdfDocument = parsePDFExport(vcl::pdf::PDFiumLibrary::get(), "secret"_ostr); CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount()); std::unique_ptr pPdfPage = pPdfDocument->openPage(0); CPPUNIT_ASSERT(pPdfPage); int nLinkIndex = 0; std::unique_ptr pLink = pPdfPage->enumerateLink(&nLinkIndex, pPdfDocument.get()); CPPUNIT_ASSERT(pLink); // the problem was that the padding on the encrypted string was wrong // so it could not be read CPPUNIT_ASSERT_EQUAL(aLinkURL, pLink->getURIPath()); } CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testComputeHashForR6) { const sal_uInt8 pOwnerPass[] = { 'T', 'e', 's', 't' }; const sal_uInt8 pUserPass[] = { 'T', 'e', 's', 't' }; std::vector U = parseHex("7BD210807A0277FECC52C261C442F02E1AD62C1A23553348B8F8AF7320" "DC9978FAB7E65E1BF4CA76F4BE5E6D2AA8C7D5"); CPPUNIT_ASSERT_EQUAL(size_t(48), U.size()); std::vector O = parseHex("E4507A474CEFBBA1AF76BA0EB40EC322C91C1900D3FD65FEC98B873BA1" "9B27F89FBC9331D5E14DBCEE2A0ADDA52267C9"); CPPUNIT_ASSERT_EQUAL(size_t(48), O.size()); // User Password { std::vector aUserHash(U.begin(), U.begin() + 32); CPPUNIT_ASSERT_EQUAL(size_t(32), aUserHash.size()); CPPUNIT_ASSERT_EQUAL( std::string("7bd210807a0277fecc52c261c442f02e1ad62c1a23553348b8f8af7320dc9978"), comphelper::hashToString(aUserHash)); std::vector aUserValidationSalt(U.begin() + 32, U.begin() + 32 + 8); auto aComputedHash = vcl::pdf::computeHashR6(pUserPass, 4, aUserValidationSalt); CPPUNIT_ASSERT_EQUAL( std::string("7bd210807a0277fecc52c261c442f02e1ad62c1a23553348b8f8af7320dc9978"), comphelper::hashToString(aComputedHash)); } // Owner Password { std::vector aOwnerHash(O.begin(), O.begin() + 32); CPPUNIT_ASSERT_EQUAL(size_t(32), aOwnerHash.size()); std::vector aOwnerValidationSalt(O.begin() + 32, O.begin() + 32 + 8); CPPUNIT_ASSERT_EQUAL(size_t(8), aOwnerValidationSalt.size()); CPPUNIT_ASSERT_EQUAL( std::string("e4507a474cefbba1af76ba0eb40ec322c91c1900d3fd65fec98b873ba19b27f8"), comphelper::hashToString(aOwnerHash)); auto RO = vcl::pdf::computeHashR6(pOwnerPass, 4, aOwnerValidationSalt, U); CPPUNIT_ASSERT_EQUAL( std::string("e4507a474cefbba1af76ba0eb40ec322c91c1900d3fd65fec98b873ba19b27f8"), comphelper::hashToString(RO)); } } CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testComputeHashForR6LoopEndCondition) { sal_uInt8 const pPassword[] = { 'T', 'e', 's', 't' }; // This should be 80 rounds. std::vector aValidationSalt = parseHex("6A5260B509E42E4F"); CPPUNIT_ASSERT_EQUAL( std::string("058d0adf871eaedf549963a8a60499af48c647abacf32605bf69518d4366dfe9"), comphelper::hashToString(vcl::pdf::computeHashR6(pPassword, 4, aValidationSalt))); // This should be 69 rounds. aValidationSalt = parseHex("B8B897AF2CB52F00"); CPPUNIT_ASSERT_EQUAL( std::string("e9f540fad0c2531781e7d9671da52527ab281497cb0d51bf0e986dffa0e9e323"), comphelper::hashToString(vcl::pdf::computeHashR6(pPassword, 4, aValidationSalt))); // Test the owner key generation, taking additional U argument. // This should be 69 rounds. std::vector U = parseHex("7BD210807A0277FECC52C261C442F02E1AD62C1A23553348B8F8AF7320" "DC9978FAB7E65E1BF4CA76F4BE5E6D2AA8C7D5"); aValidationSalt = parseHex("35A0F8DF2F495D23"); CPPUNIT_ASSERT_EQUAL( std::string("a8a6e16751faebf5f8aef5d02eae8ddd8885fa3fe9d29e132ff9abe86bd4d0c8"), comphelper::hashToString(vcl::pdf::computeHashR6(pPassword, 4, aValidationSalt, U))); } CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testGenerateUandUE) { // Checks we calculate U and UE correctly const sal_uInt8 pUserPass[] = { 'T', 'e', 's', 't' }; std::vector aInputKey = vcl::pdf::generateKey(); std::vector U; std::vector UE; // Generate the U and UE from the user password and encrypt the // encryption key into UE vcl::pdf::generateUandUE(pUserPass, 4, aInputKey, U, UE); // Checks that the U validates the password (would fail if the U // would be calculated wrongly). CPPUNIT_ASSERT_EQUAL(true, vcl::pdf::validateUserPassword(pUserPass, 4, U)); // Decrypt the key - this would fail if U and UE would be calculated // wrongly auto aDecryptedKey = vcl::pdf::decryptKey(pUserPass, 4, U, UE); // Decrypted key and input key need to match CPPUNIT_ASSERT_EQUAL(comphelper::hashToString(aInputKey), comphelper::hashToString(aDecryptedKey)); } CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testGenerateOandOE) { // Checks we calculate O and OE correctly const auto aUserPass = std::to_array({ 'T', 'e', 's', 't' }); const auto aOwnerPass = std::to_array({ 'T', 'e', 's', 't', '2' }); std::vector aInputKey = vcl::pdf::generateKey(); std::vector U; std::vector UE; std::vector O; std::vector OE; // Generates U and UE - we need U in generateOandOE vcl::pdf::generateUandUE(aUserPass.data(), aUserPass.size(), aInputKey, U, UE); vcl::pdf::generateOandOE(aOwnerPass.data(), aOwnerPass.size(), aInputKey, U, O, OE); // Checks the user password is valid CPPUNIT_ASSERT_EQUAL(true, vcl::pdf::validateUserPassword(aUserPass.data(), aUserPass.size(), U)); // Checks the owner password is valid CPPUNIT_ASSERT_EQUAL( true, vcl::pdf::validateOwnerPassword(aOwnerPass.data(), aOwnerPass.size(), U, O)); } CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testPermsEncryption) { // Encrypts file permissions for /Perms entry // We use an existing encrypted /Perm to validate the decryption and re-encryption // algorithm works correctly. const sal_uInt8 pUserPass[] = { 'T', 'e', 's', 't' }; // U and UE taken from a PDF that was encrypted with "Test" as password std::vector U = parseHex("7BD210807A0277FECC52C261C442F02E1AD62C1A23553348B8F8AF7320" "DC9978FAB7E65E1BF4CA76F4BE5E6D2AA8C7D5"); std::vector UE = parseHex("67022D91A6BDF3179F488DC9658E54B78A0AD05C6A9C419DCD17A6941C151197"); // We decrypt the key, which is needed to decrypt and encrypt the /Perms content std::vector aKey = vcl::pdf::decryptKey(pUserPass, 4, U, UE); // Known encrypted /Perms content taken from the PDF std::vector aPermEncrypted = parseHex("6a2306c6e5e71a5bbd8404b07abec38f"); // Decrypt std::vector aPermsDecrpyted = vcl::pdf::decryptPerms(aPermEncrypted, aKey); // Encrypt again std::vector aPermsReencrypted = vcl::pdf::encryptPerms(aPermsDecrpyted, aKey); // Original encrypted /Perm content should be equal to decrypted and encrypted again CPPUNIT_ASSERT_EQUAL(std::string("6a2306c6e5e71a5bbd8404b07abec38f"), comphelper::hashToString(aPermsReencrypted)); // Always should be a,b,d CPPUNIT_ASSERT_EQUAL(sal_uInt8('a'), aPermsDecrpyted[9]); CPPUNIT_ASSERT_EQUAL(sal_uInt8('d'), aPermsDecrpyted[10]); CPPUNIT_ASSERT_EQUAL(sal_uInt8('b'), aPermsDecrpyted[11]); // Metadata encrypted? - T or F CPPUNIT_ASSERT_EQUAL(sal_uInt8('T'), aPermsDecrpyted[8]); // Decrypting the access permissions sal_Int32 aAccessPermissions = sal_Int32(aPermsDecrpyted[0]) + sal_Int32(aPermsDecrpyted[1] << 8) + sal_Int32(aPermsDecrpyted[2] << 16) + sal_Int32(aPermsDecrpyted[3] << 24); // Taken from the PDF (/P entry) sal_Int32 nExpectedAccessPermisssions = -4; CPPUNIT_ASSERT_EQUAL(nExpectedAccessPermisssions, aAccessPermissions); // the whole decrypted /Perms content CPPUNIT_ASSERT_EQUAL(std::string("fcffffffffffffff54616462bb609a8a"), comphelper::hashToString(aPermsDecrpyted)); // Check that creating /Perm content from access permissions works correctly std::vector aPermsCreated = vcl::pdf::createPerms(nExpectedAccessPermisssions, true); // Last 12 bytes are random, so we shouldn't check those std::vector aPermsWithoutRandomBytes(aPermsCreated.begin(), aPermsCreated.begin() + 12); // Should match the decrypted content CPPUNIT_ASSERT_EQUAL(std::string("fcffffffffffffff54616462"), comphelper::hashToString(aPermsWithoutRandomBytes)); } CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testPadding) { constexpr size_t constBlockSize = 16; std::vector aVector{ 'T', 'e', 's', 't', '!' }; CPPUNIT_ASSERT_EQUAL(size_t(5), aVector.size()); size_t nPaddedSize = vcl::pdf::addPaddingToVector(aVector, constBlockSize); CPPUNIT_ASSERT_EQUAL(size_t(constBlockSize), aVector.size()); CPPUNIT_ASSERT_EQUAL(size_t(constBlockSize), nPaddedSize); for (size_t i = 5; i < constBlockSize; i++) CPPUNIT_ASSERT_EQUAL(sal_uInt8(0x0B), aVector[i]); } CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testPaddingBlockBoundary) { // Data that already ends on a block boundary gets a whole block of padding constexpr size_t constBlockSize = 16; std::vector aVector(constBlockSize, 'T'); size_t nPaddedSize = vcl::pdf::addPaddingToVector(aVector, constBlockSize); CPPUNIT_ASSERT_EQUAL(size_t(2 * constBlockSize), aVector.size()); CPPUNIT_ASSERT_EQUAL(size_t(2 * constBlockSize), nPaddedSize); for (size_t i = constBlockSize; i < 2 * constBlockSize; i++) CPPUNIT_ASSERT_EQUAL(sal_uInt8(constBlockSize), aVector[i]); // The encrypted size that is written as the stream length has to account // for that block as well vcl::pdf::PDFEncryptorR6 aEncryptor; CPPUNIT_ASSERT_EQUAL(sal_uInt64(48), aEncryptor.calculateSizeIncludingHeader(16)); CPPUNIT_ASSERT_EQUAL(sal_uInt64(32), aEncryptor.calculateSizeIncludingHeader(5)); } 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.setupEncryption(aKey, 0); aEncryptor.encryptWithIV(aData.data(), aData.size(), aEncryptedBuffer, aIV); 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)); } std::vector decrypt_AES_256_CBC(std::vector& rKey, std::vector& rIV, std::vector& rEncryptedBuffer) { std::vector aDecryptedBuffer(rEncryptedBuffer.size()); comphelper::Decrypt aDecryptor(rKey, rIV, comphelper::CryptoType::AES_256_CBC); aDecryptor.update(aDecryptedBuffer, rEncryptedBuffer); return aDecryptedBuffer; } CPPUNIT_TEST_FIXTURE(PDFEncryptionTest, testFileEncryption_checkDifferentIV) { // Check each call to encrypt is using a different IV (initialization vector) std::vector aKey = parseHex("90e657b78c0315610f3f421bd396ff635fa8fe3cf2ea399e7e1ae23e6185b4fc"); static constexpr const auto aData = std::to_array({ 'a', 'b', 'c' }); vcl::pdf::PDFEncryptorR6 aEncryptor; aEncryptor.setupEncryption(aKey, 0); std::vector aEncrypted_1; aEncryptor.encrypt(aData.data(), aData.size(), aEncrypted_1, aData.size()); std::vector aIV_1(aEncrypted_1.begin(), aEncrypted_1.begin() + 16); std::vector aEncrypted_2; aEncryptor.encrypt(aData.data(), aData.size(), aEncrypted_2, aData.size()); std::vector aIV_2(aEncrypted_2.begin(), aEncrypted_2.begin() + 16); std::vector aEncrypted_3; aEncryptor.encrypt(aData.data(), aData.size(), aEncrypted_3, aData.size()); std::vector aIV_3(aEncrypted_3.begin(), aEncrypted_3.begin() + 16); // All IV should be different CPPUNIT_ASSERT(!std::equal(aIV_1.begin(), aIV_1.end(), aIV_2.begin())); CPPUNIT_ASSERT(!std::equal(aIV_1.begin(), aIV_1.end(), aIV_3.begin())); CPPUNIT_ASSERT(!std::equal(aIV_2.begin(), aIV_2.end(), aIV_3.begin())); { std::vector aEncrypted(aEncrypted_1.begin() + 16, aEncrypted_1.end()); // expected 'a', 'b', 'c' + padding CPPUNIT_ASSERT_EQUAL( std::string("6162630d0d0d0d0d0d0d0d0d0d0d0d0d"), comphelper::hashToString(decrypt_AES_256_CBC(aKey, aIV_1, aEncrypted))); } { std::vector aEncrypted(aEncrypted_2.begin() + 16, aEncrypted_2.end()); CPPUNIT_ASSERT_EQUAL( std::string("6162630d0d0d0d0d0d0d0d0d0d0d0d0d"), comphelper::hashToString(decrypt_AES_256_CBC(aKey, aIV_2, aEncrypted))); } { std::vector aEncrypted(aEncrypted_3.begin() + 16, aEncrypted_3.end()); CPPUNIT_ASSERT_EQUAL( std::string("6162630d0d0d0d0d0d0d0d0d0d0d0d0d"), comphelper::hashToString(decrypt_AES_256_CBC(aKey, aIV_3, aEncrypted))); } } } // namespace CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */