# Sources used:
# https://zeroshell.org/kerberos/kerberos-operation/
# https://tools.ietf.org/html/rfc4120
# https://tools.ietf.org/html/rfc6113 (FAST extension)

# TODO:
# https://tools.ietf.org/html/rfc4556

from asn1crypto import core
import enum
import os

# KerberosV5Spec2 DEFINITIONS EXPLICIT TAGS ::=
TAG = 'explicit'

# class
UNIVERSAL = 0
APPLICATION = 1
CONTEXT = 2
krb5_pvno = 5 #-- current Kerberos protocol version number

class PADATA_TYPE(core.Enumerated):
	_map = {
		0   : 'NONE', #(0),
		1   : 'TGS-REQ', #(1), #		1   : 'AP-REQ', #(1),
		2   : 'ENC-TIMESTAMP', #(2),
		3   : 'PW-SALT', #(3),
		5   : 'ENC-UNIX-TIME', #(5),
		6   : 'SANDIA-SECUREID', #(6),
		7   : 'SESAME', #(7),
		8   : 'OSF-DCE', #(8),
		9   : 'CYBERSAFE-SECUREID', #(9),
		10  : 'AFS3-SALT', #(10),
		11  : 'ETYPE-INFO', #(11),
		12  : 'SAM-CHALLENGE', #(12), -- ', #(sam/otp)
		13  : 'SAM-RESPONSE', #(13), -- ', #(sam/otp)
		14  : 'PK-AS-REQ-19', #(14), -- ', #(PKINIT-19)
		15  : 'PK-AS-REP-19', #(15), -- ', #(PKINIT-19)
		15  : 'PK-AS-REQ-WIN', #(15), -- ', #(PKINIT - old number)
		16  : 'PK-AS-REQ', #(16), -- ', #(PKINIT-25)
		17  : 'PK-AS-REP', #(17), -- ', #(PKINIT-25)
		18  : 'PA-PK-OCSP-RESPONSE', #(18),
		19  : 'ETYPE-INFO2', #(19),
		20  : 'USE-SPECIFIED-KVNO', #(20),
		20  : 'SVR-REFERRAL-INFO', #(20), --- old ms referral number
		21  : 'SAM-REDIRECT', #(21), -- ', #(sam/otp)
		22  : 'GET-FROM-TYPED-DATA', #(22),
		23  : 'SAM-ETYPE-INFO', #(23),
		25  : 'SERVER-REFERRAL', #(25),
		24  : 'ALT-PRINC', #(24),		-- ', #(crawdad@fnal.gov)
		30  : 'SAM-CHALLENGE2', #(30),		-- ', #(kenh@pobox.com)
		31  : 'SAM-RESPONSE2', #(31),		-- ', #(kenh@pobox.com)
		41  : 'PA-EXTRA-TGT', #(41),			-- Reserved extra TGT
		102 : 'TD-KRB-PRINCIPAL', #(102),	-- PrincipalName
		104 : 'PK-TD-TRUSTED-CERTIFIERS', #(104), -- PKINIT
		105 : 'PK-TD-CERTIFICATE-INDEX', #(105), -- PKINIT
		106 : 'TD-APP-DEFINED-ERROR', #(106),	-- application specific
		107 : 'TD-REQ-NONCE', #(107),		-- INTEGER
		108 : 'TD-REQ-SEQ', #(108),		-- INTEGER
		128 : 'PA-PAC-REQUEST', #(128),	-- jbrezak@exchange.microsoft.com
		129 : 'PA-FOR-USER', #(129),		-- MS-KILE
		130 : 'FOR-X509-USER', #(130),		-- MS-KILE
		131 : 'FOR-CHECK-DUPS', #(131),	-- MS-KILE
		132 : 'AS-CHECKSUM', #(132),		-- MS-KILE
		132 : 'PK-AS-09-BINDING', #(132),	-- client send this to -- tell KDC that is supports -- the asCheckSum in the --  PK-AS-REP
		133 : 'CLIENT-CANONICALIZED', #(133),	-- referals
		133 : 'FX-COOKIE', #(133),		-- krb-wg-preauth-framework
		134 : 'AUTHENTICATION-SET', #(134),	-- krb-wg-preauth-framework
		135 : 'AUTH-SET-SELECTED', #(135),	-- krb-wg-preauth-framework
		136 : 'FX-FAST', #(136),		-- krb-wg-preauth-framework
		137 : 'FX-ERROR', #(137),		-- krb-wg-preauth-framework
		138 : 'ENCRYPTED-CHALLENGE', #(138),	-- krb-wg-preauth-framework
		141 : 'OTP-CHALLENGE', #(141),		-- ', #(gareth.richards@rsa.com)
		142 : 'OTP-REQUEST', #(142),		-- ', #(gareth.richards@rsa.com)
		143 : 'OTP-CONFIRM', #(143),		-- ', #(gareth.richards@rsa.com)
		144 : 'OTP-PIN-CHANGE', #(144),	-- ', #(gareth.richards@rsa.com)
		145 : 'EPAK-AS-REQ', #(145),
		146 : 'EPAK-AS-REP', #(146),
		147 : 'PKINIT-KX', #(147),		-- krb-wg-anon
		148 : 'PKU2U-NAME', #(148),		-- zhu-pku2u
		149 : 'REQ-ENC-PA-REP', #(149),	--
		151 : 'SPAKE', #(151),	https://datatracker.ietf.org/doc/draft-ietf-kitten-krb-spake-preauth/?include_text=1
		165 : 'SUPPORTED-ETYPES', #(165)	-- MS-KILE
		167 : 'PA-PAC-OPTIONS',
	}
	
class AUTHDATA_TYPE(core.Enumerated):
	_map = {
		1 : 'IF-RELEVANT', #1),
		2 : 'INTENDED-FOR-SERVER', #2),
		3 : 'INTENDED-FOR-APPLICATION-CLASS', #3),
		4 : 'KDC-ISSUED', #4),
		5 : 'AND-OR', #5),
		6 : 'MANDATORY-TICKET-EXTENSIONS', #6),
		7 : 'IN-TICKET-EXTENSIONS', #7),
		8 : 'MANDATORY-FOR-KDC', #8),
		9 : 'INITIAL-VERIFIED-CAS', #9),
		64 : 'OSF-DCE', #64),
		65 : 'SESAME', #65),
		66 : 'OSF-DCE-PKI-CERTID', #66),
		70 : 'AD-authentication-strength',
		71 : 'AD-fx-fast-armor',
		72 : 'AD-fx-fast-used',
		128 : 'WIN2K-PAC', #128),
		129 : 'GSS-API-ETYPE-NEGOTIATION', #129), -- Authenticator only
		-17 : 'SIGNTICKET-OLDER', #-17),
		142 : 'SIGNTICKET-OLD', #142),
		512 : 'SIGNTICKET', #512)
	}

class CKSUMTYPE(core.Enumerated):
	_map = {
		0 : 'NONE', #0),
		1 : 'CRC32', #1),
		2 : 'RSA_MD4', #2),
		3 : 'RSA_MD4_DES', #3),
		4 : 'DES_MAC', #4),
		5 : 'DES_MAC_K', #5),
		6 : 'RSA_MD4_DES_K', #6),
		7 : 'RSA_MD5', #7),
		8 : 'RSA_MD5_DES', #8),
		9 : 'RSA_MD5_DES3', #9),
		10 : 'SHA1_OTHER', #10),
		12 : 'HMAC_SHA1_DES3', #12),
		14 : 'SHA1', #14),
		15 : 'HMAC_SHA1_96_AES_128', #15),
		16 : 'HMAC_SHA1_96_AES_256', #16),
		0x8003 : 'GSSAPI', #0x8003),
		-138 : 'HMAC_MD5', #-138),	-- unofficial microsoft number
		-1138 : 'HMAC_MD5_ENC', #-1138)	-- even more unofficial
	}

#enctypes
class ENCTYPE(core.Enumerated):
	_map = {
		0 : 'NULL', #0),
		1 : 'DES_CBC_CRC', #1),
		2 : 'DES_CBC_MD4', #2),
		3 : 'DES_CBC_MD5', #3),
		5 : 'DES3_CBC_MD5', #5),
		7 : 'OLD_DES3_CBC_SHA1', #7),
		8 : 'SIGN_DSA_GENERATE', #8),
		9 : 'ENCRYPT_RSA_PRIV', #9),
		10 : 'ENCRYPT_RSA_PUB', #10),
		16 : 'DES3_CBC_SHA1', #16),	-- with key derivation
		17 : 'AES128_CTS_HMAC_SHA1_96', #17),
		18 : 'AES256_CTS_HMAC_SHA1_96', #18),
		23 : 'ARCFOUR_HMAC_MD5', #23),
		24 : 'ARCFOUR_HMAC_MD5_56', #24),
		48 : 'ENCTYPE_PK_CROSS', #48),
		#-- some "old" windows types
		-128 : 'ARCFOUR_MD4', #-128),
		-133 : 'ARCFOUR_HMAC_OLD', #-133),
		-135 : 'ARCFOUR_HMAC_OLD_EXP', #-135),
		#-- these are for Heimdal internal use
		-0x1000 : 'DES_CBC_NONE', #-0x1000),
		-0x1001 : 'DES3_CBC_NONE', #-0x1001),
		-0x1002 : 'DES_CFB64_NONE', #-0x1002),
		-0x1003 : 'DES_PCBC_NONE', #-0x1003),
		-0x1004 : 'DIGEST_MD5_NONE', #-0x1004),		-- private use, lukeh@padl.com
		-0x1005 : 'CRAM_MD5_NONE', #-0x1005)		-- private use, lukeh@padl.com
	}
	
class SequenceOfEnctype(core.SequenceOf):
	_child_spec = core.Integer

class Microseconds(core.Integer):
	"""    ::= INTEGER (0..999999)
	-- microseconds
    """      
class krb5int32 (core.Integer):
    """krb5int32  ::= INTEGER (-2147483648..2147483647)
    """


class krb5uint32 (core.Integer):
    """krb5uint32  ::= INTEGER (0..4294967295)
    """

class KerberosString(core.GeneralString):
	"""KerberosString ::= GeneralString (IA5String)
	For compatibility, implementations MAY choose to accept GeneralString
	values that contain characters other than those permitted by
	IA5String...
	"""
	_encoding = 'utf-8'
	
class SequenceOfKerberosString(core.SequenceOf):
	_child_spec = KerberosString
	
# https://github.com/tiran/kkdcpasn1/blob/asn1crypto/pykkdcpasn1.py
class Realm(KerberosString):
	"""Realm ::= KerberosString
	"""

	
# https://github.com/tiran/kkdcpasn1/blob/asn1crypto/pykkdcpasn1.py
class PrincipalName(core.Sequence):
	"""PrincipalName for KDC-REQ-BODY and Ticket
	PrincipalName ::= SEQUENCE {
		name-type	[0] Int32,
		name-string  [1] SEQUENCE OF KerberosString
	}
	"""
	_fields = [
		('name-type', krb5int32, {'tag_type': TAG, 'tag': 0}),
		('name-string', SequenceOfKerberosString, {'tag_type': TAG, 'tag': 1}),
	]
	
	
