Remove non-const Sequence::begin()/end() in internal code
[LibreOffice.git] / xmlsecurity / qa / unit / pdfsigning / pdfsigning.cxx
blobdb3ac9b3d66307e4efa292d6644a73d1adba89e6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <sal/config.h>
12 #include <string_view>
14 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
15 #include <com/sun/star/security/DocumentSignatureInformation.hpp>
17 #include <osl/file.hxx>
18 #include <sal/log.hxx>
19 #include <test/bootstrapfixture.hxx>
20 #include <unotest/macros_test.hxx>
21 #include <tools/datetime.hxx>
22 #include <unotools/streamwrap.hxx>
23 #include <unotools/ucbstreamhelper.hxx>
24 #include <vcl/filter/pdfdocument.hxx>
25 #include <vcl/filter/PDFiumLibrary.hxx>
27 #include <documentsignaturemanager.hxx>
28 #include <pdfsignaturehelper.hxx>
30 #ifdef _WIN32
31 #define WIN32_LEAN_AND_MEAN
32 #include <windows.h>
33 #endif
35 using namespace com::sun::star;
37 namespace
39 constexpr OUStringLiteral DATA_DIRECTORY = u"/xmlsecurity/qa/unit/pdfsigning/data/";
42 /// Testsuite for the PDF signing feature.
43 class PDFSigningTest : public test::BootstrapFixture, public unotest::MacrosTest
45 protected:
46 /**
47 * Sign rInURL once and save the result as rOutURL, asserting that rInURL
48 * had nOriginalSignatureCount signatures.
50 bool sign(const OUString& rInURL, const OUString& rOutURL, size_t nOriginalSignatureCount);
51 /**
52 * Read a pdf and make sure that it has the expected number of valid
53 * signatures.
55 std::vector<SignatureInformation> verify(const OUString& rURL, size_t nCount);
57 public:
58 PDFSigningTest();
59 void setUp() override;
60 void tearDown() override;
63 PDFSigningTest::PDFSigningTest() {}
65 void PDFSigningTest::setUp()
67 test::BootstrapFixture::setUp();
68 MacrosTest::setUpNssGpg(m_directories, "xmlsecurity_pdfsigning");
71 void PDFSigningTest::tearDown()
73 MacrosTest::tearDownNssGpg();
74 test::BootstrapFixture::tearDown();
77 std::vector<SignatureInformation> PDFSigningTest::verify(const OUString& rURL, size_t nCount)
79 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
80 = xml::crypto::SEInitializer::create(mxComponentContext);
81 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
82 = xSEInitializer->createSecurityContext(OUString());
83 std::vector<SignatureInformation> aRet;
85 SvFileStream aStream(rURL, StreamMode::READ);
86 PDFSignatureHelper aHelper;
87 CPPUNIT_ASSERT(aHelper.ReadAndVerifySignatureSvStream(aStream));
89 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
90 if (pPDFium)
92 CPPUNIT_ASSERT_EQUAL(nCount, aHelper.GetSignatureInformations().size());
94 for (size_t i = 0; i < aHelper.GetSignatureInformations().size(); ++i)
96 const SignatureInformation& rInfo = aHelper.GetSignatureInformations()[i];
97 aRet.push_back(rInfo);
100 return aRet;
103 bool PDFSigningTest::sign(const OUString& rInURL, const OUString& rOutURL,
104 size_t nOriginalSignatureCount)
106 // Make sure that input has nOriginalSignatureCount signatures.
107 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
108 = xml::crypto::SEInitializer::create(mxComponentContext);
109 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
110 = xSEInitializer->createSecurityContext(OUString());
111 vcl::filter::PDFDocument aDocument;
113 SvFileStream aStream(rInURL, StreamMode::READ);
114 CPPUNIT_ASSERT(aDocument.Read(aStream));
115 std::vector<vcl::filter::PDFObjectElement*> aSignatures = aDocument.GetSignatureWidgets();
116 CPPUNIT_ASSERT_EQUAL(nOriginalSignatureCount, aSignatures.size());
119 bool bSignSuccessful = false;
120 // Sign it and write out the result.
122 uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment
123 = xSecurityContext->getSecurityEnvironment();
124 uno::Sequence<uno::Reference<security::XCertificate>> aCertificates
125 = xSecurityEnvironment->getPersonalCertificates();
126 DateTime now(DateTime::SYSTEM);
127 for (auto& cert : asNonConstRange(aCertificates))
129 css::util::DateTime aNotValidAfter = cert->getNotValidAfter();
130 css::util::DateTime aNotValidBefore = cert->getNotValidBefore();
132 // Only try certificates that are already active and not expired
133 if ((now > aNotValidAfter) || (now < aNotValidBefore))
135 SAL_WARN("xmlsecurity.qa",
136 "Skipping a certificate that is not yet valid or already not valid");
138 else
140 bool bSignResult = aDocument.Sign(cert, "test", /*bAdES=*/true);
141 #ifdef _WIN32
142 if (!bSignResult)
144 DWORD dwErr = GetLastError();
145 if (HRESULT_FROM_WIN32(dwErr) == CRYPT_E_NO_KEY_PROPERTY)
147 SAL_WARN("xmlsecurity.qa", "Skipping a certificate without a private key");
148 continue; // The certificate does not have a private key - not a valid certificate
151 #endif
152 CPPUNIT_ASSERT(bSignResult);
153 SvFileStream aOutStream(rOutURL, StreamMode::WRITE | StreamMode::TRUNC);
154 CPPUNIT_ASSERT(aDocument.Write(aOutStream));
155 bSignSuccessful = true;
156 break;
161 // This was nOriginalSignatureCount when PDFDocument::Sign() silently returned success, without doing anything.
162 if (bSignSuccessful)
163 verify(rOutURL, nOriginalSignatureCount + 1);
165 // May return false if NSS failed to parse its own profile or Windows has no valid certificates installed.
166 return bSignSuccessful;
169 /// Test adding a new signature to a previously unsigned file.
170 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDFAdd)
172 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
173 if (!pPDFium)
175 return;
178 OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
179 OUString aInURL = aSourceDir + "no.pdf";
180 OUString aTargetDir
181 = m_directories.getURLFromWorkdir(u"/CppunitTest/xmlsecurity_pdfsigning.test.user/");
182 OUString aOutURL = aTargetDir + "add.pdf";
183 bool bHadCertificates = sign(aInURL, aOutURL, 0);
185 if (bHadCertificates)
187 // Assert that the SubFilter is not adbe.pkcs7.detached in the bAdES case.
188 std::vector<SignatureInformation> aInfos = verify(aOutURL, 1);
189 CPPUNIT_ASSERT(aInfos[0].bHasSigningCertificate);
190 // Make sure the timestamp is correct.
191 DateTime aDateTime(DateTime::SYSTEM);
192 // This was 0 (on Windows), as neither the /M key nor the PKCS#7 blob contained a timestamp.
193 CPPUNIT_ASSERT_EQUAL(aDateTime.GetYear(), aInfos[0].stDateTime.Year);
194 // Assert that the digest algorithm is not SHA-1 in the bAdES case.
195 CPPUNIT_ASSERT_EQUAL(xml::crypto::DigestID::SHA256, aInfos[0].nDigestID);
199 /// Test signing a previously unsigned file twice.
200 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDFAdd2)
202 // Sign.
203 OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
204 OUString aInURL = aSourceDir + "no.pdf";
205 OUString aTargetDir
206 = m_directories.getURLFromWorkdir(u"/CppunitTest/xmlsecurity_pdfsigning.test.user/");
207 OUString aOutURL = aTargetDir + "add.pdf";
208 bool bHadCertificates = sign(aInURL, aOutURL, 0);
210 // Sign again.
211 aInURL = aTargetDir + "add.pdf";
212 aOutURL = aTargetDir + "add2.pdf";
213 // This failed with "second range end is not the end of the file" for the
214 // first signature.
215 if (bHadCertificates)
216 sign(aInURL, aOutURL, 1);
219 /// Test removing a signature from a previously signed file.
220 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDFRemove)
222 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
223 if (!pPDFium)
225 return;
228 // Make sure that good.pdf has 1 valid signature.
229 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
230 = xml::crypto::SEInitializer::create(mxComponentContext);
231 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
232 = xSEInitializer->createSecurityContext(OUString());
233 vcl::filter::PDFDocument aDocument;
234 OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
235 OUString aInURL = aSourceDir + "good.pdf";
237 SvFileStream aStream(aInURL, StreamMode::READ);
238 PDFSignatureHelper aHelper;
239 aHelper.ReadAndVerifySignatureSvStream(aStream);
240 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aHelper.GetSignatureInformations().size());
243 // Remove the signature and write out the result as remove.pdf.
244 OUString aTargetDir
245 = m_directories.getURLFromWorkdir(u"/CppunitTest/xmlsecurity_pdfsigning.test.user/");
246 OUString aOutURL = aTargetDir + "remove.pdf";
247 osl::File::copy(aInURL, aOutURL);
249 uno::Reference<io::XInputStream> xInputStream;
250 std::unique_ptr<SvStream> pStream
251 = utl::UcbStreamHelper::CreateStream(aOutURL, StreamMode::READWRITE);
252 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(std::move(pStream)));
253 xInputStream.set(xStream, uno::UNO_QUERY);
254 PDFSignatureHelper::RemoveSignature(xInputStream, 0);
257 // Read back the pdf and make sure that it no longer has signatures.
258 // This failed when PDFDocument::RemoveSignature() silently returned
259 // success, without doing anything.
260 verify(aOutURL, 0);
263 /// Test removing all signatures from a previously multi-signed file.
264 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDFRemoveAll)
266 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
267 if (!pPDFium)
269 return;
272 // Make sure that good2.pdf has 2 valid signatures. Unlike in
273 // testPDFRemove(), here intentionally test DocumentSignatureManager and
274 // PDFSignatureHelper code as well.
275 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
276 = xml::crypto::SEInitializer::create(mxComponentContext);
277 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
278 = xSEInitializer->createSecurityContext(OUString());
280 // Copy the test document to a temporary file, as it'll be modified.
281 OUString aTargetDir
282 = m_directories.getURLFromWorkdir(u"/CppunitTest/xmlsecurity_pdfsigning.test.user/");
283 OUString aOutURL = aTargetDir + "remove-all.pdf";
284 CPPUNIT_ASSERT_EQUAL(
285 osl::File::RC::E_None,
286 osl::File::copy(m_directories.getURLFromSrc(DATA_DIRECTORY) + "2good.pdf", aOutURL));
287 // Load the test document as a storage and read its two signatures.
288 DocumentSignatureManager aManager(mxComponentContext, DocumentSignatureMode::Content);
289 std::unique_ptr<SvStream> pStream
290 = utl::UcbStreamHelper::CreateStream(aOutURL, StreamMode::READ | StreamMode::WRITE);
291 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(std::move(pStream)));
292 aManager.setSignatureStream(xStream);
293 aManager.read(/*bUseTempStream=*/false);
294 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
295 // This was 1 when NSS_CMSSignerInfo_GetSigningCertificate() failed, which
296 // means that we only used the locally imported certificates for
297 // verification, not the ones provided in the PDF signature data.
298 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), rInformations.size());
300 // Request removal of the first signature, should imply removal of the
301 // second chained signature as well.
302 aManager.remove(0);
303 // This was 2, Manager didn't write anything to disk when removal succeeded
304 // (instead of doing that when removal failed).
305 // Then this was 1, when the chained signature wasn't removed.
306 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(0), rInformations.size());
309 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testTdf107782)
311 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
312 = xml::crypto::SEInitializer::create(mxComponentContext);
313 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
314 = xSEInitializer->createSecurityContext(OUString());
316 // Load the test document as a storage and read its signatures.
317 DocumentSignatureManager aManager(mxComponentContext, DocumentSignatureMode::Content);
318 OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf107782.pdf";
319 std::unique_ptr<SvStream> pStream
320 = utl::UcbStreamHelper::CreateStream(aURL, StreamMode::READ | StreamMode::WRITE);
321 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(std::move(pStream)));
322 aManager.setSignatureStream(xStream);
323 aManager.read(/*bUseTempStream=*/false);
324 CPPUNIT_ASSERT(aManager.hasPDFSignatureHelper());
326 // This failed with an std::bad_alloc exception on Windows.
327 aManager.getPDFSignatureHelper().GetDocumentSignatureInformations(
328 aManager.getSecurityEnvironment());
331 /// Test a PDF 1.4 document, signed by Adobe.
332 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDF14Adobe)
334 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
335 if (!pPDFium)
337 return;
340 // Two signatures, first is SHA1, the second is SHA256.
341 // This was 0, as we failed to find the Annots key's value when it was a
342 // reference-to-array, not an array.
343 std::vector<SignatureInformation> aInfos
344 = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf14adobe.pdf", 2);
345 // This was 0, out-of-PKCS#7 signature date wasn't read.
346 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(2016), aInfos[1].stDateTime.Year);
349 /// Test a PDF 1.6 document, signed by Adobe.
350 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDF16Adobe)
352 // Contains a cross-reference stream, object streams and a compressed
353 // stream with a predictor. And a valid signature.
354 // Found signatures was 0, as parsing failed due to lack of support for
355 // these features.
356 verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf16adobe.pdf", 1);
359 /// Test adding a signature to a PDF 1.6 document.
360 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDF16Add)
362 // Contains PDF 1.6 features, make sure we can add a signature using that
363 // markup correctly.
364 OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
365 OUString aInURL = aSourceDir + "pdf16adobe.pdf";
366 OUString aTargetDir
367 = m_directories.getURLFromWorkdir(u"/CppunitTest/xmlsecurity_pdfsigning.test.user/");
368 OUString aOutURL = aTargetDir + "add.pdf";
369 // This failed: verification broke as incorrect xref stream was written as
370 // part of the new signature.
371 bool bHadCertificates = sign(aInURL, aOutURL, 1);
373 // Sign again.
374 aInURL = aTargetDir + "add.pdf";
375 aOutURL = aTargetDir + "add2.pdf";
376 // This failed as non-compressed AcroForm wasn't handled.
377 if (bHadCertificates)
378 sign(aInURL, aOutURL, 2);
381 /// Test a PDF 1.4 document, signed by LO on Windows.
382 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDF14LOWin)
384 // mscrypto used SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION as a digest
385 // algorithm when it meant SEC_OID_SHA1, make sure we tolerate that on all
386 // platforms.
387 // This failed, as NSS HASH_Create() didn't handle the sign algorithm.
388 verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf14lowin.pdf", 1);
391 /// Test a PAdES document, signed by LO on Linux.
392 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDFPAdESGood)
394 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
395 if (!pPDFium)
397 return;
400 std::vector<SignatureInformation> aInfos
401 = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "good-pades.pdf", 1);
402 CPPUNIT_ASSERT(aInfos[0].bHasSigningCertificate);
405 /// Test a valid signature that does not cover the whole file.
406 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPartial)
408 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
409 if (!pPDFium)
411 return;
414 std::vector<SignatureInformation> aInfos
415 = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "partial.pdf", 1);
416 CPPUNIT_ASSERT(!aInfos.empty());
417 SignatureInformation& rInformation = aInfos[0];
418 CPPUNIT_ASSERT(rInformation.bPartialDocumentSignature);
421 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPartialInBetween)
423 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
424 if (!pPDFium)
426 return;
429 std::vector<SignatureInformation> aInfos
430 = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "partial-in-between.pdf", 2);
431 CPPUNIT_ASSERT(!aInfos.empty());
432 SignatureInformation& rInformation = aInfos[0];
433 // Without the accompanying fix in place, this test would have failed, as unsigned incremental
434 // update between two signatures were not detected.
435 CPPUNIT_ASSERT(rInformation.bPartialDocumentSignature);
438 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testBadCertP1)
440 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
441 if (!pPDFium)
443 return;
446 std::vector<SignatureInformation> aInfos
447 = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "bad-cert-p1.pdf", 1);
448 CPPUNIT_ASSERT(!aInfos.empty());
449 SignatureInformation& rInformation = aInfos[0];
450 // Without the accompanying fix in place, this test would have failed with:
451 // - Expected: 0 (SecurityOperationStatus_UNKNOWN)
452 // - Actual : 1 (SecurityOperationStatus_OPERATION_SUCCEEDED)
453 // i.e. annotation after a P1 signature was not considered as a bad modification.
454 CPPUNIT_ASSERT_EQUAL(xml::crypto::SecurityOperationStatus::SecurityOperationStatus_UNKNOWN,
455 rInformation.nStatus);
458 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testBadCertP3Stamp)
460 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
461 if (!pPDFium)
463 return;
466 std::vector<SignatureInformation> aInfos
467 = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "bad-cert-p3-stamp.pdf", 1);
468 CPPUNIT_ASSERT(!aInfos.empty());
469 SignatureInformation& rInformation = aInfos[0];
471 // Without the accompanying fix in place, this test would have failed with:
472 // - Expected: 0 (SecurityOperationStatus_UNKNOWN)
473 // - Actual : 1 (SecurityOperationStatus_OPERATION_SUCCEEDED)
474 // i.e. adding a stamp annotation was not considered as a bad modification.
475 CPPUNIT_ASSERT_EQUAL(xml::crypto::SecurityOperationStatus::SecurityOperationStatus_UNKNOWN,
476 rInformation.nStatus);
479 /// Test writing a PAdES signature.
480 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testSigningCertificateAttribute)
482 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
483 if (!pPDFium)
485 return;
488 // Create a new signature.
489 OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
490 OUString aInURL = aSourceDir + "no.pdf";
491 OUString aTargetDir
492 = m_directories.getURLFromWorkdir(u"/CppunitTest/xmlsecurity_pdfsigning.test.user/");
493 OUString aOutURL = aTargetDir + "signing-certificate-attribute.pdf";
494 bool bHadCertificates = sign(aInURL, aOutURL, 0);
495 if (!bHadCertificates)
496 return;
498 // Verify it.
499 std::vector<SignatureInformation> aInfos = verify(aOutURL, 1);
500 CPPUNIT_ASSERT(!aInfos.empty());
501 SignatureInformation& rInformation = aInfos[0];
502 // Assert that it has a signed signingCertificateV2 attribute.
503 CPPUNIT_ASSERT(rInformation.bHasSigningCertificate);
506 /// Test that we accept files which are supposed to be good.
507 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testGood)
509 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
510 if (!pPDFium)
512 return;
515 const std::initializer_list<std::u16string_view> aNames = {
516 // We failed to determine if this is good or bad.
517 u"good-non-detached.pdf",
518 // Boolean value for dictionary key caused read error.
519 u"dict-bool.pdf",
522 for (const auto& rName : aNames)
524 std::vector<SignatureInformation> aInfos
525 = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + rName, 1);
526 CPPUNIT_ASSERT(!aInfos.empty());
527 SignatureInformation& rInformation = aInfos[0];
528 CPPUNIT_ASSERT_EQUAL(int(xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED),
529 static_cast<int>(rInformation.nStatus));
533 /// Test that we don't crash / loop while tokenizing these files.
534 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testTokenize)
536 const std::initializer_list<std::u16string_view> aNames = {
537 // We looped on this broken input.
538 u"no-eof.pdf",
539 // ']' in a name token was mishandled.
540 u"name-bracket.pdf",
541 // %%EOF at the end wasn't followed by a newline.
542 u"noeol.pdf",
543 // File that's intentionally smaller than 1024 bytes.
544 u"small.pdf",
545 u"tdf107149.pdf",
546 // Nested parentheses were not handled.
547 u"tdf114460.pdf",
548 // Valgrind was unhappy about this.
549 u"forcepoint16.pdf",
552 for (const auto& rName : aNames)
554 SvFileStream aStream(m_directories.getURLFromSrc(DATA_DIRECTORY) + rName, StreamMode::READ);
555 vcl::filter::PDFDocument aDocument;
556 // Just make sure the tokenizer finishes without an error, don't look at the signature.
557 CPPUNIT_ASSERT(aDocument.Read(aStream));
559 if (rName == u"tdf107149.pdf")
560 // This failed, page list was empty.
561 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aDocument.GetPages().size());
565 /// Test handling of unknown SubFilter values.
566 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testUnknownSubFilter)
568 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
569 if (!pPDFium)
571 return;
574 // Tokenize the bugdoc.
575 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
576 = xml::crypto::SEInitializer::create(mxComponentContext);
577 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
578 = xSEInitializer->createSecurityContext(OUString());
579 std::unique_ptr<SvStream> pStream = utl::UcbStreamHelper::CreateStream(
580 m_directories.getURLFromSrc(DATA_DIRECTORY) + "cr-comment.pdf", StreamMode::STD_READ);
581 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(std::move(pStream)));
582 DocumentSignatureManager aManager(mxComponentContext, DocumentSignatureMode::Content);
583 aManager.setSignatureStream(xStream);
584 aManager.read(/*bUseTempStream=*/false);
586 // Make sure we find both signatures, even if the second has unknown SubFilter.
587 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
588 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), rInformations.size());
591 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testGoodCustomMagic)
593 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
594 if (!pPDFium)
596 return;
599 // Tokenize the bugdoc.
600 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
601 = xml::crypto::SEInitializer::create(mxComponentContext);
602 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
603 = xSEInitializer->createSecurityContext(OUString());
604 std::unique_ptr<SvStream> pStream = utl::UcbStreamHelper::CreateStream(
605 m_directories.getURLFromSrc(DATA_DIRECTORY) + "good-custom-magic.pdf",
606 StreamMode::STD_READ);
607 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(std::move(pStream)));
608 DocumentSignatureManager aManager(mxComponentContext, DocumentSignatureMode::Content);
609 aManager.setSignatureStream(xStream);
610 aManager.read(/*bUseTempStream=*/false);
612 // Without the accompanying fix in place, this test would have failed with:
613 // - Expected: 1 (SecurityOperationStatus_OPERATION_SUCCEEDED)
614 // - Actual : 0 (SecurityOperationStatus_UNKNOWN)
615 // i.e. no signatures were found due to a custom non-comment magic after the header.
616 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
617 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
620 CPPUNIT_PLUGIN_IMPLEMENT();
622 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */