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# Copyright (C) 2013 by the Massachusetts Institute of Technology.

2# All rights reserved.

3#

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

5# modification, are permitted provided that the following conditions

6# are met:

7#

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

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

10#

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

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

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

14# distribution.

15#

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

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

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

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

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

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

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

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

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

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

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

27# OF THE POSSIBILITY OF SUCH DAMAGE.

28

29 from binascii import unhexlify

30 from functools import reduce

31 from os import urandom

32# XXX current status:

33# * Done and tested

34# - AES encryption, checksum, string2key, prf

35# - cf2 (needed for FAST)

36# * Still to do:

37# - DES enctypes and cksumtypes

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

39# - Unkeyed checksums

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

41# * Difficult or low priority:

42# - Camellia not supported by PyCrypto

43# - Cipher state only needed for kcmd suite

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

45 from struct import pack, unpack

46

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

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

49 from Cryptodome.Protocol.KDF import PBKDF2

50 from Cryptodome.Util.number import GCD as gcd

51 from six import b, PY3, indexbytes

52

53

54 def get_random_bytes(lenBytes):

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

56 return urandom(lenBytes)

57

58 class Enctype(object):

59 DES_CRC = 1

60 DES_MD4 = 2

61 DES_MD5 = 3

62 DES3 = 16

63 AES128 = 17

64 AES256 = 18

65 RC4 = 23

66

67

68 class Cksumtype(object):

69 CRC32 = 1

70 MD4 = 2

71 MD4_DES = 3

72 MD5 = 7

73 MD5_DES = 8

74 SHA1 = 9

75 SHA1_DES3 = 12

76 SHA1_AES128 = 15

77 SHA1_AES256 = 16

78 HMAC_MD5 = -138

79

80

81 class InvalidChecksum(ValueError):

82 pass

83

84

85 def _zeropad(s, padsize):

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

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

88 return s + b'\0'*padlen

89

90

91 def _xorbytes(b1, b2):

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

93 assert len(b1) == len(b2)

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

95

96

97 def _mac_equal(mac1, mac2):

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

99 # since we use truncated macs.)

100 assert len(mac1) == len(mac2)

101 res = 0

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

103 res |= x ^ y

104 return res == 0

105

106

107 def _nfold(ba, nbytes):

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

109 # operation.

110

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

112 def rotate_right(ba, nbits):

113 ba = bytearray(ba)

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

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

116

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

118 def add_ones_complement(str1, str2):

119 n = len(str1)

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

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

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

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

124 return bytearray(x for x in v)

125

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

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

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

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

130 # big-endian ones' complement integers.

131 slen = len(ba)

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

133 bigstr = bytearray()

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

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

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

137 return bytes(reduce(add_ones_complement, slices))

138

139

140 def _is_weak_des_key(keybytes):

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

142 b'\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE',

143 b'\x1F\x1F\x1F\x1F\x0E\x0E\x0E\x0E',

144 b'\xE0\xE0\xE0\xE0\xF1\xF1\xF1\xF1',

145 b'\x01\xFE\x01\xFE\x01\xFE\x01\xFE',

146 b'\xFE\x01\xFE\x01\xFE\x01\xFE\x01',

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

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

149 b'\x01\xE0\x01\xE0\x01\xF1\x01\xF1',

150 b'\xE0\x01\xE0\x01\xF1\x01\xF1\x01',

151 b'\x1F\xFE\x1F\xFE\x0E\xFE\x0E\xFE',

152 b'\xFE\x1F\xFE\x1F\xFE\x0E\xFE\x0E',

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

154 b'\x1F\x01\x1F\x01\x0E\x01\x0E\x01',

155 b'\xE0\xFE\xE0\xFE\xF1\xFE\xF1\xFE',

156 b'\xFE\xE0\xFE\xE0\xFE\xF1\xFE\xF1')

157

158

159 class _EnctypeProfile(object):

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

161 # * enctype: enctype number

162 # * keysize: protocol size of key in bytes

163 # * seedsize: random_to_key input size in bytes

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

165 # * string_to_key

166 # * encrypt

167 # * decrypt

168 # * prf

169

170 @classmethod

171 def random_to_key(cls, seed):

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

173 raise ValueError('Wrong seed length')

174 return Key(cls.enctype, seed)

175

176

177 class _SimplifiedEnctype(_EnctypeProfile):

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

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

180 # define:

181 # * blocksize: Underlying cipher block size in bytes

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

183 # * macsize: Size of integrity MAC in bytes

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

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

186

187 @classmethod

188 def derive(cls, key, constant):

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

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

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

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

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

194 plaintext = _nfold(constant, cls.blocksize)

195 rndseed = b''

196 while len(rndseed) < cls.seedsize:

197 ciphertext = cls.basic_encrypt(key, plaintext)

198 rndseed += ciphertext

199 plaintext = ciphertext

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

201

202 @classmethod

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

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

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

206 if confounder is None:

207 confounder = get_random_bytes(cls.blocksize)

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

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

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

211

212 @classmethod

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

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

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

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

