参考链接

可厉害的土豆的个人空间-可厉害的土豆个人主页-哔哩哔哩视频

[原创]密码学基础:AES加密算法-密码应用-看雪-安全社区|安全招聘|kanxue.com

AES128-CMAC_cmac算法-CSDN博客

RFC 4493 - AES-CMAC 算法

前置知识

有限域(伽罗瓦域)

有限域有时也称伽罗瓦域,它指的是由有限个元素组成的集合,在这个集合内可以执行加、减、乘和逆运算。而在密码编码学中,我们只研究拥有有限个元素的域,也就是有限域。域中包含元素的个数称为域的==阶==。只有当m是一个素数幂时,即$m=p^n$(其中n为正整数,p为素数),阶为m的域才存在。p称为这个有限域的特征。

也就是说,有限域中元素的个数可以是11(p=11是一个素数,n=1)、可以是81(p=3是一个素数,n=4)、也可以是256(p=2是一个素数,n=8)…但有限域的中不可能拥有12个元素,因为12=2·2·3,因此12也不是一个素数幂。

有限域中最直观的例子就是阶为素数的域,即n=1的域。域GF(p)的元素可以用整数0、1、…、p-1来表示。域的两种操作就是:

  1. 模整数加法
  2. 整数乘法模p。

由于p是一个素数,整数环Z(Z表示为GF(p))也成为拥有素数个元素的素数域或者伽罗瓦域。GF(p)中所有的非零元素都存在逆元,GF(p)内所有的运算都是模p实现的。

素数域内的算数运算规则如下:

  1. 加法和乘法都是通过模p实现的
  2. 任何一个元素a的加法逆元都是由$(a+a的逆元) \quad mod\quad p=0$得到的
  3. 任何一个非零元素a的乘法逆元定义为$a·a的逆元 \quad mod \quad p=1$

[!tip]

举个例子,在素域GF(5)={0、1、2、3、4}中,2的加法逆元为3,这是因为2+(3)=5,5mod5=0,所以2+3=5mod5=0。2的乘法逆元为3,这是因为2·3=6,6mod5=1,所以2·3=6mod5=1。(在很多地方a的加法逆元用-a表示,a的乘法逆元用1/a表示)

[!note]

注:GF(2)是一个非常重要的素域,也是存在的最小的有限域,由于GF(2)的加法,即模2加法与异或(XOR)门等价,GF(2)的乘法与逻辑与(AND)门等价,所以GF(2)对AES非常重要。

如果有限域的阶不是素数,则这样的有限域内的加法和乘法运算就不能用模整数加法和整数乘法模p表示。而且m>1的域被称为扩展域,为了处理扩展域,我们就要使用不同的符号表示扩展域内的元素,使用不同的规则执行扩展域内元素的算术运算。
  在扩展域$GF(2^m)$中,元素并不是用整数表示的,而是用系数为域GF(2)中元素的多项式表示。这个多项式最大的度(幂)为m-1,所以每个元素共有m个系数,在AES算法使用的域$GF(2^8)$中,每个元素$A∈GF(2^8)$都可以表示为:
$$
A(x)=a_7x^7+a_6x^6+\ldots+a_1x+a_0\quad,a_i\in GF(2)=0,1
$$

[!note]

注意:在域$GF(2^8)$中这样的多项式共有256个,这256个多项式组成的集合就是扩展域$GF(2^8)$。每个多项式都可以按一个8位项链的数值形式存储:
$$
A=(a7,a6,a5,a4,a3,a2,a1,a0)
$$
像$x^7$、$x^6$等因子都无需存储,因为从位的位置就可以清楚地判断出每个系数对应的幂。

在AES算法中的密钥加法层中就使用了这部分的知识,但是不是很明显,因为我们通常把扩展域中的加法当作异或运算进行处理了,因为在扩展域中的加减法处理都是在底层域GF(2)内完成的,与按位异或运算等价。假设$A(x)、B(x)∈GF(2^m)$,计算两个元素之和的方法就是:
$$
C(x)=A(x)+B(x)=\sum_{i=0}^{m-1}C_ix^i\quad c_i\equiv(a_i+b_i)mod2
$$
而两个元素之差的计算公式就是:
$$
C(x)=A(x)-B(x)=\sum_{i=0}^{m-1}c_ix^i\quad c_i\equiv(a_i-b_i)mod2\equiv(a_i+b_i)mod2
$$

[!note]

注:在减法运算中减号之所以变成加号,这就和二进制减法的性质有关了,大家可以试着验算下。从上述两个公式中我们发现在扩展域中加法和减法等价,并且与XOR等价(异或运算也被称作二进制加法)。

扩展域的乘法主要运用在AES算法的列混淆层(Mix Column)中,也是列混淆层中最重要的操作。我们项要将扩展域中的两个元素用多项式形式展开,然后使用标准的多项式乘法规则将两个多项式相乘:
$$
\begin{aligned}&A(x)\cdot B(x)=(a_{m-1}x^{m-1}+\ldots+a_0)\cdot(b_{m-1}x^{m-1}+\ldots+b_0)\&C^{\prime}(x)=c_{2m-2}^{\prime}x^{2m-2}+\ldots+c_0^{\prime}\&\text{其中:}\&c_0^{\prime}=a_0b_0mod2\&c_1^{\prime}=(a_1b_0+a_0b_1)mod2\&c_{2m-2}^{\prime}=a_{m-1}b_{m-1}mod2\end{aligned}
$$
注意:通常在多项式乘法中C(x)的度会大于m-1,因此需要对此进行化简,而化简的基本思想与素域内乘法情况相似:在素域GF(p)中,将两个整数相乘得到的结果除以一个素数,化简后的结果就是最后的余数。而在扩展域中进行的操作就是:将两个多项式相乘的结果除以一个不可约多项式,最后的结果就是最后的余数。(这里的不可约多项式大致可以看作一个素数)

假设$A(x),B(x)\in GF(2^m)$,且$P(x)=\sum_{i=0}^{m}p_ix^i$其中$p\in GF(2)$是一个不可约多项式,那么两个元素$A(x)$和$B(x)$的乘法运算为:
$$
C(x)=A(x) * B(x) \quad mod \quad P(x)
$$

[!tip]

在AES算法中使用的不可约多项式为:
$$
P(x)=x^8+x^4+x^3+x^1+1
$$

MAC

消息认证码 (Message Authentication Code) 是一种确认完整性并进行认证的技术。消息认证码是一种与密钥相关联的单向散列函数。

MAC计算过程

实现方法 简要描述
HMAC (Hash-based Message Authentication Code) 结合散列函数和密钥,使用密钥对消息进行哈希运算,生成固定长度的哈希值
CMAC (Cipher-based Message Authentication Code) Poly1305 使用对称加密算法(如AES)和密钥来生成MAC
GCM (Galois/Counter Mode) HMAC-based Key Derivation Functions (HKDF) 一种用于生成消息认证码(Message Authentication Code,MAC)的专用算法 一种带有认证的加密模式,通常与AES一起使用 一种基于HMAC的密钥派生函数

AES

AES计算过程如下:

AES算法主要有四种操作处理:

  1. 密钥加法层(也叫轮密钥加,英文Add Round Key)
  2. 字节代换层(SubByte)
  3. 行位移层(Shift Rows)
  4. 列混淆层(Mix Column)。

而明文x和密钥k都是由16个字节(128位)组成的数据(当然密钥还支持192位和256位的长度,暂时不考虑)。

AES加密框图

明文和密钥都是按照字节的先后顺序从上到下、从左到右进行排列的。而加密出的密文读取顺序也是按照这个顺序读取的,相当于将数组还原成字符串的模样了,然后再解密的时候又是按照4·4数组处理的。

排列顺序

AES算法在处理的轮数上只有最后一轮操作与前面的轮处理上有些许不同(最后一轮只是少了列混淆处理),在轮处理开始前还单独进行了一次轮密钥加的处理。在处理轮数上,我们只考虑128位密钥的10轮处理。接下来,就开始一步步的介绍AES算法的处理流程了。

AES加密流程

具体流程如下图所示:

AES具体流程

密钥轮加层

在密钥加法层中有两个输入的参数,分别是明文和子密钥k[0],而且这两个输入都是128位的。k[0]实际上就等同于密钥k,具体原因在密钥生成中进行介绍。我们前面在介绍扩展域加减法中提到过,在扩展域中加减法操作和异或运算等价,所以这里的处理也就异常的简单了,只需要将两个输入的数据进行按字节==异或==操作就会得到运算的结果。

密钥加法层运算过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int AddRoundKey(unsigned char(*PlainArray)[4], unsigned char(*ExtendKeyArray)[44], unsigned int MinCol)
{
int ret = 0;

for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
PlainArray[i][j] ^= ExtendKeyArray[i][MinCol + j];
}
}

return ret;
}

字节代换层

字节代换层的主要功能就是让输入的数据通过==S_box(s盒)表==完成从一个字节到另一个字节的映射,这里的S_box表是通过某种方法计算出来的。

S_box表是一个拥有256个字节元素的数组,可以将其定义为一维数组,也可以将其定义为16·16的二维数组,如果将其定义为二维数组,读取S_box数据的方法就是要将输入数据的每个字节的高四位作为第一个下标,第四位作为第二个下标,略有点麻烦。这里建议将其视作一维数组即可。逆S盒与S盒对应,用于解密时对数据处理,我们对解密时的程序处理称作逆字节代换,只是使用的代换表盒加密时不同而已。

==S盒==

S盒

==逆S盒==

逆S盒

那么处理过程如下:

字节代换的处理过程

假设p1数据为0x19。那么我们需要将这个数据分为高四字节1和低四字节9,高四字节对应行,低四字节对应列。如果S盒算法0x190xd4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//S盒
const unsigned char S_Table[16][16] =
{
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
};

//字节代换
int Plain_S_Substitution(unsigned char *PlainArray)
{
int ret = 0;

for (int i = 0; i < 16; i++)
{
PlainArray[i] = S_Table[PlainArray[i] >> 4][PlainArray[i] & 0x0F];
}

return ret;
}


//逆S盒
const unsigned char ReS_Table[16][16] =
{
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
};

//逆字节代换
int Cipher_S_Substitution(unsigned char *CipherArray)
{
int ret = 0;

for (int i = 0; i < 16; i++)
{
CipherArray[i] = ReS_Table[CipherArray[i] >> 4][CipherArray[i] & 0x0F];
}

return ret;
}

行移位层

行位移操作最为简单,它是用来将输入数据作为一个4·4的字节矩阵进行处理的,然后将这个矩阵的字节进行位置上的置换。

行移位层属于AES手动的扩散层,目的是将单个位上的变换扩散到影响整个状态,从而达到雪崩效应。

在加密时行位移处理与解密时的处理相反,我们这里将解密时的处理称作逆行位移。它之所以称作行位移,是因为它只在4·4矩阵的行间进行操作,每行4字节的数据。在加密时,保持矩阵的第一行不变,第二行向左移动8Bit(一个字节)、第三行向左移动2个字节、第四行向左移动3个字节。而在解密时恰恰相反,依然保持第一行不变,将第二行向右移动一个字节、第三行右移2个字节、第四行右移3个字节。操作结束!

==正向行移位==

正向行位移

我们可以将移位看作是循环左移,不同的行移不同数量字节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int ShiftRows(unsigned int *PlainArray)
{
int ret = 0;

//第一行 不移位
//PlainArray[0] = PlainArray[0];

//第二行 左移8Bit
PlainArray[1] = (PlainArray[1] >> 8) | (PlainArray[1] << 24);

//第三行 左移16Bit
PlainArray[2] = (PlainArray[2] >> 16) | (PlainArray[2] << 16);

//第四行 左移24Bit
PlainArray[3] = (PlainArray[3] >> 24) | (PlainArray[3] << 8);

return ret;
}

==逆向行移位==

逆向行移位

我们可以将移位看作是循环右移,不同的行移不同数量字节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int ReShiftRows(unsigned int *CipherArray)
{
int ret = 0;

//第一行 不移位
//CipherArray[0] = CipherArray[0];

//第二行 右移8Bit
CipherArray[1] = (CipherArray[1] << 8) | (CipherArray[1] >> 24);

//第三行 右移16Bit
CipherArray[2] = (CipherArray[2] << 16) | (CipherArray[2] >> 16);

//第四行 右移24Bit
CipherArray[3] = (CipherArray[3] << 24) | (CipherArray[3] >> 8);

return ret;
}

列混淆层

列混淆子层是AES算法中最为复杂的部分,属于扩散层,列混淆操作是AES算法中主要的扩散元素,它混淆了输入矩阵的每一列,使输入的每个字节都会影响到4个输出字节。行位移子层和列混淆子层的组合使得经过三轮处理以后,矩阵的每个字节都依赖于16个明文字节成可能。其中包含了矩阵乘法、伽罗瓦域内加法和乘法的相关知识。

在加密的正向列混淆中,我们要将输入的4·4矩阵==左乘==一个==给定的==4·4矩阵。而它们之间的加法、乘法都在扩展域$GF(2^8)$中进行。

==正向列混淆矩阵==

正向列混淆矩阵

==逆向列混淆矩阵==

逆向列混淆矩阵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//列混淆左乘矩阵
const unsigned char MixArray[4][4] =
{
0x02, 0x03, 0x01, 0x01,
0x01, 0x02, 0x03, 0x01,
0x01, 0x01, 0x02, 0x03,
0x03, 0x01, 0x01, 0x02
};

int MixColum(unsigned char(*PlainArray)[4])
{
int ret = 0;
//定义变量
unsigned char ArrayTemp[4][4];

//初始化变量
memcpy(ArrayTemp, PlainArray, 16);

//矩阵乘法 4*4
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
PlainArray[i][j] =
MixArray[i][0] * ArrayTemp[0][j] +
MixArray[i][1] * ArrayTemp[1][j] +
MixArray[i][2] * ArrayTemp[2][j] +
MixArray[i][3] * ArrayTemp[3][j];
}
}

return ret;
}

我们发现在上述的矩阵乘法中,出现了加法和乘法运算,我们前面也提到过在扩展域中加法操作等同于异或运算,而乘法操作需要一个特殊的方式进行处理,于是我们就先把代码中的加号换成异或符号,然后将伽罗瓦域的乘法定义成一个有两个参数的函数,并让他返回最后计算结果。于是列混淆的代码就会变成下面的样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const unsigned char MixArray[4][4] =
{
0x02, 0x03, 0x01, 0x01,
0x01, 0x02, 0x03, 0x01,
0x01, 0x01, 0x02, 0x03,
0x03, 0x01, 0x01, 0x02
};

int MixColum(unsigned char(*PlainArray)[4])
{
int ret = 0;
//定义变量
unsigned char ArrayTemp[4][4];

//初始化变量
memcpy(ArrayTemp, PlainArray, 16);

//矩阵乘法 4*4
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
PlainArray[i][j] =
GaloisMultiplication(MixArray[i][0], ArrayTemp[0][j]) ^
GaloisMultiplication(MixArray[i][1], ArrayTemp[1][j]) ^
GaloisMultiplication(MixArray[i][2], ArrayTemp[2][j]) ^
GaloisMultiplication(MixArray[i][3], ArrayTemp[3][j]);
}
}
return ret;
}

接下来我们就只用处理伽罗瓦域乘法相关处理了,由于前面介绍过相关概念,所以代码就不在此进行讲解了,大家可以参考下方的代码注释进行理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
///////////////////////////////////////////////////////////////
//功能: 伽罗瓦域内的乘法运算 GF(128)
//参数: Num_L 输入的左参数
// Num_R 输入的右参数
//返回值:计算结果
char GaloisMultiplication(unsigned char Num_L, unsigned char Num_R)
{
//定义变量
unsigned char Result = 0; //伽罗瓦域内乘法计算的结果

while (Num_L)
{
//如果Num_L最低位是1就异或Num_R,相当于加上Num_R * 1
if (Num_L & 0x01)
{
Result ^= Num_R;
}

//Num_L右移一位,相当于除以2
Num_L = Num_L >> 1;

//如果Num_R最高位为1
if (Num_R & 0x80)
{
//左移一位相当于乘二
Num_R = Num_R << 1; //注:这里会丢失最高位,但是不用担心

Num_R ^= 0x1B; //计算伽罗瓦域内除法Num_R = Num_R / (x^8(刚好丢失最高位) + x^4 + x^3 + x^1 + 1)
}
else
{
//左移一位相当于乘二
Num_R = Num_R << 1;
}
}
return Result;
}

在解密的逆向列混淆中与正向列混淆的不同之处在于使用的左乘矩阵不同,它与正向列混淆的左乘矩阵互为逆矩阵,也就是说,数据矩阵同时左乘这两个矩阵后,数据矩阵不会发生任何变化。

加解密验证

最后只需要将得到的密文矩阵写成128位的即可

子密钥生成

子密钥的生成是以列为单位进行的,一列是32Bit,四列组成子密钥共128Bit。生成子密钥的数量比AES算法的轮数多一个,因为第一个密钥加法层进行密钥漂白时也需要子密钥。密钥漂白是指在AES的输入和输出中都使用的子密钥的XOR加法。子密钥在图中都存储在W[0]、W[1]、…、W[43]的扩展密钥数组之中。k1-k16表示原始密钥对应的字节,而图中子密钥k0与原始子密钥相同。

密钥生成

在生成的扩展密钥中W的下标如果是4的倍数时(从零开始)需要对异或的参数进行G函数处理。扩展密钥生成有关公式如下:
$$
W[i]=W[i-4] \oplus G(W[i-1])\quad 4< i <44
$$
是4的倍数

如果不是4的倍数的话,那么为:
$$
W[i]=W[i-4] \oplus W[i-1]\quad 4< i <44
$$
不是4的倍数

经过以上计算即可得到子密钥

子密钥结果

函数G()首先将4个输入字节进行翻转,并执行一个按字节的S盒代换,最后用第一个字节与轮系数Rcon进行异或运算。轮系数是一个有10个元素的一维数组,一个元素1个字节。G()函数存在的目的有两个,一是增加密钥编排中的非线性;二是消除AES中的对称性。这两种属性都是抵抗某些分组密码攻击必要的。

轮系数 0x01 0x02 0x04 0x08 0x10 0x20 0x40 0x80 0x1B 0x36
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//用于密钥扩展    Rcon[0]作为填充,没有实际用途
const unsigned int Rcon[11] = { 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 };


int Key_S_Substitution(unsigned char(*ExtendKeyArray)[44], unsigned int nCol)
{
int ret = 0;

for (int i = 0; i < 4; i++)
{
ExtendKeyArray[i][nCol] = S_Table[(ExtendKeyArray[i][nCol]) >> 4][(ExtendKeyArray[i][nCol]) & 0x0F];
}

return ret;
}


int G_Function(unsigned char(*ExtendKeyArray)[44], unsigned int nCol)
{
int ret = 0;

//1、将扩展密钥矩阵的nCol-1列复制到nCol列上,并将nCol列第一行的元素移动到最后一行,其他行数上移一行
for (int i = 0; i < 4; i++)
{
ExtendKeyArray[i][nCol] = ExtendKeyArray[(i + 1) % 4][nCol - 1];
}

//2、将nCol列进行S盒替换
Key_S_Substitution(ExtendKeyArray, nCol);

//3、将该列第一行元素与Rcon进行异或运算
ExtendKeyArray[0][nCol] ^= Rcon[nCol / 4];

return ret;
}


int CalculateExtendKeyArray(const unsigned char(*PasswordArray)[4], unsigned char(*ExtendKeyArray)[44])
{
int ret = 0;

//1、将密钥数组放入前四列扩展密钥组
for (int i = 0; i < 16; i++)
{
ExtendKeyArray[i & 0x03][i >> 2] = PasswordArray[i & 0x03][i >> 2];
}

//2、计算扩展矩阵的后四十列
for (int i = 1; i < 11; i++) //进行十轮循环
{
//(1)如果列号是4的倍数,这执行G函数 否则将nCol-1列复制到nCol列上
G_Function(ExtendKeyArray, 4*i);

//(2)每一轮中,各列进行异或运算
// 列号是4的倍数
for (int k = 0; k < 4; k++)//行号
{
ExtendKeyArray[k][4 * i] = ExtendKeyArray[k][4 * i] ^ ExtendKeyArray[k][4 * (i - 1)];
}

// 其他三列
for (int j = 1; j < 4; j++)//每一轮的列号
{
for (int k = 0; k < 4; k++)//行号
{
ExtendKeyArray[k][4 * i + j] = ExtendKeyArray[k][4 * i + j - 1] ^ ExtendKeyArray[k][4 * (i - 1) + j];
}
}
}

return ret;
}

解密

AES解密过程

我们可以看到解密就是加密的反过程,其中用到了逆字节代换和逆向列混淆矩阵

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# ifndef _AES_TABLE_H_
# define _AES_TABLE_H_

//===================================================================================================
//===================================================================================================
//===================================================================================================


//用于密钥扩展 Rcon[0]作为填充,没有实际用途
const unsigned int Rcon[11] = { 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 };


//S盒
const unsigned char S_Table[16][16] =
{
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
};

//逆S盒
const unsigned char ReS_Table[16][16] =
{
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
};


//列混淆左乘矩阵
const unsigned char MixArray[4][4] =
{
0x02, 0x03, 0x01, 0x01,
0x01, 0x02, 0x03, 0x01,
0x01, 0x01, 0x02, 0x03,
0x03, 0x01, 0x01, 0x02
};

//列混淆左乘逆矩阵
const unsigned char ReMixArray[4][4] =
{
0x0E, 0x0B, 0x0D, 0x09,
0x09, 0x0E, 0x0B, 0x0D,
0x0D, 0x09, 0x0E, 0x0B,
0x0B, 0x0D, 0x09, 0x0E
};


//===================================================================================================
//===================================================================================================
//===================================================================================================


# endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
# define  _CRT_SECURE_NO_WARNINGS
# include "stdio.h"
# include "stdlib.h"
# include "string.h"
# include "AES_Table.h"


int Print_Table(const unsigned char* Table, int TableSize)
{
for (int i = 0; i < TableSize; i++)
{
printf("%02X ", Table[i]);
}
printf("\n");

return 0;
}

///////////////////////////////////////////////////////////////
//功能: 将字符串转换成AES标准数组
//参数: String 输入的字符串
// Array 输出的AES标准数组
//返回值:int
int StringToArray(const unsigned char *String, unsigned char (*Array)[4])
{
int ret = 0;

for (int i = 0; i < 16; i++)
{
Array[i & 0x03][i >> 2] = String[i];
}

return ret;
}

///////////////////////////////////////////////////////////////
//功能: 将AES标准数组转换成字符串
//参数: Array 输入的AES标准数组
// String 输出的字符串
//返回值:int
int ArrayToString(const unsigned char(*Array)[4], unsigned char *String)
{
int ret = 0;

for (int i = 0; i < 16; i++)
{
String[i] = Array[i & 0x03][i >> 2];
}

return ret;
}

///////////////////////////////////////////////////////////////
//功能: 对指定的扩展密钥矩阵列进行S盒替换
//参数: ExtendKeyArray 输入的扩展密钥矩阵
// nCol 输入的列号
//返回值:int
int Key_S_Substitution(unsigned char(*ExtendKeyArray)[44], unsigned int nCol)
{
int ret = 0;

for (int i = 0; i < 4; i++)
{
ExtendKeyArray[i][nCol] = S_Table[(ExtendKeyArray[i][nCol]) >> 4][(ExtendKeyArray[i][nCol]) & 0x0F];
}

return ret;
}

///////////////////////////////////////////////////////////////
//功能: 对列号是四的倍数的密钥扩展矩阵执行G函数
//参数: ExtendKeyArray 输入的扩展密钥矩阵
// nCol 输入的列号
//返回值:int
int G_Function(unsigned char(*ExtendKeyArray)[44], unsigned int nCol)
{
int ret = 0;

//1、将扩展密钥矩阵的nCol-1列复制到nCol列上,并将nCol列第一行的元素移动到最后一行,其他行数上移一行
for (int i = 0; i < 4; i++)
{
ExtendKeyArray[i][nCol] = ExtendKeyArray[(i + 1) % 4][nCol - 1];
}

//2、将nCol列进行S盒替换
Key_S_Substitution(ExtendKeyArray, nCol);

//3、将该列第一行元素与Rcon进行异或运算
ExtendKeyArray[0][nCol] ^= Rcon[nCol / 4];

return ret;
}

///////////////////////////////////////////////////////////////
//功能: 计算扩展密钥数组
//参数: PasswordArray 输入的密钥字符串数组
// ExtendKeyArray 输出的扩展密钥数组
//返回值:int
int CalculateExtendKeyArray(const unsigned char(*PasswordArray)[4], unsigned char(*ExtendKeyArray)[44])
{
int ret = 0;

//1、将密钥数组放入前四列扩展密钥组
for (int i = 0; i < 16; i++)
{
ExtendKeyArray[i & 0x03][i >> 2] = PasswordArray[i & 0x03][i >> 2];
}

//2、计算扩展矩阵的后四十列
for (int i = 1; i < 11; i++) //进行十轮循环
{
//(1)如果列号是4的倍数,这执行G函数 否则将nCol-1列复制到nCol列上
G_Function(ExtendKeyArray, 4*i);

//(2)每一轮中,各列进行异或运算
// 列号是4的倍数
for (int k = 0; k < 4; k++)//行号
{
ExtendKeyArray[k][4 * i] = ExtendKeyArray[k][4 * i] ^ ExtendKeyArray[k][4 * (i - 1)];
}

// 其他三列
for (int j = 1; j < 4; j++)//每一轮的列号
{
for (int k = 0; k < 4; k++)//行号
{
ExtendKeyArray[k][4 * i + j] = ExtendKeyArray[k][4 * i + j - 1] ^ ExtendKeyArray[k][4 * (i - 1) + j];
}
}
}

return ret;
}


///////////////////////////////////////////////////////////////
//功能: 轮密钥加
//参数: PlainArray 输入的明文数组矩阵
// ExtendKeyArray 输入的扩展密钥数组
// MinCol 输入的最小列号
//返回值:int
int AddRoundKey(unsigned char(*PlainArray)[4], unsigned char(*ExtendKeyArray)[44], unsigned int MinCol)
{
int ret = 0;

for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
PlainArray[i][j] ^= ExtendKeyArray[i][MinCol + j];
}
}

return ret;
}


///////////////////////////////////////////////////////////////
//功能: 对明文矩阵进行S盒的字节代换
//参数: PlainArray 输入的明文矩阵
//返回值:int
int Plain_S_Substitution(unsigned char *PlainArray)
{
int ret = 0;

for (int i = 0; i < 16; i++)
{
PlainArray[i] = S_Table[PlainArray[i] >> 4][PlainArray[i] & 0x0F];
}

return ret;
}

///////////////////////////////////////////////////////////////
//功能: ShiftRows子层的行位移
//参数: PlainArray 输入的明文矩阵(强制转换成int类型)
//返回值:int
int ShiftRows(unsigned int *PlainArray)
{
int ret = 0;

//第一行 不移位
//PlainArray[0] = PlainArray[0];

//第二行 左移8Bit
PlainArray[1] = (PlainArray[1] >> 8) | (PlainArray[1] << 24);

//第三行 左移16Bit
PlainArray[2] = (PlainArray[2] >> 16) | (PlainArray[2] << 16);

//第四行 左移24Bit
PlainArray[3] = (PlainArray[3] >> 24) | (PlainArray[3] << 8);

return ret;
}

///////////////////////////////////////////////////////////////
//功能: 伽罗瓦域内的乘法运算 GF(128)
//参数: Num_L 输入的左参数
// Num_R 输入的右参数
//返回值:计算结果
char GaloisMultiplication(unsigned char Num_L, unsigned char Num_R)
{
//定义变量
unsigned char Result = 0; //伽罗瓦域内乘法计算的结果

while (Num_L)
{
//如果Num_L最低位是1就异或Num_R,相当于加上Num_R * 1
if (Num_L & 0x01)
{
Result ^= Num_R;
}

//Num_L右移一位,相当于除以2
Num_L = Num_L >> 1;

//如果Num_R最高位为1
if (Num_R & 0x80)
{
//左移一位相当于乘二
Num_R = Num_R << 1; //注:这里会丢失最高位,但是不用担心

Num_R ^= 0x1B; //计算伽罗瓦域内除法Num_R = Num_R / (x^8(刚好丢失最高位) + x^4 + x^3 + x^1 + 1)
}
else
{
//左移一位相当于乘二
Num_R = Num_R << 1;
}
}

return Result;
}


///////////////////////////////////////////////////////////////
//功能: MixColum子层的列混淆
//参数: PlainArray 输入的明文矩阵
//返回值:int
int MixColum(unsigned char(*PlainArray)[4])
{
int ret = 0;

//定义变量
unsigned char ArrayTemp[4][4];

//初始化变量
memcpy(ArrayTemp, PlainArray, 16);

//矩阵乘法 4*4
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
PlainArray[i][j] =
GaloisMultiplication(MixArray[i][0], ArrayTemp[0][j]) ^
GaloisMultiplication(MixArray[i][1], ArrayTemp[1][j]) ^
GaloisMultiplication(MixArray[i][2], ArrayTemp[2][j]) ^
GaloisMultiplication(MixArray[i][3], ArrayTemp[3][j]);
}
}

return ret;
}

int AES_EnCryption(const unsigned char *PlainText, const unsigned char *PassWord, unsigned char *CipherText)
{
int ret = 0;

//1、定义变量
unsigned char PlainArray[4][4]; //明文的4*4矩阵128Bit
unsigned char PasswordArray[4][4]; //密钥扩展前的4*4矩阵128Bit
unsigned char ExtendKeyArray[4][44]; //存储密钥扩展后的矩阵4*44

//2、初始化变量
memset(PlainArray, 0, 16);
memset(PasswordArray, 0, 16);
memset(ExtendKeyArray, 0, 176);

//3、将字符串转换成AES指定的数组排列
StringToArray(PlainText, PlainArray);
StringToArray(PassWord, PasswordArray);

//4、计算扩展密钥
CalculateExtendKeyArray(PasswordArray, ExtendKeyArray);

//5、轮密钥加
AddRoundKey(PlainArray, ExtendKeyArray, 0);

//6、循环前n-1轮
for (int i = 1; i < 10; i++)
{
//(1)字节代换层
Plain_S_Substitution((unsigned char *)PlainArray);

//(2)ShiftRow子层 行位移
ShiftRows((unsigned int *)PlainArray);

//(3)MixColum子层 列混淆
MixColum(PlainArray);

//(4)轮密钥加
AddRoundKey(PlainArray, ExtendKeyArray, 4 * i);
}

//7、最后一轮
//(1)字节代换层
Plain_S_Substitution((unsigned char *)PlainArray);

//(2)ShiftRow子层 行位移
ShiftRows((unsigned int *)PlainArray);

//(3)轮密钥加
AddRoundKey(PlainArray, ExtendKeyArray, 4 * 10);

//8、传出输出的密文 注意密文的排列方式
ArrayToString(PlainArray, CipherText);

return ret;
}



///////////////////////////////////////////////////////////////
//功能: 逆向ShiftRows子层的行位移
//参数: CipherArray 输入的密文矩阵(强制转换成int类型)
//返回值:int
int ReShiftRows(unsigned int *CipherArray)
{
int ret = 0;

//第一行 不移位
//CipherArray[0] = CipherArray[0];

//第二行 右移8Bit
CipherArray[1] = (CipherArray[1] << 8) | (CipherArray[1] >> 24);

//第三行 右移16Bit
CipherArray[2] = (CipherArray[2] << 16) | (CipherArray[2] >> 16);

//第四行 右移24Bit
CipherArray[3] = (CipherArray[3] << 24) | (CipherArray[3] >> 8);

return ret;
}

