package outsource

import (
	"crypto/rand"
	"math/big"
	"testing"

	"github.com/stretchr/testify/assert"
	"xdx.jelly/xgcl/gmath"
	"xdx.jelly/xgcl/grand"
	"xdx.jelly/xgcl/he/elgamal"
	"xdx.jelly/xgcl/sm/sm2"
	"xdx.jelly/xgcl/sm/sm3"
	"xdx.jelly/xgcl/tpc/sm2/sm2m"
)

// 示例ElGamal 不能加入盲化因子。弃用。
func TestOutsourceWithElGamal(t *testing.T) {
	clientKey, clientTempKey, _ := sm2m.ClientGenSignKey_one(grand.Reader)
	serverKey, serverTempKey, publicKey, _ := sm2m.ServerGenSignKey(clientTempKey, grand.GetRandom(32))
	err := sm2m.ClientGenSignKey_two(clientKey, serverTempKey, publicKey)
	if err != nil {
		t.Fatal("client's public key and server's public key are not equal.")
	}
	e := grand.GetRandom(32)

	sk, pk, err := elgamal.GenerateKey(grand.Reader, elgamal.P1024)
	assert.Nil(t, err)

	encryptedClientKey, err := pk.Encryption(clientKey.D, grand.Reader)
	assert.Nil(t, err)

	// 受托方计算
	k1, err := rand.Int(grand.Reader, sm2.OrderN())
	assert.Nil(t, err)
	px, py := sm2.Curve256.ScalarMult(sm2.BaseX(), sm2.BaseY(), k1.Bytes())

	data := make([]byte, sm3.Size+2*sm2.ByteSize())
	pos := copy(data, e)
	pos += copy(data[pos:], gmath.BigIntToNByte(px, sm2.ByteSize()))
	copy(data[pos:], gmath.BigIntToNByte(py, sm2.ByteSize()))

	// data = (e,p) => 协同服务端
	// 协同服务端计算
	data, err = sm2m.ServerSign(serverKey, data, grand.Reader)
	assert.Nil(t, err)

	// data = (r||s1||s2)
	r := new(big.Int)
	r.SetBytes(data[:sm2.ByteSize()])
	s1 := new(big.Int).SetBytes(data[sm2.ByteSize() : 2*sm2.ByteSize()])
	s2 := new(big.Int).SetBytes(data[2*sm2.ByteSize():])

	// s1, s2 => 受托方
	s := big.NewInt(0).Mul(k1, s1)
	s.Add(s, s2)
	s.Mod(s, sm2.OrderN())

	// 计算 c = Enc(dc*(k1*s1+s2))
	S, err := pk.Encryption(s, grand.Reader)
	assert.Nil(t, err)
	c := &elgamal.Cipher{}
	c, err = c.HomoMap(encryptedClientKey, S)
	assert.Nil(t, err)

	// 加入随机因子?HOW.
	// k, err := rand.Int(grand.Reader, pk.Param.Q)
	// assert.Nil(t, err)
	// nminus1 := new(big.Int).Set(sm2.OrderN())

	// k.Mul(k, nminus1)
	// k.Mod(k, pk.Param.Q)
	// k.Exp(pk.Param.G, k, pk.Param.P)
	// c1, err := pk.Encryption(k, grand.Reader)
	// assert.Nil(t, err)

	// c.HomoMap(c, c1) // c = Enc(dc*(k1*s1+s2) * g^{K(N-1)})

	// c 发送给委托方
	// 委托方计算
	s, err = sk.Decryption(c)
	assert.Nil(t, err)

	s.Sub(s, r)
	s.Mod(s, sm2.OrderN())
	sig := &sm2.Signature{
		R: r,
		S: s,
	}

	if !sm2.Verify(e, publicKey, sig) {
		t.Fatal("verify failed")
	}
}