217 raise ValueError('ciphertext too short')

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

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

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

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

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

223 expmac = hmac[:cls.macsize]

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

225 raise InvalidChecksum('ciphertext integrity failure')

226 # Discard the confounder.

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

228

229 @classmethod

230 def prf(cls, key, string):

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

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

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

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

235 # Encrypt the hash with a derived key.

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

237 return cls.basic_encrypt(kp, truncated)

238

239 class _DESCBC(_SimplifiedEnctype):

240 enctype = Enctype.DES_MD5

241 keysize = 8

242 seedsize = 8

243 blocksize = 8

244 padsize = 8

245 macsize = 16

246 hashmod = MD5

247

248 @classmethod

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

250 if confounder is None:

251 confounder = get_random_bytes(cls.blocksize)

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

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

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

255 return cls.basic_encrypt(key, basic_plaintext)

256

257

258 @classmethod

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

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

261 raise ValueError('ciphertext too short')

262

263 complex_plaintext = cls.basic_decrypt(key, ciphertext)

264 cofounder = complex_plaintext[:cls.padsize]

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

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

267

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

269 if not _mac_equal(mac, expmac):

270 raise InvalidChecksum('ciphertext integrity failure')

271 return bytes(message)

272

273 @classmethod

274 def mit_des_string_to_key(cls,string,salt):

275

276 def fixparity(deskey):

277 temp = b''

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

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

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

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

282 else:

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

284 return temp

285

286 def addparity(l1):

287 temp = list()

288 for byte in l1:

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

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

291 else:

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

293 temp.append(byte)

294 return temp

295

296 def XOR(l1,l2):

297 temp = list()

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

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

300

301 return temp

302

303 odd = True

304 tempstring = [0,0,0,0,0,0,0,0]

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

306

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

308 temp56 = list()

309 #removeMSBits

310 for byte in block:

311 if PY3: 311 ↛ 314line 311 didn't jump to line 314, because the condition on line 311 was never false

312 temp56.append(byte&0b01111111)

313 else:

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

315

316 #reverse

317 if odd is False:

318 bintemp = b''

319 for byte in temp56:

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

321 bintemp = bintemp[::-1]

322

323 temp56 = list()

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

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

326

327 odd = not odd

328

329 tempstring = XOR(tempstring,temp56)

330

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

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

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

334

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

336 chekcsumkey = cipher.encrypt(s)[-8:]

337 chekcsumkey = fixparity(chekcsumkey)

338 if _is_weak_des_key(chekcsumkey): 338 ↛ 339line 338 didn't jump to line 339, because the condition on line 338 was never true

339 chekcsumkey[7] = chr(ord(chekcsumkey[7]) ^ 0xF0)

340

341 return Key(cls.enctype, chekcsumkey)

342

343 @classmethod

344 def basic_encrypt(cls, key, plaintext):

345 assert len(plaintext) % 8 == 0

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

347 return des.encrypt(bytes(plaintext))

348

349 @classmethod

350 def basic_decrypt(cls, key, ciphertext):

351 assert len(ciphertext) % 8 == 0

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

353 return des.decrypt(bytes(ciphertext))

354

355 @classmethod

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

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

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

359 key = cls.mit_des_string_to_key(string, salt)

360 return key

361

362

363

364 class _DES3CBC(_SimplifiedEnctype):

365 enctype = Enctype.DES3

366 keysize = 24

367 seedsize = 21

368 blocksize = 8

369 padsize = 8

370 macsize = 20

371 hashmod = SHA

372

373 @classmethod

374 def random_to_key(cls, seed):

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

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

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

378 def expand(seed):

379 def parity(b):

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

381 b &= ~1

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

383 assert len(seed) == 7

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

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

386 keybytes= bytearray(firstbytes + [lastbyte])

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

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

389 return bytes(keybytes)

390

391 seed = bytearray(seed)

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

393 raise ValueError('Wrong seed length')

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

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

396

397 @classmethod

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

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

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

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

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

403

404 @classmethod

405 def basic_encrypt(cls, key, plaintext):

406 assert len(plaintext) % 8 == 0

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

408 return des3.encrypt(bytes(plaintext))

409

410 @classmethod

411 def basic_decrypt(cls, key, ciphertext):

412 assert len(ciphertext) % 8 == 0

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

414 return des3.decrypt(bytes(ciphertext))

415

416

417 class _AESEnctype(_SimplifiedEnctype):

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

419 blocksize = 16

420 padsize = 1

421 macsize = 12

422 hashmod = SHA

423

424 @classmethod

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

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

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

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

429 tkey = cls.random_to_key(seed)

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

431

432 @classmethod

433 def basic_encrypt(cls, key, plaintext):

434 assert len(plaintext) >= 16

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

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

437 if len(plaintext) > 16:

438 # Swap the last two ciphertext blocks and truncate the

439 # final block to match the plaintext length.

440 lastlen = len(plaintext) % 16 or 16

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

442 return ctext

443

444 @classmethod

445 def basic_decrypt(cls, key, ciphertext):

