1# Copyright: (c) 2020, Jordan Borean (@jborean93) <jborean93@gmail.com>
2# MIT License (see LICENSE or https://opensource.org/licenses/MIT)
3
4"""Kerberos Messages
5
6Various Kerberos message structures that is used by 'python -m spnego' to unpack raw ASN.1 values and print out a
7pretty structure for end users. This code is not used in the actual spnego authentication processes and is just used
8for debugging purposes.
9"""
10
11import base64
12import collections
13import datetime
14import enum
15import struct
16import typing
17
18from spnego._asn1 import (
19    ASN1Value,
20    TagClass,
21    extract_asn1_tlv,
22    get_sequence_value,
23    unpack_asn1,
24    unpack_asn1_bit_string,
25    unpack_asn1_general_string,
26    unpack_asn1_generalized_time,
27    unpack_asn1_integer,
28    unpack_asn1_octet_string,
29    unpack_asn1_sequence,
30    unpack_asn1_tagged_sequence,
31)
32from spnego._text import to_text
33
34
35def _enum_labels(
36    value: typing.Union[int, str, enum.Enum],
37    enum_type: typing.Optional[typing.Type] = None,
38) -> typing.Dict[int, str]:
39    """ Gets the human friendly labels of a known enum and what value they map to. """
40    def get_labels(v):
41        return getattr(v, 'native_labels', lambda: {})()
42
43    return get_labels(enum_type) if enum_type else get_labels(value)
44
45
46def parse_enum(
47    value: typing.Union[int, str, enum.Enum],
48    enum_type: typing.Optional[typing.Type] = None,
49) -> str:
50    """ Parses an IntEnum into a human representative object of that enum. """
51    enum_name = 'UNKNOWN'
52
53    labels = _enum_labels(value, enum_type)
54    value = int(value) if isinstance(value, int) else value
55
56    for v, name in labels.items():
57        if value == v:
58            enum_name = name
59            break
60
61    return "%s (%s)" % (enum_name, value)
62
63
64def parse_flags(
65    value: typing.Union[int, enum.IntFlag],
66    enum_type: typing.Optional[typing.Type] = None,
67) -> typing.Dict[str, typing.Any]:
68    """ Parses an IntFlag into each flag value that is set. """
69    raw_value = int(value)
70    flags = []
71
72    labels = _enum_labels(value, enum_type)
73    value = int(value)
74
75    for v, name in labels.items():
76        if value & v == v:
77            value &= ~v
78            flags.append("%s (%d)" % (name, v))
79
80    if value != 0:
81        flags.append('UNKNOWN (%d)' % value)
82
83    return {
84        'raw': raw_value,
85        'flags': flags,
86    }
87
88
89def parse_kerberos_token(
90    token: "KerberosV5Msg",
91    secret: typing.Optional[str] = None,
92    encoding: typing.Optional[str] = None,
93) -> typing.Dict[str, typing.Any]:
94    """ Parses a KerberosV5Msg object to a dict. """
95    if not encoding:
96        encoding = 'utf-8'
97
98    def parse_default(value: typing.Any) -> typing.Any:
99        return value
100
101    def parse_datetime(value: datetime.datetime) -> str:
102        return value.isoformat()
103
104    def parse_text(value: bytes) -> str:
105        return to_text(value, encoding=encoding, errors='replace')
106
107    def parse_bytes(value: bytes) -> str:
108        return base64.b16encode(value).decode()
109
110    def parse_principal_name(value: PrincipalName) -> typing.Dict[str, typing.Any]:
111        return {
112            'name-type': parse_enum(value.name_type),
113            'name-string': [parse_text(v) for v in value.value],
114        }
115
116    def parse_host_address(value: HostAddress) -> typing.Dict[str, typing.Any]:
117        return {
118            'addr-type': parse_enum(value.addr_type),
119            'address': parse_text(value.value),
120        }
121
122    def parse_token(value: typing.Any) -> typing.Dict[str, typing.Any]:
123        return parse_kerberos_token(value, secret, encoding)
124
125    if isinstance(token, bytes):
126        return parse_bytes(token)
127
128    msg = {}
129    for name, attr_name, attr_type in token.PARSE_MAP:
130        attr_value = getattr(token, attr_name)
131
132        parse_args = []
133        if isinstance(attr_type, tuple):
134            parse_args.append(attr_type[1])
135            attr_type = attr_type[0]
136
137        parse_func = {
138            ParseType.default: parse_default,
139            ParseType.enum: parse_enum,
140            ParseType.flags: parse_flags,
141            ParseType.datetime: parse_datetime,
142            ParseType.text: parse_text,
143            ParseType.bytes: parse_bytes,
144            ParseType.principal_name: parse_principal_name,
145            ParseType.host_address: parse_host_address,
146            ParseType.token: parse_token,
147        }[attr_type]
148
149        if attr_value is None:
150            parsed_value = None
151
152        elif isinstance(attr_value, list):
153            parsed_value = [parse_func(v, *parse_args) if v is not None else None for v in attr_value]
154
155        else:
156            parsed_value = parse_func(attr_value, *parse_args)
157
158        msg[name] = parsed_value
159
160    return msg
161
162
163def unpack_hostname(value: ASN1Value) -> "HostAddress":
164    """ Unpacks an ASN.1 value to a HostAddress. """
165    s = unpack_asn1_tagged_sequence(value)
166
167    name_type = KerberosHostAddressType(get_sequence_value(s, 0, 'HostAddress', 'addr-type', unpack_asn1_integer))
168    name = get_sequence_value(s, 1, 'HostAddress', 'address', unpack_asn1_octet_string)
169
170    return HostAddress(name_type, name)
171
172
173def unpack_principal_name(value: ASN1Value) -> "PrincipalName":
174    """ Unpacks an ASN.1 value to a PrincipalName. """
175    s = unpack_asn1_tagged_sequence(value)
176
177    name_type = KerberosPrincipalNameType(get_sequence_value(s, 0, 'PrincipalName', 'name-type', unpack_asn1_integer))
178    name = [unpack_asn1_general_string(n) for n in
179            get_sequence_value(s, 1, 'PrincipalName', 'name-string', unpack_asn1_sequence)]
180
181    return PrincipalName(name_type, name)
182
183
184HostAddress = collections.namedtuple('HostAddress', ['addr_type', 'value'])
185"""Kerberos HostAddress and HostAddresses
186
187A Kerberos Host Address ASN.1 definition is defined in `RFC 4120 5.2.5`_::
188
189    HostAddress     ::= SEQUENCE  {
190        addr-type       [0] Int32,
191        address         [1] OCTET STRING
192    }
193
194Attributes:
195    addr_type (KerberosHostAddressType): The type of address that was encoded.
196    value (bytes): The address as a byte string.
197
198.. _RFC 4120 5.2.5:
199    https://www.rfc-editor.org/rfc/rfc4120#section-5.2.5
200"""
201
202PrincipalName = collections.namedtuple('PrincipalName', ['name_type', 'value'])
203"""Kerberos Realm and PrincipalName
204
205A Kerberos Principal Name ASN.1 definition is defined in `RFC 4120 5.2.2`_::
206
207    PrincipalName   ::= SEQUENCE {
208        name-type       [0] Int32,
209        name-string     [1] SEQUENCE OF KerberosString
210    }
211
212Attributes:
213    name_type (KerberosPrincipalNameType): The type of name the was encoded.
214    value (List[bytes]): Each component that forms the name.
215
216.. _RFC 4120 5.2.2:
217    https://www.rfc-editor.org/rfc/rfc4120#section-5.2.2
218"""
219
220
221# https://www.rfc-editor.org/rfc/rfc4120#section-5.5.1 - ap-options
222class KerberosAPOptions(enum.IntFlag):
223    mutual_required = 0x00000020
224    use_session_key = 0x00000040
225    reserved = 0x00000080
226
227    @classmethod
228    def native_labels(cls) -> typing.Dict["KerberosAPOptions", str]:
229        return {
230            KerberosAPOptions.mutual_required: 'mutual-required',
231            KerberosAPOptions.use_session_key: 'use-session-key',
232            KerberosAPOptions.reserved: 'reserved',
233        }
234
235
236# https://www.rfc-editor.org/rfc/rfc4120#section-5.4.1 - KDCOptions
237class KerberosKDCOptions(enum.IntFlag):
238    reserved = 0x80000000
239    forwardable = 0x40000000
240    forwarded = 0x20000000
241    proxiable = 0x10000000
242    proxy = 0x08000000
243    allow_postdate = 0x04000000
244    postdated = 0x02000000
245    unused7 = 0x01000000
246    renewable = 0x00800000
247    unused9 = 0x00400000
248    unused10 = 0x00200000
249    opt_hardware_auth = 0x00100000
250    unused12 = 0x00080000
251    unused13 = 0x00040000
252    constrained_delegation = 0x00020000
253    canonicalize = 0x00010000
254    request_anonymous = 0x00008000
255    unused17 = 0x00004000
256    unused18 = 0x00002000
257    unused19 = 0x00001000
258    unused20 = 0x00000800
259    unused21 = 0x00000400
260    unused22 = 0x00000200
261    unused23 = 0x00000100
262    unused24 = 0x00000080
263    unused25 = 0x00000040
264    disable_transited_check = 0x00000020
265    renewable_ok = 0x00000010
266    enc_tkt_in_skey = 0x00000008
267    unused29 = 0x00000004
268    renew = 0x00000002
269    validate = 0x00000001
270
271    @classmethod
272    def native_labels(cls) -> typing.Dict["KerberosKDCOptions", str]:
273        return {
274            KerberosKDCOptions.reserved: 'reserved',
275            KerberosKDCOptions.forwardable: 'forwardable',
276            KerberosKDCOptions.forwarded: 'forwarded',
277            KerberosKDCOptions.proxiable: 'proxiable',
278            KerberosKDCOptions.proxy: 'proxy',
279            KerberosKDCOptions.allow_postdate: 'allow-postdate',
280            KerberosKDCOptions.postdated: 'postdated',
281            KerberosKDCOptions.unused7: 'unused7',
282            KerberosKDCOptions.renewable: 'renewable',
283            KerberosKDCOptions.unused9: 'unused9',
284            KerberosKDCOptions.unused10: 'unused10',
285            KerberosKDCOptions.opt_hardware_auth: 'opt-hardware-auth',
286            KerberosKDCOptions.unused12: 'unused12',
287            KerberosKDCOptions.unused13: 'unused13',
288            KerberosKDCOptions.constrained_delegation: 'constrained-delegation',
289            KerberosKDCOptions.canonicalize: 'canonicalize',
290            KerberosKDCOptions.request_anonymous: 'request-anonymous',
291            KerberosKDCOptions.unused17: 'unused17',
292            KerberosKDCOptions.unused18: 'unused18',
293            KerberosKDCOptions.unused19: 'unused19',
294            KerberosKDCOptions.unused20: 'unused20',
295            KerberosKDCOptions.unused21: 'unused21',
296            KerberosKDCOptions.unused22: 'unused22',
297            KerberosKDCOptions.unused23: 'unused23',
298            KerberosKDCOptions.unused24: 'unused24',
299            KerberosKDCOptions.unused25: 'unused25',
300            KerberosKDCOptions.disable_transited_check: 'disable-transited-check',
301            KerberosKDCOptions.renewable_ok: 'renewable-ok',
302            KerberosKDCOptions.enc_tkt_in_skey: 'enc-tkt-in-skey',
303            KerberosKDCOptions.unused29: 'unused29',
304            KerberosKDCOptions.renew: 'renew',
305            KerberosKDCOptions.validate: 'validate',
306        }
307
308
309# https://ldapwiki.com/wiki/Kerberos%20Encryption%20Types - etypes
310class KerberosEncryptionType(enum.IntEnum):
311    des_cbc_crc = 0x0001
312    des_cbc_md4 = 0x0002
313    des_cbc_md5 = 0x0003
314    des_cbc_raw = 0x0004
315    des3_cbc_raw = 0x0006
316    des3_cbc_sha1 = 0x0010
317    aes128_cts_hmac_sha1_96 = 0x0011
318    aes256_cts_hmac_sha1_96 = 0x0012
319    aes128_cts_hmac_sha256_128 = 0x0013
320    aes256_cts_hmac_sha384_192 = 0x0014
321    rc4_hmac = 0x0017
322    rc4_hmac_exp = 0x0018
323    camellia128_cts_cmac = 0x0019
324    camellia256_cts_cmac = 0x001a
325
326    @classmethod
327    def native_labels(cls) -> typing.Dict["KerberosEncryptionType", str]:
328        return {
329            KerberosEncryptionType.des_cbc_crc: 'DES_CBC_CRC',
330            KerberosEncryptionType.des_cbc_md4: 'DES_CBC_MD4',
331            KerberosEncryptionType.des_cbc_md5: 'DES_CBC_MD5',
332            KerberosEncryptionType.des_cbc_raw: 'DES_CBC_RAW',
333            KerberosEncryptionType.des3_cbc_raw: 'DES3_CBC_RAW',
334            KerberosEncryptionType.des3_cbc_sha1: 'DES3_CBC_SHA1',
335            KerberosEncryptionType.aes128_cts_hmac_sha1_96: 'AES128_CTS_HMAC_SHA1_96',
336            KerberosEncryptionType.aes256_cts_hmac_sha1_96: 'AES256_CTS_HMAC_SHA1_96',
337            KerberosEncryptionType.aes128_cts_hmac_sha256_128: 'AES128_CTS_HMAC_SHA256_128',
338            KerberosEncryptionType.aes256_cts_hmac_sha384_192: 'AES256_CTS_HMAC_SHA384_192',
339            KerberosEncryptionType.rc4_hmac: 'RC4_HMAC',
340            KerberosEncryptionType.rc4_hmac_exp: 'RC4_HMAC_EXP',
341            KerberosEncryptionType.camellia128_cts_cmac: 'CAMELLIA128_CTS_CMAC',
342            KerberosEncryptionType.camellia256_cts_cmac: 'CAMELLIA256_CTS_CMAC',
343        }
344
345
346# https://www.rfc-editor.org/rfc/rfc4120#section-7.5.9
347class KerberosErrorCode(enum.IntEnum):
348    none = 0
349    name_exp = 1
350    service_exp = 2
351    bad_pvno = 3
352    c_old_mast_kvno = 4
353    s_old_mast_kvno = 5
354    c_principal_unknown = 6
355    s_principal_unknown = 7
356    principal_not_unique = 8
357    null_key = 9
358    cannot_postdate = 10
359    never_valid = 11
360    policy = 12
361    badoption = 13
362    etype_nosupp = 14
363    sumtype_nosupp = 15
364    padata_type_nosupp = 16
365    trtype_nosupp = 17
366    client_revoked = 18
367    service_revoked = 19
368    tgt_revoked = 20
369    client_notyet = 21
370    service_notyet = 22
371    key_expired = 23
372    preauth_failed = 24
373    preauth_required = 25
374    server_nomatch = 26
375    must_use_user2user = 27
376    path_not_accepted = 28
377    kdc_svc_unavailable = 29
378    ap_bad_integrity = 31
379    ap_txt_expired = 32
380    ap_tkt_nyv = 33
381    ap_repeat = 34
382    ap_not_use = 35
383    ap_badmatch = 36
384    ap_skew = 37
385    ap_badaddr = 38
386    ap_badversion = 39
387    ap_msg_type = 40
388    ap_modified = 41
389    ap_badorder = 42
390    ap_badkeyver = 44
391    ap_nokey = 45
392    ap_mut_fail = 46
393    ap_baddirection = 47
394    ap_method = 48
395    ap_badseq = 49
396    ap_inapp_cksum = 50
397    ap_path_not_accepted = 51
398    response_too_big = 52
399    generic = 60
400    field_toolong = 61
401    kdc_client_not_trusted = 62
402    kdc_not_trusted = 53
403    kdc_invalid_sig = 64
404    kdc_key_too_weak = 65
405    kdc_certificate_mismatch = 66
406    ap_no_tgt = 67
407    kdc_wrong_realm = 68
408    ap_user_to_user_required = 69
409    kdc_cant_verify_certificate = 70
410    kdc_invalid_certificate = 71
411    kdc_revoked_certificate = 72
412    kdc_revocation_status_unknown = 73
413    kdc_revocation_status_unavailable = 74
414    kdc_client_name_mismatch = 75
415    kdc_name_mismatch = 76
416
417    @classmethod
418    def native_labels(cls) -> typing.Dict["KerberosErrorCode", str]:
419        return {
420            KerberosErrorCode.none: 'KDC_ERR_NONE',
421            KerberosErrorCode.name_exp: 'KDC_ERR_NAME_EXP',
422            KerberosErrorCode.service_exp: 'KDC_ERR_SERVICE_EXP',
423            KerberosErrorCode.bad_pvno: 'KDC_ERR_BAD_PVNO',
424            KerberosErrorCode.c_old_mast_kvno: 'KDC_ERR_C_OLD_MAST_KVNO',
425            KerberosErrorCode.s_old_mast_kvno: 'KDC_ERR_S_OLD_MAST_KVNO',
426            KerberosErrorCode.c_principal_unknown: 'KDC_ERR_C_PRINCIPAL_UNKNOWN',
427            KerberosErrorCode.s_principal_unknown: 'KDC_ERR_S_PRINCIPAL_UNKNOWN',
428            KerberosErrorCode.principal_not_unique: 'KDC_ERR_PRINCIPAL_NOT_UNIQUE',
429            KerberosErrorCode.null_key: 'KDC_ERR_NULL_KEY',
430            KerberosErrorCode.cannot_postdate: 'KDC_ERR_CANNOT_POSTDATE',
431            KerberosErrorCode.never_valid: 'KDC_ERR_NEVER_VALID',
432            KerberosErrorCode.policy: 'KDC_ERR_POLICY',
433            KerberosErrorCode.badoption: 'KDC_ERR_BADOPTION',
434            KerberosErrorCode.etype_nosupp: 'KDC_ERR_ETYPE_NOSUPP',
435            KerberosErrorCode.sumtype_nosupp: 'KDC_ERR_SUMTYPE_NOSUPP',
436            KerberosErrorCode.padata_type_nosupp: 'KDC_ERR_PADATA_TYPE_NOSUPP',
437            KerberosErrorCode.trtype_nosupp: 'KDC_ERR_TRTYPE_NOSUPP',
438            KerberosErrorCode.client_revoked: 'KDC_ERR_CLIENT_REVOKED',
439            KerberosErrorCode.service_revoked: 'KDC_ERR_SERVICE_REVOKED',
440            KerberosErrorCode.tgt_revoked: 'KDC_ERR_TGT_REVOKED',
441            KerberosErrorCode.client_notyet: 'KDC_ERR_CLIENT_NOTYET',
442            KerberosErrorCode.service_notyet: 'KDC_ERR_SERVICE_NOTYET',
443            KerberosErrorCode.key_expired: 'KDC_ERR_KEY_EXPIRED',
444            KerberosErrorCode.preauth_failed: 'KDC_ERR_PREAUTH_FAILED',
445            KerberosErrorCode.preauth_required: 'KDC_ERR_PREAUTH_REQUIRED',
446            KerberosErrorCode.server_nomatch: 'KDC_ERR_SERVER_NOMATCH',
447            KerberosErrorCode.must_use_user2user: 'KDC_ERR_MUST_USE_USER2USER',
448            KerberosErrorCode.path_not_accepted: 'KDC_ERR_PATH_NOT_ACCEPTED',
449            KerberosErrorCode.kdc_svc_unavailable: 'KDC_ERR_SVC_UNAVAILABLE',
450            KerberosErrorCode.ap_bad_integrity: 'KRB_AP_ERR_BAD_INTEGRITY',
451            KerberosErrorCode.ap_txt_expired: 'KRB_AP_ERR_TKT_EXPIRED',
452            KerberosErrorCode.ap_tkt_nyv: 'KRB_AP_ERR_TKT_NYV',
453            KerberosErrorCode.ap_repeat: 'KRB_AP_ERR_REPEAT',
454            KerberosErrorCode.ap_not_use: 'KRB_AP_ERR_NOT_US',
455            KerberosErrorCode.ap_badmatch: 'KRB_AP_ERR_BADMATCH',
456            KerberosErrorCode.ap_skew: 'KRB_AP_ERR_SKEW',
457            KerberosErrorCode.ap_badaddr: 'KRB_AP_ERR_BADADDR',
458            KerberosErrorCode.ap_badversion: 'KRB_AP_ERR_BADVERSION',
459            KerberosErrorCode.ap_msg_type: 'KRB_AP_ERR_MSG_TYPE',
460            KerberosErrorCode.ap_modified: 'KRB_AP_ERR_MODIFIED',
461            KerberosErrorCode.ap_badorder: 'KRB_AP_ERR_BADORDER',
462            KerberosErrorCode.ap_badkeyver: 'KRB_AP_ERR_BADKEYVER',
463            KerberosErrorCode.ap_nokey: 'KRB_AP_ERR_NOKEY',
464            KerberosErrorCode.ap_mut_fail: 'KRB_AP_ERR_MUT_FAIL',
465            KerberosErrorCode.ap_baddirection: 'KRB_AP_ERR_BADDIRECTION',
466            KerberosErrorCode.ap_method: 'KRB_AP_ERR_METHOD',
467            KerberosErrorCode.ap_badseq: 'KRB_AP_ERR_BADSEQ',
468            KerberosErrorCode.ap_inapp_cksum: 'KRB_AP_ERR_INAPP_CKSUM',
469            KerberosErrorCode.ap_path_not_accepted: 'KRB_AP_PATH_NOT_ACCEPTED',
470            KerberosErrorCode.response_too_big: 'KRB_ERR_RESPONSE_TOO_BIG',
471            KerberosErrorCode.generic: 'KRB_ERR_GENERIC',
472            KerberosErrorCode.field_toolong: 'KRB_ERR_FIELD_TOOLONG',
473            KerberosErrorCode.kdc_client_not_trusted: 'KDC_ERROR_CLIENT_NOT_TRUSTED',
474            KerberosErrorCode.kdc_not_trusted: 'KDC_ERROR_KDC_NOT_TRUSTED',
475            KerberosErrorCode.kdc_invalid_sig: 'KDC_ERROR_INVALID_SIG',
476            KerberosErrorCode.kdc_key_too_weak: 'KDC_ERR_KEY_TOO_WEAK',
477            KerberosErrorCode.kdc_certificate_mismatch: 'KDC_ERR_CERTIFICATE_MISMATCH',
478            KerberosErrorCode.ap_no_tgt: 'KRB_AP_ERR_NO_TGT',
479            KerberosErrorCode.kdc_wrong_realm: 'KDC_ERR_WRONG_REALM',
480            KerberosErrorCode.ap_user_to_user_required: 'KRB_AP_ERR_USER_TO_USER_REQUIRED',
481            KerberosErrorCode.kdc_cant_verify_certificate: 'KDC_ERR_CANT_VERIFY_CERTIFICATE',
482            KerberosErrorCode.kdc_invalid_certificate: 'KDC_ERR_INVALID_CERTIFICATE',
483            KerberosErrorCode.kdc_revoked_certificate: 'KDC_ERR_REVOKED_CERTIFICATE',
484            KerberosErrorCode.kdc_revocation_status_unknown: 'KDC_ERR_REVOCATION_STATUS_UNKNOWN',
485            KerberosErrorCode.kdc_revocation_status_unavailable: 'KDC_ERR_REVOCATION_STATUS_UNAVAILABLE',
486            KerberosErrorCode.kdc_client_name_mismatch: 'KDC_ERR_CLIENT_NAME_MISMATCH',
487            KerberosErrorCode.kdc_name_mismatch: 'KDC_ERR_KDC_NAME_MISMATCH',
488        }
489
490
491# https://www.rfc-editor.org/rfc/rfc4120#section-5.10
492class KerberosMessageType(enum.IntEnum):
493    unknown = 0
494    as_req = 10
495    as_rep = 11
496    tgs_req = 12
497    tgs_rep = 13
498    ap_req = 14
499    ap_rep = 15
500    error = 30
501
502    @classmethod
503    def native_labels(cls) -> typing.Dict["KerberosMessageType", str]:
504        return {
505            KerberosMessageType.unknown: 'UNKNOWN',
506            KerberosMessageType.as_req: 'AS-REQ',
507            KerberosMessageType.as_rep: 'AS-REP',
508            KerberosMessageType.tgs_req: 'TGS-REQ',
509            KerberosMessageType.tgs_rep: 'TGS-REP',
510            KerberosMessageType.ap_req: 'AP-REQ',
511            KerberosMessageType.ap_rep: 'AP-REP',
512            KerberosMessageType.error: 'KRB-ERROR',
513        }
514
515
516# https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml
517class KerberosPADataType(enum.IntEnum):
518    tgs_req = 1
519    enc_timestamp = 2
520    pw_salt = 3
521    reserved = 4
522    enc_unix_time = 5
523    sandia_secureid = 6
524    sesame = 7
525    osf_dce = 8
526    cybersafe_secureid = 9
527    afs3_salt = 10
528    etype_info = 11
529    sam_challenge = 12
530    sam_response = 13
531    pk_as_req_old = 14
532    pk_as_rep_old = 15
533    pk_as_req = 16
534    pk_as_rep = 17
535    pk_ocsp_response = 18
536    etype_info2 = 19
537    use_specified_kvno = 20
538    svr_referral_info = 20
539    sam_redirect = 21
540    get_from_typed_data = 22
541    td_padata = 22
542    sam_etype_info = 23
543    alt_princ = 24
544    server_referral = 25
545    sam_challenge2 = 30
546    sam_response2 = 31
547    extra_tgt = 41
548    td_pkinit_cms_certificates = 101
549    td_krb_principal = 102
550    td_krb_realm = 103
551    td_trusted_certifiers = 104
552    td_certificate_index = 105
553    td_app_defined_error = 106
554    td_req_nonce = 107
555    td_req_seq = 108
556    td_dh_parameters = 109
557    td_cms_digest_algorithms = 111
558    td_cert_digest_algorithms = 112
559    pac_request = 128
560    for_user = 128
561    for_x509_user = 130
562    for_check_dups = 131
563    as_checksum = 132
564    fx_cookie = 133
565    authentication_set = 134
566    auth_set_selected = 165
567    fx_fast = 136
568    fx_error = 137
569    encrypted_challenge = 138
570    otp_challenge = 141
571    otp_request = 142
572    otp_confirm = 143
573    otp_pin_change = 144
574    epak_as_req = 145
575    epak_as_rep = 146
576    pkinit_kx = 147
577    pku2u_name = 148
578    enc_pa_rep = 149
579    as_freshness = 150
580    spake = 151
581    kerb_key_list_req = 161
582    kerb_key_list_rep = 162
583    supported_etypes = 165
584    extended_error = 166
585    pac_options = 167
586
587    @classmethod
588    def native_labels(cls) -> typing.Dict["KerberosPADataType", str]:
589        return {
590            KerberosPADataType.tgs_req: 'PA-TGS-REQ',
591            KerberosPADataType.enc_timestamp: 'PA-ENC-TIMESTAMP',
592            KerberosPADataType.pw_salt: 'PA-PW-SALT',
593            KerberosPADataType.reserved: 'reserved',
594            KerberosPADataType.enc_unix_time: 'PA-ENC-UNIX-TIME',
595            KerberosPADataType.sandia_secureid: 'PA-SANDIA-SECUREID',
596            KerberosPADataType.sesame: 'PA-SESAME',
597            KerberosPADataType.osf_dce: 'PA-OSF-DCE',
598            KerberosPADataType.cybersafe_secureid: 'PA-CYBERSAFE-SECUREID',
599            KerberosPADataType.afs3_salt: 'PA-AFS3-SALT',
600            KerberosPADataType.etype_info: 'PA-ETYPE-INFO',
601            KerberosPADataType.sam_challenge: 'PA-SAM-CHALLENGE',
602            KerberosPADataType.sam_response: 'PA-SAM-RESPONSE',
603            KerberosPADataType.pk_as_req_old: 'PA-PK-AS-REQ_OLD',
604            KerberosPADataType.pk_as_rep_old: 'PA-PK-AS-REP_OLD',
605            KerberosPADataType.pk_as_req: 'PA-PK-AS-REQ',
606            KerberosPADataType.pk_as_rep: 'PA-PK-AS-REP',
607            KerberosPADataType.pk_ocsp_response: 'PA-PK-OCSP-RESPONSE',
608            KerberosPADataType.etype_info2: 'PA-ETYPE-INFO2',
609            KerberosPADataType.use_specified_kvno: 'PA-USE-SPECIFIED-KVNO or PA-SVR-REFERRAL-INFO',
610            KerberosPADataType.sam_redirect: 'PA-SAM-REDIRECT',
611            KerberosPADataType.get_from_typed_data: 'PA-GET-FROM-TYPED-DATA',
612            KerberosPADataType.td_padata: 'TD-PADATA',
613            KerberosPADataType.sam_etype_info: 'PA-SAM-ETYPE-INFO',
614            KerberosPADataType.alt_princ: 'PA-ALT-PRINC',
615            KerberosPADataType.server_referral: 'PA-SERVER-REFERRAL',
616            KerberosPADataType.sam_challenge2: 'PA-SAM-CHALLENGE2',
617            KerberosPADataType.sam_response2: 'PA-SAM-RESPONSE2',
618            KerberosPADataType.extra_tgt: 'PA-EXTRA-TGT',
619            KerberosPADataType.td_pkinit_cms_certificates: 'TD-PKINIT-CMS-CERTIFICATES',
620            KerberosPADataType.td_krb_principal: 'TD-KRB-PRINCIPAL',
621            KerberosPADataType.td_krb_realm: 'TD-KRB-REALM',
622            KerberosPADataType.td_trusted_certifiers: 'TD-TRUSTED-CERTIFIERS',
623            KerberosPADataType.td_certificate_index: 'TD-CERTIFICATE-INDEX',
624            KerberosPADataType.td_app_defined_error: 'TD-APP-DEFINED-ERROR',
625            KerberosPADataType.td_req_nonce: 'TD-REQ-NONCE',
626            KerberosPADataType.td_req_seq: 'TD-REQ-SEQ',
627            KerberosPADataType.td_dh_parameters: 'TD_DH_PARAMETERS',
628            KerberosPADataType.td_cms_digest_algorithms: 'TD-CMS-DIGEST-ALGORITHMS',
629            KerberosPADataType.td_cert_digest_algorithms: 'TD-CERT-DIGEST-ALGORITHMS',
630            KerberosPADataType.pac_request: 'PA-PAC-REQUEST',
631            KerberosPADataType.for_user: 'PA-FOR_USER',
632            KerberosPADataType.for_x509_user: 'PA-FOR-X509-USER',
633            KerberosPADataType.for_check_dups: 'PA-FOR-CHECK_DUPS',
634            KerberosPADataType.as_checksum: 'PA-AS-CHECKSUM',
635            KerberosPADataType.fx_cookie: 'PA-FX-COOKIE',
636            KerberosPADataType.authentication_set: 'PA-AUTHENTICATION-SET',
637            KerberosPADataType.auth_set_selected: 'PA-AUTH-SET-SELECTED',
638            KerberosPADataType.fx_fast: 'PA-FX-FAST',
639            KerberosPADataType.fx_error: 'PA-FX-ERROR',
640            KerberosPADataType.encrypted_challenge: 'PA-ENCRYPTED-CHALLENGE',
641            KerberosPADataType.otp_challenge: 'PA-OTP-CHALLENGE',
642            KerberosPADataType.otp_request: 'PA-OTP-REQUEST',
643            KerberosPADataType.otp_confirm: 'PA-OTP-CONFIRM',
644            KerberosPADataType.otp_pin_change: 'PA-OTP-PIN-CHANGE',
645            KerberosPADataType.epak_as_req: 'PA-EPAK-AS-REQ',
646            KerberosPADataType.epak_as_rep: 'PA-EPAK-AS-REP',
647            KerberosPADataType.pkinit_kx: 'PA_PKINIT_KX',
648            KerberosPADataType.pku2u_name: 'PA_PKU2U_NAME',
649            KerberosPADataType.enc_pa_rep: 'PA-REQ-ENC-PA-REP',
650            KerberosPADataType.as_freshness: 'PA_AS_FRESHNESS',
651            KerberosPADataType.spake: 'PA-SPAKE',
652            KerberosPADataType.kerb_key_list_req: 'KERB-KEY-LIST-REQ',
653            KerberosPADataType.kerb_key_list_rep: 'KERB-KEY-LIST-REP',
654            KerberosPADataType.supported_etypes: 'PA-SUPPORTED-ETYPES',
655            KerberosPADataType.extended_error: 'PA-EXTENDED_ERROR',
656            KerberosPADataType.pac_options: 'PA-PAC-OPTIONS',
657        }
658
659
660# https://www.rfc-editor.org/rfc/rfc4120#section-6.2
661class KerberosPrincipalNameType(enum.IntEnum):
662    unknown = 0
663    principal = 1
664    srv_inst = 2
665    srv_hst = 3
666    srv_xhst = 4
667    uid = 5
668    x500_principal = 6
669    smtp_name = 7
670    enterprise = 10
671
672    @classmethod
673    def native_labels(cls) -> typing.Dict["KerberosPrincipalNameType", str]:
674        return {
675            KerberosPrincipalNameType.unknown: 'NT-UNKNOWN',
676            KerberosPrincipalNameType.principal: 'NT-PRINCIPAL',
677            KerberosPrincipalNameType.srv_inst: 'NT-SRV-INST',
678            KerberosPrincipalNameType.srv_hst: 'NT-SRV-HST',
679            KerberosPrincipalNameType.srv_xhst: 'NT-SRV-XHST',
680            KerberosPrincipalNameType.uid: 'NT-UID',
681            KerberosPrincipalNameType.x500_principal: 'NT-X500-PRINCIPAL',
682            KerberosPrincipalNameType.smtp_name: 'NT-SMTP-NAME',
683            KerberosPrincipalNameType.enterprise: 'NT-ENTERPRISE',
684        }
685
686
687# https://www.rfc-editor.org/rfc/rfc4120#section-7.5.3
688class KerberosHostAddressType(enum.IntEnum):
689    ipv4 = 2
690    directional = 3
691    chaos_net = 5
692    xns = 6
693    iso = 7
694    decnet_phase_iv = 12
695    apple_talk_ddp = 16
696    netbios = 20
697    ipv6 = 26
698
699    @classmethod
700    def native_labels(cls) -> typing.Dict["KerberosHostAddressType", str]:
701        return {
702            KerberosHostAddressType.ipv4: 'IPv4',
703            KerberosHostAddressType.directional: 'Directional',
704            KerberosHostAddressType.chaos_net: 'ChaosNet',
705            KerberosHostAddressType.xns: 'XNS',
706            KerberosHostAddressType.iso: 'ISO',
707            KerberosHostAddressType.decnet_phase_iv: 'DECNET Phase IV',
708            KerberosHostAddressType.apple_talk_ddp: 'AppleTalk DDP',
709            KerberosHostAddressType.netbios: 'NetBios',
710            KerberosHostAddressType.ipv6: 'IPv6',
711        }
712
713
714class ParseType(enum.IntEnum):
715    default = 0
716    enum = 1
717    flags = 2
718    datetime = 3
719    text = 4
720    bytes = 5
721    principal_name = 6
722    host_address = 7
723    token = 8
724
725
726class _KerberosMsgType(type):
727    __registry = {}
728
729    def __init__(cls, name, bases, attributes):
730        pvno = getattr(cls, 'PVNO', 0)
731
732        if pvno not in cls.__registry:
733            cls.__registry[pvno] = {}
734
735        if hasattr(cls, 'MESSAGE_TYPE'):
736            cls.__registry[pvno][cls.MESSAGE_TYPE] = cls
737
738    def __call__(cls, sequence):
739        # The KrbAsReq msg starts at 1 not 0, so do a check for that.
740        pvno_idx = 0
741        if 0 not in sequence:
742            pvno_idx = 1
743
744        pvno = unpack_asn1_integer(sequence[pvno_idx])
745        message_type = unpack_asn1_integer(sequence[pvno_idx + 1])
746        new_cls = cls.__registry[pvno].get(message_type, cls)
747        return super(_KerberosMsgType, new_cls).__call__(sequence)
748
749
750class KerberosV5Msg(metaclass=_KerberosMsgType):
751
752    MESSAGE_TYPE = KerberosMessageType.unknown
753    PVNO = 5
754
755    def __init__(self, sequence: typing.Dict[int, ASN1Value]) -> None:
756        # This is only used if decoding an unknown Kerberos message type.
757        self.sequence = sequence
758
759    @staticmethod
760    def unpack(value: typing.Union[ASN1Value, bytes]) -> "KerberosV5Msg":
761        msg_sequence = unpack_asn1_tagged_sequence(value)
762        return KerberosV5Msg(msg_sequence)
763
764
765class KrbAsReq(KerberosV5Msg):
766    """The KRB_AS_REQ message.
767
768    The KRB_AS_REQ message is used when the client wishes to retrieve a the initial ticket for a service. The
769    KRB_TGS_REQ message is identical except for the tag and msg-type is used when retrieving additional tickets for a
770    service.
771
772    The ASN.1 definition for the KDC-REQ structure is defined in `RFC 4120 5.4.1`_::
773
774        KDC-REQ         ::= SEQUENCE {
775            -- NOTE: first tag is [1], not [0]
776            pvno            [1] INTEGER (5) ,
777            msg-type        [2] INTEGER (10 -- AS -- | 12 -- TGS --),
778            padata          [3] SEQUENCE OF PA-DATA OPTIONAL
779                                -- NOTE: not empty --,
780            req-body        [4] KDC-REQ-BODY
781        }
782
783    Args:
784        sequence: The ASN.1 sequence value as a dict to unpack.
785
786    Attributes:
787        padata (PAData): The pre-authentication data.
788        req_body (KdcReqBody): The body of the request.
789
790    .. _RFC 4120 5.4.1:
791        https://www.rfc-editor.org/rfc/rfc4120#section-5.4.1
792    """
793    MESSAGE_TYPE = KerberosMessageType.as_req
794
795    PARSE_MAP = [
796        ('pvno', 'PVNO', ParseType.default),
797        ('msg-type', 'MESSAGE_TYPE', ParseType.enum),
798        ('padata', 'padata', ParseType.token),
799        ('req-body', 'req_body', ParseType.token)
800    ]
801
802    def __init__(self, sequence: typing.Dict[int, ASN1Value]) -> None:
803        def unpack_padata(value):
804            return [PAData.unpack(p) for p in unpack_asn1_sequence(value)]
805
806        self.padata = get_sequence_value(sequence, 3, 'KDC-REQ', 'pa-data', unpack_padata)
807        self.req_body = get_sequence_value(sequence, 4, 'KDC-REQ', 'req-body', KdcReqBody.unpack)
808
809
810class KrbAsRep(KerberosV5Msg):
811    """The KRB_AS_REP message.
812
813    The KRB_AS_REP message is used for a reply from the KDC to a KRB_AS_REQ message. The KRB_TGS_REP message is
814    identical except for the tag and msg-type.
815
816    The ASN.1 definition for the KDC-REP structure is defined in `RFC 4120 5.4.2`_::
817
818        KDC-REP         ::= SEQUENCE {
819            pvno            [0] INTEGER (5),
820            msg-type        [1] INTEGER (11 -- AS -- | 13 -- TGS --),
821            padata          [2] SEQUENCE OF PA-DATA OPTIONAL
822                                -- NOTE: not empty --,
823            crealm          [3] Realm,
824            cname           [4] PrincipalName,
825            ticket          [5] Ticket,
826            enc-part        [6] EncryptedData
827                                -- EncASRepPart or EncTGSRepPart,
828                                -- as appropriate
829        }
830
831    Args:
832        sequence: The ASN.1 sequence value as a dict to unpack.
833
834    Attributes:
835        padata (PAData): The pre-authentication data.
836        crealm (bytes): The client realm.
837        cname (PrincipalName): The client principal name.
838        ticket (Ticket): The newly issued ticket.
839        enc_part (EncryptedData): The encrypted part of the message.
840
841    .. _RFC 4120 5.4.2:
842        https://www.rfc-editor.org/rfc/rfc4120#section-5.4.2
843    """
844    MESSAGE_TYPE = KerberosMessageType.as_rep
845
846    PARSE_MAP = [
847        ('pvno', 'PVNO', ParseType.default),
848        ('msg-type', 'MESSAGE_TYPE', ParseType.enum),
849        ('padata', 'padata', ParseType.token),
850        ('crealm', 'crealm', ParseType.text),
851        ('cname', 'cname', ParseType.principal_name),
852        ('ticket', 'ticket', ParseType.token),
853        ('enc-part', 'enc_part', ParseType.token),
854    ]
855
856    def __init__(self, sequence: typing.Dict[int, ASN1Value]) -> None:
857        def unpack_padata(value):
858            return [PAData.unpack(p) for p in unpack_asn1_sequence(value)]
859
860        self.padata = get_sequence_value(sequence, 2, 'KDC-REP', 'pa-data', unpack_padata)
861        self.crealm = get_sequence_value(sequence, 3, 'KDC-REP', 'crealm', unpack_asn1_general_string)
862        self.cname = get_sequence_value(sequence, 4, 'KDC-REP', 'cname', unpack_principal_name)
863        self.ticket = get_sequence_value(sequence, 5, 'KDC-REP', 'ticket', Ticket.unpack)
864        self.enc_part = get_sequence_value(sequence, 6, 'KDC-REP', 'enc-part', EncryptedData.unpack)
865
866
867class KrbTgsReq(KrbAsReq):
868    """ The KRB_TGS_REQ is the same as KRB_AS_REQ but with a different MESSAGE_TYPE. """
869    MESSAGE_TYPE = KerberosMessageType.tgs_req
870
871
872class KrbTgsRep(KrbAsRep):
873    """ The KRB_TGS_REP is the same as KRB_AS_REP but with a different MESSAGE_TYPE. """
874    MESSAGE_TYPE = KerberosMessageType.tgs_rep
875
876
877class KrbApReq(KerberosV5Msg):
878    """The KRB_AP_REQ message.
879
880    The KRB_AP_REQ message contains is used to authenticate the initiator to an acceptor.
881
882    The ASN.1 definition for the KRB_AP_REQ structure is defined in `RFC 4120 5.5.1`_::
883
884        AP-REQ          ::= [APPLICATION 14] SEQUENCE {
885            pvno            [0] INTEGER (5),
886            msg-type        [1] INTEGER (14),
887            ap-options      [2] APOptions,
888            ticket          [3] Ticket,
889            authenticator   [4] EncryptedData -- Authenticator
890        }
891
892    Args:
893        sequence: The ASN.1 sequence value as a dict to unpack.
894
895    Attributes:
896        ap_options (KerberosAPOptions): Options related to the AP request.
897        ticket (Ticket): The ticket authenticating the client to the server.
898        authenticator (EncryptedData): The encrypted authenticator.
899
900    .. _RFC 4120 5.5.1:
901        https://www.rfc-editor.org/rfc/rfc4120#section-5.5.1
902    """
903    MESSAGE_TYPE = KerberosMessageType.ap_req
904
905    PARSE_MAP = [
906        ('pvno', 'PVNO', ParseType.default),
907        ('msg-type', 'MESSAGE_TYPE', ParseType.enum),
908        ('ap-options', 'ap_options', ParseType.flags),
909        ('ticket', 'ticket', ParseType.token),
910        ('authenticator', 'authenticator', ParseType.token),
911    ]
912
913    def __init__(self, sequence: typing.Dict[int, ASN1Value]) -> None:
914        raw_ap_options = get_sequence_value(sequence, 2, 'AP-REQ', 'ap-options', unpack_asn1_bit_string)
915        ap_options = KerberosAPOptions(struct.unpack("<I", raw_ap_options)[0])
916
917        self.ap_options = ap_options
918        self.ticket = get_sequence_value(sequence, 3, 'AP-REQ', 'ticket', Ticket.unpack)
919        self.authenticator = get_sequence_value(sequence, 4, 'AP-REQ', 'authenticator', EncryptedData.unpack)
920
921
922class KrbApRep(KerberosV5Msg):
923    """The KRB_AP_REP message.
924
925    The KRB_AP_REP is a response to an application request `KRB_AP_REQ`.
926
927    The ASN.1 definition for the KRB_AP_REP structure is defined in `RFC 4120 5.5.2`_::
928
929        AP-REP          ::= [APPLICATION 15] SEQUENCE {
930            pvno            [0] INTEGER (5),
931            msg-type        [1] INTEGER (15),
932            enc-part        [2] EncryptedData -- EncAPRepPart
933        }
934
935    Args:
936        sequence: The ASN.1 sequence value as a dict to unpack.
937
938    Attributes:
939        enc_part (EncryptedData): The encrypted authenticator.
940
941    .. _RFC 4120 5.5.2:
942        https://www.rfc-editor.org/rfc/rfc4120#section-5.5.2
943    """
944    MESSAGE_TYPE = KerberosMessageType.ap_rep
945
946    PARSE_MAP = [
947        ('pvno', 'PVNO', ParseType.default),
948        ('msg-type', 'MESSAGE_TYPE', ParseType.enum),
949        ('enc-part', 'enc_part', ParseType.token),
950    ]
951
952    def __init__(self, sequence: typing.Dict[int, ASN1Value]) -> None:
953        self.enc_part = get_sequence_value(sequence, 2, 'AP-REP', 'enc-part', EncryptedData.unpack)
954
955
956class KrbError(KerberosV5Msg):
957    """The KRB_ERROR message.
958
959    The KRB_ERROR is a message sent in the occurrence of an error.
960
961    The ASN.1 definition for the KRB_ERROR structure is defined in `RFC 4120 5.9.1`_::
962
963        KRB-ERROR       ::= [APPLICATION 30] SEQUENCE {
964            pvno            [0] INTEGER (5),
965            msg-type        [1] INTEGER (30),
966            ctime           [2] KerberosTime OPTIONAL,
967            cusec           [3] Microseconds OPTIONAL,
968            stime           [4] KerberosTime,
969            susec           [5] Microseconds,
970            error-code      [6] Int32,
971            crealm          [7] Realm OPTIONAL,
972            cname           [8] PrincipalName OPTIONAL,
973            realm           [9] Realm -- service realm --,
974            sname           [10] PrincipalName -- service name --,
975            e-text          [11] KerberosString OPTIONAL,
976            e-data          [12] OCTET STRING OPTIONAL
977        }
978
979    Args:
980        sequence: The ASN.1 sequence value as a dict to unpack.
981
982    Attributes:
983        ctime (datetime.datetime): The current time on the client's host.
984        cusec (int): The microsecond part of the client's timestamp.
985        stime (datetime.datetime): The current time of the server.
986        susec (int): The microsecond part of the server's timestamp.
987        error_code (KerberosErrorCode): THe error code returned by the kerberos when a request fails.
988        crealm (bytes): The realm that issues a ticket.
989        cname (PrincipalName): The principal name in the ticket.
990        realm (bytes): The service realm.
991        sname (PrincipalName): The service name.
992        e_text (bytes): Additional text to explain the error code.
993        e_data (bytes): Additional data about the error.
994
995    .. _RFC 4120 5.9.1:
996        https://www.rfc-editor.org/rfc/rfc4120#section-5.9.1
997    """
998    MESSAGE_TYPE = KerberosMessageType.error
999
1000    PARSE_MAP = [
1001        ('pvno', 'PVNO', ParseType.default),
1002        ('msg-type', 'MESSAGE_TYPE', ParseType.enum),
1003        ('ctime', 'ctime', ParseType.datetime),
1004        ('cusec', 'cusec', ParseType.default),
1005        ('stime', 'stime', ParseType.datetime),
1006        ('susec', 'susec', ParseType.default),
1007        ('error-code', 'error_code', ParseType.enum),
1008        ('crealm', 'crealm', ParseType.text),
1009        ('cname', 'cname', ParseType.principal_name),
1010        ('realm', 'realm', ParseType.text),
1011        ('sname', 'sname', ParseType.principal_name),
1012        ('e-text', 'e_text', ParseType.text),
1013        ('e-data', 'e_data', ParseType.bytes),
1014    ]
1015
1016    def __init__(self, sequence: typing.Dict[int, ASN1Value]) -> None:
1017        self.ctime = get_sequence_value(sequence, 2, 'KRB-ERROR', 'ctime', unpack_asn1_generalized_time)
1018        self.cusec = get_sequence_value(sequence, 3, 'KRB-ERROR', 'cusec', unpack_asn1_integer)
1019        self.stime = get_sequence_value(sequence, 4, 'KRB-ERROR', 'stime', unpack_asn1_generalized_time)
1020        self.susec = get_sequence_value(sequence, 5, 'KRB-ERROR', 'susec', unpack_asn1_integer)
1021        self.error_code = KerberosErrorCode(get_sequence_value(sequence, 6, 'KRB-ERROR', 'error-code',
1022                                                               unpack_asn1_integer))
1023        self.crealm = get_sequence_value(sequence, 7, 'KRB-ERROR', 'crealm', unpack_asn1_general_string)
1024        self.cname = get_sequence_value(sequence, 8, 'KRB-ERROR', 'cname', unpack_principal_name)
1025        self.realm = get_sequence_value(sequence, 9, 'KRB-ERROR', 'realm', unpack_asn1_general_string)
1026        self.sname = get_sequence_value(sequence, 10, 'KRB-ERROR', 'realm', unpack_principal_name)
1027        self.e_text = get_sequence_value(sequence, 11, 'KRB-ERROR', 'e-text', unpack_asn1_general_string)
1028        self.e_data = get_sequence_value(sequence, 12, 'KRB-ERROR', 'e-data', unpack_asn1_octet_string)
1029
1030
1031class PAData:
1032    """Kerberos PA-DATA.
1033
1034    The ASN.1 definition for the PA-DATA structure is defined in `RFC 4120 5.2.7`_::
1035
1036        PA-DATA         ::= SEQUENCE {
1037            -- NOTE: first tag is [1], not [0]
1038            padata-type     [1] Int32,
1039            padata-value    [2] OCTET STRING -- might be encoded AP-REQ
1040        }
1041
1042    Args:
1043        data_type: Indicates the type of data the value represents.
1044        value: The PAData value, usually the DER encoding of another message.
1045
1046    Attributes:
1047        data_type (Union[int, KerberosPADataType]): See args.
1048        b_value (bytes): The raw bytes of padata-value, use `value` to get a structured object of these bytes if
1049            available.
1050
1051    .. RFC 4120 5.2.7:
1052        https://www.rfc-editor.org/rfc/rfc4120#section-5.2.7
1053    """
1054    PARSE_MAP = [
1055        ('padata-type', 'data_type', ParseType.enum),
1056        ('padata-value', 'value', ParseType.token),
1057    ]
1058
1059    def __init__(self, data_type: typing.Union[int, KerberosPADataType], value: bytes) -> None:
1060        self.data_type = data_type
1061        self.b_value = value
1062
1063    @property
1064    def value(self) -> typing.Any:
1065        if self.data_type == KerberosPADataType.tgs_req:
1066            # Special edge case for this PA type due to how the messages require the data in unpack
1067            return KrbTgsReq.unpack(unpack_asn1(unpack_asn1(self.b_value)[0].b_data)[0])
1068
1069        unpack_func = {
1070            KerberosPADataType.enc_timestamp: (EncryptedData.unpack, False),
1071            KerberosPADataType.etype_info2: (PAETypeInfo2.unpack, True),
1072        }.get(self.data_type, None)
1073
1074        if unpack_func:
1075            b_value = unpack_asn1(self.b_value)[0]
1076            if unpack_func[1]:
1077                # Is a SEQUENCE OF (list of entries).
1078                return [unpack_func[0](v) for v in unpack_asn1_sequence(b_value)]
1079
1080            else:
1081                return unpack_func[0](b_value.b_data)
1082
1083        else:
1084            return self.b_value
1085
1086    @staticmethod
1087    def unpack(value: ASN1Value) -> "PAData":
1088        sequence = unpack_asn1_tagged_sequence(value)
1089
1090        def unpack_data_type(value):
1091            int_val = unpack_asn1_integer(value)
1092
1093            try:
1094                return KerberosPADataType(int_val)
1095            except ValueError:
1096                return int_val
1097
1098        data_type = get_sequence_value(sequence, 1, 'PA-DATA', 'padata-type', unpack_data_type)
1099        value = get_sequence_value(sequence, 2, 'PA-DATA', 'padata-value', unpack_asn1_octet_string)
1100
1101        return PAData(data_type, value)
1102
1103
1104class PAETypeInfo2:
1105    """Kerberos PA-ETYPE-INFO2 container.
1106
1107    The ASN.1 definition for the PA-ETYPE-INFO2 structure is defined in `RFC 4120 5.2.7.5`_::
1108
1109        ETYPE-INFO2-ENTRY       ::= SEQUENCE {
1110            etype           [0] Int32,
1111            salt            [1] KerberosString OPTIONAL,
1112            s2kparams       [2] OCTET STRING OPTIONAL
1113        }
1114
1115    Args:
1116        etype: The etype that defines the cipher used.
1117        salt: The used in the cipher associated with the cryptosystem.
1118        s2kparams: Extra params to be interpreted by the cipher associated with the cryptosystem.
1119
1120    Attributes:
1121        etype (KerberosEncryptionType): See args.
1122        salt (Optional[bytes]): See args.
1123        s2kparams (Optional[bytes]): See args.
1124
1125    .. RFC 4120 5.2.7.5:
1126        https://www.rfc-editor.org/rfc/rfc4120#section-5.2.7.5
1127    """
1128    PARSE_MAP = [
1129        ('etype', 'etype', ParseType.enum),
1130        ('salt', 'salt', ParseType.bytes),
1131        ('s2kparams', 's2kparams', ParseType.bytes),
1132    ]
1133
1134    def __init__(
1135        self,
1136        etype: KerberosEncryptionType,
1137        salt: typing.Optional[bytes],
1138        s2kparams: typing.Optional[bytes],
1139    ) -> None:
1140        self.etype = etype
1141        self.salt = salt
1142        self.s2kparams = s2kparams
1143
1144    @staticmethod
1145    def unpack(value: ASN1Value) -> "PAETypeInfo2":
1146        sequence = unpack_asn1_tagged_sequence(value)
1147
1148        etype = KerberosEncryptionType(get_sequence_value(sequence, 0, 'PA-ETYPE-INFO2', 'etype', unpack_asn1_integer))
1149        salt = get_sequence_value(sequence, 1, 'ETYPE-INFO2-ENTRY', 'salt', unpack_asn1_general_string)
1150        s2kparams = get_sequence_value(sequence, 2, 'ETYPE-INFO2-ENTRY', 's2kparams', unpack_asn1_octet_string)
1151
1152        return PAETypeInfo2(etype, salt, s2kparams)
1153
1154
1155class EncryptedData:
1156    """Kerberos EncryptedData container.
1157
1158    The ASN.1 definition for the EncryptedData structure is defined in `RFC 4120 5.2.9`_::
1159
1160        EncryptedData   ::= SEQUENCE {
1161            etype   [0] Int32 -- EncryptionType --,
1162            kvno    [1] UInt32 OPTIONAL,
1163            cipher  [2] OCTET STRING -- ciphertext
1164        }
1165
1166    Args:
1167        etype: The encryption algorithm that was used to encipher the cipher.
1168        kvno: The version number of the key under which data is encrypted. It is only present in messages encrypted
1169            under long lasting keys.
1170        cipher: The enciphered text.
1171
1172    Attributes:
1173        etype (KerberosEncryptionType): See args.
1174        kvno (Optional[int]): See args.
1175        cipher (bytes): See args.
1176
1177    .. RFC 4120 5.2.9:
1178        https://www.rfc-editor.org/rfc/rfc4120#section-5.2.9
1179    """
1180    PARSE_MAP = [
1181        ('etype', 'etype', ParseType.enum),
1182        ('kvno', 'kvno', ParseType.default),
1183        ('cipher', 'cipher', ParseType.bytes),
1184    ]
1185
1186    def __init__(self, etype: KerberosEncryptionType, kvno: typing.Optional[int], cipher: bytes) -> None:
1187        self.etype = etype
1188        self.kvno = kvno
1189        self.cipher = cipher
1190
1191    @staticmethod
1192    def unpack(value: ASN1Value) -> "EncryptedData":
1193        sequence = unpack_asn1_tagged_sequence(value)
1194
1195        etype = KerberosEncryptionType(get_sequence_value(sequence, 0, 'EncryptedData', 'etype', unpack_asn1_integer))
1196        kvno = get_sequence_value(sequence, 1, 'EncryptedData', 'kvno', unpack_asn1_integer)
1197        cipher = get_sequence_value(sequence, 2, 'EncryptedData', 'cipher', unpack_asn1_octet_string)
1198
1199        return EncryptedData(etype, kvno, cipher)
1200
1201
1202class Ticket:
1203    """Kerberos Ticket.
1204
1205    The ASN.1 definition for the Ticket structure is defined in `RFC 4120 5.3`_::
1206
1207        Ticket          ::= [APPLICATION 1] SEQUENCE {
1208            tkt-vno         [0] INTEGER (5),
1209            realm           [1] Realm,
1210            sname           [2] PrincipalName,
1211            enc-part        [3] EncryptedData -- EncTicketPart
1212        }
1213
1214    Args:
1215        tkt_vno: The version number for the ticket format.
1216        realm: The realm that issued a ticket.
1217        sname: All the name components of the server's identity.
1218        enc_part: The encrypted part of the ticket, it is encrypted in the key shared by Kerberos and the end server.
1219
1220    Attributes:
1221        tkt_vno (int): See args.
1222        realm (bytes): See args.
1223        sname (List[PrincipalName]): See args.
1224        enc_part (EncryptedData): See args.
1225
1226    .. _RFC 4120 5.3:
1227        https://www.rfc-editor.org/rfc/rfc4120#section-5.3
1228    """
1229    PARSE_MAP = [
1230        ('tkt-vno', 'tkt_vno', ParseType.default),
1231        ('realm', 'realm', ParseType.text),
1232        ('sname', 'sname', ParseType.principal_name),
1233        ('enc-part', 'enc_part', ParseType.token),
1234    ]
1235
1236    def __init__(
1237        self,
1238        tkt_vno: int,
1239        realm: bytes,
1240        sname: typing.List[PrincipalName],
1241        enc_part: EncryptedData,
1242    ) -> None:
1243        self.tkt_vno = tkt_vno
1244        self.realm = realm
1245        self.sname = sname
1246        self.enc_part = enc_part
1247
1248    @staticmethod
1249    def unpack(value: typing.Union[ASN1Value, bytes]) -> "Ticket":
1250        b_data = extract_asn1_tlv(value, TagClass.application, 1)
1251        sequence = unpack_asn1_tagged_sequence(unpack_asn1(b_data)[0])
1252
1253        tkt_vno = get_sequence_value(sequence, 0, 'Ticket', 'tkt-vno', unpack_asn1_integer)
1254        realm = get_sequence_value(sequence, 1, 'Ticket', 'realm', unpack_asn1_general_string)
1255        sname = get_sequence_value(sequence, 2, 'Ticket', 'sname', unpack_principal_name)
1256        enc_part = get_sequence_value(sequence, 3, 'Ticket', 'enc-part', EncryptedData.unpack)
1257
1258        return Ticket(tkt_vno, realm, sname, enc_part)
1259
1260
1261class KdcReqBody:
1262    """The KRB_AS_REQ message.
1263
1264    The KRB_AS_REQ message is used when the client wishes to retrieve a the initial ticket for a service. The
1265    KRB_TGS_REQ message is identical except for the tag and msg-type is used when retrieving additional tickets for a
1266    service.
1267
1268    The ASN.1 definition for the KDC-REQ structure is defined in `RFC 4120 5.4.1`_::
1269
1270        KDC-REQ-BODY    ::= SEQUENCE {
1271            kdc-options             [0] KDCOptions,
1272            cname                   [1] PrincipalName OPTIONAL
1273                                        -- Used only in AS-REQ --,
1274            realm                   [2] Realm
1275                                        -- Server's realm
1276                                        -- Also client's in AS-REQ --,
1277            sname                   [3] PrincipalName OPTIONAL,
1278            from                    [4] KerberosTime OPTIONAL,
1279            till                    [5] KerberosTime,
1280            rtime                   [6] KerberosTime OPTIONAL,
1281            nonce                   [7] UInt32,
1282            etype                   [8] SEQUENCE OF Int32 -- EncryptionType
1283                                        -- in preference order --,
1284            addresses               [9] HostAddresses OPTIONAL,
1285            enc-authorization-data  [10] EncryptedData OPTIONAL
1286                                        -- AuthorizationData --,
1287            additional-tickets      [11] SEQUENCE OF Ticket OPTIONAL
1288                                        -- NOTE: not empty
1289        }
1290
1291    Args:
1292        kdc_options: Flags desired by the client and other behaviour desired.
1293        cname: The client name.
1294        realm: The realm part of the server's principal.
1295        sname: The service name.
1296        postdated_from: When the requested ticket is to be posted from.
1297        postdated_till: The expiration date requested by the client.
1298        rtime: The requested renew-till time.
1299        nonce: Random number generated by the client.
1300        etype: The desired encryption algorithm to be used in priority order.
1301        addresses: Addresses from which the requested ticket is to be valid.
1302        enc_authorization_data: Encrypted authorization data.
1303        additional_tickets: Additional tickets to be optionally included in a request.
1304
1305    Attributes:
1306        kdc_options (int): See args.
1307        cname (Optional[PrincipalName]): See args.
1308        realm (bytes): See args.
1309        sname (PrincipalName): See args.
1310        postdated_from (Optional[datetime.datetime]): See args.
1311        postdated_till (datetime.datetime): See args.
1312        rtime (Optional[datetime.datetime]): See args.
1313        nonce (int): See args.
1314        etype (List[KerberosEncryptionType]): See args.
1315        addresses (Optional[List[HostAddress]]): See args.
1316        enc_authorization_data (Optional[EncryptedData]): See args.
1317        additional_tickets (Optional[Ticket]): See args.
1318
1319    .. _RFC 4120 5.4.1:
1320        https://www.rfc-editor.org/rfc/rfc4120#section-5.4.1
1321    """
1322    PARSE_MAP = [
1323        ('kdc-options', 'kdc_options', (ParseType.flags, KerberosKDCOptions)),
1324        ('cname', 'cname', ParseType.principal_name),
1325        ('realm', 'realm', ParseType.text),
1326        ('sname', 'sname', ParseType.principal_name),
1327        ('from', 'postdated_from', ParseType.datetime),
1328        ('till', 'postdated_till', ParseType.datetime),
1329        ('rtime', 'rtime', ParseType.datetime),
1330        ('nonce', 'nonce', ParseType.default),
1331        ('etype', 'etype', ParseType.enum),
1332        ('addresses', 'addresses', ParseType.host_address),
1333        ('enc-authorization-data', 'enc_authorization_data', ParseType.token),
1334        ('additional-tickets', 'additional_tickets', ParseType.token),
1335    ]
1336
1337    def __init__(
1338        self,
1339        kdc_options: int,
1340        cname: typing.Optional[PrincipalName],
1341        realm: bytes,
1342        sname: PrincipalName,
1343        postdated_from: typing.Optional[datetime.datetime],
1344        postdated_till: datetime.datetime,
1345        rtime: typing.Optional[datetime.datetime],
1346        nonce: int,
1347        etype: typing.List[KerberosEncryptionType],
1348        addresses: typing.Optional[typing.List[HostAddress]],
1349        enc_authorization_data: typing.Optional[EncryptedData],
1350        additional_tickets: typing.Optional[typing.List[Ticket]],
1351    ) -> None:
1352        self.kdc_options = kdc_options
1353        self.cname = cname
1354        self.realm = realm
1355        self.sname = sname
1356        self.postdated_from = postdated_from
1357        self.postdated_till = postdated_till
1358        self.rtime = rtime
1359        self.nonce = nonce
1360        self.etype = etype
1361        self.addresses = addresses
1362        self.enc_authorization_data = enc_authorization_data
1363        self.additional_tickets = additional_tickets
1364
1365    @staticmethod
1366    def unpack(value: typing.Union[ASN1Value, bytes]) -> "KdcReqBody":
1367        sequence = unpack_asn1_tagged_sequence(value)
1368
1369        def unpack_kdc_options(value):
1370            b_data = unpack_asn1_bit_string(value)
1371            return struct.unpack(">I", b_data)[0]
1372
1373        def unpack_etype(value):
1374            return [KerberosEncryptionType(unpack_asn1_integer(e)) for e in unpack_asn1_sequence(value)]
1375
1376        def unpack_addresses(value):
1377            return [unpack_hostname(h) for h in unpack_asn1_sequence(value)]
1378
1379        def unpack_ticket(value):
1380            return [Ticket.unpack(t) for t in unpack_asn1_sequence(value)]
1381
1382        kdc_options = get_sequence_value(sequence, 0, 'KDC-REQ-BODY', 'kdc-options', unpack_kdc_options)
1383        cname = get_sequence_value(sequence, 1, 'KDC-REQ-BODY', 'cname', unpack_principal_name)
1384        realm = get_sequence_value(sequence, 2, 'KDC-REQ-BODY', 'realm', unpack_asn1_general_string)
1385        sname = get_sequence_value(sequence, 3, 'KDC-REQ-BODY', 'sname', unpack_principal_name)
1386        postdated_from = get_sequence_value(sequence, 4, 'KDC-REQ-BODY', 'from', unpack_asn1_generalized_time)
1387        postdated_till = get_sequence_value(sequence, 5, 'KDC-REQ-BODY', 'till', unpack_asn1_generalized_time)
1388        rtime = get_sequence_value(sequence, 6, 'KDC-REQ-BODY', 'rtime', unpack_asn1_generalized_time)
1389        nonce = get_sequence_value(sequence, 7, 'KDC-REQ-BODY', 'nonce', unpack_asn1_integer)
1390        etype = get_sequence_value(sequence, 8, 'KDC-REQ-BODY', 'etype', unpack_etype)
1391        addresses = get_sequence_value(sequence, 9, 'KDC-REQ-BODY', 'addresses', unpack_addresses)
1392        enc_auth_data = get_sequence_value(sequence, 10, 'KDC-REQ-BODY', 'enc-authorization-data',
1393                                           EncryptedData.unpack)
1394        additional_tickets = get_sequence_value(sequence, 11, 'KDC-REQ-BODY', 'additional-tickets', unpack_ticket)
1395
1396        return KdcReqBody(kdc_options, cname, realm, sname, postdated_from, postdated_till, rtime, nonce, etype,
1397                          addresses, enc_auth_data, additional_tickets)
1398