comparison 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
comparison
equal deleted inserted replaced
14564:4c14862f7fcc 14565:ca943d7fb274
18 * You should have received a copy of the GNU General Public License 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 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 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * 21 *
22 * 22 *
23 * OICQ encryption algorithm 23 * QQ encryption algorithm
24 * Convert from ASM code provided by PerlOICQ 24 * Convert from ASM code provided by PerlOICQ
25 * 25 *
26 * Puzzlebird, Nov-Dec 2002 26 * Puzzlebird, Nov-Dec 2002
27 */ 27 */
28 28
29 /*Notes: (OICQ uses 0x10 iterations, and modified something...) 29 /*Notes: (QQ uses 16 rounds, and modified something...)
30 30
31 IN : 64 bits of data in v[0] - v[1]. 31 IN : 64 bits of data in v[0] - v[1].
32 OUT: 64 bits of data in w[0] - w[1]. 32 OUT: 64 bits of data in w[0] - w[1].
33 KEY: 128 bits of key in k[0] - k[3]. 33 KEY: 128 bits of key in k[0] - k[3].
34 34
51 51
52 /******************************************************************** 52 /********************************************************************
53 * encryption 53 * encryption
54 *******************************************************************/ 54 *******************************************************************/
55 55
56 /* Tiny Encryption Algorithm (TEA) */
56 static void qq_encipher(guint32 *const v, const guint32 *const k, guint32 *const w) 57 static void qq_encipher(guint32 *const v, const guint32 *const k, guint32 *const w)
57 { 58 {
58 register guint32 y = ntohl(v[0]), 59 register guint32 y = ntohl(v[0]),
59 z = ntohl(v[1]), 60 z = ntohl(v[1]),
60 a = ntohl(k[0]), 61 a = ntohl(k[0]),
77 78
78 static gint rand(void) { /* it can be the real random seed function */ 79 static gint rand(void) { /* it can be the real random seed function */
79 return 0xdead; 80 return 0xdead;
80 } /* override with number, convenient for debug */ 81 } /* override with number, convenient for debug */
81 82
82 /* we encrypt every eight byte chunk */ 83 /* 64-bit blocks and some kind of feedback mode of operation */
83 static void encrypt_every_8_byte(guint8 *plain, guint8 *plain_pre_8, guint8 **crypted, 84 static void encrypt_block(guint8 *plain, guint8 *plain_pre_8, guint8 **crypted,
84 guint8 **crypted_pre_8, const guint8 *const key, gint *count, 85 guint8 **crypted_pre_8, const guint8 *const key, gint *count,
85 gint *pos_in_byte, gint *is_header) 86 gint *pos_in_block, gint *is_header)
86 { 87 {
87 /* prepare plain text */ 88 /* prepare input text */
88 for (*pos_in_byte = 0; *pos_in_byte < 8; (*pos_in_byte)++) { 89 if (!*is_header)
89 if (*is_header) { 90 *(guint64 *) plain ^= **(guint64 **) crypted_pre_8;
90 plain[*pos_in_byte] ^= plain_pre_8[*pos_in_byte]; 91
91 } else {
92 plain[*pos_in_byte] ^= (*crypted_pre_8)[*pos_in_byte];
93 }
94 }
95 /* encrypt it */ 92 /* encrypt it */
96 qq_encipher((guint32 *) plain, (guint32 *) key, (guint32 *) *crypted); 93 qq_encipher((guint32 *) plain, (guint32 *) key, (guint32 *) *crypted);
97 94
98 for (*pos_in_byte = 0; *pos_in_byte < 8; (*pos_in_byte)++) { 95 **(guint64 **) crypted ^= *(guint64 *) plain_pre_8;
99 (*crypted)[*pos_in_byte] ^= plain_pre_8[*pos_in_byte]; 96
100 }
101 memcpy(plain_pre_8, plain, 8); /* prepare next */ 97 memcpy(plain_pre_8, plain, 8); /* prepare next */
102 98
103 *crypted_pre_8 = *crypted; /* store position of previous 8 byte */ 99 *crypted_pre_8 = *crypted; /* store position of previous 8 byte */
104 *crypted += 8; /* prepare next output */ 100 *crypted += 8; /* prepare next output */
105 *count += 8; /* outstrlen increase by 8 */ 101 *count += 8; /* outstrlen increase by 8 */
106 *pos_in_byte = 0; /* back to start */ 102 *pos_in_block = 0; /* back to start */
107 *is_header = 0; /* and exit header */ 103 *is_header = 0; /* and exit header */
108 } /* encrypt_every_8_byte */ 104 } /* encrypt_block */
109
110 105
111 static void qq_encrypt(const guint8 *const instr, gint instrlen, 106 static void qq_encrypt(const guint8 *const instr, gint instrlen,
112 const guint8 *const key, 107 const guint8 *const key,
113 guint8 *outstr, gint *outstrlen_prt) 108 guint8 *outstr, gint *outstrlen_ptr)
114 { 109 {
115 guint8 plain[8], /* plain text buffer */ 110 guint8 plain[8], /* plain text buffer */
116 plain_pre_8[8], /* plain text buffer, previous 8 bytes */ 111 plain_pre_8[8], /* plain text buffer, previous 8 bytes */
117 *crypted, /* crypted text */ 112 *crypted, /* crypted text */
118 *crypted_pre_8; /* crypted test, previous 8 bytes */ 113 *crypted_pre_8; /* crypted text, previous 8 bytes */
119 const guint8 *inp; /* current position in instr */ 114 const guint8 *inp; /* current position in instr */
120 gint pos_in_byte = 1, /* loop in the byte */ 115 gint pos_in_block = 1, /* loop in the byte */
121 is_header = 1, /* header is one byte */ 116 is_header = 1, /* header is one byte */
122 count = 0, /* number of bytes being crypted */ 117 count = 0, /* number of bytes being crypted */
123 padding = 0; /* number of padding stuff */ 118 padding = 0; /* number of padding stuff */
124 119
125 pos_in_byte = (instrlen + 0x0a) % 8; /* header padding decided by instrlen */ 120 pos_in_block = (instrlen + 0x0a) % 8; /* header padding decided by instrlen */
126 if (pos_in_byte) { 121 if (pos_in_block)
127 pos_in_byte = 8 - pos_in_byte; 122 pos_in_block = 8 - pos_in_block;
128 } 123
129 plain[0] = (rand() & 0xf8) | pos_in_byte; 124 /* initialization vector */
130 125 plain[0] = (rand() & 0xf8) | pos_in_block;
131 memset(plain + 1, rand() & 0xff, pos_in_byte++); 126 memset(plain + 1, rand() & 0xff, pos_in_block++);
127
132 memset(plain_pre_8, 0x00, sizeof(plain_pre_8)); 128 memset(plain_pre_8, 0x00, sizeof(plain_pre_8));
133 129
134 crypted = crypted_pre_8 = outstr; 130 crypted = crypted_pre_8 = outstr;
135 131
136 padding = 1; /* pad some stuff in header */ 132 padding = 1; /* pad some stuff in header */
137 while (padding <= 2) { /* at most two bytes */ 133 while (padding <= 2) { /* at most two bytes */
138 if (pos_in_byte < 8) { 134 if (pos_in_block < 8) {
139 plain[pos_in_byte++] = rand() & 0xff; 135 plain[pos_in_block++] = rand() & 0xff;
140 padding++; 136 padding++;
141 } 137 }
142 if (pos_in_byte == 8) { 138 if (pos_in_block == 8) {
143 encrypt_every_8_byte(plain, plain_pre_8, &crypted, &crypted_pre_8, 139 encrypt_block(plain, plain_pre_8, &crypted, &crypted_pre_8,
144 key, &count, &pos_in_byte, &is_header); 140 key, &count, &pos_in_block, &is_header);
145 } 141 }
146 } 142 }
147 143
148 inp = instr; 144 inp = instr;
149 while (instrlen > 0) { 145 while (instrlen > 0) {
150 if (pos_in_byte < 8) { 146 if (pos_in_block < 8) {
151 plain[pos_in_byte++] = *(inp++); 147 plain[pos_in_block++] = *(inp++);
152 instrlen--; 148 instrlen--;
153 } 149 }
154 if (pos_in_byte == 8) { 150 if (pos_in_block == 8) {
155 encrypt_every_8_byte(plain, plain_pre_8, &crypted, &crypted_pre_8, 151 encrypt_block(plain, plain_pre_8, &crypted, &crypted_pre_8,
156 key, &count, &pos_in_byte, &is_header); 152 key, &count, &pos_in_block, &is_header);
157 } 153 }
158 } 154 }
159 155
160 padding = 1; /* pad some stuff in tail */ 156 padding = 1; /* pad some stuff in tail */
161 while (padding <= 7) { /* at most seven bytes */ 157 while (padding <= 7) { /* at most seven bytes */
162 if (pos_in_byte < 8) { 158 if (pos_in_block < 8) {
163 plain[pos_in_byte++] = 0x00; 159 plain[pos_in_block++] = 0x00;
164 padding++; 160 padding++;
165 } 161 }
166 if (pos_in_byte == 8) { 162 if (pos_in_block == 8) {
167 encrypt_every_8_byte(plain, plain_pre_8, &crypted, &crypted_pre_8, 163 encrypt_block(plain, plain_pre_8, &crypted, &crypted_pre_8,
168 key, &count, &pos_in_byte, &is_header); 164 key, &count, &pos_in_block, &is_header);
169 } 165 }
170 } 166 }
171 167
172 *outstrlen_prt = count; 168 *outstrlen_ptr = count;
173 } 169 }
174 170
175 171
176 /******************************************************************** 172 /********************************************************************
177 * decryption 173 * decryption
198 194
199 w[0] = htonl(y); 195 w[0] = htonl(y);
200 w[1] = htonl(z); 196 w[1] = htonl(z);
201 } 197 }
202 198
203 static gint decrypt_every_8_byte(const guint8 **crypt_buff, const gint instrlen, 199 static gint decrypt_block(const guint8 **crypt_buff, const gint instrlen,
204 const guint8 *const key, gint *context_start, 200 const guint8 *const key, gint *context_start,
205 guint8 *decrypted, gint *pos_in_byte) 201 guint8 *decrypted, gint *pos_in_block)
206 { 202 {
207 for (*pos_in_byte = 0; *pos_in_byte < 8; (*pos_in_byte)++) { 203 if (*context_start == instrlen)
208 if (*context_start + *pos_in_byte >= instrlen) 204 return 1;
209 return 1; 205
210 decrypted[*pos_in_byte] ^= (*crypt_buff)[*pos_in_byte]; 206 *(guint64 *) decrypted ^= **(guint64 **) crypt_buff;
211 } 207
212 qq_decipher((guint32 *) decrypted, (guint32 *) key, (guint32 *) decrypted); 208 qq_decipher((guint32 *) decrypted, (guint32 *) key, (guint32 *) decrypted);
213 209
214 *context_start += 8; 210 *context_start += 8;
215 *crypt_buff += 8; 211 *crypt_buff += 8;
216 *pos_in_byte = 0; 212 *pos_in_block = 0;
217 213
218 return 1; 214 return 1;
219 } 215 }
220 216
221 /* return 0 if failed, 1 otherwise */ 217 /* return 0 if failed, 1 otherwise */
223 const guint8 *const key, 219 const guint8 *const key,
224 guint8 *outstr, gint *outstrlen_ptr) 220 guint8 *outstr, gint *outstrlen_ptr)
225 { 221 {
226 guint8 decrypted[8], m[8], *outp; 222 guint8 decrypted[8], m[8], *outp;
227 const guint8 *crypt_buff, *crypt_buff_pre_8; 223 const guint8 *crypt_buff, *crypt_buff_pre_8;
228 gint count, context_start, pos_in_byte, padding; 224 gint count, context_start, pos_in_block, padding;
229 225
230 /* at least 16 bytes and %8 == 0 */ 226 /* at least 16 bytes and %8 == 0 */
231 if ((instrlen % 8) || (instrlen < 16)) { 227 if ((instrlen % 8) || (instrlen < 16)) {
232 gaim_debug(GAIM_DEBUG_ERROR, "QQ", 228 gaim_debug(GAIM_DEBUG_ERROR, "QQ",
233 "Packet len is either too short or not a multiple of 8 bytes, read %d bytes\n", 229 "Ciphertext len is either too short or not a multiple of 8 bytes, read %d bytes\n",
234 instrlen); 230 instrlen);
235 return 0; 231 return 0;
236 } 232 }
237 /* get information from header */ 233 /* get information from header */
238 qq_decipher((guint32 *) instr, (guint32 *) key, (guint32 *) decrypted); 234 qq_decipher((guint32 *) instr, (guint32 *) key, (guint32 *) decrypted);
239 pos_in_byte = decrypted[0] & 0x7; 235 pos_in_block = decrypted[0] & 0x7;
240 count = instrlen - pos_in_byte - 10; /* this is the plaintext length */ 236 count = instrlen - pos_in_block - 10; /* this is the plaintext length */
241 /* return if outstr buffer is not large enough or error plaintext length */ 237 /* return if outstr buffer is not large enough or error plaintext length */
242 if (*outstrlen_ptr < count || count < 0) { 238 if (*outstrlen_ptr < count || count < 0) {
243 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Buffer len %d is less than real len %d", 239 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Buffer len %d is less than real len %d",
244 *outstrlen_ptr, count); 240 *outstrlen_ptr, count);
245 return 0; 241 return 0;
249 crypt_buff_pre_8 = m; 245 crypt_buff_pre_8 = m;
250 *outstrlen_ptr = count; /* everything is ok! set return string length */ 246 *outstrlen_ptr = count; /* everything is ok! set return string length */
251 247
252 crypt_buff = instr + 8; /* address of real data start */ 248 crypt_buff = instr + 8; /* address of real data start */
253 context_start = 8; /* context is at the second block of 8 bytes */ 249 context_start = 8; /* context is at the second block of 8 bytes */
254 pos_in_byte++; /* start of paddng stuff */ 250 pos_in_block++; /* start of paddng stuff */
255 251
256 padding = 1; /* at least one in header */ 252 padding = 1; /* at least one in header */
257 while (padding <= 2) { /* there are 2 byte padding stuff in header */ 253 while (padding <= 2) { /* there are 2 byte padding stuff in header */
258 if (pos_in_byte < 8) { /* bypass the padding stuff, it's nonsense data */ 254 if (pos_in_block < 8) { /* bypass the padding stuff, it's nonsense data */
259 pos_in_byte++; 255 pos_in_block++;
260 padding++; 256 padding++;
261 } 257 }
262 if (pos_in_byte == 8) { 258 if (pos_in_block == 8) {
263 crypt_buff_pre_8 = instr; 259 crypt_buff_pre_8 = instr;
264 if (!decrypt_every_8_byte(&crypt_buff, instrlen, key, 260 if (!decrypt_block(&crypt_buff, instrlen, key,
265 &context_start, decrypted, &pos_in_byte)) { 261 &context_start, decrypted, &pos_in_block)) {
266 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "decrypt every 8 bytes error A"); 262 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "decrypt every 8 bytes error A");
267 return 0; 263 return 0;
268 } 264 }
269 } 265 }
270 } 266 }
271 267
272 outp = outstr; 268 outp = outstr;
273 while (count != 0) { 269 while (count != 0) {
274 if (pos_in_byte < 8) { 270 if (pos_in_block < 8) {
275 *outp = crypt_buff_pre_8[pos_in_byte] ^ decrypted[pos_in_byte]; 271 *outp = crypt_buff_pre_8[pos_in_block] ^ decrypted[pos_in_block];
276 outp++; 272 outp++;
277 count--; 273 count--;
278 pos_in_byte++; 274 pos_in_block++;
279 } 275 }
280 if (pos_in_byte == 8) { 276 if (pos_in_block == 8) {
281 crypt_buff_pre_8 = crypt_buff - 8; 277 crypt_buff_pre_8 = crypt_buff - 8;
282 if (!decrypt_every_8_byte(&crypt_buff, instrlen, key, 278 if (!decrypt_block(&crypt_buff, instrlen, key,
283 &context_start, decrypted, &pos_in_byte)) { 279 &context_start, decrypted, &pos_in_block)) {
284 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "decrypt every 8 bytes error B"); 280 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "decrypt every 8 bytes error B");
285 return 0; 281 return 0;
286 } 282 }
287 } 283 }
288 } 284 }
289 285
290 for (padding = 1; padding < 8; padding++) { 286 for (padding = 1; padding < 8; padding++) {
291 if (pos_in_byte < 8) { 287 if (pos_in_block < 8) {
292 if (crypt_buff_pre_8[pos_in_byte] ^ decrypted[pos_in_byte]) 288 if (crypt_buff_pre_8[pos_in_block] ^ decrypted[pos_in_block])
293 return 0; 289 return 0;
294 pos_in_byte++; 290 pos_in_block++;
295 } 291 }
296 if (pos_in_byte == 8) { 292 if (pos_in_block == 8) {
297 crypt_buff_pre_8 = crypt_buff; 293 crypt_buff_pre_8 = crypt_buff;
298 if (!decrypt_every_8_byte(&crypt_buff, instrlen, key, 294 if (!decrypt_block(&crypt_buff, instrlen, key,
299 &context_start, decrypted, &pos_in_byte)) { 295 &context_start, decrypted, &pos_in_block)) {
300 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "decrypt every 8 bytes error C"); 296 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "decrypt every 8 bytes error C");
301 return 0; 297 return 0;
302 } 298 }
303 } 299 }
304 } 300 }