446 assert len(ciphertext) >= 16

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

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

449 return aes.decrypt(ciphertext)

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

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

452 lastlen = len(cblocks[-1])

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

454 prev_cblock = bytearray(16)

455 plaintext = b''

456 for bb in cblocks[:-2]:

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

458 prev_cblock = bb

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

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

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

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

463 # block.

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

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

466 omitted = bb[lastlen:]

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

468 # the second-to-last plaintext block.

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

470 return plaintext + lastplaintext

471

472

473 class _AES128CTS(_AESEnctype):

474 enctype = Enctype.AES128

475 keysize = 16

476 seedsize = 16

477

478

479 class _AES256CTS(_AESEnctype):

480 enctype = Enctype.AES256

481 keysize = 32

482 seedsize = 32

483

484

485 class _RC4(_EnctypeProfile):

486 enctype = Enctype.RC4

487 keysize = 16

488 seedsize = 16

489

490 @staticmethod

491 def usage_str(keyusage):

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

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

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

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

496 return pack('<I', msusage)

497

498 @classmethod

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

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

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

502

503 @classmethod

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

505 if confounder is None:

506 confounder = get_random_bytes(8)

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

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

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

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

511

512 @classmethod

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

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

515 raise ValueError('ciphertext too short')

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

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

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

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

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

521 ok = _mac_equal(cksum, exp_cksum)

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

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

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

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

526 ok = _mac_equal(cksum, exp_cksum)

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

528 raise InvalidChecksum('ciphertext integrity failure')

529 # Discard the confounder.

530 return bytes(basic_plaintext[8:])

531

532 @classmethod

533 def prf(cls, key, string):

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

535

536

537 class _ChecksumProfile(object):

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

539 # define:

540 # * checksum

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

542 @classmethod

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

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

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

546 raise InvalidChecksum('checksum verification failure')

547

548

549 class _SimplifiedChecksum(_ChecksumProfile):

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

551 # Defines the checksum and verify methods. Subclasses must

552 # define:

553 # * macsize: Size of checksum in bytes

554 # * enc: Profile of associated enctype

555

556 @classmethod

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

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

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

560 return hmac[:cls.macsize]

561

562 @classmethod

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

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

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

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

567

568

569 class _SHA1AES128(_SimplifiedChecksum):

570 macsize = 12

571 enc = _AES128CTS

572

573

574 class _SHA1AES256(_SimplifiedChecksum):

575 macsize = 12

576 enc = _AES256CTS

577

578

579 class _SHA1DES3(_SimplifiedChecksum):

580 macsize = 20

581 enc = _DES3CBC

582

583

584 class _HMACMD5(_ChecksumProfile):

585 @classmethod

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

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

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

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

590

591 @classmethod

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

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

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

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

596

597

598 _enctype_table = {

599 Enctype.DES_MD5: _DESCBC,

600 Enctype.DES3: _DES3CBC,

601 Enctype.AES128: _AES128CTS,

602 Enctype.AES256: _AES256CTS,

603 Enctype.RC4: _RC4

604}

605

606

607 _checksum_table = {

608 Cksumtype.SHA1_DES3: _SHA1DES3,

609 Cksumtype.SHA1_AES128: _SHA1AES128,

610 Cksumtype.SHA1_AES256: _SHA1AES256,

611 Cksumtype.HMAC_MD5: _HMACMD5,

612 0xffffff76: _HMACMD5

613}

614

615

616 def _get_enctype_profile(enctype):

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

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

619 return _enctype_table[enctype]

620

621

622 def _get_checksum_profile(cksumtype):

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

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

625 return _checksum_table[cksumtype]

626

627

628 class Key(object):

629 def __init__(self, enctype, contents):

630 e = _get_enctype_profile(enctype)

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

632 raise ValueError('Wrong key length')

633 self.enctype = enctype

634 self.contents = contents

635

636

637 def random_to_key(enctype, seed):

638 e = _get_enctype_profile(enctype)

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

640 raise ValueError('Wrong crypto seed length')

641 return e.random_to_key(seed)

642

643

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

645 e = _get_enctype_profile(enctype)

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

647

648

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

650 e = _get_enctype_profile(key.enctype)

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

652

653

654 def decrypt(key, keyusage, ciphertext):

655 # Throw InvalidChecksum on checksum failure. Throw ValueError on

656 # invalid key enctype or malformed ciphertext.

657 e = _get_enctype_profile(key.enctype)

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

659

660

661 def prf(key, string):

662 e = _get_enctype_profile(key.enctype)

663 return e.prf(key, string)

664

665

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

667 c = _get_checksum_profile(cksumtype)

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

669

670

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

672 # Throw InvalidChecksum exception on checksum failure. Throw

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

674 # malformed checksum.

675 c = _get_checksum_profile(cksumtype)

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

677

678

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

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

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

682 def prfplus(key, pepper, l):

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

684 out = b''

685 count = 1

686 while len(out) < l:

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

688 count += 1

689 return out[:l]

690

691 e = _get_enctype_profile(enctype)

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

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