class Principal(core.Sequence):
	_fields = [
		('name', PrincipalName, {'tag_type': TAG, 'tag': 0}),
		('realm', Realm, {'tag_type': TAG, 'tag': 1}),
	]

	
class Principals(core.SequenceOf):
	_child_spec = Principal

	
class HostAddress(core.Sequence):
    """HostAddress for HostAddresses
    HostAddress ::= SEQUENCE {
        addr-type        [0] Int32,
        address  [1] OCTET STRING
    }
    """
    _fields = [
        ('addr-type', krb5int32, {'tag_type': TAG, 'tag': 0}),
        ('address', core.OctetString, {'tag_type': TAG, 'tag': 1}),
]


class HostAddresses(core.SequenceOf):
	"""SEQUENCE OF HostAddress
	"""
	_child_spec = HostAddress
	
	
class KerberosTime(core.GeneralizedTime):
    """KerberosTime ::= GeneralizedTime
    """

	
class AuthorizationDataElement(core.Sequence):
	_fields = [
        ('ad-type', krb5int32, {'tag_type': TAG, 'tag': 0}),
        ('ad-data', core.OctetString, {'tag_type': TAG, 'tag': 1}),
	]

	
class AuthorizationData(core.SequenceOf):
	"""SEQUENCE OF HostAddress
	"""
	_child_spec = AuthorizationDataElement
	

class APOptions(core.BitString):
	_map = {
		0 : 'reserved', #(0),
		1 : 'use-session-key', #(1),
		2 : 'mutual-required', #(2)
	}

	
class TicketFlags(core.BitString):
	_map = {
		0: 'reserved',
		1: 'forwardable',
		2: 'forwarded',
		3: 'proxiable',
		4: 'proxy',
		5: 'may-postdate',
		6: 'postdated',
		7: 'invalid',
		8: 'renewable',
		9: 'initial',
		10: 'pre-authent',
		11: 'hw-authent',
		12: 'transited-policy-checked',
		13: 'ok-as-delegate',
		14: 'anonymous',
		15: 'enc-pa-rep'
	}


class KDCOptions(core.BitString):
	_map = {
		0: 'reserved',
		1: 'forwardable',
		2: 'forwarded',
		3: 'proxiable',
		4: 'proxy',
		5: 'allow-postdate',
		6: 'postdated',
		7: 'unused7',
		8: 'renewable',
		9: 'unused9',
		10: 'unused10',
		11: 'opt-hardware-auth',
		12: 'unused12',
		13: 'unused13',
		14: 'constrained-delegation', #-- cname-in-addl-tkt (14) 
		15: 'canonicalize',
		16: 'request-anonymous',
		17: 'unused17',
		18: 'unused18',
		19: 'unused19',
		20: 'unused20',
		21: 'unused21',
		22: 'unused22',
		23: 'unused23',
		24: 'unused24',
		25: 'unused25',
		26: 'disable-transited-check',
		27: 'renewable-ok',
		28: 'enc-tkt-in-skey',
		30: 'renew',
		31: 'validate',
	}

class LR_TYPE(core.Enumerated):
	_map = {
		0 : 'NONE', #0),		-- no information
		1 : 'INITIAL_TGT', #1),	-- last initial TGT request
		2 : 'INITIAL', #2),		-- last initial request
		3 : 'ISSUE_USE_TGT', #3),	-- time of newest TGT used
		4 : 'RENEWAL', #4),		-- time of last renewal
		5 : 'REQUEST', #5),		-- time of last request ', #of any type)
		6 : 'PW_EXPTIME', #6),	-- expiration time of password
		7 : 'ACCT_EXPTIME', #7)	-- expiration time of account
	}
	
class LastReqInner(core.Sequence):
	_fields = [
		('lr-type', krb5int32, {'tag_type': TAG, 'tag': 0}), #LR_TYPE
		('lr-value', KerberosTime, {'tag_type': TAG, 'tag': 1}),
	]

class LastReq(core.SequenceOf):
	_child_spec = LastReqInner


class EncryptedData(core.Sequence):
	_fields = [
		('etype', krb5int32, {'tag_type': TAG, 'tag': 0}), #-- EncryptionType
		('kvno', krb5uint32, {'tag_type': TAG, 'tag': 1, 'optional': True}), #
		('cipher', core.OctetString, {'tag_type': TAG, 'tag': 2}), #ciphertext
	]


class EncryptionKey(core.Sequence):
	_fields = [
		('keytype', krb5uint32, {'tag_type': TAG, 'tag': 0}), #-- EncryptionType
		('keyvalue', core.OctetString, {'tag_type': TAG, 'tag': 1}), #
	]

class EncryptionKeys(core.SequenceOf):
	_child_spec = EncryptionKey

class KERB_KEY_LIST_REP(EncryptionKeys):
	pass

class KERB_KEY_LIST_REQ(core.SequenceOf):
	_child_spec = krb5uint32 #core.Integer

#-- encoded Transited field

class TransitedEncoding(core.Sequence):
	_fields = [
		('tr-type', krb5uint32, {'tag_type': TAG, 'tag': 0}), #-- must be registered
		('contents', core.OctetString, {'tag_type': TAG, 'tag': 1}), #
	]


# https://github.com/tiran/kkdcpasn1/blob/asn1crypto/pykkdcpasn1.py
class Ticket(core.Sequence):
	explicit = (APPLICATION,1)
	
	_fields = [
		('tkt-vno', krb5int32, {'tag_type': TAG, 'tag': 0}),
		('realm', Realm, {'tag_type': TAG, 'tag': 1}),
		('sname', PrincipalName, {'tag_type': TAG, 'tag': 2}),
		('enc-part', EncryptedData, {'tag_type': TAG, 'tag': 3}), #EncTicketPart
	]
	
class SequenceOfTicket(core.SequenceOf):
	"""SEQUENCE OF Ticket for KDC-REQ-BODY
	"""
	_child_spec = Ticket


#-- Encrypted part of ticket
class EncTicketPart(core.Sequence):
	explicit = (APPLICATION, 3)
	
	_fields = [
		('flags', TicketFlags, {'tag_type': TAG, 'tag': 0}),
		('key', EncryptionKey, {'tag_type': TAG, 'tag': 1}),
		('crealm', Realm, {'tag_type': TAG, 'tag': 2}),
		('cname', PrincipalName, {'tag_type': TAG, 'tag': 3}),
		('transited', TransitedEncoding, {'tag_type': TAG, 'tag': 4}),
		('authtime', KerberosTime, {'tag_type': TAG, 'tag': 5}),
		('starttime', KerberosTime, {'tag_type': TAG, 'tag': 6, 'optional': True}),
		('endtime', KerberosTime, {'tag_type': TAG, 'tag': 7}),
		('renew-till', KerberosTime, {'tag_type': TAG, 'tag': 8, 'optional': True}),
		('caddr', HostAddresses, {'tag_type': TAG, 'tag': 9, 'optional': True}),
		('authorization-data', AuthorizationData, {'tag_type': TAG, 'tag': 10, 'optional': True}),
	]


class Checksum(core.Sequence):
	_fields = [
		('cksumtype', krb5int32, {'tag_type': TAG, 'tag': 0}), #CKSUMTYPE
		('checksum', core.OctetString, {'tag_type': TAG, 'tag': 1}),
	]


class Authenticator(core.Sequence):
	explicit = (APPLICATION,2)
	
	_fields = [
		('authenticator-vno', krb5int32, {'tag_type': TAG, 'tag': 0}),
		('crealm', Realm, {'tag_type': TAG, 'tag': 1}),
		('cname', PrincipalName, {'tag_type': TAG, 'tag': 2}),
		('cksum', Checksum, {'tag_type': TAG, 'tag': 3, 'optional': True}),
		('cusec', krb5int32, {'tag_type': TAG, 'tag': 4}),
		('ctime', KerberosTime, {'tag_type': TAG, 'tag': 5}),
		('subkey', EncryptionKey, {'tag_type': TAG, 'tag': 6, 'optional': True}),
		('seq-number', krb5uint32, {'tag_type': TAG, 'tag': 7, 'optional': True}),
		('authorization-data', AuthorizationData, {'tag_type': TAG, 'tag': 8, 'optional': True}),
	]


class PA_DATA(core.Sequence): #!!!! IT STARTS AT ONE!!!!
	_fields = [
		('padata-type', core.Integer, {'tag_type': TAG, 'tag': 1}),
		('padata-value', core.OctetString, {'tag_type': TAG, 'tag': 2}),
	]
	
class ETYPE_INFO_ENTRY(core.Sequence):
	_fields = [
		('etype', krb5int32, {'tag_type': TAG, 'tag': 0}),
		('salt', core.OctetString, {'tag_type': TAG, 'tag': 1, 'optional': True}),
		('salttype', krb5int32, {'tag_type': TAG, 'tag': 2, 'optional': True}),
	]

class ETYPE_INFO(core.SequenceOf):
	_child_spec = ETYPE_INFO_ENTRY


class ETYPE_INFO2_ENTRY(core.Sequence):
	_fields = [
		('etype', krb5int32, {'tag_type': TAG, 'tag': 0}),
		('salt', KerberosString, {'tag_type': TAG, 'tag': 1, 'optional': True}),
		('s2kparams', core.OctetString, {'tag_type': TAG, 'tag': 2, 'optional': True}),
	]
	
class ETYPE_INFO2(core.SequenceOf):
	_child_spec = ETYPE_INFO2_ENTRY

class METHOD_DATA(core.SequenceOf):
	_child_spec = PA_DATA


class TypedData(core.Sequence):
	_fields = [
		('data-type', krb5int32, {'tag_type': TAG, 'tag': 0}),
		('data-value', core.OctetString, {'tag_type': TAG, 'tag': 1, 'optional': True}),
	]

"""
class TYPED-DATA ::= SEQUENCE SIZE (1..MAX) OF TypedData
"""


class KDC_REQ_BODY(core.Sequence):
	_fields = [
		('kdc-options', KDCOptions, {'tag_type': TAG, 'tag': 0}),
		('cname', PrincipalName, {'tag_type': TAG, 'tag': 1, 'optional': True}),
		('realm', Realm, {'tag_type': TAG, 'tag': 2}),
		('sname', PrincipalName , {'tag_type': TAG, 'tag': 3, 'optional': True}),
		('from', KerberosTime , {'tag_type': TAG, 'tag': 4, 'optional': True}),
		('till', KerberosTime , {'tag_type': TAG, 'tag': 5, 'optional': True}),
		('rtime', KerberosTime , {'tag_type': TAG, 'tag': 6, 'optional': True}),
		('nonce', krb5int32 , {'tag_type': TAG, 'tag': 7}),
		('etype', SequenceOfEnctype , {'tag_type': TAG, 'tag': 8}), # -- EncryptionType,preference order
		('addresses', HostAddresses , {'tag_type': TAG, 'tag': 9, 'optional': True}),
		('enc-authorization-data', EncryptedData , {'tag_type': TAG, 'tag': 10, 'optional': True}), #-- Encrypted AuthorizationData encoding
		('additional-tickets', SequenceOfTicket , {'tag_type': TAG, 'tag': 11, 'optional': True}),
	
	]

class KDC_REQ(core.Sequence):
	_fields = [
		('pvno', krb5int32, {'tag_type': TAG, 'tag': 1}),
		('msg-type', krb5int32, {'tag_type': TAG, 'tag': 2}), #MESSAGE_TYPE
		('padata', METHOD_DATA , {'tag_type': TAG, 'tag': 3, 'optional': True}),
		('req-body', KDC_REQ_BODY , {'tag_type': TAG, 'tag': 4}),
	]


class AS_REQ(KDC_REQ):
	explicit = (APPLICATION, 10)
	
class TGS_REQ(KDC_REQ):
	explicit = (APPLICATION, 12)

class FastOptions(core.BitString):
	_map = {
		0:  'reserved',
		1:  'hide-client-names',
		2:  'critical_2', #forwarded',
		3:  'critical_3', #proxiable',
		4:  'critical_4', #proxy',
		5:  'critical_5', #may-postdate',
		6:  'critical_6', #postdated',
		7:  'critical_7', #invalid',
		8:  'critical_8', #renewable',
		9:  'critical_9', #initial',
		10: 'critical_10', #pre-authent',
		11: 'critical_11', #hw-authent',
		12: 'critical_12', #transited-policy-checked',
		13: 'critical_13', #ok-as-delegate',
		14: 'critical_14', #anonymous',
		15: 'critical_15', #enc-pa-rep',
		16: 'kdc-follow-referrals'
	}
	# error: KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS   93
#5.4.1.  FAST Armors

class EncryptedChallenge(EncryptedData):
	pass

class AUTHENTICATION_SET_ELEM(core.SequenceOf):
	_fields = [
		('pa-type', krb5int32, {'tag_type': TAG, 'tag': 0}),
		('pa-hint', core.OctetString, {'tag_type': TAG, 'tag': 1 , 'optional': True}),
		('pa-value', core.OctetString, {'tag_type': TAG, 'tag': 1, 'optional': True}),
	]

class AUTHENTICATION_SET(core.SequenceOf):
	_child_spec = AUTHENTICATION_SET_ELEM

class KrbFastArmor(core.Sequence):
	_fields = [
		('armor-type', krb5int32, {'tag_type': TAG, 'tag': 0}),
		('armor-value', core.OctetString, {'tag_type': TAG, 'tag': 1}),
	]


class KrbFastArmoredReq(core.Sequence):
	_fields = [
		('armor', KrbFastArmor, {'tag_type': TAG, 'tag': 0, 'optional':True}),
		('req-checksum', Checksum, {'tag_type': TAG, 'tag': 1}),
		('enc-fast-req', EncryptedData, {'tag_type': TAG, 'tag': 2}), #KrbFastReq #KEY_USAGE_FAST_REQ_CHKSUM          50 #KEY_USAGE_FAST_ENC                 51
	]

class KrbFastReq(core.Sequence):
	_fields = [
		('fast-options', FastOptions, {'tag_type': TAG, 'tag': 0}),
		('padata', METHOD_DATA, {'tag_type': TAG, 'tag': 1}),
		('req-body', KDC_REQ_BODY, {'tag_type': TAG, 'tag': 2}),
	]


class KrbFastFinished(core.Sequence):
	_fields = [
		('timestamp', KerberosTime, {'tag_type': TAG, 'tag': 0}), 
		('usec', Microseconds, {'tag_type': TAG, 'tag': 1}), 
		('crealm', Realm, {'tag_type': TAG, 'tag': 2}), 
		('cname', PrincipalName, {'tag_type': TAG, 'tag': 3}), 
		('ticket-checksum', Checksum, {'tag_type': TAG, 'tag': 4}), #KEY_USAGE_FAST_FINISHED            53

	]

class KrbFastArmoredRep(core.Sequence):
	_fields = [
		('enc-fast-rep', EncryptedData, {'tag_type': TAG, 'tag': 0}), #KrbFastResponse KEY_USAGE_FAST_REP                 52
	]

class KrbFastResponse(core.Sequence):
	_fields = [
		('padata', METHOD_DATA, {'tag_type': TAG, 'tag': 0}),
		('strengthen-key', EncryptionKey, {'tag_type': TAG, 'tag': 1, 'optional':True}),
		('finished', KrbFastFinished, {'tag_type': TAG, 'tag': 2, 'optional':True}), #KrbFastReq #KEY_USAGE_FAST_REQ_CHKSUM          50 #KEY_USAGE_FAST_ENC                 51
		('nonce', krb5uint32, {'tag_type': TAG, 'tag': 3}),
	]

#-- padata-type ::= PA-ENC-TIMESTAMP
#-- padata-value ::= EncryptedData - PA-ENC-TS-ENC

class PA_PAC_OPTIONSTypes(core.BitString):
	_map = {
			0: 'Claims',
			1: 'Branch Aware',
			2: 'Forward to Full DC',
			3: 'resource-based constrained delegation',
		}

class PA_FX_FAST_REQUEST(core.Choice):
	_alternatives = [
		('armored-data', KrbFastArmoredReq, {'explicit': (CONTEXT,0) }  ),
	]

class PA_FX_FAST_REPLY(core.Choice):
	_alternatives = [
		('armored-data', KrbFastArmoredRep, {'explicit': (CONTEXT,0) }  ),
	]



class PA_PAC_OPTIONS(core.Sequence):
	_fields = [
		('value', PA_PAC_OPTIONSTypes, {'tag_type': TAG, 'tag': 0}),
	]
	

	

class PA_ENC_TS_ENC(core.Sequence):
	_fields = [
		('patimestamp', KerberosTime, {'tag_type': TAG, 'tag': 0}), #-- client's time
		('pausec', krb5int32, {'tag_type': TAG, 'tag': 1, 'optional':True}),
	]

#-- draft-brezak-win2k-krb-authz-01
class PA_PAC_REQUEST(core.Sequence):
	_fields = [
		('include-pac', core.Boolean, {'tag_type': TAG, 'tag': 0}), #-- Indicates whether a PAC should be included or not
	]

#-- PacketCable provisioning server location, PKT-SP-SEC-I09-030728.pdf
class PROV_SRV_LOCATION(core.GeneralString):
	pass


class KDC_REP(core.Sequence):
	_fields = [
		('pvno', core.Integer, {'tag_type': TAG, 'tag': 0}),
		('msg-type', krb5int32, {'tag_type': TAG, 'tag': 1}), #MESSAGE_TYPE
		('padata', METHOD_DATA, {'tag_type': TAG, 'tag': 2, 'optional': True}),
		('crealm', Realm , {'tag_type': TAG, 'tag': 3}),
		('cname', PrincipalName , {'tag_type': TAG, 'tag': 4}),
		('ticket', Ticket , {'tag_type': TAG, 'tag': 5}),
		('enc-part', EncryptedData , {'tag_type': TAG, 'tag': 6}), #EncKDCRepPart
	]
	

class AS_REP(KDC_REP):
	#::= [APPLICATION 11] KDC-REP
	explicit = (APPLICATION, 11)
	
class TGS_REP(KDC_REP): # ::= [APPLICATION 13] KDC-REP
	explicit = (APPLICATION, 13)
	
	
class EncKDCRepPart(core.Sequence):
	_fields = [
		('key', EncryptionKey, {'tag_type': TAG, 'tag': 0}),
		('last-req', LastReq, {'tag_type': TAG, 'tag': 1}),
		('nonce', krb5int32, {'tag_type': TAG, 'tag': 2}),
		('key-expiration', KerberosTime , {'tag_type': TAG, 'tag': 3, 'optional': True}),
		('flags', TicketFlags , {'tag_type': TAG, 'tag': 4}),
		('authtime', KerberosTime , {'tag_type': TAG, 'tag': 5}),
		('starttime', KerberosTime , {'tag_type': TAG, 'tag': 6, 'optional': True}),
		('endtime', KerberosTime , {'tag_type': TAG, 'tag': 7}),
		('renew-till', KerberosTime , {'tag_type': TAG, 'tag': 8, 'optional': True}),
		('srealm', Realm , {'tag_type': TAG, 'tag': 9}),
		('sname', PrincipalName , {'tag_type': TAG, 'tag': 10}),
		('caddr', HostAddresses , {'tag_type': TAG, 'tag': 11, 'optional': True}),
		('encrypted-pa-data', METHOD_DATA , {'tag_type': TAG, 'tag': 12, 'optional': True}),
	]

class EncASRepPart(EncKDCRepPart):
	explicit = (APPLICATION, 25)
	
class EncTGSRepPart(EncKDCRepPart):
	explicit = (APPLICATION, 26)

class AP_REQ(core.Sequence):
	explicit = (APPLICATION, 14)
	_fields = [
		('pvno', krb5int32, {'tag_type': TAG, 'tag': 0}),
		('msg-type', krb5int32, {'tag_type': TAG, 'tag': 1}), #MESSAGE_TYPE
		('ap-options', APOptions, {'tag_type': TAG, 'tag': 2}),
		('ticket', Ticket , {'tag_type': TAG, 'tag': 3}),
		('authenticator', EncryptedData , {'tag_type': TAG, 'tag': 4}),
	]

class AP_REP(core.Sequence):
	explicit = (APPLICATION, 15)
	_fields = [
		('pvno', krb5int32, {'tag_type': TAG, 'tag': 0}),
		('msg-type', krb5int32, {'tag_type': TAG, 'tag': 1}),#MESSAGE_TYPE
		('enc-part', EncryptedData , {'tag_type': TAG, 'tag': 2}),
	]


class EncAPRepPart(core.Sequence):
	explicit = (APPLICATION, 27)
	_fields = [
		('ctime', KerberosTime, {'tag_type': TAG, 'tag': 0}),
		('cusec', krb5int32, {'tag_type': TAG, 'tag': 1}),
		('subkey', EncryptionKey , {'tag_type': TAG, 'tag': 2, 'optional': True}),
		('seq-number', krb5uint32 , {'tag_type': TAG, 'tag': 3, 'optional': True}),
	]


class KRB_SAFE_BODY(core.Sequence):
	_fields = [
		('user-data', core.OctetString, {'tag_type': TAG, 'tag': 0}),
		('timestamp', KerberosTime, {'tag_type': TAG, 'tag': 1, 'optional': True}),
		('usec', krb5int32 , {'tag_type': TAG, 'tag': 2, 'optional': True}),
		('seq-number', krb5uint32 , {'tag_type': TAG, 'tag': 3, 'optional': True}),
		('s-address', HostAddress , {'tag_type': TAG, 'tag': 4, 'optional': True}),
		('r-address', HostAddress , {'tag_type': TAG, 'tag': 5, 'optional': True}),
	]


class KRB_SAFE(core.Sequence):
	explicit = (APPLICATION, 20)
	_fields = [
		('pvno', krb5int32, {'tag_type': TAG, 'tag': 0}),
		('msg-type', krb5int32, {'tag_type': TAG, 'tag': 1}),#MESSAGE_TYPE
		('safe-body', KRB_SAFE_BODY , {'tag_type': TAG, 'tag': 2}),
		('cksum', Checksum , {'tag_type': TAG, 'tag': 3}),
	]

