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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/types.h>
21 #include <cppunit/TestAssert.h>
22 #include <cppunit/TestFixture.h>
23 #include <cppunit/extensions/HelperMacros.h>
24 #include <cppunit/plugin/TestPlugIn.h>
26 #include <boost/scoped_array.hpp>
28 #include <rtl/digest.h>
29 #include <rtl/ustring.hxx>
30 #include <rtl/ustrbuf.hxx>
31 #include <rtl/strbuf.hxx>
40 const OString sSampleString
= "This is a sample sentence, which we use to check some crypto functions in sal.";
41 const OString sSampleString_only_one_diff
= "This is a sample sentence. which we use to check some crypto functions in sal.";
43 const rtlDigestAlgorithm constDigestAlgorithms
[] =
45 rtl_Digest_AlgorithmMD2
,
46 rtl_Digest_AlgorithmMD5
,
47 rtl_Digest_AlgorithmSHA
,
48 rtl_Digest_AlgorithmSHA1
,
49 rtl_Digest_AlgorithmHMAC_MD5
,
50 rtl_Digest_AlgorithmHMAC_SHA1
,
53 const sal_uInt32 constDigestAlgorithmLengths
[] =
55 RTL_DIGEST_LENGTH_MD2
,
56 RTL_DIGEST_LENGTH_MD5
,
57 RTL_DIGEST_LENGTH_SHA
,
58 RTL_DIGEST_LENGTH_SHA1
,
59 RTL_DIGEST_LENGTH_HMAC_MD5
,
60 RTL_DIGEST_LENGTH_HMAC_SHA1
,
63 const OString constSampleStringSums
[] =
65 "647ee6c9d4aa5fdd374ed9d7a156acbf",
66 "b16b903e6fc0b62ae389013ed93fe531",
67 "eab2814429b2613301c8a077b806af3680548914",
68 "2bc5bdb7506a2cdc2fd27fc8b9889343012d5008",
69 "0b1b0e1a6f2e4420326354b031063605",
70 "1998c6a556915be76451bfb587fa7c34d849936e"
73 // Create hex-value string from the digest value to keep the string size minimal
74 OString
createHex(sal_uInt8
* pKeyBuffer
, sal_uInt32 nKeyLen
)
76 OStringBuffer
aBuffer(nKeyLen
* 2 + 1);
77 for (sal_uInt32 i
= 0; i
< nKeyLen
; ++i
)
79 sal_Int32 nValue
= (sal_Int32
) pKeyBuffer
[i
];
82 aBuffer
.append(nValue
, 16);
84 return aBuffer
.makeStringAndClear();
87 OString
getDigest(const OString
& aMessage
, rtlDigestAlgorithm aAlgorithm
)
89 rtlDigest handle
= rtl_digest_create(aAlgorithm
);
91 const sal_uInt8
* pData
= reinterpret_cast<const sal_uInt8
*>(aMessage
.getStr());
92 sal_uInt32 nSize
= aMessage
.getLength();
94 rtl_digest_init(handle
, pData
, nSize
);
95 rtl_digest_update(handle
, pData
, nSize
);
97 sal_uInt32 nKeyLen
= rtl_digest_queryLength(handle
);
98 boost::scoped_array
<sal_uInt8
> pKeyBuffer(new sal_uInt8
[nKeyLen
]);
100 rtl_digest_get(handle
, pKeyBuffer
.get(), nKeyLen
);
101 OString aSum
= createHex(pKeyBuffer
.get(), nKeyLen
);
103 rtl_digest_destroy( handle
);
107 class DigestTest
: public CppUnit::TestFixture
112 int aAlgorithmSize
= sizeof(constDigestAlgorithms
) / sizeof(constDigestAlgorithms
[0]);
114 for (int i
= 0; i
< aAlgorithmSize
; i
++)
116 rtlDigest handle
= rtl_digest_create( constDigestAlgorithms
[i
] );
117 CPPUNIT_ASSERT_MESSAGE("create digest", handle
!= NULL
);
118 rtl_digest_destroy( handle
);
121 rtlDigest handle
= rtl_digest_create( rtl_Digest_AlgorithmInvalid
);
122 CPPUNIT_ASSERT_MESSAGE("create invalid digest", handle
== NULL
);
123 rtl_digest_destroy( handle
);
128 int aAlgorithmSize
= sizeof(constDigestAlgorithms
) / sizeof(constDigestAlgorithms
[0]);
130 for (int i
= 0; i
< aAlgorithmSize
; i
++)
132 rtlDigest handle
= rtl_digest_create(constDigestAlgorithms
[i
]);
133 rtlDigestAlgorithm aAlgo
= rtl_digest_queryAlgorithm(handle
);
134 CPPUNIT_ASSERT_MESSAGE("query handle", constDigestAlgorithms
[i
] == aAlgo
);
135 rtl_digest_destroy( handle
);
140 void testQueryLength()
142 int aAlgorithmSize
= sizeof(constDigestAlgorithms
) / sizeof(constDigestAlgorithms
[0]);
144 sal_uInt32 nAlgoLength
;
146 for (int i
= 0; i
< aAlgorithmSize
; i
++)
148 handle
= rtl_digest_create(constDigestAlgorithms
[i
]);
149 nAlgoLength
= rtl_digest_queryLength(handle
);
150 CPPUNIT_ASSERT_MESSAGE("query Length", constDigestAlgorithmLengths
[i
] == nAlgoLength
);
151 rtl_digest_destroy( handle
);
154 handle
= rtl_digest_create( rtl_Digest_AlgorithmInvalid
);
155 nAlgoLength
= rtl_digest_queryLength(handle
);
156 CPPUNIT_ASSERT_MESSAGE("query length", 0 == nAlgoLength
);
157 rtl_digest_destroy( handle
);
162 rtlDigestError aError
;
166 aError
= rtl_digest_init(handle
, NULL
, 0);
167 CPPUNIT_ASSERT_MESSAGE("init(NULL, 0, 0)", aError
== rtl_Digest_E_Argument
);
169 handle
= rtl_digest_create( rtl_Digest_AlgorithmMD5
);
170 aError
= rtl_digest_init(handle
, NULL
, 0);
171 CPPUNIT_ASSERT_MESSAGE("init(handle, 0, 0)", aError
== rtl_Digest_E_None
);
172 rtl_digest_destroy( handle
);
174 int aAlgorithmSize
= sizeof(constDigestAlgorithms
) / sizeof(constDigestAlgorithms
[0]);
176 for (int i
= 0; i
< aAlgorithmSize
; i
++)
178 handle
= rtl_digest_create(constDigestAlgorithms
[i
]);
180 OString aMessage
= sSampleString
;
181 const sal_uInt8
* pData
= reinterpret_cast<const sal_uInt8
*>(aMessage
.getStr());
182 sal_uInt32 nSize
= aMessage
.getLength();
184 aError
= rtl_digest_init(handle
, pData
, nSize
);
185 CPPUNIT_ASSERT_MESSAGE("init(handle, pData, nSize)", aError
== rtl_Digest_E_None
);
187 rtl_digest_update(handle
, pData
, nSize
);
189 sal_uInt32 nKeyLen
= rtl_digest_queryLength( handle
);
190 boost::scoped_array
<sal_uInt8
> pKeyBuffer(new sal_uInt8
[nKeyLen
]);
192 rtl_digest_get( handle
, pKeyBuffer
.get(), nKeyLen
);
193 createHex(pKeyBuffer
.get(), nKeyLen
);
195 rtl_digest_destroy( handle
);
202 OString aMsg1
= sSampleString
;
203 OString aMsg2
= sSampleString
;
205 OString aSum1
= getDigest(aMsg1
, rtl_Digest_AlgorithmMD5
);
206 OString aSum2
= getDigest(aMsg2
, rtl_Digest_AlgorithmMD5
);
208 CPPUNIT_ASSERT_MESSAGE("md5sum must have a length", aSum1
.getLength() == 32 && aSum2
.getLength() == 32 );
209 CPPUNIT_ASSERT_MESSAGE("source is the same, dest must be also the same", aSum1
.equals(aSum2
));
213 OString aMsg1
= sSampleString
;
214 OString aMsg2
= sSampleString_only_one_diff
;
216 OString aSum1
= getDigest(aMsg1
, rtl_Digest_AlgorithmMD5
);
217 OString aSum2
= getDigest(aMsg2
, rtl_Digest_AlgorithmMD5
);
219 CPPUNIT_ASSERT_MESSAGE("md5sum must have a length", aSum1
.getLength() == 32 && aSum2
.getLength() == 32 );
220 CPPUNIT_ASSERT_MESSAGE("differ only in one char", !aSum1
.equals(aSum2
));
226 int aAlgorithmSize
= sizeof(constDigestAlgorithms
) / sizeof(constDigestAlgorithms
[0]);
228 for (int i
= 0; i
< aAlgorithmSize
; i
++)
230 OString aSum
= getDigest(sSampleString
, constDigestAlgorithms
[i
]);
231 CPPUNIT_ASSERT_EQUAL_MESSAGE("Checksum of sample string is wrong.", constSampleStringSums
[i
], aSum
);
235 OString
runCheckPBKDF2(OString
& sPassword
, bool bClearSalt
, sal_uInt32 nCount
)
237 sal_uInt32 nKeyLen
= RTL_DIGEST_LENGTH_HMAC_SHA1
;
238 boost::scoped_array
<sal_uInt8
> pKeyBuffer(new sal_uInt8
[nKeyLen
]);
240 memset(pKeyBuffer
.get(), 0, nKeyLen
);
242 sal_uInt8
const * pPassword
= reinterpret_cast<sal_uInt8
const *>(sPassword
.getStr());
243 sal_Int32 nPasswordLen
= sPassword
.getLength();
245 sal_uInt32 nSaltDataLen
= RTL_DIGEST_LENGTH_HMAC_SHA1
;
246 boost::scoped_array
<sal_uInt8
> pSaltData(new sal_uInt8
[nSaltDataLen
]);
247 memset(pSaltData
.get(), 0, nSaltDataLen
);
251 // wilful contamination
255 rtlDigestError aError
= rtl_digest_PBKDF2(pKeyBuffer
.get(), nKeyLen
, pPassword
, nPasswordLen
, pSaltData
.get(), nSaltDataLen
, nCount
);
257 CPPUNIT_ASSERT(aError
== rtl_Digest_E_None
);
259 rtl::OString aKey
= createHex(pKeyBuffer
.get(), nKeyLen
);
261 // OString sSalt = createHex(pSaltData, nSaltDataLen);
262 // printf("Salt: %s\n", sSalt.getStr());
264 // CPPUNIT_ASSERT_MESSAGE("md5sum of sample string is wrong. Code changes or sample problems, please check.", aStr.equals(sSampleString_PBKDF2) );
270 OString aPassword
= "Password";
273 runCheckPBKDF2(aPassword
, false, 1);
274 runCheckPBKDF2(aPassword
, false, 2);
275 runCheckPBKDF2(aPassword
, true, 1);
276 runCheckPBKDF2(aPassword
, true, 2);
277 runCheckPBKDF2(aPassword
, false, 3);
278 runCheckPBKDF2(aPassword
, false, 4);
279 runCheckPBKDF2(aPassword
, true, 3);
280 runCheckPBKDF2(aPassword
, true, 4);
285 rtlDigestError aError
;
289 aError
= rtl_digest_update(aHandle
, NULL
, 0);
290 CPPUNIT_ASSERT_MESSAGE("does not handle wrong parameter", aError
== rtl_Digest_E_Argument
);
293 aError
= rtl_digest_updateMD2(aHandle
, NULL
, 0);
294 CPPUNIT_ASSERT_MESSAGE("does not handle wrong parameter", aError
== rtl_Digest_E_Argument
);
296 aError
= rtl_digest_updateMD5(aHandle
, NULL
, 0);
297 CPPUNIT_ASSERT_MESSAGE("does not handle wrong parameter", aError
== rtl_Digest_E_Argument
);
299 aHandle
= rtl_digest_create( rtl_Digest_AlgorithmMD2
);
300 CPPUNIT_ASSERT_MESSAGE("create with rtl_Digest_AlgorithmMD2", aHandle
!= 0);
302 const sal_uInt8
* pData
= reinterpret_cast<const sal_uInt8
*>(sSampleString
.getStr());
303 sal_uInt32 nSize
= sSampleString
.getLength();
305 aError
= rtl_digest_updateMD2(aHandle
, NULL
, 0);
306 CPPUNIT_ASSERT_MESSAGE("handle parameter 'pData' wrong", aError
== rtl_Digest_E_Argument
);
308 aError
= rtl_digest_updateMD2(aHandle
, pData
, 0);
309 CPPUNIT_ASSERT_MESSAGE("handle parameter 'nSize' wrong", aError
== rtl_Digest_E_None
);
311 rtl_digest_destroyMD2(aHandle
);
313 // use wrong Algorithm!!! This is volitional!
314 aHandle
= rtl_digest_create(rtl_Digest_AlgorithmMD2
);
315 CPPUNIT_ASSERT_MESSAGE("create with rtl_Digest_AlgorithmMD2", aHandle
!= 0);
317 aError
= rtl_digest_updateMD5(aHandle
, pData
, nSize
);
318 CPPUNIT_ASSERT_MESSAGE("handle parameter 'handle' wrong", aError
== rtl_Digest_E_Algorithm
);
320 rtl_digest_destroyMD5(aHandle
);
322 aHandle
= rtl_digest_create( rtl_Digest_AlgorithmMD5
);
323 CPPUNIT_ASSERT_MESSAGE("create with rtl_Digest_AlgorithmMD5", aHandle
!= 0);
325 aError
= rtl_digest_updateMD5(aHandle
, NULL
, 0);
326 CPPUNIT_ASSERT_MESSAGE("handle parameter 'pData' wrong", aError
== rtl_Digest_E_Argument
);
328 aError
= rtl_digest_updateMD5(aHandle
, pData
, 0);
329 CPPUNIT_ASSERT_MESSAGE("handle parameter 'nSize' wrong", aError
== rtl_Digest_E_None
);
331 rtl_digest_destroyMD5(aHandle
);
337 rtlDigestError aError
;
340 aError
= rtl_digest_get(aHandle
, NULL
, 0);
341 CPPUNIT_ASSERT_MESSAGE("does not handle wrong parameter", aError
== rtl_Digest_E_Argument
);
344 aError
= rtl_digest_getMD5(aHandle
, NULL
, 0);
345 CPPUNIT_ASSERT_MESSAGE("does not handle wrong parameter", aError
== rtl_Digest_E_Argument
);
347 // test with wrong algorithm
348 aHandle
= rtl_digest_create(rtl_Digest_AlgorithmMD2
);
349 CPPUNIT_ASSERT_MESSAGE("create with rtl_Digest_AlgorithmMD2", aHandle
!= 0);
351 sal_uInt32 nKeyLen
= rtl_digest_queryLength(aHandle
);
352 boost::scoped_array
<sal_uInt8
> pKeyBuffer(new sal_uInt8
[nKeyLen
]);
354 aError
= rtl_digest_getMD5(aHandle
, NULL
, 0);
355 CPPUNIT_ASSERT_MESSAGE("handle 2. parameter wrong", aError
== rtl_Digest_E_Argument
);
357 aError
= rtl_digest_getMD5(aHandle
, pKeyBuffer
.get(), 0);
358 CPPUNIT_ASSERT_MESSAGE("handle parameter 'handle' wrong", aError
== rtl_Digest_E_Algorithm
);
360 rtl_digest_destroyMD2(aHandle
);
362 aHandle
= rtl_digest_create(rtl_Digest_AlgorithmMD5
);
363 CPPUNIT_ASSERT_MESSAGE("create with rtl_Digest_AlgorithmMD5", aHandle
!= 0);
365 aError
= rtl_digest_getMD5(aHandle
, NULL
, nKeyLen
);
366 CPPUNIT_ASSERT_MESSAGE("handle parameter 'pData' wrong", aError
== rtl_Digest_E_Argument
);
368 aError
= rtl_digest_getMD5(aHandle
, pKeyBuffer
.get(), 0);
369 CPPUNIT_ASSERT_MESSAGE("handle parameter 'nSize' wrong", aError
== rtl_Digest_E_BufferSize
);
371 rtl_digest_destroyMD5(aHandle
);
374 void testSHA1SumForBiggerInputData()
376 // The test data was extracted from oox encrypted document (salt + password).
377 // First case: 16 bytes salt + 34 bytes password (12345678901234567)
378 // Second case: 16 bytes salt + 36 bytes password (123456789012345678)
380 const unsigned char aData
[] = {
381 0x37, 0x5f, 0x47, 0x7a, 0xd2, 0x13, 0xbe, 0xd2, 0x3c, 0x23, 0x33, 0x39,
382 0x68, 0x21, 0x03, 0x6d, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00,
383 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x30, 0x00,
384 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00,
388 boost::scoped_array
<sal_uInt8
> pResult(new sal_uInt8
[RTL_DIGEST_LENGTH_SHA1
]);
390 OString sExpected
= "06f460d693aecdd3b5cbe8365408eccfc570f32a";
392 rtl_digest_SHA1(aData
, sizeof(aData
), pResult
.get(), RTL_DIGEST_LENGTH_SHA1
);
394 OString sKey
= createHex(pResult
.get(), RTL_DIGEST_LENGTH_SHA1
);
396 CPPUNIT_ASSERT_EQUAL(sExpected
, sKey
);
400 #if 0 // Don't remove, but instead fix the test or something
402 // With this test case rtl_digest_SHA1 computes the wrong sum. This was confirmed
403 // by decrytion of a MSO encrypted document. Replacing the rtl_digest_SHA1 calculation
404 // with sha1 calculation from NSS was able to decrypt the document.
406 const unsigned char aData
[] = {
407 0x37, 0x5f, 0x47, 0x7a, 0xd2, 0x13, 0xbe, 0xd2, 0x3c, 0x23, 0x33, 0x39,
408 0x68, 0x21, 0x03, 0x6d, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00,
409 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x30, 0x00,
410 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00,
411 0x37, 0x00, 0x38, 0x00
414 boost::scoped_array
<sal_uInt8
> pResult(new sal_uInt8
[RTL_DIGEST_LENGTH_SHA1
]);
416 OString sExpected
= "0bfe41eb7fb3edf5f5a6de57192de4ba1b925758";
418 rtl_digest_SHA1(aData
, sizeof(aData
), pResult
.get(), RTL_DIGEST_LENGTH_SHA1
);
420 OString sKey
= createHex(pResult
.get(), RTL_DIGEST_LENGTH_SHA1
);
422 CPPUNIT_ASSERT_EQUAL(sExpected
, sKey
);
427 CPPUNIT_TEST_SUITE(DigestTest
);
428 CPPUNIT_TEST(testCreate
);
429 CPPUNIT_TEST(testQuery
);
430 CPPUNIT_TEST(testQueryLength
);
431 CPPUNIT_TEST(testInit
);
432 CPPUNIT_TEST(testEqual
);
433 CPPUNIT_TEST(testCheckSum
);
434 CPPUNIT_TEST(testPBKDF2
);
435 CPPUNIT_TEST(testUpdate
);
436 CPPUNIT_TEST(testGet
);
437 CPPUNIT_TEST(testSHA1SumForBiggerInputData
);
439 CPPUNIT_TEST_SUITE_END();
442 CPPUNIT_TEST_SUITE_REGISTRATION(DigestTest
);
444 } // namespace rtl_digest
446 CPPUNIT_PLUGIN_IMPLEMENT();
448 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */