summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThorsten Behrens <Thorsten.Behrens@CIB.de>2017-08-20 03:38:05 +0200
committerThorsten Behrens <Thorsten.Behrens@CIB.de>2017-12-07 16:17:10 +0100
commit688eefd6b21154b18ea3652e52fcbcd653e455ef (patch)
tree629c0a5efe5bf179cac817f2dda54ad8b93f4b71
parentee0f1c7971ff06969ee3e6fd567e202377c5d616 (diff)
gpg4libre: [API CHANGE] add storage helper for GPG encryption data
OpenPGP encryption needs to pass down slightly different meta data to package / zip storage. Change-Id: Idba9ad7a821cb33070cf5e5a0f79ae55db99b276
-rw-r--r--comphelper/Library_comphelper.mk1
-rw-r--r--comphelper/source/misc/storagehelper.cxx111
-rw-r--r--offapi/com/sun/star/embed/XEncryptionProtectedStorage.idl31
-rw-r--r--package/inc/PackageConstants.hxx1
-rw-r--r--package/inc/ZipPackage.hxx1
-rw-r--r--package/source/xstor/xstorage.cxx60
-rw-r--r--package/source/xstor/xstorage.hxx1
-rw-r--r--package/source/zippackage/ZipPackage.cxx25
8 files changed, 216 insertions, 15 deletions
diff --git a/comphelper/Library_comphelper.mk b/comphelper/Library_comphelper.mk
index e997bd5e6f32..4a24d65da15c 100644
--- a/comphelper/Library_comphelper.mk
+++ b/comphelper/Library_comphelper.mk
@@ -37,6 +37,7 @@ $(eval $(call gb_Library_add_defs,comphelper,\
))
$(eval $(call gb_Library_use_externals,comphelper,\
+ gpgmepp \
boost_headers \
icuuc \
icu_headers \
diff --git a/comphelper/source/misc/storagehelper.cxx b/comphelper/source/misc/storagehelper.cxx
index 166955d3b226..a05fb1358f3f 100644
--- a/comphelper/source/misc/storagehelper.cxx
+++ b/comphelper/source/misc/storagehelper.cxx
@@ -19,6 +19,7 @@
#include <com/sun/star/embed/ElementModes.hpp>
#include <com/sun/star/embed/XEncryptionProtectedSource2.hpp>
+#include <com/sun/star/embed/XEncryptionProtectedStorage.hpp>
#include <com/sun/star/embed/XStorage.hpp>
#include <com/sun/star/embed/XTransactedObject.hpp>
#include <com/sun/star/embed/StorageFactory.hpp>
@@ -43,6 +44,7 @@
#include <rtl/random.h>
#include <osl/time.h>
#include <osl/diagnose.h>
+#include <sax/tools/converter.hxx>
#include <ucbhelper/content.hxx>
@@ -50,6 +52,15 @@
#include <comphelper/processfactory.hxx>
#include <comphelper/documentconstants.hxx>
#include <comphelper/storagehelper.hxx>
+#include <comphelper/sequence.hxx>
+
+#if GPGME_HAVE_GPGME
+# include <gpgme.h>
+# include <context.h>
+# include <encryptionresult.h>
+# include <key.h>
+# include <data.h>
+#endif
using namespace ::com::sun::star;
@@ -194,11 +205,21 @@ void OStorageHelper::SetCommonStorageEncryptionData(
const uno::Reference< embed::XStorage >& xStorage,
const uno::Sequence< beans::NamedValue >& aEncryptionData )
{
- uno::Reference< embed::XEncryptionProtectedSource2 > xEncrSet( xStorage, uno::UNO_QUERY );
+ uno::Reference< embed::XEncryptionProtectedStorage > xEncrSet( xStorage, uno::UNO_QUERY );
if ( !xEncrSet.is() )
throw io::IOException(); // TODO
- xEncrSet->setEncryptionData( aEncryptionData );
+ if ( aEncryptionData.getLength() == 2 &&
+ aEncryptionData[0].Name == "GpgInfos" &&
+ aEncryptionData[1].Name == "EncryptionKey" )
+ {
+ xEncrSet->setGpgProperties(
+ aEncryptionData[0].Value.get< uno::Sequence< uno::Sequence< beans::NamedValue > > >() );
+ xEncrSet->setEncryptionData(
+ aEncryptionData[1].Value.get< uno::Sequence< beans::NamedValue > >() );
+ }
+ else
+ xEncrSet->setEncryptionData( aEncryptionData );
}
@@ -409,6 +430,7 @@ uno::Sequence< beans::NamedValue > OStorageHelper::CreatePackageEncryptionData(
uno::Sequence< beans::NamedValue > OStorageHelper::CreateGpgPackageEncryptionData()
{
+#if GPGME_HAVE_GPGME
// generate session key
// --------------------
@@ -425,34 +447,95 @@ uno::Sequence< beans::NamedValue > OStorageHelper::CreateGpgPackageEncryptionDat
rtl_random_destroyPool(aRandomPool);
uno::Sequence< beans::NamedValue > aContainer(2);
- uno::Sequence< beans::NamedValue > aGpgEncryptionData(3);
+ std::vector< uno::Sequence< beans::NamedValue > > aGpgEncryptions;
+ uno::Sequence< beans::NamedValue > aGpgEncryptionEntry(3);
uno::Sequence< beans::NamedValue > aEncryptionData(1);
- // TODO fire certificate chooser dialog
uno::Reference< security::XDocumentDigitalSignatures > xSigner(
security::DocumentDigitalSignatures::createWithVersion(
comphelper::getProcessComponentContext(), "1.2" ) );
- // The user may provide a description while choosing a certificate.
- OUString aDescription;
- uno::Reference< security::XCertificate > xSignCertificate=
- xSigner->chooseEncryptionCertificate(aDescription);
+ // fire up certificate chooser dialog - user can multi-select!
+ uno::Sequence< uno::Reference< security::XCertificate > > xSignCertificates=
+ xSigner->chooseEncryptionCertificate();
+
+ // generate one encrypted key entry for each recipient
+ // ---------------------------------------------------
+
+ std::unique_ptr<GpgME::Context> ctx;
+ GpgME::Error err = GpgME::checkEngine(GpgME::OpenPGP);
+ if (err)
+ throw uno::RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ ctx.reset( GpgME::Context::createForProtocol(GpgME::OpenPGP) );
+ if (ctx == nullptr)
+ throw uno::RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ ctx->setArmor(false);
- uno::Sequence < sal_Int8 > aKeyID;
- if (xSignCertificate.is())
+ // TODO: add self-encryption key from user config
+ const uno::Reference< security::XCertificate >* pCerts=xSignCertificates.getConstArray();
+ for (sal_uInt32 i = 0, nNum = xSignCertificates.getLength(); i < nNum; i++, pCerts++)
{
- aKeyID = xSignCertificate->getSHA1Thumbprint();
+ uno::Sequence < sal_Int8 > aKeyID;
+ if (pCerts->is())
+ aKeyID = (*pCerts)->getSHA256Thumbprint();
+
+ std::vector<GpgME::Key> keys;
+ keys.push_back(
+ ctx->key(
+ reinterpret_cast<const char*>(aKeyID.getConstArray()),
+ err, true));
+
+ // ctx is setup now, let's encrypt the lot!
+ GpgME::Data plain(
+ reinterpret_cast<const char*>(aVector.getConstArray()),
+ aVector.getLength(), false);
+ GpgME::Data cipher;
+
+ GpgME::EncryptionResult crypt_res = ctx->encrypt(
+ keys, plain,
+ cipher, GpgME::Context::NoCompress);
+
+ off_t result = cipher.seek(0,SEEK_SET);
+ (void) result;
+ assert(result == 0);
+ int len=0, curr=0; char buf;
+ while( (curr=cipher.read(&buf, 1)) )
+ len += curr;
+
+ if(crypt_res.error() || !len)
+ throw uno::RuntimeException("The GpgME library failed to encrypt.");
+
+ uno::Sequence < sal_Int8 > aCipherValue(len);
+ result = cipher.seek(0,SEEK_SET);
+ assert(result == 0);
+ if( cipher.read(aCipherValue.getArray(), len) != len )
+ throw uno::RuntimeException("The GpgME library failed to read the encrypted value.");
+
+ SAL_INFO("comphelper.crypto", "Generated gpg crypto of length: " << len);
+
+ aGpgEncryptionEntry[0].Name = "KeyId";
+ aGpgEncryptionEntry[0].Value <<= aKeyID;
+ aGpgEncryptionEntry[1].Name = "KeyPacket";
+ aGpgEncryptionEntry[1].Value <<= aKeyID;
+ aGpgEncryptionEntry[2].Name = "CipherValue";
+ aGpgEncryptionEntry[2].Value <<= aCipherValue;
+
+ aGpgEncryptions.push_back(aGpgEncryptionEntry);
}
- aGpgEncryptionData[0].Name = "KeyId";
- aGpgEncryptionData[0].Value <<= aKeyID;
+ aEncryptionData[0].Name = PACKAGE_ENCRYPTIONDATA_SHA256UTF8;
+ aEncryptionData[0].Value <<= aVector;
aContainer[0].Name = "GpgInfos";
- aContainer[0].Value <<= aGpgEncryptionData;
+ aContainer[0].Value <<= comphelper::containerToSequence(aGpgEncryptions);
aContainer[1].Name = "EncryptionKey";
aContainer[1].Value <<= aEncryptionData;
return aContainer;
+#else
+ return uno::Sequence< beans::NamedValue >();
+#endif
}
bool OStorageHelper::IsValidZipEntryFileName( const OUString& aName, bool bSlashAllowed )
diff --git a/offapi/com/sun/star/embed/XEncryptionProtectedStorage.idl b/offapi/com/sun/star/embed/XEncryptionProtectedStorage.idl
index 5ddcc6831844..ecba64d51995 100644
--- a/offapi/com/sun/star/embed/XEncryptionProtectedStorage.idl
+++ b/offapi/com/sun/star/embed/XEncryptionProtectedStorage.idl
@@ -84,6 +84,37 @@ interface XEncryptionProtectedStorage: XEncryptionProtectedSource2
/** allows to get the encryption algorithms of the object.
*/
sequence< ::com::sun::star::beans::NamedValue > getEncryptionAlgorithms();
+
+ /** set OpenPGP-specific encryption properties
+
+ <p>
+ When provided, switch ODF package encryption to OpenPGP.
+ </p>
+ <p>
+ For each recipient, add one sequence of named values, each of
+ the same structure. The following values could be part of that
+ provided sequence:
+ </p>
+ <dl>
+ <dt>KeyId</dt>
+ <dd>
+ specifies OpenPGP key ID or fingerprint of the public
+ key used to encrypt this session key against
+ </dd>
+ <dt>KeyPacket</dt>
+ <dd>
+ (optional) public key packet of the key used to encrypt
+ </dd>
+ <dt>CipherValue</dt>
+ <dd>
+ OpenPGP-encrypted session key for this recipient
+ </dd>
+ </dl>
+
+ @since LibreOffice 6.0
+ */
+ void setGpgProperties( [in] sequence< sequence< ::com::sun::star::beans::NamedValue > > aProps )
+ raises( ::com::sun::star::lang::IllegalArgumentException );
};
diff --git a/package/inc/PackageConstants.hxx b/package/inc/PackageConstants.hxx
index df7bebcf6bd4..b81c0dd0a904 100644
--- a/package/inc/PackageConstants.hxx
+++ b/package/inc/PackageConstants.hxx
@@ -51,6 +51,7 @@ const sal_Int32 n_ConstDigestDecrypt = 1056; // 1024 + 32
#define ENCRYPTION_KEY_PROPERTY "EncryptionKey"
#define STORAGE_ENCRYPTION_KEYS_PROPERTY "StorageEncryptionKeys"
#define ENCRYPTION_ALGORITHMS_PROPERTY "EncryptionAlgorithms"
+#define ENCRYPTION_GPG_PROPERTIES "EncryptionGpGProperties"
#define HAS_ENCRYPTED_ENTRIES_PROPERTY "HasEncryptedEntries"
#define HAS_NONENCRYPTED_ENTRIES_PROPERTY "HasNonEncryptedEntries"
#define IS_INCONSISTENT_PROPERTY "IsInconsistent"
diff --git a/package/inc/ZipPackage.hxx b/package/inc/ZipPackage.hxx
index 61b0fc883575..f46eb33b0172 100644
--- a/package/inc/ZipPackage.hxx
+++ b/package/inc/ZipPackage.hxx
@@ -72,6 +72,7 @@ class ZipPackage final : public cppu::WeakImplHelper
css::uno::Sequence< css::beans::NamedValue > m_aStorageEncryptionKeys;
css::uno::Sequence< sal_Int8 > m_aEncryptionKey;
+ css::uno::Sequence< css::uno::Sequence< css::beans::NamedValue > > m_aGpgProps;
FolderHash m_aRecent;
OUString m_aURL;
diff --git a/package/source/xstor/xstorage.cxx b/package/source/xstor/xstorage.cxx
index 9291eb53cbc7..41d65fb97b7c 100644
--- a/package/source/xstor/xstorage.cxx
+++ b/package/source/xstor/xstorage.cxx
@@ -4205,6 +4205,66 @@ void SAL_CALL OStorage::setEncryptionAlgorithms( const uno::Sequence< beans::Nam
}
}
+void SAL_CALL OStorage::setGpgProperties( const uno::Sequence< uno::Sequence< beans::NamedValue > >& aProps )
+{
+ ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() );
+
+ if ( !m_pImpl )
+ {
+ SAL_INFO("package.xstor", THROW_WHERE "Disposed!");
+ throw lang::DisposedException( THROW_WHERE );
+ }
+
+ if ( m_pData->m_nStorageType != embed::StorageFormats::PACKAGE )
+ throw uno::RuntimeException( THROW_WHERE ); // the interface must be visible only for package storage
+
+ if ( !aProps.getLength() )
+ throw uno::RuntimeException( THROW_WHERE "Unexpected empty encryption algorithms list!" );
+
+ SAL_WARN_IF( !m_pData->m_bIsRoot, "package.xstor", "setGpgProperties() method is not available for nonroot storages!" );
+ if ( m_pData->m_bIsRoot )
+ {
+ try {
+ m_pImpl->ReadContents();
+ }
+ catch ( const uno::RuntimeException& aRuntimeException )
+ {
+ SAL_INFO("package.xstor", "Rethrow: " << aRuntimeException.Message);
+ throw;
+ }
+ catch ( const uno::Exception& aException )
+ {
+ SAL_INFO("package.xstor", "Rethrow: " << aException.Message);
+
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ throw lang::WrappedTargetRuntimeException( THROW_WHERE "Can not open package!",
+ static_cast< OWeakObject* >( this ),
+ aCaught );
+ }
+
+ uno::Reference< beans::XPropertySet > xPackPropSet( m_pImpl->m_xPackage, uno::UNO_QUERY_THROW );
+ try
+ {
+ xPackPropSet->setPropertyValue( ENCRYPTION_GPG_PROPERTIES,
+ uno::makeAny( aProps ) );
+ }
+ catch ( const uno::RuntimeException& aRuntimeException )
+ {
+ SAL_INFO("package.xstor", "Rethrow: " << aRuntimeException.Message);
+ throw;
+ }
+ catch( const uno::Exception& aException )
+ {
+ SAL_INFO("package.xstor", "Rethrow: " << aException.Message);
+
+ uno::Any aCaught( ::cppu::getCaughtException() );
+ throw lang::WrappedTargetRuntimeException( THROW_WHERE "Can not open package!",
+ static_cast< OWeakObject* >( this ),
+ aCaught );
+ }
+ }
+}
+
uno::Sequence< beans::NamedValue > SAL_CALL OStorage::getEncryptionAlgorithms()
{
::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() );
diff --git a/package/source/xstor/xstorage.hxx b/package/source/xstor/xstorage.hxx
index 532088359184..f55679d3d826 100644
--- a/package/source/xstor/xstorage.hxx
+++ b/package/source/xstor/xstorage.hxx
@@ -459,6 +459,7 @@ public:
// XEncryptionProtectedStorage
virtual void SAL_CALL setEncryptionAlgorithms( const css::uno::Sequence< css::beans::NamedValue >& aAlgorithms ) override;
+ virtual void SAL_CALL setGpgProperties( const css::uno::Sequence< css::uno::Sequence< css::beans::NamedValue > >& aCryptProps ) override;
virtual css::uno::Sequence< css::beans::NamedValue > SAL_CALL getEncryptionAlgorithms() override;
diff --git a/package/source/zippackage/ZipPackage.cxx b/package/source/zippackage/ZipPackage.cxx
index 3823ed07ffb8..5e61d4678490 100644
--- a/package/source/zippackage/ZipPackage.cxx
+++ b/package/source/zippackage/ZipPackage.cxx
@@ -1206,7 +1206,9 @@ uno::Reference< io::XInputStream > ZipPackage::writeTempFile()
if ( m_nFormat == embed::StorageFormats::PACKAGE )
{
- uno::Sequence < PropertyValue > aPropSeq( PKG_SIZE_NOENCR_MNFST );
+ bool bIsGpgEncrypt = m_aGpgProps.hasElements();
+ uno::Sequence < PropertyValue > aPropSeq(
+ bIsGpgEncrypt ? PKG_SIZE_NOENCR_MNFST+1 : PKG_SIZE_NOENCR_MNFST );
aPropSeq [PKG_MNFST_MEDIATYPE].Name = sMediaType;
aPropSeq [PKG_MNFST_MEDIATYPE].Value <<= m_xRootFolder->GetMediaType();
aPropSeq [PKG_MNFST_VERSION].Name = sVersion;
@@ -1214,6 +1216,11 @@ uno::Reference< io::XInputStream > ZipPackage::writeTempFile()
aPropSeq [PKG_MNFST_FULLPATH].Name = sFullPath;
aPropSeq [PKG_MNFST_FULLPATH].Value <<= OUString("/");
+ if( bIsGpgEncrypt )
+ {
+ aPropSeq[PKG_SIZE_NOENCR_MNFST].Name = "KeyInfo";
+ aPropSeq[PKG_SIZE_NOENCR_MNFST].Value <<= m_aGpgProps;
+ }
aManList.push_back( aPropSeq );
}
@@ -1749,6 +1756,22 @@ void SAL_CALL ZipPackage::setPropertyValue( const OUString& aPropertyName, const
}
}
}
+ else if ( aPropertyName == ENCRYPTION_GPG_PROPERTIES )
+ {
+ uno::Sequence< uno::Sequence< beans::NamedValue > > aGpgProps;
+ if ( m_pZipFile || !( aValue >>= aGpgProps ) || aGpgProps.getLength() == 0 )
+ {
+ throw IllegalArgumentException(THROW_WHERE "unexpected Gpg properties are provided.", uno::Reference< uno::XInterface >(), 2 );
+ }
+
+ m_aGpgProps = aGpgProps;
+
+ // override algorithm defaults (which are some legacy ODF
+ // defaults) with reasonable values
+ m_nStartKeyGenerationID = 0; // this is unused for PGP
+ m_nCommonEncryptionID = xml::crypto::CipherID::AES_CBC_W3C_PADDING;
+ m_nChecksumDigestID = xml::crypto::DigestID::SHA512_1K;
+ }
else
throw UnknownPropertyException(THROW_WHERE );
}