From 5bb36ffa9b44a0f9b25f4fac7702c61c4b237b44 Mon Sep 17 00:00:00 2001 From: p1-bmu Date: Tue, 20 Oct 2020 18:16:41 +0200 Subject: [PATCH] crypto: initial support for EAP and IKEv2 formats --- pycrate_crypto/EAP.py | 147 +++++++ pycrate_crypto/IKEv2.py | 895 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1042 insertions(+) create mode 100644 pycrate_crypto/EAP.py create mode 100644 pycrate_crypto/IKEv2.py diff --git a/pycrate_crypto/EAP.py b/pycrate_crypto/EAP.py new file mode 100644 index 0000000..945f403 --- /dev/null +++ b/pycrate_crypto/EAP.py @@ -0,0 +1,147 @@ +# −*− coding: UTF−8 −*− +#/** +# * Software Name : pycrate +# * Version : 0.4 +# * +# * Copyright 2020. Benoit Michau. P1Sec. +# * +# * This library is free software; you can redistribute it and/or +# * modify it under the terms of the GNU Lesser General Public +# * License as published by the Free Software Foundation; either +# * version 2.1 of the License, or (at your option) any later version. +# * +# * This library is distributed in the hope that it will be usefu, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# * Lesser General Public License for more details. +# * +# * You should have received a copy of the GNU Lesser General Public +# * License along with this library; if not, write to the Free Software +# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# * MA 02110-1301 USA +# * +# *-------------------------------------------------------- +# * File Name : pycrate_crypto/EAP.py +# * Created : 2020-10-20 +# * Authors : Benoit Michau +# *-------------------------------------------------------- +#*/ + + +from enum import Enum + +from pycrate_core.utils import * +from pycrate_core.elt import * +from pycrate_core.base import * +from pycrate_core.repr import * + + +#------------------------------------------------------------------------------# +# Extensible Authentication Protocol +# IETF RFC 3748: https://tools.ietf.org/html/rfc3748 +# IANA EAP Parameters: https://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml +#------------------------------------------------------------------------------# + + +#------------------------------------------------------------------------------# +# EAP complete packet +# section 4 - EAP Packet Format +#------------------------------------------------------------------------------# + +class EAPPacketCode(Enum): + Request = 1 + Response = 2 + Success = 3 + Failure = 4 + Initiate = 5 + Finish = 6 + +EAPPacketCode_dict = {e.value: e.name for e in EAPPacketCode} + + +class EAPPacketType(Enum): + Identity = 1 + Notification = 2 + Legacy_Nak = 3 + MD5_Challenge = 4 + One_Time_Password = 5 + Generic_Token_Card = 6 + RSA_Public_Key_Authentication = 9 + DSS_Unilateral = 10 + KEA = 11 + KEA_VALIDATE = 12 + EAP_TLS = 13 + Defender_Token = 14 + RSA_Security_SecurID_EAP = 15 + Arcot_Systems_EAP = 16 + EAP_Cisco_Wireless = 17 + EAP_SIM = 18 + SRP_SHA1 = 19 + EAP_TTLS = 21 + Remote_Access_Service = 22 + EAP_AKA = 23 + EAP_3Com_Wireless = 24 + PEAP = 25 + MS_EAP_Authentication = 26 + MAKE = 27 + CRYPTOCard = 28 + MSCHAPv2 = 29 + DynamID = 30 + Rob_EAP = 31 + Protected_One_Time_Password = 32 + MS_Authentication_TLV = 33 + SentriNET = 34 + EAP_Actiontec_Wireless = 35 + Cogent_Systems_Biometrics_Authentication_EAP = 36 + AirFortress_EAP = 37 + HTTP_Digest = 38 + SecureSuite_EAP = 39 + DeviceConnect_EAP = 40 + EAP_SPEKE = 41 + EAP_MOBAC = 42 + EAP_FAST = 43 + ZoneLabs_EAP = 44 + EAP_Link = 45 + EAP_PAX = 46 + EAP_PSK = 47 + EAP_SAKE = 48 + EAP_IKEv2 = 49 + EAP_AKA_prime = 50 + EAP_GPSK = 51 + EAP_pwd = 52 + EAP_EKE_V1 = 53 + EAP_for_PT_EAP = 54 + TEAP = 55 + +EAPPacketType_dict = {e.value: e.name for e in EAPPacketType} + + +class EAPDial(Envelope): + _GEN = ( + Uint8('Type', val=EAPPacketType.Identity.value, dic=EAPPacketType_dict), + Buf('Data', val=b'', rep=REPR_HEX) + ) + + +class EAP(Envelope): + _GEN = ( + Uint8('Code', val=EAPPacketCode.Request.value, dic=EAPPacketCode_dict), + Uint8('Ident', val=1), + Uint16('Len'), + Alt('Data', GEN={ + EAPPacketCode.Request.value : EAPDial('Req'), + EAPPacketCode.Response.value : EAPDial('Resp'), + EAPPacketCode.Success.value : Buf('none', bl=0, rep=REPR_HEX), + EAPPacketCode.Failure.value : Buf('none', bl=0, rep=REPR_HEX), + EAPPacketCode.Initiate.value : EAPDial('Init'), + EAPPacketCode.Finish.value : EAPDial('Fin')}, + DEFAULT=Buf('unk', val=b'', rep=REPR_HEX), + sel=lambda self: self.get_env()['Code'].get_val()) + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self['Len'].set_valauto(lambda: 4+self['Data'].get_len()) + self['Data'].set_blauto(lambda: (self['Len'].get_val()-4)<<3) + + diff --git a/pycrate_crypto/IKEv2.py b/pycrate_crypto/IKEv2.py new file mode 100644 index 0000000..7099fa6 --- /dev/null +++ b/pycrate_crypto/IKEv2.py @@ -0,0 +1,895 @@ +# −*− coding: UTF−8 −*− +#/** +# * Software Name : pycrate +# * Version : 0.4 +# * +# * Copyright 2020. Benoit Michau. P1Sec. +# * +# * This library is free software; you can redistribute it and/or +# * modify it under the terms of the GNU Lesser General Public +# * License as published by the Free Software Foundation; either +# * version 2.1 of the License, or (at your option) any later version. +# * +# * This library is distributed in the hope that it will be usefu, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# * Lesser General Public License for more details. +# * +# * You should have received a copy of the GNU Lesser General Public +# * License along with this library; if not, write to the Free Software +# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# * MA 02110-1301 USA +# * +# *-------------------------------------------------------- +# * File Name : pycrate_crypto/IKEv2.py +# * Created : 2020-10-14 +# * Authors : Benoit Michau +# *-------------------------------------------------------- +#*/ + + +from enum import Enum + +from pycrate_core.utils import * +from pycrate_core.elt import * +from pycrate_core.base import * +from pycrate_core.repr import * +# +from pycrate_ether.IP import IPProt_dict +from pycrate_crypto.EAP import EAP + + +#------------------------------------------------------------------------------# +# Internet Key Exchange v2 +# IETF RFC 7296: https://tools.ietf.org/html/rfc7296 +# IANA IKEv2 Parameters: https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml +#------------------------------------------------------------------------------# + + +#------------------------------------------------------------------------------# +# Security Association Payload +# section 3.3 +#------------------------------------------------------------------------------# + +class _AttributeLV(Envelope): + _GEN = ( + Uint16('L'), + Buf('V', val=b'', rep=REPR_HEX) + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self[0].set_valauto(lambda: self[1].get_len()) + self[1].set_blauto(lambda: self[0].get_val()<<3) + + +class Attribute(Envelope): + _GEN = ( + Uint('AF', bl=1), + Uint('Type', bl=15), + Alt('Attr', GEN={ + 0: _AttributeLV('LV'), + 1: Buf('V', bl=16, rep=REPR_HEX)}, + sel=lambda self: self.get_env()['AF'].get_val() + ) + ) + + +class IKEv2TransType(Enum): + ENCR = 1 + PRF = 2 + INTEG = 3 + DH = 4 + ESN = 5 + +IKEv2TransType_dict = {e.value: e.name for e in IKEv2TransType} + +class IKEv2TransENCR(Enum): + ENCR_DES_IV64 = 1 + ENCR_DES = 2 + ENCR_3DES = 3 + ENCR_RC5 = 4 + ENCR_IDEA = 5 + ENCR_CAST = 6 + ENCR_BLOWFISH = 7 + ENCR_3IDEA = 8 + ENCR_DES_IV32 = 9 + ENCR_NULL = 11 + ENCR_AES_CBC = 12 + ENCR_AES_CTR = 13 + ENCR_AES_CCM_8 = 14 + ENCR_AES_CCM_12 = 15 + ENCR_AES_CCM_16 = 16 + ENCR_AES_GCM_8 = 18 + ENCR_AES_GCM_12 = 19 + ENCR_AES_GCM_16 = 20 + ENCR_NULL_AUTH_AES_GMAC = 21 + ENCR_IEEE_P1619_XT_AES = 22 + ENCR_CAMELLIA_CBC = 23 + ENCR_CAMELLIA_CTR = 24 + ENCR_CAMELLIA_CCM_8 = 25 + ENCR_CAMELLIA_CCM_12 = 26 + ENCR_CAMELLIA_CCM_16 = 27 + ENCR_CHACHA20_POLY1305 = 28 + ENCR_AES_CCM_8_IIV = 29 + ENCR_AES_GCM_16_IIV = 30 + ENCR_CHACHA20_POLY1305_IIV = 31 + ENCR_KUZNYECHIK_MGM_KTREE = 32 + ENCR_MAGMA_MGM_KTREE = 33 + ENCR_KUZNYECHIK_MGM_MAC_KTREE = 34 + ENCR_MAGMA_MGM_MAC_KTREE = 35 + +IKEv2TransENCR_dict = {e.value: e.name for e in IKEv2TransENCR} + +class IKEv2TransPRF(Enum): + PRF_HMAC_MD5 = 1 + PRF_HMAC_SHA1 = 2 + PRF_HMAC_TIGER = 3 + PRF_AES128_XCBC = 4 + PRF_HMAC_SHA2_256 = 5 + PRF_HMAC_SHA2_384 = 6 + PRF_HMAC_SHA2_512 = 7 + PRF_AES128_CMAC = 8 + PRF_HMAC_STRIBOG_512 = 9 + +IKEv2TransPRF_dict = {e.value: e.name for e in IKEv2TransPRF} + +class IKEv2TransAUTH(Enum): + NONE = 0 + AUTH_HMAC_MD5_96 = 1 + AUTH_HMAC_SHA1_96 = 2 + AUTH_DES_MAC = 3 + AUTH_KPDK_MD5 = 4 + AUTH_AES_XCBC_96 = 5 + AUTH_HMAC_MD5_128 = 6 + AUTH_HMAC_SHA1_160 = 7 + AUTH_AES_CMAC_96 = 8 + AUTH_AES_128_GMAC = 9 + AUTH_AES_192_GMAC = 10 + AUTH_AES_256_GMAC = 11 + AUTH_HMAC_SHA2_256_128 = 12 + AUTH_HMAC_SHA2_384_192 = 13 + AUTH_HMAC_SHA2_512_256 = 14 + +IKEv2TransAUTH_dict = {e.value: e.name for e in IKEv2TransAUTH} + +class IKEv2TransDH(Enum): + NONE = 0 + MODP_Group_768 = 1 + MODP_Group_1024 = 2 + MODP_Group_1536 = 5 + MODP_Group_2048 = 14 + MODP_Group_3072 = 15 + MODP_Group_4096 = 16 + MODP_Group_6144 = 17 + MODP_Group_8192 = 18 + Random_ECP_Group_256 = 19 + Random_ECP_Group_384 = 20 + Random_ECP_Group_521 = 21 + MODP_Group_1024_Prime_Order_Subgroup_160 = 22 + MODP_Group_2048_Prime_Order_Subgroup_224 = 23 + MODP_Group_2048_Prime_Order_Subgroup_256 = 24 + Random_ECP_Group_192 = 25 + Random_ECP_Group_224 = 26 + BrainpoolP224r1 = 27 + BrainpoolP256r1 = 28 + BrainpoolP384r1 = 29 + BrainpoolP512r1 = 30 + Curve25519 = 31 + Curve448 = 32 + GOST3410_2012_256 = 33 + GOST3410_2012_512 = 34 + +IKEv2TransDH_dict = {e.value: e.name for e in IKEv2TransDH} + +class IKEv2TransESN(Enum): + NO_ESN = 0 + ESN = 1 + +IKEv2TransESN_dict = {e.value: e.name for e in IKEv2TransESN} + +IKEv2TransID_dict = { + 1 : IKEv2TransENCR_dict, + 2 : IKEv2TransPRF_dict, + 3 : IKEv2TransAUTH_dict, + 4 : IKEv2TransDH_dict, + 5 : IKEv2TransESN_dict + } + + +class Transform(Envelope): + _GEN = ( + Uint8('Last'), + Uint8('res'), + Uint16('Len'), + Uint8('Type', val=IKEv2TransType.ENCR.value, dic=IKEv2TransType_dict), + Uint8('res'), + Uint16('ID', val=1), + Sequence('Attributes', GEN=Attribute()) + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self['Last'].set_valauto(lambda: 0 if self.get_next() is None else 3) + self['Len'].set_valauto(lambda: 8 + self['Attributes'].get_len()) + self['ID'].set_dicauto(lambda: IKEv2TransID_dict.get(self['Type'].get_val(), {})) + self['Attributes'].set_blauto(lambda: (self['Len'].get_val() - 8)<<3) + + +class IKEv2ProtID(Enum): + IKE = 1 + AH = 2 + ESP = 3 + +IKEv2ProtID_dict = {e.value: e.name for e in IKEv2ProtID} + + +class Proposal(Envelope): + _GEN = ( + Uint8('Last'), + Uint8('res'), + Uint16('Len'), + Uint8('Num'), + Uint8('ProtID', val=IKEv2ProtID.IKE.value, dic=IKEv2ProtID_dict), + Uint8('SPISize'), # 0 for initial IKE SA nego, then 4 (AH, ESP) or 8 (IKE) + Uint8('NumTrans'), + Buf('SPI', val=b'', rep=REPR_HEX), + Sequence('Transforms', GEN=Transform()) + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self['Last'].set_valauto(lambda: 0 if self.get_next() is None else 2) + self['Len'].set_valauto(lambda: 8 + self['SPI'].get_len() + self['Transforms'].get_len()) + self['Num'].set_valauto(lambda: self.get_env().index(self)) + self['SPISize'].set_valauto(lambda: self['SPI'].get_len()) + self['NumTrans'].set_valauto(lambda: self['Transforms'].get_num()) + self['SPI'].set_blauto(lambda: self['SPISize'].get_val()<<3) + self['Transforms'].set_numauto(lambda: self['NumTrans'].get_val()) + self['Transforms'].set_blauto(lambda: (self['Len'].get_val() - self['SPISize'].get_val() - 8)<<3) + + +class PaySA(Sequence): + _GEN = Proposal() + + +#------------------------------------------------------------------------------# +# Key Exchange Payload +# section 3.4 +#------------------------------------------------------------------------------# + +class PayKE(Envelope): + _GEN = ( + Uint16('DHGroup', val=IKEv2TransDH.MODP_Group_1024.value, dic=IKEv2TransDH_dict), + Uint16('res', rep=REPR_HEX), + Buf('Data', val=b'', rep=REPR_HEX) + ) + + +#------------------------------------------------------------------------------# +# Identification Payloads +# section 3.5 +#------------------------------------------------------------------------------# + +class IKEv2IDType(Enum): + ID_IPV4_ADDR = 1 + ID_FQDN = 2 + ID_RFC822_ADDR = 3 + ID_IPV6_ADDR = 5 + ID_DER_ASN1_DN = 9 + ID_DER_ASN1_GN = 10 + ID_KEY_ID = 11 + ID_FC_NAME = 12 + ID_NULL = 13 + +IKEv2IDType_dict = {e.value: e.name for e in IKEv2IDType} + + +class PayID(Envelope): + _GEN = ( + Uint8('Type', val=IKEv2IDType.ID_IPV4_ADDR.value, dic=IKEv2IDType_dict), + Uint24('res', rep=REPR_HEX), + Buf('Data', val=b'', rep=REPR_HEX) + ) + + +#------------------------------------------------------------------------------# +# Certificate Payload +# section 3.6 +# Certificate Request Payload +# section 3.7 +#------------------------------------------------------------------------------# + +class IKEv2CertEncoding(Enum): + PKCS7_wrapped_X509_Cert = 1 + PGP_Cert = 2 + DNS_Signed_Key = 3 + X509_Cert_Signature = 4 + Kerberos_Token = 6 + Certificate_Revocation_List = 7 + Authority_Revocation_List = 8 + SPKI_Cert = 9 + X509_Cert_Attribute = 10 + Raw_RSA_Key = 11 + Hash_URL_X509_Cert = 12 + Hash_URL_X509_Bundle = 13 + OCSP_Content = 14 + Raw_Public_Key = 15 + +IKEv2CertEncoding_dict = {e.value: e.name for e in IKEv2CertEncoding} + + +class PayCert(Envelope): + _GEN = ( + Uint8('Encoding', val=1, dic=IKEv2CertEncoding_dict), + Buf('Data', val=b'', rep=REPR_HEX) + ) + + +#------------------------------------------------------------------------------# +# Authentication Payload +# section 3.8 +#------------------------------------------------------------------------------# + +class IKEv2AuthMethod(Enum): + RSA_Digital_Signature = 1 + Shared_Key_Message_Integrity_Code = 2 + DSS_Digital_Signature = 3 + ECDSA_SHA256_P256 = 9 + ECDSA_SHA384_P384 = 10 + ECDSA_SHA512_P521 = 11 + Generic_Secure_Password = 12 + NULL = 13 + Digital_Signature = 14 + +IKEv2AuthMethod_dict = {e.value: e.name for e in IKEv2AuthMethod} + + +class PayAuth(Envelope): + _GEN = ( + Uint8('Method', val=1, dic=IKEv2AuthMethod_dict), + Uint24('res', rep=REPR_HEX), + Buf('Data', val=b'', rep=REPR_HEX) + ) + + +#------------------------------------------------------------------------------# +# Nonce Payload +# section 3.9 +#------------------------------------------------------------------------------# + +class PayNonce(Envelope): + _GEN = ( + Buf('Data', val=b'', rep=REPR_HEX), + ) + + +#------------------------------------------------------------------------------# +# Notify Payload +# section 3.10 +#------------------------------------------------------------------------------# + +# error notifications +class IKEv2NotifTypeErr(Enum): + UNSUPPORTED_CRITICAL_PAYLOAD = 1 + INVALID_IKE_SPI = 4 + INVALID_MAJOR_VERSION = 5 + INVALID_SYNTAX = 7 + INVALID_MESSAGE_ID = 9 + INVALID_SPI = 11 + NO_PROPOSAL_CHOSEN = 14 + INVALID_KE_PAYLOAD = 17 + AUTHENTICATION_FAILED = 24 + SINGLE_PAIR_REQUIRED = 34 + NO_ADDITIONAL_SAS = 35 + INTERNAL_ADDRESS_FAILURE = 36 + FAILED_CP_REQUIRED = 37 + TS_UNACCEPTABLE = 38 + INVALID_SELECTORS = 39 + UNACCEPTABLE_ADDRESSES = 40 + UNEXPECTED_NAT_DETECTED = 41 + USE_ASSIGNED_HoA = 42 + TEMPORARY_FAILURE = 43 + CHILD_SA_NOT_FOUND = 44 + INVALID_GROUP_ID = 45 + AUTHORIZATION_FAILED = 46 + +IKEv2NotifTypeErr_dict = {e.value: e.name for e in IKEv2NotifTypeErr} + +# status notifications +class IKEv2NotifTypeStat(Enum): + INITIAL_CONTACT = 16384 + SET_WINDOW_SIZE = 16385 + ADDITIONAL_TS_POSSIBLE = 16386 + IPCOMP_SUPPORTED = 16387 + NAT_DETECTION_SOURCE_IP = 16388 + NAT_DETECTION_DESTINATION_IP = 16389 + COOKIE = 16390 + USE_TRANSPORT_MODE = 16391 + HTTP_CERT_LOOKUP_SUPPORTED = 16392 + REKEY_SA = 16393 + ESP_TFC_PADDING_NOT_SUPPORTED = 16394 + NON_FIRST_FRAGMENTS_ALSO = 16395 + MOBIKE_SUPPORTED = 16396 + ADDITIONAL_IP4_ADDRESS = 16397 + ADDITIONAL_IP6_ADDRESS = 16398 + NO_ADDITIONAL_ADDRESSES = 16399 + UPDATE_SA_ADDRESSES = 16400 + COOKIE2 = 16401 + NO_NATS_ALLOWED = 16402 + AUTH_LIFETIME = 16403 + MULTIPLE_AUTH_SUPPORTED = 16404 + ANOTHER_AUTH_FOLLOWS = 16405 + REDIRECT_SUPPORTED = 16406 + REDIRECT = 16407 + REDIRECTED_FROM = 16408 + TICKET_LT_OPAQUE = 16409 + TICKET_REQUEST = 16410 + TICKET_ACK = 16411 + TICKET_NACK = 16412 + TICKET_OPAQUE = 16413 + LINK_ID = 16414 + USE_WESP_MODE = 16415 + ROHC_SUPPORTED = 16416 + EAP_ONLY_AUTHENTICATION = 16417 + CHILDLESS_IKEV2_SUPPORTED = 16418 + QUICK_CRASH_DETECTION = 16419 + IKEV2_MESSAGE_ID_SYNC_SUPPORTED = 16420 + IPSEC_REPLAY_COUNTER_SYNC_SUPPORTED = 16421 + IKEV2_MESSAGE_ID_SYNC = 16422 + IPSEC_REPLAY_COUNTER_SYNC = 16423 + SECURE_PASSWORD_METHODS = 16424 + PSK_PERSIST = 16425 + PSK_CONFIRM = 16426 + ERX_SUPPORTED = 16427 + IFOM_CAPABILITY = 16428 + SENDER_REQUEST_ID = 16429 + IKEV2_FRAGMENTATION_SUPPORTED = 16430 + SIGNATURE_HASH_ALGORITHMS = 16431 + CLONE_IKE_SA_SUPPORTED = 16432 + CLONE_IKE_SA = 16433 + PUZZLE = 16434 + USE_PPK = 16435 + PPK_IDENTITY = 16436 + NO_PPK_AUTH = 16437 + INTERMEDIATE_EXCHANGE_SUPPORTED = 16438 + +IKEv2NotifTypeStat_dict = {e.value: e.name for e in IKEv2NotifTypeStat} + +# all notifications +IKEv2NotifType_dict = dict(IKEv2NotifTypeErr_dict) +IKEv2NotifType_dict.update(IKEv2NotifTypeStat_dict) + + +class PayNotif(Envelope): + _GEN = ( + Uint8('ProtID', val=IKEv2ProtID.IKE.value, dic=IKEv2ProtID_dict), + Uint8('SPISize'), # 0 for initial IKE SA nego, then 4 (AH, ESP) or 8 (IKE) + Uint16('Type', val=IKEv2NotifTypeErr.TEMPORARY_FAILURE.value, dic=IKEv2NotifType_dict), + Buf('SPI', val=b'', rep=REPR_HEX), + Buf('Data', val=b'', rep=REPR_HEX) + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self['SPISize'].set_valauto(lambda: self['SPI'].get_len()) + self['SPI'].set_blauto(lambda: self['SPISize'].get_val()<<3) + + +#------------------------------------------------------------------------------# +# Delete Payload +# section 3.11 +#------------------------------------------------------------------------------# + +class PayDelete(Envelope): + _GEN = ( + Uint8('ProtID', val=IKEv2ProtID.IKE.value, dic=IKEv2ProtID_dict), + Uint8('SPISize'), # 0 for initial IKE SA nego, then 4 (AH, ESP) or 8 (IKE) + Uint8('NumSPIs'), + Sequence('SPIs', GEN=Buf('SPI', rep=REPR_HEX)) + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self['SPISize'].set_valauto(lambda: self['SPIs'][0].get_len() if self['SPIs'].get_num() else 0) + self['NumSPIs'].set_valauto(lambda: self['SPIs'].get_num()) + self['SPIs'].set_numauto(lambda: self['NumSPIs'].get_val()) + + def _from_char(self, char): + self[0]._from_char(char) + self[1]._from_char(char) + self[2]._from_char(char) + self[3]._tmpl._bl = self[1].get_val()<<3 + self[3]._from_char(char) + self[3]._tmpl._bl = None + + +#------------------------------------------------------------------------------# +# VendorID Payload +# section 3.12 +#------------------------------------------------------------------------------# + +class PayVID(Envelope): + _GEN = ( + Buf('Data', val=b'', rep=REPR_HEX), + ) + + +#------------------------------------------------------------------------------# +# Traffic Selector Payload +# section 3.13 +#------------------------------------------------------------------------------# + +class IKEv2TSType(Enum): + IPV4_ADDR_RANGE = 7 + IPV6_ADDR_RANGE = 8 + +IKEv2TSType_dict = {e.value: e.name for e in IKEv2TSType} + + +class IKEv2TS(Envelope): + _GEN = ( + Uint8('Type', val=IKEv2TSType.IPV4_ADDR_RANGE.value, dic=IKEv2TSType_dict), + Uint8('IPProt', val=0, dic=IPProt_dict), + Uint16('Len'), + Uint16('PortStart', val=0), + Uint16('PortEnd', val=0), + Buf('AddrStart', val=b'', rep=REPR_HEX), + Buf('AddrEnd', val=b'', rep=REPR_HEX) + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self['Len'].set_valauto(lambda: 8 + self['AddrStart'].get_len() + self['AddrEnd'].get_len()) + self['AddrStart'].set_blauto(lambda: (self['Len'].get_val()-8)<<2) + self['AddrEnd'].set_blauto(lambda: (self['Len'].get_val()-8)<<2) + + +class PayTS(Envelope): + _GEN = ( + Uint8('NumTSs'), + Uint24('res', rep=REPR_HEX), + Sequence('TSs', GEN=IKEv2TS('TS')) + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self['NumTSs'].set_valauto(lambda: self['TSs'].get_num()) + self['TSs'].set_numauto(lambda: self['NumTSs'].get_val()) + + +#------------------------------------------------------------------------------# +# Encrypted Payload +# section 3.14 +#------------------------------------------------------------------------------# + +# length of IV +# Warning: some of those values are not confirmed / tested / verified +IKEv2EncrIVLen_dict = { + IKEv2TransENCR.ENCR_DES_IV64 : 8, + IKEv2TransENCR.ENCR_DES : 8, + IKEv2TransENCR.ENCR_3DES : 8, + IKEv2TransENCR.ENCR_RC5 : 8, + IKEv2TransENCR.ENCR_IDEA : 8, + IKEv2TransENCR.ENCR_CAST : 8, + IKEv2TransENCR.ENCR_BLOWFISH : 8, + IKEv2TransENCR.ENCR_3IDEA : 8, + IKEv2TransENCR.ENCR_DES_IV32 : 8, + IKEv2TransENCR.ENCR_NULL : 0, + IKEv2TransENCR.ENCR_AES_CBC : 16, + IKEv2TransENCR.ENCR_AES_CTR : 16, + IKEv2TransENCR.ENCR_AES_CCM_8 : 16, + IKEv2TransENCR.ENCR_AES_CCM_12 : 16, + IKEv2TransENCR.ENCR_AES_CCM_16 : 16, + IKEv2TransENCR.ENCR_AES_GCM_8 : 16, + IKEv2TransENCR.ENCR_AES_GCM_12 : 16, + IKEv2TransENCR.ENCR_AES_GCM_16 : 16, + IKEv2TransENCR.ENCR_NULL_AUTH_AES_GMAC : 16, + IKEv2TransENCR.ENCR_IEEE_P1619_XT_AES : 16, + IKEv2TransENCR.ENCR_CAMELLIA_CBC : 16, + IKEv2TransENCR.ENCR_CAMELLIA_CTR : 16, + IKEv2TransENCR.ENCR_CAMELLIA_CCM_8 : 16, + IKEv2TransENCR.ENCR_CAMELLIA_CCM_12 : 16, + IKEv2TransENCR.ENCR_CAMELLIA_CCM_16 : 16, + IKEv2TransENCR.ENCR_CHACHA20_POLY1305 : 16, + IKEv2TransENCR.ENCR_AES_CCM_8_IIV : 16, + IKEv2TransENCR.ENCR_AES_GCM_16_IIV : 16, + IKEv2TransENCR.ENCR_CHACHA20_POLY1305_IIV : 16, + IKEv2TransENCR.ENCR_KUZNYECHIK_MGM_KTREE : 8, + IKEv2TransENCR.ENCR_MAGMA_MGM_KTREE : 8, + IKEv2TransENCR.ENCR_KUZNYECHIK_MGM_MAC_KTREE : 8, + IKEv2TransENCR.ENCR_MAGMA_MGM_MAC_KTREE : 8, + } + + + +# length of ICS +IKEv2AuthICSLen_dict = { + IKEv2TransAUTH.NONE : 0, + IKEv2TransAUTH.AUTH_HMAC_MD5_96 : 12, + IKEv2TransAUTH.AUTH_HMAC_SHA1_96 : 12, + IKEv2TransAUTH.AUTH_DES_MAC : 8, + IKEv2TransAUTH.AUTH_KPDK_MD5 : 16, + IKEv2TransAUTH.AUTH_AES_XCBC_96 : 12, + IKEv2TransAUTH.AUTH_HMAC_MD5_128 : 16, + IKEv2TransAUTH.AUTH_HMAC_SHA1_160 : 20, + IKEv2TransAUTH.AUTH_AES_CMAC_96 : 12, + IKEv2TransAUTH.AUTH_AES_128_GMAC : 16, + IKEv2TransAUTH.AUTH_AES_192_GMAC : 24, + IKEv2TransAUTH.AUTH_AES_256_GMAC : 32, + IKEv2TransAUTH.AUTH_HMAC_SHA2_256_128 : 16, + IKEv2TransAUTH.AUTH_HMAC_SHA2_384_192 : 24, + IKEv2TransAUTH.AUTH_HMAC_SHA2_512_256 : 32, + } + + +class PayEncr(Envelope): + + # the configuration of the encryption and integrity checksum algorithms + # is contextual + ALG_ENCR = IKEv2TransENCR.ENCR_NULL + ALG_AUTH = IKEv2TransAUTH.NONE + + _GEN = ( + Buf('IV', rep=REPR_HEX), + Buf('Data', val=b'', rep=REPR_HEX), + Buf('Pad', val=b'', rep=REPR_HEX), + Uint8('PadLen'), + Buf('ICS', rep=REPR_HEX) + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self['IV'].set_blauto(lambda: IKEv2EncrIVLen_dict.get(self.ALG_ENCR, 0)) + self['PadLen'].set_valauto(lambda: self['Pad'].get_len()) + self['ICS'].set_blauto(lambda: IKEv2AuthICSLen_dict.get(self.ALG_AUTH, 0)) + + # TODO + def encrypt(self, data, key): + pass + + def decrypt(self, key): + pass + + def ics_compute(self, key): + pass + + def ics_verify(self, key): + pass + + +#------------------------------------------------------------------------------# +# Configuration Payload +# section 3.15 +#------------------------------------------------------------------------------# + +class IKEv2AttrType(Enum): + INTERNAL_IP4_ADDRESS = 1 + INTERNAL_IP4_NETMASK = 2 + INTERNAL_IP4_DNS = 3 + INTERNAL_IP4_NBNS = 4 + INTERNAL_IP4_DHCP = 6 + APPLICATION_VERSION = 7 + INTERNAL_IP6_ADDRESS = 8 + INTERNAL_IP6_DNS = 10 + INTERNAL_IP6_DHCP = 12 + INTERNAL_IP4_SUBNET = 13 + SUPPORTED_ATTRIBUTES = 14 + INTERNAL_IP6_SUBNET = 15 + MIP6_HOME_PREFIX = 16 + INTERNAL_IP6_LINK = 17 + INTERNAL_IP6_PREFIX = 18 + HOME_AGENT_ADDRESS = 19 + P_CSCF_IP4_ADDRESS = 20 + P_CSCF_IP6_ADDRESS = 21 + FTT_KAT = 22 + EXTERNAL_SOURCE_IP4_NAT_INFO = 23 + TIMEOUT_PERIOD_FOR_LIVENESS_CHECK = 24 + INTERNAL_DNS_DOMAIN = 25 + INTERNAL_DNSSEC_TA = 26 + +IKEv2AttrType_dict = {e.value: e.name for e in IKEv2AttrType} + + +class IKEv2Attr(Envelope): + _GEN = ( + Uint('R', bl=1), + Uint('Type', val=IKEv2AttrType.SUPPORTED_ATTRIBUTES.value, bl=15, dic=IKEv2AttrType_dict), + Uint8('Len'), + Buf('Value', val=b'', rep=REPR_HEX), + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self['Len'].set_valauto(lambda: self['Value'].get_len()) + self['Value'].set_blauto(lambda: self['Len'].get_val()<<3) + + +class IKEv2ConfigType(Enum): + CFG_REQUEST = 1 + CFG_REPLY = 2 + CFG_SET = 3 + CFG_ACK = 4 + +IKEv2ConfigType_dict = {e.value: e.name for e in IKEv2ConfigType} + + +class PayConfig(Envelope): + _GEN = ( + Uint8('Type', val=IKEv2ConfigType.CFG_REQUEST.value, dic=IKEv2ConfigType_dict), + Uint24('res', rep=REPR_HEX), + Sequence('Attrs', GEN=IKEv2Attr('Attr')) + ) + + +#------------------------------------------------------------------------------# +# Extensible Authentication Protocol (EAP) Payload +# section 3.16 +#------------------------------------------------------------------------------# + +class PayEAP(Envelope): + _GEN = ( + EAP(), + ) + + + +#------------------------------------------------------------------------------# +# IKEv2 payload header +# section 3.2 +#------------------------------------------------------------------------------# + +class IKEv2PayType(Enum): + NoNextPayload = 0 + SecurityAssociation = 33 + KeyExchange = 34 + IdentInitiator = 35 + IdentResponder = 36 + Certifiate = 37 + CertificateRequest = 38 + Authentication = 39 + Nonce = 40 + Notify = 41 + Delete = 42 + VendorID = 43 + TrafficSelectorInitiator = 44 + TrafficSelectorResponder = 45 + EncryptedAuthenticated = 46 + Configuration = 47 + EAP = 48 + GenericSecurePwdMethod = 49 + GroupIdentification = 50 + GroupSecurityAssociation = 51 + KeyDownload = 52 + EncryptedAuthenticatedFrag = 53 + PuzzleSolution = 54 + +IKEv2PayType_dict = {e.value: e.name for e in IKEv2PayType} + + +class IKEv2Pay(Envelope): + + # LUT for payload + LUTPay = { + 33 : PaySA('SA'), + 34 : PayKE('KE'), + 35 : PayID('IDi'), + 36 : PayID('IDr'), + 37 : PayCert('CERT'), + 38 : PayCert('CERTREQ'), + 39 : PayAuth('AUTH'), + 40 : PayNonce('NONCE'), + 41 : PayNotif('N'), + 42 : PayDelete('D'), + 43 : PayVID('V'), + 44 : PayTS('TSi'), + 45 : PayTS('TSr'), + 46 : PayEncr('SK'), + 47 : PayConfig('CP'), + 48 : PayEAP('EAP'), + } + + _GEN = ( + Uint8('Next'), + Uint('C', bl=1), + Uint('res', bl=7, rep=REPR_HEX), + Uint16('Len'), + Buf('Pay', val=b'', rep=REPR_HEX) + ) + + def __init__(self, *args, **kwargs): + self.Type = 1 + Envelope.__init__(self, *args, **kwargs) + self['Next'].set_valauto(lambda: getattr(self.get_next(), 'Type', 0)) + self['Len'].set_valauto(lambda: 4 + self[4].get_len()) + self['Pay'].set_blauto(lambda: (self[3].get_val() - 4)<<3) + + def set_val(self, val): + if isinstance(val, dict) and 'Type' in val and val['Type'] in self.LUTPay: + self.Type = val['Type'] + del val['Type'] + pay = self.LUTPay[val['Type']].clone() + pay.set_blauto(lambda: (self[3].get_val() - 4)<<3) + self.replace(self[4], pay) + Envelope.set_val(self, val) + + def _from_char(self, char): + e, n = self.get_env(), None + if e is not None: + if e.get_num(): + # get the last IKEv2Pay within the Payloads Sequence + n = e[-1] + else: + e = e.get_env() + if e is not None: + # get the IKEv2 header + n = e[0] + if n is not None: + next = n['Next'].get_val() + if next in self.LUTPay: + self.Type = next + pay = self.LUTPay[next].clone() + pay.set_blauto(lambda: (self[3].get_val() - 4)<<3) + self.replace(self[4], pay) + Envelope._from_char(self, char) + + +#------------------------------------------------------------------------------# +# IKEv2 header +# section 3.1 +#------------------------------------------------------------------------------# + +class IKEv2ExchType(Enum): + IKE_SA_INIT = 34 + IKE_AUTH = 35 + CREATE_CHILD_SA = 36 + INFORMATIONAL = 37 + IKE_SESSION_RESUME = 38 + GSA_AUTH = 39 + GSA_REGISTRATION = 40 + GSA_REKEY = 41 + IKE_INTERMEDIATE = 43 + +IKEv2ExchType_dict = {e.value: e.name for e in IKEv2ExchType} + + +class IKEv2HdrFlags(Envelope): + _GEN = ( + Uint('undef', bl=2, rep=REPR_HEX), + Uint('R', bl=1), + Uint('V', bl=1), + Uint('I', bl=1), + Uint('undef', bl=3, rep=REPR_HEX) + ) + + +class IKEv2Hdr(Envelope): + _GEN = ( + Buf('SPIInitiator', bl=64, rep=REPR_HEX), + Buf('SPIResponder', bl=64, rep=REPR_HEX), + Uint8('Next'), + Uint('VersMaj', val=2, bl=4), + Uint('VersMin', val=0, bl=4), + Uint8('ExchType', val=IKEv2ExchType.IKE_SA_INIT.value, dic=IKEv2ExchType_dict), + IKEv2HdrFlags('Flags'), + Uint32('MID', rep=REPR_HEX), + Uint32('Len', val=28) + ) + + +#------------------------------------------------------------------------------# +# IKEv2 complete packet +#------------------------------------------------------------------------------# + +class IKEv2(Envelope): + _GEN = ( + IKEv2Hdr('Header'), + Sequence('Payloads', GEN=IKEv2Pay(), hier=1) + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self[0]['Next'].set_valauto(lambda: self[1][0].Type if self[1].get_num() else 0) + self[0]['Len'].set_valauto(lambda: 28 + self[1].get_len()) + self[1].set_blauto(lambda: (self[0]['Len'].get_val()-28)<<3) +