Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/libs/rfc3961.py: 2%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

638 statements  

1# SPDX-License-Identifier: BSD-2-Clause 

2# This file is part of Scapy 

3# See https://scapy.net/ for more information 

4# Copyright (c) 2013, Marc Horowitz 

5# Copyright (C) 2013, Massachusetts Institute of Technology 

6# Copyright (C) 2022-2024, Gabriel Potter and the secdev/scapy community 

7 

8""" 

9Implementation of cryptographic functions for Kerberos 5 

10 

11- RFC 3961: Encryption and Checksum Specifications for Kerberos 5 

12- RFC 3962: Advanced Encryption Standard (AES) Encryption for Kerberos 5 

13- RFC 4757: The RC4-HMAC Kerberos Encryption Types Used by Microsoft Windows 

14- RFC 6113: A Generalized Framework for Kerberos Pre-Authentication 

15- RFC 8009: AES Encryption with HMAC-SHA2 for Kerberos 5 

16 

17.. note:: 

18 You will find more complete documentation for Kerberos over at 

19 `SMB <https://scapy.readthedocs.io/en/latest/layers/kerberos.html>`_ 

20""" 

21 

22# TODO: support cipher states... 

23 

24__all__ = [ 

25 "ChecksumType", 

26 "EncryptionType", 

27 "InvalidChecksum", 

28 "KRB_FX_CF2", 

29 "Key", 

30 "SP800108_KDFCTR", 

31 "_rfc1964pad", 

32] 

33 

34# The following is a heavily modified version of 

35# https://github.com/SecureAuthCorp/impacket/blob/3ec59074ec35c06bbd4312d1042f0e23f4a1b41f/impacket/krb5/crypto.py 

36# itself heavily inspired from 

37# https://github.com/mhorowitz/pykrb5/blob/master/krb5/crypto.py 

38# Note that the following work is based only on THIS COMMIT from impacket, 

39# which is therefore under mhorowitz's BSD 2-clause "simplified" license. 

40 

41import abc 

42import enum 

43import math 

44import os 

45import struct 

46from scapy.compat import ( 

47 orb, 

48 chb, 

49 int_bytes, 

50 bytes_int, 

51 plain_str, 

52) 

53 

54# Typing 

55from typing import ( 

56 Any, 

57 Callable, 

58 List, 

59 Optional, 

60 Type, 

61 Union, 

62) 

63 

64# We end up using our own crypto module for hashes / hmac because 

65# we need MD4 which was dropped everywhere. It's just a wrapper above 

66# the builtin python ones (except for MD4). 

67 

68from scapy.layers.tls.crypto.hash import ( 

69 _GenericHash, 

70 Hash_MD4, 

71 Hash_MD5, 

72 Hash_SHA, 

73 Hash_SHA256, 

74 Hash_SHA384, 

75) 

76from scapy.layers.tls.crypto.h_mac import ( 

77 Hmac, 

78 Hmac_MD5, 

79 Hmac_SHA, 

80) 

81 

82# For everything else, use cryptography. 

83 

84try: 

85 from cryptography.hazmat.primitives import hashes 

86 from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC 

87 from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes 

88 

89 try: 

90 # cryptography > 43.0 

91 from cryptography.hazmat.decrepit.ciphers import ( 

92 algorithms as decrepit_algorithms, 

93 ) 

94 except ImportError: 

95 decrepit_algorithms = algorithms 

96except ImportError: 

97 raise ImportError("To use kerberos cryptography, you need to install cryptography.") 

98 

99 

100# cryptography's TripleDES can be used to simulate DES behavior 

101def DES(key: bytes) -> decrepit_algorithms.TripleDES: 

102 return decrepit_algorithms.TripleDES(key * 3) 

103 

104 

105# https://go.microsoft.com/fwlink/?LinkId=186039 

106# https://csrc.nist.gov/CSRC/media/Publications/sp/800-108/archive/2008-11-06/documents/sp800-108-Nov2008.pdf 

107# [SP800-108] section 5.1 (used in [MS-SMB2] sect 3.1.4.2) 

108 

109 

110def SP800108_KDFCTR( 

111 K_I: bytes, 

112 Label: bytes, 

113 Context: bytes, 

114 L: int, 

115 hashmod: _GenericHash = Hash_SHA256, 

116) -> bytes: 

117 """ 

118 KDF in Counter Mode as section 5.1 of [SP800-108] 

119 

120 This assumes r=32, and defaults to SHA256 ([MS-SMB2] default). 

121 """ 

122 PRF = Hmac(K_I, hashmod).digest 

123 h = hashmod.hash_len 

124 n = math.ceil(L / h) 

125 if n >= 0xFFFFFFFF: 

126 # 2^r-1 = 0xffffffff with r=32 per [MS-SMB2] 

127 raise ValueError("Invalid n value in SP800108_KDFCTR") 

128 result = b"".join( 

129 PRF(struct.pack(">I", i) + Label + b"\x00" + Context + struct.pack(">I", L)) 

130 for i in range(1, n + 1) 

131 ) 

