crypt.c revision 43092
11984Scsgr/*
24246Sphk * ----------------------------------------------------------------------------
34246Sphk * "THE BEER-WARE LICENSE" (Revision 42):
44246Sphk * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
54246Sphk * can do whatever you want with this stuff. If we meet some day, and you think
64246Sphk * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
74246Sphk * ----------------------------------------------------------------------------
843092Smarkm *
943092Smarkm * $Id$
1043092Smarkm *
1142981Sbrandon */
1242981Sbrandon
1343092Smarkm#if defined(LIBC_SCCS) && !defined(lint)
1443092Smarkmstatic char rcsid[] = "$Header$";
1543092Smarkm#endif /* LIBC_SCCS and not lint */
1642981Sbrandon
171984Scsgr#include <unistd.h>
181984Scsgr#include <stdio.h>
1917141Sjkh#include <string.h>
2043092Smarkm#include <md5.h>
2143092Smarkm#include <string.h>
221984Scsgr
2343092Smarkmstatic unsigned char itoa64[] =		/* 0 ... 63 => ascii - 64 */
2443092Smarkm	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
254246Sphk
2643092Smarkmstatic void to64 __P((char *, unsigned long, int));
2718918Sphk
2843092Smarkmstatic void
294246Sphkto64(s, v, n)
304246Sphk	char *s;
314246Sphk	unsigned long v;
324246Sphk	int n;
334246Sphk{
3443092Smarkmstatic void to64 __P((char *, unsigned long, int));
3543092Smarkm
364246Sphk	while (--n >= 0) {
3743092Smarkm		*s++ = itoa64[v&0x3f];
384246Sphk		v >>= 6;
394246Sphk	}
404246Sphk}
414246Sphk
4243092Smarkm/*
4343092Smarkm * UNIX password
4443092Smarkm *
4543092Smarkm * Use MD5 for what it is best at...
4643092Smarkm */
4743092Smarkm
4843092Smarkmchar *
4943092Smarkmcrypt(pw, salt)
5043092Smarkm	register const char *pw;
5143092Smarkm	register const char *salt;
521984Scsgr{
5343092Smarkm	static char	*magic = "$1$";	/*
5443092Smarkm						 * This string is magic for
5543092Smarkm						 * this algorithm.  Having
5643092Smarkm						 * it this way, we can get
5743092Smarkm						 * get better later on
5843092Smarkm						 */
5943092Smarkm	static char     passwd[120], *p;
6043092Smarkm	static const char *sp,*ep;
6143092Smarkm	unsigned char	final[16];
6243092Smarkm	int sl,pl,i,j;
6343092Smarkm	MD5_CTX	ctx,ctx1;
6443092Smarkm	unsigned long l;
651984Scsgr
6643092Smarkm	/* Refine the Salt first */
6743092Smarkm	sp = salt;
681984Scsgr
6943092Smarkm	/* If it starts with the magic string, then skip that */
7043092Smarkm	if(!strncmp(sp,magic,strlen(magic)))
7143092Smarkm		sp += strlen(magic);
721984Scsgr
7343092Smarkm	/* It stops at the first '$', max 8 chars */
7443092Smarkm	for(ep=sp;*ep && *ep != '$' && ep < (sp+8);ep++)
7543092Smarkm		continue;
764246Sphk
7743092Smarkm	/* get the length of the true salt */
7843092Smarkm	sl = ep - sp;
794246Sphk
8043092Smarkm	MD5Init(&ctx);
814246Sphk
8243092Smarkm	/* The password first, since that is what is most unknown */
8343092Smarkm	MD5Update(&ctx,pw,strlen(pw));
844246Sphk
8543092Smarkm	/* Then our magic string */
8643092Smarkm	MD5Update(&ctx,magic,strlen(magic));
874246Sphk
8843092Smarkm	/* Then the raw salt */
8943092Smarkm	MD5Update(&ctx,sp,sl);
904246Sphk
9143092Smarkm	/* Then just as many characters of the MD5(pw,salt,pw) */
9243092Smarkm	MD5Init(&ctx1);
9343092Smarkm	MD5Update(&ctx1,pw,strlen(pw));
9443092Smarkm	MD5Update(&ctx1,sp,sl);
9543092Smarkm	MD5Update(&ctx1,pw,strlen(pw));
9643092Smarkm	MD5Final(final,&ctx1);
9743092Smarkm	for(pl = strlen(pw); pl > 0; pl -= 16)
9843092Smarkm		MD5Update(&ctx,final,pl>16 ? 16 : pl);
994246Sphk
10043092Smarkm	/* Don't leave anything around in vm they could use. */
10143092Smarkm	memset(final,0,sizeof final);
1024246Sphk
10343092Smarkm	/* Then something really weird... */
10443092Smarkm	for (i = strlen(pw); i ; i >>= 1)
10543092Smarkm		if(i&1)
10643092Smarkm		    MD5Update(&ctx, final, 1);
10743092Smarkm		else
10843092Smarkm		    MD5Update(&ctx, pw, 1);
1091984Scsgr
11043092Smarkm	/* Now make the output string */
11143092Smarkm	strcpy(passwd,magic);
11243092Smarkm	strncat(passwd,sp,sl);
11343092Smarkm	strcat(passwd,"$");
1141984Scsgr
11543092Smarkm	MD5Final(final,&ctx);
1164246Sphk
11743092Smarkm	/*
11843092Smarkm	 * and now, just to make sure things don't run too fast
11943092Smarkm	 * On a 60 Mhz Pentium this takes 34 msec, so you would
12043092Smarkm	 * need 30 seconds to build a 1000 entry dictionary...
12143092Smarkm	 */
12243092Smarkm	for(i=0;i<1000;i++) {
12343092Smarkm		MD5Init(&ctx1);
12443092Smarkm		if(i & 1)
12543092Smarkm			MD5Update(&ctx1,pw,strlen(pw));
12643092Smarkm		else
12743092Smarkm			MD5Update(&ctx1,final,16);
12843092Smarkm
12943092Smarkm		if(i % 3)
13043092Smarkm			MD5Update(&ctx1,sp,sl);
13143092Smarkm
13243092Smarkm		if(i % 7)
13343092Smarkm			MD5Update(&ctx1,pw,strlen(pw));
13443092Smarkm
13543092Smarkm		if(i & 1)
13643092Smarkm			MD5Update(&ctx1,final,16);
13743092Smarkm		else
13843092Smarkm			MD5Update(&ctx1,pw,strlen(pw));
13943092Smarkm		MD5Final(final,&ctx1);
14043092Smarkm	}
14143092Smarkm
14243092Smarkm	p = passwd + strlen(passwd);
14343092Smarkm
14443092Smarkm	l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4;
14543092Smarkm	l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4;
14643092Smarkm	l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4;
14743092Smarkm	l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4;
14843092Smarkm	l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4;
14943092Smarkm	l =                    final[11]                ; to64(p,l,2); p += 2;
15043092Smarkm	*p = '\0';
15143092Smarkm
15243092Smarkm	/* Don't leave anything around in vm they could use. */
15343092Smarkm	memset(final,0,sizeof final);
15443092Smarkm
15543092Smarkm	return passwd;
1561984Scsgr}
1571984Scsgr
158