package ike

import (
	"bytes"
	"errors"
	"testing"
)

func legacyTestSuite() negotiatedSuite {
	return negotiatedSuite{
		EncryptionID:   encryptionAESCBC,
		EncryptionBits: 128,
		PRFID:          prfHMACSHA1,
		IntegrityID:    integrityHMACSHA1_96,
		DHID:           dhMODP1024,
	}
}

func TestMODP1024UsesGroup2PrimeAnd128ByteKE(t *testing.T) {
	exchange, err := newDHExchange(dhMODP1024, bytes.NewReader(bytes.Repeat([]byte{0x42}, 128)))
	if err != nil {
		t.Fatalf("newDHExchange() error = %v", err)
	}
	if bits := exchange.prime.BitLen(); bits != 1024 {
		t.Fatalf("group 2 prime BitLen() = %d, want 1024", bits)
	}
	if length := len(exchange.Public); length != 128 {
		t.Fatalf("group 2 public KE length = %d, want 128", length)
	}
	peer, err := newDHExchange(dhMODP1024, bytes.NewReader(bytes.Repeat([]byte{0x24}, 128)))
	if err != nil {
		t.Fatalf("peer newDHExchange() error = %v", err)
	}
	firstSecret, err := exchange.shared(peer.Public)
	if err != nil {
		t.Fatalf("exchange.shared() error = %v", err)
	}
	secondSecret, err := peer.shared(exchange.Public)
	if err != nil {
		t.Fatalf("peer.shared() error = %v", err)
	}
	if len(firstSecret) != 128 || !bytes.Equal(firstSecret, secondSecret) {
		t.Fatal("MODP group 2 shared secrets differ or are not 128 bytes")
	}
}

func TestEncryptedPayloadRoundTripAndTamperDetection(t *testing.T) {
	suite := legacyTestSuite()
	encryptionKey := bytes.Repeat([]byte{0x11}, 16)
	integrityKey := bytes.Repeat([]byte{0x22}, 20)
	header := ikeHeader{
		InitiatorSPI: [8]byte{1, 2, 3, 4, 5, 6, 7, 8},
		ResponderSPI: [8]byte{8, 7, 6, 5, 4, 3, 2, 1},
		Exchange:     exchangeIKEAuth,
		Flags:        flagInitiator,
		MessageID:    7,
	}
	inner := []payload{
		{Type: payloadIDi, Body: []byte{3, 0, 0, 0, 'u', '@', 'r'}},
		{Type: payloadEAP, Body: []byte{1, 9, 0, 5, 1}},
	}
	packet, err := encryptPayloads(
		header,
		inner,
		suite,
		encryptionKey,
		integrityKey,
		bytes.NewReader(bytes.Repeat([]byte{0x33}, 64)),
	)
	if err != nil {
		t.Fatalf("encryptPayloads() error = %v", err)
	}
	decodedHeader, decoded, err := decryptPayloads(packet, suite, encryptionKey, integrityKey)
	if err != nil {
		t.Fatalf("decryptPayloads() error = %v", err)
	}
	if decodedHeader.MessageID != header.MessageID || len(decoded) != len(inner) {
		t.Fatalf("decoded header/payload count mismatch: %#v %#v", decodedHeader, decoded)
	}
	for index := range inner {
		if decoded[index].Type != inner[index].Type || !bytes.Equal(decoded[index].Body, inner[index].Body) {
			t.Fatalf("decoded payload %d = %#v, want %#v", index, decoded[index], inner[index])
		}
	}

	tampered := append([]byte(nil), packet...)
	tampered[len(tampered)-1] ^= 0x80
	if _, _, err := decryptPayloads(tampered, suite, encryptionKey, integrityKey); !errors.Is(err, errIntegrityMismatch) {
		t.Fatalf("tampered decrypt error = %v, want errIntegrityMismatch", err)
	}
}

func TestIKEKeyDerivationSeparatesDirections(t *testing.T) {
	suite := legacyTestSuite()
	keys, err := deriveIKEKeys(
		suite,
		bytes.Repeat([]byte{0x44}, 128),
		bytes.Repeat([]byte{0x55}, 32),
		bytes.Repeat([]byte{0x66}, 32),
		[8]byte{1},
		[8]byte{2},
	)
	if err != nil {
		t.Fatalf("deriveIKEKeys() error = %v", err)
	}
	if len(keys.SKd) != 20 || len(keys.SKai) != 20 || len(keys.SKar) != 20 ||
		len(keys.SKei) != 16 || len(keys.SKer) != 16 ||
		len(keys.SKpi) != 20 || len(keys.SKpr) != 20 {
		t.Fatalf("unexpected key lengths: %+v", keys)
	}
	if bytes.Equal(keys.SKai, keys.SKar) || bytes.Equal(keys.SKei, keys.SKer) ||
		bytes.Equal(keys.SKpi, keys.SKpr) {
		t.Fatal("initiator and responder keys were not separated")
	}
}
