diff options
-rw-r--r-- | comphelper/Library_comphelper.mk | 1 | ||||
-rw-r--r-- | comphelper/source/misc/storagehelper.cxx | 111 | ||||
-rw-r--r-- | offapi/com/sun/star/embed/XEncryptionProtectedStorage.idl | 31 | ||||
-rw-r--r-- | package/inc/PackageConstants.hxx | 1 | ||||
-rw-r--r-- | package/inc/ZipPackage.hxx | 1 | ||||
-rw-r--r-- | package/source/xstor/xstorage.cxx | 60 | ||||
-rw-r--r-- | package/source/xstor/xstorage.hxx | 1 | ||||
-rw-r--r-- | package/source/zippackage/ZipPackage.cxx | 25 |
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 cbcceece39ea..66678d7c378d 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 262b317e4def..82cffa69a2fe 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 7eef97c5341f..769c7f662bba 100644 --- a/package/source/zippackage/ZipPackage.cxx +++ b/package/source/zippackage/ZipPackage.cxx @@ -1208,7 +1208,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; @@ -1216,6 +1218,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 ); } @@ -1751,6 +1758,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 ); } |