tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / xmlsecurity / qa / unit / pdfsigning / pdfsigning.cxx
blob36a5a3d19a9bf9a2ea6ff8d4d184231248ab72f3
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 <config_crypto.h>
14 #if USE_CRYPTO_NSS
15 #include <secoid.h>
16 #endif
18 #include <string_view>
20 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
21 #include <com/sun/star/security/DocumentSignatureInformation.hpp>
23 #include <osl/file.hxx>
24 #include <sal/log.hxx>
25 #include <test/bootstrapfixture.hxx>
26 #include <unotest/macros_test.hxx>
27 #include <tools/datetime.hxx>
28 #include <unotools/streamwrap.hxx>
29 #include <unotools/ucbstreamhelper.hxx>
30 #include <vcl/filter/pdfdocument.hxx>
31 #include <vcl/filter/PDFiumLibrary.hxx>
32 #include <svl/cryptosign.hxx>
34 #include <documentsignaturemanager.hxx>
35 #include <pdfsignaturehelper.hxx>
37 #ifdef _WIN32
38 #define WIN32_LEAN_AND_MEAN
39 #include <windows.h>
40 #endif
42 using namespace com::sun::star;
44 namespace
46 constexpr OUString DATA_DIRECTORY = u"/xmlsecurity/qa/unit/pdfsigning/data/"_ustr;
49 /// Testsuite for the PDF signing feature.
50 class PDFSigningTest : public test::BootstrapFixture, public unotest::MacrosTest
52 protected:
53 /**
54 * Sign rInURL once and save the result as rOutURL, asserting that rInURL
55 * had nOriginalSignatureCount signatures.
57 bool sign(const OUString& rInURL, const OUString& rOutURL, size_t nOriginalSignatureCount);
58 /**
59 * Read a pdf and make sure that it has the expected number of valid
60 * signatures.
62 std::vector<SignatureInformation> verify(const OUString& rURL, size_t nCount);
64 public:
65 PDFSigningTest();
66 void setUp() override;
69 PDFSigningTest::PDFSigningTest() {}
71 void PDFSigningTest::setUp()
73 test::BootstrapFixture::setUp();
74 MacrosTest::setUpX509(m_directories, u"xmlsecurity_pdfsigning"_ustr);
76 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
77 = xml::crypto::SEInitializer::create(m_xContext);
78 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
79 = xSEInitializer->createSecurityContext(OUString());
80 #if USE_CRYPTO_NSS
81 #ifdef NSS_USE_ALG_IN_ANY_SIGNATURE
82 // policy may disallow using SHA1 for signatures but unit test documents
83 // have such existing signatures (call this after createSecurityContext!)
84 NSS_SetAlgorithmPolicy(SEC_OID_SHA1, NSS_USE_ALG_IN_ANY_SIGNATURE, 0);
85 #endif
86 #endif
89 std::vector<SignatureInformation> PDFSigningTest::verify(const OUString& rURL, size_t nCount)
91 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
92 = xml::crypto::SEInitializer::create(m_xContext);
93 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
94 = xSEInitializer->createSecurityContext(OUString());
95 std::vector<SignatureInformation> aRet;
97 SvFileStream aStream(rURL, StreamMode::READ);
98 PDFSignatureHelper aHelper;
99 CPPUNIT_ASSERT(aHelper.ReadAndVerifySignatureSvStream(aStream));
101 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
102 if (pPDFium)
104 CPPUNIT_ASSERT_EQUAL(nCount, aHelper.GetSignatureInformations().size());
106 aRet.insert(aRet.end(), aHelper.GetSignatureInformations().begin(),
107 aHelper.GetSignatureInformations().end());
109 return aRet;
112 bool PDFSigningTest::sign(const OUString& rInURL, const OUString& rOutURL,
113 size_t nOriginalSignatureCount)
115 // Make sure that input has nOriginalSignatureCount signatures.
116 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
117 = xml::crypto::SEInitializer::create(m_xContext);
118 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
119 = xSEInitializer->createSecurityContext(OUString());
120 vcl::filter::PDFDocument aDocument;
122 SvFileStream aStream(rInURL, StreamMode::READ);
123 CPPUNIT_ASSERT(aDocument.Read(aStream));
124 std::vector<vcl::filter::PDFObjectElement*> aSignatures = aDocument.GetSignatureWidgets();
125 CPPUNIT_ASSERT_EQUAL(nOriginalSignatureCount, aSignatures.size());
128 bool bSignSuccessful = false;
129 // Sign it and write out the result.
131 uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment
132 = xSecurityContext->getSecurityEnvironment();
133 uno::Sequence<uno::Reference<security::XCertificate>> aCertificates
134 = xSecurityEnvironment->getPersonalCertificates();
135 for (auto& cert : asNonConstRange(aCertificates))
137 // Only try certificates that are already active and not expired
138 if (IsValid(cert, xSecurityEnvironment))
140 svl::crypto::SigningContext aSigningContext;
141 aSigningContext.m_xCertificate = cert;
142 bool bSignResult = aDocument.Sign(aSigningContext, u"test"_ustr, /*bAdES=*/true);
143 #ifdef _WIN32
144 if (!bSignResult)
146 DWORD dwErr = GetLastError();
147 if (HRESULT_FROM_WIN32(dwErr) == CRYPT_E_NO_KEY_PROPERTY)
149 SAL_WARN("xmlsecurity.qa", "Skipping a certificate without a private key");
150 continue; // The certificate does not have a private key - not a valid certificate
153 #endif
154 CPPUNIT_ASSERT(bSignResult);
155 SvFileStream aOutStream(rOutURL, StreamMode::WRITE | StreamMode::TRUNC);
156 CPPUNIT_ASSERT(aDocument.Write(aOutStream));
157 bSignSuccessful = true;
158 break;
163 // This was nOriginalSignatureCount when PDFDocument::Sign() silently returned success, without doing anything.
164 if (bSignSuccessful)
165 verify(rOutURL, nOriginalSignatureCount + 1);
167 // May return false if NSS failed to parse its own profile or Windows has no valid certificates installed.
168 return bSignSuccessful;
171 /// Test adding a new signature to a previously unsigned file.
172 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDFAdd)
174 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
175 if (!pPDFium)
177 return;
180 OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
181 OUString aInURL = aSourceDir + "no.pdf";
182 OUString aTargetDir
183 = m_directories.getURLFromWorkdir(u"/CppunitTest/xmlsecurity_pdfsigning.test.user/");
184 OUString aOutURL = aTargetDir + "add.pdf";
185 bool bHadCertificates = sign(aInURL, aOutURL, 0);
187 if (bHadCertificates)
189 // Assert that the SubFilter is not adbe.pkcs7.detached in the bAdES case.
190 std::vector<SignatureInformation> aInfos = verify(aOutURL, 1);
191 CPPUNIT_ASSERT(aInfos[0].bHasSigningCertificate);
192 // Make sure the timestamp is correct.
193 DateTime aDateTime(DateTime::SYSTEM);
194 // This was 0 (on Windows), as neither the /M key nor the PKCS#7 blob contained a timestamp.
195 CPPUNIT_ASSERT_EQUAL(aDateTime.GetYear(), aInfos[0].stDateTime.Year);
196 // Assert that the digest algorithm is not SHA-1 in the bAdES case.
197 CPPUNIT_ASSERT_EQUAL(xml::crypto::DigestID::SHA256, aInfos[0].nDigestID);
201 /// Test signing a previously unsigned file twice.
202 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDFAdd2)
204 // Sign.
205 OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
206 OUString aInURL = aSourceDir + "no.pdf";
207 OUString aTargetDir
208 = m_directories.getURLFromWorkdir(u"/CppunitTest/xmlsecurity_pdfsigning.test.user/");
209 OUString aOutURL = aTargetDir + "add.pdf";
210 bool bHadCertificates = sign(aInURL, aOutURL, 0);
212 // Sign again.
213 aInURL = aTargetDir + "add.pdf";
214 aOutURL = aTargetDir + "add2.pdf";
215 // This failed with "second range end is not the end of the file" for the
216 // first signature.
217 if (bHadCertificates)
218 sign(aInURL, aOutURL, 1);
221 /// Test removing a signature from a previously signed file.
222 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDFRemove)
224 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
225 if (!pPDFium)
227 return;
230 // Make sure that good.pdf has 1 valid signature.
231 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
232 = xml::crypto::SEInitializer::create(m_xContext);
233 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
234 = xSEInitializer->createSecurityContext(OUString());
235 vcl::filter::PDFDocument aDocument;
236 OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
237 OUString aInURL = aSourceDir + "good.pdf";
239 SvFileStream aStream(aInURL, StreamMode::READ);
240 PDFSignatureHelper aHelper;
241 aHelper.ReadAndVerifySignatureSvStream(aStream);
242 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aHelper.GetSignatureInformations().size());
245 // Remove the signature and write out the result as remove.pdf.
246 OUString aTargetDir
247 = m_directories.getURLFromWorkdir(u"/CppunitTest/xmlsecurity_pdfsigning.test.user/");
248 OUString aOutURL = aTargetDir + "remove.pdf";
249 osl::File::copy(aInURL, aOutURL);
251 uno::Reference<io::XInputStream> xInputStream;
252 std::unique_ptr<SvStream> pStream
253 = utl::UcbStreamHelper::CreateStream(aOutURL, StreamMode::READWRITE);
254 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(std::move(pStream)));
255 xInputStream.set(xStream, uno::UNO_QUERY);
256 PDFSignatureHelper::RemoveSignature(xInputStream, 0);
259 // Read back the pdf and make sure that it no longer has signatures.
260 // This failed when PDFDocument::RemoveSignature() silently returned
261 // success, without doing anything.
262 verify(aOutURL, 0);
265 /// Test removing all signatures from a previously multi-signed file.
266 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDFRemoveAll)
268 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
269 if (!pPDFium)
271 return;
274 // Make sure that good2.pdf has 2 valid signatures. Unlike in
275 // testPDFRemove(), here intentionally test DocumentSignatureManager and
276 // PDFSignatureHelper code as well.
277 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
278 = xml::crypto::SEInitializer::create(m_xContext);
279 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
280 = xSEInitializer->createSecurityContext(OUString());
282 // Copy the test document to a temporary file, as it'll be modified.
283 OUString aTargetDir
284 = m_directories.getURLFromWorkdir(u"/CppunitTest/xmlsecurity_pdfsigning.test.user/");
285 OUString aOutURL = aTargetDir + "remove-all.pdf";
286 CPPUNIT_ASSERT_EQUAL(
287 osl::File::RC::E_None,
288 osl::File::copy(m_directories.getURLFromSrc(DATA_DIRECTORY) + "2good.pdf", aOutURL));
289 // Load the test document as a storage and read its two signatures.
290 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
291 std::unique_ptr<SvStream> pStream
292 = utl::UcbStreamHelper::CreateStream(aOutURL, StreamMode::READ | StreamMode::WRITE);
293 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(std::move(pStream)));
294 aManager.setSignatureStream(xStream);
295 aManager.read(/*bUseTempStream=*/false);
296 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
297 // This was 1 when NSS_CMSSignerInfo_GetSigningCertificate() failed, which
298 // means that we only used the locally imported certificates for
299 // verification, not the ones provided in the PDF signature data.
300 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), rInformations.size());
302 // Request removal of the first signature, should imply removal of the
303 // second chained signature as well.
304 aManager.remove(0);
305 // This was 2, Manager didn't write anything to disk when removal succeeded
306 // (instead of doing that when removal failed).
307 // Then this was 1, when the chained signature wasn't removed.
308 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(0), rInformations.size());
311 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testTdf107782)
313 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
314 = xml::crypto::SEInitializer::create(m_xContext);
315 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
316 = xSEInitializer->createSecurityContext(OUString());
318 // Load the test document as a storage and read its signatures.
319 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
320 OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf107782.pdf";
321 std::unique_ptr<SvStream> pStream
322 = utl::UcbStreamHelper::CreateStream(aURL, StreamMode::READ | StreamMode::WRITE);
323 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(std::move(pStream)));
324 aManager.setSignatureStream(xStream);
325 aManager.read(/*bUseTempStream=*/false);
326 CPPUNIT_ASSERT(aManager.hasPDFSignatureHelper());
328 // This failed with an std::bad_alloc exception on Windows.
329 aManager.getPDFSignatureHelper().GetDocumentSignatureInformations(
330 aManager.getSecurityEnvironment());
333 /// Test a PDF 1.4 document, signed by Adobe.
334 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDF14Adobe)
336 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
337 if (!pPDFium)
339 return;
342 // Two signatures, first is SHA1, the second is SHA256.
343 // This was 0, as we failed to find the Annots key's value when it was a
344 // reference-to-array, not an array.
345 std::vector<SignatureInformation> aInfos
346 = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf14adobe.pdf", 2);
347 // This was 0, out-of-PKCS#7 signature date wasn't read.
348 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(2016), aInfos[1].stDateTime.Year);
351 /// Test a PDF 1.6 document, signed by Adobe.
352 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDF16Adobe)
354 // Contains a cross-reference stream, object streams and a compressed
355 // stream with a predictor. And a valid signature.
356 // Found signatures was 0, as parsing failed due to lack of support for
357 // these features.
358 verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf16adobe.pdf", 1);
361 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testTdf145312)
363 // Without the fix in place, this test would have crashed
364 verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf145312.pdf", 2);
367 /// Test adding a signature to a PDF 1.6 document.
368 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDF16Add)
370 // Contains PDF 1.6 features, make sure we can add a signature using that
371 // markup correctly.
372 OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
373 OUString aInURL = aSourceDir + "pdf16adobe.pdf";
374 OUString aTargetDir
375 = m_directories.getURLFromWorkdir(u"/CppunitTest/xmlsecurity_pdfsigning.test.user/");
376 OUString aOutURL = aTargetDir + "add.pdf";
377 // This failed: verification broke as incorrect xref stream was written as
378 // part of the new signature.
379 bool bHadCertificates = sign(aInURL, aOutURL, 1);
381 // Sign again.
382 aInURL = aTargetDir + "add.pdf";
383 aOutURL = aTargetDir + "add2.pdf";
384 // This failed as non-compressed AcroForm wasn't handled.
385 if (bHadCertificates)
386 sign(aInURL, aOutURL, 2);
389 /// Test a PDF 1.4 document, signed by LO on Windows.
390 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDF14LOWin)
392 // mscrypto used SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION as a digest
393 // algorithm when it meant SEC_OID_SHA1, make sure we tolerate that on all
394 // platforms.
395 // This failed, as NSS HASH_Create() didn't handle the sign algorithm.
396 verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf14lowin.pdf", 1);
399 /// Test a PAdES document, signed by LO on Linux.
400 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPDFPAdESGood)
402 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
403 if (!pPDFium)
405 return;
408 std::vector<SignatureInformation> aInfos
409 = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "good-pades.pdf", 1);
410 CPPUNIT_ASSERT(aInfos[0].bHasSigningCertificate);
413 /// Test a valid signature that does not cover the whole file.
414 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPartial)
416 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
417 if (!pPDFium)
419 return;
422 std::vector<SignatureInformation> aInfos
423 = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "partial.pdf", 1);
424 CPPUNIT_ASSERT(!aInfos.empty());
425 SignatureInformation& rInformation = aInfos[0];
426 CPPUNIT_ASSERT(rInformation.bPartialDocumentSignature);
429 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testPartialInBetween)
431 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
432 if (!pPDFium)
434 return;
437 std::vector<SignatureInformation> aInfos
438 = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "partial-in-between.pdf", 2);
439 CPPUNIT_ASSERT(!aInfos.empty());
440 SignatureInformation& rInformation = aInfos[0];
441 // Without the accompanying fix in place, this test would have failed, as unsigned incremental
442 // update between two signatures were not detected.
443 CPPUNIT_ASSERT(rInformation.bPartialDocumentSignature);
446 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testBadCertP1)
448 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
449 if (!pPDFium)
451 return;
454 std::vector<SignatureInformation> aInfos
455 = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "bad-cert-p1.pdf", 1);
456 CPPUNIT_ASSERT(!aInfos.empty());
457 SignatureInformation& rInformation = aInfos[0];
458 // Without the accompanying fix in place, this test would have failed with:
459 // - Expected: 0 (SecurityOperationStatus_UNKNOWN)
460 // - Actual : 1 (SecurityOperationStatus_OPERATION_SUCCEEDED)
461 // i.e. annotation after a P1 signature was not considered as a bad modification.
462 CPPUNIT_ASSERT_EQUAL(xml::crypto::SecurityOperationStatus::SecurityOperationStatus_UNKNOWN,
463 rInformation.nStatus);
466 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testBadCertP3Stamp)
468 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
469 if (!pPDFium)
471 return;
474 std::vector<SignatureInformation> aInfos
475 = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "bad-cert-p3-stamp.pdf", 1);
476 CPPUNIT_ASSERT(!aInfos.empty());
477 SignatureInformation& rInformation = aInfos[0];
479 // Without the accompanying fix in place, this test would have failed with:
480 // - Expected: 0 (SecurityOperationStatus_UNKNOWN)
481 // - Actual : 1 (SecurityOperationStatus_OPERATION_SUCCEEDED)
482 // i.e. adding a stamp annotation was not considered as a bad modification.
483 CPPUNIT_ASSERT_EQUAL(xml::crypto::SecurityOperationStatus::SecurityOperationStatus_UNKNOWN,
484 rInformation.nStatus);
487 /// Test writing a PAdES signature.
488 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testSigningCertificateAttribute)
490 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
491 if (!pPDFium)
493 return;
496 // Create a new signature.
497 OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
498 OUString aInURL = aSourceDir + "no.pdf";
499 OUString aTargetDir
500 = m_directories.getURLFromWorkdir(u"/CppunitTest/xmlsecurity_pdfsigning.test.user/");
501 OUString aOutURL = aTargetDir + "signing-certificate-attribute.pdf";
502 bool bHadCertificates = sign(aInURL, aOutURL, 0);
503 if (!bHadCertificates)
504 return;
506 // Verify it.
507 std::vector<SignatureInformation> aInfos = verify(aOutURL, 1);
508 CPPUNIT_ASSERT(!aInfos.empty());
509 SignatureInformation& rInformation = aInfos[0];
510 // Assert that it has a signed signingCertificateV2 attribute.
511 CPPUNIT_ASSERT(rInformation.bHasSigningCertificate);
514 /// Test that we accept files which are supposed to be good.
515 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testGood)
517 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
518 if (!pPDFium)
520 return;
523 const std::initializer_list<std::u16string_view> aNames = {
524 // We failed to determine if this is good or bad.
525 u"good-non-detached.pdf",
526 // Boolean value for dictionary key caused read error.
527 u"dict-bool.pdf",
530 for (const auto& rName : aNames)
532 std::vector<SignatureInformation> aInfos
533 = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + rName, 1);
534 CPPUNIT_ASSERT(!aInfos.empty());
535 SignatureInformation& rInformation = aInfos[0];
536 CPPUNIT_ASSERT_EQUAL(int(xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED),
537 static_cast<int>(rInformation.nStatus));
541 /// Test that we don't crash / loop while tokenizing these files.
542 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testTokenize)
544 const std::initializer_list<std::u16string_view> aNames = {
545 // We looped on this broken input.
546 u"no-eof.pdf",
547 // ']' in a name token was mishandled.
548 u"name-bracket.pdf",
549 // %%EOF at the end wasn't followed by a newline.
550 u"noeol.pdf",
551 // File that's intentionally smaller than 1024 bytes.
552 u"small.pdf",
553 u"tdf107149.pdf",
554 // Nested parentheses were not handled.
555 u"tdf114460.pdf",
556 // Valgrind was unhappy about this.
557 u"forcepoint16.pdf",
560 for (const auto& rName : aNames)
562 SvFileStream aStream(m_directories.getURLFromSrc(DATA_DIRECTORY) + rName, StreamMode::READ);
563 vcl::filter::PDFDocument aDocument;
564 // Just make sure the tokenizer finishes without an error, don't look at the signature.
565 CPPUNIT_ASSERT(aDocument.Read(aStream));
567 if (rName == u"tdf107149.pdf")
568 // This failed, page list was empty.
569 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aDocument.GetPages().size());
573 /// Test handling of unknown SubFilter values.
574 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testUnknownSubFilter)
576 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
577 if (!pPDFium)
579 return;
582 // Tokenize the bugdoc.
583 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
584 = xml::crypto::SEInitializer::create(m_xContext);
585 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
586 = xSEInitializer->createSecurityContext(OUString());
587 std::unique_ptr<SvStream> pStream = utl::UcbStreamHelper::CreateStream(
588 m_directories.getURLFromSrc(DATA_DIRECTORY) + "cr-comment.pdf", StreamMode::STD_READ);
589 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(std::move(pStream)));
590 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
591 aManager.setSignatureStream(xStream);
592 aManager.read(/*bUseTempStream=*/false);
594 // Make sure we find both signatures, even if the second has unknown SubFilter.
595 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
596 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), rInformations.size());
599 CPPUNIT_TEST_FIXTURE(PDFSigningTest, testGoodCustomMagic)
601 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
602 if (!pPDFium)
604 return;
607 // Tokenize the bugdoc.
608 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
609 = xml::crypto::SEInitializer::create(m_xContext);
610 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
611 = xSEInitializer->createSecurityContext(OUString());
612 std::unique_ptr<SvStream> pStream = utl::UcbStreamHelper::CreateStream(
613 m_directories.getURLFromSrc(DATA_DIRECTORY) + "good-custom-magic.pdf",
614 StreamMode::STD_READ);
615 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(std::move(pStream)));
616 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
617 aManager.setSignatureStream(xStream);
618 aManager.read(/*bUseTempStream=*/false);
620 // Without the accompanying fix in place, this test would have failed with:
621 // - Expected: 1 (SecurityOperationStatus_OPERATION_SUCCEEDED)
622 // - Actual : 0 (SecurityOperationStatus_UNKNOWN)
623 // i.e. no signatures were found due to a custom non-comment magic after the header.
624 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
625 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
628 CPPUNIT_PLUGIN_IMPLEMENT();
630 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */