package sm9m

import (
	"io"
	"math/big"

	"xdx.jelly/xgcl/gerrors"
	"xdx.jelly/xgcl/sm/sm9"
)

// 签名流程
// 1. 客户端生成随机数r, U = [r]*Kc, 发送m, U
// 2. 服务端使用[ks]U作为完整用户私钥签名得到(h, S')
// 3. 客户端计算S = [r^-1]S', 输出签名(h,S)

type ClientSignContext struct {
	r big.Int
}

type ClientSignParam struct {
	p sm9.G1
}

// MarshalBinary implements the encoding.BinaryMarshaler interface
func (cp *ClientSignParam) MarshalBinary() (data []byte, err error) {
	return cp.p.Marshal(), nil
}

// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
func (cp *ClientSignParam) UnmarshalBinary(data []byte) error {
	_, err := cp.p.Unmarshal(data)
	return err
}

// GenerateParam 客户端第一步
func (c *ClientSignContext) GenerateParam(clientSignKey *SignKeyClient, rnd io.Reader) (*ClientSignParam, error) {
	r, rInv, err := generatePairInt(rnd, sm9.N)
	if err != nil {
		return nil, err
	}
	clientParam := &ClientSignParam{}
	clientParam.p.ScalarMult(&clientSignKey.p, rInv)

	c.r.Set(r)
	return clientParam, nil
}

// GenerateParam 客户端第二步
func (c *ClientSignContext) GenerateSignature(param *ServerSignParam) (*sm9.Signature, error) {
	param.sig.S.ScalarMult(&param.sig.S, &c.r)
	return &param.sig, nil
}

type ServerSignParam struct {
	sig sm9.Signature
}

// MarshalBinary implements the encoding.BinaryMarshaler interface
func (sp *ServerSignParam) MarshalBinary() (data []byte, err error) {
	return sp.sig.MarshalBinary()
}

// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
func (sp *ServerSignParam) UnmarshalBinary(data []byte) error {
	return sp.sig.UnmarshalBinary(data)
}

// ServerSign 服务端生成协同签名数据
func ServerSign(hashed []byte, clientParam *ClientSignParam, serverSignKey *SignKeyServer, pubs *sm9.MastSignPublicKey, rnd io.Reader) (*ServerSignParam, error) {
	ds := &sm9.UserSignKey{}
	ds.G1.Set(&clientParam.p)
	preSig, err := sm9.Sign(hashed, ds, pubs, rnd)
	if err != nil {
		return nil, gerrors.WithAnnotating(err, "ServerSign failed")
	}
	preSig.S.ScalarMult(&preSig.S, &serverSignKey.k)
	param := &ServerSignParam{}
	param.sig.Set(preSig)
	return param, nil
}
