AES 算法

AES 加密处理流程分为:

1、字节替换(SubBytes) , 2、行移位(ShiftRows), 3、列混合(MixColumns),4、轮密钥加(AddRoundKey)

143057_eF59_174780.jpg

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 以上。。。

转载于:https://my.oschina.net/u/174780/blog/363478

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值