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