Skip to content

Commit 07870f7

Browse files
gzliudanJukLee0ira
authored andcommitted
crypto: use btcec/v2 for no-cgo (ethereum#24533)
1 parent bfa45c1 commit 07870f7

File tree

4 files changed

+62
-20
lines changed

4 files changed

+62
-20
lines changed

crypto/signature_nocgo.go

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,55 +24,72 @@ import (
2424
"crypto/elliptic"
2525
"errors"
2626
"fmt"
27-
"math/big"
2827

29-
"github.com/btcsuite/btcd/btcec"
28+
"github.com/btcsuite/btcd/btcec/v2"
29+
btc_ecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa"
3030
)
3131

3232
// Ecrecover returns the uncompressed public key that created the given signature.
3333
func Ecrecover(hash, sig []byte) ([]byte, error) {
34-
pub, err := SigToPub(hash, sig)
34+
pub, err := sigToPub(hash, sig)
3535
if err != nil {
3636
return nil, err
3737
}
38-
bytes := (*btcec.PublicKey)(pub).SerializeUncompressed()
38+
bytes := pub.SerializeUncompressed()
3939
return bytes, err
4040
}
4141

42-
// SigToPub returns the public key that created the given signature.
43-
func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
42+
func sigToPub(hash, sig []byte) (*btcec.PublicKey, error) {
43+
if len(sig) != SignatureLength {
44+
return nil, errors.New("invalid signature")
45+
}
4446
// Convert to btcec input format with 'recovery id' v at the beginning.
4547
btcsig := make([]byte, SignatureLength)
46-
btcsig[0] = sig[64] + 27
48+
btcsig[0] = sig[RecoveryIDOffset] + 27
4749
copy(btcsig[1:], sig)
4850

49-
pub, _, err := btcec.RecoverCompact(btcec.S256(), btcsig, hash)
50-
return (*ecdsa.PublicKey)(pub), err
51+
pub, _, err := btc_ecdsa.RecoverCompact(btcsig, hash)
52+
return pub, err
53+
}
54+
55+
// SigToPub returns the public key that created the given signature.
56+
func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
57+
pub, err := sigToPub(hash, sig)
58+
if err != nil {
59+
return nil, err
60+
}
61+
return pub.ToECDSA(), nil
5162
}
5263

