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