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
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
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
8"""
9Implementation of cryptographic functions for Kerberos 5
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
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"""
22# TODO: support cipher states...
24__all__ = [
25 "ChecksumType",
26 "EncryptionType",
27 "InvalidChecksum",
28 "KRB_FX_CF2",
29 "Key",
30 "SP800108_KDFCTR",
31 "_rfc1964pad",
32]
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.
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)
54# Typing
55from typing import (
56 Any,
57 Callable,
58 List,
59 Optional,
60 Type,
61 Union,
62)
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).
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)
82# For everything else, use cryptography.
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
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.")
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)
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)
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]
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]
135# https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-1
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
154# https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-2
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
177class InvalidChecksum(ValueError):
178 pass
181#########
182# Utils #
183#########
186# https://www.gnu.org/software/shishi/ides.pdf - APPENDIX B
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 """
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)
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)
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)
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)
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)
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))
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))
260# https://doi.org/10.6028/NBS.FIPS.74 sect 3.6
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)
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
362############
363# RFC 3961 #
364############
367# RFC3961 sect 3
370class _EncryptionAlgorithmProfile(abc.ABCMeta):
371 """
372 Base class for etype profiles.
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 """
387 etype = None # type: EncryptionType
388 keysize = None # type: int
389 seedsize = None # type: int
390 reqcksum = None # type: ChecksumType
392 @classmethod
393 @abc.abstractmethod
394 def derive(cls, key, constant):
395 # type: (Key, bytes) -> bytes
396 pass
398 @classmethod
399 @abc.abstractmethod
400 def encrypt(cls, key, keyusage, plaintext, confounder):
401 # type: (Key, int, bytes, Optional[bytes]) -> bytes
402 pass
404 @classmethod
405 @abc.abstractmethod
406 def decrypt(cls, key, keyusage, ciphertext):
407 # type: (Key, int, bytes) -> bytes
408 pass
410 @classmethod
411 @abc.abstractmethod
412 def prf(cls, key, string):
413 # type: (Key, bytes) -> bytes
414 pass
416 @classmethod
417 @abc.abstractmethod
418 def string_to_key(cls, string, salt, params):
419 # type: (bytes, bytes, Optional[bytes]) -> Key
420 pass
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)
430# RFC3961 sect 4
433class _ChecksumProfile(object):
434 """
435 Base class for checksum profiles.
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 """
443 macsize = None # type: int
445 @classmethod
446 @abc.abstractmethod
447 def checksum(cls, key, keyusage, text):
448 # type: (Key, int, bytes) -> bytes
449 pass
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")
459# RFC3961 sect 5.3
462class _SimplifiedEncryptionProfile(_EncryptionAlgorithmProfile):
463 """
464 Base class for etypes using the RFC 3961 simplified profile.
465 Defines the encrypt, decrypt, and prf methods.
467 Subclasses must define:
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 """
476 blocksize = None # type: int
477 padsize = None # type: int
478 macsize = None # type: int
479 hashmod = None # type: Any
481 # Used in RFC 8009. This is not a simplified profile per se but
482 # is still pretty close.
483 rfc8009 = False
485 @classmethod
486 @abc.abstractmethod
487 def basic_encrypt(cls, key, plaintext):
488 # type: (bytes, bytes) -> bytes
489 pass
491 @classmethod
492 @abc.abstractmethod
493 def basic_decrypt(cls, key, ciphertext):
494 # type: (bytes, bytes) -> bytes
495 pass
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
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.
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]
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 :])
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)
618# RFC3961 sect 5.4
621class _SimplifiedChecksum(_ChecksumProfile):
622 """
623 Base class for checksums using the RFC 3961 simplified profile.
624 Defines the checksum and verify methods.
626 Subclasses must define:
627 :attr enc: Profile of associated etype
628 """
630 enc = None # type: Type[_SimplifiedEncryptionProfile]
632 # Used in RFC 8009. This is not a simplified profile per se but
633 # is still pretty close.
634 rfc8009 = False
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]
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)
658# RFC3961 sect 6.1
661class _CRC32(_ChecksumProfile):
662 macsize = 4
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.
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")
679# RFC3961 sect 6.2
682class _DESCBC(_SimplifiedEncryptionProfile):
683 keysize = 8
684 seedsize = 8
685 blocksize = 8
686 padsize = 8
687 macsize = 16
688 hashmod = Hash_MD5
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)
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")
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 :]
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)
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
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
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)
753 return temp
755 odd = True
756 tempstring = [0, 0, 0, 0, 0, 0, 0, 0]
757 s = _zeropad(string + salt, cls.padsize)
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)
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]
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))
776 odd = not odd
777 tempstring = XOR(tempstring, temp56)
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
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
790 return Key(cls.etype, key=bytes(chekcsumkey))
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))
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))
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
815# RFC3961 sect 6.2.1
818class _DESMD5(_DESCBC):
819 etype = EncryptionType.DES_CBC_MD5
820 hashmod = Hash_MD5
821 reqcksum = ChecksumType.RSA_MD5_DES
824# RFC3961 sect 6.2.2
827class _DESMD4(_DESCBC):
828 etype = EncryptionType.DES_CBC_MD4
829 hashmod = Hash_MD4
830 reqcksum = ChecksumType.RSA_MD4_DES
833# RFC3961 sect 6.3
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
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
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)
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)
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 )
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))
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))
903class _SHA1DES3(_SimplifiedChecksum):
904 macsize = 20
905 enc = _DES3CBC
908############
909# RFC 3962 #
910############
913# RFC3962 sect 6
916class _AESEncryptionType_SHA1_96(_SimplifiedEncryptionProfile, abc.ABCMeta):
917 blocksize = 16
918 padsize = 1
919 macsize = 12
920 hashmod = Hash_SHA
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 )
938 # basic_encrypt and basic_decrypt implement AES in CBC-CS3 mode
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
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
987# RFC3962 sect 7
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
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
1004class _SHA1_96_AES128(_SimplifiedChecksum):
1005 macsize = 12
1006 enc = _AES128CTS_SHA1_96
1009class _SHA1_96_AES256(_SimplifiedChecksum):
1010 macsize = 12
1011 enc = _AES256CTS_SHA1_96
1014############
1015# RFC 4757 #
1016############
1018# RFC4757 sect 4
1021class _HMACMD5(_ChecksumProfile):
1022 macsize = 16
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)
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)
1039# RFC4757 sect 5
1042class _RC4(_EncryptionAlgorithmProfile):
1043 etype = EncryptionType.RC4_HMAC
1044 keysize = 16
1045 seedsize = 16
1046 reqcksum = ChecksumType.HMAC_MD5
1047 export = False
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)
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))
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))
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:])
1111 @classmethod
1112 def prf(cls, key, string):
1113 # type: (Key, bytes) -> bytes
1114 return Hmac_SHA(key.key).digest(string)
1117class _RC4_EXPORT(_RC4):
1118 etype = EncryptionType.RC4_HMAC_EXP
1119 export = True
1122############
1123# RFC 8009 #
1124############
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
1132 # Turn on RFC 8009 mode
1133 rfc8009 = True
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 )
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 )
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)
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
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
1198class _SHA256_128_AES128(_SimplifiedChecksum):
1199 macsize = 16
1200 enc = _AES128CTS_SHA256_128
1201 rfc8009 = True
1204class _SHA384_182_AES256(_SimplifiedChecksum):
1205 macsize = 24
1206 enc = _AES256CTS_SHA384_192
1207 rfc8009 = True
1210##############
1211# Key object #
1212##############
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}
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}
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.
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
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 )
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.
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)
1318 def decrypt(self, keyusage, ciphertext, **kwargs):
1319 # type: (int, bytes, **Any) -> bytes
1320 """
1321 Decrypt data using the current Key.
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)
1330 def prf(self, string):
1331 # type: (bytes) -> bytes
1332 return self.ep.prf(self, string)
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.
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)
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.
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)
1376 @classmethod
1377 def random_to_key(cls, etype, seed):
1378 # type: (EncryptionType, bytes) -> Key
1379 """
1380 random-to-key per RFC3961
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)
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))
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
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)
1419############
1420# RFC 6113 #
1421############
1424def KRB_FX_CF2(key1, key2, pepper1, pepper2):
1425 # type: (Key, Key, bytes, bytes) -> Key
1426 """
1427 KRB-FX-CF2 RFC6113
1428 """
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]
1440 return Key(
1441 key1.etype,
1442 key=bytes(
1443 _xorbytes(
1444 bytearray(prfplus(key1, pepper1)), bytearray(prfplus(key2, pepper2))
1445 )
1446 ),
1447 )