VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/crypto/shacrypt-512.cpp.h@ 102488

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

IPRT,Main/Unattended: Added a simplified API for the SHAcrypt functionality (the two step approach isn't really something we'll be needing). Corrected documentation, added constant for max output size (RTSHA512_HASH_SIZE*4 isn't a good choice) and whatnot. Added missing round count range checks. Fixed rounding error in output string buffer requirements; actually replacing the whole stuff by just calculating the size upfront before formatting anything. bugref:10551

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.7 KB
Line 
1/* $Id: shacrypt-512.cpp.h 102488 2023-12-05 23:53:09Z vboxsync $ */
2/** @file
3 * IPRT - Crypto - SHA-crypt, code template for SHA-512 core.
4 *
5 * This is almost identical to shacrypt-256.cpp.h, fixes generally applies to
6 * both. Diff the files after updates!
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) RTCrShaCrypt512(const char *pszPhrase, const char *pszSalt, uint32_t cRounds, char *pszString, size_t cbString)
42{
43 uint8_t abHash[RTSHA512_HASH_SIZE];
44 int rc = RTCrShaCrypt512Ex(pszPhrase, pszSalt, cRounds, abHash);
45 if (RT_SUCCESS(rc))
46 rc = RTCrShaCrypt512ToString(abHash, pszSalt, cRounds, pszString, cbString);
47 return rc;
48}
49
50
51
52RTR3DECL(int) RTCrShaCrypt512Ex(const char *pszPhrase, const char *pszSalt, uint32_t cRounds, uint8_t pabHash[RTSHA512_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 RTSHA512CONTEXT CtxA;
73 RTSha512Init(&CtxA); /* Step 1. */
74 RTSha512Update(&CtxA, pszPhrase, cchPhrase); /* Step 2. */
75 RTSha512Update(&CtxA, pszSalt, cchSalt); /* Step 3. */
76
77 RTSHA512CONTEXT CtxB;
78 RTSha512Init(&CtxB); /* Step 4. */
79 RTSha512Update(&CtxB, pszPhrase, cchPhrase); /* Step 5. */
80 RTSha512Update(&CtxB, pszSalt, cchSalt); /* Step 6. */
81 RTSha512Update(&CtxB, pszPhrase, cchPhrase); /* Step 7. */
82 uint8_t abDigest[RTSHA512_HASH_SIZE];
83 RTSha512Final(&CtxB, abDigest); /* Step 8. */
84
85 size_t i = cchPhrase;
86 for (; i > RTSHA512_HASH_SIZE; i -= RTSHA512_HASH_SIZE) /* Step 9. */
87 RTSha512Update(&CtxA, abDigest, sizeof(abDigest));
88 RTSha512Update(&CtxA, abDigest, i); /* Step 10. */
89
90 size_t iPhraseBit = cchPhrase;
91 while (iPhraseBit) /* Step 11. */
92 {
93 if ((iPhraseBit & 1) != 0)
94 RTSha512Update(&CtxA, abDigest, sizeof(abDigest)); /* a) */
95 else
96 RTSha512Update(&CtxA, pszPhrase, cchPhrase); /* b) */
97 iPhraseBit >>= 1;
98 }
99
100 RTSha512Final(&CtxA, abDigest); /* Step 12. */
101
102 RTSha512Init(&CtxB); /* Step 13. */
103 for (i = 0; i < cchPhrase; i++) /* Step 14. */
104 RTSha512Update(&CtxB, pszPhrase, cchPhrase);
105
106 uint8_t abDigestTemp[RTSHA512_HASH_SIZE];
107 RTSha512Final(&CtxB, abDigestTemp); /* Step 15. */
108
109 /*
110 * Byte sequence P (= password).
111 */
112 size_t const cbSeqP = cchPhrase;
113 uint8_t *pabSeqP = (uint8_t *)RTMemDup(pszPhrase, cbSeqP + 1); /* +1 because the password may be empty */
114 uint8_t *pb = pabSeqP;
115 AssertPtrReturn(pabSeqP, VERR_NO_MEMORY);
116
117 for (i = cbSeqP; i > RTSHA512_HASH_SIZE; i -= RTSHA512_HASH_SIZE) /* Step 16. */
118 {
119 memcpy(pb, abDigestTemp, sizeof(abDigestTemp)); /* a) */
120 pb += RTSHA512_HASH_SIZE;
121 }
122 memcpy(pb, abDigestTemp, i); /* b) */
123
124 RTSha512Init(&CtxB); /* Step 17. */
125
126 for (i = 0; i < 16 + (unsigned)abDigest[0]; i++) /* Step 18. */
127 RTSha512Update(&CtxB, pszSalt, cchSalt);
128
129 RTSha512Final(&CtxB, abDigestTemp); /* Step 19. */
130
131 /*
132 * Byte sequence S (= salt).
133 */
134 /* Step 20. */
135 size_t const cbSeqS = cchSalt;
136#if 0 /* Given that the salt has a fixed range (8 thru 16 bytes), and SHA-512
137 * producing 64 bytes, we can safely skip the loop part here (a) and go
138 * straight for step (b). Further, we can drop the whole memory allocation,
139 * let alone duplication (it's all overwritten!), and use an uninitalized
140 * stack buffer. */
141 uint8_t * const pabSeqS = (uint8_t *)RTMemDup(pszSalt, cbSeqS + 1);
142 AssertPtrReturn(pabSeqS, VERR_NO_MEMORY);
143
144 pb = pabSeqS;
145 for (i = cbSeqS; i > RTSHA512_HASH_SIZE; i -= RTSHA512_HASH_SIZE)
146 {
147 memcpy(pb, (void *)abDigestTemp, sizeof(abDigestTemp)); /* a) */
148 pb += RTSHA512_HASH_SIZE;
149 }
150 memcpy(pb, abDigestTemp, i); /* b) */
151#else
152 AssertCompile(RT_SHACRYPT_SALT_MAX_LEN < RTSHA512_HASH_SIZE);
153 uint8_t abSeqS[RT_SHACRYPT_SALT_MAX_LEN + 2];
154 uint8_t * const pabSeqS = abSeqS;
155 memcpy(abSeqS, abDigestTemp, cbSeqS); /* b) */
156#endif
157
158 /* Step 21. */
159 for (uint32_t iRound = 0; iRound < cRounds; iRound++)
160 {
161 RTSHA512CONTEXT CtxC;
162 RTSha512Init(&CtxC); /* a) */
163
164 if ((iRound & 1) != 0)
165 RTSha512Update(&CtxC, pabSeqP, cbSeqP); /* b) */
166 else
167 RTSha512Update(&CtxC, abDigest, sizeof(abDigest)); /* c) */
168
169 if (iRound % 3 != 0) /* d) */
170 RTSha512Update(&CtxC, pabSeqS, cbSeqS);
171
172 if (iRound % 7 != 0)
173 RTSha512Update(&CtxC, pabSeqP, cbSeqP); /* e) */
174
175 if ((iRound & 1) != 0)
176 RTSha512Update(&CtxC, abDigest, sizeof(abDigest)); /* f) */
177 else
178 RTSha512Update(&CtxC, pabSeqP, cbSeqP); /* g) */
179
180 RTSha512Final(&CtxC, abDigest); /* h) */
181 }
182
183 /*
184 * Done.
185 */
186 memcpy(pabHash, abDigest, RTSHA512_HASH_SIZE);
187
188 /*
189 * Cleanup.
190 */
191 RTMemWipeThoroughly(abDigestTemp, RTSHA512_HASH_SIZE, 3);
192 RTMemWipeThoroughly(pabSeqP, cbSeqP, 3);
193 RTMemFree(pabSeqP);
194#if 0
195 RTMemWipeThoroughly(pabSeqS, cbSeqS, 3);
196 RTMemFree(pabSeqS);
197#else
198 RTMemWipeThoroughly(abSeqS, sizeof(abSeqS), 3);
199#endif
200
201 return VINF_SUCCESS;
202}
203
204
205RTR3DECL(int) RTCrShaCrypt512ToString(uint8_t const pabHash[RTSHA512_HASH_SIZE], const char *pszSalt, uint32_t cRounds,
206 char *pszString, size_t cbString)
207{
208 /*
209 * Validate and adjust input.
210 */
211 AssertPtrReturn(pszSalt, VERR_INVALID_POINTER);
212 size_t cchSalt;
213 pszSalt = rtCrShaCryptExtractSaltAndRounds(pszSalt, &cchSalt, &cRounds);
214 AssertReturn(pszSalt != NULL, VERR_INVALID_PARAMETER);
215 AssertReturn(cchSalt >= RT_SHACRYPT_SALT_MIN_LEN, VERR_BUFFER_UNDERFLOW);
216 AssertReturn(cchSalt <= RT_SHACRYPT_SALT_MAX_LEN, VERR_TOO_MUCH_DATA);
217 AssertReturn(cRounds >= RT_SHACRYPT_ROUNDS_MIN && cRounds <= RT_SHACRYPT_ROUNDS_MAX, VERR_OUT_OF_RANGE);
218
219 AssertPtrReturn(pszString, VERR_INVALID_POINTER);
220
221 /*
222 * Calc the necessary buffer space and check that the caller supplied enough.
223 */
224 char szRounds[64];
225 ssize_t cchRounds = 0;
226 if (cRounds != RT_SHACRYPT_ROUNDS_DEFAULT)
227 {
228 cchRounds = RTStrFormatU32(szRounds, sizeof(szRounds), cRounds, 10, 0, 0, 0);
229 Assert(cchRounds > 0 && cchRounds <= 9);
230 }
231
232 size_t const cchNeeded = sizeof(RT_SHACRYPT_ID_STR_512) - 1
233 + (cRounds != RT_SHACRYPT_ROUNDS_DEFAULT ? cchRounds + sizeof("rounds=$") - 1 : 0)
234 + cchSalt + 1
235 + RTSHA512_HASH_SIZE * 4 / 3
236 + 1;
237 AssertReturn(cbString > cchNeeded, VERR_BUFFER_OVERFLOW);
238
239 /*
240 * Do the formatting.
241 */
242 memcpy(pszString, RT_STR_TUPLE(RT_SHACRYPT_ID_STR_512));
243 size_t off = sizeof(RT_SHACRYPT_ID_STR_512) - 1;
244
245 if (cRounds != RT_SHACRYPT_ROUNDS_DEFAULT)
246 {
247 memcpy(&pszString[off], RT_STR_TUPLE("rounds="));
248 off += sizeof("rounds=") - 1;
249
250 memcpy(&pszString[off], szRounds, cchRounds);
251 off += cchRounds;
252 pszString[off++] = '$';
253 }
254
255 memcpy(&pszString[off], pszSalt, cchSalt);
256 off += cchSalt;
257 pszString[off++] = '$';
258
259 BASE64_ENCODE(pszString, off, pabHash[ 0], pabHash[21], pabHash[42], 4);
260 BASE64_ENCODE(pszString, off, pabHash[22], pabHash[43], pabHash[ 1], 4);
261 BASE64_ENCODE(pszString, off, pabHash[44], pabHash[ 2], pabHash[23], 4);
262 BASE64_ENCODE(pszString, off, pabHash[ 3], pabHash[24], pabHash[45], 4);
263 BASE64_ENCODE(pszString, off, pabHash[25], pabHash[46], pabHash[ 4], 4);
264 BASE64_ENCODE(pszString, off, pabHash[47], pabHash[ 5], pabHash[26], 4);
265 BASE64_ENCODE(pszString, off, pabHash[ 6], pabHash[27], pabHash[48], 4);
266 BASE64_ENCODE(pszString, off, pabHash[28], pabHash[49], pabHash[ 7], 4);
267 BASE64_ENCODE(pszString, off, pabHash[50], pabHash[ 8], pabHash[29], 4);
268 BASE64_ENCODE(pszString, off, pabHash[ 9], pabHash[30], pabHash[51], 4);
269 BASE64_ENCODE(pszString, off, pabHash[31], pabHash[52], pabHash[10], 4);
270 BASE64_ENCODE(pszString, off, pabHash[53], pabHash[11], pabHash[32], 4);
271 BASE64_ENCODE(pszString, off, pabHash[12], pabHash[33], pabHash[54], 4);
272 BASE64_ENCODE(pszString, off, pabHash[34], pabHash[55], pabHash[13], 4);
273 BASE64_ENCODE(pszString, off, pabHash[56], pabHash[14], pabHash[35], 4);
274 BASE64_ENCODE(pszString, off, pabHash[15], pabHash[36], pabHash[57], 4);
275 BASE64_ENCODE(pszString, off, pabHash[37], pabHash[58], pabHash[16], 4);
276 BASE64_ENCODE(pszString, off, pabHash[59], pabHash[17], pabHash[38], 4);
277 BASE64_ENCODE(pszString, off, pabHash[18], pabHash[39], pabHash[60], 4);
278 BASE64_ENCODE(pszString, off, pabHash[40], pabHash[61], pabHash[19], 4);
279 BASE64_ENCODE(pszString, off, pabHash[62], pabHash[20], pabHash[41], 4);
280 BASE64_ENCODE(pszString, off, 0, 0, pabHash[63], 2);
281
282 pszString[off] = '\0';
283 Assert(off < cbString);
284
285 return VINF_SUCCESS;
286}
287
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