VirtualBox

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

Last change on this file since 102294 was 102294, checked in by vboxsync, 12 months ago

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

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