summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThorsten Behrens <Thorsten.Behrens@CIB.de>2017-12-10 23:40:00 +0100
committerThorsten Behrens <Thorsten.Behrens@CIB.de>2018-01-15 14:05:31 +0100
commit534d31fc2f06f36d7eb5d33cb6cb71f487c58586 (patch)
tree3206fb8adcded689fcbee944cb23f065636b385f
parentfd0eba79e6600c2940f4e02af21807903b8ad950 (diff)
tdf#114550: load back PGP encrypted files
This squashes the following commits from master: gpg4libre: import PGP encryption manifest Change-Id: Iadd7f8f1194299cb50907d8594114c89c668ebd0 gpg4libre: open encrypted files also via gpg Change-Id: I1f626143e6c8443b4ad0c4fc5bdbd5ab8d56a451 tdf#114550 use 32 bit random session key for gpg encryption Change-Id: I7303be71fd855aa454d07fcae04d7f42e3c9cd9c tdf#114550 recognize sym key & init vec as valid f/ decrypt Change-Id: Ie366f086a3c14d6b54b91b4edee8cfef1a42c44b tdf#114550 don't use PBKDF2 in package for gpg encryption Change-Id: Ic96b2193f8541bbd109795fb9c0212a0a10c7344 gpg4libre: add initial unit test for encryption Change-Id: Id782dd865878ae7b8a60c7c80821b1370f6ac7e7 Change-Id: Id77b67a275bf91614ab62b65fdc69e4872247ffc Reviewed-on: https://gerrit.libreoffice.org/47784 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Katarina Behrens <Katarina.Behrens@cib.de> Reviewed-by: Thorsten Behrens <Thorsten.Behrens@CIB.de> (cherry picked from commit 01c7a60c051ac4562e3a317dde3c29c507f3f40b) Reviewed-on: https://gerrit.libreoffice.org/47834 Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk> Reviewed-by: Samuel Mehrbrodt <Samuel.Mehrbrodt@cib.de> Tested-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>
-rw-r--r--comphelper/source/misc/docpasswordhelper.cxx96
-rw-r--r--comphelper/source/misc/storagehelper.cxx4
-rw-r--r--include/comphelper/docpasswordhelper.hxx3
-rw-r--r--package/inc/ZipPackageEntry.hxx1
-rw-r--r--package/inc/ZipPackageFolder.hxx2
-rw-r--r--package/inc/ZipPackageStream.hxx1
-rw-r--r--package/source/manifest/ManifestImport.cxx172
-rw-r--r--package/source/manifest/ManifestImport.hxx25
-rw-r--r--package/source/xstor/xstorage.cxx4
-rw-r--r--package/source/zipapi/XUnbufferedStream.cxx5
-rw-r--r--package/source/zipapi/ZipFile.cxx9
-rw-r--r--package/source/zippackage/ZipPackage.cxx62
-rw-r--r--package/source/zippackage/ZipPackageFolder.cxx10
-rw-r--r--package/source/zippackage/ZipPackageStream.cxx3
-rw-r--r--sfx2/source/appl/appopen.cxx9
-rw-r--r--xmlsecurity/qa/unit/signing/data/encryptedGPG.odtbin0 -> 13081 bytes
-rw-r--r--xmlsecurity/qa/unit/signing/data/pubring.gpgbin1834 -> 3060 bytes
-rw-r--r--xmlsecurity/qa/unit/signing/data/secring.gpgbin1304 -> 3832 bytes
-rw-r--r--xmlsecurity/qa/unit/signing/data/trustdb.gpgbin1280 -> 1360 bytes
-rw-r--r--xmlsecurity/qa/unit/signing/signing.cxx13
20 files changed, 403 insertions, 16 deletions
diff --git a/comphelper/source/misc/docpasswordhelper.cxx b/comphelper/source/misc/docpasswordhelper.cxx
index 7d761bb9c740..81fe3bc5268c 100644
--- a/comphelper/source/misc/docpasswordhelper.cxx
+++ b/comphelper/source/misc/docpasswordhelper.cxx
@@ -17,11 +17,15 @@
* 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/time.h>
#include <osl/diagnose.h>
@@ -29,6 +33,13 @@
#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;
@@ -420,6 +431,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/comphelper/source/misc/storagehelper.cxx b/comphelper/source/misc/storagehelper.cxx
index d51055211e8a..27e72b784e10 100644
--- a/comphelper/source/misc/storagehelper.cxx
+++ b/comphelper/source/misc/storagehelper.cxx
@@ -442,8 +442,8 @@ uno::Sequence< beans::NamedValue > OStorageHelper::CreateGpgPackageEncryptionDat
rtlRandomPool aRandomPool = rtl_random_createPool();
rtl_random_addBytes(aRandomPool, &aTime, 8);
- // get 16 random chars out of it
- uno::Sequence < sal_Int8 > aVector(16);
+ // get 32 random chars out of it
+ uno::Sequence < sal_Int8 > aVector(32);
rtl_random_getBytes( aRandomPool, aVector.getArray(), aVector.getLength() );
rtl_random_destroyPool(aRandomPool);
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/inc/ZipPackageEntry.hxx b/package/inc/ZipPackageEntry.hxx
index 300b5f25ea67..5e0064f9bf7e 100644
--- a/package/inc/ZipPackageEntry.hxx
+++ b/package/inc/ZipPackageEntry.hxx
@@ -69,6 +69,7 @@ public:
std::vector < css::uno::Sequence < css::beans::PropertyValue > > &rManList,
ZipOutputStream & rZipOut,
const css::uno::Sequence < sal_Int8 >& rEncryptionKey,
+ sal_Int32 nPBKDF2IterationCount,
const rtlRandomPool &rRandomPool ) = 0;
void clearParent()
diff --git a/package/inc/ZipPackageFolder.hxx b/package/inc/ZipPackageFolder.hxx
index 0fad51f72ba2..1f65b2ca297e 100644
--- a/package/inc/ZipPackageFolder.hxx
+++ b/package/inc/ZipPackageFolder.hxx
@@ -73,6 +73,7 @@ public:
std::vector < css::uno::Sequence < css::beans::PropertyValue > > &rManList,
ZipOutputStream & rZipOut,
const css::uno::Sequence < sal_Int8 >& rEncryptionKey,
+ sal_Int32 nPBKDF2IterationCount,
const rtlRandomPool &rRandomPool ) override;
// Recursive functions
@@ -82,6 +83,7 @@ public:
std::vector < css::uno::Sequence < css::beans::PropertyValue > > &rManList,
ZipOutputStream & rZipOut,
const css::uno::Sequence< sal_Int8 > &rEncryptionKey,
+ sal_Int32 nPBKDF2IterationCount,
const rtlRandomPool & rRandomPool) const;
// XNameContainer
diff --git a/package/inc/ZipPackageStream.hxx b/package/inc/ZipPackageStream.hxx
index d8f6903e35ac..cf8129e8d0a2 100644
--- a/package/inc/ZipPackageStream.hxx
+++ b/package/inc/ZipPackageStream.hxx
@@ -132,6 +132,7 @@ public:
std::vector < css::uno::Sequence < css::beans::PropertyValue > > &rManList,
ZipOutputStream & rZipOut,
const css::uno::Sequence < sal_Int8 >& rEncryptionKey,
+ sal_Int32 nPBKDF2IterationCount,
const rtlRandomPool &rRandomPool ) override;
void setZipEntryOnLoading( const ZipEntry &rInEntry);
diff --git a/package/source/manifest/ManifestImport.cxx b/package/source/manifest/ManifestImport.cxx
index 5ecd8c1cffcd..98a9d61128b5 100644
--- a/package/source/manifest/ManifestImport.cxx
+++ b/package/source/manifest/ManifestImport.cxx
@@ -34,6 +34,7 @@ using namespace std;
ManifestImport::ManifestImport( vector < Sequence < PropertyValue > > & rNewManVector )
: bIgnoreEncryptData ( false )
+ , bPgpEncryption ( false )
, nDerivedKeySize( 0 )
, rManVector ( rNewManVector )
@@ -57,6 +58,17 @@ ManifestImport::ManifestImport( vector < Sequence < PropertyValue > > & rNewManV
, sChecksumAttribute ( ATTRIBUTE_CHECKSUM )
, sChecksumTypeAttribute ( ATTRIBUTE_CHECKSUM_TYPE )
+ , sKeyInfoElement ( ELEMENT_ENCRYPTED_KEYINFO )
+ , sManifestKeyInfoElement ( ELEMENT_MANIFEST_KEYINFO )
+ , sEncryptedKeyElement ( ELEMENT_ENCRYPTEDKEY )
+ , sEncryptionMethodElement ( ELEMENT_ENCRYPTIONMETHOD )
+ , sPgpDataElement ( ELEMENT_PGPDATA )
+ , sPgpKeyIDElement ( ELEMENT_PGPKEYID )
+ , sPGPKeyPacketElement ( ELEMENT_PGPKEYPACKET )
+ , sAlgorithmAttribute ( ATTRIBUTE_ALGORITHM )
+ , sCipherDataElement ( ELEMENT_CIPHERDATA )
+ , sCipherValueElement ( ELEMENT_CIPHERVALUE )
+
, sFullPathProperty ( "FullPath" )
, sMediaTypeProperty ( "MediaType" )
, sVersionProperty ( "Version" )
@@ -126,6 +138,80 @@ void ManifestImport::doFileEntry(StringHashMap &rConvertedAttribs)
}
}
+void ManifestImport::doKeyInfoEntry(StringHashMap &)
+{
+}
+
+void ManifestImport::doEncryptedKey(StringHashMap &)
+{
+ aKeyInfoSequence.clear();
+ aKeyInfoSequence.resize(3);
+}
+
+void ManifestImport::doEncryptionMethod(StringHashMap &rConvertedAttribs)
+{
+ OUString aString = rConvertedAttribs[sAlgorithmAttribute];
+ if ( aKeyInfoSequence.size() != 3
+ || aString != "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p" )
+ {
+ bIgnoreEncryptData = true;
+ }
+}
+
+void ManifestImport::doEncryptedKeyInfo(StringHashMap &)
+{
+}
+
+void ManifestImport::doEncryptedCipherData(StringHashMap &)
+{
+}
+
+void ManifestImport::doEncryptedPgpData(StringHashMap &)
+{
+}
+
+void ManifestImport::doEncryptedCipherValue()
+{
+ if ( aKeyInfoSequence.size() == 3 )
+ {
+ aKeyInfoSequence[2].Name = "CipherValue";
+ uno::Sequence < sal_Int8 > aDecodeBuffer;
+ ::sax::Converter::decodeBase64(aDecodeBuffer, aCurrentCharacters);
+ aKeyInfoSequence[2].Value <<= aDecodeBuffer;
+ aCurrentCharacters = ""; // consumed
+ }
+ else
+ bIgnoreEncryptData = true;
+}
+
+void ManifestImport::doEncryptedKeyId()
+{
+ if ( aKeyInfoSequence.size() == 3 )
+ {
+ aKeyInfoSequence[0].Name = "KeyId";
+ uno::Sequence < sal_Int8 > aDecodeBuffer;
+ ::sax::Converter::decodeBase64(aDecodeBuffer, aCurrentCharacters);
+ aKeyInfoSequence[0].Value <<= aDecodeBuffer;
+ aCurrentCharacters = ""; // consumed
+ }
+ else
+ bIgnoreEncryptData = true;
+}
+
+void ManifestImport::doEncryptedKeyPacket()
+{
+ if ( aKeyInfoSequence.size() == 3 )
+ {
+ aKeyInfoSequence[1].Name = "KeyPacket";
+ uno::Sequence < sal_Int8 > aDecodeBuffer;
+ ::sax::Converter::decodeBase64(aDecodeBuffer, aCurrentCharacters);
+ aKeyInfoSequence[1].Value <<= aDecodeBuffer;
+ aCurrentCharacters = ""; // consumed
+ }
+ else
+ bIgnoreEncryptData = true;
+}
+
void ManifestImport::doEncryptionData(StringHashMap &rConvertedAttribs)
{
// If this element exists, then this stream is encrypted and we need
@@ -214,6 +300,9 @@ void ManifestImport::doKeyDerivation(StringHashMap &rConvertedAttribs)
aSequence[PKG_MNFST_DERKEYSIZE].Name = sDerivedKeySizeProperty;
aSequence[PKG_MNFST_DERKEYSIZE].Value <<= nDerivedKeySize;
+ } else if ( bPgpEncryption ) {
+ if ( aString != "PGP" )
+ bIgnoreEncryptData = true;
} else
bIgnoreEncryptData = true;
}
@@ -250,6 +339,8 @@ void SAL_CALL ManifestImport::startElement( const OUString& aName, const uno::Re
case 2: {
if (aConvertedName == sFileEntryElement) //manifest:file-entry
doFileEntry(aConvertedAttribs);
+ else if (aConvertedName == sManifestKeyInfoElement) //loext:KeyInfo
+ doKeyInfoEntry(aConvertedAttribs);
else
aStack.back().m_bValid = false;
break;
@@ -262,6 +353,8 @@ void SAL_CALL ManifestImport::startElement( const OUString& aName, const uno::Re
aStack.back().m_bValid = false;
else if (aConvertedName == sEncryptionDataElement) //manifest:encryption-data
doEncryptionData(aConvertedAttribs);
+ else if (aConvertedName == sEncryptedKeyElement) //loext:encrypted-key
+ doEncryptedKey(aConvertedAttribs);
else
aStack.back().m_bValid = false;
break;
@@ -278,6 +371,43 @@ void SAL_CALL ManifestImport::startElement( const OUString& aName, const uno::Re
doKeyDerivation(aConvertedAttribs);
else if (aConvertedName == sStartKeyAlgElement) //manifest:start-key-generation
doStartKeyAlg(aConvertedAttribs);
+ else if (aConvertedName == sEncryptionMethodElement) //loext:encryption-method
+ doEncryptionMethod(aConvertedAttribs);
+ else if (aConvertedName == sKeyInfoElement) //loext:KeyInfo
+ doEncryptedKeyInfo(aConvertedAttribs);
+ else if (aConvertedName == sCipherDataElement) //loext:CipherData
+ doEncryptedCipherData(aConvertedAttribs);
+ else
+ aStack.back().m_bValid = false;
+ break;
+ }
+ case 5: {
+ ManifestStack::reverse_iterator aIter = aStack.rbegin();
+ ++aIter;
+
+ if (!aIter->m_bValid)
+ aStack.back().m_bValid = false;
+ else if (aConvertedName == sPgpDataElement) //loext:PGPData
+ doEncryptedPgpData(aConvertedAttribs);
+ else if (aConvertedName == sCipherValueElement) //loext:CipherValue
+ // ciphervalue action happens on endElement
+ aCurrentCharacters = "";
+ else
+ aStack.back().m_bValid = false;
+ break;
+ }
+ case 6: {
+ ManifestStack::reverse_iterator aIter = aStack.rbegin();
+ ++aIter;
+
+ if (!aIter->m_bValid)
+ aStack.back().m_bValid = false;
+ else if (aConvertedName == sPgpKeyIDElement) //loext:PGPKeyID
+ // ciphervalue action happens on endElement
+ aCurrentCharacters = "";
+ else if (aConvertedName == sPGPKeyPacketElement) //loext:PGPKeyPacket
+ // ciphervalue action happens on endElement
+ aCurrentCharacters = "";
else
aStack.back().m_bValid = false;
break;
@@ -298,9 +428,19 @@ bool isEmpty(const css::beans::PropertyValue &rProp)
void SAL_CALL ManifestImport::endElement( const OUString& aName )
{
+ size_t nLevel = aStack.size();
+
+ assert(nLevel >= 1);
+
OUString aConvertedName = ConvertName( aName );
if ( !aStack.empty() && aStack.rbegin()->m_aConvertedName == aConvertedName ) {
if ( aConvertedName == sFileEntryElement && aStack.back().m_bValid ) {
+ // root folder gets KeyInfo entry if any, for PGP encryption
+ if (!bIgnoreEncryptData && !aKeys.empty() && aSequence[PKG_MNFST_FULLPATH].Value.get<OUString>() == "/" )
+ {
+ aSequence[PKG_SIZE_NOENCR_MNFST].Name = "KeyInfo";
+ aSequence[PKG_SIZE_NOENCR_MNFST].Value <<= comphelper::containerToSequence(aKeys);
+ }
css::beans::PropertyValue aEmpty;
aSequence.erase(std::remove_if(aSequence.begin(), aSequence.end(),
isEmpty), aSequence.end());
@@ -310,13 +450,43 @@ void SAL_CALL ManifestImport::endElement( const OUString& aName )
aSequence.clear();
}
+ else if ( aConvertedName == sEncryptedKeyElement && aStack.back().m_bValid ) {
+ if ( !bIgnoreEncryptData )
+ {
+ aKeys.push_back( comphelper::containerToSequence(aKeyInfoSequence) );
+ bPgpEncryption = true;
+ }
+ aKeyInfoSequence.clear();
+ }
+
+ // end element handling for elements with cdata
+ switch (nLevel) {
+ case 5: {
+ if (aConvertedName == sCipherValueElement) //loext:CipherValue
+ doEncryptedCipherValue();
+ else
+ aStack.back().m_bValid = false;
+ break;
+ }
+ case 6: {
+ if (aConvertedName == sPgpKeyIDElement) //loext:PGPKeyID
+ doEncryptedKeyId();
+ else if (aConvertedName == sPGPKeyPacketElement) //loext:PGPKeyPacket
+ doEncryptedKeyPacket();
+ else
+ aStack.back().m_bValid = false;
+ break;
+ }
+ }
aStack.pop_back();
+ return;
}
}
-void SAL_CALL ManifestImport::characters( const OUString& /*aChars*/ )
+void SAL_CALL ManifestImport::characters( const OUString& aChars )
{
+ aCurrentCharacters += aChars;
}
void SAL_CALL ManifestImport::ignorableWhitespace( const OUString& /*aWhitespaces*/ )
diff --git a/package/source/manifest/ManifestImport.hxx b/package/source/manifest/ManifestImport.hxx
index 86cafa4ef1d7..26f692be9c5b 100644
--- a/package/source/manifest/ManifestImport.hxx
+++ b/package/source/manifest/ManifestImport.hxx
@@ -22,6 +22,7 @@
#include <cppuhelper/implbase.hxx>
#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
#include <vector>
#include <HashMaps.hxx>
@@ -50,9 +51,13 @@ typedef ::std::vector< ManifestScopeEntry > ManifestStack;
class ManifestImport final : public cppu::WeakImplHelper < css::xml::sax::XDocumentHandler >
{
+ std::vector< css::beans::NamedValue > aKeyInfoSequence;
+ std::vector< css::uno::Sequence< css::beans::NamedValue > > aKeys;
std::vector< css::beans::PropertyValue > aSequence;
+ OUString aCurrentCharacters;
ManifestStack aStack;
bool bIgnoreEncryptData;
+ bool bPgpEncryption;
sal_Int32 nDerivedKeySize;
::std::vector < css::uno::Sequence < css::beans::PropertyValue > > & rManVector;
@@ -76,6 +81,17 @@ class ManifestImport final : public cppu::WeakImplHelper < css::xml::sax::XDocum
const OUString sChecksumAttribute;
const OUString sChecksumTypeAttribute;
+ const OUString sKeyInfoElement;
+ const OUString sManifestKeyInfoElement;
+ const OUString sEncryptedKeyElement;
+ const OUString sEncryptionMethodElement;
+ const OUString sPgpDataElement;
+ const OUString sPgpKeyIDElement;
+ const OUString sPGPKeyPacketElement;
+ const OUString sAlgorithmAttribute;
+ const OUString sCipherDataElement;
+ const OUString sCipherValueElement;
+
const OUString sFullPathProperty;
const OUString sMediaTypeProperty;
const OUString sVersionProperty;
@@ -136,6 +152,15 @@ private:
void doKeyDerivation(StringHashMap &rConvertedAttribs);
/// @throws css::uno::RuntimeException
void doStartKeyAlg(StringHashMap &rConvertedAttribs);
+ void doKeyInfoEntry(StringHashMap &);
+ void doEncryptedKey(StringHashMap &);
+ void doEncryptionMethod(StringHashMap &);
+ void doEncryptedKeyInfo(StringHashMap &);
+ void doEncryptedCipherData(StringHashMap &);
+ void doEncryptedPgpData(StringHashMap &);
+ void doEncryptedCipherValue();
+ void doEncryptedKeyId();
+ void doEncryptedKeyPacket();
};
#endif
diff --git a/package/source/xstor/xstorage.cxx b/package/source/xstor/xstorage.cxx
index 41d65fb97b7c..5c494a586197 100644
--- a/package/source/xstor/xstorage.cxx
+++ b/package/source/xstor/xstorage.cxx
@@ -4386,7 +4386,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 );
@@ -4505,6 +4506,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/zipapi/XUnbufferedStream.cxx b/package/source/zipapi/XUnbufferedStream.cxx
index 162bab6d7580..d20b46195c3a 100644
--- a/package/source/zipapi/XUnbufferedStream.cxx
+++ b/package/source/zipapi/XUnbufferedStream.cxx
@@ -78,7 +78,10 @@ XUnbufferedStream::XUnbufferedStream(
if (mnZipSize < 0)
throw ZipIOException("The stream seems to be broken!");
- bool bHaveEncryptData = rData.is() && rData->m_aSalt.getLength() && rData->m_aInitVector.getLength() && rData->m_nIterationCount != 0;
+ bool bHaveEncryptData = rData.is() && rData->m_aInitVector.getLength() &&
+ ((rData->m_aSalt.getLength() && rData->m_nIterationCount != 0)
+ ||
+ rData->m_aKey.getLength());
bool bMustDecrypt = nStreamMode == UNBUFF_STREAM_DATA && bHaveEncryptData && bIsEncrypted;
if ( bMustDecrypt )
diff --git a/package/source/zipapi/ZipFile.cxx b/package/source/zipapi/ZipFile.cxx
index de4c1a364acd..f772287eeaf8 100644
--- a/package/source/zipapi/ZipFile.cxx
+++ b/package/source/zipapi/ZipFile.cxx
@@ -161,7 +161,14 @@ uno::Reference< xml::crypto::XCipherContext > ZipFile::StaticGetCipher( const un
}
uno::Sequence< sal_Int8 > aDerivedKey( xEncryptionData->m_nDerivedKeySize );
- if ( rtl_Digest_E_None != rtl_digest_PBKDF2( reinterpret_cast< sal_uInt8* >( aDerivedKey.getArray() ),
+ if ( !xEncryptionData->m_nIterationCount &&
+ xEncryptionData->m_nDerivedKeySize == xEncryptionData->m_aKey.getLength() )
+ {
+ // gpg4libre: no need to derive key, m_aKey is already
+ // usable as symmetric session key
+ aDerivedKey = xEncryptionData->m_aKey;
+ }
+ else if ( rtl_Digest_E_None != rtl_digest_PBKDF2( reinterpret_cast< sal_uInt8* >( aDerivedKey.getArray() ),
aDerivedKey.getLength(),
reinterpret_cast< const sal_uInt8 * > (xEncryptionData->m_aKey.getConstArray() ),
xEncryptionData->m_aKey.getLength(),
diff --git a/package/source/zippackage/ZipPackage.cxx b/package/source/zippackage/ZipPackage.cxx
index 5e61d4678490..9a4853f439c6 100644
--- a/package/source/zippackage/ZipPackage.cxx
+++ b/package/source/zippackage/ZipPackage.cxx
@@ -198,12 +198,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++ )
{
@@ -236,6 +238,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 ) )
@@ -256,7 +260,51 @@ 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 );
+ pStream->setIterationCount(0);
+
+ // 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;
@@ -1203,10 +1251,10 @@ uno::Reference< io::XInputStream > ZipPackage::writeTempFile()
const OUString sMediaType ("MediaType");
const OUString sVersion ("Version");
const OUString sFullPath ("FullPath");
+ const bool bIsGpgEncrypt = m_aGpgProps.hasElements();
if ( m_nFormat == embed::StorageFormats::PACKAGE )
{
- 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;
@@ -1229,8 +1277,10 @@ uno::Reference< io::XInputStream > ZipPackage::writeTempFile()
// for encrypted streams
RandomPool aRandomPool;
+ sal_Int32 const nPBKDF2IterationCount = 100000;
+
// call saveContents ( it will recursively save sub-directories
- m_xRootFolder->saveContents("", aManList, aZipOut, GetEncryptionKey(), aRandomPool.get());
+ m_xRootFolder->saveContents("", aManList, aZipOut, GetEncryptionKey(), bIsGpgEncrypt ? 0 : nPBKDF2IterationCount, aRandomPool.get());
}
if( m_nFormat == embed::StorageFormats::PACKAGE )
@@ -1759,7 +1809,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 );
}
@@ -1802,6 +1852,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/package/source/zippackage/ZipPackageFolder.cxx b/package/source/zippackage/ZipPackageFolder.cxx
index f70a048b393a..01b4789720b8 100644
--- a/package/source/zippackage/ZipPackageFolder.cxx
+++ b/package/source/zippackage/ZipPackageFolder.cxx
@@ -258,6 +258,7 @@ bool ZipPackageFolder::saveChild(
std::vector < uno::Sequence < PropertyValue > > &rManList,
ZipOutputStream & rZipOut,
const uno::Sequence < sal_Int8 >& rEncryptionKey,
+ sal_Int32 nPBKDF2IterationCount,
const rtlRandomPool &rRandomPool)
{
const OUString sMediaTypeProperty ("MediaType");
@@ -279,7 +280,7 @@ bool ZipPackageFolder::saveChild(
else
aPropSet.realloc( 0 );
- saveContents( sTempName, rManList, rZipOut, rEncryptionKey, rRandomPool);
+ saveContents( sTempName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool);
// folder can have a mediatype only in package format
if ( aPropSet.getLength() && ( m_nFormat == embed::StorageFormats::PACKAGE ) )
@@ -293,6 +294,7 @@ void ZipPackageFolder::saveContents(
std::vector < uno::Sequence < PropertyValue > > &rManList,
ZipOutputStream & rZipOut,
const uno::Sequence < sal_Int8 >& rEncryptionKey,
+ sal_Int32 nPBKDF2IterationCount,
const rtlRandomPool &rRandomPool ) const
{
bool bWritingFailed = false;
@@ -331,7 +333,7 @@ void ZipPackageFolder::saveContents(
{
bMimeTypeStreamStored = true;
bWritingFailed = !aIter->second->pStream->saveChild(
- rPath + aIter->first, rManList, rZipOut, rEncryptionKey, rRandomPool );
+ rPath + aIter->first, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool );
}
}
@@ -347,12 +349,12 @@ void ZipPackageFolder::saveContents(
if (rInfo.bFolder)
{
bWritingFailed = !rInfo.pFolder->saveChild(
- rPath + rShortName, rManList, rZipOut, rEncryptionKey, rRandomPool );
+ rPath + rShortName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool );
}
else
{
bWritingFailed = !rInfo.pStream->saveChild(
- rPath + rShortName, rManList, rZipOut, rEncryptionKey, rRandomPool );
+ rPath + rShortName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool );
}
}
}
diff --git a/package/source/zippackage/ZipPackageStream.cxx b/package/source/zippackage/ZipPackageStream.cxx
index 160dfa8d310f..f415995a125e 100644
--- a/package/source/zippackage/ZipPackageStream.cxx
+++ b/package/source/zippackage/ZipPackageStream.cxx
@@ -502,6 +502,7 @@ bool ZipPackageStream::saveChild(
std::vector < uno::Sequence < beans::PropertyValue > > &rManList,
ZipOutputStream & rZipOut,
const uno::Sequence < sal_Int8 >& rEncryptionKey,
+ sal_Int32 nPBKDF2IterationCount,
const rtlRandomPool &rRandomPool)
{
bool bSuccess = true;
@@ -647,8 +648,6 @@ bool ZipPackageStream::saveChild(
uno::Sequence < sal_Int8 > aSalt( 16 ), aVector( GetBlockSize() );
rtl_random_getBytes ( rRandomPool, aSalt.getArray(), 16 );
rtl_random_getBytes ( rRandomPool, aVector.getArray(), aVector.getLength() );
- sal_Int32 const nPBKDF2IterationCount = 100000;
-
if ( !m_bHaveOwnKey )
{
m_aEncryptionKey = rEncryptionKey;
diff --git a/sfx2/source/appl/appopen.cxx b/sfx2/source/appl/appopen.cxx
index 0ae86d85f9b4..6c5d833af317 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 );
diff --git a/xmlsecurity/qa/unit/signing/data/encryptedGPG.odt b/xmlsecurity/qa/unit/signing/data/encryptedGPG.odt
new file mode 100644
index 000000000000..9490a0ce58c5
--- /dev/null
+++ b/xmlsecurity/qa/unit/signing/data/encryptedGPG.odt
Binary files differ
diff --git a/xmlsecurity/qa/unit/signing/data/pubring.gpg b/xmlsecurity/qa/unit/signing/data/pubring.gpg
index 40a8d53fb401..007ea98afd55 100644
--- a/xmlsecurity/qa/unit/signing/data/pubring.gpg
+++ b/xmlsecurity/qa/unit/signing/data/pubring.gpg
Binary files differ
diff --git a/xmlsecurity/qa/unit/signing/data/secring.gpg b/xmlsecurity/qa/unit/signing/data/secring.gpg
index d98950c22ecd..f7626df5aee3 100644
--- a/xmlsecurity/qa/unit/signing/data/secring.gpg
+++ b/xmlsecurity/qa/unit/signing/data/secring.gpg
Binary files differ
diff --git a/xmlsecurity/qa/unit/signing/data/trustdb.gpg b/xmlsecurity/qa/unit/signing/data/trustdb.gpg
index c86bb02f3d79..1966b5374cfb 100644
--- a/xmlsecurity/qa/unit/signing/data/trustdb.gpg
+++ b/xmlsecurity/qa/unit/signing/data/trustdb.gpg
Binary files differ
diff --git a/xmlsecurity/qa/unit/signing/signing.cxx b/xmlsecurity/qa/unit/signing/signing.cxx
index 18dd99644f35..39a6d0ca6299 100644
--- a/xmlsecurity/qa/unit/signing/signing.cxx
+++ b/xmlsecurity/qa/unit/signing/signing.cxx
@@ -113,6 +113,8 @@ public:
void testODFBrokenStreamGPG();
/// Test a typical broken ODF signature where the XML dsig hash is corrupted.
void testODFBrokenDsigGPG();
+ /// Test loading an encrypted ODF document
+ void testODFEncryptedGPG();
#endif
CPPUNIT_TEST_SUITE(SigningTest);
CPPUNIT_TEST(testDescription);
@@ -141,6 +143,7 @@ public:
CPPUNIT_TEST(testODFUntrustedGoodGPG);
CPPUNIT_TEST(testODFBrokenStreamGPG);
CPPUNIT_TEST(testODFBrokenDsigGPG);
+ CPPUNIT_TEST(testODFEncryptedGPG);
#endif
CPPUNIT_TEST_SUITE_END();
@@ -739,6 +742,16 @@ void SigningTest::testODFBrokenDsigGPG()
CPPUNIT_ASSERT(pObjectShell);
CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN), static_cast<int>(pObjectShell->GetDocumentSignatureState()));
}
+
+void SigningTest::testODFEncryptedGPG()
+{
+ createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "encryptedGPG.odt");
+ SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
+ CPPUNIT_ASSERT(pBaseModel);
+ SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
+ CPPUNIT_ASSERT(pObjectShell);
+}
+
#endif
void SigningTest::registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx)