VirtualBox

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

Last change on this file since 106580 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 8.8 KB
Line 
1/* $Id: shacrypt.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - Crypto - SHA-crypt.
4 */
5
6/*
7 * Copyright (C) 2023-2024 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#include "internal/iprt.h"
42#include <iprt/crypto/shacrypt.h>
43
44#include <iprt/mem.h>
45#include <iprt/rand.h>
46#include <iprt/sha.h>
47#include <iprt/string.h>
48
49
50/*********************************************************************************************************************************
51* Defined Constants And Macros *
52*********************************************************************************************************************************/
53/*
54 * The SHACRYPT_MINIMAL option reduces the .text + .rdata size in a Windows release
55 * build from 6956 to 4592 bytes by not unrolling the not-base64 encoding and instead
56 * using common code w/ a mapping table.
57 */
58#define SHACRYPT_MINIMAL /* don't unroll the digest -> characters conversion */
59
60/**
61 * Encode a byte triplet into four characters (or less), base64-style.
62 *
63 * @note This differs from base64 in that it is LSB oriented,
64 * so where base64 will use bits[7:2] from the first byte for the first
65 * char, this will use bits [5:0].
66 *
67 * The character set is also different.
68 */
69#define NOT_BASE64_ENCODE(a_psz, a_off, a_bVal2, a_bVal1, a_bVal0, a_cOutputChars) \
70 do { \
71 uint32_t uWord = RT_MAKE_U32_FROM_MSB_U8(0, a_bVal2, a_bVal1, a_bVal0); \
72 a_psz[(a_off)++] = g_achCryptBase64[uWord & 0x3f]; \
73 if ((a_cOutputChars) > 1) \
74 { \
75 uWord >>= 6; \
76 a_psz[(a_off)++] = g_achCryptBase64[uWord & 0x3f]; \
77 } \
78 if ((a_cOutputChars) > 2) \
79 { \
80 uWord >>= 6; \
81 a_psz[(a_off)++] = g_achCryptBase64[uWord & 0x3f]; \
82 } \
83 if ((a_cOutputChars) > 3) \
84 { \
85 uWord >>= 6; \
86 a_psz[(a_off)++] = g_achCryptBase64[uWord & 0x3f]; \
87 } \
88 } while (0)
89
90
91/*********************************************************************************************************************************
92* Global Variables *
93*********************************************************************************************************************************/
94/** This is the non-standard base64 encoding characters used by SHA-crypt and friends. */
95static const char g_achCryptBase64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
96AssertCompile(sizeof(g_achCryptBase64) == 64 + 1);
97
98
99#ifdef SHACRYPT_MINIMAL
100/**
101 * Common "base64" encoding function used by RTCrShaCrypt256ToString and
102 * RTCrShaCrypt512ToString in minimal mode.
103 */
104static size_t rtCrShaCryptDigestToChars(char *pszString, size_t off, uint8_t const *pabHash, size_t cbHash,
105 uint8_t const *pabMapping)
106{
107 /* full triplets first: */
108 uintptr_t idx = 0;
109 while (idx + 3 <= cbHash)
110 {
111 NOT_BASE64_ENCODE(pszString, off,
112 pabHash[pabMapping[idx + 2]],
113 pabHash[pabMapping[idx + 1]],
114 pabHash[pabMapping[idx + 0]], 4);
115 idx += 3;
116 }
117
118 /* Anything remaining: Either 1 or 2, never zero. */
119 switch (cbHash - idx)
120 {
121 case 1:
122 NOT_BASE64_ENCODE(pszString, off,
123 0,
124 0,
125 pabHash[pabMapping[idx + 0]], 2);
126 break;
127 case 2:
128 NOT_BASE64_ENCODE(pszString, off,
129 0,
130 pabHash[pabMapping[idx + 1]],
131 pabHash[pabMapping[idx + 0]], 3);
132 break;
133 default: AssertFailedBreak();
134 }
135
136 return off;
137}
138#endif /* SHACRYPT_MINIMAL */
139
140
141/**
142 * Extracts the salt from a given string.
143 *
144 * @returns Pointer to the salt string, or NULL if not found / invalid.
145 * @param pszSalt The string containing the salt.
146 * @param pcchSalt Where to return the extracted salt length (in
147 * characters) on success.
148 * @param pcRounds Where to return the round count on success.
149 */
150static const char *rtCrShaCryptExtractSaltAndRounds(const char *pszSalt, size_t *pcchSalt, uint32_t *pcRounds)
151{
152 /*
153 * Skip either of the two SHA-2 prefixes.
154 */
155 if ( pszSalt[0] == '$'
156 && (pszSalt[1] == '5' || pszSalt[1] == '6')
157 && pszSalt[2] == '$')
158 pszSalt += 3;
159
160 /* Look for 'rounds=xxxxx$'. */
161 if (strncmp(pszSalt, RT_STR_TUPLE("rounds=")) == 0)
162 {
163 const char * const pszValue = pszSalt ? &pszSalt[sizeof("rounds=") - 1] : NULL; /* For ASAN build (false positive). */
164 const char * const pszDollar = strchr(pszValue, '$');
165 if (pszDollar)
166 {
167 char *pszNext = NULL;
168 int rc = RTStrToUInt32Ex(pszValue, &pszNext, 10, pcRounds);
169 if (rc == VWRN_TRAILING_CHARS && pszNext == pszDollar)
170 { /* likely */ }
171 else if (rc == VWRN_NUMBER_TOO_BIG)
172 *pcRounds = UINT32_MAX;
173 else
174 return NULL;
175 pszSalt = pszDollar + 1;
176 }
177 }
178
179 /* Find the length of the salt - it sends with '$' or '\0'. */
180 const char * const pszDollar = strchr(pszSalt, '$');
181 if (!pszDollar)
182 *pcchSalt = strlen(pszSalt);
183 else
184 *pcchSalt = (size_t)(pszDollar - pszSalt);
185 return pszSalt;
186}
187
188
189/*
190 * The algorithm for the SHA-256 and SHA-512 encryption is identical, except for
191 * how the bytes are distributed in the final step. So we use a pre-processor
192 * code template for the implementation.
193 */
194
195/* SHA-256*/
196#define TMPL_HASH_BITS 256
197#define TMPL_HASH_SIZE RTSHA256_HASH_SIZE
198#define TMPL_HASH_CONTEXT_T RTSHA256CONTEXT
199#define TmplHashInit RTSha256Init
200#define TmplHashUpdate RTSha256Update
201#define TmplHashFinal RTSha256Final
202#define TMPL_SHACRYPT_ID_STR RT_SHACRYPT_ID_STR_256
203#define RTCrShaCryptTmpl RTCrShaCrypt256
204#define RTCrShaCryptTmplEx RTCrShaCrypt256Ex
205#define RTCrShaCryptTmplToString RTCrShaCrypt256ToString
206#include "shacrypt-tmpl.cpp.h"
207
208/* SHA-512*/
209#define TMPL_HASH_BITS 512
210#define TMPL_HASH_SIZE RTSHA512_HASH_SIZE
211#define TMPL_HASH_CONTEXT_T RTSHA512CONTEXT
212#define TmplHashInit RTSha512Init
213#define TmplHashUpdate RTSha512Update
214#define TmplHashFinal RTSha512Final
215#define TMPL_SHACRYPT_ID_STR RT_SHACRYPT_ID_STR_512
216#define RTCrShaCryptTmpl RTCrShaCrypt512
217#define RTCrShaCryptTmplEx RTCrShaCrypt512Ex
218#define RTCrShaCryptTmplToString RTCrShaCrypt512ToString
219#include "shacrypt-tmpl.cpp.h"
220
221
222RTDECL(int) RTCrShaCryptGenerateSalt(char *pszSalt, size_t cchSalt)
223{
224 AssertMsgReturn(cchSalt >= RT_SHACRYPT_SALT_MIN_LEN && cchSalt <= RT_SHACRYPT_SALT_MAX_LEN, ("len=%zu\n", cchSalt),
225 VERR_OUT_OF_RANGE);
226
227 for (size_t i = 0; i < cchSalt; i++)
228 pszSalt[i] = g_achCryptBase64[RTRandU32Ex(0, sizeof(g_achCryptBase64) - 2)];
229
230 pszSalt[cchSalt] = '\0';
231 return VINF_SUCCESS;
232}
233
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