zoukankan      html  css  js  c++  java
  • TEA加密算法的C/C++实现

     
    TEA(Tiny Encryption Algorithm) 是一种简单高效的加密算法,以加密解密速度快,实现简单著称。算法真的很简单,TEA算法每一次可以操作64-bit(8-byte),采用128-bit(16-byte)作为key,算法采用迭代的形式,推荐的迭代轮数是64轮,最少32轮。目前我只知道QQ一直用的是16轮TEA。没什么好说的,先给出C语言的源代码(默认是32轮):
     
    微型加密算法(TEA)及其相关变种(XTEA,Block TEA,XXTEA) 都是分组加密算法,它们很容易被描述,实现也很简单(典型的几行代码)。
     
    TEA 算法最初是由剑桥计算机实验室的 David Wheeler 和 Roger Needham 在 1994 年设计的。该算法使用 128 位的密钥为 64 位的信息块进行加密,它需要进行 64 轮迭代,尽管作者认为 32 轮已经足够了。该算法使用了一个神秘常数δ作为倍数,它来源于黄金比率,以保证每一轮加密都不相同。但δ的精确值似乎并不重要,这里 TEA 把它定义为 δ=「(√5 - 1)231」(也就是程序中的 0×9E3779B9)。

    之后 TEA 算法被发现存在缺陷,作为回应,设计者提出了一个 TEA 的升级版本——XTEA(有时也被称为“tean”)。XTEA 跟 TEA 使用了相同的简单运算,但它采用了截然不同的顺序,为了阻止密钥表攻击,四个子密钥(在加密过程中,原 128 位的密钥被拆分为 4 个 32 位的子密钥)采用了一种不太正规的方式进行混合,但速度更慢了。

    在跟描述 XTEA 算法的同一份报告中,还介绍了另外一种被称为 Block TEA 算法的变种,它可以对 32 位大小任意倍数的变量块进行操作。该算法将 XTEA 轮循函数依次应用于块中的每个字,并且将它附加于它的邻字。该操作重复多少轮依赖于块的大小,但至少需要 6 轮。该方法的优势在于它无需操作模式(CBC,OFB,CFB 等),密钥可直接用于信息。对于长的信息它可能比 XTEA 更有效率。

    在 1998 年,Markku-Juhani Saarinen 给出了一个可有效攻击 Block TEA 算法的代码,但之后很快 David J. Wheeler 和 Roger M. Needham 就给出了 Block TEA 算法的修订版,这个算法被称为 XXTEA。XXTEA 使用跟 Block TEA 相似的结构,但在处理块中每个字时利用了相邻字。它利用一个更复杂的 MX 函数代替了 XTEA 轮循函数,MX 使用 2 个输入量。
     
     1 void encrypt(unsigned long *v, unsigned long *k) {
     2     unsigned long y=v[0], z=v[1], sum=0, i;         /* set up */

     3     unsigned long delta=0x9e3779b9;                 /* a key schedule constant */
     4     unsigned long a=k[0], b=k[1], c=k[2], d=k[3];   /* cache key */
     5     for (i=0; i < 32; i++) {                        /* basic cycle start */
     6         sum += delta;
     7         y += ((z<<4) + a) ^ (z + sum) ^ ((z>>5) +
     b);
     8         z += ((y<<4) + c) ^ (y + sum) ^ ((y>>5) + d);/* end cycle */

     9     }
    10     v[0]=
    y;
    11     v[1]=
    z;
    12 
    }
    13 

    14 void decrypt(unsigned long *v, unsigned long *k) {
    15     unsigned long y=v[0], z=v[1], sum=0xC6EF3720, i; /* set up */

    16     unsigned long delta=0x9e3779b9;                  /* a key schedule constant */
    17     unsigned long a=k[0], b=k[1], c=k[2], d=k[3];    /* cache key */
    18     for(i=0; i<32; i++) {                            /* basic cycle start */
    19         z -= ((y<<4) + c) ^ (y + sum) ^ ((y>>5) + d);
    20         y -= ((z<<4) + a) ^ (z + sum) ^ ((z>>5) +
     b);
    21         sum -= delta;                                /* end cycle */

    22     }
    23     v[0]=
    y;
    24     v[1]=
    z;
    25 }

    C语言写的用起来当然不方便,没关系,用C++封装以下就OK了:
    util.h
     1 #ifndef UTIL_H
     2 
    #define UTIL_H
     3 

     4 #include <string>
     5 #include <cmath>
     6 #include <cstdlib>
     7 
     8 typedef unsigned char byte;
     9 typedef unsigned long
     ulong;
    10 

    11 inline double logbase(double base, double x) {
    12     return log(x)/
    log(base);
    13 
    }
    14 

    15 /*
    16 *convert int to hex char.
    17 
    *example:10 -> 'A',15 -> 'F'
    18 */

    19 char intToHexChar(int x);
    20 

    21 /*
    22 *convert hex char to int.
    23 
    *example:'A' -> 10,'F' -> 15
    24 */

    25 int hexCharToInt(char hex);
    26 

    27 using std::string;
    28 /*

    29 *convert a byte array to hex string.
    30 
    *hex string format example:"AF B0 80 7D"
    31 */

    32 string bytesToHexString(const byte *in, size_t size);
    33 

    34 /*
    35 *convert a hex string to a byte array.
    36 
    *hex string format example:"AF B0 80 7D"
    37 */

    38 size_t hexStringToBytes(const string &str, byte *out);
    39 

    40 #endif/*UTIL_H*/

    util.cpp
     1 #include "util.h"
     2 #include <vector>
     3 
     4 using namespace std;
     5 

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

    12     };
    13     return
     HEX[x];
    14 
    }
    15 

    16 int hexCharToInt(char hex) {
    17     hex =
     toupper(hex);
    18     if
     (isdigit(hex))
    19         return (hex - '0'
    );
    20     if
     (isalpha(hex))
    21         return (hex - 'A' + 10
    );
    22     return 0
    ;
    23 
    }
    24 

    25 string bytesToHexString(const byte *in, size_t size) {
    26 
        string str;
    27     for (size_t i = 0; i < size; ++
    i) {
    28         int t =
     in[i];
    29         int a = t / 16
    ;
    30         int b = t % 16
    ;
    31         str.append(1
    , intToHexChar(a));
    32         str.append(1
    , intToHexChar(b));
    33         if (i != size - 1
    )
    34             str.append(1, ' '
    );
    35 
        }
    36     return
     str;
    37 
    }
    38 

    39 size_t hexStringToBytes(const string &str, byte *out) {
    40 

    41     vector<string> vec;
    42     string::size_type currPos = 0, prevPos = 0
    ;
    43     while ((currPos = str.find(' ', prevPos)) !=
     string::npos) {
    44         string b(str.substr(prevPos, currPos -
     prevPos));
    45 
            vec.push_back(b);
    46         prevPos = currPos + 1
    ;
    47 
        }
    48     if (prevPos <
     str.size()) {
    49 
            string b(str.substr(prevPos));
    50 
            vec.push_back(b);
    51 
        }
    52     typedef vector<string>
    ::size_type sz_type;
    53     sz_type size =
     vec.size();
    54     for (sz_type i = 0; i < size; ++
    i) {
    55         int a = hexCharToInt(vec[i][0
    ]);
    56         int b = hexCharToInt(vec[i][1
    ]);
    57         out[i] = a * 16 +
     b;
    58 
        }
    59     return
     size;
    60 }

    tea.h
     1 #ifndef TEA_H
     2 
    #define TEA_H
     3 

     4 /*
     5 *for htonl,htonl
     6 
    *do remember link "ws2_32.lib"
     7 */

     8 #include <winsock2.h>
     9 #include "util.h"
    10 
    11 class TEA {
    12 public
    :
    13     TEA(const byte *key, int round = 32, bool isNetByte = false
    );
    14     TEA(const TEA &
    rhs);
    15     TEA& operator=(const TEA &
    rhs);
    16     void encrypt(const byte *in, byte *
    out);
    17     void decrypt(const byte *in, byte *
    out);
    18 private
    :
    19     void encrypt(const ulong *in, ulong *
    out);
    20     void decrypt(const ulong *in, ulong *
    out);
    21     ulong ntoh(ulong netlong) { return _isNetByte ?
     ntohl(netlong) : netlong; }
    22     ulong hton(ulong hostlong) { return _isNetByte ?
     htonl(hostlong) : hostlong; }
    23 private
    :
    24     int _round; //iteration round to encrypt or decrypt

    25     bool _isNetByte; //whether input bytes come from network
    26     byte _key[16]; //encrypt or decrypt key
    27 };
    28 

    29 #endif/*TEA_H*/

    tea.cpp
     1 #include "tea.h"
     2 #include <cstring> //for memcpy,memset
     3 
     4 using namespace std;
     5 

     6 TEA::TEA(const byte *key, int round /*= 32*/, bool isNetByte /*= false*/)
     7 
    :_round(round)
     8 
    ,_isNetByte(isNetByte) {
     9     if (key != 0
    )
    10         memcpy(_key, key, 16
    );
    11     else

    12         memset(_key, 0, 16);
    13 
    }
    14 

    15 TEA::TEA(const TEA &rhs)
    16 
    :_round(rhs._round)
    17 
    ,_isNetByte(rhs._isNetByte) {
    18     memcpy(_key, rhs._key, 16
    );
    19 
    }
    20 

    21 TEA& TEA::operator=(const TEA &rhs) {
    22     if (&rhs != this
    ) {
    23         _round =
     rhs._round;
    24         _isNetByte =
     rhs._isNetByte;
    25         memcpy(_key, rhs._key, 16
    );
    26 
        }
    27     return *this
    ;
    28 
    }
    29 

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

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

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

    40     ulong *k = (ulong*)_key;
    41     register ulong y = ntoh(in[0
    ]);
    42     register ulong z = ntoh(in[1
    ]);
    43     register ulong a = ntoh(k[0
    ]);
    44     register ulong b = ntoh(k[1
    ]);
    45     register ulong c = ntoh(k[2
    ]);
    46     register ulong d = ntoh(k[3
    ]);
    47     register ulong delta = 0x9E3779B9; /* (sqrt(5)-1)/2*2^32 */

    48     register int round = _round;
    49     register ulong sum = 0
    ;
    50 

    51     while (round--) {    /* basic cycle start */
    52         sum += delta;
    53         y += ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) +
     b);
    54         z += ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) +
     d);
    55     }    /* end cycle */

    56     out[0] = ntoh(y);
    57     out[1] =
     ntoh(z);
    58 
    }
    59 

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

    62     ulong *k = (ulong*)_key;
    63     register ulong y = ntoh(in[0
    ]);
    64     register ulong z = ntoh(in[1
    ]);
    65     register ulong a = ntoh(k[0
    ]);
    66     register ulong b = ntoh(k[1
    ]);
    67     register ulong c = ntoh(k[2
    ]);
    68     register ulong d = ntoh(k[3
    ]);
    69     register ulong delta = 0x9E3779B9; /* (sqrt(5)-1)/2*2^32 */

    70     register int round = _round;
    71     register ulong sum = 0
    ;
    72 

    73     if (round == 32)
    74         sum = 0xC6EF3720; /* delta << 5*/

    75     else if (round == 16)
    76         sum = 0xE3779B90; /* delta << 4*/

    77     else
    78         sum = delta << static_cast<int>(logbase(2, round));
    79 

    80     while (round--) {    /* basic cycle start */
    81         z -= ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d);
    82         y -= ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) +
     b);
    83         sum -=
     delta;
    84     }    /* end cycle */

    85     out[0] = ntoh(y);
    86     out[1] =
     ntoh(z);
    87 }

    需要说明的是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 #include "tea.h"
     2 #include "util.h"
     3 #include <iostream>
     4 
     5 using namespace std;
     6 

     7 int main() {
     8 

     9     const string plainStr("AD DE E2 DB B3 E2 DB B3");
    10     const string keyStr("3A DA 75 21 DB E2 DB B3 11 B4 49 01 A5 C6 EA D4"
    );
    11     const int SIZE_IN = 8, SIZE_OUT = 8, SIZE_KEY = 16
    ;
    12     byte
     plain[SIZE_IN], crypt[SIZE_OUT], key[SIZE_KEY];
    13 

    14     size_t size_in = hexStringToBytes(plainStr, plain);
    15     size_t size_key =
     hexStringToBytes(keyStr, key);
    16 

    17     if (size_in != SIZE_IN || size_key != SIZE_KEY)
    18         return -1
    ;
    19 

    20     cout << "Plain: " << bytesToHexString(plain, size_in) << endl;
    21     cout << "Key  : " << bytesToHexString(key, size_key) <<
     endl;
    22 

    23     TEA tea(key, 16, true);
    24 
        tea.encrypt(plain, crypt);
    25     cout << "Crypt: " << bytesToHexString(crypt, SIZE_OUT) <<
     endl;
    26 

    27     tea.decrypt(crypt, plain);
    28     cout << "Plain: " << bytesToHexString(plain, SIZE_IN) <<
     endl;
    29     return 0
    ;
    30 }

    运行结果:
    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
     
     
     
     
     
  • 相关阅读:
    高盛、沃尔玛 题做出来还挂了的吐槽
    amazon师兄debrief
    到所有人家距离之和最短的中点 296. Best Meeting Point
    问问题没人回答的情况怎么办终于有解了
    找名人 277. Find the Celebrity
    数组生存游戏 289. Game of Life
    547. Number of Provinces 省份数量
    428. Serialize and Deserialize Nary Tree 序列化、反序列化n叉树
    alias别名简介和使用
    面试官:线程池执行过程中遇到异常会发生什么,怎样处理? Vincent
  • 原文地址:https://www.cnblogs.com/huhu0013/p/3334890.html
Copyright © 2011-2022 走看看