xref: /freebsd/lib/libcrypt/crypt-md5.c (revision ee2ea5ceafed78a5bd9810beb9e3ca927180c226)
1 /*
2  * ----------------------------------------------------------------------------
3  * "THE BEER-WARE LICENSE" (Revision 42):
4  * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
5  * can do whatever you want with this stuff. If we meet some day, and you think
6  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7  * ----------------------------------------------------------------------------
8  */
9 
10 #include <sys/cdefs.h>
11 __FBSDID("$FreeBSD$");
12 
13 #include <unistd.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <md5.h>
17 #include <err.h>
18 #include "crypt.h"
19 
20 /*
21  * UNIX password
22  */
23 
24 char *
25 crypt_md5(const char *pw, const char *salt)
26 {
27 	MD5_CTX	ctx,ctx1;
28 	unsigned long l;
29 	int sl, pl;
30 	u_int i;
31 	u_char final[MD5_SIZE];
32 	static const char *sp, *ep;
33 	static char passwd[120], *p;
34 	static const char *magic = "$1$";	/*
35 						 * This string is magic for
36 						 * this algorithm.  Having
37 						 * it this way, we can get
38 						 * better later on.
39 						 */
40 
41 	/* Refine the Salt first */
42 	sp = salt;
43 
44 	/* If it starts with the magic string, then skip that */
45 	if(!strncmp(sp, magic, strlen(magic)))
46 		sp += strlen(magic);
47 
48 	/* It stops at the first '$', max 8 chars */
49 	for(ep = sp; *ep && *ep != '$' && ep < (sp + 8); ep++)
50 		continue;
51 
52 	/* get the length of the true salt */
53 	sl = ep - sp;
54 
55 	MD5Init(&ctx);
56 
57 	/* The password first, since that is what is most unknown */
58 	MD5Update(&ctx, (const u_char *)pw, strlen(pw));
59 
60 	/* Then our magic string */
61 	MD5Update(&ctx, (const u_char *)magic, strlen(magic));
62 
63 	/* Then the raw salt */
64 	MD5Update(&ctx, (const u_char *)sp, (u_int)sl);
65 
66 	/* Then just as many characters of the MD5(pw,salt,pw) */
67 	MD5Init(&ctx1);
68 	MD5Update(&ctx1, (const u_char *)pw, strlen(pw));
69 	MD5Update(&ctx1, (const u_char *)sp, (u_int)sl);
70 	MD5Update(&ctx1, (const u_char *)pw, strlen(pw));
71 	MD5Final(final, &ctx1);
72 	for(pl = (int)strlen(pw); pl > 0; pl -= MD5_SIZE)
73 		MD5Update(&ctx, (const u_char *)final,
74 		    (u_int)(pl > MD5_SIZE ? MD5_SIZE : pl));
75 
76 	/* Don't leave anything around in vm they could use. */
77 	memset(final, 0, sizeof(final));
78 
79 	/* Then something really weird... */
80 	for (i = strlen(pw); i; i >>= 1)
81 		if(i & 1)
82 		    MD5Update(&ctx, (const u_char *)final, 1);
83 		else
84 		    MD5Update(&ctx, (const u_char *)pw, 1);
85 
86 	/* Now make the output string */
87 	strcpy(passwd, magic);
88 	strncat(passwd, sp, (u_int)sl);
89 	strcat(passwd, "$");
90 
91 	MD5Final(final, &ctx);
92 
93 	/*
94 	 * and now, just to make sure things don't run too fast
95 	 * On a 60 Mhz Pentium this takes 34 msec, so you would
96 	 * need 30 seconds to build a 1000 entry dictionary...
97 	 */
98 	for(i = 0; i < 1000; i++) {
99 		MD5Init(&ctx1);
100 		if(i & 1)
101 			MD5Update(&ctx1, (const u_char *)pw, strlen(pw));
102 		else
103 			MD5Update(&ctx1, (const u_char *)final, MD5_SIZE);
104 
105 		if(i % 3)
106 			MD5Update(&ctx1, (const u_char *)sp, (u_int)sl);
107 
108 		if(i % 7)
109 			MD5Update(&ctx1, (const u_char *)pw, strlen(pw));
110 
111 		if(i & 1)
112 			MD5Update(&ctx1, (const u_char *)final, MD5_SIZE);
113 		else
114 			MD5Update(&ctx1, (const u_char *)pw, strlen(pw));
115 		MD5Final(final, &ctx1);
116 	}
117 
118 	p = passwd + strlen(passwd);
119 
120 	l = (final[ 0]<<16) | (final[ 6]<<8) | final[12];
121 	_crypt_to64(p, l, 4); p += 4;
122 	l = (final[ 1]<<16) | (final[ 7]<<8) | final[13];
123 	_crypt_to64(p, l, 4); p += 4;
124 	l = (final[ 2]<<16) | (final[ 8]<<8) | final[14];
125 	_crypt_to64(p, l, 4); p += 4;
126 	l = (final[ 3]<<16) | (final[ 9]<<8) | final[15];
127 	_crypt_to64(p, l, 4); p += 4;
128 	l = (final[ 4]<<16) | (final[10]<<8) | final[ 5];
129 	_crypt_to64(p, l, 4); p += 4;
130 	l =                    final[11]                ;
131 	_crypt_to64(p, l, 2); p += 2;
132 	*p = '\0';
133 
134 	/* Don't leave anything around in vm they could use. */
135 	memset(final, 0, sizeof(final));
136 
137 	return passwd;
138 }
139 
140