1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
21 #include <com/sun/star/xml/sax/XDocumentHandler.hpp>
22 #include <com/sun/star/xml/sax/XAttributeList.hpp>
23 #include <com/sun/star/xml/crypto/DigestID.hpp>
24 #include <com/sun/star/xml/crypto/CipherID.hpp>
25 #include <com/sun/star/beans/PropertyValue.hpp>
26 #include <com/sun/star/beans/NamedValue.hpp>
27 #include <com/sun/star/uno/RuntimeException.hpp>
28 
29 #include "ManifestDefines.hxx"
30 #include "ManifestExport.hxx"
31 #include <sax/tools/converter.hxx>
32 
33 #include <osl/diagnose.h>
34 #include <rtl/ustrbuf.hxx>
35 #include <comphelper/base64.hxx>
36 #include <comphelper/documentconstants.hxx>
37 #include <comphelper/attributelist.hxx>
38 
39 using namespace ::com::sun::star;
40 
41 #if OSL_DEBUG_LEVEL > 0
42 #define THROW_WHERE SAL_WHERE
43 #else
44 #define THROW_WHERE ""
45 #endif
46 
47 ManifestExport::ManifestExport( uno::Reference< xml::sax::XDocumentHandler > const & xHandler,  const uno::Sequence< uno::Sequence < beans::PropertyValue > >& rManList )
48 {
49     const OUString sFileEntryElement     ( ELEMENT_FILE_ENTRY );
50     const OUString sManifestElement      ( ELEMENT_MANIFEST );
51     const OUString sEncryptionDataElement( ELEMENT_ENCRYPTION_DATA );
52     const OUString sAlgorithmElement     ( ELEMENT_ALGORITHM );
53     const OUString sStartKeyGenerationElement ( ELEMENT_START_KEY_GENERATION );
54     const OUString sKeyDerivationElement ( ELEMENT_KEY_DERIVATION );
55 
56     const OUString sCdataAttribute       ( ATTRIBUTE_CDATA );
57     const OUString sMediaTypeAttribute   ( ATTRIBUTE_MEDIA_TYPE );
58     const OUString sVersionAttribute     ( ATTRIBUTE_VERSION );
59     const OUString sFullPathAttribute    ( ATTRIBUTE_FULL_PATH );
60     const OUString sSizeAttribute        ( ATTRIBUTE_SIZE );
61     const OUString sKeySizeAttribute     ( ATTRIBUTE_KEY_SIZE );
62     const OUString sSaltAttribute        ( ATTRIBUTE_SALT );
63     const OUString sInitialisationVectorAttribute ( ATTRIBUTE_INITIALISATION_VECTOR );
64     const OUString sIterationCountAttribute  ( ATTRIBUTE_ITERATION_COUNT );
65     const OUString sAlgorithmNameAttribute   ( ATTRIBUTE_ALGORITHM_NAME );
66     const OUString sStartKeyGenerationNameAttribute ( ATTRIBUTE_START_KEY_GENERATION_NAME );
67     const OUString sKeyDerivationNameAttribute   ( ATTRIBUTE_KEY_DERIVATION_NAME );
68     const OUString sChecksumTypeAttribute    ( ATTRIBUTE_CHECKSUM_TYPE );
69     const OUString sChecksumAttribute    ( ATTRIBUTE_CHECKSUM);
70 
71     const OUString sKeyInfoElement              ( ELEMENT_ENCRYPTED_KEYINFO );
72     const OUString sManifestKeyInfoElement      ( ELEMENT_MANIFEST_KEYINFO );
73     const OUString sEncryptedKeyElement         ( ELEMENT_ENCRYPTEDKEY );
74     const OUString sEncryptionMethodElement     ( ELEMENT_ENCRYPTIONMETHOD );
75     const OUString sPgpDataElement              ( ELEMENT_PGPDATA );
76     const OUString sPgpKeyIDElement             ( ELEMENT_PGPKEYID );
77     const OUString sPGPKeyPacketElement         ( ELEMENT_PGPKEYPACKET );
78     const OUString sAlgorithmAttribute          ( ATTRIBUTE_ALGORITHM );
79     const OUString sCipherDataElement           ( ELEMENT_CIPHERDATA );
80     const OUString sCipherValueElement          ( ELEMENT_CIPHERVALUE );
81     const OUString sKeyInfo                     ( "KeyInfo" );
82     const OUString sPgpKeyIDProperty            ( "KeyId" );
83     const OUString sPgpKeyPacketProperty        ( "KeyPacket" );
84     const OUString sCipherValueProperty         ( "CipherValue" );
85     const OUString sFullPathProperty     ( "FullPath" );
86     const OUString sVersionProperty  ( "Version" );
87     const OUString sMediaTypeProperty    ( "MediaType" );
88     const OUString sIterationCountProperty   ( "IterationCount" );
89     const OUString sDerivedKeySizeProperty  ( "DerivedKeySize" );
90     const OUString sSaltProperty         ( "Salt" );
91     const OUString sInitialisationVectorProperty( "InitialisationVector" );
92     const OUString sSizeProperty         ( "Size" );
93     const OUString sDigestProperty       ( "Digest" );
94     const OUString sEncryptionAlgProperty    ( "EncryptionAlgorithm" );
95     const OUString sStartKeyAlgProperty  ( "StartKeyAlgorithm" );
96     const OUString sDigestAlgProperty    ( "DigestAlgorithm" );
97 
98     const OUString sWhiteSpace           ( " " );
99 
100     const OUString sSHA256_URL_ODF12     ( SHA256_URL_ODF12 );
101     const OUString sSHA256_URL           ( SHA256_URL );
102     const OUString  sSHA1_Name           ( SHA1_NAME );
103 
104     const OUString  sSHA1_1k_Name        ( SHA1_1K_NAME );
105     const OUString  sSHA256_1k_URL       ( SHA256_1K_URL );
106 
107     const OUString  sBlowfish_Name       ( BLOWFISH_NAME );
108     const OUString  sAES256_URL          ( AES256_URL );
109 
110     const OUString  sPBKDF2_Name         ( PBKDF2_NAME );
111     const OUString  sPGP_Name            ( PGP_NAME );
112 
113     ::comphelper::AttributeList * pRootAttrList = new ::comphelper::AttributeList;
114     const uno::Sequence < beans::PropertyValue > *pSequence = rManList.getConstArray();
115     const sal_uInt32 nManLength = rManList.getLength();
116 
117     // find the mediatype of the document if any
118     OUString aDocMediaType;
119     OUString aDocVersion;
120     sal_Int32 nRootFolderPropIndex=-1;
121     for (sal_uInt32 nInd = 0; nInd < nManLength ; nInd++ )
122     {
123         OUString aMediaType;
124         OUString aPath;
125         OUString aVersion;
126 
127         const beans::PropertyValue *pValue = pSequence[nInd].getConstArray();
128         for (sal_uInt32 j = 0, nNum = pSequence[nInd].getLength(); j < nNum; j++, pValue++)
129         {
130             if (pValue->Name == sMediaTypeProperty )
131             {
132                 pValue->Value >>= aMediaType;
133             }
134             else if (pValue->Name == sFullPathProperty )
135             {
136                 pValue->Value >>= aPath;
137             }
138             else if (pValue->Name == sVersionProperty )
139             {
140                 pValue->Value >>= aVersion;
141             }
142 
143             if ( !aPath.isEmpty() && !aMediaType.isEmpty() && !aVersion.isEmpty() )
144                 break;
145         }
146 
147         if ( aPath == "/" )
148         {
149             aDocMediaType = aMediaType;
150             aDocVersion = aVersion;
151             nRootFolderPropIndex = nInd;
152             break;
153         }
154     }
155 
156     bool bProvideDTD = false;
157     bool bAcceptNonemptyVersion = false;
158     bool bStoreStartKeyGeneration = false;
159     if ( !aDocMediaType.isEmpty() )
160     {
161         if ( aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_TEXT_ASCII
162           || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_TEXT_WEB_ASCII
163           || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_ASCII
164           || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_ASCII
165           || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_ASCII
166           || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_ASCII
167           || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_CHART_ASCII
168           || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII
169           || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_ASCII
170           || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_TEXT_TEMPLATE_ASCII
171           || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_TEMPLATE_ASCII
172           || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_TEMPLATE_ASCII
173           || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_TEMPLATE_ASCII
174           || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_TEMPLATE_ASCII
175           || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_CHART_TEMPLATE_ASCII
176           || aDocMediaType == MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_TEMPLATE_ASCII )
177 
178         {
179             // oasis format
180             pRootAttrList->AddAttribute ( ATTRIBUTE_XMLNS,
181                                         sCdataAttribute,
182                                         MANIFEST_OASIS_NAMESPACE );
183             bAcceptNonemptyVersion = true;
184             if ( aDocVersion.compareTo( ODFVER_012_TEXT ) >= 0 )
185             {
186                 // this is ODF12 or later generation, let encrypted
187                 // streams contain start-key-generation entry
188                 bStoreStartKeyGeneration = true;
189                 pRootAttrList->AddAttribute ( sVersionAttribute, sCdataAttribute, aDocVersion );
190                 // plus gpg4libre extensions - loext NS for that
191                 pRootAttrList->AddAttribute ( ATTRIBUTE_XMLNS_LOEXT,
192                                               sCdataAttribute,
193                                               MANIFEST_LOEXT_NAMESPACE );
194             }
195         }
196         else
197         {
198             // even if it is no SO6 format the namespace must be specified
199             // thus SO6 format is used as default one
200             pRootAttrList->AddAttribute ( ATTRIBUTE_XMLNS,
201                                         sCdataAttribute,
202                                         MANIFEST_NAMESPACE );
203 
204             bProvideDTD = true;
205         }
206     }
207 
208     uno::Reference < xml::sax::XAttributeList > xRootAttrList (pRootAttrList);
209 
210     xHandler->startDocument();
211     uno::Reference < xml::sax::XExtendedDocumentHandler > xExtHandler ( xHandler, uno::UNO_QUERY );
212     if ( xExtHandler.is() && bProvideDTD )
213     {
214         xExtHandler->unknown ( MANIFEST_DOCTYPE );
215         xHandler->ignorableWhitespace ( sWhiteSpace );
216     }
217     xHandler->startElement( sManifestElement, xRootAttrList );
218 
219     const uno::Any *pKeyInfoProperty = nullptr;
220     if ( nRootFolderPropIndex >= 0 )
221     {
222         // do we have package-wide encryption info?
223         const beans::PropertyValue *pValue =
224             pSequence[nRootFolderPropIndex].getConstArray();
225         for (sal_uInt32 j = 0, nNum = pSequence[nRootFolderPropIndex].getLength(); j < nNum; j++, pValue++)
226         {
227             if (pValue->Name == sKeyInfo )
228                 pKeyInfoProperty = &pValue->Value;
229         }
230 
231         if ( pKeyInfoProperty )
232         {
233             // yeah, so that goes directly below the manifest:manifest
234             // element
235             ::comphelper::AttributeList * pNewAttrList = new ::comphelper::AttributeList;
236             uno::Reference < xml::sax::XAttributeList > xNewAttrList (pNewAttrList);
237             OUStringBuffer aBuffer;
238 
239             xHandler->ignorableWhitespace ( sWhiteSpace );
240 
241             // ==== manifest:keyinfo & children
242             xHandler->startElement( sManifestKeyInfoElement, nullptr );
243             xHandler->ignorableWhitespace ( sWhiteSpace );
244 
245             uno::Sequence< uno::Sequence < beans::NamedValue > > aKeyInfoSequence;
246             *pKeyInfoProperty >>= aKeyInfoSequence;
247             const uno::Sequence < beans::NamedValue > *pKeyInfoSequence = aKeyInfoSequence.getConstArray();
248             const sal_uInt32 nKeyInfoLength = aKeyInfoSequence.getLength();
249             for (sal_uInt32 nInd = 0; nInd < nKeyInfoLength ; nInd++ )
250             {
251                 uno::Sequence < sal_Int8 > aPgpKeyID;
252                 uno::Sequence < sal_Int8 > aPgpKeyPacket;
253                 uno::Sequence < sal_Int8 > aCipherValue;
254                 const beans::NamedValue *pNValue = pKeyInfoSequence[nInd].getConstArray();
255                 for (sal_uInt32 j = 0, nNum = pKeyInfoSequence[nInd].getLength(); j < nNum; j++, pNValue++)
256                 {
257                     if (pNValue->Name == sPgpKeyIDProperty )
258                         pNValue->Value >>= aPgpKeyID;
259                     else if (pNValue->Name == sPgpKeyPacketProperty )
260                         pNValue->Value >>= aPgpKeyPacket;
261                     else if (pNValue->Name == sCipherValueProperty )
262                         pNValue->Value >>= aCipherValue;
263                 }
264 
265                 if (aPgpKeyID.hasElements() && aCipherValue.hasElements() )
266                 {
267                     // ==== manifest:encrypted-key & children - one for each recipient
268                     xHandler->startElement( sEncryptedKeyElement, nullptr );
269                     xHandler->ignorableWhitespace ( sWhiteSpace );
270 
271                     // TODO: the algorithm should rather be configurable
272                     pNewAttrList->AddAttribute ( sAlgorithmAttribute, sCdataAttribute,
273                                                  "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p" );
274                     xHandler->startElement( sEncryptionMethodElement, xNewAttrList );
275                     xHandler->ignorableWhitespace ( sWhiteSpace );
276                     xHandler->endElement( sEncryptionMethodElement );
277 
278                     xHandler->startElement( sKeyInfoElement, nullptr );
279                     xHandler->ignorableWhitespace ( sWhiteSpace );
280 
281                     xHandler->startElement( sPgpDataElement, nullptr );
282                     xHandler->ignorableWhitespace ( sWhiteSpace );
283 
284                     xHandler->startElement( sPgpKeyIDElement, nullptr );
285                     ::comphelper::Base64::encode(aBuffer, aPgpKeyID);
286                     xHandler->characters( aBuffer.makeStringAndClear() );
287                     xHandler->endElement( sPgpKeyIDElement );
288                     xHandler->ignorableWhitespace ( sWhiteSpace );
289 
290                     // key packet is optional
291                     if (aPgpKeyPacket.hasElements())
292                     {
293                         xHandler->startElement( sPGPKeyPacketElement, nullptr );
294                         ::comphelper::Base64::encode(aBuffer, aPgpKeyPacket);
295                         xHandler->characters( aBuffer.makeStringAndClear() );
296                         xHandler->endElement( sPGPKeyPacketElement );
297                         xHandler->ignorableWhitespace ( sWhiteSpace );
298                     }
299 
300                     xHandler->endElement( sPgpDataElement );
301                     xHandler->ignorableWhitespace ( sWhiteSpace );
302 
303                     xHandler->endElement( sKeyInfoElement );
304                     xHandler->ignorableWhitespace ( sWhiteSpace );
305 
306                     xHandler->startElement( sCipherDataElement, nullptr );
307                     xHandler->ignorableWhitespace ( sWhiteSpace );
308 
309                     xHandler->startElement( sCipherValueElement, nullptr );
310                     ::comphelper::Base64::encode(aBuffer, aCipherValue);
311                     xHandler->characters( aBuffer.makeStringAndClear() );
312                     xHandler->endElement( sCipherValueElement );
313                     xHandler->ignorableWhitespace ( sWhiteSpace );
314 
315                     xHandler->endElement( sCipherDataElement );
316                     xHandler->ignorableWhitespace ( sWhiteSpace );
317 
318                     xHandler->endElement( sEncryptedKeyElement );
319                     xHandler->ignorableWhitespace ( sWhiteSpace );
320                 }
321             }
322 
323             xHandler->endElement( sManifestKeyInfoElement );
324             xHandler->ignorableWhitespace ( sWhiteSpace );
325         }
326     }
327 
328     // now write individual file entries
329     for (sal_uInt32 i = 0 ; i < nManLength ; i++)
330     {
331         ::comphelper::AttributeList *pAttrList = new ::comphelper::AttributeList;
332         const beans::PropertyValue *pValue = pSequence[i].getConstArray();
333         OUString aString;
334         const uno::Any *pVector = nullptr, *pSalt = nullptr, *pIterationCount = nullptr, *pDigest = nullptr, *pDigestAlg = nullptr, *pEncryptAlg = nullptr, *pStartKeyAlg = nullptr, *pDerivedKeySize = nullptr;
335         for (sal_uInt32 j = 0, nNum = pSequence[i].getLength(); j < nNum; j++, pValue++)
336         {
337             if (pValue->Name == sMediaTypeProperty )
338             {
339                 pValue->Value >>= aString;
340                 pAttrList->AddAttribute ( sMediaTypeAttribute, sCdataAttribute, aString );
341             }
342             else if (pValue->Name == sVersionProperty )
343             {
344                 pValue->Value >>= aString;
345                 // the version is stored only if it is not empty
346                 if ( bAcceptNonemptyVersion && !aString.isEmpty() )
347                     pAttrList->AddAttribute ( sVersionAttribute, sCdataAttribute, aString );
348             }
349             else if (pValue->Name == sFullPathProperty )
350             {
351                 pValue->Value >>= aString;
352                 pAttrList->AddAttribute ( sFullPathAttribute, sCdataAttribute, aString );
353             }
354             else if (pValue->Name == sSizeProperty )
355             {
356                 sal_Int64 nSize = 0;
357                 pValue->Value >>= nSize;
358                 OUStringBuffer aBuffer;
359                 aBuffer.append ( nSize );
360                 pAttrList->AddAttribute ( sSizeAttribute, sCdataAttribute, aBuffer.makeStringAndClear() );
361             }
362             else if (pValue->Name == sInitialisationVectorProperty )
363                 pVector = &pValue->Value;
364             else if (pValue->Name == sSaltProperty )
365                 pSalt = &pValue->Value;
366             else if (pValue->Name == sIterationCountProperty )
367                 pIterationCount = &pValue->Value;
368             else if (pValue->Name == sDigestProperty )
369                 pDigest = &pValue->Value;
370             else if (pValue->Name == sDigestAlgProperty )
371                 pDigestAlg = &pValue->Value;
372             else if (pValue->Name == sEncryptionAlgProperty )
373                 pEncryptAlg = &pValue->Value;
374             else if (pValue->Name == sStartKeyAlgProperty )
375                 pStartKeyAlg = &pValue->Value;
376             else if (pValue->Name == sDerivedKeySizeProperty )
377                 pDerivedKeySize = &pValue->Value;
378         }
379 
380         xHandler->ignorableWhitespace ( sWhiteSpace );
381         uno::Reference < xml::sax::XAttributeList > xAttrList ( pAttrList );
382         xHandler->startElement( sFileEntryElement , xAttrList);
383         if ( pVector && pSalt && pIterationCount && pDigest && pDigestAlg && pEncryptAlg && pStartKeyAlg && pDerivedKeySize )
384         {
385             // ==== Encryption Data
386             ::comphelper::AttributeList * pNewAttrList = new ::comphelper::AttributeList;
387             uno::Reference < xml::sax::XAttributeList > xNewAttrList (pNewAttrList);
388             OUStringBuffer aBuffer;
389             uno::Sequence < sal_Int8 > aSequence;
390 
391             xHandler->ignorableWhitespace ( sWhiteSpace );
392 
393             // ==== Digest
394             OUString sChecksumType;
395             sal_Int32 nDigestAlgID = 0;
396             *pDigestAlg >>= nDigestAlgID;
397             if ( nDigestAlgID == xml::crypto::DigestID::SHA256_1K )
398                 sChecksumType = sSHA256_1k_URL;
399             else if ( nDigestAlgID == xml::crypto::DigestID::SHA1_1K )
400                 sChecksumType = sSHA1_1k_Name;
401             else
402                 throw uno::RuntimeException( THROW_WHERE "Unexpected digest algorithm is provided!" );
403 
404             pNewAttrList->AddAttribute ( sChecksumTypeAttribute, sCdataAttribute, sChecksumType );
405             *pDigest >>= aSequence;
406             ::comphelper::Base64::encode(aBuffer, aSequence);
407             pNewAttrList->AddAttribute ( sChecksumAttribute, sCdataAttribute, aBuffer.makeStringAndClear() );
408 
409             xHandler->startElement( sEncryptionDataElement , xNewAttrList);
410 
411             // ==== Algorithm
412             pNewAttrList = new ::comphelper::AttributeList;
413             xNewAttrList = pNewAttrList;
414 
415             sal_Int32 nEncAlgID = 0;
416             sal_Int32 nDerivedKeySize = 0;
417             *pEncryptAlg >>= nEncAlgID;
418             *pDerivedKeySize >>= nDerivedKeySize;
419 
420             OUString sEncAlgName;
421             if ( nEncAlgID == xml::crypto::CipherID::AES_CBC_W3C_PADDING )
422             {
423                 OSL_ENSURE( nDerivedKeySize, "Unexpected key size is provided!" );
424                 if ( nDerivedKeySize != 32 )
425                     throw uno::RuntimeException( THROW_WHERE "Unexpected key size is provided!" );
426 
427                 sEncAlgName = sAES256_URL;
428             }
429             else if ( nEncAlgID == xml::crypto::CipherID::BLOWFISH_CFB_8 )
430             {
431                 sEncAlgName = sBlowfish_Name;
432             }
433             else
434                 throw uno::RuntimeException( THROW_WHERE "Unexpected encryption algorithm is provided!" );
435 
436             pNewAttrList->AddAttribute ( sAlgorithmNameAttribute, sCdataAttribute, sEncAlgName );
437 
438             *pVector >>= aSequence;
439             ::comphelper::Base64::encode(aBuffer, aSequence);
440             pNewAttrList->AddAttribute ( sInitialisationVectorAttribute, sCdataAttribute, aBuffer.makeStringAndClear() );
441 
442             xHandler->ignorableWhitespace ( sWhiteSpace );
443             xHandler->startElement( sAlgorithmElement , xNewAttrList);
444             xHandler->ignorableWhitespace ( sWhiteSpace );
445             xHandler->endElement( sAlgorithmElement );
446 
447             // ==== Key Derivation
448             pNewAttrList = new ::comphelper::AttributeList;
449             xNewAttrList = pNewAttrList;
450 
451             if ( pKeyInfoProperty )
452             {
453                 pNewAttrList->AddAttribute ( sKeyDerivationNameAttribute,
454                                              sCdataAttribute,
455                                              sPGP_Name );
456                 // no start-key-generation needed, our session key has
457                 // max size already
458                 bStoreStartKeyGeneration = false;
459             }
460             else
461             {
462                 pNewAttrList->AddAttribute ( sKeyDerivationNameAttribute,
463                                              sCdataAttribute,
464                                              sPBKDF2_Name );
465 
466                 if ( bStoreStartKeyGeneration )
467                 {
468                     aBuffer.append( nDerivedKeySize );
469                     pNewAttrList->AddAttribute ( sKeySizeAttribute, sCdataAttribute, aBuffer.makeStringAndClear() );
470                 }
471 
472                 sal_Int32 nCount = 0;
473                 *pIterationCount >>= nCount;
474                 aBuffer.append (nCount);
475                 pNewAttrList->AddAttribute ( sIterationCountAttribute, sCdataAttribute, aBuffer.makeStringAndClear() );
476 
477                 *pSalt >>= aSequence;
478                 ::comphelper::Base64::encode(aBuffer, aSequence);
479                 pNewAttrList->AddAttribute ( sSaltAttribute, sCdataAttribute, aBuffer.makeStringAndClear() );
480             }
481 
482             xHandler->ignorableWhitespace ( sWhiteSpace );
483             xHandler->startElement( sKeyDerivationElement , xNewAttrList);
484             xHandler->ignorableWhitespace ( sWhiteSpace );
485             xHandler->endElement( sKeyDerivationElement );
486 
487             // we have to store start-key-generation element as the last one to workaround the parsing problem
488             // in OOo3.1 and older versions
489             if ( bStoreStartKeyGeneration )
490             {
491                 // ==== Start Key Generation
492                 pNewAttrList = new ::comphelper::AttributeList;
493                 xNewAttrList = pNewAttrList;
494 
495                 OUString sStartKeyAlg;
496                 OUString sStartKeySize;
497                 sal_Int32 nStartKeyAlgID = 0;
498                 *pStartKeyAlg >>= nStartKeyAlgID;
499                 if ( nStartKeyAlgID == xml::crypto::DigestID::SHA256 )
500                 {
501                     sStartKeyAlg = sSHA256_URL_ODF12; // TODO use sSHA256_URL
502                     (void) sSHA256_URL;
503                     aBuffer.append( sal_Int32(32) );
504                     sStartKeySize = aBuffer.makeStringAndClear();
505                 }
506                 else if ( nStartKeyAlgID == xml::crypto::DigestID::SHA1 )
507                 {
508                     sStartKeyAlg = sSHA1_Name;
509                     aBuffer.append( sal_Int32(20) );
510                     sStartKeySize = aBuffer.makeStringAndClear();
511                 }
512                 else
513                     throw uno::RuntimeException( THROW_WHERE "Unexpected start key algorithm is provided!" );
514 
515                 pNewAttrList->AddAttribute ( sStartKeyGenerationNameAttribute, sCdataAttribute, sStartKeyAlg );
516                 pNewAttrList->AddAttribute ( sKeySizeAttribute, sCdataAttribute, sStartKeySize );
517 
518                 xHandler->ignorableWhitespace ( sWhiteSpace );
519                 xHandler->startElement( sStartKeyGenerationElement , xNewAttrList);
520                 xHandler->ignorableWhitespace ( sWhiteSpace );
521                 xHandler->endElement( sStartKeyGenerationElement );
522             }
523 
524             xHandler->ignorableWhitespace ( sWhiteSpace );
525             xHandler->endElement( sEncryptionDataElement );
526         }
527         xHandler->ignorableWhitespace ( sWhiteSpace );
528         xHandler->endElement( sFileEntryElement );
529     }
530     xHandler->ignorableWhitespace ( sWhiteSpace );
531     xHandler->endElement( sManifestElement );
532     xHandler->endDocument();
533 }
534 
535 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
536