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