TEA加密算法的C/C++实现

TEA(Tiny Encryption Algorithm) 是一种简单高效的加密算法,以加密解密速度快,实现简单著称。算法真的很简单,TEA算法每一次可以操作64-bit(8-byte),采用128-bit(16-byte)作为key,算法采用迭代的形式,推荐的迭代轮数是64轮,最少32轮。目前我只知道QQ一直用的是16轮TEA。没什么好说的,先给出C语言的源代码(默认是32轮):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void encrypt(unsigned long *v, unsigned long *k) {
    unsigned long y=v[0], z=v[1], sum=0, i;         /* set up */
    unsigned long delta=0x9e3779b9;                 /* a key schedule constant */
    unsigned long a=k[0], b=k[1], c=k[2], d=k[3];   /* cache key */
    for (i=0; i < 32; i++) {                        /* basic cycle start */
        sum += delta;
        y += ((z<<4) + a) ^ (z + sum) ^ ((z>>5) + b);
        z += ((y<<4) + c) ^ (y + sum) ^ ((y>>5) + d);/* end cycle */
    }
    v[0]=y;
    v[1]=z;
}

void decrypt(unsigned long *v, unsigned long *k) {
    unsigned long y=v[0], z=v[1], sum=0xC6EF3720, i;  /* set up */
    unsigned long delta=0x9e3779b9;                   /* a key schedule constant */
    unsigned long a=k[0], b=k[1], c=k[2], d=k[3];     /* cache key */
    for(i=0; i<32; i++) {                             /* basic cycle start */
        z -= ((y<<4) + c) ^ (y + sum) ^ ((y>>5) + d);
        y -= ((z<<4) + a) ^ (z + sum) ^ ((z>>5) + b);
        sum -= delta;                                 /* end cycle */
    }
    v[0]=y;
    v[1]=z;
}

C语言写的用起来当然不方便,没关系,用C++封装以下就OK了:

  • util.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

#ifndef UTIL_H
#define UTIL_H

#include <string>
#include <cmath>
#include <cstdlib>

typedef unsigned char byte;
typedef unsigned long ulong;10 
inline double logbase(double base, double x) {
    return log(x)/log(base);
}

/*
*convert int to hex char.
*example:10 -> 'A',15 -> 'F'
*/
char intToHexChar(int x);20 
/*
*convert hex char to int.
*example:'A' -> 10,'F' -> 15
*/
int hexCharToInt(char hex);26 
using std::string;
/*
*convert a byte array to hex string.
*hex string format example:"AF B0 80 7D"
*/
string bytesToHexString(const byte *in, size_t size);

/*
*convert a hex string to a byte array.
*hex string format example:"AF B0 80 7D"
*/
size_t hexStringToBytes(const string &str, byte *out);

#endif/*UTIL_H*/
  • util.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include "util.h"
#include <vector>

using namespace std;

char intToHexChar(int x) {
    static const char HEX[16] = {
        '0', '1', '2', '3',
        '4', '5', '6', '7',
        '8', '9', 'A', 'B',
        'C', 'D', 'E', 'F'
    };
    return HEX[x];
}

int hexCharToInt(char hex) {
    hex = toupper(hex);
    if (isdigit(hex))
        return (hex - '0');
    if (isalpha(hex))
        return (hex - 'A' + 10);
    return 0;
}

string bytesToHexString(const byte *in, size_t size) {
    string str;
    for (size_t i = 0; i < size; ++i) {
        int t = in[i];
        int a = t / 16;
        int b = t % 16;
        str.append(1, intToHexChar(a));
        str.append(1, intToHexChar(b));
        if (i != size - 1)
            str.append(1, ' ');
    }
    return str;
}

size_t hexStringToBytes(const string &str, byte *out) {
    vector<string> vec;
    string::size_type currPos = 0, prevPos = 0;
    while ((currPos = str.find(' ', prevPos)) != string::npos) {
        string b(str.substr(prevPos, currPos - prevPos));
        vec.push_back(b);
        prevPos = currPos + 1;
    }
    if (prevPos < str.size()) {
        string b(str.substr(prevPos));
        vec.push_back(b);
    }
    typedef vector<string>::size_type sz_type;
    sz_type size = vec.size();
    for (sz_type i = 0; i < size; ++i) {
        int a = hexCharToInt(vec[i][0]);
        int b = hexCharToInt(vec[i][1]);
        out[i] = a * 16 + b;
    }
    return size;
}
  • tea.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#ifndef TEA_H
#define TEA_H

/*
*for htonl,htonl
*do remember link "ws2_32.lib"
*/
#include <winsock2.h>
#include "util.h"

class TEA {
public:
    TEA(const byte *key, int round = 32, bool isNetByte = false);
    TEA(const TEA &rhs);
    TEA& operator=(const TEA &rhs);
    void encrypt(const byte *in, byte *out);
    void decrypt(const byte *in, byte *out);
private:
    void encrypt(const ulong *in, ulong *out);
    void decrypt(const ulong *in, ulong *out);
    ulong ntoh(ulong netlong) { return _isNetByte ? ntohl(netlong) : netlong; }
    ulong hton(ulong hostlong) { return _isNetByte ? htonl(hostlong) : hostlong; }
private:
    int _round; //iteration round to encrypt or decrypt
    bool _isNetByte; //whether input bytes come from network
    byte _key[16]; //encrypt or decrypt key
};

#endif/*TEA_H*/
  • tea.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include "tea.h"
#include <cstring> //for memcpy,memset

using namespace std;

TEA::TEA(const byte *key, int round /*= 32*/, bool isNetByte /*= false*/)
:_round(round)
,_isNetByte(isNetByte) {
    if (key != 0)
        memcpy(_key, key, 16);
    else
        memset(_key, 0, 16);
}

TEA::TEA(const TEA &rhs)
:_round(rhs._round)
,_isNetByte(rhs._isNetByte) {
    memcpy(_key, rhs._key, 16);
}

TEA& TEA::operator=(const TEA &rhs) {
    if (&rhs != this) {
        _round = rhs._round;
        _isNetByte = rhs._isNetByte;
        memcpy(_key, rhs._key, 16);
    }
    return *this;
}

void TEA::encrypt(const byte *in, byte *out) {
    encrypt((const ulong*)in, (ulong*)out);
}

void TEA::decrypt(const byte *in, byte *out) {
    decrypt((const ulong*)in, (ulong*)out);
}

void TEA::encrypt(const ulong *in, ulong *out) {

    ulong *k = (ulong*)_key;
    register ulong y = ntoh(in[0]);
    register ulong z = ntoh(in[1]);
    register ulong a = ntoh(k[0]);
    register ulong b = ntoh(k[1]);
    register ulong c = ntoh(k[2]);
    register ulong d = ntoh(k[3]);
    register ulong delta = 0x9E3779B9; /* (sqrt(5)-1)/2*2^32 */
    register int round = _round;
    register ulong sum = 0;

    while (round--) {    /* basic cycle start */
        sum += delta;
        y += ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b);
        z += ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d);
    }    /* end cycle */
    out[0] = ntoh(y);
    out[1] = ntoh(z);
}

void TEA::decrypt(const ulong *in, ulong *out) {

    ulong *k = (ulong*)_key;
    register ulong y = ntoh(in[0]);
    register ulong z = ntoh(in[1]);
    register ulong a = ntoh(k[0]);
    register ulong b = ntoh(k[1]);
    register ulong c = ntoh(k[2]);
    register ulong d = ntoh(k[3]);
    register ulong delta = 0x9E3779B9; /* (sqrt(5)-1)/2*2^32 */
    register int round = _round;
    register ulong sum = 0;

    if (round == 32)
        sum = 0xC6EF3720; /* delta << 5*/
    else if (round == 16)
        sum = 0xE3779B90; /* delta << 4*/
    else
        sum = delta << static_cast<int>(logbase(2, round));

    while (round--) {    /* basic cycle start */
        z -= ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d);
        y -= ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b);
        sum -= delta;
    }    /* end cycle */
    out[0] = ntoh(y);
    out[1] = ntoh(z);
}

需要说明的是TEA的构造函数: TEA(const byte *key, int round = 32, bool isNetByte = false); 1.key - 加密或解密用的128-bit(16byte)密钥。 2.round - 加密或解密的轮数,常用的有64,32,16。 3.isNetByte - 用来标记待处理的字节是不是来自网络,为true时在加密/解密前先要转换成本地字节,执行加密/解密,然后再转换回网络字节。偷偷告诉你,QQ就是这样做的!

最后当然少不了测试代码:

  • test.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "tea.h"
#include "util.h"
#include <iostream>

using namespace std;

int main() {
    const string plainStr("AD DE E2 DB B3 E2 DB B3");
    const string keyStr("3A DA 75 21 DB E2 DB B3 11 B4 49 01 A5 C6 EA D4");
    const int SIZE_IN = 8, SIZE_OUT = 8, SIZE_KEY = 16;
    byte plain[SIZE_IN], crypt[SIZE_OUT], key[SIZE_KEY];
    size_t size_in = hexStringToBytes(plainStr, plain);
    size_t size_key = hexStringToBytes(keyStr, key);
    if (size_in != SIZE_IN || size_key != SIZE_KEY)
        return -1;

    cout << "Plain: " << bytesToHexString(plain, size_in) << endl;
    cout << "Key  : " << bytesToHexString(key, size_key) << endl;
    TEA tea(key, 16, true);
    tea.encrypt(plain, crypt);
    cout << "Crypt: " << bytesToHexString(crypt, SIZE_OUT) << endl;
    tea.decrypt(crypt, plain);
    cout << "Plain: " << bytesToHexString(plain, SIZE_IN) << endl;
    return 0;
}

运行结果: Plain: AD DE E2 DB B3 E2 DB B3 Key : 3A DA 75 21 DB E2 DB B3 11 B4 49 01 A5 C6 EA D4 Crypt: 3B 3B 4D 8C 24 3A FD F2 Plain: AD DE E2 DB B3 E2 DB B3

源代码下载:

点击下载