summaryrefslogtreecommitdiffstats
path: root/xmlsecurity/source/gpg
diff options
context:
space:
mode:
Diffstat (limited to 'xmlsecurity/source/gpg')
-rw-r--r--xmlsecurity/source/gpg/CertificateImpl.cxx261
-rw-r--r--xmlsecurity/source/gpg/CertificateImpl.hxx100
-rw-r--r--xmlsecurity/source/gpg/CipherContext.cxx27
-rw-r--r--xmlsecurity/source/gpg/CipherContext.hxx26
-rw-r--r--xmlsecurity/source/gpg/DigestContext.cxx23
-rw-r--r--xmlsecurity/source/gpg/DigestContext.hxx24
-rw-r--r--xmlsecurity/source/gpg/SEInitializer.cxx80
-rw-r--r--xmlsecurity/source/gpg/SecurityEnvironment.cxx232
-rw-r--r--xmlsecurity/source/gpg/SecurityEnvironment.hxx72
-rw-r--r--xmlsecurity/source/gpg/XMLEncryption.cxx37
-rw-r--r--xmlsecurity/source/gpg/XMLEncryption.hxx37
-rw-r--r--xmlsecurity/source/gpg/XMLSecurityContext.cxx87
-rw-r--r--xmlsecurity/source/gpg/XMLSecurityContext.hxx58
-rw-r--r--xmlsecurity/source/gpg/xmlsignature_gpgimpl.cxx527
14 files changed, 1591 insertions, 0 deletions
diff --git a/xmlsecurity/source/gpg/CertificateImpl.cxx b/xmlsecurity/source/gpg/CertificateImpl.cxx
new file mode 100644
index 000000000..a0a08fc80
--- /dev/null
+++ b/xmlsecurity/source/gpg/CertificateImpl.cxx
@@ -0,0 +1,261 @@
+/* -*- 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 <config_gpgme.h>
+
+#include "CertificateImpl.hxx"
+
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <com/sun/star/security/KeyUsage.hpp>
+#include <officecfg/Office/Common.hxx>
+#include <svl/sigstruct.hxx>
+
+#include <context.h>
+#include <data.h>
+
+using namespace css;
+using namespace css::uno;
+using namespace css::security;
+using namespace css::util;
+
+CertificateImpl::CertificateImpl()
+{
+}
+
+CertificateImpl::~CertificateImpl()
+{
+}
+
+//Methods from XCertificateImpl
+sal_Int16 SAL_CALL CertificateImpl::getVersion()
+{
+ return 0;
+}
+
+Sequence< sal_Int8 > SAL_CALL CertificateImpl::getSerialNumber()
+{
+ // TODO: perhaps map to subkey's cardSerialNumber - if you have
+ // one to test
+ return Sequence< sal_Int8 >();
+}
+
+OUString SAL_CALL CertificateImpl::getIssuerName()
+{
+ const GpgME::UserID userId = m_pKey.userID(0);
+ if (userId.isNull())
+ return OUString();
+
+ return OStringToOUString(userId.id(), RTL_TEXTENCODING_UTF8);
+}
+
+OUString SAL_CALL CertificateImpl::getSubjectName()
+{
+ // Same as issuer name (user ID)
+ return getIssuerName();
+}
+
+namespace {
+ DateTime convertUnixTimeToDateTime(time_t time)
+ {
+ DateTime dateTime;
+ struct tm *timeStruct = gmtime(&time);
+ dateTime.Year = timeStruct->tm_year + 1900;
+ dateTime.Month = timeStruct->tm_mon + 1;
+ dateTime.Day = timeStruct->tm_mday;
+ dateTime.Hours = timeStruct->tm_hour;
+ dateTime.Minutes = timeStruct->tm_min;
+ dateTime.Seconds = timeStruct->tm_sec;
+ return dateTime;
+ }
+}
+
+DateTime SAL_CALL CertificateImpl::getNotValidBefore()
+{
+ const GpgME::Subkey subkey = m_pKey.subkey(0);
+ if (subkey.isNull())
+ return DateTime();
+
+ return convertUnixTimeToDateTime(m_pKey.subkey(0).creationTime());
+}
+
+DateTime SAL_CALL CertificateImpl::getNotValidAfter()
+{
+ const GpgME::Subkey subkey = m_pKey.subkey(0);
+ if (subkey.isNull() || subkey.neverExpires())
+ return DateTime();
+
+ return convertUnixTimeToDateTime(m_pKey.subkey(0).expirationTime());
+}
+
+Sequence< sal_Int8 > SAL_CALL CertificateImpl::getIssuerUniqueID()
+{
+ // Empty for gpg
+ return Sequence< sal_Int8 > ();
+}
+
+Sequence< sal_Int8 > SAL_CALL CertificateImpl::getSubjectUniqueID()
+{
+ // Empty for gpg
+ return Sequence< sal_Int8 > ();
+}
+
+Sequence< Reference< XCertificateExtension > > SAL_CALL CertificateImpl::getExtensions()
+{
+ // Empty for gpg
+ return Sequence< Reference< XCertificateExtension > > ();
+}
+
+Reference< XCertificateExtension > SAL_CALL CertificateImpl::findCertificateExtension( const Sequence< sal_Int8 >& /*oid*/ )
+{
+ // Empty for gpg
+ return Reference< XCertificateExtension > ();
+}
+
+Sequence< sal_Int8 > SAL_CALL CertificateImpl::getEncoded()
+{
+ // Export key to base64Empty for gpg
+ return m_aBits;
+}
+
+OUString SAL_CALL CertificateImpl::getSubjectPublicKeyAlgorithm()
+{
+ const GpgME::Subkey subkey = m_pKey.subkey(0);
+ if (subkey.isNull())
+ return OUString();
+
+ return OStringToOUString(subkey.publicKeyAlgorithmAsString(), RTL_TEXTENCODING_UTF8);
+}
+
+Sequence< sal_Int8 > SAL_CALL CertificateImpl::getSubjectPublicKeyValue()
+{
+ return Sequence< sal_Int8 > ();
+}
+
+OUString SAL_CALL CertificateImpl::getSignatureAlgorithm()
+{
+ const GpgME::UserID userId = m_pKey.userID(0);
+ if (userId.isNull())
+ return OUString();
+
+ const GpgME::UserID::Signature signature = userId.signature(0);
+ if (signature.isNull())
+ return OUString();
+
+ return OStringToOUString(signature.algorithmAsString(), RTL_TEXTENCODING_UTF8);
+}
+
+Sequence< sal_Int8 > SAL_CALL CertificateImpl::getSHA1Thumbprint()
+{
+ // This is mapped to the fingerprint for gpg
+ const char* keyId = m_pKey.primaryFingerprint();
+ return comphelper::arrayToSequence<sal_Int8>(
+ keyId, strlen(keyId)+1);
+}
+
+Sequence<sal_Int8> CertificateImpl::getSHA256Thumbprint()
+{
+ // This is mapped to the fingerprint for gpg (though that's only
+ // SHA1 actually)
+ const char* keyId = m_pKey.primaryFingerprint();
+ return comphelper::arrayToSequence<sal_Int8>(
+ keyId, strlen(keyId)+1);
+}
+
+svl::crypto::SignatureMethodAlgorithm CertificateImpl::getSignatureMethodAlgorithm()
+{
+ return svl::crypto::SignatureMethodAlgorithm::RSA;
+}
+
+Sequence< sal_Int8 > SAL_CALL CertificateImpl::getMD5Thumbprint()
+{
+ // This is mapped to the shorter keyID for gpg
+ const char* keyId = m_pKey.keyID();
+ return comphelper::arrayToSequence<sal_Int8>(
+ keyId, strlen(keyId)+1);
+}
+
+CertificateKind SAL_CALL CertificateImpl::getCertificateKind()
+{
+ return CertificateKind_OPENPGP;
+}
+
+sal_Int32 SAL_CALL CertificateImpl::getCertificateUsage()
+{
+ return KeyUsage::DIGITAL_SIGNATURE | KeyUsage::NON_REPUDIATION | KeyUsage::KEY_ENCIPHERMENT | KeyUsage::DATA_ENCIPHERMENT;
+}
+
+/* XUnoTunnel */
+sal_Int64 SAL_CALL CertificateImpl::getSomething(const Sequence< sal_Int8 >& aIdentifier)
+{
+ return comphelper::getSomethingImpl(aIdentifier, this);
+}
+
+/* XUnoTunnel extension */
+
+const Sequence< sal_Int8>& CertificateImpl::getUnoTunnelId() {
+ static const comphelper::UnoIdInit theCertificateImplUnoTunnelId;
+ return theCertificateImplUnoTunnelId.getSeq();
+}
+
+void CertificateImpl::setCertificate(GpgME::Context* ctx, const GpgME::Key& key)
+{
+ m_pKey = key;
+
+ // extract key data, store into m_aBits
+ GpgME::Data data_out;
+ ctx->setArmor(false); // caller will base64-encode anyway
+ GpgME::Error err = ctx->exportPublicKeys(
+ key.primaryFingerprint(),
+ data_out,
+ officecfg::Office::Common::Security::OpenPGP::MinimalKeyExport::get()
+ ? GpgME::Context::ExportMinimal : 0
+ );
+
+ if (err)
+ throw RuntimeException("The GpgME library failed to retrieve the public key");
+
+ off_t result = data_out.seek(0,SEEK_SET);
+ (void) result;
+ assert(result == 0);
+ int len=0, curr=0; char buf;
+ while( (curr=data_out.read(&buf, 1)) )
+ len += curr;
+
+ // write bits to sequence of bytes
+ m_aBits.realloc(len);
+ result = data_out.seek(0,SEEK_SET);
+ assert(result == 0);
+ if( data_out.read(m_aBits.getArray(), len) != len )
+ throw RuntimeException("The GpgME library failed to read the key");
+}
+
+const GpgME::Key* CertificateImpl::getCertificate() const
+{
+ return &m_pKey;
+}
+
+/* XServiceInfo */
+OUString SAL_CALL CertificateImpl::getImplementationName()
+{
+ return "com.sun.star.xml.security.gpg.XCertificate_GpgImpl";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL CertificateImpl::supportsService(const OUString& serviceName)
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+/* XServiceInfo */
+Sequence<OUString> SAL_CALL CertificateImpl::getSupportedServiceNames() { return { OUString() }; }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/CertificateImpl.hxx b/xmlsecurity/source/gpg/CertificateImpl.hxx
new file mode 100644
index 000000000..b6c50cb2e
--- /dev/null
+++ b/xmlsecurity/source/gpg/CertificateImpl.hxx
@@ -0,0 +1,100 @@
+/* -*- 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 <certificate.hxx>
+
+#include <sal/types.h>
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/security/CertificateKind.hpp>
+#include <com/sun/star/security/XCertificate.hpp>
+
+#if defined _MSC_VER && defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundef"
+#endif
+#include <key.h>
+#if defined _MSC_VER && defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+class CertificateImpl : public cppu::WeakImplHelper< css::security::XCertificate,
+ css::lang::XUnoTunnel,
+ css::lang::XServiceInfo >,
+ public xmlsecurity::Certificate
+{
+private:
+ GpgME::Key m_pKey;
+ css::uno::Sequence< sal_Int8 > m_aBits;
+
+public:
+ CertificateImpl();
+ virtual ~CertificateImpl() override;
+
+ //Methods from XCertificate
+ virtual sal_Int16 SAL_CALL getVersion() override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getSerialNumber() override;
+
+ virtual OUString SAL_CALL getIssuerName() override;
+ virtual OUString SAL_CALL getSubjectName() override;
+
+ virtual css::util::DateTime SAL_CALL getNotValidBefore() override;
+ virtual css::util::DateTime SAL_CALL getNotValidAfter() override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getIssuerUniqueID() override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getSubjectUniqueID() override;
+
+ virtual css::uno::Sequence< css::uno::Reference< css::security::XCertificateExtension > > SAL_CALL getExtensions() override;
+
+ virtual css::uno::Reference< css::security::XCertificateExtension > SAL_CALL findCertificateExtension(const css::uno::Sequence< sal_Int8 >& oid) override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getEncoded() override;
+
+ virtual OUString SAL_CALL getSubjectPublicKeyAlgorithm() override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getSubjectPublicKeyValue() override;
+
+ virtual OUString SAL_CALL getSignatureAlgorithm() override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getSHA1Thumbprint() override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getMD5Thumbprint() override;
+
+ virtual sal_Int32 SAL_CALL getCertificateUsage() override;
+
+ //Methods from XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething(const css::uno::Sequence< sal_Int8 >& aIdentifier) override;
+
+ static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId();
+
+ /// @see xmlsecurity::Certificate::getSHA256Thumbprint().
+ virtual css::uno::Sequence<sal_Int8> getSHA256Thumbprint() override;
+ /// @see xmlsecurity::Certificate::getSignatureMethodAlgorithm().
+ virtual svl::crypto::SignatureMethodAlgorithm getSignatureMethodAlgorithm() override;
+ virtual css::security::CertificateKind SAL_CALL getCertificateKind() override;
+
+ // Helper methods
+ void setCertificate(GpgME::Context* ctx, const GpgME::Key& key);
+ const GpgME::Key* getCertificate() const;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+} ;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/CipherContext.cxx b/xmlsecurity/source/gpg/CipherContext.cxx
new file mode 100644
index 000000000..291db0ba6
--- /dev/null
+++ b/xmlsecurity/source/gpg/CipherContext.cxx
@@ -0,0 +1,27 @@
+/* -*- 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 "CipherContext.hxx"
+
+using namespace css;
+using namespace css::uno;
+using namespace css::lang;
+
+Sequence<sal_Int8>
+ SAL_CALL CipherContext::convertWithCipherContext(const Sequence<sal_Int8>& /*aData*/)
+{
+ return Sequence<sal_Int8>();
+}
+
+uno::Sequence<sal_Int8> SAL_CALL CipherContext::finalizeCipherContextAndDispose()
+{
+ return Sequence<sal_Int8>();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/CipherContext.hxx b/xmlsecurity/source/gpg/CipherContext.hxx
new file mode 100644
index 000000000..2c0ffcdc9
--- /dev/null
+++ b/xmlsecurity/source/gpg/CipherContext.hxx
@@ -0,0 +1,26 @@
+/* -*- 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/xml/crypto/XCipherContext.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+class CipherContext : public cppu::WeakImplHelper<css::xml::crypto::XCipherContext>
+{
+private:
+public:
+ // XCipherContext
+ virtual css::uno::Sequence<::sal_Int8>
+ SAL_CALL convertWithCipherContext(const css::uno::Sequence<::sal_Int8>& aData) override;
+ virtual css::uno::Sequence<::sal_Int8> SAL_CALL finalizeCipherContextAndDispose() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/DigestContext.cxx b/xmlsecurity/source/gpg/DigestContext.cxx
new file mode 100644
index 000000000..4864191bb
--- /dev/null
+++ b/xmlsecurity/source/gpg/DigestContext.cxx
@@ -0,0 +1,23 @@
+/* -*- 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 "DigestContext.hxx"
+
+using namespace css;
+using namespace css::uno;
+using namespace css::lang;
+
+void SAL_CALL DigestContext::updateDigest(const Sequence<sal_Int8>& /*aData*/) {}
+
+uno::Sequence<sal_Int8> SAL_CALL DigestContext::finalizeDigestAndDispose()
+{
+ return Sequence<sal_Int8>();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/DigestContext.hxx b/xmlsecurity/source/gpg/DigestContext.hxx
new file mode 100644
index 000000000..2d48daf34
--- /dev/null
+++ b/xmlsecurity/source/gpg/DigestContext.hxx
@@ -0,0 +1,24 @@
+/* -*- 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/xml/crypto/XDigestContext.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+class DigestContext : public cppu::WeakImplHelper<css::xml::crypto::XDigestContext>
+{
+public:
+ // XDigestContext
+ virtual void SAL_CALL updateDigest(const css::uno::Sequence<::sal_Int8>& aData) override;
+ virtual css::uno::Sequence<::sal_Int8> SAL_CALL finalizeDigestAndDispose() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/SEInitializer.cxx b/xmlsecurity/source/gpg/SEInitializer.cxx
new file mode 100644
index 000000000..5cfe325aa
--- /dev/null
+++ b/xmlsecurity/source/gpg/SEInitializer.cxx
@@ -0,0 +1,80 @@
+/* -*- 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 <cppuhelper/supportsservice.hxx>
+#include <gpg/SEInitializer.hxx>
+#include "SecurityEnvironment.hxx"
+#include "XMLSecurityContext.hxx"
+
+#include <global.h>
+
+namespace com::sun::star::uno
+{
+class XComponentContext;
+}
+
+using namespace css;
+using namespace css::lang;
+using namespace css::uno;
+using namespace css::xml::crypto;
+
+SEInitializerGpg::SEInitializerGpg()
+{
+ // Also init GpgME while we're at it
+ GpgME::initializeLibrary();
+}
+
+SEInitializerGpg::~SEInitializerGpg() {}
+
+/* XSEInitializer */
+Reference<XXMLSecurityContext> SAL_CALL SEInitializerGpg::createSecurityContext(const OUString&)
+{
+ try
+ {
+ /* Build XML Security Context */
+ Reference<XXMLSecurityContext> xSecCtx(new XMLSecurityContextGpg());
+
+ Reference<XSecurityEnvironment> xSecEnv(new SecurityEnvironmentGpg());
+ sal_Int32 n = xSecCtx->addSecurityEnvironment(xSecEnv);
+ //originally the SecurityEnvironment with the internal slot was set as default
+ xSecCtx->setDefaultSecurityEnvironmentIndex(n);
+ return xSecCtx;
+ }
+ catch (const uno::Exception&)
+ {
+ return nullptr;
+ }
+}
+
+void SAL_CALL SEInitializerGpg::freeSecurityContext(const uno::Reference<XXMLSecurityContext>&) {}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL SEInitializerGpg::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL SEInitializerGpg::getSupportedServiceNames()
+{
+ return { "com.sun.star.xml.crypto.GPGSEInitializer" };
+}
+
+OUString SAL_CALL SEInitializerGpg::getImplementationName()
+{
+ return "com.sun.star.xml.security.SEInitializer_Gpg";
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_xml_security_SEInitializer_Gpg_get_implementation(
+ uno::XComponentContext* /*pCtx*/, uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SEInitializerGpg());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/SecurityEnvironment.cxx b/xmlsecurity/source/gpg/SecurityEnvironment.cxx
new file mode 100644
index 000000000..da2310765
--- /dev/null
+++ b/xmlsecurity/source/gpg/SecurityEnvironment.cxx
@@ -0,0 +1,232 @@
+/* -*- 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 "SecurityEnvironment.hxx"
+#include "CertificateImpl.hxx"
+
+#include <com/sun/star/security/CertificateCharacters.hpp>
+#include <com/sun/star/security/CertificateValidity.hpp>
+
+#include <comphelper/servicehelper.hxx>
+#include <vector>
+#include <rtl/ref.hxx>
+
+#ifdef _WIN32
+#include <config_folders.h>
+#include <osl/file.hxx>
+#include <osl/process.h>
+#include <rtl/bootstrap.hxx>
+#include <tools/urlobj.hxx>
+#endif
+
+#include <key.h>
+#include <keylistresult.h>
+#include <xmlsec-wrapper.h>
+
+#if defined _MSC_VER && defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundef"
+#endif
+#include <gpgme.h>
+#if defined _MSC_VER && defined __clang__
+#pragma clang diagnostic pop
+#endif
+#include <context.h>
+
+using namespace css;
+using namespace css::security;
+using namespace css::uno;
+using namespace css::lang;
+
+SecurityEnvironmentGpg::SecurityEnvironmentGpg()
+{
+#ifdef _WIN32
+ // On Windows, gpgme expects gpgme-w32spawn.exe to be in the same directory as the current
+ // process executable. This assumption might be wrong, e.g., for bundled python, which is
+ // in instdir/program/python-core-x.y.z/bin, while gpgme-w32spawn.exe is in instdir/program.
+ // If we can't find gpgme-w32spawn.exe in the current executable location, then try to find
+ // the spawn executable, and inform gpgme about actual location using gpgme_set_global_flag.
+ [[maybe_unused]] static bool bSpawnPathInitialized = [] {
+ auto accessUrl = [](const INetURLObject& url) {
+ osl::File file(url.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+ return file.open(osl_File_OpenFlag_Read) == osl::FileBase::E_None;
+ };
+ OUString sPath;
+ osl_getExecutableFile(&sPath.pData);
+ INetURLObject aPathUrl(sPath);
+ aPathUrl.setName(u"gpgme-w32spawn.exe");
+ if (!accessUrl(aPathUrl))
+ {
+ sPath = "$BRAND_BASE_DIR/" LIBO_LIBEXEC_FOLDER "/gpgme-w32spawn.exe";
+ rtl::Bootstrap::expandMacros(sPath);
+ aPathUrl.SetURL(sPath);
+ if (accessUrl(aPathUrl))
+ {
+ aPathUrl.removeSegment();
+ GpgME::setGlobalFlag("w32-inst-dir",
+ aPathUrl.getFSysPath(FSysStyle::Dos).toUtf8().getStr());
+ }
+ }
+ return true;
+ }();
+#endif
+ GpgME::Error err = GpgME::checkEngine(GpgME::OpenPGP);
+ if (err)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ m_ctx.reset( GpgME::Context::createForProtocol(GpgME::OpenPGP) );
+ if (m_ctx == nullptr)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ m_ctx->setArmor(false);
+}
+
+SecurityEnvironmentGpg::~SecurityEnvironmentGpg()
+{
+}
+
+/* XUnoTunnel */
+sal_Int64 SAL_CALL SecurityEnvironmentGpg::getSomething( const Sequence< sal_Int8 >& aIdentifier )
+{
+ return comphelper::getSomethingImpl(aIdentifier, this);
+}
+
+/* XUnoTunnel extension */
+
+namespace
+{
+}
+
+const Sequence< sal_Int8>& SecurityEnvironmentGpg::getUnoTunnelId() {
+ static const comphelper::UnoIdInit theSecurityEnvironmentUnoTunnelId;
+ return theSecurityEnvironmentUnoTunnelId.getSeq();
+}
+
+OUString SecurityEnvironmentGpg::getSecurityEnvironmentInformation()
+{
+ return OUString();
+}
+
+Sequence< Reference < XCertificate > > SecurityEnvironmentGpg::getCertificatesImpl( bool bPrivateOnly )
+{
+ std::vector< GpgME::Key > keyList;
+ std::vector< rtl::Reference<CertificateImpl> > certsList;
+
+ m_ctx->setKeyListMode(GPGME_KEYLIST_MODE_LOCAL);
+ GpgME::Error err = m_ctx->startKeyListing("", bPrivateOnly );
+ while (!err) {
+ GpgME::Key k = m_ctx->nextKey(err);
+ if (err)
+ break;
+ if (!k.isRevoked() && !k.isExpired() && !k.isDisabled() && !k.isInvalid()) {
+ // We can't create CertificateImpl here as CertificateImpl::setCertificate uses GpgME API
+ // which interrupts our key listing here. So first get the keys from GpgME, then create the CertificateImpls
+ keyList.push_back(k);
+ }
+ }
+ m_ctx->endKeyListing();
+
+ for (auto const& key : keyList) {
+ rtl::Reference<CertificateImpl> xCert = new CertificateImpl();
+ xCert->setCertificate(m_ctx.get(),key);
+ certsList.push_back(xCert);
+ }
+
+ Sequence< Reference< XCertificate > > xCertificateSequence(certsList.size());
+ auto xCertificateSequenceRange = asNonConstRange(xCertificateSequence);
+ int i = 0;
+ for (const auto& cert : certsList) {
+ xCertificateSequenceRange[i++] = cert;
+ }
+
+ return xCertificateSequence;
+}
+
+Sequence< Reference < XCertificate > > SecurityEnvironmentGpg::getPersonalCertificates()
+{
+ return getCertificatesImpl( true );
+}
+
+Sequence< Reference < XCertificate > > SecurityEnvironmentGpg::getAllCertificates()
+{
+ return getCertificatesImpl( false );
+}
+
+Reference< XCertificate > SecurityEnvironmentGpg::getCertificate( const OUString& keyId, const Sequence< sal_Int8 >& /*serialNumber*/ )
+{
+ //xmlChar* pSignatureValue=xmlNodeGetContent(cur);
+ OString ostr = OUStringToOString( keyId , RTL_TEXTENCODING_UTF8 );
+ const xmlChar* strKeyId = reinterpret_cast<const xmlChar*>(ostr.getStr());
+ if(xmlSecBase64Decode(strKeyId, const_cast<xmlSecByte*>(strKeyId), xmlStrlen(strKeyId)) < 0)
+ throw RuntimeException("Base64 decode failed");
+
+ m_ctx->setKeyListMode(GPGME_KEYLIST_MODE_LOCAL);
+ GpgME::Error err = m_ctx->startKeyListing("", false);
+ while (!err) {
+ GpgME::Key k = m_ctx->nextKey(err);
+ if (err)
+ break;
+ if (!k.isInvalid() && strcmp(k.primaryFingerprint(), reinterpret_cast<const char*>(strKeyId)) == 0) {
+ rtl::Reference<CertificateImpl> xCert = new CertificateImpl();
+ xCert->setCertificate(m_ctx.get(), k);
+ m_ctx->endKeyListing();
+ return xCert;
+ }
+ }
+ m_ctx->endKeyListing();
+
+ return nullptr;
+}
+
+Sequence< Reference < XCertificate > > SecurityEnvironmentGpg::buildCertificatePath( const Reference< XCertificate >& /*begin*/ )
+{
+ return Sequence< Reference < XCertificate > >();
+}
+
+Reference< XCertificate > SecurityEnvironmentGpg::createCertificateFromRaw( const Sequence< sal_Int8 >& /*rawCertificate*/ )
+{
+ return nullptr;
+}
+
+Reference< XCertificate > SecurityEnvironmentGpg::createCertificateFromAscii( const OUString& /*asciiCertificate*/ )
+{
+ return nullptr;
+}
+
+sal_Int32 SecurityEnvironmentGpg::verifyCertificate( const Reference< XCertificate >& aCert,
+ const Sequence< Reference< XCertificate > >& /*intermediateCerts*/ )
+{
+ const CertificateImpl* xCert = dynamic_cast<CertificateImpl*>(aCert.get());
+ if (xCert == nullptr) {
+ // Can't find the key locally -> unknown owner
+ return security::CertificateValidity::ISSUER_UNKNOWN;
+ }
+
+ const GpgME::Key* key = xCert->getCertificate();
+ if (key->ownerTrust() == GpgME::Key::OwnerTrust::Marginal ||
+ key->ownerTrust() == GpgME::Key::OwnerTrust::Full ||
+ key->ownerTrust() == GpgME::Key::OwnerTrust::Ultimate)
+ {
+ return security::CertificateValidity::VALID;
+ }
+
+ return security::CertificateValidity::ISSUER_UNTRUSTED;
+}
+
+sal_Int32 SecurityEnvironmentGpg::getCertificateCharacters(
+ const Reference< XCertificate >& aCert)
+{
+ if (comphelper::getFromUnoTunnel<CertificateImpl>(aCert) == nullptr)
+ throw RuntimeException();
+
+ // we only listed private keys anyway, up in
+ // SecurityEnvironmentGpg::getPersonalCertificates
+ return CertificateCharacters::HAS_PRIVATE_KEY;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/SecurityEnvironment.hxx b/xmlsecurity/source/gpg/SecurityEnvironment.hxx
new file mode 100644
index 000000000..262578682
--- /dev/null
+++ b/xmlsecurity/source/gpg/SecurityEnvironment.hxx
@@ -0,0 +1,72 @@
+/* -*- 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 <sal/config.h>
+
+#include <memory>
+
+#include <rtl/ustring.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+
+namespace com::sun::star::security { class XCertificate; }
+namespace GpgME { class Context; }
+
+class SecurityEnvironmentGpg : public cppu::WeakImplHelper< css::xml::crypto::XSecurityEnvironment,
+ css::lang::XUnoTunnel >
+{
+ std::unique_ptr<GpgME::Context> m_ctx;
+
+public:
+ SecurityEnvironmentGpg();
+ virtual ~SecurityEnvironmentGpg() override;
+
+ //Methods from XSecurityEnvironment
+ virtual ::sal_Int32 SAL_CALL verifyCertificate(
+ const css::uno::Reference<
+ css::security::XCertificate >& xCert,
+ const css::uno::Sequence<
+ css::uno::Reference< css::security::XCertificate > > &
+ intermediateCerts) override;
+
+ virtual ::sal_Int32 SAL_CALL getCertificateCharacters( const css::uno::Reference< css::security::XCertificate >& xCert ) override;
+
+ virtual OUString SAL_CALL getSecurityEnvironmentInformation() override;
+
+ //Methods from XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override;
+
+ static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId() ;
+
+ virtual css::uno::Sequence< css::uno::Reference< css::security::XCertificate > > SAL_CALL getPersonalCertificates() override;
+
+ /** We reinterpret the first parameter (originally issuerName) as keyId. We have no other way to identify a gpg key. */
+ virtual css::uno::Reference< css::security::XCertificate > SAL_CALL getCertificate( const OUString& keyId, const css::uno::Sequence< sal_Int8 >& serialNumber ) override;
+
+ virtual css::uno::Sequence< css::uno::Reference< css::security::XCertificate > > SAL_CALL buildCertificatePath(
+ const css::uno::Reference< css::security::XCertificate >& beginCert ) override;
+
+ virtual css::uno::Reference< css::security::XCertificate > SAL_CALL createCertificateFromRaw(
+ const css::uno::Sequence< sal_Int8 >& rawCertificate ) override;
+ virtual css::uno::Reference< css::security::XCertificate > SAL_CALL createCertificateFromAscii(
+ const OUString& asciiCertificate ) override;
+
+ GpgME::Context& getGpgContext() { return *m_ctx; }
+ virtual css::uno::Sequence< css::uno::Reference< css::security::XCertificate > > SAL_CALL getAllCertificates() override;
+
+private:
+ css::uno::Sequence< css::uno::Reference< css::security::XCertificate > > getCertificatesImpl( bool bPrivateOnly );
+} ;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/XMLEncryption.cxx b/xmlsecurity/source/gpg/XMLEncryption.cxx
new file mode 100644
index 000000000..65083ff68
--- /dev/null
+++ b/xmlsecurity/source/gpg/XMLEncryption.cxx
@@ -0,0 +1,37 @@
+/* -*- 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 "XMLEncryption.hxx"
+
+using namespace css::uno;
+using namespace css::lang;
+using namespace css::xml::wrapper;
+using namespace css::xml::crypto;
+
+XMLEncryptionGpg::XMLEncryptionGpg() {
+}
+
+XMLEncryptionGpg::~XMLEncryptionGpg() {
+}
+
+/* XXMLEncryption */
+Reference< XXMLEncryptionTemplate > SAL_CALL XMLEncryptionGpg::encrypt(const Reference< XXMLEncryptionTemplate >& /*aTemplate*/,
+ const Reference< XSecurityEnvironment >& /*aEnvironment*/)
+{
+ return nullptr;
+}
+
+/* XXMLEncryption */
+Reference< XXMLEncryptionTemplate > SAL_CALL XMLEncryptionGpg::decrypt(const Reference< XXMLEncryptionTemplate >& /*aTemplate*/,
+ const Reference< XXMLSecurityContext >& /*aSecurityCtx*/)
+{
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/XMLEncryption.hxx b/xmlsecurity/source/gpg/XMLEncryption.hxx
new file mode 100644
index 000000000..4ff47ccc4
--- /dev/null
+++ b/xmlsecurity/source/gpg/XMLEncryption.hxx
@@ -0,0 +1,37 @@
+/* -*- 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 <sal/config.h>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/xml/crypto/XXMLEncryption.hpp>
+
+namespace com::sun::star::xml::crypto { class XXMLEncryptionTemplate; }
+namespace com::sun::star::xml::crypto { class XXMLSecurityContext; }
+
+class XMLEncryptionGpg : public cppu::WeakImplHelper< css::xml::crypto::XXMLEncryption >
+{
+public:
+ explicit XMLEncryptionGpg();
+ virtual ~XMLEncryptionGpg() override;
+
+ // XXMLEncryption
+ virtual css::uno::Reference< css::xml::crypto::XXMLEncryptionTemplate > SAL_CALL encrypt(
+ const css::uno::Reference< css::xml::crypto::XXMLEncryptionTemplate >& aTemplate,
+ const css::uno::Reference< css::xml::crypto::XSecurityEnvironment >& aEnvironment) override;
+
+ virtual css::uno::Reference< css::xml::crypto::XXMLEncryptionTemplate > SAL_CALL decrypt(
+ const css::uno::Reference< css::xml::crypto::XXMLEncryptionTemplate >& aTemplate,
+ const css::uno::Reference< css::xml::crypto::XXMLSecurityContext >& aContext) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/XMLSecurityContext.cxx b/xmlsecurity/source/gpg/XMLSecurityContext.cxx
new file mode 100644
index 000000000..1fb407113
--- /dev/null
+++ b/xmlsecurity/source/gpg/XMLSecurityContext.cxx
@@ -0,0 +1,87 @@
+/* -*- 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 "XMLSecurityContext.hxx"
+
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/safeint.hxx>
+
+using namespace css::uno;
+using namespace css::lang;
+using namespace css::xml::crypto;
+
+XMLSecurityContextGpg::XMLSecurityContextGpg()
+ : m_nDefaultEnvIndex(-1)
+{
+}
+
+XMLSecurityContextGpg::~XMLSecurityContextGpg() {}
+
+sal_Int32 SAL_CALL XMLSecurityContextGpg::addSecurityEnvironment(
+ const Reference<XSecurityEnvironment>& aSecurityEnvironment)
+{
+ if (!aSecurityEnvironment.is())
+ throw RuntimeException("Invalid SecurityEnvironment given!");
+
+ m_vSecurityEnvironments.push_back(aSecurityEnvironment);
+ return m_vSecurityEnvironments.size() - 1;
+}
+
+sal_Int32 SAL_CALL XMLSecurityContextGpg::getSecurityEnvironmentNumber()
+{
+ return m_vSecurityEnvironments.size();
+}
+
+Reference<XSecurityEnvironment>
+ SAL_CALL XMLSecurityContextGpg::getSecurityEnvironmentByIndex(sal_Int32 index)
+{
+ if (index < 0 || o3tl::make_unsigned(index) >= m_vSecurityEnvironments.size())
+ throw RuntimeException("Invalid index");
+
+ return m_vSecurityEnvironments[index];
+}
+
+Reference<XSecurityEnvironment> SAL_CALL XMLSecurityContextGpg::getSecurityEnvironment()
+{
+ if (m_nDefaultEnvIndex < 0
+ || o3tl::make_unsigned(m_nDefaultEnvIndex) >= m_vSecurityEnvironments.size())
+ throw RuntimeException("Invalid index");
+
+ return getSecurityEnvironmentByIndex(m_nDefaultEnvIndex);
+}
+
+sal_Int32 SAL_CALL XMLSecurityContextGpg::getDefaultSecurityEnvironmentIndex()
+{
+ return m_nDefaultEnvIndex;
+}
+
+void SAL_CALL XMLSecurityContextGpg::setDefaultSecurityEnvironmentIndex(sal_Int32 nDefaultEnvIndex)
+{
+ m_nDefaultEnvIndex = nDefaultEnvIndex;
+}
+
+/* XServiceInfo */
+OUString SAL_CALL XMLSecurityContextGpg::getImplementationName()
+{
+ return "com.sun.star.xml.security.gpg.XMLSecurityContext_GpgImpl";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL XMLSecurityContextGpg::supportsService(const OUString& serviceName)
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+/* XServiceInfo */
+Sequence<OUString> SAL_CALL XMLSecurityContextGpg::getSupportedServiceNames()
+{
+ return { OUString("com.sun.star.xml.crypto.XMLSecurityContext") };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/XMLSecurityContext.hxx b/xmlsecurity/source/gpg/XMLSecurityContext.hxx
new file mode 100644
index 000000000..e3dea7310
--- /dev/null
+++ b/xmlsecurity/source/gpg/XMLSecurityContext.hxx
@@ -0,0 +1,58 @@
+/* -*- 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 <sal/config.h>
+#include <rtl/ustring.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/xml/crypto/XXMLSecurityContext.hpp>
+
+#include <vector>
+
+namespace com::sun::star::xml::crypto { class XSecurityEnvironment; }
+
+class XMLSecurityContextGpg : public cppu::WeakImplHelper< css::xml::crypto::XXMLSecurityContext,
+ css::lang::XServiceInfo>
+{
+private:
+ std::vector< css::uno::Reference< css::xml::crypto::XSecurityEnvironment > > m_vSecurityEnvironments;
+
+ sal_Int32 m_nDefaultEnvIndex;
+
+public:
+ XMLSecurityContextGpg();
+ virtual ~XMLSecurityContextGpg() override;
+
+ // XXMLSecurityContext
+ virtual sal_Int32 SAL_CALL addSecurityEnvironment(
+ const css::uno::Reference< css::xml::crypto::XSecurityEnvironment >& aSecurityEnvironment) override;
+
+ virtual ::sal_Int32 SAL_CALL getSecurityEnvironmentNumber() override;
+
+ virtual css::uno::Reference<css::xml::crypto::XSecurityEnvironment > SAL_CALL getSecurityEnvironmentByIndex(sal_Int32 index) override;
+
+ virtual css::uno::Reference<css::xml::crypto::XSecurityEnvironment > SAL_CALL getSecurityEnvironment() override;
+
+ virtual sal_Int32 SAL_CALL getDefaultSecurityEnvironmentIndex() override;
+
+ virtual void SAL_CALL setDefaultSecurityEnvironmentIndex( sal_Int32 nDefaultEnvIndex ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override ;
+
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override ;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override ;
+} ;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/gpg/xmlsignature_gpgimpl.cxx b/xmlsecurity/source/gpg/xmlsignature_gpgimpl.cxx
new file mode 100644
index 000000000..3b4e138ae
--- /dev/null
+++ b/xmlsecurity/source/gpg/xmlsignature_gpgimpl.cxx
@@ -0,0 +1,527 @@
+/* -*- 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 <sal/config.h>
+#include <sal/log.hxx>
+#include <xmlsec-wrapper.h>
+#include <cppuhelper/supportsservice.hxx>
+#include <gpg/xmlsignature_gpgimpl.hxx>
+
+#if defined _MSC_VER && defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundef"
+#endif
+#include <gpgme.h>
+#if defined _MSC_VER && defined __clang__
+#pragma clang diagnostic pop
+#endif
+#include <context.h>
+#include <key.h>
+#include <data.h>
+#include <signingresult.h>
+#include <importresult.h>
+
+#include <xmlelementwrapper_xmlsecimpl.hxx>
+#include <xmlsec/xmlstreamio.hxx>
+#include <xmlsec/errorcallback.hxx>
+
+#include "SecurityEnvironment.hxx"
+
+using namespace css::uno;
+using namespace css::lang;
+using namespace css::xml::wrapper;
+using namespace css::xml::crypto;
+
+XMLSignature_GpgImpl::XMLSignature_GpgImpl() {
+}
+
+XMLSignature_GpgImpl::~XMLSignature_GpgImpl() {
+}
+
+/* XXMLSignature */
+Reference< XXMLSignatureTemplate >
+SAL_CALL XMLSignature_GpgImpl::generate(
+ const Reference< XXMLSignatureTemplate >& aTemplate ,
+ const Reference< XSecurityEnvironment >& aEnvironment
+)
+{
+ xmlSecDSigCtxPtr pDsigCtx = nullptr ;
+ xmlNodePtr pNode = nullptr ;
+
+ if( !aTemplate.is() )
+ throw RuntimeException() ;
+
+ if( !aEnvironment.is() )
+ throw RuntimeException() ;
+
+ //Get the xml node
+ Reference< XXMLElementWrapper > xElement = aTemplate->getTemplate() ;
+ if( !xElement.is() ) {
+ throw RuntimeException() ;
+ }
+
+ XMLElementWrapper_XmlSecImpl* pElement =
+ dynamic_cast<XMLElementWrapper_XmlSecImpl*>(xElement.get());
+ if( pElement == nullptr ) {
+ throw RuntimeException() ;
+ }
+
+ pNode = pElement->getNativeElement() ;
+
+ //Get the stream/URI binding
+ Reference< XUriBinding > xUriBinding = aTemplate->getBinding() ;
+ if( xUriBinding.is() ) {
+ //Register the stream input callbacks into libxml2
+ if( xmlRegisterStreamInputCallbacks( xUriBinding ) < 0 )
+ throw RuntimeException() ;
+ }
+
+ //Get Keys Manager
+ SecurityEnvironmentGpg* pSecEnv =
+ dynamic_cast<SecurityEnvironmentGpg*>(aEnvironment.get());
+ if( pSecEnv == nullptr )
+ throw RuntimeException() ;
+
+ setErrorRecorder();
+
+ //Create Signature context
+ pDsigCtx = xmlSecDSigCtxCreate( nullptr ) ;
+ if( pDsigCtx == nullptr )
+ {
+ clearErrorRecorder();
+ return aTemplate;
+ }
+
+ // set intended operation to sign - several asserts inside libxmlsec
+ // wanting that for digest / transforms
+ pDsigCtx->operation = xmlSecTransformOperationSign;
+
+ // we default to SHA512 for all digests - nss crypto does not have it...
+ //pDsigCtx->defDigestMethodId = xmlSecTransformSha512Id;
+
+ // Calculate digest for all references
+ xmlNodePtr cur = xmlSecGetNextElementNode(pNode->children);
+ if( cur != nullptr )
+ cur = xmlSecGetNextElementNode(cur->children);
+ while( cur != nullptr )
+ {
+ // some of those children I suppose should be reference elements
+ if( xmlSecCheckNodeName(cur, xmlSecNodeReference, xmlSecDSigNs) )
+ {
+ xmlSecDSigReferenceCtxPtr pDsigRefCtx =
+ xmlSecDSigReferenceCtxCreate(pDsigCtx,
+ xmlSecDSigReferenceOriginSignedInfo);
+ if(pDsigRefCtx == nullptr)
+ throw RuntimeException();
+
+ // add this one to the list
+ if( xmlSecPtrListAdd(&(pDsigCtx->signedInfoReferences),
+ pDsigRefCtx) < 0 )
+ {
+ // TODO resource handling
+ xmlSecDSigReferenceCtxDestroy(pDsigRefCtx);
+ throw RuntimeException();
+ }
+
+ if( xmlSecDSigReferenceCtxProcessNode(pDsigRefCtx, cur) < 0 )
+ throw RuntimeException();
+
+ // final check - all good?
+ if(pDsigRefCtx->status != xmlSecDSigStatusSucceeded)
+ {
+ pDsigCtx->status = xmlSecDSigStatusInvalid;
+ return aTemplate; // TODO - harder error?
+ }
+ }
+
+ cur = xmlSecGetNextElementNode(cur->next);
+ }
+
+ // get me a digestible buffer from the signature template!
+ // -------------------------------------------------------
+
+ // run the transformations over SignedInfo element (first child of
+ // pNode)
+ xmlSecNodeSetPtr nodeset = nullptr;
+ cur = xmlSecGetNextElementNode(pNode->children);
+ // TODO assert that...
+ nodeset = xmlSecNodeSetGetChildren(pNode->doc, cur, 1, 0);
+ if(nodeset == nullptr)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ if( xmlSecTransformCtxXmlExecute(&(pDsigCtx->transformCtx), nodeset) < 0 )
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ // now extract the keyid from PGPData
+ // walk xml tree to PGPData node - go to children, first is
+ // SignedInfo, 2nd is signaturevalue, 3rd is KeyInfo
+ // 1st child is PGPData, 1st grandchild is PGPKeyID
+ cur = xmlSecGetNextElementNode(pNode->children);
+ // TODO error handling
+ cur = xmlSecGetNextElementNode(cur->next);
+ cur = xmlSecGetNextElementNode(cur->next);
+ cur = xmlSecGetNextElementNode(cur->children);
+ // check that this is now PGPData
+ if(!xmlSecCheckNodeName(cur, xmlSecNodePGPData, xmlSecDSigNs))
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ // check that this is now PGPKeyID
+ cur = xmlSecGetNextElementNode(cur->children);
+ static const xmlChar xmlSecNodePGPKeyID[] = "PGPKeyID";
+ if(!xmlSecCheckNodeName(cur, xmlSecNodePGPKeyID, xmlSecDSigNs))
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ GpgME::Context& rCtx=pSecEnv->getGpgContext();
+ rCtx.setKeyListMode(GPGME_KEYLIST_MODE_LOCAL);
+ GpgME::Error err;
+ xmlChar* pKey=xmlNodeGetContent(cur);
+ if(xmlSecBase64Decode(pKey, reinterpret_cast<xmlSecByte*>(pKey), xmlStrlen(pKey)) < 0)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ if( rCtx.addSigningKey(
+ rCtx.key(
+ reinterpret_cast<char*>(pKey), err, true)) )
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ xmlFree(pKey);
+
+ // good, ctx is setup now, let's sign the lot
+ GpgME::Data data_in(
+ reinterpret_cast<char*>(xmlSecBufferGetData(pDsigCtx->transformCtx.result)),
+ xmlSecBufferGetSize(pDsigCtx->transformCtx.result), false);
+ GpgME::Data data_out;
+
+ SAL_INFO("xmlsecurity.xmlsec.gpg", "Generating signature for: " << xmlSecBufferGetData(pDsigCtx->transformCtx.result));
+
+ // we base64-encode anyway
+ rCtx.setArmor(false);
+ GpgME::SigningResult sign_res=rCtx.sign(data_in, data_out,
+ GpgME::Detached);
+ off_t result = data_out.seek(0,SEEK_SET);
+ (void) result;
+ assert(result == 0);
+ int len=0, curr=0; char buf;
+ while( (curr=data_out.read(&buf, 1)) )
+ len += curr;
+
+ if(sign_res.error() || !len)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ // write signed data to xml
+ xmlChar* signature = static_cast<xmlChar*>(xmlMalloc(len + 1));
+ if(signature == nullptr)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ result = data_out.seek(0,SEEK_SET);
+ assert(result == 0);
+ if( data_out.read(signature, len) != len )
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ // conversion to base64
+ xmlChar* signatureEncoded=nullptr;
+ if( !(signatureEncoded=xmlSecBase64Encode(reinterpret_cast<xmlSecByte*>(signature), len, 79)) )
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ xmlFree(signature);
+
+ // walk xml tree to sign value node - go to children, first is
+ // SignedInfo, 2nd is signaturevalue
+ cur = xmlSecGetNextElementNode(pNode->children);
+ cur = xmlSecGetNextElementNode(cur->next);
+
+ // TODO some assert would be good...
+ xmlNodeSetContentLen(cur, signatureEncoded, xmlStrlen(signatureEncoded));
+ xmlFree(signatureEncoded);
+
+ aTemplate->setStatus(SecurityOperationStatus_OPERATION_SUCCEEDED);
+
+ // done
+ xmlSecDSigCtxDestroy( pDsigCtx ) ;
+
+ //Unregistered the stream/URI binding
+ if( xUriBinding.is() )
+ xmlUnregisterStreamInputCallbacks() ;
+
+ clearErrorRecorder();
+ return aTemplate ;
+}
+
+/* XXMLSignature */
+Reference< XXMLSignatureTemplate >
+SAL_CALL XMLSignature_GpgImpl::validate(
+ const Reference< XXMLSignatureTemplate >& aTemplate ,
+ const Reference< XXMLSecurityContext >& aSecurityCtx
+) {
+ xmlSecDSigCtxPtr pDsigCtx = nullptr ;
+ xmlNodePtr pNode = nullptr ;
+
+ if( !aTemplate.is() )
+ throw RuntimeException() ;
+
+ if( !aSecurityCtx.is() )
+ throw RuntimeException() ;
+
+ //Get the xml node
+ Reference< XXMLElementWrapper > xElement = aTemplate->getTemplate() ;
+ if( !xElement.is() )
+ throw RuntimeException() ;
+
+ XMLElementWrapper_XmlSecImpl* pElement =
+ dynamic_cast<XMLElementWrapper_XmlSecImpl*>(xElement.get());
+ if( pElement == nullptr )
+ throw RuntimeException() ;
+
+ pNode = pElement->getNativeElement() ;
+
+ //Get the stream/URI binding
+ Reference< XUriBinding > xUriBinding = aTemplate->getBinding() ;
+ if( xUriBinding.is() ) {
+ //Register the stream input callbacks into libxml2
+ if( xmlRegisterStreamInputCallbacks( xUriBinding ) < 0 )
+ throw RuntimeException() ;
+ }
+
+ setErrorRecorder();
+
+ sal_Int32 nSecurityEnvironment = aSecurityCtx->getSecurityEnvironmentNumber();
+ sal_Int32 i;
+
+ for (i=0; i<nSecurityEnvironment; ++i)
+ {
+ Reference< XSecurityEnvironment > aEnvironment = aSecurityCtx->getSecurityEnvironmentByIndex(i);
+
+ SecurityEnvironmentGpg* pSecEnv =
+ dynamic_cast<SecurityEnvironmentGpg*>(aEnvironment.get());
+ if( pSecEnv == nullptr )
+ throw RuntimeException() ;
+
+ // TODO figure out key from pSecEnv!
+ // unclear how/where that is transported in nss impl...
+
+ //Create Signature context
+ pDsigCtx = xmlSecDSigCtxCreate( nullptr ) ;
+ if( pDsigCtx == nullptr )
+ {
+ clearErrorRecorder();
+ return aTemplate;
+ }
+
+ // set intended operation to verify - several asserts inside libxmlsec
+ // wanting that for digest / transforms
+ pDsigCtx->operation = xmlSecTransformOperationVerify;
+
+ // reset status - to be set later
+ pDsigCtx->status = xmlSecDSigStatusUnknown;
+
+ // get me a digestible buffer from the SignatureInfo node!
+ // -------------------------------------------------------
+
+ // run the transformations - first child node is required to
+ // be SignatureInfo
+ xmlSecNodeSetPtr nodeset = nullptr;
+ xmlNodePtr cur = xmlSecGetNextElementNode(pNode->children);
+ // TODO assert that...
+ nodeset = xmlSecNodeSetGetChildren(pNode->doc, cur, 1, 0);
+ if(nodeset == nullptr)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ // TODO assert we really have the SignatureInfo here?
+ if( xmlSecTransformCtxXmlExecute(&(pDsigCtx->transformCtx), nodeset) < 0 )
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ // Validate the template via gpgme
+ GpgME::Context& rCtx=pSecEnv->getGpgContext();
+
+ GpgME::Data data_text(
+ reinterpret_cast<char*>(xmlSecBufferGetData(pDsigCtx->transformCtx.result)),
+ xmlSecBufferGetSize(pDsigCtx->transformCtx.result), false);
+
+ SAL_INFO("xmlsecurity.xmlsec.gpg", "Validating SignatureInfo: " << xmlSecBufferGetData(pDsigCtx->transformCtx.result));
+
+ // walk xml tree to sign value node - go to children, first is
+ // SignedInfo, 2nd is signaturevalue
+ cur = xmlSecGetNextElementNode(pNode->children);
+ cur = xmlSecGetNextElementNode(cur->next);
+
+ if(!xmlSecCheckNodeName(cur, xmlSecNodeSignatureValue, xmlSecDSigNs))
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ xmlChar* pSignatureValue=xmlNodeGetContent(cur);
+ int nSigSize = xmlSecBase64Decode(pSignatureValue, reinterpret_cast<xmlSecByte*>(pSignatureValue), xmlStrlen(pSignatureValue));
+ if( nSigSize < 0)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ GpgME::Data data_signature(
+ reinterpret_cast<char*>(pSignatureValue),
+ nSigSize, false);
+
+ GpgME::VerificationResult verify_res=rCtx.verifyDetachedSignature(
+ data_signature, data_text);
+
+ // TODO: needs some more error handling, needs checking _all_ signatures
+ if( verify_res.isNull() || verify_res.numSignatures() == 0
+ // there is at least 1 signature and it is anything else than fully valid
+ || ( (verify_res.numSignatures() > 0)
+ && verify_res.signature(0).status().encodedError() > 0 ) )
+ {
+ // let's try again, but this time import the public key
+ // payload (avoiding that in a first cut for being a bit
+ // speedier. also prevents all too easy poisoning/sha1
+ // fingerprint collision attacks)
+
+ // walk xml tree to PGPData node - go to children, first is
+ // SignedInfo, 2nd is signaturevalue, 3rd is KeyInfo
+ // 1st child is PGPData, 1st or 2nd grandchild is PGPKeyPacket
+ cur = xmlSecGetNextElementNode(pNode->children);
+ // TODO error handling
+ cur = xmlSecGetNextElementNode(cur->next);
+ cur = xmlSecGetNextElementNode(cur->next);
+ cur = xmlSecGetNextElementNode(cur->children);
+ // check that this is now PGPData
+ if(!xmlSecCheckNodeName(cur, xmlSecNodePGPData, xmlSecDSigNs))
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ // check that this is now PGPKeyPacket
+ cur = xmlSecGetNextElementNode(cur->children);
+ static const xmlChar xmlSecNodePGPKeyPacket[] = "PGPKeyPacket";
+ if(!xmlSecCheckNodeName(cur, xmlSecNodePGPKeyPacket, xmlSecDSigNs))
+ {
+ // not this one, maybe the next?
+ cur = xmlSecGetNextElementNode(cur->next);
+ if(!xmlSecCheckNodeName(cur, xmlSecNodePGPKeyPacket, xmlSecDSigNs))
+ {
+ // ok, giving up
+ clearErrorRecorder();
+ xmlFree(pSignatureValue);
+
+ return aTemplate;
+ }
+ }
+
+ // got a key packet, import & re-validate
+ xmlChar* pKeyPacket=xmlNodeGetContent(cur);
+ int nKeyLen = xmlSecBase64Decode(pKeyPacket, reinterpret_cast<xmlSecByte*>(pKeyPacket), xmlStrlen(pKeyPacket));
+ if( nKeyLen < 0)
+ throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ GpgME::Data data_key(
+ reinterpret_cast<char*>(pKeyPacket),
+ nKeyLen, false);
+
+ rCtx.importKeys(data_key);
+ xmlFree(pKeyPacket);
+
+ // and re-run (rewind text and signature streams to position 0)
+ (void)data_text.seek(0,SEEK_SET);
+ (void)data_signature.seek(0,SEEK_SET);
+ verify_res=rCtx.verifyDetachedSignature(data_signature, data_text);
+
+ // TODO: needs some more error handling, needs checking _all_ signatures
+ if( verify_res.isNull() || verify_res.numSignatures() == 0
+ // there is at least 1 signature and it is anything else than valid
+ || ( (verify_res.numSignatures() > 0)
+ && verify_res.signature(0).status().encodedError() > 0 ) )
+ {
+ clearErrorRecorder();
+ xmlFree(pSignatureValue);
+
+ return aTemplate;
+ }
+ }
+
+ xmlFree(pSignatureValue);
+
+ // now verify digest for all references
+ cur = xmlSecGetNextElementNode(pNode->children);
+ if( cur != nullptr )
+ cur = xmlSecGetNextElementNode(cur->children);
+ while( cur != nullptr )
+ {
+ // some of those children I suppose should be reference elements
+ if( xmlSecCheckNodeName(cur, xmlSecNodeReference, xmlSecDSigNs) )
+ {
+ xmlSecDSigReferenceCtxPtr pDsigRefCtx =
+ xmlSecDSigReferenceCtxCreate(pDsigCtx,
+ xmlSecDSigReferenceOriginSignedInfo);
+ if(pDsigRefCtx == nullptr)
+ throw RuntimeException();
+
+ // add this one to the list
+ if( xmlSecPtrListAdd(&(pDsigCtx->signedInfoReferences),
+ pDsigRefCtx) < 0 )
+ {
+ // TODO resource handling
+ xmlSecDSigReferenceCtxDestroy(pDsigRefCtx);
+ throw RuntimeException();
+ }
+
+ if( xmlSecDSigReferenceCtxProcessNode(pDsigRefCtx, cur) < 0 )
+ throw RuntimeException();
+
+ // final check - all good?
+ if(pDsigRefCtx->status != xmlSecDSigStatusSucceeded)
+ {
+ pDsigCtx->status = xmlSecDSigStatusInvalid;
+ return aTemplate; // TODO - harder error?
+ }
+ }
+
+ cur = xmlSecGetNextElementNode(cur->next);
+ }
+
+ // TODO - also verify manifest (only relevant for ooxml)?
+ aTemplate->setStatus(SecurityOperationStatus_OPERATION_SUCCEEDED);
+
+ // done
+ xmlSecDSigCtxDestroy( pDsigCtx ) ;
+ }
+
+ //Unregistered the stream/URI binding
+ if( xUriBinding.is() )
+ xmlUnregisterStreamInputCallbacks() ;
+
+ clearErrorRecorder();
+ return aTemplate ;
+}
+
+/* XServiceInfo */
+OUString SAL_CALL XMLSignature_GpgImpl::getImplementationName() {
+ return impl_getImplementationName() ;
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL XMLSignature_GpgImpl::supportsService( const OUString& serviceName) {
+ return cppu::supportsService(this, serviceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL XMLSignature_GpgImpl::getSupportedServiceNames() {
+ return impl_getSupportedServiceNames() ;
+}
+
+//Helper for XServiceInfo
+Sequence< OUString > XMLSignature_GpgImpl::impl_getSupportedServiceNames() {
+ Sequence<OUString> seqServiceNames { "com.sun.star.xml.crypto.XMLSignature" };
+ return seqServiceNames ;
+}
+
+OUString XMLSignature_GpgImpl::impl_getImplementationName() {
+ return "com.sun.star.xml.security.bridge.xmlsec.XMLSignature_GpgImpl" ;
+}
+
+//Helper for registry
+Reference< XInterface > XMLSignature_GpgImpl::impl_createInstance( const Reference< XMultiServiceFactory >& ) {
+ return Reference< XInterface >( *new XMLSignature_GpgImpl ) ;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */