forked from cerc-io/plugeth
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.
This commit is contained in:
parent
dff9b4246f
commit
85e6c40c00
@ -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{
|
return &Manager{
|
||||||
keyStore: keyStore,
|
keyStore: newKeyStorePassphrase(keydir, scryptN, scryptP),
|
||||||
|
unlocked: make(map[common.Address]*unlocked),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlaintextManager(keydir string) *Manager {
|
||||||
|
return &Manager{
|
||||||
|
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))
|
132
accounts/presale.go
Normal file
132
accounts/presale.go
Normal file
@ -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 := ð.Config{
|
ethConf := ð.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
|
||||||
|
106
crypto/crypto.go
106
crypto/crypto.go
@ -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…
Reference in New Issue
Block a user