AES-C语言实现

参考文章

https://ppppz.net/2022/01/31/AES-P-Z/

无论是实现还是debug的时候都离不开大佬的博客

源代码

实现的时候发现没学完整,导致中间实现的时候出现了问题,代码写的有点丑,我自己都看不下去了(

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
#include<stdio.h>
#include<string.h>
#include<Windows.h>
//用于字节代换的S盒
static const int 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
};

//用于列混合乘法的表
static int colM[4][4]
{
2,3,1,1,
1,2,3,1,
1,1,2,3,
3,1,1,2
};
//密钥扩展存储的w表
int w[44]={0};

int round_const[10] = {
0x01000000,0x02000000,0x04000000,0x08000000,
0x10000000,0x20000000,0x40000000,0x80000000,
0x1B000000,0x36000000
};

int CharToInt(char* key)
{
int a[4] = { 0 };
a[0] = (int) * key << 24;
a[1] = (int) * (key + 1) << 16;
a[2] = (int) * (key + 2) << 8;
a[3] = (int) * (key + 3);
return a[0] | a[1] | a[2] | a[3];
}
int GetWordFromStr(char* key)
{
int tmp=0;
//因为最后返回的是一个整型,而我们传入的是四个字节,所以要先转为
tmp = CharToInt(key);
return tmp;
}


void IntToArray(int w, int* array)
{
array[0] = (w >> 24)&0xff;
array[1] = (w >> 16)&0xff;
array[2] = (w >> 8) & 0xff;
array[3] = w & 0xff;
return ;
}
void ByteLeftMove(int* array)
{
int tmp = array[0];
for (int i = 0; i < 3; ++i)
{
array[i] = array[i + 1];
}
array[3] = tmp;
return;
}

int ByteChange(int byte)
{
int left = (byte & 0xF0) >> 4;
int right = (byte & 0xF);
return S[left][right];
}

int ArrayToInt(int* temp)
{
int one = temp[0] << 24;
int two = temp[1] << 16;
int three = temp[2] << 8;
int four = temp[3];
return one | two | three | four;
}
int T(int w, int round)
{
int temp[4] = { 0 };
IntToArray(w, temp);//先把int型的w转为数组,方便进行字循环操作
ByteLeftMove(temp);
//字节代换,此时需要用到S表
for (int i = 0; i < 4; ++i)
{
temp[i]=ByteChange(temp[i]);
}
//轮常量异或,为了方便异或,先把数组转为int整型
w=ArrayToInt(temp);
w ^= round_const[round];
return w;
}

void ExtendKey(char* key)
{
//先生成一个子表,在此基础上扩展-子密钥初始化
for (int i = 0; i < 4; ++i)
{
w[i] = GetWordFromStr(key + i * 4);//每四个字节生成一个w数组的元素,便于待会异或
}
//密钥扩展
for (int i = 4,j=0; i < 44; ++i)
{
if (i % 4 == 0)//根据是否是4的整数倍,进行操作
{
//j表示轮数,因为最终为10轮,所以只要是4的倍数就需要+1
w[i] = w[i - 4] ^ T(w[i - 1], j);
j++;
}
else
w[i] = w[i - 4] ^ w[i - 1];
}
return;
}

void PlainToMatrix(char* Plain, int array[4][4])
{
//矩阵 存放和二维数组不同,这里需要注意一下
int k = 0;
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
array[j][i] = Plain[k++];
}
}
return;
}
void w_To_matrix(int array[4][4])
{
//先把每个w转为数组,然后再依次赋值给二维数组
for (int i = 0; i < 4; ++i)
{
int w_temp[4];//使用临时数组存放w的值
IntToArray(w[i],w_temp);
for (int j = 0; j < 4; ++j)
{
array[j][i] = w_temp[j];
}
}
return;
}



void Matrix_Bytechange(int array[4][4])
{
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
array[i][j]=ByteChange(array[i][j]);
}
}
return;
}

//行移位
void RowMove(int array[4][4])
{
//第二行往左移动一个字符
int temp = array[1][0];
for (int i = 0; i < 3; ++i)
{
array[1][i] = array[1][i + 1];
}
array[1][3] = temp;
//第三行往左移动两个字符
int temp2 = array[2][0];
int temp3 = array[2][1];
array[2][0] = array[2][2];
array[2][1] = array[2][3];
array[2][2] = temp2;
array[2][3] = temp3;
//第四行往左移动三个字符,相当于往右移动一位
int temp4 = array[3][3];
for (int i = 3; i >= 0; --i)
{
array[3][i] = array[3][i - 1];
}
array[3][0] = temp4;
return;
}


unsigned char XTIME(unsigned char x)
{
return ((x << 1) ^ ((x & 0x80) ? 0x1b : 0x00));
}
//GF基于有限域上的运算
int GFMul(int a, int b)
{
unsigned char temp[8] = { a };
unsigned char tempmultiply = 0x00;
int i = 0;
for (i = 1; i < 8; i++)
{
temp[i] = XTIME(temp[i - 1]);
}
tempmultiply = (b & 0x01) * a;
for (i = 1; i <= 7; i++)
{
tempmultiply ^= (((b >> i) & 0x01) * temp[i]);
}
return tempmultiply;
}

