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

29from binascii import unhexlify 

30from functools import reduce 

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

45from struct import pack, unpack 

46 

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

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

49from Cryptodome.Protocol.KDF import PBKDF2 

50from Cryptodome.Util.number import GCD as gcd 

51from six import b, PY3, indexbytes 

52 

53 

54def get_random_bytes(lenBytes): 

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

56 return urandom(lenBytes) 

57 

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

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

81class InvalidChecksum(ValueError): 

82 pass 

83 

84 

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

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

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

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

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

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

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

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

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

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

473class _AES128CTS(_AESEnctype): 

474 enctype = Enctype.AES128 

475 keysize = 16 

476 seedsize = 16 

477 

478 

479class _AES256CTS(_AESEnctype): 

480 enctype = Enctype.AES256 

481 keysize = 32 

482 seedsize = 32 

483 

484 

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

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

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

569class _SHA1AES128(_SimplifiedChecksum): 

570 macsize = 12 

571 enc = _AES128CTS 

572 

573 

574class _SHA1AES256(_SimplifiedChecksum): 

575 macsize = 12 

576 enc = _AES256CTS 

577 

578 

579class _SHA1DES3(_SimplifiedChecksum): 

580 macsize = 20 

581 enc = _DES3CBC 

582 

583 

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

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

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

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

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

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

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

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

661def prf(key, string): 

662 e = _get_enctype_profile(key.enctype) 

663 return e.prf(key, string) 

664 

665 

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

667 c = _get_checksum_profile(cksumtype) 

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

669 

670 

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

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