非常值得参考的是官方文档,它详细介绍了AES及其实验过程。博文AES加密算法的C++实现就是基于该文档的介绍及实现,是难得的一篇好文,故在本文最后会附上该文,以作备份。
还有很值得推荐的就是AES的动画演示,做的很形象,非常有助于理解!
对AES而言,它采用了“代换-置换网络”结构(Substitution-Permutation Network, SPN)。其最复杂的计算在于列混淆,而列混淆的复杂又来自有限域的乘法;另外,一方面,我们还要考虑加密过程中需要考虑的字节填充。下边将进行介绍。
1. 有限域乘法
这部分主要参考自《密码编码学与网络安全——原理与实践》(第五版)(P. 96-97)。
在该书中,作者提到“本质上说,域就是一个集合,我们可以在其上进行加法、减法、乘法和除法而不脱离该集合”。有限域是域的一种,它指的是阶(元素个数,记为p)有限的域,记为GF(p),其中有限域的阶必须是一个素数的幂p'^n(p'为素数,n为正整数)。GF(2^n)是在密码学中用得很多的有限域,它表示该域总共只有2^n个元素。特别地,GF(2^8)被用于AES的加解密。GF(2^8)是一个包含256个元素的域,它的每一个元素可被赋值为0~2^8-1中的唯一整数(注意这里提到的可,它意味着GF(2^8)的每一个元素同样可以取其他范围的值(不在0~2^8-1),但就是要满足域的条件)。在AES中,用到的有限域是GF(2^8),它的每一个元素被赋值为0~2^8-1中的唯一整数。
同时,在该书中,作者还提出GF(2^n)的乘法计算公式如下:
详细推导请参考该书P. 96。
下边参考博文有限域GF(2^8)内乘法代码实现以及原理(这篇博文开头对有限域的说明有点问题,作者貌似是把有限域理解成因为该域内的元素的值是有范围的,所以才叫有限域)的例子来说明如何进行GF(2^8)有限域乘法。
在二进制中,所有的数都能用0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80异或得到,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80的二进制表示如下:
后一个分别是前一个的2倍。假设任意一个数a,他的二进制表示为10101101,可以由以下组合组成:
而任何一个数x和a相乘都可以表示为
所以只要计算出
最后再对这些结果进行异或就可以求出最终的乘法结果。那如何求0x3a*0x24?
首先0x3a=00111010,分别求
0x24=00100100,所以0x3a*0x24=0x3a*00100100=0x04*0x3a^0x20*0x3a=0xe8^0x01=0xe9.
作者还附带了一个C/C++程序来计算GF(2^8)有限域乘法:
unsigned char XTIME(unsigned char x) { return ((x << 1) ^ ((x & 0x80) ? 0x1b : 0x00)); } unsigned char multiply(unsigned char a, unsigned char b) { unsigned char temp[8] = { a }; unsigned char tempmultiply = 0x00; int i = 0; for (i = 1; i < 8; i++) { temp[i] = XTIME(temp[i - 1]); } tempmultiply = (b & 0x01) * a; for (i = 1; i <= 7; i++) { tempmultiply ^= (((b >> i) & 0x01) * temp[i]); } return tempmultiply; }
关于程序的解释可以参考该博文。
2. 字节填充
AES是分块计算,当数据内容不足,16字节(128 bit AES),24字节(192 bit AES),32字节(256 bit AES),不足部分就需要填充。维基百科(翻译)上面列举填充方式有如下几种:
1)ANSI X.923
不足部分填充0,最后一字节为填充字节数。如下面8字节的块,需要填充4字节时:
… | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 04 |
2)ISO 10126
不足部分填充随机数字,最后一字节为填充字节数。如下面8字节的块,需要填充4字节时:
… | DD DD DD DD DD DD DD DD | DD DD DD DD BC DA EF 04 |
3)PKCS7与PKCS5
不足部分填充为需要填充字节数。若数据大小是分块大小N的倍数时,则增加一个全为N的分块。如下面8字节的块,需要填充4字节时:
… | DD DD DD DD DD DD DD DD | DD DD DD DD 04 04 04 04 |
4)ISO/IEC 7816-4
不足的部分,首先填充一个0×80,剩余部分全为0。如下面8字节的块,需要填充4字节时:
… | DD DD DD DD DD DD DD DD | DD DD DD DD 80 00 00 00 |
要求数据内容本身不包含0×80
5)Zero padding
不足部分全部填充0。如下面8字节的块,需要填充4字节时:
… | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 00 |
这种方法不能区分数据内容本身末尾包含0的情况,因而也不是标准的填充方式。
本人在实现的时候采用的是ANSI X.923标准。
3. 个人实现
代码请见Github.
4. 博文AES加密算法的C++实现摘录
摘要:作为新一代的加密标准,AES 旨在取代 DES(请看《DES加密算法的C++实现》),以适应当今分布式开放网络对数据加密安全性的要求。本文在分析了 AES 加密原理的基础上着重说明了算法实现的具体步骤,并用 C++ 实现了对文件的加密和解密。
一、AES 介绍
AES(高级加密标准,Advanced Encryption Standard),在密码学中又称 Rijndael 加密法,是美国联邦政府采用的一种分组加密标准。这个标准用来替代原先的 DES,目前已经广为全世界所使用,成为对称密钥算法中最流行的算法之一。
在 AES 出现之前,最常用的对称密钥算法是 DES 加密算法,它在 1977 年被公布成为美国政府的商用加密标准。DES 的主要问题是密钥长度较短,渐渐不适合于分布式开放网络对数据加密安全性的要求。因此,1998年美国政府决定不再继续延用 DES 作为联邦加密标准,并发起了征集 AES 候选算法的活动。征集活动对 AES 的基本要求是: 比三重DES快、至少与三重DES一样安全、数据分组长度为128比特、密钥长度为128/192/256比特。
经过三年多的甄选,比利时的密码学家所设计的 Rijndael 算法最终脱颖而出,成为新一代的高级加密标准,并于 2001 年由美国国家标准与技术研究院(NIST)发布于 FIPS PUB 197。
二、AES 算法原理
AES算法(即 Rijndael 算法)是一个对称分组密码算法。数据分组长度必须是 128 bits,使用的密钥长度为 128,192 或 256 bits。对于三种不同密钥长度的 AES 算法,分别称为“AES-128”、“AES-192”、“AES-256”。(Rijndael 的设计还可以处理其它的分组长度和密钥长度,但 AES 标准中没有采用)
下图是 AES 加密解密的整体流程图:
这里我们需要知道3个符号:Nb
—— 状态 State 包含的列(32-bit 字)的个数,也就是说 Nb=4;Nk
—— 密钥包含的 32-bit 字的个数,也就是说 Nk=4,6 或 8;Nr
—— 加密的轮数,对于不同密钥长度,轮数不一样,具体如下图所示:
下面分为密钥扩展、分组加密、分组解密三个部分来讲 AES 算法,我会尽可能地简明扼要,若还有不懂的,请自行 Google。
1)密钥扩展
AES 算法通过密钥扩展程序(Key Expansion)将用户输入的密钥 K 扩展生成 Nb(Nr+1)
个字,存放在一个线性数组w[Nb*(Nr+1)]
中。具体如下:
-
位置变换函数
RotWord()
,接受一个字 [a0, a1, a2, a3] 作为输入,循环左移一个字节后输出 [a1, a2, a3, a0]。 -
S盒变换函数
SubWord()
,接受一个字 [a0, a1, a2, a3] 作为输入。S盒是一个16x16的表,其中每一个元素是一个字节。对于输入的每一个字节,前四位组成十六进制数 x 作为行号,后四位组成的十六进制数 y 作为列号,查找表中对应的值。最后函数输出 4 个新字节组成的 32-bit 字。 -
轮常数
Rcon[]
,如何计算的就不说了,直接把它当做常量数组。 -
扩展密钥数组
w[]
的前 Nk 个元素就是外部密钥 K,以后的元素w[i]
等于它前一个元素w[i-1]
与前第 Nk 个元素w[i-Nk]
的异或,即w[i] = w[i-1] XOR w[i-Nk]
;但若 i 为 Nk 的倍数,则w[i] = w[i-Nk] XOR SubWord(RotWord(w[i-1])) XOR Rcon[i/Nk-1]
。
注意,上面的第四步说明适合于 AES-128 和 AES-192,详细的伪代码如下:
密钥扩展程序的 C++ 代码(AES-128):
1 #include <iostream> 2 #include <bitset> 3 using namespace std; 4 typedef bitset<8> byte; 5 typedef bitset<32> word; 6 7 const int Nr = 10; // AES-128需要 10 轮加密 8 const int Nk = 4; // Nk 表示输入密钥的 word 个数 9 10 byte S_Box[16][16] = { 11 { 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76 }, 12 { 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0 }, 13 { 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15 }, 14 { 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75 }, 15 { 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84 }, 16 { 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF }, 17 { 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8 }, 18 { 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2 }, 19 { 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73 }, 20 { 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB }, 21 { 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79 }, 22 { 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08 }, 23 { 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A }, 24 { 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E }, 25 { 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF }, 26 { 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 } 27 }; 28 29 // 轮常数,密钥扩展中用到。(AES-128只需要10轮) 30 word Rcon[10] = { 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 31 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000 }; 32 33 /** 34 * 将4个 byte 转换为一个 word. 35 */ 36 word Word(byte& k1, byte& k2, byte& k3, byte& k4) 37 { 38 word result(0x00000000); 39 word temp; 40 temp = k1.to_ulong(); // K1 41 temp <<= 24; 42 result |= temp; 43 temp = k2.to_ulong(); // K2 44 temp <<= 16; 45 result |= temp; 46 temp = k3.to_ulong(); // K3 47 temp <<= 8; 48 result |= temp; 49 temp = k4.to_ulong(); // K4 50 result |= temp; 51 return result; 52 } 53 54 /** 55 * 按字节 循环左移一位 56 * 即把[a0, a1, a2, a3]变成[a1, a2, a3, a0] 57 */ 58 word RotWord(word& rw) 59 { 60 word high = rw << 8; 61 word low = rw >> 24; 62 return high | low; 63 } 64 65 /** 66 * 对输入word中的每一个字节进行S-盒变换 67 */ 68 word SubWord(word& sw) 69 { 70 word temp; 71 for (int i = 0; i<32; i += 8) 72 { 73 int row = sw[i + 7] * 8 + sw[i + 6] * 4 + sw[i + 5] * 2 + sw[i + 4]; 74 int col = sw[i + 3] * 8 + sw[i + 2] * 4 + sw[i + 1] * 2 + sw[i]; 75 byte val = S_Box[row][col]; 76 for (int j = 0; j<8; ++j) 77 temp[i + j] = val[j]; 78 } 79 return temp; 80 } 81 82 /** 83 * 密钥扩展函数 - 对128位密钥进行扩展得到 w[4*(Nr+1)] 84 */ 85 void KeyExpansion(byte key[4 * Nk], word w[4 * (Nr + 1)]) 86 { 87 word temp; 88 int i = 0; 89 // w[]的前4个就是输入的key 90 while (i < Nk) 91 { 92 w[i] = Word(key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]); 93 ++i; 94 } 95 96 i = Nk; 97 98 while (i < 4 * (Nr + 1)) 99 { 100 temp = w[i - 1]; // 记录前一个word 101 if (i % Nk == 0) 102 w[i] = w[i - Nk] ^ SubWord(RotWord(temp)) ^ Rcon[i / Nk - 1]; 103 else 104 w[i] = w[i - Nk] ^ temp; 105 ++i; 106 } 107 } 108 109 int main() 110 { 111 byte key[16] = { 0x2b, 0x7e, 0x15, 0x16, 112 0x28, 0xae, 0xd2, 0xa6, 113 0xab, 0xf7, 0x15, 0x88, 114 0x09, 0xcf, 0x4f, 0x3c }; 115 116 word w[4 * (Nr + 1)]; 117 118 cout << "KEY IS: "; 119 for (int i = 0; i<16; ++i) 120 cout << hex << key[i].to_ulong() << " "; 121 cout << endl; 122 123 KeyExpansion(key, w); 124 // 测试 125 for (int i = 0; i<4 * (Nr + 1); ++i) 126 cout << "w[" << dec << i << "] = " << hex << w[i].to_ulong() << endl; 127 128 return 0; 129 }
测试输出结果:
2)加密
根据 AES 加密的整体流程图(本文开头),伪代码如下:
从伪代码描述中可以看出,AES 加密时涉及到的子程序有SubBytes()
、ShiftRows()
、MixColumns()
和AddRoundKey()
。下面我们一个一个进行介绍:
① S盒变换-SubBytes()
在密钥扩展部分已经讲过了,S盒是一个 16 行 16 列的表,表中每个元素都是一个字节。S盒变换很简单:函数SubBytes()
接受一个 4x4 的字节矩阵作为输入,对其中的每个字节,前四位组成十六进制数 x 作为行号,后四位组成的十六进制数 y 作为列号,查找表中对应的值替换原来位置上的字节。
② 行变换-ShiftRows()
行变换也很简单,它仅仅是将矩阵的每一行以字节为单位循环移位:第一行不变,第二行左移一位,第三行左移两位,第四行左移三位。如下图所示:
③ 列变换-MixColumns()
函数MixColumns()
同样接受一个 4x4 的字节矩阵作为输入,并对矩阵进行逐列变换,变换方式如下:
注意公式中用到的乘法是伽罗华域(GF,有限域)上的乘法,高级加密标准文档 fips-197 上有讲,如果还是不懂,请自行Google。
④ 与扩展密钥的异或-AddRoundKey()
扩展密钥只参与了这一步。根据当前加密的轮数,用w[]
中的 4 个扩展密钥与矩阵的 4 个列进行按位异或。如下图:
好了,到这里 AES 加密的各个部分就讲完了。算法实现的 C++ 源码在文章后面第三部分。
3)解密
根据 AES 解密的整体流程图(本文开头),伪代码如下:
从伪代码可以看出,我们需要分别实现 S 盒变换、行变换和列变换的逆变换InvShiftRows()
、InvSubBytes()
和InvMixColumns()
。下面就简单的讲一下这三个逆变换:
① 逆行变换-InvShiftRows()
上面讲到ShiftRows()
是对矩阵的每一行进行循环左移,所以InvShiftRows()
是对矩阵每一行进行循环右移。
② 逆 S 盒变换-InvSubBytes()
与 S 盒变换一样,也是查表,查表的方式也一样,只不过查的是另外一个置换表(S-Box的逆表)。
③ 逆列变换-InvMixColumns()
与列变换的方式一样,只不过计算公式的系数矩阵发生了变化。如下图:
好了,AES 解密到这里也讲完了。只要写出三个逆变换的函数,然后根据伪代码就很容易实现 AES 解密算法了。
三、C++实现
下面我用 C++ 实现 AES 的加密和解密算法,并实现了对文件的加密和解密。这里我使用 C++ STL 的bitset
定义了两个类型:byte
和word
。需要提到的是,对于有限域上的乘法,我们既可以通过查表(6个结果表),也可以写一个函数来实现。当然,查表的效率会更高,但考虑到贴代码,这里我就用一个函数来实现的。
下面是 AES-128 对一个 128 位数据加密和解密的源代码:
1 /************************************************************************* 2 > File Name: AES.cpp 3 > Author: SongLee 4 > E-mail: lisong.shine@qq.com 5 > Created Time: 2014年12月12日 星期五 20时15分50秒 6 > Personal Blog: http://songlee24.github.com 7 ************************************************************************/ 8 #include <iostream> 9 #include <bitset> 10 #include <string> 11 using namespace std; 12 typedef bitset<8> byte; 13 typedef bitset<32> word; 14 15 const int Nr = 10; // AES-128需要 10 轮加密 16 const int Nk = 4; // Nk 表示输入密钥的 word 个数 17 18 byte S_Box[16][16] = { 19 { 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76 }, 20 { 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0 }, 21 { 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15 }, 22 { 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75 }, 23 { 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84 }, 24 { 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF }, 25 { 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8 }, 26 { 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2 }, 27 { 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73 }, 28 { 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB }, 29 { 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79 }, 30 { 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08 }, 31 { 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A }, 32 { 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E }, 33 { 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF }, 34 { 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 } 35 }; 36 37 byte Inv_S_Box[16][16] = { 38 { 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB }, 39 { 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB }, 40 { 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E }, 41 { 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25 }, 42 { 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92 }, 43 { 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84 }, 44 { 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06 }, 45 { 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B }, 46 { 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73 }, 47 { 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E }, 48 { 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B }, 49 { 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4 }, 50 { 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F }, 51 { 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF }, 52 { 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61 }, 53 { 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D } 54 }; 55 56 // 轮常数,密钥扩展中用到。(AES-128只需要10轮) 57 word Rcon[10] = { 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 58 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000 }; 59 60 /**********************************************************************/ 61 /* */ 62 /* AES算法实现 */ 63 /* */ 64 /**********************************************************************/ 65 66 /******************************下面是加密的变换函数**********************/ 67 /** 68 * S盒变换 - 前4位为行号,后4位为列号 69 */ 70 void SubBytes(byte mtx[4 * 4]) 71 { 72 for (int i = 0; i<16; ++i) 73 { 74 int row = mtx[i][7] * 8 + mtx[i][6] * 4 + mtx[i][5] * 2 + mtx[i][4]; 75 int col = mtx[i][3] * 8 + mtx[i][2] * 4 + mtx[i][1] * 2 + mtx[i][0]; 76 mtx[i] = S_Box[row][col]; 77 } 78 } 79 80 /** 81 * 行变换 - 按字节循环移位 82 */ 83 void ShiftRows(byte mtx[4 * 4]) 84 { 85 // 第二行循环左移一位 86 byte temp = mtx[4]; 87 for (int i = 0; i<3; ++i) 88 mtx[i + 4] = mtx[i + 5]; 89 mtx[7] = temp; 90 // 第三行循环左移两位 91 for (int i = 0; i<2; ++i) 92 { 93 temp = mtx[i + 8]; 94 mtx[i + 8] = mtx[i + 10]; 95 mtx[i + 10] = temp; 96 } 97 // 第四行循环左移三位 98 temp = mtx[15]; 99 for (int i = 3; i>0; --i) 100 mtx[i + 12] = mtx[i + 11]; 101 mtx[12] = temp; 102 } 103 104 /** 105 * 有限域上的乘法 GF(2^8) 106 */ 107 byte GFMul(byte a, byte b) { 108 byte p = 0; 109 byte hi_bit_set; 110 for (int counter = 0; counter < 8; counter++) { 111 if ((b & byte(1)) != 0) { 112 p ^= a; 113 } 114 hi_bit_set = (byte)(a & byte(0x80)); 115 a <<= 1; 116 if (hi_bit_set != 0) { 117 a ^= 0x1b; /* x^8 + x^4 + x^3 + x + 1 */ 118 } 119 b >>= 1; 120 } 121 return p; 122 } 123 124 /** 125 * 列变换 126 */ 127 void MixColumns(byte mtx[4 * 4]) 128 { 129 byte arr[4]; 130 for (int i = 0; i<4; ++i) 131 { 132 for (int j = 0; j<4; ++j) 133 arr[j] = mtx[i + j * 4]; 134 135 mtx[i] = GFMul(0x02, arr[0]) ^ GFMul(0x03, arr[1]) ^ arr[2] ^ arr[3]; 136 mtx[i + 4] = arr[0] ^ GFMul(0x02, arr[1]) ^ GFMul(0x03, arr[2]) ^ arr[3]; 137 mtx[i + 8] = arr[0] ^ arr[1] ^ GFMul(0x02, arr[2]) ^ GFMul(0x03, arr[3]); 138 mtx[i + 12] = GFMul(0x03, arr[0]) ^ arr[1] ^ arr[2] ^ GFMul(0x02, arr[3]); 139 } 140 } 141 142 /** 143 * 轮密钥加变换 - 将每一列与扩展密钥进行异或 144 */ 145 void AddRoundKey(byte mtx[4 * 4], word k[4]) 146 { 147 for (int i = 0; i<4; ++i) 148 { 149 word k1 = k[i] >> 24; 150 word k2 = (k[i] << 8) >> 24; 151 word k3 = (k[i] << 16) >> 24; 152 word k4 = (k[i] << 24) >> 24; 153 154 mtx[i] = mtx[i] ^ byte(k1.to_ulong()); 155 mtx[i + 4] = mtx[i + 4] ^ byte(k2.to_ulong()); 156 mtx[i + 8] = mtx[i + 8] ^ byte(k3.to_ulong()); 157 mtx[i + 12] = mtx[i + 12] ^ byte(k4.to_ulong()); 158 } 159 } 160 161 /**************************下面是解密的逆变换函数***********************/ 162 /** 163 * 逆S盒变换 164 */ 165 void InvSubBytes(byte mtx[4 * 4]) 166 { 167 for (int i = 0; i<16; ++i) 168 { 169 int row = mtx[i][7] * 8 + mtx[i][6] * 4 + mtx[i][5] * 2 + mtx[i][4]; 170 int col = mtx[i][3] * 8 + mtx[i][2] * 4 + mtx[i][1] * 2 + mtx[i][0]; 171 mtx[i] = Inv_S_Box[row][col]; 172 } 173 } 174 175 /** 176 * 逆行变换 - 以字节为单位循环右移 177 */ 178 void InvShiftRows(byte mtx[4 * 4]) 179 { 180 // 第二行循环右移一位 181 byte temp = mtx[7]; 182 for (int i = 3; i>0; --i) 183 mtx[i + 4] = mtx[i + 3]; 184 mtx[4] = temp; 185 // 第三行循环右移两位 186 for (int i = 0; i<2; ++i) 187 { 188 temp = mtx[i + 8]; 189 mtx[i + 8] = mtx[i + 10]; 190 mtx[i + 10] = temp; 191 } 192 // 第四行循环右移三位 193 temp = mtx[12]; 194 for (int i = 0; i<3; ++i) 195 mtx[i + 12] = mtx[i + 13]; 196 mtx[15] = temp; 197 } 198 199 void InvMixColumns(byte mtx[4 * 4]) 200 { 201 byte arr[4]; 202 for (int i = 0; i<4; ++i) 203 { 204 for (int j = 0; j<4; ++j) 205 arr[j] = mtx[i + j * 4]; 206 207 mtx[i] = GFMul(0x0e, arr[0]) ^ GFMul(0x0b, arr[1]) ^ GFMul(0x0d, arr[2]) ^ GFMul(0x09, arr[3]); 208 mtx[i + 4] = GFMul(0x09, arr[0]) ^ GFMul(0x0e, arr[1]) ^ GFMul(0x0b, arr[2]) ^ GFMul(0x0d, arr[3]); 209 mtx[i + 8] = GFMul(0x0d, arr[0]) ^ GFMul(0x09, arr[1]) ^ GFMul(0x0e, arr[2]) ^ GFMul(0x0b, arr[3]); 210 mtx[i + 12] = GFMul(0x0b, arr[0]) ^ GFMul(0x0d, arr[1]) ^ GFMul(0x09, arr[2]) ^ GFMul(0x0e, arr[3]); 211 } 212 } 213 214 /******************************下面是密钥扩展部分***********************/ 215 /** 216 * 将4个 byte 转换为一个 word. 217 */ 218 word Word(byte& k1, byte& k2, byte& k3, byte& k4) 219 { 220 word result(0x00000000); 221 word temp; 222 temp = k1.to_ulong(); // K1 223 temp <<= 24; 224 result |= temp; 225 temp = k2.to_ulong(); // K2 226 temp <<= 16; 227 result |= temp; 228 temp = k3.to_ulong(); // K3 229 temp <<= 8; 230 result |= temp; 231 temp = k4.to_ulong(); // K4 232 result |= temp; 233 return result; 234 } 235 236 /** 237 * 按字节 循环左移一位 238 * 即把[a0, a1, a2, a3]变成[a1, a2, a3, a0] 239 */ 240 word RotWord(word& rw) 241 { 242 word high = rw << 8; 243 word low = rw >> 24; 244 return high | low; 245 } 246 247 /** 248 * 对输入word中的每一个字节进行S-盒变换 249 */ 250 word SubWord(word& sw) 251 { 252 word temp; 253 for (int i = 0; i<32; i += 8) 254 { 255 int row = sw[i + 7] * 8 + sw[i + 6] * 4 + sw[i + 5] * 2 + sw[i + 4]; 256 int col = sw[i + 3] * 8 + sw[i + 2] * 4 + sw[i + 1] * 2 + sw[i]; 257 byte val = S_Box[row][col]; 258 for (int j = 0; j<8; ++j) 259 temp[i + j] = val[j]; 260 } 261 return temp; 262 } 263 264 /** 265 * 密钥扩展函数 - 对128位密钥进行扩展得到 w[4*(Nr+1)] 266 */ 267 void KeyExpansion(byte key[4 * Nk], word w[4 * (Nr + 1)]) 268 { 269 word temp; 270 int i = 0; 271 // w[]的前4个就是输入的key 272 while (i < Nk) 273 { 274 w[i] = Word(key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]); 275 ++i; 276 } 277 278 i = Nk; 279 280 while (i < 4 * (Nr + 1)) 281 { 282 temp = w[i - 1]; // 记录前一个word 283 if (i % Nk == 0) 284 w[i] = w[i - Nk] ^ SubWord(RotWord(temp)) ^ Rcon[i / Nk - 1]; 285 else 286 w[i] = w[i - Nk] ^ temp; 287 ++i; 288 } 289 } 290 291 /******************************下面是加密和解密函数**************************/ 292 /** 293 * 加密 294 */ 295 void encrypt(byte in[4 * 4], word w[4 * (Nr + 1)]) 296 { 297 word key[4]; 298 for (int i = 0; i<4; ++i) 299 key[i] = w[i]; 300 AddRoundKey(in, key); 301 302 for (int round = 1; round<Nr; ++round) 303 { 304 SubBytes(in); 305 ShiftRows(in); 306 MixColumns(in); 307 for (int i = 0; i<4; ++i) 308 key[i] = w[4 * round + i]; 309 AddRoundKey(in, key); 310 } 311 312 SubBytes(in); 313 ShiftRows(in); 314 for (int i = 0; i<4; ++i) 315 key[i] = w[4 * Nr + i]; 316 AddRoundKey(in, key); 317 } 318 319 /** 320 * 解密 321 */ 322 void decrypt(byte in[4 * 4], word w[4 * (Nr + 1)]) 323 { 324 word key[4]; 325 for (int i = 0; i<4; ++i) 326 key[i] = w[4 * Nr + i]; 327 AddRoundKey(in, key); 328 329 for (int round = Nr - 1; round>0; --round) 330 { 331 InvShiftRows(in); 332 InvSubBytes(in); 333 for (int i = 0; i<4; ++i) 334 key[i] = w[4 * round + i]; 335 AddRoundKey(in, key); 336 InvMixColumns(in); 337 } 338 339 InvShiftRows(in); 340 InvSubBytes(in); 341 for (int i = 0; i<4; ++i) 342 key[i] = w[i]; 343 AddRoundKey(in, key); 344 } 345 346 /**********************************************************************/ 347 /* */ 348 /* 测试 */ 349 /* */ 350 /**********************************************************************/ 351 int main() 352 { 353 byte key[16] = { 0x2b, 0x7e, 0x15, 0x16, 354 0x28, 0xae, 0xd2, 0xa6, 355 0xab, 0xf7, 0x15, 0x88, 356 0x09, 0xcf, 0x4f, 0x3c }; 357 358 byte plain[16] = { 0x32, 0x88, 0x31, 0xe0, 359 0x43, 0x5a, 0x31, 0x37, 360 0xf6, 0x30, 0x98, 0x07, 361 0xa8, 0x8d, 0xa2, 0x34 }; 362 // 输出密钥 363 cout << "密钥是:"; 364 for (int i = 0; i<16; ++i) 365 cout << hex << key[i].to_ulong() << " "; 366 cout << endl; 367 368 word w[4 * (Nr + 1)]; 369 KeyExpansion(key, w); 370 371 // 输出待加密的明文 372 cout << endl << "待加密的明文:" << endl; 373 for (int i = 0; i<16; ++i) 374 { 375 cout << hex << plain[i].to_ulong() << " "; 376 if ((i + 1) % 4 == 0) 377 cout << endl; 378 } 379 cout << endl; 380 381 // 加密,输出密文 382 encrypt(plain, w); 383 cout << "加密后的密文:" << endl; 384 for (int i = 0; i<16; ++i) 385 { 386 cout << hex << plain[i].to_ulong() << " "; 387 if ((i + 1) % 4 == 0) 388 cout << endl; 389 } 390 cout << endl; 391 392 // 解密,输出明文 393 decrypt(plain, w); 394 cout << "解密后的明文:" << endl; 395 for (int i = 0; i<16; ++i) 396 { 397 cout << hex << plain[i].to_ulong() << " "; 398 if ((i + 1) % 4 == 0) 399 cout << endl; 400 } 401 cout << endl; 402 return 0; 403 }
测试用例如下图:
测试结果截图:
可见,测试结果和预期输出相同,表明对数据的加密和解密成功!!!
下面我们来写 AES 对文件的加密和解密,在对 128 位的数据加解密成功以后,对文件的加解密就很简单了!只需要每次读 128 位,加密以后,将 128 位的密文写入另外一个文件…..如此循环,直到文件尾。下面是对一张图片进行 AES 加密和解密的测试代码(效率先不管了,有时间我再优化):
1 //#include <fstream> 2 typedef bitset<8> byte; 3 typedef bitset<32> word; 4 /** 5 * 将一个char字符数组转化为二进制 6 * 存到一个 byte 数组中 7 */ 8 void charToByte(byte out[16], const char s[16]) 9 { 10 for (int i = 0; i<16; ++i) 11 for (int j = 0; j<8; ++j) 12 out[i][j] = ((s[i] >> j) & 1); 13 } 14 15 /** 16 * 将连续的128位分成16组,存到一个 byte 数组中 17 */ 18 void divideToByte(byte out[16], bitset<128>& data) 19 { 20 bitset<128> temp; 21 for (int i = 0; i<16; ++i) 22 { 23 temp = (data << 8 * i) >> 120; 24 out[i] = temp.to_ulong(); 25 } 26 } 27 28 /** 29 * 将16个 byte 合并成连续的128位 30 */ 31 bitset<128> mergeByte(byte in[16]) 32 { 33 bitset<128> res; 34 res.reset(); // 置0 35 bitset<128> temp; 36 for (int i = 0; i<16; ++i) 37 { 38 temp = in[i].to_ulong(); 39 temp <<= 8 * (15 - i); 40 res |= temp; 41 } 42 return res; 43 } 44 45 int main() 46 { 47 string keyStr = "abcdefghijklmnop"; 48 byte key[16]; 49 charToByte(key, keyStr.c_str()); 50 // 密钥扩展 51 word w[4 * (Nr + 1)]; 52 KeyExpansion(key, w); 53 54 bitset<128> data; 55 byte plain[16]; 56 // 将文件 flower.jpg 加密到 cipher.txt 中 57 ifstream in; 58 ofstream out; 59 in.open("D://flower.jpg", ios::binary); 60 out.open("D://cipher.txt", ios::binary); 61 while (in.read((char*)&data, sizeof(data))) 62 { 63 divideToByte(plain, data); 64 encrypt(plain, w); 65 data = mergeByte(plain); 66 out.write((char*)&data, sizeof(data)); 67 data.reset(); // 置0 68 } 69 in.close(); 70 out.close(); 71 72 // 解密 cipher.txt,并写入图片 flower1.jpg 73 in.open("D://cipher.txt", ios::binary); 74 out.open("D://flower1.jpg", ios::binary); 75 while (in.read((char*)&data, sizeof(data))) 76 { 77 divideToByte(plain, data); 78 decrypt(plain, w); 79 data = mergeByte(plain); 80 out.write((char*)&data, sizeof(data)); 81 data.reset(); // 置0 82 } 83 in.close(); 84 out.close(); 85 86 return 0; 87 }
(全文完)
更新 —— 2014.12.21
有限域 GF(28) 上的乘法改用查表的方式实现,AES的加密速度马上提升 80% 以上,所以建议最好使用查表的方式。下面是 AES 算法中用到的 6 个乘法结果表:
1 byte Mul_02[256] = { 2 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 3 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 4 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 5 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e, 6 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e, 7 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe, 8 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, 9 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe, 10 0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05, 11 0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25, 12 0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45, 13 0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65, 14 0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85, 15 0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5, 16 0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5, 17 0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5 18 }; 19 20 byte Mul_03[256] = { 21 0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11, 22 0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21, 23 0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71, 24 0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41, 25 0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1, 26 0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1, 27 0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1, 28 0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81, 29 0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a, 30 0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba, 31 0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea, 32 0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda, 33 0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a, 34 0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a, 35 0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a, 36 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a 37 }; 38 39 byte Mul_09[256] = { 40 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, 41 0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7, 42 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, 43 0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc, 44 0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01, 45 0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91, 46 0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a, 47 0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa, 48 0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b, 49 0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b, 50 0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0, 51 0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30, 52 0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed, 53 0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d, 54 0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6, 55 0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46 56 }; 57 58 byte Mul_0b[256] = { 59 0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69, 60 0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9, 61 0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12, 62 0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2, 63 0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f, 64 0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f, 65 0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4, 66 0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54, 67 0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e, 68 0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e, 69 0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5, 70 0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55, 71 0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68, 72 0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8, 73 0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13, 74 0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3 75 }; 76 77 byte Mul_0d[256] = { 78 0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b, 79 0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b, 80 0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0, 81 0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20, 82 0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26, 83 0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6, 84 0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d, 85 0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d, 86 0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91, 87 0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41, 88 0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a, 89 0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa, 90 0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc, 91 0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c, 92 0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47, 93 0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97 94 }; 95 96 byte Mul_0e[256] = { 97 0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a, 98 0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba, 99 0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81, 100 0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61, 101 0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7, 102 0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17, 103 0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c, 104 0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc, 105 0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b, 106 0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb, 107 0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0, 108 0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20, 109 0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6, 110 0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56, 111 0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d, 112 0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d 113 };