gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/ecdsa_ext/ecdsa_ext.go (about)

     1  // Copyright (c) 2023 zhaochun
     2  // core-gm is licensed under Mulan PSL v2.
     3  // You can use this software according to the terms and conditions of the Mulan PSL v2.
     4  // You may obtain a copy of Mulan PSL v2 at:
     5  //          http://license.coscl.org.cn/MulanPSL2
     6  // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
     7  // See the Mulan PSL v2 for more details.
     8  
     9  // Package ecdsa_ext ecdsa扩展包
    10  package ecdsa_ext
    11  
    12  import (
    13  	"crypto"
    14  	"crypto/ecdsa"
    15  	"crypto/elliptic"
    16  	"errors"
    17  	"fmt"
    18  	"gitee.com/ks-custle/core-gm/ecbase"
    19  	"golang.org/x/crypto/cryptobyte"
    20  	"golang.org/x/crypto/cryptobyte/asn1"
    21  	"io"
    22  	"log"
    23  	"math/big"
    24  )
    25  
    26  // PublicKey ecdsa_ext扩展公钥
    27  //
    28  //	注意, 该结构体指针上绑定了Verify方法从而实现了`ecbase.EcVerifier`接口
    29  type PublicKey struct {
    30  	ecdsa.PublicKey
    31  }
    32  
    33  // Equal 为ecdsa_ext扩展公钥绑定Equal方法, 用于实现`x509.publicKey`接口
    34  func (pub *PublicKey) Equal(x crypto.PublicKey) bool {
    35  	xx, ok := x.(*PublicKey)
    36  	if !ok {
    37  		return false
    38  	}
    39  	return pub.X.Cmp(xx.X) == 0 && pub.Y.Cmp(xx.Y) == 0 &&
    40  		// Standard library Curve implementations are singletons, so this check
    41  		// will work for those. Other Curves might be equivalent even if not
    42  		// singletons, but there is no definitive way to check for that, and
    43  		// better to err on the side of safety.
    44  		pub.Curve == xx.Curve
    45  }
    46  
    47  // EcVerify 为ecdsa_ext扩展公钥绑定Verify方法, 用于实现`ecbase.EcVerifier`接口
    48  //
    49  //	默认需要low-s检查
    50  func (pub *PublicKey) EcVerify(digest []byte, sig []byte, opts ecbase.EcSignerOpts) (bool, error) {
    51  	if opts == nil {
    52  		opts = ecbase.CreateDefaultEcSignerOpts()
    53  	}
    54  	// 如果有low-s要求,则检查签名s值是否low-s
    55  	if opts.NeedLowS() {
    56  		log.Println("在ecdsa_ext验签时执行IsSigLowS检查")
    57  		lowS, err := IsSigLowS(&pub.PublicKey, sig)
    58  		if err != nil {
    59  			return false, err
    60  		}
    61  		if !lowS {
    62  			return false, errors.New("ecdsa_ext签名的s值不是low-s值")
    63  		}
    64  		log.Println("在ecdsa_ext验签时执行IsSigLowS检查OK")
    65  	}
    66  	valid := ecdsa.VerifyASN1(&pub.PublicKey, digest, sig)
    67  	if !valid {
    68  		log.Println("ecdsa_ext验签失败")
    69  		return false, errors.New("ecdsa_ext验签失败")
    70  	}
    71  	log.Println("ecdsa_ext验签成功")
    72  	return true, nil
    73  }
    74  
    75  // ConvPubKeyFromOrigin 将`*ecdsa.PublicKey`封装为ecdsa_ext扩展公钥
    76  //
    77  //goland:noinspection GoUnusedExportedFunction
    78  func ConvPubKeyFromOrigin(oriKey *ecdsa.PublicKey) *PublicKey {
    79  	pubKey := &PublicKey{
    80  		PublicKey: *oriKey,
    81  	}
    82  	return pubKey
    83  }
    84  
    85  // PrivateKey ecdsa_ext扩展私钥
    86  //
    87  //	注意,该结构体指针上绑定了Public与Sign方法从而实现了`crypto.Signer`接口
    88  type PrivateKey struct {
    89  	ecdsa.PrivateKey
    90  }
    91  
    92  // Public 为ecdsa_ext扩展私钥绑定Public方法, 用于实现`crypto.Signer`接口
    93  func (priv *PrivateKey) Public() crypto.PublicKey {
    94  	oriPub := priv.PublicKey
    95  	return &PublicKey{
    96  		PublicKey: oriPub,
    97  	}
    98  }
    99  
   100  // Sign 为ecdsa_ext扩展私钥绑定Sign方法, 用于实现`crypto.Signer`接口
   101  //
   102  //	注意,这里实现的Sign方法可以通过使用`ecbase.CreateEcSignerOpts()`生成opts参数,并通过其参数needLowS来指定是否需要对签名做lows处理。
   103  //	ecdsa签名是(r,s),其中s值有一个反s值(n-s), s与n-s是沿n/2对称的,一个是高值,一个是低值。而在ecdsa的验签逻辑里,如果取反s值也可以创建一个有效的签名(r, n-s)。
   104  //	这种特性可能会导致ECDSA签名被重放攻击和双花攻击利用,因为攻击者可以使用同一个r值和反s值来伪造不同的消息。
   105  //	而low-s处理则在签名后判断s值是否是高值,是则替换为低值,然后在验签时需要额外检查签名s值是不是low-s值,这样就避免了攻击者使用反s值伪造消息。
   106  func (priv *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
   107  	r, s, err := ecdsa.Sign(rand, &priv.PrivateKey, digest)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	if opts == nil {
   112  		opts = ecbase.CreateDefaultEcSignerOpts()
   113  	}
   114  	// 判断是否需要low-s处理
   115  	if ecOpts, ok := opts.(ecbase.EcSignerOpts); ok {
   116  		if ecOpts.NeedLowS() {
   117  			log.Println("在sign时尝试ToLowS处理")
   118  			doLow := false
   119  			doLow, s, err = ToLowS(&priv.PublicKey, s)
   120  			if err != nil {
   121  				return nil, err
   122  			}
   123  			if doLow {
   124  				log.Println("在sign时完成ToLowS处理")
   125  			}
   126  
   127  		}
   128  	}
   129  
   130  	var b cryptobyte.Builder
   131  	b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
   132  		b.AddASN1BigInt(r)
   133  		b.AddASN1BigInt(s)
   134  	})
   135  	return b.Bytes()
   136  }
   137  
   138  func ConvPrivKeyFromOrigin(oriKey *ecdsa.PrivateKey) *PrivateKey {
   139  	privKey := &PrivateKey{
   140  		PrivateKey: *oriKey,
   141  	}
   142  	return privKey
   143  }
   144  
   145  func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) {
   146  	oriKey, err := ecdsa.GenerateKey(c, rand)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  	return ConvPrivKeyFromOrigin(oriKey), nil
   151  }
   152  
   153  var (
   154  	// curveHalfOrders contains the precomputed curve group orders halved.
   155  	// It is used to ensure that signature' S value is lower or equal to the
   156  	// curve group order halved. We accept only low-S signatures.
   157  	// They are precomputed for efficiency reasons.
   158  	curveHalfOrders = map[elliptic.Curve]*big.Int{
   159  		elliptic.P224(): new(big.Int).Rsh(elliptic.P224().Params().N, 1),
   160  		elliptic.P256(): new(big.Int).Rsh(elliptic.P256().Params().N, 1),
   161  		elliptic.P384(): new(big.Int).Rsh(elliptic.P384().Params().N, 1),
   162  		elliptic.P521(): new(big.Int).Rsh(elliptic.P521().Params().N, 1),
   163  	}
   164  )
   165  
   166  //goland:noinspection GoUnusedExportedFunction
   167  func AddCurveHalfOrders(curve elliptic.Curve, halfOrder *big.Int) {
   168  	curveHalfOrders[curve] = halfOrder
   169  }
   170  
   171  //goland:noinspection GoUnusedExportedFunction
   172  func GetCurveHalfOrdersAt(c elliptic.Curve) *big.Int {
   173  	return big.NewInt(0).Set(curveHalfOrders[c])
   174  }
   175  
   176  // IsSigLowS 检查ecdsa签名的s值是否是low-s值
   177  func IsSigLowS(k *ecdsa.PublicKey, signature []byte) (bool, error) {
   178  	_, s, err := ecbase.UnmarshalECSignature(signature)
   179  	if err != nil {
   180  		return false, err
   181  	}
   182  	return IsLowS(k, s)
   183  }
   184  
   185  // SignatureToLowS 检查ecdsa签名的s值是否是lower-s值,如果不是,则将s转为对应的lower-s值并重新序列化为ecdsa签名
   186  //
   187  //goland:noinspection GoUnusedExportedFunction
   188  func SignatureToLowS(k *ecdsa.PublicKey, signature []byte) (bool, []byte, error) {
   189  	r, s, err := ecbase.UnmarshalECSignature(signature)
   190  	if err != nil {
   191  		return false, nil, err
   192  	}
   193  	hasToLow := false
   194  	hasToLow, s, err = ToLowS(k, s)
   195  	if err != nil {
   196  		return hasToLow, nil, err
   197  	}
   198  
   199  	ecSignature, err := ecbase.MarshalECSignature(r, s)
   200  	if err != nil {
   201  		return hasToLow, nil, err
   202  	}
   203  
   204  	return hasToLow, ecSignature, nil
   205  }
   206  
   207  // IsLowS checks that s is a low-S
   208  func IsLowS(k *ecdsa.PublicKey, s *big.Int) (bool, error) {
   209  	halfOrder, ok := curveHalfOrders[k.Curve]
   210  	if !ok {
   211  		return false, fmt.Errorf("curve not recognized [%s]", k.Curve)
   212  	}
   213  	return s.Cmp(halfOrder) != 1, nil
   214  
   215  }
   216  
   217  func ToLowS(k *ecdsa.PublicKey, s *big.Int) (bool, *big.Int, error) {
   218  	lowS, err := IsLowS(k, s)
   219  	if err != nil {
   220  		return false, nil, err
   221  	}
   222  
   223  	if !lowS {
   224  		// Set s to N - s that will be then in the lower part of signature space
   225  		// less or equal to half order
   226  		//fmt.Println("执行lows处理")
   227  		s.Sub(k.Params().N, s)
   228  		return true, s, nil
   229  	}
   230  
   231  	return false, s, nil
   232  }