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 allow the usage of a 56bit key, which thus behaves like DES
101DES = decrepit_algorithms.TripleDES
102
103
104# https://go.microsoft.com/fwlink/?LinkId=186039
105# https://csrc.nist.gov/CSRC/media/Publications/sp/800-108/archive/2008-11-06/documents/sp800-108-Nov2008.pdf
106# [SP800-108] section 5.1 (used in [MS-SMB2] sect 3.1.4.2)
107
108
109def SP800108_KDFCTR(
110 K_I: bytes,
111 Label: bytes,
112 Context: bytes,
113 L: int,
114 hashmod: _GenericHash = Hash_SHA256,
115) -> bytes:
116 """
117 KDF in Counter Mode as section 5.1 of [SP800-108]
118
119 This assumes r=32, and defaults to SHA256 ([MS-SMB2] default).
120 """
121 PRF = Hmac(K_I, hashmod).digest
122 h = hashmod.hash_len
123 n = math.ceil(L / h)
124 if n >= 0xFFFFFFFF:
125 # 2^r-1 = 0xffffffff with r=32 per [MS-SMB2]
126 raise ValueError("Invalid n value in SP800108_KDFCTR")
127 result = b"".join(
128 PRF(struct.pack(">I", i) + Label + b"\x00" + Context + struct.pack(">I", L))
129 for i in range(1, n + 1)
130 )
131 return result[: L // 8]
132
133
134# https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-1
135
136
137class EncryptionType(enum.IntEnum):
138 DES_CBC_CRC = 1
139 DES_CBC_MD4 = 2
140 DES_CBC_MD5 = 3
141 # DES3_CBC_SHA1 = 7
142 DES3_CBC_SHA1_KD = 16
143 AES128_CTS_HMAC_SHA1_96 = 17
144 AES256_CTS_HMAC_SHA1_96 = 18
145 AES128_CTS_HMAC_SHA256_128 = 19
146 AES256_CTS_HMAC_SHA384_192 = 20
147 RC4_HMAC = 23
148 RC4_HMAC_EXP = 24
149 # CAMELLIA128-CTS-CMAC = 25
150 # CAMELLIA256-CTS-CMAC = 26
151
152
153# https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-2
154
155
156class ChecksumType(enum.IntEnum):
157 CRC32 = 1
158 RSA_MD4 = 2
159 RSA_MD4_DES = 3
160 # RSA_MD5 = 7
161 RSA_MD5_DES = 8
162 # RSA_MD5_DES3 = 9
163 # SHA1 = 10
164 HMAC_SHA1_DES3_KD = 12
165 # HMAC_SHA1_DES3 = 13
166 # SHA1 = 14
167 HMAC_SHA1_96_AES128 = 15
168 HMAC_SHA1_96_AES256 = 16
169 # CMAC-CAMELLIA128 = 17
170 # CMAC-CAMELLIA256 = 18
171 HMAC_SHA256_128_AES128 = 19
172 HMAC_SHA384_192_AES256 = 20
173 HMAC_MD5 = -138
174
175
176class InvalidChecksum(ValueError):
177 pass
178
179
180#########
181# Utils #
182#########
183
184
185# https://www.gnu.org/software/shishi/ides.pdf - APPENDIX B
186
187
188def _n_fold(s, n):
189 # type: (bytes, int) -> bytes
190 """
191 n-fold is an algorithm that takes m input bits and "stretches" them
192 to form n output bits with equal contribution from each input bit to
193 the output (quote from RFC 3961 sect 3.1).
194 """
195
196 def rot13(y, nb):
197 # type: (bytes, int) -> bytes
198 x = bytes_int(y)
199 mod = (1 << (nb * 8)) - 1
200 if nb == 0:
201 return y
202 elif nb == 1:
203 return int_bytes(((x >> 5) | (x << (nb * 8 - 5))) & mod, nb)
204 else:
205 return int_bytes(((x >> 13) | (x << (nb * 8 - 13))) & mod, nb)
206
207 def ocadd(x, y, nb):
208 # type: (bytearray, bytearray, int) -> bytearray
209 v = [a + b for a, b in zip(x, y)]
210 while any(x & ~0xFF for x in v):
211 v = [(v[i - nb + 1] >> 8) + (v[i] & 0xFF) for i in range(nb)]
212 return bytearray(x for x in v)
213
214 m = len(s)
215 lcm = n // math.gcd(n, m) * m # lcm = math.lcm(n, m) on Python>=3.9
216 buf = bytearray()
217 for _ in range(lcm // m):
218 buf += s
219 s = rot13(s, m)
220 out = bytearray(b"\x00" * n)
221 for i in range(0, lcm, n):
222 out = ocadd(out, buf[i : i + n], n)
223 return bytes(out)
224
225
226def _zeropad(s, padsize):
227 # type: (bytes, int) -> bytes
228 """
229 Return s padded with 0 bytes to a multiple of padsize.
230 """
231 return s + b"\x00" * (-len(s) % padsize)
232
233
234def _rfc1964pad(s):
235 # type: (bytes) -> bytes
236 """
237 Return s padded as RFC1964 mandates
238 """
239 pad = (-len(s)) % 8
240 return s + pad * struct.pack("!B", pad)
241
242
243def _xorbytes(b1, b2):
244 # type: (bytearray, bytearray) -> bytearray
245 """
246 xor two strings together and return the resulting string
247 """
248 assert len(b1) == len(b2)
249 return bytearray((x ^ y) for x, y in zip(b1, b2))
250
251
252def _mac_equal(mac1, mac2):
253 # type: (bytes, bytes) -> bool
254 # Constant-time comparison function. (We can't use HMAC.verify
255 # since we use truncated macs.)
256 return all(x == y for x, y in zip(mac1, mac2))
257
258
259# https://doi.org/10.6028/NBS.FIPS.74 sect 3.6
260
261WEAK_DES_KEYS = set(
262 [
263 # 1
264 b"\xe0\x01\xe0\x01\xf1\x01\xf1\x01",
265 b"\x01\xe0\x01\xe0\x01\xf1\x01\xf1",
266 # 2
267 b"\xfe\x1f\xfe\x1f\xfe\x0e\xfe\x0e",
268 b"\x1f\xfe\x1f\xfe\x0e\xfe\x0e\xfe",
269 # 3
270 b"\xe0\x1f\xe0\x1f\xf1\x0e\xf1\x0e",
271 b"\x1f\xe0\x1f\xe0\x0e\xf1\x0e\xf1",
272 # 4
273 b"\x01\xfe\x01\xfe\x01\xfe\x01\xfe",
274 b"\xfe\x01\xfe\x01\xfe\x01\xfe\x01",
275 # 5
276 b"\x01\x1f\x01\x1f\x01\x0e\x01\x0e",
277 b"\x1f\x01\x1f\x01\x0e\x01\x0e\x01",
278 # 6
279 b"\xe0\xfe\xe0\xfe\xf1\xfe\xf1\xfe",
280 b"\xfe\xe0\xfe\xe0\xfe\xf1\xfe\xf1",
281 # 7
282 b"\x01" * 8,
283 # 8
284 b"\xfe" * 8,
285 # 9
286 b"\xe0" * 4 + b"\xf1" * 4,
287 # 10
288 b"\x1f" * 4 + b"\x0e" * 4,
289 ]
290)
291
292# fmt: off
293CRC32_TABLE = [
294 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
295 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
296 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
297 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
298 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
299 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
300 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
301 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
302 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
303 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
304 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
305 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
306 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
307 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
308 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
309 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
310 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
311 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
312 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
313 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
314 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
315 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
316 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
317 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
318 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
319 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
320 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
321 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
322 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
323 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
324 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
325 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
326 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
327 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
328 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
329 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
330 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
331 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
332 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
333 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
334 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
335 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
336 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
337 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
338 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
339 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
340 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
341 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
342 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
343 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
344 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
345 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
346 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
347 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
348 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
349 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
350 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
351 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
352 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
353 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
354 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
355 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
356 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
357 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
358]
359# fmt: on
360
361############
362# RFC 3961 #
363############
364
365
366# RFC3961 sect 3
367
368
369class _EncryptionAlgorithmProfile(abc.ABCMeta):
370 """
371 Base class for etype profiles.
372
373 Usable etype classes must define:
374 :attr etype: etype number
375 :attr keysize: protocol size of key in bytes
376 :attr seedsize: random_to_key input size in bytes
377 :attr reqcksum: 'required checksum mechanism' per RFC3961.
378 this is the default checksum used for this algorithm.
379 :attr random_to_key: (if the keyspace is not dense)
380 :attr string_to_key:
381 :attr encrypt:
382 :attr decrypt:
383 :attr prf:
384 """
385
386 etype = None # type: EncryptionType
387 keysize = None # type: int
388 seedsize = None # type: int
389 reqcksum = None # type: ChecksumType
390
391 @classmethod
392 @abc.abstractmethod
393 def derive(cls, key, constant):
394 # type: (Key, bytes) -> bytes
395 pass
396
397 @classmethod
398 @abc.abstractmethod
399 def encrypt(cls, key, keyusage, plaintext, confounder):
400 # type: (Key, int, bytes, Optional[bytes]) -> bytes
401 pass
402
403 @classmethod
404 @abc.abstractmethod
405 def decrypt(cls, key, keyusage, ciphertext):
406 # type: (Key, int, bytes) -> bytes
407 pass
408
409 @classmethod
410 @abc.abstractmethod
411 def prf(cls, key, string):
412 # type: (Key, bytes) -> bytes
413 pass
414
415 @classmethod
416 @abc.abstractmethod
417 def string_to_key(cls, string, salt, params):
418 # type: (bytes, bytes, Optional[bytes]) -> Key
419 pass
420
421 @classmethod
422 def random_to_key(cls, seed):
423 # type: (bytes) -> Key
424 if len(seed) != cls.seedsize:
425 raise ValueError("Wrong seed length")
426 return Key(cls.etype, key=seed)
427
428
429# RFC3961 sect 4
430
431
432class _ChecksumProfile(object):
433 """
434 Base class for checksum profiles.
435
436 Usable checksum classes must define:
437 :func checksum:
438 :attr macsize: Size of checksum in bytes
439 :func verify: (if verification is not just checksum-and-compare)
440 """
441
442 macsize = None # type: int
443
444 @classmethod
445 @abc.abstractmethod
446 def checksum(cls, key, keyusage, text):
447 # type: (Key, int, bytes) -> bytes
448 pass
449
450 @classmethod
451 def verify(cls, key, keyusage, text, cksum):
452 # type: (Key, int, bytes, bytes) -> None
453 expected = cls.checksum(key, keyusage, text)
454 if not _mac_equal(cksum, expected):
455 raise InvalidChecksum("checksum verification failure")
456
457
458# RFC3961 sect 5.3
459
460
461class _SimplifiedEncryptionProfile(_EncryptionAlgorithmProfile):
462 """
463 Base class for etypes using the RFC 3961 simplified profile.
464 Defines the encrypt, decrypt, and prf methods.
465
466 Subclasses must define:
467
468 :param blocksize: Underlying cipher block size in bytes
469 :param padsize: Underlying cipher padding multiple (1 or blocksize)
470 :param macsize: Size of integrity MAC in bytes
471 :param hashmod: underlying hash function
472 :param basic_encrypt, basic_decrypt: Underlying CBC/CTS cipher
473 """
474
475 blocksize = None # type: int
476 padsize = None # type: int
477 macsize = None # type: int
478 hashmod = None # type: Any
479
480 # Used in RFC 8009. This is not a simplified profile per se but
481 # is still pretty close.
482 rfc8009 = False
483
484 @classmethod
485 @abc.abstractmethod
486 def basic_encrypt(cls, key, plaintext):
487 # type: (bytes, bytes) -> bytes
488 pass
489
490 @classmethod
491 @abc.abstractmethod
492 def basic_decrypt(cls, key, ciphertext):
493 # type: (bytes, bytes) -> bytes
494 pass
495
496 @classmethod
497 def derive(cls, key, constant):
498 # type: (Key, bytes) -> bytes
499 """
500 Also known as "DK" in RFC3961.
501 """
502 # RFC 3961 only says to n-fold the constant only if it is
503 # shorter than the cipher block size. But all Unix
504 # implementations n-fold constants if their length is larger
505 # than the block size as well, and n-folding when the length
506 # is equal to the block size is a no-op.
507 plaintext = _n_fold(constant, cls.blocksize)
508 rndseed = b""
509 while len(rndseed) < cls.seedsize:
510 ciphertext = cls.basic_encrypt(key.key, plaintext)
511 rndseed += ciphertext
512 plaintext = ciphertext
513 # DK(Key, Constant) = random-to-key(DR(Key, Constant))
514 return cls.random_to_key(rndseed[0 : cls.seedsize]).key
515
516 @classmethod
517 def encrypt(cls, key, keyusage, plaintext, confounder, signtext=None):
518 # type: (Key, int, bytes, Optional[bytes], Optional[bytes]) -> bytes
519 """
520 Encryption function.
521
522 :param key: the key
523 :param keyusage: the keyusage
524 :param plaintext: the text to encrypt
525 :param confounder: (optional) the confounder. If none, will be random
526 :param signtext: (optional) make the checksum include different data than what
527 is encrypted. Useful for kerberos GSS_WrapEx. If none, same as
528 plaintext.
529 """
530 if not cls.rfc8009:
531 ki = cls.derive(key, struct.pack(">IB", keyusage, 0x55))
532 ke = cls.derive(key, struct.pack(">IB", keyusage, 0xAA))
533 else:
534 ki = cls.derive(key, struct.pack(">IB", keyusage, 0x55), cls.macsize * 8) # type: ignore # noqa: E501
535 ke = cls.derive(key, struct.pack(">IB", keyusage, 0xAA), cls.keysize * 8) # type: ignore # noqa: E501
536 if confounder is None:
537 confounder = os.urandom(cls.blocksize)
538 basic_plaintext = confounder + _zeropad(plaintext, cls.padsize)
539 if signtext is None:
540 signtext = basic_plaintext
541 if not cls.rfc8009:
542 # Simplified profile
543 hmac = Hmac(ki, cls.hashmod).digest(signtext)
544 return cls.basic_encrypt(ke, basic_plaintext) + hmac[: cls.macsize]
545 else:
546 # RFC 8009
547 C = cls.basic_encrypt(ke, basic_plaintext)
548 hmac = Hmac(ki, cls.hashmod).digest(b"\0" * 16 + C) # XXX IV
549 return C + hmac[: cls.macsize]
550
551 @classmethod
552 def decrypt(cls, key, keyusage, ciphertext, presignfunc=None):
553 # type: (Key, int, bytes, Optional[Callable[[bytes, bytes], bytes]]) -> bytes
554 """
555 decryption function
556 """
557 if not cls.rfc8009:
558 ki = cls.derive(key, struct.pack(">IB", keyusage, 0x55))
559 ke = cls.derive(key, struct.pack(">IB", keyusage, 0xAA))
560 else:
561 ki = cls.derive(key, struct.pack(">IB", keyusage, 0x55), cls.macsize * 8) # type: ignore # noqa: E501
562 ke = cls.derive(key, struct.pack(">IB", keyusage, 0xAA), cls.keysize * 8) # type: ignore # noqa: E501
563 if len(ciphertext) < cls.blocksize + cls.macsize:
564 raise ValueError("Ciphertext too short")
565 basic_ctext, mac = ciphertext[: -cls.macsize], ciphertext[-cls.macsize :]
566 if len(basic_ctext) % cls.padsize != 0:
567 raise ValueError("ciphertext does not meet padding requirement")
568 if not cls.rfc8009:
569 # Simplified profile
570 basic_plaintext = cls.basic_decrypt(ke, basic_ctext)
571 signtext = basic_plaintext
572 if presignfunc:
573 # Allow to have additional processing of the data that is to be signed.
574 # This is useful for GSS_WrapEx
575 signtext = presignfunc(
576 basic_plaintext[: cls.blocksize],
577 basic_plaintext[cls.blocksize :],
578 )
579 hmac = Hmac(ki, cls.hashmod).digest(signtext)
580 expmac = hmac[: cls.macsize]
581 if not _mac_equal(mac, expmac):
582 raise ValueError("ciphertext integrity failure")
583 else:
584 # RFC 8009
585 signtext = b"\0" * 16 + basic_ctext # XXX IV
586 if presignfunc:
587 # Allow to have additional processing of the data that is to be signed.
588 # This is useful for GSS_WrapEx
589 signtext = presignfunc(
590 basic_ctext[16 : 16 + cls.blocksize],
591 basic_ctext[16 + cls.blocksize :],
592 )
593 hmac = Hmac(ki, cls.hashmod).digest(signtext)
594 expmac = hmac[: cls.macsize]
595 if not _mac_equal(mac, expmac):
596 raise ValueError("ciphertext integrity failure")
597 basic_plaintext = cls.basic_decrypt(ke, basic_ctext)
598 # Discard the confounder.
599 return bytes(basic_plaintext[cls.blocksize :])
600
601 @classmethod
602 def prf(cls, key, string):
603 # type: (Key, bytes) -> bytes
604 """
605 pseudo-random function
606 """
607 # Hash the input. RFC 3961 says to truncate to the padding
608 # size, but implementations truncate to the block size.
609 hashval = cls.hashmod().digest(string)
610 if len(hashval) % cls.blocksize:
611 hashval = hashval[: -(len(hashval) % cls.blocksize)]
612 # Encrypt the hash with a derived key.
613 kp = cls.derive(key, b"prf")
614 return cls.basic_encrypt(kp, hashval)
615
616
617# RFC3961 sect 5.4
618
619
620class _SimplifiedChecksum(_ChecksumProfile):
621 """
622 Base class for checksums using the RFC 3961 simplified profile.
623 Defines the checksum and verify methods.
624
625 Subclasses must define:
626 :attr enc: Profile of associated etype
627 """
628
629 enc = None # type: Type[_SimplifiedEncryptionProfile]
630
631 # Used in RFC 8009. This is not a simplified profile per se but
632 # is still pretty close.
633 rfc8009 = False
634
635 @classmethod
636 def checksum(cls, key, keyusage, text):
637 # type: (Key, int, bytes) -> bytes
638 if not cls.rfc8009:
639 # Simplified profile
640 kc = cls.enc.derive(key, struct.pack(">IB", keyusage, 0x99))
641 else:
642 # RFC 8009
643 kc = cls.enc.derive( # type: ignore
644 key, struct.pack(">IB", keyusage, 0x99), cls.macsize * 8
645 )
646 hmac = Hmac(kc, cls.enc.hashmod).digest(text)
647 return hmac[: cls.macsize]
648
649 @classmethod
650 def verify(cls, key, keyusage, text, cksum):
651 # type: (Key, int, bytes, bytes) -> None
652 if key.etype != cls.enc.etype:
653 raise ValueError("Wrong key type for checksum")
654 super(_SimplifiedChecksum, cls).verify(key, keyusage, text, cksum)
655
656
657# RFC3961 sect 6.1
658
659
660class _CRC32(_ChecksumProfile):
661 macsize = 4
662
663 # This isn't your usual CRC32, it's a "modified version" according to the RFC3961.
664 # Another RFC states it's just a buggy version of the actual CRC32.
665
666 @classmethod
667 def checksum(cls, key, keyusage, text):
668 # type: (Optional[Key], int, bytes) -> bytes
669 c = 0
670 for i in range(len(text)):
671 idx = text[i] ^ c
672 idx &= 0xFF
673 c >>= 8
674 c ^= CRC32_TABLE[idx]
675 return c.to_bytes(4, "little")
676
677
678# RFC3961 sect 6.2
679
680
681class _DESCBC(_SimplifiedEncryptionProfile):
682 keysize = 8
683 seedsize = 8
684 blocksize = 8
685 padsize = 8
686 macsize = 16
687 hashmod = Hash_MD5
688
689 @classmethod
690 def encrypt(cls, key, keyusage, plaintext, confounder, signtext=None):
691 # type: (Key, int, bytes, Optional[bytes], Any) -> bytes
692 if confounder is None:
693 confounder = os.urandom(cls.blocksize)
694 basic_plaintext = (
695 confounder + b"\x00" * cls.macsize + _zeropad(plaintext, cls.padsize)
696 )
697 checksum = cls.hashmod().digest(basic_plaintext)
698 basic_plaintext = (
699 basic_plaintext[: len(confounder)]
700 + checksum
701 + basic_plaintext[len(confounder) + len(checksum) :]
702 )
703 return cls.basic_encrypt(key.key, basic_plaintext)
704
705 @classmethod
706 def decrypt(cls, key, keyusage, ciphertext, presignfunc=None):
707 # type: (Key, int, bytes, Any) -> bytes
708 if len(ciphertext) < cls.blocksize + cls.macsize:
709 raise ValueError("ciphertext too short")
710
711 complex_plaintext = cls.basic_decrypt(key.key, ciphertext)
712 cofounder = complex_plaintext[: cls.padsize]
713 mac = complex_plaintext[cls.padsize : cls.padsize + cls.macsize]
714 message = complex_plaintext[cls.padsize + cls.macsize :]
715
716 expmac = cls.hashmod().digest(cofounder + b"\x00" * cls.macsize + message)
717 if not _mac_equal(mac, expmac):
718 raise InvalidChecksum("ciphertext integrity failure")
719 return bytes(message)
720
721 @classmethod
722 def mit_des_string_to_key(cls, string, salt):
723 # type: (bytes, bytes) -> Key
724 def fixparity(deskey):
725 # type: (List[int]) -> bytes
726 temp = b""
727 for i in range(len(deskey)):
728 t = (bin(orb(deskey[i]))[2:]).rjust(8, "0")
729 if t[:7].count("1") % 2 == 0:
730 temp += chb(int(t[:7] + "1", 2))
731 else:
732 temp += chb(int(t[:7] + "0", 2))
733 return temp
734
735 def addparity(l1):
736 # type: (List[int]) -> List[int]
737 temp = list()
738 for byte in l1:
739 if (bin(byte).count("1") % 2) == 0:
740 byte = (byte << 1) | 0b00000001
741 else:
742 byte = (byte << 1) & 0b11111110
743 temp.append(byte)
744 return temp
745
746 def XOR(l1, l2):
747 # type: (List[int], List[int]) -> List[int]
748 temp = list()
749 for b1, b2 in zip(l1, l2):
750 temp.append((b1 ^ b2) & 0b01111111)
751
752 return temp
753
754 odd = True
755 tempstring = [0, 0, 0, 0, 0, 0, 0, 0]
756 s = _zeropad(string + salt, cls.padsize)
757
758 for block in [s[i : i + 8] for i in range(0, len(s), 8)]:
759 temp56 = list()
760 # removeMSBits
761 for byte in block:
762 temp56.append(orb(byte) & 0b01111111)
763
764 # reverse
765 if odd is False:
766 bintemp = b""
767 for byte in temp56:
768 bintemp += bin(byte)[2:].rjust(7, "0").encode()
769 bintemp = bintemp[::-1]
770
771 temp56 = list()
772 for bits7 in [bintemp[i : i + 7] for i in range(0, len(bintemp), 7)]:
773 temp56.append(int(bits7, 2))
774
775 odd = not odd
776 tempstring = XOR(tempstring, temp56)
777
778 tempkey = bytearray(b"".join(chb(byte) for byte in addparity(tempstring)))
779 if bytes(tempkey) in WEAK_DES_KEYS:
780 tempkey[7] = tempkey[7] ^ 0xF0
781
782 tempkeyb = bytes(tempkey)
783 des = Cipher(DES(tempkeyb), modes.CBC(tempkeyb)).encryptor()
784 chekcsumkey = des.update(s)[-8:]
785 chekcsumkey = bytearray(fixparity(chekcsumkey))
786 if bytes(chekcsumkey) in WEAK_DES_KEYS:
787 chekcsumkey[7] = chekcsumkey[7] ^ 0xF0
788
789 return Key(cls.etype, key=bytes(chekcsumkey))
790
791 @classmethod
792 def basic_encrypt(cls, key, plaintext):
793 # type: (bytes, bytes) -> bytes
794 assert len(plaintext) % 8 == 0
795 des = Cipher(DES(key), modes.CBC(b"\0" * 8)).encryptor()
796 return des.update(bytes(plaintext))
797
798 @classmethod
799 def basic_decrypt(cls, key, ciphertext):
800 # type: (bytes, bytes) -> bytes
801 assert len(ciphertext) % 8 == 0
802 des = Cipher(DES(key), modes.CBC(b"\0" * 8)).decryptor()
803 return des.update(bytes(ciphertext))
804
805 @classmethod
806 def string_to_key(cls, string, salt, params):
807 # type: (bytes, bytes, Optional[bytes]) -> Key
808 if params is not None and params != b"":
809 raise ValueError("Invalid DES string-to-key parameters")
810 key = cls.mit_des_string_to_key(string, salt)
811 return key
812
813
814# RFC3961 sect 6.2.1
815
816
817class _DESMD5(_DESCBC):
818 etype = EncryptionType.DES_CBC_MD5
819 hashmod = Hash_MD5
820 reqcksum = ChecksumType.RSA_MD5_DES
821
822
823# RFC3961 sect 6.2.2
824
825
826class _DESMD4(_DESCBC):
827 etype = EncryptionType.DES_CBC_MD4
828 hashmod = Hash_MD4
829 reqcksum = ChecksumType.RSA_MD4_DES
830
831
832# RFC3961 sect 6.3
833
834
835class _DES3CBC(_SimplifiedEncryptionProfile):
836 etype = EncryptionType.DES3_CBC_SHA1_KD
837 keysize = 24
838 seedsize = 21
839 blocksize = 8
840 padsize = 8
841 macsize = 20
842 hashmod = Hash_SHA
843 reqcksum = ChecksumType.HMAC_SHA1_DES3_KD
844
845 @classmethod
846 def random_to_key(cls, seed):
847 # type: (bytes) -> Key
848 # XXX Maybe reframe as _DESEncryptionType.random_to_key and use that
849 # way from DES3 random-to-key when DES is implemented, since
850 # MIT does this instead of the RFC 3961 random-to-key.
851 def expand(seed):
852 # type: (bytes) -> bytes
853 def parity(b):
854 # type: (int) -> int
855 # Return b with the low-order bit set to yield odd parity.
856 b &= ~1
857 return b if bin(b & ~1).count("1") % 2 else b | 1
858
859 assert len(seed) == 7
860 firstbytes = [parity(b & ~1) for b in seed]
861 lastbyte = parity(sum((seed[i] & 1) << i + 1 for i in range(7)))
862 keybytes = bytearray(firstbytes + [lastbyte])
863 if bytes(keybytes) in WEAK_DES_KEYS:
864 keybytes[7] = keybytes[7] ^ 0xF0
865 return bytes(keybytes)
866
867 if len(seed) != 21:
868 raise ValueError("Wrong seed length")
869 k1, k2, k3 = expand(seed[:7]), expand(seed[7:14]), expand(seed[14:])
870 return Key(cls.etype, key=k1 + k2 + k3)
871
872 @classmethod
873 def string_to_key(cls, string, salt, params):
874 # type: (bytes, bytes, Optional[bytes]) -> Key
875 if params is not None and params != b"":
876 raise ValueError("Invalid DES3 string-to-key parameters")
877 k = cls.random_to_key(_n_fold(string + salt, 21))
878 return Key(
879 cls.etype,
880 key=cls.derive(k, b"kerberos"),
881 )
882
883 @classmethod
884 def basic_encrypt(cls, key, plaintext):
885 # type: (bytes, bytes) -> bytes
886 assert len(plaintext) % 8 == 0
887 des3 = Cipher(
888 decrepit_algorithms.TripleDES(key), modes.CBC(b"\0" * 8)
889 ).encryptor()
890 return des3.update(bytes(plaintext))
891
892 @classmethod
893 def basic_decrypt(cls, key, ciphertext):
894 # type: (bytes, bytes) -> bytes
895 assert len(ciphertext) % 8 == 0
896 des3 = Cipher(
897 decrepit_algorithms.TripleDES(key), modes.CBC(b"\0" * 8)
898 ).decryptor()
899 return des3.update(bytes(ciphertext))
900
901
902class _SHA1DES3(_SimplifiedChecksum):
903 macsize = 20
904 enc = _DES3CBC
905
906
907############
908# RFC 3962 #
909############
910
911
912# RFC3962 sect 6
913
914
915class _AESEncryptionType_SHA1_96(_SimplifiedEncryptionProfile, abc.ABCMeta):
916 blocksize = 16
917 padsize = 1
918 macsize = 12
919 hashmod = Hash_SHA
920
921 @classmethod
922 def string_to_key(cls, string, salt, params):
923 # type: (bytes, bytes, Optional[bytes]) -> Key
924 iterations = struct.unpack(">L", params or b"\x00\x00\x10\x00")[0]
925 kdf = PBKDF2HMAC(
926 algorithm=hashes.SHA1(),
927 length=cls.seedsize,
928 salt=salt,
929 iterations=iterations,
930 )
931 tkey = cls.random_to_key(kdf.derive(string))
932 return Key(
933 cls.etype,
934 key=cls.derive(tkey, b"kerberos"),
935 )
936
937 # basic_encrypt and basic_decrypt implement AES in CBC-CS3 mode
938
939 @classmethod
940 def basic_encrypt(cls, key, plaintext):
941 # type: (bytes, bytes) -> bytes
942 assert len(plaintext) >= 16
943 aes = Cipher(algorithms.AES(key), modes.CBC(b"\0" * 16)).encryptor()
944 ctext = aes.update(_zeropad(bytes(plaintext), 16))
945 if len(plaintext) > 16:
946 # Swap the last two ciphertext blocks and truncate the
947 # final block to match the plaintext length.
948 lastlen = len(plaintext) % 16 or 16
949 ctext = ctext[:-32] + ctext[-16:] + ctext[-32:-16][:lastlen]
950 return ctext
951
952 @classmethod
953 def basic_decrypt(cls, key, ciphertext):
954 # type: (bytes, bytes) -> bytes
955 assert len(ciphertext) >= 16
956 aes = Cipher(algorithms.AES(key), modes.ECB()).decryptor()
957 if len(ciphertext) == 16:
958 return aes.update(ciphertext)
959 # Split the ciphertext into blocks. The last block may be partial.
960 cblocks = [
961 bytearray(ciphertext[p : p + 16]) for p in range(0, len(ciphertext), 16)
962 ]
963 lastlen = len(cblocks[-1])
964 # CBC-decrypt all but the last two blocks.
965 prev_cblock = bytearray(16)
966 plaintext = b""
967 for bb in cblocks[:-2]:
968 plaintext += _xorbytes(bytearray(aes.update(bytes(bb))), prev_cblock)
969 prev_cblock = bb
970 # Decrypt the second-to-last cipher block. The left side of
971 # the decrypted block will be the final block of plaintext
972 # xor'd with the final partial cipher block; the right side
973 # will be the omitted bytes of ciphertext from the final
974 # block.
975 bb = bytearray(aes.update(bytes(cblocks[-2])))
976 lastplaintext = _xorbytes(bb[:lastlen], cblocks[-1])
977 omitted = bb[lastlen:]
978 # Decrypt the final cipher block plus the omitted bytes to get
979 # the second-to-last plaintext block.
980 plaintext += _xorbytes(
981 bytearray(aes.update(bytes(cblocks[-1]) + bytes(omitted))), prev_cblock
982 )
983 return plaintext + lastplaintext
984
985
986# RFC3962 sect 7
987
988
989class _AES128CTS_SHA1_96(_AESEncryptionType_SHA1_96):
990 etype = EncryptionType.AES128_CTS_HMAC_SHA1_96
991 keysize = 16
992 seedsize = 16
993 reqcksum = ChecksumType.HMAC_SHA1_96_AES128
994
995
996class _AES256CTS_SHA1_96(_AESEncryptionType_SHA1_96):
997 etype = EncryptionType.AES256_CTS_HMAC_SHA1_96
998 keysize = 32
999 seedsize = 32
1000 reqcksum = ChecksumType.HMAC_SHA1_96_AES256
1001
1002
1003class _SHA1_96_AES128(_SimplifiedChecksum):
1004 macsize = 12
1005 enc = _AES128CTS_SHA1_96
1006
1007
1008class _SHA1_96_AES256(_SimplifiedChecksum):
1009 macsize = 12
1010 enc = _AES256CTS_SHA1_96
1011
1012
1013############
1014# RFC 4757 #
1015############
1016
1017# RFC4757 sect 4
1018
1019
1020class _HMACMD5(_ChecksumProfile):
1021 macsize = 16
1022
1023 @classmethod
1024 def checksum(cls, key, keyusage, text):
1025 # type: (Key, int, bytes) -> bytes
1026 ksign = Hmac_MD5(key.key).digest(b"signaturekey\0")
1027 md5hash = Hash_MD5().digest(_RC4.usage_str(keyusage) + text)
1028 return Hmac_MD5(ksign).digest(md5hash)
1029
1030 @classmethod
1031 def verify(cls, key, keyusage, text, cksum):
1032 # type: (Key, int, bytes, bytes) -> None
1033 if key.etype not in [EncryptionType.RC4_HMAC, EncryptionType.RC4_HMAC_EXP]:
1034 raise ValueError("Wrong key type for checksum")
1035 super(_HMACMD5, cls).verify(key, keyusage, text, cksum)
1036
1037
1038# RFC4757 sect 5
1039
1040
1041class _RC4(_EncryptionAlgorithmProfile):
1042 etype = EncryptionType.RC4_HMAC
1043 keysize = 16
1044 seedsize = 16
1045 reqcksum = ChecksumType.HMAC_MD5
1046 export = False
1047
1048 @staticmethod
1049 def usage_str(keyusage):
1050 # type: (int) -> bytes
1051 # Return a four-byte string for an RFC 3961 keyusage, using
1052 # the RFC 4757 rules sect 3. Per the errata, do not map 9 to 8.
1053 table = {3: 8, 23: 13}
1054 msusage = table[keyusage] if keyusage in table else keyusage
1055 return struct.pack("<I", msusage)
1056
1057 @classmethod
1058 def string_to_key(cls, string, salt, params):
1059 # type: (bytes, bytes, Optional[bytes]) -> Key
1060 if params is not None and params != b"":
1061 raise ValueError("Invalid RC4 string-to-key parameters")
1062 utf16string = plain_str(string).encode("UTF-16LE")
1063 return Key(cls.etype, key=Hash_MD4().digest(utf16string))
1064
1065 @classmethod
1066 def encrypt(cls, key, keyusage, plaintext, confounder):
1067 # type: (Key, int, bytes, Optional[bytes]) -> bytes
1068 if confounder is None:
1069 confounder = os.urandom(8)
1070 if cls.export:
1071 ki = Hmac_MD5(key.key).digest(b"fortybits\x00" + cls.usage_str(keyusage))
1072 else:
1073 ki = Hmac_MD5(key.key).digest(cls.usage_str(keyusage))
1074 cksum = Hmac_MD5(ki).digest(confounder + plaintext)
1075 if cls.export:
1076 ki = ki[:7] + b"\xab" * 9
1077 ke = Hmac_MD5(ki).digest(cksum)
1078 rc4 = Cipher(algorithms.ARC4(ke), mode=None).encryptor()
1079 return cksum + rc4.update(bytes(confounder + plaintext))
1080
1081 @classmethod
1082 def decrypt(cls, key, keyusage, ciphertext):
1083 # type: (Key, int, bytes) -> bytes
1084 if len(ciphertext) < 24:
1085 raise ValueError("ciphertext too short")
1086 cksum, basic_ctext = ciphertext[:16], ciphertext[16:]
1087 if cls.export:
1088 ki = Hmac_MD5(key.key).digest(b"fortybits\x00" + cls.usage_str(keyusage))
1089 else:
1090 ki = Hmac_MD5(key.key).digest(cls.usage_str(keyusage))
1091 if cls.export:
1092 kie = ki[:7] + b"\xab" * 9
1093 else:
1094 kie = ki
1095 ke = Hmac_MD5(kie).digest(cksum)
1096 rc4 = Cipher(decrepit_algorithms.ARC4(ke), mode=None).decryptor()
1097 basic_plaintext = rc4.update(bytes(basic_ctext))
1098 exp_cksum = Hmac_MD5(ki).digest(basic_plaintext)
1099 ok = _mac_equal(cksum, exp_cksum)
1100 if not ok and keyusage == 9:
1101 # Try again with usage 8, due to RFC 4757 errata.
1102 ki = Hmac_MD5(key.key).digest(struct.pack("<I", 8))
1103 exp_cksum = Hmac_MD5(ki).digest(basic_plaintext)
1104 ok = _mac_equal(cksum, exp_cksum)
1105 if not ok:
1106 raise InvalidChecksum("ciphertext integrity failure")
1107 # Discard the confounder.
1108 return bytes(basic_plaintext[8:])
1109
1110 @classmethod
1111 def prf(cls, key, string):
1112 # type: (Key, bytes) -> bytes
1113 return Hmac_SHA(key.key).digest(string)
1114
1115
1116class _RC4_EXPORT(_RC4):
1117 etype = EncryptionType.RC4_HMAC_EXP
1118 export = True
1119
1120
1121############
1122# RFC 8009 #
1123############
1124
1125
1126class _AESEncryptionType_SHA256_SHA384(_AESEncryptionType_SHA1_96, abc.ABCMeta):
1127 enctypename = None # type: bytes
1128 hashmod: _GenericHash = None # Scapy
1129 _hashmod: hashes.HashAlgorithm = None # Cryptography
1130
1131 # Turn on RFC 8009 mode
1132 rfc8009 = True
1133
1134 @classmethod
1135 def derive(cls, key, label, k, context=b""): # type: ignore
1136 # type: (Key, bytes, int, bytes) -> bytes
1137 """
1138 Also known as "KDF-HMAC-SHA2" in RFC8009.
1139 """
1140 # RFC 8009 sect 3
1141 return SP800108_KDFCTR(
1142 K_I=key.key,
1143 Label=label,
1144 Context=context,
1145 L=k,
1146 hashmod=cls.hashmod,
1147 )
1148
1149 @classmethod
1150 def string_to_key(cls, string, salt, params):
1151 # type: (bytes, bytes, Optional[bytes]) -> Key
1152 # RFC 8009 sect 4
1153 iterations = struct.unpack(">L", params or b"\x00\x00\x80\x00")[0]
1154 saltp = cls.enctypename + b"\x00" + salt
1155 kdf = PBKDF2HMAC(
1156 algorithm=cls._hashmod(),
1157 length=cls.seedsize,
1158 salt=saltp,
1159 iterations=iterations,
1160 )
1161 tkey = cls.random_to_key(kdf.derive(string))
1162 return Key(
1163 cls.etype,
1164 key=cls.derive(tkey, b"kerberos", cls.keysize * 8),
1165 )
1166
1167 @classmethod
1168 def prf(cls, key, string):
1169 # type: (Key, bytes) -> bytes
1170 return cls.derive(key, b"prf", cls.hashmod.hash_len * 8, string)
1171
1172
1173class _AES128CTS_SHA256_128(_AESEncryptionType_SHA256_SHA384):
1174 etype = EncryptionType.AES128_CTS_HMAC_SHA256_128
1175 keysize = 16
1176 seedsize = 16
1177 macsize = 16
1178 reqcksum = ChecksumType.HMAC_SHA256_128_AES128
1179 # _AESEncryptionType_SHA256_SHA384 parameters
1180 enctypename = b"aes128-cts-hmac-sha256-128"
1181 hashmod = Hash_SHA256
1182 _hashmod = hashes.SHA256
1183
1184
1185class _AES256CTS_SHA384_192(_AESEncryptionType_SHA256_SHA384):
1186 etype = EncryptionType.AES256_CTS_HMAC_SHA384_192
1187 keysize = 32
1188 seedsize = 32
1189 macsize = 24
1190 reqcksum = ChecksumType.HMAC_SHA384_192_AES256
1191 # _AESEncryptionType_SHA256_SHA384 parameters
1192 enctypename = b"aes256-cts-hmac-sha384-192"
1193 hashmod = Hash_SHA384
1194 _hashmod = hashes.SHA384
1195
1196
1197class _SHA256_128_AES128(_SimplifiedChecksum):
1198 macsize = 16
1199 enc = _AES128CTS_SHA256_128
1200 rfc8009 = True
1201
1202
1203class _SHA384_182_AES256(_SimplifiedChecksum):
1204 macsize = 24
1205 enc = _AES256CTS_SHA384_192
1206 rfc8009 = True
1207
1208
1209##############
1210# Key object #
1211##############
1212
1213_enctypes = {
1214 # DES_CBC_CRC - UNIMPLEMENTED
1215 EncryptionType.DES_CBC_MD5: _DESMD5,
1216 EncryptionType.DES_CBC_MD4: _DESMD4,
1217 # DES3_CBC_SHA1 - UNIMPLEMENTED
1218 EncryptionType.DES3_CBC_SHA1_KD: _DES3CBC,
1219 EncryptionType.AES128_CTS_HMAC_SHA1_96: _AES128CTS_SHA1_96,
1220 EncryptionType.AES256_CTS_HMAC_SHA1_96: _AES256CTS_SHA1_96,
1221 EncryptionType.AES128_CTS_HMAC_SHA256_128: _AES128CTS_SHA256_128,
1222 EncryptionType.AES256_CTS_HMAC_SHA384_192: _AES256CTS_SHA384_192,
1223 # CAMELLIA128-CTS-CMAC - UNIMPLEMENTED
1224 # CAMELLIA256-CTS-CMAC - UNIMPLEMENTED
1225 EncryptionType.RC4_HMAC: _RC4,
1226 EncryptionType.RC4_HMAC_EXP: _RC4_EXPORT,
1227}
1228
1229
1230_checksums = {
1231 ChecksumType.CRC32: _CRC32,
1232 # RSA_MD4 - UNIMPLEMENTED
1233 # RSA_MD4_DES - UNIMPLEMENTED
1234 # RSA_MD5 - UNIMPLEMENTED
1235 # RSA_MD5_DES - UNIMPLEMENTED
1236 # SHA1 - UNIMPLEMENTED
1237 ChecksumType.HMAC_SHA1_DES3_KD: _SHA1DES3,
1238 # HMAC_SHA1_DES3 - UNIMPLEMENTED
1239 ChecksumType.HMAC_SHA1_96_AES128: _SHA1_96_AES128,
1240 ChecksumType.HMAC_SHA1_96_AES256: _SHA1_96_AES256,
1241 # CMAC-CAMELLIA128 - UNIMPLEMENTED
1242 # CMAC-CAMELLIA256 - UNIMPLEMENTED
1243 ChecksumType.HMAC_SHA256_128_AES128: _SHA256_128_AES128,
1244 ChecksumType.HMAC_SHA384_192_AES256: _SHA384_182_AES256,
1245 ChecksumType.HMAC_MD5: _HMACMD5,
1246 0xFFFFFF76: _HMACMD5,
1247}
1248
1249
1250class Key(object):
1251 def __init__(
1252 self,
1253 etype: Union[EncryptionType, int, None] = None,
1254 key: bytes = b"",
1255 cksumtype: Union[ChecksumType, int, None] = None,
1256 ) -> None:
1257 """
1258 Kerberos Key object.
1259
1260 :param etype: the EncryptionType
1261 :param cksumtype: the ChecksumType
1262 :param key: the bytes containing the key bytes for this Key.
1263 """
1264 assert etype or cksumtype, "Provide an etype or a cksumtype !"
1265 assert key, "Provide a key !"
1266 if isinstance(etype, int):
1267 etype = EncryptionType(etype)
1268 if isinstance(cksumtype, int):
1269 cksumtype = ChecksumType(cksumtype)
1270 self.etype = etype
1271 if etype is not None:
1272 try:
1273 self.ep = _enctypes[etype]
1274 except ValueError:
1275 raise ValueError("UNKNOWN/UNIMPLEMENTED etype '%s'" % etype)
1276 if len(key) != self.ep.keysize:
1277 raise ValueError(
1278 "Wrong key length. Got %s. Expected %s"
1279 % (len(key), self.ep.keysize)
1280 )
1281 if cksumtype is None and self.ep.reqcksum in _checksums:
1282 cksumtype = self.ep.reqcksum
1283 self.cksumtype = cksumtype
1284 if cksumtype is not None:
1285 try:
1286 self.cp = _checksums[cksumtype]
1287 except ValueError:
1288 raise ValueError("UNKNOWN/UNIMPLEMENTED cksumtype '%s'" % cksumtype)
1289 if self.etype is None and issubclass(self.cp, _SimplifiedChecksum):
1290 self.etype = self.cp.enc.etype # type: ignore
1291 self.key = key
1292
1293 def __repr__(self):
1294 # type: () -> str
1295 if self.etype:
1296 name = self.etype.name
1297 elif self.cksumtype:
1298 name = self.cksumtype.name
1299 else:
1300 return "<Key UNKNOWN>"
1301 return "<Key %s%s>" % (
1302 name,
1303 " (%s octets)" % len(self.key),
1304 )
1305
1306 def encrypt(self, keyusage, plaintext, confounder=None, **kwargs):
1307 # type: (int, bytes, Optional[bytes], **Any) -> bytes
1308 """
1309 Encrypt data using the current Key.
1310
1311 :param keyusage: the key usage
1312 :param plaintext: the plain text to encrypt
1313 :param confounder: (optional) choose the confounder. Otherwise random.
1314 """
1315 return self.ep.encrypt(self, keyusage, bytes(plaintext), confounder, **kwargs)
1316
1317 def decrypt(self, keyusage, ciphertext, **kwargs):
1318 # type: (int, bytes, **Any) -> bytes
1319 """
1320 Decrypt data using the current Key.
1321
1322 :param keyusage: the key usage
1323 :param ciphertext: the encrypted text to decrypt
1324 """
1325 # Throw InvalidChecksum on checksum failure. Throw ValueError on
1326 # invalid key enctype or malformed ciphertext.
1327 return self.ep.decrypt(self, keyusage, ciphertext, **kwargs)
1328
1329 def prf(self, string):
1330 # type: (bytes) -> bytes
1331 return self.ep.prf(self, string)
1332
1333 def make_checksum(self, keyusage, text, cksumtype=None, **kwargs):
1334 # type: (int, bytes, Optional[int], **Any) -> bytes
1335 """
1336 Create a checksum using the current Key.
1337
1338 :param keyusage: the key usage
1339 :param text: the text to create a checksum from
1340 :param cksumtype: (optional) override the checksum type
1341 """
1342 if cksumtype is not None and cksumtype != self.cksumtype:
1343 # Clone key and use a different cksumtype
1344 return Key(
1345 cksumtype=cksumtype,
1346 key=self.key,
1347 ).make_checksum(keyusage=keyusage, text=text, **kwargs)
1348 if self.cksumtype is None:
1349 raise ValueError("cksumtype not specified !")
1350 return self.cp.checksum(self, keyusage, text, **kwargs)
1351
1352 def verify_checksum(self, keyusage, text, cksum, cksumtype=None):
1353 # type: (int, bytes, bytes, Optional[int]) -> None
1354 """
1355 Verify a checksum using the current Key.
1356
1357 :param keyusage: the key usage
1358 :param text: the text to verify
1359 :param cksum: the expected checksum
1360 :param cksumtype: (optional) override the checksum type
1361 """
1362 if cksumtype is not None and cksumtype != self.cksumtype:
1363 # Clone key and use a different cksumtype
1364 return Key(
1365 cksumtype=cksumtype,
1366 key=self.key,
1367 ).verify_checksum(keyusage=keyusage, text=text, cksum=cksum)
1368 # Throw InvalidChecksum exception on checksum failure. Throw
1369 # ValueError on invalid cksumtype, invalid key enctype, or
1370 # malformed checksum.
1371 if self.cksumtype is None:
1372 raise ValueError("cksumtype not specified !")
1373 self.cp.verify(self, keyusage, text, cksum)
1374
1375 @classmethod
1376 def random_to_key(cls, etype, seed):
1377 # type: (EncryptionType, bytes) -> Key
1378 """
1379 random-to-key per RFC3961
1380
1381 This is used to create a random Key from a seed.
1382 """
1383 try:
1384 ep = _enctypes[etype]
1385 except ValueError:
1386 raise ValueError("Unknown etype '%s'" % etype)
1387 if len(seed) != ep.seedsize:
1388 raise ValueError("Wrong crypto seed length")
1389 return ep.random_to_key(seed)
1390
1391 @classmethod
1392 def new_random_key(cls, etype):
1393 # type: (EncryptionType) -> Key
1394 """
1395 Generates a seed then calls random-to-key
1396 """
1397 try:
1398 ep = _enctypes[etype]
1399 except ValueError:
1400 raise ValueError("Unknown etype '%s'" % etype)
1401 return cls.random_to_key(etype, os.urandom(ep.seedsize))
1402
1403 @classmethod
1404 def string_to_key(cls, etype, string, salt, params=None):
1405 # type: (EncryptionType, bytes, bytes, Optional[bytes]) -> Key
1406 """
1407 string-to-key per RFC3961
1408
1409 This is typically used to create a Key object from a password + salt
1410 """
1411 try:
1412 ep = _enctypes[etype]
1413 except ValueError:
1414 raise ValueError("Unknown etype '%s'" % etype)
1415 return ep.string_to_key(string, salt, params)
1416
1417
1418############
1419# RFC 6113 #
1420############
1421
1422
1423def KRB_FX_CF2(key1, key2, pepper1, pepper2):
1424 # type: (Key, Key, bytes, bytes) -> Key
1425 """
1426 KRB-FX-CF2 RFC6113
1427 """
1428
1429 def prfplus(key, pepper):
1430 # type: (Key, bytes) -> bytes
1431 # Produce l bytes of output using the RFC 6113 PRF+ function.
1432 out = b""
1433 count = 1
1434 while len(out) < key.ep.seedsize:
1435 out += key.prf(chb(count) + pepper)
1436 count += 1
1437 return out[: key.ep.seedsize]
1438
1439 return Key(
1440 key1.etype,
1441 key=bytes(
1442 _xorbytes(
1443 bytearray(prfplus(key1, pepper1)), bytearray(prfplus(key2, pepper2))
1444 )
1445 ),
1446 )