From 22cdeb1e827cb9276bce9c00625d94445229bd33 Mon Sep 17 00:00:00 2001 From: Alex Sharov Date: Sat, 2 Nov 2024 09:08:47 +0700 Subject: [PATCH] Pick dcrd pr (#12572) --- cl/sentinel/utils.go | 6 +- erigon-lib/crypto/signature_nocgo.go | 92 +++++++++++++++++++------- erigon-lib/go.mod | 3 +- erigon-lib/go.sum | 4 -- eth/stagedsync/stages/stages.go | 13 ++-- go.mod | 3 +- go.sum | 4 -- rlp/internal/rlpstruct/rlpstruct.go | 13 ++-- tests/fuzzers/secp256k1/secp_fuzzer.go | 56 ---------------- tests/fuzzers/secp256k1/secp_test.go | 54 ++++++++++++++- 10 files changed, 139 insertions(+), 109 deletions(-) delete mode 100644 tests/fuzzers/secp256k1/secp_fuzzer.go diff --git a/cl/sentinel/utils.go b/cl/sentinel/utils.go index 4758815bd0d..7c04e988542 100644 --- a/cl/sentinel/utils.go +++ b/cl/sentinel/utils.go @@ -24,7 +24,7 @@ import ( "strings" "time" - "github.com/btcsuite/btcd/btcec/v2" + "github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-multiaddr" @@ -39,7 +39,7 @@ import ( ) func convertToInterfacePubkey(pubkey *ecdsa.PublicKey) (crypto.PubKey, error) { - xVal, yVal := new(btcec.FieldVal), new(btcec.FieldVal) + xVal, yVal := new(secp256k1.FieldVal), new(secp256k1.FieldVal) overflows := xVal.SetByteSlice(pubkey.X.Bytes()) if overflows { return nil, errors.New("x value overflows") @@ -48,7 +48,7 @@ func convertToInterfacePubkey(pubkey *ecdsa.PublicKey) (crypto.PubKey, error) { if overflows { return nil, errors.New("y value overflows") } - newKey := crypto.PubKey((*crypto.Secp256k1PublicKey)(btcec.NewPublicKey(xVal, yVal))) + newKey := crypto.PubKey((*crypto.Secp256k1PublicKey)(secp256k1.NewPublicKey(xVal, yVal))) // Zero out temporary values. xVal.Zero() yVal.Zero() diff --git a/erigon-lib/crypto/signature_nocgo.go b/erigon-lib/crypto/signature_nocgo.go index aff2a209206..5ccfd353d69 100644 --- a/erigon-lib/crypto/signature_nocgo.go +++ b/erigon-lib/crypto/signature_nocgo.go @@ -18,17 +18,18 @@ // along with Erigon. If not, see . //go:build nacl || js || !cgo || gofuzz +// +build nacl js !cgo gofuzz package crypto import ( "crypto/ecdsa" - "crypto/elliptic" "errors" "fmt" + "math/big" - "github.com/btcsuite/btcd/btcec/v2" - btc_ecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + decred_ecdsa "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa" ) // Ecrecover returns the uncompressed public key that created the given signature. @@ -41,16 +42,16 @@ func Ecrecover(hash, sig []byte) ([]byte, error) { return bytes, err } -func sigToPub(hash, sig []byte) (*btcec.PublicKey, error) { +func sigToPub(hash, sig []byte) (*secp256k1.PublicKey, error) { if len(sig) != SignatureLength { return nil, errors.New("invalid signature") } - // Convert to btcec input format with 'recovery id' v at the beginning. + // Convert to secp256k1 input format with 'recovery id' v at the beginning. btcsig := make([]byte, SignatureLength) btcsig[0] = sig[RecoveryIDOffset] + 27 copy(btcsig[1:], sig) - pub, _, err := btc_ecdsa.RecoverCompact(btcsig, hash) + pub, _, err := decred_ecdsa.RecoverCompact(btcsig, hash) return pub, err } @@ -60,7 +61,13 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { if err != nil { return nil, err } - return pub.ToECDSA(), nil + // We need to explicitly set the curve here, because we're wrapping + // the original curve to add (un-)marshalling + return &ecdsa.PublicKey{ + Curve: S256(), + X: pub.X(), + Y: pub.Y(), + }, nil } // Sign calculates an ECDSA signature. @@ -75,19 +82,16 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) { if len(hash) != 32 { return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash)) } - if prv.Curve != btcec.S256() { + if prv.Curve != S256() { return nil, errors.New("private key curve is not secp256k1") } - // ecdsa.PrivateKey -> btcec.PrivateKey - var priv btcec.PrivateKey + // ecdsa.PrivateKey -> secp256k1.PrivateKey + var priv secp256k1.PrivateKey if overflow := priv.Key.SetByteSlice(prv.D.Bytes()); overflow || priv.Key.IsZero() { return nil, errors.New("invalid private key") } defer priv.Zero() - sig, err := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey - if err != nil { - return nil, err - } + sig := decred_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey // Convert to Ethereum signature format with 'recovery id' v at the end. v := sig[0] - 27 copy(sig, sig[1:]) @@ -102,19 +106,19 @@ func VerifySignature(pubkey, hash, signature []byte) bool { if len(signature) != 64 { return false } - var r, s btcec.ModNScalar + var r, s secp256k1.ModNScalar if r.SetByteSlice(signature[:32]) { return false // overflow } if s.SetByteSlice(signature[32:]) { return false } - sig := btc_ecdsa.NewSignature(&r, &s) - key, err := btcec.ParsePubKey(pubkey) + sig := decred_ecdsa.NewSignature(&r, &s) + key, err := secp256k1.ParsePubKey(pubkey) if err != nil { return false } - // Reject malleable signatures. libsecp256k1 does this check but btcec doesn't. + // Reject malleable signatures. libsecp256k1 does this check but decred doesn't. if s.IsOverHalfOrder() { return false } @@ -126,11 +130,17 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) { if len(pubkey) != 33 { return nil, errors.New("invalid compressed public key length") } - key, err := btcec.ParsePubKey(pubkey) + key, err := secp256k1.ParsePubKey(pubkey) if err != nil { return nil, err } - return key.ToECDSA(), nil + // We need to explicitly set the curve here, because we're wrapping + // the original curve to add (un-)marshalling + return &ecdsa.PublicKey{ + Curve: S256(), + X: key.X(), + Y: key.Y(), + }, nil } // CompressPubkey encodes a public key to the 33-byte compressed format. The @@ -141,14 +151,46 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) { // when constructing a PrivateKey. func CompressPubkey(pubkey *ecdsa.PublicKey) []byte { // NOTE: the coordinates may be validated with - // btcec.ParsePubKey(FromECDSAPub(pubkey)) - var x, y btcec.FieldVal + // secp256k1.ParsePubKey(FromECDSAPub(pubkey)) + var x, y secp256k1.FieldVal x.SetByteSlice(pubkey.X.Bytes()) y.SetByteSlice(pubkey.Y.Bytes()) - return btcec.NewPublicKey(&x, &y).SerializeCompressed() + return secp256k1.NewPublicKey(&x, &y).SerializeCompressed() } // S256 returns an instance of the secp256k1 curve. -func S256() elliptic.Curve { - return btcec.S256() +func S256() EllipticCurve { + return btCurve{secp256k1.S256()} +} + +type btCurve struct { + *secp256k1.KoblitzCurve +} + +// Marshal converts a point given as (x, y) into a byte slice. +func (curve btCurve) Marshal(x, y *big.Int) []byte { + byteLen := (curve.Params().BitSize + 7) / 8 + + ret := make([]byte, 1+2*byteLen) + ret[0] = 4 // uncompressed point + + x.FillBytes(ret[1 : 1+byteLen]) + y.FillBytes(ret[1+byteLen : 1+2*byteLen]) + + return ret +} + +// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On +// error, x = nil. +func (curve btCurve) Unmarshal(data []byte) (x, y *big.Int) { + byteLen := (curve.Params().BitSize + 7) / 8 + if len(data) != 1+2*byteLen { + return nil, nil + } + if data[0] != 4 { // uncompressed form + return nil, nil + } + x = new(big.Int).SetBytes(data[1 : 1+byteLen]) + y = new(big.Int).SetBytes(data[1+byteLen:]) + return } diff --git a/erigon-lib/go.mod b/erigon-lib/go.mod index b2fdd309050..6bf019b33ea 100644 --- a/erigon-lib/go.mod +++ b/erigon-lib/go.mod @@ -16,11 +16,11 @@ require ( github.com/anacrolix/go-libutp v1.3.1 github.com/anacrolix/log v0.15.2 github.com/anacrolix/torrent v1.52.6-0.20231201115409-7ea994b6bbd8 - github.com/btcsuite/btcd/btcec/v2 v2.3.4 github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 github.com/containerd/cgroups/v3 v3.0.3 github.com/crate-crypto/go-kzg-4844 v0.7.0 github.com/deckarep/golang-set/v2 v2.3.1 + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 github.com/edsrzf/mmap-go v1.1.0 github.com/elastic/go-freelru v0.13.0 github.com/erigontech/speedtest v0.0.2 @@ -55,7 +55,6 @@ require ( require ( github.com/cespare/xxhash v1.1.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/ianlancetaylor/cgosymbolizer v0.0.0-20240503222823-736c933a666d // indirect github.com/klauspost/compress v1.17.9 // indirect diff --git a/erigon-lib/go.sum b/erigon-lib/go.sum index c1ed7c228da..f2165a0823a 100644 --- a/erigon-lib/go.sum +++ b/erigon-lib/go.sum @@ -100,10 +100,6 @@ github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2w github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8= github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og= -github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= -github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 h1:6lhrsTEnloDPXyeZBvSYvQf8u86jbKehZPVDDlkgDl4= github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/eth/stagedsync/stages/stages.go b/eth/stagedsync/stages/stages.go index a6d70c5b8b2..851aada1f1c 100644 --- a/eth/stagedsync/stages/stages.go +++ b/eth/stagedsync/stages/stages.go @@ -1,18 +1,21 @@ -// Copyright 2020 The Erigon Authors -// This file is part of the Erigon library. +// Copyright 2017 The go-ethereum Authors +// (original work) +// Copyright 2024 The Erigon Authors +// (modifications) +// This file is part of Erigon. // -// The go-ethereum library is free software: you can redistribute it and/or modify +// Erigon is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // -// The go-ethereum library is distributed in the hope that it will be useful, +// Erigon is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . +// along with Erigon. If not, see . package stages diff --git a/go.mod b/go.mod index fab36580321..ff79c4af55e 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,6 @@ require ( github.com/anacrolix/sync v0.5.1 github.com/anacrolix/torrent v1.52.6-0.20231201115409-7ea994b6bbd8 github.com/benesch/cgosymbolizer v0.0.0-20190515212042-bec6fe6e597b - github.com/btcsuite/btcd/btcec/v2 v2.3.4 github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 github.com/cenkalti/backoff/v4 v4.2.1 github.com/consensys/gnark-crypto v0.12.1 @@ -158,7 +157,7 @@ require ( github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect diff --git a/go.sum b/go.sum index f0c36a7cddf..6661c7a2b0c 100644 --- a/go.sum +++ b/go.sum @@ -175,10 +175,6 @@ github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2w github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8= github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og= -github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= -github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 h1:6lhrsTEnloDPXyeZBvSYvQf8u86jbKehZPVDDlkgDl4= github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= diff --git a/rlp/internal/rlpstruct/rlpstruct.go b/rlp/internal/rlpstruct/rlpstruct.go index b5e9e0aa7b9..e9383a573d7 100644 --- a/rlp/internal/rlpstruct/rlpstruct.go +++ b/rlp/internal/rlpstruct/rlpstruct.go @@ -1,18 +1,21 @@ -// Copyright 2022 The go-ethereum Authors -// This file is part of the go-ethereum library. +// Copyright 2017 The go-ethereum Authors +// (original work) +// Copyright 2024 The Erigon Authors +// (modifications) +// This file is part of Erigon. // -// The go-ethereum library is free software: you can redistribute it and/or modify +// Erigon is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // -// The go-ethereum library is distributed in the hope that it will be useful, +// Erigon is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . +// along with Erigon. If not, see . // Package rlpstruct implements struct processing for RLP encoding/decoding. // diff --git a/tests/fuzzers/secp256k1/secp_fuzzer.go b/tests/fuzzers/secp256k1/secp_fuzzer.go deleted file mode 100644 index ea4058a4bc2..00000000000 --- a/tests/fuzzers/secp256k1/secp_fuzzer.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2021 The go-ethereum Authors -// (original work) -// Copyright 2024 The Erigon Authors -// (modifications) -// This file is part of Erigon. -// -// Erigon is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Erigon is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Erigon. If not, see . - -// build +gofuzz - -// build +gofuzz - -package secp256k1 - -import ( - "fmt" - - "github.com/btcsuite/btcd/btcec/v2" - fuzz "github.com/google/gofuzz" - - "github.com/erigontech/secp256k1" -) - -func Fuzz(input []byte) int { - var ( - fuzzer = fuzz.NewFromGoFuzz(input) - curveA = secp256k1.S256() - curveB = btcec.S256() - dataP1 []byte - dataP2 []byte - ) - // first point - fuzzer.Fuzz(&dataP1) - x1, y1 := curveB.ScalarBaseMult(dataP1) - // second point - fuzzer.Fuzz(&dataP2) - x2, y2 := curveB.ScalarBaseMult(dataP2) - resAX, resAY := curveA.Add(x1, y1, x2, y2) - resBX, resBY := curveB.Add(x1, y1, x2, y2) - if resAX.Cmp(resBX) != 0 || resAY.Cmp(resBY) != 0 { - fmt.Printf("%s %s %s %s\n", x1, y1, x2, y2) - panic(fmt.Sprintf("Addition failed: geth: %s %s btcd: %s %s", resAX, resAY, resBX, resBY)) - } - return 0 -} diff --git a/tests/fuzzers/secp256k1/secp_test.go b/tests/fuzzers/secp256k1/secp_test.go index 76bae87086f..53b1b8fbfe8 100644 --- a/tests/fuzzers/secp256k1/secp_test.go +++ b/tests/fuzzers/secp256k1/secp_test.go @@ -1,8 +1,56 @@ +// Copyright 2017 The go-ethereum Authors +// (original work) +// Copyright 2024 The Erigon Authors +// (modifications) +// This file is part of Erigon. +// +// Erigon is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Erigon is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Erigon. If not, see . + package secp256k1 -import "testing" +import ( + "fmt" + "testing" + + dcred_secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/erigontech/secp256k1" +) func TestFuzzer(t *testing.T) { - test := "00000000N0000000/R00000000000000000U0000S0000000mkhP000000000000000U" - Fuzz([]byte(test)) + a, b := "00000000N0000000/R0000000000000000", "0U0000S0000000mkhP000000000000000U" + fuzz([]byte(a), []byte(b)) +} + +func Fuzz(f *testing.F) { + f.Fuzz(func(t *testing.T, a, b []byte) { + fuzz(a, b) + }) +} + +func fuzz(dataP1, dataP2 []byte) { + var ( + curveA = secp256k1.S256() + curveB = dcred_secp256k1.S256() + ) + // first point + x1, y1 := curveB.ScalarBaseMult(dataP1) + // second points + x2, y2 := curveB.ScalarBaseMult(dataP2) + resAX, resAY := curveA.Add(x1, y1, x2, y2) + resBX, resBY := curveB.Add(x1, y1, x2, y2) + if resAX.Cmp(resBX) != 0 || resAY.Cmp(resBY) != 0 { + fmt.Printf("%s %s %s %s\n", x1, y1, x2, y2) + panic(fmt.Sprintf("Addition failed: geth: %s %s btcd: %s %s", resAX, resAY, resBX, resBY)) + } }