summaryrefslogtreecommitdiffstats
path: root/xmlsecurity/source/helper
diff options
context:
space:
mode:
Diffstat (limited to 'xmlsecurity/source/helper')
-rw-r--r--xmlsecurity/source/helper/UriBindingHelper.cxx113
-rw-r--r--xmlsecurity/source/helper/documentsignaturehelper.cxx650
-rw-r--r--xmlsecurity/source/helper/documentsignaturemanager.cxx705
-rw-r--r--xmlsecurity/source/helper/ooxmlsecexporter.cxx565
-rw-r--r--xmlsecurity/source/helper/ooxmlsecexporter.hxx47
-rw-r--r--xmlsecurity/source/helper/ooxmlsecparser.cxx1352
-rw-r--r--xmlsecurity/source/helper/ooxmlsecparser.hxx110
-rw-r--r--xmlsecurity/source/helper/pdfsignaturehelper.cxx626
-rw-r--r--xmlsecurity/source/helper/xmlsignaturehelper.cxx713
-rw-r--r--xmlsecurity/source/helper/xsecctl.cxx1005
-rw-r--r--xmlsecurity/source/helper/xsecparser.cxx1632
-rw-r--r--xmlsecurity/source/helper/xsecparser.hxx159
-rw-r--r--xmlsecurity/source/helper/xsecsign.cxx462
-rw-r--r--xmlsecurity/source/helper/xsecverify.cxx630
14 files changed, 8769 insertions, 0 deletions
diff --git a/xmlsecurity/source/helper/UriBindingHelper.cxx b/xmlsecurity/source/helper/UriBindingHelper.cxx
new file mode 100644
index 0000000000..f6c36d2c8b
--- /dev/null
+++ b/xmlsecurity/source/helper/UriBindingHelper.cxx
@@ -0,0 +1,113 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <UriBindingHelper.hxx>
+
+#include <tools/solar.h>
+#include <unotools/streamhelper.hxx>
+
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <osl/diagnose.h>
+#include <rtl/uri.hxx>
+#include <sal/log.hxx>
+
+using namespace com::sun::star;
+
+// XUriBinding
+
+UriBindingHelper::UriBindingHelper()
+{
+}
+
+UriBindingHelper::UriBindingHelper( const css::uno::Reference < css::embed::XStorage >& rxStorage )
+{
+ mxStorage = rxStorage;
+}
+
+void SAL_CALL UriBindingHelper::setUriBinding( const OUString& /*uri*/, const uno::Reference< io::XInputStream >&)
+{
+}
+
+uno::Reference< io::XInputStream > SAL_CALL UriBindingHelper::getUriBinding( const OUString& uri )
+{
+ uno::Reference< io::XInputStream > xInputStream;
+ if ( mxStorage.is() )
+ {
+ xInputStream = OpenInputStream( mxStorage, uri );
+ }
+ else
+ {
+ SvFileStream* pStream = new SvFileStream( uri, StreamMode::READ );
+ sal_uInt64 nBytes = pStream->TellEnd();
+ SvLockBytesRef xLockBytes = new SvLockBytes( pStream, true );
+ xInputStream = new utl::OInputStreamHelper( xLockBytes, nBytes );
+ }
+ return xInputStream;
+}
+
+uno::Reference < io::XInputStream > UriBindingHelper::OpenInputStream( const uno::Reference < embed::XStorage >& rxStore, const OUString& rURI )
+{
+ OSL_ASSERT(!rURI.isEmpty());
+ uno::Reference < io::XInputStream > xInStream;
+
+ OUString aURI(rURI);
+ // Ignore leading slash, don't attempt to open a storage with name "".
+ if (aURI.startsWith("/"))
+ aURI = aURI.copy(1);
+ // Ignore query part of the URI.
+ sal_Int32 nQueryPos = aURI.indexOf('?');
+ if (nQueryPos != -1)
+ aURI = aURI.copy(0, nQueryPos);
+
+
+ sal_Int32 nSepPos = aURI.indexOf( '/' );
+ if ( nSepPos == -1 )
+ {
+ // Cloning because of I can't keep all storage references open
+ // MBA with think about a better API...
+ const OUString sName = ::rtl::Uri::decode(
+ aURI, rtl_UriDecodeStrict, rtl_UriCharClassRelSegment);
+ if (sName.isEmpty() && !aURI.isEmpty())
+ throw uno::Exception("Could not decode URI for stream element.", nullptr);
+
+ uno::Reference< io::XStream > xStream;
+ if (!rxStore->hasByName(sName))
+ SAL_WARN("xmlsecurity.helper", "expected stream, but not found: " << sName);
+ else
+ xStream = rxStore->cloneStreamElement( sName );
+ if ( !xStream.is() )
+ throw uno::RuntimeException();
+ xInStream = xStream->getInputStream();
+ }
+ else
+ {
+ const OUString aStoreName = ::rtl::Uri::decode(
+ aURI.copy( 0, nSepPos ), rtl_UriDecodeStrict, rtl_UriCharClassRelSegment);
+ if (aStoreName.isEmpty() && !aURI.isEmpty())
+ throw uno::Exception("Could not decode URI for stream element.", nullptr);
+
+ OUString aElement = aURI.copy( nSepPos+1 );
+ uno::Reference < embed::XStorage > xSubStore = rxStore->openStorageElement( aStoreName, embed::ElementModes::READ );
+ xInStream = OpenInputStream( xSubStore, aElement );
+ }
+ return xInStream;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/documentsignaturehelper.cxx b/xmlsecurity/source/helper/documentsignaturehelper.cxx
new file mode 100644
index 0000000000..8e666b14ba
--- /dev/null
+++ b/xmlsecurity/source/helper/documentsignaturehelper.cxx
@@ -0,0 +1,650 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <documentsignaturehelper.hxx>
+
+#include <algorithm>
+#include <functional>
+#include <string_view>
+
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+
+#include <comphelper/attributelist.hxx>
+#include <comphelper/documentconstants.hxx>
+#include <comphelper/ofopxmlhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ref.hxx>
+#include <rtl/uri.hxx>
+#include <sal/log.hxx>
+#include <svx/xoutbmp.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <xsecctl.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace css::xml::sax;
+
+namespace
+{
+std::u16string_view getElement(std::u16string_view version, size_t * index)
+{
+ while (*index < version.size() && version[*index] == '0') {
+ ++*index;
+ }
+ return o3tl::getToken(version, u'.', *index);
+}
+
+
+// Return 1 if version1 is greater than version 2, 0 if they are equal
+//and -1 if version1 is less version 2
+int compareVersions(
+ std::u16string_view version1, std::u16string_view version2)
+{
+ for (size_t i1 = 0, i2 = 0; i1 != std::u16string_view::npos || i2 != std::u16string_view::npos;) {
+ std::u16string_view e1(getElement(version1, &i1));
+ std::u16string_view e2(getElement(version2, &i2));
+ if (e1.size() < e2.size()) {
+ return -1;
+ } else if (e1.size() > e2.size()) {
+ return 1;
+ } else if (e1 < e2) {
+ return -1;
+ } else if (e1 > e2) {
+ return 1;
+ }
+ }
+ return 0;
+}
+}
+
+static void ImplFillElementList(
+ std::vector< OUString >& rList, const Reference < css::embed::XStorage >& rxStore,
+ std::u16string_view rRootStorageName, const bool bRecursive,
+ const DocumentSignatureAlgorithm mode)
+{
+ const Sequence< OUString > aElements = rxStore->getElementNames();
+
+ for ( const auto& rName : aElements )
+ {
+ if (rName == "[Content_Types].xml")
+ // OOXML
+ continue;
+
+ // If the user enabled validating according to OOo 3.0
+ // then mimetype and all content of META-INF must be excluded.
+ if (mode != DocumentSignatureAlgorithm::OOo3_2
+ && (rName == "META-INF" || rName == "mimetype"))
+ {
+ continue;
+ }
+ else
+ {
+ OUString sEncName = ::rtl::Uri::encode(
+ rName, rtl_UriCharClassRelSegment,
+ rtl_UriEncodeStrict, RTL_TEXTENCODING_UTF8);
+ if (sEncName.isEmpty() && !rName.isEmpty())
+ throw css::uno::RuntimeException("Failed to encode element name of XStorage", nullptr);
+
+ if ( rxStore->isStreamElement( rName ) )
+ {
+ //Exclude documentsignatures.xml!
+ if (rName ==
+ DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName())
+ continue;
+ OUString aFullName( rRootStorageName + sEncName );
+ rList.push_back(aFullName);
+ }
+ else if ( bRecursive && rxStore->isStorageElement( rName ) )
+ {
+ Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( rName, css::embed::ElementModes::READ );
+ OUString aFullRootName( rRootStorageName + sEncName + "/" );
+ ImplFillElementList(rList, xSubStore, aFullRootName, bRecursive, mode);
+ }
+ }
+ }
+}
+
+
+bool DocumentSignatureHelper::isODFPre_1_2(std::u16string_view sVersion)
+{
+ //The property version exists only if the document is at least version 1.2
+ //That is, if the document has version 1.1 and sVersion is empty.
+ //The constant is defined in comphelper/documentconstants.hxx
+ return compareVersions(sVersion, ODFVER_012_TEXT) == -1;
+}
+
+bool DocumentSignatureHelper::isOOo3_2_Signature(const SignatureInformation & sigInfo)
+{
+ return std::any_of(sigInfo.vSignatureReferenceInfors.cbegin(),
+ sigInfo.vSignatureReferenceInfors.cend(),
+ [](const SignatureReferenceInformation& info) { return info.ouURI == "META-INF/manifest.xml"; });
+}
+
+DocumentSignatureAlgorithm
+DocumentSignatureHelper::getDocumentAlgorithm(
+ std::u16string_view sODFVersion, const SignatureInformation & sigInfo)
+{
+ OSL_ASSERT(!sODFVersion.empty());
+ DocumentSignatureAlgorithm mode = DocumentSignatureAlgorithm::OOo3_2;
+ if (!isOOo3_2_Signature(sigInfo))
+ {
+ if (isODFPre_1_2(sODFVersion))
+ mode = DocumentSignatureAlgorithm::OOo2;
+ else
+ mode = DocumentSignatureAlgorithm::OOo3_0;
+ }
+ return mode;
+}
+
+//The function creates a list of files which are to be signed or for which
+//the signature is to be validated. The strings are UTF8 encoded URIs which
+//contain '/' as path separators.
+//
+//The algorithm how document signatures are created and validated has
+//changed over time. The change affects only which files within the document
+//are changed. Document signatures created by OOo 2.x only used particular files. Since
+//OOo 3.0 everything except "mimetype" and "META-INF" are signed. As of OOo 3.2 everything
+//except META-INF/documentsignatures.xml is signed.
+//Signatures are validated according to the algorithm which was then used for validation.
+//That is, when validating a signature which was created by OOo 3.0, then mimetype and
+//META-INF are not used.
+//
+//When a signature is created then we always use the latest algorithm. That is, we use
+//that of OOo 3.2
+std::vector< OUString >
+DocumentSignatureHelper::CreateElementList(
+ const Reference < css::embed::XStorage >& rxStore,
+ DocumentSignatureMode eMode,
+ const DocumentSignatureAlgorithm mode)
+{
+ std::vector< OUString > aElements;
+ OUString aSep( "/" );
+
+ switch ( eMode )
+ {
+ case DocumentSignatureMode::Content:
+ {
+ if (mode == DocumentSignatureAlgorithm::OOo2) //that is, ODF 1.0, 1.1
+ {
+ // 1) Main content
+ ImplFillElementList(aElements, rxStore, std::u16string_view(), false, mode);
+
+ // 2) Pictures...
+ OUString aSubStorageName( "Pictures" );
+ try
+ {
+ Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
+ ImplFillElementList(
+ aElements, xSubStore, Concat2View(aSubStorageName+aSep), true, mode);
+ }
+ catch(css::io::IOException& )
+ {
+ ; // Doesn't have to exist...
+ }
+ // 3) OLE...
+ aSubStorageName = "ObjectReplacements";
+ try
+ {
+ Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
+ ImplFillElementList(
+ aElements, xSubStore, Concat2View(aSubStorageName+aSep), true, mode);
+ xSubStore.clear();
+
+ // Object folders...
+ const Sequence< OUString > aElementNames = rxStore->getElementNames();
+ for ( const auto& rName : aElementNames )
+ {
+ if ( ( rName.match( "Object " ) ) && rxStore->isStorageElement( rName ) )
+ {
+ Reference < css::embed::XStorage > xTmpSubStore = rxStore->openStorageElement( rName, css::embed::ElementModes::READ );
+ ImplFillElementList(
+ aElements, xTmpSubStore, Concat2View(rName+aSep), true, mode);
+ }
+ }
+ }
+ catch( css::io::IOException& )
+ {
+ ; // Doesn't have to exist...
+ }
+ }
+ else
+ {
+ // Everything except META-INF
+ ImplFillElementList(aElements, rxStore, std::u16string_view(), true, mode);
+ }
+ }
+ break;
+ case DocumentSignatureMode::Macros:
+ {
+ // 1) Macros
+ OUString aSubStorageName( "Basic" );
+ try
+ {
+ Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
+ ImplFillElementList(
+ aElements, xSubStore, Concat2View(aSubStorageName+aSep), true, mode);
+ }
+ catch( css::io::IOException& )
+ {
+ ; // Doesn't have to exist...
+ }
+
+ // 2) Dialogs
+ aSubStorageName = "Dialogs";
+ try
+ {
+ Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
+ ImplFillElementList(
+ aElements, xSubStore, Concat2View(aSubStorageName+aSep), true, mode);
+ }
+ catch( css::io::IOException& )
+ {
+ ; // Doesn't have to exist...
+ }
+ // 3) Scripts
+ aSubStorageName = "Scripts";
+ try
+ {
+ Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
+ ImplFillElementList(
+ aElements, xSubStore, Concat2View(aSubStorageName+aSep), true, mode);
+ }
+ catch( css::io::IOException& )
+ {
+ ; // Doesn't have to exist...
+ }
+ }
+ break;
+ case DocumentSignatureMode::Package:
+ {
+ // Everything except META-INF
+ ImplFillElementList(aElements, rxStore, std::u16string_view(), true, mode);
+ }
+ break;
+ }
+
+ return aElements;
+}
+
+void DocumentSignatureHelper::AppendContentTypes(const uno::Reference<embed::XStorage>& xStorage, std::vector<OUString>& rElements)
+{
+ if (!xStorage.is() || !xStorage->hasByName("[Content_Types].xml"))
+ // ODF
+ return;
+
+ uno::Reference<io::XInputStream> xRelStream(xStorage->openStreamElement("[Content_Types].xml", embed::ElementModes::READ), uno::UNO_QUERY);
+ uno::Sequence< uno::Sequence<beans::StringPair> > aContentTypeInfo = comphelper::OFOPXMLHelper::ReadContentTypeSequence(xRelStream, comphelper::getProcessComponentContext());
+ if (aContentTypeInfo.getLength() < 2)
+ {
+ SAL_WARN("xmlsecurity.helper", "no defaults or overrides in aContentTypeInfo");
+ return;
+ }
+ const uno::Sequence<beans::StringPair>& rDefaults = aContentTypeInfo[0];
+ const uno::Sequence<beans::StringPair>& rOverrides = aContentTypeInfo[1];
+
+ for (OUString& rElement : rElements)
+ {
+ auto it = std::find_if(rOverrides.begin(), rOverrides.end(), [&](const beans::StringPair& rPair)
+ {
+ return rPair.First == Concat2View("/" + rElement);
+ });
+
+ if (it != rOverrides.end())
+ {
+ rElement = "/" + rElement + "?ContentType=" + it->Second;
+ continue;
+ }
+
+ it = std::find_if(rDefaults.begin(), rDefaults.end(), [&](const beans::StringPair& rPair)
+ {
+ return rElement.endsWith(Concat2View("." + rPair.First));
+ });
+
+ if (it != rDefaults.end())
+ {
+ rElement = "/" + rElement + "?ContentType=" + it->Second;
+ continue;
+ }
+ SAL_WARN("xmlsecurity.helper", "found no content type for " << rElement);
+ }
+
+ std::sort(rElements.begin(), rElements.end());
+}
+
+SignatureStreamHelper DocumentSignatureHelper::OpenSignatureStream(
+ const Reference < css::embed::XStorage >& rxStore, sal_Int32 nOpenMode, DocumentSignatureMode eDocSigMode )
+{
+ sal_Int32 nSubStorageOpenMode = css::embed::ElementModes::READ;
+ if ( nOpenMode & css::embed::ElementModes::WRITE )
+ nSubStorageOpenMode = css::embed::ElementModes::WRITE;
+
+ SignatureStreamHelper aHelper;
+
+ if (!rxStore.is())
+ return aHelper;
+
+ if (rxStore->hasByName("META-INF"))
+ {
+ try
+ {
+ aHelper.xSignatureStorage = rxStore->openStorageElement( "META-INF", nSubStorageOpenMode );
+ if ( aHelper.xSignatureStorage.is() )
+ {
+ OUString aSIGStreamName;
+ if ( eDocSigMode == DocumentSignatureMode::Content )
+ aSIGStreamName = DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName();
+ else if ( eDocSigMode == DocumentSignatureMode::Macros )
+ aSIGStreamName = DocumentSignatureHelper::GetScriptingContentSignatureDefaultStreamName();
+ else
+ aSIGStreamName = DocumentSignatureHelper::GetPackageSignatureDefaultStreamName();
+
+#ifdef SAL_LOG_INFO
+ aHelper.xSignatureStream
+ = aHelper.xSignatureStorage->openStreamElement(aSIGStreamName, nOpenMode);
+ SAL_INFO("xmlsecurity.helper",
+ "DocumentSignatureHelper::OpenSignatureStream: stream name is '"
+ << aSIGStreamName << "'");
+ if (aHelper.xSignatureStream.is())
+ {
+ uno::Reference<io::XInputStream> xInputStream(aHelper.xSignatureStream,
+ uno::UNO_QUERY);
+ sal_Int64 nSize = 0;
+ uno::Reference<beans::XPropertySet> xPropertySet(xInputStream, uno::UNO_QUERY);
+ xPropertySet->getPropertyValue("Size") >>= nSize;
+ if (nSize >= 0 || nSize < SAL_MAX_INT32)
+ {
+ uno::Sequence<sal_Int8> aData;
+ xInputStream->readBytes(aData, nSize);
+ SAL_INFO("xmlsecurity.helper",
+ "DocumentSignatureHelper::OpenSignatureStream: stream content is '"
+ << OString(reinterpret_cast<const char*>(aData.getArray()),
+ aData.getLength())
+ << "'");
+ }
+ }
+ aHelper.xSignatureStream.clear();
+#endif
+
+ aHelper.xSignatureStream = aHelper.xSignatureStorage->openStreamElement( aSIGStreamName, nOpenMode );
+ }
+ }
+ catch(css::io::IOException& )
+ {
+ // Doesn't have to exist...
+ SAL_WARN_IF( nOpenMode != css::embed::ElementModes::READ, "xmlsecurity.helper", "Error creating signature stream..." );
+ }
+ }
+ else if(rxStore->hasByName("[Content_Types].xml"))
+ {
+ try
+ {
+ if (rxStore->hasByName("_xmlsignatures") && (nOpenMode & embed::ElementModes::TRUNCATE))
+ // Truncate, then all signatures will be written -> remove previous ones.
+ rxStore->removeElement("_xmlsignatures");
+
+ aHelper.xSignatureStorage = rxStore->openStorageElement("_xmlsignatures", nSubStorageOpenMode);
+ aHelper.nStorageFormat = embed::StorageFormats::OFOPXML;
+ }
+ catch (const io::IOException&)
+ {
+ TOOLS_WARN_EXCEPTION_IF(nOpenMode != css::embed::ElementModes::READ, "xmlsecurity.helper", "DocumentSignatureHelper::OpenSignatureStream:");
+ }
+ }
+
+ return aHelper;
+}
+
+/** Check whether the current file can be signed with GPG (only ODF >= 1.2 can currently) */
+bool DocumentSignatureHelper::CanSignWithGPG(
+ const Reference < css::embed::XStorage >& rxStore,
+ std::u16string_view sOdfVersion)
+{
+ if (!rxStore.is())
+ return false;
+
+ if (rxStore->hasByName("META-INF")) // ODF
+ {
+ return !isODFPre_1_2(sOdfVersion);
+ }
+
+ return false;
+}
+
+
+
+//sElementList contains all files which are expected to be signed. Only those files must me signed,
+//no more, no less.
+//The DocumentSignatureAlgorithm indicates if the document was created with OOo 2.x. Then
+//the uri s in the Reference elements in the signature, were not properly encoded.
+// For example: <Reference URI="ObjectReplacements/Object 1">
+bool DocumentSignatureHelper::checkIfAllFilesAreSigned(
+ const ::std::vector< OUString > & sElementList,
+ const SignatureInformation & sigInfo,
+ const DocumentSignatureAlgorithm alg)
+{
+ // Can only be valid if ALL streams are signed, which means real stream count == signed stream count
+ unsigned int nRealCount = 0;
+ std::function<OUString(const OUString&)> fEncode = [](const OUString& rStr) { return rStr; };
+ if (alg == DocumentSignatureAlgorithm::OOo2)
+ //Comparing URIs is a difficult. Therefore we kind of normalize
+ //it before comparing. We assume that our URI do not have a leading "./"
+ //and fragments at the end (...#...)
+ fEncode = [](const OUString& rStr) {
+ return rtl::Uri::encode(rStr, rtl_UriCharClassPchar, rtl_UriEncodeCheckEscapes, RTL_TEXTENCODING_UTF8);
+ };
+
+ for ( int i = sigInfo.vSignatureReferenceInfors.size(); i; )
+ {
+ const SignatureReferenceInformation& rInf = sigInfo.vSignatureReferenceInfors[--i];
+ // There is also an extra entry of type SignatureReferenceType::SAMEDOCUMENT because of signature date.
+ if ( ( rInf.nType == SignatureReferenceType::BINARYSTREAM ) || ( rInf.nType == SignatureReferenceType::XMLSTREAM ) )
+ {
+ //find the file in the element list
+ if (std::any_of(sElementList.cbegin(), sElementList.cend(),
+ [&fEncode, &rInf](const OUString& rElement) { return fEncode(rElement) == fEncode(rInf.ouURI); }))
+ nRealCount++;
+ }
+ }
+ return sElementList.size() == nRealCount;
+}
+
+/*Compares the Uri which are obtained from CreateElementList with
+ the path obtained from the manifest.xml.
+ Returns true if both strings are equal.
+*/
+bool DocumentSignatureHelper::equalsReferenceUriManifestPath(
+ std::u16string_view rUri, std::u16string_view rPath)
+{
+ //split up the uri and path into segments. Both are separated by '/'
+ std::vector<OUString> vUriSegments;
+ for (sal_Int32 nIndex = 0; nIndex >= 0; )
+ vUriSegments.push_back(OUString(o3tl::getToken(rUri, 0, '/', nIndex )));
+
+ std::vector<OUString> vPathSegments;
+ for (sal_Int32 nIndex = 0; nIndex >= 0; )
+ vPathSegments.push_back(OUString(o3tl::getToken(rPath, 0, '/', nIndex )));
+
+ if (vUriSegments.size() != vPathSegments.size())
+ return false;
+
+ //Now compare each segment of the uri with its counterpart from the path
+ return std::equal(
+ vUriSegments.cbegin(), vUriSegments.cend(), vPathSegments.cbegin(),
+ [](const OUString& rUriSegment, const OUString& rPathSegment) {
+ //Decode the uri segment, so that %20 becomes ' ', etc.
+ OUString sDecUri = rtl::Uri::decode(rUriSegment, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8);
+ return sDecUri == rPathSegment;
+ });
+}
+
+OUString DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName()
+{
+ return "documentsignatures.xml";
+}
+
+OUString DocumentSignatureHelper::GetScriptingContentSignatureDefaultStreamName()
+{
+ return "macrosignatures.xml";
+}
+
+OUString DocumentSignatureHelper::GetPackageSignatureDefaultStreamName()
+{
+ return "packagesignatures.xml";
+}
+
+void DocumentSignatureHelper::writeDigestMethod(
+ const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler)
+{
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("Algorithm", ALGO_XMLDSIGSHA256);
+ xDocumentHandler->startElement("DigestMethod", uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ xDocumentHandler->endElement("DigestMethod");
+}
+
+static void WriteXadesCert(
+ uno::Reference<xml::sax::XDocumentHandler> const& xDocumentHandler,
+ SignatureInformation::X509CertInfo const& rCertInfo)
+{
+ xDocumentHandler->startElement("xd:Cert", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ xDocumentHandler->startElement("xd:CertDigest", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ DocumentSignatureHelper::writeDigestMethod(xDocumentHandler);
+ xDocumentHandler->startElement("DigestValue", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ assert(!rCertInfo.CertDigest.isEmpty());
+ xDocumentHandler->characters(rCertInfo.CertDigest);
+ xDocumentHandler->endElement("DigestValue");
+ xDocumentHandler->endElement("xd:CertDigest");
+ xDocumentHandler->startElement("xd:IssuerSerial", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ xDocumentHandler->startElement("X509IssuerName", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ xDocumentHandler->characters(rCertInfo.X509IssuerName);
+ xDocumentHandler->endElement("X509IssuerName");
+ xDocumentHandler->startElement("X509SerialNumber", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ xDocumentHandler->characters(rCertInfo.X509SerialNumber);
+ xDocumentHandler->endElement("X509SerialNumber");
+ xDocumentHandler->endElement("xd:IssuerSerial");
+ xDocumentHandler->endElement("xd:Cert");
+}
+
+void DocumentSignatureHelper::writeSignedProperties(
+ const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler,
+ const SignatureInformation& signatureInfo,
+ const OUString& sDate, const bool bWriteSignatureLineData)
+{
+ {
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("Id", "idSignedProperties_" + signatureInfo.ouSignatureId);
+ xDocumentHandler->startElement("xd:SignedProperties", uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+
+ xDocumentHandler->startElement("xd:SignedSignatureProperties", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ xDocumentHandler->startElement("xd:SigningTime", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ xDocumentHandler->characters(sDate);
+ xDocumentHandler->endElement("xd:SigningTime");
+ xDocumentHandler->startElement("xd:SigningCertificate", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ assert(signatureInfo.GetSigningCertificate() || !signatureInfo.ouGpgKeyID.isEmpty());
+ if (signatureInfo.GetSigningCertificate())
+ {
+ // how should this deal with multiple X509Data elements?
+ // for now, let's write all of the certificates ...
+ for (auto const& rData : signatureInfo.X509Datas)
+ {
+ for (auto const& it : rData)
+ {
+ WriteXadesCert(xDocumentHandler, it);
+ }
+ }
+ }
+ else
+ {
+ // for PGP, write empty mandatory X509IssuerName, X509SerialNumber
+ SignatureInformation::X509CertInfo temp;
+ temp.CertDigest = signatureInfo.ouGpgKeyID;
+ WriteXadesCert(xDocumentHandler, temp);
+ }
+ xDocumentHandler->endElement("xd:SigningCertificate");
+ xDocumentHandler->startElement("xd:SignaturePolicyIdentifier", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ xDocumentHandler->startElement("xd:SignaturePolicyImplied", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ xDocumentHandler->endElement("xd:SignaturePolicyImplied");
+ xDocumentHandler->endElement("xd:SignaturePolicyIdentifier");
+
+ if (bWriteSignatureLineData && !signatureInfo.ouSignatureLineId.isEmpty()
+ && signatureInfo.aValidSignatureImage.is() && signatureInfo.aInvalidSignatureImage.is())
+ {
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute(
+ "xmlns:loext", "urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0");
+ xDocumentHandler->startElement(
+ "loext:SignatureLine",
+ Reference<XAttributeList>(pAttributeList));
+
+ {
+ // Write SignatureLineId element
+ xDocumentHandler->startElement(
+ "loext:SignatureLineId",
+ Reference<XAttributeList>(new comphelper::AttributeList()));
+ xDocumentHandler->characters(signatureInfo.ouSignatureLineId);
+ xDocumentHandler->endElement("loext:SignatureLineId");
+ }
+
+ {
+ // Write SignatureLineValidImage element
+ xDocumentHandler->startElement(
+ "loext:SignatureLineValidImage",
+ Reference<XAttributeList>(new comphelper::AttributeList()));
+
+ OUString aGraphicInBase64;
+ Graphic aGraphic(signatureInfo.aValidSignatureImage);
+ if (!XOutBitmap::GraphicToBase64(aGraphic, aGraphicInBase64, false))
+ SAL_WARN("xmlsecurity.helper", "could not convert graphic to base64");
+
+ xDocumentHandler->characters(aGraphicInBase64);
+ xDocumentHandler->endElement("loext:SignatureLineValidImage");
+ }
+
+ {
+ // Write SignatureLineInvalidImage element
+ xDocumentHandler->startElement(
+ "loext:SignatureLineInvalidImage",
+ Reference<XAttributeList>(new comphelper::AttributeList()));
+ OUString aGraphicInBase64;
+ Graphic aGraphic(signatureInfo.aInvalidSignatureImage);
+ if (!XOutBitmap::GraphicToBase64(aGraphic, aGraphicInBase64, false))
+ SAL_WARN("xmlsecurity.helper", "could not convert graphic to base64");
+ xDocumentHandler->characters(aGraphicInBase64);
+ xDocumentHandler->endElement("loext:SignatureLineInvalidImage");
+ }
+
+ xDocumentHandler->endElement("loext:SignatureLine");
+ }
+
+ xDocumentHandler->endElement("xd:SignedSignatureProperties");
+
+ xDocumentHandler->endElement("xd:SignedProperties");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/documentsignaturemanager.cxx b/xmlsecurity/source/helper/documentsignaturemanager.cxx
new file mode 100644
index 0000000000..80d74c27ba
--- /dev/null
+++ b/xmlsecurity/source/helper/documentsignaturemanager.cxx
@@ -0,0 +1,705 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <documentsignaturemanager.hxx>
+#include <config_gpgme.h>
+
+#include <gpg/SEInitializer.hxx>
+
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/io/XTempFile.hpp>
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/xml/crypto/SEInitializer.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/packages/manifest/ManifestReader.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/xml/sax/XWriter.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+#include <comphelper/base64.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <tools/datetime.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <certificate.hxx>
+#include <biginteger.hxx>
+
+#include <xmlsec/xmlsec_init.hxx>
+
+#include <pdfsignaturehelper.hxx>
+
+#include <memory>
+
+using namespace css;
+using namespace css::graphic;
+using namespace css::uno;
+
+DocumentSignatureManager::DocumentSignatureManager(
+ const uno::Reference<uno::XComponentContext>& xContext, DocumentSignatureMode eMode)
+ : mxContext(xContext)
+ , maSignatureHelper(xContext)
+ , meSignatureMode(eMode)
+{
+}
+
+DocumentSignatureManager::~DocumentSignatureManager() { deInitXmlSec(); }
+
+bool DocumentSignatureManager::init()
+{
+ SAL_WARN_IF(mxSEInitializer.is(), "xmlsecurity.helper",
+ "DocumentSignatureManager::Init - mxSEInitializer already set!");
+ SAL_WARN_IF(mxSecurityContext.is(), "xmlsecurity.helper",
+ "DocumentSignatureManager::Init - mxSecurityContext already set!");
+ SAL_WARN_IF(mxGpgSEInitializer.is(), "xmlsecurity.helper",
+ "DocumentSignatureManager::Init - mxGpgSEInitializer already set!");
+
+ // xmlsec is needed by both services, so init before those
+ initXmlSec();
+
+ mxSEInitializer = xml::crypto::SEInitializer::create(mxContext);
+#if HAVE_FEATURE_GPGME
+ mxGpgSEInitializer.set(new SEInitializerGpg());
+#endif
+
+ if (mxSEInitializer.is())
+ mxSecurityContext = mxSEInitializer->createSecurityContext(OUString());
+
+#if HAVE_FEATURE_GPGME
+ if (mxGpgSEInitializer.is())
+ mxGpgSecurityContext = mxGpgSEInitializer->createSecurityContext(OUString());
+
+ return mxSecurityContext.is() || mxGpgSecurityContext.is();
+#else
+ return mxSecurityContext.is();
+#endif
+}
+
+PDFSignatureHelper& DocumentSignatureManager::getPDFSignatureHelper()
+{
+ bool bInit = true;
+ if (!mxSecurityContext.is())
+ bInit = init();
+
+ SAL_WARN_IF(!bInit, "xmlsecurity.comp", "Error initializing security context!");
+
+ if (!mpPDFSignatureHelper)
+ mpPDFSignatureHelper = std::make_unique<PDFSignatureHelper>();
+
+ return *mpPDFSignatureHelper;
+}
+
+#if 0 // For some reason does not work
+bool DocumentSignatureManager::IsXAdESRelevant()
+{
+ if (mxStore.is())
+ {
+ // ZIP-based: ODF or OOXML.
+ maSignatureHelper.StartMission();
+
+ SignatureStreamHelper aStreamHelper = ImplOpenSignatureStream(embed::ElementModes::READ, /*bUseTempStream=*/true);
+ if (aStreamHelper.nStorageFormat == embed::StorageFormats::OFOPXML)
+ {
+ maSignatureHelper.EndMission();
+ return false;
+ }
+ // FIXME: How to figure out if it is ODF 1.2?
+ maSignatureHelper.EndMission();
+ return true;
+ }
+ return false;
+}
+#endif
+
+bool DocumentSignatureManager::readManifest()
+{
+ // Check if manifest was already read
+ if (m_manifest.hasElements())
+ return true;
+
+ if (!mxContext.is())
+ return false;
+
+ if (!mxStore.is())
+ return false;
+
+ uno::Reference<packages::manifest::XManifestReader> xReader
+ = packages::manifest::ManifestReader::create(mxContext);
+
+ if (mxStore->hasByName("META-INF"))
+ {
+ //Get the manifest.xml
+ uno::Reference<embed::XStorage> xSubStore(
+ mxStore->openStorageElement("META-INF", embed::ElementModes::READ), UNO_SET_THROW);
+
+ uno::Reference<io::XInputStream> xStream(
+ xSubStore->openStreamElement("manifest.xml", css::embed::ElementModes::READ),
+ UNO_QUERY_THROW);
+
+ m_manifest = xReader->readManifestSequence(xStream);
+ }
+ return true;
+}
+
+/* Using the zip storage, we cannot get the properties "MediaType" and "IsEncrypted"
+ We use the manifest to find out if a file is xml and if it is encrypted.
+ The parameter is an encoded uri. However, the manifest contains paths. Therefore
+ the path is encoded as uri, so they can be compared.
+*/
+bool DocumentSignatureManager::isXML(std::u16string_view rURI)
+{
+ SAL_WARN_IF(!mxStore.is(), "xmlsecurity.helper", "empty storage reference");
+
+ bool bIsXML = false;
+ bool bPropsAvailable = false;
+ static constexpr OUStringLiteral sPropFullPath(u"FullPath");
+ static constexpr OUStringLiteral sPropMediaType(u"MediaType");
+ static constexpr OUStringLiteral sPropDigest(u"Digest");
+
+ if (readManifest())
+ {
+ for (const uno::Sequence<beans::PropertyValue>& entry : std::as_const(m_manifest))
+ {
+ OUString sPath;
+ OUString sMediaType;
+ bool bEncrypted = false;
+ for (const beans::PropertyValue& prop : entry)
+ {
+ if (prop.Name == sPropFullPath)
+ prop.Value >>= sPath;
+ else if (prop.Name == sPropMediaType)
+ prop.Value >>= sMediaType;
+ else if (prop.Name == sPropDigest)
+ bEncrypted = true;
+ }
+ if (DocumentSignatureHelper::equalsReferenceUriManifestPath(rURI, sPath))
+ {
+ bIsXML = sMediaType == "text/xml" && !bEncrypted;
+ bPropsAvailable = true;
+ break;
+ }
+ }
+ }
+ if (!bPropsAvailable)
+ {
+ //This would be the case for at least mimetype, META-INF/manifest.xml
+ //META-INF/macrosignatures.xml.
+ //Files can only be encrypted if they are in the manifest.xml.
+ //That is, the current file cannot be encrypted, otherwise bPropsAvailable
+ //would be true.
+ size_t nSep = rURI.rfind('.');
+ if (nSep != std::u16string_view::npos)
+ {
+ std::u16string_view aExt = rURI.substr(nSep + 1);
+ if (o3tl::equalsIgnoreAsciiCase(aExt, u"XML"))
+ bIsXML = true;
+ }
+ }
+ return bIsXML;
+}
+
+//If bTempStream is true, then a temporary stream is return. If it is false then, the actual
+//signature stream is used.
+//Every time the user presses Add a new temporary stream is created.
+//We keep the temporary stream as member because ImplGetSignatureInformations
+//will later access the stream to create DocumentSignatureInformation objects
+//which are stored in maCurrentSignatureInformations.
+SignatureStreamHelper DocumentSignatureManager::ImplOpenSignatureStream(sal_Int32 nStreamOpenMode,
+ bool bTempStream)
+{
+ SignatureStreamHelper aHelper;
+ if (mxStore.is() && mxStore->hasByName("[Content_Types].xml"))
+ aHelper.nStorageFormat = embed::StorageFormats::OFOPXML;
+
+ if (bTempStream)
+ {
+ if (nStreamOpenMode & embed::ElementModes::TRUNCATE)
+ {
+ //We write always into a new temporary stream.
+ mxTempSignatureStream = new utl::TempFileFastService;
+ if (aHelper.nStorageFormat != embed::StorageFormats::OFOPXML)
+ aHelper.xSignatureStream = mxTempSignatureStream;
+ else
+ {
+ mxTempSignatureStorage = comphelper::OStorageHelper::GetStorageOfFormatFromStream(
+ ZIP_STORAGE_FORMAT_STRING, mxTempSignatureStream);
+ aHelper.xSignatureStorage = mxTempSignatureStorage;
+ }
+ }
+ else
+ {
+ //When we read from the temp stream, then we must have previously
+ //created one.
+ SAL_WARN_IF(!mxTempSignatureStream.is(), "xmlsecurity.helper",
+ "empty temp. signature stream reference");
+ }
+ aHelper.xSignatureStream = mxTempSignatureStream;
+ if (aHelper.nStorageFormat == embed::StorageFormats::OFOPXML)
+ aHelper.xSignatureStorage = mxTempSignatureStorage;
+ }
+ else
+ {
+ //No temporary stream
+ if (!mxSignatureStream.is())
+ {
+ //We may not have a dedicated stream for writing the signature
+ //So we take one directly from the storage
+ //Or DocumentDigitalSignatures::showDocumentContentSignatures was called,
+ //in which case Add/Remove is not allowed. This is done, for example, if the
+ //document is readonly
+ aHelper = DocumentSignatureHelper::OpenSignatureStream(mxStore, nStreamOpenMode,
+ meSignatureMode);
+ }
+ else
+ {
+ aHelper.xSignatureStream = mxSignatureStream;
+ }
+ }
+
+ if (nStreamOpenMode & embed::ElementModes::TRUNCATE)
+ {
+ if (aHelper.xSignatureStream.is()
+ && aHelper.nStorageFormat != embed::StorageFormats::OFOPXML)
+ {
+ uno::Reference<io::XTruncate> xTruncate(aHelper.xSignatureStream, uno::UNO_QUERY_THROW);
+ xTruncate->truncate();
+ }
+ }
+ else if (bTempStream || mxSignatureStream.is())
+ {
+ //In case we read the signature stream from the storage directly,
+ //which is the case when DocumentDigitalSignatures::showDocumentContentSignatures
+ //then XSeakable is not supported
+ uno::Reference<io::XSeekable> xSeek(aHelper.xSignatureStream, uno::UNO_QUERY_THROW);
+ xSeek->seek(0);
+ }
+
+ return aHelper;
+}
+
+bool DocumentSignatureManager::add(
+ const uno::Reference<security::XCertificate>& xCert,
+ const uno::Reference<xml::crypto::XXMLSecurityContext>& xSecurityContext,
+ const OUString& rDescription, sal_Int32& nSecurityId, bool bAdESCompliant,
+ const OUString& rSignatureLineId, const Reference<XGraphic>& xValidGraphic,
+ const Reference<XGraphic>& xInvalidGraphic)
+{
+ if (!xCert.is())
+ {
+ SAL_WARN("xmlsecurity.helper", "no certificate selected");
+ return false;
+ }
+
+ // GPG or X509 key?
+ uno::Reference<lang::XServiceInfo> xServiceInfo(xSecurityContext, uno::UNO_QUERY);
+ if (xServiceInfo->getImplementationName()
+ == "com.sun.star.xml.security.gpg.XMLSecurityContext_GpgImpl")
+ {
+ // GPG keys only really have PGPKeyId and PGPKeyPacket
+ if (!mxStore.is())
+ {
+ SAL_WARN("xmlsecurity.helper", "cannot sign pdfs with GPG keys");
+ return false;
+ }
+
+ maSignatureHelper.StartMission(xSecurityContext);
+
+ nSecurityId = maSignatureHelper.GetNewSecurityId();
+
+ OUStringBuffer aStrBuffer;
+ comphelper::Base64::encode(aStrBuffer, xCert->getEncoded());
+
+ OUString aKeyId;
+ if (auto pCertificate = dynamic_cast<xmlsecurity::Certificate*>(xCert.get()))
+ {
+ OUStringBuffer aBuffer;
+ comphelper::Base64::encode(aBuffer, pCertificate->getSHA256Thumbprint());
+ aKeyId = aBuffer.makeStringAndClear();
+ }
+ else
+ SAL_WARN("xmlsecurity.helper",
+ "XCertificate implementation without an xmlsecurity::Certificate one");
+
+ maSignatureHelper.SetGpgCertificate(nSecurityId, aKeyId, aStrBuffer.makeStringAndClear(),
+ xCert->getIssuerName());
+ }
+ else
+ {
+ OUString aCertSerial = xmlsecurity::bigIntegerToNumericString(xCert->getSerialNumber());
+ if (aCertSerial.isEmpty())
+ {
+ SAL_WARN("xmlsecurity.helper", "Error in Certificate, problem with serial number!");
+ return false;
+ }
+
+ if (!mxStore.is())
+ {
+ // Something not ZIP based, try PDF.
+ nSecurityId = getPDFSignatureHelper().GetNewSecurityId();
+ getPDFSignatureHelper().SetX509Certificate(xCert);
+ getPDFSignatureHelper().SetDescription(rDescription);
+ uno::Reference<io::XInputStream> xInputStream(mxSignatureStream, uno::UNO_QUERY);
+ if (!getPDFSignatureHelper().Sign(mxModel, xInputStream, bAdESCompliant))
+ {
+ SAL_WARN("xmlsecurity.helper", "PDFSignatureHelper::Sign() failed");
+ return false;
+ }
+ return true;
+ }
+
+ maSignatureHelper.StartMission(xSecurityContext);
+
+ nSecurityId = maSignatureHelper.GetNewSecurityId();
+
+ OUStringBuffer aStrBuffer;
+ comphelper::Base64::encode(aStrBuffer, xCert->getEncoded());
+
+ OUString aCertDigest;
+ svl::crypto::SignatureMethodAlgorithm eAlgorithmID
+ = svl::crypto::SignatureMethodAlgorithm::RSA;
+ if (auto pCertificate = dynamic_cast<xmlsecurity::Certificate*>(xCert.get()))
+ {
+ OUStringBuffer aBuffer;
+ comphelper::Base64::encode(aBuffer, pCertificate->getSHA256Thumbprint());
+ aCertDigest = aBuffer.makeStringAndClear();
+
+ eAlgorithmID = pCertificate->getSignatureMethodAlgorithm();
+ }
+ else
+ SAL_WARN("xmlsecurity.helper",
+ "XCertificate implementation without an xmlsecurity::Certificate one");
+
+ maSignatureHelper.SetX509Certificate(nSecurityId, xCert->getIssuerName(), aCertSerial,
+ aStrBuffer.makeStringAndClear(), aCertDigest,
+ eAlgorithmID);
+ }
+
+ const uno::Sequence<uno::Reference<security::XCertificate>> aCertPath
+ = xSecurityContext->getSecurityEnvironment()->buildCertificatePath(xCert);
+
+ OUStringBuffer aStrBuffer;
+ for (uno::Reference<security::XCertificate> const& rxCertificate : aCertPath)
+ {
+ comphelper::Base64::encode(aStrBuffer, rxCertificate->getEncoded());
+ OUString aString = aStrBuffer.makeStringAndClear();
+ maSignatureHelper.AddEncapsulatedX509Certificate(aString);
+ }
+
+ std::vector<OUString> aElements = DocumentSignatureHelper::CreateElementList(
+ mxStore, meSignatureMode, DocumentSignatureAlgorithm::OOo3_2);
+ DocumentSignatureHelper::AppendContentTypes(mxStore, aElements);
+
+ for (OUString const& rUri : aElements)
+ {
+ bool bBinaryMode = !isXML(rUri);
+ maSignatureHelper.AddForSigning(nSecurityId, rUri, bBinaryMode, bAdESCompliant);
+ }
+
+ maSignatureHelper.SetDateTime(nSecurityId, DateTime(DateTime::SYSTEM));
+ maSignatureHelper.SetDescription(nSecurityId, rDescription);
+
+ if (!rSignatureLineId.isEmpty())
+ maSignatureHelper.SetSignatureLineId(nSecurityId, rSignatureLineId);
+
+ if (xValidGraphic.is())
+ maSignatureHelper.SetSignatureLineValidGraphic(nSecurityId, xValidGraphic);
+
+ if (xInvalidGraphic.is())
+ maSignatureHelper.SetSignatureLineInvalidGraphic(nSecurityId, xInvalidGraphic);
+
+ // We open a signature stream in which the existing and the new
+ //signature is written. ImplGetSignatureInformation (later in this function) will
+ //then read the stream and fill maCurrentSignatureInformations. The final signature
+ //is written when the user presses OK. Then only maCurrentSignatureInformation and
+ //a sax writer are used to write the information.
+ SignatureStreamHelper aStreamHelper
+ = ImplOpenSignatureStream(embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE, true);
+
+ if (aStreamHelper.nStorageFormat != embed::StorageFormats::OFOPXML)
+ {
+ uno::Reference<io::XOutputStream> xOutputStream(aStreamHelper.xSignatureStream,
+ uno::UNO_QUERY_THROW);
+ uno::Reference<xml::sax::XWriter> xSaxWriter
+ = maSignatureHelper.CreateDocumentHandlerWithHeader(xOutputStream);
+
+ // Export old signatures...
+ uno::Reference<xml::sax::XDocumentHandler> xDocumentHandler(xSaxWriter,
+ uno::UNO_QUERY_THROW);
+ std::size_t nInfos = maCurrentSignatureInformations.size();
+ for (std::size_t n = 0; n < nInfos; n++)
+ XMLSignatureHelper::ExportSignature(xDocumentHandler, maCurrentSignatureInformations[n],
+ bAdESCompliant);
+
+ // Create a new one...
+ maSignatureHelper.CreateAndWriteSignature(xDocumentHandler, bAdESCompliant);
+
+ // That's it...
+ XMLSignatureHelper::CloseDocumentHandler(xDocumentHandler);
+ }
+ else
+ {
+ // OOXML
+
+ // Handle relations.
+ maSignatureHelper.EnsureSignaturesRelation(mxStore, /*bAdd=*/true);
+ // Old signatures + the new one.
+ int nSignatureCount = maCurrentSignatureInformations.size() + 1;
+ maSignatureHelper.ExportSignatureRelations(aStreamHelper.xSignatureStorage,
+ nSignatureCount);
+
+ // Export old signatures.
+ for (std::size_t i = 0; i < maCurrentSignatureInformations.size(); ++i)
+ maSignatureHelper.ExportOOXMLSignature(mxStore, aStreamHelper.xSignatureStorage,
+ maCurrentSignatureInformations[i], i + 1);
+
+ // Create a new signature.
+ maSignatureHelper.CreateAndWriteOOXMLSignature(mxStore, aStreamHelper.xSignatureStorage,
+ nSignatureCount);
+
+ // Flush objects.
+ uno::Reference<embed::XTransactedObject> xTransact(aStreamHelper.xSignatureStorage,
+ uno::UNO_QUERY);
+ xTransact->commit();
+ uno::Reference<io::XOutputStream> xOutputStream(aStreamHelper.xSignatureStream,
+ uno::UNO_QUERY);
+ xOutputStream->closeOutput();
+
+ uno::Reference<io::XTempFile> xTempFile(aStreamHelper.xSignatureStream, uno::UNO_QUERY);
+ SAL_INFO("xmlsecurity.helper",
+ "DocumentSignatureManager::add temporary storage at " << xTempFile->getUri());
+ }
+
+ maSignatureHelper.EndMission();
+ return true;
+}
+
+void DocumentSignatureManager::remove(sal_uInt16 nPosition)
+{
+ if (!mxStore.is())
+ {
+ // Something not ZIP based, try PDF.
+ uno::Reference<io::XInputStream> xInputStream(mxSignatureStream, uno::UNO_QUERY);
+ if (!PDFSignatureHelper::RemoveSignature(xInputStream, nPosition))
+ {
+ SAL_WARN("xmlsecurity.helper", "PDFSignatureHelper::RemoveSignature() failed");
+ return;
+ }
+
+ // Only erase when the removal was successful, it may fail for PDF.
+ // Also, erase the requested and all following signatures, as PDF signatures are always chained.
+ maCurrentSignatureInformations.erase(maCurrentSignatureInformations.begin() + nPosition,
+ maCurrentSignatureInformations.end());
+ return;
+ }
+
+ maCurrentSignatureInformations.erase(maCurrentSignatureInformations.begin() + nPosition);
+
+ // Export all other signatures...
+ SignatureStreamHelper aStreamHelper = ImplOpenSignatureStream(
+ embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE, /*bTempStream=*/true);
+
+ if (aStreamHelper.nStorageFormat != embed::StorageFormats::OFOPXML)
+ {
+ uno::Reference<io::XOutputStream> xOutputStream(aStreamHelper.xSignatureStream,
+ uno::UNO_QUERY_THROW);
+ uno::Reference<xml::sax::XWriter> xSaxWriter
+ = maSignatureHelper.CreateDocumentHandlerWithHeader(xOutputStream);
+
+ uno::Reference<xml::sax::XDocumentHandler> xDocumentHandler(xSaxWriter,
+ uno::UNO_QUERY_THROW);
+ std::size_t nInfos = maCurrentSignatureInformations.size();
+ for (std::size_t n = 0; n < nInfos; ++n)
+ XMLSignatureHelper::ExportSignature(xDocumentHandler, maCurrentSignatureInformations[n],
+ false /* ??? */);
+
+ XMLSignatureHelper::CloseDocumentHandler(xDocumentHandler);
+ }
+ else
+ {
+ // OOXML
+
+ // Handle relations.
+ int nSignatureCount = maCurrentSignatureInformations.size();
+ maSignatureHelper.ExportSignatureRelations(aStreamHelper.xSignatureStorage,
+ nSignatureCount);
+
+ // Export old signatures.
+ for (std::size_t i = 0; i < maCurrentSignatureInformations.size(); ++i)
+ maSignatureHelper.ExportOOXMLSignature(mxStore, aStreamHelper.xSignatureStorage,
+ maCurrentSignatureInformations[i], i + 1);
+
+ // Flush objects.
+ uno::Reference<embed::XTransactedObject> xTransact(aStreamHelper.xSignatureStorage,
+ uno::UNO_QUERY);
+ xTransact->commit();
+ uno::Reference<io::XOutputStream> xOutputStream(aStreamHelper.xSignatureStream,
+ uno::UNO_QUERY);
+ xOutputStream->closeOutput();
+
+ uno::Reference<io::XTempFile> xTempFile(aStreamHelper.xSignatureStream, uno::UNO_QUERY);
+ SAL_INFO("xmlsecurity.helper", "DocumentSignatureManager::remove: temporary storage is at "
+ << xTempFile->getUri());
+ }
+}
+
+void DocumentSignatureManager::read(bool bUseTempStream, bool bCacheLastSignature)
+{
+ maCurrentSignatureInformations.clear();
+
+ if (mxStore.is())
+ {
+ // ZIP-based: ODF or OOXML.
+ maSignatureHelper.StartMission(mxSecurityContext);
+
+ SignatureStreamHelper aStreamHelper
+ = ImplOpenSignatureStream(embed::ElementModes::READ, bUseTempStream);
+ if (aStreamHelper.nStorageFormat != embed::StorageFormats::OFOPXML
+ && aStreamHelper.xSignatureStream.is())
+ {
+ uno::Reference<io::XInputStream> xInputStream(aStreamHelper.xSignatureStream,
+ uno::UNO_QUERY);
+ maSignatureHelper.ReadAndVerifySignature(xInputStream);
+ }
+ else if (aStreamHelper.nStorageFormat == embed::StorageFormats::OFOPXML
+ && aStreamHelper.xSignatureStorage.is())
+ maSignatureHelper.ReadAndVerifySignatureStorage(aStreamHelper.xSignatureStorage,
+ bCacheLastSignature);
+ maSignatureHelper.EndMission();
+
+ // this parses the XML independently from ImplVerifySignatures() - check
+ // certificates here too ...
+ for (auto const& it : maSignatureHelper.GetSignatureInformations())
+ {
+ if (!it.X509Datas.empty())
+ {
+ uno::Reference<xml::crypto::XSecurityEnvironment> const xSecEnv(
+ getSecurityEnvironment());
+ getSignatureHelper().CheckAndUpdateSignatureInformation(xSecEnv, it);
+ }
+ }
+
+ maCurrentSignatureInformations = maSignatureHelper.GetSignatureInformations();
+ }
+ else
+ {
+ // Something not ZIP based, try PDF.
+ uno::Reference<io::XInputStream> xInputStream(mxSignatureStream, uno::UNO_QUERY);
+ if (getPDFSignatureHelper().ReadAndVerifySignature(xInputStream))
+ maCurrentSignatureInformations = getPDFSignatureHelper().GetSignatureInformations();
+ }
+}
+
+void DocumentSignatureManager::write(bool bXAdESCompliantIfODF)
+{
+ if (!mxStore.is())
+ {
+ // Something not ZIP based, assume PDF, which is written directly in add() already.
+ return;
+ }
+
+ // Export all other signatures...
+ SignatureStreamHelper aStreamHelper = ImplOpenSignatureStream(
+ embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE, false);
+
+ if (aStreamHelper.xSignatureStream.is()
+ && aStreamHelper.nStorageFormat != embed::StorageFormats::OFOPXML)
+ {
+ // ODF
+ uno::Reference<io::XOutputStream> xOutputStream(aStreamHelper.xSignatureStream,
+ uno::UNO_QUERY);
+ uno::Reference<xml::sax::XWriter> xSaxWriter
+ = maSignatureHelper.CreateDocumentHandlerWithHeader(xOutputStream);
+
+ uno::Reference<xml::sax::XDocumentHandler> xDocumentHandler(xSaxWriter,
+ uno::UNO_QUERY_THROW);
+ std::size_t nInfos = maCurrentSignatureInformations.size();
+ for (std::size_t n = 0; n < nInfos; ++n)
+ XMLSignatureHelper::ExportSignature(xDocumentHandler, maCurrentSignatureInformations[n],
+ bXAdESCompliantIfODF);
+
+ XMLSignatureHelper::CloseDocumentHandler(xDocumentHandler);
+ }
+ else if (aStreamHelper.xSignatureStorage.is()
+ && aStreamHelper.nStorageFormat == embed::StorageFormats::OFOPXML)
+ {
+ // OOXML
+ std::size_t nSignatureCount = maCurrentSignatureInformations.size();
+ maSignatureHelper.ExportSignatureContentTypes(mxStore, nSignatureCount);
+ if (nSignatureCount > 0)
+ maSignatureHelper.ExportSignatureRelations(aStreamHelper.xSignatureStorage,
+ nSignatureCount);
+ else
+ {
+ // Removing all signatures: then need to remove the signature relation as well.
+ maSignatureHelper.EnsureSignaturesRelation(mxStore, /*bAdd=*/false);
+ // Also remove the whole signature sub-storage: release our read-write reference + remove the element.
+ aStreamHelper = SignatureStreamHelper();
+ mxStore->removeElement("_xmlsignatures");
+ }
+
+ for (std::size_t i = 0; i < nSignatureCount; ++i)
+ maSignatureHelper.ExportOOXMLSignature(mxStore, aStreamHelper.xSignatureStorage,
+ maCurrentSignatureInformations[i], i + 1);
+ }
+
+ // If stream was not provided, we are responsible for committing it...
+ if (!mxSignatureStream.is() && aStreamHelper.xSignatureStorage.is())
+ {
+ uno::Reference<embed::XTransactedObject> xTrans(aStreamHelper.xSignatureStorage,
+ uno::UNO_QUERY);
+ xTrans->commit();
+ }
+}
+
+uno::Reference<xml::crypto::XSecurityEnvironment> DocumentSignatureManager::getSecurityEnvironment()
+{
+ return mxSecurityContext.is() ? mxSecurityContext->getSecurityEnvironment()
+ : uno::Reference<xml::crypto::XSecurityEnvironment>();
+}
+
+uno::Reference<xml::crypto::XSecurityEnvironment>
+DocumentSignatureManager::getGpgSecurityEnvironment()
+{
+ return mxGpgSecurityContext.is() ? mxGpgSecurityContext->getSecurityEnvironment()
+ : uno::Reference<xml::crypto::XSecurityEnvironment>();
+}
+
+uno::Reference<xml::crypto::XXMLSecurityContext> const&
+DocumentSignatureManager::getSecurityContext() const
+{
+ return mxSecurityContext;
+}
+
+uno::Reference<xml::crypto::XXMLSecurityContext> const&
+DocumentSignatureManager::getGpgSecurityContext() const
+{
+ return mxGpgSecurityContext;
+}
+
+void DocumentSignatureManager::setModel(const uno::Reference<frame::XModel>& xModel)
+{
+ mxModel = xModel;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/ooxmlsecexporter.cxx b/xmlsecurity/source/helper/ooxmlsecexporter.cxx
new file mode 100644
index 0000000000..804cfd2748
--- /dev/null
+++ b/xmlsecurity/source/helper/ooxmlsecexporter.cxx
@@ -0,0 +1,565 @@
+/* -*- 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/.
+ */
+
+#include "ooxmlsecexporter.hxx"
+
+#include <algorithm>
+#include <memory>
+#include <string_view>
+
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XHierarchicalStorageAccess.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+
+#include <comphelper/attributelist.hxx>
+#include <comphelper/ofopxmlhelper.hxx>
+#include <o3tl/string_view.hxx>
+#include <rtl/ref.hxx>
+#include <sal/log.hxx>
+#include <svx/xoutbmp.hxx>
+#include <unotools/datetime.hxx>
+#include <vcl/salctype.hxx>
+
+#include <documentsignaturehelper.hxx>
+#include <xsecctl.hxx>
+
+using namespace com::sun::star;
+using namespace css::xml::sax;
+
+struct OOXMLSecExporter::Impl
+{
+private:
+ const uno::Reference<uno::XComponentContext>& m_xComponentContext;
+ const uno::Reference<embed::XStorage>& m_xRootStorage;
+ const uno::Reference<xml::sax::XDocumentHandler>& m_xDocumentHandler;
+ const SignatureInformation& m_rInformation;
+ OUString m_aSignatureTimeValue;
+
+public:
+ Impl(const uno::Reference<uno::XComponentContext>& xComponentContext,
+ const uno::Reference<embed::XStorage>& xRootStorage,
+ const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler,
+ const SignatureInformation& rInformation)
+ : m_xComponentContext(xComponentContext)
+ , m_xRootStorage(xRootStorage)
+ , m_xDocumentHandler(xDocumentHandler)
+ , m_rInformation(rInformation)
+ {
+ }
+
+ /// Should we intentionally not sign this stream?
+ static bool isOOXMLDenylist(std::u16string_view rStreamName);
+ /// Should we intentionally not sign this relation type?
+ static bool isOOXMLRelationDenylist(const OUString& rRelationName);
+
+ const uno::Reference<xml::sax::XDocumentHandler>& getDocumentHandler() const
+ {
+ return m_xDocumentHandler;
+ }
+
+ void writeSignature();
+ void writeSignedInfo();
+ void writeCanonicalizationMethod();
+ void writeCanonicalizationTransform();
+ void writeSignatureMethod();
+ void writeSignedInfoReferences();
+ void writeSignatureValue();
+ void writeKeyInfo();
+ void writePackageObject();
+ void writeManifest();
+ void writeRelationshipTransform(const OUString& rURI);
+ /// Writes <SignatureProperties> inside idPackageObject.
+ void writePackageObjectSignatureProperties();
+ /// Writes a single <Reference> inside <Manifest>.
+ void writeManifestReference(const SignatureReferenceInformation& rReference);
+ void writeOfficeObject();
+ /// Writes <SignatureInfoV1>.
+ void writeSignatureInfo();
+ void writePackageSignature();
+ void writeSignatureLineImages();
+};
+
+bool OOXMLSecExporter::Impl::isOOXMLDenylist(std::u16string_view rStreamName)
+{
+ static const std::initializer_list<std::u16string_view> vDenylist
+ = { u"/%5BContent_Types%5D.xml", u"/docProps/app.xml", u"/docProps/core.xml",
+ // Don't attempt to sign other signatures for now.
+ u"/_xmlsignatures" };
+ // Just check the prefix, as we don't care about the content type part of the stream name.
+ return std::any_of(vDenylist.begin(), vDenylist.end(),
+ [&](const std::u16string_view& rLiteral) {
+ return o3tl::starts_with(rStreamName, rLiteral);
+ });
+}
+
+bool OOXMLSecExporter::Impl::isOOXMLRelationDenylist(const OUString& rRelationName)
+{
+ static const std::initializer_list<std::u16string_view> vDenylist = {
+ u"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties",
+ u"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties",
+ u"http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin"
+ };
+ return std::find(vDenylist.begin(), vDenylist.end(), rRelationName) != vDenylist.end();
+}
+
+void OOXMLSecExporter::Impl::writeSignedInfo()
+{
+ m_xDocumentHandler->startElement(
+ "SignedInfo", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+
+ writeCanonicalizationMethod();
+ writeSignatureMethod();
+ writeSignedInfoReferences();
+
+ m_xDocumentHandler->endElement("SignedInfo");
+}
+
+void OOXMLSecExporter::Impl::writeCanonicalizationMethod()
+{
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("Algorithm", ALGO_C14N);
+ m_xDocumentHandler->startElement("CanonicalizationMethod",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ m_xDocumentHandler->endElement("CanonicalizationMethod");
+}
+
+void OOXMLSecExporter::Impl::writeCanonicalizationTransform()
+{
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("Algorithm", ALGO_C14N);
+ m_xDocumentHandler->startElement("Transform",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ m_xDocumentHandler->endElement("Transform");
+}
+
+void OOXMLSecExporter::Impl::writeSignatureMethod()
+{
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+
+ if (m_rInformation.eAlgorithmID == svl::crypto::SignatureMethodAlgorithm::ECDSA)
+ pAttributeList->AddAttribute("Algorithm", ALGO_ECDSASHA256);
+ else
+ pAttributeList->AddAttribute("Algorithm", ALGO_RSASHA256);
+
+ m_xDocumentHandler->startElement("SignatureMethod",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ m_xDocumentHandler->endElement("SignatureMethod");
+}
+
+void OOXMLSecExporter::Impl::writeSignedInfoReferences()
+{
+ const SignatureReferenceInformations& rReferences = m_rInformation.vSignatureReferenceInfors;
+ for (const SignatureReferenceInformation& rReference : rReferences)
+ {
+ if (rReference.nType == SignatureReferenceType::SAMEDOCUMENT)
+ {
+ {
+ rtl::Reference<comphelper::AttributeList> pAttributeList(
+ new comphelper::AttributeList());
+ if (!rReference.ouURI.startsWith("idSignedProperties"))
+ pAttributeList->AddAttribute("Type",
+ "http://www.w3.org/2000/09/xmldsig#Object");
+ else
+ pAttributeList->AddAttribute("Type",
+ "http://uri.etsi.org/01903#SignedProperties");
+ pAttributeList->AddAttribute("URI", "#" + rReference.ouURI);
+ m_xDocumentHandler->startElement(
+ "Reference", uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+ if (rReference.ouURI.startsWith("idSignedProperties"))
+ {
+ m_xDocumentHandler->startElement(
+ "Transforms",
+ uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ writeCanonicalizationTransform();
+ m_xDocumentHandler->endElement("Transforms");
+ }
+
+ DocumentSignatureHelper::writeDigestMethod(m_xDocumentHandler);
+ m_xDocumentHandler->startElement(
+ "DigestValue",
+ uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ m_xDocumentHandler->characters(rReference.ouDigestValue);
+ m_xDocumentHandler->endElement("DigestValue");
+ m_xDocumentHandler->endElement("Reference");
+ }
+ }
+}
+
+void OOXMLSecExporter::Impl::writeSignatureValue()
+{
+ m_xDocumentHandler->startElement("SignatureValue", uno::Reference<xml::sax::XAttributeList>(
+ new comphelper::AttributeList()));
+ m_xDocumentHandler->characters(m_rInformation.ouSignatureValue);
+ m_xDocumentHandler->endElement("SignatureValue");
+}
+
+void OOXMLSecExporter::Impl::writeKeyInfo()
+{
+ m_xDocumentHandler->startElement(
+ "KeyInfo", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ assert(m_rInformation.GetSigningCertificate());
+ for (auto const& rData : m_rInformation.X509Datas)
+ {
+ m_xDocumentHandler->startElement(
+ "X509Data", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ for (auto const& it : rData)
+ {
+ m_xDocumentHandler->startElement(
+ "X509Certificate",
+ uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ m_xDocumentHandler->characters(it.X509Certificate);
+ m_xDocumentHandler->endElement("X509Certificate");
+ }
+ m_xDocumentHandler->endElement("X509Data");
+ }
+ m_xDocumentHandler->endElement("KeyInfo");
+}
+
+void OOXMLSecExporter::Impl::writePackageObject()
+{
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("Id", "idPackageObject_" + m_rInformation.ouSignatureId);
+ m_xDocumentHandler->startElement("Object",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+
+ writeManifest();
+ writePackageObjectSignatureProperties();
+
+ m_xDocumentHandler->endElement("Object");
+}
+
+void OOXMLSecExporter::Impl::writeManifest()
+{
+ m_xDocumentHandler->startElement(
+ "Manifest", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ const SignatureReferenceInformations& rReferences = m_rInformation.vSignatureReferenceInfors;
+ for (const SignatureReferenceInformation& rReference : rReferences)
+ {
+ if (rReference.nType != SignatureReferenceType::SAMEDOCUMENT)
+ {
+ if (OOXMLSecExporter::Impl::isOOXMLDenylist(rReference.ouURI))
+ continue;
+
+ writeManifestReference(rReference);
+ }
+ }
+ m_xDocumentHandler->endElement("Manifest");
+}
+
+void OOXMLSecExporter::Impl::writeRelationshipTransform(const OUString& rURI)
+{
+ uno::Reference<embed::XHierarchicalStorageAccess> xHierarchicalStorageAccess(m_xRootStorage,
+ uno::UNO_QUERY);
+ uno::Reference<io::XInputStream> xRelStream(
+ xHierarchicalStorageAccess->openStreamElementByHierarchicalName(rURI,
+ embed::ElementModes::READ),
+ uno::UNO_QUERY);
+ {
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("Algorithm", ALGO_RELATIONSHIP);
+ m_xDocumentHandler->startElement("Transform",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+
+ const uno::Sequence<uno::Sequence<beans::StringPair>> aRelationsInfo
+ = comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(xRelStream, rURI,
+ m_xComponentContext);
+ for (const uno::Sequence<beans::StringPair>& rPairs : aRelationsInfo)
+ {
+ OUString aId;
+ OUString aType;
+ for (const beans::StringPair& rPair : rPairs)
+ {
+ if (rPair.First == "Id")
+ aId = rPair.Second;
+ else if (rPair.First == "Type")
+ aType = rPair.Second;
+ }
+
+ if (OOXMLSecExporter::Impl::isOOXMLRelationDenylist(aType))
+ continue;
+
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("xmlns:mdssi", NS_MDSSI);
+ pAttributeList->AddAttribute("SourceId", aId);
+ m_xDocumentHandler->startElement("mdssi:RelationshipReference",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ m_xDocumentHandler->endElement("mdssi:RelationshipReference");
+ }
+
+ m_xDocumentHandler->endElement("Transform");
+}
+
+void OOXMLSecExporter::Impl::writePackageObjectSignatureProperties()
+{
+ m_xDocumentHandler->startElement(
+ "SignatureProperties",
+ uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ {
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("Id", "idSignatureTime_" + m_rInformation.ouSignatureId);
+ pAttributeList->AddAttribute("Target", "#" + m_rInformation.ouSignatureId);
+ m_xDocumentHandler->startElement("SignatureProperty",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+ {
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("xmlns:mdssi", NS_MDSSI);
+ m_xDocumentHandler->startElement("mdssi:SignatureTime",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+ m_xDocumentHandler->startElement(
+ "mdssi:Format", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ m_xDocumentHandler->characters("YYYY-MM-DDThh:mm:ssTZD");
+ m_xDocumentHandler->endElement("mdssi:Format");
+
+ m_xDocumentHandler->startElement(
+ "mdssi:Value", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ if (!m_rInformation.ouDateTime.isEmpty())
+ m_aSignatureTimeValue = m_rInformation.ouDateTime;
+ else
+ {
+ m_aSignatureTimeValue = utl::toISO8601(m_rInformation.stDateTime);
+ // Ignore sub-seconds.
+ sal_Int32 nCommaPos = m_aSignatureTimeValue.indexOf(',');
+ if (nCommaPos != -1)
+ {
+ m_aSignatureTimeValue
+ = OUString::Concat(m_aSignatureTimeValue.subView(0, nCommaPos)) + "Z";
+ }
+ }
+ m_xDocumentHandler->characters(m_aSignatureTimeValue);
+ m_xDocumentHandler->endElement("mdssi:Value");
+
+ m_xDocumentHandler->endElement("mdssi:SignatureTime");
+ m_xDocumentHandler->endElement("SignatureProperty");
+ m_xDocumentHandler->endElement("SignatureProperties");
+}
+
+void OOXMLSecExporter::Impl::writeManifestReference(const SignatureReferenceInformation& rReference)
+{
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("URI", rReference.ouURI);
+ m_xDocumentHandler->startElement("Reference",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+
+ // Transforms
+ if (rReference.ouURI.endsWith(
+ "?ContentType=application/vnd.openxmlformats-package.relationships+xml"))
+ {
+ OUString aURI = rReference.ouURI;
+ // Ignore leading slash.
+ if (aURI.startsWith("/"))
+ aURI = aURI.copy(1);
+ // Ignore query part of the URI.
+ sal_Int32 nQueryPos = aURI.indexOf('?');
+ if (nQueryPos != -1)
+ aURI = aURI.copy(0, nQueryPos);
+
+ m_xDocumentHandler->startElement("Transforms", uno::Reference<xml::sax::XAttributeList>(
+ new comphelper::AttributeList()));
+
+ writeRelationshipTransform(aURI);
+ writeCanonicalizationTransform();
+
+ m_xDocumentHandler->endElement("Transforms");
+ }
+
+ DocumentSignatureHelper::writeDigestMethod(m_xDocumentHandler);
+ m_xDocumentHandler->startElement(
+ "DigestValue", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ m_xDocumentHandler->characters(rReference.ouDigestValue);
+ m_xDocumentHandler->endElement("DigestValue");
+ m_xDocumentHandler->endElement("Reference");
+}
+
+void OOXMLSecExporter::Impl::writeOfficeObject()
+{
+ {
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("Id", "idOfficeObject_" + m_rInformation.ouSignatureId);
+ m_xDocumentHandler->startElement("Object",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+ m_xDocumentHandler->startElement(
+ "SignatureProperties",
+ uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ {
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("Id", "idOfficeV1Details_" + m_rInformation.ouSignatureId);
+ pAttributeList->AddAttribute("Target", "#" + m_rInformation.ouSignatureId);
+ m_xDocumentHandler->startElement("SignatureProperty",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+ writeSignatureInfo();
+ m_xDocumentHandler->endElement("SignatureProperty");
+ m_xDocumentHandler->endElement("SignatureProperties");
+ m_xDocumentHandler->endElement("Object");
+}
+
+void OOXMLSecExporter::Impl::writeSignatureInfo()
+{
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("xmlns", "http://schemas.microsoft.com/office/2006/digsig");
+ m_xDocumentHandler->startElement("SignatureInfoV1",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+
+ m_xDocumentHandler->startElement(
+ "SetupID", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ m_xDocumentHandler->characters(m_rInformation.ouSignatureLineId);
+ m_xDocumentHandler->endElement("SetupID");
+ m_xDocumentHandler->startElement(
+ "SignatureText", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ m_xDocumentHandler->endElement("SignatureText");
+ m_xDocumentHandler->startElement("SignatureImage", uno::Reference<xml::sax::XAttributeList>(
+ new comphelper::AttributeList()));
+ m_xDocumentHandler->endElement("SignatureImage");
+ m_xDocumentHandler->startElement("SignatureComments", uno::Reference<xml::sax::XAttributeList>(
+ new comphelper::AttributeList()));
+ m_xDocumentHandler->characters(m_rInformation.ouDescription);
+ m_xDocumentHandler->endElement("SignatureComments");
+ // Just hardcode something valid according to [MS-OFFCRYPTO].
+ m_xDocumentHandler->startElement("WindowsVersion", uno::Reference<xml::sax::XAttributeList>(
+ new comphelper::AttributeList()));
+ m_xDocumentHandler->characters("6.1");
+ m_xDocumentHandler->endElement("WindowsVersion");
+ m_xDocumentHandler->startElement(
+ "OfficeVersion", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ m_xDocumentHandler->characters("16.0");
+ m_xDocumentHandler->endElement("OfficeVersion");
+ m_xDocumentHandler->startElement("ApplicationVersion", uno::Reference<xml::sax::XAttributeList>(
+ new comphelper::AttributeList()));
+ m_xDocumentHandler->characters("16.0");
+ m_xDocumentHandler->endElement("ApplicationVersion");
+ m_xDocumentHandler->startElement(
+ "Monitors", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ m_xDocumentHandler->characters("1");
+ m_xDocumentHandler->endElement("Monitors");
+ m_xDocumentHandler->startElement(
+ "HorizontalResolution",
+ uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ m_xDocumentHandler->characters("1280");
+ m_xDocumentHandler->endElement("HorizontalResolution");
+ m_xDocumentHandler->startElement("VerticalResolution", uno::Reference<xml::sax::XAttributeList>(
+ new comphelper::AttributeList()));
+ m_xDocumentHandler->characters("800");
+ m_xDocumentHandler->endElement("VerticalResolution");
+ m_xDocumentHandler->startElement(
+ "ColorDepth", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ m_xDocumentHandler->characters("32");
+ m_xDocumentHandler->endElement("ColorDepth");
+ m_xDocumentHandler->startElement(
+ "SignatureProviderId",
+ uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ m_xDocumentHandler->characters("{00000000-0000-0000-0000-000000000000}");
+ m_xDocumentHandler->endElement("SignatureProviderId");
+ m_xDocumentHandler->startElement(
+ "SignatureProviderUrl",
+ uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ m_xDocumentHandler->endElement("SignatureProviderUrl");
+ m_xDocumentHandler->startElement(
+ "SignatureProviderDetails",
+ uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ m_xDocumentHandler->characters(
+ "9"); // This is what MSO 2016 writes, though [MS-OFFCRYPTO] doesn't document what the value means.
+ m_xDocumentHandler->endElement("SignatureProviderDetails");
+ m_xDocumentHandler->startElement(
+ "SignatureType", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ m_xDocumentHandler->characters("2");
+ m_xDocumentHandler->endElement("SignatureType");
+
+ m_xDocumentHandler->endElement("SignatureInfoV1");
+}
+
+void OOXMLSecExporter::Impl::writePackageSignature()
+{
+ m_xDocumentHandler->startElement(
+ "Object", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ {
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("xmlns:xd", NS_XD);
+ pAttributeList->AddAttribute("Target", "#" + m_rInformation.ouSignatureId);
+ m_xDocumentHandler->startElement("xd:QualifyingProperties",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+
+ DocumentSignatureHelper::writeSignedProperties(m_xDocumentHandler, m_rInformation,
+ m_aSignatureTimeValue, false);
+
+ m_xDocumentHandler->endElement("xd:QualifyingProperties");
+ m_xDocumentHandler->endElement("Object");
+}
+
+void OOXMLSecExporter::Impl::writeSignatureLineImages()
+{
+ if (m_rInformation.aValidSignatureImage.is())
+ {
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("Id", "idValidSigLnImg");
+ m_xDocumentHandler->startElement("Object",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ OUString aGraphicInBase64;
+ Graphic aGraphic(m_rInformation.aValidSignatureImage);
+ if (!XOutBitmap::GraphicToBase64(aGraphic, aGraphicInBase64, false, ConvertDataFormat::EMF))
+ SAL_WARN("xmlsecurity.helper", "could not convert graphic to base64");
+ m_xDocumentHandler->characters(aGraphicInBase64);
+ m_xDocumentHandler->endElement("Object");
+ }
+ if (!m_rInformation.aInvalidSignatureImage.is())
+ return;
+
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("Id", "idInvalidSigLnImg");
+ m_xDocumentHandler->startElement("Object",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ OUString aGraphicInBase64;
+ Graphic aGraphic(m_rInformation.aInvalidSignatureImage);
+ if (!XOutBitmap::GraphicToBase64(aGraphic, aGraphicInBase64, false, ConvertDataFormat::EMF))
+ SAL_WARN("xmlsecurity.helper", "could not convert graphic to base64");
+ m_xDocumentHandler->characters(aGraphicInBase64);
+ m_xDocumentHandler->endElement("Object");
+}
+
+void OOXMLSecExporter::Impl::writeSignature()
+{
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("xmlns", NS_XMLDSIG);
+ pAttributeList->AddAttribute("Id", m_rInformation.ouSignatureId);
+ getDocumentHandler()->startElement("Signature",
+ uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+
+ writeSignedInfo();
+ writeSignatureValue();
+ writeKeyInfo();
+ writePackageObject();
+ writeOfficeObject();
+ writePackageSignature();
+ writeSignatureLineImages();
+
+ getDocumentHandler()->endElement("Signature");
+}
+
+OOXMLSecExporter::OOXMLSecExporter(
+ const uno::Reference<uno::XComponentContext>& xComponentContext,
+ const uno::Reference<embed::XStorage>& xRootStorage,
+ const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler,
+ const SignatureInformation& rInformation)
+ : m_pImpl(
+ std::make_unique<Impl>(xComponentContext, xRootStorage, xDocumentHandler, rInformation))
+{
+}
+
+OOXMLSecExporter::~OOXMLSecExporter() = default;
+
+void OOXMLSecExporter::writeSignature() { m_pImpl->writeSignature(); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/ooxmlsecexporter.hxx b/xmlsecurity/source/helper/ooxmlsecexporter.hxx
new file mode 100644
index 0000000000..77ea7c4849
--- /dev/null
+++ b/xmlsecurity/source/helper/ooxmlsecexporter.hxx
@@ -0,0 +1,47 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <svl/sigstruct.hxx>
+
+namespace com::sun::star
+{
+namespace embed
+{
+class XStorage;
+}
+namespace uno
+{
+class XComponentContext;
+}
+namespace xml::sax
+{
+class XDocumentHandler;
+}
+}
+
+/// Writes a single OOXML digital signature.
+class OOXMLSecExporter
+{
+ struct Impl;
+ std::unique_ptr<Impl> m_pImpl;
+
+public:
+ OOXMLSecExporter(const css::uno::Reference<css::uno::XComponentContext>& xComponentContext,
+ const css::uno::Reference<css::embed::XStorage>& xRootStorage,
+ const css::uno::Reference<css::xml::sax::XDocumentHandler>& xDocumentHandler,
+ const SignatureInformation& rInformation);
+ ~OOXMLSecExporter();
+ void writeSignature();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/ooxmlsecparser.cxx b/xmlsecurity/source/helper/ooxmlsecparser.cxx
new file mode 100644
index 0000000000..2692190ccb
--- /dev/null
+++ b/xmlsecurity/source/helper/ooxmlsecparser.cxx
@@ -0,0 +1,1352 @@
+/* -*- 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/.
+ */
+
+
+#include "ooxmlsecparser.hxx"
+#include <xmlsignaturehelper.hxx>
+#include <xsecctl.hxx>
+
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltkmap.hxx>
+#include <xmloff/xmlimp.hxx>
+
+#include <com/sun/star/xml/sax/SAXException.hpp>
+
+#include <sal/log.hxx>
+
+using namespace com::sun::star;
+
+class OOXMLSecParser::Context
+{
+ protected:
+ friend class OOXMLSecParser;
+ OOXMLSecParser & m_rParser;
+ private:
+ std::optional<SvXMLNamespaceMap> m_pOldNamespaceMap;
+
+ public:
+ Context(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : m_rParser(rParser)
+ , m_pOldNamespaceMap(std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual ~Context() = default;
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& /*xAttrs*/)
+ {
+ }
+
+ virtual void EndElement()
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const /*nNamespace*/, OUString const& /*rName*/);
+
+ virtual void Characters(OUString const& /*rChars*/)
+ {
+ }
+};
+
+// it's possible that an unsupported element has an Id attribute and a
+// ds:Reference digesting it - probably this means XSecController needs to know
+// about it. (For known elements, the Id attribute is only processed according
+// to the schema.)
+class OOXMLSecParser::UnknownContext
+ : public OOXMLSecParser::Context
+{
+ public:
+ UnknownContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+};
+
+auto OOXMLSecParser::Context::CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const /*nNamespace*/, OUString const& /*rName*/)
+-> std::unique_ptr<Context>
+{
+ // default: create new base context
+ return std::make_unique<UnknownContext>(m_rParser, std::move(pOldNamespaceMap));
+}
+
+/**
+note: anything in ds:Object should be trusted *only* if there is a ds:Reference
+ to it so it is signed (exception: the xades:EncapsulatedX509Certificate).
+ ds:SignedInfo precedes all ds:Object.
+
+ There may be multiple ds:Signature for purpose of counter-signatures
+ but the way XAdES describes these, only the ds:SignatureValue element
+ would be referenced, so requiring a ds:Reference for anything in
+ ds:Object shouldn't cause issues.
+ */
+class OOXMLSecParser::ReferencedContextImpl
+ : public OOXMLSecParser::Context
+{
+ protected:
+ bool m_isReferenced;
+
+ public:
+ ReferencedContextImpl(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_isReferenced(isReferenced)
+ {
+ }
+
+ OUString CheckIdAttrReferenced(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs)
+ {
+ OUString const id(m_rParser.HandleIdAttr(xAttrs));
+ if (!id.isEmpty() && m_rParser.m_pXSecController->haveReferenceForId(id))
+ {
+ m_isReferenced = true;
+ }
+ return id;
+ }
+};
+
+class OOXMLSecParser::DsX509CertificateContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509CertificateContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::DsX509SerialNumberContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509SerialNumberContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::DsX509IssuerNameContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509IssuerNameContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::DsX509IssuerSerialContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rX509IssuerName;
+ OUString & m_rX509SerialNumber;
+
+ public:
+ DsX509IssuerSerialContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rIssuerName, OUString& rSerialNumber)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rX509IssuerName(rIssuerName)
+ , m_rX509SerialNumber(rSerialNumber)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerName")
+ {
+ return std::make_unique<DsX509IssuerNameContext>(m_rParser, std::move(pOldNamespaceMap), m_rX509IssuerName);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509SerialNumber")
+ {
+ return std::make_unique<DsX509SerialNumberContext>(m_rParser, std::move(pOldNamespaceMap), m_rX509SerialNumber);
+ }
+ // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+/// can't be sure what is supposed to happen here because the spec is clear as mud
+class OOXMLSecParser::DsX509DataContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ // sigh... "No ordering is implied by the above constraints."
+ // so store the ball of mud in vectors and try to figure it out later.
+ std::vector<std::pair<OUString, OUString>> m_X509IssuerSerials;
+ std::vector<OUString> m_X509Certificates;
+
+ public:
+ DsX509DataContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setX509Data(m_X509IssuerSerials, m_X509Certificates);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerSerial")
+ {
+ m_X509IssuerSerials.emplace_back();
+ return std::make_unique<DsX509IssuerSerialContext>(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerSerials.back().first, m_X509IssuerSerials.back().second);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509Certificate")
+ {
+ m_X509Certificates.emplace_back();
+ return std::make_unique<DsX509CertificateContext>(m_rParser, std::move(pOldNamespaceMap), m_X509Certificates.back());
+ }
+ // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsKeyInfoContext
+ : public OOXMLSecParser::Context
+{
+ public:
+ DsKeyInfoContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509Data")
+ {
+ return std::make_unique<DsX509DataContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: ds:PGPData
+ // missing: ds:KeyName, ds:KeyValue, ds:RetrievalMethod, ds:SPKIData, ds:MgmtData
+ // (old code would read ds:Transform inside ds:RetrievalMethod but
+ // presumably that was a bug)
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+
+};
+
+class OOXMLSecParser::DsSignatureValueContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString m_Value;
+
+ public:
+ DsSignatureValueContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setSignatureValue(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class OOXMLSecParser::DsDigestValueContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsDigestValueContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString & rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& /*xAttrs*/) override
+ {
+ m_rValue.clear();
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::DsDigestMethodContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ sal_Int32 & m_rReferenceDigestID;
+
+ public:
+ DsDigestMethodContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_Int32& rReferenceDigestID)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rReferenceDigestID(rReferenceDigestID)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm");
+
+ SAL_WARN_IF( ouAlgorithm.isEmpty(), "xmlsecurity.helper", "no Algorithm in Reference" );
+ if (ouAlgorithm.isEmpty())
+ return;
+
+ SAL_WARN_IF( ouAlgorithm != ALGO_XMLDSIGSHA1
+ && ouAlgorithm != ALGO_XMLDSIGSHA256
+ && ouAlgorithm != ALGO_XMLDSIGSHA512,
+ "xmlsecurity.helper", "Algorithm neither SHA1, SHA256 nor SHA512");
+ if (ouAlgorithm == ALGO_XMLDSIGSHA1)
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA1;
+ else if (ouAlgorithm == ALGO_XMLDSIGSHA256)
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA256;
+ else if (ouAlgorithm == ALGO_XMLDSIGSHA512)
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA512;
+ else
+ m_rReferenceDigestID = 0;
+ }
+};
+
+class OOXMLSecParser::DsTransformContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ bool & m_rIsC14N;
+
+ public:
+ DsTransformContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool& rIsC14N)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rIsC14N(rIsC14N)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString aAlgorithm = xAttrs->getValueByName("Algorithm");
+
+ if (aAlgorithm == ALGO_RELATIONSHIP)
+ {
+ m_rIsC14N = true;
+ }
+ }
+};
+
+class OOXMLSecParser::DsTransformsContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ bool & m_rIsC14N;
+
+ public:
+ DsTransformsContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool& rIsC14N)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rIsC14N(rIsC14N)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Transform")
+ {
+ return std::make_unique<DsTransformContext>(m_rParser, std::move(pOldNamespaceMap), m_rIsC14N);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsReferenceContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString m_URI;
+ OUString m_Type;
+ OUString m_DigestValue;
+ bool m_IsC14N = false;
+ // Relevant for ODF. The digest algorithm selected by the DigestMethod
+ // element's Algorithm attribute. @see css::xml::crypto::DigestID.
+ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA256;
+
+ public:
+ DsReferenceContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+
+ m_URI = xAttrs->getValueByName("URI");
+ SAL_WARN_IF(m_URI.isEmpty(), "xmlsecurity.helper", "URI is empty");
+ // Remember the type of this reference.
+ m_Type = xAttrs->getValueByName("Type");
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_URI.startsWith("#"))
+ {
+ /*
+ * remove the first character '#' from the attribute value
+ */
+ m_rParser.m_pXSecController->addReference(m_URI.copy(1), m_nReferenceDigestID, m_Type);
+ }
+ else
+ {
+ if (m_IsC14N) // this is determined by nested ds:Transform
+ {
+ m_rParser.m_pXSecController->addStreamReference(m_URI, false, m_nReferenceDigestID);
+ }
+ else
+ /*
+ * it must be an octet stream
+ */
+ {
+ m_rParser.m_pXSecController->addStreamReference(m_URI, true, m_nReferenceDigestID);
+ }
+ }
+
+ m_rParser.m_pXSecController->setDigestValue(m_nReferenceDigestID, m_DigestValue);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Transforms")
+ {
+ return std::make_unique<DsTransformsContext>(m_rParser, std::move(pOldNamespaceMap), m_IsC14N);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod")
+ {
+ return std::make_unique<DsDigestMethodContext>(m_rParser, std::move(pOldNamespaceMap), m_nReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue")
+ {
+ return std::make_unique<DsDigestValueContext>(m_rParser, std::move(pOldNamespaceMap), m_DigestValue);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsSignatureMethodContext
+ : public OOXMLSecParser::Context
+{
+ public:
+ DsSignatureMethodContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm");
+ if (ouAlgorithm == ALGO_ECDSASHA1 || ouAlgorithm == ALGO_ECDSASHA256
+ || ouAlgorithm == ALGO_ECDSASHA512)
+ {
+ m_rParser.m_pXSecController->setSignatureMethod(svl::crypto::SignatureMethodAlgorithm::ECDSA);
+ }
+ }
+};
+
+class OOXMLSecParser::DsSignedInfoContext
+ : public OOXMLSecParser::Context
+{
+ public:
+ DsSignedInfoContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setReferenceCount();
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureMethod")
+ {
+ return std::make_unique<DsSignatureMethodContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Reference")
+ {
+ return std::make_unique<DsReferenceContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: ds:CanonicalizationMethod
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesCertDigestContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rDigestValue;
+ sal_Int32 & m_rReferenceDigestID;
+
+ public:
+ XadesCertDigestContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rDigestValue, sal_Int32& rReferenceDigestID)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rDigestValue(rDigestValue)
+ , m_rReferenceDigestID(rReferenceDigestID)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod")
+ {
+ return std::make_unique<DsDigestMethodContext>(m_rParser, std::move(pOldNamespaceMap), m_rReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue")
+ {
+ return std::make_unique<DsDigestValueContext>(m_rParser, std::move(pOldNamespaceMap), m_rDigestValue);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesCertContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ private:
+ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1;
+ OUString m_CertDigest;
+ OUString m_X509IssuerName;
+ OUString m_X509SerialNumber;
+
+ public:
+ XadesCertContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setX509CertDigest(m_CertDigest, m_nReferenceDigestID, m_X509IssuerName, m_X509SerialNumber);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned xades:Cert");
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "CertDigest")
+ {
+ return std::make_unique<XadesCertDigestContext>(m_rParser, std::move(pOldNamespaceMap), m_CertDigest, m_nReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "IssuerSerial")
+ {
+ return std::make_unique<DsX509IssuerSerialContext>(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerName, m_X509SerialNumber);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesSigningCertificateContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSigningCertificateContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "Cert")
+ {
+ return std::make_unique<XadesCertContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesSigningTimeContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_Value;
+
+ public:
+ XadesSigningTimeContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setDate("", m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SigningTime");
+ }
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class OOXMLSecParser::XadesSignedSignaturePropertiesContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSignedSignaturePropertiesContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningTime")
+ {
+ return std::make_unique<XadesSigningTimeContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningCertificate")
+ {
+ return std::make_unique<XadesSigningCertificateContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: xades:SignaturePolicyIdentifier, xades:SignatureProductionPlace, xades:SignerRole
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesSignedPropertiesContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSignedPropertiesContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedSignatureProperties")
+ {
+ return std::make_unique<XadesSignedSignaturePropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: xades:SignedDataObjectProperties
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::XadesQualifyingPropertiesContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ XadesQualifyingPropertiesContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedProperties")
+ {
+ return std::make_unique<XadesSignedPropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: xades:UnsignedSignatureProperties
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::MsodigsigSetupIDContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ MsodigsigSetupIDContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::MsodigsigSignatureCommentsContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ MsodigsigSignatureCommentsContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::MsodigsigSignatureInfoV1Context
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_SetupID;
+ OUString m_SignatureComments;
+
+ public:
+ MsodigsigSignatureInfoV1Context(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_MSODIGSIG && rName == "SetupID")
+ {
+ return std::make_unique<MsodigsigSetupIDContext>(m_rParser, std::move(pOldNamespaceMap), m_SetupID);
+ }
+ if (nNamespace == XML_NAMESPACE_MSODIGSIG && rName == "SignatureComments")
+ {
+ return std::make_unique<MsodigsigSignatureCommentsContext>(m_rParser, std::move(pOldNamespaceMap), m_SignatureComments);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ if (!m_SetupID.isEmpty())
+ {
+ m_rParser.m_pXSecController->setSignatureLineId(m_SetupID);
+ }
+ if (!m_SignatureComments.isEmpty())
+ {
+ m_rParser.m_pXSecController->setDescription("", m_SignatureComments);
+
+ }
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureInfoV1");
+ }
+ }
+};
+
+class OOXMLSecParser::MdssiValueContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ MdssiValueContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class OOXMLSecParser::MdssiSignatureTimeContext
+ : public OOXMLSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ MdssiSignatureTimeContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_MDSSI && rName == "Value")
+ {
+ return std::make_unique<MdssiValueContext>(m_rParser, std::move(pOldNamespaceMap), m_rValue);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+
+class OOXMLSecParser::DsSignaturePropertyContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ private:
+ enum class SignatureProperty { Unknown, Date, Info };
+ SignatureProperty m_Property = SignatureProperty::Unknown;
+ OUString m_Id;
+ OUString m_Value;
+
+ public:
+ DsSignaturePropertyContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_Id = CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ switch (m_Property)
+ {
+ case SignatureProperty::Unknown:
+ SAL_INFO("xmlsecurity.helper", "Unknown property in ds:Object ignored");
+ break;
+ case SignatureProperty::Info:
+ break; // handled by child context
+ case SignatureProperty::Date:
+ m_rParser.m_pXSecController->setDate(m_Id, m_Value);
+ break;
+ }
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureProperty");
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_MDSSI && rName == "SignatureTime")
+ {
+ m_Property = SignatureProperty::Date;
+ return std::make_unique<MdssiSignatureTimeContext>(m_rParser, std::move(pOldNamespaceMap), m_Value);
+ }
+ if (nNamespace == XML_NAMESPACE_MSODIGSIG && rName == "SignatureInfoV1")
+ {
+ return std::make_unique<MsodigsigSignatureInfoV1Context>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsSignaturePropertiesContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ DsSignaturePropertiesContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperty")
+ {
+ return std::make_unique<DsSignaturePropertyContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsManifestContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ public:
+ DsManifestContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+#if 0
+ ???
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setReferenceCount();
+ }
+#endif
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Reference")
+ {
+ return std::make_unique<DsReferenceContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: ds:CanonicalizationMethod
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsObjectContext
+ : public OOXMLSecParser::ReferencedContextImpl
+{
+ enum class Mode { Default, ValidSignatureLineImage, InvalidSignatureLineImage };
+ Mode m_Mode = Mode::Default;
+ OUString m_Value;
+
+ public:
+ DsObjectContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ // init with "false" here - the Signature element can't be referenced by its child
+ : OOXMLSecParser::ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), false)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString const id(CheckIdAttrReferenced(xAttrs));
+ if (id == "idValidSigLnImg")
+ {
+ m_Mode = Mode::ValidSignatureLineImage;
+ }
+ else if (id == "idInvalidSigLnImg")
+ {
+ m_Mode = Mode::InvalidSignatureLineImage;
+ }
+ }
+
+ virtual void EndElement() override
+ {
+ switch (m_Mode)
+ {
+ case Mode::ValidSignatureLineImage:
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setValidSignatureImage(m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineValidImage");
+ }
+ break;
+ case Mode::InvalidSignatureLineImage:
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setInvalidSignatureImage(m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineInvalidImage");
+ }
+ break;
+ case Mode::Default:
+ break;
+ }
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperties")
+ {
+ return std::make_unique<DsSignaturePropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "QualifyingProperties")
+ {
+ return std::make_unique<XadesQualifyingPropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Manifest")
+ {
+ return std::make_unique<DsManifestContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class OOXMLSecParser::DsSignatureContext
+ : public OOXMLSecParser::Context
+{
+ public:
+ DsSignatureContext(OOXMLSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : OOXMLSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString const ouIdAttr(m_rParser.HandleIdAttr(xAttrs));
+ m_rParser.m_rXMLSignatureHelper.StartVerifySignatureElement();
+ m_rParser.m_pXSecController->addSignature();
+ if (!ouIdAttr.isEmpty())
+ {
+ m_rParser.m_pXSecController->setId( ouIdAttr );
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignedInfo")
+ {
+ return std::make_unique<DsSignedInfoContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureValue")
+ {
+ return std::make_unique<DsSignatureValueContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "KeyInfo")
+ {
+ return std::make_unique<DsKeyInfoContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Object")
+ {
+ return std::make_unique<DsObjectContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ return OOXMLSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+
+OOXMLSecParser::OOXMLSecParser(XMLSignatureHelper& rXMLSignatureHelper, XSecController* pXSecController)
+ : m_pNamespaceMap(SvXMLNamespaceMap())
+ , m_pXSecController(pXSecController)
+ ,m_rXMLSignatureHelper(rXMLSignatureHelper)
+{
+ using namespace xmloff::token;
+ m_pNamespaceMap->Add( GetXMLToken(XML_XML), GetXMLToken(XML_N_XML), XML_NAMESPACE_XML );
+ m_pNamespaceMap->Add( "_ds", GetXMLToken(XML_N_DS), XML_NAMESPACE_DS );
+ m_pNamespaceMap->Add( "_xades132", GetXMLToken(XML_N_XADES132), XML_NAMESPACE_XADES132);
+ m_pNamespaceMap->Add( "_xades141", GetXMLToken(XML_N_XADES141), XML_NAMESPACE_XADES141);
+ m_pNamespaceMap->Add( "_dc", GetXMLToken(XML_N_DC), XML_NAMESPACE_DC );
+ m_pNamespaceMap->Add( "_mdssi", NS_MDSSI, XML_NAMESPACE_MDSSI );
+ m_pNamespaceMap->Add( "_msodigsig", "http://schemas.microsoft.com/office/2006/digsig", XML_NAMESPACE_MSODIGSIG );
+ m_pNamespaceMap->Add( "_office_libo",
+ GetXMLToken(XML_N_LO_EXT), XML_NAMESPACE_LO_EXT);
+}
+
+OOXMLSecParser::~OOXMLSecParser()
+{
+}
+
+OUString OOXMLSecParser::HandleIdAttr(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs)
+{
+ OUString const aId = xAttrs->getValueByName("Id");
+ if (!aId.isEmpty())
+ {
+ m_pXSecController->collectToVerify(aId);
+ }
+ return aId;
+}
+
+void SAL_CALL OOXMLSecParser::startDocument()
+{
+ if (m_xNextHandler.is())
+ m_xNextHandler->startDocument();
+}
+
+void SAL_CALL OOXMLSecParser::endDocument()
+{
+ if (m_xNextHandler.is())
+ m_xNextHandler->endDocument();
+}
+
+void SAL_CALL OOXMLSecParser::startElement(const OUString& rName, const uno::Reference<xml::sax::XAttributeList>& xAttribs)
+{
+ assert(m_pNamespaceMap);
+ std::optional<SvXMLNamespaceMap> pRewindMap(
+ SvXMLImport::processNSAttributes(m_pNamespaceMap, nullptr, xAttribs));
+
+ OUString localName;
+ sal_uInt16 const nPrefix(m_pNamespaceMap->GetKeyByAttrName(rName, &localName));
+
+ std::unique_ptr<Context> pContext;
+
+ if (m_ContextStack.empty())
+ {
+ if (nPrefix != XML_NAMESPACE_DS || localName != "Signature")
+ {
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: unexpected root element", nullptr,
+ css::uno::Any());
+ }
+
+ pContext.reset(new DsSignatureContext(*this, std::move(pRewindMap)));
+
+ }
+ else
+ {
+ pContext = m_ContextStack.top()->CreateChildContext(
+ std::move(pRewindMap), nPrefix, localName);
+ }
+
+ m_ContextStack.push(std::move(pContext));
+
+ m_ContextStack.top()->StartElement(xAttribs);
+
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->startElement(rName, xAttribs);
+ }
+
+}
+
+void SAL_CALL OOXMLSecParser::endElement(const OUString& rName)
+{
+ assert(!m_ContextStack.empty()); // this should be checked by sax parser?
+
+ m_ContextStack.top()->EndElement();
+
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->endElement(rName);
+ }
+
+ if (m_ContextStack.top()->m_pOldNamespaceMap)
+ {
+ m_pNamespaceMap = std::move(m_ContextStack.top()->m_pOldNamespaceMap);
+ }
+ m_ContextStack.pop();
+}
+
+void SAL_CALL OOXMLSecParser::characters(const OUString& rChars)
+{
+ assert(!m_ContextStack.empty()); // this should be checked by sax parser?
+ m_ContextStack.top()->Characters(rChars);
+
+ if (m_xNextHandler.is())
+ m_xNextHandler->characters(rChars);
+}
+
+void SAL_CALL OOXMLSecParser::ignorableWhitespace(const OUString& rWhitespace)
+{
+ if (m_xNextHandler.is())
+ m_xNextHandler->ignorableWhitespace(rWhitespace);
+}
+
+void SAL_CALL OOXMLSecParser::processingInstruction(const OUString& rTarget, const OUString& rData)
+{
+ if (m_xNextHandler.is())
+ m_xNextHandler->processingInstruction(rTarget, rData);
+}
+
+void SAL_CALL OOXMLSecParser::setDocumentLocator(const uno::Reference<xml::sax::XLocator>& xLocator)
+{
+ if (m_xNextHandler.is())
+ m_xNextHandler->setDocumentLocator(xLocator);
+}
+
+void SAL_CALL OOXMLSecParser::initialize(const uno::Sequence<uno::Any>& rArguments)
+{
+ rArguments[0] >>= m_xNextHandler;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/ooxmlsecparser.hxx b/xmlsecurity/source/helper/ooxmlsecparser.hxx
new file mode 100644
index 0000000000..1ac72a46f0
--- /dev/null
+++ b/xmlsecurity/source/helper/ooxmlsecparser.hxx
@@ -0,0 +1,110 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <xmloff/namespacemap.hxx>
+
+#include <memory>
+#include <optional>
+#include <stack>
+
+class XSecController;
+class XMLSignatureHelper;
+
+/// Parses an OOXML digital signature.
+class OOXMLSecParser: public cppu::WeakImplHelper
+ <
+ css::xml::sax::XDocumentHandler,
+ css::lang::XInitialization
+ >
+{
+public:
+ class Context;
+private:
+ class UnknownContext;
+ class ReferencedContextImpl;
+ class DsX509CertificateContext;
+ class DsX509SerialNumberContext;
+ class DsX509IssuerNameContext;
+ class DsX509IssuerSerialContext;
+ class DsX509DataContext;
+ class DsKeyInfoContext;
+ class DsSignatureValueContext;
+ class DsDigestValueContext;
+ class DsDigestMethodContext;
+ class DsTransformContext;
+ class DsTransformsContext;
+ class DsReferenceContext;
+ class DsSignatureMethodContext;
+ class DsSignedInfoContext;
+ class XadesEncapsulatedX509CertificateContext;
+ class XadesCertificateValuesContext;
+ class XadesUnsignedSignaturePropertiesContext;
+ class XadesUnsignedPropertiesContext;
+ class XadesCertDigestContext;
+ class XadesCertContext;
+ class XadesSigningCertificateContext;
+ class XadesSigningTimeContext;
+ class XadesSignedSignaturePropertiesContext;
+ class XadesSignedPropertiesContext;
+ class XadesQualifyingPropertiesContext;
+ class MdssiValueContext;
+ class MdssiSignatureTimeContext;
+ class MsodigsigSetupIDContext;
+ class MsodigsigSignatureCommentsContext;
+ class MsodigsigSignatureInfoV1Context;
+ class DsSignaturePropertyContext;
+ class DsSignaturePropertiesContext;
+ class DsManifestContext;
+ class DsObjectContext;
+ class DsSignatureContext;
+ class DsigSignaturesContext;
+
+ std::stack<std::unique_ptr<Context>> m_ContextStack;
+ std::optional<SvXMLNamespaceMap> m_pNamespaceMap;
+
+ XSecController* m_pXSecController;
+ css::uno::Reference<css::xml::sax::XDocumentHandler> m_xNextHandler;
+
+ XMLSignatureHelper& m_rXMLSignatureHelper;
+
+ OUString HandleIdAttr(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs);
+
+public:
+ explicit OOXMLSecParser(XMLSignatureHelper& rXMLSignatureHelper, XSecController* pXSecController);
+ virtual ~OOXMLSecParser() override;
+
+ // XDocumentHandler
+ virtual void SAL_CALL startDocument() override;
+
+ virtual void SAL_CALL endDocument() override;
+
+ virtual void SAL_CALL startElement(const OUString& aName, const css::uno::Reference<css::xml::sax::XAttributeList>& xAttribs) override;
+
+ virtual void SAL_CALL endElement(const OUString& aName) override;
+
+ virtual void SAL_CALL characters(const OUString& aChars) override;
+
+ virtual void SAL_CALL ignorableWhitespace(const OUString& aWhitespaces) override;
+
+ virtual void SAL_CALL processingInstruction(const OUString& aTarget, const OUString& aData) override;
+
+ virtual void SAL_CALL setDocumentLocator(const css::uno::Reference<css::xml::sax::XLocator>& xLocator) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& rArguments) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/pdfsignaturehelper.cxx b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
new file mode 100644
index 0000000000..3c0084c8b4
--- /dev/null
+++ b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
@@ -0,0 +1,626 @@
+/* -*- 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/.
+ */
+
+#include <pdfsignaturehelper.hxx>
+
+#include <memory>
+
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/security/CertificateValidity.hpp>
+#include <com/sun/star/uno/SecurityException.hpp>
+#include <com/sun/star/security/DocumentSignatureInformation.hpp>
+#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XDrawView.hpp>
+
+#include <comphelper/propertysequence.hxx>
+#include <sal/log.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/streamwrap.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <vcl/filter/pdfdocument.hxx>
+#include <vcl/checksum.hxx>
+#include <svl/cryptosign.hxx>
+#include <vcl/filter/PDFiumLibrary.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+/// Gets the current page of the current view from xModel and puts it to the 1-based rPage.
+bool GetSignatureLinePage(const uno::Reference<frame::XModel>& xModel, sal_Int32& rPage)
+{
+ uno::Reference<drawing::XDrawView> xController(xModel->getCurrentController(), uno::UNO_QUERY);
+ if (!xController.is())
+ {
+ return false;
+ }
+
+ uno::Reference<beans::XPropertySet> xPage(xController->getCurrentPage(), uno::UNO_QUERY);
+ if (!xPage.is())
+ {
+ return false;
+ }
+
+ return xPage->getPropertyValue("Number") >>= rPage;
+}
+
+/// If the currently selected shape is a Draw signature line, export that to PDF.
+void GetSignatureLineShape(const uno::Reference<frame::XModel>& xModel, sal_Int32& rPage,
+ std::vector<sal_Int8>& rSignatureLineShape)
+{
+ if (!xModel.is())
+ {
+ return;
+ }
+
+ if (!GetSignatureLinePage(xModel, rPage))
+ {
+ return;
+ }
+
+ uno::Reference<drawing::XShapes> xShapes(xModel->getCurrentSelection(), uno::UNO_QUERY);
+ if (!xShapes.is() || xShapes->getCount() < 1)
+ {
+ return;
+ }
+
+ uno::Reference<beans::XPropertySet> xShapeProps(xShapes->getByIndex(0), uno::UNO_QUERY);
+ if (!xShapeProps.is())
+ {
+ return;
+ }
+
+ comphelper::SequenceAsHashMap aMap(xShapeProps->getPropertyValue("InteropGrabBag"));
+ auto it = aMap.find("SignatureCertificate");
+ if (it == aMap.end())
+ {
+ return;
+ }
+
+ // We know that we add a signature line shape to an existing PDF at this point.
+
+ uno::Reference<frame::XStorable> xStorable(xModel, uno::UNO_QUERY);
+ if (!xStorable.is())
+ {
+ return;
+ }
+
+ // Export just the signature line.
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xStream(new utl::OStreamWrapper(aStream));
+ aMediaDescriptor["OutputStream"] <<= xStream;
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "Selection", uno::Any(xShapes) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
+ xStream->flush();
+
+ aStream.Seek(0);
+ rSignatureLineShape = std::vector<sal_Int8>(aStream.GetSize());
+ aStream.ReadBytes(rSignatureLineShape.data(), rSignatureLineShape.size());
+}
+
+/// Represents a parsed signature.
+struct Signature
+{
+ std::unique_ptr<vcl::pdf::PDFiumSignature> m_pSignature;
+ /// Offset+length pairs.
+ std::vector<std::pair<size_t, size_t>> m_aByteRanges;
+};
+
+/// Turns an array of floats into offset + length pairs.
+void GetByteRangesFromPDF(const std::unique_ptr<vcl::pdf::PDFiumSignature>& pSignature,
+ std::vector<std::pair<size_t, size_t>>& rByteRanges)
+{
+ std::vector<int> aByteRange = pSignature->getByteRange();
+ if (aByteRange.empty())
+ {
+ SAL_WARN("xmlsecurity.helper", "GetByteRangesFromPDF: no byte ranges");
+ return;
+ }
+
+ size_t nByteRangeOffset = 0;
+ for (size_t i = 0; i < aByteRange.size(); ++i)
+ {
+ if (i % 2 == 0)
+ {
+ nByteRangeOffset = aByteRange[i];
+ continue;
+ }
+
+ size_t nLength = aByteRange[i];
+ rByteRanges.emplace_back(nByteRangeOffset, nLength);
+ }
+}
+
+/// Determines the last position that is covered by a signature.
+bool GetEOFOfSignature(const Signature& rSignature, size_t& rEOF)
+{
+ if (rSignature.m_aByteRanges.size() < 2)
+ {
+ return false;
+ }
+
+ rEOF = rSignature.m_aByteRanges[1].first + rSignature.m_aByteRanges[1].second;
+ return true;
+}
+
+/**
+ * Get the value of the "modification detection and prevention" permission:
+ * Valid values are 1, 2 and 3: only 3 allows annotations after signing.
+ */
+int GetMDPPerm(const std::vector<Signature>& rSignatures)
+{
+ int nRet = 3;
+
+ if (rSignatures.empty())
+ {
+ return nRet;
+ }
+
+ for (const auto& rSignature : rSignatures)
+ {
+ int nPerm = rSignature.m_pSignature->getDocMDPPermission();
+ if (nPerm != 0)
+ {
+ return nPerm;
+ }
+ }
+
+ return nRet;
+}
+
+/// Checks if there are unsigned incremental updates between the signatures or after the last one.
+bool IsCompleteSignature(SvStream& rStream, const Signature& rSignature,
+ const std::set<unsigned int>& rSignedEOFs,
+ const std::vector<unsigned int>& rAllEOFs)
+{
+ size_t nSignatureEOF = 0;
+ if (!GetEOFOfSignature(rSignature, nSignatureEOF))
+ {
+ return false;
+ }
+
+ bool bFoundOwn = false;
+ for (const auto& rEOF : rAllEOFs)
+ {
+ if (rEOF == nSignatureEOF)
+ {
+ bFoundOwn = true;
+ continue;
+ }
+
+ if (!bFoundOwn)
+ {
+ continue;
+ }
+
+ if (rSignedEOFs.find(rEOF) == rSignedEOFs.end())
+ {
+ // Unsigned incremental update found.
+ return false;
+ }
+ }
+
+ // Make sure we find the incremental update of the signature itself.
+ if (!bFoundOwn)
+ {
+ return false;
+ }
+
+ // No additional content after the last incremental update.
+ rStream.Seek(STREAM_SEEK_TO_END);
+ size_t nFileEnd = rStream.Tell();
+ return std::find(rAllEOFs.begin(), rAllEOFs.end(), nFileEnd) != rAllEOFs.end();
+}
+
+/**
+ * Contains checksums of a PDF page, which is rendered without annotations. It also contains
+ * the geometry of a few dangerous annotation types.
+ */
+struct PageChecksum
+{
+ BitmapChecksum m_nPageContent;
+ std::vector<basegfx::B2DRectangle> m_aAnnotations;
+ bool operator==(const PageChecksum& rChecksum) const;
+};
+
+bool PageChecksum::operator==(const PageChecksum& rChecksum) const
+{
+ if (m_nPageContent != rChecksum.m_nPageContent)
+ {
+ return false;
+ }
+
+ return m_aAnnotations == rChecksum.m_aAnnotations;
+}
+
+/// Collects the checksum of each page of one version of the PDF.
+void AnalyizeSignatureStream(SvMemoryStream& rStream, std::vector<PageChecksum>& rPageChecksums,
+ int nMDPPerm)
+{
+ auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
+ = pPdfium->openDocument(rStream.GetData(), rStream.GetSize(), OString());
+ if (!pPdfDocument)
+ {
+ return;
+ }
+
+ int nPageCount = pPdfDocument->getPageCount();
+ for (int nPage = 0; nPage < nPageCount; ++nPage)
+ {
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(nPage);
+ if (!pPdfPage)
+ {
+ return;
+ }
+
+ PageChecksum aPageChecksum;
+ aPageChecksum.m_nPageContent = pPdfPage->getChecksum(nMDPPerm);
+ for (int i = 0; i < pPdfPage->getAnnotationCount(); ++i)
+ {
+ std::unique_ptr<vcl::pdf::PDFiumAnnotation> pPdfAnnotation = pPdfPage->getAnnotation(i);
+ if (!pPdfAnnotation)
+ {
+ SAL_WARN("xmlsecurity.helper", "Cannot get PDFiumAnnotation");
+ continue;
+ }
+ vcl::pdf::PDFAnnotationSubType eType = pPdfAnnotation->getSubType();
+ switch (eType)
+ {
+ case vcl::pdf::PDFAnnotationSubType::Unknown:
+ case vcl::pdf::PDFAnnotationSubType::FreeText:
+ case vcl::pdf::PDFAnnotationSubType::Stamp:
+ case vcl::pdf::PDFAnnotationSubType::Redact:
+ aPageChecksum.m_aAnnotations.push_back(pPdfAnnotation->getRectangle());
+ break;
+ default:
+ break;
+ }
+ }
+ rPageChecksums.push_back(aPageChecksum);
+ }
+}
+
+/**
+ * Checks if incremental updates after singing performed valid modifications only.
+ * nMDPPerm decides if annotations/commenting is OK, other changes are always not.
+ */
+bool IsValidSignature(SvStream& rStream, const Signature& rSignature, int nMDPPerm)
+{
+ size_t nSignatureEOF = 0;
+ if (!GetEOFOfSignature(rSignature, nSignatureEOF))
+ {
+ return false;
+ }
+
+ SvMemoryStream aSignatureStream;
+ sal_uInt64 nPos = rStream.Tell();
+ rStream.Seek(0);
+ aSignatureStream.WriteStream(rStream, nSignatureEOF);
+ rStream.Seek(nPos);
+ aSignatureStream.Seek(0);
+ std::vector<PageChecksum> aSignedPages;
+ AnalyizeSignatureStream(aSignatureStream, aSignedPages, nMDPPerm);
+
+ SvMemoryStream aFullStream;
+ nPos = rStream.Tell();
+ rStream.Seek(0);
+ aFullStream.WriteStream(rStream);
+ rStream.Seek(nPos);
+ aFullStream.Seek(0);
+ std::vector<PageChecksum> aAllPages;
+ AnalyizeSignatureStream(aFullStream, aAllPages, nMDPPerm);
+
+ // Fail if any page looks different after signing and at the end. Annotations/commenting doesn't
+ // count, though.
+ return aSignedPages == aAllPages;
+}
+
+/**
+ * @param rInformation The actual result.
+ * @param rDocument the parsed document to see if the signature is partial.
+ * @return If we can determinate a result.
+ */
+bool ValidateSignature(SvStream& rStream, const Signature& rSignature,
+ SignatureInformation& rInformation, int nMDPPerm,
+ const std::set<unsigned int>& rSignatureEOFs,
+ const std::vector<unsigned int>& rTrailerEnds)
+{
+ std::vector<unsigned char> aContents = rSignature.m_pSignature->getContents();
+ if (aContents.empty())
+ {
+ SAL_WARN("xmlsecurity.helper", "ValidateSignature: no contents");
+ return false;
+ }
+
+ OString aSubFilter = rSignature.m_pSignature->getSubFilter();
+
+ const bool bNonDetached = aSubFilter == "adbe.pkcs7.sha1";
+ if (aSubFilter.isEmpty()
+ || (aSubFilter != "adbe.pkcs7.detached" && !bNonDetached
+ && aSubFilter != "ETSI.CAdES.detached"))
+ {
+ if (aSubFilter.isEmpty())
+ SAL_WARN("xmlsecurity.helper", "ValidateSignature: missing sub-filter");
+ else
+ SAL_WARN("xmlsecurity.helper",
+ "ValidateSignature: unsupported sub-filter: '" << aSubFilter << "'");
+ return false;
+ }
+
+ // Reason / comment / description is optional.
+ rInformation.ouDescription = rSignature.m_pSignature->getReason();
+
+ // Date: used only when the time of signing is not available in the
+ // signature.
+ rInformation.stDateTime = rSignature.m_pSignature->getTime();
+
+ // Detect if the byte ranges don't cover everything, but the signature itself.
+ if (rSignature.m_aByteRanges.size() < 2)
+ {
+ SAL_WARN("xmlsecurity.helper", "ValidateSignature: expected 2 byte ranges");
+ return false;
+ }
+ if (rSignature.m_aByteRanges[0].first != 0)
+ {
+ SAL_WARN("xmlsecurity.helper", "ValidateSignature: first range start is not 0");
+ return false;
+ }
+ // Binary vs hex dump and 2 is the leading "<" and the trailing ">" around the hex string.
+ size_t nSignatureLength = aContents.size() * 2 + 2;
+ if (rSignature.m_aByteRanges[1].first
+ != (rSignature.m_aByteRanges[0].second + nSignatureLength))
+ {
+ SAL_WARN("xmlsecurity.helper",
+ "ValidateSignature: second range start is not the end of the signature");
+ return false;
+ }
+ rInformation.bPartialDocumentSignature
+ = !IsCompleteSignature(rStream, rSignature, rSignatureEOFs, rTrailerEnds);
+ if (!IsValidSignature(rStream, rSignature, nMDPPerm))
+ {
+ SAL_WARN("xmlsecurity.helper", "ValidateSignature: invalid incremental update detected");
+ return false;
+ }
+
+ // At this point there is no obviously missing info to validate the
+ // signature.
+ return svl::crypto::Signing::Verify(rStream, rSignature.m_aByteRanges, bNonDetached, aContents,
+ rInformation);
+}
+}
+
+PDFSignatureHelper::PDFSignatureHelper() = default;
+
+bool PDFSignatureHelper::ReadAndVerifySignature(
+ const uno::Reference<io::XInputStream>& xInputStream)
+{
+ if (!xInputStream.is())
+ {
+ SAL_WARN("xmlsecurity.helper", "input stream missing");
+ return false;
+ }
+
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+ return ReadAndVerifySignatureSvStream(*pStream);
+}
+
+bool PDFSignatureHelper::ReadAndVerifySignatureSvStream(SvStream& rStream)
+{
+ auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+ if (!pPdfium)
+ {
+ return true;
+ }
+
+ SvMemoryStream aStream;
+ sal_uInt64 nPos = rStream.Tell();
+ rStream.Seek(0);
+ aStream.WriteStream(rStream);
+ rStream.Seek(nPos);
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
+ = pPdfium->openDocument(aStream.GetData(), aStream.GetSize(), OString());
+ if (!pPdfDocument)
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to read the document");
+ return false;
+ }
+
+ int nSignatureCount = pPdfDocument->getSignatureCount();
+ if (nSignatureCount <= 0)
+ {
+ return true;
+ }
+ std::vector<Signature> aSignatures(nSignatureCount);
+ for (int i = 0; i < nSignatureCount; ++i)
+ {
+ std::unique_ptr<vcl::pdf::PDFiumSignature> pSignature = pPdfDocument->getSignature(i);
+ std::vector<std::pair<size_t, size_t>> aByteRanges;
+ GetByteRangesFromPDF(pSignature, aByteRanges);
+ aSignatures[i] = Signature{ std::move(pSignature), aByteRanges };
+ }
+
+ std::set<unsigned int> aSignatureEOFs;
+ for (const auto& rSignature : aSignatures)
+ {
+ size_t nEOF = 0;
+ if (GetEOFOfSignature(rSignature, nEOF))
+ {
+ aSignatureEOFs.insert(nEOF);
+ }
+ }
+
+ std::vector<unsigned int> aTrailerEnds = pPdfDocument->getTrailerEnds();
+
+ m_aSignatureInfos.clear();
+
+ int nMDPPerm = GetMDPPerm(aSignatures);
+
+ for (size_t i = 0; i < aSignatures.size(); ++i)
+ {
+ SignatureInformation aInfo(i);
+
+ if (!ValidateSignature(rStream, aSignatures[i], aInfo, nMDPPerm, aSignatureEOFs,
+ aTrailerEnds))
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to determine digest match");
+ }
+
+ m_aSignatureInfos.push_back(aInfo);
+ }
+
+ return true;
+}
+
+SignatureInformations const& PDFSignatureHelper::GetSignatureInformations() const
+{
+ return m_aSignatureInfos;
+}
+
+uno::Sequence<security::DocumentSignatureInformation>
+PDFSignatureHelper::GetDocumentSignatureInformations(
+ const uno::Reference<xml::crypto::XSecurityEnvironment>& xSecEnv) const
+{
+ uno::Sequence<security::DocumentSignatureInformation> aRet(m_aSignatureInfos.size());
+ auto aRetRange = asNonConstRange(aRet);
+
+ for (size_t i = 0; i < m_aSignatureInfos.size(); ++i)
+ {
+ const SignatureInformation& rInternal = m_aSignatureInfos[i];
+ security::DocumentSignatureInformation& rExternal = aRetRange[i];
+ rExternal.SignatureIsValid
+ = rInternal.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
+ if (rInternal.GetSigningCertificate()
+ && !rInternal.GetSigningCertificate()->X509Certificate.isEmpty())
+ {
+ rExternal.Signer = xSecEnv->createCertificateFromAscii(
+ rInternal.GetSigningCertificate()->X509Certificate);
+ }
+ rExternal.PartialDocumentSignature = rInternal.bPartialDocumentSignature;
+
+ // Verify certificate.
+ if (rExternal.Signer.is())
+ {
+ try
+ {
+ rExternal.CertificateStatus = xSecEnv->verifyCertificate(rExternal.Signer, {});
+ }
+ catch (const uno::SecurityException&)
+ {
+ DBG_UNHANDLED_EXCEPTION("xmlsecurity.helper", "failed to verify certificate");
+ rExternal.CertificateStatus = security::CertificateValidity::INVALID;
+ }
+ }
+ else
+ rExternal.CertificateStatus = security::CertificateValidity::INVALID;
+ }
+
+ return aRet;
+}
+
+sal_Int32 PDFSignatureHelper::GetNewSecurityId() const { return m_aSignatureInfos.size(); }
+
+void PDFSignatureHelper::SetX509Certificate(
+ const uno::Reference<security::XCertificate>& xCertificate)
+{
+ m_xCertificate = xCertificate;
+}
+
+void PDFSignatureHelper::SetDescription(const OUString& rDescription)
+{
+ m_aDescription = rDescription;
+}
+
+bool PDFSignatureHelper::Sign(const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<io::XInputStream>& xInputStream, bool bAdES)
+{
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+ vcl::filter::PDFDocument aDocument;
+ if (!aDocument.Read(*pStream))
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to read the document");
+ return false;
+ }
+
+ sal_Int32 nPage = 0;
+ std::vector<sal_Int8> aSignatureLineShape;
+ GetSignatureLineShape(xModel, nPage, aSignatureLineShape);
+ if (nPage > 0)
+ {
+ // UNO page number is 1-based.
+ aDocument.SetSignaturePage(nPage - 1);
+ }
+ if (!aSignatureLineShape.empty())
+ {
+ aDocument.SetSignatureLine(std::move(aSignatureLineShape));
+ }
+
+ if (!aDocument.Sign(m_xCertificate, m_aDescription, bAdES))
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to sign");
+ return false;
+ }
+
+ uno::Reference<io::XStream> xStream(xInputStream, uno::UNO_QUERY);
+ std::unique_ptr<SvStream> pOutStream(utl::UcbStreamHelper::CreateStream(xStream, true));
+ if (!aDocument.Write(*pOutStream))
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to write signed data");
+ return false;
+ }
+
+ return true;
+}
+
+bool PDFSignatureHelper::RemoveSignature(const uno::Reference<io::XInputStream>& xInputStream,
+ sal_uInt16 nPosition)
+{
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+ vcl::filter::PDFDocument aDocument;
+ if (!aDocument.Read(*pStream))
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to read the document");
+ return false;
+ }
+
+ if (!aDocument.RemoveSignature(nPosition))
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to remove signature");
+ return false;
+ }
+
+ uno::Reference<io::XStream> xStream(xInputStream, uno::UNO_QUERY);
+ uno::Reference<io::XTruncate> xTruncate(xStream, uno::UNO_QUERY);
+ if (!xTruncate.is())
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to truncate");
+ return false;
+ }
+ xTruncate->truncate();
+ std::unique_ptr<SvStream> pOutStream(utl::UcbStreamHelper::CreateStream(xStream, true));
+ if (!aDocument.Write(*pOutStream))
+ {
+ SAL_WARN("xmlsecurity.helper", "failed to write without signature");
+ return false;
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/xmlsignaturehelper.cxx b/xmlsecurity/source/helper/xmlsignaturehelper.cxx
new file mode 100644
index 0000000000..0b5825b125
--- /dev/null
+++ b/xmlsecurity/source/helper/xmlsignaturehelper.cxx
@@ -0,0 +1,713 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <xmlsignaturehelper.hxx>
+#include <documentsignaturehelper.hxx>
+#include <xsecctl.hxx>
+#include <biginteger.hxx>
+
+#include <UriBindingHelper.hxx>
+
+#include <tools/datetime.hxx>
+
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+
+#include <comphelper/attributelist.hxx>
+#include <comphelper/ofopxmlhelper.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+
+#include <optional>
+
+constexpr OUStringLiteral NS_DOCUMENTSIGNATURES = u"http://openoffice.org/2004/documentsignatures";
+constexpr OUStringLiteral NS_DOCUMENTSIGNATURES_ODF_1_2 = u"urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0";
+constexpr OUString OOXML_SIGNATURE_ORIGIN = u"http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin"_ustr;
+constexpr OUString OOXML_SIGNATURE_SIGNATURE = u"http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature"_ustr;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::graphic;
+using namespace ::com::sun::star::uno;
+
+XMLSignatureHelper::XMLSignatureHelper( const uno::Reference< uno::XComponentContext >& rxCtx)
+ : mxCtx(rxCtx), mbODFPre1_2(false)
+{
+ mpXSecController = new XSecController(rxCtx);
+ mbError = false;
+}
+
+XMLSignatureHelper::~XMLSignatureHelper()
+{
+}
+
+void XMLSignatureHelper::SetStorage(
+ const Reference < css::embed::XStorage >& rxStorage,
+ std::u16string_view sODFVersion)
+{
+ SAL_WARN_IF( mxUriBinding.is(), "xmlsecurity.helper", "SetStorage - UriBinding already set!" );
+ mxUriBinding = new UriBindingHelper( rxStorage );
+ SAL_WARN_IF(!rxStorage.is(), "xmlsecurity.helper", "SetStorage - empty storage!");
+ mbODFPre1_2 = DocumentSignatureHelper::isODFPre_1_2(sODFVersion);
+}
+
+
+void XMLSignatureHelper::SetStartVerifySignatureHdl( const Link<LinkParamNone*,bool>& rLink )
+{
+ maStartVerifySignatureHdl = rLink;
+}
+
+
+void XMLSignatureHelper::StartMission(const uno::Reference<xml::crypto::XXMLSecurityContext>& xSecurityContext)
+{
+ if ( !mxUriBinding.is() )
+ mxUriBinding = new UriBindingHelper();
+
+ mpXSecController->startMission(mxUriBinding, xSecurityContext);
+}
+
+void XMLSignatureHelper::EndMission()
+{
+ mpXSecController->endMission();
+}
+
+sal_Int32 XMLSignatureHelper::GetNewSecurityId()
+{
+ return mpXSecController->getNewSecurityId();
+}
+
+void XMLSignatureHelper::SetX509Certificate(
+ sal_Int32 nSecurityId,
+ const OUString& ouX509IssuerName,
+ const OUString& ouX509SerialNumber,
+ const OUString& ouX509Cert,
+ const OUString& ouX509CertDigest,
+ svl::crypto::SignatureMethodAlgorithm eAlgorithmID)
+{
+ mpXSecController->setX509Certificate(
+ nSecurityId,
+ ouX509IssuerName,
+ ouX509SerialNumber,
+ ouX509Cert,
+ ouX509CertDigest,
+ eAlgorithmID);
+}
+
+void XMLSignatureHelper::AddEncapsulatedX509Certificate(const OUString& ouEncapsulatedX509Certificate)
+{
+ mpXSecController->addEncapsulatedX509Certificate(ouEncapsulatedX509Certificate);
+}
+
+void XMLSignatureHelper::SetGpgCertificate(sal_Int32 nSecurityId,
+ const OUString& ouGpgCertDigest,
+ const OUString& ouGpgCert,
+ const OUString& ouGpgOwner)
+{
+ mpXSecController->setGpgCertificate(
+ nSecurityId,
+ ouGpgCertDigest,
+ ouGpgCert,
+ ouGpgOwner);
+}
+
+void XMLSignatureHelper::SetDateTime( sal_Int32 nSecurityId, const ::DateTime& rDateTime )
+{
+ css::util::DateTime stDateTime = rDateTime.GetUNODateTime();
+ mpXSecController->setDate( nSecurityId, stDateTime );
+}
+
+void XMLSignatureHelper::SetDescription(sal_Int32 nSecurityId, const OUString& rDescription)
+{
+ mpXSecController->setDescription(nSecurityId, rDescription);
+}
+
+void XMLSignatureHelper::SetSignatureLineId(sal_Int32 nSecurityId, const OUString& rSignatureLineId)
+{
+ mpXSecController->setSignatureLineId(nSecurityId, rSignatureLineId);
+}
+
+void XMLSignatureHelper::SetSignatureLineValidGraphic(
+ sal_Int32 nSecurityId, const css::uno::Reference<XGraphic>& xValidGraphic)
+{
+ mpXSecController->setSignatureLineValidGraphic(nSecurityId, xValidGraphic);
+}
+
+void XMLSignatureHelper::SetSignatureLineInvalidGraphic(
+ sal_Int32 nSecurityId, const css::uno::Reference<XGraphic>& xInvalidGraphic)
+{
+ mpXSecController->setSignatureLineInvalidGraphic(nSecurityId, xInvalidGraphic);
+}
+
+void XMLSignatureHelper::AddForSigning( sal_Int32 nSecurityId, const OUString& uri, bool bBinary, bool bXAdESCompliantIfODF )
+{
+ mpXSecController->signAStream( nSecurityId, uri, bBinary, bXAdESCompliantIfODF );
+}
+
+
+uno::Reference<xml::sax::XWriter> XMLSignatureHelper::CreateDocumentHandlerWithHeader(
+ const css::uno::Reference< css::io::XOutputStream >& xOutputStream )
+{
+ /*
+ * get SAX writer component
+ */
+ uno::Reference< xml::sax::XWriter > xSaxWriter = xml::sax::Writer::create(mxCtx);
+
+ /*
+ * connect XML writer to output stream
+ */
+ xSaxWriter->setOutputStream( xOutputStream );
+
+ /*
+ * write the xml context for signatures
+ */
+ rtl::Reference<comphelper::AttributeList> pAttributeList = new comphelper::AttributeList();
+ OUString sNamespace;
+ if (mbODFPre1_2)
+ sNamespace = NS_DOCUMENTSIGNATURES;
+ else
+ sNamespace = NS_DOCUMENTSIGNATURES_ODF_1_2;
+
+ pAttributeList->AddAttribute(
+ "xmlns",
+ sNamespace);
+
+ xSaxWriter->startDocument();
+ xSaxWriter->startElement(
+ "document-signatures",
+ pAttributeList);
+
+ return xSaxWriter;
+}
+
+void XMLSignatureHelper::CloseDocumentHandler( const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler )
+{
+ xDocumentHandler->endElement( "document-signatures" );
+ xDocumentHandler->endDocument();
+}
+
+void XMLSignatureHelper::ExportSignature(
+ const uno::Reference< xml::sax::XDocumentHandler >& xDocumentHandler,
+ const SignatureInformation& signatureInfo,
+ bool bXAdESCompliantIfODF )
+{
+ XSecController::exportSignature(xDocumentHandler, signatureInfo, bXAdESCompliantIfODF);
+}
+
+void XMLSignatureHelper::ExportOOXMLSignature(const uno::Reference<embed::XStorage>& xRootStorage, const uno::Reference<embed::XStorage>& xSignatureStorage, const SignatureInformation& rInformation, int nSignatureIndex)
+{
+ uno::Reference<io::XOutputStream> xOutputStream(xSignatureStorage->openStreamElement("sig" + OUString::number(nSignatureIndex) + ".xml", embed::ElementModes::READWRITE), uno::UNO_QUERY);
+
+ if (rInformation.aSignatureBytes.hasElements())
+ // This is a signature roundtrip, just write back the signature as-is.
+ xOutputStream->writeBytes(rInformation.aSignatureBytes);
+ else
+ {
+ uno::Reference<xml::sax::XWriter> xSaxWriter = xml::sax::Writer::create(mxCtx);
+ xSaxWriter->setOutputStream(xOutputStream);
+ xSaxWriter->startDocument();
+
+ mpXSecController->exportOOXMLSignature(xRootStorage, xSaxWriter, rInformation);
+
+ xSaxWriter->endDocument();
+ }
+}
+
+void XMLSignatureHelper::CreateAndWriteSignature( const uno::Reference< xml::sax::XDocumentHandler >& xDocumentHandler, bool bXAdESCompliantIfODF )
+{
+ mbError = false;
+
+ if ( !mpXSecController->WriteSignature( xDocumentHandler, bXAdESCompliantIfODF ) )
+ {
+ mbError = true;
+ }
+}
+
+bool XMLSignatureHelper::ReadAndVerifySignature( const css::uno::Reference< css::io::XInputStream >& xInputStream )
+{
+ mbError = false;
+
+ SAL_WARN_IF(!xInputStream.is(), "xmlsecurity.helper", "input stream missing");
+
+ // prepare ParserInputSource
+ xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream = xInputStream;
+
+ // get SAX parser component
+ uno::Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(mxCtx);
+
+ // create a signature reader
+ uno::Reference< xml::sax::XDocumentHandler > xHandler
+ = mpXSecController->createSignatureReader(*this);
+
+ // setup the connection:
+ // Parser -> SignatureReader
+ xParser->setDocumentHandler( xHandler );
+
+ // Parse the stream.
+ try
+ {
+ xParser->parseStream( aParserInput );
+ }
+ catch( uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("xmlsecurity.helper");
+ mbError = true;
+ }
+
+ // release the signature reader
+ mpXSecController->releaseSignatureReader( );
+
+ return !mbError;
+}
+
+SignatureInformation XMLSignatureHelper::GetSignatureInformation( sal_Int32 nSecurityId ) const
+{
+ return mpXSecController->getSignatureInformation( nSecurityId );
+}
+
+SignatureInformations XMLSignatureHelper::GetSignatureInformations() const
+{
+ return mpXSecController->getSignatureInformations();
+}
+
+void XMLSignatureHelper::StartVerifySignatureElement()
+{
+ if ( !maStartVerifySignatureHdl.IsSet() || maStartVerifySignatureHdl.Call(nullptr) )
+ {
+ sal_Int32 nSignatureId = mpXSecController->getNewSecurityId();
+ mpXSecController->addSignature( nSignatureId );
+ }
+}
+
+namespace
+{
+bool lcl_isSignatureType(const beans::StringPair& rPair)
+{
+ return rPair.First == "Type" && rPair.Second == OOXML_SIGNATURE_SIGNATURE;
+}
+bool lcl_isSignatureOriginType(const beans::StringPair& rPair)
+{
+ return rPair.First == "Type" && rPair.Second == OOXML_SIGNATURE_ORIGIN;
+}
+}
+
+bool XMLSignatureHelper::ReadAndVerifySignatureStorage(const uno::Reference<embed::XStorage>& xStorage, bool bCacheLastSignature)
+{
+ sal_Int32 nOpenMode = embed::ElementModes::READ;
+ if (xStorage.is() && !xStorage->hasByName("_rels"))
+ {
+ SAL_WARN("xmlsecurity.helper", "expected stream, in signature storage but not found: _rels");
+ return false;
+ }
+
+ uno::Reference<embed::XStorage> xSubStorage = xStorage->openStorageElement("_rels", nOpenMode);
+ uno::Reference<io::XInputStream> xRelStream(xSubStorage->openStreamElement("origin.sigs.rels", nOpenMode), uno::UNO_QUERY);
+ uno::Sequence< uno::Sequence<beans::StringPair> > aRelationsInfo = comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(xRelStream, u"origin.sigs.rels", mxCtx);
+
+ for (sal_Int32 i = 0; i < aRelationsInfo.getLength(); ++i)
+ {
+ const uno::Sequence<beans::StringPair>& rRelation = aRelationsInfo[i];
+ if (std::any_of(rRelation.begin(), rRelation.end(), lcl_isSignatureType))
+ {
+ auto it = std::find_if(rRelation.begin(), rRelation.end(), [](const beans::StringPair& rPair) { return rPair.First == "Target"; });
+ if (it != rRelation.end())
+ {
+ if (xStorage.is() && !xStorage->hasByName(it->Second))
+ {
+ SAL_WARN("xmlsecurity.helper", "expected stream, but not found: " << it->Second);
+ continue;
+ }
+
+ uno::Reference<io::XInputStream> xInputStream(xStorage->openStreamElement(it->Second, nOpenMode), uno::UNO_QUERY);
+ if (!ReadAndVerifySignatureStorageStream(xInputStream))
+ return false;
+
+ // By default, we cache. If it's requested, then we don't cache the last signature.
+ bool bCache = true;
+ if (!bCacheLastSignature && i == aRelationsInfo.getLength() - 1)
+ bCache = false;
+
+ if (!bCache)
+ continue;
+ // Store the contents of the stream as is, in case we need to write it back later.
+ xInputStream.clear();
+ xInputStream.set(xStorage->openStreamElement(it->Second, nOpenMode), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xPropertySet(xInputStream, uno::UNO_QUERY);
+ if (!xPropertySet.is())
+ continue;
+
+ sal_Int64 nSize = 0;
+ xPropertySet->getPropertyValue("Size") >>= nSize;
+ if (nSize < 0 || nSize > SAL_MAX_INT32)
+ {
+ SAL_WARN("xmlsecurity.helper", "bogus signature size: " << nSize);
+ continue;
+ }
+ uno::Sequence<sal_Int8> aData;
+ xInputStream->readBytes(aData, nSize);
+ mpXSecController->setSignatureBytes(aData);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool XMLSignatureHelper::ReadAndVerifySignatureStorageStream(const css::uno::Reference<css::io::XInputStream>& xInputStream)
+{
+ mbError = false;
+
+ // Create the input source.
+ xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream = xInputStream;
+
+ // Create the sax parser.
+ uno::Reference<xml::sax::XParser> xParser = xml::sax::Parser::create(mxCtx);
+
+ // Create the signature reader.
+ uno::Reference<xml::sax::XDocumentHandler> xHandler = mpXSecController->createSignatureReader(*this, embed::StorageFormats::OFOPXML);
+
+ // Parser -> signature reader.
+ xParser->setDocumentHandler(xHandler);
+
+ // Parse the stream.
+ try
+ {
+ xParser->parseStream(aParserInput);
+ }
+ catch(const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("xmlsecurity.helper");
+ mbError = true;
+ }
+
+ // release the signature reader
+ mpXSecController->releaseSignatureReader();
+
+ return !mbError;
+}
+
+void XMLSignatureHelper::EnsureSignaturesRelation(const css::uno::Reference<css::embed::XStorage>& xStorage, bool bAdd)
+{
+ sal_Int32 nOpenMode = embed::ElementModes::READWRITE;
+ uno::Reference<embed::XStorage> xSubStorage = xStorage->openStorageElement("_rels", nOpenMode);
+ uno::Reference<io::XInputStream> xRelStream(xSubStorage->openStreamElement(".rels", nOpenMode), uno::UNO_QUERY);
+ std::vector< uno::Sequence<beans::StringPair> > aRelationsInfo = comphelper::sequenceToContainer< std::vector< uno::Sequence<beans::StringPair> > >(comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(xRelStream, u".rels", mxCtx));
+
+ // Do we have a relation already?
+ bool bHaveRelation = false;
+ int nCount = 0;
+ for (const uno::Sequence<beans::StringPair>& rRelation : aRelationsInfo)
+ {
+ auto aRelation = comphelper::sequenceToContainer< std::vector<beans::StringPair> >(rRelation);
+ if (std::any_of(aRelation.begin(), aRelation.end(), lcl_isSignatureOriginType))
+ {
+ bHaveRelation = true;
+ break;
+ }
+ ++nCount;
+ }
+
+ if (!bHaveRelation && bAdd)
+ {
+ // No, and have to add one.
+ std::vector<beans::StringPair> aRelation;
+ aRelation.emplace_back("Id", "rId" + OUString::number(++nCount));
+ aRelation.emplace_back("Type", OOXML_SIGNATURE_ORIGIN);
+ aRelation.emplace_back("Target", "_xmlsignatures/origin.sigs");
+ aRelationsInfo.push_back(comphelper::containerToSequence(aRelation));
+ }
+ else if (bHaveRelation && !bAdd)
+ {
+ // Yes, and need to remove it.
+ for (std::vector< uno::Sequence<beans::StringPair> >::iterator it = aRelationsInfo.begin(); it != aRelationsInfo.end();)
+ {
+ auto aRelation = comphelper::sequenceToContainer< std::vector<beans::StringPair> >(*it);
+ if (std::any_of(aRelation.begin(), aRelation.end(), lcl_isSignatureOriginType))
+ it = aRelationsInfo.erase(it);
+ else
+ ++it;
+ }
+ }
+
+ // Write it back.
+ uno::Reference<io::XTruncate> xTruncate(xRelStream, uno::UNO_QUERY);
+ xTruncate->truncate();
+ uno::Reference<io::XOutputStream> xOutputStream(xRelStream, uno::UNO_QUERY);
+ comphelper::OFOPXMLHelper::WriteRelationsInfoSequence(xOutputStream, comphelper::containerToSequence(aRelationsInfo), mxCtx);
+
+ // Commit it.
+ uno::Reference<embed::XTransactedObject> xTransact(xSubStorage, uno::UNO_QUERY);
+ xTransact->commit();
+ xTransact.set(xStorage, uno::UNO_QUERY);
+ xTransact->commit();
+}
+
+void XMLSignatureHelper::ExportSignatureRelations(const css::uno::Reference<css::embed::XStorage>& xStorage, int nSignatureCount)
+{
+ // Write the empty file, its relations will be the signatures.
+ sal_Int32 nOpenMode = embed::ElementModes::READWRITE;
+ uno::Reference<io::XOutputStream> xOriginStream(xStorage->openStreamElement("origin.sigs", nOpenMode), uno::UNO_QUERY);
+ uno::Reference<io::XTruncate> xTruncate(xOriginStream, uno::UNO_QUERY);
+ xTruncate->truncate();
+ xOriginStream->closeOutput();
+
+ // Write the relations.
+ uno::Reference<embed::XStorage> xSubStorage = xStorage->openStorageElement("_rels", nOpenMode);
+ uno::Reference<io::XOutputStream> xRelStream(xSubStorage->openStreamElement("origin.sigs.rels", nOpenMode), uno::UNO_QUERY);
+ std::vector< uno::Sequence<beans::StringPair> > aRelations;
+ for (int i = 0; i < nSignatureCount; ++i)
+ {
+ std::vector<beans::StringPair> aRelation;
+ aRelation.emplace_back("Id", "rId" + OUString::number(i + 1));
+ aRelation.emplace_back("Type", OOXML_SIGNATURE_SIGNATURE);
+ aRelation.emplace_back("Target", "sig" + OUString::number(i + 1) + ".xml");
+ aRelations.push_back(comphelper::containerToSequence(aRelation));
+ }
+ comphelper::OFOPXMLHelper::WriteRelationsInfoSequence(xRelStream, comphelper::containerToSequence(aRelations), mxCtx);
+ uno::Reference<embed::XTransactedObject> xTransact(xSubStorage, uno::UNO_QUERY);
+ xTransact->commit();
+}
+
+void XMLSignatureHelper::ExportSignatureContentTypes(const css::uno::Reference<css::embed::XStorage>& xStorage, int nSignatureCount)
+{
+ uno::Reference<io::XStream> xStream = xStorage->openStreamElement("[Content_Types].xml", embed::ElementModes::READWRITE);
+ uno::Reference<io::XInputStream> xInputStream = xStream->getInputStream();
+ uno::Sequence< uno::Sequence<beans::StringPair> > aContentTypeInfo = comphelper::OFOPXMLHelper::ReadContentTypeSequence(xInputStream, mxCtx);
+ if (aContentTypeInfo.getLength() < 2)
+ {
+ SAL_WARN("xmlsecurity.helper", "no defaults or overrides in aContentTypeInfo");
+ return;
+ }
+ auto pContentTypeInfo = aContentTypeInfo.getArray();
+
+ // Append rels and sigs to defaults, if it's not there already.
+ uno::Sequence<beans::StringPair>& rDefaults = pContentTypeInfo[0];
+ auto aDefaults = comphelper::sequenceToContainer< std::vector<beans::StringPair> >(rDefaults);
+ if (std::none_of(std::cbegin(rDefaults), std::cend(rDefaults), [](const beans::StringPair& rPair) { return rPair.First == "rels"; }))
+ aDefaults.emplace_back("rels", "application/vnd.openxmlformats-package.relationships+xml");
+
+ if (std::none_of(std::cbegin(rDefaults), std::cend(rDefaults), [](const beans::StringPair& rPair) { return rPair.First == "sigs"; }))
+ aDefaults.emplace_back("sigs", "application/vnd.openxmlformats-package.digital-signature-origin");
+ rDefaults = comphelper::containerToSequence(aDefaults);
+
+ // Remove existing signature overrides.
+ uno::Sequence<beans::StringPair>& rOverrides = pContentTypeInfo[1];
+ auto aOverrides = comphelper::sequenceToContainer< std::vector<beans::StringPair> >(rOverrides);
+ std::erase_if(aOverrides, [](const beans::StringPair& rPair)
+ {
+ return rPair.First.startsWith("/_xmlsignatures/sig");
+ });
+
+ // Add our signature overrides.
+ for (int i = 1; i <= nSignatureCount; ++i)
+ aOverrides.emplace_back("/_xmlsignatures/sig" + OUString::number(i) + ".xml", "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml");
+
+ rOverrides = comphelper::containerToSequence(aOverrides);
+ uno::Reference<io::XOutputStream> xOutputStream = xStream->getOutputStream();
+ uno::Reference <io::XTruncate> xTruncate(xOutputStream, uno::UNO_QUERY);
+ xTruncate->truncate();
+ comphelper::OFOPXMLHelper::WriteContentSequence(xOutputStream, rDefaults, rOverrides, mxCtx);
+ uno::Reference<embed::XTransactedObject> xTransact(xStorage, uno::UNO_QUERY);
+ xTransact->commit();
+}
+void XMLSignatureHelper::CreateAndWriteOOXMLSignature(const uno::Reference<embed::XStorage>& xRootStorage, const uno::Reference<embed::XStorage>& xSignatureStorage, int nSignatureIndex)
+{
+ uno::Reference<io::XOutputStream> xOutputStream(xSignatureStorage->openStreamElement("sig" + OUString::number(nSignatureIndex) + ".xml", embed::ElementModes::READWRITE), uno::UNO_QUERY);
+ uno::Reference<xml::sax::XWriter> xSaxWriter = xml::sax::Writer::create(mxCtx);
+ xSaxWriter->setOutputStream(xOutputStream);
+ xSaxWriter->startDocument();
+
+ mbError = false;
+ if (!mpXSecController->WriteOOXMLSignature(xRootStorage, xSaxWriter))
+ mbError = true;
+
+ xSaxWriter->endDocument();
+}
+
+/** check this constraint from xmldsig-core 4.5.4:
+
+ All certificates appearing in an X509Data element MUST relate to the
+ validation key by either containing it or being part of a certification
+ chain that terminates in a certificate containing the validation key.
+ */
+static auto CheckX509Data(
+ uno::Reference<xml::crypto::XSecurityEnvironment> const& xSecEnv,
+ std::vector<SignatureInformation::X509CertInfo> const& rX509CertInfos,
+ std::vector<uno::Reference<security::XCertificate>> & rCerts,
+ std::vector<SignatureInformation::X509CertInfo> & rSorted) -> bool
+{
+ assert(rCerts.empty());
+ assert(rSorted.empty());
+ if (rX509CertInfos.empty())
+ {
+ SAL_WARN("xmlsecurity.comp", "no X509Data");
+ return false;
+ }
+ std::vector<uno::Reference<security::XCertificate>> certs;
+ for (SignatureInformation::X509CertInfo const& it : rX509CertInfos)
+ {
+ if (!it.X509Certificate.isEmpty())
+ {
+ certs.emplace_back(xSecEnv->createCertificateFromAscii(it.X509Certificate));
+ }
+ else
+ {
+ certs.emplace_back(xSecEnv->getCertificate(
+ it.X509IssuerName,
+ xmlsecurity::numericStringToBigInteger(it.X509SerialNumber)));
+ }
+ if (!certs.back().is())
+ {
+ SAL_WARN("xmlsecurity.comp", "X509Data cannot be parsed");
+ return false;
+ }
+ }
+
+ // first, search one whose issuer isn't in the list, or a self-signed one
+ std::optional<size_t> start;
+ for (size_t i = 0; i < certs.size(); ++i)
+ {
+ for (size_t j = 0; ; ++j)
+ {
+ if (j == certs.size())
+ {
+ if (start)
+ {
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate has no issuer but already have start of chain: " << certs[i]->getSubjectName());
+ return false;
+ }
+ start = i; // issuer isn't in the list
+ break;
+ }
+ if (xmlsecurity::EqualDistinguishedNames(certs[i]->getIssuerName(), certs[j]->getSubjectName(), xmlsecurity::NOCOMPAT))
+ {
+ if (i == j) // self signed
+ {
+ if (start)
+ {
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate is self-signed but already have start of chain: " << certs[i]->getSubjectName());
+ return false;
+ }
+ start = i;
+ }
+ break;
+ }
+ }
+ }
+ std::vector<size_t> chain;
+ if (!start)
+ {
+ // this can only be a cycle?
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: cycle detected");
+ return false;
+ }
+ chain.emplace_back(*start);
+
+ // second, check that there is a chain, no tree or cycle...
+ for (size_t i = 0; i < certs.size(); ++i)
+ {
+ assert(chain.size() == i + 1);
+ for (size_t j = 0; j < certs.size(); ++j)
+ {
+ if (chain[i] != j)
+ {
+ if (xmlsecurity::EqualDistinguishedNames(
+ certs[chain[i]]->getSubjectName(), certs[j]->getIssuerName(), xmlsecurity::NOCOMPAT))
+ {
+ if (chain.size() != i + 1) // already found issue?
+ {
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate issued 2 others: " << certs[chain[i]]->getSubjectName());
+ return false;
+ }
+ chain.emplace_back(j);
+ }
+ }
+ }
+ if (i == certs.size() - 1)
+ { // last one: must be a leaf
+ if (chain.size() != i + 1)
+ {
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate in cycle: " << certs[chain[i]]->getSubjectName());
+ return false;
+ }
+ }
+ else if (chain.size() != i + 2)
+ { // not issuer of another?
+ SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate issued 0 others: " << certs[chain[i]]->getSubjectName());
+ return false;
+ }
+ }
+
+ // success
+ assert(chain.size() == rX509CertInfos.size());
+ for (auto const& it : chain)
+ {
+ rSorted.emplace_back(rX509CertInfos[it]);
+ rCerts.emplace_back(certs[it]);
+ }
+ return true;
+}
+
+std::vector<uno::Reference<security::XCertificate>>
+XMLSignatureHelper::CheckAndUpdateSignatureInformation(
+ uno::Reference<xml::crypto::XSecurityEnvironment> const& xSecEnv,
+ SignatureInformation const& rInfo)
+{
+ // if the check fails, it's not possible to determine which X509Data
+ // contained the signing certificate - the UI cannot display something
+ // useful in this case, so prevent anything misleading by clearing the
+ // X509Datas.
+
+ std::vector<uno::Reference<security::XCertificate>> certs;
+ std::vector<SignatureInformation::X509Data> datas;
+ // TODO: for now, just merge all X509Datas together for checking...
+ // (this will probably break round-trip of signature with multiple X509Data,
+ // no idea if that is a problem)
+ SignatureInformation::X509Data temp;
+ SignatureInformation::X509Data tempResult;
+ for (auto const& rData : rInfo.X509Datas)
+ {
+ for (auto const& it : rData)
+ {
+ temp.emplace_back(it);
+ }
+ }
+ if (CheckX509Data(xSecEnv, temp, certs, tempResult))
+ {
+ datas.emplace_back(tempResult);
+ }
+
+ // rInfo is a copy, update the original
+ mpXSecController->UpdateSignatureInformation(rInfo.nSecurityId, std::move(datas));
+ return certs;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/xsecctl.cxx b/xmlsecurity/source/helper/xsecctl.cxx
new file mode 100644
index 0000000000..08b822f157
--- /dev/null
+++ b/xmlsecurity/source/helper/xsecctl.cxx
@@ -0,0 +1,1005 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_gpgme.h>
+
+#include <utility>
+#include <xsecctl.hxx>
+#include <documentsignaturehelper.hxx>
+#include <framework/saxeventkeeperimpl.hxx>
+#include <xmlsec/xmldocumentwrapper_xmlsecimpl.hxx>
+#if HAVE_FEATURE_GPGME
+# include <gpg/xmlsignature_gpgimpl.hxx>
+#endif
+
+#include <com/sun/star/xml/crypto/sax/XMissionTaker.hpp>
+#include <com/sun/star/xml/crypto/SecurityOperationStatus.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/xml/sax/XParser.hpp>
+#include <com/sun/star/xml/crypto/XXMLSignature.hpp>
+
+#include <comphelper/attributelist.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ref.hxx>
+#include <sal/log.hxx>
+#include <unotools/datetime.hxx>
+#include "ooxmlsecexporter.hxx"
+#include <UriBindingHelper.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+OUString getDigestURI(sal_Int32 nID)
+{
+ switch( nID )
+ {
+ case css::xml::crypto::DigestID::SHA1:
+ return ALGO_XMLDSIGSHA1;
+ case css::xml::crypto::DigestID::SHA256:
+ return ALGO_XMLDSIGSHA256;
+ case css::xml::crypto::DigestID::SHA512:
+ return ALGO_XMLDSIGSHA512;
+ default:
+ return ALGO_XMLDSIGSHA1;
+ }
+}
+OUString getSignatureURI(svl::crypto::SignatureMethodAlgorithm eAlgorithm, sal_Int32 nDigestID)
+{
+ OUString aRet;
+
+ if (eAlgorithm == svl::crypto::SignatureMethodAlgorithm::ECDSA)
+ {
+ switch (nDigestID)
+ {
+ case css::xml::crypto::DigestID::SHA1:
+ aRet = ALGO_ECDSASHA1;
+ break;
+ case css::xml::crypto::DigestID::SHA256:
+ aRet = ALGO_ECDSASHA256;
+ break;
+ case css::xml::crypto::DigestID::SHA512:
+ aRet = ALGO_ECDSASHA512;
+ break;
+ default:
+ aRet = ALGO_ECDSASHA1;
+ break;
+ }
+ }
+ if (!aRet.isEmpty())
+ return aRet;
+
+ switch (nDigestID)
+ {
+ case css::xml::crypto::DigestID::SHA1:
+ return ALGO_RSASHA1;
+ case css::xml::crypto::DigestID::SHA256:
+ return ALGO_RSASHA256;
+ case css::xml::crypto::DigestID::SHA512:
+ return ALGO_RSASHA512;
+ default:
+ return ALGO_RSASHA1;
+ }
+}
+}
+
+XSecController::XSecController( css::uno::Reference<css::uno::XComponentContext> xCtx )
+ : mxCtx(std::move(xCtx))
+ , m_nNextSecurityId(1)
+ , m_bIsPreviousNodeInitializable(false)
+ , m_bIsSAXEventKeeperConnected(false)
+ , m_bIsCollectingElement(false)
+ , m_bIsBlocking(false)
+ , m_eStatusOfSecurityComponents(InitializationState::UNINITIALIZED)
+ , m_bIsSAXEventKeeperSticky(false)
+ , m_nReservedSignatureId(0)
+ , m_bVerifyCurrentSignature(false)
+{
+}
+
+XSecController::~XSecController()
+{
+}
+
+
+/*
+ * private methods
+ */
+int XSecController::findSignatureInfor( sal_Int32 nSecurityId) const
+/****** XSecController/findSignatureInfor *************************************
+ *
+ * NAME
+ * findSignatureInfor -- find SignatureInformation struct for a particular
+ * signature
+ *
+ * SYNOPSIS
+ * index = findSignatureInfor( nSecurityId );
+ *
+ * INPUTS
+ * nSecurityId - the signature's id
+ *
+ * RESULT
+ * index - the index of the signature, or -1 when no such signature
+ * existing
+ ******************************************************************************/
+{
+ int i;
+ int size = m_vInternalSignatureInformations.size();
+
+ for (i=0; i<size; ++i)
+ {
+ if (m_vInternalSignatureInformations[i].signatureInfor.nSecurityId == nSecurityId)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void XSecController::createXSecComponent( )
+/****** XSecController/createXSecComponent ************************************
+ *
+ * NAME
+ * bResult = createXSecComponent -- creates xml security components
+ *
+ * FUNCTION
+ * Creates xml security components, including:
+ * 1. an xml signature bridge component
+ * 2. an XMLDocumentWrapper component
+ * 3. a SAXEventKeeper component
+ ******************************************************************************/
+{
+ /*
+ * marks all security components are not available.
+ */
+ m_eStatusOfSecurityComponents = InitializationState::FAILTOINITIALIZED;
+ m_xXMLSignature = nullptr;
+ m_xXMLDocumentWrapper = nullptr;
+ m_xSAXEventKeeper = nullptr;
+
+ css::uno::Reference< css::lang::XMultiComponentFactory > xMCF( mxCtx->getServiceManager() );
+
+#if HAVE_FEATURE_GPGME
+ uno::Reference< lang::XServiceInfo > xServiceInfo( m_xSecurityContext, css::uno::UNO_QUERY );
+ if (xServiceInfo->getImplementationName() == "com.sun.star.xml.security.gpg.XMLSecurityContext_GpgImpl")
+ m_xXMLSignature.set(new XMLSignature_GpgImpl());
+ else // xmlsec or mscrypt
+#endif
+ m_xXMLSignature.set(xMCF->createInstanceWithContext("com.sun.star.xml.crypto.XMLSignature", mxCtx), css::uno::UNO_QUERY);
+
+ bool bSuccess = m_xXMLSignature.is();
+ if ( bSuccess )
+ /*
+ * XMLSignature created successfully.
+ */
+ m_xXMLDocumentWrapper = new XMLDocumentWrapper_XmlSecImpl();
+
+ bSuccess &= m_xXMLDocumentWrapper.is();
+ if ( bSuccess )
+ m_xSAXEventKeeper = new SAXEventKeeperImpl();
+
+ bSuccess &= m_xSAXEventKeeper.is();
+
+ if (!bSuccess)
+ /*
+ * SAXEventKeeper created successfully.
+ */
+ return;
+
+ css::uno::Sequence <css::uno::Any> arg{ css::uno::Any(
+ uno::Reference<xml::wrapper::XXMLDocumentWrapper>(m_xXMLDocumentWrapper)) };
+ m_xSAXEventKeeper->initialize(arg);
+
+ css::uno::Reference< css::xml::crypto::sax::XSAXEventKeeperStatusChangeListener >
+ xStatusChangeListener = this;
+
+ m_xSAXEventKeeper->addSAXEventKeeperStatusChangeListener( xStatusChangeListener );
+
+ m_eStatusOfSecurityComponents = InitializationState::INITIALIZED;
+}
+
+bool XSecController::chainOn()
+/****** XSecController/chainOn ************************************************
+ *
+ * NAME
+ * chainOn -- tries to connect the SAXEventKeeper with the SAX chain.
+ *
+ * SYNOPSIS
+ * bJustChainingOn = chainOn();
+ *
+ * FUNCTION
+ * First, checks whether the SAXEventKeeper is on the SAX chain. If not,
+ * creates xml security components, and chains the SAXEventKeeper into
+ * the SAX chain.
+ * Before being chained in, the SAXEventKeeper needs to receive all
+ * missed key SAX events, which can promise the DOM tree buffered by the
+ * SAXEventKeeper has the same structure with the original document.
+ *
+ * RESULT
+ * bJustChainingOn - whether the SAXEventKeeper is just chained into the
+ * SAX chain.
+ *
+ * NOTES
+ * Sometimes, the last key SAX event can't be transferred to the
+ * SAXEventKeeper together.
+ * For instance, at the time a referenced element is detected, the
+ * startElement event has already been reserved by the ElementStackKeeper.
+ * Meanwhile, an ElementCollector needs to be created before the
+ * SAXEventKeeper receives that startElement event.
+ * So for the SAXEventKeeper, it needs to receive all missed key SAX
+ * events except that startElement event, then adds a new
+ * ElementCollector, then receives that startElement event.
+ ******************************************************************************/
+{
+ bool rc = false;
+
+ if (!m_bIsSAXEventKeeperSticky && !m_bIsSAXEventKeeperConnected)
+ {
+ if ( m_eStatusOfSecurityComponents == InitializationState::UNINITIALIZED )
+ {
+ createXSecComponent();
+ }
+
+ if ( m_eStatusOfSecurityComponents == InitializationState::INITIALIZED )
+ /*
+ * if all security components are ready, chains on the SAXEventKeeper
+ */
+ {
+ /*
+ * disconnect the SAXEventKeeper with its current output handler,
+ * to make sure no SAX event is forwarded during the connecting
+ * phase.
+ */
+ m_xSAXEventKeeper->setNextHandler( nullptr );
+
+ css::uno::Reference< css::xml::sax::XDocumentHandler > xSEKHandler(m_xSAXEventKeeper);
+
+ /*
+ * connects the previous document handler on the SAX chain
+ */
+ if ( m_xPreviousNodeOnSAXChain.is() )
+ {
+ if ( m_bIsPreviousNodeInitializable )
+ {
+ css::uno::Reference< css::lang::XInitialization > xInitialization
+ (m_xPreviousNodeOnSAXChain, css::uno::UNO_QUERY);
+
+ xInitialization->initialize({ css::uno::Any(xSEKHandler) });
+ }
+ else
+ {
+ css::uno::Reference< css::xml::sax::XParser > xParser
+ (m_xPreviousNodeOnSAXChain, css::uno::UNO_QUERY);
+ xParser->setDocumentHandler( xSEKHandler );
+ }
+ }
+
+ /*
+ * connects the next document handler on the SAX chain
+ */
+ m_xSAXEventKeeper->setNextHandler(uno::Reference<xml::sax::XDocumentHandler>());
+
+ m_bIsSAXEventKeeperConnected = true;
+
+ rc = true;
+ }
+ }
+
+ return rc;
+}
+
+void XSecController::chainOff()
+/****** XSecController/chainOff ***********************************************
+ *
+ * NAME
+ * chainOff -- disconnects the SAXEventKeeper from the SAX chain.
+ ******************************************************************************/
+{
+ if (m_bIsSAXEventKeeperSticky )
+ return;
+
+ if (!m_bIsSAXEventKeeperConnected)
+ return;
+
+ m_xSAXEventKeeper->setNextHandler( nullptr );
+
+ if ( m_xPreviousNodeOnSAXChain.is() )
+ {
+ if ( m_bIsPreviousNodeInitializable )
+ {
+ css::uno::Reference< css::lang::XInitialization > xInitialization
+ (m_xPreviousNodeOnSAXChain, css::uno::UNO_QUERY);
+
+ css::uno::Sequence<css::uno::Any> aArgs{ css::uno::Any(
+ uno::Reference<xml::sax::XDocumentHandler>()) };
+ xInitialization->initialize(aArgs);
+ }
+ else
+ {
+ css::uno::Reference< css::xml::sax::XParser > xParser(m_xPreviousNodeOnSAXChain, css::uno::UNO_QUERY);
+ xParser->setDocumentHandler(uno::Reference<xml::sax::XDocumentHandler>());
+ }
+ }
+
+ m_bIsSAXEventKeeperConnected = false;
+}
+
+void XSecController::checkChainingStatus()
+/****** XSecController/checkChainingStatus ************************************
+ *
+ * NAME
+ * checkChainingStatus -- connects or disconnects the SAXEventKeeper
+ * according to the current situation.
+ *
+ * SYNOPSIS
+ * checkChainingStatus( );
+ *
+ * FUNCTION
+ * The SAXEventKeeper is chained into the SAX chain, when:
+ * 1. some element is being collected, or
+ * 2. the SAX event stream is blocking.
+ * Otherwise, chain off the SAXEventKeeper.
+ ******************************************************************************/
+{
+ if ( m_bIsCollectingElement || m_bIsBlocking )
+ {
+ chainOn();
+ }
+ else
+ {
+ chainOff();
+ }
+}
+
+void XSecController::initializeSAXChain()
+/****** XSecController/initializeSAXChain *************************************
+ *
+ * NAME
+ * initializeSAXChain -- initializes the SAX chain according to the
+ * current setting.
+ *
+ * FUNCTION
+ * Initializes the SAX chain, if the SAXEventKeeper is asked to be always
+ * on the SAX chain, chains it on. Otherwise, starts the
+ * ElementStackKeeper to reserve key SAX events.
+ ******************************************************************************/
+{
+ m_bIsSAXEventKeeperConnected = false;
+ m_bIsCollectingElement = false;
+ m_bIsBlocking = false;
+
+ chainOff();
+}
+
+css::uno::Reference< css::io::XInputStream >
+ XSecController::getObjectInputStream( const OUString& objectURL )
+/****** XSecController/getObjectInputStream ************************************
+ *
+ * NAME
+ * getObjectInputStream -- get a XInputStream interface from a SotStorage
+ *
+ * SYNOPSIS
+ * xInputStream = getObjectInputStream( objectURL );
+ *
+ * INPUTS
+ * objectURL - the object uri
+ *
+ * RESULT
+ * xInputStream - the XInputStream interface
+ ******************************************************************************/
+{
+ css::uno::Reference< css::io::XInputStream > xObjectInputStream;
+
+ SAL_WARN_IF( !m_xUriBinding.is(), "xmlsecurity.helper", "Need XUriBinding!" );
+
+ xObjectInputStream = m_xUriBinding->getUriBinding(objectURL);
+
+ return xObjectInputStream;
+}
+
+/*
+ * public methods
+ */
+
+sal_Int32 XSecController::getNewSecurityId( )
+{
+ sal_Int32 nId = m_nNextSecurityId;
+ m_nNextSecurityId++;
+ return nId;
+}
+
+void XSecController::startMission(const rtl::Reference<UriBindingHelper>& xUriBinding, const css::uno::Reference< css::xml::crypto::XXMLSecurityContext >& xSecurityContext )
+/****** XSecController/startMission *******************************************
+ *
+ * NAME
+ * startMission -- starts a new security mission.
+ *
+ * FUNCTION
+ * get ready for a new mission.
+ *
+ * INPUTS
+ * xUriBinding - the Uri binding that provide maps between uris and
+ * XInputStreams
+ * xSecurityContext - the security context component which can provide
+ * cryptoken
+ ******************************************************************************/
+{
+ m_xUriBinding = xUriBinding;
+
+ m_eStatusOfSecurityComponents = InitializationState::UNINITIALIZED;
+ m_xSecurityContext = xSecurityContext;
+
+ m_vInternalSignatureInformations.clear();
+
+ m_bVerifyCurrentSignature = false;
+}
+
+void XSecController::setSAXChainConnector(const css::uno::Reference< css::lang::XInitialization >& xInitialization)
+/****** XSecController/setSAXChainConnector ***********************************
+ *
+ * NAME
+ * setSAXChainConnector -- configures the components which will
+ * collaborate with the SAXEventKeeper on the SAX chain.
+ *
+ * SYNOPSIS
+ * setSAXChainConnector(xInitialization);
+ *
+ * INPUTS
+ * xInitialization - the previous node on the SAX chain
+ ******************************************************************************/
+{
+ m_bIsPreviousNodeInitializable = true;
+ m_xPreviousNodeOnSAXChain = xInitialization;
+
+ initializeSAXChain( );
+}
+
+void XSecController::clearSAXChainConnector()
+/****** XSecController/clearSAXChainConnector *********************************
+ *
+ * NAME
+ * clearSAXChainConnector -- resets the collaborating components.
+ ******************************************************************************/
+{
+ chainOff();
+
+ m_xPreviousNodeOnSAXChain = nullptr;
+}
+
+void XSecController::endMission()
+/****** XSecController/endMission *********************************************
+ *
+ * NAME
+ * endMission -- forces to end all missions
+ *
+ * FUNCTION
+ * Deletes all signature information and forces all missions to an end.
+ ******************************************************************************/
+{
+ sal_Int32 size = m_vInternalSignatureInformations.size();
+
+ for (int i=0; i<size; ++i)
+ {
+ if ( m_eStatusOfSecurityComponents == InitializationState::INITIALIZED )
+ /*
+ * ResolvedListener only exist when the security components are created.
+ */
+ {
+ css::uno::Reference< css::xml::crypto::sax::XMissionTaker > xMissionTaker
+ ( m_vInternalSignatureInformations[i].xReferenceResolvedListener, css::uno::UNO_QUERY );
+
+ /*
+ * asks the SignatureCreator/SignatureVerifier to release
+ * all resources it uses.
+ */
+ xMissionTaker->endMission();
+ }
+ }
+
+ m_xUriBinding = nullptr;
+ m_xSecurityContext = nullptr;
+
+ /*
+ * free the status change listener reference to this object
+ */
+ if (m_xSAXEventKeeper.is())
+ m_xSAXEventKeeper->addSAXEventKeeperStatusChangeListener( nullptr );
+}
+
+namespace
+{
+void writeUnsignedProperties(
+ const css::uno::Reference<css::xml::sax::XDocumentHandler>& xDocumentHandler,
+ const SignatureInformation& signatureInfo)
+{
+ {
+ rtl::Reference<comphelper::AttributeList> pAttributeList(new comphelper::AttributeList());
+ pAttributeList->AddAttribute("Id", "idUnsignedProperties_" + signatureInfo.ouSignatureId);
+ xDocumentHandler->startElement("xd:UnsignedProperties", uno::Reference<xml::sax::XAttributeList>(pAttributeList));
+ }
+
+ {
+ xDocumentHandler->startElement("xd:UnsignedSignatureProperties", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+
+ {
+ xDocumentHandler->startElement("xd:CertificateValues", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+
+ {
+ for (const auto& i: signatureInfo.maEncapsulatedX509Certificates)
+ {
+ xDocumentHandler->startElement("xd:EncapsulatedX509Certificate", uno::Reference<xml::sax::XAttributeList>(new comphelper::AttributeList()));
+ xDocumentHandler->characters(i);
+ xDocumentHandler->endElement("xd:EncapsulatedX509Certificate");
+ }
+ }
+
+ xDocumentHandler->endElement("xd:CertificateValues");
+ }
+
+ xDocumentHandler->endElement("xd:UnsignedSignatureProperties");
+ }
+
+ xDocumentHandler->endElement("xd:UnsignedProperties");
+}
+
+}
+
+void XSecController::exportSignature(
+ const css::uno::Reference<css::xml::sax::XDocumentHandler>& xDocumentHandler,
+ const SignatureInformation& signatureInfo,
+ bool bXAdESCompliantIfODF )
+/****** XSecController/exportSignature ****************************************
+ *
+ * NAME
+ * exportSignature -- export a signature structure to an XDocumentHandler
+ *
+ * SYNOPSIS
+ * exportSignature( xDocumentHandler, signatureInfo);
+ *
+ * INPUTS
+ * xDocumentHandler - the document handler to receive the signature
+ * signatureInfo - signature to be exported
+ ******************************************************************************/
+{
+ const SignatureReferenceInformations& vReferenceInfors = signatureInfo.vSignatureReferenceInfors;
+ rtl::Reference<comphelper::AttributeList> pAttributeList;
+
+ /*
+ * Write Signature element
+ */
+ pAttributeList = new comphelper::AttributeList();
+ pAttributeList->AddAttribute(
+ "xmlns",
+ NS_XMLDSIG);
+
+ if (!signatureInfo.ouSignatureId.isEmpty())
+ {
+ pAttributeList->AddAttribute(
+ "Id",
+ signatureInfo.ouSignatureId);
+ }
+
+ xDocumentHandler->startElement( "Signature", pAttributeList);
+ {
+ /* Write SignedInfo element */
+ xDocumentHandler->startElement(
+ "SignedInfo",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new comphelper::AttributeList()));
+ {
+ /* Write CanonicalizationMethod element */
+ pAttributeList = new comphelper::AttributeList();
+ pAttributeList->AddAttribute(
+ "Algorithm",
+ ALGO_C14N);
+ xDocumentHandler->startElement( "CanonicalizationMethod", pAttributeList );
+ xDocumentHandler->endElement( "CanonicalizationMethod" );
+
+ /* Write SignatureMethod element */
+ pAttributeList = new comphelper::AttributeList();
+
+ // TODO: actually roundtrip this value from parsing documentsignatures.xml - entirely
+ // broken to assume this would in any way relate to the 1st reference's digest algo
+
+ // Assume that all Reference elements use the same DigestMethod:Algorithm, and that the
+ // SignatureMethod:Algorithm should be the corresponding one.
+ pAttributeList->AddAttribute(
+ "Algorithm",
+ getSignatureURI(signatureInfo.eAlgorithmID, vReferenceInfors[0].nDigestID));
+ xDocumentHandler->startElement( "SignatureMethod", pAttributeList );
+ xDocumentHandler->endElement( "SignatureMethod" );
+
+ /* Write Reference element */
+ int j;
+ int refNum = vReferenceInfors.size();
+
+ for(j=0; j<refNum; ++j)
+ {
+ const SignatureReferenceInformation& refInfor = vReferenceInfors[j];
+
+ pAttributeList = new comphelper::AttributeList();
+ if ( refInfor.nType != SignatureReferenceType::SAMEDOCUMENT )
+ /*
+ * stream reference
+ */
+ {
+ pAttributeList->AddAttribute(
+ "URI",
+ refInfor.ouURI);
+ }
+ else
+ /*
+ * same-document reference
+ */
+ {
+ if (refInfor.ouURI.startsWith("idSignedProperties"))
+ {
+ pAttributeList->AddAttribute("URI", "#idSignedProperties_" + signatureInfo.ouSignatureId);
+ if (bXAdESCompliantIfODF && !refInfor.ouType.isEmpty())
+ {
+ // The reference which points to the SignedProperties
+ // shall have this specific type.
+ pAttributeList->AddAttribute("Type", refInfor.ouType);
+ }
+ }
+ else
+ {
+ pAttributeList->AddAttribute(
+ "URI",
+ "#" + refInfor.ouURI);
+ }
+ }
+
+ xDocumentHandler->startElement( "Reference", pAttributeList );
+ {
+ /* Write Transforms element */
+ if (refInfor.nType == SignatureReferenceType::XMLSTREAM)
+ /*
+ * xml stream, so c14n transform is needed
+ */
+ {
+ xDocumentHandler->startElement(
+ "Transforms",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new comphelper::AttributeList()));
+ {
+ pAttributeList = new comphelper::AttributeList();
+ pAttributeList->AddAttribute(
+ "Algorithm",
+ ALGO_C14N);
+ xDocumentHandler->startElement(
+ "Transform",
+ pAttributeList );
+ xDocumentHandler->endElement( "Transform" );
+ }
+ xDocumentHandler->endElement( "Transforms" );
+ }
+
+ /* Write DigestMethod element */
+ pAttributeList = new comphelper::AttributeList();
+ pAttributeList->AddAttribute(
+ "Algorithm",
+ getDigestURI(refInfor.nDigestID));
+ xDocumentHandler->startElement(
+ "DigestMethod",
+ pAttributeList );
+ xDocumentHandler->endElement( "DigestMethod" );
+
+ /* Write DigestValue element */
+ xDocumentHandler->startElement(
+ "DigestValue",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new comphelper::AttributeList()));
+ xDocumentHandler->characters( refInfor.ouDigestValue );
+ xDocumentHandler->endElement( "DigestValue" );
+ }
+ xDocumentHandler->endElement( "Reference" );
+ }
+ }
+ xDocumentHandler->endElement( "SignedInfo" );
+
+ /* Write SignatureValue element */
+ xDocumentHandler->startElement(
+ "SignatureValue",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new comphelper::AttributeList()));
+ xDocumentHandler->characters( signatureInfo.ouSignatureValue );
+ xDocumentHandler->endElement( "SignatureValue" );
+
+ /* Write KeyInfo element */
+ xDocumentHandler->startElement(
+ "KeyInfo",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new comphelper::AttributeList()));
+ {
+ // GPG or X509 key?
+ if (!signatureInfo.ouGpgCertificate.isEmpty())
+ {
+ pAttributeList = new comphelper::AttributeList();
+ pAttributeList->AddAttribute("xmlns:loext", NS_LOEXT);
+ /* Write PGPData element */
+ xDocumentHandler->startElement(
+ "PGPData",
+ pAttributeList);
+ {
+ /* Write keyid element */
+ xDocumentHandler->startElement(
+ "PGPKeyID",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new comphelper::AttributeList()));
+ xDocumentHandler->characters(signatureInfo.ouGpgKeyID);
+ xDocumentHandler->endElement( "PGPKeyID" );
+
+ /* Write PGPKeyPacket element */
+ if (!signatureInfo.ouGpgCertificate.isEmpty())
+ {
+ xDocumentHandler->startElement(
+ "PGPKeyPacket",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new comphelper::AttributeList()));
+ xDocumentHandler->characters( signatureInfo.ouGpgCertificate );
+ xDocumentHandler->endElement( "PGPKeyPacket" );
+ }
+
+ /* Write PGPOwner element */
+ xDocumentHandler->startElement(
+ "loext:PGPOwner",
+ css::uno::Reference< css::xml::sax::XAttributeList >(new comphelper::AttributeList()));
+ xDocumentHandler->characters( signatureInfo.ouGpgOwner );
+ xDocumentHandler->endElement( "loext:PGPOwner" );
+ }
+ xDocumentHandler->endElement( "PGPData" );
+ }
+ else
+ {
+ assert(signatureInfo.GetSigningCertificate());
+ for (auto const& rData : signatureInfo.X509Datas)
+ {
+ /* Write X509Data element */
+ xDocumentHandler->startElement(
+ "X509Data",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new comphelper::AttributeList()));
+ {
+ for (auto const& it : rData)
+ {
+ /* Write X509IssuerSerial element */
+ xDocumentHandler->startElement(
+ "X509IssuerSerial",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new comphelper::AttributeList()));
+ {
+ /* Write X509IssuerName element */
+ xDocumentHandler->startElement(
+ "X509IssuerName",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new comphelper::AttributeList()));
+ xDocumentHandler->characters(it.X509IssuerName);
+ xDocumentHandler->endElement( "X509IssuerName" );
+
+ /* Write X509SerialNumber element */
+ xDocumentHandler->startElement(
+ "X509SerialNumber",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new comphelper::AttributeList()));
+ xDocumentHandler->characters(it.X509SerialNumber);
+ xDocumentHandler->endElement( "X509SerialNumber" );
+ }
+ xDocumentHandler->endElement( "X509IssuerSerial" );
+
+ /* Write X509Certificate element */
+ if (!it.X509Certificate.isEmpty())
+ {
+ xDocumentHandler->startElement(
+ "X509Certificate",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new comphelper::AttributeList()));
+ xDocumentHandler->characters(it.X509Certificate);
+ xDocumentHandler->endElement( "X509Certificate" );
+ }
+ }
+ }
+ xDocumentHandler->endElement( "X509Data" );
+ }
+ }
+ }
+ xDocumentHandler->endElement( "KeyInfo" );
+
+ OUString sDate;
+
+ /* Write Object element */
+ xDocumentHandler->startElement(
+ "Object",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new comphelper::AttributeList()));
+ {
+ /* Write SignatureProperties element */
+ xDocumentHandler->startElement(
+ "SignatureProperties",
+ css::uno::Reference< css::xml::sax::XAttributeList > (new comphelper::AttributeList()));
+ {
+ /* Write SignatureProperty element */
+ pAttributeList = new comphelper::AttributeList();
+ pAttributeList->AddAttribute(
+ "Id",
+ signatureInfo.ouDateTimePropertyId);
+ pAttributeList->AddAttribute(
+ "Target",
+ "#" + signatureInfo.ouSignatureId);
+ xDocumentHandler->startElement(
+ "SignatureProperty",
+ pAttributeList);
+ {
+ /* Write timestamp element */
+
+ pAttributeList = new comphelper::AttributeList();
+ pAttributeList->AddAttribute(
+ "xmlns:dc",
+ NS_DC);
+
+ xDocumentHandler->startElement(
+ "dc:date",
+ pAttributeList);
+
+ OUStringBuffer buffer;
+ //If the xml signature was already contained in the document,
+ //then we use the original date and time string, rather than the
+ //converted one. This avoids writing a different string due to
+ //e.g. rounding issues and thus breaking the signature.
+ if (!signatureInfo.ouDateTime.isEmpty())
+ buffer = signatureInfo.ouDateTime;
+ else
+ {
+ buffer = utl::toISO8601(signatureInfo.stDateTime);
+ // xsd:dateTime must use period as separator for fractional seconds, while
+ // utl::toISO8601 uses comma (as allowed, and even recommended, by ISO8601).
+ buffer.replace(',', '.');
+ }
+ sDate = buffer.makeStringAndClear();
+ xDocumentHandler->characters( sDate );
+
+ xDocumentHandler->endElement(
+ "dc:date");
+ }
+ xDocumentHandler->endElement( "SignatureProperty" );
+ }
+
+ // Write signature description.
+ if (!signatureInfo.ouDescription.isEmpty())
+ {
+ // SignatureProperty element.
+ pAttributeList = new comphelper::AttributeList();
+ pAttributeList->AddAttribute("Id", signatureInfo.ouDescriptionPropertyId);
+ pAttributeList->AddAttribute("Target", "#" + signatureInfo.ouSignatureId);
+ xDocumentHandler->startElement("SignatureProperty", pAttributeList);
+
+ {
+ // Description element.
+ pAttributeList = new comphelper::AttributeList();
+ pAttributeList->AddAttribute("xmlns:dc", NS_DC);
+
+ xDocumentHandler->startElement("dc:description", pAttributeList);
+ xDocumentHandler->characters(signatureInfo.ouDescription);
+ xDocumentHandler->endElement("dc:description");
+ }
+
+ xDocumentHandler->endElement("SignatureProperty");
+ }
+
+ xDocumentHandler->endElement( "SignatureProperties" );
+ }
+ xDocumentHandler->endElement( "Object" );
+
+ // In XAdES, write another Object element for the QualifyingProperties
+ if (bXAdESCompliantIfODF)
+ {
+ pAttributeList = new comphelper::AttributeList();
+ pAttributeList->AddAttribute("xmlns:xd", NS_XD);
+ xDocumentHandler->startElement(
+ "Object",
+ pAttributeList);
+ {
+ pAttributeList = new comphelper::AttributeList();
+ pAttributeList->AddAttribute("Target", "#" + signatureInfo.ouSignatureId);
+ xDocumentHandler->startElement(
+ "xd:QualifyingProperties",
+ pAttributeList);
+ DocumentSignatureHelper::writeSignedProperties(xDocumentHandler, signatureInfo, sDate, true);
+ writeUnsignedProperties(xDocumentHandler, signatureInfo);
+ xDocumentHandler->endElement( "xd:QualifyingProperties" );
+ }
+ xDocumentHandler->endElement( "Object" );
+ }
+ }
+ xDocumentHandler->endElement( "Signature" );
+}
+
+void XSecController::exportOOXMLSignature(const uno::Reference<embed::XStorage>& xRootStorage, const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler, const SignatureInformation& rInformation)
+{
+ OOXMLSecExporter aExporter(mxCtx, xRootStorage, xDocumentHandler, rInformation);
+ aExporter.writeSignature();
+}
+
+void XSecController::UpdateSignatureInformation(sal_Int32 const nSecurityId,
+ std::vector<SignatureInformation::X509Data> && rDatas)
+{
+ int const nIndex = findSignatureInfor(nSecurityId);
+ assert(nIndex != -1); // nothing should touch this between parsing and verify
+ m_vInternalSignatureInformations[nIndex].signatureInfor.X509Datas = std::move(rDatas);
+}
+
+SignatureInformation XSecController::getSignatureInformation( sal_Int32 nSecurityId ) const
+{
+ SignatureInformation aInf( 0 );
+ int nIndex = findSignatureInfor(nSecurityId);
+ SAL_WARN_IF( nIndex == -1, "xmlsecurity.helper", "getSignatureInformation - SecurityId is invalid!" );
+ if ( nIndex != -1)
+ {
+ aInf = m_vInternalSignatureInformations[nIndex].signatureInfor;
+ }
+ return aInf;
+}
+
+SignatureInformations XSecController::getSignatureInformations() const
+{
+ SignatureInformations vInfors;
+ int sigNum = m_vInternalSignatureInformations.size();
+
+ for (int i=0; i<sigNum; ++i)
+ {
+ SignatureInformation si = m_vInternalSignatureInformations[i].signatureInfor;
+ vInfors.push_back(si);
+ }
+
+ return vInfors;
+}
+
+/*
+ * XSAXEventKeeperStatusChangeListener
+ */
+
+void SAL_CALL XSecController::blockingStatusChanged( sal_Bool isBlocking )
+{
+ m_bIsBlocking = isBlocking;
+ checkChainingStatus();
+}
+
+void SAL_CALL XSecController::collectionStatusChanged(
+ sal_Bool isInsideCollectedElement )
+{
+ m_bIsCollectingElement = isInsideCollectedElement;
+ checkChainingStatus();
+}
+
+void SAL_CALL XSecController::bufferStatusChanged( sal_Bool /*isBufferEmpty*/)
+{
+
+}
+
+/*
+ * XSignatureCreationResultListener
+ */
+void SAL_CALL XSecController::signatureCreated( sal_Int32 securityId, css::xml::crypto::SecurityOperationStatus nResult )
+{
+ int index = findSignatureInfor(securityId);
+ assert(index != -1 && "Signature Not Found!");
+ SignatureInformation& signatureInfor = m_vInternalSignatureInformations.at(index).signatureInfor;
+ signatureInfor.nStatus = nResult;
+}
+
+/*
+ * XSignatureVerifyResultListener
+ */
+void SAL_CALL XSecController::signatureVerified( sal_Int32 securityId, css::xml::crypto::SecurityOperationStatus nResult )
+{
+ int index = findSignatureInfor(securityId);
+ assert(index != -1 && "Signature Not Found!");
+ SignatureInformation& signatureInfor = m_vInternalSignatureInformations.at(index).signatureInfor;
+ signatureInfor.nStatus = nResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/xsecparser.cxx b/xmlsecurity/source/helper/xsecparser.cxx
new file mode 100644
index 0000000000..af3562f46c
--- /dev/null
+++ b/xmlsecurity/source/helper/xsecparser.cxx
@@ -0,0 +1,1632 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "xsecparser.hxx"
+#include <xsecctl.hxx>
+#include <xmlsignaturehelper.hxx>
+
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltkmap.hxx>
+#include <xmloff/xmlimp.hxx>
+
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <cppuhelper/exc_hlp.hxx>
+#include <sal/log.hxx>
+
+class XSecParser::Context
+{
+ protected:
+ friend class XSecParser;
+ XSecParser & m_rParser;
+ private:
+ std::optional<SvXMLNamespaceMap> m_pOldNamespaceMap;
+
+ public:
+ Context(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : m_rParser(rParser)
+ , m_pOldNamespaceMap(std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual ~Context() = default;
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& /*xAttrs*/)
+ {
+ }
+
+ virtual void EndElement()
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const /*nNamespace*/, OUString const& /*rName*/);
+
+ virtual void Characters(OUString const& /*rChars*/)
+ {
+ }
+};
+
+// it's possible that an unsupported element has an Id attribute and a
+// ds:Reference digesting it - probably this means XSecController needs to know
+// about it. (For known elements, the Id attribute is only processed according
+// to the schema.)
+class XSecParser::UnknownContext
+ : public XSecParser::Context
+{
+ public:
+ UnknownContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+};
+
+auto XSecParser::Context::CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const /*nNamespace*/, OUString const& /*rName*/)
+-> std::unique_ptr<Context>
+{
+ // default: create new base context
+ return std::make_unique<UnknownContext>(m_rParser, std::move(pOldNamespaceMap));
+}
+
+/**
+note: anything in ds:Object should be trusted *only* if there is a ds:Reference
+ to it so it is signed (exception: the xades:EncapsulatedX509Certificate).
+ ds:SignedInfo precedes all ds:Object.
+
+ There may be multiple ds:Signature for purpose of counter-signatures
+ but the way XAdES describes these, only the ds:SignatureValue element
+ would be referenced, so requiring a ds:Reference for anything in
+ ds:Object shouldn't cause issues.
+ */
+class XSecParser::ReferencedContextImpl
+ : public XSecParser::Context
+{
+ protected:
+ bool m_isReferenced;
+
+ public:
+ ReferencedContextImpl(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_isReferenced(isReferenced)
+ {
+ }
+
+ OUString CheckIdAttrReferenced(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs)
+ {
+ OUString const id(m_rParser.HandleIdAttr(xAttrs));
+ if (!id.isEmpty() && m_rParser.m_pXSecController->haveReferenceForId(id))
+ {
+ m_isReferenced = true;
+ }
+ return id;
+ }
+};
+
+class XSecParser::LoPGPOwnerContext
+ : public XSecParser::Context
+{
+ private:
+ OUString m_Value;
+
+ public:
+ LoPGPOwnerContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setGpgOwner(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::DsPGPKeyPacketContext
+ : public XSecParser::Context
+{
+ private:
+ OUString m_Value;
+
+ public:
+ DsPGPKeyPacketContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setGpgCertificate(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::DsPGPKeyIDContext
+ : public XSecParser::Context
+{
+ private:
+ OUString m_Value;
+
+ public:
+ DsPGPKeyIDContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setGpgKeyID(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::DsPGPDataContext
+ : public XSecParser::Context
+{
+ public:
+ DsPGPDataContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& /*xAttrs*/) override
+ {
+ m_rParser.m_pXSecController->switchGpgSignature();
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "PGPKeyID")
+ {
+ return std::make_unique<DsPGPKeyIDContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "PGPKeyPacket")
+ {
+ return std::make_unique<DsPGPKeyPacketContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "PGPOwner")
+ {
+ return std::make_unique<LoPGPOwnerContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsX509CertificateContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509CertificateContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DsX509SerialNumberContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509SerialNumberContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DsX509IssuerNameContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsX509IssuerNameContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DsX509IssuerSerialContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rX509IssuerName;
+ OUString & m_rX509SerialNumber;
+
+ public:
+ DsX509IssuerSerialContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rIssuerName, OUString& rSerialNumber)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rX509IssuerName(rIssuerName)
+ , m_rX509SerialNumber(rSerialNumber)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerName")
+ {
+ return std::make_unique<DsX509IssuerNameContext>(m_rParser, std::move(pOldNamespaceMap), m_rX509IssuerName);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509SerialNumber")
+ {
+ return std::make_unique<DsX509SerialNumberContext>(m_rParser, std::move(pOldNamespaceMap), m_rX509SerialNumber);
+ }
+ // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+/// can't be sure what is supposed to happen here because the spec is clear as mud
+class XSecParser::DsX509DataContext
+ : public XSecParser::Context
+{
+ private:
+ // sigh... "No ordering is implied by the above constraints."
+ // so store the ball of mud in vectors and try to figure it out later.
+ std::vector<std::pair<OUString, OUString>> m_X509IssuerSerials;
+ std::vector<OUString> m_X509Certificates;
+
+ public:
+ DsX509DataContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setX509Data(m_X509IssuerSerials, m_X509Certificates);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerSerial")
+ {
+ m_X509IssuerSerials.emplace_back();
+ return std::make_unique<DsX509IssuerSerialContext>(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerSerials.back().first, m_X509IssuerSerials.back().second);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509Certificate")
+ {
+ m_X509Certificates.emplace_back();
+ return std::make_unique<DsX509CertificateContext>(m_rParser, std::move(pOldNamespaceMap), m_X509Certificates.back());
+ }
+ // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsKeyInfoContext
+ : public XSecParser::Context
+{
+ public:
+ DsKeyInfoContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "X509Data")
+ {
+ return std::make_unique<DsX509DataContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "PGPData")
+ {
+ return std::make_unique<DsPGPDataContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: ds:KeyName, ds:KeyValue, ds:RetrievalMethod, ds:SPKIData, ds:MgmtData
+ // (old code would read ds:Transform inside ds:RetrievalMethod but
+ // presumably that was a bug)
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+
+};
+
+class XSecParser::DsSignatureValueContext
+ : public XSecParser::Context
+{
+ private:
+ OUString m_Value;
+
+ public:
+ DsSignatureValueContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setSignatureValue(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::DsDigestValueContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DsDigestValueContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& /*xAttrs*/) override
+ {
+ m_rValue.clear();
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DsDigestMethodContext
+ : public XSecParser::Context
+{
+ private:
+ sal_Int32 & m_rReferenceDigestID;
+
+ public:
+ DsDigestMethodContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_Int32& rReferenceDigestID)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rReferenceDigestID(rReferenceDigestID)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm");
+
+ SAL_WARN_IF( ouAlgorithm.isEmpty(), "xmlsecurity.helper", "no Algorithm in Reference" );
+ if (ouAlgorithm.isEmpty())
+ return;
+
+ SAL_WARN_IF( ouAlgorithm != ALGO_XMLDSIGSHA1
+ && ouAlgorithm != ALGO_XMLDSIGSHA256
+ && ouAlgorithm != ALGO_XMLDSIGSHA512,
+ "xmlsecurity.helper", "Algorithm neither SHA1, SHA256 nor SHA512");
+ if (ouAlgorithm == ALGO_XMLDSIGSHA1)
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA1;
+ else if (ouAlgorithm == ALGO_XMLDSIGSHA256)
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA256;
+ else if (ouAlgorithm == ALGO_XMLDSIGSHA512)
+ m_rReferenceDigestID = css::xml::crypto::DigestID::SHA512;
+ else
+ m_rReferenceDigestID = 0;
+ }
+};
+
+class XSecParser::DsTransformContext
+ : public XSecParser::Context
+{
+ private:
+ bool & m_rIsC14N;
+
+ public:
+ DsTransformContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool& rIsC14N)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rIsC14N(rIsC14N)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm");
+
+ if (ouAlgorithm == ALGO_C14N)
+ /*
+ * a xml stream
+ */
+ {
+ m_rIsC14N = true;
+ }
+ }
+};
+
+class XSecParser::DsTransformsContext
+ : public XSecParser::Context
+{
+ private:
+ bool & m_rIsC14N;
+
+ public:
+ DsTransformsContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool& rIsC14N)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rIsC14N(rIsC14N)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Transform")
+ {
+ return std::make_unique<DsTransformContext>(m_rParser, std::move(pOldNamespaceMap), m_rIsC14N);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsReferenceContext
+ : public XSecParser::Context
+{
+ private:
+ OUString m_URI;
+ OUString m_Type;
+ OUString m_DigestValue;
+ bool m_IsC14N = false;
+ // Relevant for ODF. The digest algorithm selected by the DigestMethod
+ // element's Algorithm attribute. @see css::xml::crypto::DigestID.
+ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1;
+
+ public:
+ DsReferenceContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+
+ m_URI = xAttrs->getValueByName("URI");
+ SAL_WARN_IF(m_URI.isEmpty(), "xmlsecurity.helper", "URI is empty");
+ // Remember the type of this reference.
+ m_Type = xAttrs->getValueByName("Type");
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_URI.startsWith("#"))
+ {
+ /*
+ * remove the first character '#' from the attribute value
+ */
+ m_rParser.m_pXSecController->addReference(m_URI.copy(1), m_nReferenceDigestID, m_Type);
+ }
+ else
+ {
+ if (m_IsC14N) // this is determined by nested ds:Transform
+ {
+ m_rParser.m_pXSecController->addStreamReference(m_URI, false, m_nReferenceDigestID);
+ }
+ else
+ /*
+ * it must be an octet stream
+ */
+ {
+ m_rParser.m_pXSecController->addStreamReference(m_URI, true, m_nReferenceDigestID);
+ }
+ }
+
+ m_rParser.m_pXSecController->setDigestValue(m_nReferenceDigestID, m_DigestValue);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Transforms")
+ {
+ return std::make_unique<DsTransformsContext>(m_rParser, std::move(pOldNamespaceMap), m_IsC14N);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod")
+ {
+ return std::make_unique<DsDigestMethodContext>(m_rParser, std::move(pOldNamespaceMap), m_nReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue")
+ {
+ return std::make_unique<DsDigestValueContext>(m_rParser, std::move(pOldNamespaceMap), m_DigestValue);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsSignatureMethodContext
+ : public XSecParser::Context
+{
+ public:
+ DsSignatureMethodContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString ouAlgorithm = xAttrs->getValueByName("Algorithm");
+ if (ouAlgorithm == ALGO_ECDSASHA1 || ouAlgorithm == ALGO_ECDSASHA256
+ || ouAlgorithm == ALGO_ECDSASHA512)
+ {
+ m_rParser.m_pXSecController->setSignatureMethod(svl::crypto::SignatureMethodAlgorithm::ECDSA);
+ }
+ }
+};
+
+class XSecParser::DsSignedInfoContext
+ : public XSecParser::Context
+{
+ public:
+ DsSignedInfoContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->setReferenceCount();
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureMethod")
+ {
+ return std::make_unique<DsSignatureMethodContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Reference")
+ {
+ return std::make_unique<DsReferenceContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: ds:CanonicalizationMethod
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesEncapsulatedX509CertificateContext
+ : public XSecParser::Context
+{
+ private:
+ OUString m_Value;
+
+ public:
+ XadesEncapsulatedX509CertificateContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ m_rParser.m_pXSecController->addEncapsulatedX509Certificate(m_Value);
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::XadesCertificateValuesContext
+ : public XSecParser::Context
+{
+ public:
+ XadesCertificateValuesContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "EncapsulatedX509Certificate")
+ {
+ return std::make_unique<XadesEncapsulatedX509CertificateContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: xades:OtherCertificate
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesUnsignedSignaturePropertiesContext
+ : public XSecParser::Context
+{
+ public:
+ XadesUnsignedSignaturePropertiesContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "CertificateValues")
+ {
+ return std::make_unique<XadesCertificateValuesContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing:
+ // xades:CounterSignature
+ // ^ old code would read a ds:Signature inside it?
+ // xades:SignatureTimeStamp
+ // xades:CompleteCertificateRefs
+ // xades:CompleteRevocationRefs
+ // xades:AttributeCertificateRefs
+ // xades:AttributeRevocationRefs
+ // xades:SigAndRefsTimeStamp
+ // xades:RefsOnlyTimeStamp
+ // xades:RevocationValues
+ // xades:AttrAuthoritiesCertValues
+ // ^ old code: was equivalent to CertificateValues ???
+ // xades:AttributeRevocationValues
+ // xades:ArchiveTimeStamp
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesUnsignedPropertiesContext
+ : public XSecParser::Context
+{
+ public:
+ XadesUnsignedPropertiesContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_rParser.HandleIdAttr(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "UnsignedSignatureProperties")
+ {
+ return std::make_unique<XadesUnsignedSignaturePropertiesContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ // missing: xades:UnsignedDataObjectProperties
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::LoSignatureLineIdContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_Value;
+
+ public:
+ LoSignatureLineIdContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setSignatureLineId(m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineId");
+ }
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::LoSignatureLineValidImageContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_Value;
+
+ public:
+ LoSignatureLineValidImageContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setValidSignatureImage(m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineValidImage");
+ }
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::LoSignatureLineInvalidImageContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_Value;
+
+ public:
+ LoSignatureLineInvalidImageContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setInvalidSignatureImage(m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureLineInvalidImage");
+ }
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::LoSignatureLineContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ LoSignatureLineContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLineId")
+ {
+ return std::make_unique<LoSignatureLineIdContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLineValidImage")
+ {
+ return std::make_unique<LoSignatureLineValidImageContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLineInvalidImage")
+ {
+ return std::make_unique<LoSignatureLineInvalidImageContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesCertDigestContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rDigestValue;
+ sal_Int32 & m_rReferenceDigestID;
+
+ public:
+ XadesCertDigestContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rDigestValue, sal_Int32& rReferenceDigestID)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rDigestValue(rDigestValue)
+ , m_rReferenceDigestID(rReferenceDigestID)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod")
+ {
+ return std::make_unique<DsDigestMethodContext>(m_rParser, std::move(pOldNamespaceMap), m_rReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue")
+ {
+ return std::make_unique<DsDigestValueContext>(m_rParser, std::move(pOldNamespaceMap), m_rDigestValue);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesCertContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1;
+ OUString m_CertDigest;
+ OUString m_X509IssuerName;
+ OUString m_X509SerialNumber;
+
+ public:
+ XadesCertContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setX509CertDigest(m_CertDigest, m_nReferenceDigestID, m_X509IssuerName, m_X509SerialNumber);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned xades:Cert");
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "CertDigest")
+ {
+ return std::make_unique<XadesCertDigestContext>(m_rParser, std::move(pOldNamespaceMap), m_CertDigest, m_nReferenceDigestID);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "IssuerSerial")
+ {
+ return std::make_unique<DsX509IssuerSerialContext>(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerName, m_X509SerialNumber);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesSigningCertificateContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSigningCertificateContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "Cert")
+ {
+ return std::make_unique<XadesCertContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesSigningTimeContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ OUString m_Value;
+
+ public:
+ XadesSigningTimeContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ m_rParser.m_pXSecController->setDate("", m_Value);
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SigningTime");
+ }
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_Value += rChars;
+ }
+};
+
+class XSecParser::XadesSignedSignaturePropertiesContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSignedSignaturePropertiesContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningTime")
+ {
+ return std::make_unique<XadesSigningTimeContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SigningCertificate")
+ {
+ return std::make_unique<XadesSigningCertificateContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_LO_EXT && rName == "SignatureLine")
+ {
+ return std::make_unique<LoSignatureLineContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: xades:SignaturePolicyIdentifier, xades:SignatureProductionPlace, xades:SignerRole
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesSignedPropertiesContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ XadesSignedPropertiesContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedSignatureProperties")
+ {
+ return std::make_unique<XadesSignedSignaturePropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: xades:SignedDataObjectProperties
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::XadesQualifyingPropertiesContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ XadesQualifyingPropertiesContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "SignedProperties")
+ {
+ return std::make_unique<XadesSignedPropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "UnsignedProperties")
+ {
+ return std::make_unique<XadesUnsignedPropertiesContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DcDateContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DcDateContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DcDescriptionContext
+ : public XSecParser::Context
+{
+ private:
+ OUString & m_rValue;
+
+ public:
+ DcDescriptionContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ OUString& rValue)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ , m_rValue(rValue)
+ {
+ }
+
+ virtual void Characters(OUString const& rChars) override
+ {
+ m_rValue += rChars;
+ }
+};
+
+class XSecParser::DsSignaturePropertyContext
+ : public XSecParser::ReferencedContextImpl
+{
+ private:
+ enum class SignatureProperty { Unknown, Date, Description };
+ SignatureProperty m_Property = SignatureProperty::Unknown;
+ OUString m_Id;
+ OUString m_Value;
+
+ public:
+ DsSignaturePropertyContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ m_Id = CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual void EndElement() override
+ {
+ if (m_isReferenced)
+ {
+ switch (m_Property)
+ {
+ case SignatureProperty::Unknown:
+ SAL_INFO("xmlsecurity.helper", "Unknown property in ds:Object ignored");
+ break;
+ case SignatureProperty::Date:
+ m_rParser.m_pXSecController->setDate(m_Id, m_Value);
+ break;
+ case SignatureProperty::Description:
+ m_rParser.m_pXSecController->setDescription(m_Id, m_Value);
+ break;
+ }
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "ignoring unsigned SignatureProperty");
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DC && rName == "date")
+ {
+ m_Property = SignatureProperty::Date;
+ return std::make_unique<DcDateContext>(m_rParser, std::move(pOldNamespaceMap), m_Value);
+ }
+ if (nNamespace == XML_NAMESPACE_DC && rName == "description")
+ {
+ m_Property = SignatureProperty::Description;
+ return std::make_unique<DcDescriptionContext>(m_rParser, std::move(pOldNamespaceMap), m_Value);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsSignaturePropertiesContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ DsSignaturePropertiesContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ bool const isReferenced)
+ : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperty")
+ {
+ return std::make_unique<DsSignaturePropertyContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsObjectContext
+ : public XSecParser::ReferencedContextImpl
+{
+ public:
+ DsObjectContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ // init with "false" here - the Signature element can't be referenced by its child
+ : XSecParser::ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), false)
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ CheckIdAttrReferenced(xAttrs);
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureProperties")
+ {
+ return std::make_unique<DsSignaturePropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ if (nNamespace == XML_NAMESPACE_XADES132 && rName == "QualifyingProperties")
+ {
+ return std::make_unique<XadesQualifyingPropertiesContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced);
+ }
+ // missing: ds:Manifest
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsSignatureContext
+ : public XSecParser::Context
+{
+ public:
+ DsSignatureContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual void StartElement(
+ css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs) override
+ {
+ OUString const ouIdAttr(m_rParser.HandleIdAttr(xAttrs));
+ m_rParser.m_rXMLSignatureHelper.StartVerifySignatureElement();
+ m_rParser.m_pXSecController->addSignature();
+ if (!ouIdAttr.isEmpty())
+ {
+ m_rParser.m_pXSecController->setId( ouIdAttr );
+ }
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignedInfo")
+ {
+ return std::make_unique<DsSignedInfoContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "SignatureValue")
+ {
+ return std::make_unique<DsSignatureValueContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "KeyInfo")
+ {
+ return std::make_unique<DsKeyInfoContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Object")
+ {
+ return std::make_unique<DsObjectContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+class XSecParser::DsigSignaturesContext
+ : public XSecParser::Context
+{
+ public:
+ DsigSignaturesContext(XSecParser& rParser,
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap)
+ : XSecParser::Context(rParser, std::move(pOldNamespaceMap))
+ {
+ }
+
+ virtual std::unique_ptr<Context> CreateChildContext(
+ std::optional<SvXMLNamespaceMap>&& pOldNamespaceMap,
+ sal_uInt16 const nNamespace, OUString const& rName) override
+ {
+ if (nNamespace == XML_NAMESPACE_DS && rName == "Signature")
+ {
+ return std::make_unique<DsSignatureContext>(m_rParser, std::move(pOldNamespaceMap));
+ }
+ return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName);
+ }
+};
+
+
+XSecParser::XSecParser(XMLSignatureHelper& rXMLSignatureHelper,
+ XSecController* pXSecController)
+ : m_pNamespaceMap(SvXMLNamespaceMap())
+ , m_pXSecController(pXSecController)
+ , m_rXMLSignatureHelper(rXMLSignatureHelper)
+{
+ using namespace xmloff::token;
+ m_pNamespaceMap->Add( GetXMLToken(XML_XML), GetXMLToken(XML_N_XML), XML_NAMESPACE_XML );
+ m_pNamespaceMap->Add( "_dsig_ooo", GetXMLToken(XML_N_DSIG_OOO), XML_NAMESPACE_DSIG_OOO );
+ m_pNamespaceMap->Add( "_dsig", GetXMLToken(XML_N_DSIG), XML_NAMESPACE_DSIG );
+ m_pNamespaceMap->Add( "_ds", GetXMLToken(XML_N_DS), XML_NAMESPACE_DS );
+ m_pNamespaceMap->Add( "_xades132", GetXMLToken(XML_N_XADES132), XML_NAMESPACE_XADES132);
+ m_pNamespaceMap->Add( "_xades141", GetXMLToken(XML_N_XADES141), XML_NAMESPACE_XADES141);
+ m_pNamespaceMap->Add( "_dc", GetXMLToken(XML_N_DC), XML_NAMESPACE_DC );
+ m_pNamespaceMap->Add( "_office_libo",
+ GetXMLToken(XML_N_LO_EXT), XML_NAMESPACE_LO_EXT);
+}
+
+OUString XSecParser::HandleIdAttr(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs)
+{
+ OUString ouIdAttr = getIdAttr(xAttrs);
+ if (!ouIdAttr.isEmpty())
+ {
+ m_pXSecController->collectToVerify( ouIdAttr );
+ }
+ return ouIdAttr;
+}
+
+OUString XSecParser::getIdAttr(const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs )
+{
+ OUString ouIdAttr = xAttribs->getValueByName("id");
+
+ if (ouIdAttr.isEmpty())
+ {
+ ouIdAttr = xAttribs->getValueByName("Id");
+ }
+
+ return ouIdAttr;
+}
+
+/*
+ * XDocumentHandler
+ */
+void SAL_CALL XSecParser::startDocument( )
+{
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->startDocument();
+ }
+}
+
+void SAL_CALL XSecParser::endDocument( )
+{
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->endDocument();
+ }
+}
+
+void SAL_CALL XSecParser::startElement(
+ const OUString& rName,
+ const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs )
+{
+ assert(m_pNamespaceMap);
+ std::optional<SvXMLNamespaceMap> pRewindMap(
+ SvXMLImport::processNSAttributes(m_pNamespaceMap, nullptr, xAttribs));
+
+ OUString localName;
+ sal_uInt16 const nPrefix(m_pNamespaceMap->GetKeyByAttrName(rName, &localName));
+
+ std::unique_ptr<Context> pContext;
+
+ if (m_ContextStack.empty())
+ {
+ if ((nPrefix != XML_NAMESPACE_DSIG && nPrefix != XML_NAMESPACE_DSIG_OOO)
+ || localName != "document-signatures")
+ {
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: unexpected root element", nullptr,
+ css::uno::Any());
+ }
+
+ pContext.reset(new DsigSignaturesContext(*this, std::move(pRewindMap)));
+
+ }
+ else
+ {
+ pContext = m_ContextStack.top()->CreateChildContext(
+ std::move(pRewindMap), nPrefix, localName);
+ }
+
+ m_ContextStack.push(std::move(pContext));
+
+ try
+ {
+ m_ContextStack.top()->StartElement(xAttribs);
+
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->startElement(rName, xAttribs);
+ }
+ }
+ catch (css::uno::Exception& )
+ {//getCaughtException MUST be the first line in the catch block
+ css::uno::Any exc = cppu::getCaughtException();
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: Exception in XSecParser::startElement",
+ nullptr, exc);
+ }
+ catch (...)
+ {
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: unexpected exception in XSecParser::startElement", nullptr,
+ css::uno::Any());
+ }
+}
+
+void SAL_CALL XSecParser::endElement(const OUString& rName)
+{
+ assert(!m_ContextStack.empty()); // this should be checked by sax parser?
+
+ try
+ {
+ m_ContextStack.top()->EndElement();
+
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->endElement(rName);
+ }
+ }
+ catch (css::uno::Exception& )
+ {//getCaughtException MUST be the first line in the catch block
+ css::uno::Any exc = cppu::getCaughtException();
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: Exception in XSecParser::endElement",
+ nullptr, exc);
+ }
+ catch (...)
+ {
+ throw css::xml::sax::SAXException(
+ "xmlsecurity: unexpected exception in XSecParser::endElement", nullptr,
+ css::uno::Any());
+ }
+
+ if (m_ContextStack.top()->m_pOldNamespaceMap)
+ {
+ m_pNamespaceMap = std::move(m_ContextStack.top()->m_pOldNamespaceMap);
+ }
+ m_ContextStack.pop();
+}
+
+void SAL_CALL XSecParser::characters(const OUString& rChars)
+{
+ assert(!m_ContextStack.empty()); // this should be checked by sax parser?
+ m_ContextStack.top()->Characters(rChars);
+
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->characters(rChars);
+ }
+}
+
+void SAL_CALL XSecParser::ignorableWhitespace( const OUString& aWhitespaces )
+{
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->ignorableWhitespace( aWhitespaces );
+ }
+}
+
+void SAL_CALL XSecParser::processingInstruction( const OUString& aTarget, const OUString& aData )
+{
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->processingInstruction(aTarget, aData);
+ }
+}
+
+void SAL_CALL XSecParser::setDocumentLocator( const css::uno::Reference< css::xml::sax::XLocator >& xLocator )
+{
+ if (m_xNextHandler.is())
+ {
+ m_xNextHandler->setDocumentLocator( xLocator );
+ }
+}
+
+/*
+ * XInitialization
+ */
+void SAL_CALL XSecParser::initialize(
+ const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ aArguments[0] >>= m_xNextHandler;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/xsecparser.hxx b/xmlsecurity/source/helper/xsecparser.hxx
new file mode 100644
index 0000000000..6279d65439
--- /dev/null
+++ b/xmlsecurity/source/helper/xsecparser.hxx
@@ -0,0 +1,159 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <xmloff/namespacemap.hxx>
+
+#include <memory>
+#include <optional>
+#include <stack>
+
+class XMLSignatureHelper;
+class XSecController;
+
+class XSecParser: public cppu::WeakImplHelper
+<
+ css::xml::sax::XDocumentHandler,
+ css::lang::XInitialization
+>
+/****** XSecController.hxx/CLASS XSecParser ***********************************
+ *
+ * NAME
+ * XSecParser -- a SAX parser that can detect security elements
+ *
+ * FUNCTION
+ * The XSecParser object is connected on the SAX chain and detects
+ * security elements in the SAX event stream, then notifies
+ * the XSecController.
+ *
+ * NOTES
+ * This class is used when importing a document.
+ ******************************************************************************/
+{
+ friend class XSecController;
+public:
+ class Context;
+private:
+ class UnknownContext;
+ class ReferencedContextImpl;
+ class LoPGPOwnerContext;
+ class DsPGPKeyPacketContext;
+ class DsPGPKeyIDContext;
+ class DsPGPDataContext;
+ class DsX509CertificateContext;
+ class DsX509SerialNumberContext;
+ class DsX509IssuerNameContext;
+ class DsX509IssuerSerialContext;
+ class DsX509DataContext;
+ class DsKeyInfoContext;
+ class DsSignatureValueContext;
+ class DsDigestValueContext;
+ class DsDigestMethodContext;
+ class DsTransformContext;
+ class DsTransformsContext;
+ class DsReferenceContext;
+ class DsSignatureMethodContext;
+ class DsSignedInfoContext;
+ class XadesEncapsulatedX509CertificateContext;
+ class XadesCertificateValuesContext;
+ class XadesUnsignedSignaturePropertiesContext;
+ class XadesUnsignedPropertiesContext;
+ class LoSignatureLineIdContext;
+ class LoSignatureLineValidImageContext;
+ class LoSignatureLineInvalidImageContext;
+ class LoSignatureLineContext;
+ class XadesCertDigestContext;
+ class XadesCertContext;
+ class XadesSigningCertificateContext;
+ class XadesSigningTimeContext;
+ class XadesSignedSignaturePropertiesContext;
+ class XadesSignedPropertiesContext;
+ class XadesQualifyingPropertiesContext;
+ class DcDateContext;
+ class DcDescriptionContext;
+ class DsSignaturePropertyContext;
+ class DsSignaturePropertiesContext;
+ class DsObjectContext;
+ class DsSignatureContext;
+ class DsigSignaturesContext;
+
+ std::stack<std::unique_ptr<Context>> m_ContextStack;
+ std::optional<SvXMLNamespaceMap> m_pNamespaceMap;
+
+ /*
+ * the XSecController collaborating with XSecParser
+ */
+ XSecController* m_pXSecController;
+
+ /*
+ * the next XDocumentHandler on the SAX chain
+ */
+ css::uno::Reference<
+ css::xml::sax::XDocumentHandler > m_xNextHandler;
+
+ XMLSignatureHelper& m_rXMLSignatureHelper;
+
+ OUString HandleIdAttr(css::uno::Reference<css::xml::sax::XAttributeList> const& xAttrs);
+ static OUString getIdAttr(const css::uno::Reference<
+ css::xml::sax::XAttributeList >& xAttribs );
+
+public:
+ XSecParser(XMLSignatureHelper& rXMLSignatureHelper, XSecController* pXSecController);
+
+ /*
+ * XDocumentHandler
+ */
+ virtual void SAL_CALL startDocument( ) override;
+
+ virtual void SAL_CALL endDocument( ) override;
+
+ virtual void SAL_CALL startElement(
+ const OUString& aName,
+ const css::uno::Reference<
+ css::xml::sax::XAttributeList >& xAttribs ) override;
+
+ virtual void SAL_CALL endElement( const OUString& aName ) override;
+
+ virtual void SAL_CALL characters( const OUString& aChars ) override;
+
+ virtual void SAL_CALL ignorableWhitespace( const OUString& aWhitespaces ) override;
+
+ virtual void SAL_CALL processingInstruction(
+ const OUString& aTarget,
+ const OUString& aData ) override;
+
+ virtual void SAL_CALL setDocumentLocator(
+ const css::uno::Reference<
+ css::xml::sax::XLocator >& xLocator ) override;
+
+ /*
+ * XInitialization
+ */
+ virtual void SAL_CALL initialize(
+ const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+};
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/xsecsign.cxx b/xmlsecurity/source/helper/xsecsign.cxx
new file mode 100644
index 0000000000..6788f694ec
--- /dev/null
+++ b/xmlsecurity/source/helper/xsecsign.cxx
@@ -0,0 +1,462 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <xsecctl.hxx>
+
+#include <com/sun/star/xml/crypto/sax/ElementMarkPriority.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <rtl/uuid.h>
+#include <sal/log.hxx>
+
+#include <framework/signaturecreatorimpl.hxx>
+#include <framework/saxeventkeeperimpl.hxx>
+
+namespace com::sun::star::graphic { class XGraphic; }
+
+using namespace css;
+using namespace css::uno;
+using namespace css::graphic;
+
+/* protected: for signature generation */
+OUString XSecController::createId()
+{
+ sal_uInt8 aSeq[16];
+ rtl_createUuid( aSeq, nullptr, true );
+
+ char str[68]="ID_";
+ int length = 3;
+ for (sal_uInt8 i : aSeq)
+ {
+ SAL_WNODEPRECATED_DECLARATIONS_PUSH // sprintf (macOS 13 SDK)
+ length += sprintf(str+length, "%04x", i);
+ SAL_WNODEPRECATED_DECLARATIONS_POP
+ }
+
+ return OUString::createFromAscii(str);
+}
+
+css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener > XSecController::prepareSignatureToWrite(
+ InternalSignatureInformation& internalSignatureInfor,
+ sal_Int32 nStorageFormat,
+ bool bXAdESCompliantIfODF)
+{
+ sal_Int32 nSecurityId = internalSignatureInfor.signatureInfor.nSecurityId;
+ SignatureReferenceInformations& vReferenceInfors = internalSignatureInfor.signatureInfor.vSignatureReferenceInfors;
+
+ sal_Int32 nIdOfSignatureElementCollector;
+
+ nIdOfSignatureElementCollector =
+ m_xSAXEventKeeper->addSecurityElementCollector( css::xml::crypto::sax::ElementMarkPriority_AFTERMODIFY, true );
+
+ m_xSAXEventKeeper->setSecurityId(nIdOfSignatureElementCollector, nSecurityId);
+
+ rtl::Reference<SignatureCreatorImpl> xSignatureCreator(new SignatureCreatorImpl);
+
+ css::uno::Sequence<css::uno::Any> args
+ {
+ Any(OUString::number(nSecurityId)),
+ Any(uno::Reference<xml::crypto::sax::XSecuritySAXEventKeeper>(m_xSAXEventKeeper)),
+ Any(OUString::number(nIdOfSignatureElementCollector)),
+
+ //for nss, the internal module is used for signing, which needs to be improved later
+ Any(m_xSecurityContext->getSecurityEnvironment()),
+ Any(m_xXMLSignature)
+ };
+ xSignatureCreator->initialize(args);
+
+ sal_Int32 nBlockerId = m_xSAXEventKeeper->addBlocker();
+ m_xSAXEventKeeper->setSecurityId(nBlockerId, nSecurityId);
+
+ xSignatureCreator->setBlockerId(nBlockerId);
+
+ xSignatureCreator->addSignatureCreationResultListener(this);
+
+ m_xSAXEventKeeper->addReferenceResolvedListener(nIdOfSignatureElementCollector, xSignatureCreator);
+
+ int size = vReferenceInfors.size();
+ sal_Int32 nReferenceCount = 0;
+
+ for(int i=0; i<size; ++i)
+ {
+ sal_Int32 keeperId = internalSignatureInfor.vKeeperIds[i];
+
+ if ( keeperId != -1)
+ {
+ m_xSAXEventKeeper->setSecurityId(keeperId, nSecurityId);
+ m_xSAXEventKeeper->addReferenceResolvedListener( keeperId, xSignatureCreator);
+ xSignatureCreator->setReferenceId( keeperId );
+ nReferenceCount++;
+ }
+ }
+
+ xSignatureCreator->setReferenceCount( nReferenceCount );
+
+ /*
+ * adds all URI binding
+ */
+ for(int i=0; i<size; ++i)
+ {
+ const SignatureReferenceInformation& refInfor = vReferenceInfors[i];
+
+ css::uno::Reference< css::io::XInputStream > xInputStream
+ = getObjectInputStream( refInfor.ouURI );
+
+ if (xInputStream.is())
+ xSignatureCreator->setUriBinding(refInfor.ouURI,xInputStream);
+ }
+
+ xSignatureCreator->setKeyId(0);
+
+ // use sha512 for gpg signing unconditionally
+ const sal_Int32 digestID = !internalSignatureInfor.signatureInfor.ouGpgCertificate.isEmpty()?
+ css::xml::crypto::DigestID::SHA512 : (bXAdESCompliantIfODF ? css::xml::crypto::DigestID::SHA256 : css::xml::crypto::DigestID::SHA1);
+
+ if (nStorageFormat != embed::StorageFormats::OFOPXML)
+ {
+ internalSignatureInfor.signatureInfor.ouSignatureId = createId();
+ internalSignatureInfor.signatureInfor.ouDateTimePropertyId = createId();
+ internalSignatureInfor.addReference(SignatureReferenceType::SAMEDOCUMENT, digestID, internalSignatureInfor.signatureInfor.ouDateTimePropertyId, -1, OUString() );
+ size++;
+
+ if (bXAdESCompliantIfODF)
+ {
+ OUString aId = "idSignedProperties_" + internalSignatureInfor.signatureInfor.ouSignatureId;
+ // We write a new reference, so it's possible to use the correct type URI.
+ internalSignatureInfor.addReference(SignatureReferenceType::SAMEDOCUMENT, digestID, aId, -1, "http://uri.etsi.org/01903#SignedProperties");
+ size++;
+ }
+
+ if (!internalSignatureInfor.signatureInfor.ouDescription.isEmpty())
+ {
+ // Only mention the hash of the description in the signature if it's non-empty.
+ internalSignatureInfor.signatureInfor.ouDescriptionPropertyId = createId();
+ internalSignatureInfor.addReference(SignatureReferenceType::SAMEDOCUMENT, digestID, internalSignatureInfor.signatureInfor.ouDescriptionPropertyId, -1, OUString());
+ size++;
+ }
+ }
+ else // OOXML
+ {
+ OUString aID = createId();
+ internalSignatureInfor.signatureInfor.ouSignatureId = aID;
+
+ internalSignatureInfor.addReference(SignatureReferenceType::SAMEDOCUMENT, digestID, "idPackageObject_" + aID, -1, OUString());
+ size++;
+ internalSignatureInfor.addReference(SignatureReferenceType::SAMEDOCUMENT, digestID, "idOfficeObject_" + aID, -1, OUString());
+ size++;
+ internalSignatureInfor.addReference(SignatureReferenceType::SAMEDOCUMENT, digestID, "idSignedProperties_" + aID, -1, OUString());
+ size++;
+ }
+
+ /*
+ * replace both digestValues and signatureValue to " "
+ */
+ for(int i=0; i<size; ++i)
+ {
+ SignatureReferenceInformation& refInfor = vReferenceInfors[i];
+ refInfor.ouDigestValue = " ";
+ }
+
+ internalSignatureInfor.signatureInfor.ouSignatureValue = " ";
+
+ return xSignatureCreator;
+}
+
+void XSecController::signAStream( sal_Int32 securityId, const OUString& uri, bool isBinary, bool bXAdESCompliantIfODF)
+{
+ const SignatureReferenceType type = isBinary ? SignatureReferenceType::BINARYSTREAM : SignatureReferenceType::XMLSTREAM;
+ sal_Int32 digestID = bXAdESCompliantIfODF ? css::xml::crypto::DigestID::SHA256 : css::xml::crypto::DigestID::SHA1;
+
+ int index = findSignatureInfor( securityId );
+
+ if (index == -1)
+ {
+ InternalSignatureInformation isi(securityId, nullptr);
+ isi.addReference(type, digestID, uri, -1, OUString());
+ m_vInternalSignatureInformations.push_back( isi );
+ }
+ else
+ {
+ // use sha512 for gpg signing unconditionally
+ if (!m_vInternalSignatureInformations[index].signatureInfor.ouGpgCertificate.isEmpty())
+ digestID = css::xml::crypto::DigestID::SHA512;
+ m_vInternalSignatureInformations[index].addReference(type, digestID, uri, -1, OUString());
+ }
+}
+
+// note: this is called when creating a new signature from scratch
+void XSecController::setX509Certificate(
+ sal_Int32 nSecurityId,
+ const OUString& ouX509IssuerName,
+ const OUString& ouX509SerialNumber,
+ const OUString& ouX509Cert,
+ const OUString& ouX509CertDigest,
+ svl::crypto::SignatureMethodAlgorithm eAlgorithmID)
+{
+ int index = findSignatureInfor( nSecurityId );
+
+ if ( index == -1 )
+ {
+ InternalSignatureInformation isi(nSecurityId, nullptr);
+ isi.signatureInfor.X509Datas.clear();
+ isi.signatureInfor.X509Datas.emplace_back();
+ isi.signatureInfor.X509Datas.back().emplace_back();
+ isi.signatureInfor.X509Datas.back().back().X509IssuerName = ouX509IssuerName;
+ isi.signatureInfor.X509Datas.back().back().X509SerialNumber = ouX509SerialNumber;
+ isi.signatureInfor.X509Datas.back().back().X509Certificate = ouX509Cert;
+ isi.signatureInfor.X509Datas.back().back().CertDigest = ouX509CertDigest;
+ isi.signatureInfor.eAlgorithmID = eAlgorithmID;
+ m_vInternalSignatureInformations.push_back( isi );
+ }
+ else
+ {
+ SignatureInformation &si
+ = m_vInternalSignatureInformations[index].signatureInfor;
+ si.X509Datas.clear();
+ si.X509Datas.emplace_back();
+ si.X509Datas.back().emplace_back();
+ si.X509Datas.back().back().X509IssuerName = ouX509IssuerName;
+ si.X509Datas.back().back().X509SerialNumber = ouX509SerialNumber;
+ si.X509Datas.back().back().X509Certificate = ouX509Cert;
+ si.X509Datas.back().back().CertDigest = ouX509CertDigest;
+ }
+}
+
+void XSecController::setGpgCertificate(
+ sal_Int32 nSecurityId,
+ const OUString& ouKeyDigest,
+ const OUString& ouCert,
+ const OUString& ouOwner)
+{
+ int index = findSignatureInfor( nSecurityId );
+
+ if ( index == -1 )
+ {
+ InternalSignatureInformation isi(nSecurityId, nullptr);
+ isi.signatureInfor.ouGpgCertificate = ouCert;
+ isi.signatureInfor.ouGpgOwner = ouOwner;
+ isi.signatureInfor.ouGpgKeyID = ouKeyDigest;
+ m_vInternalSignatureInformations.push_back( isi );
+ }
+ else
+ {
+ SignatureInformation &si
+ = m_vInternalSignatureInformations[index].signatureInfor;
+ si.X509Datas.clear(); // it is a PGP signature now
+ si.ouGpgCertificate = ouCert;
+ si.ouGpgOwner = ouOwner;
+ si.ouGpgKeyID = ouKeyDigest;
+ }
+}
+
+void XSecController::setDate(
+ sal_Int32 nSecurityId,
+ const css::util::DateTime& rDateTime )
+{
+ int index = findSignatureInfor( nSecurityId );
+
+ if ( index == -1 )
+ {
+ InternalSignatureInformation isi(nSecurityId, nullptr);
+ isi.signatureInfor.stDateTime = rDateTime;
+ m_vInternalSignatureInformations.push_back( isi );
+ }
+ else
+ {
+ SignatureInformation &si
+ = m_vInternalSignatureInformations[index].signatureInfor;
+ si.stDateTime = rDateTime;
+ }
+}
+
+void XSecController::setDescription(sal_Int32 nSecurityId, const OUString& rDescription)
+{
+ int nIndex = findSignatureInfor(nSecurityId);
+
+ if (nIndex == -1)
+ {
+ InternalSignatureInformation aInformation(nSecurityId, nullptr);
+ aInformation.signatureInfor.ouDescription = rDescription;
+ m_vInternalSignatureInformations.push_back(aInformation);
+ }
+ else
+ {
+ SignatureInformation& rInformation = m_vInternalSignatureInformations[nIndex].signatureInfor;
+ rInformation.ouDescription = rDescription;
+ }
+}
+
+void XSecController::setSignatureLineId(sal_Int32 nSecurityId, const OUString& rSignatureLineId)
+{
+ int nIndex = findSignatureInfor(nSecurityId);
+
+ if (nIndex == -1)
+ {
+ InternalSignatureInformation aInformation(nSecurityId, nullptr);
+ aInformation.signatureInfor.ouSignatureLineId = rSignatureLineId;
+ m_vInternalSignatureInformations.push_back(aInformation);
+ }
+ else
+ {
+ SignatureInformation& rInformation = m_vInternalSignatureInformations[nIndex].signatureInfor;
+ rInformation.ouSignatureLineId = rSignatureLineId;
+ }
+}
+
+void XSecController::setSignatureLineValidGraphic(sal_Int32 nSecurityId,
+ const Reference<XGraphic>& xValidGraphic)
+{
+ int nIndex = findSignatureInfor(nSecurityId);
+
+ if (nIndex == -1)
+ {
+ InternalSignatureInformation aInformation(nSecurityId, nullptr);
+ aInformation.signatureInfor.aValidSignatureImage = xValidGraphic;
+ m_vInternalSignatureInformations.push_back(aInformation);
+ }
+ else
+ {
+ SignatureInformation& rInformation
+ = m_vInternalSignatureInformations[nIndex].signatureInfor;
+ rInformation.aValidSignatureImage = xValidGraphic;
+ }
+}
+
+void XSecController::setSignatureLineInvalidGraphic(
+ sal_Int32 nSecurityId, const Reference<XGraphic>& xInvalidGraphic)
+{
+ int nIndex = findSignatureInfor(nSecurityId);
+
+ if (nIndex == -1)
+ {
+ InternalSignatureInformation aInformation(nSecurityId, nullptr);
+ aInformation.signatureInfor.aInvalidSignatureImage = xInvalidGraphic;
+ m_vInternalSignatureInformations.push_back(aInformation);
+ }
+ else
+ {
+ SignatureInformation& rInformation
+ = m_vInternalSignatureInformations[nIndex].signatureInfor;
+ rInformation.aInvalidSignatureImage = xInvalidGraphic;
+ }
+}
+
+bool XSecController::WriteSignature(
+ const css::uno::Reference<css::xml::sax::XDocumentHandler>& xDocumentHandler,
+ bool bXAdESCompliantIfODF )
+{
+ bool rc = false;
+
+ SAL_WARN_IF( !xDocumentHandler.is(), "xmlsecurity.helper", "I really need a document handler!" );
+
+ /*
+ * chain the SAXEventKeeper to the SAX chain
+ */
+ chainOn();
+
+ if ( m_eStatusOfSecurityComponents == InitializationState::INITIALIZED )
+ /*
+ * if all security components are ready, add the signature
+ * stream.
+ */
+ {
+ m_bIsSAXEventKeeperSticky = true;
+ m_xSAXEventKeeper->setNextHandler(xDocumentHandler);
+
+ try
+ {
+ /*
+ * export the signature template
+ */
+ css::uno::Reference<css::xml::sax::XDocumentHandler> xSEKHandler(m_xSAXEventKeeper);
+
+ int i;
+ int sigNum = m_vInternalSignatureInformations.size();
+
+ for (i=0; i<sigNum; ++i)
+ {
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations[i];
+
+ // Prepare the signature creator.
+ // 0 is not a documented value of embed::StorageFormats, ugh
+ isi.xReferenceResolvedListener = prepareSignatureToWrite( isi, 0, bXAdESCompliantIfODF );
+
+ exportSignature( xSEKHandler, isi.signatureInfor, bXAdESCompliantIfODF );
+ }
+
+ m_bIsSAXEventKeeperSticky = false;
+ chainOff();
+
+ rc = true;
+ }
+ catch( css::uno::Exception& )
+ {
+ }
+
+ m_xSAXEventKeeper->setNextHandler( nullptr );
+ m_bIsSAXEventKeeperSticky = false;
+ }
+
+ return rc;
+}
+
+bool XSecController::WriteOOXMLSignature(const uno::Reference<embed::XStorage>& xRootStorage, const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler)
+{
+ bool bRet = false;
+
+ SAL_WARN_IF(!xDocumentHandler.is(), "xmlsecurity.helper", "empty xDocumentHandler reference");
+
+ // Chain the SAXEventKeeper to the SAX chain.
+ chainOn();
+
+ if (m_eStatusOfSecurityComponents == InitializationState::INITIALIZED)
+ {
+ m_bIsSAXEventKeeperSticky = true;
+ m_xSAXEventKeeper->setNextHandler(xDocumentHandler);
+
+ try
+ {
+ // Export the signature template.
+ css::uno::Reference<xml::sax::XDocumentHandler> xSEKHandler(m_xSAXEventKeeper);
+
+ for (InternalSignatureInformation & rInformation : m_vInternalSignatureInformations)
+ {
+ // Prepare the signature creator.
+ rInformation.xReferenceResolvedListener = prepareSignatureToWrite(rInformation, embed::StorageFormats::OFOPXML, false);
+
+ exportOOXMLSignature(xRootStorage, xSEKHandler, rInformation.signatureInfor);
+ }
+
+ m_bIsSAXEventKeeperSticky = false;
+ chainOff();
+
+ bRet = true;
+ }
+ catch(const uno::Exception&)
+ {
+ }
+
+ m_xSAXEventKeeper->setNextHandler(nullptr);
+ m_bIsSAXEventKeeperSticky = false;
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/helper/xsecverify.cxx b/xmlsecurity/source/helper/xsecverify.cxx
new file mode 100644
index 0000000000..c45bece5fe
--- /dev/null
+++ b/xmlsecurity/source/helper/xsecverify.cxx
@@ -0,0 +1,630 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_gpgme.h>
+
+#include <xsecctl.hxx>
+#include "xsecparser.hxx"
+#include "ooxmlsecparser.hxx"
+#include <biginteger.hxx>
+#include <framework/signatureverifierimpl.hxx>
+#include <framework/saxeventkeeperimpl.hxx>
+#include <gpg/xmlsignature_gpgimpl.hxx>
+#include <gpg/SEInitializer.hxx>
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/xml/crypto/sax/XKeyCollector.hpp>
+#include <com/sun/star/xml/crypto/sax/ElementMarkPriority.hpp>
+#include <com/sun/star/xml/crypto/sax/XReferenceCollector.hpp>
+#include <com/sun/star/xml/crypto/sax/XSignatureVerifyResultBroadcaster.hpp>
+#include <com/sun/star/xml/crypto/XSEInitializer.hpp>
+#include <com/sun/star/graphic/GraphicProvider.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <sal/log.hxx>
+#include <unotools/datetime.hxx>
+#include <comphelper/base64.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/seqstream.hxx>
+
+namespace com::sun::star::graphic { class XGraphic; }
+
+using namespace css;
+using namespace css::uno;
+using namespace css::beans;
+
+/* protected: for signature verify */
+css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener > XSecController::prepareSignatureToRead(
+ sal_Int32 nSecurityId)
+{
+ if ( m_eStatusOfSecurityComponents != InitializationState::INITIALIZED )
+ {
+ return nullptr;
+ }
+
+ sal_Int32 nIdOfSignatureElementCollector;
+ css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener > xReferenceResolvedListener;
+
+ nIdOfSignatureElementCollector =
+ m_xSAXEventKeeper->addSecurityElementCollector( css::xml::crypto::sax::ElementMarkPriority_BEFOREMODIFY, false);
+
+ m_xSAXEventKeeper->setSecurityId(nIdOfSignatureElementCollector, nSecurityId);
+
+ /*
+ * create a SignatureVerifier
+ */
+ xReferenceResolvedListener = new SignatureVerifierImpl;
+
+ css::uno::Reference<css::lang::XInitialization> xInitialization(xReferenceResolvedListener, css::uno::UNO_QUERY);
+
+ css::uno::Sequence<css::uno::Any> args
+ {
+ Any(OUString::number(nSecurityId)),
+ Any(uno::Reference<xml::crypto::sax::XSecuritySAXEventKeeper>(m_xSAXEventKeeper)),
+ Any(OUString::number(nIdOfSignatureElementCollector)),
+ Any(m_xSecurityContext),
+ Any(m_xXMLSignature)
+ };
+ xInitialization->initialize(args);
+
+ css::uno::Reference< css::xml::crypto::sax::XSignatureVerifyResultBroadcaster >
+ signatureVerifyResultBroadcaster(xReferenceResolvedListener, css::uno::UNO_QUERY);
+
+ signatureVerifyResultBroadcaster->addSignatureVerifyResultListener( this );
+
+ m_xSAXEventKeeper->addReferenceResolvedListener(
+ nIdOfSignatureElementCollector,
+ xReferenceResolvedListener);
+
+ css::uno::Reference<css::xml::crypto::sax::XKeyCollector> keyCollector (xReferenceResolvedListener, css::uno::UNO_QUERY);
+ keyCollector->setKeyId(0);
+
+ return xReferenceResolvedListener;
+}
+
+void XSecController::addSignature()
+{
+ css::uno::Reference< css::xml::crypto::sax::XReferenceResolvedListener > xReferenceResolvedListener;
+ sal_Int32 nSignatureId = 0;
+
+
+ if (m_bVerifyCurrentSignature)
+ {
+ chainOn();
+ xReferenceResolvedListener = prepareSignatureToRead( m_nReservedSignatureId );
+ m_bVerifyCurrentSignature = false;
+ nSignatureId = m_nReservedSignatureId;
+ }
+
+ InternalSignatureInformation isi( nSignatureId, xReferenceResolvedListener );
+ m_vInternalSignatureInformations.push_back( isi );
+}
+
+void XSecController::setSignatureMethod(svl::crypto::SignatureMethodAlgorithm eAlgorithmID)
+{
+ if (m_vInternalSignatureInformations.empty())
+ return;
+
+ m_vInternalSignatureInformations.back().signatureInfor.eAlgorithmID = eAlgorithmID;
+}
+
+void XSecController::switchGpgSignature()
+{
+#if HAVE_FEATURE_GPGME
+ // swap signature verifier for the Gpg one
+ m_xXMLSignature.set(new XMLSignature_GpgImpl());
+ if (m_vInternalSignatureInformations.empty())
+ return;
+
+ SignatureVerifierImpl* pImpl=
+ dynamic_cast<SignatureVerifierImpl*>(
+ m_vInternalSignatureInformations.back().xReferenceResolvedListener.get());
+ if (pImpl)
+ {
+ css::uno::Reference<css::xml::crypto::XSEInitializer> xGpgSEInitializer(
+ new SEInitializerGpg());
+ pImpl->updateSignature(new XMLSignature_GpgImpl(),
+ xGpgSEInitializer->createSecurityContext(OUString()));
+ }
+#else
+ (void) this;
+#endif
+}
+
+bool XSecController::haveReferenceForId(std::u16string_view rId) const
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::haveReferenceForId: no signature");
+ return false;
+ }
+ InternalSignatureInformation const& rIsi(m_vInternalSignatureInformations.back());
+ for (SignatureReferenceInformation const& rSri : rIsi.signatureInfor.vSignatureReferenceInfors)
+ {
+ if (rSri.nType == SignatureReferenceType::SAMEDOCUMENT
+ && rSri.ouURI == rId) // ouUri has # stripped
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void XSecController::addReference( const OUString& ouUri, sal_Int32 nDigestID, const OUString& ouType )
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::addReference: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ isi.addReference(SignatureReferenceType::SAMEDOCUMENT, nDigestID, ouUri, -1, ouType );
+}
+
+void XSecController::addStreamReference(
+ const OUString& ouUri,
+ bool isBinary,
+ sal_Int32 nDigestID )
+{
+ SignatureReferenceType type = (isBinary?SignatureReferenceType::BINARYSTREAM:SignatureReferenceType::XMLSTREAM);
+
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::addStreamReference: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+
+ if ( isi.xReferenceResolvedListener.is() )
+ {
+ /*
+ * get the input stream
+ */
+ css::uno::Reference< css::io::XInputStream > xObjectInputStream
+ = getObjectInputStream( ouUri );
+
+ if ( xObjectInputStream.is() )
+ {
+ css::uno::Reference<css::xml::crypto::XUriBinding> xUriBinding
+ (isi.xReferenceResolvedListener, css::uno::UNO_QUERY);
+ xUriBinding->setUriBinding(ouUri, xObjectInputStream);
+ }
+ }
+
+ isi.addReference(type, nDigestID, ouUri, -1, OUString());
+}
+
+void XSecController::setReferenceCount() const
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setReferenceCount: no signature");
+ return;
+ }
+ const InternalSignatureInformation &isi =
+ m_vInternalSignatureInformations.back();
+
+ if ( !isi.xReferenceResolvedListener.is() )
+ return;
+
+ const SignatureReferenceInformations &refInfors = isi.signatureInfor.vSignatureReferenceInfors;
+
+ int refNum = refInfors.size();
+ sal_Int32 referenceCount = 0;
+
+ for(int i=0 ; i<refNum; ++i)
+ {
+ if (refInfors[i].nType == SignatureReferenceType::SAMEDOCUMENT )
+ /*
+ * same-document reference
+ */
+ {
+ referenceCount++;
+ }
+ }
+
+ css::uno::Reference<css::xml::crypto::sax::XReferenceCollector> xReferenceCollector
+ (isi.xReferenceResolvedListener, css::uno::UNO_QUERY);
+ xReferenceCollector->setReferenceCount( referenceCount );
+}
+
+void XSecController::setX509Data(
+ std::vector<std::pair<OUString, OUString>> & rX509IssuerSerials,
+ std::vector<OUString> const& rX509Certificates)
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setX509IssuerName: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ SignatureInformation::X509Data data;
+ // due to the excessive flexibility of the spec it's possible that there
+ // is both a reference to a cert and the cert itself in one X509Data
+ for (OUString const& it : rX509Certificates)
+ {
+ try
+ {
+ data.emplace_back();
+ data.back().X509Certificate = it;
+ uno::Reference<xml::crypto::XSecurityEnvironment> const xSecEnv(m_xSecurityContext->getSecurityEnvironment());
+ uno::Reference<security::XCertificate> const xCert(xSecEnv->createCertificateFromAscii(it));
+ if (!xCert.is())
+ {
+ SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate");
+ continue; // will be handled in CheckX509Data
+ }
+ OUString const issuerName(xCert->getIssuerName());
+ OUString const serialNumber(xmlsecurity::bigIntegerToNumericString(xCert->getSerialNumber()));
+ auto const iter = std::find_if(rX509IssuerSerials.begin(), rX509IssuerSerials.end(),
+ [&](auto const& rX509IssuerSerial) {
+ return xmlsecurity::EqualDistinguishedNames(issuerName, rX509IssuerSerial.first, xmlsecurity::COMPAT_2ND)
+ && serialNumber == rX509IssuerSerial.second;
+ });
+ if (iter != rX509IssuerSerials.end())
+ {
+ data.back().X509IssuerName = iter->first;
+ data.back().X509SerialNumber = iter->second;
+ rX509IssuerSerials.erase(iter);
+ }
+ }
+ catch (uno::Exception const&)
+ {
+ SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate");
+ }
+ }
+ // now handle any that are left...
+ for (auto const& it : rX509IssuerSerials)
+ {
+ data.emplace_back();
+ data.back().X509IssuerName = it.first;
+ data.back().X509SerialNumber = it.second;
+ }
+ if (!data.empty())
+ {
+ isi.signatureInfor.X509Datas.push_back(data);
+ }
+}
+
+void XSecController::setSignatureValue( OUString const & ouSignatureValue )
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setSignatureValue: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ isi.signatureInfor.ouSignatureValue = ouSignatureValue;
+}
+
+void XSecController::setDigestValue( sal_Int32 nDigestID, OUString const & ouDigestValue )
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setDigestValue: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ if (isi.signatureInfor.vSignatureReferenceInfors.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setDigestValue: no signature reference");
+ return;
+ }
+ SignatureReferenceInformation &reference =
+ isi.signatureInfor.vSignatureReferenceInfors.back();
+ reference.nDigestID = nDigestID;
+ reference.ouDigestValue = ouDigestValue;
+}
+
+void XSecController::setGpgKeyID( OUString const & ouKeyID )
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setGpgKeyID: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ isi.signatureInfor.ouGpgKeyID = ouKeyID;
+}
+
+void XSecController::setGpgCertificate( OUString const & ouGpgCert )
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setGpgCertificate: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ isi.signatureInfor.ouGpgCertificate = ouGpgCert;
+}
+
+void XSecController::setGpgOwner( OUString const & ouGpgOwner )
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setGpgOwner: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ isi.signatureInfor.ouGpgOwner = ouGpgOwner;
+}
+
+void XSecController::setDate(OUString const& rId, OUString const& ouDate)
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setDate: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ // there may be multiple timestamps in a signature - check them for consistency
+ if (!isi.signatureInfor.ouDateTime.isEmpty()
+ && isi.signatureInfor.ouDateTime != ouDate)
+ {
+ isi.signatureInfor.hasInconsistentSigningTime = true;
+ }
+ (void)utl::ISO8601parseDateTime( ouDate, isi.signatureInfor.stDateTime);
+ isi.signatureInfor.ouDateTime = ouDate;
+ if (!rId.isEmpty())
+ {
+ isi.signatureInfor.ouDateTimePropertyId = rId;
+ }
+}
+
+void XSecController::setDescription(OUString const& rId, OUString const& rDescription)
+{
+ if (m_vInternalSignatureInformations.empty())
+ return;
+
+ InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back();
+ rInformation.signatureInfor.ouDescription = rDescription;
+ if (!rId.isEmpty())
+ {
+ rInformation.signatureInfor.ouDescriptionPropertyId = rId;
+ }
+}
+
+void XSecController::setSignatureBytes(const uno::Sequence<sal_Int8>& rBytes)
+{
+ if (m_vInternalSignatureInformations.empty())
+ return;
+
+ InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back();
+ rInformation.signatureInfor.aSignatureBytes = rBytes;
+}
+
+void XSecController::setX509CertDigest(
+ OUString const& rCertDigest, sal_Int32 const /*TODO nReferenceDigestID*/,
+ std::u16string_view const& rX509IssuerName, std::u16string_view const& rX509SerialNumber)
+{
+ if (m_vInternalSignatureInformations.empty())
+ return;
+
+ InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back();
+ for (auto & rData : rInformation.signatureInfor.X509Datas)
+ {
+ for (auto & it : rData)
+ {
+ if (xmlsecurity::EqualDistinguishedNames(it.X509IssuerName, rX509IssuerName, xmlsecurity::COMPAT_BOTH)
+ && it.X509SerialNumber == rX509SerialNumber)
+ {
+ it.CertDigest = rCertDigest;
+ return;
+ }
+ }
+ }
+ // fall-back: read the actual certificates
+ for (auto & rData : rInformation.signatureInfor.X509Datas)
+ {
+ for (auto & it : rData)
+ {
+ if (!it.X509Certificate.isEmpty())
+ {
+ try
+ {
+ uno::Reference<xml::crypto::XSecurityEnvironment> const xSecEnv(m_xSecurityContext->getSecurityEnvironment());
+ uno::Reference<security::XCertificate> const xCert(xSecEnv->createCertificateFromAscii(it.X509Certificate));
+ if (!xCert.is())
+ {
+ SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate");
+ }
+ else if (xmlsecurity::EqualDistinguishedNames(xCert->getIssuerName(), rX509IssuerName, xmlsecurity::COMPAT_2ND)
+ && xmlsecurity::bigIntegerToNumericString(xCert->getSerialNumber()) == rX509SerialNumber)
+ {
+ it.CertDigest = rCertDigest;
+ // note: testInsertCertificate_PEM_DOCX requires these!
+ it.X509SerialNumber = rX509SerialNumber;
+ it.X509IssuerName = rX509IssuerName;
+ return;
+ }
+ }
+ catch (uno::Exception const&)
+ {
+ SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate");
+ }
+ }
+ }
+ }
+ if (!rInformation.signatureInfor.ouGpgCertificate.isEmpty())
+ {
+ SAL_INFO_IF(rCertDigest != rInformation.signatureInfor.ouGpgKeyID,
+ "xmlsecurity.helper", "PGPKeyID vs CertDigest mismatch");
+ }
+ else
+ {
+ SAL_INFO("xmlsecurity.helper", "cannot find X509Data for CertDigest");
+ }
+}
+
+namespace {
+Reference<css::graphic::XGraphic> lcl_getGraphicFromString(std::u16string_view rImage)
+{
+ Sequence<sal_Int8> seq;
+ comphelper::Base64::decode(seq, rImage);
+
+ Reference< graphic::XGraphic > xGraphic;
+ if( !seq.hasElements() )
+ return Reference<css::graphic::XGraphic>();
+
+ Reference< graphic::XGraphicProvider > xGraphicProvider(
+ graphic::GraphicProvider::create(comphelper::getProcessComponentContext()) );
+ Reference< io::XInputStream > xInputStream( new ::comphelper::SequenceInputStream( seq ) );
+
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("InputStream", xInputStream) };
+ xGraphic = xGraphicProvider->queryGraphic(aArgs);
+
+ return xGraphic;
+}
+}
+
+void XSecController::setValidSignatureImage(std::u16string_view rValidSigImg)
+{
+ if (m_vInternalSignatureInformations.empty() || rValidSigImg.empty())
+ return;
+
+ InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back();
+ rInformation.signatureInfor.aValidSignatureImage = lcl_getGraphicFromString(rValidSigImg);
+}
+
+void XSecController::setInvalidSignatureImage(std::u16string_view rInvalidSigImg)
+{
+ if (m_vInternalSignatureInformations.empty() || rInvalidSigImg.empty())
+ return;
+
+ InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back();
+ rInformation.signatureInfor.aInvalidSignatureImage = lcl_getGraphicFromString(rInvalidSigImg);
+}
+
+void XSecController::setSignatureLineId(const OUString& rSignatureLineId)
+{
+ if (m_vInternalSignatureInformations.empty())
+ return;
+
+ InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back();
+ rInformation.signatureInfor.ouSignatureLineId = rSignatureLineId;
+}
+
+void XSecController::addEncapsulatedX509Certificate(const OUString& rEncapsulatedX509Certificate)
+{
+ if (m_vInternalSignatureInformations.empty())
+ return;
+
+ if (rEncapsulatedX509Certificate.isEmpty())
+ return;
+
+ InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back();
+ rInformation.signatureInfor.maEncapsulatedX509Certificates.insert(rEncapsulatedX509Certificate);
+}
+
+void XSecController::setId( OUString const & ouId )
+{
+ if (m_vInternalSignatureInformations.empty())
+ {
+ SAL_INFO("xmlsecurity.helper","XSecController::setId: no signature");
+ return;
+ }
+ InternalSignatureInformation &isi = m_vInternalSignatureInformations.back();
+ isi.signatureInfor.ouSignatureId = ouId;
+}
+
+/* public: for signature verify */
+void XSecController::collectToVerify( std::u16string_view referenceId )
+{
+ /* SAL_WARN_IF( !m_xSAXEventKeeper.is(), "xmlsecurity", "the SAXEventKeeper is NULL" ); */
+
+ if ( m_eStatusOfSecurityComponents != InitializationState::INITIALIZED )
+ /*
+ * if all security components are ready, verify the signature.
+ */
+ return;
+
+ bool bJustChainingOn = false;
+ css::uno::Reference< css::xml::sax::XDocumentHandler > xHandler;
+
+ int i,j;
+ int sigNum = m_vInternalSignatureInformations.size();
+
+ for (i=0; i<sigNum; ++i)
+ {
+ InternalSignatureInformation& isi = m_vInternalSignatureInformations[i];
+ SignatureReferenceInformations& vReferenceInfors = isi.signatureInfor.vSignatureReferenceInfors;
+ int refNum = vReferenceInfors.size();
+
+ for (j=0; j<refNum; ++j)
+ {
+ SignatureReferenceInformation &refInfor = vReferenceInfors[j];
+
+ if (refInfor.ouURI == referenceId)
+ {
+ if (chainOn())
+ {
+ bJustChainingOn = true;
+ xHandler = m_xSAXEventKeeper->setNextHandler(nullptr);
+ }
+
+ sal_Int32 nKeeperId = m_xSAXEventKeeper->addSecurityElementCollector(
+ css::xml::crypto::sax::ElementMarkPriority_BEFOREMODIFY, false );
+
+ css::uno::Reference<css::xml::crypto::sax::XReferenceCollector> xReferenceCollector
+ ( isi.xReferenceResolvedListener, css::uno::UNO_QUERY );
+
+ m_xSAXEventKeeper->setSecurityId(nKeeperId, isi.signatureInfor.nSecurityId);
+ m_xSAXEventKeeper->addReferenceResolvedListener( nKeeperId, isi.xReferenceResolvedListener);
+ xReferenceCollector->setReferenceId( nKeeperId );
+
+ isi.vKeeperIds[j] = nKeeperId;
+ break;
+ }
+ }
+ }
+
+ if ( bJustChainingOn )
+ {
+ m_xSAXEventKeeper->setNextHandler(xHandler);
+ }
+}
+
+void XSecController::addSignature( sal_Int32 nSignatureId )
+{
+ SAL_WARN_IF( !m_xSecParser.is(), "xmlsecurity.helper", "No XSecParser initialized" );
+
+ m_nReservedSignatureId = nSignatureId;
+ m_bVerifyCurrentSignature = true;
+}
+
+css::uno::Reference< css::xml::sax::XDocumentHandler > const & XSecController::createSignatureReader(XMLSignatureHelper& rXMLSignatureHelper, sal_Int32 nType)
+{
+ if (nType == embed::StorageFormats::OFOPXML)
+ m_xSecParser = new OOXMLSecParser(rXMLSignatureHelper, this);
+ else
+ m_xSecParser = new XSecParser(rXMLSignatureHelper, this);
+ css::uno::Reference< css::lang::XInitialization > xInitialization(m_xSecParser, uno::UNO_QUERY);
+
+ setSAXChainConnector(xInitialization);
+
+ return m_xSecParser;
+}
+
+void XSecController::releaseSignatureReader()
+{
+ clearSAXChainConnector( );
+ m_xSecParser.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */