1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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/.
10 #include <config_crypto.h>
11 #include <config_features.h>
12 #include <config_gpgme.h>
14 #include <sal/config.h>
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
68 uno::Reference
<xml::crypto::XSEInitializer
> mxSEInitializer
;
69 uno::Reference
<xml::crypto::XXMLSecurityContext
> mxSecurityContext
;
73 virtual void setUp() override
;
74 virtual void tearDown() override
;
75 void registerNamespaces(xmlXPathContextPtr
& pXmlXpathCtx
) override
;
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
);
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());
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);
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
))
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())
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())
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
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())
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
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
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())
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();
282 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations
.size());
283 // This was SecurityOperationStatus_UNKNOWN, signing with an ECDSA key was
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())
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())
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())
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())
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];
426 std::none_of(rOverrides
.begin(), rOverrides
.end(), [](const beans::StringPair
& rPair
) {
427 return rPair
.First
.startsWith("/_xmlsignatures/sig");
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());
486 CPPUNIT_ASSERT_EQUAL(sal_Int32(20210126), infos
[0].SignatureDate
);
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
590 // oddly BCryptVerifySignature returns STATUS_INVALID_SIGNATURE
591 // while the same succeeds with NSS _SGN_VerifyPKCS1DigestInfo
592 || nActual
== SignatureState::BROKEN
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
614 CPPUNIT_ASSERT(xmlsecurity::EqualDistinguishedNames(msDN
, nssDN
, xmlsecurity::COMPAT_2ND
));
616 CPPUNIT_ASSERT(xmlsecurity::EqualDistinguishedNames(nssDN
, msDN
, xmlsecurity::COMPAT_2ND
));
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();
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();
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();
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()));
726 CPPUNIT_TEST_FIXTURE(SigningTest
, testPDFAddVisibleSignature
)
728 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
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.
738 // Given: copy the test document to a temporary file, as it'll be modified.
739 createTempCopy(u
"add-visible-signature.pdf");
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(),
764 xSelectionSupplier
->select(uno::Any(xShape
));
765 auto xEnv
= mxSecurityContext
->getSecurityEnvironment();
766 auto xCert
= GetValidCertificate(xEnv
->getPersonalCertificates(), xEnv
);
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:
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
);
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
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
);
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
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())
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);
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
),
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')]",
902 // New signature always has the Type attribute.
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())
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);
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
),
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.
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())
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())
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())
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
,
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
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
);
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
);
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
1403 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage
, RTL_TEXTENCODING_UTF8
).getStr(),
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
1413 if (sFormat
== "writer8")
1414 // save as new ODT document
1415 saveAndReload(sFormat
);
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
1458 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage
, RTL_TEXTENCODING_UTF8
).getStr(),
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
1515 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage
, RTL_TEXTENCODING_UTF8
).getStr(),
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
1525 if (sFormat
== "writer8")
1526 // save as new ODT document
1527 saveAndReload(sFormat
);
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());
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: */