VirtualBox

source: vbox/trunk/src/VBox/Runtime/testcase/tstRTCrShaCrypt.cpp

Last change on this file was 102489, checked in by vboxsync, 6 months ago

tstRTCrShaCrypt name fix. bugref:10551

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.4 KB
Line 
1/* $Id: tstRTCrShaCrypt.cpp 102489 2023-12-06 00:27:54Z vboxsync $ */
2/** @file
3 * IPRT Testcase - SHA-crypt 256 / 512.
4 */
5
6/*
7 * Copyright (C) 2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/crypto/shacrypt.h>
42
43#include <iprt/errcore.h>
44#include <iprt/initterm.h>
45#include <iprt/rand.h>
46#include <iprt/string.h>
47#include <iprt/test.h>
48
49
50/*********************************************************************************************************************************
51* Test data *
52*********************************************************************************************************************************/
53
54/** Digest type. */
55typedef enum TSTDIGESTTYPE
56{
57 TSTDIGESTTYPE_RANDOM = 0,
58 TSTDIGESTTYPE_SHA256,
59 TSTDIGESTTYPE_SHA512,
60 TSTDIGESTTYPE_END
61} TSTDIGESTTYPE;
62
63static struct
64{
65 /** Cleartext password. */
66 const char *pszPassword;
67 /** Salt to use. If NULL, a random salt will be used. */
68 const char *pszSalt;
69 /** Number of rounds to use. If set to UINT32_MAX, random rounds will be used. */
70 uint32_t cRounds;
71 /** Digest type to use. If set to 0, a random digest type will be used. */
72 TSTDIGESTTYPE enmType;
73 /** Overall test outcome. */
74 int rc;
75 /** Expected result as a string. Can be NULL to skip testing this. */
76 const char *pszResult;
77} g_aTests[] =
78{
79 /*
80 * Invalid stuff.
81 */
82 { /* No salt */
83 /* .pszPassword = */ "changeme",
84 /* .pszSalt = */ "",
85 /* .cRounds = */ RT_SHACRYPT_ROUNDS_DEFAULT,
86 /* .enmType = */ TSTDIGESTTYPE_RANDOM,
87 /* .rc = */ VERR_BUFFER_UNDERFLOW,
88 /* .pszResult = */ ""
89 },
90 { /* Salt too short */
91 /* .pszPassword = */ "changeme",
92 /* .pszSalt = */ "1234",
93 /* .cRounds = */ RT_SHACRYPT_ROUNDS_DEFAULT,
94 /* .enmType = */ TSTDIGESTTYPE_RANDOM,
95 /* .rc = */ VERR_BUFFER_UNDERFLOW,
96 /* .pszResult = */ ""
97 },
98 { /* Salt too long */
99 /* .pszPassword = */ "changeme",
100 /* .pszSalt = */ "12341234123412341234123412341234",
101 /* .cRounds = */ RT_SHACRYPT_ROUNDS_DEFAULT,
102 /* .enmType = */ TSTDIGESTTYPE_RANDOM,
103 /* .rc = */ VERR_TOO_MUCH_DATA,
104 /* .pszResult = */ ""
105 },
106 { /* Invalid rounds */
107 /* .pszPassword = */ "changeme",
108 /* .pszSalt = */ "0123456789abcdef",
109 /* .cRounds = */ 42,
110 /* .enmType = */ TSTDIGESTTYPE_RANDOM,
111 /* .rc = */ VERR_OUT_OF_RANGE,
112 /* .pszResult = */ ""
113 },
114 /*
115 * Valid stuff.
116 */
117 {
118 /* .pszPassword = */ "changeme",
119 /* .pszSalt = */ "foo12345",
120 /* .cRounds = */ RT_SHACRYPT_ROUNDS_DEFAULT,
121 /* .enmType = */ TSTDIGESTTYPE_SHA256,
122 /* .rc = */ VINF_SUCCESS,
123 /* .pszResult = */ "$5$foo12345$KnOIYJmTgZ744xCqNLl1I9qF.Xq47vHTH.yVStiAMZD"
124 },
125 {
126 /* .pszPassword = */ "really-secure-semi-long-password",
127 /* .pszSalt = */ "5288d4774fd14289",
128 /* .cRounds = */ 999663,
129 /* .enmType = */ TSTDIGESTTYPE_SHA256,
130 /* .rc = */ VINF_SUCCESS,
131 /* .pszResult = */ "$5$rounds=999663$5288d4774fd14289$KkMAAPiAFgo9bFHnd79MCnBmMeJXlx02ra4e/20WoC4"
132 },
133 {
134 /* .pszPassword = */ "changeme",
135 /* .pszSalt = */ "foo12345",
136 /* .cRounds = */ RT_SHACRYPT_ROUNDS_DEFAULT,
137 /* .enmType = */ TSTDIGESTTYPE_SHA512,
138 /* .rc = */ VINF_SUCCESS,
139 /* .pszResult = */ "$6$foo12345$cb11CtCP6YgoZr8SyNoD2TAdOY4OmTzA6kfDgju5JrNVzgeCBU1ALbJHVlEuSImPKAoSnT53N7k7BqzjYRRPk/"
140 },
141 {
142 /* .pszPassword = */ "really-really-really-really-really-long-and-still-insecure-password",
143 /* .pszSalt = */ "$6$rounds=384836$AbCdEfGhijKLM",
144 /* .cRounds = */ RT_SHACRYPT_ROUNDS_DEFAULT,
145 /* .enmType = */ TSTDIGESTTYPE_SHA512,
146 /* .rc = */ VINF_SUCCESS,
147 /* .pszResult = */ "$6$rounds=384836$AbCdEfGhijKLM$pJM6Ugvo4IiVCd8KTmDNIvShHX.G6p0SC/FnBNBAf9TBm1Td/s9HsVu.iWiEBxnEDWiB5zn/NBi6VTqhCP7Ii0"
148 },
149 {
150 /* .pszPassword = */ "이것은 테스트입니다", /* "This is a test" in Korean */
151 /* .pszSalt = */ "foo12345",
152 /* .cRounds = */ RT_SHACRYPT_ROUNDS_DEFAULT,
153 /* .enmType = */ TSTDIGESTTYPE_SHA256,
154 /* .rc = */ VINF_SUCCESS,
155 /* .pszResult = */ "$5$foo12345$7fumMsJKgCGipks2nNPi185ANXwfTf9Ilz70J4wKqe1"
156 },
157 {
158 /* .pszPassword = */ "이것은 테스트입니다", /* "This is a test" in Korean */
159 /* .pszSalt = */ "foo12345",
160 /* .cRounds = */ RT_SHACRYPT_ROUNDS_DEFAULT,
161 /* .enmType = */ TSTDIGESTTYPE_SHA512,
162 /* .rc = */ VINF_SUCCESS,
163 /* .pszResult = */ "$6$foo12345$IWlIz4tyl39ETRpKlQ.R42tdeB2Ax9gz9sazAynilHDFm0zXUdsrm4nXzdlSd5jJhvwV7EPSc./2pBNoL1PIw1"
164 },
165 {
166 /* .pszPassword = */ "changeme",
167 /* .pszSalt = */ "foo12345",
168 /* .cRounds = */ 40000,
169 /* .enmType = */ TSTDIGESTTYPE_RANDOM,
170 /* .rc = */ VINF_SUCCESS,
171 /* .pszResult = */ NULL
172 },
173 {
174 /* .pszPassword = */ "changeme",
175 /* .pszSalt = */ NULL,
176 /* .cRounds = */ UINT32_MAX,
177 /* .enmType = */ TSTDIGESTTYPE_RANDOM,
178 /* .rc = */ VINF_SUCCESS,
179 /* .pszResult = */ NULL
180 },
181 {
182 /* .pszPassword = */ "changeme",
183 /* .pszSalt = */ NULL,
184 /* .cRounds = */ RT_SHACRYPT_ROUNDS_DEFAULT,
185 /* .enmType = */ TSTDIGESTTYPE_RANDOM,
186 /* .rc = */ VINF_SUCCESS,
187 /* .pszResult = */ NULL
188 }
189};
190
191
192static void test1(RTTEST hTest)
193{
194 RTTestDisableAssertions(hTest);
195
196 char *pszGuardedSalt = NULL;
197 int rc = RTTestGuardedAlloc(hTest, RT_SHACRYPT_SALT_MAX_LEN + 1, 1, false, (void **)&pszGuardedSalt);
198 RTTESTI_CHECK_RC_OK_RETV(rc);
199
200 char *pszGuardedResult = NULL;
201 rc = RTTestGuardedAlloc(hTest, RT_SHACRYPT_512_MAX_SIZE, 1, false, (void **)&pszGuardedResult);
202 RTTESTI_CHECK_RC_OK_RETV(rc);
203
204 for (uint32_t i = 0; i < RT_ELEMENTS(g_aTests); i++)
205 {
206 const char *pszSalt = g_aTests[i].pszSalt;
207 if (!pszSalt)
208 {
209 uint32_t const cchSalt = RTRandU32Ex(RT_SHACRYPT_SALT_MIN_LEN, RT_SHACRYPT_SALT_MAX_LEN);
210 rc = RTCrShaCryptGenerateSalt(&pszGuardedSalt[RT_SHACRYPT_SALT_MAX_LEN - cchSalt], cchSalt);
211 RTTEST_CHECK_RC_OK(hTest, rc);
212 pszSalt = &pszGuardedSalt[RT_SHACRYPT_SALT_MAX_LEN - cchSalt];
213 }
214
215 uint32_t cRounds = g_aTests[i].cRounds;
216 if (cRounds == UINT32_MAX)
217 cRounds = RTRandU32Ex(RT_SHACRYPT_ROUNDS_MIN, _512K /* Save a bit of time on the testboxes */);
218
219 TSTDIGESTTYPE enmType = g_aTests[i].enmType;
220 if (enmType == TSTDIGESTTYPE_RANDOM)
221 enmType = (TSTDIGESTTYPE)RTRandU32Ex(TSTDIGESTTYPE_RANDOM + 1, TSTDIGESTTYPE_END - 1);
222
223 uint8_t abDigest[RTSHA512_HASH_SIZE];
224 switch (enmType)
225 {
226 case TSTDIGESTTYPE_SHA256:
227 rc = RTCrShaCrypt256Ex(g_aTests[i].pszPassword, pszSalt, cRounds, abDigest);
228 break;
229
230 case TSTDIGESTTYPE_SHA512:
231 rc = RTCrShaCrypt512Ex(g_aTests[i].pszPassword, pszSalt, cRounds, abDigest);
232 break;
233
234 default:
235 AssertFailedStmt(rc = VERR_INTERNAL_ERROR);
236 break;
237 }
238 if (rc != g_aTests[i].rc)
239 RTTestIFailed("#%u: RTCrShaCryptXxxEx(,%s,%#x,) returns %Rrc, expected %Rrc",
240 i, pszSalt, cRounds, rc, g_aTests[i].rc);
241
242 if (RT_SUCCESS(rc))
243 {
244 RT_BZERO(pszGuardedResult, RT_SHACRYPT_512_MAX_SIZE);
245 switch (enmType)
246 {
247 case TSTDIGESTTYPE_SHA256:
248 rc = RTCrShaCrypt256ToString(abDigest, pszSalt, cRounds, pszGuardedResult, RT_SHACRYPT_512_MAX_SIZE);
249 break;
250
251 case TSTDIGESTTYPE_SHA512:
252 rc = RTCrShaCrypt512ToString(abDigest, pszSalt, cRounds, pszGuardedResult, RT_SHACRYPT_512_MAX_SIZE);
253 break;
254
255 default:
256 AssertFailedStmt(rc = VERR_INTERNAL_ERROR);
257 break;
258 }
259 if (RT_SUCCESS(rc))
260 {
261 if (g_aTests[i].pszResult && strcmp(pszGuardedResult, g_aTests[i].pszResult))
262 RTTestIFailed("#%u: RTCrShaCryptXxxString returns '%s', expected '%s'",
263 i, pszGuardedResult, g_aTests[i].pszResult);
264
265 /*
266 * Do a verification round, where we pass the above result in as the salt.
267 */
268 char szResult2[RT_SHACRYPT_512_MAX_SIZE] = {0};
269 switch (enmType)
270 {
271 case TSTDIGESTTYPE_SHA256:
272 rc = RTCrShaCrypt256(g_aTests[i].pszPassword, pszGuardedResult, cRounds, szResult2, sizeof(szResult2));
273 break;
274
275 case TSTDIGESTTYPE_SHA512:
276 rc = RTCrShaCrypt512(g_aTests[i].pszPassword, pszGuardedResult, cRounds, szResult2, sizeof(szResult2));
277 break;
278
279 default:
280 AssertFailed();
281 break;
282 }
283
284 if (strcmp(szResult2, pszGuardedResult))
285 RTTestIFailed("#%u (result as salt): Returns '%s', expected '%s'", i, szResult2, pszGuardedResult);
286
287 /*
288 * Push the buffer limit on the string formatter.
289 */
290 size_t const cchNeeded = strlen(szResult2);
291 size_t const cbBufMax = RT_MIN(RT_SHACRYPT_512_MAX_SIZE, cchNeeded + 32);
292 for (size_t cbBuf = 0; cbBuf <= cbBufMax; cbBuf++)
293 {
294 char * const pszBuf = &pszGuardedResult[RT_SHACRYPT_512_MAX_SIZE - cbBuf];
295 switch (enmType)
296 {
297 case TSTDIGESTTYPE_SHA256:
298 rc = RTCrShaCrypt256ToString(abDigest, pszSalt, cRounds, pszBuf, cbBuf);
299 break;
300 case TSTDIGESTTYPE_SHA512:
301 rc = RTCrShaCrypt512ToString(abDigest, pszSalt, cRounds, pszBuf, cbBuf);
302 break;
303 default:
304 AssertFailedStmt(rc = VERR_INTERNAL_ERROR);
305 break;
306 }
307 int rcExpect = cbBuf <= cchNeeded ? VERR_BUFFER_OVERFLOW : VINF_SUCCESS;
308 if (rc != rcExpect)
309 RTTestIFailed("#%u: cbBuf=%#zx cchNeeded=%#zx: %Rrc, expected %Rrc", i, cbBuf, cchNeeded, rc, rcExpect);
310 if (cbBuf > cchNeeded && memcmp(pszBuf, szResult2, cchNeeded + 1))
311 RTTestIFailed("#%u: cbBuf=%#zx cchNeeded=%#zx: '%s', expected '%s'",
312 i, cbBuf, cchNeeded, pszBuf, szResult2);
313 }
314 }
315 else
316 RTTestIFailed("#%u: RTCrShaCryptXxxString returns %Rrc", i, rc);
317 }
318 }
319
320 RTTestRestoreAssertions(hTest);
321}
322
323
324int main()
325{
326 /*
327 * Init.
328 */
329 RTTEST hTest;
330 RTEXITCODE rcExit = RTTestInitAndCreate("tstRTCrShaCrypt", &hTest);
331 if (rcExit != RTEXITCODE_SUCCESS)
332 return rcExit;
333 RTTestBanner(hTest);
334
335 test1(hTest);
336
337 /*
338 * Done.
339 */
340 return RTTestSummaryAndDestroy(hTest);
341}
342
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use