Skip to content

Commit 0255951

Browse files
fjlgballet
authored andcommitted
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.
1 parent 85cd64d commit 0255951

File tree

9 files changed

+62
-39
lines changed

9 files changed

+62
-39
lines changed

cmd/wnode/main.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,8 @@ func processArgs() {
140140
}
141141

142142
if *asymmetricMode && len(*argPub) > 0 {
143-
pub = crypto.ToECDSAPub(common.FromHex(*argPub))
144-
if !isKeyValid(pub) {
143+
var err error
144+
if pub, err = crypto.UnmarshalPubkey(common.FromHex(*argPub)); err != nil {
145145
utils.Fatalf("invalid public key")
146146
}
147147
}
@@ -321,10 +321,6 @@ func startServer() error {
321321
return nil
322322
}
323323

324-
func isKeyValid(k *ecdsa.PublicKey) bool {
325-
return k.X != nil && k.Y != nil
326-
}
327-
328324
func configureNode() {
329325
var err error
330326
var p2pAccept bool
@@ -340,9 +336,8 @@ func configureNode() {
340336
if b == nil {
341337
utils.Fatalf("Error: can not convert hexadecimal string")
342338
}
343-
pub = crypto.ToECDSAPub(b)
344-
if !isKeyValid(pub) {
345-
utils.Fatalf("Error: invalid public key")
339+
if pub, err = crypto.UnmarshalPubkey(b); err != nil {
340+
utils.Fatalf("Error: invalid peer public key")
346341
}
347342
}
348343
}

crypto/crypto.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ var (
3939
secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2))
4040
)
4141

42+
var errInvalidPubkey = errors.New("invalid secp256k1 public key")
43+
4244
// Keccak256 calculates and returns the Keccak256 hash of the input data.
4345
func Keccak256(data ...[]byte) []byte {
4446
d := sha3.NewKeccak256()
@@ -122,12 +124,13 @@ func FromECDSA(priv *ecdsa.PrivateKey) []byte {
122124
return math.PaddedBigBytes(priv.D, priv.Params().BitSize/8)
123125
}
124126

125-
func ToECDSAPub(pub []byte) *ecdsa.PublicKey {
126-
if len(pub) == 0 {
127-
return nil
128-
}
127+
// UnmarshalPubkey converts bytes to a secp256k1 public key.
128+
func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
129129
x, y := elliptic.Unmarshal(S256(), pub)
130-
return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}
130+
if x == nil {
131+
return nil, errInvalidPubkey
132+
}
133+
return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
131134
}
132135

133136
func FromECDSAPub(pub *ecdsa.PublicKey) []byte {

crypto/crypto_test.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ import (
2323
"io/ioutil"
2424
"math/big"
2525
"os"
26+
"reflect"
2627
"testing"
2728

2829
"github.com/ethereum/go-ethereum/common"
30+
"github.com/ethereum/go-ethereum/common/hexutil"
2931
)
3032

3133
var testAddrHex = "970e8128ab834e8eac17ab8e3812f010678cf791"
@@ -56,6 +58,33 @@ func BenchmarkSha3(b *testing.B) {
5658
}
5759
}
5860

61+
func TestUnmarshalPubkey(t *testing.T) {
62+
key, err := UnmarshalPubkey(nil)
63+
if err != errInvalidPubkey || key != nil {
64+
t.Fatalf("expected error, got %v, %v", err, key)
65+
}
66+
key, err = UnmarshalPubkey([]byte{1, 2, 3})
67+
if err != errInvalidPubkey || key != nil {
68+
t.Fatalf("expected error, got %v, %v", err, key)
69+
}
70+
71+
var (
72+
enc, _ = hex.DecodeString("04760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1b01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d")
73+
dec = &ecdsa.PublicKey{
74+
Curve: S256(),
75+
X: hexutil.MustDecodeBig("0x760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1"),
76+
Y: hexutil.MustDecodeBig("0xb01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d"),
77+
}
78+
)
79+
key, err = UnmarshalPubkey(enc)
80+
if err != nil {
81+
t.Fatalf("expected no error, got %v", err)
82+
}
83+
if !reflect.DeepEqual(key, dec) {
84+
t.Fatal("wrong result")
85+
}
86+
}
87+
5988
func TestSign(t *testing.T) {
6089
key, _ := HexToECDSA(testPrivHex)
6190
addr := common.HexToAddress(testAddrHex)
@@ -69,7 +98,7 @@ func TestSign(t *testing.T) {
6998
if err != nil {
7099
t.Errorf("ECRecover error: %s", err)
71100
}
72-
pubKey := ToECDSAPub(recoveredPub)
101+
pubKey, _ := UnmarshalPubkey(recoveredPub)
73102
recoveredAddr := PubkeyToAddress(*pubKey)
74103
if addr != recoveredAddr {
75104
t.Errorf("Address mismatch: want: %x have: %x", addr, recoveredAddr)

internal/ethapi/api.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -461,13 +461,11 @@ func (s *PrivateAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Byt
461461
}
462462
sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1
463463

464-
rpk, err := crypto.Ecrecover(signHash(data), sig)
464+
rpk, err := crypto.SigToPub(signHash(data), sig)
465465
if err != nil {
466466
return common.Address{}, err
467467
}
468-
pubKey := crypto.ToECDSAPub(rpk)
469-
recoveredAddr := crypto.PubkeyToAddress(*pubKey)
470-
return recoveredAddr, nil
468+
return crypto.PubkeyToAddress(*rpk), nil
471469
}
472470

473471
// SignAndSendTransaction was renamed to SendTransaction. This method is deprecated

p2p/rlpx.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -528,9 +528,9 @@ func importPublicKey(pubKey []byte) (*ecies.PublicKey, error) {
528528
return nil, fmt.Errorf("invalid public key length %v (expect 64/65)", len(pubKey))
529529
}
530530
// TODO: fewer pointless conversions
531-
pub := crypto.ToECDSAPub(pubKey65)
532-
if pub.X == nil {
533-
return nil, fmt.Errorf("invalid public key")
531+
pub, err := crypto.UnmarshalPubkey(pubKey65)
532+
if err != nil {
533+
return nil, err
534534
}
535535
return ecies.ImportECDSAPublic(pub), nil
536536
}

signer/core/api.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -432,13 +432,11 @@ func (api *SignerAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (c
432432
}
433433
sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1
434434
hash, _ := SignHash(data)
435-
rpk, err := crypto.Ecrecover(hash, sig)
435+
rpk, err := crypto.SigToPub(hash, sig)
436436
if err != nil {
437437
return common.Address{}, err
438438
}
439-
pubKey := crypto.ToECDSAPub(rpk)
440-
recoveredAddr := crypto.PubkeyToAddress(*pubKey)
441-
return recoveredAddr, nil
439+
return crypto.PubkeyToAddress(*rpk), nil
442440
}
443441

444442
// SignHash is a helper function that calculates a hash for the given message that can be

swarm/services/swap/swap.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package swap
1919
import (
2020
"context"
2121
"crypto/ecdsa"
22+
"errors"
2223
"fmt"
2324
"math/big"
2425
"os"
@@ -134,6 +135,11 @@ func NewSwap(local *SwapParams, remote *SwapProfile, backend chequebook.Backend,
134135
out *chequebook.Outbox
135136
)
136137

138+
remotekey, err := crypto.UnmarshalPubkey(common.FromHex(remote.PublicKey))
139+
if err != nil {
140+
return nil, errors.New("invalid remote public key")
141+
}
142+
137143
// check if remote chequebook is valid
138144
// insolvent chequebooks suicide so will signal as invalid
139145
// TODO: monitoring a chequebooks events
@@ -142,7 +148,7 @@ func NewSwap(local *SwapParams, remote *SwapProfile, backend chequebook.Backend,
142148
log.Info(fmt.Sprintf("invalid contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err))
143149
} else {
144150
// remote contract valid, create inbox
145-
in, err = chequebook.NewInbox(local.privateKey, remote.Contract, local.Beneficiary, crypto.ToECDSAPub(common.FromHex(remote.PublicKey)), backend)
151+
in, err = chequebook.NewInbox(local.privateKey, remote.Contract, local.Beneficiary, remotekey, backend)
146152
if err != nil {
147153
log.Warn(fmt.Sprintf("unable to set up inbox for chequebook contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err))
148154
}

whisper/whisperv5/api.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,7 @@ func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, er
252252

253253
// Set asymmetric key that is used to encrypt the message
254254
if pubKeyGiven {
255-
params.Dst = crypto.ToECDSAPub(req.PublicKey)
256-
if !ValidatePublicKey(params.Dst) {
255+
if params.Dst, err = crypto.UnmarshalPubkey(req.PublicKey); err != nil {
257256
return false, ErrInvalidPublicKey
258257
}
259258
}
@@ -329,8 +328,7 @@ func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc.
329328
}
330329

331330
if len(crit.Sig) > 0 {
332-
filter.Src = crypto.ToECDSAPub(crit.Sig)
333-
if !ValidatePublicKey(filter.Src) {
331+
if filter.Src, err = crypto.UnmarshalPubkey(crit.Sig); err != nil {
334332
return nil, ErrInvalidSigningPubKey
335333
}
336334
}
@@ -513,8 +511,7 @@ func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) {
513511
}
514512

515513
if len(req.Sig) > 0 {
516-
src = crypto.ToECDSAPub(req.Sig)
517-
if !ValidatePublicKey(src) {
514+
if src, err = crypto.UnmarshalPubkey(req.Sig); err != nil {
518515
return "", ErrInvalidSigningPubKey
519516
}
520517
}

whisper/whisperv6/api.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,7 @@ func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (hexutil.
272272

273273
// Set asymmetric key that is used to encrypt the message
274274
if pubKeyGiven {
275-
params.Dst = crypto.ToECDSAPub(req.PublicKey)
276-
if !ValidatePublicKey(params.Dst) {
275+
if params.Dst, err = crypto.UnmarshalPubkey(req.PublicKey); err != nil {
277276
return nil, ErrInvalidPublicKey
278277
}
279278
}
@@ -360,8 +359,7 @@ func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc.
360359
}
361360

362361
if len(crit.Sig) > 0 {
363-
filter.Src = crypto.ToECDSAPub(crit.Sig)
364-
if !ValidatePublicKey(filter.Src) {
362+
if filter.Src, err = crypto.UnmarshalPubkey(crit.Sig); err != nil {
365363
return nil, ErrInvalidSigningPubKey
366364
}
367365
}
@@ -544,8 +542,7 @@ func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) {
544542
}
545543

546544
if len(req.Sig) > 0 {
547-
src = crypto.ToECDSAPub(req.Sig)
548-
if !ValidatePublicKey(src) {
545+
if src, err = crypto.UnmarshalPubkey(req.Sig); err != nil {
549546
return "", ErrInvalidSigningPubKey
550547
}
551548
}

0 commit comments

Comments
 (0)