關(guān)于TEA數(shù)據(jù)加密算法的研究

TEA(Tiny Encryption Algorithm)是一種小型的對(duì)稱加密解密算法,最初是由劍橋計(jì)算機(jī)實(shí)驗(yàn)室的 David Wheeler 和 Roger Needham 在 1994 年設(shè)計(jì)。采用128位密鑰,以8字節(jié)(64位)對(duì)數(shù)據(jù)分塊進(jìn)行加密 / 解密。特點(diǎn)是速度快,代碼量?。用堋⒔饷艿暮诵乃惴偣膊?0來(lái)行)

TEA出現(xiàn)后針對(duì)它的攻擊也不斷出現(xiàn),在被發(fā)現(xiàn)存在缺陷后,TEA也發(fā)展出幾個(gè)版本,分別是XTEA、Block TEA和XXTEA。XTEA 跟 TEA 使用了相同的簡(jiǎn)單運(yùn)算,但它采用了截然不同的順序,為了阻止密鑰表攻擊,四個(gè)子密鑰(在加密過(guò)程中,原 128 位的密鑰被拆分為 4 個(gè) 32 位的子密鑰)采用了一種不太正規(guī)的方式進(jìn)行混合,但速度更慢了。Block TEA 是XTEA算法的變種,它可以對(duì) 32 位大小任意倍數(shù)的變量塊進(jìn)行操作。該算法將 XTEA 輪循函數(shù)依次應(yīng)用于塊中的每個(gè)字,并且將它附加于它的鄰字。該操作重復(fù)多少輪依賴于塊的大小,但至少需要 6 輪。該方法的優(yōu)勢(shì)在于它無(wú)需操作模式(CBC,OFB,CFB 等),密鑰可直接用于信息。對(duì)于長(zhǎng)的信息它可能比 XTEA 更有效率。
在 1998 年,Markku-Juhani Saarinen 給出了一個(gè)可有效攻擊 Block TEA 算法的代碼,但之后很快 David J. Wheeler 和 Roger M. Needham 就給出了 Block TEA 算法的修訂版,這個(gè)算法被稱為 XXTEA。XXTEA 使用跟 Block TEA 相似的結(jié)構(gòu),但在處理塊中每個(gè)字時(shí)利用了相鄰字。它利用一個(gè)更復(fù)雜的 MX 函數(shù)代替了 XTEA 輪循函數(shù)。
XXTEA 算法很安全,而且非常快速,非常適合應(yīng)用于 Web 及嵌入式系統(tǒng)開(kāi)發(fā)中。?但名氣不大,采用的人比較少。我們?cè)陂_(kāi)發(fā) iOS 系統(tǒng)程序時(shí),由于大量的設(shè)定數(shù)據(jù)都是采用明文格式進(jìn)行保存,容易被人分析修改。如現(xiàn)在常見(jiàn)的 iOS 非越獄機(jī)器上的內(nèi)購(gòu)應(yīng)用破解,游戲數(shù)值修改等等。所以我們考慮利用XXTEA來(lái)對(duì)這些明文數(shù)據(jù)進(jìn)行加密,來(lái)提高安全性。

#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;

}
函數(shù)把輸入的數(shù)據(jù)當(dāng)成一個(gè)整型數(shù)組?long* v ,其長(zhǎng)度為?long n,加密采用的密鑰也由數(shù)組傳入long* k。由于要求傳入的數(shù)據(jù)必須是整型對(duì)齊的,而我們平時(shí)應(yīng)用中的數(shù)據(jù)長(zhǎng)度不定。而且加密解密都在一個(gè)函數(shù)內(nèi)進(jìn)行操作,以長(zhǎng)度 n 的值為正或負(fù)來(lái)進(jìn)行區(qū)分也容易忽略,所以對(duì)它進(jìn)行一下封裝。作成單獨(dú)的加密和解密函數(shù)

Source code

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為加密函數(shù),XXTEADecode為解密函數(shù),他們的輸入?yún)?shù)相同,源數(shù)據(jù)指針pInputBuffer,輸出緩沖區(qū)指針pOutputBuffer,源數(shù)據(jù)長(zhǎng)度nLength,和密鑰數(shù)組pUserDefineKey。這里輸出緩沖區(qū)的指針需要由調(diào)用者預(yù)先分配好,但是究竟需要多少內(nèi)存呢?解密函數(shù)比較容易解決,因?yàn)榻饷芎蟮臄?shù)據(jù)肯定不會(huì)超過(guò)源數(shù)據(jù)長(zhǎng)度,但是加密的時(shí)候由于需要將數(shù)據(jù)設(shè)置為整型對(duì)齊,數(shù)據(jù)可能長(zhǎng)于輸入數(shù)據(jù)長(zhǎng)度。我們模仿 Windows API 的做法,修改一下加密函數(shù),在輸入?yún)?shù)中如果輸出緩沖區(qū)指針為空,返回需要的buffer長(zhǎng)度:

Source code

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;

}

現(xiàn)在我們就可以通過(guò)加密函數(shù)來(lái)取得需要的 buffer 長(zhǎng)度并且分配好內(nèi)存,然后進(jìn)行加密處理了。

來(lái)測(cè)試一下。首先聲明一個(gè)字符串 NSString stringTest = @”Hello XXTEA!”; ?寫(xiě)下這個(gè)字符串之后又想到一個(gè)問(wèn)題。iOS系統(tǒng)中的字符是采用UTF-16編碼格式,也就是說(shuō)所有的字符都是雙字節(jié),和平常 C 語(yǔ)言下的 ANSI 字符不同,雖然一樣可以取得緩沖區(qū)指針進(jìn)行加密,但是在iOS下我們需要處理的各種設(shè)定數(shù)據(jù)基本都是英文字符的文本,對(duì)于一個(gè)設(shè)置文件來(lái)說(shuō)比ANSI字符多了一倍的空間,我們還是轉(zhuǎn)換一下讓英文字符恢復(fù)到8位編碼。查資料可以發(fā)現(xiàn)iOS系統(tǒng)不支持ANSI格式的雙字節(jié)字符編碼,我們選用UTF-8來(lái)兼容單、雙字節(jié)字符,同時(shí)節(jié)省空間。

Source code
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);

運(yùn)行上面的代碼得到輸出結(jié)果:2F49EF03665D18EF294E29A46AE17F7E 。我們?cè)賹?duì)這串字符進(jìn)行下解密處理來(lái)驗(yàn)證是否可以還原。

Source code

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);

在Xcode的Debug窗口中看到輸出信息:test[4559:f803] Hello XXTEA! 。解密完成。現(xiàn)在我們可以用XXTEA來(lái)處理明文文件,不會(huì)再被輕易篡改。