[Libreoffice-commits] core.git: comphelper/source include/comphelper package/source sfx2/source

Thorsten Behrens Thorsten.Behrens at CIB.de
Sat Jan 13 13:33:36 UTC 2018


 comphelper/source/misc/docpasswordhelper.cxx |   96 +++++++++++++++++++++++++++
 include/comphelper/docpasswordhelper.hxx     |    3 
 package/source/xstor/xstorage.cxx            |    4 -
 package/source/zippackage/ZipPackage.cxx     |   55 ++++++++++++++-
 sfx2/source/appl/appopen.cxx                 |    9 ++
 5 files changed, 164 insertions(+), 3 deletions(-)

New commits:
commit ca6f3d7a56a3a028618413a811775328449264bf
Author: Thorsten Behrens <Thorsten.Behrens at CIB.de>
Date:   Thu Dec 14 13:23:04 2017 +0100

    gpg4libre: open encrypted files also via gpg
    
    Adds code to sfx2 and package to try gpg4libre for extracting
    session keys, and use them in turn to decrypt odf storage.
    
    Change-Id: I1f626143e6c8443b4ad0c4fc5bdbd5ab8d56a451
    Reviewed-on: https://gerrit.libreoffice.org/47780
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>

diff --git a/comphelper/source/misc/docpasswordhelper.cxx b/comphelper/source/misc/docpasswordhelper.cxx
index 13ab45f043af..81e1e996816f 100644
--- a/comphelper/source/misc/docpasswordhelper.cxx
+++ b/comphelper/source/misc/docpasswordhelper.cxx
@@ -17,17 +17,28 @@
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
+#include <config_gpgme.h>
+
 #include <algorithm>
 
 #include <comphelper/docpasswordhelper.hxx>
+#include <comphelper/storagehelper.hxx>
 #include <com/sun/star/beans/PropertyValue.hpp>
 #include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
 
 #include <osl/diagnose.h>
 #include <rtl/digest.h>
 #include <rtl/random.h>
 #include <string.h>
 
+#if HAVE_FEATURE_GPGME
+# include <gpgme.h>
+# include <context.h>
+# include <data.h>
+# include <decryptionresult.h>
+#endif
+
 using ::com::sun::star::uno::Sequence;
 using ::com::sun::star::uno::Exception;
 using ::com::sun::star::uno::Reference;
@@ -418,6 +429,91 @@ Sequence< sal_Int8 > DocPasswordHelper::GetXLHashAsSequence(
     return (eResult == DocPasswordVerifierResult::OK) ? aEncData : uno::Sequence< beans::NamedValue >();
 }
 
+/*static*/ uno::Sequence< css::beans::NamedValue >
+    DocPasswordHelper::decryptGpgSession(
+        const uno::Sequence< uno::Sequence< beans::NamedValue > >& rGpgProperties )
+{
+#if HAVE_FEATURE_GPGME
+    if ( !rGpgProperties.hasElements() )
+        return uno::Sequence< beans::NamedValue >();
+
+    uno::Sequence< beans::NamedValue > aEncryptionData(1);
+    std::unique_ptr<GpgME::Context> ctx;
+    GpgME::initializeLibrary();
+    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);
+
+    const uno::Sequence < beans::NamedValue > *pSequence = rGpgProperties.getConstArray();
+    const sal_Int32 nLength = rGpgProperties.getLength();
+    for ( sal_Int32 i = 0; i < nLength ; i++, pSequence++ )
+    {
+        const beans::NamedValue *pValues = pSequence->getConstArray();
+        if ( pSequence->getLength() == 3 )
+        {
+            // take CipherValue and try to decrypt that - stop after
+            // the first successful decryption
+
+            // ctx is setup now, let's decrypt the lot!
+            uno::Sequence < sal_Int8 > aVector;
+            pValues[2].Value >>= aVector;
+
+            GpgME::Data cipher(
+                reinterpret_cast<const char*>(aVector.getConstArray()),
+                size_t(aVector.getLength()), false);
+            GpgME::Data plain;
+
+            GpgME::DecryptionResult crypt_res = ctx->decrypt(
+                cipher, plain);
+
+            // NO_SECKEY -> skip
+            // BAD_PASSPHRASE -> retry?
+
+            off_t result = plain.seek(0,SEEK_SET);
+            (void) result;
+            assert(result == 0);
+            int len=0, curr=0; char buf;
+            while( (curr=plain.read(&buf, 1)) )
+                len += curr;
+
+            if(crypt_res.error() || !len)
+                continue; // can't use this key, take next one
+
+            uno::Sequence < sal_Int8 > aKeyValue(len);
+            result = plain.seek(0,SEEK_SET);
+            assert(result == 0);
+            if( plain.read(aKeyValue.getArray(), len) != len )
+                throw uno::RuntimeException("The GpgME library failed to read the encrypted value.");
+
+            SAL_INFO("comphelper.crypto", "Extracted gpg session key of length: " << len);
+
+            aEncryptionData[0].Name = PACKAGE_ENCRYPTIONDATA_SHA256UTF8;
+            aEncryptionData[0].Value <<= aKeyValue;
+            break;
+        }
+    }
+
+    if ( aEncryptionData[0].Value.hasValue() )
+    {
+        uno::Sequence< beans::NamedValue > aContainer(2);
+        aContainer[0].Name = "GpgInfos";
+        aContainer[0].Value <<= rGpgProperties;
+        aContainer[1].Name = "EncryptionKey";
+        aContainer[1].Value <<= aEncryptionData;
+
+        return aContainer;
+    }
+#else
+    (void)rGpgProperties;
+#endif
+    return uno::Sequence< beans::NamedValue >();
+}
+
 } // namespace comphelper
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/comphelper/docpasswordhelper.hxx b/include/comphelper/docpasswordhelper.hxx
index 2ef3e040af1a..e420cf3e69af 100644
--- a/include/comphelper/docpasswordhelper.hxx
+++ b/include/comphelper/docpasswordhelper.hxx
@@ -277,6 +277,9 @@ public:
                             const ::std::vector< OUString >* pDefaultPasswords = nullptr,
                             bool* pbIsDefaultPassword = nullptr );
 
+    static css::uno::Sequence< css::beans::NamedValue > decryptGpgSession(
+        const css::uno::Sequence< css::uno::Sequence< css::beans::NamedValue > >& rGpgProperties);
+
 private:
                         ~DocPasswordHelper();
 };
diff --git a/package/source/xstor/xstorage.cxx b/package/source/xstor/xstorage.cxx
index 21567b8eb2a5..3fabad0e51e7 100644
--- a/package/source/xstor/xstorage.cxx
+++ b/package/source/xstor/xstorage.cxx
@@ -4388,7 +4388,8 @@ void SAL_CALL OStorage::setPropertyValue( const OUString& aPropertyName, const u
                                     || aPropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY
                                     || aPropertyName == IS_INCONSISTENT_PROPERTY
                                     || aPropertyName == "URL"
-                                    || aPropertyName == "RepairPackage" ) )
+                                    || aPropertyName == "RepairPackage"
+                                    || aPropertyName == ENCRYPTION_GPG_PROPERTIES) )
            || aPropertyName == "IsRoot"
            || aPropertyName == MEDIATYPE_FALLBACK_USED_PROPERTY )
             throw beans::PropertyVetoException( THROW_WHERE );
@@ -4507,6 +4508,7 @@ uno::Any SAL_CALL OStorage::getPropertyValue( const OUString& aPropertyName )
         else if ( m_pData->m_nStorageType == embed::StorageFormats::PACKAGE
           && ( aPropertyName == HAS_ENCRYPTED_ENTRIES_PROPERTY
             || aPropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY
+            || aPropertyName == ENCRYPTION_GPG_PROPERTIES
             || aPropertyName == IS_INCONSISTENT_PROPERTY ) )
         {
             try {
diff --git a/package/source/zippackage/ZipPackage.cxx b/package/source/zippackage/ZipPackage.cxx
index a6eb5f6af97e..967e0eded056 100644
--- a/package/source/zippackage/ZipPackage.cxx
+++ b/package/source/zippackage/ZipPackage.cxx
@@ -197,12 +197,14 @@ void ZipPackage::parseManifest()
                         const OUString sPropDigestAlgorithm ("DigestAlgorithm");
                         const OUString sPropEncryptionAlgorithm ("EncryptionAlgorithm");
                         const OUString sPropStartKeyAlgorithm ("StartKeyAlgorithm");
+                        const OUString sKeyInfo ("KeyInfo");
 
                         uno::Sequence < uno::Sequence < PropertyValue > > aManifestSequence = xReader->readManifestSequence ( xSink->getInputStream() );
                         sal_Int32 nLength = aManifestSequence.getLength();
                         const uno::Sequence < PropertyValue > *pSequence = aManifestSequence.getConstArray();
                         ZipPackageStream *pStream = nullptr;
                         ZipPackageFolder *pFolder = nullptr;
+                        const Any *pKeyInfo = nullptr;
 
                         for ( sal_Int32 i = 0; i < nLength ; i++, pSequence++ )
                         {
@@ -235,6 +237,8 @@ void ZipPackage::parseManifest()
                                     pStartKeyAlg = &( pValue[j].Value );
                                 else if ( pValue[j].Name == sPropDerivedKeySize )
                                     pDerivedKeySize = &( pValue[j].Value );
+                                else if ( pValue[j].Name == sKeyInfo )
+                                    pKeyInfo = &( pValue[j].Value );
                             }
 
                             if ( !sPath.isEmpty() && hasByHierarchicalName ( sPath ) )
@@ -255,7 +259,50 @@ void ZipPackage::parseManifest()
                                     pStream->SetMediaType ( sMediaType );
                                     pStream->SetFromManifest( true );
 
-                                    if ( pSalt && pVector && pCount && pSize && pDigest && pDigestAlg && pEncryptionAlg )
+                                    if ( pKeyInfo && pVector && pSize && pDigest && pDigestAlg && pEncryptionAlg )
+                                    {
+                                        uno::Sequence < sal_Int8 > aSequence;
+                                        sal_Int64 nSize = 0;
+                                        sal_Int32 nDigestAlg = 0, nEncryptionAlg = 0;
+
+                                        pStream->SetToBeEncrypted ( true );
+
+                                        *pVector >>= aSequence;
+                                        pStream->setInitialisationVector ( aSequence );
+
+                                        *pSize >>= nSize;
+                                        pStream->setSize ( nSize );
+
+                                        *pDigest >>= aSequence;
+                                        pStream->setDigest ( aSequence );
+
+                                        *pDigestAlg >>= nDigestAlg;
+                                        pStream->SetImportedChecksumAlgorithm( nDigestAlg );
+
+                                        *pEncryptionAlg >>= nEncryptionAlg;
+                                        pStream->SetImportedEncryptionAlgorithm( nEncryptionAlg );
+
+                                        *pKeyInfo >>= m_aGpgProps;
+
+                                        pStream->SetToBeCompressed ( true );
+                                        pStream->SetToBeEncrypted ( true );
+                                        pStream->SetIsEncrypted ( true );
+
+                                        // clamp to default SHA256 start key magic value,
+                                        // c.f. ZipPackageStream::GetEncryptionKey()
+                                        // trying to get key value from properties
+                                        const sal_Int32 nStartKeyAlg = xml::crypto::DigestID::SHA256;
+                                        pStream->SetImportedStartKeyAlgorithm( nStartKeyAlg );
+
+                                        if ( !m_bHasEncryptedEntries && pStream->getName() == "content.xml" )
+                                        {
+                                            m_bHasEncryptedEntries = true;
+                                            m_nChecksumDigestID = nDigestAlg;
+                                            m_nCommonEncryptionID = nEncryptionAlg;
+                                            m_nStartKeyGenerationID = nStartKeyAlg;
+                                        }
+                                    }
+                                    else if ( pSalt && pVector && pCount && pSize && pDigest && pDigestAlg && pEncryptionAlg )
                                     {
                                         uno::Sequence < sal_Int8 > aSequence;
                                         sal_Int64 nSize = 0;
@@ -1758,7 +1805,7 @@ 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 )
+        if ( !( aValue >>= aGpgProps ) || aGpgProps.getLength() == 0 )
         {
             throw IllegalArgumentException(THROW_WHERE "unexpected Gpg properties are provided.", uno::Reference< uno::XInterface >(), 2 );
         }
@@ -1801,6 +1848,10 @@ Any SAL_CALL ZipPackage::getPropertyValue( const OUString& PropertyName )
     {
         return Any(m_bHasEncryptedEntries);
     }
+    else if ( PropertyName == ENCRYPTION_GPG_PROPERTIES )
+    {
+        return Any(m_aGpgProps);
+    }
     else if ( PropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY )
     {
         return Any(m_bHasNonEncryptedEntries);
diff --git a/sfx2/source/appl/appopen.cxx b/sfx2/source/appl/appopen.cxx
index 0c6c9fd4df10..cba90f661a86 100644
--- a/sfx2/source/appl/appopen.cxx
+++ b/sfx2/source/appl/appopen.cxx
@@ -195,9 +195,12 @@ ErrCode CheckPasswd_Impl
             if ( xStorageProps.is() )
             {
                 bool bIsEncrypted = false;
+                uno::Sequence< uno::Sequence< beans::NamedValue > > aGpgProperties;
                 try {
                     xStorageProps->getPropertyValue("HasEncryptedEntries")
                         >>= bIsEncrypted;
+                    xStorageProps->getPropertyValue("EncryptionGpGProperties")
+                        >>= aGpgProperties;
                 } catch( uno::Exception& )
                 {
                     // TODO/LATER:
@@ -230,6 +233,12 @@ ErrCode CheckPasswd_Impl
                             if ( pEncryptionDataItem )
                                 pEncryptionDataItem->GetValue() >>= aEncryptionData;
 
+                            // try if one of the public key entries is
+                            // decryptable, then extract session key
+                            // from it
+                            if ( !aEncryptionData.hasElements() && aGpgProperties.hasElements() )
+                                aEncryptionData = ::comphelper::DocPasswordHelper::decryptGpgSession(aGpgProperties);
+
                             SfxDocPasswordVerifier aVerifier( xStorage );
                             aEncryptionData = ::comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
                                 aVerifier, aEncryptionData, aPassword, xInteractionHandler, pFile->GetOrigURL(), comphelper::DocPasswordRequestType::Standard );


More information about the Libreoffice-commits mailing list