crypt.c revision 8870
1/*
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@login.dknet.dk> 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 * $Id: crypt.c,v 1.2 1994/11/07 21:07:09 phk Exp $
10 *
11 */
12
13#if defined(LIBC_SCCS) && !defined(lint)
14static char rcsid[] = "$Header: /home/ncvs/src/lib/libcrypt/crypt.c,v 1.2 1994/11/07 21:07:09 phk Exp $";
15#endif /* LIBC_SCCS and not lint */
16
17#include <unistd.h>
18#include <stdio.h>
19#include <md5.h>
20
21static unsigned char itoa64[] =		/* 0 ... 63 => ascii - 64 */
22	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
23
24static void
25to64(s, v, n)
26	char *s;
27	unsigned long v;
28	int n;
29{
30	while (--n >= 0) {
31		*s++ = itoa64[v&0x3f];
32		v >>= 6;
33	}
34}
35
36/*
37 * UNIX password
38 *
39 * Use MD5 for what it is best at...
40 */
41
42char *
43crypt(pw, salt)
44	register const char *pw;
45	register const char *salt;
46{
47	static char	*magic = "$1$";	/*
48						 * This string is magic for
49						 * this algorithm.  Having
50						 * it this way, we can get
51						 * get better later on
52						 */
53	static char     passwd[120], *p;
54	static const char *sp,*ep;
55	unsigned char	final[16];
56	int sl,pl,i,j;
57	MD5_CTX	ctx,ctx1;
58	unsigned long l;
59
60	/* Refine the Salt first */
61	sp = salt;
62
63	/* If it starts with the magic string, then skip that */
64	if(!strncmp(sp,magic,strlen(magic)))
65		sp += strlen(magic);
66
67	/* It stops at the first '$', max 8 chars */
68	for(ep=sp;*ep && *ep != '$' && ep < (sp+8);ep++)
69		continue;
70
71	/* get the length of the true salt */
72	sl = ep - sp;
73
74	MD5Init(&ctx);
75
76	/* The password first, since that is what is most unknown */
77	MD5Update(&ctx,pw,strlen(pw));
78
79	/* Then our magic string */
80	MD5Update(&ctx,magic,strlen(magic));
81
82	/* Then the raw salt */
83	MD5Update(&ctx,sp,sl);
84
85	/* Then just as many characters of the MD5(pw,salt,pw) */
86	MD5Init(&ctx1);
87	MD5Update(&ctx1,pw,strlen(pw));
88	MD5Update(&ctx1,sp,sl);
89	MD5Update(&ctx1,pw,strlen(pw));
90	MD5Final(final,&ctx1);
91	for(pl = strlen(pw); pl > 0; pl -= 16)
92		MD5Update(&ctx,final,pl>16 ? 16 : pl);
93
94	/* Don't leave anything around in vm they could use. */
95	memset(final,0,sizeof final);
96
97	/* Then something really weird... */
98	for (j=0,i = strlen(pw); i ; i >>= 1)
99		if(i&1)
100		    MD5Update(&ctx, final+j, 1);
101		else
102		    MD5Update(&ctx, pw+j, 1);
103
104	/* Now make the output string */
105	strcpy(passwd,magic);
106	strncat(passwd,sp,sl);
107	strcat(passwd,"$");
108
109	MD5Final(final,&ctx);
110
111	/*
112	 * and now, just to make sure things don't run too fast
113	 * On a 60 Mhz Pentium this takes 34 msec, so you would
114	 * need 30 seconds to build a 1000 entry dictionary...
115	 */
116	for(i=0;i<1000;i++) {
117		MD5Init(&ctx1);
118		if(i & 1)
119			MD5Update(&ctx1,pw,strlen(pw));
120		else
121			MD5Update(&ctx1,final,16);
122
123		if(i % 3)
124			MD5Update(&ctx1,sp,sl);
125
126		if(i % 7)
127			MD5Update(&ctx1,pw,strlen(pw));
128
129		if(i & 1)
130			MD5Update(&ctx1,final,16);
131		else
132			MD5Update(&ctx1,pw,strlen(pw));
133		MD5Final(final,&ctx1);
134	}
135
136	p = passwd + strlen(passwd);
137
138	l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4;
139	l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4;
140	l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4;
141	l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4;
142	l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4;
143	l =                    final[11]                ; to64(p,l,2); p += 2;
144	*p = '\0';
145
146	/* Don't leave anything around in vm they could use. */
147	memset(final,0,sizeof final);
148
149	return passwd;
150}
151
152