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