Coverage for /root/GitHubProjects/impacket/impacket/krb5/crypto.py: 85%

# Coverage for /root/GitHubProjects/impacket/impacket/krb5/crypto.py : 85%

## 422 statements

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#

39 from binascii import unhexlify

40 from functools import reduce

41 from 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

55 from struct import pack, unpack

56

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

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

59 from Cryptodome.Protocol.KDF import PBKDF2

60 from Cryptodome.Util.number import GCD as gcd

61 from six import b, PY3, indexbytes

62

63

64 def get_random_bytes(lenBytes):

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

66 return urandom(lenBytes)

67

68 class 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

78 class 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

91 class InvalidChecksum(ValueError):

92 pass

93

94

95 def _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

101 def _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

107 def _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

117 def _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

150 def _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

169 class _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

187 class _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

249 class _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 ↛ 324line 321 didn't jump to line 324, because the condition on line 321 was never false

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

374 class _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

427 class _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

483 class _AES128CTS(_AESEnctype):

484 enctype = Enctype.AES128

485 keysize = 16

486 seedsize = 16

487

488

489 class _AES256CTS(_AESEnctype):

490 enctype = Enctype.AES256

491 keysize = 32

492 seedsize = 32

493

494

495 class _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

547 class _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

559 class _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

579 class _SHA1AES128(_SimplifiedChecksum):

580 macsize = 12

581 enc = _AES128CTS

582

583

584 class _SHA1AES256(_SimplifiedChecksum):

585 macsize = 12

586 enc = _AES256CTS

587

588

589 class _SHA1DES3(_SimplifiedChecksum):

590 macsize = 20

591 enc = _DES3CBC

592

593

594 class _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

626 def _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

632 def _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

638 class 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

647 def 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

654 def 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

659 def 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

664 def 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

671 def prf(key, string):

672 e = _get_enctype_profile(key.enctype)

673 return e.prf(key, string)

674

675

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

677 c = _get_checksum_profile(cksumtype)

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

679

680

681 def 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

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