///////////////////////////////////////////////////////////////
//功能: 对密文矩阵进行逆向S盒的字节代换
//参数: CipherArray 输入的密文矩阵
//返回值:int
int Cipher_S_Substitution(unsigned char *CipherArray)
{
int ret = 0;

for (int i = 0; i < 16; i++)
{
CipherArray[i] = ReS_Table[CipherArray[i] >> 4][CipherArray[i] & 0x0F];
}

return ret;
}


///////////////////////////////////////////////////////////////
//功能: 逆向MixColum子层的列混淆
//参数: CipherArray 输入的密文矩阵
//返回值:int
int ReMixColum(unsigned char(*CipherArray)[4])
{
int ret = 0;

//定义变量
unsigned char ArrayTemp[4][4];

//初始化变量
memcpy(ArrayTemp, CipherArray, 16);

//矩阵乘法 4*4
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
CipherArray[i][j] =
GaloisMultiplication(ReMixArray[i][0], ArrayTemp[0][j]) ^
GaloisMultiplication(ReMixArray[i][1], ArrayTemp[1][j]) ^
GaloisMultiplication(ReMixArray[i][2], ArrayTemp[2][j]) ^
GaloisMultiplication(ReMixArray[i][3], ArrayTemp[3][j]);
}
}

return ret;
}

int AES_DeCipher(const unsigned char *CipherText, const unsigned char *PassWord, unsigned char *DeCipherText)
{
int ret = 0;

//1、定义变量
unsigned char CipherArray[4][4]; //密文的4*4矩阵
unsigned char PasswordArray[4][4]; //密码的4*4矩阵
unsigned char ExtendKeyArray[4][44]; //存储密钥扩展后的矩阵4*44

//2、初始化变量
memset(CipherArray, 0, 16);
memset(PasswordArray, 0, 16);
memset(ExtendKeyArray, 0, 176);

//3、将字符串转换成AES指定的数组排列
StringToArray(CipherText, CipherArray);
StringToArray(PassWord, PasswordArray);

//4、计算扩展密钥
CalculateExtendKeyArray(PasswordArray, ExtendKeyArray);

//5、逆向最后一轮 最后四列扩展密钥与密文矩阵进行轮密钥加
//(1)轮密钥加
AddRoundKey(CipherArray, ExtendKeyArray, 4 * 10);

//(2)逆向ShiftRow子层 行位移
ReShiftRows((unsigned int *)CipherArray);

//(3)字节代换层
Cipher_S_Substitution((unsigned char *)CipherArray);

//6、循环前n-1轮
for (int i = 9; i > 0; i--)
{
//(1)密钥加法层
AddRoundKey(CipherArray, ExtendKeyArray, 4 * i);

//(2)逆向列混淆
ReMixColum(CipherArray);

//(3)逆向ShiftRows
ReShiftRows((unsigned int *)CipherArray);

//(4)逆向字节代换
Cipher_S_Substitution((unsigned char *)CipherArray);
}

//7、轮密钥加
AddRoundKey(CipherArray, ExtendKeyArray, 0);

//8、传出输出的明文 注意明文的排列方式
ArrayToString(CipherArray, DeCipherText);

return ret;
}

int main()
{
int ret = 0;
//定义局部变量
//unsigned char PlainText[17]; //要加密的明文(String) 128Bit
//unsigned char PassWord[17]; //加密的密钥(String) 128Bit
unsigned char CipherText[17]; //密文(Hex) 128Bit
unsigned char DeCipherText[17]; //解密后的明文(String) 128Bit


//变量初始化
unsigned char PlainText[17] = "0123456789ABCDEF"; //要加密的明文(String) 128Bit
unsigned char PassWord[17] = "0123456789ABCDEF"; //要加密的明文(String) 128Bit

//memset(PlainText, 0, sizeof(PlainText));
//memset(PassWord, 0, sizeof(PassWord));
memset(CipherText, 0, sizeof(CipherText));
memset(DeCipherText, 0, sizeof(DeCipherText));

//获取输入
printf("Please input Plain Text (128Bit):");
//gets(PlainText);
printf("%s\n", PlainText);
printf("Please input PassWord (128Bit):");
//gets(PassWord);
printf("%s\n", PassWord);


//进行AES加密
printf("PlainText : %s\n", PlainText);
printf("PassWord : %s\n", PassWord);
AES_EnCryption(PlainText, PassWord, CipherText);
printf("CipherText :\n");
Print_Table(CipherText, 16);

//进行AES解密
AES_DeCipher(CipherText, PassWord, DeCipherText);
printf("DeCipherText : %s\n", DeCipherText);

system("pause");
return ret;
}

RSA

RSA的加密步骤为

  1. 选择一对不相等且足够大的质数pq

  2. 计算出pq的乘积
    $$
    n=p*q
    $$

  3. 计算出n的欧拉函数
    $$
    \varphi(n)=(p-1)*(q-1)
    $$

  4. 选一个与$\varphi(n)$互质的整数$e$,那么$1<e<\varphi(n)$

  5. 计算出e对于$\varphi(n)$的模反元素d,
    $$
    d*e \quad mod \quad \varphi(n)=1
    $$

  6. 计算出公钥为
    $$
    KU=(e,n)
    $$

  7. 计算出私钥为
    $$
    KR=(d,n)
    $$

  8. 发送端通过公钥来加密明文(M)得到密文©:
    $$
    M^e \quad mod \quad n = C
    $$

  9. 接受端通过私钥来解密密文:
    $$
    C^d \quad mod \quad n = M
    $$

[!tip]

欧拉函数($\varphi(n)$)是⼩于n的正整数中与n互质的数的数⽬。

互质是公约数只有1的两个整数,叫做互质整数。

质数是指在⼤于1的⾃然数中,除了1和它本身以外不再有其他因数的⾃然数。

[!tip]

如果n可以分解成2个互质的整数之积,那么n的欧拉函数等于这两个因⼦的欧拉函数之积。

即若$n=pq$,且p,q互质, 则$φ(n)=φ(pq)=φ(p)*φ(q)$。

如果两个正整数e和φ(n)互质,那么⼀定可以找到⼀个整数d,使得$ed-1$被φ(n)整 除,或者说$ed$除以φ(n)所得余数为1。 此时,d就叫做e的模反元素

==证明过程==

现要证明$C^d\quad mod \quad n = M$,证明结果如下
$$
C^d \quad mod \quad n \
=(M^e \quad mod \quad n)^d \quad mod \quad n\
=M^{ed} \quad mod \quad n
$$
那么此时只需要证明$M^{ed}mod \quad n=M$即可,其中$ed-1=k\varphi(n)$,此时以上公式变为
$$
M^{ed} \quad mod \quad n\
=M^{k\varphi(n)+1} mod \quad n\
=(M^{k\varphi(n)}*M)mod \quad n
$$
而此时M小于n,那么$M \quad mod \quad n =M$,此时只需要证明$M^{k\varphi(n)}=1$即可,由欧拉定理可知只要$M^k$和$n$互质,即可证明$M^{k\varphi(n)}=1$,而这里的n是两个很大的质数相乘,也就是n的因子为$p、q$和1,而M小于$p和q$中的任意一个,所以$M^k$和$n$必然互斥,由此得证。

[!tip]

假设有两个数字a和n,如果a和n互质,由欧拉定理可知$a^{\varphi(n)}=1\quad mod \quad n$

CMAC

初始化

  1. 密钥选择:选择一个对称密钥K,用于加密操作(这个K)
  2. 子密钥生成:通过密钥K生成两个子密钥K1和K2.
    1. 使用密钥K通过AES算法对128bit的0000…0000进行加密,得到加密后的中间值L(128bit);
    2. 根据L的最高位有效位(MSB)生成K1:
      1. 若L的MSB为0, 则$K1 = L <<1$,
      2. 若L的MSB为1, $K1 = (L << 1)\oplus R_b$。其中$R_b$是一个常数,如AES中为0x87
    3. 根据K1的最高有效位生成K2:
      1. 若K1的MSB为0, 则$K2 = K1 <<1$
      2. 若K1的MSB为1, $K2 = (K1 << 1)\oplus R_b$

[!tip]

可以认为CMAC是一种工作模式,支持的算法为AES,DES、3DES等。

不同的算法的$R_b$不同(b表示块位数,AES块位数为128,DES和3DES的块位数为64)

AES的$R_b$为$R_{128}=0^{120}10000111$其中10000111为固定值

DES、3DES的$R_b$为$R_{64}=0^{59}11011$,其中11011为固定值

消息处理及加密计算

现有一个消息M(明文)。

  1. 消息分块:将消息按分组密码的块大小(如 AES 为 128 位)进行分块,。假设分为n块。

    那么就有$M=M_1||M_2||M_3 ||\ldots||M_n$,其中||为拼接操作,$M_1$至$M_{n-1}$都为完整块。$M_n$可能为完整块,也可能是填充块。

  2. 块填充:

    1. 如果最后一块为完整块,则$M_n=K1\oplus M_n$
    2. 如果最后一块为填充块,则$M_n=K2 \oplus (M_n||10^j)$,这里的$||10^j$可以理解为将最后一块填充至128位。
  3. 块加密:

    1. 首先设置一个常量$x=0^{128}=0$

    2. 循环n轮得到最终的MAC,使用以下公式计算
      $$
      Y=M_i \oplus x,其中1 \leq i \leq n \
      x=AES-128(K,Y)
      $$

    3. 最终算出来的$x$就是MAC

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#include <stdio.h>

/* For CMAC Calculation */
unsigned char const_Rb[16] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87
};
unsigned char const_Zero[16] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

/* Basic Functions */

void xor_128(unsigned char *a, unsigned char *b, unsigned char *out)
{
int i;
for (i=0;i<16; i++)
{
out[i] = a[i] ^ b[i];
}
}

void print_hex(char *str, unsigned char *buf, int len)
{
int i;

for ( i=0; i<len; i++ ) {
if ( (i % 16) == 0 && i != 0 )
printf(str);
printf("%02x", buf[i]);
if ( (i % 4) == 3 )
printf(" ");
if ( (i % 16) == 15 )
printf("\n");
}
if ( (i % 16) != 0 )
printf("\n");
}

void print128(unsigned char *bytes)
{
int j;
for (j=0; j<16;j++) {
printf("%02x",bytes[j]);
if ( (j%4) == 3 )
printf(" ");
}
}

void print96(unsigned char *bytes)
{
int j;
for (j=0; j<12;j++) {
printf("%02x",bytes[j]);
if ( (j%4) == 3 ) printf(" ");
}
}

/* AES-CMAC Generation Function */

void leftshift_onebit(unsigned char *input,unsigned char *output)
{
int i;
unsigned char overflow = 0;

for ( i=15; i>=0; i-- ) {
output[i] = input[i] << 1;
output[i] |= overflow;
overflow = (input[i] & 0x80)?1:0;
}
return;
}

void generate_subkey(unsigned char *key, unsigned char *K1, unsigned char *K2)
{
unsigned char L[16];
unsigned char Z[16];
unsigned char tmp[16];
int i;

for ( i=0; i<16; i++ )
Z[i] = 0;

AES_128(key,Z,L);

if ( (L[0] & 0x80) == 0 ) { /* If MSB(L) = 0, then K1 = L << 1 */
leftshift_onebit(L,K1);
} else { /* Else K1 = ( L << 1 ) (+) Rb */
leftshift_onebit(L,tmp);
xor_128(tmp,const_Rb,K1);
}

if ( (K1[0] & 0x80) == 0 ) {
leftshift_onebit(K1,K2);
} else {
leftshift_onebit(K1,tmp);
xor_128(tmp,const_Rb,K2);
}
return;
}

void padding ( unsigned char *lastb, unsigned char *pad, int length )
{
int j;

/* original last block */
for ( j=0; j<16; j++ ) {
if ( j < length ) {
pad[j] = lastb[j];
} else if ( j == length ) {
pad[j] = 0x80;
} else {
pad[j] = 0x00;
}
}
}

void AES_CMAC ( unsigned char *key, unsigned char *input, int length,
unsigned char *mac )
{
unsigned char X[16],Y[16], M_last[16], padded[16];
unsigned char K1[16], K2[16];
int n, i, flag;
generate_subkey(key,K1,K2);

n = (length+15) / 16; /* n is number of rounds */

if ( n == 0 ) {
n = 1;
flag = 0;
} else {
if ( (length%16) == 0 ) { /* last block is a complete block */
flag = 1;
} else { /* last block is not complete block */
flag = 0;
}
}

if ( flag ) { /* last block is complete block */
xor_128(&input[16*(n-1)],K1,M_last);
} else {
padding(&input[16*(n-1)],padded,length%16);
xor_128(padded,K2,M_last);
}

for ( i=0; i<16; i++ ) X[i] = 0;
for ( i=0; i<n-1; i++ ) {
xor_128(X,&input[16*i],Y); /* Y := Mi (+) X */
AES_128(key,Y,X); /* X := AES-128(KEY, Y); */
}

xor_128(X,M_last,Y);
AES_128(key,Y,X);

for ( i=0; i<16; i++ ) {
mac[i] = X[i];
}
}

int main()
{
unsigned char L[16], K1[16], K2[16], T[16], TT[12];
unsigned char M[64] = {
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
};
unsigned char key[16] = {
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
};

printf("--------------------------------------------------\n");
printf("K ");
print128(key);
printf("\n");

printf("\nSubkey Generation\n");
AES_128(key,const_Zero,L);
printf("AES_128(key,0) "); print128(L); printf("\n");
generate_subkey(key,K1,K2);
printf("K1 "); print128(K1); printf("\n");
printf("K2 "); print128(K2); printf("\n");

printf("\nExample 1: len = 0\n");
printf("M "); printf("<empty string>\n");

AES_CMAC(key,M,0,T);
printf("AES_CMAC "); print128(T); printf("\n");

printf("\nExample 2: len = 16\n");
printf("M "); print_hex(" ",M,16);
AES_CMAC(key,M,16,T);
printf("AES_CMAC "); print128(T); printf("\n");
printf("\nExample 3: len = 40\n");
printf("M "); print_hex(" ",M,40);
AES_CMAC(key,M,40,T);
printf("AES_CMAC "); print128(T); printf("\n");

printf("\nExample 4: len = 64\n");
printf("M "); print_hex(" ",M,64);
AES_CMAC(key,M,64,T);
printf("AES_CMAC "); print128(T); printf("\n");

printf("--------------------------------------------------\n");

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
#include <stdlib.h>  // 包含 rand() 和 srand() 函数
#include <time.h> // 包含 time() 函数
#include <stdio.h>
#include <stdint.h>
#include <string.h>

/************************AES128_CMAC算法实现——Start**********************************/
#define IS_CSM 0

unsigned char L[16];
unsigned char K1[16];
unsigned char K2[16];
unsigned char T[16];
unsigned char TT[12];

/*AES128的秘钥在此处填写*/
unsigned char AESkey1[16] =
#if 1
{
0x40, 0x57, 0x4E, 0x41, 0x4B, 0x43, 0x56, 0x63, 0x4F, 0x6F, 0x61, 0x51, 0x4C, 0x48, 0x36, 0x30
}; // AWNAKCVcOoaQLH60//Level 1 CSM
#else
{
0x45, 0x6D, 0x6D, 0x65, 0x34, 0x4B, 0x51, 0x50,
0x66, 0x66, 0x4E, 0x6E, 0x6A, 0x78, 0x6B, 0x35
};//Level 1 TCM
#endif

unsigned char AESkey2[16] =
#if 1
{
0x42, 0x70, 0x6E, 0x34, 0x76, 0x63, 0x35, 0x65, 0x6F, 0x52, 0x41, 0x4E, 0x6B, 0x5A, 0x55, 0x6C
};
// Bpn4vc5eoRANkZUl
#else
{
0x65, 0x30, 0x71, 0x52, 0x78, 0x77, 0x7A, 0x45,
0x61, 0x64, 0x71, 0x79, 0x66, 0x36, 0x64, 0x34
};//Level 11 TCM
#endif

unsigned char AESkey3[16] =
{
0x47, 0x65, 0x65, 0x71, 0x76, 0x6E, 0x4B, 0x45,
0x68, 0x6A, 0x37, 0x38, 0x6B, 0x63, 0x31, 0x50
};
/*秘钥-end*/

typedef struct {
uint32_t eK[44], dK[44]; // encKey, decKey
int Nr; // 10 rounds
}AesKey;
void printHex(uint8_t* ptr, int len, char* tag);
#define BLOCKSIZE 16 //AES-128分组长度为16字节
typedef unsigned char uint8_t;
// uint8_t y[4] -> uint32_t x
#define LOAD32H(x, y) \
do { (x) = ((uint32_t)((y)[0] & 0xff)<<24) | ((uint32_t)((y)[1] & 0xff)<<16) | \
((uint32_t)((y)[2] & 0xff)<<8) | ((uint32_t)((y)[3] & 0xff));} while(0)

// uint32_t x -> uint8_t y[4]
#define STORE32H(x, y) \
do { (y)[0] = (uint8_t)(((x)>>24) & 0xff); (y)[1] = (uint8_t)(((x)>>16) & 0xff); \
(y)[2] = (uint8_t)(((x)>>8) & 0xff); (y)[3] = (uint8_t)((x) & 0xff); } while(0)

// 从uint32_t x中提取从低位开始的第n个字节
#define BYTE(x, n) (((x) >> (8 * (n))) & 0xff)

/* used for keyExpansion */
// 字节替换然后循环左移1位
#define MIX(x) (((S[BYTE(x, 2)] << 24) & 0xff000000) ^ ((S[BYTE(x, 1)] << 16) & 0xff0000) ^ \
((S[BYTE(x, 0)] << 8) & 0xff00) ^ (S[BYTE(x, 3)] & 0xff))

// uint32_t x循环左移n位
#define ROF32(x, n) (((x) << (n)) | ((x) >> (32-(n))))
// uint32_t x循环右移n位
#define ROR32(x, n) (((x) >> (n)) | ((x) << (32-(n))))

/* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
// AES-128轮常量
static const uint32_t rcon[10] = {
0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, 0x10000000UL,
0x20000000UL, 0x40000000UL, 0x80000000UL, 0x1B000000UL, 0x36000000UL
};
// S盒
unsigned char S[256] = {
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
};

//逆S盒
unsigned char inv_S[256] = {
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
};

/* copy in[16] to state[4][4] */
int loadStateArray(uint8_t(*state)[4], const uint8_t* in) {
int i, j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
state[j][i] = *in++;
}
}
return 0;
}

/* copy state[4][4] to out[16] */
int storeStateArray(uint8_t(*state)[4], uint8_t* out) {
int i, j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
*out++ = state[j][i];
}
}
return 0;
}
//秘钥扩展
int keyExpansion(const uint8_t* key, uint32_t keyLen, AesKey* aesKey) {

if (NULL == key || NULL == aesKey) {
printf("keyExpansion param is NULL\n");
return -1;
}

if (keyLen != 16) {
printf("keyExpansion keyLen = %d, Not support.\n", keyLen);
return -1;
}

uint32_t* w = aesKey->eK; //加密秘钥
uint32_t* v = aesKey->dK; //解密秘钥

/* keyLen is 16 Bytes, generate uint32_t W[44]. */

/* W[0-3] */
int i;
for (i = 0; i < 4; ++i) {
LOAD32H(w[i], key + 4 * i);
}

/* W[4-43] */

for (i = 0; i < 10; ++i) {
w[4] = w[0] ^ MIX(w[3]) ^ rcon[i];
w[5] = w[1] ^ w[4];
w[6] = w[2] ^ w[5];
w[7] = w[3] ^ w[6];
w += 4;
}

w = aesKey->eK + 44 - 4;
//解密秘钥矩阵为加密秘钥矩阵的倒序,方便使用,把ek的11个矩阵倒序排列分配给dk作为解密秘钥
//即dk[0-3]=ek[41-44], dk[4-7]=ek[37-40]... dk[41-44]=ek[0-3]
int j;
for (j = 0; j < 11; ++j) {

for (i = 0; i < 4; ++i) {
v[i] = w[i];
}
w -= 4;
v += 4;
}

return 0;
}

// 轮秘钥加
int addRoundKey(uint8_t(*state)[4], const uint32_t* key) {
uint8_t k[4][4];
int i;
int j;
/* i: row, j: col */
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
k[i][j] = (uint8_t)BYTE(key[j], 3 - i); /* 把 uint32 key[4] 先转换为矩阵 uint8 k[4][4] */
state[i][j] ^= k[i][j];
}
}

return 0;
}

//字节替换
int subBytes(uint8_t(*state)[4]) {
/* i: row, j: col */
int i, j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
state[i][j] = S[state[i][j]]; //直接使用原始字节作为S盒数据下标
}
}

return 0;
}

//逆字节替换
int invSubBytes(uint8_t(*state)[4]) {
/* i: row, j: col */
int i, j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
state[i][j] = inv_S[state[i][j]];
}
}
return 0;
}

//行移位
int shiftRows(uint8_t(*state)[4]) {
uint32_t block[4] = { 0 };

/* i: row */
int i;
for (i = 0; i < 4; ++i) {
//便于行循环移位,先把一行4字节拼成uint_32结构,移位后再转成独立的4个字节uint8_t
LOAD32H(block[i], state[i]);
block[i] = ROF32(block[i], 8 * i);
STORE32H(block[i], state[i]);
}
return 0;
}

//逆行移位
int invShiftRows(uint8_t(*state)[4]) {
uint32_t block[4] = { 0 };
/* i: row */
int i;
for (i = 0; i < 4; ++i) {
LOAD32H(block[i], state[i]);
block[i] = ROR32(block[i], 8 * i);
STORE32H(block[i], state[i]);
}
return 0;
}

/* Galois Field (256) Multiplication of two Bytes */
// 两字节的伽罗华域乘法运算
uint8_t GMul(uint8_t u, uint8_t v) {
uint8_t p = 0;
int i;
for (i = 0; i < 8; ++i) {
if (u & 0x01) { //
p ^= v;
}

int flag = (v & 0x80);
v <<= 1;
if (flag) {
v ^= 0x1B; /* x^8 + x^4 + x^3 + x + 1 */
}
u >>= 1;
}
return p;
}

// 列混合
int mixColumns(uint8_t(*state)[4]) {
uint8_t tmp[4][4];
uint8_t M[4][4] = { {0x02, 0x03, 0x01, 0x01},
{0x01, 0x02, 0x03, 0x01},
{0x01, 0x01, 0x02, 0x03},
{0x03, 0x01, 0x01, 0x02} };
/* copy state[4][4] to tmp[4][4] */
int i, j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
tmp[i][j] = state[i][j];
}
}

for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) { //伽罗华域加法和乘法
state[i][j] = GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j])
^ GMul(M[i][2], tmp[2][j]) ^ GMul(M[i][3], tmp[3][j]);
}
}
return 0;
}

// 逆列混合
int invMixColumns(uint8_t(*state)[4]) {
uint8_t tmp[4][4];
uint8_t M[4][4] = { {0x0E, 0x0B, 0x0D, 0x09},
{0x09, 0x0E, 0x0B, 0x0D},
{0x0D, 0x09, 0x0E, 0x0B},
{0x0B, 0x0D, 0x09, 0x0E} }; //使用列混合矩阵的逆矩阵
int i, j;
/* copy state[4][4] to tmp[4][4] */
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
tmp[i][j] = state[i][j];
}
}

for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
state[i][j] = GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j])
^ GMul(M[i][2], tmp[2][j]) ^ GMul(M[i][3], tmp[3][j]);
}
}

return 0;
}

// AES-128加密接口,输入key应为16字节长度,输入长度应该是16字节整倍数,
// 这样输出长度与输入长度相同,函数调用外部为输出数据分配内存
int aesEncrypt(const uint8_t* key, uint32_t keyLen, const uint8_t* pt, uint8_t* ct, uint32_t len) {

AesKey aesKey;
uint8_t* pos = ct;
const uint32_t* rk = aesKey.eK; //解密秘钥指针
uint8_t out[BLOCKSIZE] = { 0 };
uint8_t actualKey[16] = { 0 };
uint8_t state[4][4] = { 0 };

if (NULL == key || NULL == pt || NULL == ct) {
printf("param err.\n");
return -1;
}

if (keyLen > 16) {
printf("keyLen must be 16.\n");
return -1;
}

if (len % BLOCKSIZE) {
printf("inLen is invalid.\n");
return -1;
}

memcpy(actualKey, key, keyLen);
keyExpansion(actualKey, 16, &aesKey); // 秘钥扩展
int i;
// 使用ECB模式循环加密多个分组长度的数据
for (i = 0; i < len; i += BLOCKSIZE) {
// 把16字节的明文转换为4x4状态矩阵来进行处理
loadStateArray(state, pt);
// 轮秘钥加
addRoundKey(state, rk);
int j;
for (j = 1; j < 10; ++j) {
rk += 4;
subBytes(state); // 字节替换
shiftRows(state); // 行移位
mixColumns(state); // 列混合
addRoundKey(state, rk); // 轮秘钥加
}

subBytes(state); // 字节替换
shiftRows(state); // 行移位
// 此处不进行列混合
addRoundKey(state, rk + 4); // 轮秘钥加

// 把4x4状态矩阵转换为uint8_t一维数组输出保存
storeStateArray(state, pos);

pos += BLOCKSIZE; // 加密数据内存指针移动到下一个分组
pt += BLOCKSIZE; // 明文数据指针移动到下一个分组
rk = aesKey.eK; // 恢复rk指针到秘钥初始位置
}
return 0;
}

// AES128解密, 参数要求同加密
int aesDecrypt(const uint8_t* key, uint32_t keyLen, const uint8_t* ct, uint8_t* pt, uint32_t len) {
AesKey aesKey;
uint8_t* pos = pt;
const uint32_t* rk = aesKey.dK; //解密秘钥指针
uint8_t out[BLOCKSIZE] = { 0 };
uint8_t actualKey[16] = { 0 };
uint8_t state[4][4] = { 0 };

if (NULL == key || NULL == ct || NULL == pt) {
printf("param err.\n");
return -1;
}

if (keyLen > 16) {
printf("keyLen must be 16.\n");
return -1;
}

if (len % BLOCKSIZE) {
printf("inLen is invalid.\n");
return -1;
}

memcpy(actualKey, key, keyLen);
keyExpansion(actualKey, 16, &aesKey); //秘钥扩展,同加密
int i, j;
for (i = 0; i < len; i += BLOCKSIZE) {
// 把16字节的密文转换为4x4状态矩阵来进行处理
loadStateArray(state, ct);
// 轮秘钥加,同加密
addRoundKey(state, rk);

for (j = 1; j < 10; ++j) {
rk += 4;
invShiftRows(state); // 逆行移位
invSubBytes(state); // 逆字节替换,这两步顺序可以颠倒
addRoundKey(state, rk); // 轮秘钥加,同加密
invMixColumns(state); // 逆列混合
}

invSubBytes(state); // 逆字节替换
invShiftRows(state); // 逆行移位
// 此处没有逆列混合
addRoundKey(state, rk + 4); // 轮秘钥加,同加密

storeStateArray(state, pos); // 保存明文数据
pos += BLOCKSIZE; // 输出数据内存指针移位分组长度
ct += BLOCKSIZE; // 输入数据内存指针移位分组长度
rk = aesKey.dK; // 恢复rk指针到秘钥初始位置
}
return 0;
}

void printHex(uint8_t* ptr, int len, char* tag) {
printf("%s\ndata[%d]: ", tag, len);
int i;
for (i = 0; i < len; ++i) {
printf("%.2X ", *ptr++);
}
printf("\n");
}


//
/* For CMAC Calculation */
unsigned char const_Rb[16] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87
};
unsigned char const_Zero[16] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

/* Basic Functions */

void xor_128(unsigned char* a, unsigned char* b, unsigned char* out)
{
int i;
for (i = 0; i < 16; i++)
{
out[i] = a[i] ^ b[i];
}
}

void print_hex(char* str, unsigned char* buf, int len)
{
int i;

for (i = 0; i < len; i++) {
if ((i % 16) == 0 && i != 0) printf("%s",str);
printf("%02x", buf[i]);
if ((i % 4) == 3) printf(" ");
if ((i % 16) == 15) printf("\n");
}
if ((i % 16) != 0) printf("\n");
}
void print128(unsigned char* bytes)
{
int j;
for (j = 0; j < 16; j++) {
printf("%02x", bytes[j]);
if ((j % 4) == 3) printf(" ");
}
}

void print96(unsigned char* bytes)
{
int j;
for (j = 0; j < 12; j++) {
printf("%02x", bytes[j]);
if ((j % 4) == 3) printf(" ");
}
}

/* AES-CMAC Generation Function */

void leftshift_onebit(unsigned char* input, unsigned char* output)
{
int i;
unsigned char overflow = 0;

for (i = 15; i >= 0; i--) {
output[i] = input[i] << 1;
output[i] |= overflow;
overflow = (input[i] & 0x80) ? 1 : 0;
}
return;
}

void generate_subkey(unsigned char* key, unsigned char* K1, unsigned
char* K2)
{
unsigned char L[16];
unsigned char Z[16];
unsigned char tmp[16];
int i;

for (i = 0; i < 16; i++) Z[i] = 0;

aesEncrypt(key, 16, Z, L, 16);

if ((L[0] & 0x80) == 0) { /* If MSB(L) = 0, then K1 = L << 1 */
leftshift_onebit(L, K1);
}
else { /* Else K1 = ( L << 1 ) (+) Rb */
leftshift_onebit(L, tmp);
xor_128(tmp, const_Rb, K1);
}

if ((K1[0] & 0x80) == 0) {
leftshift_onebit(K1, K2);
}
else {
leftshift_onebit(K1, tmp);
xor_128(tmp, const_Rb, K2);
}
return;
}

void padding(unsigned char* lastb, unsigned char* pad, int length)
{
int j;

/* original last block */
for (j = 0; j < 16; j++) {
if (j < length) {
pad[j] = lastb[j];
}
else if (j == length) {
pad[j] = 0x80;
}
else {
pad[j] = 0x00;
}
}
}

void AES_CMAC(unsigned char* key, unsigned char* input, int length,
unsigned char* mac)
{
unsigned char X[16], Y[16], M_last[16], padded[16];
unsigned char K1[16], K2[16];
int n, i, flag;
generate_subkey(key, K1, K2);

n = (length + 15) / 16; /* n is number of rounds */

if (n == 0) {
n = 1;
flag = 0;
}
else {
if ((length % 16) == 0) { /* last block is a complete block */
flag = 1;
}
else { /* last block is not complete block */
flag = 0;
}
}

if (flag) { /* last block is complete block */
xor_128(&input[16 * (n - 1)], K1, M_last);
}
else {
padding(&input[16 * (n - 1)], padded, length % 16);
xor_128(padded, K2, M_last);
}

for (i = 0; i < 16; i++) X[i] = 0;
for (i = 0; i < n - 1; i++) {
xor_128(X, &input[16 * i], Y); /* Y := Mi (+) X */
aesEncrypt(key, 16, Y, X, 16); /* X := AES-128(KEY, Y); */
}

xor_128(X, M_last, Y);
aesEncrypt(key, 16, Y, X, 16);

for (i = 0; i < 16; i++) {
mac[i] = X[i];
}
}
/****************************************AES128_CMAC算法——END*****************************************************/
// u8 hex_array[8];
void InvertUint16(unsigned short *dBuf,unsigned short *srcBuf)
{
int i;
unsigned short tmp[4]={0};

for(i=0;i< 16;i++)
{
if(srcBuf[0]& (1 << i))
tmp[0]|=1<<(15 - i);
}
dBuf[0] = tmp[0];
}