CODE:
// in 实际数据,inlen实际数据长度,key密钥(16字节长度),out处理结果,outlen处理结果长度值
void encrypt_msg(unsigned char *in, int inlen, unsigned long *key, unsigned char *out, unsigned long *outlen) { register int m, i, j, count, [color=Red]p = 1[/color]; unsigned char q[12], *q1, *q2, *inp; unsigned char mkey[8]; m = (inlen+10)%8; // 为什么不用m = (inlen)%8; 来保证明文长度为 8 整数倍 ???? if (m) m = 8-m; q[0] = (rand()&0xf8) | m; // 加密结果不唯一的原因采用了随机数 i = j = 1; while(m>0) { q[i++] = rand()&0xff; m --; } count = *outlen = 0; // 传入的outlen值岂不没有用????? q2 = q1 = out; memset(mkey, 0, sizeof(mkey)); while( [color=Red]p <= 2[/color] ) { // 初值p = 1确定了,这个循环执行2次 if (i < 8) { q[i++] = rand()&0xff; p ++; } if (i == 8) { // 此时m=7, for (i = 0; i < 8; i ++) q[i] ^= mkey[i]; encrypt_qword((unsigned long *)q, key, (unsigned long *)out); // TEA 加密算法 for (i = 0; i < 8; i ++) q1[i] ^= mkey[i]; q2 = q1; q1 += 8; count += 8; memcpy(mkey, q, 8); j = i = 0; } } inp = in; while (inlen > 0) { if (i < 8) { q[i] = inp[0]; inp ++; i ++; inlen --; } if (i == 8) { for (i = 0; i < 8; i ++) { if (j) q[i] ^= mkey[i]; else q[i] ^= q2[i]; } j = 0; encrypt_qword((unsigned long *)q, key, (unsigned long *)q1); for (i = 0; i < 8; i ++) q1[i] ^= mkey[i]; count += 8; memcpy(mkey, q, 8); q2 = q1; q1 += 8; i = 0; } } p = 1; while (p < 8) { if (i < 8) { memset(q+i, 0, 4); p++; i++; } if (i == 8) { for (i = 0; i < 8; i ++) q[i] ^= q2[i]; encrypt_qword((unsigned long *)q, key, (unsigned long *)q1); for (i = 0; i < 8; i ++) q1[i] ^= mkey[i]; memcpy(mkey, q, 8); count += 8; q2 = q1; q1 += 8; i = 0; } } *outlen = count; } |
对上面这段QQ_TEA程序有点不明白,大家帮忙解释下
以下是网上对QQ_TEA的一点介绍,能对上吗?
QQ使用的TEA虽然是标准的TEA,但是QQ在使用这个算法的时候,由于需要加密不定长的数据,所以使用了一些常规的填充办法和交织算法(也就是说,把前一组的加密结果和后一组未加密的结果进行运算,产生新的结果)。QQ消 息被分为多个加密单元,每一个加密单元都是8字节,使用TEA进行加密,加密结果再作为下一个单元 的密钥。如果明文本身的长度不是8的倍数,那么还要进行填充,使其成为8的倍数。填充的时候会用一 个32位随机数存放于明文的开始位置,再在明文的最后用0填充为整个长度是8的 倍数。由于会向后反馈,这样即使对于相同的明文,因为使用了不同的随机数,也会产生完全不同的密文。使用这种特殊的填充反馈算 法所导致的结果就是,一段密文只能用加密它的密钥进行解密,如果使用不正确的密钥,就无法得到正确的填充结果。最常见的就是解密后得到的填充数值不是0, 这样就判断解密失败。
QQ消息的加密算法是一个16次的迭代过程,并且是反馈的,每一个加密单元是8字节,输出也是8字节,密钥是16字节
以prePlain表示前一个明文块,plain表示当前明文块,crypt表 示当前明文块加密得到的密文块,preCrypt表示前一个密文块
f表示加密算法, 那么从plain得到crypt的 过程是: crypt = f(plain ˆ preCrypt ) &circ
d表示解密算法,从crypt得到plain的过程 自然是 plain = d(crypt ˆ prePlain) ˆ
填充机制,其会在明文前和明文后分别填充一定的字节数,以保证明文长度是8字节的倍数填充的字节数与原始明 文长度有关,填充的方法是:
------- 消息填充算法 -----------
a = (明文长度 + 10) mod 8 //计算填充长度
if(a 不等于 0) a = 8 - a;
b = 随机数 & 0xF8 | a; //这个的作用是把a的值保存了下来
plain[0] = b; //然后把b做为明文的第0个字节,这样第0个 字节就保存了a的信息,这个信息在解密时就要用来找到真正明文的起始位置
plain[1 至 a+2] = 随机 数 & 0xFF; // 这里用随机数填充明文的第1到 第a+2个字节
plain[a+3 至 a+3+明文长度-1] = 明文; //从a+3字 节开始才是真正的明文
plain[a+3+明文长度, 最后] = 0; //在最后,填充0,填充到总长度为8的 整数为止。到此为止,结束了,这就是最后得到的要加密的明文内容
void EvaQQCrypt::Encrypt ( unsigned char* instr, int instrlen, unsigned char* key, unsigned char* outstr, int* outstrlen_prt) { unsigned char plain[8], /* plain text buffer*/ plain_pre_8[8], /* plain text buffer, previous 8 bytes*/ * crypted, /* crypted text*/ * crypted_pre_8, /* crypted test, previous 8 bytes*/ * inp; /* current position in instr*/ int pos_in_byte = 1, /* loop in the byte */ is_header=1, /* header is one byte*/ count=0, /* number of bytes being crypted*/ padding = 0; /* number of padding stuff*/ //void encrypt_every_8_byte (void); /*** we encrypt every eight byte ***/ #define encrypt_every_8_byte() / {/ for(pos_in_byte=0; pos_in_byte<8; pos_in_byte++) {/ if(is_header) { plain[pos_in_byte] ^= plain_pre_8[pos_in_byte]; }/ else { plain[pos_in_byte] ^= crypted_pre_8[pos_in_byte]; }/ } /* prepare plain text*// Tea_Encipher( (unsigned long *) plain,/ (unsigned long *) key, / (unsigned long *) crypted); /* encrypt it*// / for(pos_in_byte=0; pos_in_byte<8; pos_in_byte++) {/ crypted[pos_in_byte] ^= plain_pre_8[pos_in_byte]; / } / memcpy(plain_pre_8, plain, 8); /* prepare next*// / crypted_pre_8 = crypted; /* store position of previous 8 byte*// crypted += 8; /* prepare next output*// count += 8; /* outstrlen increase by 8*// pos_in_byte = 0; /* back to start*// is_header = 0; /* and exit header*// }/* encrypt_every_8_byte*/ pos_in_byte = (instrlen + 0x0a) % 8; /* header padding decided by instrlen*/ if (pos_in_byte) { pos_in_byte = 8 - pos_in_byte; } plain[0] = (rand() & 0xf8) | pos_in_byte; memset(plain+1, rand()&0xff, pos_in_byte++); memset(plain_pre_8, 0x00, sizeof(plain_pre_8)); crypted = crypted_pre_8 = outstr; padding = 1; /* pad some stuff in header*/ while (padding <= 2) { /* at most two byte */ if(pos_in_byte < 8) { plain[pos_in_byte++] = rand() & 0xff; padding ++; } if(pos_in_byte == 8){ encrypt_every_8_byte(); } } inp = instr; while (instrlen > 0) { if (pos_in_byte < 8) { plain[pos_in_byte++] = *(inp++); instrlen --; } if (pos_in_byte == 8){ encrypt_every_8_byte(); } } padding = 1; /* pad some stuff in tailer*/ while (padding <= 7) { /* at most sever byte*/ if (pos_in_byte < 8) { plain[pos_in_byte++] = 0x00; padding ++; } if (pos_in_byte == 8){ encrypt_every_8_byte(); } } *outstrlen_prt = count; } |
作者: nxzcc 发布日期: 2010-7-23 |
直接看不懂 |
作者: lemonade 发布日期: 2010-7-23 |
顶一下 |
作者: vfdff 发布日期: 2010-8-21 |
QQ_TEA算法仅仅对标准的 TEA算法进行了一些调整,使适应输入长度不定的情况而已,本身不包括md5算法 http://www.linuxdiyf.com/viewarticle.php?id=182915
个人为开发一些QQ应用程序,研究分析了QQ的通讯协议... 现遇到的麻烦就是所有的QQ通信均采用了TEA算法进行加必或解密,如果是公用的TEA算法,那么一下子就知道了.... QQ公司改TEA算法,用C语言实现如下:
#include <stdio.h> #include <string.h> #ifdef _WIN32 #include <winsock.h> #else #include <arpa/inet.h> #endif #include "qqcrypt.h" #include "debug.h" static int random(void) { /* it can be the real random seed function*/ return 0xdead; /* override with number, convenient for debug*/ }
static void encipher(unsigned int *const v, const unsigned int *const k, unsigned int *const w) { register unsigned int y = ntohl(v[0]), z = ntohl(v[1]), a = ntohl(k[0]), b = ntohl(k[1]), c = ntohl(k[2]), d = ntohl(k[3]), n = 0x10, /* do encrypt 16 (0x10) times */ sum = 0, delta = 0x9E3779B9; /* 0x9E3779B9 - 0x100000000 = -0x61C88647 */ while (n-- > 0) { sum += delta; y += ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b); z += ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d); } w[0] = htonl(y); w[1] = htonl(z); } static void decipher(unsigned int *const v, const unsigned int *const k, unsigned int *const w) { register unsigned int y = ntohl(v[0]), z = ntohl(v[1]), a = ntohl(k[0]), b = ntohl(k[1]), c = ntohl(k[2]), d = ntohl(k[3]), n = 0x10, sum = 0xE3779B90, /* why this ? must be related with n value*/ delta = 0x9E3779B9; /* sum = delta<<5, in general sum = delta * n */ while (n-- > 0) { z -= ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d); y -= ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b); sum -= delta; } w[0] = htonl(y); w[1] = htonl(z); } void qqencrypt( unsigned char* instr, int instrlen, unsigned char* key, unsigned char* outstr, int* outstrlen_ptr) { unsigned char plain[8], /* plain text buffer*/ plain_pre_8[8], /* plain text buffer, previous 8 bytes*/ * crypted, /* crypted text*/ * crypted_pre_8, /* crypted test, previous 8 bytes*/ * inp; /* current position in instr*/ int pos_in_byte = 1, /* loop in the byte */ is_header=1, /* header is one byte*/ count=0, /* number of bytes being crypted*/ padding = 0; /* number of padding stuff*/ //void encrypt_every_8_byte (void); /*** we encrypt every eight byte ***/ #define encrypt_every_8_byte() / {/ for(pos_in_byte=0; pos_in_byte<8; pos_in_byte++) {/ if(is_header) { plain[pos_in_byte] ^= plain_pre_8[pos_in_byte]; }/ else { plain[pos_in_byte] ^= crypted_pre_8[pos_in_byte]; }/ } /* prepare plain text*// encipher( (unsigned int *) plain,/ (unsigned int *) key, / (unsigned int *) crypted); /* encrypt it*// / for(pos_in_byte=0; pos_in_byte<8; pos_in_byte++) {/ crypted[pos_in_byte] ^= plain_pre_8[pos_in_byte]; / } / memcpy(plain_pre_8, plain, 8); /* prepare next*// / crypted_pre_8 = crypted; /* store position of previous 8 byte*// crypted += 8; /* prepare next output*// count += 8; /* outstrlen increase by 8*// pos_in_byte = 0; /* back to start*// is_header = 0; /* and exit header*// }/* encrypt_every_8_byte*/ pos_in_byte = (instrlen + 0x0a) % 8; /* header padding decided by instrlen*/ if (pos_in_byte) { pos_in_byte = 8 - pos_in_byte; } plain[0] = (random() & 0xf8) | pos_in_byte;
memset(plain+1, random()&0xff, pos_in_byte++); memset(plain_pre_8, 0x00, sizeof(plain_pre_8)); crypted = crypted_pre_8 = outstr; padding = 1; /* pad some stuff in header*/ while (padding <= 2) { /* at most two byte */ if(pos_in_byte < 8) { plain[pos_in_byte++] = random() & 0xff; padding ++; } if(pos_in_byte == 8){ encrypt_every_8_byte(); } } inp = instr; while (instrlen > 0) { if (pos_in_byte < 8) { plain[pos_in_byte++] = *(inp++); instrlen --; } if (pos_in_byte == 8){ encrypt_every_8_byte(); } } padding = 1; /* pad some stuff in tailer*/ while (padding <= 7) { /* at most sever byte*/ if (pos_in_byte < 8) { plain[pos_in_byte++] = 0x00; padding ++; } if (pos_in_byte == 8){ encrypt_every_8_byte(); } } *outstrlen_ptr = count; } int qqdecrypt( unsigned char* instr, int instrlen, unsigned char* key, unsigned char* outstr, int* outstrlen_ptr) { unsigned char decrypted[8], m[8], * crypt_buff, * crypt_buff_pre_8, * outp; int count, context_start, pos_in_byte, padding; #define decrypt_every_8_byte() {/ char bNeedRet = 0;/ for (pos_in_byte = 0; pos_in_byte < 8; pos_in_byte ++ ) {/ if (context_start + pos_in_byte >= instrlen) / {/ bNeedRet = 1;/ break;/ }/ decrypted[pos_in_byte] ^= crypt_buff[pos_in_byte];/ }/ if( !bNeedRet ) { / decipher( (unsigned int *) decrypted, / (unsigned int *) key, / (unsigned int *) decrypted);/ / context_start += 8;/ crypt_buff += 8;/ pos_in_byte = 0;/ }/ }/* decrypt_every_8_byte*/ /* at least 16 bytes and %8 == 0*/ if ((instrlen % 8) || (instrlen < 16)) return 0; /* get information from header*/ decipher( (unsigned int *) instr, (unsigned int *) key, (unsigned int *) decrypted); pos_in_byte = decrypted[0] & 0x7; count = instrlen - pos_in_byte - 10; /* this is the plaintext length*/ /* return if outstr buffer is not large enought or error plaintext length*/ if (*outstrlen_ptr < count || count < 0) return 0; memset(m, 0, 8); crypt_buff_pre_8 = m; *outstrlen_ptr = count; /* everything is ok! set return string length*/ crypt_buff = instr + 8; /* address of real data start */ context_start = 8; /* context is at the second 8 byte*/ pos_in_byte ++; /* start of paddng stuffv*/ padding = 1; /* at least one in header*/ while (padding <= 2) { /* there are 2 byte padding stuff in header*/ if (pos_in_byte < 8) { /* bypass the padding stuff, none sense data*/ pos_in_byte ++; padding ++; } if (pos_in_byte == 8) { crypt_buff_pre_8 = instr; //if (! decrypt_every_8_byte()) return 0; decrypt_every_8_byte(); } }/* while*/ outp = outstr; while(count !=0) { if (pos_in_byte < 8) { *outp = crypt_buff_pre_8[pos_in_byte] ^ decrypted[pos_in_byte]; outp ++; count --; pos_in_byte ++; } if (pos_in_byte == 8) { crypt_buff_pre_8 = crypt_buff - 8; //if (! decrypt_every_8_byte()) return 0; decrypt_every_8_byte(); } }/* while*/ for (padding = 1; padding < 8; padding ++) { if (pos_in_byte < 8) { if (crypt_buff_pre_8[pos_in_byte] ^ decrypted[pos_in_byte]) { return 0; } pos_in_byte ++; } if (pos_in_byte == 8 ) { crypt_buff_pre_8 = crypt_buff; //if (! decrypt_every_8_byte()) return 0; decrypt_every_8_byte(); } }/* for*/ return 1; }
|