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/uno/XComponentContext.hpp>
19 #include <com/sun/star/xml/sax/XFastParser.hpp>
20 #include <com/sun/star/xml/sax/XFastTokenHandler.hpp>
21 #include <com/sun/star/xml/sax/FastParser.hpp>
22 #include <com/sun/star/xml/sax/FastToken.hpp>
27 using namespace css::beans
;
28 using namespace css::io
;
29 using namespace css::lang
;
30 using namespace css::uno
;
31 using namespace css::xml::sax
;
32 using namespace css::xml
;
36 using ::comphelper::SequenceAsHashMap
;
37 using ::sax::Converter
;
41 vector
<sal_uInt8
> convertToVector(Sequence
<sal_Int8
>& input
)
43 const sal_uInt8
* inputArray
= reinterpret_cast<const sal_uInt8
*>( input
.getConstArray() );
44 return vector
<sal_uInt8
>(inputArray
, inputArray
+ input
.getLength());
47 class AgileTokenHandler
: public cppu::WeakImplHelper1
< XFastTokenHandler
>
50 virtual sal_Int32 SAL_CALL
getTokenFromUTF8( const Sequence
< sal_Int8
>& /*nIdentifier*/ ) throw (RuntimeException
, std::exception
) SAL_OVERRIDE
52 return FastToken::DONTKNOW
;
55 virtual Sequence
<sal_Int8
> SAL_CALL
getUTF8Identifier(sal_Int32
/*nToken*/) throw (RuntimeException
, std::exception
) SAL_OVERRIDE
57 return Sequence
<sal_Int8
>();
61 class AgileDocumentHandler
: public ::cppu::WeakImplHelper1
< XFastDocumentHandler
>
63 AgileEncryptionInfo
& mInfo
;
66 explicit AgileDocumentHandler(AgileEncryptionInfo
& rInfo
) :
70 void SAL_CALL
startDocument()
71 throw (RuntimeException
, SAXException
, std::exception
) SAL_OVERRIDE
73 void SAL_CALL
endDocument()
74 throw (RuntimeException
, SAXException
, std::exception
) SAL_OVERRIDE
76 void SAL_CALL
setDocumentLocator( const Reference
< XLocator
>& /*xLocator*/ )
77 throw (RuntimeException
, SAXException
, std::exception
) SAL_OVERRIDE
79 void SAL_CALL
startFastElement( sal_Int32
/*Element*/, const Reference
< XFastAttributeList
>& /*Attribs*/ )
80 throw (RuntimeException
, SAXException
, std::exception
) SAL_OVERRIDE
83 void SAL_CALL
startUnknownElement( const OUString
& /*aNamespace*/, const OUString
& aName
, const Reference
< XFastAttributeList
>& aAttributeList
)
84 throw (RuntimeException
, SAXException
, std::exception
) SAL_OVERRIDE
86 if(aName
== "keyData")
88 Sequence
<Attribute
> aAttributes(aAttributeList
->getUnknownAttributes());
90 for (int i
=0; i
<aAttributes
.getLength(); i
++)
92 if (aAttributes
[i
].Name
== "saltValue")
94 Sequence
<sal_Int8
> keyDataSalt
;
95 Converter::decodeBase64(keyDataSalt
, aAttributes
[i
].Value
);
96 mInfo
.keyDataSalt
= convertToVector(keyDataSalt
);
100 else if(aName
== "encryptedKey")
102 Sequence
<Attribute
> aAttributes(aAttributeList
->getUnknownAttributes());
103 for (int i
=0; i
<aAttributes
.getLength(); i
++)
105 if (aAttributes
[i
].Name
== "spinCount")
107 Converter::convertNumber(mInfo
.spinCount
, aAttributes
[i
].Value
);
109 else if (aAttributes
[i
].Name
== "saltSize")
111 Converter::convertNumber(mInfo
.saltSize
, aAttributes
[i
].Value
);
113 else if (aAttributes
[i
].Name
== "blockSize")
115 Converter::convertNumber(mInfo
.blockSize
, aAttributes
[i
].Value
);
117 else if (aAttributes
[i
].Name
== "keyBits")
119 Converter::convertNumber(mInfo
.keyBits
, aAttributes
[i
].Value
);
121 else if (aAttributes
[i
].Name
== "hashSize")
123 Converter::convertNumber(mInfo
.hashSize
, aAttributes
[i
].Value
);
125 else if (aAttributes
[i
].Name
== "cipherAlgorithm")
127 mInfo
.cipherAlgorithm
= aAttributes
[i
].Value
;
129 else if (aAttributes
[i
].Name
== "cipherChaining")
131 mInfo
.cipherChaining
= aAttributes
[i
].Value
;
133 else if (aAttributes
[i
].Name
== "hashAlgorithm")
135 mInfo
.hashAlgorithm
= aAttributes
[i
].Value
;
137 else if (aAttributes
[i
].Name
== "saltValue")
139 Sequence
<sal_Int8
> saltValue
;
140 Converter::decodeBase64(saltValue
, aAttributes
[i
].Value
);
141 mInfo
.saltValue
= convertToVector(saltValue
);
143 else if (aAttributes
[i
].Name
== "encryptedVerifierHashInput")
145 Sequence
<sal_Int8
> encryptedVerifierHashInput
;
146 Converter::decodeBase64(encryptedVerifierHashInput
, aAttributes
[i
].Value
);
147 mInfo
.encryptedVerifierHashInput
= convertToVector(encryptedVerifierHashInput
);
149 else if (aAttributes
[i
].Name
== "encryptedVerifierHashValue")
151 Sequence
<sal_Int8
> encryptedVerifierHashValue
;
152 Converter::decodeBase64(encryptedVerifierHashValue
, aAttributes
[i
].Value
);
153 mInfo
.encryptedVerifierHashValue
= convertToVector(encryptedVerifierHashValue
);
155 else if (aAttributes
[i
].Name
== "encryptedKeyValue")
157 Sequence
<sal_Int8
> encryptedKeyValue
;
158 Converter::decodeBase64(encryptedKeyValue
, aAttributes
[i
].Value
);
159 mInfo
.encryptedKeyValue
= convertToVector(encryptedKeyValue
);
165 void SAL_CALL
endFastElement( sal_Int32
/*aElement*/ )
166 throw (RuntimeException
, SAXException
, std::exception
) SAL_OVERRIDE
168 void SAL_CALL
endUnknownElement( const OUString
& /*aNamespace*/, const OUString
& /*aName*/ )
169 throw (RuntimeException
, SAXException
, std::exception
) SAL_OVERRIDE
172 Reference
< XFastContextHandler
> SAL_CALL
createFastChildContext( sal_Int32
/*aElement*/, const Reference
< XFastAttributeList
>& /*aAttribs*/ )
173 throw (RuntimeException
, SAXException
, std::exception
) SAL_OVERRIDE
178 Reference
< XFastContextHandler
> SAL_CALL
createUnknownChildContext( const OUString
& /*aNamespace*/, const OUString
& /*aName*/, const Reference
< XFastAttributeList
>& /*aAttribs*/ )
179 throw (RuntimeException
, SAXException
, std::exception
) SAL_OVERRIDE
184 void SAL_CALL
characters( const OUString
& /*aChars*/ )
185 throw (RuntimeException
, SAXException
, std::exception
) SAL_OVERRIDE
191 DocumentDecryption::DocumentDecryption(oox::ole::OleStorage
& rOleStorage
, Reference
<XComponentContext
> xContext
) :
193 mrOleStorage(rOleStorage
),
197 bool DocumentDecryption::generateEncryptionKey(const OUString
& rPassword
)
200 return mEngine
->generateEncryptionKey(rPassword
);
204 bool DocumentDecryption::readAgileEncryptionInfo(Reference
< XInputStream
>& xInputStream
)
206 AgileEngine
* engine
= new AgileEngine();
207 mEngine
.reset(engine
);
208 AgileEncryptionInfo
& info
= engine
->getInfo();
210 Reference
<XFastDocumentHandler
> xFastDocumentHandler( new AgileDocumentHandler(info
) );
211 Reference
<XFastTokenHandler
> xFastTokenHandler ( new AgileTokenHandler
);
213 Reference
<XFastParser
> xParser(
214 css::xml::sax::FastParser::create(mxContext
));
216 xParser
->setFastDocumentHandler( xFastDocumentHandler
);
217 xParser
->setTokenHandler( xFastTokenHandler
);
219 InputSource aInputSource
;
220 aInputSource
.aInputStream
= xInputStream
;
221 xParser
->parseStream( aInputSource
);
224 if (2 > info
.blockSize
|| info
.blockSize
> 4096)
227 if (0 > info
.spinCount
|| info
.spinCount
> 10000000)
230 if (1 > info
.saltSize
|| info
.saltSize
> 65536) // Check
233 // AES 128 CBC with SHA1
234 if (info
.keyBits
== 128 &&
235 info
.cipherAlgorithm
== "AES" &&
236 info
.cipherChaining
== "ChainingModeCBC" &&
237 info
.hashAlgorithm
== "SHA1" &&
243 // AES 256 CBC with SHA512
244 if (info
.keyBits
== 256 &&
245 info
.cipherAlgorithm
== "AES" &&
246 info
.cipherChaining
== "ChainingModeCBC" &&
247 info
.hashAlgorithm
== "SHA512" &&
248 info
.hashSize
== 64 )
256 bool DocumentDecryption::readStandard2007EncryptionInfo(BinaryInputStream
& rStream
)
258 Standard2007Engine
* engine
= new Standard2007Engine();
259 mEngine
.reset(engine
);
260 StandardEncryptionInfo
& info
= engine
->getInfo();
262 info
.header
.flags
= rStream
.readuInt32();
263 if( getFlag( info
.header
.flags
, ENCRYPTINFO_EXTERNAL
) )
266 sal_uInt32 nHeaderSize
= rStream
.readuInt32();
268 sal_uInt32 actualHeaderSize
= sizeof(info
.header
);
270 if( (nHeaderSize
< actualHeaderSize
) )
273 info
.header
.flags
= rStream
.readuInt32();
274 info
.header
.sizeExtra
= rStream
.readuInt32();
275 info
.header
.algId
= rStream
.readuInt32();
276 info
.header
.algIdHash
= rStream
.readuInt32();
277 info
.header
.keyBits
= rStream
.readuInt32();
278 info
.header
.providedType
= rStream
.readuInt32();
279 info
.header
.reserved1
= rStream
.readuInt32();
280 info
.header
.reserved2
= rStream
.readuInt32();
282 rStream
.skip( nHeaderSize
- actualHeaderSize
);
284 info
.verifier
.saltSize
= rStream
.readuInt32();
285 rStream
.readArray(info
.verifier
.salt
, SAL_N_ELEMENTS(info
.verifier
.salt
));
286 rStream
.readArray(info
.verifier
.encryptedVerifier
, SAL_N_ELEMENTS(info
.verifier
.encryptedVerifier
));
287 info
.verifier
.encryptedVerifierHashSize
= rStream
.readuInt32();
288 rStream
.readArray(info
.verifier
.encryptedVerifierHash
, SAL_N_ELEMENTS(info
.verifier
.encryptedVerifierHash
));
290 if( info
.verifier
.saltSize
!= 16 )
293 // check flags and algorithm IDs, required are AES128 and SHA-1
294 if( !getFlag( info
.header
.flags
, ENCRYPTINFO_CRYPTOAPI
) )
297 if( !getFlag( info
.header
.flags
, ENCRYPTINFO_AES
) )
300 // algorithm ID 0 defaults to AES128 too, if ENCRYPTINFO_AES flag is set
301 if( info
.header
.algId
!= 0 && info
.header
.algId
!= ENCRYPT_ALGO_AES128
)
304 // hash algorithm ID 0 defaults to SHA-1 too
305 if( info
.header
.algIdHash
!= 0 && info
.header
.algIdHash
!= ENCRYPT_HASH_SHA1
)
308 if( info
.verifier
.encryptedVerifierHashSize
!= 20 )
311 return !rStream
.isEof();
314 bool DocumentDecryption::readEncryptionInfo()
316 if( !mrOleStorage
.isStorage() )
319 Reference
< XInputStream
> xEncryptionInfo( mrOleStorage
.openInputStream( "EncryptionInfo" ), UNO_SET_THROW
);
321 bool bResult
= false;
323 BinaryXInputStream
aBinaryInputStream( xEncryptionInfo
, true );
325 sal_uInt32 aVersion
= aBinaryInputStream
.readuInt32();
329 case VERSION_INFO_2007_FORMAT
:
330 case VERSION_INFO_2007_FORMAT_SP2
:
331 mCryptoType
= STANDARD_2007
; // Set encryption info format
332 bResult
= readStandard2007EncryptionInfo( aBinaryInputStream
);
334 case VERSION_INFO_AGILE
:
335 mCryptoType
= AGILE
; // Set encryption info format
336 aBinaryInputStream
.skip(4);
337 bResult
= readAgileEncryptionInfo( xEncryptionInfo
);
346 Sequence
<NamedValue
> DocumentDecryption::createEncryptionData(const OUString
& rPassword
)
348 SequenceAsHashMap aEncryptionData
;
350 if (mCryptoType
== AGILE
)
352 aEncryptionData
["CryptoType"] <<= OUString("Agile");
354 else if (mCryptoType
== STANDARD_2007
)
356 aEncryptionData
["CryptoType"] <<= OUString("Standard");
359 aEncryptionData
["OOXPassword"] <<= rPassword
;
360 return aEncryptionData
.getAsConstNamedValueList();
363 bool DocumentDecryption::decrypt(Reference
<XStream
> xDocumentStream
)
365 bool aResult
= false;
367 if( !mrOleStorage
.isStorage() )
370 // open the required input streams in the encrypted package
371 Reference
< XInputStream
> xEncryptedPackage( mrOleStorage
.openInputStream( "EncryptedPackage" ), UNO_SET_THROW
);
373 // create temporary file for unencrypted package
374 Reference
< XOutputStream
> xDecryptedPackage( xDocumentStream
->getOutputStream(), UNO_SET_THROW
);
375 BinaryXOutputStream
aDecryptedPackage( xDecryptedPackage
, true );
376 BinaryXInputStream
aEncryptedPackage( xEncryptedPackage
, true );
378 aResult
= mEngine
->decrypt(aEncryptedPackage
, aDecryptedPackage
);
380 xDecryptedPackage
->flush();
381 aDecryptedPackage
.seekToStart();
389 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */