DES算法

DES算法细节

https://www.anquanke.com/post/id/177808#h3-5

分组加密算法的填充方式

分组大小8字节

明文长度不够1个分组,就需要填充,以最常用的PKCS7填充为例

明文没有内容或者明文刚好一个分组长度,都需要再填充一个分组

最多填充一个分组,08 08 08 08 08 08 08 08

其他填充的情况

1
2
3
4
5
6
7
ab cd ef ab cd ef ab 01
ab cd ef ab cd ef 02 02
ab cd ef ab cd 03 03 03
..
ab 07 07 07 07 07 07 07
0a 07 07 07 07 07 07 07
// 如果明文里有半个字节,先补0再填充,0a算最终明文,填充部分解密后会去掉

密钥不存在填充,DES必须8字节

子密钥生成

1
2
3
4
1. 不考虑填充方式
2. 不考虑加密模式
3. 不考虑iv向量
4. 输入明文为8个字节,密钥为8个字节,输出结果也为8个字节(不考虑填充,就是没有填充的那一个分组)

明文:0123456789ABCDEF

密钥:133457799BBCDFF1

把密钥转二进制

1
00010011 00110100 01010111 01111001 10011011 10111100 11011111 11110001

根据PC1表,对密钥进行重新排列

1
2
3
4
5
int PC1_Table[56] = {
57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18,
10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36,
63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22,
14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4};

这里的57是指的二进制位的密钥里的第57个(从1开始数不是从0开始数)bit位,也就是1

密钥编排后的结果(这也就是为什么密钥是8字节的,但是有效位数实际上是56位)

1
1111000 0110011 0010101 0101111 0101010 1011001 1001111 0001111

接下来将这56比特长的密钥分成左右两部分,命名为C0,D0

1
2
C0 = 1111000 0110011 0010101 0101111
D0 = 0101010 1011001 1001111 0001111

循环左移规定的位数,得到C1,D1到C16,D16的十六个数据,左移的位数根据下面的key_shift表来确定

1
2
3
key_shift = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1];
C1 = 1110000 1100110 0101010 1011111
D1 = 1010101 0110011 0011110 0011110

C2D2可以对C1D1分别左移1位来得到(注意是1位,key_shift的第二个元素指的是在C1D1的基础上还需要移动的位数),C2D2也可以对C0D0分别左移2位来得到

将C1D1拼接起来,继续后续的处理

1
C1D1 = 1110000 1100110 0101010 1011111 1010101 0110011 0011110 0011110

根据PC2表进行重排,56bit长的CnDn只留下48比特

1
2
3
4
5
int PC2_Table[48] = {
14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10,
23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2,
41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32};

重排后CnDn就是子密钥,命名为Kn

后续DES的十六轮运算中,第n轮就是用Kn,16个子密钥为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
K1 = 000110 110000 001011 101111 111111 000111 000001 110010
K2 = 011110 011010 111011 011001 110110 111100 100111 100101
K3 = 010101 011111 110010 001010 010000 101100 111110 011001
K4 = 011100 101010 110111 010110 110110 110011 010100 011101
K5 = 011111 001110 110000 000111 111010 110101 001110 101000
K6 = 011000 111010 010100 111110 010100 000111 101100 101111
K7 = 111011 001000 010010 110111 111101 100001 100010 111100
K8 = 111101 111000 101000 111010 110000 010011 101111 111011
K9 = 111000 001101 101111 101011 111011 011110 011110 000001
K10 = 101100 011111 001101 000111 101110 100100 011001 001111
K11 = 001000 010101 111111 010011 110111 101101 001110 000110
K12 = 011101 010111 000111 110101 100101 000110 011111 101001
K13 = 100101 111100 010111 010001 111110 101011 101001 000001
K14 = 010111 110100 001110 110111 111100 101110 011100 111010
K15 = 101111 111001 000110 001101 001111 010011 111100 001010
K16 = 110010 110011 110110 001011 000011 100001 011111 110101

明文的处理

明文的编排

把明文转二进制

1
00000001 00100011 01000101 01100111 10001001 10101011 11001101 11101111

根据IP表,对明文进行初始置换

1
2
3
4
5
int IP_Table[64] = {
58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7};

明文重新排列后的结果

1
11001100 00000000 11001100 11111111 11110000 10101010 11110000 10101010

明文的运算

首先将明文分成左右两半,像上面的密钥一样

L0 和 R0

1
2
L0:11001100 00000000 11001100 11111111 
R0:11110000 10101010 11110000 10101010

然后会对L0和R0做16轮迭代

16轮迭代的基本套路

1
2
Ln = Rn-1(本轮的L就是上一轮的R)
Rn = Ln-1 + f(Rn-1, Kn) (本轮的R是上一轮的L和R计算来的,+就是异或)

以n = 1为例:

1
2
L1 = R0 = 11110000 10101010 11110000 10101010
R1 = L0 xor f(R0, K1)
F函数的逻辑

f函数传入了R0与K1,即第1个子密钥

1
2
R0 = 11110000 10101010 11110000 10101010
K1 = 000110 110000 001011 101111 111111 000111 000001 110010

R0为32bit,K1为48bit,长度不同,没法按位异或

将32bit的R0扩展成48bit,使用到E表

1
2
3
4
5
6
7
E(R0) = 011110 100001 010101 010101 011110 100001 010101 010101

int E_Table[48] = {
32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9,
8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25,
24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1};

进行密钥混合 E(R0) ^ K1

1
2
3
E(R0)	=	011110 100001 010101 010101 011110 100001 010101 010101
K1 = 000110 110000 001011 101111 111111 000111 000001 110010
K1+E(R0)= 011000 010001 011110 111010 100001 100110 010100 100111

然后就进入到DES算法核心

也就是S盒,S盒里是8*64个数据,即8个S盒,每个盒都是64个数据

将上一步的结果分成8块6bit

1
2
3
4
5
6
7
8
B1 = 011000
B2 = 010001
B3 = 011110
B4 = 111010
B5 = 100001
B6 = 100110
B7 = 010100
B8 = 100111

把B1-B8的值当成索引,在S1-S8盒中取值,规则如下:

以B1为例,值为011000,将它分成0 1100 0,得到

1
2
i = 00 即 0 (这里是二进制的00转十进制)
j = 1100 即 12 (这里是二进制的1100转十进制)

在S1(因为是B1,所以就在S1里找)中查找第0行第12列的值(这里从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
int S_Box[8][4][16] = {
// S1
14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13,
// S2
15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9,
// S3
10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12,
// S4
7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14,
// S5
2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3,
// S6
12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13,
// S7
4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12,
// S8
13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11};

第0行第12列的值为5

1
B1 = 5 = 0101

再以B2为例

1
2
3
4
B2 = 0 1000 1
i = 01 = 1(这里是二进制的01转十进制)
j = 1000 = 8(这里是二进制的1000转十进制)
B2 = 12 = 1100

B1在S1中找,B2在S2中找,以此类推

最后,8个6比特的值在S盒作用下变成8个4比特的值

1
S(K1+E(R0)) = 0101 1100 1000 0010 1011 0101 1001 0111

得到的结果进行P表的重排

1
2
3
int P_Table[32] = {
16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25};
1
P(S(K1+E(R0))) = 0010 0011 0100 1010 1010 1001 1011 1011

F函数完事,即F = P(S(K1+E(R0)))

F的结果与L0异或

1
2
L1 = R0 = 11110000 10101010 11110000 10101010
R1 = Ln-1 xor f(Rn-1,Kn) = 11101111 01001010 01100101 01000100

一轮运算完毕,16轮运算的操作完全一致

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
L1: 11110000 10101010 11110000 10101010
R1: 11101111 01001010 01100101 01000100
L2: 11101111 01001010 01100101 01000100
R2: 11001100 00000001 01110111 00001001
L3: 11001100 00000001 01110111 00001001
R3: 10100010 01011100 00001011 11110100
L4: 10100010 01011100 00001011 11110100
R4: 01110111 00100010 00000000 01000101
L5: 01110111 00100010 00000000 01000101
R5: 10001010 01001111 10100110 00110111
L6: 10001010 01001111 10100110 00110111
R6: 11101001 01100111 11001101 01101001
L7: 11101001 01100111 11001101 01101001
R7: 00000110 01001010 10111010 00010000
L8: 00000110 01001010 10111010 00010000
R8: 11010101 01101001 01001011 10010000
L9: 11010101 01101001 01001011 10010000
R9: 00100100 01111100 11000110 01111010
L10:00100100 01111100 11000110 01111010
R10:10110111 11010101 11010111 10110010
L11:10110111 11010101 11010111 10110010
R11:11000101 01111000 00111100 01111000
L12:11000101 01111000 00111100 01111000
R12:01110101 10111101 00011000 01011000
L13:01110101 10111101 00011000 01011000
R13:00011000 11000011 00010101 01011010
L14:00011000 11000011 00010101 01011010
R14:11000010 10001100 10010110 00001101
L15:11000010 10001100 10010110 00001101
R15:01000011 01000010 00110010 00110100
L16:01000011 01000010 00110010 00110100
R16:00001010 01001100 11011001 10010101

然后,L16和R16倒换,左右调一下顺序,结果为R16L16

1
R16L16 = 00001010 01001100 11011001 10010101 01000011 01000010 00110010 00110100

最后一步,根据FP表进行末置换

1
10000101 11101000 00010011 01010100 00001111 00001010 10110100 00000101
1
2
3
4
5
int FP_Table[64] = {
40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25};

上述数据的十六进制形式为

1
85 e8 13 54 0f 0a b4 05

PS:末置换就是初始置换的逆运算,意思如下

最后这里的FP_Table和一开始的IP_Table是互为可逆的

1
2
3
4
5
6
7
8
9
10
11
12
13
int IP_Table[64] = {
58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7};

int FP_Table[64] = {
40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25};

IP_Table,第一个bit跑到了第40个,所以FP_Table里的第一个元素就是40

demo

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
#include<stdio.h>
/*------------------------
定义枚举型全局变量
------------------------*/
typedef enum {
false = 0,
true = 1
} bool;

// 十六轮子密钥
static bool SubKey[16][48] = {0};

/*---------------------*/
/*-------------------------------------------------------------
各种置换表
-------------------------------------------------------------*/
// IP置换表
const char IP_Table[64] = {
58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7
};
// IP-1置换表
const char IPR_Table[64] = {
40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25
};

// E扩展表
static char E_Table[48] = {
32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9,
8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25,
24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1
};
// PC1置换表
static char PC1_Table[56] = {
57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18,
10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36,
63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22,
14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4
};

// pc2表
static char PC2_Table[48] = {
14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10,
23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2,
41, 52, 31, 37, 47, 55, 30, 40, 51, 34, 33, 48,
44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
};
// 移位表
static char Move_Table[16] = {
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
};
// S盒
static char S_Box[8][4][16] = {
//S1
14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13,
//S2
15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9,
//S3
10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12,
//S4
7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14,
//S5
2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3,
//S6
12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
10, 15, 4, 2, 7, 12, 0, 5, 6, 1, 13, 14, 0, 11, 3, 8,
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13,
//S7
4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
13, 0, 11, 7, 4, 0, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12,
//S8
13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
};
//P置换表
static char P_Table[32] = {
16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25
};
/*-------------------------------------------------------------------*/

/*-----------------------------自定义函数-----------------------------*/
void SetKey(char My_key[8]); //生成16轮的子密钥;
void ByteToBit(bool *Data_out, char *Data_in, int Num); //字节转换成位;
void Change_bit(bool *Data_out, int Num);//二进制的位置进行转换;
void BitToByte(char My_message[8], bool *Message_in, int Num); //位转换成字节;
void TableReplace(bool *Data_out, bool *Data_in, const char *Table, int Num); //各种表的置换算法;
void Bitcopy(bool *Data_out, bool *Data_in, int Num); //二进制数组的拷贝
void Loop_bit(bool *Data_out, int movstep, int len); //左移位;
void Run_Des(char My_message[8], char HexMssage[16]);//des的轮加密算法
void Xor(bool *Message_out, bool *Message_in, int Num); //执行异或
void S_change(bool *Data_out, bool *Data_in); // S盒变换;
void HexToBit(bool *Data_out, char *Data_in, int Num); // 十六进制转二进制
void BitToHex(char *Data_out, bool *Data_in, int Num); //二进制转换成十六进制;
void Run_desDes(char My_message[8], char HexMessage[16]);// DES轮解密算法;

/*--------------------------*/

/*--------------------------主函数----------------------------------*/
int main() {
int i = 0, j;
char My_key[8] = "87654321"; //记录加密密钥;
char You_key[8] = "87654321"; //解密密钥
char My_message[8] = "12345678"; //明文
char Message_hex[16] = {0};//16进制的密文

SetKey(My_key); //生成16轮的加密子密钥;
Run_Des(My_message, Message_hex); //des的轮加密过程
printf("经过加密的密文为:\n");
for (i = 0; i < 16; i++) {
printf("%c", Message_hex[i]);
}
printf("\n");

SetKey(You_key); //生成16轮的解密子密钥;
Run_desDes(My_message, Message_hex);//解密;
printf("解密结果为:\n");
for (i = 0; i < 8; i++) {
printf("%c", My_message[i]);
}
printf("\n");
return 0;
}

/*--------------------具体函数定义----------------------*/
void Bitcopy(bool *Data_out, bool *Data_in, int Num) //二进制数组拷贝
{
int i = 0;
for (i = 0; i < Num; i++) {
Data_out[i] = Data_in[i];
}

}

void Change_bit(bool *Data_out, int Num) //二进制的位置进行转换;
{
int i, j;
static bool Temp[8] = {0};
for (i = 0; i < Num / 8; i++) {
Bitcopy(Temp, Data_out, Num / 8);
for (j = 0; j < Num / 8; j++) {
Data_out[j] = Temp[Num / 8 - 1 - j];
}
Data_out += Num / 8;
}
}

void ByteToBit(bool *Data_out, char *Data_in, int Num) //字节转位
{
int i, j;
for (i = 0; i < Num; i++) {
Data_out[i] = (Data_in[i / 8] >> (i % 8)) & 0x01;
}
//Change_bit(Data_out,Num);
}

void BitToHex(char *Data_out, bool *Data_in, int Num) //二进制转十六进制
{
int i;
for (i = 0; i < Num / 4; i++) {
Data_out[i] = 0;
}
for (i = 0; i < Num / 4; i++) {
Data_out[i] = Data_in[4 * i] + Data_in[4 * i + 1] * 2 + Data_in[4 * i + 2] * 4 + Data_in[4 * i + 3] * 8;
if (Data_out[i] % 16 > 9) {
Data_out[i] = Data_out[i] % 16 + '7';
} else
Data_out[i] = Data_out[i] % 16 + '0';
}
}

void HexToBit(bool *Data_out, char *Data_in, int Num) //十六进制转二进制
{
int i;
for (i = 0; i < Num; i++) {
if (Data_in[i / 4] <= '9') {
Data_out[i] = ((Data_in[i / 4] - '0') >> (i % 4)) & 0x01;
} else {
Data_out[i] = ((Data_in[i / 4] - '7') >> (i % 4)) & 0x01;
}
}
}

void BitToByte(char My_message[8], bool *Message_in, int Num) //位转换成字节
{
int i = 0;
for (i = 0; i < (Num / 8); i++) {
My_message[i] = 0;
}
for (i = 0; i < Num; i++) {
My_message[i / 8] |= Message_in[i] << (i % 8);
}
}

void TableReplace(bool *Data_out, bool *Data_in, const char *Table, int Num) // 置换算法
{
int i = 0;
static bool Temp[256] = {0};
for (i = 0; i < Num; i++) {
Temp[i] = Data_in[Table[i] - 1];
}
Bitcopy(Data_out, Temp, Num);
}

void Loop_bit(bool *Data_out, int movstep, int len) {
static bool Temp[256] = {0};
Bitcopy(Temp, Data_out, movstep);
Bitcopy(Data_out, Data_out + movstep, len - movstep);
Bitcopy(Data_out + len - movstep, Temp, movstep);
/*Temp=Data_out;
Temp[movstep]='\0';
Data_out=Data_out+movstep;
Data_out+(len-movstep)=Temp;*/
}

void Xor(bool *Message_out, bool *Message_in, int Num)//执行异或
{
int i;
for (i = 0; i < Num; i++) {
Message_out[i] = Message_out[i] ^ Message_in[i];
}
}

void SetKey(char My_key[8]) {
int i, j;
static bool Key_bit[64] = {0}; //Key的二进制缓存;
static bool *Key_bit_L, *Key_bit_R;
Key_bit_L = &Key_bit[0]; //key的左边28位;
Key_bit_R = &Key_bit[28]; //key的右边28位;
ByteToBit(Key_bit, My_key, 64);
/* Change_bit(Key_bit,64) ;//二进制的位置进行转换;
for(i=0;i<64;i++)
{
printf("%d ",Key_bit[i]);
}
printf("\n");
printf("\n");*/
TableReplace(Key_bit, Key_bit, PC1_Table, 56);//pc-1 置换
for (i = 0; i < 16; i++) {
Loop_bit(Key_bit_L, Move_Table[i], 28);
Loop_bit(Key_bit_R, Move_Table[i], 28);
TableReplace(SubKey[i], Key_bit, PC2_Table, 48);//pc-2置换
}
}

void S_change(bool *Data_out, bool *Data_in) //S盒变换
{
int i;
int r = 0, c = 0;//S盒的行和列;
for (i = 0; i < 8; i++, Data_in = Data_in + 6, Data_out = Data_out + 4) {
r = Data_in[0] * 2 + Data_in[5] * 1;
c = Data_in[1] * 8 + Data_in[2] * 4 + Data_in[3] * 2 + Data_in[4] * 1;
ByteToBit(Data_out, &S_Box[i][r][c], 4);
}
}

void F_change(bool Data_out[32], bool Data_in[48]) // f函数;
{
int i;
static bool Message_E[48] = {0}; //存放E置换的结果;
TableReplace(Message_E, Data_out, E_Table, 48);//E表置换
Xor(Message_E, Data_in, 48);
S_change(Data_out, Message_E); // S盒变换
TableReplace(Data_out, Data_out, P_Table, 32); //P置换
}

void Run_Des(char My_message[8], char HexMssage[16])//des轮加密算法;
{
int i;
static bool Message_bit[64] = {0};
static bool *Message_bit_L = &Message_bit[0], *Message_bit_R = &Message_bit[32];
static bool Temp[32] = {0};
ByteToBit(Message_bit, My_message, 64);
/*Change_bit(Message_bit,64) ;//二进制的位置进行转换;
for(i=0;i<64;i++)
{
printf("%d ",Message_bit[i]);
}
printf("\n");
printf("\n");*/
TableReplace(Message_bit, Message_bit, IP_Table, 64);
for (i = 0; i < 16; i++) {
Bitcopy(Temp, Message_bit_R, 32);
F_change(Message_bit_R, SubKey[i]);
Xor(Message_bit_R, Message_bit_L, 32);
Bitcopy(Message_bit_L, Temp, 32);
}
TableReplace(Message_bit, Message_bit, IPR_Table, 64);
BitToHex(HexMssage, Message_bit, 64);//二进制转换成十六进制;
}

void Run_desDes(char My_message[8], char HexMessage[16])// DES轮解密算法;
{
int i = 0;
static bool Message_bit[64] = {0};
static bool *Message_bit_L = &Message_bit[0], *Message_bit_R = &Message_bit[32];
static bool Temp[32] = {0};
HexToBit(Message_bit, HexMessage, 64);
TableReplace(Message_bit, Message_bit, IP_Table, 64);
for (i = 15; i >= 0; i--) {
Bitcopy(Temp, Message_bit_L, 32);
F_change(Message_bit_L, SubKey[i]);
Xor(Message_bit_L, Message_bit_R, 32);
Bitcopy(Message_bit_R, Temp, 32);
}
TableReplace(Message_bit, Message_bit, IPR_Table, 64);
BitToByte(My_message, Message_bit, 64);
}

DES的魔改

明文的初始置换和末置换,取消或者修改,都不影响DES的安全性

S盒

白盒,AES里比较多,效果就是反编译出了代码也拿不到密钥,密钥的生成在代码中实现,这个得去扣代码或者主动调用

DES的查表法

为了加快加密的速度,在加密结果有限的情况下,有的代码里会把所有可能的加密结果放到一张表里,直接去查这张表

比如libcrypto.so

去搜这个0x40108010就行,可以搜到是DES的,一般是固定的,用于特定场景

分组加密的填充

上面的demo,是没有填充的

明文长度不够1个分组,就需要填充,以最常用的PKCS7填充为例

明文没有内容或者明文刚好一个分组长度,都需要再填充一个分组

最多填充一个分组,08 08 08 08 08 08 08 08 (PKCS7)

其他填充的情况

1
2
3
4
5
6
7
ab cd ef ab cd ef ab 01 (明文7个字节)
ab cd ef ab cd ef 02 02 (明文6个字节)
ab cd ef ab cd 03 03 03 (明文5个字节)
..
ab 07 07 07 07 07 07 07 (明文1个字节)
0a 07 07 07 07 07 07 07
// 如果明文里有半个字节,先补0再填充,0a算最终明文,填充部分解密后会去掉

DES加密的结果一定是分组长度的倍数

分组加密的模式-ECB

明文超过1个分组长度,就需要了解加密/工作模式

ECB模式,每个分组之间单独加密、互不关联

2个分组明文一样,结果也一样,那么只需要破解其中一个就可以了

可以通过替换某段密文来达到替换明文的目的

分组加密的模式-CBC

每个明文分组先和上个分组的密文进行异或,得到的结果进行加密得到密文,这里的加密其实就是ECB的加密

第一个明文块没有上个分组的密文块,需要指定一个64bit的输入,也就是初始化向量iv

流程图

CBC模式举例

1
2
3
4
5
6
7
8
9
10
明文:123456789ABCDEF0
密钥:133457799BBCDFF1
IV:0123456789ABCDEF
结果:0ecb68bac16aece0 7cbadcfa7a974bcc
0ecb68bac16aece0对应的明文就是通过123456789ABCDEF0和0123456789ABCDEF异或得到的
123456789ABCDEF0 ^ 0123456789ABCDEF = 1317131f1317131f
对1317131f1317131f进行DES_ECB加密,得到0ecb68bac16aece0
然后计算第二个分组,即 0808080808080808
0ecb68bac16aece0 ^ 0808080808080808 = 06c360b2c962e4e8
对06c360b2c962e4e8进行DES_ECB加密,得到7cbadcfa7a974bcc

分组加密的模式-CFB

1
2
3
4
5
6
明文:123456789ABCDEF0
密钥:133457799BBCDFF1
IV:0123456789ABCDEF
结果:97dc452c95b66af5
iv即0123456789ABCDEF的des加密结果85e813540f0ab405(iv的加密是ECB加密)
85e813540f0ab405 与 123456789ABCDEF0的异或结果是97dc452c95b66af5

分组加密的模式-OFB

1
2
3
4
5
明文:123456789ABCDEF0
密钥:133457799BBCDFF1
IV:0123456789ABCDEF
结果:97dc452c95b66af5
即只有一个分组的话,OFB和CFB是一样的

两个分组的OFB模式

1
2
3
4
5
明文:123456789ABCDEF0 123456789ABCDEF0
密钥:133457799BBCDFF1
IV:0123456789ABCDEF
结果:97dc452c95b66af5 759a2c51fb637db5
第二个分组 759a2c51fb637db5 是明文和加密两次的iv的异或结果

CFB/OFB都可以作为流加密,可以理解为没有填充

1
2
3
4
5
6
明文:a1
密钥:0123456789abcdef
iv:0123456789ABCDEF
结果:f7
先把iv加密,得到56cc09e7cfdc4cef
56cc09e7cfdc4cef ^ a1 = f7

3DES算法

密钥24个字节,等于3个DES的密钥

3DES处理流程,每次使用一个DES密钥

1
流程:DES加密->DES解密->DES加密

前两个DES密钥相同时,3DES结果等于最后1个密钥DES加密之后的结果

AES算法

特点

1
2
3
4
在一个分组长度内,明文的长度变化,AES的加密结果长度都一样,因为有填充
密钥长度128,192,256三种,如果少1个16进制数,会在前面补0,对应AES128 AES192 AES256
分组长度只有128一种
密文长度与填充后的明文长度有关,一般是16个字节的倍数(有CFB和OFB这种模式,是流加密的)

Rijndael算法也叫AES算法,Rijndael算法本身分组长度可变,但是定位AES算法之后,分组长度只有128一种

DES中是针对bit进行操作,AES中是针对字节进行操作

AES128是10轮运算、AES192是12轮运算、AES256是14轮运算

明文的运算

AES整体流程

初始转换

1
2
明文state化(明文和密钥变成4*4的矩阵)
与种子密钥异或(如果是CBC模式先与iv异或)

明文state化

1
2
明文:32 43 f6 a8 88 5a 30 8d 31 31 98 a2 e0 37 07 34
密钥:2b 7e 15 16 28 ae d2 a6 ab f7 15 88 09 cf 4f 3c

AES-1.PNG

明文运算流程(ECB模式,state是明文,如果是CBC模式state会先和iv异或)

AES-2.PNG

1
2
3
4
5
6
7
8
9
10
11
1.AddRoundKey是轮密钥加
AddRoundKey和CiperKey异或(这里的CiperKey没有处理过,就是种子密钥,没有密钥扩展或者编排)

2.上个过程中得到的结果经过9轮迭代和1轮运算,总共10轮,得到加密结果

3.9轮迭代里,经过sbox替换、循环左移、列混合、轮密钥加四个步骤

4.1轮运算里,经过sbox替换、循环左移、轮密钥加三个步骤

轮密钥加是和轮密钥进行异或,轮密钥是从种子密钥扩展出来的,每轮用一个
例如AES128是10轮,就有10个轮密钥,加上种子密钥,就是11个

9轮运算

1
2
3
4
sbox替换
循环左移
列混合
state与子密钥K1-K9异或

末运算

1
2
3
sbox替换
循环左移
state与子密钥K10异或

sbox运算

AES-3.PNG

就是从sbox里面去取对应的值,比如说这里的state里的19,找sbox里的第一行第九列,就是d4…以此类推

完毕之后如下

循环左移

行方向上移动,第一行不动,第二行左移1个字节,第三行左移2个字节,第三行左移3个字节

列混淆

不用管,纯数学,就是矩阵乘法,只需要知道特征就行,会存在一个4*4数组,里面就是02 03 01这种数字

数据如果有溢出的话,需要做另外的计算,就是用到GaloisMultiplication的乘法运算(0x1B 0x80),需要与0x1B进行位与,就是有0x1B和0x80这两个数字

AddRoundKey

就是每一列进行异或,得到新的

AES-9.PNG

当然这里的RoundKey每轮不一样,总共有9轮,如下所示

AES-10.PNG

S盒与逆S盒

DES里有8个S盒,加解密用的同一个S盒

AES里只有2个,加密用一个,解密用一个,互为可逆的

推几个就很明显了

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
// 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
};

