package ecdsa

import (
	"crypto"
	"crypto/internal/randutil"
	"errors"
	"io"
	"math/big"

	boring "crypto/internal/backend"

	"golang.org/x/crypto/cryptobyte"
	"golang.org/x/crypto/cryptobyte/asn1"
)

func HashSign(rand io.Reader, priv *PrivateKey, msg []byte, h crypto.Hash) (*big.Int, *big.Int, error) {
	randutil.MaybeReadByte(rand)

	if boring.Enabled() {
		sig, err := HashSignASN1(rand, priv, msg, h)
		if err != nil {
			return nil, nil, err
		}
		r, s := new(big.Int), new(big.Int)
		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 from HashSignECDSA")
		}
		return r, s, nil
	}
	boring.UnreachableExceptTests()

	hash := h.New()
	hash.Write(msg)
	d := hash.Sum(nil)

	return Sign(rand, priv, d)
}

func HashSignASN1(rand io.Reader, priv *PrivateKey, msg []byte, h crypto.Hash) ([]byte, error) {
	randutil.MaybeReadByte(rand)

	if boring.Enabled() {
		b, err := boringPrivateKey(priv)
		if err != nil {
			return nil, err
		}
		return boring.HashSignECDSA(b, h, msg)
	}
	boring.UnreachableExceptTests()

	hash := h.New()
	hash.Write(msg)
	d := hash.Sum(nil)

	return SignASN1(rand, priv, d)
}

func HashVerify(pub *PublicKey, msg []byte, r, s *big.Int, h crypto.Hash) bool {
	if boring.Enabled() {
		sig, err := encodeSignature(r.Bytes(), s.Bytes())
		if err != nil {
			return false
		}
		return HashVerifyASN1(pub, h, msg, sig)
	}
	boring.UnreachableExceptTests()

	hash := h.New()
	hash.Write(msg)
	d := hash.Sum(nil)

	return Verify(pub, d, r, s)
}

func HashVerifyASN1(pub *PublicKey, h crypto.Hash, msg, sig []byte) bool {
	if boring.Enabled() {
		bpk, err := boringPublicKey(pub)
		if err != nil {
			return false
		}
		return boring.HashVerifyECDSA(bpk, h, msg, sig)
	}
	boring.UnreachableExceptTests()

	hash := h.New()
	hash.Write(msg)
	d := hash.Sum(nil)

	return VerifyASN1(pub, d, sig)
}
