From aa73420207cba02a68befdbb8667f1e6ceed3f4d Mon Sep 17 00:00:00 2001 From: Péter Szilágyi Date: Tue, 23 May 2017 14:58:03 +0300 Subject: accounts/keystore, crypto: enforce 256 bit keys on import --- accounts/keystore/key.go | 5 ++--- accounts/keystore/keystore.go | 1 - accounts/keystore/keystore_passphrase.go | 5 ++++- accounts/keystore/keystore_passphrase_test.go | 2 +- accounts/keystore/presale.go | 5 ++++- core/types/transaction_test.go | 2 +- crypto/crypto.go | 27 ++++++++++++--------------- internal/ethapi/api.go | 6 ++---- swarm/api/config_test.go | 5 ++++- tests/util.go | 5 ++--- 10 files changed, 32 insertions(+), 31 deletions(-) diff --git a/accounts/keystore/key.go b/accounts/keystore/key.go index 292b47b44..ecc955d74 100644 --- a/accounts/keystore/key.go +++ b/accounts/keystore/key.go @@ -124,14 +124,13 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) { if err != nil { return err } - - privkey, err := hex.DecodeString(keyJSON.PrivateKey) + privkey, err := crypto.HexToECDSA(keyJSON.PrivateKey) if err != nil { return err } k.Address = common.BytesToAddress(addr) - k.PrivateKey = crypto.ToECDSA(privkey) + k.PrivateKey = privkey return nil } diff --git a/accounts/keystore/keystore.go b/accounts/keystore/keystore.go index a81098227..9df7f2dd9 100644 --- a/accounts/keystore/keystore.go +++ b/accounts/keystore/keystore.go @@ -450,7 +450,6 @@ func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (acco if ks.cache.hasAddress(key.Address) { return accounts.Account{}, fmt.Errorf("account already exists") } - return ks.importKey(key, passphrase) } diff --git a/accounts/keystore/keystore_passphrase.go b/accounts/keystore/keystore_passphrase.go index 2eae25841..9a5c452c1 100644 --- a/accounts/keystore/keystore_passphrase.go +++ b/accounts/keystore/keystore_passphrase.go @@ -182,7 +182,10 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) { if err != nil { return nil, err } - key := crypto.ToECDSA(keyBytes) + key, err := crypto.ToECDSA(keyBytes) + if err != nil { + return nil, err + } return &Key{ Id: uuid.UUID(keyId), Address: crypto.PubkeyToAddress(key.PublicKey), diff --git a/accounts/keystore/keystore_passphrase_test.go b/accounts/keystore/keystore_passphrase_test.go index 086addbc1..630682ceb 100644 --- a/accounts/keystore/keystore_passphrase_test.go +++ b/accounts/keystore/keystore_passphrase_test.go @@ -46,7 +46,7 @@ func TestKeyEncryptDecrypt(t *testing.T) { // Decrypt with the correct password key, err := DecryptKey(keyjson, password) if err != nil { - t.Errorf("test %d: json key failed to decrypt: %v", i, err) + t.Fatalf("test %d: json key failed to decrypt: %v", i, err) } if key.Address != address { t.Errorf("test %d: key address mismatch: have %x, want %x", i, key.Address, address) diff --git a/accounts/keystore/presale.go b/accounts/keystore/presale.go index 5b883c45f..2f18a64db 100644 --- a/accounts/keystore/presale.go +++ b/accounts/keystore/presale.go @@ -74,7 +74,10 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error return nil, err } ethPriv := crypto.Keccak256(plainText) - ecKey := crypto.ToECDSA(ethPriv) + ecKey, err := crypto.ToECDSA(ethPriv) + if err != nil { + return nil, err + } key = &Key{ Id: nil, Address: crypto.PubkeyToAddress(ecKey.PublicKey), diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index 6e4519c04..edbe171e6 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -79,7 +79,7 @@ func decodeTx(data []byte) (*Transaction, error) { } func defaultTestKey() (*ecdsa.PrivateKey, common.Address) { - key := crypto.ToECDSA(common.Hex2Bytes("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")) + key, _ := crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8") addr := crypto.PubkeyToAddress(key.PublicKey) return key, addr } diff --git a/crypto/crypto.go b/crypto/crypto.go index 0b8c06008..d38ffd0d5 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -22,12 +22,14 @@ import ( "crypto/rand" "encoding/hex" "errors" + "fmt" "io" "io/ioutil" "math/big" "os" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/rlp" ) @@ -76,23 +78,22 @@ func CreateAddress(b common.Address, nonce uint64) common.Address { } // ToECDSA creates a private key with the given D value. -func ToECDSA(prv []byte) *ecdsa.PrivateKey { - if len(prv) == 0 { - return nil - } - +func ToECDSA(d []byte) (*ecdsa.PrivateKey, error) { priv := new(ecdsa.PrivateKey) priv.PublicKey.Curve = S256() - priv.D = new(big.Int).SetBytes(prv) - priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(prv) - return priv + if 8*len(d) != priv.Params().BitSize { + return nil, fmt.Errorf("invalid length, need %d bits", priv.Params().BitSize) + } + priv.D = new(big.Int).SetBytes(d) + priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(d) + return priv, nil } func FromECDSA(prv *ecdsa.PrivateKey) []byte { if prv == nil { return nil } - return prv.D.Bytes() + return math.PaddedBigBytes(prv.D, 32) } func ToECDSAPub(pub []byte) *ecdsa.PublicKey { @@ -116,10 +117,7 @@ func HexToECDSA(hexkey string) (*ecdsa.PrivateKey, error) { if err != nil { return nil, errors.New("invalid hex string") } - if len(b) != 32 { - return nil, errors.New("invalid length, need 256 bits") - } - return ToECDSA(b), nil + return ToECDSA(b) } // LoadECDSA loads a secp256k1 private key from the given file. @@ -139,8 +137,7 @@ func LoadECDSA(file string) (*ecdsa.PrivateKey, error) { if err != nil { return nil, err } - - return ToECDSA(key), nil + return ToECDSA(key) } // SaveECDSA saves a secp256k1 private key to the given file with diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 62edc695c..a22c15eca 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -19,7 +19,6 @@ package ethapi import ( "bytes" "context" - "encoding/hex" "errors" "fmt" "math/big" @@ -283,12 +282,11 @@ func fetchKeystore(am *accounts.Manager) *keystore.KeyStore { // ImportRawKey stores the given hex encoded ECDSA key into the key directory, // encrypting it with the passphrase. func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) { - hexkey, err := hex.DecodeString(privkey) + key, err := crypto.HexToECDSA(privkey) if err != nil { return common.Address{}, err } - - acc, err := fetchKeystore(s.am).ImportECDSA(crypto.ToECDSA(hexkey), password) + acc, err := fetchKeystore(s.am).ImportECDSA(key, password) return acc.Address, err } diff --git a/swarm/api/config_test.go b/swarm/api/config_test.go index 6b5cea915..85d056270 100644 --- a/swarm/api/config_test.go +++ b/swarm/api/config_test.go @@ -96,7 +96,10 @@ func TestConfigWriteRead(t *testing.T) { } defer os.RemoveAll(tmp) - prvkey := crypto.ToECDSA(common.Hex2Bytes(hexprvkey)) + prvkey, err := crypto.HexToECDSA(hexprvkey) + if err != nil { + t.Fatalf("failed to load private key: %v", err) + } orig, err := NewConfig(tmp, common.Address{}, prvkey, 323) if err != nil { t.Fatalf("expected no error, got %v", err) diff --git a/tests/util.go b/tests/util.go index 7a08e5ed8..a3a9a1f64 100644 --- a/tests/util.go +++ b/tests/util.go @@ -18,7 +18,6 @@ package tests import ( "bytes" - "encoding/hex" "fmt" "math/big" "os" @@ -161,8 +160,8 @@ func NewEVMEnvironment(vmTest bool, chainConfig *params.ChainConfig, statedb *st origin := common.HexToAddress(tx["caller"]) if len(tx["secretKey"]) > 0 { - key, _ := hex.DecodeString(tx["secretKey"]) - origin = crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey) + key, _ := crypto.HexToECDSA(tx["secretKey"]) + origin = crypto.PubkeyToAddress(key.PublicKey) } var to *common.Address -- cgit