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