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 }