循环左移的几种实现

就是把比特位左移改了一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// uint32_t x循环左移n位
#define ROF32(x, n) (((x) << (n)) | ((x) >> (32-(n))))

//行移位
int shiftRows(uint8_t (*state)[4]) {
uint32_t block[4] = {0};
printf("%x", state);
/* i: row */
for (int 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;
}

IDA里的另一种实现,是libInnoSecure.so的实现

找到数据,进入

右键数组名,选择Byte 0x63,再次右键选择array,array size是256,可以看出来是个数组了

随便找一下引用

推一下下标就知道了

1
2
3
4
5
6
原来state的排序是(数字就是序号)
0 4 8 12
1 5 9 13
2 6 10 14
3 7 11 15
就是第1个字节要换到第13个字节的地方,以此类推

个人感觉就看一下下标的特征就行了,1 5 9 13这些

密钥的编排

种子密钥state操作之后每列分为W0-W3

将种子密钥扩展,得到W0-W43

1
2
Wi = Wi-1 ^ Wi-4 (如果下标不是4的倍数)
Wi = T(Wi-1) ^ Wi-4 (下标是4的倍数的时候)

T函数流程:循环左移1个字节、S盒替换、与Rcon异或,例如:取出W3,进行移位,然后再把移位之后的结果和S盒进行替换,再与Rcon进行异或,得到中间结果,再和W0异或,结果放到W4这里

Rcon的下标和i也有关系,比如要计算W8,那么就是和Rcon[8]进行计算

一共是W0-W43,一共44组,每轮使用4组(也就是4*4的假矩阵)

其他注意

字节替换和循环左移是可以换顺序的

第一次对明文进行AddRoundKey的时候,因为那里是直接和种子密钥进行异或,在异或处可以得到种子密钥,而且如果是ECB模式的话,还能得到明文,所以在这里Hook异或的代码,就可以拿到种子密钥,当然前提是代码是标准AES算法,如果是AES查表法实现,是没有AddRoundKey的

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
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
#include <stdint.h>
#include <stdio.h>
#include <string.h>

typedef struct {
uint32_t eK[44], dK[44]; // encKey, decKey
int Nr; // 10 rounds
} AesKey;

#define BLOCKSIZE 16 //AES-128分组长度为16字节

// 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) {
for (int i = 0; i < 4; ++i) {
for (int 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) {
for (int i = 0; i < 4; ++i) {
for (int 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] */
for (int i = 0; i < 4; ++i) {
LOAD32H(w[i], key + 4 * i);
}

/* W[4-43] */
for (int 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]
for (int j = 0; j < 11; ++j) {

for (int 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];

/* i: row, j: col */
for (int i = 0; i < 4; ++i) {
for (int 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 */
for (int i = 0; i < 4; ++i) {
for (int 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 */
for (int i = 0; i < 4; ++i) {
for (int 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};
printf("%x", state);
/* i: row */
for (int 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 */
for (int 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;

for (int 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] */
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
tmp[i][j] = state[i][j];
}
}

for (int i = 0; i < 4; ++i) {
for (int 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}}; //使用列混合矩阵的逆矩阵

/* copy state[4][4] to tmp[4][4] */
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
tmp[i][j] = state[i][j];
}
}

for (int i = 0; i < 4; ++i) {
for (int 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); // 秘钥扩展

// 使用ECB模式循环加密多个分组长度的数据
for (int i = 0; i < len; i += BLOCKSIZE) {
// 把16字节的明文转换为4x4状态矩阵来进行处理
loadStateArray(state, pt);
// 轮秘钥加
addRoundKey(state, rk);

for (int 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); //秘钥扩展,同加密

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

for (int 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;
}

// 方便输出16进制数据
void printHex(char *tag, uint8_t *ptr, int len) {
printf("%s\ndata[%d]: ", tag, len);
for (int i = 0; i < len; ++i) {
printf("%.2X ", *ptr++);
}
printf("\n");
}

int main() {

// case 1
const uint8_t key[16] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f,
0x3c};
const uint8_t pt[16] = {0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07,
0x34};
uint8_t ct[16] = {0}; // 外部申请输出数据内存,用于加密后的数据
uint8_t plain[16] = {0}; // 外部申请输出数据内存,用于解密后的数据

aesEncrypt(key, 16, pt, ct, 16); // 加密
printHex("plain data:", pt, 16); // 打印初始明文数据
printHex("after encryption:", ct, 16); // 打印加密后的密文

aesDecrypt(key, 16, ct, plain, 16); // 解密
printHex("after decryption:", plain, 16); // 打印解密后的明文数据

// case 2
// 16字节字符串形式秘钥
const uint8_t key2[] = "1234567890123456";
// 32字节长度字符串明文
const uint8_t *data = (uint8_t *) "abcdefghijklmnopqrstuvwxyz123456";
uint8_t ct2[32] = {0}; //外部申请输出数据内存,用于存放加密后数据
uint8_t plain2[32] = {0}; //外部申请输出数据内存,用于存放解密后数据
//加密32字节明文
aesEncrypt(key2, 16, data, ct2, 32);
printHex("after encryption:", ct2, 32);

// 解密32字节密文
aesDecrypt(key2, 16, ct2, plain2, 32);
// 打印16进制形式的解密后的明文
printHex("after decryption:", plain2, 32);
// 因为加密前的数据为可见字符的字符串,打印解密后的明文字符,与加密前明文进行对比
printf("output plain text\n");
for (int i = 0; i < 32; ++i) {
printf("%c", plain2[i]);
}
return 0;
}

AES查表法的实现

思路,明文不管怎么经过S盒替换,产生的结果一定是有限个的,所以列混合之后的结果一定也是有限的

查表法就是把这些有限的值全部写死

查表法的核心思想是将字节代换层、ShiftRows层和MixColumn层融合为查找表:每个表的大小是32 bits(4字节)乘以256项,一般称为T盒(T-Box)或T表。加密过程4个表(Te),解密过程4个表(Td),共8个。(也不一定是8个,还有各种可能)

推导过程参考https://zhuanlan.zhihu.com/p/42264499,不过看了也看不懂,反正结论如下

每列密文都经过了4个T表的查表(就是T函数),然后和本轮子密钥 ki 异或,一共异或了5次

上面图里的A0-A15是16个元素的序号

rust实现的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// round 2 to round 9 (or 11, 13)
for i in 1..(rounds / 2) {
// even-number rounds
wa0 = TE0[ (wb0 >> 24) as usize ] ^ TE1[((wb1 >> 16) as usize) & 0xFF] ^
TE2[((wb2 >> 8) as usize) & 0xFF] ^ TE3[( wb3 as usize) & 0xFF] ^ keys[8 * i];
wa1 = TE0[ (wb1 >> 24) as usize ] ^ TE1[((wb2 >> 16) as usize) & 0xFF] ^
TE2[((wb3 >> 8) as usize) & 0xFF] ^ TE3[( wb0 as usize) & 0xFF] ^ keys[8 * i + 1];
wa2 = TE0[ (wb2 >> 24) as usize ] ^ TE1[((wb3 >> 16) as usize) & 0xFF] ^
TE2[((wb0 >> 8) as usize) & 0xFF] ^ TE3[( wb1 as usize) & 0xFF] ^ keys[8 * i + 2];
wa3 = TE0[ (wb3 >> 24) as usize ] ^ TE1[((wb0 >> 16) as usize) & 0xFF] ^
TE2[((wb1 >> 8) as usize) & 0xFF] ^ TE3[( wb2 as usize) & 0xFF] ^ keys[8 * i + 3];
// odd-number rounds
wb0 = TE0[ (wa0 >> 24) as usize ] ^ TE1[((wa1 >> 16) as usize) & 0xFF] ^
TE2[((wa2 >> 8) as usize) & 0xFF] ^ TE3[( wa3 as usize) & 0xFF] ^ keys[8 * i + 4];
wb1 = TE0[ (wa1 >> 24) as usize ] ^ TE1[((wa2 >> 16) as usize) & 0xFF] ^
TE2[((wa3 >> 8) as usize) & 0xFF] ^ TE3[( wa0 as usize) & 0xFF] ^ keys[8 * i + 5];
wb2 = TE0[ (wa2 >> 24) as usize ] ^ TE1[((wa3 >> 16) as usize) & 0xFF] ^
TE2[((wa0 >> 8) as usize) & 0xFF] ^ TE3[( wa1 as usize) & 0xFF] ^ keys[8 * i + 6];
wb3 = TE0[ (wa3 >> 24) as usize ] ^ TE1[((wa0 >> 16) as usize) & 0xFF] ^
TE2[((wa1 >> 8) as usize) & 0xFF] ^ TE3[( wa2 as usize) & 0xFF] ^ keys[8 * i + 7];
}

代码解释:

上面的原理可以看到,D0-D3这一列的结果是要依次取出A0、A5、A10、A15,然后分别查表,把查表的结果异或之后再与子密钥异或

怎么取A0到A15呢?以这一句为例

1
2
wa0 = TE0[ (wb0 >> 24) as usize        ] ^ TE1[((wb1 >> 16) as usize) & 0xFF] ^
TE2[((wb2 >> 8) as usize) & 0xFF] ^ TE3[( wb3 as usize) & 0xFF] ^ keys[8 * i];

假设A0-A15如下

1
2
3
4
5
6
7
8
wb0 0 4 8  12
wb1 1 5 9 13
wb2 2 6 10 14
wb3 3 7 11 15
wb0就代表第一行,以此类推

wb1 >> 16,得到 0 0 1 5 (9和13溢出丢了),然后和0xFF位与,其实就是取出来了最后的5,也就是拿到了A5,再去根据A5查TE1表
以此类推

当然,由于最后一轮没有列混淆操作,所以有时候还需要查一个SBOX表(S盒),但是也不一定需要S盒,比如openssl里就是4个T表搞定的,T表里的某个字节就是S盒里的元素,所以S盒也省了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// final round
output[ 0] = SBOX[ (wb0 >> 24) as usize ] ^ ((keys[keys.len() - 4] >> 24) as u8);
output[ 1] = SBOX[((wb1 >> 16) as usize) & 0xFF] ^ ((keys[keys.len() - 4] >> 16) as u8);
output[ 2] = SBOX[((wb2 >> 8) as usize) & 0xFF] ^ ((keys[keys.len() - 4] >> 8) as u8);
output[ 3] = SBOX[( wb3 as usize) & 0xFF] ^ ( keys[keys.len() - 4] as u8);
output[ 4] = SBOX[ (wb1 >> 24) as usize ] ^ ((keys[keys.len() - 3] >> 24) as u8);
output[ 5] = SBOX[((wb2 >> 16) as usize) & 0xFF] ^ ((keys[keys.len() - 3] >> 16) as u8);
output[ 6] = SBOX[((wb3 >> 8) as usize) & 0xFF] ^ ((keys[keys.len() - 3] >> 8) as u8);
output[ 7] = SBOX[( wb0 as usize) & 0xFF] ^ ( keys[keys.len() - 3] as u8);
output[ 8] = SBOX[ (wb2 >> 24) as usize ] ^ ((keys[keys.len() - 2] >> 24) as u8);
output[ 9] = SBOX[((wb3 >> 16) as usize) & 0xFF] ^ ((keys[keys.len() - 2] >> 16) as u8);
output[10] = SBOX[((wb0 >> 8) as usize) & 0xFF] ^ ((keys[keys.len() - 2] >> 8) as u8);
output[11] = SBOX[( wb1 as usize) & 0xFF] ^ ( keys[keys.len() - 2] as u8);
output[12] = SBOX[ (wb3 >> 24) as usize ] ^ ((keys[keys.len() - 1] >> 24) as u8);
output[13] = SBOX[((wb0 >> 16) as usize) & 0xFF] ^ ((keys[keys.len() - 1] >> 16) as u8);
output[14] = SBOX[((wb1 >> 8) as usize) & 0xFF] ^ ((keys[keys.len() - 1] >> 8) as u8);
output[15] = SBOX[( wb2 as usize) & 0xFF] ^ ( keys[keys.len() - 1] as u8);

openssl的AES实现

一般Android上,查表法会比较多

1
https://github.com/openssl/openssl/blob

从T表里定位出S盒的元素

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
int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
AES_KEY *key)
{

u32 *rk;
int i = 0;
u32 temp;

if (!userKey || !key)
return -1;
if (bits != 128 && bits != 192 && bits != 256)
return -2;

rk = key->rd_key;

if (bits == 128)
key->rounds = 10;
else if (bits == 192)
key->rounds = 12;
else
key->rounds = 14;

rk[0] = GETU32(userKey );
rk[1] = GETU32(userKey + 4);
rk[2] = GETU32(userKey + 8);
rk[3] = GETU32(userKey + 12);
if (bits == 128) {
while (1) {
temp = rk[3];
rk[4] = rk[0] ^
(Te2[(temp >> 16) & 0xff] & 0xff000000) ^
(Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^
(Te0[(temp ) & 0xff] & 0x0000ff00) ^
(Te1[(temp >> 24) ] & 0x000000ff) ^
rcon[i];
rk[5] = rk[1] ^ rk[4];
rk[6] = rk[2] ^ rk[5];
rk[7] = rk[3] ^ rk[6];
if (++i == 10) {
return 0;
}
rk += 4;
}
}
rk[4] = GETU32(userKey + 16);
rk[5] = GETU32(userKey + 20);
if (bits == 192) {
while (1) {
temp = rk[ 5];
rk[ 6] = rk[ 0] ^
(Te2[(temp >> 16) & 0xff] & 0xff000000) ^
(Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^
(Te0[(temp ) & 0xff] & 0x0000ff00) ^
(Te1[(temp >> 24) ] & 0x000000ff) ^
rcon[i];
rk[ 7] = rk[ 1] ^ rk[ 6];
rk[ 8] = rk[ 2] ^ rk[ 7];
rk[ 9] = rk[ 3] ^ rk[ 8];
if (++i == 8) {
return 0;
}
rk[10] = rk[ 4] ^ rk[ 9];
rk[11] = rk[ 5] ^ rk[10];
rk += 6;
}
}
rk[6] = GETU32(userKey + 24);
rk[7] = GETU32(userKey + 28);
if (bits == 256) {
while (1) {
temp = rk[ 7];
rk[ 8] = rk[ 0] ^
// 先和0xff位与,找到Te2表中的某一个值,然后该值和0xff000000位与,取出第一个字节
// 取出来的第一个字节就是S盒的一个字节
(Te2[(temp >> 16) & 0xff] & 0xff000000) ^
(Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^
(Te0[(temp ) & 0xff] & 0x0000ff00) ^
(Te1[(temp >> 24) ] & 0x000000ff) ^
rcon[i];
rk[ 9] = rk[ 1] ^ rk[ 8];
rk[10] = rk[ 2] ^ rk[ 9];
rk[11] = rk[ 3] ^ rk[10];
if (++i == 7) {
return 0;
}
temp = rk[11];
rk[12] = rk[ 4] ^
(Te2[(temp >> 24) ] & 0xff000000) ^
(Te3[(temp >> 16) & 0xff] & 0x00ff0000) ^
(Te0[(temp >> 8) & 0xff] & 0x0000ff00) ^
(Te1[(temp ) & 0xff] & 0x000000ff);
rk[13] = rk[ 5] ^ rk[12];
rk[14] = rk[ 6] ^ rk[13];
rk[15] = rk[ 7] ^ rk[14];

rk += 8;
}
}
return 0;
}

// S盒的元素:0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,...
static const u32 Te2[256] = {
// 0x63a5c663U的第一个字节就是63,以此类推
0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
};

解密的时候还是定义的逆S盒,因为逆S盒从T表里取不出来

查表法的还有一个特征就是5次异或、4次异或等

/* round 1: */
t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4];
t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5];
t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6];
t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7];
/* round 2: */
s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8];
s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9];
s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10];
s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11];
...

openssl的EVP使用

以librong360.so为例

可以看到导入表里很多函数,当然这里插件是找不到加密的,因为so自己没实现这个算法,是用的别的so里的

图1

以EVP_aes_128_cbc函数为例,找到调用的地方

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
char *__fastcall cbc_encode(int a1, int a2, _DWORD *a3, int a4)
{
int v8; // r0
int v9; // r0
unsigned int v10; // r10
char *v11; // r4
const char *v12; // r0
int v13; // r1
int v15; // [sp+A0h] [bp-4h] BYREF
_BYTE v16[16]; // [sp+A4h] [bp+0h] BYREF
char v17[140]; // [sp+B4h] [bp+10h] BYREF

memset(v16, 0, sizeof(v16));
// 初始化的,一般不用管
v8 = EVP_CIPHER_CTX_init(v17);
v9 = EVP_aes_128_cbc(v8);
// EVP_EncryptInit_ex的第一个参数是init函数的结构体,就是v17
// 第二个参数是EVP_aes_128_cbc的返回值,代表是aes128CBC的加密,如果是ECB或者别的,函数名就变了
// 第三个参数是0,一般固定的
// 第四个参数是key
// 第五个参数是iv
if ( EVP_EncryptInit_ex(v17, v9, 0, a4, v16) != 1 )
{
crypt_error("EVP_EncryptInit_ex");
LABEL_10:
v11 = 0;
goto LABEL_11;
}
v15 = 0;
v10 = a2 + EVP_CIPHER_CTX_block_size(v17);
v11 = (char *)operator new[](v10);
memset(v11, 0, v10);
// 第一个参数是init函数的结构体,就是v17
// 第二个参数是输出,第三个参数是输出的长度
// 第四个参数是输入,第五个参数是输入的长度
if ( EVP_EncryptUpdate(v17, v11, &v15, a1, a2) != 1 )
{
v12 = "EVP_EncryptUpdate";
goto LABEL_7;
}
v13 = *a3 + v15;
*a3 = v13;
// EVP_EncryptFinal_ex的第二个参数是输出,第三个参数是输出的长度
// v11就是加密的结果
if ( EVP_EncryptFinal_ex(v17, &v11[v13], &v15) != 1 )
{
v12 = "EVP_EncryptFinal_ex";
LABEL_7:
crypt_error(v12);
if ( v11 )
operator delete[](v11);
goto LABEL_10;
}
*a3 += v15;
LABEL_11:
EVP_CIPHER_CTX_cleanup(v17);
return v11;
}

白盒AES算法

白盒AES的核心是直接把key融入到一个表中,这样AES算法就变成了一个查表的过程,这样攻击者很难拿到原始的key,有160个表,160个表拿到没问题,但是不好拿到密钥

但是加密结果和AES一致

AES和DES的区别与联系

DES和AES都是对称加密算法,加解密使用同一个密钥

DES和AES都是分组加密算法,DES的分组长度是64bit,AES的分组长度是128bit

DES密钥64bit,AES密钥128、192、256

DES运算的基本单元是bit,AES的基本运算单元是字节

DES和AES都可以分成密钥的编排和明文的处理

DES和AES在密钥的编排中,都通过对密钥的处理生成一系列的子密钥subkey,每轮运算用1个

DES有16轮运算,在密钥编排中生成了16组子密钥;AES有10、12、14轮运算,也生成10、12、14组子密钥,两者都是每轮用一个

AES每轮运算经过4个步骤

1
2
3
4
5
6
第一步字节替换(S盒替换),16*16的S盒,加解密使用不同的S盒
DES中也存在S盒,是4*16的S盒,共8个,加解密使用相同的S盒

第二步是循环左移,DES或者哈希算法中都有,AES是按字节移动,DES和哈希算法是按位移动
第三步列混淆,AES特有
第四步和密钥异或,DES中有这一步

PS几点

对于概念性,原理性的东西知道即可,重点是要熟悉哪些特征是哪些算法

看到一些常量,也可以直接去搜这些常量,看能不能找到算法