VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/crypto/shacrypt-tmpl.cpp.h

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

IPRT/shacrypt: scm fix. bugref:10551

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.3 KB
Line 
1/* $Id: shacrypt-tmpl.cpp.h 102501 2023-12-06 11:26:13Z vboxsync $ */
2/** @file
3 * IPRT - Crypto - SHA-crypt, code template the core code.
4 *
5 * This is included a couple of times from shacrypt.cpp with different set of
6 * defines for each variation.
7 */
8
9/*
10 * Copyright (C) 2023 Oracle and/or its affiliates.
11 *
12 * This file is part of VirtualBox base platform packages, as
13 * available from https://www.virtualbox.org.
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation, in version 3 of the
18 * License.
19 *
20 * This program is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, see <https://www.gnu.org/licenses>.
27 *
28 * The contents of this file may alternatively be used under the terms
29 * of the Common Development and Distribution License Version 1.0
30 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
31 * in the VirtualBox distribution, in which case the provisions of the
32 * CDDL are applicable instead of those of the GPL.
33 *
34 * You may elect to license modified versions of this file under the
35 * terms and conditions of either the GPL or the CDDL or both.
36 *
37 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
38 */
39
40
41RTDECL(int) RTCrShaCryptTmpl(const char *pszPhrase, const char *pszSalt, uint32_t cRounds, char *pszString, size_t cbString)
42{
43 uint8_t abHash[TMPL_HASH_SIZE];
44 int rc = RTCrShaCryptTmplEx(pszPhrase, pszSalt, cRounds, abHash);
45 if (RT_SUCCESS(rc))
46 rc = RTCrShaCryptTmplToString(abHash, pszSalt, cRounds, pszString, cbString);
47 return rc;
48}
49
50
51
52RTR3DECL(int) RTCrShaCryptTmplEx(const char *pszPhrase, const char *pszSalt, uint32_t cRounds, uint8_t pabHash[TMPL_HASH_SIZE])
53{
54 /*
55 * Validate and adjust input.
56 */
57 AssertPtrReturn(pszPhrase, VERR_INVALID_POINTER);
58 size_t const cchPhrase = strlen(pszPhrase);
59 AssertReturn(cchPhrase, VERR_INVALID_PARAMETER);
60
61 AssertPtrReturn(pszSalt, VERR_INVALID_POINTER);
62 size_t cchSalt;
63 pszSalt = rtCrShaCryptExtractSaltAndRounds(pszSalt, &cchSalt, &cRounds);
64 AssertReturn(pszSalt != NULL, VERR_INVALID_PARAMETER);
65 AssertReturn(cchSalt >= RT_SHACRYPT_SALT_MIN_LEN, VERR_BUFFER_UNDERFLOW);
66 AssertReturn(cchSalt <= RT_SHACRYPT_SALT_MAX_LEN, VERR_TOO_MUCH_DATA);
67 AssertReturn(cRounds >= RT_SHACRYPT_ROUNDS_MIN && cRounds <= RT_SHACRYPT_ROUNDS_MAX, VERR_OUT_OF_RANGE);
68
69 /*
70 * Get started...
71 */
72 TMPL_HASH_CONTEXT_T CtxA;
73 TmplHashInit(&CtxA); /* Step 1. */
74 TmplHashUpdate(&CtxA, pszPhrase, cchPhrase); /* Step 2. */
75 TmplHashUpdate(&CtxA, pszSalt, cchSalt); /* Step 3. */
76
77 TMPL_HASH_CONTEXT_T CtxB;
78 TmplHashInit(&CtxB); /* Step 4. */
79 TmplHashUpdate(&CtxB, pszPhrase, cchPhrase); /* Step 5. */
80 TmplHashUpdate(&CtxB, pszSalt, cchSalt); /* Step 6. */
81 TmplHashUpdate(&CtxB, pszPhrase, cchPhrase); /* Step 7. */
82 uint8_t abDigest[TMPL_HASH_SIZE];
83 TmplHashFinal(&CtxB, abDigest); /* Step 8. */
84
85 size_t cbLeft = cchPhrase;
86 while (cbLeft > TMPL_HASH_SIZE) /* Step 9. */
87 {
88 TmplHashUpdate(&CtxA, abDigest, sizeof(abDigest));
89 cbLeft -= TMPL_HASH_SIZE;
90 }
91 TmplHashUpdate(&CtxA, abDigest, cbLeft); /* Step 10. */
92
93 size_t iPhraseBit = cchPhrase;
94 while (iPhraseBit) /* Step 11. */
95 {
96 if ((iPhraseBit & 1) != 0)
97 TmplHashUpdate(&CtxA, abDigest, sizeof(abDigest)); /* a) */
98 else
99 TmplHashUpdate(&CtxA, pszPhrase, cchPhrase); /* b) */
100 iPhraseBit >>= 1;
101 }
102
103 TmplHashFinal(&CtxA, abDigest); /* Step 12. */
104
105 TmplHashInit(&CtxB); /* Step 13. */
106 for (size_t i = 0; i < cchPhrase; i++) /* Step 14. */
107 TmplHashUpdate(&CtxB, pszPhrase, cchPhrase);
108
109 uint8_t abDigestTemp[TMPL_HASH_SIZE];
110 TmplHashFinal(&CtxB, abDigestTemp); /* Step 15. */
111
112 /*
113 * Byte sequence P (= password).
114 */
115 /* Step 16. */
116 size_t const cbSeqP = cchPhrase;
117 uint8_t *pabSeqP = (uint8_t *)RTMemTmpAllocZ(cbSeqP + 1); /* +1 because the password may be empty */
118 uint8_t *pb = pabSeqP;
119 AssertPtrReturn(pabSeqP, VERR_NO_MEMORY);
120 cbLeft = cbSeqP;
121 while (cbLeft > TMPL_HASH_SIZE)
122 {
123 memcpy(pb, abDigestTemp, sizeof(abDigestTemp)); /* a) */
124 pb += TMPL_HASH_SIZE;
125 cbLeft -= TMPL_HASH_SIZE;
126 }
127 memcpy(pb, abDigestTemp, cbLeft); /* b) */
128
129 TmplHashInit(&CtxB); /* Step 17. */
130
131 for (size_t i = 0; i < 16 + (unsigned)abDigest[0]; i++) /* Step 18. */
132 TmplHashUpdate(&CtxB, pszSalt, cchSalt);
133
134 TmplHashFinal(&CtxB, abDigestTemp); /* Step 19. */
135
136 /*
137 * Byte sequence S (= salt).
138 */
139 /* Step 20. */
140 size_t const cbSeqS = cchSalt;
141#if 0 /* Given that the salt has a fixed range (8 thru 16 bytes), and SHA-512/256
142 * producing 64 bytes, we can safely skip the loop part here (a) and go
143 * straight for step (b). Further, we can drop the whole memory allocation,
144 * let alone duplication (it's all overwritten!), and use an uninitalized
145 * stack buffer. */
146 uint8_t * const pabSeqS = (uint8_t *)RTMemDup(pszSalt, cbSeqS + 1);
147 AssertPtrReturn(pabSeqS, VERR_NO_MEMORY);
148
149 pb = pabSeqS;
150 cbLeft = cbSeqS;
151 while (cbLeft > TMPL_HASH_SIZE)
152 {
153 memcpy(pb, (void *)abDigestTemp, sizeof(abDigestTemp)); /* a) */
154 pb += TMPL_HASH_SIZE;
155 cbLeft -= TMPL_HASH_SIZE
156 }
157 memcpy(pb, abDigestTemp, cbLeft); /* b) */
158#else
159 AssertCompile(RT_SHACRYPT_SALT_MAX_LEN < TMPL_HASH_SIZE);
160 uint8_t abSeqS[RT_SHACRYPT_SALT_MAX_LEN + 2];
161 uint8_t * const pabSeqS = abSeqS;
162 memcpy(abSeqS, abDigestTemp, cbSeqS); /* b) */
163#endif
164
165 /* Step 21. */
166 for (uint32_t iRound = 0; iRound < cRounds; iRound++)
167 {
168 TMPL_HASH_CONTEXT_T CtxC;
169 TmplHashInit(&CtxC); /* a) */
170
171 if ((iRound & 1) != 0)
172 TmplHashUpdate(&CtxC, pabSeqP, cbSeqP); /* b) */
173 else
174 TmplHashUpdate(&CtxC, abDigest, sizeof(abDigest)); /* c) */
175
176 if (iRound % 3 != 0) /* d) */
177 TmplHashUpdate(&CtxC, pabSeqS, cbSeqS);
178
179 if (iRound % 7 != 0)
180 TmplHashUpdate(&CtxC, pabSeqP, cbSeqP); /* e) */
181
182 if ((iRound & 1) != 0)
183 TmplHashUpdate(&CtxC, abDigest, sizeof(abDigest)); /* f) */
184 else
185 TmplHashUpdate(&CtxC, pabSeqP, cbSeqP); /* g) */
186
187 TmplHashFinal(&CtxC, abDigest); /* h) */
188 }
189
190 /*
191 * Done.
192 */
193 memcpy(pabHash, abDigest, TMPL_HASH_SIZE);
194
195 /*
196 * Cleanup.
197 */
198 RTMemWipeThoroughly(abDigestTemp, TMPL_HASH_SIZE, 3);
199 RTMemWipeThoroughly(pabSeqP, cbSeqP, 3);
200 RTMemTmpFree(pabSeqP);
201#if 0
202 RTMemWipeThoroughly(pabSeqS, cbSeqS, 3);
203 RTMemFree(pabSeqS);
204#else
205 RTMemWipeThoroughly(abSeqS, sizeof(abSeqS), 3);
206#endif
207
208 return VINF_SUCCESS;
209}
210
211
212RTR3DECL(int) RTCrShaCryptTmplToString(uint8_t const pabHash[TMPL_HASH_SIZE], const char *pszSalt, uint32_t cRounds,
213 char *pszString, size_t cbString)
214{
215 /*
216 * Validate and adjust input.
217 */
218 AssertPtrReturn(pszSalt, VERR_INVALID_POINTER);
219 size_t cchSalt;
220 pszSalt = rtCrShaCryptExtractSaltAndRounds(pszSalt, &cchSalt, &cRounds);
221 AssertReturn(pszSalt != NULL, VERR_INVALID_PARAMETER);
222 AssertReturn(cchSalt >= RT_SHACRYPT_SALT_MIN_LEN, VERR_BUFFER_UNDERFLOW);
223 AssertReturn(cchSalt <= RT_SHACRYPT_SALT_MAX_LEN, VERR_TOO_MUCH_DATA);
224 AssertReturn(cRounds >= RT_SHACRYPT_ROUNDS_MIN && cRounds <= RT_SHACRYPT_ROUNDS_MAX, VERR_OUT_OF_RANGE);
225
226 AssertPtrReturn(pszString, VERR_INVALID_POINTER);
227
228 /*
229 * Calc the necessary buffer space and check that the caller supplied enough.
230 */
231 char szRounds[64];
232 ssize_t cchRounds = 0;
233 if (cRounds != RT_SHACRYPT_ROUNDS_DEFAULT)
234 {
235 cchRounds = RTStrFormatU32(szRounds, sizeof(szRounds), cRounds, 10, 0, 0, 0);
236 Assert(cchRounds > 0 && cchRounds <= 9);
237 }
238
239 size_t const cchNeeded = sizeof(TMPL_SHACRYPT_ID_STR) - 1
240 + (cRounds != RT_SHACRYPT_ROUNDS_DEFAULT ? cchRounds + sizeof("rounds=$") - 1 : 0)
241 + cchSalt + 1
242 + TMPL_HASH_SIZE * 4 / 3
243 + 1;
244 AssertReturn(cbString > cchNeeded, VERR_BUFFER_OVERFLOW);
245
246 /*
247 * Do the formatting.
248 */
249 memcpy(pszString, RT_STR_TUPLE(TMPL_SHACRYPT_ID_STR));
250 size_t off = sizeof(TMPL_SHACRYPT_ID_STR) - 1;
251
252 if (cRounds != RT_SHACRYPT_ROUNDS_DEFAULT)
253 {
254 memcpy(&pszString[off], RT_STR_TUPLE("rounds="));
255 off += sizeof("rounds=") - 1;
256
257 memcpy(&pszString[off], szRounds, cchRounds);
258 off += cchRounds;
259 pszString[off++] = '$';
260 }
261
262 memcpy(&pszString[off], pszSalt, cchSalt);
263 off += cchSalt;
264 pszString[off++] = '$';
265
266#ifdef SHACRYPT_MINIMAL
267 /*
268 * Use a table for the shuffling of the digest bytes and work it in a loop.
269 */
270 static uint8_t const s_abMapping[] =
271 {
272# if TMPL_HASH_BITS == 512
273 42, 21, 0,
274 1, 43, 22,
275 23, 2, 44,
276 45, 24, 3,
277 4, 46, 25,
278 26, 5, 47,
279 48, 27, 6,
280 7, 49, 28,
281 29, 8, 50,
282 51, 30, 9,
283 10, 52, 31,
284 32, 11, 53,
285 54, 33, 12,
286 13, 55, 34,
287 35, 14, 56,
288 57, 36, 15,
289 16, 58, 37,
290 38, 17, 59,
291 60, 39, 18,
292 19, 61, 40,
293 41, 20, 62,
294 63
295# elif TMPL_HASH_BITS == 256
296 20, 10, 0,
297 11, 1, 21,
298 2, 22, 12,
299 23, 13, 3,
300 14, 4, 24,
301 5, 25, 15,
302 26, 16, 6,
303 17, 7, 27,
304 8, 28, 18,
305 29, 19, 9,
306 30, 31,
307# else
308# error "TMPL_HASH_BITS"
309# endif
310 };
311 AssertCompile(sizeof(s_abMapping) == TMPL_HASH_SIZE);
312 off = rtCrShaCryptDigestToChars(pszString, off, pabHash, TMPL_HASH_SIZE, s_abMapping);
313
314#else /* !SHACRYPT_MINIMAL */
315 /*
316 * Unroll the digest shuffling and conversion to characters.
317 * This takes a lot of code space.
318 */
319# if TMPL_HASH_BITS == 512
320 NOT_BASE64_ENCODE(pszString, off, pabHash[ 0], pabHash[21], pabHash[42], 4);
321 NOT_BASE64_ENCODE(pszString, off, pabHash[22], pabHash[43], pabHash[ 1], 4);
322 NOT_BASE64_ENCODE(pszString, off, pabHash[44], pabHash[ 2], pabHash[23], 4);
323 NOT_BASE64_ENCODE(pszString, off, pabHash[ 3], pabHash[24], pabHash[45], 4);
324 NOT_BASE64_ENCODE(pszString, off, pabHash[25], pabHash[46], pabHash[ 4], 4);
325 NOT_BASE64_ENCODE(pszString, off, pabHash[47], pabHash[ 5], pabHash[26], 4);
326 NOT_BASE64_ENCODE(pszString, off, pabHash[ 6], pabHash[27], pabHash[48], 4);
327 NOT_BASE64_ENCODE(pszString, off, pabHash[28], pabHash[49], pabHash[ 7], 4);
328 NOT_BASE64_ENCODE(pszString, off, pabHash[50], pabHash[ 8], pabHash[29], 4);
329 NOT_BASE64_ENCODE(pszString, off, pabHash[ 9], pabHash[30], pabHash[51], 4);
330 NOT_BASE64_ENCODE(pszString, off, pabHash[31], pabHash[52], pabHash[10], 4);
331 NOT_BASE64_ENCODE(pszString, off, pabHash[53], pabHash[11], pabHash[32], 4);
332 NOT_BASE64_ENCODE(pszString, off, pabHash[12], pabHash[33], pabHash[54], 4);
333 NOT_BASE64_ENCODE(pszString, off, pabHash[34], pabHash[55], pabHash[13], 4);
334 NOT_BASE64_ENCODE(pszString, off, pabHash[56], pabHash[14], pabHash[35], 4);
335 NOT_BASE64_ENCODE(pszString, off, pabHash[15], pabHash[36], pabHash[57], 4);
336 NOT_BASE64_ENCODE(pszString, off, pabHash[37], pabHash[58], pabHash[16], 4);
337 NOT_BASE64_ENCODE(pszString, off, pabHash[59], pabHash[17], pabHash[38], 4);
338 NOT_BASE64_ENCODE(pszString, off, pabHash[18], pabHash[39], pabHash[60], 4);
339 NOT_BASE64_ENCODE(pszString, off, pabHash[40], pabHash[61], pabHash[19], 4);
340 NOT_BASE64_ENCODE(pszString, off, pabHash[62], pabHash[20], pabHash[41], 4);
341 NOT_BASE64_ENCODE(pszString, off, 0, 0, pabHash[63], 2);
342
343# elif TMPL_HASH_BITS == 256
344 NOT_BASE64_ENCODE(pszString, off, pabHash[00], pabHash[10], pabHash[20], 4);
345 NOT_BASE64_ENCODE(pszString, off, pabHash[21], pabHash[ 1], pabHash[11], 4);
346 NOT_BASE64_ENCODE(pszString, off, pabHash[12], pabHash[22], pabHash[ 2], 4);
347 NOT_BASE64_ENCODE(pszString, off, pabHash[ 3], pabHash[13], pabHash[23], 4);
348 NOT_BASE64_ENCODE(pszString, off, pabHash[24], pabHash[ 4], pabHash[14], 4);
349 NOT_BASE64_ENCODE(pszString, off, pabHash[15], pabHash[25], pabHash[ 5], 4);
350 NOT_BASE64_ENCODE(pszString, off, pabHash[ 6], pabHash[16], pabHash[26], 4);
351 NOT_BASE64_ENCODE(pszString, off, pabHash[27], pabHash[ 7], pabHash[17], 4);
352 NOT_BASE64_ENCODE(pszString, off, pabHash[18], pabHash[28], pabHash[ 8], 4);
353 NOT_BASE64_ENCODE(pszString, off, pabHash[ 9], pabHash[19], pabHash[29], 4);
354 NOT_BASE64_ENCODE(pszString, off, 0, pabHash[31], pabHash[30], 3);
355
356# else
357# error "TMPL_HASH_BITS"
358# endif
359#endif /* !SHACRYPT_MINIMAL */
360
361 pszString[off] = '\0';
362 Assert(off < cbString);
363
364 return VINF_SUCCESS;
365}
366
367
368#undef TMPL_HASH_BITS
369#undef TMPL_HASH_SIZE
370#undef TMPL_HASH_CONTEXT_T
371#undef TmplHashInit
372#undef TmplHashUpdate
373#undef TmplHashFinal
374#undef TMPL_SHACRYPT_ID_STR
375#undef RTCrShaCryptTmpl
376#undef RTCrShaCryptTmplEx
377#undef RTCrShaCryptTmplToString
378
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use