class KRB_PRIV(core.Sequence):
	explicit = (APPLICATION, 21)
	_fields = [
		('pvno', krb5int32, {'tag_type': TAG, 'tag': 0}),
		('msg-type', krb5int32, {'tag_type': TAG, 'tag': 1}),#MESSAGE_TYPE
		('enc-part', EncryptedData, {'tag_type': TAG, 'tag': 3}),
	] 


class EncKrbPrivPart(core.Sequence):
	explicit = (APPLICATION, 28)
	_fields = [
		('user-data', core.OctetString, {'tag_type': TAG, 'tag': 0}),
		('timestamp', KerberosTime, {'tag_type': TAG, 'tag': 1, 'optional': True}),
		('usec', krb5int32 , {'tag_type': TAG, 'tag': 2, 'optional': True}),
		('seq-number', krb5uint32 , {'tag_type': TAG, 'tag': 3, 'optional': True}),
		('s-address', HostAddress , {'tag_type': TAG, 'tag': 4, 'optional': True}),
		('r-address', HostAddress , {'tag_type': TAG, 'tag': 5, 'optional': True}),
	]


class KRB_CRED(core.Sequence):
	explicit = (APPLICATION, 22)
	_fields = [
		('pvno', core.Integer, {'tag_type': TAG, 'tag': 0}),
		('msg-type', core.Integer, {'tag_type': TAG, 'tag': 1}),
		('tickets', SequenceOfTicket, {'tag_type': TAG, 'tag': 2}),
		('enc-part', EncryptedData , {'tag_type': TAG, 'tag': 3}),
	
	]
	
# http://web.mit.edu/freebsd/head/crypto/heimdal/lib/asn1/krb5.asn1
class KrbCredInfo(core.Sequence):
	_fields = [
		('key', EncryptionKey, {'tag_type': TAG, 'tag': 0}),
		('prealm', Realm, {'tag_type': TAG, 'tag': 1, 'optional': True}),
		('pname', PrincipalName, {'tag_type': TAG, 'tag': 2, 'optional': True}),
		('flags', TicketFlags , {'tag_type': TAG, 'tag': 3, 'optional': True}),
		('authtime', KerberosTime , {'tag_type': TAG, 'tag': 4, 'optional': True}),
		('starttime', KerberosTime , {'tag_type': TAG, 'tag': 5, 'optional': True}),
		('endtime', KerberosTime , {'tag_type': TAG, 'tag': 6, 'optional': True}),
		('renew-till', KerberosTime , {'tag_type': TAG, 'tag': 7, 'optional': True}),
		('srealm', Realm , {'tag_type': TAG, 'tag': 8, 'optional': True}),
		('sname', PrincipalName , {'tag_type': TAG, 'tag': 9, 'optional': True}),
		('caddr', HostAddresses , {'tag_type': TAG, 'tag': 10, 'optional': True}),
	]
	
class SequenceOfKrbCredInfo(core.SequenceOf):
	_child_spec = KrbCredInfo
	
class EncKrbCredPart(core.Sequence):
	explicit = (APPLICATION, 29)
	_fields = [
		('ticket-info', SequenceOfKrbCredInfo, {'tag_type': TAG, 'tag': 0}),
		('nonce', krb5int32, {'tag_type': TAG, 'tag': 1, 'optional': True}),
		('timestamp', KerberosTime , {'tag_type': TAG, 'tag': 2, 'optional': True}),
		('usec', krb5int32 , {'tag_type': TAG, 'tag': 3, 'optional': True}),
		('s-address', HostAddress , {'tag_type': TAG, 'tag': 4, 'optional': True}),
		('r-address', HostAddress , {'tag_type': TAG, 'tag': 5, 'optional': True}),
	]

class KRB_ERROR(core.Sequence):
	explicit = (APPLICATION, 30)
	_fields = [
		('pvno', krb5int32, {'tag_type': TAG, 'tag': 0}),
		('msg-type',krb5int32 , {'tag_type': TAG, 'tag': 1}), #MESSAGE_TYPE
		('ctime', KerberosTime , {'tag_type': TAG, 'tag': 2, 'optional': True}),
		('cusec', krb5int32 , {'tag_type': TAG, 'tag': 3, 'optional': True}),
		('stime', KerberosTime , {'tag_type': TAG, 'tag': 4}),
		('susec', krb5int32 , {'tag_type': TAG, 'tag': 5}),
		('error-code', krb5int32 , {'tag_type': TAG, 'tag': 6}),
		('crealm', Realm , {'tag_type': TAG, 'tag': 7, 'optional': True}),
		('cname', PrincipalName , {'tag_type': TAG, 'tag': 8, 'optional': True}),
		('realm', Realm , {'tag_type': TAG, 'tag': 9}),
		('sname', PrincipalName , {'tag_type': TAG, 'tag': 10}),
		('e-text', core.GeneralString , {'tag_type': TAG, 'tag': 11, 'optional': True}),
		('e-data', core.OctetString , {'tag_type': TAG, 'tag': 12, 'optional': True}),
	]

class ChangePasswdDataMS(core.Sequence):
	_fields = [
		('newpasswd', core.OctetString, {'tag_type': TAG, 'tag': 0}),
		('targname', PrincipalName, {'tag_type': TAG, 'tag': 1, 'optional': True}),
		('targrealm', Realm , {'tag_type': TAG, 'tag': 2, 'optional': True}),
	]

class EtypeList(core.SequenceOf):
	#-- the client's proposed enctype list in
	#-- decreasing preference order, favorite choice first
	_child_spec = ENCTYPE

	
class KerberosResponse(core.Choice):
	_alternatives = [
		('AS_REP', AS_REP, {'implicit': (APPLICATION,11) }  ),
		('TGS_REP', TGS_REP, {'implicit': (APPLICATION,13) }  ),
		('KRB_ERROR', KRB_ERROR, {'implicit': (APPLICATION,30) } ),
	]

class KerberosMessage(core.Choice):
	_alternatives = [
		('AS_REQ', AS_REQ, {'implicit': (APPLICATION,10) }),
		('AS_REP', AS_REP, {'implicit': (APPLICATION,11) }),
		('TGS_REQ', TGS_REQ, {'implicit': (APPLICATION,12) }),
		('TGS_REP', TGS_REP, {'implicit': (APPLICATION,13) }),
		('KRB_ERROR', KRB_ERROR, {'implicit': (APPLICATION,30) }),
	]
	
	
class KRBCRED(core.Sequence):
	explicit = (APPLICATION, 22)
	
	_fields = [
		('pvno', core.Integer, {'tag_type': TAG, 'tag': 0}),
		('msg-type', core.Integer, {'tag_type': TAG, 'tag': 1}),
		('tickets', SequenceOfTicket, {'tag_type': TAG, 'tag': 2}),
		('enc-part', EncryptedData , {'tag_type': TAG, 'tag': 3}),
	
	]

#https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu/aceb70de-40f0-4409-87fa-df00ca145f5a
#other name: PA-S4U2Self
class PA_FOR_USER_ENC(core.Sequence): 
	_fields = [
		('userName', PrincipalName, {'tag_type': TAG, 'tag': 0}),
		('userRealm', Realm, {'tag_type': TAG, 'tag': 1}),
		('cksum', Checksum, {'tag_type': TAG, 'tag': 2}),
		('auth-package', KerberosString , {'tag_type': TAG, 'tag': 3}),
	
	]
	
#class S4UUserIDOptions(core.BitString):
class S4UUserIDOptions(core.IntegerBitString):
	_map = {
		0x40000000 : 'check-logon-hour', #This option causes the KDC to check logon hour restrictions for the user.
		0x20000000 : 'signed-with-kun-27', #In a request, asks the KDC to sign the reply with key usage number 27. In a reply, indicates that it was signed with key usage number 27.
		0x10000000 : 'nt-auth-policy-not-required',
		0x08000000 : 'unconditional-delegation',
	}

#https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu/cd9d5ca7-ce20-4693-872b-2f5dd41cbff6
class S4UUserID(core.Sequence):
	_fields = [
		('nonce', core.Integer, {'tag_type': TAG, 'tag': 0}), #-- the nonce in KDC-REQ-BODY
		('cname', PrincipalName, {'tag_type': TAG, 'tag': 1, 'optional' : True}),
		('crealm', Realm, {'tag_type': TAG, 'tag': 2}),
		('subject-certificate', core.OctetString, {'tag_type': TAG, 'tag': 3, 'optional' : True}),
		('options', S4UUserIDOptions, {'tag_type': TAG, 'tag': 4, 'optional' : True}),
	]
	
#https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu/cd9d5ca7-ce20-4693-872b-2f5dd41cbff6
class PA_S4U_X509_USER(core.Sequence):
	_fields = [
		('user-id', S4UUserID, {'tag_type': TAG, 'tag': 0}),
		('checksum', Checksum, {'tag_type': TAG, 'tag': 1}),	
	]

class AD_IF_RELEVANT(AuthorizationData):
	pass


class GSSAPIOID(core.ObjectIdentifier):
	_map = {
		'1.2.840.113554.1.2.2': 'krb5',
	}

	_reverse_map = {
		'krb5': '1.2.840.113554.1.2.2',
	}


class GSSAPIToken(core.Asn1Value):
	class_ = 1
	tag = 0
	method = 1

class KERB_DMSA_KEY_PACKAGE(core.Sequence):
	_fields = [
		('current-keys', KERB_KEY_LIST_REP, {'tag_type': TAG, 'tag': 0}),
		('previous-keys', KERB_KEY_LIST_REP, {'tag_type': TAG, 'tag': 1}),
		('expiration-interval', KerberosTime, {'tag_type': TAG, 'tag': 2}),
		('fetch-interval', KerberosTime, {'tag_type': TAG, 'tag': 4}),
	]

	def describe(self):
		current_keys = []
		keypackage = self.native
		for key in keypackage['current-keys']:
			current_keys.append( (key['keytype'], key['keyvalue']))
		
		previous_keys = []
		for key in keypackage['previous-keys']:
			previous_keys.append( (key['keytype'], key['keyvalue']))
		
		text = 'CURRENT KEYS:\n'
		for current_key in current_keys:
			if current_key[0] == 23:
				text += '   RC4: %s\n' % current_key[1].hex()
			elif current_key[0] == 17:
				text += '   AES128: %s\n' % current_key[1].hex()
			elif current_key[0] == 18:
				text += '   AES256: %s\n' % current_key[1].hex()
			else:
				text += '   UNK[%s]: %s\n' % (current_key[0], current_key[1].hex())
				
		text += 'PREVIOUS KEYS:\n'
		for previous_key in previous_keys:
			if previous_key[0] == 23:
				text += '   RC4: %s\n' % previous_key[1].hex()
			elif previous_key[0] == 17:
				text += '   AES128: %s\n' % previous_key[1].hex()
			elif previous_key[0] == 18:
				text += '    AES256: %s\n' % previous_key[1].hex()
			else:
				text += '   UNK[%s]: %s\n' % (previous_key[0], previous_key[1].hex())
		return text


class KERB_SUPERSEDED_BY_USER(core.Sequence):
	_fields = [
		('name', PrincipalName, {'tag_type': TAG, 'tag': 0}),
		('realm', Realm, {'tag_type': TAG, 'tag': 1}),
	]
	
	
