From 3f775118a6d9e6cbd49edf7d5309e29edeeaff47 Mon Sep 17 00:00:00 2001 From: Thorsten Behrens Date: Fri, 18 Aug 2017 21:34:11 +0200 Subject: gpg4libre: add manifest entries for gpg encryption Change-Id: I71bd7e2c6c73d997fa1ed5bb36fdc2873daca10c --- package/source/manifest/ManifestDefines.hxx | 13 +++ package/source/manifest/ManifestExport.cxx | 175 +++++++++++++++++++++++++--- 2 files changed, 174 insertions(+), 14 deletions(-) (limited to 'package') diff --git a/package/source/manifest/ManifestDefines.hxx b/package/source/manifest/ManifestDefines.hxx index 968aed648e6a..c68c241c7514 100644 --- a/package/source/manifest/ManifestDefines.hxx +++ b/package/source/manifest/ManifestDefines.hxx @@ -24,8 +24,10 @@ #define MANIFEST_NSPREFIX "manifest:" #define ELEMENT_MANIFEST "manifest:manifest" #define ATTRIBUTE_XMLNS "xmlns:manifest" +#define ATTRIBUTE_XMLNS_LOEXT "xmlns:loext" #define MANIFEST_NAMESPACE "http://openoffice.org/2001/manifest" #define MANIFEST_OASIS_NAMESPACE "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" +#define MANIFEST_LOEXT_NAMESPACE "urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" #define MANIFEST_DOCTYPE "" #define ATTRIBUTE_CDATA "CDATA" @@ -34,6 +36,16 @@ #define ATTRIBUTE_VERSION "manifest:version" #define ATTRIBUTE_MEDIA_TYPE "manifest:media-type" #define ATTRIBUTE_SIZE "manifest:size" +#define ELEMENT_MANIFEST_KEYINFO "loext:keyinfo" +#define ELEMENT_ENCRYPTED_KEYINFO "loext:KeyInfo" +#define ELEMENT_ENCRYPTEDKEY "loext:encrypted-key" +#define ELEMENT_ENCRYPTIONMETHOD "loext:encryption-method" +#define ELEMENT_PGPDATA "loext:PGPData" +#define ELEMENT_PGPKEYID "loext:PGPKeyID" +#define ELEMENT_PGPKEYPACKET "loext:PGPKeyPacket" +#define ATTRIBUTE_ALGORITHM "loext:PGPAlgorithm" +#define ELEMENT_CIPHERDATA "loext:CipherData" +#define ELEMENT_CIPHERVALUE "loext:CipherValue" #define ELEMENT_ENCRYPTION_DATA "manifest:encryption-data" #define ATTRIBUTE_CHECKSUM_TYPE "manifest:checksum-type" @@ -69,6 +81,7 @@ #define AES256_URL "http://www.w3.org/2001/04/xmlenc#aes256-cbc" #define PBKDF2_NAME "PBKDF2" +#define PGP_NAME "PGP" #define PBKDF2_URL "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0#pbkdf2" #endif diff --git a/package/source/manifest/ManifestExport.cxx b/package/source/manifest/ManifestExport.cxx index 60a5128e945d..5cf87334ca62 100644 --- a/package/source/manifest/ManifestExport.cxx +++ b/package/source/manifest/ManifestExport.cxx @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "ManifestDefines.hxx" @@ -66,11 +67,25 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con const OUString sChecksumTypeAttribute ( ATTRIBUTE_CHECKSUM_TYPE ); const OUString sChecksumAttribute ( ATTRIBUTE_CHECKSUM); + const OUString sKeyInfoElement ( ELEMENT_ENCRYPTED_KEYINFO ); + const OUString sManifestKeyInfoElement ( ELEMENT_MANIFEST_KEYINFO ); + const OUString sEncryptedKeyElement ( ELEMENT_ENCRYPTEDKEY ); + const OUString sEncryptionMethodElement ( ELEMENT_ENCRYPTIONMETHOD ); + const OUString sPgpDataElement ( ELEMENT_PGPDATA ); + const OUString sPgpKeyIDElement ( ELEMENT_PGPKEYID ); + const OUString sPGPKeyPacketElement ( ELEMENT_PGPKEYPACKET ); + const OUString sAlgorithmAttribute ( ATTRIBUTE_ALGORITHM ); + const OUString sCipherDataElement ( ELEMENT_CIPHERDATA ); + const OUString sCipherValueElement ( ELEMENT_CIPHERVALUE ); + const OUString sKeyInfo ( "KeyInfo" ); + const OUString sPgpKeyIDProperty ( "KeyId" ); + const OUString sPgpKeyPacketProperty ( "KeyPacket" ); + const OUString sCipherValueProperty ( "CipherValue" ); const OUString sFullPathProperty ( "FullPath" ); const OUString sVersionProperty ( "Version" ); const OUString sMediaTypeProperty ( "MediaType" ); const OUString sIterationCountProperty ( "IterationCount" ); - const OUString sDerivedKeySizeProperty ( "DerivedKeySize" ); + const OUString sDerivedKeySizeProperty ( "DerivedKeySize" ); const OUString sSaltProperty ( "Salt" ); const OUString sInitialisationVectorProperty( "InitialisationVector" ); const OUString sSizeProperty ( "Size" ); @@ -92,6 +107,7 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con const OUString sAES256_URL ( AES256_URL ); const OUString sPBKDF2_Name ( PBKDF2_NAME ); + const OUString sPGP_Name ( PGP_NAME ); ::comphelper::AttributeList * pRootAttrList = new ::comphelper::AttributeList; const uno::Sequence < beans::PropertyValue > *pSequence = rManList.getConstArray(); @@ -100,6 +116,7 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con // find the mediatype of the document if any OUString aDocMediaType; OUString aDocVersion; + sal_Int32 nRootFolderPropIndex=-1; for (sal_uInt32 nInd = 0; nInd < nManLength ; nInd++ ) { OUString aMediaType; @@ -130,6 +147,7 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con { aDocMediaType = aMediaType; aDocVersion = aVersion; + nRootFolderPropIndex = nInd; break; } } @@ -164,9 +182,14 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con bAcceptNonemptyVersion = true; if ( aDocVersion.compareTo( ODFVER_012_TEXT ) >= 0 ) { - // this is ODF12 generation, let encrypted streams contain start-key-generation entry + // this is ODF12 or later generation, let encrypted + // streams contain start-key-generation entry bStoreStartKeyGeneration = true; pRootAttrList->AddAttribute ( sVersionAttribute, sCdataAttribute, aDocVersion ); + // plus gpg4libre extensions - loext NS for that + pRootAttrList->AddAttribute ( ATTRIBUTE_XMLNS_LOEXT, + sCdataAttribute, + MANIFEST_LOEXT_NAMESPACE ); } } else @@ -192,6 +215,116 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con } xHandler->startElement( sManifestElement, xRootAttrList ); + const uno::Any *pKeyInfoProperty = nullptr; + if ( nRootFolderPropIndex >= 0 ) + { + // do we have package-wide encryption info? + const beans::PropertyValue *pValue = + pSequence[nRootFolderPropIndex].getConstArray(); + for (sal_uInt32 j = 0, nNum = pSequence[nRootFolderPropIndex].getLength(); j < nNum; j++, pValue++) + { + if (pValue->Name == sKeyInfo ) + pKeyInfoProperty = &pValue->Value; + } + + if ( pKeyInfoProperty ) + { + // yeah, so that goes directly below the manifest:manifest + // element + ::comphelper::AttributeList * pNewAttrList = new ::comphelper::AttributeList; + uno::Reference < xml::sax::XAttributeList > xNewAttrList (pNewAttrList); + OUStringBuffer aBuffer; + + xHandler->ignorableWhitespace ( sWhiteSpace ); + + // ==== manifest:keyinfo & children + xHandler->startElement( sManifestKeyInfoElement, nullptr ); + xHandler->ignorableWhitespace ( sWhiteSpace ); + + uno::Sequence< uno::Sequence < beans::NamedValue > > aKeyInfoSequence; + *pKeyInfoProperty >>= aKeyInfoSequence; + const uno::Sequence < beans::NamedValue > *pKeyInfoSequence = aKeyInfoSequence.getConstArray(); + const sal_uInt32 nKeyInfoLength = aKeyInfoSequence.getLength(); + for (sal_uInt32 nInd = 0; nInd < nKeyInfoLength ; nInd++ ) + { + uno::Sequence < sal_Int8 > aPgpKeyID; + uno::Sequence < sal_Int8 > aPgpKeyPacket; + uno::Sequence < sal_Int8 > aCipherValue; + const beans::NamedValue *pNValue = pKeyInfoSequence[nInd].getConstArray(); + for (sal_uInt32 j = 0, nNum = pKeyInfoSequence[nInd].getLength(); j < nNum; j++, pNValue++) + { + if (pNValue->Name == sPgpKeyIDProperty ) + pNValue->Value >>= aPgpKeyID; + else if (pNValue->Name == sPgpKeyPacketProperty ) + pNValue->Value >>= aPgpKeyPacket; + else if (pNValue->Name == sCipherValueProperty ) + pNValue->Value >>= aCipherValue; + } + + if (aPgpKeyID.hasElements() && aCipherValue.hasElements() ) + { + // ==== manifest:encrypted-key & children - one for each recipient + xHandler->startElement( sEncryptedKeyElement, nullptr ); + xHandler->ignorableWhitespace ( sWhiteSpace ); + + // TODO: the algorithm should rather be configurable + pNewAttrList->AddAttribute ( sAlgorithmAttribute, sCdataAttribute, + "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p" ); + xHandler->startElement( sEncryptionMethodElement, xNewAttrList ); + xHandler->ignorableWhitespace ( sWhiteSpace ); + xHandler->endElement( sEncryptionMethodElement ); + + xHandler->startElement( sKeyInfoElement, nullptr ); + xHandler->ignorableWhitespace ( sWhiteSpace ); + + xHandler->startElement( sPgpDataElement, nullptr ); + xHandler->ignorableWhitespace ( sWhiteSpace ); + + xHandler->startElement( sPgpKeyIDElement, nullptr ); + ::sax::Converter::encodeBase64(aBuffer, aPgpKeyID); + xHandler->characters( aBuffer.makeStringAndClear() ); + xHandler->endElement( sPgpKeyIDElement ); + xHandler->ignorableWhitespace ( sWhiteSpace ); + + // key packet is optional + if (aPgpKeyPacket.hasElements()) + { + xHandler->startElement( sPGPKeyPacketElement, nullptr ); + ::sax::Converter::encodeBase64(aBuffer, aPgpKeyPacket); + xHandler->characters( aBuffer.makeStringAndClear() ); + xHandler->endElement( sPGPKeyPacketElement ); + xHandler->ignorableWhitespace ( sWhiteSpace ); + } + + xHandler->endElement( sPgpDataElement ); + xHandler->ignorableWhitespace ( sWhiteSpace ); + + xHandler->endElement( sKeyInfoElement ); + xHandler->ignorableWhitespace ( sWhiteSpace ); + + xHandler->startElement( sCipherDataElement, nullptr ); + xHandler->ignorableWhitespace ( sWhiteSpace ); + + xHandler->startElement( sCipherValueElement, nullptr ); + ::sax::Converter::encodeBase64(aBuffer, aCipherValue); + xHandler->characters( aBuffer.makeStringAndClear() ); + xHandler->endElement( sCipherValueElement ); + xHandler->ignorableWhitespace ( sWhiteSpace ); + + xHandler->endElement( sCipherDataElement ); + xHandler->ignorableWhitespace ( sWhiteSpace ); + + xHandler->endElement( sEncryptedKeyElement ); + xHandler->ignorableWhitespace ( sWhiteSpace ); + } + } + + xHandler->endElement( sManifestKeyInfoElement ); + xHandler->ignorableWhitespace ( sWhiteSpace ); + } + } + + // now write individual file entries for (sal_uInt32 i = 0 ; i < nManLength ; i++) { ::comphelper::AttributeList *pAttrList = new ::comphelper::AttributeList; @@ -314,22 +447,36 @@ ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > con pNewAttrList = new ::comphelper::AttributeList; xNewAttrList = pNewAttrList; - pNewAttrList->AddAttribute ( sKeyDerivationNameAttribute, sCdataAttribute, sPBKDF2_Name ); - - if ( bStoreStartKeyGeneration ) + if ( pKeyInfoProperty ) { - aBuffer.append( nDerivedKeySize ); - pNewAttrList->AddAttribute ( sKeySizeAttribute, sCdataAttribute, aBuffer.makeStringAndClear() ); + pNewAttrList->AddAttribute ( sKeyDerivationNameAttribute, + sCdataAttribute, + sPGP_Name ); + // no start-key-generation needed, our session key has + // max size already + bStoreStartKeyGeneration = false; } + else + { + pNewAttrList->AddAttribute ( sKeyDerivationNameAttribute, + sCdataAttribute, + sPBKDF2_Name ); + + if ( bStoreStartKeyGeneration ) + { + aBuffer.append( nDerivedKeySize ); + pNewAttrList->AddAttribute ( sKeySizeAttribute, sCdataAttribute, aBuffer.makeStringAndClear() ); + } - sal_Int32 nCount = 0; - *pIterationCount >>= nCount; - aBuffer.append (nCount); - pNewAttrList->AddAttribute ( sIterationCountAttribute, sCdataAttribute, aBuffer.makeStringAndClear() ); + sal_Int32 nCount = 0; + *pIterationCount >>= nCount; + aBuffer.append (nCount); + pNewAttrList->AddAttribute ( sIterationCountAttribute, sCdataAttribute, aBuffer.makeStringAndClear() ); - *pSalt >>= aSequence; - ::sax::Converter::encodeBase64(aBuffer, aSequence); - pNewAttrList->AddAttribute ( sSaltAttribute, sCdataAttribute, aBuffer.makeStringAndClear() ); + *pSalt >>= aSequence; + ::sax::Converter::encodeBase64(aBuffer, aSequence); + pNewAttrList->AddAttribute ( sSaltAttribute, sCdataAttribute, aBuffer.makeStringAndClear() ); + } xHandler->ignorableWhitespace ( sWhiteSpace ); xHandler->startElement( sKeyDerivationElement , xNewAttrList); -- cgit