天天看点

利用TEA算法进行数据加密

http://blog.chukong-inc.com/index.php/2012/02/14/利用tea算法进行数据加密/

TEA(Tiny Encryption Algorithm)是一种小型的对称加密解密算法,最初是由剑桥计算机实验室的 David Wheeler 和 Roger Needham 在 1994 年设计。采用128位密钥,以8字节(64位)对数据分块进行加密 / 解密。TEA特点是速度快、效率高,实现也非常简单。

TEA出现后针对它的攻击也不断出现,在被发现存在缺陷后,TEA也发展出几个版本,分别是XTEA、Block TEA和XXTEA。XTEA 跟 TEA 使用了相同的简单运算,但它采用了截然不同的顺序,为了阻止密钥表攻击,四个子密钥(在加密过程中,原 128 位的密钥被拆分为 4 个 32 位的子密钥)采用了一种不太正规的方式进行混合,但速度更慢了。Block TEA 是XTEA算法的变种,它可以对 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 轮循函数。

XXTEA 算法很安全,而且非常快速,非常适合应用于 Web 及嵌入式系统开发中。 但名气不大,采用的人比较少。我们在开发 iOS 系统程序时,由于大量的设定数据都是采用明文格式进行保存,容易被人分析修改。如现在常见的 iOS 非越狱机器上的内购应用破解,游戏数值修改等等。所以我们考虑利用XXTEA来对这些明文数据进行加密,来提高安全性。

英文 Wiki 上面提供了 XXTEA 的 C 语言实现,代码非常简单。

Source code
#define MX (z>>5^y<<2) + (y>>3^z<<4)^(sum^y) + (k[p&3^e]^z);
 
  long btea(long* v, long n, long* k) {
    unsigned long z=v[n-1], y=v[0], sum=0, e, DELTA=0x9e3779b9;
    long p, q ;
    if (n > 1) {          /* Coding Part */
      q = 6 + 52/n;
      while (q-- > 0) {
        sum += DELTA;
        e = (sum >> 2) & 3;
        for (p=0; p<n-1; p++) y = v[p+1], z = v[p] += MX;
        y = v[0];
        z = v[n-1] += MX;
      }
      return 0 ;
    } else if (n < -1) {  /* Decoding Part */
      n = -n;
      q = 6 + 52/n;
      sum = q*DELTA ;
      while (sum != 0) {
        e = (sum >> 2) & 3;
        for (p=n-1; p>0; p--) z = v[p-1], y = v[p] -= MX;
        z = v[n-1];
        y = v[0] -= MX;
        sum -= DELTA;
      }
      return 0;
    }
    return 1;
  }      

函数把输入的数据当成一个整型数组 long* v ,其长度为 long n,加密采用的密钥也由数组传入long* k。由于要求传入的数据必须是整型对齐的,而我们平时应用中的数据长度不定。而且加密解密都在一个函数内进行操作,以长度 n 的值为正或负来进行区分也容易忽略,所以对它进行一下封装。作成单独的加密和解密函数

size_t XXTEAEncode(const unsigned char * pInputBuffer,
                                       unsigned char * pOutputBuffer,
                                       size_t nLength,
                                       const_uint_ptr pUserDefineKey)
{
    size_t nResult = 0;
    if (pInputBuffer && pOutputBuffer && nLength > 0)
    {
        nResult = nLength / XXTEA_ALIGNMENT_BYTES +
                    (nLength % XXTEA_ALIGNMENT_BYTES ? 1 : 0);
        memset(pOutputBuffer, 0, nResult * XXTEA_ALIGNMENT_BYTES);
        memcpy(pOutputBuffer, pInputBuffer, nLength);
        btea((uint32_t *)pOutputBuffer, nResult * 2, (uint32_t *)pUserDefineKey);
        nResult *= XXTEA_ALIGNMENT_BYTES;
    }
    return nResult;
}
 
bool XXTEADecode(const unsigned char * pInputBuffer,
                                     unsigned char * pOutputBuffer,
                                     size_t nLength,
                                     const_uint_ptr pUserDefineKey)
{
    if(nLength % 
        return false;
 
    bool result = false;
    if(pInputBuffer && pOutputBuffer && nLength > 0)
    {
        int nSize = (nLength / XXTEA_ALIGNMENT_BYTES) * 2;
        memset(pOutputBuffer, 0, nLength);
        memcpy(pOutputBuffer, pInputBuffer, nLength);
        btea((uint32_t *)pOutputBuffer, -nSize, (uint32_t *)pUserDefineKey);
        result = true;
    }
    return result;
}      

XXTEAEncode为加密函数,XXTEADecode为解密函数,他们的输入参数相同,源数据指针pInputBuffer,输出缓冲区指针pOutputBuffer,源数据长度nLength,和密钥数组pUserDefineKey。这里输出缓冲区的指针需要由调用者预先分配好,但是究竟需要多少内存呢?解密函数比较容易解决,因为解密后的数据肯定不会超过源数据长度,但是加密的时候由于需要将数据设置为整型对齐,数据可能长于输入数据长度。我们模仿 Windows API 的做法,修改一下加密函数,在输入参数中如果输出缓冲区指针为空,返回需要的buffer长度:

size_t XXTEAEncode(const unsigned char * pInputBuffer,
                                       unsigned char * pOutputBuffer,
                                       size_t nLength,
                                       const_uint_ptr pUserDefineKey)
{
    size_t nResult = 0;
    if (pInputBuffer && pOutputBuffer && nLength > 0)
    {
        nResult = nLength / XXTEA_ALIGNMENT_BYTES +
                    (nLength % XXTEA_ALIGNMENT_BYTES ? 1 : 0);
        memset(pOutputBuffer, 0, nResult * XXTEA_ALIGNMENT_BYTES);
        memcpy(pOutputBuffer, pInputBuffer, nLength);
        btea((uint32_t *)pOutputBuffer, nResult * 2, (uint32_t *)pUserDefineKey);
        nResult *= XXTEA_ALIGNMENT_BYTES;
    }
    else if(nLength > 0)
        nResult = ((nLength / XXTEA_ALIGNMENT_BYTES) +
                   (nLength % XXTEA_ALIGNMENT_BYTES ? 1 : 0)) * XXTEA_ALIGNMENT_BYTES;
 
    return nResult;
}      
NSString * testString = @"Hello XXTEA!";
 
    int key[4] = {0x12345678, 0x734a67fc, 0xe367a642, 0x78432562};
 
    int nSize = XXTEAEncode((const unsigned char *)[testString cStringUsingEncoding:NSUTF8StringEncoding], NULL, testString.length, key);
 
    char * outBuffer = (char *)malloc(nSize);
    XXTEAEncode((const unsigned char *)[testString cStringUsingEncoding:NSUTF8StringEncoding], (unsigned char *)outBuffer, ,nSize, key);
 
    char *formatBuffer = (char *)malloc(128);
    memset(formatBuffer, 0, 128);
    HexToString((const char *)outBuffer, nSize, formatBuffer);
    printf("%s\n\n", formatBuffer);
    free(formatBuffer);
    free(outBuffer);      
char * testData = "2F49EF03665D18EF294E29A46AE17F7E";
    char * hexData = (char *)malloc(strlen(testData));
    char * decryptBuffer = (char *)malloc(strlen(testData));
    memset(hexData, 0, strlen(testData));
    memset(decryptBuffer, 0, strlen(testData));
    int nSize = StringToHex((const char *)testData, hexData);
    XXTEADecode((const unsigned char *)hexData, (unsigned char *)decryptBuffer, nSize, key);
    NSString * decodeString = [NSString stringWithCString:decryptBuffer encoding:NSUTF8StringEncoding];
    NSLog(@"%@", decodeString);
    free(decryptBuffer);
    free(hexData);