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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/types.h>
39 #define CRYPT_ALGNAME "md5"
42 /* minimum number of rounds we do, not including the per-user ones */
44 #define BASIC_ROUND_COUNT 4096 /* enough to make things interesting */
46 #define ROUND_BUFFER_LEN 64
49 * Public domain quotation courtesy of Project Gutenberg.
50 * ftp://metalab.unc.edu/pub/docs/books/gutenberg/etext98/2ws2610.txt
51 * Hamlet III.ii - 1517 bytes, including trailing NUL
52 * ANSI-C string constant concatenation is a requirement here.
55 static const char constant_phrase
[] =
56 "To be, or not to be,--that is the question:--\n"
57 "Whether 'tis nobler in the mind to suffer\n"
58 "The slings and arrows of outrageous fortune\n"
59 "Or to take arms against a sea of troubles,\n"
60 "And by opposing end them?--To die,--to sleep,--\n"
61 "No more; and by a sleep to say we end\n"
62 "The heartache, and the thousand natural shocks\n"
63 "That flesh is heir to,--'tis a consummation\n"
64 "Devoutly to be wish'd. To die,--to sleep;--\n"
65 "To sleep! perchance to dream:--ay, there's the rub;\n"
66 "For in that sleep of death what dreams may come,\n"
67 "When we have shuffled off this mortal coil,\n"
68 "Must give us pause: there's the respect\n"
69 "That makes calamity of so long life;\n"
70 "For who would bear the whips and scorns of time,\n"
71 "The oppressor's wrong, the proud man's contumely,\n"
72 "The pangs of despis'd love, the law's delay,\n"
73 "The insolence of office, and the spurns\n"
74 "That patient merit of the unworthy takes,\n"
75 "When he himself might his quietus make\n"
76 "With a bare bodkin? who would these fardels bear,\n"
77 "To grunt and sweat under a weary life,\n"
78 "But that the dread of something after death,--\n"
79 "The undiscover'd country, from whose bourn\n"
80 "No traveller returns,--puzzles the will,\n"
81 "And makes us rather bear those ills we have\n"
82 "Than fly to others that we know not of?\n"
83 "Thus conscience does make cowards of us all;\n"
84 "And thus the native hue of resolution\n"
85 "Is sicklied o'er with the pale cast of thought;\n"
86 "And enterprises of great pith and moment,\n"
87 "With this regard, their currents turn awry,\n"
88 "And lose the name of action.--Soft you now!\n"
89 "The fair Ophelia!--Nymph, in thy orisons\n"
90 "Be all my sins remember'd.\n";
92 /* ------------------------------------------------------------------ */
95 md5bit(uint8_t *digest
, int bit_num
)
100 bit_num
%= 128; /* keep this bounded for convenience */
101 byte_off
= bit_num
/ 8;
102 bit_off
= bit_num
% 8;
104 /* return the value of bit N from the digest */
105 return ((digest
[byte_off
] & (0x01 << bit_off
)) ? 1 : 0);
108 static uchar_t itoa64
[] = /* 0 ... 63 => ascii - 64 */
109 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
112 to64(char *s
, uint64_t v
, int n
)
115 *s
++ = itoa64
[v
& 0x3f];
120 #define ROUNDS "rounds="
121 #define ROUNDSLEN (sizeof (ROUNDS) - 1)
124 * get the integer value after rounds= where ever it occurs in the string.
125 * if the last char after the int is a , or $ that is fine anything else is an
129 getrounds(const char *s
)
137 if ((r
= strstr(s
, ROUNDS
)) == NULL
) {
141 if (strncmp(r
, ROUNDS
, ROUNDSLEN
) != 0) {
147 val
= strtol(p
, &e
, 10);
149 * An error occurred or there is non-numeric stuff at the end
150 * which isn't one of the crypt(3c) special chars ',' or '$'
152 if (errno
!= 0 || val
< 0 ||
153 !(*e
== '\0' || *e
== ',' || *e
== '$')) {
155 "crypt_sunmd5: invalid rounds specification \"%s\"", s
);
159 return ((uint32_t)val
);
164 crypt_gensalt_impl(char *gsbuffer
,
167 const struct passwd
*userinfo
,
170 uint32_t confrounds
= 0;
176 char rndstr
[sizeof (rndval
) + 1]; /* rndval as a base64 string */
178 for (i
= 0; params
!= NULL
&& params
[i
] != NULL
; i
++) {
179 if (strncmp(params
[i
], ROUNDS
, ROUNDSLEN
) == 0) {
180 confrounds
= getrounds(params
[i
]);
183 "crypt_sunmd5: invalid parameter %s", params
[i
]);
190 * If the config file has a higher value for rounds= than what
191 * was in the old salt use that, otherwise keep what was in the
194 saltrounds
= getrounds(oldsalt
);
195 if (confrounds
> saltrounds
) {
196 saltrounds
= confrounds
;
199 if ((fd
= open("/dev/random", O_RDONLY
)) == -1) {
203 got
= read(fd
, &rndval
, sizeof (rndval
));
204 if (got
< sizeof (rndval
)) {
213 to64((char *)&rndstr
, rndval
, sizeof (rndval
));
214 rndstr
[sizeof (rndstr
) - 1] = '\0';
216 if (saltrounds
> 0) {
217 if (snprintf(gsbuffer
, gsbufflen
,
218 "$" CRYPT_ALGNAME
"," ROUNDS
"%d$",
219 saltrounds
) >= gsbufflen
)
222 if (snprintf(gsbuffer
, gsbufflen
,
223 "$" CRYPT_ALGNAME
"$") >= gsbufflen
)
227 if (strlcat(gsbuffer
, rndstr
, gsbufflen
) >= gsbufflen
)
229 if (strlcat(gsbuffer
, "$", gsbufflen
) >= gsbufflen
)
235 bzero(gsbuffer
, gsbufflen
);
242 crypt_genhash_impl(char *ctbuffer
,
244 const char *plaintext
,
250 int maxrounds
= BASIC_ROUND_COUNT
;
256 /* put all the sensitive data in a struct */
258 MD5_CTX context
; /* working buffer for MD5 algorithm */
259 uint8_t digest
[DIGEST_LEN
]; /* where the MD5 digest is stored */
261 int indirect_4
[16]; /* extracted array of 4bit values */
262 int shift_4
[16]; /* shift schedule, vals 0..4 */
264 int s7shift
; /* shift for shift_7 creation, vals 0..7 */
265 int indirect_7
[16]; /* extracted array of 7bit values */
266 int shift_7
[16]; /* shift schedule, vals 0..1 */
268 int indirect_a
; /* 7bit index into digest */
269 int shift_a
; /* shift schedule, vals 0..1 */
271 int indirect_b
; /* 7bit index into digest */
272 int shift_b
; /* shift schedule, vals 0..1 */
274 int bit_a
; /* single bit for cointoss */
275 int bit_b
; /* single bit for cointoss */
277 char roundascii
[ROUND_BUFFER_LEN
]; /* ascii rep of roundcount */
282 * Extract the puresalt (if it exists) from the existing salt string
283 * $md5[,rounds=%d]$<puresalt>$<optional existing encoding>
285 saltend
= strrchr(salt
, '$');
286 if (saltend
== NULL
|| saltend
== salt
) {
289 if (saltend
[1] != '\0') {
290 size_t len
= saltend
- salt
+ 1;
291 if ((puresalt
= malloc(len
)) == NULL
) {
294 (void) strlcpy(puresalt
, salt
, len
);
296 puresalt
= strdup(salt
);
297 if (puresalt
== NULL
) {
302 maxrounds
+= getrounds(salt
);
304 /* initialise the context */
306 MD5Init(&data
.context
);
308 /* update with the (hopefully entropic) plaintext */
310 MD5Update(&data
.context
, (uchar_t
*)plaintext
, strlen(plaintext
));
312 /* update with the (publically known) salt */
314 MD5Update(&data
.context
, (uchar_t
*)puresalt
, strlen(puresalt
));
317 /* compute the digest */
319 MD5Final(data
.digest
, &data
.context
);
322 * now to delay high-speed md5 implementations that have stuff
323 * like code inlining, loops unrolled and table lookup
326 for (round
= 0; round
< maxrounds
; round
++) {
327 /* re-initialise the context */
329 MD5Init(&data
.context
);
331 /* update with the previous digest */
333 MD5Update(&data
.context
, data
.digest
, sizeof (data
.digest
));
335 /* populate the shift schedules for use later */
337 for (i
= 0; i
< 16; i
++) {
340 /* offset 3 -> occasionally span more than 1 int32 fetch */
342 data
.s7shift
= data
.digest
[i
] % 8;
343 data
.shift_4
[i
] = data
.digest
[j
] % 5;
344 data
.shift_7
[i
] = (data
.digest
[j
] >> data
.s7shift
)
348 data
.shift_a
= md5bit(data
.digest
, round
);
349 data
.shift_b
= md5bit(data
.digest
, round
+ 64);
351 /* populate indirect_4 with 4bit values extracted from digest */
353 for (i
= 0; i
< 16; i
++) {
354 /* shift the digest byte and extract four bits */
356 (data
.digest
[i
] >> data
.shift_4
[i
]) & 0x0f;
360 * populate indirect_7 with 7bit values from digest
361 * indexed via indirect_4
364 for (i
= 0; i
< 16; i
++) {
365 /* shift the digest byte and extract seven bits */
366 data
.indirect_7
[i
] = (data
.digest
[data
.indirect_4
[i
]]
367 >> data
.shift_7
[i
]) & 0x7f;
371 * use the 7bit values to indirect into digest,
372 * and create two 8bit values from the results.
375 data
.indirect_a
= data
.indirect_b
= 0;
377 for (i
= 0; i
< 8; i
++) {
378 data
.indirect_a
|= (md5bit(data
.digest
,
379 data
.indirect_7
[i
]) << i
);
381 data
.indirect_b
|= (md5bit(data
.digest
,
382 data
.indirect_7
[i
+ 8]) << i
);
386 /* shall we utilise the top or bottom 7 bits? */
388 data
.indirect_a
= (data
.indirect_a
>> data
.shift_a
) & 0x7f;
389 data
.indirect_b
= (data
.indirect_b
>> data
.shift_b
) & 0x7f;
392 /* extract two data.digest bits */
394 data
.bit_a
= md5bit(data
.digest
, data
.indirect_a
);
395 data
.bit_b
= md5bit(data
.digest
, data
.indirect_b
);
399 for (i
= 0; i
< 15; i
++) {
400 (void) printf("%1x-", data
.indirect_4
[i
]);
402 (void) printf("%1x ", data
.indirect_4
[15]);
403 for (i
= 0; i
< 15; i
++) {
404 (void) printf("%02x-", data
.indirect_7
[i
]);
406 (void) printf("%02x ", data
.indirect_7
[15]);
407 (void) printf("%02x/%02x ", data
.indirect_a
, data
.indirect_b
);
408 (void) printf("%d^%d\n", data
.bit_a
, data
.bit_b
);
412 /* xor a coin-toss; if true, mix-in the constant phrase */
414 if (data
.bit_a
^ data
.bit_b
) {
415 MD5Update(&data
.context
,
416 (unsigned char *) constant_phrase
,
417 sizeof (constant_phrase
));
419 (void) printf("mixing constant_phrase\n");
424 /* digest a decimal sprintf of the current roundcount */
426 (void) snprintf(data
.roundascii
, ROUND_BUFFER_LEN
, "%d", round
);
427 MD5Update(&data
.context
,
428 (unsigned char *) data
.roundascii
, strlen(data
.roundascii
));
430 /* compute/flush the digest, and loop */
432 MD5Final(data
.digest
, &data
.context
);
437 /* print the digest */
438 for (i
= 0; i
< 16; i
++) {
439 (void) printf("%02x", data
.digest
[i
]);
444 (void) snprintf(ctbuffer
, ctbufflen
, "%s$", puresalt
);
445 p
= ctbuffer
+ strlen(ctbuffer
);
447 l
= (data
.digest
[ 0]<<16) | (data
.digest
[ 6]<<8) | data
.digest
[12];
448 to64(p
, l
, 4); p
+= 4;
449 l
= (data
.digest
[ 1]<<16) | (data
.digest
[ 7]<<8) | data
.digest
[13];
450 to64(p
, l
, 4); p
+= 4;
451 l
= (data
.digest
[ 2]<<16) | (data
.digest
[ 8]<<8) | data
.digest
[14];
452 to64(p
, l
, 4); p
+= 4;
453 l
= (data
.digest
[ 3]<<16) | (data
.digest
[ 9]<<8) | data
.digest
[15];
454 to64(p
, l
, 4); p
+= 4;
455 l
= (data
.digest
[ 4]<<16) | (data
.digest
[10]<<8) | data
.digest
[ 5];
456 to64(p
, l
, 4); p
+= 4;
457 l
= data
.digest
[11]; to64(p
, l
, 2); p
+= 2;
460 /* tidy up after ourselves */
461 bzero(&data
, sizeof (data
));