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 <cppunit/plugin/TestPlugIn.h>
11 #include <cppunit/extensions/HelperMacros.h>
12 #include <cppunit/TestFixture.h>
15 #include <tools/stream.hxx>
16 #include <unotools/streamwrap.hxx>
18 #include <oox/crypto/Standard2007Engine.hxx>
19 #include <oox/crypto/AgileEngine.hxx>
20 #include <oox/helper/binaryinputstream.hxx>
21 #include <oox/helper/binaryoutputstream.hxx>
25 class CryptoTest
: public CppUnit::TestFixture
28 void testCryptoHash();
30 void testStandard2007();
31 void testAgileEncryptionVerifier();
32 void testAgileEncrpytionInfoWritingAndParsing();
33 void testAgileDataIntegrityHmacKey();
34 void testAgileEncryptingAndDecrypting();
36 CPPUNIT_TEST_SUITE(CryptoTest
);
37 CPPUNIT_TEST(testCryptoHash
);
38 CPPUNIT_TEST(testRoundUp
);
39 CPPUNIT_TEST(testStandard2007
);
40 CPPUNIT_TEST(testAgileEncryptionVerifier
);
41 CPPUNIT_TEST(testAgileEncrpytionInfoWritingAndParsing
);
42 CPPUNIT_TEST(testAgileDataIntegrityHmacKey
);
43 CPPUNIT_TEST(testAgileEncryptingAndDecrypting
);
44 CPPUNIT_TEST_SUITE_END();
49 std::string
toString(std::vector
<sal_uInt8
> const& aInput
)
51 std::stringstream aStream
;
52 for (auto const& aValue
: aInput
)
54 aStream
<< std::setw(2) << std::setfill('0') << std::hex
<< static_cast<int>(aValue
);
61 void CryptoTest::testCryptoHash()
63 // Check examples from Wikipedia (https://en.wikipedia.org/wiki/HMAC)
64 OString
aContentString("The quick brown fox jumps over the lazy dog");
65 std::vector
<sal_uInt8
> aContent(aContentString
.getStr(),
66 aContentString
.getStr() + aContentString
.getLength());
67 std::vector
<sal_uInt8
> aKey
= { 'k', 'e', 'y' };
69 oox::core::CryptoHash
aCryptoHash(aKey
, oox::core::CryptoHashType::SHA1
);
70 aCryptoHash
.update(aContent
);
71 std::vector
<sal_uInt8
> aHash
= aCryptoHash
.finalize();
72 CPPUNIT_ASSERT_EQUAL(std::string("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"),
77 oox::core::CryptoHash
aCryptoHash(aKey
, oox::core::CryptoHashType::SHA256
);
78 aCryptoHash
.update(aContent
);
79 std::vector
<sal_uInt8
> aHash
= aCryptoHash
.finalize();
81 std::string("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8"),
86 oox::core::CryptoHash
aCryptoHash(aKey
, oox::core::CryptoHashType::SHA512
);
87 aCryptoHash
.update(aContent
);
88 std::vector
<sal_uInt8
> aHash
= aCryptoHash
.finalize();
90 std::string("b42af09057bac1e2d41708e48a902e09b5ff7f12ab428a4fe86653c73dd248fb82f948a549"
91 "f7b791a5b41915ee4d1ec3935357e4e2317250d0372afa2ebeeb3a"),
96 void CryptoTest::testRoundUp()
98 CPPUNIT_ASSERT_EQUAL(16, oox::core::roundUp(16, 16));
99 CPPUNIT_ASSERT_EQUAL(32, oox::core::roundUp(32, 16));
100 CPPUNIT_ASSERT_EQUAL(64, oox::core::roundUp(64, 16));
102 CPPUNIT_ASSERT_EQUAL(16, oox::core::roundUp(01, 16));
103 CPPUNIT_ASSERT_EQUAL(32, oox::core::roundUp(17, 16));
104 CPPUNIT_ASSERT_EQUAL(32, oox::core::roundUp(31, 16));
107 void CryptoTest::testStandard2007()
109 oox::core::Standard2007Engine aEngine
;
111 aEngine
.setupEncryption("Password");
113 SvMemoryStream aEncryptionInfo
;
114 oox::BinaryXOutputStream
aBinaryEncryptionInfoOutputStream(
115 new utl::OSeekableOutputStreamWrapper(aEncryptionInfo
), false);
117 aEngine
.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream
);
118 aBinaryEncryptionInfoOutputStream
.close();
120 CPPUNIT_ASSERT_EQUAL(sal_uInt64(224), aEncryptionInfo
.GetSize());
123 SvMemoryStream aUnencryptedInput
;
124 SvMemoryStream aEncryptedStream
;
126 OString aTestString
= OUStringToOString("1234567890ABCDEFG", RTL_TEXTENCODING_UTF8
);
128 aUnencryptedInput
.WriteBytes(aTestString
.getStr(), aTestString
.getLength() + 1);
129 aUnencryptedInput
.Seek(STREAM_SEEK_TO_BEGIN
);
132 uno::Reference
<io::XInputStream
> xInputStream(
133 new utl::OSeekableInputStreamWrapper(aUnencryptedInput
));
134 uno::Reference
<io::XOutputStream
> xOutputStream(
135 new utl::OSeekableOutputStreamWrapper(aEncryptedStream
));
137 aEngine
.encrypt(xInputStream
, xOutputStream
, aUnencryptedInput
.GetSize());
139 xOutputStream
->flush();
141 const sal_uInt8
* pData
= static_cast<const sal_uInt8
*>(aEncryptedStream
.GetData());
142 sal_uInt64 nSize
= aEncryptedStream
.GetSize();
144 std::vector
<sal_uInt8
> aData(nSize
);
145 std::copy(pData
, pData
+ nSize
, aData
.data());
147 CPPUNIT_ASSERT_EQUAL(sal_uInt64(40), nSize
);
150 aEncryptedStream
.Seek(STREAM_SEEK_TO_BEGIN
);
151 SvMemoryStream aUnencryptedOutput
;
154 oox::BinaryXInputStream
aBinaryInputStream(
155 new utl::OSeekableInputStreamWrapper(aEncryptedStream
), true);
156 oox::BinaryXOutputStream
aBinaryOutputStream(
157 new utl::OSeekableOutputStreamWrapper(aUnencryptedOutput
), true);
159 aEngine
.decrypt(aBinaryInputStream
, aBinaryOutputStream
);
160 aBinaryOutputStream
.close();
161 aBinaryInputStream
.close();
163 const sal_Char
* pData
= static_cast<const sal_Char
*>(aUnencryptedOutput
.GetData());
164 sal_uInt64 nSize
= aUnencryptedOutput
.GetSize();
166 CPPUNIT_ASSERT_EQUAL(sal_uInt64(18), nSize
);
168 OString
aString(pData
);
170 CPPUNIT_ASSERT_EQUAL(aTestString
, aString
);
174 void CryptoTest::testAgileEncryptionVerifier()
176 oox::core::AgileEngine aEngine
;
178 OUString
aPassword("Password");
180 aEngine
.setupEncryptionParameters({ 100000, 16, 128, 20, 16, OUString("AES"),
181 OUString("ChainingModeCBC"), OUString("SHA1") });
183 CPPUNIT_ASSERT_EQUAL(true, aEngine
.generateAndEncryptVerifierHash(aPassword
));
184 CPPUNIT_ASSERT_EQUAL(false, aEngine
.decryptAndCheckVerifierHash("Wrong"));
185 CPPUNIT_ASSERT_EQUAL(true, aEngine
.decryptAndCheckVerifierHash(aPassword
));
187 aEngine
.setupEncryptionParameters({ 100000, 16, 256, 64, 16, OUString("AES"),
188 OUString("ChainingModeCBC"), OUString("SHA512") });
190 CPPUNIT_ASSERT_EQUAL(true, aEngine
.generateAndEncryptVerifierHash(aPassword
));
191 CPPUNIT_ASSERT_EQUAL(false, aEngine
.decryptAndCheckVerifierHash("Wrong"));
192 CPPUNIT_ASSERT_EQUAL(true, aEngine
.decryptAndCheckVerifierHash(aPassword
));
195 void CryptoTest::testAgileEncrpytionInfoWritingAndParsing()
197 OUString
aPassword("Password");
198 std::vector
<sal_uInt8
> aKeyDataSalt
;
200 { // Preset AES128 - SHA1
201 SvMemoryStream aEncryptionInfo
;
203 oox::core::AgileEngine aEngine
;
205 aEngine
.setPreset(oox::core::AgileEncryptionPreset::AES_128_SHA1
);
206 aEngine
.setupEncryption(aPassword
);
207 aKeyDataSalt
= aEngine
.getInfo().keyDataSalt
;
209 oox::BinaryXOutputStream
aBinaryEncryptionInfoOutputStream(
210 new utl::OSeekableOutputStreamWrapper(aEncryptionInfo
), true);
212 aEngine
.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream
);
213 aBinaryEncryptionInfoOutputStream
.close();
215 CPPUNIT_ASSERT_EQUAL(sal_uInt64(996), aEncryptionInfo
.GetSize());
218 aEncryptionInfo
.Seek(STREAM_SEEK_TO_BEGIN
);
221 oox::core::AgileEngine aEngine
;
223 uno::Reference
<io::XInputStream
> xInputStream(
224 new utl::OSeekableInputStreamWrapper(aEncryptionInfo
));
226 xInputStream
->skipBytes(4); // Encryption type -> Agile
228 CPPUNIT_ASSERT(aEngine
.readEncryptionInfo(xInputStream
));
230 oox::core::AgileEncryptionInfo
& rInfo
= aEngine
.getInfo();
231 CPPUNIT_ASSERT_EQUAL(sal_Int32(100000), rInfo
.spinCount
);
232 CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo
.saltSize
);
233 CPPUNIT_ASSERT_EQUAL(sal_Int32(128), rInfo
.keyBits
);
234 CPPUNIT_ASSERT_EQUAL(sal_Int32(20), rInfo
.hashSize
);
235 CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo
.blockSize
);
236 CPPUNIT_ASSERT_EQUAL(OUString("AES"), rInfo
.cipherAlgorithm
);
237 CPPUNIT_ASSERT_EQUAL(OUString("ChainingModeCBC"), rInfo
.cipherChaining
);
238 CPPUNIT_ASSERT_EQUAL(OUString("SHA1"), rInfo
.hashAlgorithm
);
239 CPPUNIT_ASSERT_EQUAL(toString(aKeyDataSalt
), toString(rInfo
.keyDataSalt
));
241 CPPUNIT_ASSERT_EQUAL(false, aEngine
.decryptAndCheckVerifierHash("Wrong"));
242 CPPUNIT_ASSERT_EQUAL(true, aEngine
.decryptAndCheckVerifierHash(aPassword
));
246 { // Preset AES256 - SHA512
247 SvMemoryStream aEncryptionInfo
;
249 oox::core::AgileEngine aEngine
;
251 aEngine
.setPreset(oox::core::AgileEncryptionPreset::AES_256_SHA512
);
252 aEngine
.setupEncryption(aPassword
);
253 aKeyDataSalt
= aEngine
.getInfo().keyDataSalt
;
255 oox::BinaryXOutputStream
aBinaryEncryptionInfoOutputStream(
256 new utl::OSeekableOutputStreamWrapper(aEncryptionInfo
), true);
258 aEngine
.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream
);
259 aBinaryEncryptionInfoOutputStream
.close();
261 CPPUNIT_ASSERT_EQUAL(sal_uInt64(1112), aEncryptionInfo
.GetSize());
264 aEncryptionInfo
.Seek(STREAM_SEEK_TO_BEGIN
);
267 oox::core::AgileEngine aEngine
;
269 uno::Reference
<io::XInputStream
> xInputStream(
270 new utl::OSeekableInputStreamWrapper(aEncryptionInfo
));
272 xInputStream
->skipBytes(4); // Encryption type -> Agile
274 CPPUNIT_ASSERT(aEngine
.readEncryptionInfo(xInputStream
));
276 oox::core::AgileEncryptionInfo
& rInfo
= aEngine
.getInfo();
277 CPPUNIT_ASSERT_EQUAL(sal_Int32(100000), rInfo
.spinCount
);
278 CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo
.saltSize
);
279 CPPUNIT_ASSERT_EQUAL(sal_Int32(256), rInfo
.keyBits
);
280 CPPUNIT_ASSERT_EQUAL(sal_Int32(64), rInfo
.hashSize
);
281 CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo
.blockSize
);
282 CPPUNIT_ASSERT_EQUAL(OUString("AES"), rInfo
.cipherAlgorithm
);
283 CPPUNIT_ASSERT_EQUAL(OUString("ChainingModeCBC"), rInfo
.cipherChaining
);
284 CPPUNIT_ASSERT_EQUAL(OUString("SHA512"), rInfo
.hashAlgorithm
);
285 CPPUNIT_ASSERT_EQUAL(toString(aKeyDataSalt
), toString(rInfo
.keyDataSalt
));
287 CPPUNIT_ASSERT_EQUAL(false, aEngine
.decryptAndCheckVerifierHash("Wrong"));
288 CPPUNIT_ASSERT_EQUAL(true, aEngine
.decryptAndCheckVerifierHash(aPassword
));
293 void CryptoTest::testAgileDataIntegrityHmacKey()
295 OUString
aPassword("Password");
297 std::vector
<sal_uInt8
> aKeyDataSalt
;
299 std::vector
<sal_uInt8
> aHmacKey
;
300 std::vector
<sal_uInt8
> aHmacEncryptedKey
;
302 SvMemoryStream aEncryptionInfo
;
304 oox::core::AgileEngine aEngine
;
305 aEngine
.setupEncryption(aPassword
);
306 oox::BinaryXOutputStream
aBinaryEncryptionInfoOutputStream(
307 new utl::OSeekableOutputStreamWrapper(aEncryptionInfo
), true);
308 aEngine
.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream
);
309 aBinaryEncryptionInfoOutputStream
.close();
311 aHmacKey
= aEngine
.getInfo().hmacKey
;
312 aKeyDataSalt
= aEngine
.getInfo().keyDataSalt
;
313 aHmacEncryptedKey
= aEngine
.getInfo().hmacEncryptedKey
;
316 aEncryptionInfo
.Seek(STREAM_SEEK_TO_BEGIN
);
319 oox::core::AgileEngine aEngine
;
321 uno::Reference
<io::XInputStream
> xInputStream(
322 new utl::OSeekableInputStreamWrapper(aEncryptionInfo
));
324 xInputStream
->skipBytes(4); // Encryption type -> Agile
326 CPPUNIT_ASSERT(aEngine
.readEncryptionInfo(xInputStream
));
327 CPPUNIT_ASSERT(aEngine
.generateEncryptionKey(aPassword
));
329 CPPUNIT_ASSERT_EQUAL(toString(aKeyDataSalt
), toString(aEngine
.getInfo().keyDataSalt
));
331 CPPUNIT_ASSERT_EQUAL(toString(aHmacEncryptedKey
),
332 toString(aEngine
.getInfo().hmacEncryptedKey
));
334 CPPUNIT_ASSERT_EQUAL(size_t(64), aHmacKey
.size());
335 CPPUNIT_ASSERT_EQUAL(toString(aHmacKey
), toString(aEngine
.getInfo().hmacKey
));
339 void CryptoTest::testAgileEncryptingAndDecrypting()
341 OUString
aPassword("Password");
343 SvMemoryStream aEncryptionInfo
;
344 SvMemoryStream aEncryptedStream
;
346 OString aTestString
= OUStringToOString("1234567890ABCDEFGH", RTL_TEXTENCODING_UTF8
);
349 oox::core::AgileEngine aEngine
;
352 SvMemoryStream aUnencryptedInput
;
353 uno::Reference
<io::XInputStream
> xInputStream(
354 new utl::OSeekableInputStreamWrapper(aUnencryptedInput
));
356 aUnencryptedInput
.WriteBytes(aTestString
.getStr(), aTestString
.getLength() + 1);
357 aUnencryptedInput
.Seek(STREAM_SEEK_TO_BEGIN
);
360 uno::Reference
<io::XOutputStream
> xOutputStream(
361 new utl::OSeekableOutputStreamWrapper(aEncryptedStream
));
364 aEngine
.setupEncryption(aPassword
);
365 aEngine
.encrypt(xInputStream
, xOutputStream
, aUnencryptedInput
.GetSize());
366 xOutputStream
->flush();
369 sal_uInt64 nSize
= aEncryptedStream
.GetSize();
371 CPPUNIT_ASSERT_EQUAL(sal_uInt64(40), nSize
);
373 // Setup and write encryption info
374 oox::BinaryXOutputStream
aBinaryEncryptionInfoOutputStream(
375 new utl::OSeekableOutputStreamWrapper(aEncryptionInfo
), true);
376 aEngine
.writeEncryptionInfo(aBinaryEncryptionInfoOutputStream
);
377 aBinaryEncryptionInfoOutputStream
.close();
380 aEncryptedStream
.Seek(STREAM_SEEK_TO_BEGIN
);
381 aEncryptionInfo
.Seek(STREAM_SEEK_TO_BEGIN
);
384 oox::core::AgileEngine aEngine
;
386 // Read encryption info
387 uno::Reference
<io::XInputStream
> xEncryptionInfo(
388 new utl::OSeekableInputStreamWrapper(aEncryptionInfo
));
390 xEncryptionInfo
->skipBytes(4); // Encryption type -> Agile
392 CPPUNIT_ASSERT(aEngine
.readEncryptionInfo(xEncryptionInfo
));
395 CPPUNIT_ASSERT(aEngine
.generateEncryptionKey(aPassword
));
397 // Setup encrypted input stream
398 oox::BinaryXInputStream
aBinaryInputStream(
399 new utl::OSeekableInputStreamWrapper(aEncryptedStream
), true);
401 // Setup output stream
402 SvMemoryStream aUnencryptedOutput
;
403 oox::BinaryXOutputStream
aBinaryOutputStream(
404 new utl::OSeekableOutputStreamWrapper(aUnencryptedOutput
), true);
407 aEngine
.decrypt(aBinaryInputStream
, aBinaryOutputStream
);
408 aBinaryOutputStream
.close();
409 aBinaryInputStream
.close();
411 // Check decrypted output
412 CPPUNIT_ASSERT_EQUAL(sal_uInt64(19), aUnencryptedOutput
.GetSize());
414 OString
aString(static_cast<const sal_Char
*>(aUnencryptedOutput
.GetData()));
415 CPPUNIT_ASSERT_EQUAL(aTestString
, aString
);
417 // Check data integrity
418 CPPUNIT_ASSERT_EQUAL(true, aEngine
.checkDataIntegrity());
422 CPPUNIT_TEST_SUITE_REGISTRATION(CryptoTest
);
424 CPPUNIT_PLUGIN_IMPLEMENT();
426 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */