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/.
10 #include <config_oox.h>
11 #include <cppunit/plugin/TestPlugIn.h>
12 #include <cppunit/extensions/HelperMacros.h>
13 #include <cppunit/TestFixture.h>
16 #include <tools/stream.hxx>
17 #include <unotools/streamwrap.hxx>
19 #include <oox/crypto/Standard2007Engine.hxx>
20 #include <oox/crypto/AgileEngine.hxx>
21 #include <oox/helper/binaryinputstream.hxx>
22 #include <oox/helper/binaryoutputstream.hxx>
30 class CryptoTest
: public CppUnit::TestFixture
33 virtual ~CryptoTest() override
;
34 void testCryptoHash();
36 void testStandard2007();
37 void testAgileEncryptionVerifier();
38 void testAgileEncryptionInfoWritingAndParsing();
39 void testAgileDataIntegrityHmacKey();
40 void testAgileEncryptingAndDecrypting();
42 CPPUNIT_TEST_SUITE(CryptoTest
);
43 CPPUNIT_TEST(testCryptoHash
);
44 CPPUNIT_TEST(testRoundUp
);
45 CPPUNIT_TEST(testStandard2007
);
46 CPPUNIT_TEST(testAgileEncryptionVerifier
);
47 CPPUNIT_TEST(testAgileEncryptionInfoWritingAndParsing
);
48 CPPUNIT_TEST(testAgileDataIntegrityHmacKey
);
49 CPPUNIT_TEST(testAgileEncryptingAndDecrypting
);
50 CPPUNIT_TEST_SUITE_END();
55 std::string
toString(std::vector
<sal_uInt8
> const& aInput
)
57 std::stringstream aStream
;
58 for (auto const& aValue
: aInput
)
60 aStream
<< std::setw(2) << std::setfill('0') << std::hex
<< static_cast<int>(aValue
);
67 CryptoTest::~CryptoTest()
74 void CryptoTest::testCryptoHash()
76 // Check examples from Wikipedia (https://en.wikipedia.org/wiki/HMAC)
77 OString
aContentString("The quick brown fox jumps over the lazy dog");
78 std::vector
<sal_uInt8
> aContent(aContentString
.getStr(),
79 aContentString
.getStr() + aContentString
.getLength());
80 std::vector
<sal_uInt8
> aKey
= { 'k', 'e', 'y' };
82 oox::crypto::CryptoHash
aCryptoHash(aKey
, oox::crypto::CryptoHashType::SHA1
);
83 aCryptoHash
.update(aContent
);
84 std::vector
<sal_uInt8
> aHash
= aCryptoHash
.finalize();
85 CPPUNIT_ASSERT_EQUAL(std::string("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"),
90 oox::crypto::CryptoHash
aCryptoHash(aKey
, oox::crypto::CryptoHashType::SHA256
);
91 aCryptoHash
.update(aContent
);
92 std::vector
<sal_uInt8
> aHash
= aCryptoHash
.finalize();
94 std::string("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8"),
99 oox::crypto::CryptoHash
aCryptoHash(aKey
, oox::crypto::CryptoHashType::SHA384
);
100 aCryptoHash
.update(aContent
);
101 std::vector
<sal_uInt8
> aHash
= aCryptoHash
.finalize();
102 CPPUNIT_ASSERT_EQUAL(std::string("d7f4727e2c0b39ae0f1e40cc96f60242d5b7801841cea6fc592c5d3e1"
103 "ae50700582a96cf35e1e554995fe4e03381c237"),
108 oox::crypto::CryptoHash
aCryptoHash(aKey
, oox::crypto::CryptoHashType::SHA512
);
109 aCryptoHash
.update(aContent
);
110 std::vector
<sal_uInt8
> aHash
= aCryptoHash
.finalize();
111 CPPUNIT_ASSERT_EQUAL(
112 std::string("b42af09057bac1e2d41708e48a902e09b5ff7f12ab428a4fe86653c73dd248fb82f948a549"
113 "f7b791a5b41915ee4d1ec3935357e4e2317250d0372afa2ebeeb3a"),
118 void CryptoTest::testRoundUp()
120 CPPUNIT_ASSERT_EQUAL(16, oox::crypto::roundUp(16, 16));
121 CPPUNIT_ASSERT_EQUAL(32, oox::crypto::roundUp(32, 16));
122 CPPUNIT_ASSERT_EQUAL(64, oox::crypto::roundUp(64, 16));
124 CPPUNIT_ASSERT_EQUAL(16, oox::crypto::roundUp(01, 16));
125 CPPUNIT_ASSERT_EQUAL(32, oox::crypto::roundUp(17, 16));
126 CPPUNIT_ASSERT_EQUAL(32, oox::crypto::roundUp(31, 16));
129 void CryptoTest::testStandard2007()
131 oox::crypto::Standard2007Engine aEngine
;
133 aEngine
.setupEncryption("Password");
135 SvMemoryStream aEncryptionInfo
;
136 oox::BinaryXOutputStream
aBinaryEncryptionInfoOutputStream(
137 new utl::OSeekableOutputStreamWrapper(aEncryptionInfo
), false);
139 aEngine
.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream
);
140 aBinaryEncryptionInfoOutputStream
.close();
142 CPPUNIT_ASSERT_EQUAL(sal_uInt64(224), aEncryptionInfo
.GetSize());
145 SvMemoryStream aUnencryptedInput
;
146 SvMemoryStream aEncryptedStream
;
148 OString aTestString
= "1234567890ABCDEFG";
150 aUnencryptedInput
.WriteBytes(aTestString
.getStr(), aTestString
.getLength() + 1);
151 aUnencryptedInput
.Seek(STREAM_SEEK_TO_BEGIN
);
154 uno::Reference
<io::XInputStream
> xInputStream(
155 new utl::OSeekableInputStreamWrapper(aUnencryptedInput
));
156 uno::Reference
<io::XOutputStream
> xOutputStream(
157 new utl::OSeekableOutputStreamWrapper(aEncryptedStream
));
159 aEngine
.encrypt(xInputStream
, xOutputStream
, aUnencryptedInput
.GetSize());
161 xOutputStream
->flush();
163 const sal_uInt8
* pData
= static_cast<const sal_uInt8
*>(aEncryptedStream
.GetData());
164 sal_uInt64 nSize
= aEncryptedStream
.GetSize();
166 std::vector
<sal_uInt8
> aData(nSize
);
167 std::copy(pData
, pData
+ nSize
, aData
.data());
169 CPPUNIT_ASSERT_EQUAL(sal_uInt64(40), nSize
);
172 aEncryptedStream
.Seek(STREAM_SEEK_TO_BEGIN
);
173 SvMemoryStream aUnencryptedOutput
;
176 oox::BinaryXInputStream
aBinaryInputStream(
177 new utl::OSeekableInputStreamWrapper(aEncryptedStream
), true);
178 oox::BinaryXOutputStream
aBinaryOutputStream(
179 new utl::OSeekableOutputStreamWrapper(aUnencryptedOutput
), true);
181 aEngine
.decrypt(aBinaryInputStream
, aBinaryOutputStream
);
182 aBinaryOutputStream
.close();
183 aBinaryInputStream
.close();
185 const char* pData
= static_cast<const char*>(aUnencryptedOutput
.GetData());
186 sal_uInt64 nSize
= aUnencryptedOutput
.GetSize();
188 CPPUNIT_ASSERT_EQUAL(sal_uInt64(18), nSize
);
190 OString
aString(pData
);
192 CPPUNIT_ASSERT_EQUAL(aTestString
, aString
);
196 void CryptoTest::testAgileEncryptionVerifier()
198 oox::crypto::AgileEngine aEngine
;
200 OUString
aPassword("Password");
202 aEngine
.setupEncryptionParameters({ 100000, 16, 128, 20, 16, OUString("AES"),
203 OUString("ChainingModeCBC"), OUString("SHA1") });
205 CPPUNIT_ASSERT_EQUAL(true, aEngine
.generateAndEncryptVerifierHash(aPassword
));
206 CPPUNIT_ASSERT_EQUAL(false, aEngine
.decryptAndCheckVerifierHash("Wrong"));
207 CPPUNIT_ASSERT_EQUAL(true, aEngine
.decryptAndCheckVerifierHash(aPassword
));
209 aEngine
.setupEncryptionParameters({ 100000, 16, 128, 48, 16, OUString("AES"),
210 OUString("ChainingModeCBC"), OUString("SHA384") });
212 CPPUNIT_ASSERT_EQUAL(true, aEngine
.generateAndEncryptVerifierHash(aPassword
));
213 CPPUNIT_ASSERT_EQUAL(false, aEngine
.decryptAndCheckVerifierHash("Wrong"));
214 CPPUNIT_ASSERT_EQUAL(true, aEngine
.decryptAndCheckVerifierHash(aPassword
));
216 aEngine
.setupEncryptionParameters({ 100000, 16, 256, 64, 16, OUString("AES"),
217 OUString("ChainingModeCBC"), OUString("SHA512") });
219 CPPUNIT_ASSERT_EQUAL(true, aEngine
.generateAndEncryptVerifierHash(aPassword
));
220 CPPUNIT_ASSERT_EQUAL(false, aEngine
.decryptAndCheckVerifierHash("Wrong"));
221 CPPUNIT_ASSERT_EQUAL(true, aEngine
.decryptAndCheckVerifierHash(aPassword
));
224 void CryptoTest::testAgileEncryptionInfoWritingAndParsing()
226 OUString
aPassword("Password");
227 std::vector
<sal_uInt8
> aKeyDataSalt
;
229 { // Preset AES128 - SHA1
230 SvMemoryStream aEncryptionInfo
;
232 oox::crypto::AgileEngine aEngine
;
234 aEngine
.setPreset(oox::crypto::AgileEncryptionPreset::AES_128_SHA1
);
235 aEngine
.setupEncryption(aPassword
);
236 aKeyDataSalt
= aEngine
.getInfo().keyDataSalt
;
238 oox::BinaryXOutputStream
aBinaryEncryptionInfoOutputStream(
239 new utl::OSeekableOutputStreamWrapper(aEncryptionInfo
), true);
241 aEngine
.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream
);
242 aBinaryEncryptionInfoOutputStream
.close();
244 CPPUNIT_ASSERT_EQUAL(sal_uInt64(996), aEncryptionInfo
.GetSize());
247 aEncryptionInfo
.Seek(STREAM_SEEK_TO_BEGIN
);
250 oox::crypto::AgileEngine aEngine
;
252 uno::Reference
<io::XInputStream
> xInputStream(
253 new utl::OSeekableInputStreamWrapper(aEncryptionInfo
));
255 xInputStream
->skipBytes(4); // Encryption type -> Agile
257 CPPUNIT_ASSERT(aEngine
.readEncryptionInfo(xInputStream
));
259 oox::crypto::AgileEncryptionInfo
& rInfo
= aEngine
.getInfo();
260 CPPUNIT_ASSERT_EQUAL(sal_Int32(100000), rInfo
.spinCount
);
261 CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo
.saltSize
);
262 CPPUNIT_ASSERT_EQUAL(sal_Int32(128), rInfo
.keyBits
);
263 CPPUNIT_ASSERT_EQUAL(sal_Int32(20), rInfo
.hashSize
);
264 CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo
.blockSize
);
265 CPPUNIT_ASSERT_EQUAL(OUString("AES"), rInfo
.cipherAlgorithm
);
266 CPPUNIT_ASSERT_EQUAL(OUString("ChainingModeCBC"), rInfo
.cipherChaining
);
267 CPPUNIT_ASSERT_EQUAL(OUString("SHA1"), rInfo
.hashAlgorithm
);
268 CPPUNIT_ASSERT_EQUAL(toString(aKeyDataSalt
), toString(rInfo
.keyDataSalt
));
270 CPPUNIT_ASSERT_EQUAL(false, aEngine
.decryptAndCheckVerifierHash("Wrong"));
271 CPPUNIT_ASSERT_EQUAL(true, aEngine
.decryptAndCheckVerifierHash(aPassword
));
275 { // Preset AES128 - SHA384
276 SvMemoryStream aEncryptionInfo
;
278 oox::crypto::AgileEngine aEngine
;
280 aEngine
.setPreset(oox::crypto::AgileEncryptionPreset::AES_128_SHA384
);
281 aEngine
.setupEncryption(aPassword
);
282 aKeyDataSalt
= aEngine
.getInfo().keyDataSalt
;
284 oox::BinaryXOutputStream
aBinaryEncryptionInfoOutputStream(
285 new utl::OSeekableOutputStreamWrapper(aEncryptionInfo
), true);
287 aEngine
.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream
);
288 aBinaryEncryptionInfoOutputStream
.close();
290 CPPUNIT_ASSERT_EQUAL(sal_uInt64(1040), aEncryptionInfo
.GetSize());
293 aEncryptionInfo
.Seek(STREAM_SEEK_TO_BEGIN
);
296 oox::crypto::AgileEngine aEngine
;
298 uno::Reference
<io::XInputStream
> xInputStream(
299 new utl::OSeekableInputStreamWrapper(aEncryptionInfo
));
301 xInputStream
->skipBytes(4); // Encryption type -> Agile
303 CPPUNIT_ASSERT(aEngine
.readEncryptionInfo(xInputStream
));
305 oox::crypto::AgileEncryptionInfo
& rInfo
= aEngine
.getInfo();
306 CPPUNIT_ASSERT_EQUAL(sal_Int32(100000), rInfo
.spinCount
);
307 CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo
.saltSize
);
308 CPPUNIT_ASSERT_EQUAL(sal_Int32(128), rInfo
.keyBits
);
309 CPPUNIT_ASSERT_EQUAL(sal_Int32(48), rInfo
.hashSize
);
310 CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo
.blockSize
);
311 CPPUNIT_ASSERT_EQUAL(OUString("AES"), rInfo
.cipherAlgorithm
);
312 CPPUNIT_ASSERT_EQUAL(OUString("ChainingModeCBC"), rInfo
.cipherChaining
);
313 CPPUNIT_ASSERT_EQUAL(OUString("SHA384"), rInfo
.hashAlgorithm
);
314 CPPUNIT_ASSERT_EQUAL(toString(aKeyDataSalt
), toString(rInfo
.keyDataSalt
));
316 CPPUNIT_ASSERT_EQUAL(false, aEngine
.decryptAndCheckVerifierHash("Wrong"));
317 CPPUNIT_ASSERT_EQUAL(true, aEngine
.decryptAndCheckVerifierHash(aPassword
));
321 { // Preset AES256 - SHA512
322 SvMemoryStream aEncryptionInfo
;
324 oox::crypto::AgileEngine aEngine
;
326 aEngine
.setPreset(oox::crypto::AgileEncryptionPreset::AES_256_SHA512
);
327 aEngine
.setupEncryption(aPassword
);
328 aKeyDataSalt
= aEngine
.getInfo().keyDataSalt
;
330 oox::BinaryXOutputStream
aBinaryEncryptionInfoOutputStream(
331 new utl::OSeekableOutputStreamWrapper(aEncryptionInfo
), true);
333 aEngine
.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream
);
334 aBinaryEncryptionInfoOutputStream
.close();
336 CPPUNIT_ASSERT_EQUAL(sal_uInt64(1112), aEncryptionInfo
.GetSize());
339 aEncryptionInfo
.Seek(STREAM_SEEK_TO_BEGIN
);
342 oox::crypto::AgileEngine aEngine
;
344 uno::Reference
<io::XInputStream
> xInputStream(
345 new utl::OSeekableInputStreamWrapper(aEncryptionInfo
));
347 xInputStream
->skipBytes(4); // Encryption type -> Agile
349 CPPUNIT_ASSERT(aEngine
.readEncryptionInfo(xInputStream
));
351 oox::crypto::AgileEncryptionInfo
& rInfo
= aEngine
.getInfo();
352 CPPUNIT_ASSERT_EQUAL(sal_Int32(100000), rInfo
.spinCount
);
353 CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo
.saltSize
);
354 CPPUNIT_ASSERT_EQUAL(sal_Int32(256), rInfo
.keyBits
);
355 CPPUNIT_ASSERT_EQUAL(sal_Int32(64), rInfo
.hashSize
);
356 CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo
.blockSize
);
357 CPPUNIT_ASSERT_EQUAL(OUString("AES"), rInfo
.cipherAlgorithm
);
358 CPPUNIT_ASSERT_EQUAL(OUString("ChainingModeCBC"), rInfo
.cipherChaining
);
359 CPPUNIT_ASSERT_EQUAL(OUString("SHA512"), rInfo
.hashAlgorithm
);
360 CPPUNIT_ASSERT_EQUAL(toString(aKeyDataSalt
), toString(rInfo
.keyDataSalt
));
362 CPPUNIT_ASSERT_EQUAL(false, aEngine
.decryptAndCheckVerifierHash("Wrong"));
363 CPPUNIT_ASSERT_EQUAL(true, aEngine
.decryptAndCheckVerifierHash(aPassword
));
368 void CryptoTest::testAgileDataIntegrityHmacKey()
370 OUString
aPassword("Password");
372 std::vector
<sal_uInt8
> aKeyDataSalt
;
374 std::vector
<sal_uInt8
> aHmacKey
;
375 std::vector
<sal_uInt8
> aHmacEncryptedKey
;
377 SvMemoryStream aEncryptionInfo
;
379 oox::crypto::AgileEngine aEngine
;
380 aEngine
.setupEncryption(aPassword
);
381 oox::BinaryXOutputStream
aBinaryEncryptionInfoOutputStream(
382 new utl::OSeekableOutputStreamWrapper(aEncryptionInfo
), true);
383 aEngine
.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream
);
384 aBinaryEncryptionInfoOutputStream
.close();
386 aHmacKey
= aEngine
.getInfo().hmacKey
;
387 aKeyDataSalt
= aEngine
.getInfo().keyDataSalt
;
388 aHmacEncryptedKey
= aEngine
.getInfo().hmacEncryptedKey
;
391 aEncryptionInfo
.Seek(STREAM_SEEK_TO_BEGIN
);
394 oox::crypto::AgileEngine aEngine
;
396 uno::Reference
<io::XInputStream
> xInputStream(
397 new utl::OSeekableInputStreamWrapper(aEncryptionInfo
));
399 xInputStream
->skipBytes(4); // Encryption type -> Agile
401 CPPUNIT_ASSERT(aEngine
.readEncryptionInfo(xInputStream
));
402 CPPUNIT_ASSERT(aEngine
.generateEncryptionKey(aPassword
));
404 CPPUNIT_ASSERT_EQUAL(toString(aKeyDataSalt
), toString(aEngine
.getInfo().keyDataSalt
));
406 CPPUNIT_ASSERT_EQUAL(toString(aHmacEncryptedKey
),
407 toString(aEngine
.getInfo().hmacEncryptedKey
));
409 CPPUNIT_ASSERT_EQUAL(size_t(64), aHmacKey
.size());
410 CPPUNIT_ASSERT_EQUAL(toString(aHmacKey
), toString(aEngine
.getInfo().hmacKey
));
414 void CryptoTest::testAgileEncryptingAndDecrypting()
416 OUString
aPassword("Password");
418 SvMemoryStream aEncryptionInfo
;
419 SvMemoryStream aEncryptedStream
;
421 OString aTestString
= "1234567890ABCDEFGH";
424 oox::crypto::AgileEngine aEngine
;
427 SvMemoryStream aUnencryptedInput
;
428 uno::Reference
<io::XInputStream
> xInputStream(
429 new utl::OSeekableInputStreamWrapper(aUnencryptedInput
));
431 aUnencryptedInput
.WriteBytes(aTestString
.getStr(), aTestString
.getLength() + 1);
432 aUnencryptedInput
.Seek(STREAM_SEEK_TO_BEGIN
);
435 uno::Reference
<io::XOutputStream
> xOutputStream(
436 new utl::OSeekableOutputStreamWrapper(aEncryptedStream
));
439 aEngine
.setupEncryption(aPassword
);
440 aEngine
.encrypt(xInputStream
, xOutputStream
, aUnencryptedInput
.GetSize());
441 xOutputStream
->flush();
444 sal_uInt64 nSize
= aEncryptedStream
.GetSize();
446 CPPUNIT_ASSERT_EQUAL(sal_uInt64(40), nSize
);
448 // Setup and write encryption info
449 oox::BinaryXOutputStream
aBinaryEncryptionInfoOutputStream(
450 new utl::OSeekableOutputStreamWrapper(aEncryptionInfo
), true);
451 aEngine
.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream
);
452 aBinaryEncryptionInfoOutputStream
.close();
455 aEncryptedStream
.Seek(STREAM_SEEK_TO_BEGIN
);
456 aEncryptionInfo
.Seek(STREAM_SEEK_TO_BEGIN
);
459 oox::crypto::AgileEngine aEngine
;
461 // Read encryption info
462 uno::Reference
<io::XInputStream
> xEncryptionInfo(
463 new utl::OSeekableInputStreamWrapper(aEncryptionInfo
));
465 xEncryptionInfo
->skipBytes(4); // Encryption type -> Agile
467 CPPUNIT_ASSERT(aEngine
.readEncryptionInfo(xEncryptionInfo
));
470 CPPUNIT_ASSERT(aEngine
.generateEncryptionKey(aPassword
));
472 // Setup encrypted input stream
473 oox::BinaryXInputStream
aBinaryInputStream(
474 new utl::OSeekableInputStreamWrapper(aEncryptedStream
), true);
476 // Setup output stream
477 SvMemoryStream aUnencryptedOutput
;
478 oox::BinaryXOutputStream
aBinaryOutputStream(
479 new utl::OSeekableOutputStreamWrapper(aUnencryptedOutput
), true);
482 aEngine
.decrypt(aBinaryInputStream
, aBinaryOutputStream
);
483 aBinaryOutputStream
.close();
484 aBinaryInputStream
.close();
486 // Check decrypted output
487 CPPUNIT_ASSERT_EQUAL(sal_uInt64(19), aUnencryptedOutput
.GetSize());
489 OString
aString(static_cast<const char*>(aUnencryptedOutput
.GetData()));
490 CPPUNIT_ASSERT_EQUAL(aTestString
, aString
);
492 // Check data integrity
493 CPPUNIT_ASSERT_EQUAL(true, aEngine
.checkDataIntegrity());
497 CPPUNIT_TEST_SUITE_REGISTRATION(CryptoTest
);
499 CPPUNIT_PLUGIN_IMPLEMENT();
501 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */