package sm2m

import (
	"crypto/rand"
	"errors"
	"io"
	"math/big"

	"xdx.jelly/xgcl/grand"
	"xdx.jelly/xgcl/he/paillier"
	"xdx.jelly/xgcl/sm/sm2"
	"xdx.jelly/xgcl/sm/sm2/ec256"
)

// clientBlindSignContext 只能使用NewClientBlindSignContext来生成.
type clientBlindSignContext struct {
	k1                 *big.Int
	rand               io.Reader
	paillierPrivateKey *paillier.PrivateKey
}

// NewClientSignContext 生成客户端协同签名上下文
// pk没有用,可以传nil, rnd是随机数发生器. paillierPrivateKey可以临时产生密钥,也可以产生长期的保存在客户端.
// 注: pk实际是用来验证完成后的签名. 这里由调用者自己验证即可.
func NewClientBlindSignContext(pk *sm2.PublicKey, paillierPrivateKey *paillier.PrivateKey, r io.Reader) *clientBlindSignContext {
	c := &clientBlindSignContext{
		k1:                 new(big.Int),
		rand:               r,
		paillierPrivateKey: paillierPrivateKey,
	}
	return c
}

// Initial 客户端第一步, 返回e的同态密文和临时点P, 将他们同同态公钥一起发送给服务端.
//
// 客户端生成选择随机数k1 in [1, n - 1]，计算e = SM3(Z || M), P = [k1]G,
// 其中Z为32字节杂凑值，G为基点，向服务端发送签名请求并将 Enc_paillier(e), P, 发送给服务端。
// 注: 同态公钥可以使用客户端的部分私钥对应的公钥签名,服务端验证.
func (c *clientBlindSignContext) Initial(e []byte) (ecipher *paillier.Cipher, p *sm2.PublicKey, err error) {
	if c.rand == nil {
		c.rand = grand.Reader
	}
	if c.k1 == nil {
		c.k1 = new(big.Int)
	}
	buf := make([]byte, sm2.ByteSize())
	if n, err := c.rand.Read(buf); n != len(buf) || err != nil {
		return nil, nil, err
	}
	c.k1.SetBytes(buf)
	c.k1.Mod(c.k1, sm2.OrderN())

	x, y := sm2.Curve256.ScalarMult(sm2.BaseX(), sm2.BaseY(), c.k1.Bytes())
	p = &sm2.PublicKey{Curve: sm2.Curve(), X: x, Y: y}
	paillierPublicKey := c.paillierPrivateKey.Public()
	ecipher, err = paillierPublicKey.Encrypt(new(big.Int).SetBytes(e), c.rand)
	if err != nil {
		return nil, nil, err
	}

	return ecipher, p, nil
}

// ServerSign 服务端计算. serverKey是服务端的部分密钥, pcipher,p和ppublickey是客户端发送过来的数据.
//
// 服务端生成随机数k2, k3, k4 in [1,n-1]，（k = k1 * k2 + k3）
// 计算(x1,y1)=[k2]P+[k3]G,
// rcipher = Enc(r) = Enc(e)*Enc(x1)
// s1 = k2*d2 mod n
// s2cipher = Enc(s2) = (Enc(r)*E(k_3))^ds * Enc(k4*n) = Enc(ds*(r+k3)+k4*n).
// 返回rcipher, s1, s2cipher, 并发送给客户端
func ServerBlindSign(serverKey *sm2.PrivateKey, ecipher *paillier.Cipher,
	p *sm2.PublicKey, ppublickey *paillier.PublicKey, rnd io.Reader) (rcipher *paillier.Cipher, s1 *big.Int, s2cipher *paillier.Cipher, err error) {
	var k2, k3, k4 *big.Int
	for {
		k2, err = rand.Int(rnd, sm2.OrderN())
		if err != nil {
			return nil, nil, nil, err
		}
		if k2.Sign() == 0 {
			continue
		}
		k3, err = rand.Int(rnd, sm2.OrderN())
		if err != nil {
			return nil, nil, nil, err
		}
		if k3.Sign() == 0 {
			continue
		}
		k4, err = rand.Int(rnd, sm2.OrderN())
		if err != nil {
			return nil, nil, nil, err
		}
		if k4.Sign() == 0 {
			continue
		}
		break
	}

	x1, _ := ec256.CombinedMult(p.X, p.Y, k3.Bytes(), k2.Bytes())
	x1cipher, err := ppublickey.Encrypt(x1, rnd)
	if err != nil {
		return nil, nil, nil, err
	}
	rcipher = x1cipher.HomomorphicAdd(x1cipher, ecipher, ppublickey)

	s1 = new(big.Int)
	s1.Mul(k2, serverKey.D)
	s1.Mod(s1, sm2.OrderN())

	s2cipher, err = ppublickey.Encrypt(k3, rnd)
	if err != nil {
		return nil, nil, nil, err
	}
	s2cipher.HomomorphicAdd(s2cipher, rcipher, ppublickey)
	s2cipher.HomomorphicScalarMul(s2cipher, serverKey.D, ppublickey)

	k4.Mul(k4, sm2.OrderN())
	k4Cipher, err := ppublickey.Encrypt(k4, rnd)
	if err != nil {
		return nil, nil, nil, err
	}
	s2cipher.HomomorphicAdd(s2cipher, k4Cipher, ppublickey)

	return rcipher, s1, s2cipher, nil
}

// Final 客户端计算签名
//
// 客户端解密rcipher, s2cipher得到r, s2,
// 计算s = d1*(k1*s1+s2)-r mod n,并检查s,r+s是否为0 mod q。
// 对消息M的签名为（r,s），客户端可验证签名是否正确.
func (c *clientBlindSignContext) Final(clientKey *sm2.PrivateKey,
	rcipher *paillier.Cipher, s1 *big.Int, s2cipher *paillier.Cipher) (*sm2.Signature, error) {
	r, err := c.paillierPrivateKey.Decrypt(rcipher)
	if err != nil {
		return nil, err
	}
	r.Mod(r, sm2.OrderN())
	if r.Sign() == 0 {
		return nil, errors.New("Blind sign failed, r = 0")
	}

	s2, err := c.paillierPrivateKey.Decrypt(s2cipher)
	s2.Mod(s2, sm2.OrderN())

	s := c.k1.Mul(c.k1, s1)
	s.Add(s, s2).Mod(s, sm2.OrderN()).Mul(s, clientKey.D).Sub(s, r).Mod(s, sm2.OrderN())
	if s.Sign() == 0 {
		return nil, errors.New("Blind sign failed, s = 0")
	}
	c.k1 = nil
	return &sm2.Signature{R: r, S: s}, nil
}
