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/implbase.hxx>
17 #include <com/sun/star/io/XSeekable.hpp>
18 #include <com/sun/star/io/XStream.hpp>
19 #include <com/sun/star/uno/XComponentContext.hpp>
20 #include <com/sun/star/xml/sax/XFastParser.hpp>
21 #include <com/sun/star/xml/sax/XFastTokenHandler.hpp>
22 #include <com/sun/star/xml/sax/FastParser.hpp>
23 #include <com/sun/star/xml/sax/FastToken.hpp>
24 #include <oox/crypto/AgileEngine.hxx>
25 #include <oox/crypto/Standard2007Engine.hxx>
26 #include <oox/helper/binaryinputstream.hxx>
27 #include <oox/helper/binaryoutputstream.hxx>
28 #include <oox/ole/olestorage.hxx>
33 using namespace css::beans
;
34 using namespace css::io
;
35 using namespace css::lang
;
36 using namespace css::uno
;
37 using namespace css::xml::sax
;
38 using namespace css::xml
;
42 std::vector
<sal_uInt8
> convertToVector(Sequence
<sal_Int8
>& input
)
44 const sal_uInt8
* inputArray
= reinterpret_cast<const sal_uInt8
*>(input
.getConstArray());
45 return std::vector
<sal_uInt8
>(inputArray
, inputArray
+ input
.getLength());
48 class AgileTokenHandler
: public cppu::WeakImplHelper
<XFastTokenHandler
>
51 virtual sal_Int32 SAL_CALL
getTokenFromUTF8( const Sequence
< sal_Int8
>& /*nIdentifier*/ ) override
53 return FastToken::DONTKNOW
;
56 virtual Sequence
<sal_Int8
> SAL_CALL
getUTF8Identifier(sal_Int32
/*nToken*/) override
58 return Sequence
<sal_Int8
>();
62 class AgileDocumentHandler
: public ::cppu::WeakImplHelper
<XFastDocumentHandler
>
64 AgileEncryptionInfo
& mInfo
;
67 explicit AgileDocumentHandler(AgileEncryptionInfo
& rInfo
) :
71 void SAL_CALL
startDocument() override
73 void SAL_CALL
endDocument() override
75 void SAL_CALL
setDocumentLocator( const Reference
< XLocator
>& /*xLocator*/ ) override
77 void SAL_CALL
startFastElement( sal_Int32
/*Element*/, const Reference
< XFastAttributeList
>& /*Attribs*/ ) override
80 void SAL_CALL
startUnknownElement( const OUString
& /*aNamespace*/, const OUString
& rName
, const Reference
< XFastAttributeList
>& aAttributeList
) override
82 if (rName
== "keyData")
84 for (const Attribute
& rAttribute
: aAttributeList
->getUnknownAttributes())
86 if (rAttribute
.Name
== "saltValue")
88 Sequence
<sal_Int8
> keyDataSalt
;
89 ::sax::Converter::decodeBase64(keyDataSalt
, rAttribute
.Value
);
90 mInfo
.keyDataSalt
= convertToVector(keyDataSalt
);
94 else if (rName
== "encryptedKey")
96 for (const Attribute
& rAttribute
: aAttributeList
->getUnknownAttributes())
98 if (rAttribute
.Name
== "spinCount")
100 ::sax::Converter::convertNumber(mInfo
.spinCount
, rAttribute
.Value
);
102 else if (rAttribute
.Name
== "saltSize")
104 ::sax::Converter::convertNumber(mInfo
.saltSize
, rAttribute
.Value
);
106 else if (rAttribute
.Name
== "blockSize")
108 ::sax::Converter::convertNumber(mInfo
.blockSize
, rAttribute
.Value
);
110 else if (rAttribute
.Name
== "keyBits")
112 ::sax::Converter::convertNumber(mInfo
.keyBits
, rAttribute
.Value
);
114 else if (rAttribute
.Name
== "hashSize")
116 ::sax::Converter::convertNumber(mInfo
.hashSize
, rAttribute
.Value
);
118 else if (rAttribute
.Name
== "cipherAlgorithm")
120 mInfo
.cipherAlgorithm
= rAttribute
.Value
;
122 else if (rAttribute
.Name
== "cipherChaining")
124 mInfo
.cipherChaining
= rAttribute
.Value
;
126 else if (rAttribute
.Name
== "hashAlgorithm")
128 mInfo
.hashAlgorithm
= rAttribute
.Value
;
130 else if (rAttribute
.Name
== "saltValue")
132 Sequence
<sal_Int8
> saltValue
;
133 ::sax::Converter::decodeBase64(saltValue
, rAttribute
.Value
);
134 mInfo
.saltValue
= convertToVector(saltValue
);
136 else if (rAttribute
.Name
== "encryptedVerifierHashInput")
138 Sequence
<sal_Int8
> encryptedVerifierHashInput
;
139 ::sax::Converter::decodeBase64(encryptedVerifierHashInput
, rAttribute
.Value
);
140 mInfo
.encryptedVerifierHashInput
= convertToVector(encryptedVerifierHashInput
);
142 else if (rAttribute
.Name
== "encryptedVerifierHashValue")
144 Sequence
<sal_Int8
> encryptedVerifierHashValue
;
145 ::sax::Converter::decodeBase64(encryptedVerifierHashValue
, rAttribute
.Value
);
146 mInfo
.encryptedVerifierHashValue
= convertToVector(encryptedVerifierHashValue
);
148 else if (rAttribute
.Name
== "encryptedKeyValue")
150 Sequence
<sal_Int8
> encryptedKeyValue
;
151 ::sax::Converter::decodeBase64(encryptedKeyValue
, rAttribute
.Value
);
152 mInfo
.encryptedKeyValue
= convertToVector(encryptedKeyValue
);
158 void SAL_CALL
endFastElement( sal_Int32
/*aElement*/ ) override
160 void SAL_CALL
endUnknownElement( const OUString
& /*aNamespace*/, const OUString
& /*aName*/ ) override
163 Reference
< XFastContextHandler
> SAL_CALL
createFastChildContext( sal_Int32
/*aElement*/, const Reference
< XFastAttributeList
>& /*aAttribs*/ ) override
168 Reference
< XFastContextHandler
> SAL_CALL
createUnknownChildContext( const OUString
& /*aNamespace*/, const OUString
& /*aName*/, const Reference
< XFastAttributeList
>& /*aAttribs*/ ) override
173 void SAL_CALL
characters( const OUString
& /*aChars*/ ) override
179 DocumentDecryption::DocumentDecryption(oox::ole::OleStorage
& rOleStorage
, Reference
<XComponentContext
> const & xContext
) :
181 mrOleStorage(rOleStorage
),
185 bool DocumentDecryption::generateEncryptionKey(const OUString
& rPassword
)
188 return mEngine
->generateEncryptionKey(rPassword
);
192 bool DocumentDecryption::readAgileEncryptionInfo(Reference
< XInputStream
>& xInputStream
)
194 AgileEngine
* engine
= new AgileEngine
;
195 mEngine
.reset(engine
);
196 AgileEncryptionInfo
& info
= engine
->getInfo();
203 Reference
<XFastDocumentHandler
> xFastDocumentHandler( new AgileDocumentHandler(info
) );
204 Reference
<XFastTokenHandler
> xFastTokenHandler ( new AgileTokenHandler
);
206 Reference
<XFastParser
> xParser(css::xml::sax::FastParser::create(mxContext
));
208 xParser
->setFastDocumentHandler(xFastDocumentHandler
);
209 xParser
->setTokenHandler(xFastTokenHandler
);
211 InputSource aInputSource
;
212 aInputSource
.aInputStream
= xInputStream
;
213 xParser
->parseStream(aInputSource
);
216 if (2 > info
.blockSize
|| info
.blockSize
> 4096)
219 if (0 > info
.spinCount
|| info
.spinCount
> 10000000)
222 if (1 > info
.saltSize
|| info
.saltSize
> 65536) // Check
225 // AES 128 CBC with SHA1
226 if (info
.keyBits
== 128 &&
227 info
.cipherAlgorithm
== "AES" &&
228 info
.cipherChaining
== "ChainingModeCBC" &&
229 info
.hashAlgorithm
== "SHA1" &&
230 info
.hashSize
== msfilter::SHA1_HASH_LENGTH
)
235 // AES 256 CBC with SHA512
236 if (info
.keyBits
== 256 &&
237 info
.cipherAlgorithm
== "AES" &&
238 info
.cipherChaining
== "ChainingModeCBC" &&
239 info
.hashAlgorithm
== "SHA512" &&
240 info
.hashSize
== msfilter::SHA512_HASH_LENGTH
)
248 bool DocumentDecryption::readStandard2007EncryptionInfo(BinaryInputStream
& rStream
)
250 Standard2007Engine
* engine
= new Standard2007Engine
;
251 mEngine
.reset(engine
);
252 msfilter::StandardEncryptionInfo
& info
= engine
->getInfo();
254 info
.header
.flags
= rStream
.readuInt32();
255 if (getFlag(info
.header
.flags
, msfilter::ENCRYPTINFO_EXTERNAL
))
258 sal_uInt32 nHeaderSize
= rStream
.readuInt32();
260 sal_uInt32 actualHeaderSize
= sizeof(info
.header
);
262 if (nHeaderSize
< actualHeaderSize
)
265 info
.header
.flags
= rStream
.readuInt32();
266 info
.header
.sizeExtra
= rStream
.readuInt32();
267 info
.header
.algId
= rStream
.readuInt32();
268 info
.header
.algIdHash
= rStream
.readuInt32();
269 info
.header
.keyBits
= rStream
.readuInt32();
270 info
.header
.providedType
= rStream
.readuInt32();
271 info
.header
.reserved1
= rStream
.readuInt32();
272 info
.header
.reserved2
= rStream
.readuInt32();
274 rStream
.skip(nHeaderSize
- actualHeaderSize
);
276 info
.verifier
.saltSize
= rStream
.readuInt32();
277 rStream
.readArray(info
.verifier
.salt
, SAL_N_ELEMENTS(info
.verifier
.salt
));
278 rStream
.readArray(info
.verifier
.encryptedVerifier
, SAL_N_ELEMENTS(info
.verifier
.encryptedVerifier
));
279 info
.verifier
.encryptedVerifierHashSize
= rStream
.readuInt32();
280 rStream
.readArray(info
.verifier
.encryptedVerifierHash
, SAL_N_ELEMENTS(info
.verifier
.encryptedVerifierHash
));
282 if (info
.verifier
.saltSize
!= 16)
285 // check flags and algorithm IDs, required are AES128 and SHA-1
286 if (!getFlag(info
.header
.flags
, msfilter::ENCRYPTINFO_CRYPTOAPI
))
289 if (!getFlag(info
.header
.flags
, msfilter::ENCRYPTINFO_AES
))
292 // algorithm ID 0 defaults to AES128 too, if ENCRYPTINFO_AES flag is set
293 if (info
.header
.algId
!= 0 && info
.header
.algId
!= msfilter::ENCRYPT_ALGO_AES128
)
296 // hash algorithm ID 0 defaults to SHA-1 too
297 if (info
.header
.algIdHash
!= 0 && info
.header
.algIdHash
!= msfilter::ENCRYPT_HASH_SHA1
)
300 if (info
.verifier
.encryptedVerifierHashSize
!= 20)
303 return !rStream
.isEof();
306 bool DocumentDecryption::readEncryptionInfo()
308 if (!mrOleStorage
.isStorage())
311 Reference
<XInputStream
> xEncryptionInfo(mrOleStorage
.openInputStream("EncryptionInfo"), UNO_SET_THROW
);
313 bool bResult
= false;
315 BinaryXInputStream
aBinaryInputStream(xEncryptionInfo
, true);
317 sal_uInt32 aVersion
= aBinaryInputStream
.readuInt32();
321 case msfilter::VERSION_INFO_2007_FORMAT
:
322 case msfilter::VERSION_INFO_2007_FORMAT_SP2
:
323 mCryptoType
= STANDARD_2007
; // Set encryption info format
324 bResult
= readStandard2007EncryptionInfo( aBinaryInputStream
);
326 case msfilter::VERSION_INFO_AGILE
:
327 mCryptoType
= AGILE
; // Set encryption info format
328 aBinaryInputStream
.skip(4);
329 bResult
= readAgileEncryptionInfo( xEncryptionInfo
);
338 Sequence
<NamedValue
> DocumentDecryption::createEncryptionData(const OUString
& rPassword
)
340 comphelper::SequenceAsHashMap aEncryptionData
;
342 if (mCryptoType
== AGILE
)
344 aEncryptionData
["CryptoType"] <<= OUString("Agile");
346 else if (mCryptoType
== STANDARD_2007
)
348 aEncryptionData
["CryptoType"] <<= OUString("Standard");
351 aEncryptionData
["OOXPassword"] <<= rPassword
;
352 return aEncryptionData
.getAsConstNamedValueList();
355 bool DocumentDecryption::decrypt(const Reference
<XStream
>& xDocumentStream
)
357 bool aResult
= false;
359 if (!mrOleStorage
.isStorage())
362 // open the required input streams in the encrypted package
363 Reference
<XInputStream
> xEncryptedPackage(mrOleStorage
.openInputStream("EncryptedPackage"), UNO_SET_THROW
);
365 // create temporary file for unencrypted package
366 Reference
<XOutputStream
> xDecryptedPackage(xDocumentStream
->getOutputStream(), UNO_SET_THROW
);
367 BinaryXOutputStream
aDecryptedPackage(xDecryptedPackage
, true);
368 BinaryXInputStream
aEncryptedPackage(xEncryptedPackage
, true);
370 aResult
= mEngine
->decrypt(aEncryptedPackage
, aDecryptedPackage
);
372 xDecryptedPackage
->flush();
373 aDecryptedPackage
.seekToStart();
381 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */