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
}
