diff --git a/protocol/ikev2/ikev2.go b/protocol/ikev2/ikev2.go new file mode 100644 index 0000000..d9e1d7a --- /dev/null +++ b/protocol/ikev2/ikev2.go @@ -0,0 +1,147 @@ +// Package ikev2 +// A (very) basic framework for an ikev2 protocol. +// The intent with adding this package is not to add an entire, fully functional protocol but rather providing a convenient interface for generating the data structures for ike-related exploits. +// The goal is to add to this as we go, as new exploits demand it. +// +// Using this will probably require you to make your own SAInit function to generate the message data and then sending it over the ikeClient connection. +// A basic example of something like this follows: +// +// func saInit(ikeClient *ikev2.IkeClient, diffieHellmanGroup int) ([]byte, bool) { +// +// ikeClient.IkeCrypto = ikev2.IkeCrypto{} +// ikeClient.IkeCrypto.Init() +// ok := ikeClient.IkeCrypto.GenerateDHKey(diffieHellmanGroup) +// if !ok { +// return []byte{}, false +// } +// +// defaultTransforms := []ikev2.IkeTransform{ +// {NextPayload: ikev2.PayloadType["TRANSFORM"], TransformType: ikev2.TransformType["ENCRYPTION_ALGORITHM"], TransformID: ikev2.EncryptionAlgorithm["ENCR_AES_CBC"], TransformAttributes: 0x800e0100}, +// } +// +// message := make([]byte, 0) +// header := ikev2.IkePackHeader(ikeClient, ikev2.PayloadType["SECURITY_ASSOCIATION"], 0x20, ikev2.ExchangeType["IKE_SA_INIT"], 0x08, 0x0) +// message = append(message, ikev2.IkePackSecurityAssociation(ikev2.PayloadType["KEY_EXCHANGE"], ikev2.IkePackProposal(ikev2.PayloadType["NONE"], 1, 1, defaultTransforms, ""))...) +// message = append(message, ikev2.IkePackKeyExchange(ikev2.PayloadType["NONCE"], ikev2.DiffieHellmanGroup["DH_GROUP_2048_BIT_MODP"], ikeClient.IkeCrypto.ClientPubKey.Bytes())...) +// message = append(message, ikev2.IkePackNonce(ikev2.PayloadType["NOTIFY"], ikeClient.IkeCrypto.InitNonce)...) +// +// tempBytes, _ := hex.DecodeString("deadbeef") +// message = append(message, ikev2.IkePackNotify(ikev2.PayloadType["NOTIFY"], ikev2.NotifyType["NAT_DETECTION_DESTINATION_IP"], tempBytes, 1, 0)...) +// +// tempBytes, _ = hex.DecodeString("0021030203030006") +// message = append(message, ikev2.IkePackNotify(ikev2.PayloadType["NONE"], ikev2.NotifyType["SIGNATURE_HASH_ALGORITHMS"], tempBytes, 0, 0)...) +// +// // combining the message +// payload := make([]byte, 0) +// payload = append(header, []byte(transform.PackBigInt32(len(message)+len(header)+4))...) +// payload = append(payload, message...) +// return payload, true +// } +// +// func main() { +// ikeClient := ikev2.IkeClient{} +// saInitPayload, _ := saInit(&ikeClient, ikev2.DiffieHellmanGroup["DH_GROUP_2048_BIT_MODP"]) +// if !ok { +// output.PrintError("error encountered while generating SAInit payload") +// return false +// } +// +// if !ikeClient.Connect(conf){ +// return false +// } +// +// _, err := ikeClient.Conn.Write([]byte(saInitPayload)) +// if err != nil { +// output.PrintfDebug("SAInit Send failed: %s", err.Error()) +// return false +// } +// output.PrintDebug("Sent SAInit message") +// } +package ikev2 + +import ( + "crypto/rand" + "encoding/binary" + "math/big" + mrand "math/rand" + "net" + "strconv" + + "github.com/vulncheck-oss/go-exploit/config" + "github.com/vulncheck-oss/go-exploit/output" +) + +func Uint64ToString(num uint64) []byte { + bytes := make([]byte, 8) + binary.BigEndian.PutUint64(bytes, num) + + return bytes +} + +func generateNonce(length int) ([]byte, bool) { + nonce := make([]byte, length) + _, err := rand.Read(nonce) + if err != nil { + output.PrintfFrameworkError("Error while generating nonce: %s", err.Error()) + + return []byte{}, false + } + + return nonce, true +} + +// Switch case is here in case anyone wants to add additional DH support. +func (ikeCrypto *IkeCrypto) GenerateDHKey(diffieHellmanGroup int) bool { + switch diffieHellmanGroup { + case DiffieHellmanGroup["DH_GROUP_2048_BIT_MODP"]: + ikeCrypto.Prime = new(big.Int) + ikeCrypto.Prime.SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) // https://www.ietf.org/rfc/rfc3526.txt + g := big.NewInt(2) + ikeCrypto.ClientPrivKey, _ = rand.Int(rand.Reader, ikeCrypto.Prime) + ikeCrypto.ClientPubKey = new(big.Int).Exp(g, ikeCrypto.ClientPrivKey, ikeCrypto.Prime) + + return true + + default: + output.PrintFrameworkError("Provided DH group is currently unsupported.") + + return false + } +} + +func (ikeCrypto *IkeCrypto) Init() bool { + var ok bool + ikeCrypto.InitNonce, ok = generateNonce(32) + if !ok { + return false + } + ikeCrypto.RespNonce = []byte{} + ikeCrypto.InitSPI = mrand.Uint64() + ikeCrypto.RespSPI = 0x0000000000000000 + + return true +} + +// An initial reset/connect helper function. +func (ikeClient *IkeClient) Connect(conf *config.Config) bool { + var err error + if ikeClient.Conn != nil { + err = ikeClient.Conn.Close() + if err != nil { + // we probably do not want to exit on this, we're establishing a new connection anyways + output.PrintfFrameworkWarn("Failed to close socket: %s", err.Error()) + } + } + + connectionString := net.JoinHostPort(conf.Rhost, strconv.Itoa(conf.Rport)) + ikeClient.Conn, err = net.Dial("udp", connectionString) + if err != nil { + output.PrintfFrameworkError("Dial failed: %s", err.Error()) + + return false + } + + output.PrintfFrameworkDebug("Successfully established UDP connection to %s", connectionString) + + return true +} diff --git a/protocol/ikev2/ikev2_test.go b/protocol/ikev2/ikev2_test.go new file mode 100644 index 0000000..da20eb8 --- /dev/null +++ b/protocol/ikev2/ikev2_test.go @@ -0,0 +1,90 @@ +package ikev2 + +import ( + "encoding/hex" + "fmt" + "testing" +) + +func TestVendorIDPack(t *testing.T) { + want := "2b000014c590254e5403cbb71f3d493111d7fcad" + tempBytes, _ := hex.DecodeString("c590254e5403cbb71f3d493111d7fcad") + got := IkePackVendorID(PayloadType["VENDOR_ID"], tempBytes) + + if fmt.Sprintf("%02x", got) != want { + t.Fatalf("[1] %q : %02x != %s", got, got, want) + } + + want = "2b000014c61baca1f1a60cc10800000000000000" + tempBytes, _ = hex.DecodeString("c61baca1f1a60cc10800000000000000") + got = IkePackVendorID(PayloadType["VENDOR_ID"], tempBytes) + + if fmt.Sprintf("%02x", got) != want { + t.Fatalf("[2] %q : %02x != %s", got, got, want) + } + + want = "2b0000184048b7d56ebce88525e7de7f00d6c2d3c0000000" + tempBytes, _ = hex.DecodeString("4048b7d56ebce88525e7de7f00d6c2d3c0000000") + got = IkePackVendorID(PayloadType["VENDOR_ID"], tempBytes) + + if fmt.Sprintf("%02x", got) != want { + t.Fatalf("[3] %q : %02x != %s", got, got, want) + } + + want = "290000144048b7d56ebce88525e7de7f00d6c2d3" + tempBytes, _ = hex.DecodeString("4048b7d56ebce88525e7de7f00d6c2d3") + got = IkePackVendorID(PayloadType["NOTIFY"], tempBytes) + if fmt.Sprintf("%02x", got) != want { + t.Fatalf("[4] %q : %02x != %s", got, got, want) + } +} + +func TestSecurityAssociationPack(t *testing.T) { + want := "220000300000002c010100040300000c0100000c800e01000300000802000005030000080300000c000000080400000e" + defaultTransforms := []IkeTransform{ + {PayloadType["TRANSFORM"], TransformType["ENCRYPTION_ALGORITHM"], EncryptionAlgorithm["ENCR_AES_CBC"], 0x800e0100}, + {PayloadType["TRANSFORM"], TransformType["PSEUDO_RANDOM_FUNCTION"], PseudoRandomFunction["PRF_HMAC_SHA2_256"], 0}, + {PayloadType["TRANSFORM"], TransformType["INTEGRITY_ALGORITHM"], IntegrityAlgorithm["AUTH_HMAC_SHA2_256_128"], 0}, + {PayloadType["NONE"], TransformType["DIFFIE_HELLMAN_GROUP"], DiffieHellmanGroup["DH_GROUP_2048_BIT_MODP"], 0}, + } + got := IkePackSecurityAssociation(PayloadType["KEY_EXCHANGE"], IkePackProposal(PayloadType["NONE"], 1, 1, defaultTransforms, "")) + + if fmt.Sprintf("%02x", got) != want { + t.Fatalf("[1] %q : %02x != %s", got, got, want) + } +} + +func TestNotifyPack(t *testing.T) { + want := "2900001c01004005a6358d813592fdd80a9aaa3390f39c8a5a76b6e4" + tempBytes, _ := hex.DecodeString("a6358d813592fdd80a9aaa3390f39c8a5a76b6e4") + got := IkePackNotify(PayloadType["NOTIFY"], NotifyType["NAT_DETECTION_DESTINATION_IP"], tempBytes, 1, 0) + if fmt.Sprintf("%02x", got) != want { + t.Fatalf("[1] %q : %02x != %s", got, got, want) + } + + want = "2b00001c010040044cc324152ba3f68ef649ac1e6f96f33791611db2" + tempBytes, _ = hex.DecodeString("4cc324152ba3f68ef649ac1e6f96f33791611db2") + got = IkePackNotify(PayloadType["VENDOR_ID"], NotifyType["NAT_DETECTION_SOURCE_IP"], tempBytes, 1, 0) + if fmt.Sprintf("%02x", got) != want { + t.Fatalf("[2] %q : %02x != %s", got, got, want) + } + + want = "290000080000402e" + got = IkePackNotify(PayloadType["NOTIFY"], NotifyType["IKEV2_FRAGMENTATION_SUPPORTED"], []byte{}, 0, 0) + if fmt.Sprintf("%02x", got) != want { + t.Fatalf("[3] %q : %02x != %s", got, got, want) + } + + want = "2900000800004016" + got = IkePackNotify(PayloadType["NOTIFY"], NotifyType["REDIRECT_SUPPORTED"], []byte{}, 0, 0) + if fmt.Sprintf("%02x", got) != want { + t.Fatalf("[4] %q : %02x != %s", got, got, want) + } + + want = "000000100000402f0001000200030004" + tempBytes, _ = hex.DecodeString("0001000200030004") + got = IkePackNotify(PayloadType["NONE"], NotifyType["SIGNATURE_HASH_ALGORITHMS"], tempBytes, 0, 0) + if fmt.Sprintf("%02x", got) != want { + t.Fatalf("[5] %q : %02x != %s", got, got, want) + } +} diff --git a/protocol/ikev2/packs.go b/protocol/ikev2/packs.go new file mode 100644 index 0000000..b28baaa --- /dev/null +++ b/protocol/ikev2/packs.go @@ -0,0 +1,113 @@ +package ikev2 + +import ( + "github.com/vulncheck-oss/go-exploit/transform" +) + +// Creates a Nonce component. +// +// ikev2.IkePackNonce(ikev2.PayloadType["NOTIFY"], ikeClient.IkeCrypto.InitNonce) +func IkePackNonce(nextPayload int, nonce []byte) []byte { + return IkePackPayloadHeader(nextPayload, nonce) +} + +// Creates a VendorID component. +// +// tempBytes, _ = hex.DecodeString("4048b7d56ebce88525e7de7f00d6c2d3") +// ikev2.IkePackVendorID(ikev2.PayloadType["NOTIFY"], tempBytes) +func IkePackVendorID(nextPayload int, vendorID []byte) []byte { + return IkePackPayloadHeader(nextPayload, vendorID) +} + +// Creates a Notify component. +// +// tempBytes, _ = hex.DecodeString("0021030203030006") +// ikev2.IkePackNotify(ikev2.PayloadType["NONE"], ikev2.NotifyType["SIGNATURE_HASH_ALGORITHMS"], tempBytes, 0, 0) +func IkePackNotify(nextPayload int, notifyType int, data []byte, protocolID int, spiSize int) []byte { + payload := make([]byte, 0) + payload = append(payload, byte(protocolID)) + payload = append(payload, byte(spiSize)) + payload = append(payload, []byte(transform.PackBigInt16(notifyType))...) + payload = append(payload, data...) + + return IkePackPayloadHeader(nextPayload, payload) +} + +// Creates a KeyExchange component. +// +// ikev2.IkePackKeyExchange(ikev2.PayloadType["NONCE"], ikev2.DiffieHellmanGroup["DH_GROUP_2048_BIT_MODP"], ikeClient.IkeCrypto.ClientPubKey.Bytes()) +func IkePackKeyExchange(nextPayload int, dhGroup int, data []byte) []byte { + payload := []byte(transform.PackBigInt16(dhGroup)) + payload = append(payload, []byte(transform.PackBigInt16(0))...) // reserved bytes (2) + payload = append(payload, data...) + + return IkePackPayloadHeader(nextPayload, payload) +} + +// Creates a Security Association component. +// +// ikev2.IkePackSecurityAssociation(ikev2.PayloadType["KEY_EXCHANGE"], ikev2.IkePackProposal(ikev2.PayloadType["NONE"], 1, 1, defaultTransforms, "")) +func IkePackSecurityAssociation(payloadType int, proposal []byte) []byte { + return IkePackPayloadHeader(payloadType, proposal) +} + +// Creates a transform byte array from a transform. +// +// ikev2.IkeTransform{NextPayload: ikev2.PayloadType["TRANSFORM"], TransformType: ikev2.TransformType["ENCRYPTION_ALGORITHM"], TransformID: ikev2.EncryptionAlgorithm["ENCR_AES_CBC"], TransformAttributes: 0x800e0100} +func (ikeTransform *IkeTransform) Pack() []byte { + payload := make([]byte, 0) + payload = append(payload, byte(ikeTransform.TransformType)) + payload = append(payload, byte(0)) + payload = append(payload, []byte(transform.PackBigInt16(ikeTransform.TransformID))...) + transform.PackBigInt16(ikeTransform.TransformID) + + if ikeTransform.TransformAttributes != 0 { + payload = append(payload, []byte(transform.PackBigInt32(ikeTransform.TransformAttributes))...) + } + + return IkePackPayloadHeader(ikeTransform.NextPayload, payload) +} + +// Encapsulates a Packed component (or any binary array) with the nextHeader. Used by most of these packs. +func IkePackPayloadHeader(payloadType int, payloadIn []byte) []byte { + payload := make([]byte, 0) + payload = append(payload, byte(payloadType)) + payload = append(payload, byte(0)) + payload = append(payload, []byte(transform.PackBigInt16(len(payloadIn)+4))...) + payload = append(payload, payloadIn...) + + return payload +} + +// Creates a Proposal component. +// +// ikev2.IkePackProposal(ikev2.PayloadType["NONE"], 1, 1, defaultTransforms, ""). +func IkePackProposal(nextPayload int, number int, id int, transforms []IkeTransform, spi string) []byte { + payload := make([]byte, 0) + payload = append(payload, byte(number)) + payload = append(payload, byte(id)) + payload = append(payload, byte(len(spi))) + payload = append(payload, byte(len(transforms))) + payload = append(payload, []byte(spi)...) + for _, transform := range transforms { + payload = append(payload, transform.Pack()...) + } + + return IkePackPayloadHeader(nextPayload, payload) +} + +// Creates a Header component. +// +// ikev2.IkePackHeader(ikeClient, ikev2.PayloadType["SECURITY_ASSOCIATION"], 0x20, ikev2.ExchangeType["IKE_SA_INIT"], 0x08, 0x0) +func IkePackHeader(ikeClient *IkeClient, payloadType int, version int, exchangeType int, flags int, messageID int) []byte { + payload := make([]byte, 0) + payload = append(payload, Uint64ToString(ikeClient.IkeCrypto.InitSPI)...) + payload = append(payload, Uint64ToString(ikeClient.IkeCrypto.RespSPI)...) + payload = append(payload, byte(payloadType)) + payload = append(payload, byte(version)) + payload = append(payload, byte(exchangeType)) + payload = append(payload, byte(flags)) + payload = append(payload, []byte(transform.PackBigInt32(messageID))...) + + return payload +} diff --git a/protocol/ikev2/types.go b/protocol/ikev2/types.go new file mode 100644 index 0000000..8058a19 --- /dev/null +++ b/protocol/ikev2/types.go @@ -0,0 +1,226 @@ +package ikev2 + +import ( + "math/big" + "net" +) + +// I hate go idioms for enums, so we're doing string maps. +// The primary reason is that I want the context to be evident, much like a namespace when using the structures here. + +var PayloadType = map[string]int{ + "NONE": 0, + "TRANSFORM": 3, + "SECURITY_ASSOCIATION": 33, + "KEY_EXCHANGE": 34, + "IDENTIFIER_INITIATOR": 35, + "IDENTIFIER_RESPONDER": 36, + "CERTIFICATE": 37, + "CERTIFICATE_REQUEST": 38, + "AUTHENTICATION": 39, + "NONCE": 40, + "NOTIFY": 41, + "DELETE": 42, + "VENDOR_ID": 43, + "TRAFFIC_SELECTOR_INITIATOR": 44, + "TRAFFIC_SELECTOR_RESPONDER": 45, + "ENCRYPTED": 46, + "CONFIGURATION": 47, + "EXTENSIBLE_AUTHENTICATION": 48, +} + +var ExchangeType = map[string]int{ + "IKE_SA_INIT": 34, + "IKE_AUTH": 35, + "CREATE_CHILD_SA": 36, + "INFORMATIONAL": 37, +} + +var TransformType = map[string]int{ + "ENCRYPTION_ALGORITHM": 1, + "PSEUDO_RANDOM_FUNCTION": 2, + "INTEGRITY_ALGORITHM": 3, + "DIFFIE_HELLMAN_GROUP": 4, +} + +var NotifyType = map[string]int{ + "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, + "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, + "MULTIPLE_AUTH_SUPPORTED": 16404, + "REDIRECT_SUPPORTED": 16406, + "IKEV2_FRAGMENTATION_SUPPORTED": 16430, + "SIGNATURE_HASH_ALGORITHMS": 16431, +} + +var ConfigurationAttribute = map[string]int{ + "INTERNAL_IP4_ADDRESS": 1, + "INTERNAL_IP4_NETMASK": 2, + "INTERNAL_IP4_DNS": 3, + "INTERNAL_IP4_MBNS": 4, + "APPLICATION_VERSION": 7, + "INTERNAL_IP6_ADDRESS": 8, + "INTERNAL_IP6_DNS": 10, +} + +var EncryptionAlgorithm = map[string]int{ + "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, + "RESERVED": 10, + "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, + "P1619_XTS_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, +} + +var PseudoRandomFunction = map[string]int{ + "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_STREEBOG_512": 9, +} + +var IntegrityAlgorithm = map[string]int{ + "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, +} + +var DiffieHellmanGroup = map[string]int{ + "DH_GROUP_2048_BIT_MODP": 14, + "DH_GROUP_768_BIT_MODP": 1, + "DH_GROUP_1024_BIT_MODP": 2, + "DH_GROUP_1536_BIT_MODP": 5, + "DH_GROUP_3072_BIT_MODP": 15, + "DH_GROUP_4096_BIT_MODP": 16, + "DH_GROUP_6144_BIT_MODP": 17, + "DH_GROUP_8192_BIT_MODP": 18, + "RANDOM_ECP_GROUP_256_BIT": 19, + "RANDOM_ECP_GROUP_384_BIT": 20, + "RANDOM_ECP_GROUP_521_BIT": 21, + "DH_GROUP_1024_BIT_MODP_WITH_160_BIT_PRIME_ORDER_SUBGROUP": 22, + "DH_GROUP_2048_BIT_MODP_WITH_224_BIT_PRIME_ORDER_SUBGROUP": 23, + "DH_GROUP_2048_BIT_MODP_WITH_256_BIT_PRIME_ORDER_SUBGROUP": 24, + "RANDOM_ECP_GROUP_192_BIT": 25, + "RANDOM_ECP_GROUP_224_BIT": 26, + "BRAINPOOLP224R1": 27, + "BRAINPOOLP256R1": 28, + "BRAINPOOLP384R1": 29, + "BRAINPOOLP512R1": 30, + "CURVE25519": 31, + "CURVE448": 32, + "GOST3410_2012_256": 33, + "GOST3410_2012_512": 34, + "ML_KEM_512": 35, + "ML_KEM_768": 36, + "ML_KEM_1024": 37, +} + +type IkeClient struct { + Conn net.Conn + IkeCrypto IkeCrypto +} + +type IkeCrypto struct { + Prime *big.Int + + InitNonce []byte + RespNonce []byte + InitSPI uint64 + RespSPI uint64 + + ClientPrivKey *big.Int + ClientPubKey *big.Int + ServerPubKey *big.Int + + SharedSecret *big.Int + SKeySeed string + SKd string + SKai string + SKar string + SKei string + SKer string +} + +type IkeProposal struct { + ProposalNumber int + ProtocolID int + SPI int + Transforms []IkeTransform +} + +type IkeTransform struct { + NextPayload int + TransformType int + TransformID int + TransformAttributes int // a bit lazy perhaps, but it gets used infrequently enough that providing an int instead of a list of maps + enums should suffice + // Reserved int = 0 +}