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