AES 加密处理流程分为:
1、字节替换(SubBytes) , 2、行移位(ShiftRows), 3、列混合(MixColumns),4、轮密钥加(AddRoundKey)
AES 解密处理的流程类似。按照这个流程,写出代码还不算复杂,就列混合的操作要多一些。这样写出来的代码,处理速度比较慢,可能在1 mb/s。所以需要把这个流程中耗时的操作优化一下,还要把比较简单的操作合并一下。
因为列混合的操作最多,所以首先优化列混合。
列混合中,要一直使用 GF(2^8) 域乘,所以我们可以在加密前,把所有的域乘结果缓存在一张表中,在实际加密中,用查表的方式,取代调用域乘函数。
域乘的代码:
unsigned char AES::field_multiply(const unsigned char x, const unsigned char y) {
int i;
unsigned char rem, tmp;
unsigned char result = 0;
rem = y;
for( i = 0; i < 8; i++) { //一个字节有8比特,所以循环8次,对于 AES ,循环4次就可以了,因为只用到后4比特
if( (x >> i ) & 0x01 ) {
result ^= rem;
}
tmp = rem << 1; //将GF(2)多项式相乘,变成一多项式不断乘 x,如果高位是1,则异或不可约多项式。
if( rem & 0x80 ) {
tmp ^= 0x1b;
}
rem = tmp;
}
return result;
}
(发现 CRC 的计算也是用 GF(2),而且只是单纯的不断求余就可以了,把这个域乘改一下,就能算 CRC 了。)
加密列混合中的输入矩阵中有3个数:01、02、03,而数据的输入矩阵有256个可用的数:00 到 FF,那域乘的结果缓存下来会的到 3 x 256 大小的表。再加上解密的4个数:09、0B、0D、0E,那这张域乘表的大小为 7 x 256。
生成的表很大,但对 AES 加密处理的提速很明显,可以从以前的 1mb/s 提升到 3mb/s。
域乘填表的实现方式:我把 AES 写成了一个类,在 AES 类的构造函数中,调用填表函数,填表函数计算所有的域乘结果,并缓存在一个数组中,在列混合中,原先的域乘函数,替换为域乘结果数组,并用下标从数组中查到域乘的结果。
代码:
unsigned char AES::FieldBox[7][256];
void AES::fill_field_box()
{
int i, j;
unsigned char mix_box[] = {0x01, 0x02, 0x03, 0x09, 0x0b, 0x0d, 0x0e};
for(i = 0; i < 7; ++i)
{
for(j = 0; j < 256; ++j)
{
FieldBox[i][j] = AES::field_multiply(mix_box[i], (unsigned char) j );
}
}
}
既然查表可以很显著的提升处理速度,那把其他操作一起拿来查表吧。
而 字节替换 已经是用查表方式实现的,那合并表。现在我们把 字节替换 的表,和域乘的表合并在一起。
原先是:MixColums( FieldMultiply [ MixMatirix ] [ SubBytes[ x ] ] )
现在要变为: MixColums( Table[ x ] )
要实现这个就比较简单了,先把域乘的表填完后: x = 00 To FF; Table[ x ] = FieldMultiplyTable [ MixMatrix ] [ SubBytes[x] ];
这样一种包含字节替换和域乘的表就生成了,处理速度又可以加快了。。。
但是这样简单的填完这张表,还不太好,这样填表并没有考虑列混合的特点,列混合是 两个 4x4 的输入矩阵相乘得到输出矩阵,那在算列混合的时候,相同的一个值,需要查4次表,其实可以把表改变表的排列方式,让列混合中的每个值只需查1次表,就得到一个输出矩阵。
列混合:
02 03 01 01 E6 B1 CA B7 a0 a4 a8 a12
01 02 03 01 X 1B 5B 12 7F = a1 a5 a9 a13
01 01 02 03 50 FD 7C 7B a2 a6 a10 a14
03 01 01 02 18 79 04 23 a3 a7 a11 a15
分解矩阵乘法:
a0 = 02xE6 + 03x1B + 01x50 + 01x18
a1 = 01xE6 + 02x1B + 03x50 + 01x18
a2 = 01xE6 + 01x1B + 02x50 + 03x18
a3 = 03xE6 + 01x1B + 01x50 + 02x18
这样规律就很明显了,只要将表结果和列混合矩阵的列一一对应,那查一次表,就可以得到要得到4个值,把这四个值相异或就得到一列结果了。
实现:我用两个三维数组缓存表,一个是加密用的表,一个是解密用的表,AesBox[4][256][4],InvAesBox[4][256][4]
代码实现:
unsigned char AES::AesBox[4][256][4];
unsigned char AES::InvAesBox[4][256][4];
void AES::fill_aes_box()
{
int i, j, k;
unsigned char mix_box[4][4] = {{1, 0, 0, 2}, {2, 1, 0, 0}, {0, 2, 1, 0}, {0, 0, 2, 1}};
unsigned char inv_mix_box[4][4] = {{6, 3, 5, 4}, {4, 6, 3, 5}, {5, 4, 6, 3}, {3, 5, 4, 6}};
for(i = 0; i < 4; ++i)
{
for(j = 0; j < 256; ++j)
{
for(k = 0; k < 4; ++k)
{
AesBox[i][j][k] = FieldBox[ mix_box[i][k] ] [ Sbox[j] ];
InvAesBox[i][j][k] = FieldBox[ inv_mix_box[i][k] ] [ j ]; //解密的话,还没想到什么方法合并S盒
}
}
}
}
合并完后,测试发现 行移位(ShiftRows) 也很费时,不过 行移位 很简单,可以直接和列混合合并。
合并方式:把列混合展开,不用 for 循环,然后在展开的代码中,调整下标访问的顺序就可以了。
代码:(状态盒是列向填充,一维数组存储)
inline void AES::bit_xor(unsigned char* array1, unsigned char* array2, unsigned char* array3, unsigned char* array4, unsigned char* result)
{
result[0] = array1[0] ^ array2[0] ^ array3[0] ^ array4[0];
result[1] = array1[1] ^ array2[1] ^ array3[1] ^ array4[1];
result[2] = array1[2] ^ array2[2] ^ array3[2] ^ array4[2];
result[3] = array1[3] ^ array2[3] ^ array3[3] ^ array4[3];
}
void AES::mix_column(unsigned char* content, unsigned char* result)
{
bit_xor(AesBox[0][content[0]] , AesBox[1][content[5]], AesBox[2][content[10]], AesBox[3][content[15]], result);
result += 4;
bit_xor(AesBox[0][content[4]] , AesBox[1][content[9]], AesBox[2][content[14]], AesBox[3][content[3]], result);
result += 4;
bit_xor(AesBox[0][content[8]] , AesBox[1][content[13]], AesBox[2][content[2]], AesBox[3][content[7]], result);
result += 4;
bit_xor(AesBox[0][content[12]] , AesBox[1][content[1]], AesBox[2][content[6]], AesBox[3][content[11]], result);
}
void AES::inv_mix_column(unsigned char* content, unsigned char* result)
{
bit_xor(InvAesBox[0][content[0]] , InvAesBox[1][content[1]], InvAesBox[2][content[2]], InvAesBox[3][content[3]], result);
result += 4;
bit_xor(InvAesBox[0][content[4]] , InvAesBox[1][content[5]], InvAesBox[2][content[6]], InvAesBox[3][content[7]], result);
result += 4;
bit_xor(InvAesBox[0][content[8]] , InvAesBox[1][content[9]], InvAesBox[2][content[10]], InvAesBox[3][content[11]], result);
result += 4;
bit_xor(InvAesBox[0][content[12]] , InvAesBox[1][content[13]], InvAesBox[2][content[14]], InvAesBox[3][content[15]], result);
}
AES优化处理过程后,处理速度可以在 6 mb/s 以上。。。