Kaydet (Commit) 0a58fdfd authored tarafından Thorsten Behrens's avatar Thorsten Behrens

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
üst 3f775118
......@@ -37,6 +37,7 @@ $(eval $(call gb_Library_add_defs,comphelper,\
))
$(eval $(call gb_Library_use_externals,comphelper,\
gpgmepp \
boost_headers \
icuuc \
icu_headers \
......
......@@ -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 )
......
......@@ -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 );
};
......
......@@ -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"
......
......@@ -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;
......
......@@ -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() );
......
......@@ -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;
......
......@@ -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 );
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment