/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace com::sun::star; using namespace css::xml::sax; struct OOXMLSecExporter::Impl { private: const uno::Reference& m_xComponentContext; const uno::Reference& m_xRootStorage; const uno::Reference& m_xDocumentHandler; const SignatureInformation& m_rInformation; OUString m_aSignatureTimeValue; public: Impl(const uno::Reference& xComponentContext, const uno::Reference& xRootStorage, const uno::Reference& 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(const OUString& rStreamName); /// Should we intentionally not sign this relation type? static bool isOOXMLRelationDenylist(const OUString& rRelationName); const uno::Reference& getDocumentHandler() const { return m_xDocumentHandler; } void writeSignedInfo(); void writeCanonicalizationMethod(); void writeCanonicalizationTransform(); void writeSignatureMethod(); void writeSignedInfoReferences(); void writeSignatureValue(); void writeKeyInfo(); void writePackageObject(); void writeManifest(); void writeRelationshipTransform(const OUString& rURI); /// Writes inside idPackageObject. void writePackageObjectSignatureProperties(); /// Writes a single inside . void writeManifestReference(const SignatureReferenceInformation& rReference); void writeOfficeObject(); /// Writes . void writeSignatureInfo(); void writePackageSignature(); void writeSignatureLineImages(); }; bool OOXMLSecExporter::Impl::isOOXMLDenylist(const OUString& rStreamName) { static const std::initializer_list 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 rStreamName.startsWith(rLiteral); }); } bool OOXMLSecExporter::Impl::isOOXMLRelationDenylist(const OUString& rRelationName) { static const std::initializer_list 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(new SvXMLAttributeList())); writeCanonicalizationMethod(); writeSignatureMethod(); writeSignedInfoReferences(); m_xDocumentHandler->endElement("SignedInfo"); } void OOXMLSecExporter::Impl::writeCanonicalizationMethod() { rtl::Reference pAttributeList(new SvXMLAttributeList()); pAttributeList->AddAttribute("Algorithm", ALGO_C14N); m_xDocumentHandler->startElement("CanonicalizationMethod", uno::Reference(pAttributeList)); m_xDocumentHandler->endElement("CanonicalizationMethod"); } void OOXMLSecExporter::Impl::writeCanonicalizationTransform() { rtl::Reference pAttributeList(new SvXMLAttributeList()); pAttributeList->AddAttribute("Algorithm", ALGO_C14N); m_xDocumentHandler->startElement("Transform", uno::Reference(pAttributeList)); m_xDocumentHandler->endElement("Transform"); } void OOXMLSecExporter::Impl::writeSignatureMethod() { rtl::Reference pAttributeList(new SvXMLAttributeList()); 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(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 pAttributeList(new SvXMLAttributeList()); if (rReference.ouURI != "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(pAttributeList)); } if (rReference.ouURI == "idSignedProperties") { m_xDocumentHandler->startElement( "Transforms", uno::Reference(new SvXMLAttributeList())); writeCanonicalizationTransform(); m_xDocumentHandler->endElement("Transforms"); } DocumentSignatureHelper::writeDigestMethod(m_xDocumentHandler); m_xDocumentHandler->startElement( "DigestValue", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->characters(rReference.ouDigestValue); m_xDocumentHandler->endElement("DigestValue"); m_xDocumentHandler->endElement("Reference"); } } } void OOXMLSecExporter::Impl::writeSignatureValue() { m_xDocumentHandler->startElement( "SignatureValue", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->characters(m_rInformation.ouSignatureValue); m_xDocumentHandler->endElement("SignatureValue"); } void OOXMLSecExporter::Impl::writeKeyInfo() { m_xDocumentHandler->startElement( "KeyInfo", uno::Reference(new SvXMLAttributeList())); assert(m_rInformation.GetSigningCertificate()); for (auto const& rData : m_rInformation.X509Datas) { m_xDocumentHandler->startElement( "X509Data", uno::Reference(new SvXMLAttributeList())); for (auto const& it : rData) { m_xDocumentHandler->startElement( "X509Certificate", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->characters(it.X509Certificate); m_xDocumentHandler->endElement("X509Certificate"); } m_xDocumentHandler->endElement("X509Data"); } m_xDocumentHandler->endElement("KeyInfo"); } void OOXMLSecExporter::Impl::writePackageObject() { rtl::Reference pAttributeList(new SvXMLAttributeList()); pAttributeList->AddAttribute("Id", "idPackageObject"); m_xDocumentHandler->startElement("Object", uno::Reference(pAttributeList)); writeManifest(); writePackageObjectSignatureProperties(); m_xDocumentHandler->endElement("Object"); } void OOXMLSecExporter::Impl::writeManifest() { m_xDocumentHandler->startElement( "Manifest", uno::Reference(new SvXMLAttributeList())); 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 xHierarchicalStorageAccess(m_xRootStorage, uno::UNO_QUERY); uno::Reference xRelStream( xHierarchicalStorageAccess->openStreamElementByHierarchicalName(rURI, embed::ElementModes::READ), uno::UNO_QUERY); { rtl::Reference pAttributeList(new SvXMLAttributeList()); pAttributeList->AddAttribute("Algorithm", ALGO_RELATIONSHIP); m_xDocumentHandler->startElement("Transform", uno::Reference(pAttributeList)); } const uno::Sequence> aRelationsInfo = comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(xRelStream, rURI, m_xComponentContext); for (const uno::Sequence& 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 pAttributeList(new SvXMLAttributeList()); pAttributeList->AddAttribute("xmlns:mdssi", NS_MDSSI); pAttributeList->AddAttribute("SourceId", aId); m_xDocumentHandler->startElement("mdssi:RelationshipReference", uno::Reference(pAttributeList)); m_xDocumentHandler->endElement("mdssi:RelationshipReference"); } m_xDocumentHandler->endElement("Transform"); } void OOXMLSecExporter::Impl::writePackageObjectSignatureProperties() { m_xDocumentHandler->startElement( "SignatureProperties", uno::Reference(new SvXMLAttributeList())); { rtl::Reference pAttributeList(new SvXMLAttributeList()); pAttributeList->AddAttribute("Id", "idSignatureTime"); pAttributeList->AddAttribute("Target", "#idPackageSignature"); m_xDocumentHandler->startElement("SignatureProperty", uno::Reference(pAttributeList)); } { rtl::Reference pAttributeList(new SvXMLAttributeList()); pAttributeList->AddAttribute("xmlns:mdssi", NS_MDSSI); m_xDocumentHandler->startElement("mdssi:SignatureTime", uno::Reference(pAttributeList)); } m_xDocumentHandler->startElement( "mdssi:Format", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->characters("YYYY-MM-DDThh:mm:ssTZD"); m_xDocumentHandler->endElement("mdssi:Format"); m_xDocumentHandler->startElement( "mdssi:Value", uno::Reference(new SvXMLAttributeList())); 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 = m_aSignatureTimeValue.copy(0, nCommaPos); m_aSignatureTimeValue += "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 pAttributeList(new SvXMLAttributeList()); pAttributeList->AddAttribute("URI", rReference.ouURI); m_xDocumentHandler->startElement("Reference", uno::Reference(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(new SvXMLAttributeList())); writeRelationshipTransform(aURI); writeCanonicalizationTransform(); m_xDocumentHandler->endElement("Transforms"); } DocumentSignatureHelper::writeDigestMethod(m_xDocumentHandler); m_xDocumentHandler->startElement( "DigestValue", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->characters(rReference.ouDigestValue); m_xDocumentHandler->endElement("DigestValue"); m_xDocumentHandler->endElement("Reference"); } void OOXMLSecExporter::Impl::writeOfficeObject() { { rtl::Reference pAttributeList(new SvXMLAttributeList()); pAttributeList->AddAttribute("Id", "idOfficeObject"); m_xDocumentHandler->startElement("Object", uno::Reference(pAttributeList)); } m_xDocumentHandler->startElement( "SignatureProperties", uno::Reference(new SvXMLAttributeList())); { rtl::Reference pAttributeList(new SvXMLAttributeList()); pAttributeList->AddAttribute("Id", "idOfficeV1Details"); pAttributeList->AddAttribute("Target", "#idPackageSignature"); m_xDocumentHandler->startElement("SignatureProperty", uno::Reference(pAttributeList)); } writeSignatureInfo(); m_xDocumentHandler->endElement("SignatureProperty"); m_xDocumentHandler->endElement("SignatureProperties"); m_xDocumentHandler->endElement("Object"); } void OOXMLSecExporter::Impl::writeSignatureInfo() { rtl::Reference pAttributeList(new SvXMLAttributeList()); pAttributeList->AddAttribute("xmlns", "http://schemas.microsoft.com/office/2006/digsig"); m_xDocumentHandler->startElement("SignatureInfoV1", uno::Reference(pAttributeList)); m_xDocumentHandler->startElement( "SetupID", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->characters(m_rInformation.ouSignatureLineId); m_xDocumentHandler->endElement("SetupID"); m_xDocumentHandler->startElement( "SignatureText", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->endElement("SignatureText"); m_xDocumentHandler->startElement( "SignatureImage", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->endElement("SignatureImage"); m_xDocumentHandler->startElement( "SignatureComments", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->characters(m_rInformation.ouDescription); m_xDocumentHandler->endElement("SignatureComments"); // Just hardcode something valid according to [MS-OFFCRYPTO]. m_xDocumentHandler->startElement( "WindowsVersion", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->characters("6.1"); m_xDocumentHandler->endElement("WindowsVersion"); m_xDocumentHandler->startElement( "OfficeVersion", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->characters("16.0"); m_xDocumentHandler->endElement("OfficeVersion"); m_xDocumentHandler->startElement( "ApplicationVersion", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->characters("16.0"); m_xDocumentHandler->endElement("ApplicationVersion"); m_xDocumentHandler->startElement( "Monitors", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->characters("1"); m_xDocumentHandler->endElement("Monitors"); m_xDocumentHandler->startElement( "HorizontalResolution", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->characters("1280"); m_xDocumentHandler->endElement("HorizontalResolution"); m_xDocumentHandler->startElement( "VerticalResolution", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->characters("800"); m_xDocumentHandler->endElement("VerticalResolution"); m_xDocumentHandler->startElement( "ColorDepth", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->characters("32"); m_xDocumentHandler->endElement("ColorDepth"); m_xDocumentHandler->startElement( "SignatureProviderId", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->characters("{00000000-0000-0000-0000-000000000000}"); m_xDocumentHandler->endElement("SignatureProviderId"); m_xDocumentHandler->startElement( "SignatureProviderUrl", uno::Reference(new SvXMLAttributeList())); m_xDocumentHandler->endElement("SignatureProviderUrl"); m_xDocumentHandler->startElement( "SignatureProviderDetails", uno::Reference(new SvXMLAttributeList())); 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(new SvXMLAttributeList())); m_xDocumentHandler->characters("2"); m_xDocumentHandler->endElement("SignatureType"); m_xDocumentHandler->endElement("SignatureInfoV1"); } void OOXMLSecExporter::Impl::writePackageSignature() { m_xDocumentHandler->startElement( "Object", uno::Reference(new SvXMLAttributeList())); { rtl::Reference pAttributeList(new SvXMLAttributeList()); pAttributeList->AddAttribute("xmlns:xd", NS_XD); pAttributeList->AddAttribute("Target", "#idPackageSignature"); m_xDocumentHandler->startElement("xd:QualifyingProperties", uno::Reference(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 pAttributeList(new SvXMLAttributeList()); pAttributeList->AddAttribute("Id", "idValidSigLnImg"); m_xDocumentHandler->startElement("Object", uno::Reference(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 pAttributeList(new SvXMLAttributeList()); pAttributeList->AddAttribute("Id", "idInvalidSigLnImg"); m_xDocumentHandler->startElement("Object", uno::Reference(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"); } OOXMLSecExporter::OOXMLSecExporter( const uno::Reference& xComponentContext, const uno::Reference& xRootStorage, const uno::Reference& xDocumentHandler, const SignatureInformation& rInformation) : m_pImpl( std::make_unique(xComponentContext, xRootStorage, xDocumentHandler, rInformation)) { } OOXMLSecExporter::~OOXMLSecExporter() = default; void OOXMLSecExporter::writeSignature() { rtl::Reference pAttributeList(new SvXMLAttributeList()); pAttributeList->AddAttribute("xmlns", NS_XMLDSIG); pAttributeList->AddAttribute("Id", "idPackageSignature"); m_pImpl->getDocumentHandler()->startElement( "Signature", uno::Reference(pAttributeList)); m_pImpl->writeSignedInfo(); m_pImpl->writeSignatureValue(); m_pImpl->writeKeyInfo(); m_pImpl->writePackageObject(); m_pImpl->writeOfficeObject(); m_pImpl->writePackageSignature(); m_pImpl->writeSignatureLineImages(); m_pImpl->getDocumentHandler()->endElement("Signature"); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */