xref: /illumos-gate/usr/src/lib/crypt_modules/sha256/crypt_sha.c (revision fe54a78e1aacf39261ad56e9903bce02e3fb6d21)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Portions of this code from crypt_bsdmd5.so (bsdmd5.c) :
28  * ----------------------------------------------------------------------------
29  * "THE BEER-WARE LICENSE" (Revision 42):
30  * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
31  * can do whatever you want with this stuff. If we meet some day, and you think
32  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
33  * ----------------------------------------------------------------------------
34  *
35  * $FreeBSD: crypt.c,v 1.5 1996/10/14 08:34:02 phk Exp $
36  *
37  */
38 
39 /*
40  * Implements the specification from:
41  *
42  * From http://people.redhat.com/drepper/SHA-crypt.txt
43  *
44  * Portions of the code taken from inspired by or verified against the
45  * source in the above document which is licensed as:
46  *
47  * "Released into the Public Domain by Ulrich Drepper <drepper@redhat.com>."
48  */
49 
50 
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <sys/sysmacros.h>
54 #include <fcntl.h>
55 #include <unistd.h>
56 #include <string.h>
57 #include <stdio.h>
58 #include <errno.h>
59 #include <stdlib.h>
60 #include <alloca.h>
61 
62 #include <sha2.h>
63 #include <crypt.h>
64 
65 #define	MAX_SALT_LEN	16
66 #define	ROUNDS_DEFAULT	5000
67 #define	ROUNDS_MIN	1000
68 #define	ROUNDS_MAX	999999999
69 
70 #ifdef CRYPT_SHA256
71 
72 #define	DIGEST_CTX	SHA256_CTX
73 #define	DIGESTInit	SHA256Init
74 #define	DIGESTUpdate	SHA256Update
75 #define	DIGESTFinal	SHA256Final
76 #define	DIGEST_LEN	SHA256_DIGEST_LENGTH
77 #define	MIXCHARS	32
78 static const char crypt_alg_magic[] = "$5$";
79 
80 #elif CRYPT_SHA512
81 
82 #define	DIGEST_CTX	SHA512_CTX
83 #define	DIGESTInit	SHA512Init
84 #define	DIGESTUpdate	SHA512Update
85 #define	DIGESTFinal	SHA512Final
86 #define	DIGEST_LEN	SHA512_DIGEST_LENGTH
87 #define	MIXCHARS	64
88 static const char crypt_alg_magic[] = "$6$";
89 
90 #else
91 #error	"One of CRYPT_256 or CRYPT_512 must be defined"
92 #endif
93 
94 static const int crypt_alg_magic_len = sizeof (crypt_alg_magic) - 1;
95 static const char rounds_prefix[] = "rounds=";
96 
97 
98 static uchar_t b64t[] =		/* 0 ... 63 => ascii - 64 */
99 	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
100 
101 #define	b64_from_24bit(B2, B1, B0, N) \
102 { \
103 	uint_t w = ((B2) << 16) | ((B1) << 8) | (B0); \
104 	int n = (N); \
105 	while (--n >= 0 && ctbufflen > 0) { \
106 		*p++ = b64t[w & 0x3f]; \
107 		w >>= 6; \
108 		ctbufflen--; \
109 	} \
110 }
111 
112 static void
113 to64(char *s, uint64_t v, int n)
114 {
115 	while (--n >= 0) {
116 		*s++ = b64t[v&0x3f];
117 		v >>= 6;
118 	}
119 }
120 
121 char *
122 crypt_genhash_impl(char *ctbuffer,
123 	    size_t ctbufflen,
124 	    const char *plaintext,
125 	    const char *switchsalt,
126 	    const char **params)
127 {
128 	int salt_len, plaintext_len, i;
129 	char *salt;
130 	uchar_t A[DIGEST_LEN];
131 	uchar_t B[DIGEST_LEN];
132 	uchar_t DP[DIGEST_LEN];
133 	uchar_t DS[DIGEST_LEN];
134 	DIGEST_CTX ctxA, ctxB, ctxC, ctxDP, ctxDS;
135 	int rounds = ROUNDS_DEFAULT;
136 	boolean_t custom_rounds = B_FALSE;
137 	char *p;
138 	char *P, *Pp;
139 	char *S, *Sp;
140 
141 	/* Refine the salt */
142 	salt = (char *)switchsalt;
143 
144 	/* skip our magic string */
145 	if (strncmp((char *)salt, crypt_alg_magic, crypt_alg_magic_len) == 0) {
146 		salt += crypt_alg_magic_len;
147 	}
148 
149 	if (strncmp(salt, rounds_prefix, sizeof (rounds_prefix) - 1) == 0) {
150 		char *num = salt + sizeof (rounds_prefix) - 1;
151 		char *endp;
152 		ulong_t srounds = strtoul(num, &endp, 10);
153 		if (*endp == '$') {
154 			salt = endp + 1;
155 			rounds = MAX(ROUNDS_MIN, MIN(srounds, ROUNDS_MAX));
156 			custom_rounds = B_TRUE;
157 		}
158 	}
159 
160 	salt_len = MIN(strcspn(salt, "$"), MAX_SALT_LEN);
161 	plaintext_len = strlen(plaintext);
162 
163 	/* 1. */
164 	DIGESTInit(&ctxA);
165 
166 	/* 2. The password first, since that is what is most unknown */
167 	DIGESTUpdate(&ctxA, plaintext, plaintext_len);
168 
169 	/* 3. Then the raw salt */
170 	DIGESTUpdate(&ctxA, salt, salt_len);
171 
172 	/* 4. - 8. */
173 	DIGESTInit(&ctxB);
174 	DIGESTUpdate(&ctxB, plaintext, plaintext_len);
175 	DIGESTUpdate(&ctxB, salt, salt_len);
176 	DIGESTUpdate(&ctxB, plaintext, plaintext_len);
177 	DIGESTFinal(B, &ctxB);
178 
179 	/* 9. - 10. */
180 	for (i = plaintext_len; i > MIXCHARS; i -= MIXCHARS)
181 		DIGESTUpdate(&ctxA, B, MIXCHARS);
182 	DIGESTUpdate(&ctxA, B, i);
183 
184 	/* 11. */
185 	for (i = plaintext_len; i > 0; i >>= 1) {
186 		if ((i & 1) != 0) {
187 			DIGESTUpdate(&ctxA, B, MIXCHARS);
188 		} else {
189 			DIGESTUpdate(&ctxA, plaintext, plaintext_len);
190 		}
191 	}
192 
193 	/* 12. */
194 	DIGESTFinal(A, &ctxA);
195 
196 	/* 13. - 15. */
197 	DIGESTInit(&ctxDP);
198 	for (i = 0; i < plaintext_len; i++)
199 		DIGESTUpdate(&ctxDP, plaintext, plaintext_len);
200 	DIGESTFinal(DP, &ctxDP);
201 
202 	/* 16. */
203 	Pp = P = alloca(plaintext_len);
204 	for (i = plaintext_len; i >= MIXCHARS; i -= MIXCHARS) {
205 		Pp = (char *)(memcpy(Pp, DP, MIXCHARS)) + MIXCHARS;
206 	}
207 	memcpy(Pp, DP, i);
208 
209 	/* 17. - 19. */
210 	DIGESTInit(&ctxDS);
211 	for (i = 0; i < 16 + (uint8_t)A[0]; i++)
212 		DIGESTUpdate(&ctxDS, salt, salt_len);
213 	DIGESTFinal(DS, &ctxDS);
214 
215 	/* 20. */
216 	Sp = S = alloca(salt_len);
217 	for (i = salt_len; i >= MIXCHARS; i -= MIXCHARS) {
218 		Sp = (char *)(memcpy(Sp, DS, MIXCHARS)) + MIXCHARS;
219 	}
220 	memcpy(Sp, DS, i);
221 
222 	/*  21. */
223 	for (i = 0; i < rounds; i++) {
224 		DIGESTInit(&ctxC);
225 
226 		if ((i & 1) != 0) {
227 			DIGESTUpdate(&ctxC, P, plaintext_len);
228 		} else {
229 			if (i == 0)
230 				DIGESTUpdate(&ctxC, A, MIXCHARS);
231 			else
232 				DIGESTUpdate(&ctxC, DP, MIXCHARS);
233 		}
234 
235 		if (i % 3 != 0) {
236 			DIGESTUpdate(&ctxC, S, salt_len);
237 		}
238 
239 		if (i % 7 != 0) {
240 			DIGESTUpdate(&ctxC, P, plaintext_len);
241 		}
242 
243 		if ((i & 1) != 0) {
244 			if (i == 0)
245 				DIGESTUpdate(&ctxC, A, MIXCHARS);
246 			else
247 				DIGESTUpdate(&ctxC, DP, MIXCHARS);
248 		} else {
249 			DIGESTUpdate(&ctxC, P, plaintext_len);
250 		}
251 		DIGESTFinal(DP, &ctxC);
252 	}
253 
254 	/* 22. Now make the output string */
255 	(void) strlcpy(ctbuffer, crypt_alg_magic, ctbufflen);
256 	if (custom_rounds) {
257 		(void) snprintf(ctbuffer, ctbufflen,
258 		    "%srounds=%zu$", ctbuffer, rounds);
259 	}
260 
261 	(void) strncat(ctbuffer, (const char *)salt, salt_len);
262 	(void) strlcat(ctbuffer, "$", ctbufflen);
263 	p = ctbuffer + strlen(ctbuffer);
264 	ctbufflen -= strlen(ctbuffer);
265 
266 #ifdef CRYPT_SHA256
267 	b64_from_24bit(DP[ 0], DP[10], DP[20], 4);
268 	b64_from_24bit(DP[21], DP[ 1], DP[11], 4);
269 	b64_from_24bit(DP[12], DP[22], DP[ 2], 4);
270 	b64_from_24bit(DP[ 3], DP[13], DP[23], 4);
271 	b64_from_24bit(DP[24], DP[ 4], DP[14], 4);
272 	b64_from_24bit(DP[15], DP[25], DP[ 5], 4);
273 	b64_from_24bit(DP[ 6], DP[16], DP[26], 4);
274 	b64_from_24bit(DP[27], DP[ 7], DP[17], 4);
275 	b64_from_24bit(DP[18], DP[28], DP[ 8], 4);
276 	b64_from_24bit(DP[ 9], DP[19], DP[29], 4);
277 	b64_from_24bit(0, DP[31], DP[30], 3);
278 #elif CRYPT_SHA512
279 	b64_from_24bit(DP[ 0], DP[21], DP[42], 4);
280 	b64_from_24bit(DP[22], DP[43], DP[ 1], 4);
281 	b64_from_24bit(DP[44], DP[ 2], DP[23], 4);
282 	b64_from_24bit(DP[ 3], DP[24], DP[45], 4);
283 	b64_from_24bit(DP[25], DP[46], DP[ 4], 4);
284 	b64_from_24bit(DP[47], DP[ 5], DP[26], 4);
285 	b64_from_24bit(DP[ 6], DP[27], DP[48], 4);
286 	b64_from_24bit(DP[28], DP[49], DP[ 7], 4);
287 	b64_from_24bit(DP[50], DP[ 8], DP[29], 4);
288 	b64_from_24bit(DP[ 9], DP[30], DP[51], 4);
289 	b64_from_24bit(DP[31], DP[52], DP[10], 4);
290 	b64_from_24bit(DP[53], DP[11], DP[32], 4);
291 	b64_from_24bit(DP[12], DP[33], DP[54], 4);
292 	b64_from_24bit(DP[34], DP[55], DP[13], 4);
293 	b64_from_24bit(DP[56], DP[14], DP[35], 4);
294 	b64_from_24bit(DP[15], DP[36], DP[57], 4);
295 	b64_from_24bit(DP[37], DP[58], DP[16], 4);
296 	b64_from_24bit(DP[59], DP[17], DP[38], 4);
297 	b64_from_24bit(DP[18], DP[39], DP[60], 4);
298 	b64_from_24bit(DP[40], DP[61], DP[19], 4);
299 	b64_from_24bit(DP[62], DP[20], DP[41], 4);
300 	b64_from_24bit(0, 0, DP[63], 2);
301 #endif
302 	*p = '\0';
303 
304 	(void) memset(A, 0, sizeof (A));
305 	(void) memset(B, 0, sizeof (B));
306 	(void) memset(DP, 0, sizeof (DP));
307 	(void) memset(DS, 0, sizeof (DS));
308 
309 	return (ctbuffer);
310 }
311 
312 char *
313 crypt_gensalt_impl(char *gsbuffer,
314 	    size_t gsbufflen,
315 	    const char *oldsalt,
316 	    const struct passwd *userinfo,
317 	    const char **params)
318 {
319 	int fd;
320 	int err;
321 	ssize_t got;
322 	uint64_t rndval;
323 
324 	if ((fd = open("/dev/urandom", O_RDONLY)) == -1) {
325 		return (NULL);
326 	}
327 
328 	(void) strlcpy(gsbuffer, crypt_alg_magic, gsbufflen);
329 
330 	got = read(fd, &rndval, sizeof (rndval));
331 	if (got < sizeof (rndval)) {
332 		err = errno;
333 		(void) close(fd);
334 		errno = err;
335 		return (NULL);
336 	}
337 
338 	to64(&gsbuffer[strlen(crypt_alg_magic)], rndval, sizeof (rndval));
339 
340 	(void) close(fd);
341 
342 	return (gsbuffer);
343 }
344