package sm2

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	_subtle "crypto/subtle"
	"errors"
	"hash"
	"io"
	"math/big"
	"sync"

	"github.com/emmansun/gmsm/ecdh"
	"github.com/emmansun/gmsm/internal/bigmod"
	"github.com/emmansun/gmsm/internal/randutil"
	_sm2ec "github.com/emmansun/gmsm/internal/sm2ec"
	"github.com/emmansun/gmsm/sm2/sm2ec"
	"github.com/emmansun/gmsm/sm3"
	"golang.org/x/crypto/cryptobyte"
	"golang.org/x/crypto/cryptobyte/asn1"
)

// directSigning is a standard Hash value that signals that no pre-hashing
// should be performed.
var directSigning crypto.Hash = 0

// Signer is an interface for an opaque private key that can be used for
// signing operations. For example, an SM2 key kept in a hardware module.
// Deprecated: please use crypto.Signer directly.
type Signer interface {
	// Public returns the public key corresponding to the opaque,
	// private key.
	Public() crypto.PublicKey

	// SignWithSM2 signs raw message with the private key, possibly using entropy from
	// rand, and the user ID (UID). If the UID is not provided, a default UID (1234567812345678) is used.
	// The signature is generated using the SM2 algorithm.
	SignWithSM2(rand io.Reader, uid, msg []byte) ([]byte, error)
}

// SM2SignerOption implements crypto.SignerOpts interface and is used for SM2-specific signing options.
// It is specific for SM2, used in private key's Sign method.
type SM2SignerOption struct {
	uid         []byte
	forceGMSign bool
}

// NewSM2SignerOption creates a SM2 specific signer option.
// forceGMSign - if use GM specific sign logic, if yes, should pass raw message to sign.
// uid - if forceGMSign is true, then you can pass uid, if no uid is provided, system will use default one.
func NewSM2SignerOption(forceGMSign bool, uid []byte) *SM2SignerOption {
	opt := &SM2SignerOption{
		uid:         uid,
		forceGMSign: forceGMSign,
	}
	if forceGMSign && len(uid) == 0 {
		opt.uid = defaultUID
	}
	return opt
}

// DefaultSM2SignerOpts uses default UID and forceGMSign is true.
var DefaultSM2SignerOpts = NewSM2SignerOption(true, nil)

func (*SM2SignerOption) HashFunc() crypto.Hash {
	return directSigning
}

var (
	errInvalidPrivateKey = errors.New("sm2: invalid private key")
	errInvalidPublicKey  = errors.New("sm2: invalid public key")
)

// PrivateKey represents an ECDSA SM2 private key.
// It embeds ecdsa.PrivateKey and includes additional fields for SM2-specific operations.
// It implements both crypto.Decrypter and crypto.Signer interfaces.
type PrivateKey struct {
	ecdsa.PrivateKey
	// inverseOfKeyPlus1 stores the modular inverse of (private key + 1) modulo the curve order.
	// It is computed lazily and cached using sync.Once to ensure it is only calculated once.
	inverseOfKeyPlus1     *bigmod.Nat
	inverseOfKeyPlus1Once sync.Once
}

// FromECPrivateKey convert an ecdsa private key to SM2 private key.
func (priv *PrivateKey) FromECPrivateKey(key *ecdsa.PrivateKey) (*PrivateKey, error) {
	if key.Curve != sm2ec.P256() {
		return nil, errors.New("sm2: not an SM2 curve private key")
	}
	// Copy the ECDSA private key fields to the SM2 private key
	priv.PrivateKey = *key
	return priv, nil
}

func (priv *PrivateKey) Equal(x crypto.PrivateKey) bool {
	xx, ok := x.(*PrivateKey)
	if !ok {
		return false
	}
	return priv.PublicKey.Equal(&xx.PublicKey) && _subtle.ConstantTimeCompare(priv.D.Bytes(), xx.D.Bytes()) == 1
}

// Sign signs digest with priv, reading randomness from rand. Compliance with GB/T 32918.2-2016.
// The opts argument is currently used for SM2SignerOption checking only.
// If the opts argument is SM2SignerOption and its ForceGMSign is true,
// digest argument will be treated as raw data and UID will be taken from opts.
//
// This method implements crypto.Signer, which is an interface to support keys
// where the private part is kept in, for example, a hardware module.
func (priv *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
	return SignASN1(rand, priv, digest, opts)
}

// SignWithSM2 signs uid, msg with priv, reading randomness from rand. Compliance with GB/T 32918.2-2016.
// Deprecated: please use Sign method directly.
func (priv *PrivateKey) SignWithSM2(rand io.Reader, uid, msg []byte) ([]byte, error) {
	return priv.Sign(rand, msg, NewSM2SignerOption(true, uid))
}

// SignMessage signs a message with the private key, reading randomness from rand.
// If opts is an instance of SM2SignerOption, it will use the UID from opts.
// This method is used to comply with the [crypto.MessageSigner] interface.
func (priv *PrivateKey) SignMessage(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
	var uid []byte
	if sm2Opts, ok := opts.(*SM2SignerOption); ok {
		uid = sm2Opts.uid
	}
	return priv.SignWithSM2(rand, uid, msg)
}

// GenerateKey generates a new SM2 private key.
//
// Most applications should use [crypto/rand.Reader] as rand. Note that the
// returned key does not depend deterministically on the bytes read from rand,
// and may change between calls and/or between versions.
//
// According GB/T 32918.1-2016, the private key must be in [1, n-2].
func GenerateKey(rand io.Reader) (*PrivateKey, error) {
	randutil.MaybeReadByte(rand)

	c := p256()
	k, Q, err := randomPoint(c, rand, true)
	if err != nil {
		return nil, err
	}

	priv := new(PrivateKey)
	priv.PublicKey.Curve = c.curve
	priv.D = new(big.Int).SetBytes(k.Bytes(c.N))
	priv.PublicKey.X, priv.PublicKey.Y, err = c.pointToAffine(Q)
	if err != nil {
		return nil, err
	}
	return priv, nil
}

// NewPrivateKey checks that key is valid and returns a SM2 PrivateKey.
//
// key - the private key byte slice, the length must be 32 for SM2.
//
// According GB/T 32918.1-2016, the private key must be in [1, n-2].
func NewPrivateKey(key []byte) (*PrivateKey, error) {
	c := p256()
	if len(key) != c.N.Size() {
		return nil, errors.New("sm2: invalid private key size")
	}
	k, err := bigmod.NewNat().SetBytes(key, c.N)
	if err != nil || k.IsZero() == 1 || k.Equal(c.nMinus1) == 1 {
		return nil, errInvalidPrivateKey
	}
	p, err := c.newPoint().ScalarBaseMult(k.Bytes(c.N))
	if err != nil {
		return nil, err
	}
	priv := new(PrivateKey)
	priv.PublicKey.Curve = c.curve
	priv.D = new(big.Int).SetBytes(k.Bytes(c.N))
	priv.PublicKey.X, priv.PublicKey.Y, err = c.pointToAffine(p)
	if err != nil {
		return nil, err
	}
	return priv, nil
}

// ParseRawPrivateKey parses a private key encoded as a fixed-length big-endian
// integer, according to SEC 1, Version 2.0, Section 2.3.6 (sometimes referred
// to as the raw format). It returns an error if the value is not reduced modulo
// the curve's order minus one, or if it's zero.
//
// Note that private keys are more commonly encoded in ASN.1 or PKCS#8 format,
// which can be parsed with [smx509.ParseECPrivateKey] or
// [smx509.ParsePKCS8PrivateKey] (and [encoding/pem]).
func ParseRawPrivateKey(data []byte) (*PrivateKey, error) {
	return NewPrivateKey(data)
}

// NewPrivateKeyFromInt creates a new SM2 private key from a given big integer.
// It returns an error if the provided key is nil.
func NewPrivateKeyFromInt(key *big.Int) (*PrivateKey, error) {
	if key == nil {
		return nil, errors.New("sm2: private key is nil")
	}
	keyBytes := make([]byte, p256().N.Size())
	return NewPrivateKey(key.FillBytes(keyBytes))
}

// NewPublicKey checks that the provided key is valid and returns an SM2 PublicKey.
//
// The key parameter is a byte slice representing the public key in uncompressed format.
// According to GB/T 32918.1-2016, the public key must be in the correct format and on the curve.
func NewPublicKey(key []byte) (*ecdsa.PublicKey, error) {
	c := p256()
	// Reject the point at infinity and compressed encodings.
	// Points at infinity are invalid because they do not represent a valid point on the curve.
	// Compressed encodings are not supported by this implementation, so they are also rejected.
	if len(key) == 0 || key[0] != 4 {
		return nil, errInvalidPublicKey
	}
	// SetBytes also checks that the point is on the curve.
	p, err := c.newPoint().SetBytes(key)
	if err != nil {
		return nil, err
	}
	k := new(ecdsa.PublicKey)
	k.Curve = c.curve
	k.X, k.Y, err = c.pointToAffine(p)
	if err != nil {
		return nil, err
	}
	return k, nil
}

// ParseUncompressedPublicKey parses a public key encoded as an uncompressed
// point according to SEC 1, Version 2.0, Section 2.3.3 (also known as the X9.62
// uncompressed format). It returns an error if the point is not in uncompressed
// form, is not on the curve, or is the point at infinity.
//
// Note that public keys are more commonly encoded in DER (or PEM) format, which
// can be parsed with [smx509.ParsePKIXPublicKey] (and [encoding/pem]).
func ParseUncompressedPublicKey(data []byte) (*ecdsa.PublicKey, error) {
	return NewPublicKey(data)
}

var defaultUID = []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}

// CalculateZA ZA = H256(ENTLA || IDA || a || b || xG || yG || xA || yA).
// Compliance with GB/T 32918.2-2016 5.5.
//
// This function will NOT use default UID even the uid argument is empty.
// Reference: GM/T 0009-2023 Chapter 8.1.
func CalculateZA(pub *ecdsa.PublicKey, uid []byte) ([]byte, error) {
	uidLen := len(uid)
	if uidLen > 0x1fff {
		return nil, errors.New("sm2: the uid is too long")
	}
	uidBitLength := uint16(uidLen) << 3
	md := sm3.New()
	md.Write([]byte{byte(uidBitLength >> 8), byte(uidBitLength)})
	if uidLen > 0 {
		md.Write(uid)
	}
	writeCurveParams(md, pub.Curve)
	md.Write(bigIntToBytes(pub.Curve, pub.X))
	md.Write(bigIntToBytes(pub.Curve, pub.Y))
	// Return the calculated ZA value
	return md.Sum(nil), nil
}

// writeCurveParams writes the parameters of the given elliptic curve to the provided hash.Hash.
// It writes the following parameters in order:
// - a: P - 3 (where P is the prime specifying the base field of the curve)
// - B: the coefficient B of the curve equation
// - Gx: the x-coordinate of the base point G
// - Gy: the y-coordinate of the base point G
//
// Parameters:
// - md: the hash.Hash to write the curve parameters to
// - curve: the elliptic.Curve whose parameters are to be written
func writeCurveParams(md hash.Hash, curve elliptic.Curve) {
	a := new(big.Int).Sub(curve.Params().P, big.NewInt(3))
	md.Write(bigIntToBytes(curve, a))
	md.Write(bigIntToBytes(curve, curve.Params().B))
	md.Write(bigIntToBytes(curve, curve.Params().Gx))
	md.Write(bigIntToBytes(curve, curve.Params().Gy))
}

// bigIntToBytes converts a big integer value to a byte slice of the appropriate length for the given elliptic curve.
// The byte slice is zero-padded to the left if necessary to match the curve's byte length.
func bigIntToBytes(curve elliptic.Curve, value *big.Int) []byte {
	byteLen := (curve.Params().BitSize + 7) >> 3
	byteArray := make([]byte, byteLen)
	value.FillBytes(byteArray)
	return byteArray
}

// CalculateSM2Hash calculates the SM2 hash for the given public key, data, and user ID (UID).
// If the UID is not provided, a default UID (1234567812345678) is used.
// The public key must be valid, otherwise will be panic.
// This function is used to calculate the hash value for SM2 signature.
// Reference: GM/T 0009-2023 Chapter 8.1 and 8.2.
func CalculateSM2Hash(pub *ecdsa.PublicKey, data, uid []byte) ([]byte, error) {
	if len(uid) == 0 {
		uid = defaultUID
	}
	za, err := CalculateZA(pub, uid)
	if err != nil {
		return nil, err
	}
	md := sm3.New()
	md.Write(za)
	md.Write(data)
	return md.Sum(nil), nil
}

// SignASN1 signs a hash (which should be the result of hashing a larger message)
// using the private key, priv. If the hash is longer than the bit-length of the
// private key's curve order, the hash will be truncated to that length. It
// returns the ASN.1 encoded signature.
//
// The signature is randomized. Most applications should use [crypto/rand.Reader]
// as rand. Note that the returned signature does not depend deterministically on
// the bytes read from rand, and may change between calls and/or between versions.
//
// If the opts argument is instance of [*SM2SignerOption], and its ForceGMSign is true,
// then the hash will be treated as raw message.
func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte, opts crypto.SignerOpts) ([]byte, error) {
	if sm2Opts, ok := opts.(*SM2SignerOption); ok && sm2Opts.forceGMSign {
		newHash, err := CalculateSM2Hash(&priv.PublicKey, hash, sm2Opts.uid)
		if err != nil {
			return nil, err
		}
		hash = newHash
	}

	randutil.MaybeReadByte(rand)

	switch priv.Curve.Params() {
	case P256().Params():
		return signSM2EC(p256(), priv, rand, hash)
	default:
		return signLegacy(priv, rand, hash)
	}
}

// inverseOfPrivateKeyPlus1 calculates and returns the modular inverse of (private key + 1) modulo the curve order.
// It uses lazy initialization and caching to ensure the calculation is performed only once.
// If the private key is invalid, it returns an error.
func (priv *PrivateKey) inverseOfPrivateKeyPlus1(c *sm2Curve) (*bigmod.Nat, error) {
	var (
		err           error
		oneNat        = bigmod.NewNat().SetUint(1, c.N)
		inverseDPlus1 *bigmod.Nat
		dp1Bytes      []byte
	)
	priv.inverseOfKeyPlus1Once.Do(func() {
		inverseDPlus1, err = bigmod.NewNat().SetBytes(priv.D.Bytes(), c.N)
		if err == nil {
			inverseDPlus1.Add(oneNat, c.N)
			if inverseDPlus1.IsZero() == 1 { // make sure private key is NOT N-1
				err = errInvalidPrivateKey
			} else {
				dp1Bytes, err = _sm2ec.P256OrdInverse(inverseDPlus1.Bytes(c.N))
				if err == nil {
					priv.inverseOfKeyPlus1, err = bigmod.NewNat().SetBytes(dp1Bytes, c.N)
				}
			}
		}
	})
	if err != nil {
		return nil, errInvalidPrivateKey
	}
	return priv.inverseOfKeyPlus1, nil
}

// signSM2EC generates an SM2 digital signature using the provided private key and hash.
// It follows the SM2 signature algorithm as specified in the Chinese cryptographic standards.
//
// Parameters:
// - c: A pointer to the sm2Curve structure representing the elliptic curve parameters.
// - priv: A pointer to the PrivateKey structure containing the private key for signing.
// - rand: An io.Reader instance used to generate random values.
// - hash: A byte slice containing the hash of the message to be signed.
//
// Returns:
// - sig: A byte slice containing the generated signature.
// - err: An error value indicating any issues encountered during the signing process.
//
// The function performs the following steps:
// 1. Computes the inverse of (d + 1) where d is the private key.
// 2. Converts the hash to an integer.
// 3. Generates a random point on the elliptic curve and computes the signature components (r, s).
// 4. Ensures that the signature components are non-zero and valid.
// 5. Encodes the signature components into a byte slice and returns it.
func signSM2EC(c *sm2Curve, priv *PrivateKey, rand io.Reader, hash []byte) (sig []byte, err error) {
	inverseDPlus1, err := priv.inverseOfPrivateKeyPlus1(c)
	if err != nil {
		return nil, err
	}

	var (
		k, r, s *bigmod.Nat
		R       *_sm2ec.SM2P256Point
	)

	// hash to int
	e := bigmod.NewNat()
	hashToNat(c, e, hash)

	for {
		for {
			k, R, err = randomPoint(c, rand, false)
			if err != nil {
				return nil, err
			}
			Rx, err := R.BytesX()
			if err != nil {
				return nil, err
			}
			r, err = bigmod.NewNat().SetOverflowingBytes(Rx, c.N)
			if err != nil {
				return nil, err
			}

			// r = [Rx + e]
			r.Add(e, c.N)

			// checks if r is zero or [r+k] is zero
			if r.IsZero() == 0 {
				t := bigmod.NewNat().Set(k).Add(r, c.N)
				if t.IsZero() == 0 {
					break
				}
			}
		}
		// s = [r * d]
		s, err = bigmod.NewNat().SetBytes(priv.D.Bytes(), c.N)
		if err != nil {
			return nil, err
		}
		s.Mul(r, c.N)
		// k = [k - s]
		k.Sub(s, c.N)
		// k = [(d+1)⁻¹ * (k - r * d)]
		k.Mul(inverseDPlus1, c.N)
		if k.IsZero() == 0 {
			break
		}
	}

	return encodeSignature(r.Bytes(c.N), k.Bytes(c.N))
}

func encodeSignature(r, s []byte) ([]byte, error) {
	var b cryptobyte.Builder
	b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
		addASN1IntBytes(b, r)
		addASN1IntBytes(b, s)
	})
	return b.Bytes()
}

// addASN1IntBytes encodes in ASN.1 a positive integer represented as
// a big-endian byte slice with zero or more leading zeroes.
func addASN1IntBytes(b *cryptobyte.Builder, bytes []byte) {
	for len(bytes) > 0 && bytes[0] == 0 {
		bytes = bytes[1:]
	}
	if len(bytes) == 0 {
		b.SetError(errors.New("invalid integer"))
		return
	}
	b.AddASN1(asn1.INTEGER, func(c *cryptobyte.Builder) {
		if bytes[0]&0x80 != 0 {
			c.AddUint8(0)
		}
		c.AddBytes(bytes)
	})
}

var ErrInvalidSignature = errors.New("sm2: invalid signature")

// VerifyASN1 verifies the ASN.1 encoded signature, sig, of hash using the
// public key, pub. Its return value records whether the signature is valid.
//
// Compliance with GB/T 32918.2-2016 regardless it's SM2 curve or not.
// Caller should make sure the hash's correctness, in other words,
// the caller must pre-compute the hash value.
func VerifyASN1(pub *ecdsa.PublicKey, hash, sig []byte) bool {
	switch pub.Curve.Params() {
	case P256().Params():
		return verifySM2EC(p256(), pub, hash, sig)
	default:
		return verifyLegacy(pub, hash, sig)
	}
}

func verifySM2EC(c *sm2Curve, pub *ecdsa.PublicKey, hash, sig []byte) bool {
	rBytes, sBytes, err := parseSignature(sig)
	if err != nil {
		return false
	}

	Q, err := c.pointFromAffine(pub.X, pub.Y)
	if err != nil {
		return false
	}

	r, err := bigmod.NewNat().SetBytes(rBytes, c.N)
	if err != nil || r.IsZero() == 1 {
		return false
	}
	s, err := bigmod.NewNat().SetBytes(sBytes, c.N)
	if err != nil || s.IsZero() == 1 {
		return false
	}

	e := bigmod.NewNat()
	hashToNat(c, e, hash)

	// p₁ = [s]G
	p1, err := c.newPoint().ScalarBaseMult(s.Bytes(c.N))
	if err != nil {
		return false
	}

	// s = [r + s]
	s.Add(r, c.N)
	if s.IsZero() == 1 {
		return false
	}

	// p₂ = [r+s]Q
	p2, err := Q.ScalarMult(Q, s.Bytes(c.N))
	if err != nil {
		return false
	}

	// BytesX returns an error for the point at infinity.
	Rx, err := p1.Add(p1, p2).BytesX()
	if err != nil {
		return false
	}

	_, err = s.SetOverflowingBytes(Rx, c.N)
	if err != nil {
		return false
	}
	s.Add(e, c.N)

	return s.Equal(r) == 1
}

// VerifyASN1WithSM2 verifies the signature in ASN.1 encoding format sig of raw msg
// and uid using the public key, pub. The uid can be empty, meaning to use the default value.
//
// It returns value records whether the signature is valid. Compliance with GB/T 32918.2-2016.
func VerifyASN1WithSM2(pub *ecdsa.PublicKey, uid, msg, sig []byte) bool {
	digest, err := CalculateSM2Hash(pub, msg, uid)
	if err != nil {
		return false
	}
	return VerifyASN1(pub, digest, sig)
}

func parseSignature(sig []byte) (r, s []byte, err error) {
	var inner cryptobyte.String
	input := cryptobyte.String(sig)
	if !input.ReadASN1(&inner, asn1.SEQUENCE) ||
		!input.Empty() ||
		!inner.ReadASN1Integer(&r) ||
		!inner.ReadASN1Integer(&s) ||
		!inner.Empty() {
		return nil, nil, errors.New("invalid ASN.1")
	}
	return r, s, nil
}

// hashToNat sets e to the left-most bits of hash, according to
// SEC 1, Section 4.1.3, point 5 and Section 4.1.4, point 3.
func hashToNat(c *sm2Curve, e *bigmod.Nat, hash []byte) {
	// ECDSA asks us to take the left-most log2(N) bits of hash, and use them as
	// an integer modulo N. This is the absolute worst of all worlds: we still
	// have to reduce, because the result might still overflow N, but to take
	// the left-most bits for P-521 we have to do a right shift.
	if size := c.N.Size(); len(hash) > size {
		hash = hash[:size]
		if excess := len(hash)*8 - c.N.BitLen(); excess > 0 {
			hash = append([]byte{}, hash...)
			for i := len(hash) - 1; i >= 0; i-- {
				hash[i] >>= excess
				if i > 0 {
					hash[i] |= hash[i-1] << (8 - excess)
				}
			}
		}
	}
	_, err := e.SetOverflowingBytes(hash, c.N)
	if err != nil {
		panic("sm2: internal error: truncated hash is too long")
	}
}

// IsSM2PublicKey checks if the provided public key is an SM2 public key.
// It takes an interface{} as input and attempts to assert it to an *ecdsa.PublicKey.
// The function returns true if the assertion is successful and the public key's curve is SM2 P-256.
func IsSM2PublicKey(publicKey any) bool {
	pub, ok := publicKey.(*ecdsa.PublicKey)
	return ok && pub.Curve == sm2ec.P256()
}

// P256 returns sm2 curve signleton, this function is for backward compatibility.
func P256() elliptic.Curve {
	return sm2ec.P256()
}

// PublicKeyToECDH returns k as a [ecdh.PublicKey]. It returns an error if the key is
// invalid according to the definition of [ecdh.Curve.NewPublicKey], or if the
// Curve is not supported by ecdh.
func PublicKeyToECDH(k *ecdsa.PublicKey) (*ecdh.PublicKey, error) {
	c := curveToECDH(k.Curve)
	if c == nil {
		return nil, errors.New("sm2: unsupported curve by ecdh")
	}
	if !k.Curve.IsOnCurve(k.X, k.Y) {
		return nil, errInvalidPublicKey
	}
	return c.NewPublicKey(elliptic.Marshal(k.Curve, k.X, k.Y))
}

// ECDH returns k as a [ecdh.PrivateKey]. It returns an error if the key is
// invalid according to the definition of [ecdh.Curve.NewPrivateKey], or if the
// Curve is not supported by ecdh.
func (k *PrivateKey) ECDH() (*ecdh.PrivateKey, error) {
	c := curveToECDH(k.Curve)
	if c == nil {
		return nil, errors.New("sm2: unsupported curve by ecdh")
	}
	size := (k.Curve.Params().N.BitLen() + 7) / 8
	if k.D.BitLen() > size*8 {
		return nil, errInvalidPrivateKey
	}
	return c.NewPrivateKey(k.D.FillBytes(make([]byte, size)))
}

func curveToECDH(c elliptic.Curve) ecdh.Curve {
	switch c {
	case sm2ec.P256():
		return ecdh.P256()
	default:
		return nil
	}
}

// randomPoint returns a random scalar and the corresponding point using the
// procedure given in FIPS 186-4, Appendix B.5.2 (rejection sampling).
func randomPoint(c *sm2Curve, rand io.Reader, checkOrderMinus1 bool) (k *bigmod.Nat, p *_sm2ec.SM2P256Point, err error) {
	k = bigmod.NewNat()
	for {
		b := make([]byte, c.N.Size())
		if _, err = io.ReadFull(rand, b); err != nil {
			return
		}

		// Mask off any excess bits to increase the chance of hitting a value in
		// (0, N). These are the most dangerous lines in the package and maybe in
		// the library: a single bit of bias in the selection of nonces would likely
		// lead to key recovery, but no tests would fail. Look but DO NOT TOUCH.
		if excess := len(b)*8 - c.N.BitLen(); excess > 0 {
			// Just to be safe, assert that this only happens for the one curve that
			// doesn't have a round number of bits.
			if excess != 0 {
				panic("sm2: internal error: unexpectedly masking off bits")
			}
			b[0] >>= excess
		}

		// Checking 0 < k <= N - 2.
		// None of this matters anyway because the chance of selecting
		// zero is cryptographically negligible.
		if _, err = k.SetBytes(b, c.N); err == nil && k.IsZero() == 0 && (!checkOrderMinus1 || k.Equal(c.nMinus1) == 0) {
			break
		}

		if testingOnlyRejectionSamplingLooped != nil {
			testingOnlyRejectionSamplingLooped()
		}
	}

	p, err = c.newPoint().ScalarBaseMult(k.Bytes(c.N))
	return
}

// testingOnlyRejectionSamplingLooped is called when rejection sampling in
// randomPoint rejects a candidate for being higher than the modulus.
var testingOnlyRejectionSamplingLooped func()

// RecoverPublicKeysFromSM2Signature attempts to recover the public keys from an SM2 signature.
// This function takes a hash and a signature as input and returns a slice of possible public keys
// that could have generated the given signature.
//
// Parameters:
// - hash: The hash of the message that was signed.
// - sig: The SM2 signature.
//
// Returns:
// - A slice of pointers to ecdsa.PublicKey, representing the possible public keys.
// - An error if the signature is invalid or if any other error occurs during the recovery process.
//
// The function performs the following steps:
// 1. Parses the signature to extract the r and s values.
// 2. Converts the hash to a big integer (Nat).
// 3. Computes the point p₁ = [-s]G.
// 4. Computes s = [r + s] and its modular inverse.
// 5. Computes the possible x-coordinates (Rx) for the point R.
// 6. For each possible Rx, computes the corresponding point R and derives the public key.
//
// Note: The function handles the case where there are one or two possible values for Rx,
// resulting in two or four possible public keys.
func RecoverPublicKeysFromSM2Signature(hash, sig []byte) ([]*ecdsa.PublicKey, error) {
	c := p256()
	rBytes, sBytes, err := parseSignature(sig)
	if err != nil {
		return nil, err
	}
	r, err := bigmod.NewNat().SetBytes(rBytes, c.N)
	if err != nil || r.IsZero() == 1 {
		return nil, ErrInvalidSignature
	}
	s, err := bigmod.NewNat().SetBytes(sBytes, c.N)
	if err != nil || s.IsZero() == 1 {
		return nil, ErrInvalidSignature
	}

	e := bigmod.NewNat()
	hashToNat(c, e, hash)

	// p₁ = [-s]G
	negS := bigmod.NewNat().ExpandFor(c.N).Sub(s, c.N)
	p1, err := c.newPoint().ScalarBaseMult(negS.Bytes(c.N))
	if err != nil {
		return nil, err
	}

	// s = [r + s]
	s.Add(r, c.N)
	if s.IsZero() == 1 {
		return nil, ErrInvalidSignature
	}
	// sBytes = (r+s)⁻¹
	sBytes, err = _sm2ec.P256OrdInverse(s.Bytes(c.N))
	if err != nil {
		return nil, err
	}

	// r = (Rx + e) mod N
	// Rx = r - e
	r.Sub(e, c.N)
	if r.IsZero() == 1 {
		return nil, ErrInvalidSignature
	}
	pointRx := make([]*bigmod.Nat, 0, 2)
	pointRx = append(pointRx, r)
	// check if Rx in (N, P), small probability event
	s.Set(r)
	s = s.Add(c.N.Nat(), c.P)
	if s.CmpGeq(c.N.Nat()) == 1 {
		pointRx = append(pointRx, s)
	}
	pubs := make([]*ecdsa.PublicKey, 0, 4)
	bytes := make([]byte, 32+1)
	compressFlags := []byte{compressed02, compressed03}
	// Rx has one or two possible values, so point R has two or four possible values
	for _, x := range pointRx {
		rBytes = x.Bytes(c.N)
		copy(bytes[1:], rBytes)
		for _, flag := range compressFlags {
			bytes[0] = flag
			// p0 = R
			p0, err := c.newPoint().SetBytes(bytes)
			if err != nil {
				return nil, err
			}
			// p0 = R - [s]G
			p0.Add(p0, p1)
			// Pub = [(r + s)⁻¹](R - [s]G)
			p0.ScalarMult(p0, sBytes)
			pub := new(ecdsa.PublicKey)
			pub.Curve = c.curve
			pub.X, pub.Y, err = c.pointToAffine(p0)
			if err != nil {
				return nil, err
			}
			pubs = append(pubs, pub)
		}
	}

	return pubs, nil
}

type sm2Curve struct {
	newPoint func() *_sm2ec.SM2P256Point
	curve    elliptic.Curve
	N        *bigmod.Modulus
	P        *bigmod.Modulus
	nMinus1  *bigmod.Nat
	nMinus2  []byte
}

// pointFromAffine is used to convert the PublicKey to a sm2 Point.
func (curve *sm2Curve) pointFromAffine(x, y *big.Int) (p *_sm2ec.SM2P256Point, err error) {
	bitSize := curve.curve.Params().BitSize
	// Reject values that would not get correctly encoded.
	if x.Sign() < 0 || y.Sign() < 0 {
		return p, errors.New("negative coordinate")
	}
	if x.BitLen() > bitSize || y.BitLen() > bitSize {
		return p, errors.New("overflowing coordinate")
	}
	// Encode the coordinates and let SetBytes reject invalid points.
	byteLen := (bitSize + 7) / 8
	buf := make([]byte, 1+2*byteLen)
	buf[0] = 4 // uncompressed point
	x.FillBytes(buf[1 : 1+byteLen])
	y.FillBytes(buf[1+byteLen : 1+2*byteLen])
	return curve.newPoint().SetBytes(buf)
}

// pointToAffine is used to convert a sm2 Point to a PublicKey.
func (curve *sm2Curve) pointToAffine(p *_sm2ec.SM2P256Point) (x, y *big.Int, err error) {
	out := p.Bytes()
	if len(out) == 1 && out[0] == 0 {
		// This is the encoding of the point at infinity.
		return nil, nil, errors.New("sm2: public key point is the infinity")
	}
	byteLen := (curve.curve.Params().BitSize + 7) / 8
	x = new(big.Int).SetBytes(out[1 : 1+byteLen])
	y = new(big.Int).SetBytes(out[1+byteLen:])
	return x, y, nil
}

var p256Once sync.Once
var _p256 *sm2Curve

func p256() *sm2Curve {
	p256Once.Do(func() {
		_p256 = &sm2Curve{
			newPoint: _sm2ec.NewSM2P256Point,
		}
		precomputeParams(_p256, P256())
	})
	return _p256
}

func precomputeParams(c *sm2Curve, curve elliptic.Curve) {
	params := curve.Params()
	c.curve = curve
	c.N, _ = bigmod.NewModulus(params.N.Bytes())
	c.P, _ = bigmod.NewModulus(params.P.Bytes())
	c.nMinus1 = c.N.Nat().SubOne(c.N)
	c.nMinus2 = new(bigmod.Nat).Set(c.nMinus1).SubOne(c.N).Bytes(c.N)
}

// sm2Hasher is a wrapper around a hash.Hash that includes the ZA value for SM2 hashing.
// It is used to perform SM2-specific hashing operations with the provided public key and user ID.
type sm2Hasher struct {
	inner hash.Hash
	za    []byte
}

// NewHash creates a new hash.Hash instance using the provided SM2 public key.
// It uses the default SM3 hash function and default user ID.
func NewHash(pub *ecdsa.PublicKey) (hash.Hash, error) {
	return NewHashWithUserID(pub, defaultUID)
}

// NewHashWithUserID creates a new hash.Hash instance using the provided SM2 public key and user ID.
// It internally uses the SM3 hash function.
func NewHashWithUserID(pub *ecdsa.PublicKey, userID []byte) (hash.Hash, error) {
	return NewHashWithHashAndUserID(pub, sm3.New, userID)
}

// NewHashWithHashAndUserID creates a new hash.Hash instance that incorporates SM2-specific
// hashing with the provided public key, inner hash and user ID.
// The returned hasher is reset before being returned.
func NewHashWithHashAndUserID(pub *ecdsa.PublicKey, h func() hash.Hash, userID []byte) (hash.Hash, error) {
	inner := h()
	za, err := CalculateZA(pub, userID)
	if err != nil {
		return nil, err
	}
	hasher := &sm2Hasher{inner: inner, za: za}
	hasher.Write(za)
	return hasher, nil
}

// Write writes the contents of p into the underlying hash function.
// It returns the number of bytes written from p (n) and any error encountered (err).
// This method satisfies the io.Writer interface.
func (s *sm2Hasher) Write(p []byte) (n int, err error) {
	return s.inner.Write(p)
}

// Sum appends the current hash to b and returns the resulting slice.
// It does not change the underlying hash state.
func (s *sm2Hasher) Sum(b []byte) []byte {
	return s.inner.Sum(b)
}

// Reset clears the current state of the sm2Hasher and reinitializes it.
// It first resets the inner hash state and then writes the ZA value to it.
func (s *sm2Hasher) Reset() {
	s.inner.Reset()
	s.inner.Write(s.za)
}

// Size returns the size of the hash in bytes.
func (s *sm2Hasher) Size() int {
	return s.inner.Size()
}

// BlockSize returns the block size of the hash function in bytes.
// It delegates the call to the inner hash function's BlockSize method.
func (s *sm2Hasher) BlockSize() int {
	return s.inner.BlockSize()
}
