VirtualBox

source: vbox/trunk/src/VBox/Runtime/testcase/tstRTShaCrypt.cpp@ 102289

Last change on this file since 102289 was 102289, checked in by vboxsync, 14 months ago

IPRT: Implemented SHA-crypt 256 / 512 variants, along with testcases. Needed for password hashing in cloud-init-based Linux installers. bugref:10551

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.2 KB
Line 
1/* $Id: tstRTShaCrypt.cpp 102289 2023-11-24 12:46:36Z 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#include <iprt/errcore.h>
43#include <iprt/initterm.h>
44#include <iprt/rand.h>
45#include <iprt/sha.h>
46#include <iprt/string.h>
47#include <iprt/test.h>
48
49
50/*********************************************************************************************************************************
51* Global Variables *
52*********************************************************************************************************************************/
53static RTTEST g_hTest;
54
55
56/*********************************************************************************************************************************
57* Test data *
58*********************************************************************************************************************************/
59
60/** Digest type. */
61typedef enum TST_DIGESTTYPE
62{
63 TST_DIGESTTYPE_RANDOM = 0,
64 TST_DIGESTTYPE_SHA256,
65 TST_DIGESTTYPE_SHA512,
66 TST_DIGESTTYPE_LAST
67} TST_DIGESTTYPE;
68
69static struct
70{
71 /** Cleartext password. */
72 const char *pszPassword;
73 /** Salt to use. If NULL, a random salt will be used. */
74 const char *pszSalt;
75 /** Number of rounds to use. If set to UINT32_MAX, random rounds will be used. */
76 uint32_t cRounds;
77 /** Digest type to use. If set to 0, a random digest type will be used. */
78 TST_DIGESTTYPE enmType;
79 /** Overall test outcome. */
80 int rc;
81 /** Expected result as a string. Can be NULL to skip testing this. */
82 const char *pszResultStr;
83} g_aTests[] =
84{
85 /*
86 * Invalid stuff.
87 */
88 { /* No salt */
89 /* pszPassword */ "changeme",
90 /* pszSalt */ "",
91 /* cRounds */ RT_SHACRYPT_DEFAULT_ROUNDS,
92 /* enmType */ TST_DIGESTTYPE_RANDOM,
93 /* rc */ VERR_INVALID_PARAMETER,
94 /* pszResultStr */ ""
95 },
96 { /* Salt too short */
97 /* pszPassword */ "changeme",
98 /* pszSalt */ "1234",
99 /* cRounds */ RT_SHACRYPT_DEFAULT_ROUNDS,
100 /* enmType */ TST_DIGESTTYPE_RANDOM,
101 /* rc */ VERR_INVALID_PARAMETER,
102 /* pszResultStr */ ""
103 },
104 { /* Salt too long */
105 /* pszPassword */ "changeme",
106 /* pszSalt */ "12341234123412341234123412341234",
107 /* cRounds */ RT_SHACRYPT_DEFAULT_ROUNDS,
108 /* enmType */ TST_DIGESTTYPE_RANDOM,
109 /* rc */ VERR_INVALID_PARAMETER,
110 /* pszResultStr */ ""
111 },
112 { /* Invalid rounds */
113 /* pszPassword */ "changeme",
114 /* pszSalt */ "12341234123412341234123412341234",
115 /* cRounds */ 0,
116 /* enmType */ TST_DIGESTTYPE_RANDOM,
117 /* rc */ VERR_INVALID_PARAMETER,
118 /* pszResultStr */ ""
119 },
120 /*
121 * Valid stuff.
122 */
123 { /* Expected string */
124 /* pszPassword */ "changeme",
125 /* pszSalt */ "foo12345",
126 /* cRounds */ RT_SHACRYPT_DEFAULT_ROUNDS,
127 /* enmType */ TST_DIGESTTYPE_SHA256,
128 /* rc */ VINF_SUCCESS,
129 /* pszResultStr */ "$5$foo12345$KnOIYJmTgZ744xCqNLl1I9qF.Xq47vHTH.yVStiAMZD"
130 },
131 { /* Expected string */
132 /* pszPassword */ "changeme",
133 /* pszSalt */ "foo12345",
134 /* cRounds */ RT_SHACRYPT_DEFAULT_ROUNDS,
135 /* enmType */ TST_DIGESTTYPE_SHA512,
136 /* rc */ VINF_SUCCESS,
137 /* pszResultStr */ "$6$foo12345$cb11CtCP6YgoZr8SyNoD2TAdOY4OmTzA6kfDgju5JrNVzgeCBU1ALbJHVlEuSImPKAoSnT53N7k7BqzjYRRPk/"
138 },
139 { /* Custom rounds */
140 /* pszPassword */ "changeme",
141 /* pszSalt */ "foo12345",
142 /* cRounds */ 42,
143 /* enmType */ TST_DIGESTTYPE_RANDOM,
144 /* rc */ VINF_SUCCESS,
145 /* pszResultStr */ NULL
146 },
147 { /* Random salt + rounds */
148 /* pszPassword */ "changeme",
149 /* pszSalt */ NULL,
150 /* cRounds */ UINT32_MAX,
151 /* enmType */ TST_DIGESTTYPE_RANDOM,
152 /* rc */ VINF_SUCCESS,
153 /* pszResultStr */ NULL
154 },
155 { /* Random salt */
156 /* pszPassword */ "changeme",
157 /* pszSalt */ NULL,
158 /* cRounds */ RT_SHACRYPT_DEFAULT_ROUNDS,
159 /* enmType */ TST_DIGESTTYPE_RANDOM,
160 /* rc */ VINF_SUCCESS,
161 /* pszResultStr */ NULL
162 }
163};
164
165
166int main()
167{
168 /*
169 * Init.
170 */
171 RTTEST hTest;
172 int rc = RTTestInitAndCreate("tstRTShaCrypt", &hTest);
173 if (rc)
174 return rc;
175 RTTestBanner(hTest);
176 g_hTest = hTest;
177
178 bool const fAssertMayPanic = RTAssertMayPanic();
179 RTAssertSetMayPanic(false); /* To test invalid stuff. */
180 bool const fAssertQuiet = RTAssertAreQuiet();
181 RTAssertSetQuiet(true); /* Ditto. */
182
183 char szSalt[RT_SHACRYPT_MAX_SALT_LEN + 1];
184 uint8_t abDigest[RTSHA512_HASH_SIZE];
185
186 for (uint32_t i = 0; i < RT_ELEMENTS(g_aTests); i++)
187 {
188 const char *pszSalt;
189 if (g_aTests[i].pszSalt)
190 pszSalt = g_aTests[i].pszSalt;
191 else
192 {
193 static const char aRange[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!?+\"%&/()[]{}=#";
194 for (unsigned s = 0; s < RT_SHACRYPT_MAX_SALT_LEN; s++) /* Always go with a strong salt by default. */
195 szSalt[s] = aRange[RTRandU32Ex(0, sizeof(aRange) - 2)];
196 pszSalt = szSalt;
197 }
198
199 uint32_t cRounds;
200 if (g_aTests[i].cRounds == UINT32_MAX)
201 cRounds = RTRandU32Ex(1, _512K /* Save a bit of time on the testboxes */);
202 else
203 cRounds = g_aTests[i].cRounds;
204
205 TST_DIGESTTYPE enmType;
206 if (g_aTests[i].enmType == TST_DIGESTTYPE_RANDOM)
207 enmType = (TST_DIGESTTYPE)RTRandU32Ex(1, TST_DIGESTTYPE_LAST - 1);
208 else
209 enmType = g_aTests[i].enmType;
210
211 switch (enmType)
212 {
213 case TST_DIGESTTYPE_SHA256:
214 rc = RTShaCrypt256(g_aTests[i].pszPassword, pszSalt, cRounds, abDigest);
215 break;
216
217 case TST_DIGESTTYPE_SHA512:
218 rc = RTShaCrypt512(g_aTests[i].pszPassword, pszSalt, cRounds, abDigest);
219 break;
220
221 default:
222 AssertFailed();
223 break;
224 }
225
226 if ( RT_SUCCESS(rc)
227 && g_aTests[i].pszResultStr)
228 {
229 char szResult[RTSHA512_DIGEST_LEN];
230
231 switch (enmType)
232 {
233 case TST_DIGESTTYPE_SHA256:
234 rc = RTShaCrypt256ToString(abDigest, pszSalt, cRounds, szResult, sizeof(szResult));
235 break;
236
237 case TST_DIGESTTYPE_SHA512:
238 rc = RTShaCrypt512ToString(abDigest, pszSalt, cRounds, szResult, sizeof(szResult));
239 break;
240
241 default:
242 AssertFailed();
243 break;
244 }
245
246 if (RT_SUCCESS(rc))
247 {
248 if (RTStrCmp(szResult, g_aTests[i].pszResultStr))
249 RTTestIFailed("#%u: Returns '%s', expected '%s'", i, szResult, g_aTests[i].pszResultStr);
250 }
251 }
252
253 if (rc != g_aTests[i].rc)
254 RTTestIFailed("#%u: Returned %Rrc, expected %Rrc", i, rc, g_aTests[i].rc);
255 }
256
257 RTAssertSetMayPanic(fAssertMayPanic);
258 RTAssertSetQuiet(fAssertQuiet);
259
260 /*
261 * Done.
262 */
263 return RTTestSummaryAndDestroy(hTest);
264}
265
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette