-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdes_cipher.py
More file actions
415 lines (360 loc) · 20.3 KB
/
des_cipher.py
File metadata and controls
415 lines (360 loc) · 20.3 KB
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
# des_cipher.py
import binascii
# Таблиця початкової перестановки IP (64 біти)
# Значення вказують, який біт вхідного блоку стає бітом на позиції індексу+1
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]
# Таблиця кінцевої перестановки IP^-1 (64 біти)
# Значення вказують, який біт вхідного блоку (після 16 раундів і з'єднання R16L16)
# стає бітом на позиції індексу+1 у вихідному шифротексті
IP_INV = [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 біт)
# Значення вказують, який біт правої половини R (32 біти)
# стає бітом на позиції індексу+1 у розширеному 48-бітному блоці
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 (32 біти)
# Значення вказують, який біт 32-бітного виходу S-блоків
# стає бітом на позиції індексу+1 у результаті функції F
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]
# S-блоки (8 блоків, кожен 4x16)
# S_BOX[номер_блоку_S-1][номер_рядка][номер_стовпця]
S_BOX = [
# 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]]
]
# Таблиця початкової перестановки ключа PC-1 (64 -> 56 біт)
# Значення вказують, який біт 64-бітного ключа стає бітом на позиції індексу+1
PC1 = [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, # Кінець C0
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] # Кінець D0
# Таблиця стискаючої перестановки PC-2 (56 -> 48 біт)
# Значення вказують, який біт 56-бітного ключа (C+D) стає бітом на позиції індексу+1
PC2 = [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]
# Кількість лівих циклічних зсувів для кожного раунду (1-16)
SHIFTS = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]
def hex_to_bin(hex_string):
"""Перетворює HEX рядок у двійковий рядок '0' та '1'."""
# Перевірка на парну кількість символів HEX
if len(hex_string) % 2 != 0:
hex_string = '0' + hex_string # Доповнюємо нулем зліва, якщо потрібно
scale = 16 ## equals to hexadecimal
# Розраховуємо потрібну кількість біт (4 біти на кожен HEX символ)
num_of_bits = len(hex_string) * 4
# Перетворюємо hex в байти, потім в int, потім в бінарний рядок
# bin() додає префікс '0b', який треба прибрати [2:]
# zfill() доповнює нулями зліва до потрібної довжини
try:
binary_string = bin(int(hex_string, scale))[2:].zfill(num_of_bits)
except ValueError:
print(f"Помилка: Некоректний HEX рядок: {hex_string}")
return None
return binary_string
def bin_to_hex(bin_string):
"""Перетворює двійковий рядок '0' та '1' у HEX рядок."""
# Перевірка, чи довжина кратна 4
if len(bin_string) % 4 != 0:
print(f"Помилка: Довжина двійкового рядка ({len(bin_string)}) не кратна 4.")
# Доповнюємо нулями зліва до найближчої довжини, кратної 4
padding = (4 - (len(bin_string) % 4)) % 4
bin_string = '0' * padding + bin_string
print(f"Рядок доповнено до {len(bin_string)} біт.")
# return None # Або можна доповнити нулями зліва
scale = 16
num_of_hex = len(bin_string) // 4
# Перетворюємо бінарний рядок в int, потім в hex
# hex() додає префікс '0x', який треба прибрати [2:]
# zfill() доповнює нулями зліва, якщо потрібно
# upper() робить літери HEX великими
try:
hex_string = hex(int(bin_string, 2))[2:].zfill(num_of_hex).upper()
except ValueError:
print(f"Помилка: Некоректний двійковий рядок: {bin_string}")
return None
return hex_string
def permute(block, table):
"""Виконує перестановку бітів блоку (двійковий рядок) згідно з таблицею."""
# Пам'ятаємо, що таблиці нумеруються з 1, а індекси рядків/списків з 0
# Тому від кожного значення в таблиці віднімаємо 1, щоб отримати правильний індекс
permuted_block = "".join([block[i-1] for i in table])
return permuted_block
def xor(bin_str1, bin_str2):
"""Виконує побітовий XOR для двох двійкових рядків однакової довжини."""
# Перетворюємо кожен символ ('0' або '1') в int, робимо XOR, перетворюємо результат назад в str
result = "".join([str(int(a) ^ int(b)) for a, b in zip(bin_str1, bin_str2)])
return result
def shift_left(key_half, n):
"""Виконує n лівих циклічних зсувів для половини ключа (двійковий рядок)."""
# Беремо зріз від n-го символу до кінця і додаємо зріз від початку до n-го символу
shifted_half = key_half[n:] + key_half[:n]
return shifted_half
def generate_round_keys(key64_bin):
"""Генерує 16 раундових 48-бітних ключів з 64-бітного ключа (двійковий рядок)."""
# Перевірка довжини вхідного ключа
if len(key64_bin) != 64:
print(f"Помилка: Довжина ключа має бути 64 біти, отримано {len(key64_bin)}")
return None
# 1. Застосувати PC-1 (Permuted Choice 1): 64 -> 56 біт
key56_bin = permute(key64_bin, PC1)
# print(f"Після PC-1 (56 біт): {key56_bin[:28]} {key56_bin[28:]}") # Для відладки
# 2. Розділити на C0 та D0 (по 28 біт)
C = key56_bin[:28]
D = key56_bin[28:]
# print(f"C0: {C}") # Для відладки
# print(f"D0: {D}") # Для відладки
round_keys = [] # Список для зберігання 16 раундових ключів
for i in range(16):
# 3. Виконати циклічні зсуви згідно з таблицею SHIFTS
shift_count = SHIFTS[i]
C = shift_left(C, shift_count)
D = shift_left(D, shift_count)
# print(f"Раунд {i+1}, Зсув={shift_count}") # Для відладки
# print(f" C{i+1}: {C}") # Для відладки
# print(f" D{i+1}: {D}") # Для відладки
# 4. Об'єднати C і D
CD = C + D
# print(f" CD{i+1}: {CD}") # Для відладки
# 5. Застосувати PC-2 (Permuted Choice 2): 56 -> 48 біт
round_key = permute(CD, PC2)
round_keys.append(round_key)
# print(f" K{i+1} (48 біт): {round_key}") # Для відладки
return round_keys
def feistel_function(R_half, round_key):
"""Обчислює функцію Фейстеля F(R, K).
Приймає 32-бітну праву половину (двійковий рядок)
та 48-бітний раундовий ключ (двійковий рядок).
Повертає 32-бітний результат (двійковий рядок)."""
# Перевірка довжин вхідних даних
if len(R_half) != 32:
print(f"Помилка функції F: Довжина R_half має бути 32 біти, отримано {len(R_half)}")
return None
if len(round_key) != 48:
print(f"Помилка функції F: Довжина round_key має бути 48 біт, отримано {len(round_key)}")
return None
# 1. Розширення E (Expansion): 32 -> 48 біт
expanded_R = permute(R_half, E)
# print(f" E(R): {expanded_R}") # Для відладки
# 2. XOR з раундовим ключем
xor_result = xor(expanded_R, round_key)
# print(f" E(R) XOR K: {xor_result}") # Для відладки
# 3. S-блоки (Substitution Boxes): 48 -> 32 біти
s_box_output = ""
for i in range(8): # Цикл по 8 S-блоках
# Виділяємо 6-бітний блок Bi з результату XOR
block_6bit = xor_result[i*6 : (i+1)*6]
# Визначаємо номер рядка (0-3) за першим та останнім бітом
row_bin = block_6bit[0] + block_6bit[5]
row = int(row_bin, 2)
# Визначаємо номер стовпця (0-15) за середніми 4 бітами
col_bin = block_6bit[1:5]
col = int(col_bin, 2)
# Знаходимо значення в таблиці S_BOX[i][row][col]
# i - номер S-блоку (від 0 до 7)
s_value = S_BOX[i][row][col]
# Перетворюємо десяткове значення (0-15) на 4-бітний двійковий рядок
# і додаємо до загального виходу S-блоків
s_box_output += bin(s_value)[2:].zfill(4)
# print(f" B{i+1}={block_6bit} -> S{i+1}(row={row}, col={col}) -> val={s_value} -> B'{i+1}={bin(s_value)[2:].zfill(4)}") # Для відладки
# print(f" S-Box Output (32 біти): {s_box_output}") # Для відладки
# 4. Перестановка P (Permutation): 32 біти
f_result = permute(s_box_output, P)
# print(f" P(S-Box Output): {f_result}") # Для відладки
return f_result
def des_encrypt(plaintext_hex, key_hex):
"""Шифрує 64-бітний блок тексту (HEX) ключем (HEX) за алгоритмом DES."""
# Перетворення HEX у бінарні рядки
plaintext_bin = hex_to_bin(plaintext_hex)
key_bin = hex_to_bin(key_hex)
# Перевірка коректності перетворення та довжин
if plaintext_bin is None or len(plaintext_bin) != 64:
print(f"Помилка: Некоректний формат або довжина тексту: {plaintext_hex}")
return None
if key_bin is None or len(key_bin) != 64:
print(f"Помилка: Некоректний формат або довжина ключа: {key_hex}")
return None
# Генеруємо 16 раундових ключів
round_keys = generate_round_keys(key_bin)
if round_keys is None:
print("Помилка генерації раундових ключів.")
return None
# 1. Початкова перестановка IP (Initial Permutation)
permuted_block = permute(plaintext_bin, IP)
# print(f"Після IP: {permuted_block[:32]} {permuted_block[32:]}") # Для відладки
# 2. Розділення на L0 та R0 (по 32 біти)
L = permuted_block[:32]
R = permuted_block[32:]
# print(f"L0: {L}") # Для відладки
# print(f"R0: {R}") # Для відладки
# 3. 16 раундів мережі Фейстеля
for i in range(16):
# Зберігаємо попередній R, він стане наступним L
prev_R = R
# Обчислюємо функцію Фейстеля F(R_поточний, K_i+1)
f_output = feistel_function(R, round_keys[i])
if f_output is None: # Перевірка на помилку в функції F
return None
# Новий R = L_попередній XOR F(R_попередній, K_i+1)
R = xor(L, f_output)
# Новий L = R_попередній
L = prev_R
# Друк проміжних результатів (для перевірки Завдання 1)
# Важливо: Виводимо L і R, які будуть на вході *наступного* раунду
print(f"Round {i+1} Output:")
print(f" L{i+1}: {L}")
print(f" R{i+1}: {R}")
# print(f" K{i+1}: {round_keys[i]}") # Ключ, що ВИКОРИСТОВУВАВСЯ в цьому раунді
# 4. Зворотне з'єднання R16 L16
# Після 16 раундів L і R містять L16 та R16.
# Перед кінцевою перестановкою їх міняють місцями: R16 L16
final_block_before_inv = R + L # Зверни увагу: R йде першим!
# 5. Кінцева перестановка IP^-1 (Final Permutation)
ciphertext_bin = permute(final_block_before_inv, IP_INV)
# Перетворюємо результат у HEX
ciphertext_hex = bin_to_hex(ciphertext_bin)
return ciphertext_hex
# --- Тестування ---
if __name__ == "__main__":
print("Запуск тестування реалізації DES...")
# Приклад 1
print("\n--- Приклад 1 ---")
text1_hex = "0123456789ABCDEF"
key1_hex = "FEFEFEFEFEFEFEFE"
expected1_hex = "6DCE0DC9006556A3"
print(f"Текст: {text1_hex}")
print(f"Ключ: {key1_hex}")
print(f"Очікуваний шифр: {expected1_hex}")
result1_hex = des_encrypt(text1_hex, key1_hex)
if result1_hex: # Перевіряємо, чи функція не повернула None (помилку)
print(f"Отриманий шифр: {result1_hex}")
print(f"Тест 1 пройдено: {result1_hex == expected1_hex}")
else:
print("Помилка під час шифрування Прикладу 1.")
print("-" * 17)
# Приклад 2
print("\n--- Приклад 2 ---")
text2_hex = "0000000000000000"
key2_hex = "0000000000000000"
expected2_hex = "8CA64DE9C1B123A7"
print(f"Текст: {text2_hex}")
print(f"Ключ: {key2_hex}")
print(f"Очікуваний шифр: {expected2_hex}")
result2_hex = des_encrypt(text2_hex, key2_hex)
if result2_hex:
print(f"Отриманий шифр: {result2_hex}")
print(f"Тест 2 пройдено: {result2_hex == expected2_hex}")
else:
print("Помилка під час шифрування Прикладу 2.")
print("-" * 17)
# Приклад 3
print("\n--- Приклад 3 ---")
text3_hex = "0123456789ABCDEF"
key3_hex = "FEDCBA9876543210"
expected3_hex = "ED39D950FA74BCC4"
print(f"Текст: {text3_hex}")
print(f"Ключ: {key3_hex}")
print(f"Очікуваний шифр: {expected3_hex}")
result3_hex = des_encrypt(text3_hex, key3_hex)
if result3_hex:
print(f"Отриманий шифр: {result3_hex}")
print(f"Тест 3 пройдено: {result3_hex == expected3_hex}")
else:
print("Помилка під час шифрування Прикладу 3.")
print("-" * 17)
# Приклад 4 (Перевірка Завдання 1)
print("\n--- Приклад 4 (Перевірка Завдання 1) ---")
# Твої дані з Завдання 1 (ZarubinA)
text4_plaintext_bin = "0101101001100001011100100111010101100010011010010110111001000001"
text4_hex = bin_to_hex(text4_plaintext_bin)
# Ключ 'password'
key4_password_bin = "0111000001100001011100110111001101110111011011110111001001100100"
key4_hex = bin_to_hex(key4_password_bin)
if text4_hex and key4_hex: # Перевіряємо, чи конвертація пройшла успішно
print(f"Текст (ZarubinA): {text4_hex}")
print(f"Ключ (password): {key4_hex}")
print("\nЗапуск шифрування (вивід включає проміжні раунди)...")
result4_hex = des_encrypt(text4_hex, key4_hex)
if result4_hex:
print(f"\nФінальний шифр (після 16 раундів): {result4_hex}")
print("-" * 17)
print("\nПорівняння результатів 1-го раунду:")
# Твої ручні розрахунки L1 та R1 з Завдання 1
manual_L1 = "01010101000001110101010000111001"
manual_R1 = "01111111010011010101010110101010"
print(f"Ручний розрахунок L1: {manual_L1}")
print(f"Ручний розрахунок R1: {manual_R1}")
print("Програмний результат L1 та R1 див. у виводі 'Round 1 Output' вище.")
# Тут ти маєш візуально порівняти вивід програми для Round 1 з manual_L1 та manual_R1
else:
print("Помилка під час шифрування Прикладу 4.")
else:
print("Помилка конвертації даних для Прикладу 4.")