tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / xmlsecurity / qa / unit / signing / signing.cxx
blob0a22681fb9a00fba6a732fd5d7248d6685f92fa4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 <config_crypto.h>
11 #include <config_features.h>
12 #include <config_gpgme.h>
14 #include <sal/config.h>
16 #if USE_CRYPTO_NSS
17 #include <secoid.h>
18 #endif
20 #include <test/unoapixml_test.hxx>
22 #include <com/sun/star/beans/XPropertySet.hpp>
23 #include <com/sun/star/document/BrokenPackageRequest.hpp>
24 #include <com/sun/star/embed/XStorage.hpp>
25 #include <com/sun/star/embed/XTransactedObject.hpp>
26 #include <com/sun/star/security/CertificateValidity.hpp>
27 #include <com/sun/star/security/DocumentDigitalSignatures.hpp>
28 #include <com/sun/star/security/XDocumentDigitalSignatures.hpp>
29 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
30 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
31 #include <com/sun/star/view/XSelectionSupplier.hpp>
33 #include <comphelper/processfactory.hxx>
34 #include <comphelper/propertysequence.hxx>
35 #include <unotools/tempfile.hxx>
36 #include <unotools/saveopt.hxx>
37 #include <unotools/ucbstreamhelper.hxx>
38 #include <comphelper/storagehelper.hxx>
39 #include <sfx2/sfxbasemodel.hxx>
40 #include <sfx2/objsh.hxx>
41 #include <osl/thread.hxx>
42 #include <comphelper/ofopxmlhelper.hxx>
43 #include <unotools/streamwrap.hxx>
45 #include <documentsignaturehelper.hxx>
46 #include <xmlsignaturehelper.hxx>
47 #include <documentsignaturemanager.hxx>
48 #include <biginteger.hxx>
49 #include <certificate.hxx>
50 #include <xsecctl.hxx>
51 #include <ucbhelper/interceptedinteraction.hxx>
52 #include <sfx2/docfile.hxx>
53 #include <sfx2/docfilt.hxx>
54 #include <comphelper/configuration.hxx>
55 #include <svx/signaturelinehelper.hxx>
56 #include <sfx2/viewsh.hxx>
57 #include <comphelper/propertyvalue.hxx>
58 #include <vcl/filter/PDFiumLibrary.hxx>
59 #include <vcl/scheduler.hxx>
60 #include <svl/cryptosign.hxx>
62 using namespace com::sun::star;
64 /// Testsuite for the document signing feature.
65 class SigningTest : public UnoApiXmlTest
67 protected:
68 uno::Reference<xml::crypto::XSEInitializer> mxSEInitializer;
69 uno::Reference<xml::crypto::XXMLSecurityContext> mxSecurityContext;
71 public:
72 SigningTest();
73 virtual void setUp() override;
74 virtual void tearDown() override;
75 void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) override;
77 protected:
78 uno::Reference<security::XCertificate>
79 getCertificate(DocumentSignatureManager& rSignatureManager,
80 svl::crypto::SignatureMethodAlgorithm eAlgo);
81 #if HAVE_FEATURE_GPGVERIFY
82 SfxObjectShell* assertDocument(const ::CppUnit::SourceLine aSrcLine,
83 const OUString& rFilterName, const SignatureState nDocSign,
84 const SignatureState nMacroSign, const OUString& sVersion);
85 #endif
88 SigningTest::SigningTest()
89 : UnoApiXmlTest(u"/xmlsecurity/qa/unit/signing/data/"_ustr)
93 void SigningTest::setUp()
95 UnoApiXmlTest::setUp();
97 MacrosTest::setUpX509(m_directories, u"xmlsecurity_signing"_ustr);
98 MacrosTest::setUpGpg(m_directories, std::u16string_view(u"xmlsecurity_signing"));
100 // Initialize crypto after setting up the environment variables.
101 mxSEInitializer = xml::crypto::SEInitializer::create(m_xContext);
102 mxSecurityContext = mxSEInitializer->createSecurityContext(OUString());
103 #if USE_CRYPTO_NSS
104 #ifdef NSS_USE_ALG_IN_ANY_SIGNATURE
105 // policy may disallow using SHA1 for signatures but unit test documents
106 // have such existing signatures (call this after createSecurityContext!)
107 NSS_SetAlgorithmPolicy(SEC_OID_SHA1, NSS_USE_ALG_IN_ANY_SIGNATURE, 0);
108 #endif
109 #endif
112 void SigningTest::tearDown()
114 MacrosTest::tearDownGpg();
116 UnoApiXmlTest::tearDown();
119 uno::Reference<security::XCertificate>
120 SigningTest::getCertificate(DocumentSignatureManager& rSignatureManager,
121 svl::crypto::SignatureMethodAlgorithm eAlgo)
123 uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment
124 = rSignatureManager.getSecurityEnvironment();
125 const uno::Sequence<uno::Reference<security::XCertificate>> aCertificates
126 = xSecurityEnvironment->getPersonalCertificates();
128 for (const auto& xCertificate : aCertificates)
130 auto pCertificate = dynamic_cast<xmlsecurity::Certificate*>(xCertificate.get());
131 CPPUNIT_ASSERT(pCertificate);
132 if (pCertificate->getSignatureMethodAlgorithm() == eAlgo
133 && IsValid(xCertificate, xSecurityEnvironment))
134 return xCertificate;
136 return uno::Reference<security::XCertificate>();
139 CPPUNIT_TEST_FIXTURE(SigningTest, testDescription)
141 // Create an empty document and store it to a tempfile, finally load it as a storage.
142 loadFromURL(u"private:factory/swriter"_ustr);
144 save(u"writer8"_ustr);
146 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
147 CPPUNIT_ASSERT(aManager.init());
148 uno::Reference<embed::XStorage> xStorage
149 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
150 ZIP_STORAGE_FORMAT_STRING, maTempFile.GetURL(), embed::ElementModes::READWRITE);
151 CPPUNIT_ASSERT(xStorage.is());
152 aManager.setStore(xStorage);
153 aManager.getSignatureHelper().SetStorage(xStorage, u"1.2");
155 // Then add a signature document.
156 uno::Reference<security::XCertificate> xCertificate
157 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA);
158 if (!xCertificate.is())
159 return;
160 OUString aDescription(u"SigningTest::testDescription"_ustr);
161 sal_Int32 nSecurityId;
162 svl::crypto::SigningContext aSigningContext;
163 aSigningContext.m_xCertificate = xCertificate;
164 aManager.add(aSigningContext, mxSecurityContext, aDescription, nSecurityId, false);
166 // Read back the signature and make sure that the description survives the roundtrip.
167 aManager.read(/*bUseTempStream=*/true);
168 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
169 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
170 CPPUNIT_ASSERT_EQUAL(aDescription, rInformations[0].ouDescription);
173 CPPUNIT_TEST_FIXTURE(SigningTest, testECDSA)
175 // Create an empty document and store it to a tempfile, finally load it as a storage.
176 loadFromURL(u"private:factory/swriter"_ustr);
178 save(u"writer8"_ustr);
180 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
181 CPPUNIT_ASSERT(aManager.init());
182 uno::Reference<embed::XStorage> xStorage
183 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
184 ZIP_STORAGE_FORMAT_STRING, maTempFile.GetURL(), embed::ElementModes::READWRITE);
185 CPPUNIT_ASSERT(xStorage.is());
186 aManager.setStore(xStorage);
187 aManager.getSignatureHelper().SetStorage(xStorage, u"1.2");
189 // Then add a signature.
190 uno::Reference<security::XCertificate> xCertificate
191 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::ECDSA);
192 if (!xCertificate.is())
193 return;
194 sal_Int32 nSecurityId;
195 svl::crypto::SigningContext aSigningContext;
196 aSigningContext.m_xCertificate = xCertificate;
197 aManager.add(aSigningContext, mxSecurityContext, u""_ustr, nSecurityId, false);
199 // Read back the signature and make sure that it's valid.
200 aManager.read(/*bUseTempStream=*/true);
201 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
202 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
203 // This was SecurityOperationStatus_UNKNOWN, signing with an ECDSA key was
204 // broken.
205 CPPUNIT_ASSERT_EQUAL(css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED,
206 rInformations[0].nStatus);
209 CPPUNIT_TEST_FIXTURE(SigningTest, testECDSAOOXML)
211 // Create an empty document and store it to a tempfile, finally load it as a storage.
212 loadFromURL(u"private:factory/swriter"_ustr);
214 save(u"MS Word 2007 XML"_ustr);
216 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
217 CPPUNIT_ASSERT(aManager.init());
218 uno::Reference<embed::XStorage> xStorage
219 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
220 ZIP_STORAGE_FORMAT_STRING, maTempFile.GetURL(), embed::ElementModes::READWRITE);
221 CPPUNIT_ASSERT(xStorage.is());
222 aManager.setStore(xStorage);
223 aManager.getSignatureHelper().SetStorage(xStorage, u"1.2");
225 // Then add a document signature.
226 uno::Reference<security::XCertificate> xCertificate
227 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::ECDSA);
228 if (!xCertificate.is())
229 return;
230 sal_Int32 nSecurityId;
231 svl::crypto::SigningContext aSigningContext;
232 aSigningContext.m_xCertificate = xCertificate;
233 aManager.add(aSigningContext, mxSecurityContext, u""_ustr, nSecurityId,
234 /*bAdESCompliant=*/false);
236 // Read back the signature and make sure that it's valid.
237 aManager.read(/*bUseTempStream=*/true);
238 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
239 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
240 // This was SecurityOperationStatus_UNKNOWN, signing with an ECDSA key was
241 // broken.
242 CPPUNIT_ASSERT_EQUAL(css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED,
243 rInformations[0].nStatus);
246 CPPUNIT_TEST_FIXTURE(SigningTest, testECDSAPDF)
248 // Create an empty document and store it to a tempfile, finally load it as
249 // a stream.
250 loadFromURL(u"private:factory/swriter"_ustr);
252 save(u"writer_pdf_Export"_ustr);
254 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
255 CPPUNIT_ASSERT(aManager.init());
256 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(
257 maTempFile.GetURL(), StreamMode::READ | StreamMode::WRITE));
258 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
259 CPPUNIT_ASSERT(xStream.is());
260 aManager.setSignatureStream(xStream);
262 // Then add a document signature.
263 uno::Reference<security::XCertificate> xCertificate
264 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::ECDSA);
265 if (!xCertificate.is())
266 return;
267 sal_Int32 nSecurityId;
268 svl::crypto::SigningContext aSigningContext;
269 aSigningContext.m_xCertificate = xCertificate;
270 aManager.add(aSigningContext, mxSecurityContext, u""_ustr, nSecurityId,
271 /*bAdESCompliant=*/true);
273 // Read back the signature and make sure that it's valid.
274 aManager.read(/*bUseTempStream=*/false);
275 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
276 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
277 if (!pPDFium)
279 return;
282 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
283 // This was SecurityOperationStatus_UNKNOWN, signing with an ECDSA key was
284 // broken.
285 CPPUNIT_ASSERT_EQUAL(css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED,
286 rInformations[0].nStatus);
289 CPPUNIT_TEST_FIXTURE(SigningTest, testOOXMLDescription)
291 // Create an empty document and store it to a tempfile, finally load it as a storage.
292 loadFromURL(u"private:factory/swriter"_ustr);
294 save(u"MS Word 2007 XML"_ustr);
296 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
297 CPPUNIT_ASSERT(aManager.init());
298 uno::Reference<embed::XStorage> xStorage
299 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
300 ZIP_STORAGE_FORMAT_STRING, maTempFile.GetURL(), embed::ElementModes::READWRITE);
301 CPPUNIT_ASSERT(xStorage.is());
302 aManager.setStore(xStorage);
303 aManager.getSignatureHelper().SetStorage(xStorage, u"1.2");
305 // Then add a document signature.
306 uno::Reference<security::XCertificate> xCertificate
307 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA);
308 if (!xCertificate.is())
309 return;
310 OUString aDescription(u"SigningTest::testDescription"_ustr);
311 sal_Int32 nSecurityId;
312 svl::crypto::SigningContext aSigningContext;
313 aSigningContext.m_xCertificate = xCertificate;
314 aManager.add(aSigningContext, mxSecurityContext, aDescription, nSecurityId, false);
316 // Read back the signature and make sure that the description survives the roundtrip.
317 aManager.read(/*bUseTempStream=*/true);
318 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
319 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
320 CPPUNIT_ASSERT_EQUAL(aDescription, rInformations[0].ouDescription);
323 /// Test appending a new signature next to an existing one.
324 CPPUNIT_TEST_FIXTURE(SigningTest, testOOXMLAppend)
326 // Copy the test document to a temporary file, as it'll be modified.
327 createTempCopy(u"partial.docx");
328 // Load the test document as a storage and read its single signature.
329 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
330 CPPUNIT_ASSERT(aManager.init());
331 uno::Reference<embed::XStorage> xStorage
332 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
333 ZIP_STORAGE_FORMAT_STRING, maTempFile.GetURL(), embed::ElementModes::READWRITE);
334 CPPUNIT_ASSERT(xStorage.is());
335 aManager.setStore(xStorage);
336 aManager.getSignatureHelper().SetStorage(xStorage, u"1.2");
337 aManager.read(/*bUseTempStream=*/false);
338 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
339 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
341 // Then add a second document signature.
342 uno::Reference<security::XCertificate> xCertificate
343 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA);
344 if (!xCertificate.is())
345 return;
346 sal_Int32 nSecurityId;
347 svl::crypto::SigningContext aSigningContext;
348 aSigningContext.m_xCertificate = xCertificate;
349 aManager.add(aSigningContext, mxSecurityContext, OUString(), nSecurityId, false);
351 // Read back the signatures and make sure that we have the expected amount.
352 aManager.read(/*bUseTempStream=*/true);
353 // This was 1: the original signature was lost.
354 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), rInformations.size());
357 /// Test removing a signature from existing ones.
358 CPPUNIT_TEST_FIXTURE(SigningTest, testOOXMLRemove)
360 // Load the test document as a storage and read its signatures: purpose1 and purpose2.
361 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
362 CPPUNIT_ASSERT(aManager.init());
363 createTempCopy(u"multi.docx");
364 uno::Reference<embed::XStorage> xStorage
365 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
366 ZIP_STORAGE_FORMAT_STRING, maTempFile.GetURL(), embed::ElementModes::READWRITE);
367 CPPUNIT_ASSERT(xStorage.is());
368 aManager.setStore(xStorage);
369 aManager.getSignatureHelper().SetStorage(xStorage, u"1.2");
370 aManager.read(/*bUseTempStream=*/false);
371 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
372 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), rInformations.size());
374 // Then remove the last added signature.
375 uno::Reference<security::XCertificate> xCertificate
376 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA);
377 if (!xCertificate.is())
378 return;
379 aManager.remove(0);
381 // Read back the signatures and make sure that only purpose1 is left.
382 aManager.read(/*bUseTempStream=*/true);
383 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
384 CPPUNIT_ASSERT_EQUAL(u"purpose1"_ustr, rInformations[0].ouDescription);
387 /// Test removing all signatures from a document.
388 CPPUNIT_TEST_FIXTURE(SigningTest, testOOXMLRemoveAll)
390 // Copy the test document to a temporary file, as it'll be modified.
391 createTempCopy(u"partial.docx");
392 // Load the test document as a storage and read its single signature.
393 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
394 CPPUNIT_ASSERT(aManager.init());
395 uno::Reference<embed::XStorage> xStorage
396 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
397 ZIP_STORAGE_FORMAT_STRING, maTempFile.GetURL(), embed::ElementModes::READWRITE);
398 CPPUNIT_ASSERT(xStorage.is());
399 aManager.setStore(xStorage);
400 aManager.getSignatureHelper().SetStorage(xStorage, u"1.2");
401 aManager.read(/*bUseTempStream=*/false);
402 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
403 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
405 // Then remove the only signature in the document.
406 uno::Reference<security::XCertificate> xCertificate
407 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA);
408 if (!xCertificate.is())
409 return;
410 aManager.remove(0);
411 aManager.read(/*bUseTempStream=*/true);
412 aManager.write(/*bXAdESCompliantIfODF=*/false);
414 // Make sure that the signature count is zero and the whole signature storage is removed completely.
415 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(0), rInformations.size());
416 CPPUNIT_ASSERT(!xStorage->hasByName(u"_xmlsignatures"_ustr));
418 // And that content types no longer contains signature types.
419 uno::Reference<io::XStream> xStream
420 = xStorage->openStreamElement(u"[Content_Types].xml"_ustr, embed::ElementModes::READWRITE);
421 uno::Reference<io::XInputStream> xInputStream = xStream->getInputStream();
422 uno::Sequence<uno::Sequence<beans::StringPair>> aContentTypeInfo
423 = comphelper::OFOPXMLHelper::ReadContentTypeSequence(xInputStream, m_xContext);
424 const uno::Sequence<beans::StringPair>& rOverrides = aContentTypeInfo[1];
425 CPPUNIT_ASSERT(
426 std::none_of(rOverrides.begin(), rOverrides.end(), [](const beans::StringPair& rPair) {
427 return rPair.First.startsWith("/_xmlsignatures/sig");
428 }));
431 /// Test a typical ODF where all streams are signed.
432 CPPUNIT_TEST_FIXTURE(SigningTest, testODFGood)
434 loadFromFile(u"good.odt");
435 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
436 CPPUNIT_ASSERT(pBaseModel);
437 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
438 CPPUNIT_ASSERT(pObjectShell);
439 // We expect NOTVALIDATED in case the root CA is not imported on the system, and OK otherwise, so accept both.
440 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
441 CPPUNIT_ASSERT_MESSAGE(
442 (OString::number(o3tl::to_underlying(nActual)).getStr()),
443 (nActual == SignatureState::NOTVALIDATED || nActual == SignatureState::OK));
446 /// Test a typical broken ODF signature where one stream is corrupted.
447 CPPUNIT_TEST_FIXTURE(SigningTest, testODFBroken)
449 loadFromFile(u"bad.odt");
450 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
451 CPPUNIT_ASSERT(pBaseModel);
452 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
453 CPPUNIT_ASSERT(pObjectShell);
454 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN),
455 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
458 // Document has a signature stream, but no actual signatures.
459 CPPUNIT_TEST_FIXTURE(SigningTest, testODFNo)
461 loadFromFile(u"no.odt");
462 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
463 CPPUNIT_ASSERT(pBaseModel);
464 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
465 CPPUNIT_ASSERT(pObjectShell);
466 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::NOSIGNATURES),
467 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
470 // document has one signed timestamp and one unsigned timestamp
471 CPPUNIT_TEST_FIXTURE(SigningTest, testODFUnsignedTimestamp)
473 loadFromFile(u"02_doc_signed_by_trusted_person_manipulated.odt");
474 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
475 CPPUNIT_ASSERT(pBaseModel);
476 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
477 CPPUNIT_ASSERT(pObjectShell);
478 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
479 CPPUNIT_ASSERT_MESSAGE(
480 (OString::number(o3tl::to_underlying(nActual)).getStr()),
481 (nActual == SignatureState::NOTVALIDATED || nActual == SignatureState::OK));
482 uno::Sequence<security::DocumentSignatureInformation> const infos(
483 pObjectShell->GetDocumentSignatureInformation(false));
484 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), infos.getLength());
485 // was: 66666666
486 CPPUNIT_ASSERT_EQUAL(sal_Int32(20210126), infos[0].SignatureDate);
487 // was: 0
488 CPPUNIT_ASSERT_EQUAL(sal_Int32(18183742), infos[0].SignatureTime);
491 // FIXME: For some unknown reason, this test fails on tml's Mac unless it is the only or the first
492 // test that is run in this CppunitTest program. When using our patched bundled cppunit library (as
493 // we obviously always do on macOS), the CPPUNIT_TEST_FIXTUREs are run in lexicographical order so
494 // use a name for this test that makes it the first one to run.
496 CPPUNIT_TEST_FIXTURE(SigningTest, aaa_testODFX509CertificateChain)
498 loadFromFile(u"signed_with_x509certificate_chain.odt");
499 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
500 CPPUNIT_ASSERT(pBaseModel);
501 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
502 CPPUNIT_ASSERT(pObjectShell);
503 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
504 CPPUNIT_ASSERT_MESSAGE(
505 (OString::number(o3tl::to_underlying(nActual)).getStr()),
506 (nActual == SignatureState::NOTVALIDATED || nActual == SignatureState::OK));
507 uno::Sequence<security::DocumentSignatureInformation> const infos(
508 pObjectShell->GetDocumentSignatureInformation(false));
509 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), infos.getLength());
510 // check that the signing certificate was picked, not one of the 2 CA ones
511 CPPUNIT_ASSERT_EQUAL(security::CertificateValidity::VALID, infos[0].CertificateStatus);
512 CPPUNIT_ASSERT(infos[0].Signer.is());
513 CPPUNIT_ASSERT_EQUAL(
514 u"CN=Xmlsecurity RSA Test example Alice,O=Xmlsecurity RSA Test,ST=England,C=UK"_ustr,
515 // CryptoAPI puts a space after comma, NSS does not...
516 infos[0].Signer->getSubjectName().replaceAll(", ", ","));
519 CPPUNIT_TEST_FIXTURE(SigningTest, testODFDoubleX509Data)
521 loadFromFile(u"02_doc_signed_by_attacker_manipulated.odt");
522 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
523 CPPUNIT_ASSERT(pBaseModel);
524 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
525 CPPUNIT_ASSERT(pObjectShell);
526 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
527 CPPUNIT_ASSERT_MESSAGE(
528 (OString::number(o3tl::to_underlying(nActual)).getStr()),
529 (nActual == SignatureState::NOTVALIDATED || nActual == SignatureState::OK));
530 uno::Sequence<security::DocumentSignatureInformation> const infos(
531 pObjectShell->GetDocumentSignatureInformation(false));
532 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), infos.getLength());
533 // the signature in this manipulated document is technically valid but we can't tell who signed
534 // it, so make sure no misleading info is shown to the user
535 CPPUNIT_ASSERT_EQUAL(security::CertificateValidity::INVALID, infos[0].CertificateStatus);
536 CPPUNIT_ASSERT(!infos[0].Signer.is());
539 CPPUNIT_TEST_FIXTURE(SigningTest, testODFTripleX509Data)
541 loadFromFile(u"02_doc_signed_by_attacker_manipulated_triple.odt");
542 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
543 CPPUNIT_ASSERT(pBaseModel);
544 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
545 CPPUNIT_ASSERT(pObjectShell);
546 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
547 // here, libxmlsec will pick the 1st X509Data but signing key is the 2nd
548 CPPUNIT_ASSERT_EQUAL_MESSAGE((OString::number(o3tl::to_underlying(nActual)).getStr()),
549 SignatureState::BROKEN, nActual);
550 uno::Sequence<security::DocumentSignatureInformation> const infos(
551 pObjectShell->GetDocumentSignatureInformation(false));
552 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), infos.getLength());
553 // the signature in this manipulated document is technically valid but we can't tell who signed
554 // it, so make sure no misleading info is shown to the user
555 CPPUNIT_ASSERT_EQUAL(security::CertificateValidity::INVALID, infos[0].CertificateStatus);
556 CPPUNIT_ASSERT(!infos[0].Signer.is());
559 CPPUNIT_TEST_FIXTURE(SigningTest, testODFMacroDoubleX509Data)
561 loadFromFile(u"02_doc_macros_signed_by_attacker_manipulated.odt");
562 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
563 CPPUNIT_ASSERT(pBaseModel);
564 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
565 CPPUNIT_ASSERT(pObjectShell);
566 SignatureState nActual = pObjectShell->GetScriptingSignatureState();
567 CPPUNIT_ASSERT_MESSAGE(
568 (OString::number(o3tl::to_underlying(nActual)).getStr()),
569 (nActual == SignatureState::NOTVALIDATED || nActual == SignatureState::OK));
570 uno::Sequence<security::DocumentSignatureInformation> const infos(
571 pObjectShell->GetDocumentSignatureInformation(true));
572 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), infos.getLength());
573 // the signature in this manipulated document is technically valid but we can't tell who signed
574 // it, so make sure no misleading info is shown to the user
575 CPPUNIT_ASSERT_EQUAL(security::CertificateValidity::INVALID, infos[0].CertificateStatus);
576 CPPUNIT_ASSERT(!infos[0].Signer.is());
579 CPPUNIT_TEST_FIXTURE(SigningTest, testODFDoubleX509Certificate)
581 loadFromFile(u"02_doc_signed_by_attacker_manipulated2.odt");
582 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
583 CPPUNIT_ASSERT(pBaseModel);
584 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
585 CPPUNIT_ASSERT(pObjectShell);
586 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
587 bool const nTemp((nActual == SignatureState::NOTVALIDATED
588 || nActual == SignatureState::OK
589 #if defined(_WIN32)
590 // oddly BCryptVerifySignature returns STATUS_INVALID_SIGNATURE
591 // while the same succeeds with NSS _SGN_VerifyPKCS1DigestInfo
592 || nActual == SignatureState::BROKEN
593 #endif
595 CPPUNIT_ASSERT_MESSAGE((OString::number(o3tl::to_underlying(nActual)).getStr()), nTemp);
596 uno::Sequence<security::DocumentSignatureInformation> const infos(
597 pObjectShell->GetDocumentSignatureInformation(false));
598 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), infos.getLength());
599 // the signature in this manipulated document is technically valid but we can't tell who signed
600 // it, so make sure no misleading info is shown to the user
601 CPPUNIT_ASSERT_EQUAL(security::CertificateValidity::INVALID, infos[0].CertificateStatus);
602 CPPUNIT_ASSERT(!infos[0].Signer.is());
605 CPPUNIT_TEST_FIXTURE(SigningTest, testDNCompatibility)
607 static constexpr OUString msDN(u"CN=\"\"\"ABC\"\".\", O=\"Enterprise \"\"ABC\"\"\""_ustr);
608 static constexpr OUString nssDN(u"CN=\\\"ABC\\\".,O=Enterprise \\\"ABC\\\""_ustr);
609 // this is just the status quo, possibly either NSS or CryptoAPI might change
610 CPPUNIT_ASSERT(!xmlsecurity::EqualDistinguishedNames(msDN, nssDN, xmlsecurity::NOCOMPAT));
611 CPPUNIT_ASSERT(!xmlsecurity::EqualDistinguishedNames(nssDN, msDN, xmlsecurity::NOCOMPAT));
612 // with compat flag it should work, with the string one 2nd and the native one 1st
613 #ifdef _WIN32
614 CPPUNIT_ASSERT(xmlsecurity::EqualDistinguishedNames(msDN, nssDN, xmlsecurity::COMPAT_2ND));
615 #else
616 CPPUNIT_ASSERT(xmlsecurity::EqualDistinguishedNames(nssDN, msDN, xmlsecurity::COMPAT_2ND));
617 #endif
620 /// Test a typical OOXML where a number of (but not all) streams are signed.
621 CPPUNIT_TEST_FIXTURE(SigningTest, testOOXMLPartial)
623 loadFromFile(u"partial.docx");
624 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
625 CPPUNIT_ASSERT(pBaseModel);
626 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
627 CPPUNIT_ASSERT(pObjectShell);
628 // This was SignatureState::BROKEN due to missing RelationshipTransform and SHA-256 support.
629 // We expect NOTVALIDATED_PARTIAL_OK in case the root CA is not imported on the system, and PARTIAL_OK otherwise, so accept both.
630 // But reject NOTVALIDATED, hiding incompleteness is not OK.
631 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
632 CPPUNIT_ASSERT_MESSAGE((OString::number(o3tl::to_underlying(nActual)).getStr()),
633 (nActual == SignatureState::NOTVALIDATED_PARTIAL_OK
634 || nActual == SignatureState::PARTIAL_OK));
637 /// Test a typical broken OOXML signature where one stream is corrupted.
638 CPPUNIT_TEST_FIXTURE(SigningTest, testOOXMLBroken)
640 loadFromFile(u"bad.docx");
641 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
642 CPPUNIT_ASSERT(pBaseModel);
643 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
644 CPPUNIT_ASSERT(pObjectShell);
645 // This was SignatureState::NOTVALIDATED/PARTIAL_OK as we did not validate manifest references.
646 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN),
647 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
650 #if HAVE_FEATURE_PDFIMPORT
652 /// Test a typical PDF where the signature is good.
653 CPPUNIT_TEST_FIXTURE(SigningTest, testPDFGood)
655 loadFromFile(u"good.pdf");
656 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
657 CPPUNIT_ASSERT(pBaseModel);
658 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
659 CPPUNIT_ASSERT(pObjectShell);
660 // We expect NOTVALIDATED in case the root CA is not imported on the system, and OK otherwise, so accept both.
661 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
662 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
663 if (!pPDFium)
665 return;
668 CPPUNIT_ASSERT_MESSAGE(
669 (OString::number(o3tl::to_underlying(nActual)).getStr()),
670 (nActual == SignatureState::NOTVALIDATED || nActual == SignatureState::OK));
673 /// Test a typical PDF where the signature is bad.
674 CPPUNIT_TEST_FIXTURE(SigningTest, testPDFBad)
676 loadFromFile(u"bad.pdf");
677 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
678 CPPUNIT_ASSERT(pBaseModel);
679 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
680 CPPUNIT_ASSERT(pObjectShell);
681 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
682 if (!pPDFium)
684 return;
687 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN),
688 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
691 CPPUNIT_TEST_FIXTURE(SigningTest, testPDFHideAndReplace)
693 loadFromFile(u"hide-and-replace-shadow-file-signed-2.pdf");
694 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
695 CPPUNIT_ASSERT(pBaseModel);
696 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
697 CPPUNIT_ASSERT(pObjectShell);
698 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
699 if (!pPDFium)
701 return;
704 // Without the accompanying fix in place, this test would have failed with:
705 // - Expected: 2 (BROKEN)
706 // - Actual : 6 (NOTVALIDATED_PARTIAL_OK)
707 // i.e. a non-commenting update after a signature was not marked as invalid.
708 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN),
709 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
712 /// Test a typical PDF which is not signed.
713 CPPUNIT_TEST_FIXTURE(SigningTest, testPDFNo)
715 loadFromFile(u"no.pdf");
716 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
717 CPPUNIT_ASSERT(pBaseModel);
718 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
719 CPPUNIT_ASSERT(pObjectShell);
720 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::NOSIGNATURES),
721 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
724 #endif
726 CPPUNIT_TEST_FIXTURE(SigningTest, testPDFAddVisibleSignature)
728 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
729 if (!pPDFium)
731 return;
734 // FIXME: the DPI check should be removed when either (1) the test is fixed to work with
735 // non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
736 if (!IsDefaultDPI())
737 return;
738 // Given: copy the test document to a temporary file, as it'll be modified.
739 createTempCopy(u"add-visible-signature.pdf");
741 // Open it.
742 uno::Sequence<beans::PropertyValue> aArgs
743 = { comphelper::makePropertyValue(u"ReadOnly"_ustr, true) };
744 loadWithParams(maTempFile.GetURL(), aArgs);
745 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
746 CPPUNIT_ASSERT(pBaseModel);
747 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
748 CPPUNIT_ASSERT(pObjectShell);
750 // Add a signature line.
751 uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
752 uno::Reference<drawing::XShape> xShape(
753 xFactory->createInstance(u"com.sun.star.drawing.GraphicObjectShape"_ustr), uno::UNO_QUERY);
754 xShape->setPosition(awt::Point(1000, 15000));
755 xShape->setSize(awt::Size(10000, 10000));
756 uno::Reference<drawing::XDrawPagesSupplier> xSupplier(mxComponent, uno::UNO_QUERY);
757 uno::Reference<drawing::XDrawPages> xDrawPages = xSupplier->getDrawPages();
758 uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPages->getByIndex(0), uno::UNO_QUERY);
759 xDrawPage->add(xShape);
761 // Select it and assign a certificate.
762 uno::Reference<view::XSelectionSupplier> xSelectionSupplier(pBaseModel->getCurrentController(),
763 uno::UNO_QUERY);
764 xSelectionSupplier->select(uno::Any(xShape));
765 auto xEnv = mxSecurityContext->getSecurityEnvironment();
766 auto xCert = GetValidCertificate(xEnv->getPersonalCertificates(), xEnv);
767 if (!xCert)
769 return;
771 SfxViewShell* pCurrent = SfxViewShell::Current();
772 CPPUNIT_ASSERT(pCurrent);
773 SdrView* pView = pCurrent->GetDrawView();
774 svx::SignatureLineHelper::setShapeCertificate(pView, xCert);
776 // the document is modified now, but Sign function can't show SaveAs dialog
777 // in unit test, so just clear the modified
778 pObjectShell->SetModified(false);
780 // When: do the actual signing.
781 svl::crypto::SigningContext aSigningContext;
782 aSigningContext.m_xCertificate = xCert;
783 pObjectShell->SignDocumentContentUsingCertificate(aSigningContext);
785 // Then: count the # of shapes on the signature widget/annotation.
786 std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
788 std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
789 CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getAnnotationCount());
790 std::unique_ptr<vcl::pdf::PDFiumAnnotation> pAnnot = pPdfPage->getAnnotation(/*nIndex=*/0);
791 // Without the accompanying fix in place, this test would have failed with:
792 // - Expected: 4
793 // - Actual : 0
794 // i.e. the signature was there, but it was empty / not visible.
795 CPPUNIT_ASSERT_EQUAL(4, pAnnot->getObjectCount());
798 CPPUNIT_TEST_FIXTURE(SigningTest, test96097Calc)
800 loadFromFile(u"tdf96097.ods");
801 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
802 CPPUNIT_ASSERT_MESSAGE("Failed to access document base model", pBaseModel);
804 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
805 CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pObjectShell);
807 SignatureState nActual = pObjectShell->GetScriptingSignatureState();
808 CPPUNIT_ASSERT_MESSAGE((OString::number(o3tl::to_underlying(nActual)).getStr()),
809 (nActual == SignatureState::OK || nActual == SignatureState::NOTVALIDATED
810 || nActual == SignatureState::INVALID));
812 uno::Reference<frame::XStorable> xDocStorable(mxComponent, uno::UNO_QUERY_THROW);
814 // Save a copy
815 uno::Sequence<beans::PropertyValue> descSaveACopy(comphelper::InitPropertySequence(
816 { { "SaveACopy", uno::Any(true) }, { "FilterName", uno::Any(u"calc8"_ustr) } }));
817 xDocStorable->storeToURL(maTempFile.GetURL(), descSaveACopy);
819 // FIXME: Error: element "document-signatures" is missing "version" attribute
820 skipValidation();
822 // Save As
823 save(u"calc8"_ustr);
826 CPPUNIT_TEST_FIXTURE(SigningTest, test96097Doc)
828 loadFromFile(u"tdf96097.odt");
829 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
830 CPPUNIT_ASSERT(pBaseModel);
831 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
832 CPPUNIT_ASSERT(pObjectShell);
834 SignatureState nActual = pObjectShell->GetScriptingSignatureState();
835 CPPUNIT_ASSERT_MESSAGE((OString::number(o3tl::to_underlying(nActual)).getStr()),
836 (nActual == SignatureState::OK || nActual == SignatureState::NOTVALIDATED
837 || nActual == SignatureState::INVALID));
839 uno::Reference<frame::XStorable> xDocStorable(mxComponent, uno::UNO_QUERY_THROW);
841 // Save a copy
842 uno::Sequence<beans::PropertyValue> descSaveACopy(comphelper::InitPropertySequence(
843 { { "SaveACopy", uno::Any(true) }, { "FilterName", uno::Any(u"writer8"_ustr) } }));
844 xDocStorable->storeToURL(maTempFile.GetURL(), descSaveACopy);
846 // FIXME: Error: element "document-signatures" is missing "version" attribute
847 skipValidation();
849 // Save As
850 save(u"writer8"_ustr);
853 CPPUNIT_TEST_FIXTURE(SigningTest, testXAdESNotype)
855 // Create a working copy.
856 createTempCopy(u"notype-xades.odt");
858 // Read existing signature.
859 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
860 CPPUNIT_ASSERT(aManager.init());
861 uno::Reference<embed::XStorage> xStorage
862 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
863 ZIP_STORAGE_FORMAT_STRING, maTempFile.GetURL(), embed::ElementModes::READWRITE);
864 CPPUNIT_ASSERT(xStorage.is());
865 aManager.setStore(xStorage);
866 aManager.getSignatureHelper().SetStorage(xStorage, u"1.2");
867 aManager.read(/*bUseTempStream=*/false);
869 // Create a new signature.
870 uno::Reference<security::XCertificate> xCertificate
871 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA);
872 if (!xCertificate.is())
873 return;
874 sal_Int32 nSecurityId;
875 svl::crypto::SigningContext aSigningContext;
876 aSigningContext.m_xCertificate = xCertificate;
877 aManager.add(aSigningContext, mxSecurityContext, /*rDescription=*/OUString(), nSecurityId,
878 /*bAdESCompliant=*/true);
880 // Write to storage.
881 aManager.read(/*bUseTempStream=*/true);
882 aManager.write(/*bXAdESCompliantIfODF=*/true);
883 uno::Reference<embed::XTransactedObject> xTransactedObject(xStorage, uno::UNO_QUERY);
884 xTransactedObject->commit();
886 // Parse the resulting XML.
887 uno::Reference<embed::XStorage> xMetaInf
888 = xStorage->openStorageElement(u"META-INF"_ustr, embed::ElementModes::READ);
889 uno::Reference<io::XInputStream> xInputStream(
890 xMetaInf->openStreamElement(u"documentsignatures.xml"_ustr, embed::ElementModes::READ),
891 uno::UNO_QUERY);
892 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
893 xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
895 // Without the accompanying fix in place, this test would have failed with "unexpected 'Type'
896 // attribute", i.e. the signature without such an attribute was not preserved correctly.
897 assertXPathNoAttribute(pXmlDoc,
898 "/odfds:document-signatures/dsig:Signature[1]/dsig:SignedInfo/"
899 "dsig:Reference[starts-with(@URI, '#idSignedProperties')]",
900 "Type");
902 // New signature always has the Type attribute.
903 assertXPath(pXmlDoc,
904 "/odfds:document-signatures/dsig:Signature[2]/dsig:SignedInfo/"
905 "dsig:Reference[starts-with(@URI, '#idSignedProperties')]",
906 "Type", u"http://uri.etsi.org/01903#SignedProperties");
909 /// Creates a XAdES signature from scratch.
910 CPPUNIT_TEST_FIXTURE(SigningTest, testXAdES)
912 // Create an empty document, store it to a tempfile and load it as a storage.
913 loadFromURL(u"private:factory/swriter"_ustr);
915 save(u"writer8"_ustr);
917 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
918 CPPUNIT_ASSERT(aManager.init());
919 uno::Reference<embed::XStorage> xStorage
920 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
921 ZIP_STORAGE_FORMAT_STRING, maTempFile.GetURL(), embed::ElementModes::READWRITE);
922 CPPUNIT_ASSERT(xStorage.is());
923 aManager.setStore(xStorage);
924 aManager.getSignatureHelper().SetStorage(xStorage, u"1.2");
926 // Create a signature.
927 uno::Reference<security::XCertificate> xCertificate
928 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA);
929 if (!xCertificate.is())
930 return;
931 sal_Int32 nSecurityId;
932 svl::crypto::SigningContext aSigningContext;
933 aSigningContext.m_xCertificate = xCertificate;
934 aManager.add(aSigningContext, mxSecurityContext, /*rDescription=*/OUString(), nSecurityId,
935 /*bAdESCompliant=*/true);
937 // Write to storage.
938 aManager.read(/*bUseTempStream=*/true);
939 aManager.write(/*bXAdESCompliantIfODF=*/true);
940 uno::Reference<embed::XTransactedObject> xTransactedObject(xStorage, uno::UNO_QUERY);
941 xTransactedObject->commit();
943 // Parse the resulting XML.
944 uno::Reference<embed::XStorage> xMetaInf
945 = xStorage->openStorageElement(u"META-INF"_ustr, embed::ElementModes::READ);
946 uno::Reference<io::XInputStream> xInputStream(
947 xMetaInf->openStreamElement(u"documentsignatures.xml"_ustr, embed::ElementModes::READ),
948 uno::UNO_QUERY);
949 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
950 xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
952 // Assert that the digest algorithm is SHA-256 in the bAdESCompliant case, not SHA-1.
953 assertXPath(pXmlDoc,
954 "/odfds:document-signatures/dsig:Signature/dsig:SignedInfo/"
955 "dsig:Reference[@URI='content.xml']/dsig:DigestMethod",
956 "Algorithm", ALGO_XMLDSIGSHA256);
958 // Assert that the digest of the signing certificate is included.
959 assertXPath(pXmlDoc, "//xd:CertDigest", 1);
961 // Assert that the Type attribute is set on all URI's that start with #idSignedProperties
962 assertXPath(pXmlDoc, "//dsig:Reference[starts-with(@URI, '#idSignedProperties')]", "Type",
963 u"http://uri.etsi.org/01903#SignedProperties");
966 CPPUNIT_TEST_FIXTURE(SigningTest, testSigningMultipleTimes_ODT)
968 loadFromURL(u"private:factory/swriter"_ustr);
970 save(u"writer8"_ustr);
972 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
973 CPPUNIT_ASSERT(aManager.init());
974 uno::Reference<embed::XStorage> xStorage
975 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
976 ZIP_STORAGE_FORMAT_STRING, maTempFile.GetURL(), embed::ElementModes::READWRITE);
977 CPPUNIT_ASSERT(xStorage.is());
978 aManager.setStore(xStorage);
979 aManager.getSignatureHelper().SetStorage(xStorage, u"1.2");
981 // Create a signature.
982 uno::Reference<security::XCertificate> xCertificate
983 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA);
985 if (!xCertificate.is())
986 return;
987 sal_Int32 nSecurityId;
988 svl::crypto::SigningContext aSigningContext;
989 aSigningContext.m_xCertificate = xCertificate;
990 aManager.add(aSigningContext, mxSecurityContext, /*rDescription=*/OUString(), nSecurityId,
991 /*bAdESCompliant=*/true);
993 // Read back the signature and make sure that it's valid.
994 aManager.read(/*bUseTempStream=*/true);
996 std::vector<SignatureInformation>& rInformations
997 = aManager.getCurrentSignatureInformations();
998 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
999 CPPUNIT_ASSERT_EQUAL(css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED,
1000 rInformations[0].nStatus);
1003 aManager.add(aSigningContext, mxSecurityContext, /*rDescription=*/OUString(), nSecurityId,
1004 /*bAdESCompliant=*/true);
1005 aManager.read(/*bUseTempStream=*/true);
1007 std::vector<SignatureInformation>& rInformations
1008 = aManager.getCurrentSignatureInformations();
1009 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), rInformations.size());
1010 CPPUNIT_ASSERT_EQUAL(css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED,
1011 rInformations[1].nStatus);
1014 aManager.add(aSigningContext, mxSecurityContext, /*rDescription=*/OUString(), nSecurityId,
1015 /*bAdESCompliant=*/true);
1016 aManager.read(/*bUseTempStream=*/true);
1018 std::vector<SignatureInformation>& rInformations
1019 = aManager.getCurrentSignatureInformations();
1020 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(3), rInformations.size());
1021 CPPUNIT_ASSERT_EQUAL(css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED,
1022 rInformations[2].nStatus);
1025 aManager.write(/*bXAdESCompliantIfODF=*/true);
1026 uno::Reference<embed::XTransactedObject> xTransactedObject(xStorage, uno::UNO_QUERY);
1027 xTransactedObject->commit();
1030 Scheduler::ProcessEventsToIdle();
1032 loadFromURL(maTempFile.GetURL());
1034 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
1035 CPPUNIT_ASSERT(pBaseModel);
1036 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
1037 CPPUNIT_ASSERT(pObjectShell);
1038 CPPUNIT_ASSERT_EQUAL(SignatureState::OK, pObjectShell->GetDocumentSignatureState());
1041 CPPUNIT_TEST_FIXTURE(SigningTest, testSigningMultipleTimes_OOXML)
1043 loadFromURL(u"private:factory/swriter"_ustr);
1045 save(u"MS Word 2007 XML"_ustr);
1047 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
1048 CPPUNIT_ASSERT(aManager.init());
1049 uno::Reference<embed::XStorage> xStorage
1050 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
1051 ZIP_STORAGE_FORMAT_STRING, maTempFile.GetURL(), embed::ElementModes::READWRITE);
1052 CPPUNIT_ASSERT(xStorage.is());
1053 aManager.setStore(xStorage);
1054 aManager.getSignatureHelper().SetStorage(xStorage, u"1.2");
1056 // Create a signature.
1057 uno::Reference<security::XCertificate> xCertificate
1058 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::ECDSA);
1059 if (!xCertificate.is())
1060 return;
1062 sal_Int32 nSecurityId;
1063 svl::crypto::SigningContext aSigningContext;
1064 aSigningContext.m_xCertificate = xCertificate;
1065 aManager.add(aSigningContext, mxSecurityContext, u""_ustr, nSecurityId,
1066 /*bAdESCompliant=*/false);
1067 aManager.read(/*bUseTempStream=*/true);
1069 std::vector<SignatureInformation>& rInformations
1070 = aManager.getCurrentSignatureInformations();
1071 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
1072 CPPUNIT_ASSERT_EQUAL(css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED,
1073 rInformations[0].nStatus);
1076 aManager.add(aSigningContext, mxSecurityContext, u""_ustr, nSecurityId,
1077 /*bAdESCompliant=*/false);
1078 aManager.read(/*bUseTempStream=*/true);
1080 std::vector<SignatureInformation>& rInformations
1081 = aManager.getCurrentSignatureInformations();
1082 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), rInformations.size());
1083 CPPUNIT_ASSERT_EQUAL(css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED,
1084 rInformations[1].nStatus);
1087 aManager.add(aSigningContext, mxSecurityContext, u""_ustr, nSecurityId,
1088 /*bAdESCompliant=*/false);
1089 aManager.read(/*bUseTempStream=*/true);
1091 std::vector<SignatureInformation>& rInformations
1092 = aManager.getCurrentSignatureInformations();
1093 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(3), rInformations.size());
1094 CPPUNIT_ASSERT_EQUAL(css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED,
1095 rInformations[2].nStatus);
1098 aManager.write(/*bXAdESCompliantIfODF=*/true);
1099 uno::Reference<embed::XTransactedObject> xTransactedObject(xStorage, uno::UNO_QUERY);
1100 xTransactedObject->commit();
1103 Scheduler::ProcessEventsToIdle();
1105 loadFromURL(maTempFile.GetURL());
1107 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
1108 CPPUNIT_ASSERT(pBaseModel);
1109 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
1110 CPPUNIT_ASSERT(pObjectShell);
1111 CPPUNIT_ASSERT_EQUAL(SignatureState::PARTIAL_OK, pObjectShell->GetDocumentSignatureState());
1114 /// Works with an existing good XAdES signature.
1115 CPPUNIT_TEST_FIXTURE(SigningTest, testXAdESGood)
1117 loadFromFile(u"good-xades.odt");
1118 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
1119 CPPUNIT_ASSERT(pBaseModel);
1120 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
1121 CPPUNIT_ASSERT(pObjectShell);
1122 // We expect NOTVALIDATED in case the root CA is not imported on the system, and OK otherwise, so accept both.
1123 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
1124 CPPUNIT_ASSERT_MESSAGE(
1125 (OString::number(o3tl::to_underlying(nActual)).getStr()),
1126 (nActual == SignatureState::NOTVALIDATED || nActual == SignatureState::OK));
1129 /// Test importing of signature line
1130 CPPUNIT_TEST_FIXTURE(SigningTest, testSignatureLineOOXML)
1132 // Given: A document (docx) with a signature line and a valid signature
1133 uno::Reference<security::XDocumentDigitalSignatures> xSignatures(
1134 security::DocumentDigitalSignatures::createDefault(
1135 comphelper::getProcessComponentContext()));
1137 uno::Reference<embed::XStorage> xStorage
1138 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
1139 ZIP_STORAGE_FORMAT_STRING, createFileURL(u"signatureline.docx"),
1140 embed::ElementModes::READ);
1141 CPPUNIT_ASSERT(xStorage.is());
1143 uno::Sequence<security::DocumentSignatureInformation> xSignatureInfo
1144 = xSignatures->verifyScriptingContentSignatures(xStorage,
1145 uno::Reference<io::XInputStream>());
1147 CPPUNIT_ASSERT(xSignatureInfo.getLength());
1149 // The signature should have a valid signature, and signature line with two valid images
1150 CPPUNIT_ASSERT(xSignatureInfo[0].SignatureIsValid);
1151 CPPUNIT_ASSERT_EQUAL(u"{DEE0514B-13E8-4674-A831-46E3CDB18BB4}"_ustr,
1152 xSignatureInfo[0].SignatureLineId);
1153 CPPUNIT_ASSERT(xSignatureInfo[0].ValidSignatureLineImage.is());
1154 CPPUNIT_ASSERT(xSignatureInfo[0].InvalidSignatureLineImage.is());
1157 CPPUNIT_TEST_FIXTURE(SigningTest, testSignatureLineODF)
1159 loadFromFile(u"signatureline.odt");
1160 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
1161 CPPUNIT_ASSERT(pBaseModel);
1162 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
1163 CPPUNIT_ASSERT(pObjectShell);
1165 uno::Sequence<security::DocumentSignatureInformation> xSignatureInfo
1166 = pObjectShell->GetDocumentSignatureInformation(false);
1168 CPPUNIT_ASSERT(xSignatureInfo.getLength());
1170 CPPUNIT_ASSERT(xSignatureInfo[0].SignatureIsValid);
1171 CPPUNIT_ASSERT_EQUAL(u"{41CF56EE-331B-4125-97D8-2F5669DD3AAC}"_ustr,
1172 xSignatureInfo[0].SignatureLineId);
1173 CPPUNIT_ASSERT(xSignatureInfo[0].ValidSignatureLineImage.is());
1174 CPPUNIT_ASSERT(xSignatureInfo[0].InvalidSignatureLineImage.is());
1177 CPPUNIT_TEST_FIXTURE(SigningTest, testImplicitScriptSign)
1179 // Given an ODT file with macros, and two signature managers to create macro + doc signatures:
1180 createTempCopy(u"macro.odt");
1181 OUString aFileURL = maTempFile.GetURL();
1182 uno::Reference<embed::XStorage> xWriteableZipStor
1183 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(ZIP_STORAGE_FORMAT_STRING, aFileURL,
1184 embed::ElementModes::READWRITE);
1186 uno::Reference<embed::XStorage> xMetaInf
1187 = xWriteableZipStor->openStorageElement(u"META-INF"_ustr, embed::ElementModes::READWRITE);
1188 uno::Reference<io::XStream> xStream = xMetaInf->openStreamElement(
1189 u"documentsignatures.xml"_ustr, embed::ElementModes::READWRITE);
1190 uno::Reference<io::XStream> xScriptingStream
1191 = xMetaInf->openStreamElement(u"macrosignatures.xml"_ustr, embed::ElementModes::READWRITE);
1192 uno::Reference<embed::XStorage> xZipStor
1193 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(ZIP_STORAGE_FORMAT_STRING, aFileURL,
1194 embed::ElementModes::READ);
1195 DocumentSignatureManager aManager(m_xContext, DocumentSignatureMode::Content);
1196 CPPUNIT_ASSERT(aManager.init());
1197 aManager.setStore(xZipStor);
1198 aManager.setSignatureStream(xStream);
1199 aManager.getSignatureHelper().SetStorage(xZipStor, u"1.2", xScriptingStream);
1200 DocumentSignatureManager aScriptManager(m_xContext, DocumentSignatureMode::Macros);
1201 CPPUNIT_ASSERT(aScriptManager.init());
1202 aScriptManager.setStore(xZipStor);
1203 aScriptManager.getSignatureHelper().SetStorage(xZipStor, u"1.2");
1204 aScriptManager.setSignatureStream(xScriptingStream);
1205 uno::Reference<security::XCertificate> xCertificate
1206 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA);
1207 if (!xCertificate.is())
1208 return;
1210 // When adding those signatures and writing them to the streams from the read-write storage:
1211 OUString aDescription;
1212 sal_Int32 nSecurityId;
1213 bool bAdESCompliant = true;
1214 svl::crypto::SigningContext aSigningContext;
1215 aSigningContext.m_xCertificate = xCertificate;
1216 aScriptManager.add(aSigningContext, mxSecurityContext, aDescription, nSecurityId,
1217 bAdESCompliant);
1218 aScriptManager.read(/*bUseTempStream=*/true, /*bCacheLastSignature=*/false);
1219 aScriptManager.write(bAdESCompliant);
1220 aManager.setScriptingSignatureStream(xScriptingStream);
1221 aManager.add(aSigningContext, mxSecurityContext, aDescription, nSecurityId, bAdESCompliant);
1222 aManager.read(/*bUseTempStream=*/true, /*bCacheLastSignature=*/false);
1223 aManager.write(bAdESCompliant);
1225 // Then make sure both signatures are created correctly:
1226 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xScriptingStream, true));
1227 xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
1228 OUString aScriptDigest
1229 = getXPathContent(pXmlDoc, "/odfds:document-signatures/dsig:Signature[1]/dsig:SignedInfo/"
1230 "dsig:Reference[@URI='Basic/script-lc.xml']/dsig:DigestValue");
1231 // Without the accompanying fix in place, this test would have failed, the digest value was just a
1232 // " " placeholder.
1233 CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(1), aScriptDigest.getLength());
1234 pStream = utl::UcbStreamHelper::CreateStream(xStream, true);
1235 pXmlDoc = parseXmlStream(pStream.get());
1236 // Without the accompanying fix in place, this test would have failed, the macro signature was
1237 // not part of the signed data of the document signature.
1238 assertXPath(pXmlDoc,
1239 "/odfds:document-signatures/dsig:Signature[1]/dsig:SignedInfo/"
1240 "dsig:Reference[@URI='META-INF/macrosignatures.xml']",
1244 #if HAVE_FEATURE_GPGVERIFY
1245 /// Test a typical ODF where all streams are GPG-signed.
1246 CPPUNIT_TEST_FIXTURE(SigningTest, testODFGoodGPG)
1248 loadFromFile(u"goodGPG.odt");
1249 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
1250 CPPUNIT_ASSERT(pBaseModel);
1251 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
1252 CPPUNIT_ASSERT(pObjectShell);
1253 // Our local gpg config fully trusts the signing cert, so in
1254 // contrast to the X509 test we can fail on NOTVALIDATED here
1255 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
1256 CPPUNIT_ASSERT_EQUAL_MESSAGE((OString::number(o3tl::to_underlying(nActual)).getStr()),
1257 SignatureState::OK, nActual);
1260 /// Test a typical ODF where all streams are GPG-signed, but we don't trust the signature.
1261 CPPUNIT_TEST_FIXTURE(SigningTest, testODFUntrustedGoodGPG)
1263 loadFromFile(u"untrustedGoodGPG.odt");
1264 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
1265 CPPUNIT_ASSERT(pBaseModel);
1266 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
1267 CPPUNIT_ASSERT(pObjectShell);
1268 // Our local gpg config does _not_ trust the signing cert, so in
1269 // contrast to the X509 test we can fail everything but
1270 // NOTVALIDATED here
1271 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
1272 CPPUNIT_ASSERT_EQUAL_MESSAGE((OString::number(o3tl::to_underlying(nActual)).getStr()),
1273 SignatureState::NOTVALIDATED, nActual);
1276 CPPUNIT_TEST_FIXTURE(SigningTest, testInvalidZIP)
1278 // set RepairPackage via interaction handler, same as soffice does
1279 // - if it's passed to load the behavior is different, oddly enough.
1280 std::vector<::ucbhelper::InterceptedInteraction::InterceptedRequest> interceptions{
1281 { css::uno::Any(css::document::BrokenPackageRequest()),
1282 cppu::UnoType<css::task::XInteractionApprove>::get(), 0 },
1284 ::rtl::Reference<ucbhelper::InterceptedInteraction> pIH(new ucbhelper::InterceptedInteraction);
1285 pIH->setInterceptions(std::move(interceptions));
1287 uno::Sequence<beans::PropertyValue> args = { comphelper::makePropertyValue(
1288 u"InteractionHandler"_ustr, uno::Reference<task::XInteractionHandler>(pIH)) };
1289 loadWithParams(createFileURL(u"signature-forgery-cdh-lfh.docx"), args);
1290 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
1291 CPPUNIT_ASSERT(pBaseModel);
1292 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
1293 CPPUNIT_ASSERT(pObjectShell);
1294 // the problem was that the document Zip structure is interpreted
1295 // misleadingly in RepairPackage case, but signature was still returned
1296 // as partially valid.
1297 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN),
1298 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
1301 /// Test a typical broken ODF signature where one stream is corrupted.
1302 CPPUNIT_TEST_FIXTURE(SigningTest, testODFBrokenStreamGPG)
1304 loadFromFile(u"badStreamGPG.odt");
1305 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
1306 CPPUNIT_ASSERT(pBaseModel);
1307 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
1308 CPPUNIT_ASSERT(pObjectShell);
1309 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN),
1310 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
1313 /// Test a typical broken ODF signature where the XML dsig hash is corrupted.
1314 CPPUNIT_TEST_FIXTURE(SigningTest, testODFBrokenDsigGPG)
1316 loadFromFile(u"badDsigGPG.odt");
1317 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
1318 CPPUNIT_ASSERT(pBaseModel);
1319 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
1320 CPPUNIT_ASSERT(pObjectShell);
1321 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN),
1322 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
1325 #if HAVE_GPGCONF_SOCKETDIR
1327 /// Test loading an encrypted ODF document
1328 CPPUNIT_TEST_FIXTURE(SigningTest, testODFEncryptedGPG)
1330 // ODF1.2 + loext flavour
1331 loadFromFile(u"encryptedGPG.odt");
1332 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
1333 CPPUNIT_ASSERT(pBaseModel);
1334 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
1335 CPPUNIT_ASSERT(pObjectShell);
1337 // ODF1.3 flavour
1338 loadFromFile(u"encryptedGPG_odf13.odt");
1339 pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
1340 CPPUNIT_ASSERT(pBaseModel);
1341 pObjectShell = pBaseModel->GetObjectShell();
1342 CPPUNIT_ASSERT(pObjectShell);
1344 // export and import again
1345 saveAndReload(u"writer8"_ustr);
1347 pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
1348 CPPUNIT_ASSERT(pBaseModel);
1349 pObjectShell = pBaseModel->GetObjectShell();
1350 CPPUNIT_ASSERT(pObjectShell);
1353 #endif
1355 SfxObjectShell* SigningTest::assertDocument(const ::CppUnit::SourceLine aSrcLine,
1356 const OUString& rFilterName,
1357 const SignatureState nDocSign,
1358 const SignatureState nMacroSign,
1359 const OUString& sVersion)
1361 std::string sPos = aSrcLine.fileName() + ":" + OString::number(aSrcLine.lineNumber()).getStr();
1363 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
1364 CPPUNIT_ASSERT_MESSAGE(sPos, pBaseModel);
1365 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
1366 CPPUNIT_ASSERT_MESSAGE(sPos, pObjectShell);
1368 CPPUNIT_ASSERT_EQUAL_MESSAGE(sPos, rFilterName,
1369 pObjectShell->GetMedium()->GetFilter()->GetFilterName());
1370 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
1371 CPPUNIT_ASSERT_EQUAL_MESSAGE(sPos, nDocSign, nActual);
1372 nActual = pObjectShell->GetScriptingSignatureState();
1373 CPPUNIT_ASSERT_EQUAL_MESSAGE(sPos, nMacroSign, nActual);
1375 OUString aODFVersion;
1376 uno::Reference<beans::XPropertySet> xPropSet(pObjectShell->GetStorage(), uno::UNO_QUERY_THROW);
1377 xPropSet->getPropertyValue(u"Version"_ustr) >>= aODFVersion;
1378 CPPUNIT_ASSERT_EQUAL(sVersion, aODFVersion);
1380 return pObjectShell;
1383 /// Test if a macro signature from a OTT 1.2 template is preserved for ODT 1.2
1384 CPPUNIT_TEST_FIXTURE(SigningTest, testPreserveMacroTemplateSignature12_ODF)
1386 const OUString aFormats[] = { u"writer8"_ustr, u"writer8_template"_ustr };
1388 for (OUString const& sFormat : aFormats)
1390 const OUString aURL(createFileURL(u"tdf42316_odt12.ott"));
1391 const OUString sLoadMessage = "loading failed: " + aURL;
1393 // load the template as-is to validate signatures
1394 loadWithParams(aURL,
1395 comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1397 // we are a template, and have a valid document and macro signature
1398 assertDocument(CPPUNIT_SOURCELINE(), u"writer8_template"_ustr, SignatureState::OK,
1399 SignatureState::OK, ODFVER_012_TEXT);
1401 // create new document from template
1402 loadFromURL(aURL);
1403 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage, RTL_TEXTENCODING_UTF8).getStr(),
1404 mxComponent.is());
1406 // we are somehow a template (?), and have just a valid macro signature
1407 assertDocument(CPPUNIT_SOURCELINE(), u"writer8_template"_ustr, SignatureState::NOSIGNATURES,
1408 SignatureState::OK, ODFVER_012_TEXT);
1410 // FIXME: Error: element "document-signatures" is missing "version" attribute
1411 skipValidation();
1413 if (sFormat == "writer8")
1414 // save as new ODT document
1415 saveAndReload(sFormat);
1416 else
1418 // save as new OTT template
1419 save(u"writer8_template"_ustr);
1421 // load the saved OTT template as-is to validate signatures
1422 loadWithParams(maTempFile.GetURL(),
1423 comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1426 // the loaded document is a OTT/ODT with a macro signature
1427 assertDocument(CPPUNIT_SOURCELINE(), sFormat, SignatureState::NOSIGNATURES,
1428 SignatureState::OK, ODFVER_014_TEXT);
1430 // save as new OTT template
1431 save(u"writer8_template"_ustr);
1433 // load the template as-is to validate signatures
1434 loadWithParams(maTempFile.GetURL(),
1435 comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1437 // the loaded document is a OTT with a valid macro signature
1438 assertDocument(CPPUNIT_SOURCELINE(), u"writer8_template"_ustr, SignatureState::NOSIGNATURES,
1439 SignatureState::OK, ODFVER_014_TEXT);
1443 /// Test if a macro signature from an OTT 1.0 is dropped for ODT 1.2
1444 CPPUNIT_TEST_FIXTURE(SigningTest, testDropMacroTemplateSignature)
1446 const OUString aURL(createFileURL(u"tdf42316.ott"));
1447 const OUString sLoadMessage = "loading failed: " + aURL;
1449 // load the template as-is to validate signatures
1450 loadWithParams(aURL, comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1452 // we are a template, and have a non-invalid macro signature
1453 assertDocument(CPPUNIT_SOURCELINE(), u"writer8_template"_ustr, SignatureState::NOSIGNATURES,
1454 SignatureState::NOTVALIDATED, OUString());
1456 // create new document from template
1457 loadFromURL(aURL);
1458 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage, RTL_TEXTENCODING_UTF8).getStr(),
1459 mxComponent.is());
1461 // we are somehow a template (?), and have just a valid macro signature
1462 assertDocument(CPPUNIT_SOURCELINE(), u"writer8_template"_ustr, SignatureState::NOSIGNATURES,
1463 SignatureState::NOTVALIDATED, OUString());
1465 // save as new ODT document
1466 saveAndReload(u"writer8"_ustr);
1468 // the loaded document is a 1.2 ODT without any signatures
1469 assertDocument(CPPUNIT_SOURCELINE(), u"writer8"_ustr, SignatureState::NOSIGNATURES,
1470 SignatureState::NOSIGNATURES, ODFVER_014_TEXT);
1472 // load the template as-is to validate signatures
1473 loadWithParams(aURL, comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1475 // we are a template, and have a non-invalid macro signature
1476 assertDocument(CPPUNIT_SOURCELINE(), u"writer8_template"_ustr, SignatureState::NOSIGNATURES,
1477 SignatureState::NOTVALIDATED, OUString());
1479 // save as new OTT template
1480 save(u"writer8_template"_ustr);
1482 // load the template as-is to validate signatures
1483 loadWithParams(maTempFile.GetURL(),
1484 comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1486 // the loaded document is a 1.2 OTT without any signatures
1487 assertDocument(CPPUNIT_SOURCELINE(), u"writer8_template"_ustr, SignatureState::NOSIGNATURES,
1488 SignatureState::NOSIGNATURES, ODFVER_014_TEXT);
1491 /// Test if a macro signature from a OTT 1.0 template is preserved for ODT 1.0
1492 CPPUNIT_TEST_FIXTURE(SigningTest, testPreserveMacroTemplateSignature10)
1494 // set ODF version 1.0 / 1.1 as default
1495 Resetter resetter([]() { SetODFDefaultVersion(SvtSaveOptions::ODFVER_LATEST); });
1496 SetODFDefaultVersion(SvtSaveOptions::ODFVER_011);
1498 const OUString aFormats[] = { u"writer8"_ustr, u"writer8_template"_ustr };
1500 for (OUString const& sFormat : aFormats)
1502 const OUString aURL(createFileURL(u"tdf42316.ott"));
1503 const OUString sLoadMessage = "loading failed: " + aURL;
1505 // load the template as-is to validate signatures
1506 loadWithParams(aURL,
1507 comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1509 // we are a template, and have a non-invalid macro signature
1510 assertDocument(CPPUNIT_SOURCELINE(), u"writer8_template"_ustr, SignatureState::NOSIGNATURES,
1511 SignatureState::NOTVALIDATED, OUString());
1513 // create new document from template
1514 loadFromURL(aURL);
1515 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage, RTL_TEXTENCODING_UTF8).getStr(),
1516 mxComponent.is());
1518 // we are somehow a template (?), and have just a valid macro signature
1519 assertDocument(CPPUNIT_SOURCELINE(), u"writer8_template"_ustr, SignatureState::NOSIGNATURES,
1520 SignatureState::NOTVALIDATED, OUString());
1522 // FIXME: Error: element "manifest:manifest" is missing "version" attribute
1523 skipValidation();
1525 if (sFormat == "writer8")
1526 // save as new ODT document
1527 saveAndReload(sFormat);
1528 else
1530 // save as new OTT template
1531 save(u"writer8_template"_ustr);
1533 // load the saved OTT template as-is to validate signatures
1534 loadWithParams(maTempFile.GetURL(),
1535 comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1538 assertDocument(CPPUNIT_SOURCELINE(), sFormat, SignatureState::NOSIGNATURES,
1539 SignatureState::NOTVALIDATED, OUString());
1541 save(u"writer8_template"_ustr);
1543 // load the template as-is to validate signatures
1544 loadWithParams(maTempFile.GetURL(),
1545 comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1547 // the loaded document is a OTT with a non-invalid macro signature
1548 assertDocument(CPPUNIT_SOURCELINE(), u"writer8_template"_ustr, SignatureState::NOSIGNATURES,
1549 SignatureState::NOTVALIDATED, OUString());
1553 #endif
1555 void SigningTest::registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx)
1557 xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("odfds"),
1558 BAD_CAST("urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0"));
1559 xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("dsig"),
1560 BAD_CAST("http://www.w3.org/2000/09/xmldsig#"));
1561 xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("xd"), BAD_CAST("http://uri.etsi.org/01903/v1.3.2#"));
1564 CPPUNIT_PLUGIN_IMPLEMENT();
1566 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */