AES

简介

AES是在DES被证明不安全后出现的加密算法,是经典的块加密算法,可以使用128bit,192bit,256bit三种长度的密钥,数据块长度固定为128bit(16字节),整个加密过程在一个44的矩阵中进行,AES加密基于代换-置换网络,主要操作有轮密钥加,字节代换,行位移,列混合四种,其中数值运算相关的操作都是定义在$GF(2^8)$这个有限域下的(可以理解为为了防止溢出损失信息),对于有限域下的数值计算如何处理会在实现中说明,AES的运算采用*大端序

特征

以AES128为例,在主循环中会依次执行字节代换,行位移,列混合,轮密钥加四个操作共9轮,第十轮不进行列混合操作,不过这个因为编译器会把加密操作优化的面目全非所以不一定看得出来
其次是S盒,AES有一个256位的S盒用于进行字节代换操作
标准S盒如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const uint8_t S[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};

以及十个轮常量用于参与密钥拓展算法
标准轮常量如下

1
2
3
4
5
const uint32_t Rcon[10] = {0x01000000, 0x02000000,
0x04000000, 0x08000000,
0x10000000, 0x20000000,
0x40000000, 0x80000000,
0x1b000000, 0x36000000};

实现

AES首先进行密钥拓展,AES的密钥由原本128位密钥组成的4个uint32_t为基础,拓展出10轮,总计44个共11组密钥
因为加密是在4*4矩阵上进行操作,且矩阵是列优先排列的,所以先将密文按块转化为矩阵,加密完成后在转换回去

$s_0$ $s_1$ $s_2$ $s_{14}$ $s_{15}$

转换后变为:

$s_0$ $s_4$ $s_8$ $s_{12}$
$s_1$ $s_5$ $s_9$ $s_{13}$
$s_2$ $s_6$ $s_{10}$ $s_{14}$
$s_3$ $s_7$ $s_{11}$ $s_{15}$

一开始先进行初始密钥加操作,然后循环9轮,最后再进行第十轮,第十轮中不进行列混合

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
void convertToStateArray(uint8_t s[16], uint8_t a[4][4])
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
a[j][i] = s[i * 4 + j];
}
}
}
void AES(uint8_t *message, uint64_t messageLen, uint8_t *key)
{
extendKey(key);
uint8_t sArray[4][4];

for (int i = 0; i < messageLen; i += 16)
{
convertToStateArray(message + i, sArray); // 转换为状态矩阵
addRoundKey(sArray, 0); // 初始轮密钥加
for (int j = 1; j < 10; j++) // 1~9
{
subbytes(sArray); // 字节代换
shiftRows(sArray); // 行移位
columnMix(sArray); // 列混合
addRoundKey(sArray, j); // 轮密钥加
}
subbytes(sArray);
shiftRows(sArray);
addRoundKey(sArray, 10); // 最终轮密钥加
convertToStr(sArray, message + i); // 转换回字符串
}
}

字节拓展操作首先把密钥按大端序转换为32位字$W_0$ ~ $W_3$,然后按照以下规则进行拓展出$W_4$ ~ $W_{43}$:
$$
W_i =
\begin{cases}
W_{i-4} \oplus T(W_{i-1}), & \text{if } i \bmod 4 = 0 \
W_{i-4} \oplus W_{i-1}, & \text{otherwise}
\end{cases}
$$

其中T是密钥拓展使用的轮函数,由字节偏移,字节代换,轮常量异或三个操作组成

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
uint32_t T(uint32_t w, int round)
{
w = (w >> 24) | (w << 8); // 字节偏移,循环左移一字节
uint8_t temp[4];
convertToWordArray(w, temp); // 转换为字节数组
for (int i = 0; i < 4; i++)
{
temp[i] = S[temp[i] >> 4][temp[i] & 0x0F]; // 字节代换
}
w = convertToWord(temp);
w ^= Rcon[round]; // 轮常量异或
return w;
}
void extendKey(uint8_t *key)
{
for (int i = 0; i < 4; i++)
{
w[i] = getBigEndian(key + i * 4); // 转大端序
}
for (int i = 4, j = 0; i < 44; i++) // 密钥拓展
{
if (i % 4 == 0)
{
w[i] = w[i - 4] ^ T(w[i - 1], j);
j++;
}
else
w[i] = w[i - 4] ^ w[i - 1];
}
}

轮密钥加操作就是将当前轮对应的密钥与数据进行异或,因为在$GF(2^8)$下的加法与异或等价

1
2
3
4
5
6
7
8
9
10
11
12
void addRoundKey(uint8_t sArray[4][4], int round) // 轮密钥加
{
uint8_t wArray[4];
for (int i = 0; i < 4; i++)
{
convertToWordArray(w[round * 4 + i], wArray); // 转换为字节数组
for (int j = 0; j < 4; j++)
{
sArray[j][i] ^= wArray[j]; // 异或操作
}
}
}

行位移操作就是把整个矩阵第一行不动,第二行循环左移1位,第三行2位,第四行3位,可以直接模拟

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
void shiftRows(uint8_t sArray[4][4])
{
// 第0行不移位

// 第1行左移1位
uint8_t temp = sArray[1][0];
sArray[1][0] = sArray[1][1];
sArray[1][1] = sArray[1][2];
sArray[1][2] = sArray[1][3];
sArray[1][3] = temp;

// 第2行左移2位
uint8_t temp1 = sArray[2][0];
uint8_t temp2 = sArray[2][1];
sArray[2][0] = sArray[2][2];
sArray[2][1] = sArray[2][3];
sArray[2][2] = temp1;
sArray[2][3] = temp2;

// 第3行左移3位
temp = sArray[3][0];
sArray[3][0] = sArray[3][3];
sArray[3][3] = sArray[3][2];
sArray[3][2] = sArray[3][1];
sArray[3][1] = temp;
}

字节代换操作就是将矩阵中一个字节的高4位作为行号,低四位作为列号在S盒中查表

1
2
3
4
5
6
void subbytes(uint8_t sArray[4][4])
{
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
sArray[i][j] = S[sArray[i][j] >> 4][sArray[i][j] & 0x0F]; // 字节代换
}

列混合操作是使用一个事先准备好的4*4矩阵和当前矩阵进行矩阵乘法,
GF(2^8)下的乘法满足交换律,结合律和分配律,且与二相乘可以等价于如下操作:
1.左移一位
2.如果左移前最高位为1,则与0x1B异或
所以只要实现乘二就能实现所有情况下的乘法

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
uint8_t GFMul(uint8_t s, int n)
{
if (n == 1)
{
return s;
}
if (n == 2)
{
uint8_t result = s << 1;

if (s & 0x80)
{
result = result ^ 0x1b;
}

return result;
}
if (n % 2 == 0)
return GFMul(GFMul(s, n / 2), 2);
else
return GFMul(s, n - 1) ^ s;
}
const uint8_t colM[4][4] = {2, 3, 1, 1,
1, 2, 3, 1,
1, 1, 2, 3,
3, 1, 1, 2};
void columnMix(uint8_t sArray[4][4])
{
uint8_t tempArray[4][4];
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
tempArray[i][j] = sArray[i][j];
for (int j = 0; j < 4; j++)
{
for (int i = 0; i < 4; i++)
{
sArray[i][j] = GFMul(tempArray[0][j], colM[i][0]) ^
GFMul(tempArray[1][j], colM[i][1]) ^
GFMul(tempArray[2][j], colM[i][2]) ^
GFMul(tempArray[3][j], colM[i][3]);
}
}
}

最后整个流程的图示如下所示

加密完整代码

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
#include <bits/stdc++.h>
using namespace std;
int w[44];
uint32_t getBigEndian(uint8_t a[4])
{
uint32_t res = 0;
res = (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
return res;
}
void convertToStateArray(uint8_t s[16], uint8_t a[4][4])
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
a[j][i] = s[i * 4 + j];
}
}
}
void convertToStr(uint8_t a[4][4], uint8_t s[16])
{
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
s[i * 4 + j] = a[j][i];
}
void convertToWordArray(uint32_t W, uint8_t w[4])
{
w[0] = (W >> 24) & 0xFF;
w[1] = (W >> 16) & 0xFF;
w[2] = (W >> 8) & 0xFF;
w[3] = W & 0xFF;
}
uint32_t convertToWord(uint8_t w[4])
{
uint32_t res = 0;
res = (w[0] << 24) | (w[1] << 16) | (w[2] << 8) | w[3];
return res;
}
const uint8_t S[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};
const uint32_t Rcon[10] = {0x01000000, 0x02000000,
0x04000000, 0x08000000,
0x10000000, 0x20000000,
0x40000000, 0x80000000,
0x1b000000, 0x36000000};
uint32_t T(uint32_t w, int round)
{
w = (w >> 24) | (w << 8); // 字节偏移,循环左移一字节
uint8_t temp[4];
convertToWordArray(w, temp); // 转换为字节数组
for (int i = 0; i < 4; i++)
{
temp[i] = S[temp[i] >> 4][temp[i] & 0x0F]; // 字节代换
}
w = convertToWord(temp);
w ^= Rcon[round]; // 轮常量异或
return w;
}
void extendKey(uint8_t *key)
{
for (int i = 0; i < 4; i++)
{
w[i] = getBigEndian(key + i * 4); // 转大端序
}
for (int i = 4, j = 0; i < 44; i++) // 密钥拓展
{
if (i % 4 == 0)
{
w[i] = w[i - 4] ^ T(w[i - 1], j);
j++;
}
else
w[i] = w[i - 4] ^ w[i - 1];
}
}
void addRoundKey(uint8_t sArray[4][4], int round) // 轮密钥加
{
uint8_t wArray[4];
for (int i = 0; i < 4; i++)
{
convertToWordArray(w[round * 4 + i], wArray); // 转换为字节数组
for (int j = 0; j < 4; j++)
{
sArray[j][i] ^= wArray[j]; // 异或操作
}
}
}
void subbytes(uint8_t sArray[4][4])
{
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
sArray[i][j] = S[sArray[i][j] >> 4][sArray[i][j] & 0x0F]; // 字节代换
}
void shiftRows(uint8_t sArray[4][4])
{
// 第0行不移位

// 第1行左移1位
uint8_t temp = sArray[1][0];
sArray[1][0] = sArray[1][1];
sArray[1][1] = sArray[1][2];
sArray[1][2] = sArray[1][3];
sArray[1][3] = temp;

// 第2行左移2位
uint8_t temp1 = sArray[2][0];
uint8_t temp2 = sArray[2][1];
sArray[2][0] = sArray[2][2];
sArray[2][1] = sArray[2][3];
sArray[2][2] = temp1;
sArray[2][3] = temp2;

// 第3行左移3位
temp = sArray[3][0];
sArray[3][0] = sArray[3][3];
sArray[3][3] = sArray[3][2];
sArray[3][2] = sArray[3][1];
sArray[3][1] = temp;
}
const uint8_t colM[4][4] = {2, 3, 1, 1,
1, 2, 3, 1,
1, 1, 2, 3,
3, 1, 1, 2};
uint8_t GFMul(uint8_t s, int n)
{
if (n == 1)
{
return s;
}
if (n == 2)
{
uint8_t result = s << 1;

if (s & 0x80)
{
result = result ^ 0x1b;
}

return result;
}
if (n % 2 == 0)
return GFMul(GFMul(s, n / 2), 2);
else
return GFMul(s, n - 1) ^ s;
}
void columnMix(uint8_t sArray[4][4])
{
uint8_t tempArray[4][4];
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
tempArray[i][j] = sArray[i][j];
for (int j = 0; j < 4; j++)
{
for (int i = 0; i < 4; i++)
{
sArray[i][j] = GFMul(tempArray[0][j], colM[i][0]) ^
GFMul(tempArray[1][j], colM[i][1]) ^
GFMul(tempArray[2][j], colM[i][2]) ^
GFMul(tempArray[3][j], colM[i][3]);
}
}
}
void AES(uint8_t *message, uint64_t messageLen, uint8_t *key)
{
extendKey(key);
uint8_t sArray[4][4];

for (int i = 0; i < messageLen; i += 16)
{
convertToStateArray(message + i, sArray); // 转换为状态矩阵
addRoundKey(sArray, 0); // 初始轮密钥加
for (int j = 1; j < 10; j++) // 1~9
{
subbytes(sArray); // 字节代换
shiftRows(sArray); // 行移位
columnMix(sArray); // 列混合
addRoundKey(sArray, j); // 轮密钥加
}
subbytes(sArray);
shiftRows(sArray);
addRoundKey(sArray, 10); // 最终轮密钥加
convertToStr(sArray, message + i); // 转换回字符串
}
}
int main()
{
char key[] = "0123456789abcdef";
char message[] = "0123456789abcdef";
AES((uint8_t *)message, strlen(message), (uint8_t *)key);
for (int i = 0; i < 16; i++)
{
printf("%02X ", message[i] & 0xFF);
}
return 0;
}

解密

因为是对称加密算法,AES的每一步操作都可以对其实现逆操作,只要把逆操作都实现出来然后按照加密的顺序反向执行就可以解密
逆字节代换
就是根据S盒可以准备一个反S盒,同样是查表替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const uint8_t S2[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};
void deSubBytes(uint8_t sArray[4][4])
{
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
sArray[i][j] = S2[sArray[i][j] >> 4][sArray[i][j] & 0x0F]; // 字节代换
}

逆行位移
把循环左移改为循环右移

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
void deShiftRows(uint8_t sArray[4][4])
{
// 第0行不移位

// 第1行右移1位
uint8_t temp = sArray[1][3];
sArray[1][3] = sArray[1][2];
sArray[1][2] = sArray[1][1];
sArray[1][1] = sArray[1][0];
sArray[1][0] = temp;

// 第2行右移2位
uint8_t temp1 = sArray[2][0];
uint8_t temp2 = sArray[2][1];
sArray[2][0] = sArray[2][2];
sArray[2][1] = sArray[2][3];
sArray[2][2] = temp1;
sArray[2][3] = temp2;

// 第3行右移3位
temp = sArray[3][0];
sArray[3][0] = sArray[3][1];
sArray[3][1] = sArray[3][2];
sArray[3][2] = sArray[3][3];
sArray[3][3] = temp;
}

逆列混合
因为是矩阵乘法,所以只要构造出colM的逆矩阵就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const uint8_t deColM[4][4] = {0xe, 0xb, 0xd, 0x9,
0x9, 0xe, 0xb, 0xd,
0xd, 0x9, 0xe, 0xb,
0xb, 0xd, 0x9, 0xe};
void deMixColumns(uint8_t sArray[4][4])
{
uint8_t tempArray[4][4];
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
tempArray[i][j] = sArray[i][j];
for (int j = 0; j < 4; j++)
for (int i = 0; i < 4; i++)
sArray[i][j] = GFMul(tempArray[0][j], deColM[i][0]) ^
GFMul(tempArray[1][j], deColM[i][1]) ^
GFMul(tempArray[2][j], deColM[i][2]) ^
GFMul(tempArray[3][j], deColM[i][3]);
}

轮密钥加因为是异或,所以不用额外实现反向操作
可以给出完整解密如下

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
const uint8_t S2[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};
void deSubBytes(uint8_t sArray[4][4])
{
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
sArray[i][j] = S2[sArray[i][j] >> 4][sArray[i][j] & 0x0F]; // 字节代换
}
void deShiftRows(uint8_t sArray[4][4])
{
// 第0行不移位

// 第1行右移1位
uint8_t temp = sArray[1][3];
sArray[1][3] = sArray[1][2];
sArray[1][2] = sArray[1][1];
sArray[1][1] = sArray[1][0];
sArray[1][0] = temp;

// 第2行右移2位
uint8_t temp1 = sArray[2][0];
uint8_t temp2 = sArray[2][1];
sArray[2][0] = sArray[2][2];
sArray[2][1] = sArray[2][3];
sArray[2][2] = temp1;
sArray[2][3] = temp2;

// 第3行右移3位
temp = sArray[3][0];
sArray[3][0] = sArray[3][1];
sArray[3][1] = sArray[3][2];
sArray[3][2] = sArray[3][3];
sArray[3][3] = temp;
}
const uint8_t deColM[4][4] = {0xe, 0xb, 0xd, 0x9,
0x9, 0xe, 0xb, 0xd,
0xd, 0x9, 0xe, 0xb,
0xb, 0xd, 0x9, 0xe};
void deMixColumns(uint8_t sArray[4][4])
{
uint8_t tempArray[4][4];
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
tempArray[i][j] = sArray[i][j];
for (int j = 0; j < 4; j++)
for (int i = 0; i < 4; i++)
sArray[i][j] = GFMul(tempArray[0][j], deColM[i][0]) ^
GFMul(tempArray[1][j], deColM[i][1]) ^
GFMul(tempArray[2][j], deColM[i][2]) ^
GFMul(tempArray[3][j], deColM[i][3]);
}
void AESdecrypt(uint8_t *message, uint64_t messageLen, uint8_t *key)
{
extendKey(key);
uint8_t sArray[4][4];

for (int i = 0; i < messageLen; i += 16)
{
convertToStateArray(message + i, sArray); // 转换为状态矩阵
addRoundKey(sArray, 10); // 初始轮密钥加
deShiftRows(sArray); // 行移位
deSubBytes(sArray); // 字节代换
for (int j = 9; j >= 1; j--)
{
addRoundKey(sArray, j); // 轮密钥加
deMixColumns(sArray); // 列混合
deShiftRows(sArray); // 行移位
deSubBytes(sArray); // 字节代换
}
addRoundKey(sArray, 0); // 最终轮密钥加
convertToStr(sArray, message + i);
}
}

reference

AES-wikipedia

Author

SGSG

Posted on

2025-04-15

Updated on

2025-04-18

Licensed under