mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
Delete custom secp256 implementation (#215)
## Why this should be merged This PR removes the custom secp256 implementation in a single, seperate commit, as requested in https://github.com/ava-labs/libevm/pull/214
This commit is contained in:
parent
62dc2ea087
commit
ba21474eeb
5 changed files with 0 additions and 5305 deletions
|
|
@ -1,122 +0,0 @@
|
|||
// Copyright 2025 the libevm authors.
|
||||
//
|
||||
// The libevm additions to go-ethereum are free software: you can redistribute
|
||||
// them and/or modify them 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 libevm additions are distributed in the hope that they 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
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package p256verify implements an EVM precompile to verify P256 ECDSA
|
||||
// signatures, as described in RIP-7212.
|
||||
package p256verify
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"math/big"
|
||||
|
||||
"github.com/ava-labs/libevm/params"
|
||||
)
|
||||
|
||||
// Precompile implements ECDSA verification on the P256 curve, as defined by
|
||||
// [RIP-7212].
|
||||
//
|
||||
// [RIP-7212]: https://github.com/ethereum/RIPs/blob/1f55794f65caa4c4bb2b8d9bda7d713b8c734157/RIPS/rip-7212.md
|
||||
type Precompile struct{}
|
||||
|
||||
// RequiredGas returns [params.P256VerifyGas].
|
||||
func (Precompile) RequiredGas([]byte) uint64 {
|
||||
return params.P256VerifyGas
|
||||
}
|
||||
|
||||
const (
|
||||
wordLen = 32
|
||||
inputLen = 5 * wordLen
|
||||
)
|
||||
|
||||
type input [inputLen]byte
|
||||
|
||||
type index int
|
||||
|
||||
const (
|
||||
hashPos index = iota * wordLen
|
||||
rPos
|
||||
sPos
|
||||
xPos
|
||||
yPos
|
||||
)
|
||||
|
||||
// Run parses and verifies the signature. On success it returns a 32-byte
|
||||
// big-endian representation of the number 1, otherwise it returns an empty
|
||||
// slice. The returned error is always nil.
|
||||
func (Precompile) Run(sig []byte) ([]byte, error) {
|
||||
if len(sig) != inputLen || !(*input)(sig).verify() {
|
||||
return nil, nil
|
||||
}
|
||||
return bigEndianOne(), nil
|
||||
}
|
||||
|
||||
func bigEndianOne() []byte {
|
||||
return []byte{wordLen - 1: 1}
|
||||
}
|
||||
|
||||
func (in *input) verify() bool {
|
||||
key, ok := in.pubkey()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return ecdsa.Verify(key, in.word(hashPos), in.bigWord(rPos), in.bigWord(sPos))
|
||||
}
|
||||
|
||||
func (in *input) pubkey() (*ecdsa.PublicKey, bool) {
|
||||
x := in.bigWord(xPos)
|
||||
y := in.bigWord(yPos)
|
||||
|
||||
// There is no need to explicitly check for the point at infinity because
|
||||
// [elliptic.Curve] documentation states that it's not on the curve and the
|
||||
// check would therefore be performed twice.
|
||||
// See https://cs.opensource.google/go/go/+/refs/tags/go1.24.3:src/crypto/elliptic/nistec.go;l=132
|
||||
curve := elliptic.P256()
|
||||
if !curve.IsOnCurve(x, y) {
|
||||
return nil, false
|
||||
}
|
||||
return &ecdsa.PublicKey{
|
||||
Curve: curve,
|
||||
X: x,
|
||||
Y: y,
|
||||
}, true
|
||||
}
|
||||
|
||||
func (in *input) word(i index) []byte {
|
||||
return in[i : i+wordLen]
|
||||
}
|
||||
|
||||
func (in *input) bigWord(i index) *big.Int {
|
||||
return new(big.Int).SetBytes(in.word(i))
|
||||
}
|
||||
|
||||
// Pack packs the arguments into a byte slice compatible with [Precompile.Run].
|
||||
// It does NOT perform any validation on its inputs and therefore may panic if,
|
||||
// for example, a [big.Int] with >256 bits is received. Keys and signatures
|
||||
// generated with [elliptic.GenerateKey] and [ecdsa.Sign] are valid inputs.
|
||||
func Pack(hash [32]byte, r, s *big.Int, key *ecdsa.PublicKey) []byte {
|
||||
var in input
|
||||
|
||||
copy(in.word(hashPos), hash[:])
|
||||
|
||||
r.FillBytes(in.word(rPos))
|
||||
s.FillBytes(in.word(sPos))
|
||||
|
||||
key.X.FillBytes(in.word(xPos))
|
||||
key.Y.FillBytes(in.word(yPos))
|
||||
|
||||
return in[:]
|
||||
}
|
||||
|
|
@ -1,264 +0,0 @@
|
|||
// Copyright 2025 the libevm authors.
|
||||
//
|
||||
// The libevm additions to go-ethereum are free software: you can redistribute
|
||||
// them and/or modify them 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 libevm additions are distributed in the hope that they 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
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
package p256verify
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/asn1"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ava-labs/libevm/common"
|
||||
"github.com/ava-labs/libevm/core/vm"
|
||||
"github.com/ava-labs/libevm/libevm"
|
||||
"github.com/ava-labs/libevm/libevm/ethtest"
|
||||
"github.com/ava-labs/libevm/libevm/hookstest"
|
||||
"github.com/ava-labs/libevm/params"
|
||||
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
var _ vm.PrecompiledContract = Precompile{}
|
||||
|
||||
// ulerdoganTestCase is the test case from
|
||||
// https://github.com/ulerdogan/go-ethereum/blob/cec0b058115282168c5afc5197de3f6b5479dc4a/core/vm/testdata/precompiles/p256Verify.json,
|
||||
// copied under LGPL. See the respective commit for copyright and license
|
||||
// information.
|
||||
const ulerdoganTestCase = `4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e`
|
||||
|
||||
//go:embed testdata/ecdsa_secp256r1_sha256_test.json
|
||||
var wycheproofECDSASHA256 []byte
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
in []byte
|
||||
wantSuccess bool
|
||||
}
|
||||
|
||||
func signAndPack(tb testing.TB, priv *ecdsa.PrivateKey, hash [32]byte) []byte {
|
||||
tb.Helper()
|
||||
r, s, err := ecdsa.Sign(rand.Reader, priv, hash[:])
|
||||
require.NoError(tb, err, "ecdsa.Sign()")
|
||||
return Pack(hash, r, s, &priv.PublicKey)
|
||||
}
|
||||
|
||||
func TestPrecompile(t *testing.T) {
|
||||
assert.Equal(t, params.P256VerifyGas, Precompile{}.RequiredGas(nil), "RequiredGas()")
|
||||
|
||||
tests := []testCase{
|
||||
{
|
||||
name: "empty_input",
|
||||
},
|
||||
{
|
||||
name: "input_too_short",
|
||||
in: make([]byte, inputLen-1),
|
||||
},
|
||||
{
|
||||
name: "input_too_long",
|
||||
in: make([]byte, inputLen+1),
|
||||
},
|
||||
{
|
||||
name: "pub_key_at_infinity",
|
||||
in: make([]byte, inputLen),
|
||||
},
|
||||
{
|
||||
name: "pub_key_not_on_curve",
|
||||
in: []byte{inputLen - 1: 1},
|
||||
},
|
||||
{
|
||||
name: "ulerdogan",
|
||||
in: common.Hex2Bytes(ulerdoganTestCase),
|
||||
wantSuccess: true,
|
||||
},
|
||||
}
|
||||
|
||||
for range 50 {
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
require.NoError(t, err, "ecdsa.GenerateKey(elliptic.P256(), crypto/rand.Reader)")
|
||||
|
||||
for range 50 {
|
||||
var toSign [32]byte
|
||||
_, err := rand.Read(toSign[:])
|
||||
require.NoErrorf(t, err, "crypto/rand.Read(%T)", toSign)
|
||||
|
||||
in := signAndPack(t, priv, toSign)
|
||||
tests = append(tests, testCase{
|
||||
name: "fuzz_valid",
|
||||
in: in,
|
||||
wantSuccess: true,
|
||||
})
|
||||
corrupt := slices.Clone(in)
|
||||
corrupt[0]++ // different signed hash
|
||||
tests = append(tests, testCase{
|
||||
name: "fuzz_invalid",
|
||||
in: corrupt,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
tests = append(tests, wycheproofTestCases(t)...)
|
||||
if t.Failed() {
|
||||
return
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Precompile{}.Run(tt.in)
|
||||
require.NoError(t, err, "Run() always returns nil, even on verification failure")
|
||||
|
||||
var want []byte
|
||||
if tt.wantSuccess {
|
||||
want = common.LeftPadBytes([]byte{1}, 32)
|
||||
}
|
||||
assert.Equal(t, want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type jsonHex []byte
|
||||
|
||||
var _ json.Unmarshaler = (*jsonHex)(nil)
|
||||
|
||||
func (j *jsonHex) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*j = b
|
||||
return nil
|
||||
}
|
||||
|
||||
func wycheproofTestCases(t *testing.T) []testCase {
|
||||
t.Helper()
|
||||
|
||||
var raw struct {
|
||||
Groups []struct {
|
||||
Key struct {
|
||||
X jsonHex `json:"wx"`
|
||||
Y jsonHex `json:"wy"`
|
||||
}
|
||||
Tests []struct {
|
||||
ID int `json:"tcId"`
|
||||
Comment string
|
||||
Preimage jsonHex `json:"msg"`
|
||||
ASNSig jsonHex `json:"sig"`
|
||||
Result string
|
||||
} `json:"tests"`
|
||||
} `json:"testGroups"`
|
||||
}
|
||||
require.NoError(t, json.Unmarshal(wycheproofECDSASHA256, &raw))
|
||||
|
||||
var cases []testCase
|
||||
for _, group := range raw.Groups {
|
||||
key := &ecdsa.PublicKey{
|
||||
Curve: elliptic.P256(),
|
||||
X: new(big.Int).SetBytes(group.Key.X),
|
||||
Y: new(big.Int).SetBytes(group.Key.Y),
|
||||
}
|
||||
|
||||
for _, test := range group.Tests {
|
||||
t.Run(fmt.Sprintf("parse_test_%d", test.ID), func(t *testing.T) {
|
||||
// Many of the invalid cases are due to ASN1-specific problems,
|
||||
// which aren't of concern to us.
|
||||
include := test.Result == "valid" ||
|
||||
strings.Contains(test.Comment, "r or s") ||
|
||||
strings.Contains(test.Comment, "r and s") ||
|
||||
slices.Contains(
|
||||
[]int{
|
||||
// Special cases of r and/or s.
|
||||
286, 294, 295, 303, 304, 340, 341,
|
||||
342, 343, 356, 357, 358, 359,
|
||||
},
|
||||
test.ID,
|
||||
)
|
||||
|
||||
include = include && !slices.Contains(
|
||||
// These cases have negative r or s value(s) with the same
|
||||
// absolute value(s) as valid signatures. Packing and then
|
||||
// unpacking via [big.Int.Bytes] therefore converts them to
|
||||
// the valid, positive values that pass verification and
|
||||
// raise false-positive test errors.
|
||||
[]int{133, 139, 140},
|
||||
test.ID,
|
||||
)
|
||||
if !include {
|
||||
return
|
||||
}
|
||||
|
||||
var rs [2]*big.Int
|
||||
rest, err := asn1.Unmarshal(test.ASNSig, &rs)
|
||||
if err != nil || len(rest) > 0 {
|
||||
return
|
||||
}
|
||||
if rs[0].BitLen() > 256 || rs[1].BitLen() > 256 {
|
||||
return
|
||||
}
|
||||
cases = append(cases, testCase{
|
||||
name: fmt.Sprintf("wycheproof_ecdsa_secp256r1_sha256_%d", test.ID),
|
||||
in: Pack(sha256.Sum256(test.Preimage), rs[0], rs[1], key),
|
||||
wantSuccess: test.Result == "valid",
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
t.Logf("%d Wycheproof cases", len(cases))
|
||||
return cases
|
||||
}
|
||||
|
||||
func BenchmarkPrecompile(b *testing.B) {
|
||||
in := common.Hex2Bytes(ulerdoganTestCase)
|
||||
var p Precompile
|
||||
|
||||
for range b.N {
|
||||
// Explicitly drop return values to placate the linter. The error is
|
||||
// always nil and the input is tested above.
|
||||
_, _ = p.Run(in)
|
||||
}
|
||||
}
|
||||
|
||||
func TestViaEVM(t *testing.T) {
|
||||
addr := common.Address{42}
|
||||
hooks := hookstest.Stub{
|
||||
PrecompileOverrides: map[common.Address]libevm.PrecompiledContract{
|
||||
addr: Precompile{},
|
||||
},
|
||||
}
|
||||
hooks.Register(t)
|
||||
|
||||
_, evm := ethtest.NewZeroEVM(t)
|
||||
in := common.Hex2Bytes(ulerdoganTestCase)
|
||||
|
||||
got, _, err := evm.Call(vm.AccountRef{}, addr, in, 25000, uint256.NewInt(0))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte{31: 1}, got)
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
Test vectors from Project Wycheproof; see [original source](https://github.com/C2SP/wycheproof/tree/4a6c2bf5dc4c0b67c770233ad33961ee653996a0) for license and copyright. No changes made.
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright 2025 the libevm authors.
|
||||
//
|
||||
// The libevm additions to go-ethereum are free software: you can redistribute
|
||||
// them and/or modify them 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 libevm additions are distributed in the hope that they 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
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
package params
|
||||
|
||||
// P256VerifyGas is the gas required by the RIP-7212 precompile for P256 ECDSA
|
||||
// verification.
|
||||
const P256VerifyGas uint64 = 3450
|
||||
Loading…
Reference in a new issue