DES和IDEA的CPP实现

在std版本中,当输入的文本以@开头且后面跟的是绝对地址时,可以读取文档。

DES

数据加密标准(Data Encryption Standard,DES)能够依赖64位密钥加密64位明文,将64位密文反向使用64位密钥生成的子密钥可以按照完全相同的步骤把密文解密为明文,加密步骤如下:

  1. 输入准备
  • 明文:64 位分组(若不足64位则进行填充)。 (cnblogs.com)
  • 密钥:64 位(其中有效56 位,8 位用于奇偶校验)用于生成子密钥。 (维基百科)
  1. 初始置换(Initial Permutation,IP)
  • 将64 位明文按照固定表进行重排,得到一个置换后的64 位数据。 (CSDN博客)
  • 将置换结果分成左右两个32 位部分:L₀ 和 R₀。 (Echo Blog)
  1. 子密钥生成(密钥调度)
  • 对主密钥执行「选择置换 PC-1」从64 位中选出56 位。 (Pion1eer)
  • 将56 位分为两个28 位半密钥,分别左旋移位(每轮移位量由轮次决定)。 (维基百科)
  • 再通过「选择置换 PC-2」从移位后的56 位中选出48 位,作为第1轮子密钥 K₁。重复共生成16 个子密钥 K₁…K₁₆。 (Pion1eer)
  1. 16 轮 Feistel 结构迭代

对于 i = 1 到 16 轮,进行以下操作:

  • $L_{i}$= $R_{i - 1}$
  • $R_{i}$ = $L_{i - 1} \bigoplus f(R_{i - 1}, K_{i}) $ (Echo Blog1)
    其中 f 函数包含:
    1. 扩展置换 E:将32 位 $R_{i - 1}$ 扩展至48 位。 (cnblogs.com)
    2. 与子密钥 Kᵢ 做 XOR。 (知乎专栏)
    3. 分为8块,每块6 位,输入各自 S‐盒,输出4 位。总输出32 位。 (Pion1eer)
    4. 最后通过固定 P 置换将32 位再重排。 (Echo Blog)
  1. 轮结束后的交换与末置换
  • 完成第16轮后,将 L₁₆ 和 R₁₆ 交换,得到新 L₁₇ = R₁₆, R₁₇ = L₁₆。 (Echo Blog)
  • 将 L₁₇ ∥ R₁₇ (合并成64 位)输入「最终置换 FP (IP⁻¹)」进行重排,输出密文。 (cnblogs.com)
  1. 解密流程
  • 解密时流程与加密相同,但子密钥的使用顺序为 K₁₆ → K₁₅ → … → K₁;其余步骤一致。 (cnblogs.com)

base

