Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# Impacket - Collection of Python classes for working with network protocols. 

2# 

3# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved. 

4# 

5# This software is provided under a slightly modified version 

6# of the Apache Software License. See the accompanying LICENSE file 

7# for more information. 

8# 

9# Copyright and license note from crypto.py: 

10# 

11# Copyright (C) 2013 by the Massachusetts Institute of Technology. 

12# All rights reserved. 

13# 

14# Redistribution and use in source and binary forms, with or without 

15# modification, are permitted provided that the following conditions 

16# are met: 

17# 

18# * Redistributions of source code must retain the above copyright 

19# notice, this list of conditions and the following disclaimer. 

20# 

21# * Redistributions in binary form must reproduce the above copyright 

22# notice, this list of conditions and the following disclaimer in 

23# the documentation and/or other materials provided with the 

24# distribution. 

25# 

26# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 

27# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 

28# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 

29# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 

30# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 

31# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 

32# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 

33# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 

34# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 

35# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 

36# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 

37# OF THE POSSIBILITY OF SUCH DAMAGE. 

38# 

39from binascii import unhexlify 

40from functools import reduce 

41from os import urandom 

42# XXX current status: 

43# * Done and tested 

44# - AES encryption, checksum, string2key, prf 

45# - cf2 (needed for FAST) 

46# * Still to do: 

47# - DES enctypes and cksumtypes 

48# - RC4 exported enctype (if we need it for anything) 

49# - Unkeyed checksums 

50# - Special RC4, raw DES/DES3 operations for GSSAPI 

51# * Difficult or low priority: 

52# - Camellia not supported by PyCrypto 

53# - Cipher state only needed for kcmd suite 

54# - Nonstandard enctypes and cksumtypes like des-hmac-sha1 

55from struct import pack, unpack 

56 

57from Cryptodome.Cipher import AES, DES3, ARC4, DES 

58from Cryptodome.Hash import HMAC, MD4, MD5, SHA 

59from Cryptodome.Protocol.KDF import PBKDF2 

60from Cryptodome.Util.number import GCD as gcd 

61from six import b, PY3, indexbytes 

62 

63 

64def get_random_bytes(lenBytes): 

65 # We don't really need super strong randomness here to use PyCrypto.Random 

66 return urandom(lenBytes) 

67 

68class Enctype(object): 

69 DES_CRC = 1 

70 DES_MD4 = 2 

71 DES_MD5 = 3 

72 DES3 = 16 

73 AES128 = 17 

74 AES256 = 18 

75 RC4 = 23 

76 

77 

78class Cksumtype(object): 

79 CRC32 = 1 

80 MD4 = 2 

81 MD4_DES = 3 

82 MD5 = 7 

83 MD5_DES = 8 

84 SHA1 = 9 

85 SHA1_DES3 = 12 

86 SHA1_AES128 = 15 

87 SHA1_AES256 = 16 

88 HMAC_MD5 = -138 

89 

90 

91class InvalidChecksum(ValueError): 

92 pass 

93 

94 

95def _zeropad(s, padsize): 

96 # Return s padded with 0 bytes to a multiple of padsize. 

97 padlen = (padsize - (len(s) % padsize)) % padsize 

98 return s + b'\0'*padlen 

99 

100 

101def _xorbytes(b1, b2): 

102 # xor two strings together and return the resulting string. 

103 assert len(b1) == len(b2) 

104 return bytearray((x ^ y) for x, y in zip(b1, b2)) 

105 

106 

107def _mac_equal(mac1, mac2): 

108 # Constant-time comparison function. (We can't use HMAC.verify 

109 # since we use truncated macs.) 

110 assert len(mac1) == len(mac2) 

111 res = 0 

112 for x, y in zip(mac1, mac2): 

113 res |= x ^ y 

114 return res == 0 

115 

116 

117def _nfold(ba, nbytes): 

118 # Convert bytearray to a string of length nbytes using the RFC 3961 nfold 

119 # operation. 

120 

121 # Rotate the bytes in ba to the right by nbits bits. 

122 def rotate_right(ba, nbits): 

123 ba = bytearray(ba) 

124 nbytes, remain = (nbits//8) % len(ba), nbits % 8 

125 return bytearray((ba[i-nbytes] >> remain) | ((ba[i-nbytes-1] << (8-remain)) & 0xff) for i in range(len(ba))) 

126 

127 # Add equal-length strings together with end-around carry. 

128 def add_ones_complement(str1, str2): 

129 n = len(str1) 

130 v = [a + b for a, b in zip(str1, str2)] 

131 # Propagate carry bits to the left until there aren't any left. 

132 while any(x & ~0xff for x in v): 

133 v = [(v[i-n+1]>>8) + (v[i]&0xff) for i in range(n)] 

134 return bytearray(x for x in v) 

135 

136 # Concatenate copies of str to produce the least common multiple 

137 # of len(str) and nbytes, rotating each copy of str to the right 

138 # by 13 bits times its list position. Decompose the concatenation 

139 # into slices of length nbytes, and add them together as 

140 # big-endian ones' complement integers. 

141 slen = len(ba) 

142 lcm = nbytes * slen // gcd(nbytes, slen) 

143 bigstr = bytearray() 

144 for i in range(lcm//slen): 

145 bigstr += rotate_right(ba, 13 * i) 

146 slices = (bigstr[p:p+nbytes] for p in range(0, lcm, nbytes)) 

147 return bytes(reduce(add_ones_complement, slices)) 

148 

149 

150def _is_weak_des_key(keybytes): 

151 return keybytes in (b'\x01\x01\x01\x01\x01\x01\x01\x01', 

152 b'\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE', 

153 b'\x1F\x1F\x1F\x1F\x0E\x0E\x0E\x0E', 

154 b'\xE0\xE0\xE0\xE0\xF1\xF1\xF1\xF1', 

155 b'\x01\xFE\x01\xFE\x01\xFE\x01\xFE', 

156 b'\xFE\x01\xFE\x01\xFE\x01\xFE\x01', 

157 b'\x1F\xE0\x1F\xE0\x0E\xF1\x0E\xF1', 

158 b'\xE0\x1F\xE0\x1F\xF1\x0E\xF1\x0E', 

159 b'\x01\xE0\x01\xE0\x01\xF1\x01\xF1', 

160 b'\xE0\x01\xE0\x01\xF1\x01\xF1\x01', 

161 b'\x1F\xFE\x1F\xFE\x0E\xFE\x0E\xFE', 

162 b'\xFE\x1F\xFE\x1F\xFE\x0E\xFE\x0E', 

163 b'\x01\x1F\x01\x1F\x01\x0E\x01\x0E', 

164 b'\x1F\x01\x1F\x01\x0E\x01\x0E\x01', 

165 b'\xE0\xFE\xE0\xFE\xF1\xFE\xF1\xFE', 

166 b'\xFE\xE0\xFE\xE0\xFE\xF1\xFE\xF1') 

167 

168 

169class _EnctypeProfile(object): 

170 # Base class for enctype profiles. Usable enctype classes must define: 

171 # * enctype: enctype number 

172 # * keysize: protocol size of key in bytes 

173 # * seedsize: random_to_key input size in bytes 

174 # * random_to_key (if the keyspace is not dense) 

175 # * string_to_key 

176 # * encrypt 

177 # * decrypt 

178 # * prf 

179 

180 @classmethod 

181 def random_to_key(cls, seed): 

182 if len(seed) != cls.seedsize: 182 ↛ 183line 182 didn't jump to line 183, because the condition on line 182 was never true

183 raise ValueError('Wrong seed length') 

184 return Key(cls.enctype, seed) 

185 

186 

187class _SimplifiedEnctype(_EnctypeProfile): 

188 # Base class for enctypes using the RFC 3961 simplified profile. 

189 # Defines the encrypt, decrypt, and prf methods. Subclasses must 

190 # define: 

191 # * blocksize: Underlying cipher block size in bytes 

192 # * padsize: Underlying cipher padding multiple (1 or blocksize) 

193 # * macsize: Size of integrity MAC in bytes 

194 # * hashmod: PyCrypto hash module for underlying hash function 

195 # * basic_encrypt, basic_decrypt: Underlying CBC/CTS cipher 

196 

197 @classmethod 

198 def derive(cls, key, constant): 

199 # RFC 3961 only says to n-fold the constant only if it is 

200 # shorter than the cipher block size. But all Unix 

201 # implementations n-fold constants if their length is larger 

202 # than the block size as well, and n-folding when the length 

203 # is equal to the block size is a no-op. 

204 plaintext = _nfold(constant, cls.blocksize) 

205 rndseed = b'' 

206 while len(rndseed) < cls.seedsize: 

207 ciphertext = cls.basic_encrypt(key, plaintext) 

208 rndseed += ciphertext 

209 plaintext = ciphertext 

210 return cls.random_to_key(rndseed[0:cls.seedsize]) 

211 

212 @classmethod 

213 def encrypt(cls, key, keyusage, plaintext, confounder): 

214 ki = cls.derive(key, pack('>IB', keyusage, 0x55)) 

215 ke = cls.derive(key, pack('>IB', keyusage, 0xAA)) 

216 if confounder is None: 

217 confounder = get_random_bytes(cls.blocksize) 

218 basic_plaintext = confounder + _zeropad(plaintext, cls.padsize) 

219 hmac = HMAC.new(ki.contents, basic_plaintext, cls.hashmod).digest() 

220 return cls.basic_encrypt(ke, basic_plaintext) + hmac[:cls.macsize] 

221 

222 @classmethod 

223 def decrypt(cls, key, keyusage, ciphertext): 

224 ki = cls.derive(key, pack('>IB', keyusage, 0x55)) 

225 ke = cls.derive(key, pack('>IB', keyusage, 0xAA)) 

226 if len(ciphertext) < cls.blocksize + cls.macsize: 226 ↛ 227line 226 didn't jump to line 227, because the condition on line 226 was never true

227 raise ValueError('ciphertext too short') 

228 basic_ctext, mac = bytearray(ciphertext[:-cls.macsize]), bytearray(ciphertext[-cls.macsize:]) 

229 if len(basic_ctext) % cls.padsize != 0: 229 ↛ 230line 229 didn't jump to line 230, because the condition on line 229 was never true

230 raise ValueError('ciphertext does not meet padding requirement') 

231 basic_plaintext = cls.basic_decrypt(ke, bytes(basic_ctext)) 

232 hmac = bytearray(HMAC.new(ki.contents, basic_plaintext, cls.hashmod).digest()) 

233 expmac = hmac[:cls.macsize] 

234 if not _mac_equal(mac, expmac): 234 ↛ 235line 234 didn't jump to line 235, because the condition on line 234 was never true

235 raise InvalidChecksum('ciphertext integrity failure') 

236 # Discard the confounder. 

237 return bytes(basic_plaintext[cls.blocksize:]) 

238 

239 @classmethod 

240 def prf(cls, key, string): 

241 # Hash the input. RFC 3961 says to truncate to the padding 

242 # size, but implementations truncate to the block size. 

243 hashval = cls.hashmod.new(string).digest() 

244 truncated = hashval[:-(len(hashval) % cls.blocksize)] 

245 # Encrypt the hash with a derived key. 

246 kp = cls.derive(key, b'prf') 

247 return cls.basic_encrypt(kp, truncated) 

248 

249class _DESCBC(_SimplifiedEnctype): 

250 enctype = Enctype.DES_MD5 

251 keysize = 8 

252 seedsize = 8 

253 blocksize = 8 

254 padsize = 8 

255 macsize = 16 

256 hashmod = MD5 

257 

258 @classmethod 

259 def encrypt(cls, key, keyusage, plaintext, confounder): 

260 if confounder is None: 

261 confounder = get_random_bytes(cls.blocksize) 

262 basic_plaintext = confounder + b'\x00'*cls.macsize + _zeropad(plaintext, cls.padsize) 

263 checksum = cls.hashmod.new(basic_plaintext).digest() 

264 basic_plaintext = basic_plaintext[:len(confounder)] + checksum + basic_plaintext[len(confounder)+len(checksum):] 

265 return cls.basic_encrypt(key, basic_plaintext) 

266 

267 

268 @classmethod 

269 def decrypt(cls, key, keyusage, ciphertext): 

270 if len(ciphertext) < cls.blocksize + cls.macsize: 

271 raise ValueError('ciphertext too short') 

272 

273 complex_plaintext = cls.basic_decrypt(key, ciphertext) 

274 cofounder = complex_plaintext[:cls.padsize] 

275 mac = complex_plaintext[cls.padsize:cls.padsize+cls.macsize] 

276 message = complex_plaintext[cls.padsize+cls.macsize:] 

277 

278 expmac = cls.hashmod.new(cofounder+b'\x00'*cls.macsize+message).digest() 

279 if not _mac_equal(mac, expmac): 

280 raise InvalidChecksum('ciphertext integrity failure') 

281 return bytes(message) 

282 

283 @classmethod 

284 def mit_des_string_to_key(cls,string,salt): 

285 

286 def fixparity(deskey): 

287 temp = b'' 

288 for i in range(len(deskey)): 

289 t = (bin(indexbytes(deskey,i))[2:]).rjust(8,'0') 

290 if t[:7].count('1') %2 == 0: 

291 temp+= b(chr(int(t[:7]+'1',2))) 

292 else: 

293 temp+= b(chr(int(t[:7]+'0',2))) 

294 return temp 

295 

296 def addparity(l1): 

297 temp = list() 

298 for byte in l1: 

299 if (bin(byte).count('1') % 2) == 0: 

300 byte = (byte << 1)|0b00000001 

301 else: 

302 byte = (byte << 1)&0b11111110 

303 temp.append(byte) 

304 return temp 

305 

306 def XOR(l1,l2): 

307 temp = list() 

308 for b1,b2 in zip(l1,l2): 

309 temp.append((b1^b2)&0b01111111) 

310 

311 return temp 

312 

313 odd = True 

314 tempstring = [0,0,0,0,0,0,0,0] 

315 s = _zeropad(string + salt, cls.padsize) 

316 

317 for block in [s[i:i+8] for i in range(0, len(s), 8)]: 

318 temp56 = list() 

319 #removeMSBits 

320 for byte in block: 

321 if PY3: 321 ↛ 322line 321 didn't jump to line 322, because the condition on line 321 was never true

322 temp56.append(byte&0b01111111) 

323 else: 

324 temp56.append(ord(byte)&0b01111111) 

325 

326 #reverse 

327 if odd is False: 

328 bintemp = b'' 

329 for byte in temp56: 

330 bintemp += b(bin(byte)[2:].rjust(7,'0')) 

331 bintemp = bintemp[::-1] 

332 

333 temp56 = list() 

334 for bits7 in [bintemp[i:i+7] for i in range(0, len(bintemp), 7)]: 

335 temp56.append(int(bits7,2)) 

336 

337 odd = not odd 

338 

339 tempstring = XOR(tempstring,temp56) 

340 

341 tempkey = ''.join(chr(byte) for byte in addparity(tempstring)) 

342 if _is_weak_des_key(tempkey): 342 ↛ 343line 342 didn't jump to line 343, because the condition on line 342 was never true

343 tempkey[7] = chr(ord(tempkey[7]) ^ 0xF0) 

344 

345 cipher = DES.new(b(tempkey), DES.MODE_CBC, b(tempkey)) 

346 checksumkey = cipher.encrypt(s)[-8:] 

347 checksumkey = fixparity(checksumkey) 

348 if _is_weak_des_key(checksumkey): 348 ↛ 349line 348 didn't jump to line 349, because the condition on line 348 was never true

349 checksumkey[7] = chr(ord(checksumkey[7]) ^ 0xF0) 

350 

351 return Key(cls.enctype, checksumkey) 

352 

353 @classmethod 

354 def basic_encrypt(cls, key, plaintext): 

355 assert len(plaintext) % 8 == 0 

356 des = DES.new(key.contents, DES.MODE_CBC, b'\0' * 8) 

357 return des.encrypt(bytes(plaintext)) 

358 

359 @classmethod 

360 def basic_decrypt(cls, key, ciphertext): 

361 assert len(ciphertext) % 8 == 0 

362 des = DES.new(key.contents, DES.MODE_CBC, b'\0' * 8) 

363 return des.decrypt(bytes(ciphertext)) 

364 

365 @classmethod 

366 def string_to_key(cls, string, salt, params): 

367 if params is not None and params != b'': 367 ↛ 368line 367 didn't jump to line 368, because the condition on line 367 was never true

368 raise ValueError('Invalid DES string-to-key parameters') 

369 key = cls.mit_des_string_to_key(string, salt) 

370 return key 

371 

372 

373 

374class _DES3CBC(_SimplifiedEnctype): 

375 enctype = Enctype.DES3 

376 keysize = 24 

377 seedsize = 21 

378 blocksize = 8 

379 padsize = 8 

380 macsize = 20 

381 hashmod = SHA 

382 

383 @classmethod 

384 def random_to_key(cls, seed): 

385 # XXX Maybe reframe as _DESEnctype.random_to_key and use that 

386 # way from DES3 random-to-key when DES is implemented, since 

387 # MIT does this instead of the RFC 3961 random-to-key. 

388 def expand(seed): 

389 def parity(b): 

390 # Return b with the low-order bit set to yield odd parity. 

391 b &= ~1 

392 return b if bin(b & ~1).count('1') % 2 else b | 1 

393 assert len(seed) == 7 

394 firstbytes = [parity(b & ~1) for b in seed] 

395 lastbyte = parity(sum((seed[i]&1) << i+1 for i in range(7))) 

396 keybytes= bytearray(firstbytes + [lastbyte]) 

397 if _is_weak_des_key(keybytes): 397 ↛ 398line 397 didn't jump to line 398, because the condition on line 397 was never true

398 keybytes[7] = keybytes[7] ^ 0xF0 

399 return bytes(keybytes) 

400 

401 seed = bytearray(seed) 

402 if len(seed) != 21: 402 ↛ 403line 402 didn't jump to line 403, because the condition on line 402 was never true

403 raise ValueError('Wrong seed length') 

404 k1, k2, k3 = expand(seed[:7]), expand(seed[7:14]), expand(seed[14:]) 

405 return Key(cls.enctype, k1 + k2 + k3) 

406 

407 @classmethod 

408 def string_to_key(cls, string, salt, params): 

409 if params is not None and params != b'': 409 ↛ 410line 409 didn't jump to line 410, because the condition on line 409 was never true

410 raise ValueError('Invalid DES3 string-to-key parameters') 

411 k = cls.random_to_key(_nfold(string + salt, 21)) 

412 return cls.derive(k, b'kerberos') 

413 

414 @classmethod 

415 def basic_encrypt(cls, key, plaintext): 

416 assert len(plaintext) % 8 == 0 

417 des3 = DES3.new(key.contents, AES.MODE_CBC, b'\0' * 8) 

418 return des3.encrypt(bytes(plaintext)) 

419 

420 @classmethod 

421 def basic_decrypt(cls, key, ciphertext): 

422 assert len(ciphertext) % 8 == 0 

423 des3 = DES3.new(key.contents, AES.MODE_CBC, b'\0' * 8) 

424 return des3.decrypt(bytes(ciphertext)) 

425 

426 

427class _AESEnctype(_SimplifiedEnctype): 

428 # Base class for aes128-cts and aes256-cts. 

429 blocksize = 16 

430 padsize = 1 

431 macsize = 12 

432 hashmod = SHA 

433 

434 @classmethod 

435 def string_to_key(cls, string, salt, params): 

436 (iterations,) = unpack('>L', params or b'\x00\x00\x10\x00') 

437 prf = lambda p, s: HMAC.new(p, s, SHA).digest() 

438 seed = PBKDF2(string, salt, cls.seedsize, iterations, prf) 

439 tkey = cls.random_to_key(seed) 

440 return cls.derive(tkey, b'kerberos') 

441 

442 @classmethod 

443 def basic_encrypt(cls, key, plaintext): 

444 assert len(plaintext) >= 16 

445 aes = AES.new(key.contents, AES.MODE_CBC, b'\0' * 16) 

446 ctext = aes.encrypt(_zeropad(bytes(plaintext), 16)) 

447 if len(plaintext) > 16: 

448 # Swap the last two ciphertext blocks and truncate the 

449 # final block to match the plaintext length. 

450 lastlen = len(plaintext) % 16 or 16 

451 ctext = ctext[:-32] + ctext[-16:] + ctext[-32:-16][:lastlen] 

452 return ctext 

453 

454 @classmethod 

455 def basic_decrypt(cls, key, ciphertext): 

456 assert len(ciphertext) >= 16 

457 aes = AES.new(key.contents, AES.MODE_ECB) 

458 if len(ciphertext) == 16: 458 ↛ 459line 458 didn't jump to line 459, because the condition on line 458 was never true

459 return aes.decrypt(ciphertext) 

460 # Split the ciphertext into blocks. The last block may be partial. 

461 cblocks = [bytearray(ciphertext[p:p+16]) for p in range(0, len(ciphertext), 16)] 

462 lastlen = len(cblocks[-1]) 

463 # CBC-decrypt all but the last two blocks. 

464 prev_cblock = bytearray(16) 

465 plaintext = b'' 

466 for bb in cblocks[:-2]: 

467 plaintext += _xorbytes(bytearray(aes.decrypt(bytes(bb))), prev_cblock) 

468 prev_cblock = bb 

469 # Decrypt the second-to-last cipher block. The left side of 

470 # the decrypted block will be the final block of plaintext 

471 # xor'd with the final partial cipher block; the right side 

472 # will be the omitted bytes of ciphertext from the final 

473 # block. 

474 bb = bytearray(aes.decrypt(bytes(cblocks[-2]))) 

475 lastplaintext =_xorbytes(bb[:lastlen], cblocks[-1]) 

476 omitted = bb[lastlen:] 

477 # Decrypt the final cipher block plus the omitted bytes to get 

478 # the second-to-last plaintext block. 

479 plaintext += _xorbytes(bytearray(aes.decrypt(bytes(cblocks[-1]) + bytes(omitted))), prev_cblock) 

480 return plaintext + lastplaintext 

481 

482 

483class _AES128CTS(_AESEnctype): 

484 enctype = Enctype.AES128 

485 keysize = 16 

486 seedsize = 16 

487 

488 

489class _AES256CTS(_AESEnctype): 

490 enctype = Enctype.AES256 

491 keysize = 32 

492 seedsize = 32 

493 

494 

495class _RC4(_EnctypeProfile): 

496 enctype = Enctype.RC4 

497 keysize = 16 

498 seedsize = 16 

499 

500 @staticmethod 

501 def usage_str(keyusage): 

502 # Return a four-byte string for an RFC 3961 keyusage, using 

503 # the RFC 4757 rules. Per the errata, do not map 9 to 8. 

504 table = {3: 8, 23: 13} 

505 msusage = table[keyusage] if keyusage in table else keyusage 

506 return pack('<I', msusage) 

507 

508 @classmethod 

509 def string_to_key(cls, string, salt, params): 

510 utf16string = string.encode('UTF-16LE') 

511 return Key(cls.enctype, MD4.new(utf16string).digest()) 

512 

513 @classmethod 

514 def encrypt(cls, key, keyusage, plaintext, confounder): 

515 if confounder is None: 

516 confounder = get_random_bytes(8) 

517 ki = HMAC.new(key.contents, cls.usage_str(keyusage), MD5).digest() 

518 cksum = HMAC.new(ki, confounder + plaintext, MD5).digest() 

519 ke = HMAC.new(ki, cksum, MD5).digest() 

520 return cksum + ARC4.new(ke).encrypt(bytes(confounder + plaintext)) 

521 

522 @classmethod 

523 def decrypt(cls, key, keyusage, ciphertext): 

524 if len(ciphertext) < 24: 524 ↛ 525line 524 didn't jump to line 525, because the condition on line 524 was never true

525 raise ValueError('ciphertext too short') 

526 cksum, basic_ctext = bytearray(ciphertext[:16]), bytearray(ciphertext[16:]) 

527 ki = HMAC.new(key.contents, cls.usage_str(keyusage), MD5).digest() 

528 ke = HMAC.new(ki, cksum, MD5).digest() 

529 basic_plaintext = bytearray(ARC4.new(ke).decrypt(bytes(basic_ctext))) 

530 exp_cksum = bytearray(HMAC.new(ki, basic_plaintext, MD5).digest()) 

531 ok = _mac_equal(cksum, exp_cksum) 

532 if not ok and keyusage == 9: 532 ↛ 534line 532 didn't jump to line 534, because the condition on line 532 was never true

533 # Try again with usage 8, due to RFC 4757 errata. 

534 ki = HMAC.new(key.contents, pack('<I', 8), MD5).digest() 

535 exp_cksum = HMAC.new(ki, basic_plaintext, MD5).digest() 

536 ok = _mac_equal(cksum, exp_cksum) 

537 if not ok: 537 ↛ 538line 537 didn't jump to line 538, because the condition on line 537 was never true

538 raise InvalidChecksum('ciphertext integrity failure') 

539 # Discard the confounder. 

540 return bytes(basic_plaintext[8:]) 

541 

542 @classmethod 

543 def prf(cls, key, string): 

544 return HMAC.new(key.contents, bytes(string), SHA).digest() 

545 

546 

547class _ChecksumProfile(object): 

548 # Base class for checksum profiles. Usable checksum classes must 

549 # define: 

550 # * checksum 

551 # * verify (if verification is not just checksum-and-compare) 

552 @classmethod 

553 def verify(cls, key, keyusage, text, cksum): 

554 expected = cls.checksum(key, keyusage, text) 

555 if not _mac_equal(bytearray(cksum), bytearray(expected)): 555 ↛ 556line 555 didn't jump to line 556, because the condition on line 555 was never true

556 raise InvalidChecksum('checksum verification failure') 

557 

558 

559class _SimplifiedChecksum(_ChecksumProfile): 

560 # Base class for checksums using the RFC 3961 simplified profile. 

561 # Defines the checksum and verify methods. Subclasses must 

562 # define: 

563 # * macsize: Size of checksum in bytes 

564 # * enc: Profile of associated enctype 

565 

566 @classmethod 

567 def checksum(cls, key, keyusage, text): 

568 kc = cls.enc.derive(key, pack('>IB', keyusage, 0x99)) 

569 hmac = HMAC.new(kc.contents, text, cls.enc.hashmod).digest() 

570 return hmac[:cls.macsize] 

571 

572 @classmethod 

573 def verify(cls, key, keyusage, text, cksum): 

574 if key.enctype != cls.enc.enctype: 574 ↛ 575line 574 didn't jump to line 575, because the condition on line 574 was never true

575 raise ValueError('Wrong key type for checksum') 

576 super(_SimplifiedChecksum, cls).verify(key, keyusage, text, cksum) 

577 

578 

579class _SHA1AES128(_SimplifiedChecksum): 

580 macsize = 12 

581 enc = _AES128CTS 

582 

583 

584class _SHA1AES256(_SimplifiedChecksum): 

585 macsize = 12 

586 enc = _AES256CTS 

587 

588 

589class _SHA1DES3(_SimplifiedChecksum): 

590 macsize = 20 

591 enc = _DES3CBC 

592 

593 

594class _HMACMD5(_ChecksumProfile): 

595 @classmethod 

596 def checksum(cls, key, keyusage, text): 

597 ksign = HMAC.new(key.contents, b'signaturekey\0', MD5).digest() 

598 md5hash = MD5.new(_RC4.usage_str(keyusage) + text).digest() 

599 return HMAC.new(ksign, md5hash, MD5).digest() 

600 

601 @classmethod 

602 def verify(cls, key, keyusage, text, cksum): 

603 if key.enctype != Enctype.RC4: 603 ↛ 604line 603 didn't jump to line 604, because the condition on line 603 was never true

604 raise ValueError('Wrong key type for checksum') 

605 super(_HMACMD5, cls).verify(key, keyusage, text, cksum) 

606 

607 

608_enctype_table = { 

609 Enctype.DES_MD5: _DESCBC, 

610 Enctype.DES3: _DES3CBC, 

611 Enctype.AES128: _AES128CTS, 

612 Enctype.AES256: _AES256CTS, 

613 Enctype.RC4: _RC4 

614} 

615 

616 

617_checksum_table = { 

618 Cksumtype.SHA1_DES3: _SHA1DES3, 

619 Cksumtype.SHA1_AES128: _SHA1AES128, 

620 Cksumtype.SHA1_AES256: _SHA1AES256, 

621 Cksumtype.HMAC_MD5: _HMACMD5, 

622 0xffffff76: _HMACMD5 

623} 

624 

625 

626def _get_enctype_profile(enctype): 

627 if enctype not in _enctype_table: 627 ↛ 628line 627 didn't jump to line 628, because the condition on line 627 was never true

628 raise ValueError('Invalid enctype %d' % enctype) 

629 return _enctype_table[enctype] 

630 

631 

632def _get_checksum_profile(cksumtype): 

633 if cksumtype not in _checksum_table: 633 ↛ 634line 633 didn't jump to line 634, because the condition on line 633 was never true

634 raise ValueError('Invalid cksumtype %d' % cksumtype) 

635 return _checksum_table[cksumtype] 

636 

637 

638class Key(object): 

639 def __init__(self, enctype, contents): 

640 e = _get_enctype_profile(enctype) 

641 if len(contents) != e.keysize: 641 ↛ 642line 641 didn't jump to line 642, because the condition on line 641 was never true

642 raise ValueError('Wrong key length') 

643 self.enctype = enctype 

644 self.contents = contents 

645 

646 

647def random_to_key(enctype, seed): 

648 e = _get_enctype_profile(enctype) 

649 if len(seed) != e.seedsize: 

650 raise ValueError('Wrong crypto seed length') 

651 return e.random_to_key(seed) 

652 

653 

654def string_to_key(enctype, string, salt, params=None): 

655 e = _get_enctype_profile(enctype) 

656 return e.string_to_key(string, salt, params) 

657 

658 

659def encrypt(key, keyusage, plaintext, confounder=None): 

660 e = _get_enctype_profile(key.enctype) 

661 return e.encrypt(key, keyusage, bytes(plaintext), bytes(confounder)) 

662 

663 

664def decrypt(key, keyusage, ciphertext): 

665 # Throw InvalidChecksum on checksum failure. Throw ValueError on 

666 # invalid key enctype or malformed ciphertext. 

667 e = _get_enctype_profile(key.enctype) 

668 return e.decrypt(key, keyusage, ciphertext) 

669 

670 

671def prf(key, string): 

672 e = _get_enctype_profile(key.enctype) 

673 return e.prf(key, string) 

674 

675 

676def make_checksum(cksumtype, key, keyusage, text): 

677 c = _get_checksum_profile(cksumtype) 

678 return c.checksum(key, keyusage, text) 

679 

680 

681def verify_checksum(cksumtype, key, keyusage, text, cksum): 

682 # Throw InvalidChecksum exception on checksum failure. Throw 

683 # ValueError on invalid cksumtype, invalid key enctype, or 

684 # malformed checksum. 

685 c = _get_checksum_profile(cksumtype) 

686 c.verify(key, keyusage, text, cksum) 

687 

688 

689def cf2(enctype, key1, key2, pepper1, pepper2): 

690 # Combine two keys and two pepper strings to produce a result key 

691 # of type enctype, using the RFC 6113 KRB-FX-CF2 function. 

692 def prfplus(key, pepper, l): 

693 # Produce l bytes of output using the RFC 6113 PRF+ function. 

694 out = b'' 

695 count = 1 

696 while len(out) < l: 

697 out += prf(key, b(chr(count)) + pepper) 

698 count += 1 

699 return out[:l] 

700 

701 e = _get_enctype_profile(enctype) 

702 return e.random_to_key(_xorbytes(bytearray(prfplus(key1, pepper1, e.seedsize)), 

703 bytearray(prfplus(key2, pepper2, e.seedsize))))