1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
11 #include "oox/crypto/DocumentDecryption.hxx"
13 #include <comphelper/sequenceashashmap.hxx>
14 #include <sax/tools/converter.hxx>
15 #include <cppuhelper/implbase1.hxx>
17 #include <com/sun/star/io/XSeekable.hpp>
18 #include <com/sun/star/xml/sax/XFastParser.hpp>
19 #include <com/sun/star/xml/sax/XFastTokenHandler.hpp>
20 #include <com/sun/star/xml/sax/FastToken.hpp>
25 using namespace css::beans
;
26 using namespace css::io
;
27 using namespace css::lang
;
28 using namespace css::uno
;
29 using namespace css::xml::sax
;
30 using namespace css::xml
;
34 using ::comphelper::SequenceAsHashMap
;
35 using ::sax::Converter
;
39 vector
<sal_uInt8
> convertToVector(Sequence
<sal_Int8
>& input
)
41 const sal_uInt8
* inputArray
= reinterpret_cast<const sal_uInt8
*>( input
.getConstArray() );
42 return vector
<sal_uInt8
>(inputArray
, inputArray
+ input
.getLength());
45 class AgileTokenHandler
: public cppu::WeakImplHelper1
< XFastTokenHandler
>
48 virtual sal_Int32 SAL_CALL
getToken( const OUString
& /*nIdentifier*/ ) throw (RuntimeException
)
50 return FastToken::DONTKNOW
;
53 virtual sal_Int32 SAL_CALL
getTokenFromUTF8( const Sequence
< sal_Int8
>& /*nIdentifier*/ ) throw (RuntimeException
)
55 return FastToken::DONTKNOW
;
58 virtual OUString SAL_CALL
getIdentifier( sal_Int32
/*nToken*/ ) throw (RuntimeException
)
63 virtual Sequence
<sal_Int8
> SAL_CALL
getUTF8Identifier(sal_Int32
/*nToken*/) throw (RuntimeException
)
65 return Sequence
<sal_Int8
>();
69 class AgileDocumentHandler
: public ::cppu::WeakImplHelper1
< XFastDocumentHandler
>
71 AgileEncryptionInfo
& mInfo
;
74 AgileDocumentHandler(AgileEncryptionInfo
& rInfo
) :
78 void SAL_CALL
startDocument()
79 throw (RuntimeException
, SAXException
)
81 void SAL_CALL
endDocument()
82 throw (RuntimeException
, SAXException
)
84 void SAL_CALL
setDocumentLocator( const Reference
< XLocator
>& /*xLocator*/ )
85 throw (RuntimeException
, SAXException
)
87 void SAL_CALL
startFastElement( sal_Int32
/*Element*/, const Reference
< XFastAttributeList
>& /*Attribs*/ )
88 throw (RuntimeException
, SAXException
)
91 void SAL_CALL
startUnknownElement( const OUString
& /*aNamespace*/, const OUString
& aName
, const Reference
< XFastAttributeList
>& aAttributeList
)
92 throw (RuntimeException
, SAXException
)
94 if(aName
== "keyData")
96 Sequence
<Attribute
> aAttributes(aAttributeList
->getUnknownAttributes());
98 for (int i
=0; i
<aAttributes
.getLength(); i
++)
100 if (aAttributes
[i
].Name
== "saltValue")
102 Sequence
<sal_Int8
> keyDataSalt
;
103 Converter::decodeBase64(keyDataSalt
, aAttributes
[i
].Value
);
104 mInfo
.keyDataSalt
= convertToVector(keyDataSalt
);
108 else if(aName
== "encryptedKey")
110 Sequence
<Attribute
> aAttributes(aAttributeList
->getUnknownAttributes());
111 for (int i
=0; i
<aAttributes
.getLength(); i
++)
113 if (aAttributes
[i
].Name
== "spinCount")
115 Converter::convertNumber(mInfo
.spinCount
, aAttributes
[i
].Value
);
117 else if (aAttributes
[i
].Name
== "saltSize")
119 Converter::convertNumber(mInfo
.saltSize
, aAttributes
[i
].Value
);
121 else if (aAttributes
[i
].Name
== "blockSize")
123 Converter::convertNumber(mInfo
.blockSize
, aAttributes
[i
].Value
);
125 else if (aAttributes
[i
].Name
== "keyBits")
127 Converter::convertNumber(mInfo
.keyBits
, aAttributes
[i
].Value
);
129 else if (aAttributes
[i
].Name
== "hashSize")
131 Converter::convertNumber(mInfo
.hashSize
, aAttributes
[i
].Value
);
133 else if (aAttributes
[i
].Name
== "cipherAlgorithm")
135 mInfo
.cipherAlgorithm
= aAttributes
[i
].Value
;
137 else if (aAttributes
[i
].Name
== "cipherChaining")
139 mInfo
.cipherChaining
= aAttributes
[i
].Value
;
141 else if (aAttributes
[i
].Name
== "hashAlgorithm")
143 mInfo
.hashAlgorithm
= aAttributes
[i
].Value
;
145 else if (aAttributes
[i
].Name
== "saltValue")
147 Sequence
<sal_Int8
> saltValue
;
148 Converter::decodeBase64(saltValue
, aAttributes
[i
].Value
);
149 mInfo
.saltValue
= convertToVector(saltValue
);
151 else if (aAttributes
[i
].Name
== "encryptedVerifierHashInput")
153 Sequence
<sal_Int8
> encryptedVerifierHashInput
;
154 Converter::decodeBase64(encryptedVerifierHashInput
, aAttributes
[i
].Value
);
155 mInfo
.encryptedVerifierHashInput
= convertToVector(encryptedVerifierHashInput
);
157 else if (aAttributes
[i
].Name
== "encryptedVerifierHashValue")
159 Sequence
<sal_Int8
> encryptedVerifierHashValue
;
160 Converter::decodeBase64(encryptedVerifierHashValue
, aAttributes
[i
].Value
);
161 mInfo
.encryptedVerifierHashValue
= convertToVector(encryptedVerifierHashValue
);
163 else if (aAttributes
[i
].Name
== "encryptedKeyValue")
165 Sequence
<sal_Int8
> encryptedKeyValue
;
166 Converter::decodeBase64(encryptedKeyValue
, aAttributes
[i
].Value
);
167 mInfo
.encryptedKeyValue
= convertToVector(encryptedKeyValue
);
173 void SAL_CALL
endFastElement( sal_Int32
/*aElement*/ )
174 throw (RuntimeException
, SAXException
)
176 void SAL_CALL
endUnknownElement( const OUString
& /*aNamespace*/, const OUString
& /*aName*/ )
177 throw (RuntimeException
, SAXException
)
180 Reference
< XFastContextHandler
> SAL_CALL
createFastChildContext( sal_Int32
/*aElement*/, const Reference
< XFastAttributeList
>& /*aAttribs*/ )
181 throw (RuntimeException
, SAXException
)
186 Reference
< XFastContextHandler
> SAL_CALL
createUnknownChildContext( const OUString
& /*aNamespace*/, const OUString
& /*aName*/, const Reference
< XFastAttributeList
>& /*aAttribs*/ )
187 throw (RuntimeException
, SAXException
)
192 void SAL_CALL
characters( const OUString
& /*aChars*/ )
193 throw (RuntimeException
, SAXException
)
199 DocumentDecryption::DocumentDecryption(oox::ole::OleStorage
& rOleStorage
, Reference
<XComponentContext
> xContext
) :
201 mrOleStorage(rOleStorage
),
205 bool DocumentDecryption::checkEncryptionData(const Sequence
<NamedValue
>& /*rEncryptionData*/)
210 bool DocumentDecryption::generateEncryptionKey(const OUString
& rPassword
)
213 return mEngine
->generateEncryptionKey(rPassword
);
217 bool DocumentDecryption::readAgileEncryptionInfo(Reference
< XInputStream
>& xInputStream
)
219 AgileEngine
* engine
= new AgileEngine();
220 mEngine
.reset(engine
);
221 AgileEncryptionInfo
& info
= engine
->getInfo();
223 Reference
<XMultiComponentFactory
> xFactory( mxContext
->getServiceManager(), UNO_SET_THROW
);
224 Reference
<XFastDocumentHandler
> xFastDocumentHandler( new AgileDocumentHandler(info
) );
225 Reference
<XFastTokenHandler
> xFastTokenHandler ( new AgileTokenHandler
);
227 Reference
<XFastParser
> xParser
;
228 xParser
.set( xFactory
->createInstanceWithContext( "com.sun.star.xml.sax.FastParser", mxContext
), UNO_QUERY_THROW
);
233 xParser
->setFastDocumentHandler( xFastDocumentHandler
);
234 xParser
->setTokenHandler( xFastTokenHandler
);
236 InputSource aInputSource
;
237 aInputSource
.aInputStream
= xInputStream
;
238 xParser
->parseStream( aInputSource
);
241 if (2 > info
.blockSize
|| info
.blockSize
> 4096)
244 if (0 > info
.spinCount
|| info
.spinCount
> 10000000)
247 if (1 > info
.saltSize
|| info
.saltSize
> 65536) // Check
250 // AES 128 CBC with SHA1
251 if (info
.keyBits
== 128 &&
252 info
.cipherAlgorithm
== "AES" &&
253 info
.cipherChaining
== "ChainingModeCBC" &&
254 info
.hashAlgorithm
== "SHA1" &&
260 // AES 256 CBC with SHA512
261 if (info
.keyBits
== 256 &&
262 info
.cipherAlgorithm
== "AES" &&
263 info
.cipherChaining
== "ChainingModeCBC" &&
264 info
.hashAlgorithm
== "SHA512" &&
265 info
.hashSize
== 64 )
273 bool DocumentDecryption::readStandard2007EncryptionInfo(BinaryInputStream
& rStream
)
275 Standard2007Engine
* engine
= new Standard2007Engine();
276 mEngine
.reset(engine
);
277 StandardEncryptionInfo
& info
= engine
->getInfo();
280 rStream
>> info
.header
.flags
;
281 if( getFlag( info
.header
.flags
, ENCRYPTINFO_EXTERNAL
) )
284 sal_uInt32 nHeaderSize
;
285 rStream
>> nHeaderSize
;
287 sal_uInt32 actualHeaderSize
= sizeof(info
.header
);
289 if( (nHeaderSize
< actualHeaderSize
) )
292 rStream
>> info
.header
;
293 rStream
.skip( nHeaderSize
- actualHeaderSize
);
294 rStream
>> info
.verifier
;
296 if( info
.verifier
.saltSize
!= 16 )
299 // check flags and algorithm IDs, required are AES128 and SHA-1
300 if( !getFlag( info
.header
.flags
, ENCRYPTINFO_CRYPTOAPI
) )
303 if( !getFlag( info
.header
.flags
, ENCRYPTINFO_AES
) )
306 // algorithm ID 0 defaults to AES128 too, if ENCRYPTINFO_AES flag is set
307 if( info
.header
.algId
!= 0 && info
.header
.algId
!= ENCRYPT_ALGO_AES128
)
310 // hash algorithm ID 0 defaults to SHA-1 too
311 if( info
.header
.algIdHash
!= 0 && info
.header
.algIdHash
!= ENCRYPT_HASH_SHA1
)
314 if( info
.verifier
.encryptedVerifierHashSize
!= 20 )
317 return !rStream
.isEof();
320 bool DocumentDecryption::readEncryptionInfo()
322 if( !mrOleStorage
.isStorage() )
325 Reference
< XInputStream
> xEncryptionInfo( mrOleStorage
.openInputStream( "EncryptionInfo" ), UNO_SET_THROW
);
327 bool bResult
= false;
329 BinaryXInputStream
aBinaryInputStream( xEncryptionInfo
, true );
332 aBinaryInputStream
>> aVersion
;
336 case VERSION_INFO_2007_FORMAT
:
337 mCryptoType
= STANDARD_2007
; // Set encryption info format
338 bResult
= readStandard2007EncryptionInfo( aBinaryInputStream
);
340 case VERSION_INFO_AGILE
:
341 mCryptoType
= AGILE
; // Set encryption info format
342 aBinaryInputStream
.skip(4);
343 bResult
= readAgileEncryptionInfo( xEncryptionInfo
);
352 Sequence
<NamedValue
> DocumentDecryption::createEncryptionData(const OUString
& rPassword
)
354 SequenceAsHashMap aEncryptionData
;
356 if (mCryptoType
== AGILE
)
358 aEncryptionData
["CryptoType"] <<= OUString("Agile");
360 else if (mCryptoType
== STANDARD_2007
)
362 aEncryptionData
["CryptoType"] <<= OUString("Standard");
365 aEncryptionData
["OOXPassword"] <<= rPassword
;
366 return aEncryptionData
.getAsConstNamedValueList();
369 bool DocumentDecryption::decrypt(Reference
<XStream
> xDocumentStream
)
371 bool aResult
= false;
373 if( !mrOleStorage
.isStorage() )
376 // open the required input streams in the encrypted package
377 Reference
< XInputStream
> xEncryptedPackage( mrOleStorage
.openInputStream( "EncryptedPackage" ), UNO_SET_THROW
);
379 // create temporary file for unencrypted package
380 Reference
< XOutputStream
> xDecryptedPackage( xDocumentStream
->getOutputStream(), UNO_SET_THROW
);
381 BinaryXOutputStream
aDecryptedPackage( xDecryptedPackage
, true );
382 BinaryXInputStream
aEncryptedPackage( xEncryptedPackage
, true );
384 aResult
= mEngine
->decrypt(aEncryptedPackage
, aDecryptedPackage
);
386 xDecryptedPackage
->flush();
387 aDecryptedPackage
.seekToStart();
395 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */