aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Lange <fjl@users.noreply.github.com>2018-06-12 21:26:08 +0800
committerGuillaume Ballet <gballet@gmail.com>2018-06-12 21:26:08 +0800
commit0255951587ef0eada5d162f3404bc481f70a2ce2 (patch)
tree6aa0c1c9405df6a88f4cbeb72e170e6e19cf55d5
parent85cd64df0e3331e46f41ec86a647f1b8ff306eda (diff)
downloaddexon-0255951587ef0eada5d162f3404bc481f70a2ce2.tar.gz
dexon-0255951587ef0eada5d162f3404bc481f70a2ce2.tar.zst
dexon-0255951587ef0eada5d162f3404bc481f70a2ce2.zip
crypto: replace ToECDSAPub with error-checking func UnmarshalPubkey (#16932)
ToECDSAPub was unsafe because it returned a non-nil key with nil X, Y in case of invalid input. This change replaces ToECDSAPub with UnmarshalPubkey across the codebase.
-rw-r--r--cmd/wnode/main.go13
-rw-r--r--crypto/crypto.go13
-rw-r--r--crypto/crypto_test.go31
-rw-r--r--internal/ethapi/api.go6
-rw-r--r--p2p/rlpx.go6
-rw-r--r--signer/core/api.go6
-rw-r--r--swarm/services/swap/swap.go8
-rw-r--r--whisper/whisperv5/api.go9
-rw-r--r--whisper/whisperv6/api.go9
9 files changed, 62 insertions, 39 deletions
diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go
index 5031a088c..31b65c1da 100644
--- a/cmd/wnode/main.go
+++ b/cmd/wnode/main.go
@@ -140,8 +140,8 @@ func processArgs() {
}
if *asymmetricMode && len(*argPub) > 0 {
- pub = crypto.ToECDSAPub(common.FromHex(*argPub))
- if !isKeyValid(pub) {
+ var err error
+ if pub, err = crypto.UnmarshalPubkey(common.FromHex(*argPub)); err != nil {
utils.Fatalf("invalid public key")
}
}
@@ -321,10 +321,6 @@ func startServer() error {
return nil
}
-func isKeyValid(k *ecdsa.PublicKey) bool {
- return k.X != nil && k.Y != nil
-}
-
func configureNode() {
var err error
var p2pAccept bool
@@ -340,9 +336,8 @@ func configureNode() {
if b == nil {
utils.Fatalf("Error: can not convert hexadecimal string")
}
- pub = crypto.ToECDSAPub(b)
- if !isKeyValid(pub) {
- utils.Fatalf("Error: invalid public key")
+ if pub, err = crypto.UnmarshalPubkey(b); err != nil {
+ utils.Fatalf("Error: invalid peer public key")
}
}
}
diff --git a/crypto/crypto.go b/crypto/crypto.go
index 76d1ffaf6..619440e81 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -39,6 +39,8 @@ var (
secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2))
)
+var errInvalidPubkey = errors.New("invalid secp256k1 public key")
+
// Keccak256 calculates and returns the Keccak256 hash of the input data.
func Keccak256(data ...[]byte) []byte {
d := sha3.NewKeccak256()
@@ -122,12 +124,13 @@ func FromECDSA(priv *ecdsa.PrivateKey) []byte {
return math.PaddedBigBytes(priv.D, priv.Params().BitSize/8)
}
-func ToECDSAPub(pub []byte) *ecdsa.PublicKey {
- if len(pub) == 0 {
- return nil
- }
+// UnmarshalPubkey converts bytes to a secp256k1 public key.
+func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
x, y := elliptic.Unmarshal(S256(), pub)
- return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}
+ if x == nil {
+ return nil, errInvalidPubkey
+ }
+ return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
}
func FromECDSAPub(pub *ecdsa.PublicKey) []byte {
diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go
index 804de3fe2..177c19c0c 100644
--- a/crypto/crypto_test.go
+++ b/crypto/crypto_test.go
@@ -23,9 +23,11 @@ import (
"io/ioutil"
"math/big"
"os"
+ "reflect"
"testing"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
)
var testAddrHex = "970e8128ab834e8eac17ab8e3812f010678cf791"
@@ -56,6 +58,33 @@ func BenchmarkSha3(b *testing.B) {
}
}
+func TestUnmarshalPubkey(t *testing.T) {
+ key, err := UnmarshalPubkey(nil)
+ if err != errInvalidPubkey || key != nil {
+ t.Fatalf("expected error, got %v, %v", err, key)
+ }
+ key, err = UnmarshalPubkey([]byte{1, 2, 3})
+ if err != errInvalidPubkey || key != nil {
+ t.Fatalf("expected error, got %v, %v", err, key)
+ }
+
+ var (
+ enc, _ = hex.DecodeString("04760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1b01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d")
+ dec = &ecdsa.PublicKey{
+ Curve: S256(),
+ X: hexutil.MustDecodeBig("0x760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1"),
+ Y: hexutil.MustDecodeBig("0xb01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d"),
+ }
+ )
+ key, err = UnmarshalPubkey(enc)
+ if err != nil {
+ t.Fatalf("expected no error, got %v", err)
+ }
+ if !reflect.DeepEqual(key, dec) {
+ t.Fatal("wrong result")
+ }
+}
+
func TestSign(t *testing.T) {
key, _ := HexToECDSA(testPrivHex)
addr := common.HexToAddress(testAddrHex)
@@ -69,7 +98,7 @@ func TestSign(t *testing.T) {
if err != nil {
t.Errorf("ECRecover error: %s", err)
}
- pubKey := ToECDSAPub(recoveredPub)
+ pubKey, _ := UnmarshalPubkey(recoveredPub)
recoveredAddr := PubkeyToAddress(*pubKey)
if addr != recoveredAddr {
t.Errorf("Address mismatch: want: %x have: %x", addr, recoveredAddr)
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index f5753da22..a465a5904 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -461,13 +461,11 @@ func (s *PrivateAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Byt
}
sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1
- rpk, err := crypto.Ecrecover(signHash(data), sig)
+ rpk, err := crypto.SigToPub(signHash(data), sig)
if err != nil {
return common.Address{}, err
}
- pubKey := crypto.ToECDSAPub(rpk)
- recoveredAddr := crypto.PubkeyToAddress(*pubKey)
- return recoveredAddr, nil
+ return crypto.PubkeyToAddress(*rpk), nil
}
// SignAndSendTransaction was renamed to SendTransaction. This method is deprecated
diff --git a/p2p/rlpx.go b/p2p/rlpx.go
index a320e81e7..149eda689 100644
--- a/p2p/rlpx.go
+++ b/p2p/rlpx.go
@@ -528,9 +528,9 @@ func importPublicKey(pubKey []byte) (*ecies.PublicKey, error) {
return nil, fmt.Errorf("invalid public key length %v (expect 64/65)", len(pubKey))
}
// TODO: fewer pointless conversions
- pub := crypto.ToECDSAPub(pubKey65)
- if pub.X == nil {
- return nil, fmt.Errorf("invalid public key")
+ pub, err := crypto.UnmarshalPubkey(pubKey65)
+ if err != nil {
+ return nil, err
}
return ecies.ImportECDSAPublic(pub), nil
}
diff --git a/signer/core/api.go b/signer/core/api.go
index 45933284b..1372646de 100644
--- a/signer/core/api.go
+++ b/signer/core/api.go
@@ -432,13 +432,11 @@ func (api *SignerAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (c
}
sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1
hash, _ := SignHash(data)
- rpk, err := crypto.Ecrecover(hash, sig)
+ rpk, err := crypto.SigToPub(hash, sig)
if err != nil {
return common.Address{}, err
}
- pubKey := crypto.ToECDSAPub(rpk)
- recoveredAddr := crypto.PubkeyToAddress(*pubKey)
- return recoveredAddr, nil
+ return crypto.PubkeyToAddress(*rpk), nil
}
// SignHash is a helper function that calculates a hash for the given message that can be
diff --git a/swarm/services/swap/swap.go b/swarm/services/swap/swap.go
index 1f9b22b90..1eac111be 100644
--- a/swarm/services/swap/swap.go
+++ b/swarm/services/swap/swap.go
@@ -19,6 +19,7 @@ package swap
import (
"context"
"crypto/ecdsa"
+ "errors"
"fmt"
"math/big"
"os"
@@ -134,6 +135,11 @@ func NewSwap(local *SwapParams, remote *SwapProfile, backend chequebook.Backend,
out *chequebook.Outbox
)
+ remotekey, err := crypto.UnmarshalPubkey(common.FromHex(remote.PublicKey))
+ if err != nil {
+ return nil, errors.New("invalid remote public key")
+ }
+
// check if remote chequebook is valid
// insolvent chequebooks suicide so will signal as invalid
// TODO: monitoring a chequebooks events
@@ -142,7 +148,7 @@ func NewSwap(local *SwapParams, remote *SwapProfile, backend chequebook.Backend,
log.Info(fmt.Sprintf("invalid contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err))
} else {
// remote contract valid, create inbox
- in, err = chequebook.NewInbox(local.privateKey, remote.Contract, local.Beneficiary, crypto.ToECDSAPub(common.FromHex(remote.PublicKey)), backend)
+ in, err = chequebook.NewInbox(local.privateKey, remote.Contract, local.Beneficiary, remotekey, backend)
if err != nil {
log.Warn(fmt.Sprintf("unable to set up inbox for chequebook contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err))
}
diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go
index c56d13949..2ce464220 100644
--- a/whisper/whisperv5/api.go
+++ b/whisper/whisperv5/api.go
@@ -252,8 +252,7 @@ func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, er
// Set asymmetric key that is used to encrypt the message
if pubKeyGiven {
- params.Dst = crypto.ToECDSAPub(req.PublicKey)
- if !ValidatePublicKey(params.Dst) {
+ if params.Dst, err = crypto.UnmarshalPubkey(req.PublicKey); err != nil {
return false, ErrInvalidPublicKey
}
}
@@ -329,8 +328,7 @@ func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc.
}
if len(crit.Sig) > 0 {
- filter.Src = crypto.ToECDSAPub(crit.Sig)
- if !ValidatePublicKey(filter.Src) {
+ if filter.Src, err = crypto.UnmarshalPubkey(crit.Sig); err != nil {
return nil, ErrInvalidSigningPubKey
}
}
@@ -513,8 +511,7 @@ func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) {
}
if len(req.Sig) > 0 {
- src = crypto.ToECDSAPub(req.Sig)
- if !ValidatePublicKey(src) {
+ if src, err = crypto.UnmarshalPubkey(req.Sig); err != nil {
return "", ErrInvalidSigningPubKey
}
}
diff --git a/whisper/whisperv6/api.go b/whisper/whisperv6/api.go
index c60bc46a1..d729e79c3 100644
--- a/whisper/whisperv6/api.go
+++ b/whisper/whisperv6/api.go
@@ -272,8 +272,7 @@ func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (hexutil.
// Set asymmetric key that is used to encrypt the message
if pubKeyGiven {
- params.Dst = crypto.ToECDSAPub(req.PublicKey)
- if !ValidatePublicKey(params.Dst) {
+ if params.Dst, err = crypto.UnmarshalPubkey(req.PublicKey); err != nil {
return nil, ErrInvalidPublicKey
}
}
@@ -360,8 +359,7 @@ func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc.
}
if len(crit.Sig) > 0 {
- filter.Src = crypto.ToECDSAPub(crit.Sig)
- if !ValidatePublicKey(filter.Src) {
+ if filter.Src, err = crypto.UnmarshalPubkey(crit.Sig); err != nil {
return nil, ErrInvalidSigningPubKey
}
}
@@ -544,8 +542,7 @@ func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) {
}
if len(req.Sig) > 0 {
- src = crypto.ToECDSAPub(req.Sig)
- if !ValidatePublicKey(src) {
+ if src, err = crypto.UnmarshalPubkey(req.Sig); err != nil {
return "", ErrInvalidSigningPubKey
}
}