package main

import (
	"math/big"
	"errors"
	"crypto/elliptic"
	"crypto/ecdsa"
	"crypto"
	"strconv"
	"fmt"
	"encoding/asn1"
	"encoding/json"
	"hash"
	"github.com/coupons/sm_crypto/sm2"
	"github.com/coupons/sm_crypto/sm3"
	"encoding/hex"
)

type PublicKeyAlgorithm int

const (
	UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota
	RSA
	DSA
	ECDSA
	Ed25519
	SM2
)

var publicKeyAlgoName = [...]string{
	RSA:     "RSA",
	DSA:     "DSA",
	ECDSA:   "ECDSA",
	Ed25519: "Ed25519",
	SM2:     "SM2",
}

const (
	// number of bits in a big.Word
	wordBits = 32 << (uint64(^big.Word(0)) >> 63)
	// number of bytes in a big.Word
	wordBytes = wordBits / 8
)

/*
	得到string 类型的PublicKeyAlgorithm
*/
func (algo PublicKeyAlgorithm) String() string {
	if 0 < algo && int(algo) < len(publicKeyAlgoName) {
		return publicKeyAlgoName[algo]
	}
	return strconv.Itoa(int(algo))
}

type SignatureAlgorithm int

const (
	UnknownSignatureAlgorithm SignatureAlgorithm = iota
	MD2WithRSA
	MD5WithRSA
	SHA1WithRSA
	SHA256WithRSA
	SHA384WithRSA
	SHA512WithRSA
	DSAWithSHA1
	DSAWithSHA256
	ECDSAWithSHA1
	ECDSAWithSHA256
	ECDSAWithSHA384
	ECDSAWithSHA512
	SHA256WithRSAPSS
	SHA384WithRSAPSS
	SHA512WithRSAPSS
	SM2WithSM3
	PureEd25519
)

type CertType int
const (
	UnknownCertType CertType = iota
	OrganizationCert
	ChannelCert
	MerchantCert

)

type TxType int
const (
	UnknownTxType TxType = iota
	CreateCoupon
	DisCoupon
	ApplyCoupon
	UseCoupon

)
type ecdsaSignature dsaSignature

var signatureAlgorithmDetails = []struct {
	algo       SignatureAlgorithm
	name       string
	pubKeyAlgo PublicKeyAlgorithm
	hash       crypto.Hash
}{
	{ECDSAWithSHA1, "ECDSA-SHA1", ECDSA, crypto.SHA1},
	{ECDSAWithSHA256, "ECDSA-SHA256", ECDSA, crypto.SHA256},
	{ECDSAWithSHA384, "ECDSA-SHA384", ECDSA, crypto.SHA384},
	{ECDSAWithSHA512, "ECDSA-SHA512", ECDSA, crypto.SHA512},
	{PureEd25519, "Ed25519", Ed25519, crypto.Hash(0) /* no pre-hashing */},
	{SM2WithSM3, "SM2", SM2, crypto.Hash(21) /* no pre-hashing */},
}

// ErrUnsupportedAlgorithm results from attempting to perform an operation that
// involves algorithms that are not currently implemented.
var ErrUnsupportedAlgorithm = errors.New("x509: cannot verify signature: algorithm unimplemented")

func signaturePublicKeyAlgoMismatchError(expectedPubKeyAlgo PublicKeyAlgorithm, pubKey interface{}) error {
	return fmt.Errorf("x509: signature algorithm specifies an %s public key, but have public key of type %T", expectedPubKeyAlgo.String(), pubKey)
}

// RFC 5480, 2.1.1.1. Named Curve
//
// secp224r1 OBJECT IDENTIFIER ::= {
//   iso(1) identified-organization(3) certicom(132) curve(0) 33 }
//
// secp256r1 OBJECT IDENTIFIER ::= {
//   iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3)
//   prime(1) 7 }
//
// secp384r1 OBJECT IDENTIFIER ::= {
//   iso(1) identified-organization(3) certicom(132) curve(0) 34 }
//
// secp521r1 OBJECT IDENTIFIER ::= {
//   iso(1) identified-organization(3) certicom(132) curve(0) 35 }
//
// NB: secp256r1 is equivalent to prime256v1
var (
	oidNamedCurveP224    = asn1.ObjectIdentifier{1, 3, 132, 0, 33}
	oidNamedCurveP256    = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}
	oidNamedCurveP384    = asn1.ObjectIdentifier{1, 3, 132, 0, 34}
	oidNamedCurveP521    = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
	oidNamedCurveP256SM2 = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 301} // I get the SM2 ID through parsing the pem file generated by gmssl
	oidSignatureEd25519  = asn1.ObjectIdentifier{1, 3, 101, 112}
)

// RFC 3279, 2.3 Public Key Algorithms
//
// pkcs-1 OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
//    rsadsi(113549) pkcs(1) 1 }
//
// rsaEncryption OBJECT IDENTIFIER ::== { pkcs1-1 1 }
//
// id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
//    x9-57(10040) x9cm(4) 1 }
//
// RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters
//
// id-ecPublicKey OBJECT IDENTIFIER ::= {
//       iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
var (
	oidPublicKeyRSA     = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
	oidPublicKeyDSA     = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
	oidPublicKeyECDSA   = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
	oidPublicKeyEd25519 = oidSignatureEd25519
)

type dsaSignature struct {
	R, S *big.Int
}


type PrivateKeySerialize struct {
	PrivSerialize    []byte     `json:"priv_serialize"`  //私钥序列化的数据
	PrivType		 crypto.Hash`json:"priv_type"`
}

/*
   券发行证书格式
*/
type Certificate struct {
	PublicKeyHashAlgorithm crypto.Hash    `json:"public_key_hash_algorithm""` //公钥算法
	PublicKeyByte      []byte             `json:"public_key_byte""`
	PublicKey          interface{}       `json:"publickey"`  //公钥
	CertType 		   CertType				`json:"cert_type"`  //证书类型
	Extensions         interface{}       `json:"extensions"` //拓展字段信息，这个字段的扩展信息与证书的扩展字段不同
	NotBefore          int64              `json:"not_before"`
	NotAfter           int64              `json:"not_after"`           // Validity bounds.
	CertHash           []byte             `json:"cert_hash"`           // 原证书hash，留存
	Signature          []byte             `json:"signature"`           //签发者证书签名数据，保证数据不被篡改
	SignatureAlgorithm SignatureAlgorithm `json:"signature_algorithm"` //使用的签名and hash 算法
}



/*
	验证是否是我们当前支持的签名算法
*/
func  isSupportSignatureAlgorithm(algo crypto.Hash) bool {
	switch algo {
	case crypto.SHA256, crypto.SHA1, crypto.SHA224,crypto.SHA384,crypto.SHA512,crypto.Hash(21):
		return true
	default:
		return false
	}
}
/*
	验证cert 证书是否是parent证书签发
	parent：父签名证书
	cert: 子签名整数
*/
func (this *Certificate) CheckSignatureFrom(parent *Certificate) (err error) {
	if !isSupportSignatureAlgorithm(parent.PublicKeyHashAlgorithm)  {
		return ErrUnsupportedAlgorithm
	}
	signature := this.Signature
	publicKey := this.PublicKey    //公钥信息无法直接unmarshal,所以设为nil
	this.Signature = nil
	this.PublicKey = nil
	certRaw, err := json.Marshal(this)
	if err != nil {
		return
	}
	this.Signature = signature
	this.PublicKey = publicKey
	return parent.CheckSignature(this.SignatureAlgorithm, certRaw, signature)
}

/*
	验证数据是否是由this签发
	Parameters:
		algo:sign和hash 算法，
		signed:待签名的原始数据
		signature：签名
*/
func (this *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature []byte) (err error) {
	return checkSignature(algo, signed, signature, this.PublicKey)
}

/*
	证书解析
	Parameters:
		certByte:byte 类型的证书信息，
*/
func ParseCertificate(certByte []byte) (*Certificate, error) {
	cert := &Certificate{}
	err := json.Unmarshal(certByte, cert)
	if err != nil {
		return nil, err
	}
	pub, err := unmarshalPubkey(cert.PublicKeyByte, namedCurveFromHash(cert.PublicKeyHashAlgorithm))
	if err != nil {
		return nil, err
	}
	cert.PublicKey = pub
	return cert, nil
}

/*
	私钥解析
	Parameters:
		curve: 私钥使用的椭圆曲线参数，
		pk: 私钥序列化数据
*/
func PrivKeyFromBytes(curve elliptic.Curve, pk []byte) (priv crypto.Signer,pubKey []byte) {
	x, y := curve.ScalarBaseMult(pk)
	pubKey = elliptic.Marshal(curve, x, y)
	switch curve {
	case sm2.P256Sm2():
		priv = &sm2.PrivateKey{
			PublicKey: sm2.PublicKey{
				Curve: curve,
				X:     x,
				Y:     y,
			},
			D: new(big.Int).SetBytes(pk),
		}
		return
	default:
		priv = &ecdsa.PrivateKey{
			PublicKey: ecdsa.PublicKey{
				Curve: curve,
				X:     x,
				Y:     y,
			},
			D: new(big.Int).SetBytes(pk),
		}
		return
	}
}

/*
	证书解析
	Parameters:
		privHex:十六进制的PrivateKeySerialize 编码信息
*/
func ParsePrivateKey(privHex string)(priv crypto.Signer,pubKey []byte,err error){
	privByte,err := hex.DecodeString(privHex)
	if err!=nil{
		return nil,nil,err
	}
	privS :=&PrivateKeySerialize{}
	err = json.Unmarshal(privByte,privS)
	if err!=nil{
		return nil,nil,err
	}
	curve := namedCurveFromHash(privS.PrivType)
	priv,pubKey = PrivKeyFromBytes(curve,privS.PrivSerialize)
	return
}
/*
	私钥解析
	Parameters:
		curve: 私钥使用的椭圆曲线参数，
		d: privateKey.D
*/
func SerializePrivateKey(curve elliptic.Curve,d *big.Int)(privHex string,err error){
	hash := namedCurveToHash(curve)
	if hash == crypto.Hash(0){
		return "", errors.New("invaild curve")
	}
	privSerialize := &PrivateKeySerialize{
		PrivSerialize:Serialize(d),
		PrivType:hash,
	}
	privSByte,err := json.Marshal(privSerialize)
	if err!= nil{
		return "",err
	}
	return hex.EncodeToString(privSByte),nil
}

const PrivKeyBytesLen = 32

// 序列化privKey.D。
func Serialize(d *big.Int) []byte {
	b := make([]byte, 0, PrivKeyBytesLen)
	return paddedAppend(PrivKeyBytesLen, b, d.Bytes())
}

