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