Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
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>
25 :
26 : #include <boost/scoped_array.hpp>
27 :
28 : #include <rtl/digest.h>
29 : #include <rtl/ustring.hxx>
30 : #include <rtl/ustrbuf.hxx>
31 : #include <rtl/strbuf.hxx>
32 :
33 : #include <string.h>
34 :
35 : using namespace rtl;
36 :
37 : namespace
38 : {
39 :
40 1 : const OString sSampleString = "This is a sample sentence, which we use to check some crypto functions in sal.";
41 1 : const OString sSampleString_only_one_diff = "This is a sample sentence. which we use to check some crypto functions in sal.";
42 :
43 : const rtlDigestAlgorithm constDigestAlgorithms[] =
44 : {
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,
51 : };
52 :
53 : const sal_uInt32 constDigestAlgorithmLengths[] =
54 : {
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,
61 : };
62 :
63 1 : const OString constSampleStringSums[] =
64 : {
65 : "647ee6c9d4aa5fdd374ed9d7a156acbf",
66 : "b16b903e6fc0b62ae389013ed93fe531",
67 : "eab2814429b2613301c8a077b806af3680548914",
68 : "2bc5bdb7506a2cdc2fd27fc8b9889343012d5008",
69 : "0b1b0e1a6f2e4420326354b031063605",
70 : "1998c6a556915be76451bfb587fa7c34d849936e"
71 1 : };
72 :
73 : // Create hex-value string from the digest value to keep the string size minimal
74 25 : OString createHex(sal_uInt8* pKeyBuffer, sal_uInt32 nKeyLen)
75 : {
76 25 : OStringBuffer aBuffer(nKeyLen * 2 + 1);
77 485 : for (sal_uInt32 i = 0; i < nKeyLen; ++i)
78 : {
79 460 : sal_Int32 nValue = (sal_Int32) pKeyBuffer[i];
80 460 : if (nValue < 16)
81 28 : aBuffer.append('0');
82 460 : aBuffer.append(nValue, 16);
83 : }
84 25 : return aBuffer.makeStringAndClear();
85 : }
86 :
87 10 : OString getDigest(const OString& aMessage, rtlDigestAlgorithm aAlgorithm)
88 : {
89 10 : rtlDigest handle = rtl_digest_create(aAlgorithm);
90 :
91 10 : const sal_uInt8* pData = reinterpret_cast<const sal_uInt8*>(aMessage.getStr());
92 10 : sal_uInt32 nSize = aMessage.getLength();
93 :
94 10 : rtl_digest_init(handle, pData, nSize);
95 10 : rtl_digest_update(handle, pData, nSize);
96 :
97 10 : sal_uInt32 nKeyLen = rtl_digest_queryLength(handle);
98 10 : boost::scoped_array<sal_uInt8> pKeyBuffer(new sal_uInt8[nKeyLen]);
99 :
100 10 : rtl_digest_get(handle, pKeyBuffer.get(), nKeyLen);
101 10 : OString aSum = createHex(pKeyBuffer.get(), nKeyLen);
102 :
103 10 : rtl_digest_destroy( handle );
104 10 : return aSum;
105 : }
106 :
107 30 : class DigestTest : public CppUnit::TestFixture
108 : {
109 : public:
110 1 : void testCreate()
111 : {
112 1 : int aAlgorithmSize = sizeof(constDigestAlgorithms) / sizeof(constDigestAlgorithms[0]);
113 :
114 7 : for (int i = 0; i < aAlgorithmSize; i++)
115 : {
116 6 : rtlDigest handle = rtl_digest_create( constDigestAlgorithms[i] );
117 6 : CPPUNIT_ASSERT_MESSAGE("create digest", handle != NULL);
118 6 : rtl_digest_destroy( handle );
119 : }
120 :
121 1 : rtlDigest handle = rtl_digest_create( rtl_Digest_AlgorithmInvalid );
122 1 : CPPUNIT_ASSERT_MESSAGE("create invalid digest", handle == NULL);
123 1 : rtl_digest_destroy( handle );
124 1 : }
125 :
126 1 : void testQuery()
127 : {
128 1 : int aAlgorithmSize = sizeof(constDigestAlgorithms) / sizeof(constDigestAlgorithms[0]);
129 :
130 7 : for (int i = 0; i < aAlgorithmSize; i++)
131 : {
132 6 : rtlDigest handle = rtl_digest_create(constDigestAlgorithms[i]);
133 6 : rtlDigestAlgorithm aAlgo = rtl_digest_queryAlgorithm(handle);
134 6 : CPPUNIT_ASSERT_MESSAGE("query handle", constDigestAlgorithms[i] == aAlgo);
135 6 : rtl_digest_destroy( handle );
136 : }
137 :
138 1 : }
139 :
140 1 : void testQueryLength()
141 : {
142 1 : int aAlgorithmSize = sizeof(constDigestAlgorithms) / sizeof(constDigestAlgorithms[0]);
143 : rtlDigest handle;
144 : sal_uInt32 nAlgoLength;
145 :
146 7 : for (int i = 0; i < aAlgorithmSize; i++)
147 : {
148 6 : handle = rtl_digest_create(constDigestAlgorithms[i]);
149 6 : nAlgoLength = rtl_digest_queryLength(handle);
150 6 : CPPUNIT_ASSERT_MESSAGE("query Length", constDigestAlgorithmLengths[i] == nAlgoLength);
151 6 : rtl_digest_destroy( handle );
152 : }
153 :
154 1 : handle = rtl_digest_create( rtl_Digest_AlgorithmInvalid );
155 1 : nAlgoLength = rtl_digest_queryLength(handle);
156 1 : CPPUNIT_ASSERT_MESSAGE("query length", 0 == nAlgoLength);
157 1 : rtl_digest_destroy( handle );
158 1 : }
159 :
160 1 : void testInit()
161 : {
162 : rtlDigestError aError;
163 : rtlDigest handle;
164 :
165 1 : handle = NULL;
166 1 : aError = rtl_digest_init(handle, NULL, 0);
167 1 : CPPUNIT_ASSERT_MESSAGE("init(NULL, 0, 0)", aError == rtl_Digest_E_Argument);
168 :
169 1 : handle = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
170 1 : aError = rtl_digest_init(handle, NULL, 0);
171 1 : CPPUNIT_ASSERT_MESSAGE("init(handle, 0, 0)", aError == rtl_Digest_E_None);
172 1 : rtl_digest_destroy( handle );
173 :
174 1 : int aAlgorithmSize = sizeof(constDigestAlgorithms) / sizeof(constDigestAlgorithms[0]);
175 :
176 7 : for (int i = 0; i < aAlgorithmSize; i++)
177 : {
178 6 : handle = rtl_digest_create(constDigestAlgorithms[i]);
179 :
180 6 : OString aMessage = sSampleString;
181 6 : const sal_uInt8* pData = reinterpret_cast<const sal_uInt8*>(aMessage.getStr());
182 6 : sal_uInt32 nSize = aMessage.getLength();
183 :
184 6 : aError = rtl_digest_init(handle, pData, nSize);
185 6 : CPPUNIT_ASSERT_MESSAGE("init(handle, pData, nSize)", aError == rtl_Digest_E_None);
186 :
187 6 : rtl_digest_update(handle, pData, nSize);
188 :
189 6 : sal_uInt32 nKeyLen = rtl_digest_queryLength( handle );
190 12 : boost::scoped_array<sal_uInt8> pKeyBuffer(new sal_uInt8[nKeyLen]);
191 :
192 6 : rtl_digest_get( handle, pKeyBuffer.get(), nKeyLen );
193 6 : createHex(pKeyBuffer.get(), nKeyLen);
194 :
195 6 : rtl_digest_destroy( handle );
196 6 : }
197 1 : }
198 :
199 1 : void testEqual()
200 : {
201 : {
202 1 : OString aMsg1 = sSampleString;
203 2 : OString aMsg2 = sSampleString;
204 :
205 2 : OString aSum1 = getDigest(aMsg1, rtl_Digest_AlgorithmMD5);
206 2 : OString aSum2 = getDigest(aMsg2, rtl_Digest_AlgorithmMD5);
207 :
208 1 : CPPUNIT_ASSERT_MESSAGE("md5sum must have a length", aSum1.getLength() == 32 && aSum2.getLength() == 32 );
209 2 : CPPUNIT_ASSERT_MESSAGE("source is the same, dest must be also the same", aSum1.equals(aSum2));
210 : }
211 :
212 : {
213 1 : OString aMsg1 = sSampleString;
214 2 : OString aMsg2 = sSampleString_only_one_diff;
215 :
216 2 : OString aSum1 = getDigest(aMsg1, rtl_Digest_AlgorithmMD5);
217 2 : OString aSum2 = getDigest(aMsg2, rtl_Digest_AlgorithmMD5);
218 :
219 1 : CPPUNIT_ASSERT_MESSAGE("md5sum must have a length", aSum1.getLength() == 32 && aSum2.getLength() == 32 );
220 2 : CPPUNIT_ASSERT_MESSAGE("differ only in one char", !aSum1.equals(aSum2));
221 : }
222 1 : }
223 :
224 1 : void testCheckSum()
225 : {
226 1 : int aAlgorithmSize = sizeof(constDigestAlgorithms) / sizeof(constDigestAlgorithms[0]);
227 :
228 7 : for (int i = 0; i < aAlgorithmSize; i++)
229 : {
230 6 : OString aSum = getDigest(sSampleString, constDigestAlgorithms[i]);
231 6 : CPPUNIT_ASSERT_EQUAL_MESSAGE("Checksum of sample string is wrong.", constSampleStringSums[i], aSum);
232 6 : }
233 1 : }
234 :
235 8 : OString runCheckPBKDF2(OString& sPassword, bool bClearSalt, sal_uInt32 nCount)
236 : {
237 8 : sal_uInt32 nKeyLen = RTL_DIGEST_LENGTH_HMAC_SHA1;
238 8 : boost::scoped_array<sal_uInt8> pKeyBuffer(new sal_uInt8[nKeyLen]);
239 :
240 8 : memset(pKeyBuffer.get(), 0, nKeyLen);
241 :
242 8 : sal_uInt8 const * pPassword = reinterpret_cast<sal_uInt8 const *>(sPassword.getStr());
243 8 : sal_Int32 nPasswordLen = sPassword.getLength();
244 :
245 8 : sal_uInt32 nSaltDataLen = RTL_DIGEST_LENGTH_HMAC_SHA1;
246 16 : boost::scoped_array<sal_uInt8> pSaltData(new sal_uInt8[nSaltDataLen]);
247 8 : memset(pSaltData.get(), 0, nSaltDataLen);
248 :
249 8 : if (!bClearSalt)
250 : {
251 : // wilful contamination
252 4 : pSaltData[0] = 1;
253 : }
254 :
255 8 : rtlDigestError aError = rtl_digest_PBKDF2(pKeyBuffer.get(), nKeyLen, pPassword, nPasswordLen, pSaltData.get(), nSaltDataLen, nCount);
256 :
257 8 : CPPUNIT_ASSERT(aError == rtl_Digest_E_None );
258 :
259 8 : rtl::OString aKey = createHex(pKeyBuffer.get(), nKeyLen);
260 :
261 : // OString sSalt = createHex(pSaltData, nSaltDataLen);
262 : // printf("Salt: %s\n", sSalt.getStr());
263 :
264 : // CPPUNIT_ASSERT_MESSAGE("md5sum of sample string is wrong. Code changes or sample problems, please check.", aStr.equals(sSampleString_PBKDF2) );
265 16 : return aKey;
266 : }
267 :
268 1 : void testPBKDF2()
269 : {
270 1 : OString aPassword = "Password";
271 :
272 : // all permutations
273 1 : runCheckPBKDF2(aPassword, false, 1);
274 1 : runCheckPBKDF2(aPassword, false, 2);
275 1 : runCheckPBKDF2(aPassword, true, 1);
276 1 : runCheckPBKDF2(aPassword, true, 2);
277 1 : runCheckPBKDF2(aPassword, false, 3);
278 1 : runCheckPBKDF2(aPassword, false, 4);
279 1 : runCheckPBKDF2(aPassword, true, 3);
280 1 : runCheckPBKDF2(aPassword, true, 4);
281 1 : }
282 :
283 1 : void testUpdate()
284 : {
285 : rtlDigestError aError;
286 : rtlDigest aHandle;
287 :
288 1 : aHandle = NULL;
289 1 : aError = rtl_digest_update(aHandle, NULL, 0);
290 1 : CPPUNIT_ASSERT_MESSAGE("does not handle wrong parameter", aError == rtl_Digest_E_Argument);
291 :
292 1 : aHandle = NULL;
293 1 : aError = rtl_digest_updateMD2(aHandle, NULL, 0);
294 1 : CPPUNIT_ASSERT_MESSAGE("does not handle wrong parameter", aError == rtl_Digest_E_Argument);
295 :
296 1 : aError = rtl_digest_updateMD5(aHandle, NULL, 0);
297 1 : CPPUNIT_ASSERT_MESSAGE("does not handle wrong parameter", aError == rtl_Digest_E_Argument);
298 :
299 1 : aHandle = rtl_digest_create( rtl_Digest_AlgorithmMD2 );
300 1 : CPPUNIT_ASSERT_MESSAGE("create with rtl_Digest_AlgorithmMD2", aHandle != 0);
301 :
302 1 : const sal_uInt8* pData = reinterpret_cast<const sal_uInt8*>(sSampleString.getStr());
303 1 : sal_uInt32 nSize = sSampleString.getLength();
304 :
305 1 : aError = rtl_digest_updateMD2(aHandle, NULL, 0);
306 1 : CPPUNIT_ASSERT_MESSAGE("handle parameter 'pData' wrong", aError == rtl_Digest_E_Argument);
307 :
308 1 : aError = rtl_digest_updateMD2(aHandle, pData, 0);
309 1 : CPPUNIT_ASSERT_MESSAGE("handle parameter 'nSize' wrong", aError == rtl_Digest_E_None);
310 :
311 1 : rtl_digest_destroyMD2(aHandle);
312 :
313 : // use wrong Algorithm!!! This is volitional!
314 1 : aHandle = rtl_digest_create(rtl_Digest_AlgorithmMD2);
315 1 : CPPUNIT_ASSERT_MESSAGE("create with rtl_Digest_AlgorithmMD2", aHandle != 0);
316 :
317 1 : aError = rtl_digest_updateMD5(aHandle, pData, nSize);
318 1 : CPPUNIT_ASSERT_MESSAGE("handle parameter 'handle' wrong", aError == rtl_Digest_E_Algorithm);
319 :
320 1 : rtl_digest_destroyMD5(aHandle);
321 :
322 1 : aHandle = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
323 1 : CPPUNIT_ASSERT_MESSAGE("create with rtl_Digest_AlgorithmMD5", aHandle != 0);
324 :
325 1 : aError = rtl_digest_updateMD5(aHandle, NULL, 0);
326 1 : CPPUNIT_ASSERT_MESSAGE("handle parameter 'pData' wrong", aError == rtl_Digest_E_Argument);
327 :
328 1 : aError = rtl_digest_updateMD5(aHandle, pData, 0);
329 1 : CPPUNIT_ASSERT_MESSAGE("handle parameter 'nSize' wrong", aError == rtl_Digest_E_None);
330 :
331 1 : rtl_digest_destroyMD5(aHandle);
332 1 : }
333 :
334 1 : void testGet()
335 : {
336 : rtlDigest aHandle;
337 : rtlDigestError aError;
338 :
339 1 : aHandle = NULL;
340 1 : aError = rtl_digest_get(aHandle, NULL, 0);
341 1 : CPPUNIT_ASSERT_MESSAGE("does not handle wrong parameter", aError == rtl_Digest_E_Argument);
342 :
343 1 : aHandle = NULL;
344 1 : aError = rtl_digest_getMD5(aHandle, NULL, 0);
345 1 : CPPUNIT_ASSERT_MESSAGE("does not handle wrong parameter", aError == rtl_Digest_E_Argument);
346 :
347 : // test with wrong algorithm
348 1 : aHandle = rtl_digest_create(rtl_Digest_AlgorithmMD2);
349 1 : CPPUNIT_ASSERT_MESSAGE("create with rtl_Digest_AlgorithmMD2", aHandle != 0);
350 :
351 1 : sal_uInt32 nKeyLen = rtl_digest_queryLength(aHandle);
352 1 : boost::scoped_array<sal_uInt8> pKeyBuffer(new sal_uInt8[nKeyLen]);
353 :
354 1 : aError = rtl_digest_getMD5(aHandle, NULL, 0);
355 1 : CPPUNIT_ASSERT_MESSAGE("handle 2. parameter wrong", aError == rtl_Digest_E_Argument);
356 :
357 1 : aError = rtl_digest_getMD5(aHandle, pKeyBuffer.get(), 0);
358 1 : CPPUNIT_ASSERT_MESSAGE("handle parameter 'handle' wrong", aError == rtl_Digest_E_Algorithm);
359 :
360 1 : rtl_digest_destroyMD2(aHandle);
361 :
362 1 : aHandle = rtl_digest_create(rtl_Digest_AlgorithmMD5);
363 1 : CPPUNIT_ASSERT_MESSAGE("create with rtl_Digest_AlgorithmMD5", aHandle != 0);
364 :
365 1 : aError = rtl_digest_getMD5(aHandle, NULL, nKeyLen);
366 1 : CPPUNIT_ASSERT_MESSAGE("handle parameter 'pData' wrong", aError == rtl_Digest_E_Argument );
367 :
368 1 : aError = rtl_digest_getMD5(aHandle, pKeyBuffer.get(), 0);
369 1 : CPPUNIT_ASSERT_MESSAGE("handle parameter 'nSize' wrong", aError == rtl_Digest_E_BufferSize );
370 :
371 1 : rtl_digest_destroyMD5(aHandle);
372 1 : }
373 :
374 1 : void testSHA1SumForBiggerInputData()
375 : {
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)
379 : {
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,
385 : 0x37, 0x00
386 1 : };
387 :
388 1 : boost::scoped_array<sal_uInt8> pResult(new sal_uInt8[RTL_DIGEST_LENGTH_SHA1]);
389 :
390 2 : OString sExpected = "06f460d693aecdd3b5cbe8365408eccfc570f32a";
391 :
392 1 : rtl_digest_SHA1(aData, sizeof(aData), pResult.get(), RTL_DIGEST_LENGTH_SHA1);
393 :
394 2 : OString sKey = createHex(pResult.get(), RTL_DIGEST_LENGTH_SHA1);
395 :
396 2 : CPPUNIT_ASSERT_EQUAL(sExpected, sKey);
397 : }
398 :
399 : {
400 : #if 0 // Don't remove, but instead fix the test or something
401 :
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.
405 :
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
412 : };
413 :
414 : boost::scoped_array<sal_uInt8> pResult(new sal_uInt8[RTL_DIGEST_LENGTH_SHA1]);
415 :
416 : OString sExpected = "0bfe41eb7fb3edf5f5a6de57192de4ba1b925758";
417 :
418 : rtl_digest_SHA1(aData, sizeof(aData), pResult.get(), RTL_DIGEST_LENGTH_SHA1);
419 :
420 : OString sKey = createHex(pResult.get(), RTL_DIGEST_LENGTH_SHA1);
421 :
422 : CPPUNIT_ASSERT_EQUAL(sExpected, sKey);
423 : #endif
424 : }
425 1 : }
426 :
427 2 : CPPUNIT_TEST_SUITE(DigestTest);
428 1 : CPPUNIT_TEST(testCreate);
429 1 : CPPUNIT_TEST(testQuery);
430 1 : CPPUNIT_TEST(testQueryLength);
431 1 : CPPUNIT_TEST(testInit);
432 1 : CPPUNIT_TEST(testEqual);
433 1 : CPPUNIT_TEST(testCheckSum);
434 1 : CPPUNIT_TEST(testPBKDF2);
435 1 : CPPUNIT_TEST(testUpdate);
436 1 : CPPUNIT_TEST(testGet);
437 1 : CPPUNIT_TEST(testSHA1SumForBiggerInputData);
438 :
439 5 : CPPUNIT_TEST_SUITE_END();
440 : };
441 :
442 1 : CPPUNIT_TEST_SUITE_REGISTRATION(DigestTest);
443 :
444 : } // namespace rtl_digest
445 :
446 4 : CPPUNIT_PLUGIN_IMPLEMENT();
447 :
448 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|