132 return result[: L // 8] 

133 

134 

135# https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-1 

136 

137 

138class EncryptionType(enum.IntEnum): 

139 DES_CBC_CRC = 1 

140 DES_CBC_MD4 = 2 

141 DES_CBC_MD5 = 3 

142 # DES3_CBC_SHA1 = 7 

143 DES3_CBC_SHA1_KD = 16 

144 AES128_CTS_HMAC_SHA1_96 = 17 

145 AES256_CTS_HMAC_SHA1_96 = 18 

146 AES128_CTS_HMAC_SHA256_128 = 19 

147 AES256_CTS_HMAC_SHA384_192 = 20 

148 RC4_HMAC = 23 

149 RC4_HMAC_EXP = 24 

150 # CAMELLIA128-CTS-CMAC = 25 

151 # CAMELLIA256-CTS-CMAC = 26 

152 

153 

154# https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-2 

155 

156 

157class ChecksumType(enum.IntEnum): 

158 CRC32 = 1 

159 RSA_MD4 = 2 

160 RSA_MD4_DES = 3 

161 # RSA_MD5 = 7 

162 RSA_MD5_DES = 8 

163 # RSA_MD5_DES3 = 9 

164 # SHA1 = 10 

165 HMAC_SHA1_DES3_KD = 12 

166 # HMAC_SHA1_DES3 = 13 

167 # SHA1 = 14 

168 HMAC_SHA1_96_AES128 = 15 

169 HMAC_SHA1_96_AES256 = 16 

170 # CMAC-CAMELLIA128 = 17 

171 # CMAC-CAMELLIA256 = 18 

172 HMAC_SHA256_128_AES128 = 19 

173 HMAC_SHA384_192_AES256 = 20 

174 HMAC_MD5 = -138 

175 

176 

177class InvalidChecksum(ValueError): 

178 pass 

179 

180 

181######### 

182# Utils # 

183######### 

184 

185 

186# https://www.gnu.org/software/shishi/ides.pdf - APPENDIX B 

187 

188 

189def _n_fold(s, n): 

190 # type: (bytes, int) -> bytes 

191 """ 

192 n-fold is an algorithm that takes m input bits and "stretches" them 

193 to form n output bits with equal contribution from each input bit to 

194 the output (quote from RFC 3961 sect 3.1). 

195 """ 

196 

197 def rot13(y, nb): 

198 # type: (bytes, int) -> bytes 

199 x = bytes_int(y) 

200 mod = (1 << (nb * 8)) - 1 

201 if nb == 0: 

202 return y 

203 elif nb == 1: 

204 return int_bytes(((x >> 5) | (x << (nb * 8 - 5))) & mod, nb) 

205 else: 

206 return int_bytes(((x >> 13) | (x << (nb * 8 - 13))) & mod, nb) 

207 

208 def ocadd(x, y, nb): 

209 # type: (bytearray, bytearray, int) -> bytearray 

210 v = [a + b for a, b in zip(x, y)] 

211 while any(x & ~0xFF for x in v): 

212 v = [(v[i - nb + 1] >> 8) + (v[i] & 0xFF) for i in range(nb)] 

213 return bytearray(x for x in v) 

214 

215 m = len(s) 

216 lcm = n // math.gcd(n, m) * m # lcm = math.lcm(n, m) on Python>=3.9 

217 buf = bytearray() 

218 for _ in range(lcm // m): 

219 buf += s 

220 s = rot13(s, m) 

221 out = bytearray(b"\x00" * n) 

222 for i in range(0, lcm, n): 

223 out = ocadd(out, buf[i : i + n], n) 

224 return bytes(out) 

225 

226 

227def _zeropad(s, padsize): 

228 # type: (bytes, int) -> bytes 

229 """ 

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

231 """ 

232 return s + b"\x00" * (-len(s) % padsize) 

233 

234 

235def _rfc1964pad(s): 

236 # type: (bytes) -> bytes 

237 """ 

238 Return s padded as RFC1964 mandates 

239 """ 

240 pad = (-len(s)) % 8 

241 return s + pad * struct.pack("!B", pad) 

242 

243 

244def _xorbytes(b1, b2): 

245 # type: (bytearray, bytearray) -> bytearray 

246 """ 

247 xor two strings together and return the resulting string 

248 """ 

249 assert len(b1) == len(b2) 

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

251 

252 

253def _mac_equal(mac1, mac2): 

254 # type: (bytes, bytes) -> bool 

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

256 # since we use truncated macs.) 

257 return all(x == y for x, y in zip(mac1, mac2)) 

258 

259 

260# https://doi.org/10.6028/NBS.FIPS.74 sect 3.6 

261 

262WEAK_DES_KEYS = set( 

263 [ 

264 # 1 

265 b"\xe0\x01\xe0\x01\xf1\x01\xf1\x01", 

266 b"\x01\xe0\x01\xe0\x01\xf1\x01\xf1", 

267 # 2 

268 b"\xfe\x1f\xfe\x1f\xfe\x0e\xfe\x0e", 

269 b"\x1f\xfe\x1f\xfe\x0e\xfe\x0e\xfe", 

270 # 3 

271 b"\xe0\x1f\xe0\x1f\xf1\x0e\xf1\x0e", 

272 b"\x1f\xe0\x1f\xe0\x0e\xf1\x0e\xf1", 

273 # 4 

274 b"\x01\xfe\x01\xfe\x01\xfe\x01\xfe", 

275 b"\xfe\x01\xfe\x01\xfe\x01\xfe\x01", 

276 # 5 

277 b"\x01\x1f\x01\x1f\x01\x0e\x01\x0e", 

278 b"\x1f\x01\x1f\x01\x0e\x01\x0e\x01", 

279 # 6 

280 b"\xe0\xfe\xe0\xfe\xf1\xfe\xf1\xfe", 

281 b"\xfe\xe0\xfe\xe0\xfe\xf1\xfe\xf1", 

282 # 7 

283 b"\x01" * 8, 

284 # 8 

285 b"\xfe" * 8, 

286 # 9 

287 b"\xe0" * 4 + b"\xf1" * 4, 

288 # 10 

289 b"\x1f" * 4 + b"\x0e" * 4, 

290 ] 

291) 

292 

293# fmt: off 

294CRC32_TABLE = [ 

295 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 

296 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 

297 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 

298 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 

299 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 

300 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 

301 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 

302 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 

303 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 

304 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 

305 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 

306 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 

307 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 

308 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 

309 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 

310 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 

311 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 

312 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 

313 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 

314 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 

315 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 

316 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 

317 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 

318 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 

319 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 

320 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 

321 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 

322 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 

323 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 

324 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 

325 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 

326 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 

327 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 

328 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 

329 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 

330 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 

331 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 

332 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 

333 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 

334 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 

335 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 

336 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 

337 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 

338 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 

339 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 

340 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 

341 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 

342 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 

343 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 

344 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 

345 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 

346 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 

347 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 

348 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 

349 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 

350 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 

351 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 

352 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 

353 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 

354 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 

355 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 

356 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 

357 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 

358 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d 

359] 

360# fmt: on 

361 

362############ 

363# RFC 3961 # 

364############ 

365 

366 

367# RFC3961 sect 3 

368 

369 

370class _EncryptionAlgorithmProfile(abc.ABCMeta): 

371 """ 

372 Base class for etype profiles. 

373 

374 Usable etype classes must define: 

375 :attr etype: etype number 

376 :attr keysize: protocol size of key in bytes 

377 :attr seedsize: random_to_key input size in bytes 

378 :attr reqcksum: 'required checksum mechanism' per RFC3961. 

379 this is the default checksum used for this algorithm. 

380 :attr random_to_key: (if the keyspace is not dense) 

381 :attr string_to_key: 

382 :attr encrypt: 

383 :attr decrypt: 

384 :attr prf: 

385 """ 

386 

387 etype = None # type: EncryptionType 

388 keysize = None # type: int 

389 seedsize = None # type: int 

390 reqcksum = None # type: ChecksumType 

391 

392 @classmethod 

393 @abc.abstractmethod 

394 def derive(cls, key, constant): 

395 # type: (Key, bytes) -> bytes 

396 pass 

397 

398 @classmethod 

399 @abc.abstractmethod 

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

401 # type: (Key, int, bytes, Optional[bytes]) -> bytes 

402 pass 

403 

404 @classmethod 

405 @abc.abstractmethod 

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

407 # type: (Key, int, bytes) -> bytes 

408 pass 

409 

410 @classmethod 

411 @abc.abstractmethod 

412 def prf(cls, key, string): 

413 # type: (Key, bytes) -> bytes 

414 pass 

415 

416 @classmethod 

417 @abc.abstractmethod 

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

419 # type: (bytes, bytes, Optional[bytes]) -> Key 

420 pass 

421 

422 @classmethod 

423 def random_to_key(cls, seed): 

424 # type: (bytes) -> Key 

425 if len(seed) != cls.seedsize: 

426 raise ValueError("Wrong seed length") 

427 return Key(cls.etype, key=seed) 

428 

429 

430# RFC3961 sect 4 

431 

432 

433class _ChecksumProfile(object): 

434 """ 

435 Base class for checksum profiles. 

436 

437 Usable checksum classes must define: 

438 :func checksum: 

439 :attr macsize: Size of checksum in bytes 

440 :func verify: (if verification is not just checksum-and-compare) 

441 """ 

442 

443 macsize = None # type: int 

444 

445 @classmethod 

446 @abc.abstractmethod 

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

448 # type: (Key, int, bytes) -> bytes 

449 pass 

450 

451 @classmethod 

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

453 # type: (Key, int, bytes, bytes) -> None 

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

455 if not _mac_equal(cksum, expected): 

456 raise InvalidChecksum("checksum verification failure") 

457 

458 

459# RFC3961 sect 5.3 

460 

461 

462class _SimplifiedEncryptionProfile(_EncryptionAlgorithmProfile): 

463 """ 

464 Base class for etypes using the RFC 3961 simplified profile. 

465 Defines the encrypt, decrypt, and prf methods. 

466 

467 Subclasses must define: 

468 

469 :param blocksize: Underlying cipher block size in bytes 

470 :param padsize: Underlying cipher padding multiple (1 or blocksize) 

471 :param macsize: Size of integrity MAC in bytes 

472 :param hashmod: underlying hash function 

473 :param basic_encrypt, basic_decrypt: Underlying CBC/CTS cipher 

474 """ 

475 

476 blocksize = None # type: int 

477 padsize = None # type: int 

478 macsize = None # type: int 

479 hashmod = None # type: Any 

480 

481 # Used in RFC 8009. This is not a simplified profile per se but 

482 # is still pretty close. 

483 rfc8009 = False 

484 

485 @classmethod 

486 @abc.abstractmethod 

487 def basic_encrypt(cls, key, plaintext): 

488 # type: (bytes, bytes) -> bytes 

489 pass 

490 

491 @classmethod 

492 @abc.abstractmethod 

493 def basic_decrypt(cls, key, ciphertext): 

494 # type: (bytes, bytes) -> bytes 

495 pass 

496 

497 @classmethod 

498 def derive(cls, key, constant): 

499 # type: (Key, bytes) -> bytes 

500 """ 

501 Also known as "DK" in RFC3961. 

502 """ 

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

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

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

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

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

508 plaintext = _n_fold(constant, cls.blocksize) 

509 rndseed = b"" 

510 while len(rndseed) < cls.seedsize: 

511 ciphertext = cls.basic_encrypt(key.key, plaintext) 

512 rndseed += ciphertext 

513 plaintext = ciphertext 

514 # DK(Key, Constant) = random-to-key(DR(Key, Constant)) 

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

516 

517 @classmethod 

518 def encrypt(cls, key, keyusage, plaintext, confounder, signtext=None): 

519 # type: (Key, int, bytes, Optional[bytes], Optional[bytes]) -> bytes 

520 """ 

521 Encryption function. 

522 

523 :param key: the key 

524 :param keyusage: the keyusage 

525 :param plaintext: the text to encrypt 

526 :param confounder: (optional) the confounder. If none, will be random 

527 :param signtext: (optional) make the checksum include different data than what 

528 is encrypted. Useful for kerberos GSS_WrapEx. If none, same as 

529 plaintext. 

530 """ 

531 if not cls.rfc8009: 

532 ki = cls.derive(key, struct.pack(">IB", keyusage, 0x55)) 

533 ke = cls.derive(key, struct.pack(">IB", keyusage, 0xAA)) 

534 else: 

535 ki = cls.derive(key, struct.pack(">IB", keyusage, 0x55), cls.macsize * 8) # type: ignore # noqa: E501 

536 ke = cls.derive(key, struct.pack(">IB", keyusage, 0xAA), cls.keysize * 8) # type: ignore # noqa: E501 

537 if confounder is None: 

538 confounder = os.urandom(cls.blocksize) 

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

540 if signtext is None: 

541 signtext = basic_plaintext 

542 if not cls.rfc8009: 

543 # Simplified profile 

544 hmac = Hmac(ki, cls.hashmod).digest(signtext) 

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

546 else: 

547 # RFC 8009 

548 C = cls.basic_encrypt(ke, basic_plaintext) 

549 hmac = Hmac(ki, cls.hashmod).digest(b"\0" * 16 + C) # XXX IV 

550 return C + hmac[: cls.macsize] 

551 

552 @classmethod 

553 def decrypt(cls, key, keyusage, ciphertext, presignfunc=None): 

554 # type: (Key, int, bytes, Optional[Callable[[bytes, bytes], bytes]]) -> bytes 

555 """ 

556 decryption function 

557 """ 

558 if not cls.rfc8009: 

559 ki = cls.derive(key, struct.pack(">IB", keyusage, 0x55)) 

560 ke = cls.derive(key, struct.pack(">IB", keyusage, 0xAA)) 

561 else: 

562 ki = cls.derive(key, struct.pack(">IB", keyusage, 0x55), cls.macsize * 8) # type: ignore # noqa: E501 

563 ke = cls.derive(key, struct.pack(">IB", keyusage, 0xAA), cls.keysize * 8) # type: ignore # noqa: E501 

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

565 raise ValueError("Ciphertext too short") 

566 basic_ctext, mac = ciphertext[: -cls.macsize], ciphertext[-cls.macsize :] 

567 if len(basic_ctext) % cls.padsize != 0: 

568 raise ValueError("ciphertext does not meet padding requirement") 

569 if not cls.rfc8009: 

570 # Simplified profile 

571 basic_plaintext = cls.basic_decrypt(ke, basic_ctext) 

572 signtext = basic_plaintext 

573 if presignfunc: 

574 # Allow to have additional processing of the data that is to be signed. 

575 # This is useful for GSS_WrapEx 

576 signtext = presignfunc( 

577 basic_plaintext[: cls.blocksize], 

578 basic_plaintext[cls.blocksize :], 

579 ) 

580 hmac = Hmac(ki, cls.hashmod).digest(signtext) 

581 expmac = hmac[: cls.macsize] 

582 if not _mac_equal(mac, expmac): 

583 raise ValueError("ciphertext integrity failure") 

584 else: 

585 # RFC 8009 

586 signtext = b"\0" * 16 + basic_ctext # XXX IV 

587 if presignfunc: 

588 # Allow to have additional processing of the data that is to be signed. 

589 # This is useful for GSS_WrapEx 

590 signtext = presignfunc( 

591 basic_ctext[16 : 16 + cls.blocksize], 

592 basic_ctext[16 + cls.blocksize :], 

593 ) 

594 hmac = Hmac(ki, cls.hashmod).digest(signtext) 

595 expmac = hmac[: cls.macsize] 

596 if not _mac_equal(mac, expmac): 

597 raise ValueError("ciphertext integrity failure") 

598 basic_plaintext = cls.basic_decrypt(ke, basic_ctext) 

599 # Discard the confounder. 

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

601 

602 @classmethod 

603 def prf(cls, key, string): 

604 # type: (Key, bytes) -> bytes 

605 """ 

606 pseudo-random function 

607 """ 

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

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

610 hashval = cls.hashmod().digest(string) 

611 if len(hashval) % cls.blocksize: 

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

613 # Encrypt the hash with a derived key. 

614 kp = cls.derive(key, b"prf") 

615 return cls.basic_encrypt(kp, hashval) 

616 

617 

618# RFC3961 sect 5.4 

619 

620 

621class _SimplifiedChecksum(_ChecksumProfile): 

622 """ 

623 Base class for checksums using the RFC 3961 simplified profile. 

624 Defines the checksum and verify methods. 

625 

626 Subclasses must define: 

627 :attr enc: Profile of associated etype 

628 """ 

629 

630 enc = None # type: Type[_SimplifiedEncryptionProfile] 

631 

632 # Used in RFC 8009. This is not a simplified profile per se but 

633 # is still pretty close. 

634 rfc8009 = False 

635 

636 @classmethod 

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

638 # type: (Key, int, bytes) -> bytes 

639 if not cls.rfc8009: 

640 # Simplified profile 

641 kc = cls.enc.derive(key, struct.pack(">IB", keyusage, 0x99)) 

642 else: 

643 # RFC 8009 

644 kc = cls.enc.derive( # type: ignore 

645 key, struct.pack(">IB", keyusage, 0x99), cls.macsize * 8 

646 ) 

647 hmac = Hmac(kc, cls.enc.hashmod).digest(text) 

648 return hmac[: cls.macsize] 

649 

650 @classmethod 

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

652 # type: (Key, int, bytes, bytes) -> None 

653 if key.etype != cls.enc.etype: 

654 raise ValueError("Wrong key type for checksum") 

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

656 

657 

658# RFC3961 sect 6.1 

659 

660 

661class _CRC32(_ChecksumProfile): 

662 macsize = 4 

663 

664 # This isn't your usual CRC32, it's a "modified version" according to the RFC3961. 

665 # Another RFC states it's just a buggy version of the actual CRC32. 

666 

667 @classmethod 

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

669 # type: (Optional[Key], int, bytes) -> bytes 

670 c = 0 

671 for i in range(len(text)): 

672 idx = text[i] ^ c 

673 idx &= 0xFF 

674 c >>= 8 

675 c ^= CRC32_TABLE[idx] 

676 return c.to_bytes(4, "little") 

677 

678 

679# RFC3961 sect 6.2 

680 

681 

682class _DESCBC(_SimplifiedEncryptionProfile): 

683 keysize = 8 

684 seedsize = 8 

685 blocksize = 8 

686 padsize = 8 

687 macsize = 16 

688 hashmod = Hash_MD5 

689 

690 @classmethod 

691 def encrypt(cls, key, keyusage, plaintext, confounder, signtext=None): 

692 # type: (Key, int, bytes, Optional[bytes], Any) -> bytes 

693 if confounder is None: 

694 confounder = os.urandom(cls.blocksize) 

695 basic_plaintext = ( 

696 confounder + b"\x00" * cls.macsize + _zeropad(plaintext, cls.padsize) 

697 ) 

698 checksum = cls.hashmod().digest(basic_plaintext) 

699 basic_plaintext = ( 

700 basic_plaintext[: len(confounder)] 

701 + checksum 

702 + basic_plaintext[len(confounder) + len(checksum) :] 

703 ) 

704 return cls.basic_encrypt(key.key, basic_plaintext) 

705 

706 @classmethod 

707 def decrypt(cls, key, keyusage, ciphertext, presignfunc=None): 

708 # type: (Key, int, bytes, Any) -> bytes 

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

710 raise ValueError("ciphertext too short") 

711 

712 complex_plaintext = cls.basic_decrypt(key.key, ciphertext) 

713 cofounder = complex_plaintext[: cls.padsize] 

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

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

716 

717 expmac = cls.hashmod().digest(cofounder + b"\x00" * cls.macsize + message) 

718 if not _mac_equal(mac, expmac): 

719 raise InvalidChecksum("ciphertext integrity failure") 

720 return bytes(message) 

721 

722 @classmethod 

723 def mit_des_string_to_key(cls, string, salt): 

724 # type: (bytes, bytes) -> Key 

725 def fixparity(deskey): 

726 # type: (List[int]) -> bytes 

727 temp = b"" 

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

729 t = (bin(orb(deskey[i]))[2:]).rjust(8, "0") 

730 if t[:7].count("1") % 2 == 0: 

731 temp += chb(int(t[:7] + "1", 2)) 

732 else: 

733 temp += chb(int(t[:7] + "0", 2)) 

734 return temp 

735 

736 def addparity(l1): 

737 # type: (List[int]) -> List[int] 

738 temp = list() 

739 for byte in l1: 

740 if (bin(byte).count("1") % 2) == 0: 

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

742 else: 

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

744 temp.append(byte) 

745 return temp 

746 

747 def XOR(l1, l2): 

748 # type: (List[int], List[int]) -> List[int] 

749 temp = list() 

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

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

752 

753 return temp 

754 

755 odd = True 

756 tempstring = [0, 0, 0, 0, 0, 0, 0, 0] 

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

758 

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

760 temp56 = list() 

761 # removeMSBits 

762 for byte in block: 

763 temp56.append(orb(byte) & 0b01111111) 

764 

765 # reverse 

766 if odd is False: 

767 bintemp = b"" 

768 for byte in temp56: 

769 bintemp += bin(byte)[2:].rjust(7, "0").encode() 

770 bintemp = bintemp[::-1] 

771 

772 temp56 = list() 

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

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

775 

776 odd = not odd 

777 tempstring = XOR(tempstring, temp56) 

778 

779 tempkey = bytearray(b"".join(chb(byte) for byte in addparity(tempstring))) 

780 if bytes(tempkey) in WEAK_DES_KEYS: 

781 tempkey[7] = tempkey[7] ^ 0xF0 

782 

783 tempkeyb = bytes(tempkey) 

784 des = Cipher(DES(tempkeyb), modes.CBC(tempkeyb)).encryptor() 

785 chekcsumkey = des.update(s)[-8:] 

786 chekcsumkey = bytearray(fixparity(chekcsumkey)) 

787 if bytes(chekcsumkey) in WEAK_DES_KEYS: 

788 chekcsumkey[7] = chekcsumkey[7] ^ 0xF0 

789 

790 return Key(cls.etype, key=bytes(chekcsumkey)) 

791 

792 @classmethod 

793 def basic_encrypt(cls, key, plaintext): 

794 # type: (bytes, bytes) -> bytes 

795 assert len(plaintext) % 8 == 0 

796 des = Cipher(DES(key), modes.CBC(b"\0" * 8)).encryptor() 

797 return des.update(bytes(plaintext)) 

798 

799 @classmethod 

800 def basic_decrypt(cls, key, ciphertext): 

801 # type: (bytes, bytes) -> bytes 

802 assert len(ciphertext) % 8 == 0 

803 des = Cipher(DES(key), modes.CBC(b"\0" * 8)).decryptor() 

804 return des.update(bytes(ciphertext)) 

805 

806 @classmethod 

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

808 # type: (bytes, bytes, Optional[bytes]) -> Key 

809 if params is not None and params != b"": 

810 raise ValueError("Invalid DES string-to-key parameters") 

811 key = cls.mit_des_string_to_key(string, salt) 

812 return key 

813 

814 

815# RFC3961 sect 6.2.1 

816 

817 

818class _DESMD5(_DESCBC): 

819 etype = EncryptionType.DES_CBC_MD5 

820 hashmod = Hash_MD5 

821 reqcksum = ChecksumType.RSA_MD5_DES 

822 

823 

824# RFC3961 sect 6.2.2 

825 

826 

827class _DESMD4(_DESCBC): 

828 etype = EncryptionType.DES_CBC_MD4 

829 hashmod = Hash_MD4 

830 reqcksum = ChecksumType.RSA_MD4_DES 

831 

832 

833# RFC3961 sect 6.3 

834 

835 

836class _DES3CBC(_SimplifiedEncryptionProfile): 

837 etype = EncryptionType.DES3_CBC_SHA1_KD 

838 keysize = 24 

839 seedsize = 21 

840 blocksize = 8 

841 padsize = 8 

842 macsize = 20 

843 hashmod = Hash_SHA 

844 reqcksum = ChecksumType.HMAC_SHA1_DES3_KD 

845 

846 @classmethod 

847 def random_to_key(cls, seed): 

848 # type: (bytes) -> Key 

849 # XXX Maybe reframe as _DESEncryptionType.random_to_key and use that 

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

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

852 def expand(seed): 

853 # type: (bytes) -> bytes 

854 def parity(b): 

855 # type: (int) -> int 

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

857 b &= ~1 

858 return b if bin(b & ~1).count("1") % 2 else b | 1 

859 

860 assert len(seed) == 7 

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

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

863 keybytes = bytearray(firstbytes + [lastbyte]) 

864 if bytes(keybytes) in WEAK_DES_KEYS: 

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

866 return bytes(keybytes) 

867 

868 if len(seed) != 21: 

869 raise ValueError("Wrong seed length") 

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

871 return Key(cls.etype, key=k1 + k2 + k3) 

872 

873 @classmethod 

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

875 # type: (bytes, bytes, Optional[bytes]) -> Key 

876 if params is not None and params != b"": 

877 raise ValueError("Invalid DES3 string-to-key parameters") 

878 k = cls.random_to_key(_n_fold(string + salt, 21)) 

879 return Key( 

880 cls.etype, 

881 key=cls.derive(k, b"kerberos"), 

882 ) 

883 

884 @classmethod 

885 def basic_encrypt(cls, key, plaintext): 

886 # type: (bytes, bytes) -> bytes 

887 assert len(plaintext) % 8 == 0 

888 des3 = Cipher( 

889 decrepit_algorithms.TripleDES(key), modes.CBC(b"\0" * 8) 

890 ).encryptor() 

891 return des3.update(bytes(plaintext)) 

892 

893 @classmethod 

894 def basic_decrypt(cls, key, ciphertext): 

895 # type: (bytes, bytes) -> bytes 

896 assert len(ciphertext) % 8 == 0 

897 des3 = Cipher( 

898 decrepit_algorithms.TripleDES(key), modes.CBC(b"\0" * 8) 

899 ).decryptor() 

900 return des3.update(bytes(ciphertext)) 

901 

902 

903class _SHA1DES3(_SimplifiedChecksum): 

904 macsize = 20 

905 enc = _DES3CBC 

906 

907 

908############ 

909# RFC 3962 # 

910############ 

911 

912 

913# RFC3962 sect 6 

914 

915 

916class _AESEncryptionType_SHA1_96(_SimplifiedEncryptionProfile, abc.ABCMeta): 

917 blocksize = 16 

918 padsize = 1 

919 macsize = 12 

920 hashmod = Hash_SHA 

921 

922 @classmethod 

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

924 # type: (bytes, bytes, Optional[bytes]) -> Key 

925 iterations = struct.unpack(">L", params or b"\x00\x00\x10\x00")[0] 

926 kdf = PBKDF2HMAC( 

927 algorithm=hashes.SHA1(), 

928 length=cls.seedsize, 

929 salt=salt, 

930 iterations=iterations, 

931 ) 

932 tkey = cls.random_to_key(kdf.derive(string)) 

933 return Key( 

934 cls.etype, 

935 key=cls.derive(tkey, b"kerberos"), 

936 ) 

937 

938 # basic_encrypt and basic_decrypt implement AES in CBC-CS3 mode 

939 

940 @classmethod 

941 def basic_encrypt(cls, key, plaintext): 

942 # type: (bytes, bytes) -> bytes 

943 assert len(plaintext) >= 16 

944 aes = Cipher(algorithms.AES(key), modes.CBC(b"\0" * 16)).encryptor() 

945 ctext = aes.update(_zeropad(bytes(plaintext), 16)) 

946 if len(plaintext) > 16: 

947 # Swap the last two ciphertext blocks and truncate the 

948 # final block to match the plaintext length. 

949 lastlen = len(plaintext) % 16 or 16 

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

951 return ctext 

952 

953 @classmethod 

954 def basic_decrypt(cls, key, ciphertext): 

955 # type: (bytes, bytes) -> bytes 

956 assert len(ciphertext) >= 16 

957 aes = Cipher(algorithms.AES(key), modes.ECB()).decryptor() 

958 if len(ciphertext) == 16: 

959 return aes.update(ciphertext) 

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

961 cblocks = [ 

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

963 ] 

964 lastlen = len(cblocks[-1]) 

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

966 prev_cblock = bytearray(16) 

967 plaintext = b"" 

968 for bb in cblocks[:-2]: 

969 plaintext += _xorbytes(bytearray(aes.update(bytes(bb))), prev_cblock) 

970 prev_cblock = bb 

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

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

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

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

975 # block. 

976 bb = bytearray(aes.update(bytes(cblocks[-2]))) 

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

978 omitted = bb[lastlen:] 

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

980 # the second-to-last plaintext block. 

981 plaintext += _xorbytes( 

982 bytearray(aes.update(bytes(cblocks[-1]) + bytes(omitted))), prev_cblock 

983 ) 

984 return plaintext + lastplaintext 

985 

986 

987# RFC3962 sect 7 

988 

989 

990class _AES128CTS_SHA1_96(_AESEncryptionType_SHA1_96): 

991 etype = EncryptionType.AES128_CTS_HMAC_SHA1_96 

992 keysize = 16 

993 seedsize = 16 

994 reqcksum = ChecksumType.HMAC_SHA1_96_AES128 

995 

996 

997class _AES256CTS_SHA1_96(_AESEncryptionType_SHA1_96): 

998 etype = EncryptionType.AES256_CTS_HMAC_SHA1_96 

999 keysize = 32 

1000 seedsize = 32 

1001 reqcksum = ChecksumType.HMAC_SHA1_96_AES256 

1002 

1003 

1004class _SHA1_96_AES128(_SimplifiedChecksum): 

1005 macsize = 12 

1006 enc = _AES128CTS_SHA1_96 

1007 

1008 

1009class _SHA1_96_AES256(_SimplifiedChecksum): 

1010 macsize = 12 

1011 enc = _AES256CTS_SHA1_96 

1012 

1013 

1014############ 

1015# RFC 4757 # 

1016############ 

1017 

1018# RFC4757 sect 4 

1019 

1020 

1021class _HMACMD5(_ChecksumProfile): 

1022 macsize = 16 

1023 

1024 @classmethod 

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

1026 # type: (Key, int, bytes) -> bytes 

1027 ksign = Hmac_MD5(key.key).digest(b"signaturekey\0") 

1028 md5hash = Hash_MD5().digest(_RC4.usage_str(keyusage) + text) 

1029 return Hmac_MD5(ksign).digest(md5hash) 

1030 

1031 @classmethod 

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

1033 # type: (Key, int, bytes, bytes) -> None 

1034 if key.etype not in [EncryptionType.RC4_HMAC, EncryptionType.RC4_HMAC_EXP]: 

1035 raise ValueError("Wrong key type for checksum") 

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

1037 

1038 

1039# RFC4757 sect 5 

1040 

1041 

1042class _RC4(_EncryptionAlgorithmProfile): 

1043 etype = EncryptionType.RC4_HMAC 

1044 keysize = 16 

1045 seedsize = 16 

1046 reqcksum = ChecksumType.HMAC_MD5 

1047 export = False 

1048 

1049 @staticmethod 

1050 def usage_str(keyusage): 

1051 # type: (int) -> bytes 

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

1053 # the RFC 4757 rules sect 3. Per the errata, do not map 9 to 8. 

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

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

1056 return struct.pack("<I", msusage) 

1057 

1058 @classmethod 

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

1060 # type: (bytes, bytes, Optional[bytes]) -> Key 

1061 if params is not None and params != b"": 

1062 raise ValueError("Invalid RC4 string-to-key parameters") 

1063 utf16string = plain_str(string).encode("UTF-16LE") 

1064 return Key(cls.etype, key=Hash_MD4().digest(utf16string)) 

1065 

1066 @classmethod 

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

1068 # type: (Key, int, bytes, Optional[bytes]) -> bytes 

1069 if confounder is None: 

1070 confounder = os.urandom(8) 

1071 if cls.export: 

1072 ki = Hmac_MD5(key.key).digest(b"fortybits\x00" + cls.usage_str(keyusage)) 

1073 else: 

1074 ki = Hmac_MD5(key.key).digest(cls.usage_str(keyusage)) 

1075 cksum = Hmac_MD5(ki).digest(confounder + plaintext) 

1076 if cls.export: 

1077 ki = ki[:7] + b"\xab" * 9 

1078 ke = Hmac_MD5(ki).digest(cksum) 

1079 rc4 = Cipher(algorithms.ARC4(ke), mode=None).encryptor() 

1080 return cksum + rc4.update(bytes(confounder + plaintext)) 

1081 

1082 @classmethod 

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

1084 # type: (Key, int, bytes) -> bytes 

1085 if len(ciphertext) < 24: 

1086 raise ValueError("ciphertext too short") 

1087 cksum, basic_ctext = ciphertext[:16], ciphertext[16:] 

1088 if cls.export: 

1089 ki = Hmac_MD5(key.key).digest(b"fortybits\x00" + cls.usage_str(keyusage)) 

1090 else: 

1091 ki = Hmac_MD5(key.key).digest(cls.usage_str(keyusage)) 

1092 if cls.export: 

1093 kie = ki[:7] + b"\xab" * 9 

1094 else: 

1095 kie = ki 

1096 ke = Hmac_MD5(kie).digest(cksum) 

1097 rc4 = Cipher(decrepit_algorithms.ARC4(ke), mode=None).decryptor() 

1098 basic_plaintext = rc4.update(bytes(basic_ctext)) 

1099 exp_cksum = Hmac_MD5(ki).digest(basic_plaintext) 

1100 ok = _mac_equal(cksum, exp_cksum) 

1101 if not ok and keyusage == 9: 

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

1103 ki = Hmac_MD5(key.key).digest(struct.pack("<I", 8)) 

1104 exp_cksum = Hmac_MD5(ki).digest(basic_plaintext) 

1105 ok = _mac_equal(cksum, exp_cksum) 

1106 if not ok: 

1107 raise InvalidChecksum("ciphertext integrity failure") 

1108 # Discard the confounder. 

1109 return bytes(basic_plaintext[8:]) 

1110 

1111 @classmethod 

1112 def prf(cls, key, string): 

1113 # type: (Key, bytes) -> bytes 

1114 return Hmac_SHA(key.key).digest(string) 

1115 

1116 

1117class _RC4_EXPORT(_RC4): 

1118 etype = EncryptionType.RC4_HMAC_EXP 

1119 export = True 

1120 

1121 

1122############ 

1123# RFC 8009 # 

1124############ 

1125 

1126 

1127class _AESEncryptionType_SHA256_SHA384(_AESEncryptionType_SHA1_96, abc.ABCMeta): 

1128 enctypename = None # type: bytes 

1129 hashmod: _GenericHash = None # Scapy 

1130 _hashmod: hashes.HashAlgorithm = None # Cryptography 

1131 

1132 # Turn on RFC 8009 mode 

1133 rfc8009 = True 

1134 

1135 @classmethod 

1136 def derive(cls, key, label, k, context=b""): # type: ignore 

1137 # type: (Key, bytes, int, bytes) -> bytes 

1138 """ 

1139 Also known as "KDF-HMAC-SHA2" in RFC8009. 

1140 """ 

1141 # RFC 8009 sect 3 

1142 return SP800108_KDFCTR( 

1143 K_I=key.key, 

1144 Label=label, 

1145 Context=context, 

1146 L=k, 

1147 hashmod=cls.hashmod, 

1148 ) 

1149 

1150 @classmethod 

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

1152 # type: (bytes, bytes, Optional[bytes]) -> Key 

1153 # RFC 8009 sect 4 

1154 iterations = struct.unpack(">L", params or b"\x00\x00\x80\x00")[0] 

1155 saltp = cls.enctypename + b"\x00" + salt 

1156 kdf = PBKDF2HMAC( 

1157 algorithm=cls._hashmod(), 

1158 length=cls.seedsize, 

1159 salt=saltp, 

1160 iterations=iterations, 

1161 ) 

1162 tkey = cls.random_to_key(kdf.derive(string)) 

1163 return Key( 

1164 cls.etype, 

1165 key=cls.derive(tkey, b"kerberos", cls.keysize * 8), 

1166 ) 

1167 

1168 @classmethod 

1169 def prf(cls, key, string): 

1170 # type: (Key, bytes) -> bytes 

1171 return cls.derive(key, b"prf", cls.hashmod.hash_len * 8, string) 

1172 

1173 

1174class _AES128CTS_SHA256_128(_AESEncryptionType_SHA256_SHA384): 

1175 etype = EncryptionType.AES128_CTS_HMAC_SHA256_128 

1176 keysize = 16 

1177 seedsize = 16 

1178 macsize = 16 

1179 reqcksum = ChecksumType.HMAC_SHA256_128_AES128 

1180 # _AESEncryptionType_SHA256_SHA384 parameters 

1181 enctypename = b"aes128-cts-hmac-sha256-128" 

1182 hashmod = Hash_SHA256 

1183 _hashmod = hashes.SHA256 

1184 

1185 

1186class _AES256CTS_SHA384_192(_AESEncryptionType_SHA256_SHA384): 

1187 etype = EncryptionType.AES256_CTS_HMAC_SHA384_192 

1188 keysize = 32 

1189 seedsize = 32 

1190 macsize = 24 

1191 reqcksum = ChecksumType.HMAC_SHA384_192_AES256 

1192 # _AESEncryptionType_SHA256_SHA384 parameters 

1193 enctypename = b"aes256-cts-hmac-sha384-192" 

1194 hashmod = Hash_SHA384 

1195 _hashmod = hashes.SHA384 

1196 

1197 

1198class _SHA256_128_AES128(_SimplifiedChecksum): 

1199 macsize = 16 

1200 enc = _AES128CTS_SHA256_128 

1201 rfc8009 = True 

1202 

1203 

1204class _SHA384_182_AES256(_SimplifiedChecksum): 

1205 macsize = 24 

1206 enc = _AES256CTS_SHA384_192 

1207 rfc8009 = True 

1208 

1209 

1210############## 

1211# Key object # 

1212############## 

1213 

1214_enctypes = { 

1215 # DES_CBC_CRC - UNIMPLEMENTED 

1216 EncryptionType.DES_CBC_MD5: _DESMD5, 

1217 EncryptionType.DES_CBC_MD4: _DESMD4, 

1218 # DES3_CBC_SHA1 - UNIMPLEMENTED 

1219 EncryptionType.DES3_CBC_SHA1_KD: _DES3CBC, 

1220 EncryptionType.AES128_CTS_HMAC_SHA1_96: _AES128CTS_SHA1_96, 

1221 EncryptionType.AES256_CTS_HMAC_SHA1_96: _AES256CTS_SHA1_96, 

1222 EncryptionType.AES128_CTS_HMAC_SHA256_128: _AES128CTS_SHA256_128, 

1223 EncryptionType.AES256_CTS_HMAC_SHA384_192: _AES256CTS_SHA384_192, 

1224 # CAMELLIA128-CTS-CMAC - UNIMPLEMENTED 

1225 # CAMELLIA256-CTS-CMAC - UNIMPLEMENTED 

1226 EncryptionType.RC4_HMAC: _RC4, 

1227 EncryptionType.RC4_HMAC_EXP: _RC4_EXPORT, 

1228} 

1229 

1230 

1231_checksums = { 

1232 ChecksumType.CRC32: _CRC32, 

1233 # RSA_MD4 - UNIMPLEMENTED 

1234 # RSA_MD4_DES - UNIMPLEMENTED 

1235 # RSA_MD5 - UNIMPLEMENTED 

1236 # RSA_MD5_DES - UNIMPLEMENTED 

1237 # SHA1 - UNIMPLEMENTED 

1238 ChecksumType.HMAC_SHA1_DES3_KD: _SHA1DES3, 

1239 # HMAC_SHA1_DES3 - UNIMPLEMENTED 

1240 ChecksumType.HMAC_SHA1_96_AES128: _SHA1_96_AES128, 

1241 ChecksumType.HMAC_SHA1_96_AES256: _SHA1_96_AES256, 

1242 # CMAC-CAMELLIA128 - UNIMPLEMENTED 

1243 # CMAC-CAMELLIA256 - UNIMPLEMENTED 

1244 ChecksumType.HMAC_SHA256_128_AES128: _SHA256_128_AES128, 

1245 ChecksumType.HMAC_SHA384_192_AES256: _SHA384_182_AES256, 

1246 ChecksumType.HMAC_MD5: _HMACMD5, 

1247 0xFFFFFF76: _HMACMD5, 

1248} 

1249 

1250 

1251class Key(object): 

1252 def __init__( 

1253 self, 

1254 etype: Union[EncryptionType, int, None] = None, 

1255 key: bytes = b"", 

1256 cksumtype: Union[ChecksumType, int, None] = None, 

1257 ) -> None: 

1258 """ 

1259 Kerberos Key object. 

1260 

1261 :param etype: the EncryptionType 

1262 :param cksumtype: the ChecksumType 

1263 :param key: the bytes containing the key bytes for this Key. 

1264 """ 

1265 assert etype or cksumtype, "Provide an etype or a cksumtype !" 

1266 assert key, "Provide a key !" 

1267 if isinstance(etype, int): 

1268 etype = EncryptionType(etype) 

1269 if isinstance(cksumtype, int): 

1270 cksumtype = ChecksumType(cksumtype) 

1271 self.etype = etype 

1272 if etype is not None: 

1273 try: 

1274 self.ep = _enctypes[etype] 

1275 except ValueError: 

1276 raise ValueError("UNKNOWN/UNIMPLEMENTED etype '%s'" % etype) 

1277 if len(key) != self.ep.keysize: 

1278 raise ValueError( 

1279 "Wrong key length. Got %s. Expected %s" 

1280 % (len(key), self.ep.keysize) 

1281 ) 

1282 if cksumtype is None and self.ep.reqcksum in _checksums: 

1283 cksumtype = self.ep.reqcksum 

1284 self.cksumtype = cksumtype 

1285 if cksumtype is not None: 

1286 try: 

1287 self.cp = _checksums[cksumtype] 

1288 except ValueError: 

1289 raise ValueError("UNKNOWN/UNIMPLEMENTED cksumtype '%s'" % cksumtype) 

1290 if self.etype is None and issubclass(self.cp, _SimplifiedChecksum): 

1291 self.etype = self.cp.enc.etype # type: ignore 

1292 self.key = key 

1293 

1294 def __repr__(self): 

1295 # type: () -> str 

1296 if self.etype: 

1297 name = self.etype.name 

1298 elif self.cksumtype: 

1299 name = self.cksumtype.name 

1300 else: 

1301 return "<Key UNKNOWN>" 

1302 return "<Key %s%s>" % ( 

1303 name, 

1304 " (%s octets)" % len(self.key), 

1305 ) 

1306 

1307 def encrypt(self, keyusage, plaintext, confounder=None, **kwargs): 

1308 # type: (int, bytes, Optional[bytes], **Any) -> bytes 

1309 """ 

1310 Encrypt data using the current Key. 

1311 

1312 :param keyusage: the key usage 

1313 :param plaintext: the plain text to encrypt 

1314 :param confounder: (optional) choose the confounder. Otherwise random. 

1315 """ 

1316 return self.ep.encrypt(self, keyusage, bytes(plaintext), confounder, **kwargs) 

1317 

1318 def decrypt(self, keyusage, ciphertext, **kwargs): 

1319 # type: (int, bytes, **Any) -> bytes 

1320 """ 

1321 Decrypt data using the current Key. 

1322 

1323 :param keyusage: the key usage 

1324 :param ciphertext: the encrypted text to decrypt 

1325 """ 

1326 # Throw InvalidChecksum on checksum failure. Throw ValueError on 

1327 # invalid key enctype or malformed ciphertext. 

1328 return self.ep.decrypt(self, keyusage, ciphertext, **kwargs) 

1329 

1330 def prf(self, string): 

1331 # type: (bytes) -> bytes 

1332 return self.ep.prf(self, string) 

1333 

1334 def make_checksum(self, keyusage, text, cksumtype=None, **kwargs): 

1335 # type: (int, bytes, Optional[int], **Any) -> bytes 

1336 """ 

1337 Create a checksum using the current Key. 

1338 

1339 :param keyusage: the key usage 

1340 :param text: the text to create a checksum from 

1341 :param cksumtype: (optional) override the checksum type 

1342 """ 

1343 if cksumtype is not None and cksumtype != self.cksumtype: 

1344 # Clone key and use a different cksumtype 

1345 return Key( 

1346 cksumtype=cksumtype, 

1347 key=self.key, 

1348 ).make_checksum(keyusage=keyusage, text=text, **kwargs) 

1349 if self.cksumtype is None: 

1350 raise ValueError("cksumtype not specified !") 

1351 return self.cp.checksum(self, keyusage, text, **kwargs) 

1352 

1353 def verify_checksum(self, keyusage, text, cksum, cksumtype=None): 

1354 # type: (int, bytes, bytes, Optional[int]) -> None 

1355 """ 

1356 Verify a checksum using the current Key. 

1357 

1358 :param keyusage: the key usage 

1359 :param text: the text to verify 

1360 :param cksum: the expected checksum 

1361 :param cksumtype: (optional) override the checksum type 

1362 """ 

1363 if cksumtype is not None and cksumtype != self.cksumtype: 

1364 # Clone key and use a different cksumtype 

1365 return Key( 

1366 cksumtype=cksumtype, 

1367 key=self.key, 

1368 ).verify_checksum(keyusage=keyusage, text=text, cksum=cksum) 

1369 # Throw InvalidChecksum exception on checksum failure. Throw 

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

1371 # malformed checksum. 

1372 if self.cksumtype is None: 

1373 raise ValueError("cksumtype not specified !") 

1374 self.cp.verify(self, keyusage, text, cksum) 

1375 

1376 @classmethod 

1377 def random_to_key(cls, etype, seed): 

1378 # type: (EncryptionType, bytes) -> Key 

1379 """ 

1380 random-to-key per RFC3961 

1381 

1382 This is used to create a random Key from a seed. 

1383 """ 

1384 try: 

1385 ep = _enctypes[etype] 

1386 except ValueError: 

1387 raise ValueError("Unknown etype '%s'" % etype) 

1388 if len(seed) != ep.seedsize: 

1389 raise ValueError("Wrong crypto seed length") 

1390 return ep.random_to_key(seed) 

1391 

1392 @classmethod 

1393 def new_random_key(cls, etype): 

1394 # type: (EncryptionType) -> Key 

1395 """ 

1396 Generates a seed then calls random-to-key 

1397 """ 

1398 try: 

1399 ep = _enctypes[etype] 

1400 except ValueError: 

1401 raise ValueError("Unknown etype '%s'" % etype) 

1402 return cls.random_to_key(etype, os.urandom(ep.seedsize)) 

1403 

1404 @classmethod 

1405 def string_to_key(cls, etype, string, salt, params=None): 

1406 # type: (EncryptionType, bytes, bytes, Optional[bytes]) -> Key 

1407 """ 

1408 string-to-key per RFC3961 

1409 

1410 This is typically used to create a Key object from a password + salt 

1411 """ 

1412 try: 

1413 ep = _enctypes[etype] 

1414 except ValueError: 

1415 raise ValueError("Unknown etype '%s'" % etype) 

1416 return ep.string_to_key(string, salt, params) 

1417 

1418 

1419############ 

1420# RFC 6113 # 

1421############ 

1422 

1423 

1424def KRB_FX_CF2(key1, key2, pepper1, pepper2): 

1425 # type: (Key, Key, bytes, bytes) -> Key 

1426 """ 

1427 KRB-FX-CF2 RFC6113 

1428 """ 

1429 

1430 def prfplus(key, pepper): 

1431 # type: (Key, bytes) -> bytes 

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

1433 out = b"" 

1434 count = 1 

1435 while len(out) < key.ep.seedsize: 

1436 out += key.prf(chb(count) + pepper) 

1437 count += 1 

1438 return out[: key.ep.seedsize] 

1439 

1440 return Key( 

1441 key1.etype, 

1442 key=bytes( 

1443 _xorbytes( 

1444 bytearray(prfplus(key1, pepper1)), bytearray(prfplus(key2, pepper2)) 

1445 ) 

1446 ), 

1447 )