Mercurial > pidgin.yaz
annotate libgaim/protocols/qq/crypt.c @ 14565:ca943d7fb274
[gaim-migrate @ 17289]
This is easier to read and slightly more efficient.
committer: Tailor Script <tailor@pidgin.im>
author | Mark Huetsch <markhuetsch> |
---|---|
date | Sat, 16 Sep 2006 20:16:47 +0000 |
parents | 516ef76d6430 |
children | 473b225e7352 |
rev | line source |
---|---|
14192 | 1 /** |
2 * The QQ2003C protocol plugin | |
3 * | |
4 * for gaim | |
5 * | |
6 * Copyright (C) 2004 Puzzlebird | |
7 * | |
8 * This program is free software; you can redistribute it and/or modify | |
9 * it under the terms of the GNU General Public License as published by | |
10 * the Free Software Foundation; either version 2 of the License, or | |
11 * (at your option) any later version. | |
12 * | |
13 * This program is distributed in the hope that it will be useful, | |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 * GNU General Public License for more details. | |
17 * | |
18 * You should have received a copy of the GNU General Public License | |
19 * along with this program; if not, write to the Free Software | |
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 * | |
22 * | |
14565 | 23 * QQ encryption algorithm |
14192 | 24 * Convert from ASM code provided by PerlOICQ |
25 * | |
26 * Puzzlebird, Nov-Dec 2002 | |
27 */ | |
28 | |
14565 | 29 /*Notes: (QQ uses 16 rounds, and modified something...) |
14192 | 30 |
31 IN : 64 bits of data in v[0] - v[1]. | |
32 OUT: 64 bits of data in w[0] - w[1]. | |
33 KEY: 128 bits of key in k[0] - k[3]. | |
34 | |
35 delta is chosen to be the real part of | |
36 the golden ratio: Sqrt(5/4) - 1/2 ~ 0.618034 multiplied by 2^32. | |
37 | |
38 0x61C88647 is what we can track on the ASM codes.!! | |
39 */ | |
40 | |
14554
516ef76d6430
[gaim-migrate @ 17277]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14236
diff
changeset
|
41 #ifdef _WIN32 |
516ef76d6430
[gaim-migrate @ 17277]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14236
diff
changeset
|
42 #include "win32dep.h" |
516ef76d6430
[gaim-migrate @ 17277]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14236
diff
changeset
|
43 #else |
14192 | 44 #include <arpa/inet.h> |
14554
516ef76d6430
[gaim-migrate @ 17277]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14236
diff
changeset
|
45 #endif |
14192 | 46 |
47 #include <string.h> | |
48 | |
49 #include "crypt.h" | |
50 #include "debug.h" | |
51 | |
52 /******************************************************************** | |
53 * encryption | |
54 *******************************************************************/ | |
55 | |
14565 | 56 /* Tiny Encryption Algorithm (TEA) */ |
14236 | 57 static void qq_encipher(guint32 *const v, const guint32 *const k, guint32 *const w) |
14192 | 58 { |
14236 | 59 register guint32 y = ntohl(v[0]), |
14192 | 60 z = ntohl(v[1]), |
61 a = ntohl(k[0]), | |
62 b = ntohl(k[1]), | |
63 c = ntohl(k[2]), | |
64 d = ntohl(k[3]), | |
65 n = 0x10, | |
66 sum = 0, | |
67 delta = 0x9E3779B9; /* 0x9E3779B9 - 0x100000000 = -0x61C88647 */ | |
68 | |
69 while (n-- > 0) { | |
70 sum += delta; | |
71 y += ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b); | |
72 z += ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d); | |
73 } | |
74 | |
75 w[0] = htonl(y); | |
76 w[1] = htonl(z); | |
77 } | |
78 | |
14236 | 79 static gint rand(void) { /* it can be the real random seed function */ |
14192 | 80 return 0xdead; |
81 } /* override with number, convenient for debug */ | |
82 | |
14565 | 83 /* 64-bit blocks and some kind of feedback mode of operation */ |
84 static void encrypt_block(guint8 *plain, guint8 *plain_pre_8, guint8 **crypted, | |
14236 | 85 guint8 **crypted_pre_8, const guint8 *const key, gint *count, |
14565 | 86 gint *pos_in_block, gint *is_header) |
14192 | 87 { |
14565 | 88 /* prepare input text */ |
89 if (!*is_header) | |
90 *(guint64 *) plain ^= **(guint64 **) crypted_pre_8; | |
91 | |
14192 | 92 /* encrypt it */ |
14236 | 93 qq_encipher((guint32 *) plain, (guint32 *) key, (guint32 *) *crypted); |
14192 | 94 |
14565 | 95 **(guint64 **) crypted ^= *(guint64 *) plain_pre_8; |
96 | |
14192 | 97 memcpy(plain_pre_8, plain, 8); /* prepare next */ |
98 | |
99 *crypted_pre_8 = *crypted; /* store position of previous 8 byte */ | |
100 *crypted += 8; /* prepare next output */ | |
101 *count += 8; /* outstrlen increase by 8 */ | |
14565 | 102 *pos_in_block = 0; /* back to start */ |
14192 | 103 *is_header = 0; /* and exit header */ |
14565 | 104 } /* encrypt_block */ |
14192 | 105 |
14236 | 106 static void qq_encrypt(const guint8 *const instr, gint instrlen, |
107 const guint8 *const key, | |
14565 | 108 guint8 *outstr, gint *outstrlen_ptr) |
14192 | 109 { |
14236 | 110 guint8 plain[8], /* plain text buffer */ |
14192 | 111 plain_pre_8[8], /* plain text buffer, previous 8 bytes */ |
112 *crypted, /* crypted text */ | |
14565 | 113 *crypted_pre_8; /* crypted text, previous 8 bytes */ |
14236 | 114 const guint8 *inp; /* current position in instr */ |
14565 | 115 gint pos_in_block = 1, /* loop in the byte */ |
14192 | 116 is_header = 1, /* header is one byte */ |
117 count = 0, /* number of bytes being crypted */ | |
118 padding = 0; /* number of padding stuff */ | |
119 | |
14565 | 120 pos_in_block = (instrlen + 0x0a) % 8; /* header padding decided by instrlen */ |
121 if (pos_in_block) | |
122 pos_in_block = 8 - pos_in_block; | |
14192 | 123 |
14565 | 124 /* initialization vector */ |
125 plain[0] = (rand() & 0xf8) | pos_in_block; | |
126 memset(plain + 1, rand() & 0xff, pos_in_block++); | |
127 | |
14192 | 128 memset(plain_pre_8, 0x00, sizeof(plain_pre_8)); |
129 | |
130 crypted = crypted_pre_8 = outstr; | |
131 | |
132 padding = 1; /* pad some stuff in header */ | |
133 while (padding <= 2) { /* at most two bytes */ | |
14565 | 134 if (pos_in_block < 8) { |
135 plain[pos_in_block++] = rand() & 0xff; | |
14192 | 136 padding++; |
137 } | |
14565 | 138 if (pos_in_block == 8) { |
139 encrypt_block(plain, plain_pre_8, &crypted, &crypted_pre_8, | |
140 key, &count, &pos_in_block, &is_header); | |
14192 | 141 } |
142 } | |
143 | |
144 inp = instr; | |
145 while (instrlen > 0) { | |
14565 | 146 if (pos_in_block < 8) { |
147 plain[pos_in_block++] = *(inp++); | |
14192 | 148 instrlen--; |
149 } | |
14565 | 150 if (pos_in_block == 8) { |
151 encrypt_block(plain, plain_pre_8, &crypted, &crypted_pre_8, | |
152 key, &count, &pos_in_block, &is_header); | |
14192 | 153 } |
154 } | |
155 | |
156 padding = 1; /* pad some stuff in tail */ | |
157 while (padding <= 7) { /* at most seven bytes */ | |
14565 | 158 if (pos_in_block < 8) { |
159 plain[pos_in_block++] = 0x00; | |
14192 | 160 padding++; |
161 } | |
14565 | 162 if (pos_in_block == 8) { |
163 encrypt_block(plain, plain_pre_8, &crypted, &crypted_pre_8, | |
164 key, &count, &pos_in_block, &is_header); | |
14192 | 165 } |
166 } | |
167 | |
14565 | 168 *outstrlen_ptr = count; |
14192 | 169 } |
170 | |
171 | |
172 /******************************************************************** | |
173 * decryption | |
174 ********************************************************************/ | |
175 | |
14236 | 176 static void qq_decipher(guint32 *const v, const guint32 *const k, guint32 *const w) |
14192 | 177 { |
14236 | 178 register guint32 y = ntohl(v[0]), |
14192 | 179 z = ntohl(v[1]), |
180 a = ntohl(k[0]), | |
181 b = ntohl(k[1]), | |
182 c = ntohl(k[2]), | |
183 d = ntohl(k[3]), | |
184 n = 0x10, | |
185 sum = 0xE3779B90, /* why this ? must be related with n value */ | |
186 delta = 0x9E3779B9; | |
187 | |
188 /* sum = delta<<5, in general sum = delta * n */ | |
189 while (n-- > 0) { | |
190 z -= ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d); | |
191 y -= ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b); | |
192 sum -= delta; | |
193 } | |
194 | |
195 w[0] = htonl(y); | |
196 w[1] = htonl(z); | |
197 } | |
198 | |
14565 | 199 static gint decrypt_block(const guint8 **crypt_buff, const gint instrlen, |
14236 | 200 const guint8 *const key, gint *context_start, |
14565 | 201 guint8 *decrypted, gint *pos_in_block) |
14192 | 202 { |
14565 | 203 if (*context_start == instrlen) |
204 return 1; | |
205 | |
206 *(guint64 *) decrypted ^= **(guint64 **) crypt_buff; | |
207 | |
14236 | 208 qq_decipher((guint32 *) decrypted, (guint32 *) key, (guint32 *) decrypted); |
14192 | 209 |
210 *context_start += 8; | |
211 *crypt_buff += 8; | |
14565 | 212 *pos_in_block = 0; |
14192 | 213 |
214 return 1; | |
215 } | |
216 | |
217 /* return 0 if failed, 1 otherwise */ | |
14236 | 218 static gint qq_decrypt(const guint8 *const instr, gint instrlen, |
219 const guint8 *const key, | |
220 guint8 *outstr, gint *outstrlen_ptr) | |
14192 | 221 { |
14236 | 222 guint8 decrypted[8], m[8], *outp; |
223 const guint8 *crypt_buff, *crypt_buff_pre_8; | |
14565 | 224 gint count, context_start, pos_in_block, padding; |
14192 | 225 |
226 /* at least 16 bytes and %8 == 0 */ | |
227 if ((instrlen % 8) || (instrlen < 16)) { | |
228 gaim_debug(GAIM_DEBUG_ERROR, "QQ", | |
14565 | 229 "Ciphertext len is either too short or not a multiple of 8 bytes, read %d bytes\n", |
14236 | 230 instrlen); |
14192 | 231 return 0; |
232 } | |
233 /* get information from header */ | |
14236 | 234 qq_decipher((guint32 *) instr, (guint32 *) key, (guint32 *) decrypted); |
14565 | 235 pos_in_block = decrypted[0] & 0x7; |
236 count = instrlen - pos_in_block - 10; /* this is the plaintext length */ | |
14192 | 237 /* return if outstr buffer is not large enough or error plaintext length */ |
238 if (*outstrlen_ptr < count || count < 0) { | |
14236 | 239 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Buffer len %d is less than real len %d", |
240 *outstrlen_ptr, count); | |
14192 | 241 return 0; |
242 } | |
243 | |
244 memset(m, 0, 8); | |
245 crypt_buff_pre_8 = m; | |
246 *outstrlen_ptr = count; /* everything is ok! set return string length */ | |
247 | |
248 crypt_buff = instr + 8; /* address of real data start */ | |
249 context_start = 8; /* context is at the second block of 8 bytes */ | |
14565 | 250 pos_in_block++; /* start of paddng stuff */ |
14192 | 251 |
252 padding = 1; /* at least one in header */ | |
253 while (padding <= 2) { /* there are 2 byte padding stuff in header */ | |
14565 | 254 if (pos_in_block < 8) { /* bypass the padding stuff, it's nonsense data */ |
255 pos_in_block++; | |
14192 | 256 padding++; |
257 } | |
14565 | 258 if (pos_in_block == 8) { |
14192 | 259 crypt_buff_pre_8 = instr; |
14565 | 260 if (!decrypt_block(&crypt_buff, instrlen, key, |
261 &context_start, decrypted, &pos_in_block)) { | |
14192 | 262 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "decrypt every 8 bytes error A"); |
263 return 0; | |
264 } | |
265 } | |
266 } | |
267 | |
268 outp = outstr; | |
269 while (count != 0) { | |
14565 | 270 if (pos_in_block < 8) { |
271 *outp = crypt_buff_pre_8[pos_in_block] ^ decrypted[pos_in_block]; | |
14192 | 272 outp++; |
273 count--; | |
14565 | 274 pos_in_block++; |
14192 | 275 } |
14565 | 276 if (pos_in_block == 8) { |
14192 | 277 crypt_buff_pre_8 = crypt_buff - 8; |
14565 | 278 if (!decrypt_block(&crypt_buff, instrlen, key, |
279 &context_start, decrypted, &pos_in_block)) { | |
14192 | 280 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "decrypt every 8 bytes error B"); |
281 return 0; | |
282 } | |
283 } | |
284 } | |
285 | |
286 for (padding = 1; padding < 8; padding++) { | |
14565 | 287 if (pos_in_block < 8) { |
288 if (crypt_buff_pre_8[pos_in_block] ^ decrypted[pos_in_block]) | |
14192 | 289 return 0; |
14565 | 290 pos_in_block++; |
14192 | 291 } |
14565 | 292 if (pos_in_block == 8) { |
14192 | 293 crypt_buff_pre_8 = crypt_buff; |
14565 | 294 if (!decrypt_block(&crypt_buff, instrlen, key, |
295 &context_start, decrypted, &pos_in_block)) { | |
14192 | 296 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "decrypt every 8 bytes error C"); |
297 return 0; | |
298 } | |
299 } | |
300 } | |
301 return 1; | |
302 } | |
303 | |
304 /* return 1 is succeed, otherwise return 0 */ | |
14236 | 305 gint qq_crypt(gint flag, |
306 const guint8 *const instr, gint instrlen, | |
307 const guint8 *const key, | |
308 guint8 *outstr, gint *outstrlen_ptr) | |
14192 | 309 { |
310 if (flag == DECRYPT) | |
311 return qq_decrypt(instr, instrlen, key, outstr, outstrlen_ptr); | |
312 else if (flag == ENCRYPT) | |
313 qq_encrypt(instr, instrlen, key, outstr, outstrlen_ptr); | |
314 else | |
315 return 0; | |
316 | |
317 return 1; | |
318 } |