1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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 */
9
10 #include <config_features.h>
11 #include <config_gpgme.h>
12
13 #include <sal/config.h>
14
15 #include <cstdlib>
16
17 #include <test/bootstrapfixture.hxx>
18 #include <unotest/macros_test.hxx>
19 #include <test/xmltesttools.hxx>
20
21 #include <com/sun/star/beans/XPropertySet.hpp>
22 #include <com/sun/star/document/MacroExecMode.hpp>
23 #include <com/sun/star/embed/XStorage.hpp>
24 #include <com/sun/star/embed/XTransactedObject.hpp>
25 #include <com/sun/star/frame/Desktop.hpp>
26 #include <com/sun/star/frame/XStorable.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
31 #include <comphelper/processfactory.hxx>
32 #include <comphelper/propertysequence.hxx>
33 #include <unotools/mediadescriptor.hxx>
34 #include <unotools/tempfile.hxx>
35 #include <unotools/ucbstreamhelper.hxx>
36 #include <comphelper/storagehelper.hxx>
37 #include <sfx2/sfxbasemodel.hxx>
38 #include <sfx2/objsh.hxx>
39 #include <osl/file.hxx>
40 #include <osl/process.h>
41 #include <osl/thread.hxx>
42 #include <comphelper/ofopxmlhelper.hxx>
43 #include <unotools/streamwrap.hxx>
44
45 #include <documentsignaturehelper.hxx>
46 #include <xmlsignaturehelper.hxx>
47 #include <documentsignaturemanager.hxx>
48 #include <certificate.hxx>
49 #include <xsecctl.hxx>
50 #include <sfx2/docfile.hxx>
51 #include <sfx2/docfilt.hxx>
52 #include <officecfg/Office/Common.hxx>
53 #include <comphelper/configuration.hxx>
54
55 using namespace com::sun::star;
56
57 namespace
58 {
59 char const DATA_DIRECTORY[] = "/xmlsecurity/qa/unit/signing/data/";
60 }
61
62 /// Testsuite for the document signing feature.
63 class SigningTest : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools
64 {
65 protected:
66 uno::Reference<uno::XComponentContext> mxComponentContext;
67 uno::Reference<lang::XComponent> mxComponent;
68 uno::Reference<xml::crypto::XSEInitializer> mxSEInitializer;
69 uno::Reference<xml::crypto::XXMLSecurityContext> mxSecurityContext;
70
71 #if HAVE_GPGCONF_SOCKETDIR
72 OString m_gpgconfCommandPrefix;
73 #endif
74
75 public:
76 SigningTest();
77 virtual void setUp() override;
78 virtual void tearDown() override;
79 void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) override;
80
81 protected:
82 void createDoc(const OUString& rURL);
83 void createCalc(const OUString& rURL);
84 uno::Reference<security::XCertificate>
85 getCertificate(DocumentSignatureManager& rSignatureManager,
86 svl::crypto::SignatureMethodAlgorithm eAlgo);
87 #if HAVE_FEATURE_GPGVERIFY
88 SfxObjectShell* assertDocument(const ::CppUnit::SourceLine aSrcLine,
89 const OUString& rFilterName, const SignatureState nDocSign,
90 const SignatureState nMacroSign, const OUString& sVersion);
91 #endif
92 };
93
SigningTest()94 SigningTest::SigningTest() {}
95
setUp()96 void SigningTest::setUp()
97 {
98 test::BootstrapFixture::setUp();
99
100 OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
101 OUString aTargetDir
102 = m_directories.getURLFromWorkdir("CppunitTest/xmlsecurity_signing.test.user");
103
104 // Set up cert8.db in workdir/CppunitTest/
105 osl::File::copy(aSourceDir + "cert8.db", aTargetDir + "/cert8.db");
106 osl::File::copy(aSourceDir + "key3.db", aTargetDir + "/key3.db");
107
108 // Make gpg use our own defined setup & keys
109 osl::File::copy(aSourceDir + "pubring.gpg", aTargetDir + "/pubring.gpg");
110 osl::File::copy(aSourceDir + "random_seed", aTargetDir + "/random_seed");
111 osl::File::copy(aSourceDir + "secring.gpg", aTargetDir + "/secring.gpg");
112 osl::File::copy(aSourceDir + "trustdb.gpg", aTargetDir + "/trustdb.gpg");
113
114 OUString aTargetPath;
115 osl::FileBase::getSystemPathFromFileURL(aTargetDir, aTargetPath);
116
117 OUString mozCertVar("MOZILLA_CERTIFICATE_FOLDER");
118 osl_setEnvironment(mozCertVar.pData, aTargetPath.pData);
119 OUString gpgHomeVar("GNUPGHOME");
120 osl_setEnvironment(gpgHomeVar.pData, aTargetPath.pData);
121
122 #if HAVE_GPGCONF_SOCKETDIR
123 auto const ldPath = std::getenv("LIBO_LD_PATH");
124 m_gpgconfCommandPrefix
125 = ldPath == nullptr ? OString() : OStringLiteral("LD_LIBRARY_PATH=") + ldPath + " ";
126 OString path;
127 bool ok = aTargetPath.convertToString(&path, osl_getThreadTextEncoding(),
128 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
129 | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR);
130 // if conversion fails, at least provide a best-effort conversion in the message here, for
131 // context
132 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(aTargetPath, RTL_TEXTENCODING_UTF8).getStr(), ok);
133 m_gpgconfCommandPrefix += "GNUPGHOME=" + path + " " GPGME_GPGCONF;
134 // HAVE_GPGCONF_SOCKETDIR is only defined in configure.ac for Linux for now, so (a) std::system
135 // behavior will conform to POSIX (and the relevant env var to set is named LD_LIBRARY_PATH), and
136 // (b) gpgconf --create-socketdir should return zero:
137 OString cmd = m_gpgconfCommandPrefix + " --create-socketdir";
138 int res = std::system(cmd.getStr());
139 CPPUNIT_ASSERT_EQUAL_MESSAGE(cmd.getStr(), 0, res);
140 #endif
141
142 // Initialize crypto after setting up the environment variables.
143 mxComponentContext.set(comphelper::getComponentContext(getMultiServiceFactory()));
144 mxDesktop.set(frame::Desktop::create(mxComponentContext));
145 mxSEInitializer = xml::crypto::SEInitializer::create(mxComponentContext);
146 mxSecurityContext = mxSEInitializer->createSecurityContext(OUString());
147 }
148
tearDown()149 void SigningTest::tearDown()
150 {
151 if (mxComponent.is())
152 mxComponent->dispose();
153
154 #if HAVE_GPGCONF_SOCKETDIR
155 // HAVE_GPGCONF_SOCKETDIR is only defined in configure.ac for Linux for now, so (a) std::system
156 // behavior will conform to POSIX, and (b) gpgconf --remove-socketdir should return zero:
157 OString cmd = m_gpgconfCommandPrefix + " --remove-socketdir";
158 int res = std::system(cmd.getStr());
159 CPPUNIT_ASSERT_EQUAL_MESSAGE(cmd.getStr(), 0, res);
160 #endif
161
162 test::BootstrapFixture::tearDown();
163 }
164
createDoc(const OUString & rURL)165 void SigningTest::createDoc(const OUString& rURL)
166 {
167 if (mxComponent.is())
168 mxComponent->dispose();
169 if (rURL.isEmpty())
170 mxComponent = loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument");
171 else
172 mxComponent = loadFromDesktop(rURL, "com.sun.star.text.TextDocument");
173 }
174
createCalc(const OUString & rURL)175 void SigningTest::createCalc(const OUString& rURL)
176 {
177 if (mxComponent.is())
178 mxComponent->dispose();
179 if (rURL.isEmpty())
180 mxComponent
181 = loadFromDesktop("private:factory/swriter", "com.sun.star.sheet.SpreadsheetDocument");
182 else
183 mxComponent = loadFromDesktop(rURL, "com.sun.star.sheet.SpreadsheetDocument");
184 }
185
186 uno::Reference<security::XCertificate>
getCertificate(DocumentSignatureManager & rSignatureManager,svl::crypto::SignatureMethodAlgorithm eAlgo)187 SigningTest::getCertificate(DocumentSignatureManager& rSignatureManager,
188 svl::crypto::SignatureMethodAlgorithm eAlgo)
189 {
190 uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment
191 = rSignatureManager.getSecurityEnvironment();
192 const uno::Sequence<uno::Reference<security::XCertificate>> aCertificates
193 = xSecurityEnvironment->getPersonalCertificates();
194
195 for (const auto& xCertificate : aCertificates)
196 {
197 auto pCertificate = dynamic_cast<xmlsecurity::Certificate*>(xCertificate.get());
198 CPPUNIT_ASSERT(pCertificate);
199 if (pCertificate->getSignatureMethodAlgorithm() == eAlgo)
200 return xCertificate;
201 }
202 return uno::Reference<security::XCertificate>();
203 }
204
CPPUNIT_TEST_FIXTURE(SigningTest,testDescription)205 CPPUNIT_TEST_FIXTURE(SigningTest, testDescription)
206 {
207 // Create an empty document and store it to a tempfile, finally load it as a storage.
208 createDoc("");
209
210 utl::TempFile aTempFile;
211 aTempFile.EnableKillingFile();
212 uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
213 utl::MediaDescriptor aMediaDescriptor;
214 aMediaDescriptor["FilterName"] <<= OUString("writer8");
215 xStorable->storeAsURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
216
217 DocumentSignatureManager aManager(mxComponentContext, DocumentSignatureMode::Content);
218 CPPUNIT_ASSERT(aManager.init());
219 uno::Reference<embed::XStorage> xStorage
220 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
221 ZIP_STORAGE_FORMAT_STRING, aTempFile.GetURL(), embed::ElementModes::READWRITE);
222 CPPUNIT_ASSERT(xStorage.is());
223 aManager.setStore(xStorage);
224 aManager.getSignatureHelper().SetStorage(xStorage, "1.2");
225
226 // Then add a signature document.
227 uno::Reference<security::XCertificate> xCertificate
228 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA);
229 if (!xCertificate.is())
230 return;
231 OUString aDescription("SigningTest::testDescription");
232 sal_Int32 nSecurityId;
233 aManager.add(xCertificate, mxSecurityContext, aDescription, nSecurityId, false);
234
235 // Read back the signature and make sure that the description survives the roundtrip.
236 aManager.read(/*bUseTempStream=*/true);
237 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
238 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
239 CPPUNIT_ASSERT_EQUAL(aDescription, rInformations[0].ouDescription);
240 }
241
CPPUNIT_TEST_FIXTURE(SigningTest,testECDSA)242 CPPUNIT_TEST_FIXTURE(SigningTest, testECDSA)
243 {
244 // Create an empty document and store it to a tempfile, finally load it as a storage.
245 createDoc("");
246
247 utl::TempFile aTempFile;
248 aTempFile.EnableKillingFile();
249 uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
250 utl::MediaDescriptor aMediaDescriptor;
251 aMediaDescriptor["FilterName"] <<= OUString("writer8");
252 xStorable->storeAsURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
253
254 DocumentSignatureManager aManager(mxComponentContext, DocumentSignatureMode::Content);
255 CPPUNIT_ASSERT(aManager.init());
256 uno::Reference<embed::XStorage> xStorage
257 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
258 ZIP_STORAGE_FORMAT_STRING, aTempFile.GetURL(), embed::ElementModes::READWRITE);
259 CPPUNIT_ASSERT(xStorage.is());
260 aManager.setStore(xStorage);
261 aManager.getSignatureHelper().SetStorage(xStorage, "1.2");
262
263 // Then add a signature.
264 uno::Reference<security::XCertificate> xCertificate
265 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::ECDSA);
266 if (!xCertificate.is())
267 return;
268 OUString aDescription;
269 sal_Int32 nSecurityId;
270 aManager.add(xCertificate, mxSecurityContext, aDescription, nSecurityId, false);
271
272 // Read back the signature and make sure that it's valid.
273 aManager.read(/*bUseTempStream=*/true);
274 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
275 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
276 // This was SecurityOperationStatus_UNKNOWN, signing with an ECDSA key was
277 // broken.
278 CPPUNIT_ASSERT_EQUAL(css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED,
279 rInformations[0].nStatus);
280 }
281
CPPUNIT_TEST_FIXTURE(SigningTest,testECDSAOOXML)282 CPPUNIT_TEST_FIXTURE(SigningTest, testECDSAOOXML)
283 {
284 // Create an empty document and store it to a tempfile, finally load it as a storage.
285 createDoc("");
286
287 utl::TempFile aTempFile;
288 aTempFile.EnableKillingFile();
289 uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
290 utl::MediaDescriptor aMediaDescriptor;
291 aMediaDescriptor["FilterName"] <<= OUString("MS Word 2007 XML");
292 xStorable->storeAsURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
293
294 DocumentSignatureManager aManager(mxComponentContext, DocumentSignatureMode::Content);
295 CPPUNIT_ASSERT(aManager.init());
296 uno::Reference<embed::XStorage> xStorage
297 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
298 ZIP_STORAGE_FORMAT_STRING, aTempFile.GetURL(), embed::ElementModes::READWRITE);
299 CPPUNIT_ASSERT(xStorage.is());
300 aManager.setStore(xStorage);
301 aManager.getSignatureHelper().SetStorage(xStorage, "1.2");
302
303 // Then add a document signature.
304 uno::Reference<security::XCertificate> xCertificate
305 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::ECDSA);
306 if (!xCertificate.is())
307 return;
308 OUString aDescription;
309 sal_Int32 nSecurityId;
310 aManager.add(xCertificate, mxSecurityContext, aDescription, nSecurityId,
311 /*bAdESCompliant=*/false);
312
313 // Read back the signature and make sure that it's valid.
314 aManager.read(/*bUseTempStream=*/true);
315 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
316 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
317 // This was SecurityOperationStatus_UNKNOWN, signing with an ECDSA key was
318 // broken.
319 CPPUNIT_ASSERT_EQUAL(css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED,
320 rInformations[0].nStatus);
321 }
322
CPPUNIT_TEST_FIXTURE(SigningTest,testECDSAPDF)323 CPPUNIT_TEST_FIXTURE(SigningTest, testECDSAPDF)
324 {
325 // Create an empty document and store it to a tempfile, finally load it as
326 // a stream.
327 createDoc("");
328
329 utl::TempFile aTempFile;
330 aTempFile.EnableKillingFile();
331 uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
332 utl::MediaDescriptor aMediaDescriptor;
333 aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
334 xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
335
336 DocumentSignatureManager aManager(mxComponentContext, DocumentSignatureMode::Content);
337 CPPUNIT_ASSERT(aManager.init());
338 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(
339 aTempFile.GetURL(), StreamMode::READ | StreamMode::WRITE));
340 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
341 CPPUNIT_ASSERT(xStream.is());
342 aManager.setSignatureStream(xStream);
343
344 // Then add a document signature.
345 uno::Reference<security::XCertificate> xCertificate
346 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::ECDSA);
347 if (!xCertificate.is())
348 return;
349 OUString aDescription;
350 sal_Int32 nSecurityId;
351 aManager.add(xCertificate, mxSecurityContext, aDescription, nSecurityId,
352 /*bAdESCompliant=*/true);
353
354 // Read back the signature and make sure that it's valid.
355 aManager.read(/*bUseTempStream=*/false);
356 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
357 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
358 // This was SecurityOperationStatus_UNKNOWN, signing with an ECDSA key was
359 // broken.
360 CPPUNIT_ASSERT_EQUAL(css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED,
361 rInformations[0].nStatus);
362 }
363
CPPUNIT_TEST_FIXTURE(SigningTest,testOOXMLDescription)364 CPPUNIT_TEST_FIXTURE(SigningTest, testOOXMLDescription)
365 {
366 // Create an empty document and store it to a tempfile, finally load it as a storage.
367 createDoc("");
368
369 utl::TempFile aTempFile;
370 aTempFile.EnableKillingFile();
371 uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
372 utl::MediaDescriptor aMediaDescriptor;
373 aMediaDescriptor["FilterName"] <<= OUString("MS Word 2007 XML");
374 xStorable->storeAsURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
375
376 DocumentSignatureManager aManager(mxComponentContext, DocumentSignatureMode::Content);
377 CPPUNIT_ASSERT(aManager.init());
378 uno::Reference<embed::XStorage> xStorage
379 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
380 ZIP_STORAGE_FORMAT_STRING, aTempFile.GetURL(), embed::ElementModes::READWRITE);
381 CPPUNIT_ASSERT(xStorage.is());
382 aManager.setStore(xStorage);
383 aManager.getSignatureHelper().SetStorage(xStorage, "1.2");
384
385 // Then add a document signature.
386 uno::Reference<security::XCertificate> xCertificate
387 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA);
388 if (!xCertificate.is())
389 return;
390 OUString aDescription("SigningTest::testDescription");
391 sal_Int32 nSecurityId;
392 aManager.add(xCertificate, mxSecurityContext, aDescription, nSecurityId, false);
393
394 // Read back the signature and make sure that the description survives the roundtrip.
395 aManager.read(/*bUseTempStream=*/true);
396 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
397 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
398 CPPUNIT_ASSERT_EQUAL(aDescription, rInformations[0].ouDescription);
399 }
400
401 /// Test appending a new signature next to an existing one.
CPPUNIT_TEST_FIXTURE(SigningTest,testOOXMLAppend)402 CPPUNIT_TEST_FIXTURE(SigningTest, testOOXMLAppend)
403 {
404 // Copy the test document to a temporary file, as it'll be modified.
405 utl::TempFile aTempFile;
406 aTempFile.EnableKillingFile();
407 OUString aURL = aTempFile.GetURL();
408 CPPUNIT_ASSERT_EQUAL(
409 osl::File::RC::E_None,
410 osl::File::copy(m_directories.getURLFromSrc(DATA_DIRECTORY) + "partial.docx", aURL));
411 // Load the test document as a storage and read its single signature.
412 DocumentSignatureManager aManager(mxComponentContext, DocumentSignatureMode::Content);
413 CPPUNIT_ASSERT(aManager.init());
414 uno::Reference<embed::XStorage> xStorage
415 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(ZIP_STORAGE_FORMAT_STRING, aURL,
416 embed::ElementModes::READWRITE);
417 CPPUNIT_ASSERT(xStorage.is());
418 aManager.setStore(xStorage);
419 aManager.getSignatureHelper().SetStorage(xStorage, "1.2");
420 aManager.read(/*bUseTempStream=*/false);
421 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
422 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
423
424 // Then add a second document signature.
425 uno::Reference<security::XCertificate> xCertificate
426 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA);
427 if (!xCertificate.is())
428 return;
429 sal_Int32 nSecurityId;
430 aManager.add(xCertificate, mxSecurityContext, OUString(), nSecurityId, false);
431
432 // Read back the signatures and make sure that we have the expected amount.
433 aManager.read(/*bUseTempStream=*/true);
434 // This was 1: the original signature was lost.
435 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), rInformations.size());
436 }
437
438 /// Test removing a signature from existing ones.
CPPUNIT_TEST_FIXTURE(SigningTest,testOOXMLRemove)439 CPPUNIT_TEST_FIXTURE(SigningTest, testOOXMLRemove)
440 {
441 // Load the test document as a storage and read its signatures: purpose1 and purpose2.
442 DocumentSignatureManager aManager(mxComponentContext, DocumentSignatureMode::Content);
443 CPPUNIT_ASSERT(aManager.init());
444 utl::TempFile aTempFile;
445 aTempFile.EnableKillingFile();
446 OUString aURL = aTempFile.GetURL();
447 CPPUNIT_ASSERT_EQUAL(
448 osl::File::RC::E_None,
449 osl::File::copy(m_directories.getURLFromSrc(DATA_DIRECTORY) + "multi.docx", aURL));
450 uno::Reference<embed::XStorage> xStorage
451 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(ZIP_STORAGE_FORMAT_STRING, aURL,
452 embed::ElementModes::READWRITE);
453 CPPUNIT_ASSERT(xStorage.is());
454 aManager.setStore(xStorage);
455 aManager.getSignatureHelper().SetStorage(xStorage, "1.2");
456 aManager.read(/*bUseTempStream=*/false);
457 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
458 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), rInformations.size());
459
460 // Then remove the last added signature.
461 uno::Reference<security::XCertificate> xCertificate
462 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA);
463 if (!xCertificate.is())
464 return;
465 aManager.remove(0);
466
467 // Read back the signatures and make sure that only purpose1 is left.
468 aManager.read(/*bUseTempStream=*/true);
469 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
470 CPPUNIT_ASSERT_EQUAL(OUString("purpose1"), rInformations[0].ouDescription);
471 }
472
473 /// Test removing all signatures from a document.
CPPUNIT_TEST_FIXTURE(SigningTest,testOOXMLRemoveAll)474 CPPUNIT_TEST_FIXTURE(SigningTest, testOOXMLRemoveAll)
475 {
476 // Copy the test document to a temporary file, as it'll be modified.
477 utl::TempFile aTempFile;
478 aTempFile.EnableKillingFile();
479 OUString aURL = aTempFile.GetURL();
480 CPPUNIT_ASSERT_EQUAL(
481 osl::File::RC::E_None,
482 osl::File::copy(m_directories.getURLFromSrc(DATA_DIRECTORY) + "partial.docx", aURL));
483 // Load the test document as a storage and read its single signature.
484 DocumentSignatureManager aManager(mxComponentContext, DocumentSignatureMode::Content);
485 CPPUNIT_ASSERT(aManager.init());
486 uno::Reference<embed::XStorage> xStorage
487 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(ZIP_STORAGE_FORMAT_STRING, aURL,
488 embed::ElementModes::READWRITE);
489 CPPUNIT_ASSERT(xStorage.is());
490 aManager.setStore(xStorage);
491 aManager.getSignatureHelper().SetStorage(xStorage, "1.2");
492 aManager.read(/*bUseTempStream=*/false);
493 std::vector<SignatureInformation>& rInformations = aManager.getCurrentSignatureInformations();
494 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), rInformations.size());
495
496 // Then remove the only signature in the document.
497 uno::Reference<security::XCertificate> xCertificate
498 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA);
499 if (!xCertificate.is())
500 return;
501 aManager.remove(0);
502 aManager.read(/*bUseTempStream=*/true);
503 aManager.write(/*bXAdESCompliantIfODF=*/false);
504
505 // Make sure that the signature count is zero and the whole signature storage is removed completely.
506 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(0), rInformations.size());
507 CPPUNIT_ASSERT(!xStorage->hasByName("_xmlsignatures"));
508
509 // And that content types no longer contains signature types.
510 uno::Reference<io::XStream> xStream
511 = xStorage->openStreamElement("[Content_Types].xml", embed::ElementModes::READWRITE);
512 uno::Reference<io::XInputStream> xInputStream = xStream->getInputStream();
513 uno::Sequence<uno::Sequence<beans::StringPair>> aContentTypeInfo
514 = comphelper::OFOPXMLHelper::ReadContentTypeSequence(xInputStream, mxComponentContext);
515 uno::Sequence<beans::StringPair>& rOverrides = aContentTypeInfo[1];
516 CPPUNIT_ASSERT(
517 std::none_of(rOverrides.begin(), rOverrides.end(), [](const beans::StringPair& rPair) {
518 return rPair.First.startsWith("/_xmlsignatures/sig");
519 }));
520 }
521
522 /// Test a typical ODF where all streams are signed.
CPPUNIT_TEST_FIXTURE(SigningTest,testODFGood)523 CPPUNIT_TEST_FIXTURE(SigningTest, testODFGood)
524 {
525 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "good.odt");
526 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
527 CPPUNIT_ASSERT(pBaseModel);
528 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
529 CPPUNIT_ASSERT(pObjectShell);
530 // We expect NOTVALIDATED in case the root CA is not imported on the system, and OK otherwise, so accept both.
531 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
532 CPPUNIT_ASSERT_MESSAGE(
533 (OString::number(o3tl::underlyingEnumValue(nActual)).getStr()),
534 (nActual == SignatureState::NOTVALIDATED || nActual == SignatureState::OK));
535 }
536
537 /// Test a typical broken ODF signature where one stream is corrupted.
CPPUNIT_TEST_FIXTURE(SigningTest,testODFBroken)538 CPPUNIT_TEST_FIXTURE(SigningTest, testODFBroken)
539 {
540 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "bad.odt");
541 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
542 CPPUNIT_ASSERT(pBaseModel);
543 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
544 CPPUNIT_ASSERT(pObjectShell);
545 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN),
546 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
547 }
548
549 // Document has a signature stream, but no actual signatures.
CPPUNIT_TEST_FIXTURE(SigningTest,testODFNo)550 CPPUNIT_TEST_FIXTURE(SigningTest, testODFNo)
551 {
552 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "no.odt");
553 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
554 CPPUNIT_ASSERT(pBaseModel);
555 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
556 CPPUNIT_ASSERT(pObjectShell);
557 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::NOSIGNATURES),
558 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
559 }
560
561 /// Test a typical OOXML where a number of (but not all) streams are signed.
CPPUNIT_TEST_FIXTURE(SigningTest,testOOXMLPartial)562 CPPUNIT_TEST_FIXTURE(SigningTest, testOOXMLPartial)
563 {
564 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "partial.docx");
565 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
566 CPPUNIT_ASSERT(pBaseModel);
567 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
568 CPPUNIT_ASSERT(pObjectShell);
569 // This was SignatureState::BROKEN due to missing RelationshipTransform and SHA-256 support.
570 // We expect NOTVALIDATED_PARTIAL_OK in case the root CA is not imported on the system, and PARTIAL_OK otherwise, so accept both.
571 // But reject NOTVALIDATED, hiding incompleteness is not OK.
572 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
573 CPPUNIT_ASSERT_MESSAGE((OString::number(o3tl::underlyingEnumValue(nActual)).getStr()),
574 (nActual == SignatureState::NOTVALIDATED_PARTIAL_OK
575 || nActual == SignatureState::PARTIAL_OK));
576 }
577
578 /// Test a typical broken OOXML signature where one stream is corrupted.
CPPUNIT_TEST_FIXTURE(SigningTest,testOOXMLBroken)579 CPPUNIT_TEST_FIXTURE(SigningTest, testOOXMLBroken)
580 {
581 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "bad.docx");
582 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
583 CPPUNIT_ASSERT(pBaseModel);
584 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
585 CPPUNIT_ASSERT(pObjectShell);
586 // This was SignatureState::NOTVALIDATED/PARTIAL_OK as we did not validate manifest references.
587 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN),
588 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
589 }
590
591 #if HAVE_FEATURE_PDFIMPORT
592
593 /// Test a typical PDF where the signature is good.
CPPUNIT_TEST_FIXTURE(SigningTest,testPDFGood)594 CPPUNIT_TEST_FIXTURE(SigningTest, testPDFGood)
595 {
596 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "good.pdf");
597 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
598 CPPUNIT_ASSERT(pBaseModel);
599 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
600 CPPUNIT_ASSERT(pObjectShell);
601 // We expect NOTVALIDATED in case the root CA is not imported on the system, and OK otherwise, so accept both.
602 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
603 CPPUNIT_ASSERT_MESSAGE(
604 (OString::number(o3tl::underlyingEnumValue(nActual)).getStr()),
605 (nActual == SignatureState::NOTVALIDATED || nActual == SignatureState::OK));
606 }
607
608 /// Test a typical PDF where the signature is bad.
CPPUNIT_TEST_FIXTURE(SigningTest,testPDFBad)609 CPPUNIT_TEST_FIXTURE(SigningTest, testPDFBad)
610 {
611 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "bad.pdf");
612 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
613 CPPUNIT_ASSERT(pBaseModel);
614 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
615 CPPUNIT_ASSERT(pObjectShell);
616 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN),
617 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
618 }
619
CPPUNIT_TEST_FIXTURE(SigningTest,testPDFHideAndReplace)620 CPPUNIT_TEST_FIXTURE(SigningTest, testPDFHideAndReplace)
621 {
622 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY)
623 + "hide-and-replace-shadow-file-signed-2.pdf");
624 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
625 CPPUNIT_ASSERT(pBaseModel);
626 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
627 CPPUNIT_ASSERT(pObjectShell);
628 // Without the accompanying fix in place, this test would have failed with:
629 // - Expected: 2 (BROKEN)
630 // - Actual : 6 (NOTVALIDATED_PARTIAL_OK)
631 // i.e. a non-commenting update after a signature was not marked as invalid.
632 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN),
633 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
634 }
635
636 /// Test a typical PDF which is not signed.
CPPUNIT_TEST_FIXTURE(SigningTest,testPDFNo)637 CPPUNIT_TEST_FIXTURE(SigningTest, testPDFNo)
638 {
639 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "no.pdf");
640 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
641 CPPUNIT_ASSERT(pBaseModel);
642 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
643 CPPUNIT_ASSERT(pObjectShell);
644 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::NOSIGNATURES),
645 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
646 }
647
648 #endif
649
CPPUNIT_TEST_FIXTURE(SigningTest,test96097Calc)650 CPPUNIT_TEST_FIXTURE(SigningTest, test96097Calc)
651 {
652 createCalc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf96097.ods");
653 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
654 CPPUNIT_ASSERT_MESSAGE("Failed to access document base model", pBaseModel);
655
656 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
657 CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pObjectShell);
658
659 SignatureState nActual = pObjectShell->GetScriptingSignatureState();
660 CPPUNIT_ASSERT_MESSAGE((OString::number(o3tl::underlyingEnumValue(nActual)).getStr()),
661 (nActual == SignatureState::OK || nActual == SignatureState::NOTVALIDATED
662 || nActual == SignatureState::INVALID));
663
664 uno::Reference<frame::XStorable> xDocStorable(mxComponent, uno::UNO_QUERY_THROW);
665
666 // Save a copy
667 utl::TempFile aTempFileSaveCopy;
668 aTempFileSaveCopy.EnableKillingFile();
669 uno::Sequence<beans::PropertyValue> descSaveACopy(comphelper::InitPropertySequence(
670 { { "SaveACopy", uno::Any(true) }, { "FilterName", uno::Any(OUString("calc8")) } }));
671 xDocStorable->storeToURL(aTempFileSaveCopy.GetURL(), descSaveACopy);
672
673 try
674 {
675 // Save As
676 utl::TempFile aTempFileSaveAs;
677 aTempFileSaveAs.EnableKillingFile();
678 uno::Sequence<beans::PropertyValue> descSaveAs(
679 comphelper::InitPropertySequence({ { "FilterName", uno::Any(OUString("calc8")) } }));
680 xDocStorable->storeAsURL(aTempFileSaveAs.GetURL(), descSaveAs);
681 }
682 catch (...)
683 {
684 CPPUNIT_FAIL("Fail to save as the document");
685 }
686 }
687
CPPUNIT_TEST_FIXTURE(SigningTest,test96097Doc)688 CPPUNIT_TEST_FIXTURE(SigningTest, test96097Doc)
689 {
690 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf96097.odt");
691 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
692 CPPUNIT_ASSERT(pBaseModel);
693 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
694 CPPUNIT_ASSERT(pObjectShell);
695
696 SignatureState nActual = pObjectShell->GetScriptingSignatureState();
697 CPPUNIT_ASSERT_MESSAGE((OString::number(o3tl::underlyingEnumValue(nActual)).getStr()),
698 (nActual == SignatureState::OK || nActual == SignatureState::NOTVALIDATED
699 || nActual == SignatureState::INVALID));
700
701 uno::Reference<frame::XStorable> xDocStorable(mxComponent, uno::UNO_QUERY_THROW);
702
703 // Save a copy
704 utl::TempFile aTempFileSaveCopy;
705 aTempFileSaveCopy.EnableKillingFile();
706 uno::Sequence<beans::PropertyValue> descSaveACopy(comphelper::InitPropertySequence(
707 { { "SaveACopy", uno::Any(true) }, { "FilterName", uno::Any(OUString("writer8")) } }));
708 xDocStorable->storeToURL(aTempFileSaveCopy.GetURL(), descSaveACopy);
709
710 try
711 {
712 // Save As
713 utl::TempFile aTempFileSaveAs;
714 aTempFileSaveAs.EnableKillingFile();
715 uno::Sequence<beans::PropertyValue> descSaveAs(
716 comphelper::InitPropertySequence({ { "FilterName", uno::Any(OUString("writer8")) } }));
717 xDocStorable->storeAsURL(aTempFileSaveAs.GetURL(), descSaveAs);
718 }
719 catch (...)
720 {
721 CPPUNIT_FAIL("Fail to save as the document");
722 }
723 }
724
CPPUNIT_TEST_FIXTURE(SigningTest,testXAdESNotype)725 CPPUNIT_TEST_FIXTURE(SigningTest, testXAdESNotype)
726 {
727 // Create a working copy.
728 utl::TempFile aTempFile;
729 aTempFile.EnableKillingFile();
730 OUString aURL = aTempFile.GetURL();
731 CPPUNIT_ASSERT_EQUAL(
732 osl::File::RC::E_None,
733 osl::File::copy(m_directories.getURLFromSrc(DATA_DIRECTORY) + "notype-xades.odt", aURL));
734
735 // Read existing signature.
736 DocumentSignatureManager aManager(mxComponentContext, DocumentSignatureMode::Content);
737 CPPUNIT_ASSERT(aManager.init());
738 uno::Reference<embed::XStorage> xStorage
739 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
740 ZIP_STORAGE_FORMAT_STRING, aTempFile.GetURL(), embed::ElementModes::READWRITE);
741 CPPUNIT_ASSERT(xStorage.is());
742 aManager.setStore(xStorage);
743 aManager.getSignatureHelper().SetStorage(xStorage, "1.2");
744 aManager.read(/*bUseTempStream=*/false);
745
746 // Create a new signature.
747 uno::Reference<security::XCertificate> xCertificate
748 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA);
749 if (!xCertificate.is())
750 return;
751 sal_Int32 nSecurityId;
752 aManager.add(xCertificate, mxSecurityContext, /*rDescription=*/OUString(), nSecurityId,
753 /*bAdESCompliant=*/true);
754
755 // Write to storage.
756 aManager.read(/*bUseTempStream=*/true);
757 aManager.write(/*bXAdESCompliantIfODF=*/true);
758 uno::Reference<embed::XTransactedObject> xTransactedObject(xStorage, uno::UNO_QUERY);
759 xTransactedObject->commit();
760
761 // Parse the resulting XML.
762 uno::Reference<embed::XStorage> xMetaInf
763 = xStorage->openStorageElement("META-INF", embed::ElementModes::READ);
764 uno::Reference<io::XInputStream> xInputStream(
765 xMetaInf->openStreamElement("documentsignatures.xml", embed::ElementModes::READ),
766 uno::UNO_QUERY);
767 std::shared_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
768 xmlDocPtr pXmlDoc = parseXmlStream(pStream.get());
769
770 // Without the accompanying fix in place, this test would have failed with "unexpected 'Type'
771 // attribute", i.e. the signature without such an attribute was not preserved correctly.
772 assertXPathNoAttribute(pXmlDoc,
773 "/odfds:document-signatures/dsig:Signature[1]/dsig:SignedInfo/"
774 "dsig:Reference[@URI='#idSignedProperties']",
775 "Type");
776
777 // New signature always has the Type attribute.
778 assertXPath(pXmlDoc,
779 "/odfds:document-signatures/dsig:Signature[2]/dsig:SignedInfo/"
780 "dsig:Reference[@URI='#idSignedProperties']",
781 "Type", "http://uri.etsi.org/01903#SignedProperties");
782 }
783
784 /// Creates a XAdES signature from scratch.
CPPUNIT_TEST_FIXTURE(SigningTest,testXAdES)785 CPPUNIT_TEST_FIXTURE(SigningTest, testXAdES)
786 {
787 // Create an empty document, store it to a tempfile and load it as a storage.
788 createDoc(OUString());
789
790 utl::TempFile aTempFile;
791 aTempFile.EnableKillingFile();
792 uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
793 utl::MediaDescriptor aMediaDescriptor;
794 aMediaDescriptor["FilterName"] <<= OUString("writer8");
795 xStorable->storeAsURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
796
797 DocumentSignatureManager aManager(mxComponentContext, DocumentSignatureMode::Content);
798 CPPUNIT_ASSERT(aManager.init());
799 uno::Reference<embed::XStorage> xStorage
800 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
801 ZIP_STORAGE_FORMAT_STRING, aTempFile.GetURL(), embed::ElementModes::READWRITE);
802 CPPUNIT_ASSERT(xStorage.is());
803 aManager.setStore(xStorage);
804 aManager.getSignatureHelper().SetStorage(xStorage, "1.2");
805
806 // Create a signature.
807 uno::Reference<security::XCertificate> xCertificate
808 = getCertificate(aManager, svl::crypto::SignatureMethodAlgorithm::RSA);
809 if (!xCertificate.is())
810 return;
811 sal_Int32 nSecurityId;
812 aManager.add(xCertificate, mxSecurityContext, /*rDescription=*/OUString(), nSecurityId,
813 /*bAdESCompliant=*/true);
814
815 // Write to storage.
816 aManager.read(/*bUseTempStream=*/true);
817 aManager.write(/*bXAdESCompliantIfODF=*/true);
818 uno::Reference<embed::XTransactedObject> xTransactedObject(xStorage, uno::UNO_QUERY);
819 xTransactedObject->commit();
820
821 // Parse the resulting XML.
822 uno::Reference<embed::XStorage> xMetaInf
823 = xStorage->openStorageElement("META-INF", embed::ElementModes::READ);
824 uno::Reference<io::XInputStream> xInputStream(
825 xMetaInf->openStreamElement("documentsignatures.xml", embed::ElementModes::READ),
826 uno::UNO_QUERY);
827 std::shared_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
828 xmlDocPtr pXmlDoc = parseXmlStream(pStream.get());
829
830 // Assert that the digest algorithm is SHA-256 in the bAdESCompliant case, not SHA-1.
831 assertXPath(pXmlDoc,
832 "/odfds:document-signatures/dsig:Signature/dsig:SignedInfo/"
833 "dsig:Reference[@URI='content.xml']/dsig:DigestMethod",
834 "Algorithm", ALGO_XMLDSIGSHA256);
835
836 // Assert that the digest of the signing certificate is included.
837 assertXPath(pXmlDoc, "//xd:CertDigest", 1);
838
839 // Assert that the Type attribute on the idSignedProperties reference is
840 // not missing.
841 assertXPath(pXmlDoc,
842 "/odfds:document-signatures/dsig:Signature/dsig:SignedInfo/"
843 "dsig:Reference[@URI='#idSignedProperties']",
844 "Type", "http://uri.etsi.org/01903#SignedProperties");
845 }
846
847 /// Works with an existing good XAdES signature.
CPPUNIT_TEST_FIXTURE(SigningTest,testXAdESGood)848 CPPUNIT_TEST_FIXTURE(SigningTest, testXAdESGood)
849 {
850 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "good-xades.odt");
851 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
852 CPPUNIT_ASSERT(pBaseModel);
853 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
854 CPPUNIT_ASSERT(pObjectShell);
855 // We expect NOTVALIDATED in case the root CA is not imported on the system, and OK otherwise, so accept both.
856 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
857 CPPUNIT_ASSERT_MESSAGE(
858 (OString::number(o3tl::underlyingEnumValue(nActual)).getStr()),
859 (nActual == SignatureState::NOTVALIDATED || nActual == SignatureState::OK));
860 }
861
862 /// Test importing of signature line
CPPUNIT_TEST_FIXTURE(SigningTest,testSignatureLineOOXML)863 CPPUNIT_TEST_FIXTURE(SigningTest, testSignatureLineOOXML)
864 {
865 // Given: A document (docx) with a signature line and a valid signature
866 uno::Reference<security::XDocumentDigitalSignatures> xSignatures(
867 security::DocumentDigitalSignatures::createWithVersion(
868 comphelper::getProcessComponentContext(), "1.2"));
869
870 uno::Reference<embed::XStorage> xStorage
871 = comphelper::OStorageHelper::GetStorageOfFormatFromURL(
872 ZIP_STORAGE_FORMAT_STRING,
873 m_directories.getURLFromSrc(DATA_DIRECTORY) + "signatureline.docx",
874 embed::ElementModes::READ);
875 CPPUNIT_ASSERT(xStorage.is());
876
877 uno::Sequence<security::DocumentSignatureInformation> xSignatureInfo
878 = xSignatures->verifyScriptingContentSignatures(xStorage,
879 uno::Reference<io::XInputStream>());
880
881 // The signature should have a valid signature, and signature line with two valid images
882 CPPUNIT_ASSERT(xSignatureInfo[0].SignatureIsValid);
883 CPPUNIT_ASSERT_EQUAL(OUString("{DEE0514B-13E8-4674-A831-46E3CDB18BB4}"),
884 xSignatureInfo[0].SignatureLineId);
885 CPPUNIT_ASSERT(xSignatureInfo[0].ValidSignatureLineImage.is());
886 CPPUNIT_ASSERT(xSignatureInfo[0].InvalidSignatureLineImage.is());
887 }
888
CPPUNIT_TEST_FIXTURE(SigningTest,testSignatureLineODF)889 CPPUNIT_TEST_FIXTURE(SigningTest, testSignatureLineODF)
890 {
891 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "signatureline.odt");
892 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
893 CPPUNIT_ASSERT(pBaseModel);
894 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
895 CPPUNIT_ASSERT(pObjectShell);
896
897 uno::Sequence<security::DocumentSignatureInformation> xSignatureInfo
898 = pObjectShell->GetDocumentSignatureInformation(false);
899
900 CPPUNIT_ASSERT(xSignatureInfo[0].SignatureIsValid);
901 CPPUNIT_ASSERT_EQUAL(OUString("{41CF56EE-331B-4125-97D8-2F5669DD3AAC}"),
902 xSignatureInfo[0].SignatureLineId);
903 CPPUNIT_ASSERT(xSignatureInfo[0].ValidSignatureLineImage.is());
904 CPPUNIT_ASSERT(xSignatureInfo[0].InvalidSignatureLineImage.is());
905 }
906
907 #if HAVE_FEATURE_GPGVERIFY
908 /// Test a typical ODF where all streams are GPG-signed.
CPPUNIT_TEST_FIXTURE(SigningTest,testODFGoodGPG)909 CPPUNIT_TEST_FIXTURE(SigningTest, testODFGoodGPG)
910 {
911 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "goodGPG.odt");
912 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
913 CPPUNIT_ASSERT(pBaseModel);
914 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
915 CPPUNIT_ASSERT(pObjectShell);
916 // Our local gpg config fully trusts the signing cert, so in
917 // contrast to the X509 test we can fail on NOTVALIDATED here
918 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
919 CPPUNIT_ASSERT_EQUAL_MESSAGE((OString::number(o3tl::underlyingEnumValue(nActual)).getStr()),
920 SignatureState::OK, nActual);
921 }
922
923 /// Test a typical ODF where all streams are GPG-signed, but we don't trust the signature.
CPPUNIT_TEST_FIXTURE(SigningTest,testODFUntrustedGoodGPG)924 CPPUNIT_TEST_FIXTURE(SigningTest, testODFUntrustedGoodGPG)
925 {
926 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "untrustedGoodGPG.odt");
927 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
928 CPPUNIT_ASSERT(pBaseModel);
929 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
930 CPPUNIT_ASSERT(pObjectShell);
931 // Our local gpg config does _not_ trust the signing cert, so in
932 // contrast to the X509 test we can fail everything but
933 // NOTVALIDATED here
934 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
935 CPPUNIT_ASSERT_EQUAL_MESSAGE((OString::number(o3tl::underlyingEnumValue(nActual)).getStr()),
936 SignatureState::NOTVALIDATED, nActual);
937 }
938
939 /// Test a typical broken ODF signature where one stream is corrupted.
CPPUNIT_TEST_FIXTURE(SigningTest,testODFBrokenStreamGPG)940 CPPUNIT_TEST_FIXTURE(SigningTest, testODFBrokenStreamGPG)
941 {
942 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "badStreamGPG.odt");
943 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
944 CPPUNIT_ASSERT(pBaseModel);
945 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
946 CPPUNIT_ASSERT(pObjectShell);
947 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN),
948 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
949 }
950
951 /// Test a typical broken ODF signature where the XML dsig hash is corrupted.
CPPUNIT_TEST_FIXTURE(SigningTest,testODFBrokenDsigGPG)952 CPPUNIT_TEST_FIXTURE(SigningTest, testODFBrokenDsigGPG)
953 {
954 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "badDsigGPG.odt");
955 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
956 CPPUNIT_ASSERT(pBaseModel);
957 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
958 CPPUNIT_ASSERT(pObjectShell);
959 CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN),
960 static_cast<int>(pObjectShell->GetDocumentSignatureState()));
961 }
962
963 #if HAVE_GPGCONF_SOCKETDIR
964
965 /// Test loading an encrypted ODF document
CPPUNIT_TEST_FIXTURE(SigningTest,testODFEncryptedGPG)966 CPPUNIT_TEST_FIXTURE(SigningTest, testODFEncryptedGPG)
967 {
968 // ODF1.2 + loext flavour
969 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "encryptedGPG.odt");
970 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
971 CPPUNIT_ASSERT(pBaseModel);
972 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
973 CPPUNIT_ASSERT(pObjectShell);
974
975 // ODF1.3 flavour
976 createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "encryptedGPG_odf13.odt");
977 pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
978 CPPUNIT_ASSERT(pBaseModel);
979 pObjectShell = pBaseModel->GetObjectShell();
980 CPPUNIT_ASSERT(pObjectShell);
981 }
982
983 #endif
984
assertDocument(const::CppUnit::SourceLine aSrcLine,const OUString & rFilterName,const SignatureState nDocSign,const SignatureState nMacroSign,const OUString & sVersion)985 SfxObjectShell* SigningTest::assertDocument(const ::CppUnit::SourceLine aSrcLine,
986 const OUString& rFilterName,
987 const SignatureState nDocSign,
988 const SignatureState nMacroSign,
989 const OUString& sVersion)
990 {
991 std::string sPos = aSrcLine.fileName() + ":" + OString::number(aSrcLine.lineNumber()).getStr();
992
993 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
994 CPPUNIT_ASSERT_MESSAGE(sPos, pBaseModel);
995 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
996 CPPUNIT_ASSERT_MESSAGE(sPos, pObjectShell);
997
998 CPPUNIT_ASSERT_EQUAL_MESSAGE(sPos, rFilterName,
999 pObjectShell->GetMedium()->GetFilter()->GetFilterName());
1000 SignatureState nActual = pObjectShell->GetDocumentSignatureState();
1001 CPPUNIT_ASSERT_EQUAL_MESSAGE(sPos, nDocSign, nActual);
1002 nActual = pObjectShell->GetScriptingSignatureState();
1003 CPPUNIT_ASSERT_EQUAL_MESSAGE(sPos, nMacroSign, nActual);
1004
1005 OUString aODFVersion;
1006 uno::Reference<beans::XPropertySet> xPropSet(pObjectShell->GetStorage(), uno::UNO_QUERY_THROW);
1007 xPropSet->getPropertyValue("Version") >>= aODFVersion;
1008 CPPUNIT_ASSERT_EQUAL(sVersion, aODFVersion);
1009
1010 return pObjectShell;
1011 }
1012
1013 /// Test if a macro signature from a OTT 1.2 template is preserved for ODT 1.2
CPPUNIT_TEST_FIXTURE(SigningTest,testPreserveMacroTemplateSignature12_ODF)1014 CPPUNIT_TEST_FIXTURE(SigningTest, testPreserveMacroTemplateSignature12_ODF)
1015 {
1016 const OUString aURL(m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf42316_odt12.ott");
1017 const OUString sLoadMessage = "loading failed: " + aURL;
1018
1019 // load the template as-is to validate signatures
1020 mxComponent = loadFromDesktop(
1021 aURL, OUString(), comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1022 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage, RTL_TEXTENCODING_UTF8).getStr(),
1023 mxComponent.is());
1024
1025 // we are a template, and have a valid document and macro signature
1026 assertDocument(CPPUNIT_SOURCELINE(), "writer8_template", SignatureState::OK, SignatureState::OK,
1027 ODFVER_012_TEXT);
1028
1029 // create new document from template
1030 // we can't use createDoc / MacrosTest::loadFromDesktop, because ALWAYS_EXECUTE_NO_WARN
1031 // won't verify the signature for templates, so the resulting document won't be able to
1032 // preserve the templates signature.
1033 mxComponent->dispose();
1034 mxComponent = mxDesktop->loadComponentFromURL(
1035 aURL, "_default", 0,
1036 comphelper::InitPropertySequence(
1037 { { "MacroExecutionMode",
1038 uno::Any(document::MacroExecMode::FROM_LIST_AND_SIGNED_NO_WARN) } }));
1039 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage, RTL_TEXTENCODING_UTF8).getStr(),
1040 mxComponent.is());
1041
1042 // we are somehow a template (?), and have just a valid macro signature
1043 assertDocument(CPPUNIT_SOURCELINE(), "writer8_template", SignatureState::NOSIGNATURES,
1044 SignatureState::OK, ODFVER_012_TEXT);
1045
1046 // save as new ODT document
1047 utl::TempFile aTempFileSaveAsODT;
1048 aTempFileSaveAsODT.EnableKillingFile();
1049 try
1050 {
1051 uno::Reference<frame::XStorable> xDocStorable(mxComponent, uno::UNO_QUERY);
1052 uno::Sequence<beans::PropertyValue> descSaveAs(
1053 comphelper::InitPropertySequence({ { "FilterName", uno::Any(OUString("writer8")) } }));
1054 xDocStorable->storeAsURL(aTempFileSaveAsODT.GetURL(), descSaveAs);
1055 }
1056 catch (...)
1057 {
1058 CPPUNIT_FAIL("Failed to save ODT document");
1059 }
1060
1061 // save as new OTT template
1062 utl::TempFile aTempFileSaveAsOTT;
1063 aTempFileSaveAsOTT.EnableKillingFile();
1064 try
1065 {
1066 uno::Reference<frame::XStorable> xDocStorable(mxComponent, uno::UNO_QUERY);
1067 uno::Sequence<beans::PropertyValue> descSaveAs(comphelper::InitPropertySequence(
1068 { { "FilterName", uno::Any(OUString("writer8_template")) } }));
1069 xDocStorable->storeAsURL(aTempFileSaveAsOTT.GetURL(), descSaveAs);
1070 }
1071 catch (...)
1072 {
1073 CPPUNIT_FAIL("Failed to save OTT template");
1074 }
1075
1076 // load the saved OTT template as-is to validate signatures
1077 mxComponent->dispose();
1078 mxComponent
1079 = loadFromDesktop(aTempFileSaveAsOTT.GetURL(), OUString(),
1080 comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1081 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage, RTL_TEXTENCODING_UTF8).getStr(),
1082 mxComponent.is());
1083
1084 // the loaded document is a OTT with a valid macro signature
1085 assertDocument(CPPUNIT_SOURCELINE(), "writer8_template", SignatureState::NOSIGNATURES,
1086 SignatureState::OK, ODFVER_012_TEXT);
1087
1088 // load saved ODT document
1089 createDoc(aTempFileSaveAsODT.GetURL());
1090
1091 // the loaded document is a ODT with a macro signature
1092 assertDocument(CPPUNIT_SOURCELINE(), "writer8", SignatureState::NOSIGNATURES,
1093 SignatureState::OK, ODFVER_012_TEXT);
1094
1095 // save as new OTT template
1096 utl::TempFile aTempFileSaveAsODT_OTT;
1097 aTempFileSaveAsODT_OTT.EnableKillingFile();
1098 try
1099 {
1100 uno::Reference<frame::XStorable> xDocStorable(mxComponent, uno::UNO_QUERY);
1101 uno::Sequence<beans::PropertyValue> descSaveAs(comphelper::InitPropertySequence(
1102 { { "FilterName", uno::Any(OUString("writer8_template")) } }));
1103 xDocStorable->storeAsURL(aTempFileSaveAsODT_OTT.GetURL(), descSaveAs);
1104 }
1105 catch (...)
1106 {
1107 CPPUNIT_FAIL("Failed to save OTT template");
1108 }
1109
1110 // load the template as-is to validate signatures
1111 mxComponent->dispose();
1112 mxComponent
1113 = loadFromDesktop(aTempFileSaveAsODT_OTT.GetURL(), OUString(),
1114 comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1115 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage, RTL_TEXTENCODING_UTF8).getStr(),
1116 mxComponent.is());
1117
1118 // the loaded document is a OTT with a valid macro signature
1119 assertDocument(CPPUNIT_SOURCELINE(), "writer8_template", SignatureState::NOSIGNATURES,
1120 SignatureState::OK, ODFVER_012_TEXT);
1121 }
1122
1123 /// Test if a macro signature from an OTT 1.0 is dropped for ODT 1.2
CPPUNIT_TEST_FIXTURE(SigningTest,testDropMacroTemplateSignature)1124 CPPUNIT_TEST_FIXTURE(SigningTest, testDropMacroTemplateSignature)
1125 {
1126 const OUString aURL(m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf42316.ott");
1127 const OUString sLoadMessage = "loading failed: " + aURL;
1128
1129 // load the template as-is to validate signatures
1130 mxComponent = loadFromDesktop(
1131 aURL, OUString(), comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1132 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage, RTL_TEXTENCODING_UTF8).getStr(),
1133 mxComponent.is());
1134
1135 // we are a template, and have a non-invalid macro signature
1136 assertDocument(CPPUNIT_SOURCELINE(), "writer8_template", SignatureState::NOSIGNATURES,
1137 SignatureState::NOTVALIDATED, OUString());
1138
1139 // create new document from template
1140 // we can't use createDoc / MacrosTest::loadFromDesktop, because ALWAYS_EXECUTE_NO_WARN
1141 // won't verify the signature for templates, so the resulting document won't be able to
1142 // preserve the templates signature.
1143 mxComponent->dispose();
1144 mxComponent = mxDesktop->loadComponentFromURL(
1145 aURL, "_default", 0,
1146 comphelper::InitPropertySequence(
1147 { { "MacroExecutionMode",
1148 uno::Any(document::MacroExecMode::FROM_LIST_AND_SIGNED_NO_WARN) } }));
1149 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage, RTL_TEXTENCODING_UTF8).getStr(),
1150 mxComponent.is());
1151
1152 // we are somehow a template (?), and have just a valid macro signature
1153 assertDocument(CPPUNIT_SOURCELINE(), "writer8_template", SignatureState::NOSIGNATURES,
1154 SignatureState::NOTVALIDATED, OUString());
1155
1156 // save as new ODT document
1157 utl::TempFile aTempFileSaveAs;
1158 aTempFileSaveAs.EnableKillingFile();
1159 try
1160 {
1161 uno::Reference<frame::XStorable> xDocStorable(mxComponent, uno::UNO_QUERY);
1162 uno::Sequence<beans::PropertyValue> descSaveAs(
1163 comphelper::InitPropertySequence({ { "FilterName", uno::Any(OUString("writer8")) } }));
1164 xDocStorable->storeAsURL(aTempFileSaveAs.GetURL(), descSaveAs);
1165 }
1166 catch (...)
1167 {
1168 CPPUNIT_FAIL("Failed to save ODT document");
1169 }
1170
1171 // load saved document
1172 createDoc(aTempFileSaveAs.GetURL());
1173
1174 // the loaded document is a 1.2 ODT without any signatures
1175 assertDocument(CPPUNIT_SOURCELINE(), "writer8", SignatureState::NOSIGNATURES,
1176 SignatureState::NOSIGNATURES, ODFVER_012_TEXT);
1177
1178 // load the template as-is to validate signatures
1179 mxComponent->dispose();
1180 mxComponent = loadFromDesktop(
1181 aURL, OUString(), comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1182 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage, RTL_TEXTENCODING_UTF8).getStr(),
1183 mxComponent.is());
1184
1185 // we are a template, and have a non-invalid macro signature
1186 assertDocument(CPPUNIT_SOURCELINE(), "writer8_template", SignatureState::NOSIGNATURES,
1187 SignatureState::NOTVALIDATED, OUString());
1188
1189 // save as new OTT template
1190 utl::TempFile aTempFileSaveAsOTT;
1191 aTempFileSaveAsOTT.EnableKillingFile();
1192 try
1193 {
1194 uno::Reference<frame::XStorable> xDocStorable(mxComponent, uno::UNO_QUERY);
1195 uno::Sequence<beans::PropertyValue> descSaveAs(comphelper::InitPropertySequence(
1196 { { "FilterName", uno::Any(OUString("writer8_template")) } }));
1197 xDocStorable->storeAsURL(aTempFileSaveAsOTT.GetURL(), descSaveAs);
1198 }
1199 catch (...)
1200 {
1201 CPPUNIT_FAIL("Failed to save OTT template");
1202 }
1203
1204 // load the template as-is to validate signatures
1205 mxComponent->dispose();
1206 mxComponent
1207 = loadFromDesktop(aTempFileSaveAsOTT.GetURL(), OUString(),
1208 comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1209 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage, RTL_TEXTENCODING_UTF8).getStr(),
1210 mxComponent.is());
1211
1212 // the loaded document is a 1.2 OTT without any signatures
1213 assertDocument(CPPUNIT_SOURCELINE(), "writer8_template", SignatureState::NOSIGNATURES,
1214 SignatureState::NOSIGNATURES, ODFVER_012_TEXT);
1215 }
1216
1217 class Resetter
1218 {
1219 private:
1220 std::function<void()> m_Func;
1221
1222 public:
Resetter(std::function<void ()> const & rFunc)1223 Resetter(std::function<void()> const& rFunc)
1224 : m_Func(rFunc)
1225 {
1226 }
~Resetter()1227 ~Resetter()
1228 {
1229 try
1230 {
1231 m_Func();
1232 }
1233 catch (...) // has to be reliable
1234 {
1235 fprintf(stderr, "resetter failed with exception\n");
1236 abort();
1237 }
1238 }
1239 };
1240
1241 /// Test if a macro signature from a OTT 1.0 template is preserved for ODT 1.0
CPPUNIT_TEST_FIXTURE(SigningTest,testPreserveMacroTemplateSignature10)1242 CPPUNIT_TEST_FIXTURE(SigningTest, testPreserveMacroTemplateSignature10)
1243 {
1244 // set ODF version 1.0 / 1.1 as default
1245 Resetter _([]() {
1246 std::shared_ptr<comphelper::ConfigurationChanges> pBatch(
1247 comphelper::ConfigurationChanges::create());
1248 officecfg::Office::Common::Save::ODF::DefaultVersion::set(3, pBatch);
1249 return pBatch->commit();
1250 });
1251 std::shared_ptr<comphelper::ConfigurationChanges> pBatch(
1252 comphelper::ConfigurationChanges::create());
1253 officecfg::Office::Common::Save::ODF::DefaultVersion::set(2, pBatch);
1254 pBatch->commit();
1255
1256 const OUString aURL(m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf42316.ott");
1257 const OUString sLoadMessage = "loading failed: " + aURL;
1258
1259 // load the template as-is to validate signatures
1260 mxComponent = loadFromDesktop(
1261 aURL, OUString(), comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1262 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage, RTL_TEXTENCODING_UTF8).getStr(),
1263 mxComponent.is());
1264
1265 // we are a template, and have a non-invalid macro signature
1266 assertDocument(CPPUNIT_SOURCELINE(), "writer8_template", SignatureState::NOSIGNATURES,
1267 SignatureState::NOTVALIDATED, OUString());
1268
1269 // create new document from template
1270 // we can't use createDoc / MacrosTest::loadFromDesktop, because ALWAYS_EXECUTE_NO_WARN
1271 // won't verify the signature for templates, so the resulting document won't be able to
1272 // preserve the templates signature.
1273 mxComponent->dispose();
1274 mxComponent = mxDesktop->loadComponentFromURL(
1275 aURL, "_default", 0,
1276 comphelper::InitPropertySequence(
1277 { { "MacroExecutionMode",
1278 uno::Any(document::MacroExecMode::FROM_LIST_AND_SIGNED_NO_WARN) } }));
1279 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage, RTL_TEXTENCODING_UTF8).getStr(),
1280 mxComponent.is());
1281
1282 // we are somehow a template (?), and have just a valid macro signature
1283 assertDocument(CPPUNIT_SOURCELINE(), "writer8_template", SignatureState::NOSIGNATURES,
1284 SignatureState::NOTVALIDATED, OUString());
1285
1286 // save as new ODT document
1287 utl::TempFile aTempFileSaveAsODT;
1288 aTempFileSaveAsODT.EnableKillingFile();
1289 try
1290 {
1291 uno::Reference<frame::XStorable> xDocStorable(mxComponent, uno::UNO_QUERY);
1292 uno::Sequence<beans::PropertyValue> descSaveAs(
1293 comphelper::InitPropertySequence({ { "FilterName", uno::Any(OUString("writer8")) } }));
1294 xDocStorable->storeAsURL(aTempFileSaveAsODT.GetURL(), descSaveAs);
1295 }
1296 catch (...)
1297 {
1298 CPPUNIT_FAIL("Failed to save ODT document");
1299 }
1300
1301 // save as new OTT template
1302 utl::TempFile aTempFileSaveAsOTT;
1303 aTempFileSaveAsOTT.EnableKillingFile();
1304 try
1305 {
1306 uno::Reference<frame::XStorable> xDocStorable(mxComponent, uno::UNO_QUERY);
1307 uno::Sequence<beans::PropertyValue> descSaveAs(comphelper::InitPropertySequence(
1308 { { "FilterName", uno::Any(OUString("writer8_template")) } }));
1309 xDocStorable->storeAsURL(aTempFileSaveAsOTT.GetURL(), descSaveAs);
1310 }
1311 catch (...)
1312 {
1313 CPPUNIT_FAIL("Failed to save OTT template");
1314 }
1315
1316 // load the saved OTT template as-is to validate signatures
1317 mxComponent->dispose();
1318 mxComponent
1319 = loadFromDesktop(aTempFileSaveAsOTT.GetURL(), OUString(),
1320 comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1321 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage, RTL_TEXTENCODING_UTF8).getStr(),
1322 mxComponent.is());
1323
1324 // the loaded document is a OTT with a non-invalid macro signature
1325 assertDocument(CPPUNIT_SOURCELINE(), "writer8_template", SignatureState::NOSIGNATURES,
1326 SignatureState::NOTVALIDATED, OUString());
1327
1328 // load saved ODT document
1329 createDoc(aTempFileSaveAsODT.GetURL());
1330
1331 // the loaded document is a ODT with a non-invalid macro signature
1332 assertDocument(CPPUNIT_SOURCELINE(), "writer8", SignatureState::NOSIGNATURES,
1333 SignatureState::NOTVALIDATED, OUString());
1334
1335 // save as new OTT template
1336 utl::TempFile aTempFileSaveAsODT_OTT;
1337 aTempFileSaveAsODT_OTT.EnableKillingFile();
1338 try
1339 {
1340 uno::Reference<frame::XStorable> xDocStorable(mxComponent, uno::UNO_QUERY);
1341 uno::Sequence<beans::PropertyValue> descSaveAs(comphelper::InitPropertySequence(
1342 { { "FilterName", uno::Any(OUString("writer8_template")) } }));
1343 xDocStorable->storeAsURL(aTempFileSaveAsODT_OTT.GetURL(), descSaveAs);
1344 }
1345 catch (...)
1346 {
1347 CPPUNIT_FAIL("Failed to save OTT template");
1348 }
1349
1350 // load the template as-is to validate signatures
1351 mxComponent->dispose();
1352 mxComponent
1353 = loadFromDesktop(aTempFileSaveAsODT_OTT.GetURL(), OUString(),
1354 comphelper::InitPropertySequence({ { "AsTemplate", uno::Any(false) } }));
1355 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sLoadMessage, RTL_TEXTENCODING_UTF8).getStr(),
1356 mxComponent.is());
1357
1358 // the loaded document is a OTT with a non-invalid macro signature
1359 assertDocument(CPPUNIT_SOURCELINE(), "writer8_template", SignatureState::NOSIGNATURES,
1360 SignatureState::NOTVALIDATED, OUString());
1361 }
1362
1363 #endif
1364
registerNamespaces(xmlXPathContextPtr & pXmlXpathCtx)1365 void SigningTest::registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx)
1366 {
1367 xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("odfds"),
1368 BAD_CAST("urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0"));
1369 xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("dsig"),
1370 BAD_CAST("http://www.w3.org/2000/09/xmldsig#"));
1371 xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("xd"), BAD_CAST("http://uri.etsi.org/01903/v1.3.2#"));
1372 }
1373
1374 CPPUNIT_PLUGIN_IMPLEMENT();
1375
1376 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1377