/src/pidgin/libpurple/ciphers/md4.c
Line | Count | Source |
1 | | /* |
2 | | * purple |
3 | | * |
4 | | * Purple is the legal property of its developers, whose names are too numerous |
5 | | * to list here. Please refer to the COPYRIGHT file distributed with this |
6 | | * source distribution. |
7 | | * |
8 | | * Original md4 taken from linux kernel |
9 | | * MD4 Message Digest Algorithm (RFC1320). |
10 | | * |
11 | | * Implementation derived from Andrew Tridgell and Steve French's |
12 | | * CIFS MD4 implementation, and the cryptoapi implementation |
13 | | * originally based on the public domain implementation written |
14 | | * by Colin Plumb in 1993. |
15 | | * |
16 | | * Copyright (c) Andrew Tridgell 1997-1998. |
17 | | * Modified by Steve French (sfrench@us.ibm.com) 2002 |
18 | | * Copyright (c) Cryptoapi developers. |
19 | | * Copyright (c) 2002 David S. Miller (davem@redhat.com) |
20 | | * Copyright (c) 2002 James Morris <jmorris@intercode.com.au> |
21 | | * |
22 | | * This program is free software; you can redistribute it and/or modify |
23 | | * it under the terms of the GNU General Public License as published by |
24 | | * the Free Software Foundation; either version 2 of the License, or |
25 | | * (at your option) any later version. |
26 | | * |
27 | | * This program is distributed in the hope that it will be useful, |
28 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
29 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
30 | | * GNU General Public License for more details. |
31 | | * |
32 | | * You should have received a copy of the GNU General Public License |
33 | | * along with this program; if not, write to the Free Software |
34 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
35 | | */ |
36 | | #include <cipher.h> |
37 | | |
38 | | #define MD4_DIGEST_SIZE 16 |
39 | 0 | #define MD4_HMAC_BLOCK_SIZE 64 |
40 | | #define MD4_BLOCK_WORDS 16 |
41 | | #define MD4_HASH_WORDS 4 |
42 | | |
43 | | struct MD4_Context { |
44 | | guint32 hash[MD4_HASH_WORDS]; |
45 | | guint32 block[MD4_BLOCK_WORDS]; |
46 | | guint64 byte_count; |
47 | | }; |
48 | | |
49 | | static inline guint32 lshift(guint32 x, unsigned int s) |
50 | 0 | { |
51 | 0 | x &= 0xFFFFFFFF; |
52 | 0 | return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s)); |
53 | 0 | } |
54 | | |
55 | | static inline guint32 F(guint32 x, guint32 y, guint32 z) |
56 | 0 | { |
57 | 0 | return (x & y) | ((~x) & z); |
58 | 0 | } |
59 | | |
60 | | static inline guint32 G(guint32 x, guint32 y, guint32 z) |
61 | 0 | { |
62 | 0 | return (x & y) | (x & z) | (y & z); |
63 | 0 | } |
64 | | |
65 | | static inline guint32 H(guint32 x, guint32 y, guint32 z) |
66 | 0 | { |
67 | 0 | return x ^ y ^ z; |
68 | 0 | } |
69 | | |
70 | 0 | #define ROUND1(a,b,c,d,k,s) (a = lshift(a + F(b,c,d) + k, s)) |
71 | 0 | #define ROUND2(a,b,c,d,k,s) (a = lshift(a + G(b,c,d) + k + (guint32)0x5A827999,s)) |
72 | 0 | #define ROUND3(a,b,c,d,k,s) (a = lshift(a + H(b,c,d) + k + (guint32)0x6ED9EBA1,s)) |
73 | | |
74 | | static inline void le32_to_cpu_array(guint32 *buf, unsigned int words) |
75 | 0 | { |
76 | 0 | while (words--) { |
77 | 0 | *buf=GUINT_FROM_LE(*buf); |
78 | 0 | buf++; |
79 | 0 | } |
80 | 0 | } |
81 | | |
82 | | static inline void cpu_to_le32_array(guint32 *buf, unsigned int words) |
83 | 0 | { |
84 | 0 | while (words--) { |
85 | 0 | *buf=GUINT_TO_LE(*buf); |
86 | 0 | buf++; |
87 | 0 | } |
88 | 0 | } |
89 | | |
90 | | static void md4_transform(guint32 *hash, guint32 const *in) |
91 | 0 | { |
92 | 0 | guint32 a, b, c, d; |
93 | |
|
94 | 0 | a = hash[0]; |
95 | 0 | b = hash[1]; |
96 | 0 | c = hash[2]; |
97 | 0 | d = hash[3]; |
98 | |
|
99 | 0 | ROUND1(a, b, c, d, in[0], 3); |
100 | 0 | ROUND1(d, a, b, c, in[1], 7); |
101 | 0 | ROUND1(c, d, a, b, in[2], 11); |
102 | 0 | ROUND1(b, c, d, a, in[3], 19); |
103 | 0 | ROUND1(a, b, c, d, in[4], 3); |
104 | 0 | ROUND1(d, a, b, c, in[5], 7); |
105 | 0 | ROUND1(c, d, a, b, in[6], 11); |
106 | 0 | ROUND1(b, c, d, a, in[7], 19); |
107 | 0 | ROUND1(a, b, c, d, in[8], 3); |
108 | 0 | ROUND1(d, a, b, c, in[9], 7); |
109 | 0 | ROUND1(c, d, a, b, in[10], 11); |
110 | 0 | ROUND1(b, c, d, a, in[11], 19); |
111 | 0 | ROUND1(a, b, c, d, in[12], 3); |
112 | 0 | ROUND1(d, a, b, c, in[13], 7); |
113 | 0 | ROUND1(c, d, a, b, in[14], 11); |
114 | 0 | ROUND1(b, c, d, a, in[15], 19); |
115 | |
|
116 | 0 | ROUND2(a, b, c, d,in[ 0], 3); |
117 | 0 | ROUND2(d, a, b, c, in[4], 5); |
118 | 0 | ROUND2(c, d, a, b, in[8], 9); |
119 | 0 | ROUND2(b, c, d, a, in[12], 13); |
120 | 0 | ROUND2(a, b, c, d, in[1], 3); |
121 | 0 | ROUND2(d, a, b, c, in[5], 5); |
122 | 0 | ROUND2(c, d, a, b, in[9], 9); |
123 | 0 | ROUND2(b, c, d, a, in[13], 13); |
124 | 0 | ROUND2(a, b, c, d, in[2], 3); |
125 | 0 | ROUND2(d, a, b, c, in[6], 5); |
126 | 0 | ROUND2(c, d, a, b, in[10], 9); |
127 | 0 | ROUND2(b, c, d, a, in[14], 13); |
128 | 0 | ROUND2(a, b, c, d, in[3], 3); |
129 | 0 | ROUND2(d, a, b, c, in[7], 5); |
130 | 0 | ROUND2(c, d, a, b, in[11], 9); |
131 | 0 | ROUND2(b, c, d, a, in[15], 13); |
132 | |
|
133 | 0 | ROUND3(a, b, c, d,in[ 0], 3); |
134 | 0 | ROUND3(d, a, b, c, in[8], 9); |
135 | 0 | ROUND3(c, d, a, b, in[4], 11); |
136 | 0 | ROUND3(b, c, d, a, in[12], 15); |
137 | 0 | ROUND3(a, b, c, d, in[2], 3); |
138 | 0 | ROUND3(d, a, b, c, in[10], 9); |
139 | 0 | ROUND3(c, d, a, b, in[6], 11); |
140 | 0 | ROUND3(b, c, d, a, in[14], 15); |
141 | 0 | ROUND3(a, b, c, d, in[1], 3); |
142 | 0 | ROUND3(d, a, b, c, in[9], 9); |
143 | 0 | ROUND3(c, d, a, b, in[5], 11); |
144 | 0 | ROUND3(b, c, d, a, in[13], 15); |
145 | 0 | ROUND3(a, b, c, d, in[3], 3); |
146 | 0 | ROUND3(d, a, b, c, in[11], 9); |
147 | 0 | ROUND3(c, d, a, b, in[7], 11); |
148 | 0 | ROUND3(b, c, d, a, in[15], 15); |
149 | |
|
150 | 0 | hash[0] += a; |
151 | 0 | hash[1] += b; |
152 | 0 | hash[2] += c; |
153 | 0 | hash[3] += d; |
154 | 0 | } |
155 | | |
156 | | static inline void md4_transform_helper(struct MD4_Context *ctx) |
157 | 0 | { |
158 | 0 | le32_to_cpu_array(ctx->block, sizeof(ctx->block) / sizeof(guint32)); |
159 | 0 | md4_transform(ctx->hash, ctx->block); |
160 | 0 | } |
161 | | |
162 | | static void |
163 | 0 | md4_init(PurpleCipherContext *context, gpointer extra) { |
164 | 0 | struct MD4_Context *mctx; |
165 | 0 | mctx = g_new0(struct MD4_Context, 1); |
166 | 0 | purple_cipher_context_set_data(context, mctx); |
167 | 0 | purple_cipher_context_reset(context, extra); |
168 | |
|
169 | 0 | mctx->hash[0] = 0x67452301; |
170 | 0 | mctx->hash[1] = 0xefcdab89; |
171 | 0 | mctx->hash[2] = 0x98badcfe; |
172 | 0 | mctx->hash[3] = 0x10325476; |
173 | 0 | mctx->byte_count = 0; |
174 | 0 | } |
175 | | |
176 | | static void |
177 | 0 | md4_reset(PurpleCipherContext *context, gpointer extra) { |
178 | 0 | struct MD4_Context *mctx; |
179 | |
|
180 | 0 | mctx = purple_cipher_context_get_data(context); |
181 | |
|
182 | 0 | mctx->hash[0] = 0x67452301; |
183 | 0 | mctx->hash[1] = 0xefcdab89; |
184 | 0 | mctx->hash[2] = 0x98badcfe; |
185 | 0 | mctx->hash[3] = 0x10325476; |
186 | 0 | mctx->byte_count = 0; |
187 | 0 | } |
188 | | |
189 | | static void |
190 | | md4_append(PurpleCipherContext *context, const guchar *data, size_t len) |
191 | 0 | { |
192 | 0 | struct MD4_Context *mctx = purple_cipher_context_get_data(context); |
193 | 0 | const guint32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f); |
194 | |
|
195 | 0 | mctx->byte_count += len; |
196 | |
|
197 | 0 | if (avail > len) { |
198 | 0 | memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), |
199 | 0 | data, len); |
200 | 0 | return; |
201 | 0 | } |
202 | | |
203 | 0 | memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), |
204 | 0 | data, avail); |
205 | |
|
206 | 0 | md4_transform_helper(mctx); |
207 | 0 | data += avail; |
208 | 0 | len -= avail; |
209 | |
|
210 | 0 | while (len >= sizeof(mctx->block)) { |
211 | 0 | memcpy(mctx->block, data, sizeof(mctx->block)); |
212 | 0 | md4_transform_helper(mctx); |
213 | 0 | data += sizeof(mctx->block); |
214 | 0 | len -= sizeof(mctx->block); |
215 | 0 | } |
216 | |
|
217 | 0 | memcpy(mctx->block, data, len); |
218 | 0 | } |
219 | | |
220 | | static gboolean |
221 | | md4_digest(PurpleCipherContext *context, size_t in_len, guchar *out, |
222 | | size_t *out_len) |
223 | 0 | { |
224 | 0 | struct MD4_Context *mctx = purple_cipher_context_get_data(context); |
225 | 0 | const unsigned int offset = mctx->byte_count & 0x3f; |
226 | 0 | char *p = (char *)mctx->block + offset; |
227 | 0 | int padding = 56 - (offset + 1); |
228 | | |
229 | |
|
230 | 0 | if(in_len<16) return FALSE; |
231 | 0 | if(out_len) *out_len = 16; |
232 | 0 | *p++ = 0x80; |
233 | 0 | if (padding < 0) { |
234 | 0 | memset(p, 0x00, padding + sizeof (guint64)); |
235 | 0 | md4_transform_helper(mctx); |
236 | 0 | p = (char *)mctx->block; |
237 | 0 | padding = 56; |
238 | 0 | } |
239 | |
|
240 | 0 | memset(p, 0, padding); |
241 | 0 | mctx->block[14] = mctx->byte_count << 3; |
242 | 0 | mctx->block[15] = mctx->byte_count >> 29; |
243 | 0 | le32_to_cpu_array(mctx->block, (sizeof(mctx->block) - |
244 | 0 | sizeof(guint64)) / sizeof(guint32)); |
245 | 0 | md4_transform(mctx->hash, mctx->block); |
246 | 0 | cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(guint32)); |
247 | 0 | memcpy(out, mctx->hash, sizeof(mctx->hash)); |
248 | 0 | memset(mctx, 0, sizeof(*mctx)); |
249 | 0 | return TRUE; |
250 | 0 | } |
251 | | |
252 | | static void |
253 | 0 | md4_uninit(PurpleCipherContext *context) { |
254 | 0 | struct MD4_Context *md4_context; |
255 | |
|
256 | 0 | purple_cipher_context_reset(context, NULL); |
257 | |
|
258 | 0 | md4_context = purple_cipher_context_get_data(context); |
259 | 0 | memset(md4_context, 0, sizeof(*md4_context)); |
260 | |
|
261 | 0 | g_free(md4_context); |
262 | 0 | md4_context = NULL; |
263 | 0 | } |
264 | | |
265 | | static size_t |
266 | | md4_get_block_size(PurpleCipherContext *context) |
267 | 0 | { |
268 | | /* This does not change (in this case) */ |
269 | 0 | return MD4_HMAC_BLOCK_SIZE; |
270 | 0 | } |
271 | | |
272 | | static PurpleCipherOps MD4Ops = { |
273 | | NULL, /* Set option */ |
274 | | NULL, /* Get option */ |
275 | | md4_init, /* init */ |
276 | | md4_reset, /* reset */ |
277 | | md4_uninit, /* uninit */ |
278 | | NULL, /* set iv */ |
279 | | md4_append, /* append */ |
280 | | md4_digest, /* digest */ |
281 | | NULL, /* encrypt */ |
282 | | NULL, /* decrypt */ |
283 | | NULL, /* set salt */ |
284 | | NULL, /* get salt size */ |
285 | | NULL, /* set key */ |
286 | | NULL, /* get key size */ |
287 | | NULL, /* set batch mode */ |
288 | | NULL, /* get batch mode */ |
289 | | md4_get_block_size, /* get block size */ |
290 | | NULL /* set key with len */ |
291 | | }; |
292 | | |
293 | | PurpleCipherOps * |
294 | 0 | purple_md4_cipher_get_ops(void) { |
295 | 0 | return &MD4Ops; |
296 | 0 | } |
297 | | |