只接受16进制的16位密钥输出和16进制的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
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
#include<bits/stdc++.h>
using namespace std;
vector<string> Key0(16);
vector<int> LS = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1},
EX_K1 = {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},
EX_K2 = {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},
IP = { 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 },
IPR = {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 = {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},
P = {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};
int SBOX[8][4][16] = {
{{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}},
{{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}},
{{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}},
{{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}},
{{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}},
{{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}},
{{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}},
{{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}}
};
string EX(string s, vector<int> & ex) {
string s2;
for(auto & x : ex) s2 += s[x - 1];
return s2;
}
string movel(string s, int i) {
return s.substr(LS[i]) + s.substr(0, LS[i]);
}
void make_key(string key) {
string key2 = EX(key, EX_K1);
for(int i = 0; i < 16; i ++) {
string l = key2.substr(0, 28), r = key2.substr(28);
l = movel(l, i);
r = movel(r, i);
key2 = l + r;
Key0[i] = EX(key2, EX_K2);
}
}
string XOR(string s1, string s2) {
string s;
for(int i = 0; i < (int)s1.size(); i ++) s += '0' + (s1[i] != s2[i]);
return s;
}
string S(string s) {
string s2;
for(int i = 0; i < 48; i += 6) {
int x = ((s[i] - '0') << 1) + (s[i + 5] - '0'), y = 0;
for(int j = i + 4, base = 1; j > i; j --, base *= 2) y += (s[j] - '0') * base;
int z = SBOX[i / 6][x][y];
for(int j = 3; j >= 0; j --) s2 += '0' + ((z >> j) & 1);
}
return s2;
}
string F(string s, string key) {
s = EX(s, E);
s = XOR(s, key);
s = S(s);
s = EX(s, P);
return s;
}
string DES(string con, bool f) {
con = EX(con, IP);
string l0 = con.substr(0, 32), r0 = con.substr(32), mid;
for(int i = 0; i < 16; i ++) {
mid = r0;
if(f) r0 = XOR(l0, F(r0, Key0[i]));
else r0 = XOR(l0, F(r0, Key0[15 - i]));
l0 = mid;
}
con = EX(r0 + l0, IPR);
return con;
}
string H2B(string s) {
string s2 = "";
vector<string> base = {"0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111" };
for (int i = 0; i < (int)s.size(); i++) {
if (s[i] >= 'A' && s[i] <= 'F') s2 += base[s[i] - 'A' + 10];
else s2 += base[s[i] - '0'];
}
return s2;
}
string B2H(string s) {
string s2 = "";
int temp = 0;
while (s.size() % 4 != 0) s = "0" + s;
for (int i = 0; i < (int)s.size(); i += 4) {
temp = (s[i] - '0') * 8 + (s[i + 1] - '0') * 4 + (s[i + 2] - '0') * 2 + (s[i + 3] - '0') * 1;
if (temp < 10) s2 += '0' + temp;
else s2 += 'A' + (temp - 10);
}
return s2;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
string key, con;
cin >> key >> con;
string key1 = H2B(key), con1 = H2B(con);
make_key(key1);
string mi = DES(con1, 1);
string con2 = DES(mi, 0);
cout << "key: " << key << "\n" << "content: " << con << "\n" << "content_key: " << B2H(mi) << "\n";
cout << "content: " << B2H(con2) << "\n";
return 0;
}

std

示例输入:

1
2
3
4
5
/*
enc
jielycat
Hello! DES!
*/

程序:

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

#include <bits/stdc++.h>
using namespace std;
using u64 = uint64_t;
using u32 = uint32_t;
using u8 = uint8_t;

// ---- 常量与置换表 ----
static const int LS[16] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1}; // 每轮左移位数

// 密钥置换 PC-1(64->56,去奇偶校验位)
static const int PC1[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};
// 压缩置换 PC-2(56->48)
static const int PC2[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};

// 初始/逆初始置换
static const int IP[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};
static const int FP[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 扩展(32->48)
static const int E_tab[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};

// P 置换(F 函数输出置换)
static const int P_tab[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};

// 8 个 S 盒
static const int SBOX[8][4][16] = {
{{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}},
{{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}},
{{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}},
{{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}},
{{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}},
{{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}},
{{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}},
{{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}}};

// ---- 通用置换:按表抽取位 ----
template <int N>
static inline u64 permute(u64 input, const int (&tbl)[N]) {
u64 out = 0;
for (int i = 0; i < N; ++i) {
int src = tbl[i] - 1; // 表索引从 1 开始
u64 bit = (input >> (64 - 1 - src)) & 1u; // 统一按 64 位视角取位
out = (out << 1) | bit;
}
return out;
}

// 指定输入宽度的置换(E/PC2/P 使用)
static inline u64 permute_n(u64 input, const int *tbl, int n, int in_width) {
u64 out = 0;
for (int i = 0; i < n; ++i) {
int src = tbl[i] - 1;
u64 bit = (input >> (in_width - 1 - src)) & 1u;
out = (out << 1) | bit;
}
return out;
}

// 28 位循环左移(仅低 28 位有效)
static inline u32 rol28(u32 v, int r) {
v &= 0x0FFFFFFFu;
return ((v << r) | (v >> (28 - r))) & 0x0FFFFFFFu;
}

// 8 字节大端装载/存储
static inline u64 load64_be(const u8 *b) {
return ((u64)b[0] << 56) | ((u64)b[1] << 48) | ((u64)b[2] << 40) | ((u64)b[3] << 32) |
((u64)b[4] << 24) | ((u64)b[5] << 16) | ((u64)b[6] << 8) | ((u64)b[7]);
}
static inline void store64_be(u64 v, u8 *b) {
b[0] = (u8)(v >> 56);
b[1] = (u8)(v >> 48);
b[2] = (u8)(v >> 40);
b[3] = (u8)(v >> 32);
b[4] = (u8)(v >> 24);
b[5] = (u8)(v >> 16);
b[6] = (u8)(v >> 8);
b[7] = (u8)(v);
}

// ---- 密钥安排:生成 16 轮 48 位子密钥 ----
static inline void des_key_schedule(const u8 key8[8], u64 subkey[16]) {
u64 key64 = load64_be(key8); // 原始 64 位密钥
u64 k56 = permute<56>(key64, PC1); // PC-1 去校验位
u32 C = (u32)((k56 >> 28) & 0x0FFFFFFFu); // 左 28 位
u32 D = (u32)(k56 & 0x0FFFFFFFu); // 右 28 位
for (int r = 0; r < 16; ++r) {
C = rol28(C, LS[r]); // 左移
D = rol28(D, LS[r]);
u64 CD = (((u64)C) << 28) | (u64)D; // 合并 56 位
u64 k48 = permute_n(CD, PC2, 48, 56); // 压缩到 48 位
subkey[r] = k48;
}
}

// ---- 轮函数 F(R, K) ----
static inline u32 des_f(u32 R, u64 k48) {
u64 ER = permute_n((u64)R, E_tab, 48, 32); // 扩展 32->48
u64 x = ER ^ k48; // 与子密钥异或
u32 out32 = 0;
for (int i = 0; i < 8; ++i) { // 8 个 S 盒
int shift = 42 - 6 * i;
u8 six = (u8)((x >> shift) & 0x3Fu);
int row = ((six & 0x20) >> 4) | (six & 0x01);
int col = (six >> 1) & 0x0F;
u8 s = (u8)SBOX[i][row][col];
out32 = (out32 << 4) | s;
}
u64 Pout = permute_n((u64)out32, P_tab, 32, 32); // P 置换
return (u32)Pout;
}

// ---- 单块加密(ECB 基元)----
static inline void des_encrypt_block(const u8 in[8], u8 out[8], const u64 subkey[16]) {
u64 B = load64_be(in);
u64 IPB = permute<64>(B, IP); // 初始置换
u32 L = (u32)(IPB >> 32), R = (u32)IPB; // 拆成 L/R
for (int r = 0; r < 16; ++r) { // 16 轮 Feistel
u32 f = des_f(R, subkey[r]);
u32 newL = R;
u32 newR = L ^ f;
L = newL;
R = newR;
}
u64 preout = (((u64)R) << 32) | L; // 交换 L/R
u64 C = permute<64>(preout, FP); // 逆初始置换
store64_be(C, out);
}

// ---- 单块解密(子密钥逆序)----
static inline void des_decrypt_block(const u8 in[8], u8 out[8], const u64 subkey[16]) {
u64 rev[16];
for (int i = 0; i < 16; ++i)
rev[i] = subkey[15 - i]; // 反向子密钥
des_encrypt_block(in, out, rev);
}

// ---- PKCS#7 填充/去填充 ----
static inline vector<u8> pkcs7_pad_vec(const vector<u8> &data, size_t block = 8) {
size_t n = data.size();
size_t pad = block - (n % block);
if (pad == 0) pad = block;
vector<u8> out;
out.reserve(n + pad);
out.insert(out.end(), data.begin(), data.end());
out.insert(out.end(), pad, (u8)pad);
return out;
}
static inline bool pkcs7_unpad_inplace(vector<u8> &buf, size_t block = 8) {
if (buf.empty() || (buf.size() % block) != 0)
return false;
u8 p = buf.back();
if (p == 0 || p > block || p > buf.size())
return false;
for (size_t i = 0; i < p; ++i)
if (buf[buf.size() - 1 - i] != p)
return false;
buf.resize(buf.size() - p);
return true;
}

// ---- ECB 模式(演示用,不安全)----
static inline vector<u8> des_ecb_encrypt_vec(const vector<u8> &plain, const u8 key8[8]) {
u64 subkey[16];
des_key_schedule(key8, subkey);
vector<u8> in = pkcs7_pad_vec(plain, 8);
vector<u8> out(in.size());
for (size_t i = 0; i < in.size(); i += 8)
des_encrypt_block(&in[i], &out[i], subkey);
return out;
}
static inline bool des_ecb_decrypt_vec(const vector<u8> &cipher, const u8 key8[8], vector<u8> &plain_out) {
if (cipher.size() % 8)
return false;
u64 subkey[16];
des_key_schedule(key8, subkey);
plain_out.assign(cipher.size(), 0);
for (size_t i = 0; i < cipher.size(); i += 8)
des_decrypt_block(&cipher[i], &plain_out[i], subkey);
return pkcs7_unpad_inplace(plain_out, 8);
}

// ---- hex 编解码(调试可用)----
static inline string to_hex(const vector<u8> &v) {
static const char *H = "0123456789abcdef";
string s;
s.resize(v.size() * 2);
for (size_t i = 0; i < v.size(); ++i) {
u8 b = v[i];
s[2 * i] = H[b >> 4];
s[2 * i + 1] = H[b & 0xF];
}
return s;
}
static inline vector<u8> from_hex(const string &h) {
auto hexv = [](char c) -> int {
if ('0' <= c && c <= '9')
return c - '0';
if ('a' <= c && c <= 'f')
return c - 'a' + 10;
if ('A' <= c && c <= 'F')
return c - 'A' + 10;
return -1;
};
vector<u8> v;
if (h.size() % 2)
return v;
v.resize(h.size() / 2);
for (size_t i = 0; i < v.size(); ++i) {
int hi = hexv(h[2 * i]), lo = hexv(h[2 * i + 1]);
if (hi < 0 || lo < 0) {
v.clear();
return v;
}
v[i] = (u8)((hi << 4) | lo);
}
return v;
}

// ---- 输入读取:支持 @path 文件或直接 stdin ----
static inline bool is_space_le(char c) { return static_cast<unsigned char>(c) <= ' '; }

vector<u8> get_plain() {
string all((istreambuf_iterator<char>(cin)), istreambuf_iterator<char>());
size_t i = 0;
while (i < all.size() && is_space_le(all[i]))
++i;
if (i < all.size() && all[i] == '@') { // @ 路径读取文件
size_t j = i + 1;
while (j < all.size() && is_space_le(all[j]))
++j;
size_t k = all.size();
while (k > j && is_space_le(all[k - 1]))
--k;
string path = all.substr(j, k - j);
FILE *fp = fopen(path.c_str(), "rb");
if (!fp)
return {};
fseek(fp, 0, SEEK_END);
long n = ftell(fp);
if (n < 0) {
fclose(fp);
return {};
}
fseek(fp, 0, SEEK_SET);
vector<u8> buf(static_cast<size_t>(n));
if (n > 0)
fread(buf.data(), 1, static_cast<size_t>(n), fp);
fclose(fp);
return buf;
}
return vector<u8>(all.begin(), all.end()); // 直接返回全部字节
}

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);