5364
// Sign calculates an ECDSA signature.
5465
//
5566
// This function is susceptible to chosen plaintext attacks that can leak
5667
// information about the private key that is used for signing. Callers must
57-
// be aware that the given hash cannot be chosen by an adversery. Common
68+
// be aware that the given hash cannot be chosen by an adversary. Common
5869
// solution is to hash any input before calculating the signature.
5970
//
6071
// The produced signature is in the [R || S || V] format where V is 0 or 1.
6172
func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) {
62-
if len(hash) != DigestLength {
73+
if len(hash) != 32 {
6374
return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
6475
}
6576
if prv.Curve != btcec.S256() {
6677
return nil, fmt.Errorf("private key curve is not secp256k1")
6778
}
68-
sig, err := btcec.SignCompact(btcec.S256(), (*btcec.PrivateKey)(prv), hash, false)
79+
// ecdsa.PrivateKey -> btcec.PrivateKey
80+
var priv btcec.PrivateKey
81+
if overflow := priv.Key.SetByteSlice(prv.D.Bytes()); overflow || priv.Key.IsZero() {
82+
return nil, fmt.Errorf("invalid private key")
83+
}
84+
defer priv.Zero()
85+
sig, err := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey
6986
if err != nil {
7087
return nil, err
7188
}
7289
// Convert to Ethereum signature format with 'recovery id' v at the end.
7390
v := sig[0] - 27
7491
copy(sig, sig[1:])
75-
sig[64] = v
92+
sig[RecoveryIDOffset] = v
7693
return sig, nil
7794
}
7895

@@ -83,13 +100,20 @@ func VerifySignature(pubkey, hash, signature []byte) bool {
83100
if len(signature) != 64 {
84101
return false
85102
}
86-
sig := &btcec.Signature{R: new(big.Int).SetBytes(signature[:32]), S: new(big.Int).SetBytes(signature[32:])}
87-
key, err := btcec.ParsePubKey(pubkey, btcec.S256())
103+
var r, s btcec.ModNScalar
104+
if r.SetByteSlice(signature[:32]) {
105+
return false // overflow
106+
}
107+
if s.SetByteSlice(signature[32:]) {
108+
return false
109+
}
110+
sig := btc_ecdsa.NewSignature(&r, &s)
111+
key, err := btcec.ParsePubKey(pubkey)
88112
if err != nil {
89113
return false
90114
}
91115
// Reject malleable signatures. libsecp256k1 does this check but btcec doesn't.
92-
if sig.S.Cmp(secp256k1_halfN) > 0 {
116+
if s.IsOverHalfOrder() {
93117
return false
94118
}
95119
return sig.Verify(hash, key)
@@ -100,16 +124,26 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
100124
if len(pubkey) != 33 {
101125
return nil, errors.New("invalid compressed public key length")
102126
}
103-
key, err := btcec.ParsePubKey(pubkey, btcec.S256())
127+
key, err := btcec.ParsePubKey(pubkey)
104128
if err != nil {
105129
return nil, err
106130
}
107131
return key.ToECDSA(), nil
108132
}
109133

110-
// CompressPubkey encodes a public key to the 33-byte compressed format.
134+
// CompressPubkey encodes a public key to the 33-byte compressed format. The
135+
// provided PublicKey must be valid. Namely, the coordinates must not be larger
136+
// than 32 bytes each, they must be less than the field prime, and it must be a
137+
// point on the secp256k1 curve. This is the case for a PublicKey constructed by
138+
// elliptic.Unmarshal (see UnmarshalPubkey), or by ToECDSA and ecdsa.GenerateKey
139+
// when constructing a PrivateKey.
111140
func CompressPubkey(pubkey *ecdsa.PublicKey) []byte {
112-
return (*btcec.PublicKey)(pubkey).SerializeCompressed()
141+
// NOTE: the coordinates may be validated with
142+
// btcec.ParsePubKey(FromECDSAPub(pubkey))
143+
var x, y btcec.FieldVal
144+
x.SetByteSlice(pubkey.X.Bytes())
145+
y.SetByteSlice(pubkey.Y.Bytes())
146+
return btcec.NewPublicKey(&x, &y).SerializeCompressed()
113147
}
114148

115149
// S256 returns an instance of the secp256k1 curve.

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ require (
4444
)
4545

4646
require (
47+
github.com/btcsuite/btcd/btcec/v2 v2.3.4
4748
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f
4849
github.com/deckarep/golang-set v1.8.0
4950
github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498
@@ -59,6 +60,7 @@ require (
5960
github.com/StackExchange/wmi v1.2.1 // indirect
6061
github.com/cespare/xxhash/v2 v2.3.0 // indirect
6162
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
63+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
6264
github.com/dlclark/regexp2 v1.10.0 // indirect
6365
github.com/go-ole/go-ole v1.2.5 // indirect
6466
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98 h1:7buXGE+
99
github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98/go.mod h1:DLTg9Gp4FAXF5EpqYBQnUeBbRsNLY7b2HR94TE5XQtE=
1010
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI=
1111
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
12+
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
13+
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
1214
github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU=
1315
github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
1416
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -25,6 +27,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
2527
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2628
github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
2729
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
30+
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
31+
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
32+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
33+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
2834
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
2935
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
3036
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M=

tests/fuzzers/secp256k1/secp_fuzzer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ package secp256k1
2121
import (
2222
"fmt"
2323

24-
"github.com/btcsuite/btcd/btcec"
2524
"github.com/XinFinOrg/XDPoSChain/crypto/secp256k1"
25+
"github.com/btcsuite/btcd/btcec/v2"
2626
fuzz "github.com/google/gofuzz"
2727
)
2828

0 commit comments

Comments
 (0)