source: protocols/yahoo/crypt.c @ a40a2c2

Last change on this file since a40a2c2 was b7d3cc34, checked in by Wilmer van der Gaast <wilmer@…>, at 2005-11-06T18:23:18Z

Initial repository (0.99 release tree)

  • Property mode set to 100644
File size: 6.3 KB
Line 
1/* One way encryption based on MD5 sum.
2   Copyright (C) 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
3   This file is part of the GNU C Library.
4   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6   The GNU C Library is free software; you can redistribute it and/or
7   modify it under the terms of the GNU Lesser General Public
8   License as published by the Free Software Foundation; either
9   version 2.1 of the License, or (at your option) any later version.
10
11   The GNU C Library is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with the GNU C Library; if not, write to the Free
18   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19   02111-1307 USA.  */
20
21/* warmenhoven took this file and made it work with the md5.[ch] we
22 * already had. isn't that lovely. people should just use linux or
23 * freebsd, crypt works properly on those systems. i hate solaris */
24
25#if HAVE_CONFIG_H
26#  include <config.h>
27#endif
28
29#if HAVE_STRING_H
30#  include <string.h>
31#elif HAVE_STRINGS_H
32#  include <strings.h>
33#endif
34
35#include <stdlib.h>
36#include "yahoo_util.h"
37
38#include "md5.h"
39
40/* Define our magic string to mark salt for MD5 "encryption"
41   replacement.  This is meant to be the same as for other MD5 based
42   encryption implementations.  */
43static const char md5_salt_prefix[] = "$1$";
44
45/* Table with characters for base64 transformation.  */
46static const char b64t[64] =
47"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
48
49char *yahoo_crypt(char *key, char *salt)
50{
51        char *buffer = NULL;
52        int buflen = 0;
53        int needed = 3 + strlen (salt) + 1 + 26 + 1;
54
55        md5_byte_t alt_result[16];
56        md5_state_t ctx;
57        md5_state_t alt_ctx;
58        size_t salt_len;
59        size_t key_len;
60        size_t cnt;
61        char *cp;
62
63        if (buflen < needed) {
64                buflen = needed;
65                if ((buffer = realloc(buffer, buflen)) == NULL)
66                        return NULL;
67        }
68
69        /* Find beginning of salt string.  The prefix should normally always
70           be present.  Just in case it is not.  */
71        if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0)
72                /* Skip salt prefix.  */
73                salt += sizeof (md5_salt_prefix) - 1;
74
75        salt_len = MIN (strcspn (salt, "$"), 8);
76        key_len = strlen (key);
77
78        /* Prepare for the real work.  */
79        md5_init(&ctx);
80
81        /* Add the key string.  */
82        md5_append(&ctx, (md5_byte_t *)key, key_len);
83
84        /* Because the SALT argument need not always have the salt prefix we
85           add it separately.  */
86        md5_append(&ctx, (md5_byte_t *)md5_salt_prefix, sizeof (md5_salt_prefix) - 1);
87
88        /* The last part is the salt string.  This must be at most 8
89           characters and it ends at the first `$' character (for
90           compatibility which existing solutions).  */
91        md5_append(&ctx, (md5_byte_t *)salt, salt_len);
92
93        /* Compute alternate MD5 sum with input KEY, SALT, and KEY.  The
94           final result will be added to the first context.  */
95        md5_init(&alt_ctx);
96
97        /* Add key.  */
98        md5_append(&alt_ctx, (md5_byte_t *)key, key_len);
99
100        /* Add salt.  */
101        md5_append(&alt_ctx, (md5_byte_t *)salt, salt_len);
102
103        /* Add key again.  */
104        md5_append(&alt_ctx, (md5_byte_t *)key, key_len);
105
106        /* Now get result of this (16 bytes) and add it to the other
107           context.  */
108        md5_finish(&alt_ctx, alt_result);
109
110        /* Add for any character in the key one byte of the alternate sum.  */
111        for (cnt = key_len; cnt > 16; cnt -= 16)
112                md5_append(&ctx, alt_result, 16);
113        md5_append(&ctx, alt_result, cnt);
114
115        /* For the following code we need a NUL byte.  */
116        alt_result[0] = '\0';
117
118        /* The original implementation now does something weird: for every 1
119           bit in the key the first 0 is added to the buffer, for every 0
120           bit the first character of the key.  This does not seem to be
121           what was intended but we have to follow this to be compatible.  */
122        for (cnt = key_len; cnt > 0; cnt >>= 1)
123                md5_append(&ctx, (cnt & 1) != 0 ? alt_result : (md5_byte_t *)key, 1);
124
125        /* Create intermediate result.  */
126        md5_finish(&ctx, alt_result);
127
128        /* Now comes another weirdness.  In fear of password crackers here
129           comes a quite long loop which just processes the output of the
130           previous round again.  We cannot ignore this here.  */
131        for (cnt = 0; cnt < 1000; ++cnt) {
132                /* New context.  */
133                md5_init(&ctx);
134
135                /* Add key or last result.  */
136                if ((cnt & 1) != 0)
137                        md5_append(&ctx, (md5_byte_t *)key, key_len);
138                else
139                        md5_append(&ctx, alt_result, 16);
140
141                /* Add salt for numbers not divisible by 3.  */
142                if (cnt % 3 != 0)
143                        md5_append(&ctx, (md5_byte_t *)salt, salt_len);
144
145                /* Add key for numbers not divisible by 7.  */
146                if (cnt % 7 != 0)
147                        md5_append(&ctx, (md5_byte_t *)key, key_len);
148
149                /* Add key or last result.  */
150                if ((cnt & 1) != 0)
151                        md5_append(&ctx, alt_result, 16);
152                else
153                        md5_append(&ctx, (md5_byte_t *)key, key_len);
154
155                /* Create intermediate result.  */
156                md5_finish(&ctx, alt_result);
157        }
158
159        /* Now we can construct the result string.  It consists of three
160           parts.  */
161
162        strncpy(buffer, md5_salt_prefix, MAX (0, buflen));
163        cp = buffer + strlen(buffer);
164        buflen -= sizeof (md5_salt_prefix);
165
166        strncpy(cp, salt, MIN ((size_t) buflen, salt_len));
167        cp = cp + strlen(cp);
168        buflen -= MIN ((size_t) buflen, salt_len);
169
170        if (buflen > 0) {
171                *cp++ = '$';
172                --buflen;
173        }
174
175#define b64_from_24bit(B2, B1, B0, N) \
176        do { \
177                unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \
178                int n = (N); \
179                while (n-- > 0 && buflen > 0) { \
180                        *cp++ = b64t[w & 0x3f]; \
181                        --buflen; \
182                        w >>= 6; \
183                }\
184        } while (0)
185
186        b64_from_24bit (alt_result[0], alt_result[6], alt_result[12], 4);
187        b64_from_24bit (alt_result[1], alt_result[7], alt_result[13], 4);
188        b64_from_24bit (alt_result[2], alt_result[8], alt_result[14], 4);
189        b64_from_24bit (alt_result[3], alt_result[9], alt_result[15], 4);
190        b64_from_24bit (alt_result[4], alt_result[10], alt_result[5], 4);
191        b64_from_24bit (0, 0, alt_result[11], 2);
192        if (buflen <= 0) {
193                FREE(buffer);
194        } else
195                *cp = '\0';     /* Terminate the string.  */
196
197        /* Clear the buffer for the intermediate result so that people
198           attaching to processes or reading core dumps cannot get any
199           information.  We do it in this way to clear correct_words[]
200           inside the MD5 implementation as well.  */
201        md5_init(&ctx);
202        md5_finish(&ctx, alt_result);
203        memset (&ctx, '\0', sizeof (ctx));
204        memset (&alt_ctx, '\0', sizeof (alt_ctx));
205
206        return buffer;
207}
Note: See TracBrowser for help on using the repository browser.