// 第一行:模式
string mode;
if (!getline(cin, mode))
return 0;

// 第二行:密钥(8 chars)
string key_in;
if (!getline(cin, key_in))
return 0;
if (key_in.size() < 8)
key_in.append(8 - key_in.size(), '\0');
if (key_in.size() > 8)
key_in.resize(8);
u8 key8[8];
for (int i = 0; i < 8; ++i)
key8[i] = (u8)key_in[i];

if (mode == "enc" || mode == "ENC" || mode == "Enc") {
// 加密模式:读取后续所有数据为明文
vector<u8> plain = get_plain();
vector<u8> cipher = des_ecb_encrypt_vec(plain, key8);

// 输出:十六进制密文 + 还原明文(和你原来类似的演示)
vector<u8> plain2;
bool ok = des_ecb_decrypt_vec(cipher, key8, plain2);
if (!ok) {
cerr << "unpad failed\n";
return 1;
}
cout << to_hex(cipher) << "\n";
cout.write((const char *)plain2.data(), (streamsize)plain2.size());
cout << "\n";
} else if (mode == "dec" || mode == "DEC" || mode == "Dec") {
// 解密模式:第三行读取十六进制密文
string cipher_hex;
if (!getline(cin, cipher_hex)) {
cerr << "no ciphertext\n";
return 1;
}
vector<u8> cipher = from_hex(cipher_hex);
if (cipher_hex.size() > 0 && cipher.empty()) {
cerr << "invalid hex ciphertext\n";
return 1;
}
vector<u8> plain;
bool ok = des_ecb_decrypt_vec(cipher, key8, plain);
if (!ok) {
cerr << "decrypt or unpad failed\n";
return 1;
}
// 只输出明文
cout.write((const char *)plain.data(), (streamsize)plain.size());
cout << "\n";
} else {
cerr << "unknown mode, use enc/dec\n";
return 1;
}
return 0;
}

DEA

下面是 IDEA(International Data Encryption Algorithm)算法流程的

基本参数

关键操作(在每轮中使用的运算)

在 IDEA 中,每轮对数据块使用以下三种不同代数群的运算,以增强安全性:

  1. 异或 (XOR) 运算。 (维基百科)
  2. 模 2¹⁶ 加法(即以 2¹⁶ 为模的加法运算)。 (维基百科)
  3. 模 (2¹⁶ + 1) 乘法(并且将值 0x0000 视作 2¹⁶ )。 (维基百科)

子密钥生成 (Key Schedule)

  • 从 128 位主密钥中,提取出若干 16 位子密钥。 (维基百科)
  • 每轮使用 6 个 16 位子密钥;输出半轮使用 4 个 16 位子密钥。 总计 52 个子密钥。 (国际期刊研究与评论)
  • 每次提取子密钥后,对主密钥执行左旋 25 位,然后继续提取。 (维基百科)

加密流程(每个分组处理流程)

  1. 将 64 位明文分为四个 16 位子块:X₁, X₂, X₃, X₄。
  2. 对于 i = 1 到 8 轮(每轮过程如下):
    • 使用本轮的 6 个子密钥分别对 X₁–X₄ 进行模 2¹⁶ 加/模 (2¹⁶+1) 乘/XOR 等操作(具体组合见原文)。
    • 然后对中间结果做混合(例如两个子块之间做 XOR),再继续对其中两个子块做模 2¹⁶ 加/模 (2¹⁶+1) 乘。
    • 轮结束后,将四个子块位置进行某种置换(交换 X₂与 X₃)或重组,为下一轮输入。
  3. 经过 8 次上述全轮后,再执行一个“半轮”输出变换(使用 4 个子密钥,对四个16 位子块作最后的模运算和异或)。
  4. 最终将四个 16 位子块合并为 64 位密文。

解密流程

  • 解密流程与加密流程结构相同,但子密钥使用顺序为反向(而且在乘法/加法运算中使用相应的逆元素或逆运算)。 (国际期刊研究与评论)

特点 & 安全性

  • IDEA 通过“混合”三类不同代数运算(加、乘、XOR)增强了抗差分/线性分析的能力。 (维基百科)
  • 虽然 密钥空间达到 2¹²⁸ ,但随着时间推进,它在效率和标准化方面逐渐被新算法(如 AES)取代。 (NordVPN)

std

示例输入:

1
2
3
4
/*
jielycatjielycat
Hello! IDEA!
*/

程序:

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
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
using u64 = uint64_t;
using u32 = uint32_t;
using u8 = uint8_t;
using u16 = uint16_t;

constexpr ll ADD_MOD = 65536LL;
constexpr ll MUL_MOD = 65537LL;

u16 key[60];
u16 key2[60];
string my_key_bin_str;

static inline u16 add(u16 a, u16 b) { return a + b; }

u16 mul(u16 a, u16 b)
{
u64 a1 = (a == 0) ? ADD_MOD : a;
u64 b1 = (b == 0) ? ADD_MOD : b;
u64 res = (a1 * b1) % MUL_MOD;
return (res == ADD_MOD) ? 0 : static_cast<u16>(res);
}

static inline u16 yihuo(u16 a, u16 b) { return a ^ b; }

u16 bin_str_to_u16(string_view s)
{
u16 res = 0;
for (int i = 0; i < 16; ++i)
res = (res << 1) | (s[i] - '0');
return res;
}

// 128比特循环左移25位
string roll_left(string t)
{
string s(128, '0');
for (int i = 0; i < 128; i++)
s[i] = t[(i + 25) % 128];
return s;
}

// 生成52个子密钥
void extend(string t)
{
int k_idx = 1;
for (int i = 0; i < 6; i++)
{
key[k_idx++] = bin_str_to_u16(string_view(t.c_str() + 0, 16));
key[k_idx++] = bin_str_to_u16(string_view(t.c_str() + 16, 16));
key[k_idx++] = bin_str_to_u16(string_view(t.c_str() + 32, 16));
key[k_idx++] = bin_str_to_u16(string_view(t.c_str() + 48, 16));
key[k_idx++] = bin_str_to_u16(string_view(t.c_str() + 64, 16));
key[k_idx++] = bin_str_to_u16(string_view(t.c_str() + 80, 16));
key[k_idx++] = bin_str_to_u16(string_view(t.c_str() + 96, 16));
key[k_idx++] = bin_str_to_u16(string_view(t.c_str() + 112, 16));
}
key[k_idx++] = bin_str_to_u16(string_view(t.c_str() + 0, 16));
key[k_idx++] = bin_str_to_u16(string_view(t.c_str() + 16, 16));
key[k_idx++] = bin_str_to_u16(string_view(t.c_str() + 32, 16));
key[k_idx++] = bin_str_to_u16(string_view(t.c_str() + 48, 16));
}

void exgcd(ll a, ll b, ll &d, ll &x, ll &y)
{
if (!b)
{
d = a;
x = 1;
y = 0;
}
else
{
exgcd(b, a % b, d, y, x);
y -= x * (a / b);
}
}

u16 inv(u16 a_u16, ll p)
{
ll a = (a_u16 == 0) ? ADD_MOD : a_u16;
ll d, x, y;
exgcd(a, p, d, x, y);
ll res = (d == 1) ? (x % p + p) % p : -1;
return (res == ADD_MOD) ? 0 : static_cast<u16>(res);
}

u16 add_ni(u16 a, ll p)
{
ll b = -static_cast<ll>(a);
b = (b % p + p) % p;
return static_cast<u16>(b);
}

// 生成解密子密钥
void dkey()
{
for (int i = 1; i <= 9; i++)
{
for (int j = 1; j <= 4; j++)
{
int t = 6 * (10 - i - 1) + j;
if (j == 1 || j == 4)
key2[(i - 1) * 6 + j] = inv(key[t], MUL_MOD);
else
{
if (i == 1 || i == 9)
key2[(i - 1) * 6 + j] = add_ni(key[t], ADD_MOD);
else
{
if (j == 2)
key2[(i - 1) * 6 + j] = add_ni(key[t + 1], ADD_MOD);
else
key2[(i - 1) * 6 + j] = add_ni(key[t - 1], ADD_MOD);
}
}
}
}
for (int i = 1; i <= 8; i++)
{
key2[(i - 1) * 6 + 5] = key[(9 - i - 1) * 6 + 5];
key2[(i - 1) * 6 + 6] = key[(9 - i - 1) * 6 + 6];
}
}

// 加密块
void IDEA_encrypt_block(u16 x[4])
{
for (int i = 1; i <= 8; i++)
{
int k_idx = (i - 1) * 6 + 1;
u16 z1 = key[k_idx++], z2 = key[k_idx++], z3 = key[k_idx++],
z4 = key[k_idx++], z5 = key[k_idx++], z6 = key[k_idx++];

u16 t1 = mul(x[0], z1);
u16 t2 = add(x[1], z2);
u16 t3 = add(x[2], z3);
u16 t4 = mul(x[3], z4);
u16 t5 = yihuo(t1, t3);
u16 t6 = yihuo(t2, t4);
u16 t7 = mul(t5, z5);
u16 t8 = add(t6, t7);
u16 t9 = mul(t8, z6);
u16 t10 = add(t7, t9);

u16 w1 = yihuo(t1, t9);
u16 w2 = yihuo(t3, t9);
u16 w3 = yihuo(t2, t10);
u16 w4 = yihuo(t4, t10);

x[0] = w1;
x[1] = w2;
x[2] = w3;
x[3] = w4;
}

u16 y1 = mul(x[0], key[49]);
u16 y2 = add(x[2], key[50]);
u16 y3 = add(x[1], key[51]);
u16 y4 = mul(x[3], key[52]);
x[0] = y1;
x[1] = y2;
x[2] = y3;
x[3] = y4;
}

// 解密块
void IDEA_decrypt_block(u16 x[4])
{
for (int i = 1; i <= 8; i++)
{
int k_idx = (i - 1) * 6 + 1;
u16 z1 = key2[k_idx++], z2 = key2[k_idx++], z3 = key2[k_idx++],
z4 = key2[k_idx++], z5 = key2[k_idx++], z6 = key2[k_idx++];

u16 t1 = mul(x[0], z1);
u16 t2 = add(x[1], z2);
u16 t3 = add(x[2], z3);
u16 t4 = mul(x[3], z4);
u16 t5 = yihuo(t1, t3);
u16 t6 = yihuo(t2, t4);
u16 t7 = mul(t5, z5);
u16 t8 = add(t6, t7);
u16 t9 = mul(t8, z6);
u16 t10 = add(t7, t9);

u16 w1 = yihuo(t1, t9);
u16 w2 = yihuo(t3, t9);
u16 w3 = yihuo(t2, t10);
u16 w4 = yihuo(t4, t10);

x[0] = w1;
x[1] = w2;
x[2] = w3;
x[3] = w4;
}

u16 y1 = mul(x[0], key2[49]);
u16 y2 = add(x[2], key2[50]);
u16 y3 = add(x[1], key2[51]);
u16 y4 = mul(x[3], key2[52]);
x[0] = y1;
x[1] = y2;
x[2] = y3;
x[3] = y4;
}

// 字节转二进制字符串
string B2b(vector<char> &block)
{
string out;
out.reserve(128);
for (int i = 0; i < 16; ++i)
{
u8 u = static_cast<u8>(block[i]);
for (int b = 7; b >= 0; --b)
out.push_back(((u >> b) & 1) ? '1' : '0');
}
return out;
}

// 读取16字节密钥
vector<char> get_key()
{
string key;
getline(cin, key);
while (key.size() < 16)
key += "0";
if (key.size() > 16)
key.resize(16);
return vector<char>(key.begin(), key.end());
}

static inline bool is_space_le(char c) { return static_cast<unsigned char>(c) <= ' '; }

// 获取明文
vector<u8> get_plain()
{
string all((istreambuf_iterator<char>(cin)), (istreambuf_iterator<char>()));
size_t i = 0;
while (i < all.size() && is_space_le(all[i]))
++i;
if (i < all.size() && all[i] == '@')
{
size_t j = i + 1;
while (j < all.size() && is_space_le(all[j]))
++j;
size_t k = all.size();
while (k > j && is_space_le(all[k - 1]))
--k;
string path = all.substr(j, k - j);
FILE *fp = fopen(path.c_str(), "rb");
if (!fp)
return {};
fseek(fp, 0, SEEK_END);
long n = ftell(fp);
if (n < 0)
{
fclose(fp);
return {};
}
fseek(fp, 0, SEEK_SET);
vector<u8> buf(static_cast<size_t>(n));
if (n > 0)
fread(buf.data(), 1, static_cast<size_t>(n), fp);
fclose(fp);
return buf;
}
return vector<u8>(all.begin(), all.end());
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
vector<char> key0 = get_key();
my_key_bin_str = B2b(key0);
extend(my_key_bin_str);
dkey();

vector<u8> plain = get_plain();
size_t original_len = plain.size();
size_t padding = (8 - (original_len % 8)) % 8;
for (size_t i = 0; i < padding; ++i)
plain.push_back(padding);

size_t num_blocks = plain.size() / 8;
vector<u8> encrypted_bytes(plain.size());
vector<u8> decrypted_bytes(plain.size());
u16 x[4];

// 加密
for (size_t i = 0; i < num_blocks; ++i)
{
u8 *p_in = plain.data() + i * 8;
x[0] = (p_in[0] << 8) | p_in[1];
x[1] = (p_in[2] << 8) | p_in[3];
x[2] = (p_in[4] << 8) | p_in[5];
x[3] = (p_in[6] << 8) | p_in[7];
IDEA_encrypt_block(x);
u8 *p_out = encrypted_bytes.data() + i * 8;
p_out[0] = (x[0] >> 8);
p_out[1] = (x[0] & 0xFF);
p_out[2] = (x[1] >> 8);
p_out[3] = (x[1] & 0xFF);
p_out[4] = (x[2] >> 8);
p_out[5] = (x[2] & 0xFF);
p_out[6] = (x[3] >> 8);
p_out[7] = (x[3] & 0xFF);
}

// 解密
for (size_t i = 0; i < num_blocks; ++i)
{
u8 *p_in = encrypted_bytes.data() + i * 8;
x[0] = (p_in[0] << 8) | p_in[1];
x[1] = (p_in[2] << 8) | p_in[3];
x[2] = (p_in[4] << 8) | p_in[5];
x[3] = (p_in[6] << 8) | p_in[7];
IDEA_decrypt_block(x);
u8 *p_out = decrypted_bytes.data() + i * 8;
p_out[0] = (x[0] >> 8);
p_out[1] = (x[0] & 0xFF);
p_out[2] = (x[1] >> 8);
p_out[3] = (x[1] & 0xFF);
p_out[4] = (x[2] >> 8);
p_out[5] = (x[2] & 0xFF);
p_out[6] = (x[3] >> 8);
p_out[7] = (x[3] & 0xFF);
}

// cout << "明文:" << string(plain.begin(), plain.begin() + original_len) << endl;
// cout << "密文 (hex):";
// cout << hex << setfill('0');
// for (size_t i = 0; i < encrypted_bytes.size(); ++i)
// cout << setw(2) << static_cast<int>(encrypted_bytes[i]);
// cout << dec << endl;
// string mingwen1(decrypted_bytes.begin(), decrypted_bytes.begin() + original_len);
// cout << "解密后的明文:" << mingwen1 << endl;
return 0;
}

参考资料

DES_CSDN.

IDEA_CSDN.

IDEA_GitHub.