diff --git a/restricted/crypto/signature_nocgo.go b/restricted/crypto/signature.go
similarity index 64%
rename from restricted/crypto/signature_nocgo.go
rename to restricted/crypto/signature.go
index 2239af6..3c9903c 100644
--- a/restricted/crypto/signature_nocgo.go
+++ b/restricted/crypto/signature.go
@@ -14,8 +14,6 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// +build nacl js !cgo gofuzz
-
package crypto
import (
@@ -23,37 +21,48 @@ import (
"crypto/elliptic"
"errors"
"fmt"
- "math/big"
"github.com/btcsuite/btcd/btcec/v2"
+ btc_ecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa"
)
// Ecrecover returns the uncompressed public key that created the given signature.
func Ecrecover(hash, sig []byte) ([]byte, error) {
- pub, err := SigToPub(hash, sig)
+ pub, err := sigToPub(hash, sig)
if err != nil {
return nil, err
}
- bytes := (*btcec.PublicKey)(pub).SerializeUncompressed()
+ bytes := pub.SerializeUncompressed()
return bytes, err
}
+func sigToPub(hash, sig []byte) (*btcec.PublicKey, error) {
+ if len(sig) != SignatureLength {
+ return nil, errors.New("invalid signature")
+ }
+ // Convert to btcec 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)
+ return pub, err
+}
+
// SigToPub returns the public key that created the given signature.
func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
- // Convert to btcec input format with 'recovery id' v at the beginning.
- btcsig := make([]byte, SignatureLength)
- btcsig[0] = sig[64] + 27
- copy(btcsig[1:], sig)
-
- pub, _, err := btcec.RecoverCompact(btcec.S256(), btcsig, hash)
- return (*ecdsa.PublicKey)(pub), err
+ pub, err := sigToPub(hash, sig)
+ if err != nil {
+ return nil, err
+ }
+ return pub.ToECDSA(), nil
}
// Sign calculates an ECDSA signature.
//
// This function is susceptible to chosen plaintext attacks that can leak
// information about the private key that is used for signing. Callers must
-// be aware that the given hash cannot be chosen by an adversery. Common
+// be aware that the given hash cannot be chosen by an adversary. Common
// solution is to hash any input before calculating the signature.
//
// The produced signature is in the [R || S || V] format where V is 0 or 1.
@@ -64,14 +73,20 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) {
if prv.Curve != btcec.S256() {
return nil, fmt.Errorf("private key curve is not secp256k1")
}
- sig, err := btcec.SignCompact(btcec.S256(), (*btcec.PrivateKey)(prv), hash, false)
+ // ecdsa.PrivateKey -> btcec.PrivateKey
+ var priv btcec.PrivateKey
+ if overflow := priv.Key.SetByteSlice(prv.D.Bytes()); overflow || priv.Key.IsZero() {
+ return nil, fmt.Errorf("invalid private key")
+ }
+ defer priv.Zero()
+ sig, err := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey
if err != nil {
return nil, err
}
// Convert to Ethereum signature format with 'recovery id' v at the end.
v := sig[0] - 27
copy(sig, sig[1:])
- sig[64] = v
+ sig[RecoveryIDOffset] = v
return sig, nil
}
@@ -82,13 +97,20 @@ func VerifySignature(pubkey, hash, signature []byte) bool {
if len(signature) != 64 {
return false
}
- sig := &btcec.Signature{R: new(big.Int).SetBytes(signature[:32]), S: new(big.Int).SetBytes(signature[32:])}
- key, err := btcec.ParsePubKey(pubkey, btcec.S256())
+ var r, s btcec.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)
if err != nil {
return false
}
// Reject malleable signatures. libsecp256k1 does this check but btcec doesn't.
- if sig.S.Cmp(secp256k1halfN) > 0 {
+ if s.IsOverHalfOrder() {
return false
}
return sig.Verify(hash, key)
@@ -99,16 +121,26 @@ 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, btcec.S256())
+ key, err := btcec.ParsePubKey(pubkey)
if err != nil {
return nil, err
}
return key.ToECDSA(), nil
}
-// CompressPubkey encodes a public key to the 33-byte compressed format.
+// CompressPubkey encodes a public key to the 33-byte compressed format. The
+// provided PublicKey must be valid. Namely, the coordinates must not be larger
+// than 32 bytes each, they must be less than the field prime, and it must be a
+// point on the secp256k1 curve. This is the case for a PublicKey constructed by
+// elliptic.Unmarshal (see UnmarshalPubkey), or by ToECDSA and ecdsa.GenerateKey
+// when constructing a PrivateKey.
func CompressPubkey(pubkey *ecdsa.PublicKey) []byte {
- return (*btcec.PublicKey)(pubkey).SerializeCompressed()
+ // NOTE: the coordinates may be validated with
+ // btcec.ParsePubKey(FromECDSAPub(pubkey))
+ var x, y btcec.FieldVal
+ x.SetByteSlice(pubkey.X.Bytes())
+ y.SetByteSlice(pubkey.Y.Bytes())
+ return btcec.NewPublicKey(&x, &y).SerializeCompressed()
}
// S256 returns an instance of the secp256k1 curve.
diff --git a/restricted/crypto/signature_cgo.go b/restricted/crypto/signature_cgo.go
deleted file mode 100644
index 8a2b007..0000000
--- a/restricted/crypto/signature_cgo.go
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library 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,
-// 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 .
-
-// +build !nacl,!js,cgo,!gofuzz
-
-package crypto
-
-import (
- "crypto/ecdsa"
- "crypto/elliptic"
- "fmt"
-
- "github.com/openrelayxyz/plugeth-utils/restricted/crypto/secp256k1"
-)
-
-// Ecrecover returns the uncompressed public key that created the given signature.
-func Ecrecover(hash, sig []byte) ([]byte, error) {
- return secp256k1.RecoverPubkey(hash, sig)
-}
-
-// SigToPub returns the public key that created the given signature.
-func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
- s, err := Ecrecover(hash, sig)
- if err != nil {
- return nil, err
- }
-
- x, y := elliptic.Unmarshal(S256(), s)
- return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
-}
-
-// Sign calculates an ECDSA signature.
-//
-// This function is susceptible to chosen plaintext attacks that can leak
-// information about the private key that is used for signing. Callers must
-// be aware that the given digest cannot be chosen by an adversery. Common
-// solution is to hash any input before calculating the signature.
-//
-// The produced signature is in the [R || S || V] format where V is 0 or 1.
-func Sign(digestHash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
- if len(digestHash) != DigestLength {
- return nil, fmt.Errorf("hash is required to be exactly %d bytes (%d)", DigestLength, len(digestHash))
- }
- seckey := PaddedBigBytes(prv.D, prv.Params().BitSize/8)
- defer zeroBytes(seckey)
- return secp256k1.Sign(digestHash, seckey)
-}
-
-// VerifySignature checks that the given public key created signature over digest.
-// The public key should be in compressed (33 bytes) or uncompressed (65 bytes) format.
-// The signature should have the 64 byte [R || S] format.
-func VerifySignature(pubkey, digestHash, signature []byte) bool {
- return secp256k1.VerifySignature(pubkey, digestHash, signature)
-}
-
-// DecompressPubkey parses a public key in the 33-byte compressed format.
-func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
- x, y := secp256k1.DecompressPubkey(pubkey)
- if x == nil {
- return nil, fmt.Errorf("invalid public key")
- }
- return &ecdsa.PublicKey{X: x, Y: y, Curve: S256()}, nil
-}
-
-// CompressPubkey encodes a public key to the 33-byte compressed format.
-func CompressPubkey(pubkey *ecdsa.PublicKey) []byte {
- return secp256k1.CompressPubkey(pubkey.X, pubkey.Y)
-}
-
-// S256 returns an instance of the secp256k1 curve.
-func S256() elliptic.Curve {
- return secp256k1.S256()
-}