-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathK_recovery.py
142 lines (112 loc) · 4.13 KB
/
K_recovery.py
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
from bs4 import BeautifulSoup
import requests
from utils import PC1, PC2, plaintext, cipher_corr
import K16_recovery
def replace_unknown_bits(partial_K, val):
'''
Replaces the unknown bits of K with the bits of val.
Args:
partial_K (string) : The 56 characters string representing K in binary,
with the unknow bits represented as 'x'.
val (int) : the value to extract the missing bits from.
Returns:
string : The string with the 'x' replaces with the bits extracted from val.
'''
val = bin(val)[2:].zfill(8)
partial_K = list(partial_K)
j = 0
for i in [8, 17, 21, 24, 34, 37, 42, 53]:
partial_K[i] = val[j]
j += 1
partial_K = "".join(partial_K)
return partial_K
def add_parity_bits(partial_K):
'''
Adds the parity bits to our 56 bits key, to reach a 64 bits key.
Args:
partial_K (string) : The binary representation of the 56 bits key without 0b.
Returns:
string : The binay representation of the 64 bits key, without 0b.
'''
# Determination of the parity bits' values
partial_K = list(partial_K)
to_add = []
sum_ = int(partial_K[0], 2)
j = 1
for i in range(1, 56):
sum_ += int(partial_K[i], 2)
j += 1
if j == 7:
j = 0
if sum_ % 2 == 0:
to_add.append('0')
else:
to_add.append('1')
sum_ = 0
# Adding the parity bits
for i in [7, 15, 23, 31, 39, 47, 55, 63]:
partial_K.insert(i, to_add[j])
j += 1
partial_K = "".join(partial_K)
return partial_K
def rev_PC2(K16):
'''
Reverses the PC2 permutation applied to obtain K16.
Args:
K16 (string) : A string representing the binary value of K16 (without 0b).
Returns:
string : The binary string representing K16 after reversing PC2, with unknon bits marked with 'x'
(and without 0b).
'''
K = ''
pos_to_determine = [9, 18, 22, 25, 35, 38, 43, 54] # The unknown bits posisitions after reversing PC2
for i in range(56):
if i + 1 not in pos_to_determine:
K += K16[PC2.index(i + 1)]
else:
K+= 'x'
return K
def rev_PC1(partial_K):
'''
Reverses the PC1 permutation.
Args:
partial_K (string) : The key value on 56 bits after PC1.
Returns:
string : the original key.
'''
K = ''
for i in range(64):
if (i + 1) % 8 != 0: # The parity bits are discarded in PC1
K += partial_K[PC1.index(i + 1)]
return K
def recover_K(K16):
'''
This function recovers the 64 bits master key value from the 48 bits of the
previously recovered K16.
Args:
K16 (string) : A string representing the binary value of K16 (without 0b).
Returns:
string : The binary representation of K (the master key) without the 0b.
'''
K = rev_PC2(K16) # The master key we are reconstructing
expected_output = str(plaintext)[2:].upper() # The correct plaintext
# The parameters for the URL
iv = '&iv=0000000000000000'
input = '&input=' + str(cipher_corr)[2:].upper() # The correct ciphertext
mode = '&mode=ecb'
action = '&action=Decrypt'
output = '&output='
for i in range(256):
key = replace_unknown_bits(K, i)
key = rev_PC1(key)
key = add_parity_bits(key)
key = hex(int(key, 2))[2:].capitalize()
url = "https://emvlab.org/descalc/?" # The base URL to the DES calculator
url += 'key=' + key + iv + input + mode + action + output # The final request
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
if expected_output == soup.find(id='output').getText():
print("Key value retrieved : " + key)
return
print("No key value found")
recover_K(K16_recovery.K16_recovery())