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

1# Copyright: (c) 2018, Jordan Borean (@jborean93) <jborean93@gmail.com> 

2# MIT License (see LICENSE or https://opensource.org/licenses/MIT) 

3 

4import struct 

5 

6 

7# lots of help from 

8# http://page.math.tu-berlin.de/~kant/teaching/hess/krypto-ws2006/des.htm 

9class DES(object): 

10 

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 ] 

22 

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] 

25 

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 ] 

37 

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 ] 

49 

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 ] 

61 

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 ] 

113 

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 ] 

123 

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 ] 

135 

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. 

141 

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") 

146 

147 self.key = key 

148 self._subkeys = self._create_subkeys(self.key) 

149 

150 def encrypt(self, data, pad=True): 

151 """ 

152 DES encrypts the data based on the key it was initialised with. 

153 

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) 

168 

169 return encrypted_data 

170 

171 def decrypt(self, data): 

172 """ 

173 DES decrypts the data based on the key it was initialised with. 

174 

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") 

185 

186 decrypted_data += self._decode_block(block) 

187 

188 return decrypted_data 

189 

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, 

195 

196 For example 

197 

198 b"\x01\x02\x03\x04\x05\x06\x07" 

199 00000001 00000010 00000011 00000100 00000101 00000110 00000111 

200 

201 is converted to 

202 

203 b"\x01\x80\x80\x61\x40\x29\x19\x0E" 

204 00000001 10000000 10000000 01100001 01000000 00101001 00011001 00001110 

205 

206 https://crypto.stackexchange.com/questions/15799/des-with-actual-7-byte-key 

207 

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)) 

214 

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 

226 

227 # clear the last bit so the count isn't off 

228 new_value = new_value & ~(1 << 0) 

229 

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) 

233 

234 return new_key 

235 

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 

243 

244 return count 

245 

246 def _create_subkeys(self, key): 

247 # convert the key into a list of bits 

248 key_bits = self._get_bits(key) 

249 

250 # reorder the bits based on the pc1 table 

251 pc1_bits = [key_bits[x] for x in self._pc1] 

252 

253 # split the table into 2 and append to the first entry 

254 c = [pc1_bits[0:28]] 

255 d = [pc1_bits[28:56]] 

256 

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)) 

261 

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) 

267 

268 return subkeys 

269 

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] 

277 

278 return new_bits 

279 

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 

293 

294 def _encode_block(self, block): 

295 block_bits = self._get_bits(block) 

296 lr = [block_bits[x] for x in self._ip] 

297 

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)] 

303 

304 l.append(r[i]) 

305 r.append(new_r) 

306 

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) 

314 

315 return encrypted_bytes 

316 

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] 

322 

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 

332 

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] 

337 

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) 

342 

343 return decrypted_bytes 

344 

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)] 

348 

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]] 

357 

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 

363 

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) 

368 

369 final_block = [s_box_perm[x] for x in self._p] 

370 return final_block