红联Linux门户
Linux帮助

QQ_TEA算法如何实现 ?

发布时间:2010-07-23 02:16:06来源:红联作者:vfdff
[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, p = 1;
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( p <= 2 ) { // 初值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;
}[/code]对上面这段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的 整数为止。到此为止,结束了,这就是最后得到的要加密的明文内容
文章评论

共有 4 条评论

  1. vfdff 于 2010-08-21 01:26:10发表:

    QQ_TEA算法仅仅对标准的 TEA算法进行了一些调整,使适应输入长度不定的情况而已,本身不包括md5算法

  2. lemonade 于 2010-07-23 14:32:51发表:

    顶一下

  3. nxzcc 于 2010-07-23 08:28:29发表:

    直接看不懂

  4. vfdff 于 2010-07-23 02:22:24发表:

    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;

    }