accounts, crypto: move keystore to package accounts

The account management API was originally implemented as a thin layer
around crypto.KeyStore, on the grounds that several kinds of key stores
would be implemented later on. It turns out that this won't happen so
KeyStore is a superflous abstraction.

In this commit crypto.KeyStore and everything related to it moves to
package accounts and is unexported.
pull/2284/head
Felix Lange 9 years ago
parent dff9b4246f
commit 85e6c40c00
  1. 17
      accounts/abi/bind/auth.go
  2. 34
      accounts/abi/bind/bind_test.go
  3. 34
      accounts/account_manager.go
  4. 20
      accounts/accounts_test.go
  5. 25
      accounts/key.go
  6. 21
      accounts/key_store_passphrase.go
  7. 2
      accounts/key_store_passphrase_test.go
  8. 18
      accounts/key_store_plain.go
  9. 25
      accounts/key_store_test.go
  10. 132
      accounts/presale.go
  11. 0
      accounts/testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e
  12. 0
      accounts/testdata/v1_test_vector.json
  13. 0
      accounts/testdata/v3_test_vector.json
  14. 26
      cmd/geth/js_test.go
  15. 9
      cmd/gethrpctest/main.go
  16. 19
      cmd/utils/flags.go
  17. 106
      crypto/crypto.go
  18. 6
      eth/helper_test.go
  19. 3
      eth/protocol_test.go

@ -17,10 +17,12 @@
package bind package bind
import ( import (
"crypto/ecdsa"
"errors" "errors"
"io" "io"
"io/ioutil" "io/ioutil"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -33,23 +35,24 @@ func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
key, err := crypto.DecryptKey(json, passphrase) key, err := accounts.DecryptKey(json, passphrase)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return NewKeyedTransactor(key), nil return NewKeyedTransactor(key.PrivateKey), nil
} }
// NewKeyedTransactor is a utility method to easily create a transaction signer // NewKeyedTransactor is a utility method to easily create a transaction signer
// from a plain go-ethereum crypto key. // from a single private key.
func NewKeyedTransactor(key *crypto.Key) *TransactOpts { func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
keyAddr := crypto.PubkeyToAddress(key.PublicKey)
return &TransactOpts{ return &TransactOpts{
From: key.Address, From: keyAddr,
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
if address != key.Address { if address != keyAddr {
return nil, errors.New("not authorized to sign this account") return nil, errors.New("not authorized to sign this account")
} }
signature, err := crypto.Sign(tx.SigHash().Bytes(), key.PrivateKey) signature, err := crypto.Sign(tx.SigHash().Bytes(), key)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -167,11 +167,9 @@ var bindTests = []struct {
`[{"constant":true,"inputs":[],"name":"transactString","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":true,"inputs":[],"name":"deployString","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"str","type":"string"}],"name":"transact","outputs":[],"type":"function"},{"inputs":[{"name":"str","type":"string"}],"type":"constructor"}]`, `[{"constant":true,"inputs":[],"name":"transactString","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":true,"inputs":[],"name":"deployString","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"str","type":"string"}],"name":"transact","outputs":[],"type":"function"},{"inputs":[{"name":"str","type":"string"}],"type":"constructor"}]`,
` `
// Generate a new random account and a funded simulator // Generate a new random account and a funded simulator
key := crypto.NewKey(rand.Reader) key, _ := crypto.GenerateKey()
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: key.Address, Balance: big.NewInt(10000000000)})
// Convert the tester key to an authorized transactor for ease of use
auth := bind.NewKeyedTransactor(key) auth := bind.NewKeyedTransactor(key)
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)})
// Deploy an interaction tester contract and call a transaction on it // Deploy an interaction tester contract and call a transaction on it
_, _, interactor, err := DeployInteractor(auth, sim, "Deploy string") _, _, interactor, err := DeployInteractor(auth, sim, "Deploy string")
@ -210,11 +208,9 @@ var bindTests = []struct {
`[{"constant":true,"inputs":[],"name":"tuple","outputs":[{"name":"a","type":"string"},{"name":"b","type":"int256"},{"name":"c","type":"bytes32"}],"type":"function"}]`, `[{"constant":true,"inputs":[],"name":"tuple","outputs":[{"name":"a","type":"string"},{"name":"b","type":"int256"},{"name":"c","type":"bytes32"}],"type":"function"}]`,
` `
// Generate a new random account and a funded simulator // Generate a new random account and a funded simulator
key := crypto.NewKey(rand.Reader) key, _ := crypto.GenerateKey()
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: key.Address, Balance: big.NewInt(10000000000)})
// Convert the tester key to an authorized transactor for ease of use
auth := bind.NewKeyedTransactor(key) auth := bind.NewKeyedTransactor(key)
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)})
// Deploy a tuple tester contract and execute a structured call on it // Deploy a tuple tester contract and execute a structured call on it
_, _, tupler, err := DeployTupler(auth, sim) _, _, tupler, err := DeployTupler(auth, sim)
@ -252,11 +248,9 @@ var bindTests = []struct {
`[{"constant":true,"inputs":[{"name":"input","type":"address[]"}],"name":"echoAddresses","outputs":[{"name":"output","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"uint24[23]"}],"name":"echoFancyInts","outputs":[{"name":"output","type":"uint24[23]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"int256[]"}],"name":"echoInts","outputs":[{"name":"output","type":"int256[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"bool[]"}],"name":"echoBools","outputs":[{"name":"output","type":"bool[]"}],"type":"function"}]`, `[{"constant":true,"inputs":[{"name":"input","type":"address[]"}],"name":"echoAddresses","outputs":[{"name":"output","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"uint24[23]"}],"name":"echoFancyInts","outputs":[{"name":"output","type":"uint24[23]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"int256[]"}],"name":"echoInts","outputs":[{"name":"output","type":"int256[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"bool[]"}],"name":"echoBools","outputs":[{"name":"output","type":"bool[]"}],"type":"function"}]`,
` `
// Generate a new random account and a funded simulator // Generate a new random account and a funded simulator
key := crypto.NewKey(rand.Reader) key, _ := crypto.GenerateKey()
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: key.Address, Balance: big.NewInt(10000000000)})
// Convert the tester key to an authorized transactor for ease of use
auth := bind.NewKeyedTransactor(key) auth := bind.NewKeyedTransactor(key)
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)})
// Deploy a slice tester contract and execute a n array call on it // Deploy a slice tester contract and execute a n array call on it
_, _, slicer, err := DeploySlicer(auth, sim) _, _, slicer, err := DeploySlicer(auth, sim)
@ -265,10 +259,10 @@ var bindTests = []struct {
} }
sim.Commit() sim.Commit()
if out, err := slicer.EchoAddresses(nil, []common.Address{key.Address, common.Address{}}); err != nil { if out, err := slicer.EchoAddresses(nil, []common.Address{auth.From, common.Address{}}); err != nil {
t.Fatalf("Failed to call slice echoer: %v", err) t.Fatalf("Failed to call slice echoer: %v", err)
} else if !reflect.DeepEqual(out, []common.Address{key.Address, common.Address{}}) { } else if !reflect.DeepEqual(out, []common.Address{auth.From, common.Address{}}) {
t.Fatalf("Slice return mismatch: have %v, want %v", out, []common.Address{key.Address, common.Address{}}) t.Fatalf("Slice return mismatch: have %v, want %v", out, []common.Address{auth.From, common.Address{}})
} }
`, `,
}, },
@ -288,11 +282,9 @@ var bindTests = []struct {
`[{"constant":true,"inputs":[],"name":"caller","outputs":[{"name":"","type":"address"}],"type":"function"}]`, `[{"constant":true,"inputs":[],"name":"caller","outputs":[{"name":"","type":"address"}],"type":"function"}]`,
` `
// Generate a new random account and a funded simulator // Generate a new random account and a funded simulator
key := crypto.NewKey(rand.Reader) key, _ := crypto.GenerateKey()
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: key.Address, Balance: big.NewInt(10000000000)})
// Convert the tester key to an authorized transactor for ease of use
auth := bind.NewKeyedTransactor(key) auth := bind.NewKeyedTransactor(key)
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)})
// Deploy a default method invoker contract and execute its default method // Deploy a default method invoker contract and execute its default method
_, _, defaulter, err := DeployDefaulter(auth, sim) _, _, defaulter, err := DeployDefaulter(auth, sim)
@ -306,8 +298,8 @@ var bindTests = []struct {
if caller, err := defaulter.Caller(nil); err != nil { if caller, err := defaulter.Caller(nil); err != nil {
t.Fatalf("Failed to call address retriever: %v", err) t.Fatalf("Failed to call address retriever: %v", err)
} else if (caller != key.Address) { } else if (caller != auth.From) {
t.Fatalf("Address mismatch: have %v, want %v", caller, key.Address) t.Fatalf("Address mismatch: have %v, want %v", caller, auth.From)
} }
`, `,
}, },

@ -19,9 +19,6 @@
// This abstracts part of a user's interaction with an account she controls. // This abstracts part of a user's interaction with an account she controls.
package accounts package accounts
// Currently this is pretty much a passthrough to the KeyStore interface,
// and accounts persistence is derived from stored keys' addresses
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
crand "crypto/rand" crand "crypto/rand"
@ -49,19 +46,26 @@ func (acc *Account) MarshalJSON() ([]byte, error) {
} }
type Manager struct { type Manager struct {
keyStore crypto.KeyStore keyStore keyStore
unlocked map[common.Address]*unlocked unlocked map[common.Address]*unlocked
mutex sync.RWMutex mutex sync.RWMutex
} }
type unlocked struct { type unlocked struct {
*crypto.Key *Key
abort chan struct{} abort chan struct{}
} }
func NewManager(keyStore crypto.KeyStore) *Manager { func NewManager(keydir string, scryptN, scryptP int) *Manager {
return &Manager{
keyStore: newKeyStorePassphrase(keydir, scryptN, scryptP),
unlocked: make(map[common.Address]*unlocked),
}
}
func NewPlaintextManager(keydir string) *Manager {
return &Manager{ return &Manager{
keyStore: keyStore, keyStore: newKeyStorePlain(keydir),
unlocked: make(map[common.Address]*unlocked), unlocked: make(map[common.Address]*unlocked),
} }
} }
@ -216,19 +220,23 @@ func (am *Manager) Export(path string, addr common.Address, keyAuth string) erro
} }
func (am *Manager) Import(path string, keyAuth string) (Account, error) { func (am *Manager) Import(path string, keyAuth string) (Account, error) {
privateKeyECDSA, err := crypto.LoadECDSA(path) priv, err := crypto.LoadECDSA(path)
if err != nil { if err != nil {
return Account{}, err return Account{}, err
} }
key := crypto.NewKeyFromECDSA(privateKeyECDSA) return am.ImportECDSA(priv, keyAuth)
if err = am.keyStore.StoreKey(key, keyAuth); err != nil { }
func (am *Manager) ImportECDSA(priv *ecdsa.PrivateKey, keyAuth string) (Account, error) {
key := newKeyFromECDSA(priv)
if err := am.keyStore.StoreKey(key, keyAuth); err != nil {
return Account{}, err return Account{}, err
} }
return Account{Address: key.Address}, nil return Account{Address: key.Address}, nil
} }
func (am *Manager) Update(addr common.Address, authFrom, authTo string) (err error) { func (am *Manager) Update(addr common.Address, authFrom, authTo string) (err error) {
var key *crypto.Key var key *Key
key, err = am.keyStore.GetKey(addr, authFrom) key, err = am.keyStore.GetKey(addr, authFrom)
if err == nil { if err == nil {
@ -241,8 +249,8 @@ func (am *Manager) Update(addr common.Address, authFrom, authTo string) (err err
} }
func (am *Manager) ImportPreSaleKey(keyJSON []byte, password string) (acc Account, err error) { func (am *Manager) ImportPreSaleKey(keyJSON []byte, password string) (acc Account, err error) {
var key *crypto.Key var key *Key
key, err = crypto.ImportPreSaleKey(am.keyStore, keyJSON, password) key, err = importPreSaleKey(am.keyStore, keyJSON, password)
if err != nil { if err != nil {
return return
} }

@ -21,17 +21,14 @@ import (
"os" "os"
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/crypto"
) )
var testSigData = make([]byte, 32) var testSigData = make([]byte, 32)
func TestSign(t *testing.T) { func TestSign(t *testing.T) {
dir, ks := tmpKeyStore(t, crypto.NewKeyStorePlain) dir, am := tmpManager(t, false)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
am := NewManager(ks)
pass := "" // not used but required by API pass := "" // not used but required by API
a1, err := am.NewAccount(pass) a1, err := am.NewAccount(pass)
am.Unlock(a1.Address, "") am.Unlock(a1.Address, "")
@ -43,10 +40,9 @@ func TestSign(t *testing.T) {
} }
func TestTimedUnlock(t *testing.T) { func TestTimedUnlock(t *testing.T) {
dir, ks := tmpKeyStore(t, crypto.NewKeyStorePlain) dir, am := tmpManager(t, false)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
am := NewManager(ks)
pass := "foo" pass := "foo"
a1, err := am.NewAccount(pass) a1, err := am.NewAccount(pass)
@ -76,10 +72,9 @@ func TestTimedUnlock(t *testing.T) {
} }
func TestOverrideUnlock(t *testing.T) { func TestOverrideUnlock(t *testing.T) {
dir, ks := tmpKeyStore(t, crypto.NewKeyStorePlain) dir, am := tmpManager(t, false)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
am := NewManager(ks)
pass := "foo" pass := "foo"
a1, err := am.NewAccount(pass) a1, err := am.NewAccount(pass)
@ -115,11 +110,10 @@ func TestOverrideUnlock(t *testing.T) {
// This test should fail under -race if signing races the expiration goroutine. // This test should fail under -race if signing races the expiration goroutine.
func TestSignRace(t *testing.T) { func TestSignRace(t *testing.T) {
dir, ks := tmpKeyStore(t, crypto.NewKeyStorePlain) dir, am := tmpManager(t, false)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
// Create a test account. // Create a test account.
am := NewManager(ks)
a1, err := am.NewAccount("") a1, err := am.NewAccount("")
if err != nil { if err != nil {
t.Fatal("could not create the test account", err) t.Fatal("could not create the test account", err)
@ -141,10 +135,14 @@ func TestSignRace(t *testing.T) {
t.Errorf("Account did not lock within the timeout") t.Errorf("Account did not lock within the timeout")
} }
func tmpKeyStore(t *testing.T, new func(string) crypto.KeyStore) (string, crypto.KeyStore) { func tmpManager(t *testing.T, encrypted bool) (string, *Manager) {
d, err := ioutil.TempDir("", "eth-keystore-test") d, err := ioutil.TempDir("", "eth-keystore-test")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
new := NewPlaintextManager
if encrypted {
new = func(kd string) *Manager { return NewManager(kd, LightScryptN, LightScryptP) }
}
return d, new(d) return d, new(d)
} }

@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License // 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/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package crypto package accounts
import ( import (
"bytes" "bytes"
@ -25,6 +25,7 @@ import (
"strings" "strings"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/pborman/uuid" "github.com/pborman/uuid"
) )
@ -42,6 +43,16 @@ type Key struct {
PrivateKey *ecdsa.PrivateKey PrivateKey *ecdsa.PrivateKey
} }
type keyStore interface {
// create new key using io.Reader entropy source and optionally using auth string
GenerateNewKey(io.Reader, string) (*Key, error)
GetKey(common.Address, string) (*Key, error) // get key from addr and auth string
GetKeyAddresses() ([]common.Address, error) // get all addresses
StoreKey(*Key, string) error // store key optionally using auth string
DeleteKey(common.Address, string) error // delete key by addr and auth string
Cleanup(keyAddr common.Address) (err error)
}
type plainKeyJSON struct { type plainKeyJSON struct {
Address string `json:"address"` Address string `json:"address"`
PrivateKey string `json:"privatekey"` PrivateKey string `json:"privatekey"`
@ -87,7 +98,7 @@ type scryptParamsJSON struct {
func (k *Key) MarshalJSON() (j []byte, err error) { func (k *Key) MarshalJSON() (j []byte, err error) {
jStruct := plainKeyJSON{ jStruct := plainKeyJSON{
hex.EncodeToString(k.Address[:]), hex.EncodeToString(k.Address[:]),
hex.EncodeToString(FromECDSA(k.PrivateKey)), hex.EncodeToString(crypto.FromECDSA(k.PrivateKey)),
k.Id.String(), k.Id.String(),
version, version,
} }
@ -116,16 +127,16 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) {
} }
k.Address = common.BytesToAddress(addr) k.Address = common.BytesToAddress(addr)
k.PrivateKey = ToECDSA(privkey) k.PrivateKey = crypto.ToECDSA(privkey)
return nil return nil
} }
func NewKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key { func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
id := uuid.NewRandom() id := uuid.NewRandom()
key := &Key{ key := &Key{
Id: id, Id: id,
Address: PubkeyToAddress(privateKeyECDSA.PublicKey), Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
PrivateKey: privateKeyECDSA, PrivateKey: privateKeyECDSA,
} }
return key return key
@ -143,7 +154,7 @@ func NewKey(rand io.Reader) *Key {
panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
} }
return NewKeyFromECDSA(privateKeyECDSA) return newKeyFromECDSA(privateKeyECDSA)
} }
// generate key whose address fits into < 155 bits so it can fit into // generate key whose address fits into < 155 bits so it can fit into
@ -160,7 +171,7 @@ func NewKeyForDirectICAP(rand io.Reader) *Key {
if err != nil { if err != nil {
panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
} }
key := NewKeyFromECDSA(privateKeyECDSA) key := newKeyFromECDSA(privateKeyECDSA)
if !strings.HasPrefix(key.Address.Hex(), "0x00") { if !strings.HasPrefix(key.Address.Hex(), "0x00") {
return NewKeyForDirectICAP(rand) return NewKeyForDirectICAP(rand)
} }

@ -23,7 +23,7 @@ The crypto is documented at https://github.com/ethereum/wiki/wiki/Web3-Secret-St
*/ */
package crypto package accounts
import ( import (
"bytes" "bytes"
@ -36,6 +36,7 @@ import (
"io" "io"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/randentropy" "github.com/ethereum/go-ethereum/crypto/randentropy"
"github.com/pborman/uuid" "github.com/pborman/uuid"
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
@ -63,12 +64,12 @@ type keyStorePassphrase struct {
scryptP int scryptP int
} }
func NewKeyStorePassphrase(path string, scryptN int, scryptP int) KeyStore { func newKeyStorePassphrase(path string, scryptN int, scryptP int) keyStore {
return &keyStorePassphrase{path, scryptN, scryptP} return &keyStorePassphrase{path, scryptN, scryptP}
} }
func (ks keyStorePassphrase) GenerateNewKey(rand io.Reader, auth string) (key *Key, err error) { func (ks keyStorePassphrase) GenerateNewKey(rand io.Reader, auth string) (key *Key, err error) {
return GenerateNewKeyDefault(ks, rand, auth) return generateNewKeyDefault(ks, rand, auth)
} }
func (ks keyStorePassphrase) GetKey(keyAddr common.Address, auth string) (key *Key, err error) { func (ks keyStorePassphrase) GetKey(keyAddr common.Address, auth string) (key *Key, err error) {
@ -101,14 +102,14 @@ func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
return nil, err return nil, err
} }
encryptKey := derivedKey[:16] encryptKey := derivedKey[:16]
keyBytes := FromECDSA(key.PrivateKey) keyBytes := crypto.FromECDSA(key.PrivateKey)
iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16 iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16
cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv) cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv)
if err != nil { if err != nil {
return nil, err return nil, err
} }
mac := Keccak256(derivedKey[16:32], cipherText) mac := crypto.Keccak256(derivedKey[16:32], cipherText)
scryptParamsJSON := make(map[string]interface{}, 5) scryptParamsJSON := make(map[string]interface{}, 5)
scryptParamsJSON["n"] = scryptN scryptParamsJSON["n"] = scryptN
@ -175,10 +176,10 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
key := ToECDSA(keyBytes) key := crypto.ToECDSA(keyBytes)
return &Key{ return &Key{
Id: uuid.UUID(keyId), Id: uuid.UUID(keyId),
Address: PubkeyToAddress(key.PublicKey), Address: crypto.PubkeyToAddress(key.PublicKey),
PrivateKey: key, PrivateKey: key,
}, nil }, nil
} }
@ -230,7 +231,7 @@ func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byt
return nil, nil, err return nil, nil, err
} }
calculatedMAC := Keccak256(derivedKey[16:32], cipherText) calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
if !bytes.Equal(calculatedMAC, mac) { if !bytes.Equal(calculatedMAC, mac) {
return nil, nil, errors.New("Decryption failed: MAC mismatch") return nil, nil, errors.New("Decryption failed: MAC mismatch")
} }
@ -264,12 +265,12 @@ func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byt
return nil, nil, err return nil, nil, err
} }
calculatedMAC := Keccak256(derivedKey[16:32], cipherText) calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
if !bytes.Equal(calculatedMAC, mac) { if !bytes.Equal(calculatedMAC, mac) {
return nil, nil, errors.New("Decryption failed: MAC mismatch") return nil, nil, errors.New("Decryption failed: MAC mismatch")
} }
plainText, err := aesCBCDecrypt(Keccak256(derivedKey[:16])[:16], cipherText, iv) plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License // 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/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package crypto package accounts
import ( import (
"testing" "testing"

@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License // 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/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package crypto package accounts
import ( import (
"encoding/hex" "encoding/hex"
@ -29,29 +29,19 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
type KeyStore interface {
// create new key using io.Reader entropy source and optionally using auth string
GenerateNewKey(io.Reader, string) (*Key, error)
GetKey(common.Address, string) (*Key, error) // get key from addr and auth string
GetKeyAddresses() ([]common.Address, error) // get all addresses
StoreKey(*Key, string) error // store key optionally using auth string
DeleteKey(common.Address, string) error // delete key by addr and auth string
Cleanup(keyAddr common.Address) (err error)
}
type keyStorePlain struct { type keyStorePlain struct {
keysDirPath string keysDirPath string
} }
func NewKeyStorePlain(path string) KeyStore { func newKeyStorePlain(path string) keyStore {
return &keyStorePlain{path} return &keyStorePlain{path}
} }
func (ks keyStorePlain) GenerateNewKey(rand io.Reader, auth string) (key *Key, err error) { func (ks keyStorePlain) GenerateNewKey(rand io.Reader, auth string) (key *Key, err error) {
return GenerateNewKeyDefault(ks, rand, auth) return generateNewKeyDefault(ks, rand, auth)
} }
func GenerateNewKeyDefault(ks KeyStore, rand io.Reader, auth string) (key *Key, err error) { func generateNewKeyDefault(ks keyStore, rand io.Reader, auth string) (key *Key, err error) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
err = fmt.Errorf("GenerateNewKey error: %v", r) err = fmt.Errorf("GenerateNewKey error: %v", r)

@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License // 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/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package crypto package accounts
import ( import (
"encoding/hex" "encoding/hex"
@ -24,11 +24,12 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/randentropy" "github.com/ethereum/go-ethereum/crypto/randentropy"
) )
func TestKeyStorePlain(t *testing.T) { func TestKeyStorePlain(t *testing.T) {
ks := NewKeyStorePlain(common.DefaultDataDir()) ks := newKeyStorePlain(common.DefaultDataDir())
pass := "" // not used but required by API pass := "" // not used but required by API
k1, err := ks.GenerateNewKey(randentropy.Reader, pass) k1, err := ks.GenerateNewKey(randentropy.Reader, pass)
if err != nil { if err != nil {
@ -56,7 +57,7 @@ func TestKeyStorePlain(t *testing.T) {
} }
func TestKeyStorePassphrase(t *testing.T) { func TestKeyStorePassphrase(t *testing.T) {
ks := NewKeyStorePassphrase(common.DefaultDataDir(), LightScryptN, LightScryptP) ks := newKeyStorePassphrase(common.DefaultDataDir(), LightScryptN, LightScryptP)
pass := "foo" pass := "foo"
k1, err := ks.GenerateNewKey(randentropy.Reader, pass) k1, err := ks.GenerateNewKey(randentropy.Reader, pass)
if err != nil { if err != nil {
@ -82,7 +83,7 @@ func TestKeyStorePassphrase(t *testing.T) {
} }
func TestKeyStorePassphraseDecryptionFail(t *testing.T) { func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
ks := NewKeyStorePassphrase(common.DefaultDataDir(), LightScryptN, LightScryptP) ks := newKeyStorePassphrase(common.DefaultDataDir(), LightScryptN, LightScryptP)
pass := "foo" pass := "foo"
k1, err := ks.GenerateNewKey(randentropy.Reader, pass) k1, err := ks.GenerateNewKey(randentropy.Reader, pass)
if err != nil { if err != nil {
@ -110,16 +111,16 @@ func TestImportPreSaleKey(t *testing.T) {
// python pyethsaletool.py genwallet // python pyethsaletool.py genwallet
// with password "foo" // with password "foo"
fileContent := "{\"encseed\": \"26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba\", \"ethaddr\": \"d4584b5f6229b7be90727b0fc8c6b91bb427821f\", \"email\": \"gustav.simonsson@gmail.com\", \"btcaddr\": \"1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx\"}" fileContent := "{\"encseed\": \"26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba\", \"ethaddr\": \"d4584b5f6229b7be90727b0fc8c6b91bb427821f\", \"email\": \"gustav.simonsson@gmail.com\", \"btcaddr\": \"1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx\"}"
ks := NewKeyStorePassphrase(common.DefaultDataDir(), LightScryptN, LightScryptP) ks := newKeyStorePassphrase(common.DefaultDataDir(), LightScryptN, LightScryptP)
pass := "foo" pass := "foo"
_, err := ImportPreSaleKey(ks, []byte(fileContent), pass) _, err := importPreSaleKey(ks, []byte(fileContent), pass)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
// Test and utils for the key store tests in the Ethereum JSON tests; // Test and utils for the key store tests in the Ethereum JSON tests;
// tests/KeyStoreTests/basic_tests.json // testdataKeyStoreTests/basic_tests.json
type KeyStoreTestV3 struct { type KeyStoreTestV3 struct {
Json encryptedKeyJSONV3 Json encryptedKeyJSONV3
Password string Password string
@ -133,7 +134,7 @@ type KeyStoreTestV1 struct {
} }
func TestV3_PBKDF2_1(t *testing.T) { func TestV3_PBKDF2_1(t *testing.T) {
tests := loadKeyStoreTestV3("tests/v3_test_vector.json", t) tests := loadKeyStoreTestV3("testdata/v3_test_vector.json", t)
testDecryptV3(tests["wikipage_test_vector_pbkdf2"], t) testDecryptV3(tests["wikipage_test_vector_pbkdf2"], t)
} }
@ -153,7 +154,7 @@ func TestV3_PBKDF2_4(t *testing.T) {
} }
func TestV3_Scrypt_1(t *testing.T) { func TestV3_Scrypt_1(t *testing.T) {
tests := loadKeyStoreTestV3("tests/v3_test_vector.json", t) tests := loadKeyStoreTestV3("testdata/v3_test_vector.json", t)
testDecryptV3(tests["wikipage_test_vector_scrypt"], t) testDecryptV3(tests["wikipage_test_vector_scrypt"], t)
} }
@ -163,12 +164,12 @@ func TestV3_Scrypt_2(t *testing.T) {
} }
func TestV1_1(t *testing.T) { func TestV1_1(t *testing.T) {
tests := loadKeyStoreTestV1("tests/v1_test_vector.json", t) tests := loadKeyStoreTestV1("testdata/v1_test_vector.json", t)
testDecryptV1(tests["test1"], t) testDecryptV1(tests["test1"], t)
} }
func TestV1_2(t *testing.T) { func TestV1_2(t *testing.T) {
ks := NewKeyStorePassphrase("tests/v1", LightScryptN, LightScryptP) ks := newKeyStorePassphrase("testdata/v1", LightScryptN, LightScryptP)
addr := common.HexToAddress("cb61d5a9c4896fb9658090b597ef0e7be6f7b67e") addr := common.HexToAddress("cb61d5a9c4896fb9658090b597ef0e7be6f7b67e")
k, err := ks.GetKey(addr, "g") k, err := ks.GetKey(addr, "g")
if err != nil { if err != nil {
@ -178,7 +179,7 @@ func TestV1_2(t *testing.T) {
t.Fatal(fmt.Errorf("Unexpected address: %v, expected %v", k.Address, addr)) t.Fatal(fmt.Errorf("Unexpected address: %v, expected %v", k.Address, addr))
} }
privHex := hex.EncodeToString(FromECDSA(k.PrivateKey)) privHex := hex.EncodeToString(crypto.FromECDSA(k.PrivateKey))
expectedHex := "d1b1178d3529626a1a93e073f65028370d14c7eb0936eb42abef05db6f37ad7d" expectedHex := "d1b1178d3529626a1a93e073f65028370d14c7eb0936eb42abef05db6f37ad7d"
if privHex != expectedHex { if privHex != expectedHex {
t.Fatal(fmt.Errorf("Unexpected privkey: %v, expected %v", privHex, expectedHex)) t.Fatal(fmt.Errorf("Unexpected privkey: %v, expected %v", privHex, expectedHex))

@ -0,0 +1,132 @@
// Copyright 2016 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 <http://www.gnu.org/licenses/>.
package accounts
import (
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/crypto"
"github.com/pborman/uuid"
"golang.org/x/crypto/pbkdf2"
)
// creates a Key and stores that in the given KeyStore by decrypting a presale key JSON
func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (*Key, error) {
key, err := decryptPreSaleKey(keyJSON, password)
if err != nil {
return nil, err
}
key.Id = uuid.NewRandom()
err = keyStore.StoreKey(key, password)
return key, err
}
func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error) {
preSaleKeyStruct := struct {
EncSeed string
EthAddr string
Email string
BtcAddr string
}{}
err = json.Unmarshal(fileContent, &preSaleKeyStruct)
if err != nil {
return nil, err
}
encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed)
iv := encSeedBytes[:16]
cipherText := encSeedBytes[16:]
/*
See https://github.com/ethereum/pyethsaletool
pyethsaletool generates the encryption key from password by
2000 rounds of PBKDF2 with HMAC-SHA-256 using password as salt (:().
16 byte key length within PBKDF2 and resulting key is used as AES key
*/
passBytes := []byte(password)
derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New)
plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
if err != nil {
return nil, err
}
ethPriv := crypto.Keccak256(plainText)
ecKey := crypto.ToECDSA(ethPriv)
key = &Key{
Id: nil,
Address: crypto.PubkeyToAddress(ecKey.PublicKey),
PrivateKey: ecKey,
}
derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x"
expectedAddr := preSaleKeyStruct.EthAddr
if derivedAddr != expectedAddr {
err = fmt.Errorf("decrypted addr '%s' not equal to expected addr '%s'", derivedAddr, expectedAddr)
}
return key, err
}
func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
// AES-128 is selected due to size of encryptKey.
aesBlock, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
stream := cipher.NewCTR(aesBlock, iv)
outText := make([]byte, len(inText))
stream.XORKeyStream(outText, inText)
return outText, err
}
func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) {
aesBlock, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
paddedPlaintext := make([]byte, len(cipherText))
decrypter.CryptBlocks(paddedPlaintext, cipherText)
plaintext := pkcs7Unpad(paddedPlaintext)
if plaintext == nil {
err = errors.New("Decryption failed: PKCS7Unpad failed after AES decryption")
}
return plaintext, err
}
// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
func pkcs7Unpad(in []byte) []byte {
if len(in) == 0 {
return nil
}
padding := in[len(in)-1]
if int(padding) > len(in) || padding > aes.BlockSize {
return nil
} else if padding == 0 {
return nil
}
for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
if in[i] != padding {
return nil
}
}
return in[:len(in)-int(padding)]
}

@ -42,18 +42,17 @@ import (
const ( const (
testSolcPath = "" testSolcPath = ""
solcVersion = "0.9.23" solcVersion = "0.9.23"
testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
testKey = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674" testBalance = "10000000000000000000"
testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
testBalance = "10000000000000000000"
// of empty string // of empty string
testHash = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" testHash = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
) )
var ( var (
versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`)) versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`))
testNodeKey = crypto.ToECDSA(common.Hex2Bytes("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f")) testNodeKey, _ = crypto.HexToECDSA("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f")
testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}` testAccount, _ = crypto.HexToECDSA("e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674")
testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}`
) )
type testjethre struct { type testjethre struct {
@ -99,12 +98,9 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod
t.Fatalf("failed to create node: %v", err) t.Fatalf("failed to create node: %v", err)
} }
// Initialize and register the Ethereum protocol // Initialize and register the Ethereum protocol
keystore := crypto.NewKeyStorePlain(filepath.Join(tmp, "keystore")) accman := accounts.NewPlaintextManager(filepath.Join(tmp, "keystore"))
accman := accounts.NewManager(keystore)
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
core.WriteGenesisBlockForTesting(db, core.GenesisAccount{common.HexToAddress(testAddress), common.String2Big(testBalance)}) core.WriteGenesisBlockForTesting(db, core.GenesisAccount{common.HexToAddress(testAddress), common.String2Big(testBalance)})
ethConf := &eth.Config{ ethConf := &eth.Config{
ChainConfig: &core.ChainConfig{HomesteadBlock: new(big.Int)}, ChainConfig: &core.ChainConfig{HomesteadBlock: new(big.Int)},
TestGenesisState: db, TestGenesisState: db,
@ -122,15 +118,11 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod
t.Fatalf("failed to register ethereum protocol: %v", err) t.Fatalf("failed to register ethereum protocol: %v", err)
} }
// Initialize all the keys for testing // Initialize all the keys for testing
keyb, err := crypto.HexToECDSA(testKey) a, err := accman.ImportECDSA(testAccount, "")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
key := crypto.NewKeyFromECDSA(keyb) if err := accman.Unlock(a.Address, ""); err != nil {
if err := keystore.StoreKey(key, ""); err != nil {
t.Fatal(err)
}
if err := accman.Unlock(key.Address, ""); err != nil {
t.Fatal(err) t.Fatal(err)
} }
// Start the node and assemble the REPL tester // Start the node and assemble the REPL tester

@ -106,18 +106,17 @@ func MakeSystemNode(keydir string, privkey string, test *tests.BlockTest) (*node
return nil, err return nil, err
} }
// Create the keystore and inject an unlocked account if requested // Create the keystore and inject an unlocked account if requested
keystore := crypto.NewKeyStorePassphrase(keydir, crypto.StandardScryptN, crypto.StandardScryptP) accman := accounts.NewPlaintextManager(keydir)
accman := accounts.NewManager(keystore)
if len(privkey) > 0 { if len(privkey) > 0 {
key, err := crypto.HexToECDSA(privkey) key, err := crypto.HexToECDSA(privkey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := keystore.StoreKey(crypto.NewKeyFromECDSA(key), ""); err != nil { a, err := accman.ImportECDSA(key, "")
if err != nil {
return nil, err return nil, err
} }
if err := accman.Unlock(crypto.NewKeyFromECDSA(key).Address, ""); err != nil { if err := accman.Unlock(a.Address, ""); err != nil {
return nil, err return nil, err
} }
} }

@ -551,20 +551,15 @@ func MakeDatabaseHandles() int {
// MakeAccountManager creates an account manager from set command line flags. // MakeAccountManager creates an account manager from set command line flags.
func MakeAccountManager(ctx *cli.Context) *accounts.Manager { func MakeAccountManager(ctx *cli.Context) *accounts.Manager {
// Create the keystore crypto primitive, light if requested // Create the keystore crypto primitive, light if requested
scryptN := crypto.StandardScryptN scryptN := accounts.StandardScryptN
scryptP := crypto.StandardScryptP scryptP := accounts.StandardScryptP
if ctx.GlobalBool(LightKDFFlag.Name) { if ctx.GlobalBool(LightKDFFlag.Name) {
scryptN = crypto.LightScryptN scryptN = accounts.LightScryptN
scryptP = crypto.LightScryptP scryptP = accounts.LightScryptP
} }
// Assemble an account manager using the configured datadir datadir := MustMakeDataDir(ctx)
var ( keydir := MakeKeyStoreDir(datadir, ctx)
datadir = MustMakeDataDir(ctx) return accounts.NewManager(keydir, scryptN, scryptP)
keystoredir = MakeKeyStoreDir(datadir, ctx)
keystore = crypto.NewKeyStorePassphrase(keystoredir, scryptN, scryptP)
)
return accounts.NewManager(keystore)
} }
// MakeAddress converts an account specified directly as a hex encoded string or // MakeAddress converts an account specified directly as a hex encoded string or

@ -17,8 +17,6 @@
package crypto package crypto
import ( import (
"crypto/aes"
"crypto/cipher"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
@ -30,7 +28,6 @@ import (
"os" "os"
"encoding/hex" "encoding/hex"
"encoding/json"
"errors" "errors"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -38,8 +35,6 @@ import (
"github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/pborman/uuid"
"golang.org/x/crypto/pbkdf2"
"golang.org/x/crypto/ripemd160" "golang.org/x/crypto/ripemd160"
) )
@ -217,107 +212,6 @@ func Decrypt(prv *ecdsa.PrivateKey, ct []byte) ([]byte, error) {
return key.Decrypt(rand.Reader, ct, nil, nil) return key.Decrypt(rand.Reader, ct, nil, nil)
} }
// creates a Key and stores that in the given KeyStore by decrypting a presale key JSON
func ImportPreSaleKey(keyStore KeyStore, keyJSON []byte, password string) (*Key, error) {
key, err := decryptPreSaleKey(keyJSON, password)
if err != nil {
return nil, err
}
key.Id = uuid.NewRandom()
err = keyStore.StoreKey(key, password)
return key, err
}
func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error) {
preSaleKeyStruct := struct {
EncSeed string
EthAddr string
Email string
BtcAddr string
}{}
err = json.Unmarshal(fileContent, &preSaleKeyStruct)
if err != nil {
return nil, err
}
encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed)
iv := encSeedBytes[:16]
cipherText := encSeedBytes[16:]
/*
See https://github.com/ethereum/pyethsaletool
pyethsaletool generates the encryption key from password by
2000 rounds of PBKDF2 with HMAC-SHA-256 using password as salt (:().
16 byte key length within PBKDF2 and resulting key is used as AES key
*/
passBytes := []byte(password)
derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New)
plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
if err != nil {
return nil, err
}
ethPriv := Keccak256(plainText)
ecKey := ToECDSA(ethPriv)
key = &Key{
Id: nil,
Address: PubkeyToAddress(ecKey.PublicKey),
PrivateKey: ecKey,
}
derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x"
expectedAddr := preSaleKeyStruct.EthAddr
if derivedAddr != expectedAddr {
err = fmt.Errorf("decrypted addr '%s' not equal to expected addr '%s'", derivedAddr, expectedAddr)
}
return key, err
}
// AES-128 is selected due to size of encryptKey
func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
aesBlock, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
stream := cipher.NewCTR(aesBlock, iv)
outText := make([]byte, len(inText))
stream.XORKeyStream(outText, inText)
return outText, err
}
func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) {
aesBlock, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
paddedPlaintext := make([]byte, len(cipherText))
decrypter.CryptBlocks(paddedPlaintext, cipherText)
plaintext := PKCS7Unpad(paddedPlaintext)
if plaintext == nil {
err = errors.New("Decryption failed: PKCS7Unpad failed after AES decryption")
}
return plaintext, err
}
// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
func PKCS7Unpad(in []byte) []byte {
if len(in) == 0 {
return nil
}
padding := in[len(in)-1]
if int(padding) > len(in) || padding > aes.BlockSize {
return nil
} else if padding == 0 {
return nil
}
for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
if in[i] != padding {
return nil
}
}
return in[:len(in)-int(padding)]
}
func PubkeyToAddress(p ecdsa.PublicKey) common.Address { func PubkeyToAddress(p ecdsa.PublicKey) common.Address {
pubBytes := FromECDSAPub(&p) pubBytes := FromECDSAPub(&p)
return common.BytesToAddress(Keccak256(pubBytes[1:])[12:]) return common.BytesToAddress(Keccak256(pubBytes[1:])[12:])

@ -4,6 +4,7 @@
package eth package eth
import ( import (
"crypto/ecdsa"
"crypto/rand" "crypto/rand"
"math/big" "math/big"
"sync" "sync"
@ -94,10 +95,9 @@ func (p *testTxPool) GetTransactions() types.Transactions {
} }
// newTestTransaction create a new dummy transaction. // newTestTransaction create a new dummy transaction.
func newTestTransaction(from *crypto.Key, nonce uint64, datasize int) *types.Transaction { func newTestTransaction(from *ecdsa.PrivateKey, nonce uint64, datasize int) *types.Transaction {
tx := types.NewTransaction(nonce, common.Address{}, big.NewInt(0), big.NewInt(100000), big.NewInt(0), make([]byte, datasize)) tx := types.NewTransaction(nonce, common.Address{}, big.NewInt(0), big.NewInt(100000), big.NewInt(0), make([]byte, datasize))
tx, _ = tx.SignECDSA(from.PrivateKey) tx, _ = tx.SignECDSA(from)
return tx return tx
} }

@ -17,7 +17,6 @@
package eth package eth
import ( import (
"crypto/rand"
"fmt" "fmt"
"sync" "sync"
"testing" "testing"
@ -35,7 +34,7 @@ func init() {
// glog.SetV(6) // glog.SetV(6)
} }
var testAccount = crypto.NewKey(rand.Reader) var testAccount, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
// Tests that handshake failures are detected and reported correctly. // Tests that handshake failures are detected and reported correctly.
func TestStatusMsgErrors61(t *testing.T) { testStatusMsgErrors(t, 61) } func TestStatusMsgErrors61(t *testing.T) { testStatusMsgErrors(t, 61) }

Loading…
Cancel
Save