Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/ntlm_auth/des.py: 79%
155 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 07:03 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 07:03 +0000
1# Copyright: (c) 2018, Jordan Borean (@jborean93) <jborean93@gmail.com>
2# MIT License (see LICENSE or https://opensource.org/licenses/MIT)
4import struct
7# lots of help from
8# http://page.math.tu-berlin.de/~kant/teaching/hess/krypto-ws2006/des.htm
9class DES(object):
11 # first table used to derive the sub keys
12 _pc1 = [
13 56, 48, 40, 32, 24, 16, 8,
14 0, 57, 49, 41, 33, 25, 17,
15 9, 1, 58, 50, 42, 34, 26,
16 18, 10, 2, 59, 51, 43, 35,
17 62, 54, 46, 38, 30, 22, 14,
18 6, 61, 53, 45, 37, 29, 21,
19 13, 5, 60, 52, 44, 36, 28,
20 20, 12, 4, 27, 19, 11, 3
21 ]
23 # shifts the sub key from pc1 to calculate the 16 sub keys
24 _shift_indexes = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]
26 # second table used to derive the sub keys
27 _pc2 = [
28 13, 16, 10, 23, 0, 4,
29 2, 27, 14, 5, 20, 9,
30 22, 18, 11, 3, 25, 7,
31 15, 6, 26, 19, 12, 1,
32 40, 51, 30, 36, 46, 54,
33 29, 39, 50, 44, 32, 47,
34 43, 48, 38, 55, 33, 52,
35 45, 41, 49, 35, 28, 31
36 ]
38 # initial permutation of the 64-bits of the message data
39 _ip = [
40 57, 49, 41, 33, 25, 17, 9, 1,
41 59, 51, 43, 35, 27, 19, 11, 3,
42 61, 53, 45, 37, 29, 21, 13, 5,
43 63, 55, 47, 39, 31, 23, 15, 7,
44 56, 48, 40, 32, 24, 16, 8, 0,
45 58, 50, 42, 34, 26, 18, 10, 2,
46 60, 52, 44, 36, 28, 20, 12, 4,
47 62, 54, 46, 38, 30, 22, 14, 6
48 ]
50 # used to expand the each initial permuted half into a 48-bit values
51 _e_bit_selection = [
52 31, 0, 1, 2, 3, 4,
53 3, 4, 5, 6, 7, 8,
54 7, 8, 9, 10, 11, 12,
55 11, 12, 13, 14, 15, 16,
56 15, 16, 17, 18, 19, 20,
57 19, 20, 21, 22, 23, 24,
58 23, 24, 25, 26, 27, 28,
59 27, 28, 29, 30, 31, 0
60 ]
62 # list of boxes used in the encryption process
63 _s_boxes = [
64 [
65 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
66 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
67 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
68 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
69 ],
70 [
71 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
72 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
73 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
74 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
75 ],
76 [
77 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
78 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
79 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
80 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
81 ],
82 [
83 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
84 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
85 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
86 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
87 ],
88 [
89 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
90 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
91 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
92 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
93 ],
94 [
95 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
96 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
97 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
98 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
99 ],
100 [
101 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
102 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
103 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
104 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
105 ],
106 [
107 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
108 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
109 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
110 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
111 ]
112 ]
114 # converts the s-box permutation one more time
115 _p = [
116 15, 6, 19, 20, 28, 11,
117 27, 16, 0, 14, 22, 25,
118 4, 17, 30, 9, 1, 7,
119 23, 13, 31, 26, 2, 8,
120 18, 12, 29, 5, 21, 10,
121 3, 24
122 ]
124 # final permutation of the message
125 _final_ip = [
126 39, 7, 47, 15, 55, 23, 63, 31,
127 38, 6, 46, 14, 54, 22, 62, 30,
128 37, 5, 45, 13, 53, 21, 61, 29,
129 36, 4, 44, 12, 52, 20, 60, 28,
130 35, 3, 43, 11, 51, 19, 59, 27,
131 34, 2, 42, 10, 50, 18, 58, 26,
132 33, 1, 41, 9, 49, 17, 57, 25,
133 32, 0, 40, 8, 48, 16, 56, 24
134 ]
136 def __init__(self, key):
137 """
138 Creates a DES cipher class with the key initialised. This key must be
139 8 bytes in length. This only supports the ECB cipher mode as that is
140 what is used in the LM hash calculation.
142 :param key: The 8-byte key to use in the Cipher
143 """
144 if len(key) != 8:
145 raise ValueError("DES encryption key should be 8 bytes in length")
147 self.key = key
148 self._subkeys = self._create_subkeys(self.key)
150 def encrypt(self, data, pad=True):
151 """
152 DES encrypts the data based on the key it was initialised with.
154 :param data: The bytes string to encrypt
155 :param pad: Whether to right pad data with \x00 to a multiple of 8
156 :return: The encrypted bytes string
157 """
158 encrypted_data = b""
159 for i in range(0, len(data), 8):
160 block = data[i:i + 8]
161 block_length = len(block)
162 if block_length != 8 and pad:
163 block += b"\x00" * (8 - block_length)
164 elif block_length != 8:
165 raise ValueError("DES encryption must be a multiple of 8 "
166 "bytes")
167 encrypted_data += self._encode_block(block)
169 return encrypted_data
171 def decrypt(self, data):
172 """
173 DES decrypts the data based on the key it was initialised with.
175 :param data: The encrypted bytes string to decrypt
176 :return: The decrypted bytes string
177 """
178 decrypted_data = b""
179 for i in range(0, len(data), 8):
180 block = data[i:i + 8]
181 block_length = len(block)
182 if block_length != 8:
183 raise ValueError("DES decryption must be a multiple of 8 "
184 "bytes")
186 decrypted_data += self._decode_block(block)
188 return decrypted_data
190 @staticmethod
191 def key56_to_key64(key):
192 """
193 This takes in an a bytes string of 7 bytes and converts it to a bytes
194 string of 8 bytes with the odd parity bit being set to every 8 bits,
196 For example
198 b"\x01\x02\x03\x04\x05\x06\x07"
199 00000001 00000010 00000011 00000100 00000101 00000110 00000111
201 is converted to
203 b"\x01\x80\x80\x61\x40\x29\x19\x0E"
204 00000001 10000000 10000000 01100001 01000000 00101001 00011001 00001110
206 https://crypto.stackexchange.com/questions/15799/des-with-actual-7-byte-key
208 :param key: 7-byte string sized key
209 :return: 8-byte string with the parity bits sets from the 7-byte string
210 """
211 if len(key) != 7:
212 raise ValueError("DES 7-byte key is not 7 bytes in length, "
213 "actual: %d" % len(key))
215 new_key = b""
216 for i in range(0, 8):
217 if i == 0:
218 new_value = struct.unpack("B", key[i:i+1])[0]
219 elif i == 7:
220 new_value = struct.unpack("B", key[6:7])[0]
221 new_value = (new_value << 1) & 0xFF
222 else:
223 new_value = struct.unpack("B", key[i - 1:i])[0]
224 next_value = struct.unpack("B", key[i:i + 1])[0]
225 new_value = ((new_value << (8 - i)) & 0xFF) | next_value >> i
227 # clear the last bit so the count isn't off
228 new_value = new_value & ~(1 << 0)
230 # set the last bit if the number of set bits are even
231 new_value = new_value | int(not DES.bit_count(new_value) & 0x1)
232 new_key += struct.pack("B", new_value)
234 return new_key
236 @staticmethod
237 def bit_count(i):
238 # counts the number of bits that are 1 in the integer
239 count = 0
240 while i:
241 i &= i - 1
242 count += 1
244 return count
246 def _create_subkeys(self, key):
247 # convert the key into a list of bits
248 key_bits = self._get_bits(key)
250 # reorder the bits based on the pc1 table
251 pc1_bits = [key_bits[x] for x in self._pc1]
253 # split the table into 2 and append to the first entry
254 c = [pc1_bits[0:28]]
255 d = [pc1_bits[28:56]]
257 # now populate the remaining blocks by shifting the values
258 for i, shift_index in enumerate(self._shift_indexes):
259 c.append(self._shift_bits(c[i], shift_index))
260 d.append(self._shift_bits(d[i], shift_index))
262 subkeys = list()
263 for i in range(1, 17):
264 cd = c[i] + d[i]
265 subkey_bits = [cd[x] for x in self._pc2]
266 subkeys.append(subkey_bits)
268 return subkeys
270 def _shift_bits(self, bits, shifts):
271 new_bits = [None] * 28
272 for i in range(28):
273 shift_index = i + shifts
274 if shift_index >= 28:
275 shift_index = shift_index - 28
276 new_bits[i] = bits[shift_index]
278 return new_bits
280 def _get_bits(self, data):
281 bits = []
282 for i in range(len(data)):
283 b = struct.unpack("B", data[i:i + 1])[0]
284 bits.append(1 if b & 0x80 else 0)
285 bits.append(1 if b & 0x40 else 0)
286 bits.append(1 if b & 0x20 else 0)
287 bits.append(1 if b & 0x10 else 0)
288 bits.append(1 if b & 0x08 else 0)
289 bits.append(1 if b & 0x04 else 0)
290 bits.append(1 if b & 0x02 else 0)
291 bits.append(1 if b & 0x01 else 0)
292 return bits
294 def _encode_block(self, block):
295 block_bits = self._get_bits(block)
296 lr = [block_bits[x] for x in self._ip]
298 l = [lr[0:32]]
299 r = [lr[32:64]]
300 for i in range(16):
301 computed_block = self._compute_block(r[i], self._subkeys[i])
302 new_r = [int(computed_block[k] != l[i][k]) for k in range(32)]
304 l.append(r[i])
305 r.append(new_r)
307 # apply the final permutation on the l and r bits backwards
308 rl = r[16] + l[16]
309 encrypted_bits = [rl[x] for x in self._final_ip]
310 encrypted_bytes = b""
311 for i in range(0, 64, 8):
312 i_byte = int("".join([str(x) for x in encrypted_bits[i:i + 8]]), 2)
313 encrypted_bytes += struct.pack("B", i_byte)
315 return encrypted_bytes
317 def _decode_block(self, block):
318 block_bits = self._get_bits(block)
319 rl = [None] * 64
320 for i, idx in enumerate(self._final_ip):
321 rl[idx] = block_bits[i]
323 r = [None] * 17
324 l = [None] * 17
325 r[16] = rl[0:32]
326 l[16] = rl[32:64]
327 for i in range(15, -1, -1):
328 computed_block = self._compute_block(l[i + 1], self._subkeys[i])
329 new_l = [int(computed_block[k] != r[i + 1][k]) for k in range(32)]
330 r[i] = l[i + 1]
331 l[i] = new_l
333 lr = l[0] + r[0]
334 decrypted_bits = [None] * 64
335 for i, idx in enumerate(self._ip):
336 decrypted_bits[idx] = lr[i]
338 decrypted_bytes = b""
339 for i in range(0, 64, 8):
340 i_byte = int("".join([str(x) for x in decrypted_bits[i:i + 8]]), 2)
341 decrypted_bytes += struct.pack("B", i_byte)
343 return decrypted_bytes
345 def _compute_block(self, block, key):
346 expanded_block = [block[x] for x in self._e_bit_selection]
347 new_block = [int(key[i] != expanded_block[i]) for i in range(48)]
349 # calculate with the s-boxes
350 s_box_perm = []
351 s_box_iter = 0
352 # now go through each block (8 groups of 6 bits) and run the s-boxes
353 for i in range(0, 48, 6):
354 current_block = new_block[i:i + 6]
355 row_bits = [str(current_block[0]), str(current_block[-1])]
356 column_bits = [str(x) for x in current_block[1:-1]]
358 s_box_row = int("".join(row_bits), 2)
359 s_box_column = int("".join(column_bits), 2)
360 s_box_address = (s_box_row * 16) + s_box_column
361 s_box_value = self._s_boxes[s_box_iter][s_box_address]
362 s_box_iter += 1
364 s_box_perm.append(1 if s_box_value & 0x8 else 0)
365 s_box_perm.append(1 if s_box_value & 0x4 else 0)
366 s_box_perm.append(1 if s_box_value & 0x2 else 0)
367 s_box_perm.append(1 if s_box_value & 0x1 else 0)
369 final_block = [s_box_perm[x] for x in self._p]
370 return final_block