//go:build go1.18
// +build go1.18

package sm9

// Fuzz Test use random data and run for a long time

import (
	"bytes"
	"crypto/rand"
	"testing"

	"xdx.jelly/xgcl/sm/sm3"
	"xdx.jelly/xgcl/sm/sm4"
	"xdx.jelly/xgcl/utils/padding"
)

func FuzzSign(f *testing.F) {
	ks, pubs, err := GenerateMastSignPrivateKey(rand.Reader)
	if err != nil {
		f.Fatal(err)
	}

	f.Add([]byte{}, []byte{}, []byte{})
	f.Fuzz(func(t *testing.T, uid, msg, seed []byte) {
		ds, err := GenerateUserSignKey(uid, ks)
		if err != nil {
			t.Fatal(err)
		}
		dig := sm3.Sum(seed)
		signature, err := Sign(msg, ds, pubs, dig[:])
		if err != nil {
			t.Fatal(err)
		}
		if !Verify(signature, uid, msg, pubs) {
			t.Fatal("verify failed")
		}
	})
}

func FuzzKeyExchange(f *testing.F) {
	ke, pube, err := GenerateMastEncPrivateKey(rand.Reader)
	if err != nil {
		f.Fatal(err)
	}

	f.Add([]byte{}, []byte{}, []byte{}, uint(1))
	f.Fuzz(func(t *testing.T, idA, idB, seed []byte, kLen uint) {
		kLen += 1
		deA, _ := GenerateUserEncKey(idA, ke)
		deB, _ := GenerateUserEncKey(idB, ke)
		alice := NewSponsor(idA, deA)
		bob := NewResponsor(idB, deB)
		rnd := sm3.Sum(seed)
		tempKeyOfSponsor, _ := alice.GenerateAgreementData(idB, pube, rnd[:])
		rnd = sm3.Sum(seed)
		keyOfResponsor, tempKeyOfResponsor, _ := bob.GenerateAgreementDataAndKey(idA, pube, tempKeyOfSponsor, int(kLen), rnd[:])
		keyOfSponsor, _ := alice.GenerateKey(tempKeyOfResponsor, int(kLen))
		if !bytes.Equal(keyOfResponsor, keyOfSponsor) {
			t.Fatal()
		}
		alice.Clear()
		bob.Clear()
	})
}

func FuzzEnc(f *testing.F) {
	ke, pube, err := GenerateMastEncPrivateKey(rand.Reader)
	if err != nil {
		f.Fatal(err)
	}

	f.Add([]byte{}, []byte{}, []byte{})
	f.Fuzz(func(t *testing.T, id, msg, seed []byte) {
		de, err := GenerateUserEncKey(id, ke)
		if err != nil {
			t.Fatal(err)
		}

		// msg,id可能共享内存，因此不能直接在msg上padding。先复制一份
		paddedMsg := append([]byte{}, msg...)
		paddedMsg = padding.P7.Pad(paddedMsg, sm4.BlockSize)

		for _, encType := range []EncType{EncTypeKDF} {
			c, err := Encrypt(encType, pube, id, msg, rand.Reader, nil)
			if err != nil {
				t.Fatal(err)
			}
			plain, err := Decrypt(id, c, de)
			if err != nil {
				t.Fatal(err)
			}
			if !bytes.Equal(plain, msg) {
				t.Fatal(err)
			}
		}

		for _, encType := range []EncType{EncTypeSM4ECB, EncTypeSM4CBC, EncTypeSM4CFB, EncTypeSM4OFB} {
			c, err := Encrypt(encType, pube, id, paddedMsg, rand.Reader, nil)
			if err != nil {
				t.Fatal(err)
			}
			plain, err := Decrypt(id, c, de)
			if !bytes.Equal(plain, paddedMsg) || err != nil {
				t.Fatal()
			}
		}
	})
}
