VirtualBox

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

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

IRPT/shacrypt: Don't use RTMemDup on the passpharse in step 16 since it'll be overwritten at once, use RTMemTmpAllocZ instead. bugref:10551

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