gitee.com/zhaochuninhefei/gmgo@v0.0.31-0.20240209061119-069254a02979/ecdsa_ext/ecdsa_ext.go (about)

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