//列混合
void Line_Change(int array[4][4])
{
//这里需要用到列混合异或的表colM
int temparray[4][4] = { 0 };
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
temparray[i][j] = array[i][j];
}
}
//列混合的矩阵计算方式不同
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
array[i][j] = GFMul(colM[i][0], temparray[0][j]) ^
GFMul(colM[i][1], temparray[1][j]) ^
GFMul(colM[i][2], temparray[2][j]) ^
GFMul(colM[i][3], temparray[3][j]);
}
}
return;
}

//将矩阵的每一列转为整型
int Matrix_ArraytoInt(int one, int two, int three, int four)
{
one <<= 24;
two <<= 16;
three <<= 8;
return one | two | three | four;
}

//将加密后的每一个整型还原到矩阵的每一列
int Restore_Matrix(int temp,int num)
{
switch(num)
{
case 0:
return temp >> 24;
case 1:
return temp >> 16;
case 2:
return temp >> 8;
case 3:
return temp;
}
}
//轮密钥加
void AddRoundKey(int array[4][4], int round)
{
//对每一轮都要进行异或操作,为了方便异或操作,先转为整型
int temp[4] = { 0 };//待会可以直接和w[round]异或,四轮
for (int i = 0; i < 4; ++i)
{
IntToArray(w[round * 4 + i], temp);
for (int j = 0; j < 4; ++j)
{
array[j][i] ^= temp[j];
}
}

return;
}


//最终轮
void FinalRound(int matrix[4][4])
{
Matrix_Bytechange(matrix);

//行移位
RowMove(matrix);

//轮密钥加,注意是对应轮数的密钥
AddRoundKey(matrix, 10);
return;
}

//将矩阵还原到字符串
void MatrixToPlain(char* String, int array[4][4])
{
int k = 0;
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
String[k++] = array[j][i];
}
}
//字符串以'\0'结尾
String[k] = 0;
}
void AES_Encode(char* p, int plen, char* key)
{
//先判断明文长度是否为16的倍数,判断密钥是否为16个字节
int Text_len = strlen(p);
if (strlen(p) % plen != 0)
{
printf("长度出错,明文长度需为十六的倍数");
exit(0);
}
//密钥长度
if (strlen(key) != 16)
{
printf("密钥长度出错,密钥长度必须为十六个字节");
}
//密钥扩展,通过子密钥生成

ExtendKey(key);

//下面进行初始变换,先把明文放入矩阵中,再与第0轮的密钥进行对应位置的逐字节异或
int matrix[4][4] = { 0 };
for (int k = 0; k < plen; k += 16)
{
PlainToMatrix(p+k, matrix);
//明文矩阵和密钥矩阵进行轮密钥加,也就是逐字节异或

AddRoundKey(matrix,0); //问题
//进行九轮相同的加密
for (int i = 1; i < 10; ++i)
{
//字节代换
Matrix_Bytechange(matrix);

//行移位
RowMove(matrix);

//列混合
Line_Change(matrix);

//轮密钥加,注意是对应轮数的密钥
AddRoundKey(matrix, i);
}
FinalRound(matrix);

//最后还要把矩阵还原为字符串
MatrixToPlain(p+k, matrix);
}
}

int main()
{
char key[] = "ABCDEFGHIJKLMNOP";
char PlainText[] = "ABCDEFGHIJKLMNOP";

AES_Encode(PlainText, 16, key);
printf("加密后的密文为:");
for (int i = 0; i < strlen(PlainText); ++i)
{
printf("0x%X,",PlainText[i]&0xff);
}
return 0;
}

例题

看完AES的代码,做点AES的题巩固一下

GWCTF2019-re3

考点-SMC、AES

分析过程

这一段是SMC的解密代码,加上前面的mprotect名称,也可以大致确定,动调跑一下即可

SMC

init_key函数

看到了各种table,开始不知道是什么,但是不影响,因为并没有对我们的输入进行处理,到后面可以知道这是个生成密钥的函数

key_generate

刚好长度为16,对应AES的密钥长度

encode函数

可以看到中间有三个函数,最后的循环对加密后的数据进行比较

而三个函数中,有两个是一样的,这是因为将我们32长度的flag分成了两段进行加密,因为这是128bits的AES加密

key_add

key_add

对比AES的w表生成方式,不难看出这一段是生成w表,不过有点不同,因为我们是在矩阵中进行,并且每一次将一个w元素转为int型,倒是+0x3FFFFFFF那段不知道什么意思,网上说是无穷大的数,这里不是很影响我们的分析,就不纠结了

AES_Encode

可以看出这里就是加密过程,因为先进行第0轮的轮密钥加

Add_Round_Key

然后进行字节替换

ByteReplace

行移位,中间有很多暂时变量,然后实现互换

RowMov

列混合

MixLine

重复进行9轮,最后进行第十轮(不包括列混合)

第十轮

至此key和密文都有了,解密