Coverage for /root/GitHubProjects/impacket/impacket/krb5/crypto.py : 85%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Impacket - Collection of Python classes for working with network protocols.
2#
3# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved.
4#
5# This software is provided under a slightly modified version
6# of the Apache Software License. See the accompanying LICENSE file
7# for more information.
8#
9# Copyright and license note from crypto.py:
10#
11# Copyright (C) 2013 by the Massachusetts Institute of Technology.
12# All rights reserved.
13#
14# Redistribution and use in source and binary forms, with or without
15# modification, are permitted provided that the following conditions
16# are met:
17#
18# * Redistributions of source code must retain the above copyright
19# notice, this list of conditions and the following disclaimer.
20#
21# * Redistributions in binary form must reproduce the above copyright
22# notice, this list of conditions and the following disclaimer in
23# the documentation and/or other materials provided with the
24# distribution.
25#
26# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
31# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37# OF THE POSSIBILITY OF SUCH DAMAGE.
38#
39from binascii import unhexlify
40from functools import reduce
41from os import urandom
42# XXX current status:
43# * Done and tested
44# - AES encryption, checksum, string2key, prf
45# - cf2 (needed for FAST)
46# * Still to do:
47# - DES enctypes and cksumtypes
48# - RC4 exported enctype (if we need it for anything)
49# - Unkeyed checksums
50# - Special RC4, raw DES/DES3 operations for GSSAPI
51# * Difficult or low priority:
52# - Camellia not supported by PyCrypto
53# - Cipher state only needed for kcmd suite
54# - Nonstandard enctypes and cksumtypes like des-hmac-sha1
55from struct import pack, unpack
57from Cryptodome.Cipher import AES, DES3, ARC4, DES
58from Cryptodome.Hash import HMAC, MD4, MD5, SHA
59from Cryptodome.Protocol.KDF import PBKDF2
60from Cryptodome.Util.number import GCD as gcd
61from six import b, PY3, indexbytes
64def get_random_bytes(lenBytes):
65 # We don't really need super strong randomness here to use PyCrypto.Random
66 return urandom(lenBytes)
68class Enctype(object):
69 DES_CRC = 1
70 DES_MD4 = 2
71 DES_MD5 = 3
72 DES3 = 16
73 AES128 = 17
74 AES256 = 18
75 RC4 = 23
78class Cksumtype(object):
79 CRC32 = 1
80 MD4 = 2
81 MD4_DES = 3
82 MD5 = 7
83 MD5_DES = 8
84 SHA1 = 9
85 SHA1_DES3 = 12
86 SHA1_AES128 = 15
87 SHA1_AES256 = 16
88 HMAC_MD5 = -138
91class InvalidChecksum(ValueError):
92 pass
95def _zeropad(s, padsize):
96 # Return s padded with 0 bytes to a multiple of padsize.
97 padlen = (padsize - (len(s) % padsize)) % padsize
98 return s + b'\0'*padlen
101def _xorbytes(b1, b2):
102 # xor two strings together and return the resulting string.
103 assert len(b1) == len(b2)
104 return bytearray((x ^ y) for x, y in zip(b1, b2))
107def _mac_equal(mac1, mac2):
108 # Constant-time comparison function. (We can't use HMAC.verify
109 # since we use truncated macs.)
110 assert len(mac1) == len(mac2)
111 res = 0
112 for x, y in zip(mac1, mac2):
113 res |= x ^ y
114 return res == 0
117def _nfold(ba, nbytes):
118 # Convert bytearray to a string of length nbytes using the RFC 3961 nfold
119 # operation.
121 # Rotate the bytes in ba to the right by nbits bits.
122 def rotate_right(ba, nbits):
123 ba = bytearray(ba)
124 nbytes, remain = (nbits//8) % len(ba), nbits % 8
125 return bytearray((ba[i-nbytes] >> remain) | ((ba[i-nbytes-1] << (8-remain)) & 0xff) for i in range(len(ba)))
127 # Add equal-length strings together with end-around carry.
128 def add_ones_complement(str1, str2):
129 n = len(str1)
130 v = [a + b for a, b in zip(str1, str2)]
131 # Propagate carry bits to the left until there aren't any left.
132 while any(x & ~0xff for x in v):
133 v = [(v[i-n+1]>>8) + (v[i]&0xff) for i in range(n)]
134 return bytearray(x for x in v)
136 # Concatenate copies of str to produce the least common multiple
137 # of len(str) and nbytes, rotating each copy of str to the right
138 # by 13 bits times its list position. Decompose the concatenation
139 # into slices of length nbytes, and add them together as
140 # big-endian ones' complement integers.
141 slen = len(ba)
142 lcm = nbytes * slen // gcd(nbytes, slen)
143 bigstr = bytearray()
144 for i in range(lcm//slen):
145 bigstr += rotate_right(ba, 13 * i)
146 slices = (bigstr[p:p+nbytes] for p in range(0, lcm, nbytes))
147 return bytes(reduce(add_ones_complement, slices))
150def _is_weak_des_key(keybytes):
151 return keybytes in (b'\x01\x01\x01\x01\x01\x01\x01\x01',
152 b'\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE',
153 b'\x1F\x1F\x1F\x1F\x0E\x0E\x0E\x0E',
154 b'\xE0\xE0\xE0\xE0\xF1\xF1\xF1\xF1',
155 b'\x01\xFE\x01\xFE\x01\xFE\x01\xFE',
156 b'\xFE\x01\xFE\x01\xFE\x01\xFE\x01',
157 b'\x1F\xE0\x1F\xE0\x0E\xF1\x0E\xF1',
158 b'\xE0\x1F\xE0\x1F\xF1\x0E\xF1\x0E',
159 b'\x01\xE0\x01\xE0\x01\xF1\x01\xF1',
160 b'\xE0\x01\xE0\x01\xF1\x01\xF1\x01',
161 b'\x1F\xFE\x1F\xFE\x0E\xFE\x0E\xFE',
162 b'\xFE\x1F\xFE\x1F\xFE\x0E\xFE\x0E',
163 b'\x01\x1F\x01\x1F\x01\x0E\x01\x0E',
164 b'\x1F\x01\x1F\x01\x0E\x01\x0E\x01',
165 b'\xE0\xFE\xE0\xFE\xF1\xFE\xF1\xFE',
166 b'\xFE\xE0\xFE\xE0\xFE\xF1\xFE\xF1')
169class _EnctypeProfile(object):
170 # Base class for enctype profiles. Usable enctype classes must define:
171 # * enctype: enctype number
172 # * keysize: protocol size of key in bytes
173 # * seedsize: random_to_key input size in bytes
174 # * random_to_key (if the keyspace is not dense)
175 # * string_to_key
176 # * encrypt
177 # * decrypt
178 # * prf
180 @classmethod
181 def random_to_key(cls, seed):
182 if len(seed) != cls.seedsize: 182 ↛ 183line 182 didn't jump to line 183, because the condition on line 182 was never true
183 raise ValueError('Wrong seed length')
184 return Key(cls.enctype, seed)
187class _SimplifiedEnctype(_EnctypeProfile):
188 # Base class for enctypes using the RFC 3961 simplified profile.
189 # Defines the encrypt, decrypt, and prf methods. Subclasses must
190 # define:
191 # * blocksize: Underlying cipher block size in bytes
192 # * padsize: Underlying cipher padding multiple (1 or blocksize)
193 # * macsize: Size of integrity MAC in bytes
194 # * hashmod: PyCrypto hash module for underlying hash function
195 # * basic_encrypt, basic_decrypt: Underlying CBC/CTS cipher
197 @classmethod
198 def derive(cls, key, constant):
199 # RFC 3961 only says to n-fold the constant only if it is
200 # shorter than the cipher block size. But all Unix
201 # implementations n-fold constants if their length is larger
202 # than the block size as well, and n-folding when the length
203 # is equal to the block size is a no-op.
204 plaintext = _nfold(constant, cls.blocksize)
205 rndseed = b''
206 while len(rndseed) < cls.seedsize:
207 ciphertext = cls.basic_encrypt(key, plaintext)
208 rndseed += ciphertext
209 plaintext = ciphertext
210 return cls.random_to_key(rndseed[0:cls.seedsize])
212 @classmethod
213 def encrypt(cls, key, keyusage, plaintext, confounder):
214 ki = cls.derive(key, pack('>IB', keyusage, 0x55))
215 ke = cls.derive(key, pack('>IB', keyusage, 0xAA))
216 if confounder is None:
217 confounder = get_random_bytes(cls.blocksize)
218 basic_plaintext = confounder + _zeropad(plaintext, cls.padsize)
219 hmac = HMAC.new(ki.contents, basic_plaintext, cls.hashmod).digest()
220 return cls.basic_encrypt(ke, basic_plaintext) + hmac[:cls.macsize]
222 @classmethod
223 def decrypt(cls, key, keyusage, ciphertext):
224 ki = cls.derive(key, pack('>IB', keyusage, 0x55))
225 ke = cls.derive(key, pack('>IB', keyusage, 0xAA))
226 if len(ciphertext) < cls.blocksize + cls.macsize: 226 ↛ 227line 226 didn't jump to line 227, because the condition on line 226 was never true
227 raise ValueError('ciphertext too short')
228 basic_ctext, mac = bytearray(ciphertext[:-cls.macsize]), bytearray(ciphertext[-cls.macsize:])
229 if len(basic_ctext) % cls.padsize != 0: 229 ↛ 230line 229 didn't jump to line 230, because the condition on line 229 was never true
230 raise ValueError('ciphertext does not meet padding requirement')
231 basic_plaintext = cls.basic_decrypt(ke, bytes(basic_ctext))
232 hmac = bytearray(HMAC.new(ki.contents, basic_plaintext, cls.hashmod).digest())
233 expmac = hmac[:cls.macsize]
234 if not _mac_equal(mac, expmac): 234 ↛ 235line 234 didn't jump to line 235, because the condition on line 234 was never true
235 raise InvalidChecksum('ciphertext integrity failure')
236 # Discard the confounder.
237 return bytes(basic_plaintext[cls.blocksize:])
239 @classmethod
240 def prf(cls, key, string):
241 # Hash the input. RFC 3961 says to truncate to the padding
242 # size, but implementations truncate to the block size.
243 hashval = cls.hashmod.new(string).digest()
244 truncated = hashval[:-(len(hashval) % cls.blocksize)]
245 # Encrypt the hash with a derived key.
246 kp = cls.derive(key, b'prf')
247 return cls.basic_encrypt(kp, truncated)
249class _DESCBC(_SimplifiedEnctype):
250 enctype = Enctype.DES_MD5
251 keysize = 8
252 seedsize = 8
253 blocksize = 8
254 padsize = 8
255 macsize = 16
256 hashmod = MD5
258 @classmethod
259 def encrypt(cls, key, keyusage, plaintext, confounder):
260 if confounder is None:
261 confounder = get_random_bytes(cls.blocksize)
262 basic_plaintext = confounder + b'\x00'*cls.macsize + _zeropad(plaintext, cls.padsize)
263 checksum = cls.hashmod.new(basic_plaintext).digest()
264 basic_plaintext = basic_plaintext[:len(confounder)] + checksum + basic_plaintext[len(confounder)+len(checksum):]
265 return cls.basic_encrypt(key, basic_plaintext)
268 @classmethod
269 def decrypt(cls, key, keyusage, ciphertext):
270 if len(ciphertext) < cls.blocksize + cls.macsize:
271 raise ValueError('ciphertext too short')
273 complex_plaintext = cls.basic_decrypt(key, ciphertext)
274 cofounder = complex_plaintext[:cls.padsize]
275 mac = complex_plaintext[cls.padsize:cls.padsize+cls.macsize]
276 message = complex_plaintext[cls.padsize+cls.macsize:]
278 expmac = cls.hashmod.new(cofounder+b'\x00'*cls.macsize+message).digest()
279 if not _mac_equal(mac, expmac):
280 raise InvalidChecksum('ciphertext integrity failure')
281 return bytes(message)
283 @classmethod
284 def mit_des_string_to_key(cls,string,salt):
286 def fixparity(deskey):
287 temp = b''
288 for i in range(len(deskey)):
289 t = (bin(indexbytes(deskey,i))[2:]).rjust(8,'0')
290 if t[:7].count('1') %2 == 0:
291 temp+= b(chr(int(t[:7]+'1',2)))
292 else:
293 temp+= b(chr(int(t[:7]+'0',2)))
294 return temp
296 def addparity(l1):
297 temp = list()
298 for byte in l1:
299 if (bin(byte).count('1') % 2) == 0:
300 byte = (byte << 1)|0b00000001
301 else:
302 byte = (byte << 1)&0b11111110
303 temp.append(byte)
304 return temp
306 def XOR(l1,l2):
307 temp = list()
308 for b1,b2 in zip(l1,l2):
309 temp.append((b1^b2)&0b01111111)
311 return temp
313 odd = True
314 tempstring = [0,0,0,0,0,0,0,0]
315 s = _zeropad(string + salt, cls.padsize)
317 for block in [s[i:i+8] for i in range(0, len(s), 8)]:
318 temp56 = list()
319 #removeMSBits
320 for byte in block:
321 if PY3: 321 ↛ 324line 321 didn't jump to line 324, because the condition on line 321 was never false
322 temp56.append(byte&0b01111111)
323 else:
324 temp56.append(ord(byte)&0b01111111)
326 #reverse
327 if odd is False:
328 bintemp = b''
329 for byte in temp56:
330 bintemp += b(bin(byte)[2:].rjust(7,'0'))
331 bintemp = bintemp[::-1]
333 temp56 = list()
334 for bits7 in [bintemp[i:i+7] for i in range(0, len(bintemp), 7)]:
335 temp56.append(int(bits7,2))
337 odd = not odd
339 tempstring = XOR(tempstring,temp56)
341 tempkey = ''.join(chr(byte) for byte in addparity(tempstring))
342 if _is_weak_des_key(tempkey): 342 ↛ 343line 342 didn't jump to line 343, because the condition on line 342 was never true
343 tempkey[7] = chr(ord(tempkey[7]) ^ 0xF0)
345 cipher = DES.new(b(tempkey), DES.MODE_CBC, b(tempkey))
346 checksumkey = cipher.encrypt(s)[-8:]
347 checksumkey = fixparity(checksumkey)
348 if _is_weak_des_key(checksumkey): 348 ↛ 349line 348 didn't jump to line 349, because the condition on line 348 was never true
349 checksumkey[7] = chr(ord(checksumkey[7]) ^ 0xF0)
351 return Key(cls.enctype, checksumkey)
353 @classmethod
354 def basic_encrypt(cls, key, plaintext):
355 assert len(plaintext) % 8 == 0
356 des = DES.new(key.contents, DES.MODE_CBC, b'\0' * 8)
357 return des.encrypt(bytes(plaintext))
359 @classmethod
360 def basic_decrypt(cls, key, ciphertext):
361 assert len(ciphertext) % 8 == 0
362 des = DES.new(key.contents, DES.MODE_CBC, b'\0' * 8)
363 return des.decrypt(bytes(ciphertext))
365 @classmethod
366 def string_to_key(cls, string, salt, params):
367 if params is not None and params != b'': 367 ↛ 368line 367 didn't jump to line 368, because the condition on line 367 was never true
368 raise ValueError('Invalid DES string-to-key parameters')
369 key = cls.mit_des_string_to_key(string, salt)
370 return key
374class _DES3CBC(_SimplifiedEnctype):
375 enctype = Enctype.DES3
376 keysize = 24
377 seedsize = 21
378 blocksize = 8
379 padsize = 8
380 macsize = 20
381 hashmod = SHA
383 @classmethod
384 def random_to_key(cls, seed):
385 # XXX Maybe reframe as _DESEnctype.random_to_key and use that
386 # way from DES3 random-to-key when DES is implemented, since
387 # MIT does this instead of the RFC 3961 random-to-key.
388 def expand(seed):
389 def parity(b):
390 # Return b with the low-order bit set to yield odd parity.
391 b &= ~1
392 return b if bin(b & ~1).count('1') % 2 else b | 1
393 assert len(seed) == 7
394 firstbytes = [parity(b & ~1) for b in seed]
395 lastbyte = parity(sum((seed[i]&1) << i+1 for i in range(7)))
396 keybytes= bytearray(firstbytes + [lastbyte])
397 if _is_weak_des_key(keybytes): 397 ↛ 398line 397 didn't jump to line 398, because the condition on line 397 was never true
398 keybytes[7] = keybytes[7] ^ 0xF0
399 return bytes(keybytes)
401 seed = bytearray(seed)
402 if len(seed) != 21: 402 ↛ 403line 402 didn't jump to line 403, because the condition on line 402 was never true
403 raise ValueError('Wrong seed length')
404 k1, k2, k3 = expand(seed[:7]), expand(seed[7:14]), expand(seed[14:])
405 return Key(cls.enctype, k1 + k2 + k3)
407 @classmethod
408 def string_to_key(cls, string, salt, params):
409 if params is not None and params != b'': 409 ↛ 410line 409 didn't jump to line 410, because the condition on line 409 was never true
410 raise ValueError('Invalid DES3 string-to-key parameters')
411 k = cls.random_to_key(_nfold(string + salt, 21))
412 return cls.derive(k, b'kerberos')
414 @classmethod
415 def basic_encrypt(cls, key, plaintext):
416 assert len(plaintext) % 8 == 0
417 des3 = DES3.new(key.contents, AES.MODE_CBC, b'\0' * 8)
418 return des3.encrypt(bytes(plaintext))
420 @classmethod
421 def basic_decrypt(cls, key, ciphertext):
422 assert len(ciphertext) % 8 == 0
423 des3 = DES3.new(key.contents, AES.MODE_CBC, b'\0' * 8)
424 return des3.decrypt(bytes(ciphertext))
427class _AESEnctype(_SimplifiedEnctype):
428 # Base class for aes128-cts and aes256-cts.
429 blocksize = 16
430 padsize = 1
431 macsize = 12
432 hashmod = SHA
434 @classmethod
435 def string_to_key(cls, string, salt, params):
436 (iterations,) = unpack('>L', params or b'\x00\x00\x10\x00')
437 prf = lambda p, s: HMAC.new(p, s, SHA).digest()
438 seed = PBKDF2(string, salt, cls.seedsize, iterations, prf)
439 tkey = cls.random_to_key(seed)
440 return cls.derive(tkey, b'kerberos')
442 @classmethod
443 def basic_encrypt(cls, key, plaintext):
444 assert len(plaintext) >= 16
445 aes = AES.new(key.contents, AES.MODE_CBC, b'\0' * 16)
446 ctext = aes.encrypt(_zeropad(bytes(plaintext), 16))
447 if len(plaintext) > 16:
448 # Swap the last two ciphertext blocks and truncate the
449 # final block to match the plaintext length.
450 lastlen = len(plaintext) % 16 or 16
451 ctext = ctext[:-32] + ctext[-16:] + ctext[-32:-16][:lastlen]
452 return ctext
454 @classmethod
455 def basic_decrypt(cls, key, ciphertext):
456 assert len(ciphertext) >= 16
457 aes = AES.new(key.contents, AES.MODE_ECB)
458 if len(ciphertext) == 16: 458 ↛ 459line 458 didn't jump to line 459, because the condition on line 458 was never true
459 return aes.decrypt(ciphertext)
460 # Split the ciphertext into blocks. The last block may be partial.
461 cblocks = [bytearray(ciphertext[p:p+16]) for p in range(0, len(ciphertext), 16)]
462 lastlen = len(cblocks[-1])
463 # CBC-decrypt all but the last two blocks.
464 prev_cblock = bytearray(16)
465 plaintext = b''
466 for bb in cblocks[:-2]:
467 plaintext += _xorbytes(bytearray(aes.decrypt(bytes(bb))), prev_cblock)
468 prev_cblock = bb
469 # Decrypt the second-to-last cipher block. The left side of
470 # the decrypted block will be the final block of plaintext
471 # xor'd with the final partial cipher block; the right side
472 # will be the omitted bytes of ciphertext from the final
473 # block.
474 bb = bytearray(aes.decrypt(bytes(cblocks[-2])))
475 lastplaintext =_xorbytes(bb[:lastlen], cblocks[-1])
476 omitted = bb[lastlen:]
477 # Decrypt the final cipher block plus the omitted bytes to get
478 # the second-to-last plaintext block.
479 plaintext += _xorbytes(bytearray(aes.decrypt(bytes(cblocks[-1]) + bytes(omitted))), prev_cblock)
480 return plaintext + lastplaintext
483class _AES128CTS(_AESEnctype):
484 enctype = Enctype.AES128
485 keysize = 16
486 seedsize = 16
489class _AES256CTS(_AESEnctype):
490 enctype = Enctype.AES256
491 keysize = 32
492 seedsize = 32
495class _RC4(_EnctypeProfile):
496 enctype = Enctype.RC4
497 keysize = 16
498 seedsize = 16
500 @staticmethod
501 def usage_str(keyusage):
502 # Return a four-byte string for an RFC 3961 keyusage, using
503 # the RFC 4757 rules. Per the errata, do not map 9 to 8.
504 table = {3: 8, 23: 13}
505 msusage = table[keyusage] if keyusage in table else keyusage
506 return pack('<I', msusage)
508 @classmethod
509 def string_to_key(cls, string, salt, params):
510 utf16string = string.encode('UTF-16LE')
511 return Key(cls.enctype, MD4.new(utf16string).digest())
513 @classmethod
514 def encrypt(cls, key, keyusage, plaintext, confounder):
515 if confounder is None:
516 confounder = get_random_bytes(8)
517 ki = HMAC.new(key.contents, cls.usage_str(keyusage), MD5).digest()
518 cksum = HMAC.new(ki, confounder + plaintext, MD5).digest()
519 ke = HMAC.new(ki, cksum, MD5).digest()
520 return cksum + ARC4.new(ke).encrypt(bytes(confounder + plaintext))
522 @classmethod
523 def decrypt(cls, key, keyusage, ciphertext):
524 if len(ciphertext) < 24: 524 ↛ 525line 524 didn't jump to line 525, because the condition on line 524 was never true
525 raise ValueError('ciphertext too short')
526 cksum, basic_ctext = bytearray(ciphertext[:16]), bytearray(ciphertext[16:])
527 ki = HMAC.new(key.contents, cls.usage_str(keyusage), MD5).digest()
528 ke = HMAC.new(ki, cksum, MD5).digest()
529 basic_plaintext = bytearray(ARC4.new(ke).decrypt(bytes(basic_ctext)))
530 exp_cksum = bytearray(HMAC.new(ki, basic_plaintext, MD5).digest())
531 ok = _mac_equal(cksum, exp_cksum)
532 if not ok and keyusage == 9: 532 ↛ 534line 532 didn't jump to line 534, because the condition on line 532 was never true
533 # Try again with usage 8, due to RFC 4757 errata.
534 ki = HMAC.new(key.contents, pack('<I', 8), MD5).digest()
535 exp_cksum = HMAC.new(ki, basic_plaintext, MD5).digest()
536 ok = _mac_equal(cksum, exp_cksum)
537 if not ok: 537 ↛ 538line 537 didn't jump to line 538, because the condition on line 537 was never true
538 raise InvalidChecksum('ciphertext integrity failure')
539 # Discard the confounder.
540 return bytes(basic_plaintext[8:])
542 @classmethod
543 def prf(cls, key, string):
544 return HMAC.new(key.contents, bytes(string), SHA).digest()
547class _ChecksumProfile(object):
548 # Base class for checksum profiles. Usable checksum classes must
549 # define:
550 # * checksum
551 # * verify (if verification is not just checksum-and-compare)
552 @classmethod
553 def verify(cls, key, keyusage, text, cksum):
554 expected = cls.checksum(key, keyusage, text)
555 if not _mac_equal(bytearray(cksum), bytearray(expected)): 555 ↛ 556line 555 didn't jump to line 556, because the condition on line 555 was never true
556 raise InvalidChecksum('checksum verification failure')
559class _SimplifiedChecksum(_ChecksumProfile):
560 # Base class for checksums using the RFC 3961 simplified profile.
561 # Defines the checksum and verify methods. Subclasses must
562 # define:
563 # * macsize: Size of checksum in bytes
564 # * enc: Profile of associated enctype
566 @classmethod
567 def checksum(cls, key, keyusage, text):
568 kc = cls.enc.derive(key, pack('>IB', keyusage, 0x99))
569 hmac = HMAC.new(kc.contents, text, cls.enc.hashmod).digest()
570 return hmac[:cls.macsize]
572 @classmethod
573 def verify(cls, key, keyusage, text, cksum):
574 if key.enctype != cls.enc.enctype: 574 ↛ 575line 574 didn't jump to line 575, because the condition on line 574 was never true
575 raise ValueError('Wrong key type for checksum')
576 super(_SimplifiedChecksum, cls).verify(key, keyusage, text, cksum)
579class _SHA1AES128(_SimplifiedChecksum):
580 macsize = 12
581 enc = _AES128CTS
584class _SHA1AES256(_SimplifiedChecksum):
585 macsize = 12
586 enc = _AES256CTS
589class _SHA1DES3(_SimplifiedChecksum):
590 macsize = 20
591 enc = _DES3CBC
594class _HMACMD5(_ChecksumProfile):
595 @classmethod
596 def checksum(cls, key, keyusage, text):
597 ksign = HMAC.new(key.contents, b'signaturekey\0', MD5).digest()
598 md5hash = MD5.new(_RC4.usage_str(keyusage) + text).digest()
599 return HMAC.new(ksign, md5hash, MD5).digest()
601 @classmethod
602 def verify(cls, key, keyusage, text, cksum):
603 if key.enctype != Enctype.RC4: 603 ↛ 604line 603 didn't jump to line 604, because the condition on line 603 was never true
604 raise ValueError('Wrong key type for checksum')
605 super(_HMACMD5, cls).verify(key, keyusage, text, cksum)
608_enctype_table = {
609 Enctype.DES_MD5: _DESCBC,
610 Enctype.DES3: _DES3CBC,
611 Enctype.AES128: _AES128CTS,
612 Enctype.AES256: _AES256CTS,
613 Enctype.RC4: _RC4
614}
617_checksum_table = {
618 Cksumtype.SHA1_DES3: _SHA1DES3,
619 Cksumtype.SHA1_AES128: _SHA1AES128,
620 Cksumtype.SHA1_AES256: _SHA1AES256,
621 Cksumtype.HMAC_MD5: _HMACMD5,
622 0xffffff76: _HMACMD5
623}
626def _get_enctype_profile(enctype):
627 if enctype not in _enctype_table: 627 ↛ 628line 627 didn't jump to line 628, because the condition on line 627 was never true
628 raise ValueError('Invalid enctype %d' % enctype)
629 return _enctype_table[enctype]
632def _get_checksum_profile(cksumtype):
633 if cksumtype not in _checksum_table: 633 ↛ 634line 633 didn't jump to line 634, because the condition on line 633 was never true
634 raise ValueError('Invalid cksumtype %d' % cksumtype)
635 return _checksum_table[cksumtype]
638class Key(object):
639 def __init__(self, enctype, contents):
640 e = _get_enctype_profile(enctype)
641 if len(contents) != e.keysize: 641 ↛ 642line 641 didn't jump to line 642, because the condition on line 641 was never true
642 raise ValueError('Wrong key length')
643 self.enctype = enctype
644 self.contents = contents
647def random_to_key(enctype, seed):
648 e = _get_enctype_profile(enctype)
649 if len(seed) != e.seedsize:
650 raise ValueError('Wrong crypto seed length')
651 return e.random_to_key(seed)
654def string_to_key(enctype, string, salt, params=None):
655 e = _get_enctype_profile(enctype)
656 return e.string_to_key(string, salt, params)
659def encrypt(key, keyusage, plaintext, confounder=None):
660 e = _get_enctype_profile(key.enctype)
661 return e.encrypt(key, keyusage, bytes(plaintext), bytes(confounder))
664def decrypt(key, keyusage, ciphertext):
665 # Throw InvalidChecksum on checksum failure. Throw ValueError on
666 # invalid key enctype or malformed ciphertext.
667 e = _get_enctype_profile(key.enctype)
668 return e.decrypt(key, keyusage, ciphertext)
671def prf(key, string):
672 e = _get_enctype_profile(key.enctype)
673 return e.prf(key, string)
676def make_checksum(cksumtype, key, keyusage, text):
677 c = _get_checksum_profile(cksumtype)
678 return c.checksum(key, keyusage, text)
681def verify_checksum(cksumtype, key, keyusage, text, cksum):
682 # Throw InvalidChecksum exception on checksum failure. Throw
683 # ValueError on invalid cksumtype, invalid key enctype, or
684 # malformed checksum.
685 c = _get_checksum_profile(cksumtype)
686 c.verify(key, keyusage, text, cksum)
689def cf2(enctype, key1, key2, pepper1, pepper2):
690 # Combine two keys and two pepper strings to produce a result key
691 # of type enctype, using the RFC 6113 KRB-FX-CF2 function.
692 def prfplus(key, pepper, l):
693 # Produce l bytes of output using the RFC 6113 PRF+ function.
694 out = b''
695 count = 1
696 while len(out) < l:
697 out += prf(key, b(chr(count)) + pepper)
698 count += 1
699 return out[:l]
701 e = _get_enctype_profile(enctype)
702 return e.random_to_key(_xorbytes(bytearray(prfplus(key1, pepper1, e.seedsize)),
703 bytearray(prfplus(key2, pepper2, e.seedsize))))