VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/crypto/shacrypt.cpp@ 102336

Last change on this file since 102336 was 102336, checked in by vboxsync, 17 months ago

IPRT/crypto/shacrypt: Make sure to terminate strings in the RTCrShaCryptXXXToString() functions. bugref:10551

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.7 KB
Line 
1/* $Id: shacrypt.cpp 102336 2023-11-27 17:04:18Z vboxsync $ */
2/** @file
3 * IPRT - Crypto - SHA-crypt.
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
42#include <iprt/crypto/shacrypt.h>
43#include <iprt/types.h>
44#include <iprt/mem.h>
45#include <iprt/rand.h>
46#include <iprt/sha.h>
47#include <iprt/string.h>
48
49
50
51RTR3DECL(int) RTCrShaCryptGenerateSalt(char szSalt[RT_SHACRYPT_MAX_SALT_LEN + 1], size_t cchSalt)
52{
53 AssertMsgReturn(cchSalt >= RT_SHACRYPT_MIN_SALT_LEN && cchSalt <= RT_SHACRYPT_MAX_SALT_LEN, ("len=%zu\n", cchSalt),
54 VERR_INVALID_PARAMETER);
55
56 static const char aRange[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890./";
57 for (size_t i = 0; i < cchSalt; i++)
58 szSalt[i] = aRange[RTRandU32Ex(0, sizeof(aRange) - 2)];
59
60 szSalt[RT_SHACRYPT_MAX_SALT_LEN] = '\0';
61 return VINF_SUCCESS;
62}
63
64
65RTR3DECL(int) RTCrShaCrypt256(const char *pszKey, const char *pszSalt, uint32_t cRounds, uint8_t abHash[RTSHA256_HASH_SIZE])
66{
67 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
68 AssertPtrReturn(pszSalt, VERR_INVALID_POINTER);
69 AssertReturn (cRounds, VERR_INVALID_PARAMETER);
70
71 size_t const cbKey = strlen(pszKey);
72 AssertReturn(cbKey, VERR_INVALID_PARAMETER);
73 size_t const cbSalt = strlen(pszSalt);
74 AssertMsgReturn(cbSalt >= RT_SHACRYPT_MIN_SALT_LEN && cbSalt <= RT_SHACRYPT_MAX_SALT_LEN, ("len=%zu\n", cbSalt),
75 VERR_INVALID_PARAMETER);
76
77 uint8_t abDigest[RTSHA256_HASH_SIZE];
78 uint8_t abDigestTemp[RTSHA256_HASH_SIZE];
79
80 RTSHA256CONTEXT Ctx;
81 RTSha256Init(&Ctx); /* Step 1. */
82 RTSha256Update(&Ctx, pszKey, cbKey); /* Step 2. */
83 RTSha256Update(&Ctx, pszSalt, cbSalt); /* Step 3. */
84
85 RTSHA256CONTEXT CtxAlt;
86 RTSha256Init(&CtxAlt); /* Step 4. */
87 RTSha256Update(&CtxAlt, pszKey, cbKey); /* Step 5. */
88 RTSha256Update(&CtxAlt, pszSalt, cbSalt); /* Step 6. */
89 RTSha256Update(&CtxAlt, pszKey, cbKey); /* Step 7. */
90 RTSha256Final(&CtxAlt, abDigest); /* Step 8. */
91
92 size_t i = cbKey;
93 for (; i > RTSHA256_HASH_SIZE; i -= RTSHA256_HASH_SIZE) /* Step 9. */
94 RTSha256Update(&Ctx, abDigest, sizeof(abDigest));
95 RTSha256Update(&Ctx, abDigest, i); /* Step 10. */
96
97 size_t keyBits = cbKey;
98 while (keyBits) /* Step 11. */
99 {
100 if ((keyBits & 1) != 0)
101 RTSha256Update(&Ctx, abDigest, sizeof(abDigest)); /* a) */
102 else
103 RTSha256Update(&Ctx, pszKey, cbKey); /* b) */
104 keyBits >>= 1;
105 }
106
107 RTSha256Final(&Ctx, abDigest); /* Step 12. */
108
109 RTSha256Init(&CtxAlt); /* Step 13. */
110 for (i = 0; i < cbKey; i++) /* Step 14. */
111 RTSha256Update(&CtxAlt, pszKey, cbKey);
112 RTSha256Final(&CtxAlt, abDigestTemp); /* Step 15. */
113
114 /*
115 * Byte sequence P (= password).
116 */
117 size_t const cbSeqP = cbKey;
118 uint8_t *pabSeqP = (uint8_t *)RTMemDup(pszKey, cbSeqP);
119 uint8_t *p = pabSeqP;
120 AssertPtrReturn(pabSeqP, VERR_NO_MEMORY);
121
122 for (i = cbSeqP; i > RTSHA256_HASH_SIZE; i -= RTSHA256_HASH_SIZE) /* Step 16. */
123 {
124 memcpy(p, (void *)abDigestTemp, sizeof(abDigestTemp)); /* a) */
125 p += RTSHA256_HASH_SIZE;
126 }
127 memcpy(p, abDigestTemp, i); /* b) */
128
129 RTSha256Init(&CtxAlt); /* Step 17. */
130
131 for (i = 0; i < 16 + (unsigned)abDigest[0]; i++) /* Step 18. */
132 RTSha256Update(&CtxAlt, pszSalt, cbSalt);
133
134 RTSha256Final(&CtxAlt, abDigestTemp); /* Step 19. */
135
136 /*
137 * Byte sequence S (= salt).
138 */
139 size_t const cbSeqS = cbSalt;
140 uint8_t *pabSeqS = (uint8_t *)RTMemDup(pszSalt, cbSeqS);
141 p = pabSeqS;
142 AssertPtrReturn(pabSeqS, VERR_NO_MEMORY);
143
144 for (i = cbSeqS; i > RTSHA256_HASH_SIZE; i -= RTSHA256_HASH_SIZE) /* Step 20. */
145 {
146 memcpy(p, (void *)abDigestTemp, sizeof(abDigestTemp)); /* a) */
147 p += RTSHA256_HASH_SIZE;
148 }
149 memcpy(p, abDigestTemp, i); /* b) */
150
151 /* Step 21. */
152 for (uint32_t r = 0; r < cRounds; r++)
153 {
154 RTSHA256CONTEXT CtxC;
155 RTSha256Init(&CtxC); /* a) */
156
157 if ((r & 1) != 0)
158 RTSha256Update(&CtxC, pabSeqP, cbSeqP); /* b) */
159 else
160 RTSha256Update(&CtxC, abDigest, sizeof(abDigest)); /* c) */
161
162 if (r % 3 != 0) /* d) */
163 RTSha256Update(&CtxC, pabSeqS, cbSeqS);
164
165 if (r % 7 != 0)
166 RTSha256Update(&CtxC, pabSeqP, cbSeqP); /* e) */
167
168 if ((r & 1) != 0)
169 RTSha256Update(&CtxC, abDigest, sizeof(abDigest)); /* f) */
170 else
171 RTSha256Update(&CtxC, pabSeqP, cbSeqP); /* g) */
172
173 RTSha256Final(&CtxC, abDigest); /* h) */
174 }
175
176 memcpy(abHash, abDigest, RTSHA256_HASH_SIZE);
177
178 RTMemWipeThoroughly(abDigestTemp, RTSHA256_HASH_SIZE, 3);
179 RTMemWipeThoroughly(pabSeqP, cbSeqP, 3);
180 RTMemWipeThoroughly(pabSeqP, cbSeqP, 3);
181 RTMemFree(pabSeqP);
182 RTMemWipeThoroughly(pabSeqS, cbSeqS, 3);
183 RTMemFree(pabSeqS);
184
185 return VINF_SUCCESS;
186}
187
188
189RTR3DECL(int) RTCrShaCrypt256ToString(uint8_t abHash[RTSHA256_HASH_SIZE], const char *pszSalt, uint32_t cRounds,
190 char *pszString, size_t cchString)
191{
192 AssertPtrReturn(pszSalt, VERR_INVALID_POINTER);
193 AssertReturn (cRounds, VERR_INVALID_PARAMETER);
194 AssertReturn (cchString >= RTSHA256_DIGEST_LEN + 1, VERR_INVALID_PARAMETER);
195 AssertPtrReturn(pszString, VERR_INVALID_POINTER);
196
197 char *psz = pszString;
198 size_t cch = cchString;
199
200 *psz = '\0';
201
202 size_t cchPrefix;
203 if (cRounds == RT_SHACRYPT_DEFAULT_ROUNDS)
204 cchPrefix = RTStrPrintf2(psz, cchString, "$5$%s$", pszSalt);
205 else
206 cchPrefix = RTStrPrintf2(psz, cchString, "$5$rounds=%RU32$%s$", cRounds, pszSalt);
207 AssertReturn(cchPrefix > 0, VERR_BUFFER_OVERFLOW);
208 AssertReturn(cch >= cchPrefix, VERR_BUFFER_OVERFLOW);
209 cch -= cchPrefix;
210 psz += cchPrefix;
211
212 /* Make sure that there is enough room to store the base64-encoded hash. */
213 AssertReturn(cch >= ((RTSHA256_HASH_SIZE / 3) * 4) + 1, VERR_BUFFER_OVERFLOW);
214
215 static const char acBase64[64 + 1] =
216 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
217
218#define BASE64_ENCODE(a_Val2, a_Val1, a_Val0, a_Cnt) \
219 do { \
220 unsigned int w = ((a_Val2) << 16) | ((a_Val1) << 8) | (a_Val0); \
221 int n = (a_Cnt); \
222 while (n-- > 0 && cch > 0) \
223 { \
224 *psz++ = acBase64[w & 0x3f]; \
225 --cch; \
226 w >>= 6; \
227 } \
228 } while (0)
229
230 BASE64_ENCODE(abHash[0], abHash[10], abHash[20], 4);
231 BASE64_ENCODE(abHash[21], abHash[1], abHash[11], 4);
232 BASE64_ENCODE(abHash[12], abHash[22], abHash[2], 4);
233 BASE64_ENCODE(abHash[3], abHash[13], abHash[23], 4);
234 BASE64_ENCODE(abHash[24], abHash[4], abHash[14], 4);
235 BASE64_ENCODE(abHash[15], abHash[25], abHash[5], 4);
236 BASE64_ENCODE(abHash[6], abHash[16], abHash[26], 4);
237 BASE64_ENCODE(abHash[27], abHash[7], abHash[17], 4);
238 BASE64_ENCODE(abHash[18], abHash[28], abHash[8], 4);
239 BASE64_ENCODE(abHash[9], abHash[19], abHash[29], 4);
240 BASE64_ENCODE(0, abHash[31], abHash[30], 3);
241
242#undef BASE64_ENCODE
243
244 if (cch)
245 *psz = '\0';
246
247 return VINF_SUCCESS;
248}
249
250
251RTR3DECL(int) RTCrShaCrypt512(const char *pszKey, const char *pszSalt, uint32_t cRounds, uint8_t abHash[RTSHA512_HASH_SIZE])
252{
253 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
254 AssertPtrReturn(pszSalt, VERR_INVALID_POINTER);
255 AssertReturn (cRounds, VERR_INVALID_PARAMETER);
256
257 size_t const cbKey = strlen(pszKey);
258 AssertReturn(cbKey, VERR_INVALID_PARAMETER);
259 size_t const cbSalt = strlen(pszSalt);
260 AssertMsgReturn(cbSalt >= RT_SHACRYPT_MIN_SALT_LEN && cbSalt <= RT_SHACRYPT_MAX_SALT_LEN, ("len=%zu\n", cbSalt),
261 VERR_INVALID_PARAMETER);
262
263 uint8_t abDigest[RTSHA512_HASH_SIZE];
264 uint8_t abDigestTemp[RTSHA512_HASH_SIZE];
265
266 RTSHA512CONTEXT Ctx;
267 RTSha512Init(&Ctx); /* Step 1. */
268 RTSha512Update(&Ctx, pszKey, cbKey); /* Step 2. */
269 RTSha512Update(&Ctx, pszSalt, cbSalt); /* Step 3. */
270
271 RTSHA512CONTEXT CtxAlt;
272 RTSha512Init(&CtxAlt); /* Step 4. */
273 RTSha512Update(&CtxAlt, pszKey, cbKey); /* Step 5. */
274 RTSha512Update(&CtxAlt, pszSalt, cbSalt); /* Step 6. */
275 RTSha512Update(&CtxAlt, pszKey, cbKey); /* Step 7. */
276 RTSha512Final(&CtxAlt, abDigest); /* Step 8. */
277
278 size_t i = cbKey;
279 for (; i > RTSHA512_HASH_SIZE; i -= RTSHA512_HASH_SIZE) /* Step 9. */
280 RTSha512Update(&Ctx, abDigest, sizeof(abDigest));
281 RTSha512Update(&Ctx, abDigest, i); /* Step 10. */
282
283 size_t keyBits = cbKey;
284 while (keyBits) /* Step 11. */
285 {
286 if ((keyBits & 1) != 0)
287 RTSha512Update(&Ctx, abDigest, sizeof(abDigest)); /* a) */
288 else
289 RTSha512Update(&Ctx, pszKey, cbKey); /* b) */
290 keyBits >>= 1;
291 }
292
293 RTSha512Final(&Ctx, abDigest); /* Step 12. */
294
295 RTSha512Init(&CtxAlt); /* Step 13. */
296 for (i = 0; i < cbKey; i++) /* Step 14. */
297 RTSha512Update(&CtxAlt, pszKey, cbKey);
298 RTSha512Final(&CtxAlt, abDigestTemp); /* Step 15. */
299
300 /*
301 * Byte sequence P (= password).
302 */
303 size_t const cbSeqP = cbKey;
304 uint8_t *pabSeqP = (uint8_t *)RTMemDup(pszKey, cbSeqP);
305 uint8_t *p = pabSeqP;
306 AssertPtrReturn(pabSeqP, VERR_NO_MEMORY);
307
308 for (i = cbSeqP; i > RTSHA512_HASH_SIZE; i -= RTSHA512_HASH_SIZE) /* Step 16. */
309 {
310 memcpy(p, (void *)abDigestTemp, sizeof(abDigestTemp)); /* a) */
311 p += RTSHA512_HASH_SIZE;
312 }
313 memcpy(p, abDigestTemp, i); /* b) */
314
315 RTSha512Init(&CtxAlt); /* Step 17. */
316
317 for (i = 0; i < 16 + (unsigned)abDigest[0]; i++) /* Step 18. */
318 RTSha512Update(&CtxAlt, pszSalt, cbSalt);
319
320 RTSha512Final(&CtxAlt, abDigestTemp); /* Step 19. */
321
322 /*
323 * Byte sequence S (= salt).
324 */
325 size_t const cbSeqS = cbSalt;
326 uint8_t *pabSeqS = (uint8_t *)RTMemDup(pszSalt, cbSeqS);
327 p = pabSeqS;
328 AssertPtrReturn(pabSeqS, VERR_NO_MEMORY);
329
330 for (i = cbSeqS; i > RTSHA512_HASH_SIZE; i -= RTSHA512_HASH_SIZE) /* Step 20. */
331 {
332 memcpy(p, (void *)abDigestTemp, sizeof(abDigestTemp)); /* a) */
333 p += RTSHA512_HASH_SIZE;
334 }
335 memcpy(p, abDigestTemp, i); /* b) */
336
337 /* Step 21. */
338 for (uint32_t r = 0; r < cRounds; r++)
339 {
340 RTSHA512CONTEXT CtxC;
341 RTSha512Init(&CtxC); /* a) */
342
343 if ((r & 1) != 0)
344 RTSha512Update(&CtxC, pabSeqP, cbSeqP); /* b) */
345 else
346 RTSha512Update(&CtxC, abDigest, sizeof(abDigest)); /* c) */
347
348 if (r % 3 != 0) /* d) */
349 RTSha512Update(&CtxC, pabSeqS, cbSeqS);
350
351 if (r % 7 != 0)
352 RTSha512Update(&CtxC, pabSeqP, cbSeqP); /* e) */
353
354 if ((r & 1) != 0)
355 RTSha512Update(&CtxC, abDigest, sizeof(abDigest)); /* f) */
356 else
357 RTSha512Update(&CtxC, pabSeqP, cbSeqP); /* g) */
358
359 RTSha512Final(&CtxC, abDigest); /* h) */
360 }
361
362 memcpy(abHash, abDigest, RTSHA512_HASH_SIZE);
363
364 RTMemWipeThoroughly(abDigestTemp, RTSHA512_HASH_SIZE, 3);
365 RTMemWipeThoroughly(pabSeqP, cbSeqP, 3);
366 RTMemWipeThoroughly(pabSeqP, cbSeqP, 3);
367 RTMemFree(pabSeqP);
368 RTMemWipeThoroughly(pabSeqS, cbSeqS, 3);
369 RTMemFree(pabSeqS);
370
371 return VINF_SUCCESS;
372}
373
374
375RTR3DECL(int) RTCrShaCrypt512ToString(uint8_t abHash[RTSHA512_HASH_SIZE], const char *pszSalt, uint32_t cRounds,
376 char *pszString, size_t cchString)
377{
378 AssertPtrReturn(pszSalt, VERR_INVALID_POINTER);
379 AssertReturn (cRounds, VERR_INVALID_PARAMETER);
380 AssertReturn (cchString >= RTSHA512_DIGEST_LEN + 1, VERR_INVALID_PARAMETER);
381 AssertPtrReturn(pszString, VERR_INVALID_POINTER);
382
383 char *psz = pszString;
384 size_t cch = cchString;
385
386 size_t cchPrefix;
387 if (cRounds == RT_SHACRYPT_DEFAULT_ROUNDS)
388 cchPrefix = RTStrPrintf2(psz, cchString, "$6$%s$", pszSalt);
389 else
390 cchPrefix = RTStrPrintf2(psz, cchString, "$6$rounds=%RU32$%s$", cRounds, pszSalt);
391 AssertReturn(cchPrefix > 0, VERR_BUFFER_OVERFLOW);
392 AssertReturn(cch >= cchPrefix, VERR_BUFFER_OVERFLOW);
393 cch -= cchPrefix;
394 psz += cchPrefix;
395
396 /* Make sure that there is enough room to store the base64-encoded hash. */
397 AssertReturn(cch >= ((RTSHA512_HASH_SIZE / 3) * 4) + 1, VERR_BUFFER_OVERFLOW);
398
399 static const char acBase64[64 + 1] =
400 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
401
402#define BASE64_ENCODE(a_Val2, a_Val1, a_Val0, a_Cnt) \
403 do { \
404 unsigned int w = ((a_Val2) << 16) | ((a_Val1) << 8) | (a_Val0); \
405 int n = (a_Cnt); \
406 while (n-- > 0 && cch > 0) \
407 { \
408 *psz++ = acBase64[w & 0x3f]; \
409 --cch; \
410 w >>= 6; \
411 } \
412 } while (0)
413
414 BASE64_ENCODE(abHash[0], abHash[21], abHash[42], 4);
415 BASE64_ENCODE(abHash[22], abHash[43], abHash[1], 4);
416 BASE64_ENCODE(abHash[44], abHash[2], abHash[23], 4);
417 BASE64_ENCODE(abHash[3], abHash[24], abHash[45], 4);
418 BASE64_ENCODE(abHash[25], abHash[46], abHash[4], 4);
419 BASE64_ENCODE(abHash[47], abHash[5], abHash[26], 4);
420 BASE64_ENCODE(abHash[6], abHash[27], abHash[48], 4);
421 BASE64_ENCODE(abHash[28], abHash[49], abHash[7], 4);
422 BASE64_ENCODE(abHash[50], abHash[8], abHash[29], 4);
423 BASE64_ENCODE(abHash[9], abHash[30], abHash[51], 4);
424 BASE64_ENCODE(abHash[31], abHash[52], abHash[10], 4);
425 BASE64_ENCODE(abHash[53], abHash[11], abHash[32], 4);
426 BASE64_ENCODE(abHash[12], abHash[33], abHash[54], 4);
427 BASE64_ENCODE(abHash[34], abHash[55], abHash[13], 4);
428 BASE64_ENCODE(abHash[56], abHash[14], abHash[35], 4);
429 BASE64_ENCODE(abHash[15], abHash[36], abHash[57], 4);
430 BASE64_ENCODE(abHash[37], abHash[58], abHash[16], 4);
431 BASE64_ENCODE(abHash[59], abHash[17], abHash[38], 4);
432 BASE64_ENCODE(abHash[18], abHash[39], abHash[60], 4);
433 BASE64_ENCODE(abHash[40], abHash[61], abHash[19], 4);
434 BASE64_ENCODE(abHash[62], abHash[20], abHash[41], 4);
435 BASE64_ENCODE(0, 0, abHash[63], 2);
436
437#undef BASE64_ENCODE
438
439 if (cch)
440 *psz = '\0';
441
442 return VINF_SUCCESS;
443}
444
Note: See TracBrowser for help on using the repository browser.

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