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 }