2016-01-11 15:46:10 +01:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/*
|
|
|
|
* This file is part of the LibreOffice project.
|
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
|
|
|
|
2016-02-26 08:53:43 +01:00
|
|
|
#include <sal/config.h>
|
|
|
|
|
|
|
|
#include <type_traits>
|
|
|
|
|
2016-01-11 15:46:10 +01:00
|
|
|
#include <test/bootstrapfixture.hxx>
|
|
|
|
#include <unotest/macros_test.hxx>
|
|
|
|
|
|
|
|
#include <com/sun/star/document/XStorageBasedDocument.hpp>
|
|
|
|
#include <com/sun/star/embed/XStorage.hpp>
|
|
|
|
#include <com/sun/star/embed/XTransactedObject.hpp>
|
|
|
|
#include <com/sun/star/frame/Desktop.hpp>
|
|
|
|
#include <com/sun/star/frame/XStorable.hpp>
|
|
|
|
#include <com/sun/star/security/SerialNumberAdapter.hpp>
|
|
|
|
#include <com/sun/star/xml/crypto/SEInitializer.hpp>
|
|
|
|
#include <com/sun/star/io/TempFile.hpp>
|
2016-02-15 17:05:00 +01:00
|
|
|
#include <com/sun/star/packages/manifest/ManifestReader.hpp>
|
2016-01-11 15:46:10 +01:00
|
|
|
|
|
|
|
#include <comphelper/processfactory.hxx>
|
|
|
|
#include <sax/tools/converter.hxx>
|
|
|
|
#include <unotools/mediadescriptor.hxx>
|
|
|
|
#include <unotools/tempfile.hxx>
|
|
|
|
#include <unotools/ucbstreamhelper.hxx>
|
|
|
|
#include <unotools/streamwrap.hxx>
|
|
|
|
#include <comphelper/storagehelper.hxx>
|
|
|
|
#include <tools/date.hxx>
|
|
|
|
#include <tools/time.hxx>
|
2016-02-04 09:31:16 +01:00
|
|
|
#include <sfx2/sfxbasemodel.hxx>
|
|
|
|
#include <sfx2/objsh.hxx>
|
2016-01-11 15:46:10 +01:00
|
|
|
|
|
|
|
#include <xmlsecurity/documentsignaturehelper.hxx>
|
|
|
|
#include <xmlsecurity/xmlsignaturehelper.hxx>
|
2016-02-15 17:05:00 +01:00
|
|
|
#include <documentsignaturemanager.hxx>
|
2016-01-11 15:46:10 +01:00
|
|
|
|
|
|
|
using namespace com::sun::star;
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
const char* DATA_DIRECTORY = "/xmlsecurity/qa/unit/signing/data/";
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Testsuite for the document signing feature.
|
|
|
|
class SigningTest : public test::BootstrapFixture, public unotest::MacrosTest
|
|
|
|
{
|
|
|
|
uno::Reference<uno::XComponentContext> mxComponentContext;
|
|
|
|
uno::Reference<lang::XComponent> mxComponent;
|
|
|
|
|
|
|
|
public:
|
|
|
|
SigningTest();
|
|
|
|
virtual void setUp() override;
|
|
|
|
virtual void tearDown() override;
|
|
|
|
|
|
|
|
void testDescription();
|
2016-02-04 09:31:16 +01:00
|
|
|
/// Test a typical OOXML where a number of (but not all) streams are signed.
|
|
|
|
void testOOXMLPartial();
|
2016-02-04 09:39:32 +01:00
|
|
|
/// Test a typical broken OOXML signature where one stream is corrupted.
|
|
|
|
void testOOXMLBroken();
|
2016-02-15 17:51:00 +01:00
|
|
|
void testOOXMLDescription();
|
2016-01-11 15:46:10 +01:00
|
|
|
|
|
|
|
CPPUNIT_TEST_SUITE(SigningTest);
|
|
|
|
CPPUNIT_TEST(testDescription);
|
2016-02-04 09:31:16 +01:00
|
|
|
CPPUNIT_TEST(testOOXMLPartial);
|
2016-02-04 09:39:32 +01:00
|
|
|
CPPUNIT_TEST(testOOXMLBroken);
|
2016-02-15 17:51:00 +01:00
|
|
|
CPPUNIT_TEST(testOOXMLDescription);
|
2016-01-11 15:46:10 +01:00
|
|
|
CPPUNIT_TEST_SUITE_END();
|
|
|
|
|
|
|
|
private:
|
2016-02-04 09:31:16 +01:00
|
|
|
void createDoc(const OUString& rURL = OUString());
|
2016-01-11 15:46:10 +01:00
|
|
|
uno::Reference<security::XCertificate> getCertificate(XMLSignatureHelper& rSignatureHelper);
|
|
|
|
};
|
|
|
|
|
|
|
|
SigningTest::SigningTest()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void SigningTest::setUp()
|
|
|
|
{
|
|
|
|
test::BootstrapFixture::setUp();
|
|
|
|
|
|
|
|
mxComponentContext.set(comphelper::getComponentContext(getMultiServiceFactory()));
|
|
|
|
mxDesktop.set(frame::Desktop::create(mxComponentContext));
|
|
|
|
}
|
|
|
|
|
|
|
|
void SigningTest::tearDown()
|
|
|
|
{
|
|
|
|
if (mxComponent.is())
|
|
|
|
mxComponent->dispose();
|
|
|
|
|
|
|
|
test::BootstrapFixture::tearDown();
|
|
|
|
}
|
|
|
|
|
2016-02-04 09:31:16 +01:00
|
|
|
void SigningTest::createDoc(const OUString& rURL)
|
2016-01-11 15:46:10 +01:00
|
|
|
{
|
|
|
|
if (mxComponent.is())
|
|
|
|
mxComponent->dispose();
|
2016-02-04 09:31:16 +01:00
|
|
|
if (rURL.isEmpty())
|
|
|
|
mxComponent = loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument");
|
|
|
|
else
|
|
|
|
mxComponent = loadFromDesktop(rURL, "com.sun.star.text.TextDocument");
|
2016-01-11 15:46:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
uno::Reference<security::XCertificate> SigningTest::getCertificate(XMLSignatureHelper& rSignatureHelper)
|
|
|
|
{
|
|
|
|
uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = rSignatureHelper.GetSecurityEnvironment();
|
|
|
|
OUString aCertificate;
|
|
|
|
{
|
|
|
|
SvFileStream aStream(getURLFromSrc(DATA_DIRECTORY) + "certificate.crt", StreamMode::READ);
|
|
|
|
OString aLine;
|
|
|
|
bool bMore = aStream.ReadLine(aLine);
|
|
|
|
while (bMore)
|
|
|
|
{
|
|
|
|
aCertificate += OUString::fromUtf8(aLine);
|
|
|
|
aCertificate += "\n";
|
|
|
|
bMore = aStream.ReadLine(aLine);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return xSecurityEnvironment->createCertificateFromAscii(aCertificate);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SigningTest::testDescription()
|
|
|
|
{
|
2016-02-15 17:05:00 +01:00
|
|
|
// Create an empty document and store it to a tempfile, finally load it as a storage.
|
2016-01-11 15:46:10 +01:00
|
|
|
createDoc();
|
2016-02-15 17:05:00 +01:00
|
|
|
|
2016-01-11 15:46:10 +01:00
|
|
|
utl::TempFile aTempFile;
|
|
|
|
aTempFile.EnableKillingFile();
|
|
|
|
uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
|
|
|
|
utl::MediaDescriptor aMediaDescriptor;
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("writer8");
|
|
|
|
xStorable->storeAsURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
|
|
|
|
|
2016-02-15 17:05:00 +01:00
|
|
|
DocumentSignatureManager aManager(mxComponentContext, SignatureModeDocumentContent);
|
|
|
|
CPPUNIT_ASSERT(aManager.maSignatureHelper.Init());
|
|
|
|
uno::Reference <embed::XStorage> xStorage = comphelper::OStorageHelper::GetStorageOfFormatFromURL(ZIP_STORAGE_FORMAT_STRING, aTempFile.GetURL(), embed::ElementModes::READWRITE);
|
|
|
|
CPPUNIT_ASSERT(xStorage.is());
|
|
|
|
aManager.mxStore = xStorage;
|
|
|
|
aManager.maSignatureHelper.SetStorage(xStorage, "1.2");
|
2016-01-11 15:46:10 +01:00
|
|
|
|
2016-02-15 17:05:00 +01:00
|
|
|
// Then add a signature document.
|
|
|
|
uno::Reference<security::XCertificate> xCertificate = getCertificate(aManager.maSignatureHelper);
|
2016-02-15 17:51:00 +01:00
|
|
|
CPPUNIT_ASSERT(xCertificate.is());
|
|
|
|
OUString aDescription("SigningTest::testDescription");
|
|
|
|
sal_Int32 nSecurityId;
|
|
|
|
aManager.add(xCertificate, aDescription, nSecurityId);
|
|
|
|
|
|
|
|
// Read back the signature and make sure that the description survives the roundtrip.
|
|
|
|
aManager.read(/*bUseTempStream=*/true);
|
|
|
|
std::vector<SignatureInformation>& rInformations = aManager.maCurrentSignatureInformations;
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rInformations.size());
|
|
|
|
CPPUNIT_ASSERT_EQUAL(aDescription, rInformations[0].ouDescription);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SigningTest::testOOXMLDescription()
|
|
|
|
{
|
|
|
|
// Create an empty document and store it to a tempfile, finally load it as a storage.
|
|
|
|
createDoc();
|
|
|
|
|
|
|
|
utl::TempFile aTempFile;
|
|
|
|
aTempFile.EnableKillingFile();
|
|
|
|
uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
|
|
|
|
utl::MediaDescriptor aMediaDescriptor;
|
|
|
|
aMediaDescriptor["FilterName"] <<= OUString("MS Word 2007 XML");
|
|
|
|
xStorable->storeAsURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
|
|
|
|
|
|
|
|
DocumentSignatureManager aManager(mxComponentContext, SignatureModeDocumentContent);
|
|
|
|
CPPUNIT_ASSERT(aManager.maSignatureHelper.Init());
|
|
|
|
uno::Reference <embed::XStorage> xStorage = comphelper::OStorageHelper::GetStorageOfFormatFromURL(ZIP_STORAGE_FORMAT_STRING, aTempFile.GetURL(), embed::ElementModes::READWRITE);
|
|
|
|
CPPUNIT_ASSERT(xStorage.is());
|
|
|
|
aManager.mxStore = xStorage;
|
|
|
|
aManager.maSignatureHelper.SetStorage(xStorage, "1.2");
|
|
|
|
|
|
|
|
// Then add a signature document.
|
|
|
|
uno::Reference<security::XCertificate> xCertificate = getCertificate(aManager.maSignatureHelper);
|
2016-02-15 17:05:00 +01:00
|
|
|
CPPUNIT_ASSERT(xCertificate.is());
|
|
|
|
OUString aDescription("SigningTest::testDescription");
|
|
|
|
sal_Int32 nSecurityId;
|
|
|
|
aManager.add(xCertificate, aDescription, nSecurityId);
|
2016-01-11 15:46:10 +01:00
|
|
|
|
|
|
|
// Read back the signature and make sure that the description survives the roundtrip.
|
2016-02-15 17:05:00 +01:00
|
|
|
aManager.read(/*bUseTempStream=*/true);
|
|
|
|
std::vector<SignatureInformation>& rInformations = aManager.maCurrentSignatureInformations;
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rInformations.size());
|
|
|
|
CPPUNIT_ASSERT_EQUAL(aDescription, rInformations[0].ouDescription);
|
2016-01-11 15:46:10 +01:00
|
|
|
}
|
|
|
|
|
2016-02-04 09:31:16 +01:00
|
|
|
void SigningTest::testOOXMLPartial()
|
|
|
|
{
|
|
|
|
createDoc(getURLFromSrc(DATA_DIRECTORY) + "partial.docx");
|
|
|
|
SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
|
|
|
|
CPPUNIT_ASSERT(pBaseModel);
|
|
|
|
SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
|
|
|
|
CPPUNIT_ASSERT(pObjectShell);
|
|
|
|
// This was SignatureState::BROKEN due to missing RelationshipTransform and SHA-256 support.
|
|
|
|
// We expect NOTVALIDATED in case the root CA is not imported on the system, and PARTIAL_OK otherwise, so accept both.
|
2016-02-26 08:53:43 +01:00
|
|
|
SignatureState nActual = pObjectShell->GetDocumentSignatureState();
|
|
|
|
CPPUNIT_ASSERT_MESSAGE(
|
|
|
|
(OString::number(
|
2016-02-29 10:21:34 +01:00
|
|
|
static_cast<std::underlying_type<SignatureState>::type>(nActual))
|
2016-02-26 08:53:43 +01:00
|
|
|
.getStr()),
|
|
|
|
(nActual == SignatureState::NOTVALIDATED
|
|
|
|
|| nActual == SignatureState::PARTIAL_OK));
|
2016-02-04 09:31:16 +01:00
|
|
|
}
|
|
|
|
|
2016-02-04 09:39:32 +01:00
|
|
|
void SigningTest::testOOXMLBroken()
|
|
|
|
{
|
|
|
|
createDoc(getURLFromSrc(DATA_DIRECTORY) + "bad.docx");
|
|
|
|
SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
|
|
|
|
CPPUNIT_ASSERT(pBaseModel);
|
|
|
|
SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
|
|
|
|
CPPUNIT_ASSERT(pObjectShell);
|
|
|
|
// This was SignatureState::NOTVALIDATED/PARTIAL_OK as we did not validate manifest references.
|
|
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN), static_cast<int>(pObjectShell->GetDocumentSignatureState()));
|
|
|
|
}
|
|
|
|
|
2016-01-11 15:46:10 +01:00
|
|
|
CPPUNIT_TEST_SUITE_REGISTRATION(SigningTest);
|
|
|
|
|
|
|
|
CPPUNIT_PLUGIN_IMPLEMENT();
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|