/*
	签名验证
	Parameters:
		algo:签名使用的hash以及私钥算法
		signed：待签名数据
		signature：签名
		publicKey：验证公钥
*/
func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey crypto.PublicKey) (err error) {
	var hashType crypto.Hash
	var pubKeyAlgo PublicKeyAlgorithm

	for _, details := range signatureAlgorithmDetails {
		if details.algo == algo {
			hashType = details.hash
			pubKeyAlgo = details.pubKeyAlgo
		}
	}
	switch hashType {
	case crypto.Hash(0):
		if pubKeyAlgo != Ed25519 {
			return ErrUnsupportedAlgorithm
		}
	case crypto.MD5:
		return nil
	default:
		if !hashType.Available() {
			if hashType != crypto.Hash(21) {
				return ErrUnsupportedAlgorithm
			}
		}
		var h hash.Hash
		if pubKeyAlgo == SM2 {
			h = sm3.New()
		} else {
			h = hashType.New()
		}
		h.Write(signed)
		signed = h.Sum(nil)
	}

	switch pub := publicKey.(type) {
	case *ecdsa.PublicKey:
		if !(pubKeyAlgo == ECDSA || pubKeyAlgo == SM2) {
			return signaturePublicKeyAlgoMismatchError(pubKeyAlgo, pub)
		}

		switch pubKeyAlgo {
		case SM2:
			if len(signature) == 65 {
				signature = signature[:64]
			}
			r := new(big.Int).SetBytes(signature[:32])
			s := new(big.Int).SetBytes(signature[32:])
			if !sm2.Verify(&sm2.PublicKey{
				Curve: pub.Curve,
				X:     pub.X,
				Y:     pub.Y,
			}, signed, r, s) {
				return errors.New("x509: SM2 verification failure")
			}
		default:
			ecdsaSig := new(ecdsaSignature)
			if rest, err := asn1.Unmarshal(signature, ecdsaSig); err != nil {
				return err
			} else if len(rest) != 0 {
				return errors.New("x509: trailing data after ECDSA signature")
			}
			if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
				return errors.New("x509: ECDSA signature contained zero or negative values")
			}
			if !ecdsa.Verify(pub, signed, ecdsaSig.R, ecdsaSig.S) {
				return errors.New("x509: ECDSA verification failure")
			}
		}
		return
	case *sm2.PublicKey:
		if pubKeyAlgo != SM2 {
			return signaturePublicKeyAlgoMismatchError(pubKeyAlgo, pub)
		}
		if len(signature) == 65 {
			signature = signature[:64]
		}
		r := new(big.Int).SetBytes(signature[:32])
		s := new(big.Int).SetBytes(signature[32:])
		if !(sm2.Verify(pub, signed, r, s)) {
			return errors.New("SM2 verification failure")
		}
		return
	}
	return ErrUnsupportedAlgorithm
}
/*
	签名验证
	Parameters:
		pub:公钥编码数据
		curve：私钥使用的椭圆曲线参数，
*/
func unmarshalPubkey(pub []byte, curve elliptic.Curve) (interface{}, error) {
	if curve == nil {
		return nil, errors.New("public key curve is null")
	}
	x, y := elliptic.Unmarshal(curve, pub)
	if x == nil {
		return nil, errors.New("invalid public key")
	}
	switch curve {
	case sm2.P256Sm2():
		return &sm2.PublicKey{Curve: curve, X: x, Y: y}, nil
	default:
		return &ecdsa.PublicKey{Curve: curve, X: x, Y: y}, nil
	}
}

func namedCurveFromHash(hash crypto.Hash) elliptic.Curve {
	switch hash {
	case crypto.SHA224:
		return elliptic.P224()
	case crypto.SHA256:
		return elliptic.P256()
	case crypto.SHA384:
		return elliptic.P384()
	case crypto.SHA512:
		return elliptic.P521()
	case crypto.Hash(21):
		return sm2.P256Sm2()
	}
	return nil
}


func namedCurveToHash(curve elliptic.Curve) (crypto.Hash ){
	switch curve {
	case elliptic.P224():
		return crypto.SHA256
	case elliptic.P256():
		return crypto.SHA256
	case elliptic.P384():
		return crypto.SHA384
	case elliptic.P521():
		return crypto.SHA512
	case sm2.P256Sm2():
		return crypto.Hash(21)
	}
	return crypto.Hash(0)
}
// paddedAppend appends the src byte slice to dst, returning the new slice.
// If the length of the source is smaller than the passed size, leading zero
// bytes are appended to the dst slice before appending src.
func paddedAppend(size uint, dst, src []byte) []byte {
	for i := 0; i < int(size)-len(src); i++ {
		dst = append(dst, 0)
	}
	return append(dst, src...)
}

// ReadBits encodes the absolute value of bigint as big-endian bytes. Callers must ensure
// that buf has enough space. If buf is too short the result will be incomplete.
func ReadBits(bigint *big.Int, buf []byte) {
	i := len(buf)
	for _, d := range bigint.Bits() {
		for j := 0; j < wordBytes && i > 0; j++ {
			i--
			buf[i] = byte(d)
			d >>= 8
		}
	}
}


//func MarshalPublicKey(curve elliptic.Curve,x,y *big.Int ){
//
//}