Merge pull request #631 from Gustav-Simonsson/improve_key_store_crypto

Improve key store crypto
This commit is contained in:
Jeffrey Wilcke 2015-05-12 10:00:35 -07:00
commit d6357aa616
22 changed files with 245 additions and 200 deletions

View File

@ -33,7 +33,6 @@ and accounts persistence is derived from stored keys' addresses
package accounts package accounts
import ( import (
"bytes"
"crypto/ecdsa" "crypto/ecdsa"
crand "crypto/rand" crand "crypto/rand"
"errors" "errors"
@ -41,6 +40,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
) )
@ -50,12 +50,12 @@ var (
) )
type Account struct { type Account struct {
Address []byte Address common.Address
} }
type Manager struct { type Manager struct {
keyStore crypto.KeyStore2 keyStore crypto.KeyStore2
unlocked map[string]*unlocked unlocked map[common.Address]*unlocked
mutex sync.RWMutex mutex sync.RWMutex
} }
@ -67,40 +67,40 @@ type unlocked struct {
func NewManager(keyStore crypto.KeyStore2) *Manager { func NewManager(keyStore crypto.KeyStore2) *Manager {
return &Manager{ return &Manager{
keyStore: keyStore, keyStore: keyStore,
unlocked: make(map[string]*unlocked), unlocked: make(map[common.Address]*unlocked),
} }
} }
func (am *Manager) HasAccount(addr []byte) bool { func (am *Manager) HasAccount(addr common.Address) bool {
accounts, _ := am.Accounts() accounts, _ := am.Accounts()
for _, acct := range accounts { for _, acct := range accounts {
if bytes.Compare(acct.Address, addr) == 0 { if acct.Address == addr {
return true return true
} }
} }
return false return false
} }
func (am *Manager) Primary() (addr []byte, err error) { func (am *Manager) Primary() (addr common.Address, err error) {
addrs, err := am.keyStore.GetKeyAddresses() addrs, err := am.keyStore.GetKeyAddresses()
if os.IsNotExist(err) { if os.IsNotExist(err) {
return nil, ErrNoKeys return common.Address{}, ErrNoKeys
} else if err != nil { } else if err != nil {
return nil, err return common.Address{}, err
} }
if len(addrs) == 0 { if len(addrs) == 0 {
return nil, ErrNoKeys return common.Address{}, ErrNoKeys
} }
return addrs[0], nil return addrs[0], nil
} }
func (am *Manager) DeleteAccount(address []byte, auth string) error { func (am *Manager) DeleteAccount(address common.Address, auth string) error {
return am.keyStore.DeleteKey(address, auth) return am.keyStore.DeleteKey(address, auth)
} }
func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) { func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) {
am.mutex.RLock() am.mutex.RLock()
unlockedKey, found := am.unlocked[string(a.Address)] unlockedKey, found := am.unlocked[a.Address]
am.mutex.RUnlock() am.mutex.RUnlock()
if !found { if !found {
return nil, ErrLocked return nil, ErrLocked
@ -111,7 +111,7 @@ func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error)
// TimedUnlock unlocks the account with the given address. // TimedUnlock unlocks the account with the given address.
// When timeout has passed, the account will be locked again. // When timeout has passed, the account will be locked again.
func (am *Manager) TimedUnlock(addr []byte, keyAuth string, timeout time.Duration) error { func (am *Manager) TimedUnlock(addr common.Address, keyAuth string, timeout time.Duration) error {
key, err := am.keyStore.GetKey(addr, keyAuth) key, err := am.keyStore.GetKey(addr, keyAuth)
if err != nil { if err != nil {
return err return err
@ -124,7 +124,7 @@ func (am *Manager) TimedUnlock(addr []byte, keyAuth string, timeout time.Duratio
// Unlock unlocks the account with the given address. The account // Unlock unlocks the account with the given address. The account
// stays unlocked until the program exits or until a TimedUnlock // stays unlocked until the program exits or until a TimedUnlock
// timeout (started after the call to Unlock) expires. // timeout (started after the call to Unlock) expires.
func (am *Manager) Unlock(addr []byte, keyAuth string) error { func (am *Manager) Unlock(addr common.Address, keyAuth string) error {
key, err := am.keyStore.GetKey(addr, keyAuth) key, err := am.keyStore.GetKey(addr, keyAuth)
if err != nil { if err != nil {
return err return err
@ -157,10 +157,10 @@ func (am *Manager) Accounts() ([]Account, error) {
return accounts, err return accounts, err
} }
func (am *Manager) addUnlocked(addr []byte, key *crypto.Key) *unlocked { func (am *Manager) addUnlocked(addr common.Address, key *crypto.Key) *unlocked {
u := &unlocked{Key: key, abort: make(chan struct{})} u := &unlocked{Key: key, abort: make(chan struct{})}
am.mutex.Lock() am.mutex.Lock()
prev, found := am.unlocked[string(addr)] prev, found := am.unlocked[addr]
if found { if found {
// terminate dropLater for this key to avoid unexpected drops. // terminate dropLater for this key to avoid unexpected drops.
close(prev.abort) close(prev.abort)
@ -169,12 +169,12 @@ func (am *Manager) addUnlocked(addr []byte, key *crypto.Key) *unlocked {
// key, i.e. when Unlock was used. // key, i.e. when Unlock was used.
zeroKey(prev.PrivateKey) zeroKey(prev.PrivateKey)
} }
am.unlocked[string(addr)] = u am.unlocked[addr] = u
am.mutex.Unlock() am.mutex.Unlock()
return u return u
} }
func (am *Manager) dropLater(addr []byte, u *unlocked, timeout time.Duration) { func (am *Manager) dropLater(addr common.Address, u *unlocked, timeout time.Duration) {
t := time.NewTimer(timeout) t := time.NewTimer(timeout)
defer t.Stop() defer t.Stop()
select { select {
@ -186,9 +186,9 @@ func (am *Manager) dropLater(addr []byte, u *unlocked, timeout time.Duration) {
// was launched with. we can check that using pointer equality // was launched with. we can check that using pointer equality
// because the map stores a new pointer every time the key is // because the map stores a new pointer every time the key is
// unlocked. // unlocked.
if am.unlocked[string(addr)] == u { if am.unlocked[addr] == u {
zeroKey(u.PrivateKey) zeroKey(u.PrivateKey)
delete(am.unlocked, string(addr)) delete(am.unlocked, addr)
} }
am.mutex.Unlock() am.mutex.Unlock()
} }
@ -204,7 +204,7 @@ func zeroKey(k *ecdsa.PrivateKey) {
// USE WITH CAUTION = this will save an unencrypted private key on disk // USE WITH CAUTION = this will save an unencrypted private key on disk
// no cli or js interface // no cli or js interface
func (am *Manager) Export(path string, addr []byte, keyAuth string) error { func (am *Manager) Export(path string, addr common.Address, keyAuth string) error {
key, err := am.keyStore.GetKey(addr, keyAuth) key, err := am.keyStore.GetKey(addr, keyAuth)
if err != nil { if err != nil {
return err return err

View File

@ -126,7 +126,7 @@ func (js *jsre) pendingTransactions(call otto.FunctionCall) otto.Value {
// Add the accouns to a new set // Add the accouns to a new set
accountSet := set.New() accountSet := set.New()
for _, account := range accounts { for _, account := range accounts {
accountSet.Add(common.BytesToAddress(account.Address)) accountSet.Add(account.Address)
} }
//ltxs := make([]*tx, len(txs)) //ltxs := make([]*tx, len(txs))
@ -391,7 +391,7 @@ func (js *jsre) unlock(call otto.FunctionCall) otto.Value {
} }
} }
am := js.ethereum.AccountManager() am := js.ethereum.AccountManager()
err = am.TimedUnlock(common.FromHex(addr), passphrase, time.Duration(seconds)*time.Second) err = am.TimedUnlock(common.HexToAddress(addr), passphrase, time.Duration(seconds)*time.Second)
if err != nil { if err != nil {
fmt.Printf("Unlock account failed '%v'\n", err) fmt.Printf("Unlock account failed '%v'\n", err)
return otto.FalseValue() return otto.FalseValue()
@ -433,7 +433,7 @@ func (js *jsre) newAccount(call otto.FunctionCall) otto.Value {
fmt.Printf("Could not create the account: %v", err) fmt.Printf("Could not create the account: %v", err)
return otto.UndefinedValue() return otto.UndefinedValue()
} }
return js.re.ToVal(common.ToHex(acct.Address)) return js.re.ToVal(acct.Address.Hex())
} }
func (js *jsre) nodeInfo(call otto.FunctionCall) otto.Value { func (js *jsre) nodeInfo(call otto.FunctionCall) otto.Value {

View File

@ -26,6 +26,7 @@ import (
"strings" "strings"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/docserver" "github.com/ethereum/go-ethereum/common/docserver"
"github.com/ethereum/go-ethereum/common/natspec" "github.com/ethereum/go-ethereum/common/natspec"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
@ -164,7 +165,7 @@ func (self *jsre) UnlockAccount(addr []byte) bool {
return false return false
} }
// TODO: allow retry // TODO: allow retry
if err := self.ethereum.AccountManager().Unlock(addr, pass); err != nil { if err := self.ethereum.AccountManager().Unlock(common.BytesToAddress(addr), pass); err != nil {
return false return false
} else { } else {
fmt.Println("Account is now unlocked for this session.") fmt.Println("Account is now unlocked for this session.")

View File

@ -45,7 +45,7 @@ type testjethre struct {
} }
func (self *testjethre) UnlockAccount(acc []byte) bool { func (self *testjethre) UnlockAccount(acc []byte) bool {
err := self.ethereum.AccountManager().Unlock(acc, "") err := self.ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "")
if err != nil { if err != nil {
panic("unable to unlock") panic("unable to unlock")
} }
@ -68,7 +68,7 @@ func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) {
// set up mock genesis with balance on the testAddress // set up mock genesis with balance on the testAddress
core.GenesisData = []byte(testGenesis) core.GenesisData = []byte(testGenesis)
ks := crypto.NewKeyStorePassphrase(filepath.Join(tmp, "keys")) ks := crypto.NewKeyStorePassphrase(filepath.Join(tmp, "keystore"))
am := accounts.NewManager(ks) am := accounts.NewManager(ks)
ethereum, err := eth.New(&eth.Config{ ethereum, err := eth.New(&eth.Config{
DataDir: tmp, DataDir: tmp,

View File

@ -365,11 +365,10 @@ func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (pass
// Load startup keys. XXX we are going to need a different format // Load startup keys. XXX we are going to need a different format
// Attempt to unlock the account // Attempt to unlock the account
passphrase = getPassPhrase(ctx, "", false) passphrase = getPassPhrase(ctx, "", false)
accbytes := common.FromHex(account) if len(account) == 0 {
if len(accbytes) == 0 {
utils.Fatalf("Invalid account address '%s'", account) utils.Fatalf("Invalid account address '%s'", account)
} }
err = am.Unlock(accbytes, passphrase) err = am.Unlock(common.StringToAddress(account), passphrase)
if err != nil { if err != nil {
utils.Fatalf("Unlock account failed '%v'", err) utils.Fatalf("Unlock account failed '%v'", err)
} }
@ -385,11 +384,11 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) {
account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
if len(account) > 0 { if len(account) > 0 {
if account == "primary" { if account == "primary" {
accbytes, err := am.Primary() primaryAcc, err := am.Primary()
if err != nil { if err != nil {
utils.Fatalf("no primary account: %v", err) utils.Fatalf("no primary account: %v", err)
} }
account = common.ToHex(accbytes) account = primaryAcc.Hex()
} }
unlockAccount(ctx, am, account) unlockAccount(ctx, am, account)
} }

View File

@ -232,7 +232,7 @@ func (self *Gui) loadMergedMiningOptions() {
func (gui *Gui) insertTransaction(window string, tx *types.Transaction) { func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
var inout string var inout string
from, _ := tx.From() from, _ := tx.From()
if gui.eth.AccountManager().HasAccount(common.Hex2Bytes(from.Hex())) { if gui.eth.AccountManager().HasAccount(from) {
inout = "send" inout = "send"
} else { } else {
inout = "recv" inout = "recv"

View File

@ -346,7 +346,7 @@ func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Dat
func GetAccountManager(ctx *cli.Context) *accounts.Manager { func GetAccountManager(ctx *cli.Context) *accounts.Manager {
dataDir := ctx.GlobalString(DataDirFlag.Name) dataDir := ctx.GlobalString(DataDirFlag.Name)
ks := crypto.NewKeyStorePassphrase(filepath.Join(dataDir, "keys")) ks := crypto.NewKeyStorePassphrase(filepath.Join(dataDir, "keystore"))
return accounts.NewManager(ks) return accounts.NewManager(ks)
} }

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"strings"
"testing" "testing"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
@ -84,7 +85,7 @@ type testFrontend struct {
} }
func (self *testFrontend) UnlockAccount(acc []byte) bool { func (self *testFrontend) UnlockAccount(acc []byte) bool {
self.ethereum.AccountManager().Unlock(acc, "password") self.ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "password")
return true return true
} }
@ -103,19 +104,19 @@ func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) {
os.RemoveAll("/tmp/eth-natspec/") os.RemoveAll("/tmp/eth-natspec/")
err = os.MkdirAll("/tmp/eth-natspec/keys", os.ModePerm) err = os.MkdirAll("/tmp/eth-natspec/keystore", os.ModePerm)
if err != nil { if err != nil {
panic(err) panic(err)
} }
// create a testAddress // create a testAddress
ks := crypto.NewKeyStorePassphrase("/tmp/eth-natspec/keys") ks := crypto.NewKeyStorePassphrase("/tmp/eth-natspec/keystore")
am := accounts.NewManager(ks) am := accounts.NewManager(ks)
testAccount, err := am.NewAccount("password") testAccount, err := am.NewAccount("password")
if err != nil { if err != nil {
panic(err) panic(err)
} }
testAddress := common.Bytes2Hex(testAccount.Address) testAddress := strings.TrimPrefix(testAccount.Address.Hex(), "0x")
// set up mock genesis with balance on the testAddress // set up mock genesis with balance on the testAddress
core.GenesisData = []byte(`{ core.GenesisData = []byte(`{

View File

@ -181,11 +181,11 @@ func Decrypt(prv *ecdsa.PrivateKey, ct []byte) ([]byte, error) {
// Used only by block tests. // Used only by block tests.
func ImportBlockTestKey(privKeyBytes []byte) error { func ImportBlockTestKey(privKeyBytes []byte) error {
ks := NewKeyStorePassphrase(common.DefaultDataDir() + "/keys") ks := NewKeyStorePassphrase(common.DefaultDataDir() + "/keystore")
ecKey := ToECDSA(privKeyBytes) ecKey := ToECDSA(privKeyBytes)
key := &Key{ key := &Key{
Id: uuid.NewRandom(), Id: uuid.NewRandom(),
Address: PubkeyToAddress(ecKey.PublicKey), Address: common.BytesToAddress(PubkeyToAddress(ecKey.PublicKey)),
PrivateKey: ecKey, PrivateKey: ecKey,
} }
err := ks.StoreKey(key, "") err := ks.StoreKey(key, "")
@ -231,13 +231,13 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
ecKey := ToECDSA(ethPriv) ecKey := ToECDSA(ethPriv)
key = &Key{ key = &Key{
Id: nil, Id: nil,
Address: PubkeyToAddress(ecKey.PublicKey), Address: common.BytesToAddress(PubkeyToAddress(ecKey.PublicKey)),
PrivateKey: ecKey, PrivateKey: ecKey,
} }
derivedAddr := common.Bytes2Hex(key.Address) derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x"
expectedAddr := preSaleKeyStruct.EthAddr expectedAddr := preSaleKeyStruct.EthAddr
if derivedAddr != expectedAddr { if derivedAddr != expectedAddr {
err = errors.New("decrypted addr not equal to expected addr") err = errors.New(fmt.Sprintf("decrypted addr not equal to expected addr ", derivedAddr, expectedAddr))
} }
return key, err return key, err
} }
@ -252,7 +252,7 @@ func aesCBCDecrypt(key []byte, cipherText []byte, iv []byte) (plainText []byte,
decrypter.CryptBlocks(paddedPlainText, cipherText) decrypter.CryptBlocks(paddedPlainText, cipherText)
plainText = PKCS7Unpad(paddedPlainText) plainText = PKCS7Unpad(paddedPlainText)
if plainText == nil { if plainText == nil {
err = errors.New("Decryption failed: PKCS7Unpad failed after decryption") err = errors.New("Decryption failed: PKCS7Unpad failed after AES decryption")
} }
return plainText, err return plainText, err
} }

View File

@ -26,44 +26,69 @@ package crypto
import ( import (
"bytes" "bytes"
"crypto/ecdsa" "crypto/ecdsa"
"encoding/hex"
"encoding/json" "encoding/json"
"io" "io"
"code.google.com/p/go-uuid/uuid" "code.google.com/p/go-uuid/uuid"
"github.com/ethereum/go-ethereum/common"
)
const (
version = "1"
) )
type Key struct { type Key struct {
Id uuid.UUID // Version 4 "random" for unique id not derived from key data Id uuid.UUID // Version 4 "random" for unique id not derived from key data
// to simplify lookups we also store the address // to simplify lookups we also store the address
Address []byte Address common.Address
// we only store privkey as pubkey/address can be derived from it // we only store privkey as pubkey/address can be derived from it
// privkey in this struct is always in plaintext // privkey in this struct is always in plaintext
PrivateKey *ecdsa.PrivateKey PrivateKey *ecdsa.PrivateKey
} }
type plainKeyJSON struct { type plainKeyJSON struct {
Id []byte Address string `json:"address"`
Address []byte PrivateKey string `json:"privatekey"`
PrivateKey []byte Id string `json:"id"`
} Version string `json:"version"`
type cipherJSON struct {
Salt []byte
IV []byte
CipherText []byte
} }
type encryptedKeyJSON struct { type encryptedKeyJSON struct {
Id []byte Address string `json:"address"`
Address []byte Crypto cryptoJSON
Crypto cipherJSON Id string `json:"id"`
Version string `json:"version"`
}
type cryptoJSON struct {
Cipher string `json:"cipher"`
CipherText string `json:"ciphertext"`
CipherParams cipherparamsJSON `json:"cipherparams"`
KDF string `json:"kdf"`
KDFParams scryptParamsJSON `json:"kdfparams"`
MAC string `json:"mac"`
Version string `json:"version"`
}
type cipherparamsJSON struct {
IV string `json:"iv"`
}
type scryptParamsJSON struct {
N int `json:"n"`
R int `json:"r"`
P int `json:"p"`
DkLen int `json:"dklen"`
Salt string `json:"salt"`
} }
func (k *Key) MarshalJSON() (j []byte, err error) { func (k *Key) MarshalJSON() (j []byte, err error) {
jStruct := plainKeyJSON{ jStruct := plainKeyJSON{
k.Id, hex.EncodeToString(k.Address[:]),
k.Address, hex.EncodeToString(FromECDSA(k.PrivateKey)),
FromECDSA(k.PrivateKey), k.Id.String(),
version,
} }
j, err = json.Marshal(jStruct) j, err = json.Marshal(jStruct)
return j, err return j, err
@ -77,19 +102,29 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) {
} }
u := new(uuid.UUID) u := new(uuid.UUID)
*u = keyJSON.Id *u = uuid.Parse(keyJSON.Id)
k.Id = *u k.Id = *u
k.Address = keyJSON.Address addr, err := hex.DecodeString(keyJSON.Address)
k.PrivateKey = ToECDSA(keyJSON.PrivateKey) if err != nil {
return err
}
return err privkey, err := hex.DecodeString(keyJSON.PrivateKey)
if err != nil {
return err
}
k.Address = common.BytesToAddress(addr)
k.PrivateKey = ToECDSA(privkey)
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: common.BytesToAddress(PubkeyToAddress(privateKeyECDSA.PublicKey)),
PrivateKey: privateKeyECDSA, PrivateKey: privateKeyECDSA,
} }
return key return key

View File

@ -28,24 +28,25 @@ the private key is encrypted and on disk uses another JSON encoding.
Cryptography: Cryptography:
1. Encryption key is scrypt derived key from user passphrase. Scrypt parameters 1. Encryption key is first 16 bytes of SHA3-256 of first 16 bytes of
scrypt derived key from user passphrase. Scrypt parameters
(work factors) [1][2] are defined as constants below. (work factors) [1][2] are defined as constants below.
2. Scrypt salt is 32 random bytes from CSPRNG. It is appended to ciphertext. 2. Scrypt salt is 32 random bytes from CSPRNG.
3. Checksum is SHA3 of the private key bytes. It's stored in plain next to ciphertext in key file.
4. Plaintext is concatenation of private key bytes and checksum. 3. MAC is SHA3-256 of concatenation of ciphertext and last 16 bytes of scrypt derived key.
5. Encryption algo is AES 256 CBC [3][4] 4. Plaintext is the EC private key bytes.
6. CBC IV is 16 random bytes from CSPRNG. It is appended to ciphertext. 5. Encryption algo is AES 128 CBC [3][4]
6. CBC IV is 16 random bytes from CSPRNG.
It's stored in plain next to ciphertext in key file.
7. Plaintext padding is PKCS #7 [5][6] 7. Plaintext padding is PKCS #7 [5][6]
Encoding: Encoding:
1. On disk, ciphertext, salt and IV are encoded in a nested JSON object. 1. On disk, the ciphertext, MAC, salt and IV are encoded in a nested JSON object.
cat a key file to see the structure. cat a key file to see the structure.
2. byte arrays are base64 JSON strings. 2. byte arrays are base64 JSON strings.
3. The EC private key bytes are in uncompressed form [7]. 3. The EC private key bytes are in uncompressed form [7].
They are a big-endian byte slice of the absolute value of D [8][9]. They are a big-endian byte slice of the absolute value of D [8][9].
4. The checksum is the last 32 bytes of the plaintext byte array and the
private key is the preceeding bytes.
References: References:
@ -75,11 +76,14 @@ import (
"path/filepath" "path/filepath"
"code.google.com/p/go-uuid/uuid" "code.google.com/p/go-uuid/uuid"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/randentropy" "github.com/ethereum/go-ethereum/crypto/randentropy"
"golang.org/x/crypto/scrypt" "golang.org/x/crypto/scrypt"
) )
const ( const (
keyHeaderVersion = "1"
keyHeaderKDF = "scrypt"
// 2^18 / 8 / 1 uses 256MB memory and approx 1s CPU time on a modern CPU. // 2^18 / 8 / 1 uses 256MB memory and approx 1s CPU time on a modern CPU.
scryptN = 1 << 18 scryptN = 1 << 18
scryptr = 8 scryptr = 8
@ -99,7 +103,7 @@ func (ks keyStorePassphrase) GenerateNewKey(rand io.Reader, auth string) (key *K
return GenerateNewKeyDefault(ks, rand, auth) return GenerateNewKeyDefault(ks, rand, auth)
} }
func (ks keyStorePassphrase) GetKey(keyAddr []byte, auth string) (key *Key, err error) { func (ks keyStorePassphrase) GetKey(keyAddr common.Address, auth string) (key *Key, err error) {
keyBytes, keyId, err := DecryptKey(ks, keyAddr, auth) keyBytes, keyId, err := DecryptKey(ks, keyAddr, auth)
if err != nil { if err != nil {
return nil, err return nil, err
@ -112,43 +116,63 @@ func (ks keyStorePassphrase) GetKey(keyAddr []byte, auth string) (key *Key, err
return key, err return key, err
} }
func (ks keyStorePassphrase) GetKeyAddresses() (addresses [][]byte, err error) { func (ks keyStorePassphrase) GetKeyAddresses() (addresses []common.Address, err error) {
return GetKeyAddresses(ks.keysDirPath) return GetKeyAddresses(ks.keysDirPath)
} }
func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) { func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
authArray := []byte(auth) authArray := []byte(auth)
salt := randentropy.GetEntropyMixed(32) salt := randentropy.GetEntropyCSPRNG(32)
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen)
if err != nil { if err != nil {
return err return err
} }
keyBytes := FromECDSA(key.PrivateKey) encryptKey := Sha3(derivedKey[:16])[:16]
keyBytesHash := Sha3(keyBytes)
toEncrypt := PKCS7Pad(append(keyBytes, keyBytesHash...))
AES256Block, err := aes.NewCipher(derivedKey) keyBytes := FromECDSA(key.PrivateKey)
toEncrypt := PKCS7Pad(keyBytes)
AES128Block, err := aes.NewCipher(encryptKey)
if err != nil { if err != nil {
return err return err
} }
iv := randentropy.GetEntropyMixed(aes.BlockSize) // 16 iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16
AES256CBCEncrypter := cipher.NewCBCEncrypter(AES256Block, iv) AES128CBCEncrypter := cipher.NewCBCEncrypter(AES128Block, iv)
cipherText := make([]byte, len(toEncrypt)) cipherText := make([]byte, len(toEncrypt))
AES256CBCEncrypter.CryptBlocks(cipherText, toEncrypt) AES128CBCEncrypter.CryptBlocks(cipherText, toEncrypt)
cipherStruct := cipherJSON{ mac := Sha3(derivedKey[16:32], cipherText)
salt,
iv, scryptParamsJSON := scryptParamsJSON{
cipherText, N: scryptN,
R: scryptr,
P: scryptp,
DkLen: scryptdkLen,
Salt: hex.EncodeToString(salt),
} }
keyStruct := encryptedKeyJSON{
key.Id, cipherParamsJSON := cipherparamsJSON{
key.Address, IV: hex.EncodeToString(iv),
cipherStruct,
} }
keyJSON, err := json.Marshal(keyStruct)
cryptoStruct := cryptoJSON{
Cipher: "aes-128-cbc",
CipherText: hex.EncodeToString(cipherText),
CipherParams: cipherParamsJSON,
KDF: "scrypt",
KDFParams: scryptParamsJSON,
MAC: hex.EncodeToString(mac),
Version: "1",
}
encryptedKeyJSON := encryptedKeyJSON{
hex.EncodeToString(key.Address[:]),
cryptoStruct,
key.Id.String(),
version,
}
keyJSON, err := json.Marshal(encryptedKeyJSON)
if err != nil { if err != nil {
return err return err
} }
@ -156,18 +180,18 @@ func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
return WriteKeyFile(key.Address, ks.keysDirPath, keyJSON) return WriteKeyFile(key.Address, ks.keysDirPath, keyJSON)
} }
func (ks keyStorePassphrase) DeleteKey(keyAddr []byte, auth string) (err error) { func (ks keyStorePassphrase) DeleteKey(keyAddr common.Address, auth string) (err error) {
// only delete if correct passphrase is given // only delete if correct passphrase is given
_, _, err = DecryptKey(ks, keyAddr, auth) _, _, err = DecryptKey(ks, keyAddr, auth)
if err != nil { if err != nil {
return err return err
} }
keyDirPath := filepath.Join(ks.keysDirPath, hex.EncodeToString(keyAddr)) keyDirPath := filepath.Join(ks.keysDirPath, hex.EncodeToString(keyAddr[:]))
return os.RemoveAll(keyDirPath) return os.RemoveAll(keyDirPath)
} }
func DecryptKey(ks keyStorePassphrase, keyAddr []byte, auth string) (keyBytes []byte, keyId []byte, err error) { func DecryptKey(ks keyStorePassphrase, keyAddr common.Address, auth string) (keyBytes []byte, keyId []byte, err error) {
fileContent, err := GetKeyFile(ks.keysDirPath, keyAddr) fileContent, err := GetKeyFile(ks.keysDirPath, keyAddr)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -176,25 +200,48 @@ func DecryptKey(ks keyStorePassphrase, keyAddr []byte, auth string) (keyBytes []
keyProtected := new(encryptedKeyJSON) keyProtected := new(encryptedKeyJSON)
err = json.Unmarshal(fileContent, keyProtected) err = json.Unmarshal(fileContent, keyProtected)
keyId = keyProtected.Id keyId = uuid.Parse(keyProtected.Id)
salt := keyProtected.Crypto.Salt
iv := keyProtected.Crypto.IV mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
cipherText := keyProtected.Crypto.CipherText if err != nil {
return nil, nil, err
}
iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
if err != nil {
return nil, nil, err
}
cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
if err != nil {
return nil, nil, err
}
salt, err := hex.DecodeString(keyProtected.Crypto.KDFParams.Salt)
if err != nil {
return nil, nil, err
}
n := keyProtected.Crypto.KDFParams.N
r := keyProtected.Crypto.KDFParams.R
p := keyProtected.Crypto.KDFParams.P
dkLen := keyProtected.Crypto.KDFParams.DkLen
authArray := []byte(auth) authArray := []byte(auth)
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) derivedKey, err := scrypt.Key(authArray, salt, n, r, p, dkLen)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
calculatedMAC := Sha3(derivedKey[16:32], cipherText)
if !bytes.Equal(calculatedMAC, mac) {
err = errors.New("Decryption failed: MAC mismatch")
return nil, nil, err
}
plainText, err := aesCBCDecrypt(Sha3(derivedKey[:16])[:16], cipherText, iv)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
keyBytes = plainText[:len(plainText)-32] return plainText, keyId, err
keyBytesHash := plainText[len(plainText)-32:]
if !bytes.Equal(Sha3(keyBytes), keyBytesHash) {
err = errors.New("Decryption failed: checksum mismatch")
return nil, nil, err
}
return keyBytes, keyId, err
} }

View File

@ -27,6 +27,7 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
@ -37,10 +38,10 @@ import (
type KeyStore2 interface { type KeyStore2 interface {
// create new key using io.Reader entropy source and optionally using auth string // create new key using io.Reader entropy source and optionally using auth string
GenerateNewKey(io.Reader, string) (*Key, error) GenerateNewKey(io.Reader, string) (*Key, error)
GetKey([]byte, string) (*Key, error) // key from addr and auth string GetKey(common.Address, string) (*Key, error) // key from addr and auth string
GetKeyAddresses() ([][]byte, error) // get all addresses GetKeyAddresses() ([]common.Address, error) // get all addresses
StoreKey(*Key, string) error // store key optionally using auth string StoreKey(*Key, string) error // store key optionally using auth string
DeleteKey([]byte, string) error // delete key by addr and auth string DeleteKey(common.Address, string) error // delete key by addr and auth string
} }
type keyStorePlain struct { type keyStorePlain struct {
@ -66,7 +67,7 @@ func GenerateNewKeyDefault(ks KeyStore2, rand io.Reader, auth string) (key *Key,
return key, err return key, err
} }
func (ks keyStorePlain) GetKey(keyAddr []byte, auth string) (key *Key, err error) { func (ks keyStorePlain) GetKey(keyAddr common.Address, auth string) (key *Key, err error) {
fileContent, err := GetKeyFile(ks.keysDirPath, keyAddr) fileContent, err := GetKeyFile(ks.keysDirPath, keyAddr)
if err != nil { if err != nil {
return nil, err return nil, err
@ -77,7 +78,7 @@ func (ks keyStorePlain) GetKey(keyAddr []byte, auth string) (key *Key, err error
return key, err return key, err
} }
func (ks keyStorePlain) GetKeyAddresses() (addresses [][]byte, err error) { func (ks keyStorePlain) GetKeyAddresses() (addresses []common.Address, err error) {
return GetKeyAddresses(ks.keysDirPath) return GetKeyAddresses(ks.keysDirPath)
} }
@ -90,19 +91,19 @@ func (ks keyStorePlain) StoreKey(key *Key, auth string) (err error) {
return err return err
} }
func (ks keyStorePlain) DeleteKey(keyAddr []byte, auth string) (err error) { func (ks keyStorePlain) DeleteKey(keyAddr common.Address, auth string) (err error) {
keyDirPath := filepath.Join(ks.keysDirPath, hex.EncodeToString(keyAddr)) keyDirPath := filepath.Join(ks.keysDirPath, keyAddr.Hex())
err = os.RemoveAll(keyDirPath) err = os.RemoveAll(keyDirPath)
return err return err
} }
func GetKeyFile(keysDirPath string, keyAddr []byte) (fileContent []byte, err error) { func GetKeyFile(keysDirPath string, keyAddr common.Address) (fileContent []byte, err error) {
fileName := hex.EncodeToString(keyAddr) fileName := hex.EncodeToString(keyAddr[:])
return ioutil.ReadFile(filepath.Join(keysDirPath, fileName, fileName)) return ioutil.ReadFile(filepath.Join(keysDirPath, fileName, fileName))
} }
func WriteKeyFile(addr []byte, keysDirPath string, content []byte) (err error) { func WriteKeyFile(addr common.Address, keysDirPath string, content []byte) (err error) {
addrHex := hex.EncodeToString(addr) addrHex := hex.EncodeToString(addr[:])
keyDirPath := filepath.Join(keysDirPath, addrHex) keyDirPath := filepath.Join(keysDirPath, addrHex)
keyFilePath := filepath.Join(keyDirPath, addrHex) keyFilePath := filepath.Join(keyDirPath, addrHex)
err = os.MkdirAll(keyDirPath, 0700) // read, write and dir search for user err = os.MkdirAll(keyDirPath, 0700) // read, write and dir search for user
@ -112,7 +113,7 @@ func WriteKeyFile(addr []byte, keysDirPath string, content []byte) (err error) {
return ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user return ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user
} }
func GetKeyAddresses(keysDirPath string) (addresses [][]byte, err error) { func GetKeyAddresses(keysDirPath string) (addresses []common.Address, err error) {
fileInfos, err := ioutil.ReadDir(keysDirPath) fileInfos, err := ioutil.ReadDir(keysDirPath)
if err != nil { if err != nil {
return nil, err return nil, err
@ -122,7 +123,7 @@ func GetKeyAddresses(keysDirPath string) (addresses [][]byte, err error) {
if err != nil { if err != nil {
continue continue
} }
addresses = append(addresses, address) addresses = append(addresses, common.BytesToAddress(address))
} }
return addresses, err return addresses, err
} }

View File

@ -1,8 +1,8 @@
package crypto package crypto
import ( import (
"github.com/ethereum/go-ethereum/crypto/randentropy"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/randentropy"
"reflect" "reflect"
"testing" "testing"
) )

View File

@ -2,12 +2,8 @@ package randentropy
import ( import (
crand "crypto/rand" crand "crypto/rand"
"encoding/binary"
"github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/crypto/sha3"
"io" "io"
"os"
"strings"
"time"
) )
var Reader io.Reader = &randEntropy{} var Reader io.Reader = &randEntropy{}
@ -16,7 +12,7 @@ type randEntropy struct {
} }
func (*randEntropy) Read(bytes []byte) (n int, err error) { func (*randEntropy) Read(bytes []byte) (n int, err error) {
readBytes := GetEntropyMixed(len(bytes)) readBytes := GetEntropyCSPRNG(len(bytes))
copy(bytes, readBytes) copy(bytes, readBytes)
return len(bytes), nil return len(bytes), nil
} }
@ -29,40 +25,6 @@ func Sha3(data []byte) []byte {
return d.Sum(nil) return d.Sum(nil)
} }
// TODO: verify. this needs to be audited
// we start with crypt/rand, then XOR in additional entropy from OS
func GetEntropyMixed(n int) []byte {
startTime := time.Now().UnixNano()
// for each source, we take SHA3 of the source and use it as seed to math/rand
// then read bytes from it and XOR them onto the bytes read from crypto/rand
mainBuff := GetEntropyCSPRNG(n)
// 1. OS entropy sources
startTimeBytes := make([]byte, 32)
binary.PutVarint(startTimeBytes, startTime)
startTimeHash := Sha3(startTimeBytes)
mixBytes(mainBuff, startTimeHash)
pid := os.Getpid()
pidBytes := make([]byte, 32)
binary.PutUvarint(pidBytes, uint64(pid))
pidHash := Sha3(pidBytes)
mixBytes(mainBuff, pidHash)
osEnv := os.Environ()
osEnvBytes := []byte(strings.Join(osEnv, ""))
osEnvHash := Sha3(osEnvBytes)
mixBytes(mainBuff, osEnvHash)
// not all OS have hostname in env variables
osHostName, err := os.Hostname()
if err != nil {
osHostNameBytes := []byte(osHostName)
osHostNameHash := Sha3(osHostNameBytes)
mixBytes(mainBuff, osHostNameHash)
}
return mainBuff
}
func GetEntropyCSPRNG(n int) []byte { func GetEntropyCSPRNG(n int) []byte {
mainBuff := make([]byte, n) mainBuff := make([]byte, n)
_, err := io.ReadFull(crand.Reader, mainBuff) _, err := io.ReadFull(crand.Reader, mainBuff)
@ -71,14 +33,3 @@ func GetEntropyCSPRNG(n int) []byte {
} }
return mainBuff return mainBuff
} }
func mixBytes(buff []byte, mixBuff []byte) []byte {
bytesToMix := len(buff)
if bytesToMix > 32 {
bytesToMix = 32
}
for i := 0; i < bytesToMix; i++ {
buff[i] ^= mixBuff[i]
}
return buff
}

View File

@ -59,7 +59,7 @@ func GenerateKeyPair() ([]byte, []byte) {
const seckey_len = 32 const seckey_len = 32
var pubkey []byte = make([]byte, pubkey_len) var pubkey []byte = make([]byte, pubkey_len)
var seckey []byte = randentropy.GetEntropyMixed(seckey_len) var seckey []byte = randentropy.GetEntropyCSPRNG(seckey_len)
var pubkey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&pubkey[0])) var pubkey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&pubkey[0]))
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0])) var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
@ -99,7 +99,7 @@ func GeneratePubKey(seckey []byte) ([]byte, error) {
} }
func Sign(msg []byte, seckey []byte) ([]byte, error) { func Sign(msg []byte, seckey []byte) ([]byte, error) {
nonce := randentropy.GetEntropyMixed(32) nonce := randentropy.GetEntropyCSPRNG(32)
var sig []byte = make([]byte, 65) var sig []byte = make([]byte, 65)
var recid C.int var recid C.int

View File

@ -14,7 +14,7 @@ const SigSize = 65 //64+1
func Test_Secp256_00(t *testing.T) { func Test_Secp256_00(t *testing.T) {
var nonce []byte = randentropy.GetEntropyMixed(32) //going to get bitcoins stolen! var nonce []byte = randentropy.GetEntropyCSPRNG(32) //going to get bitcoins stolen!
if len(nonce) != 32 { if len(nonce) != 32 {
t.Fatal() t.Fatal()
@ -52,7 +52,7 @@ func Test_Secp256_01(t *testing.T) {
//test size of messages //test size of messages
func Test_Secp256_02s(t *testing.T) { func Test_Secp256_02s(t *testing.T) {
pubkey, seckey := GenerateKeyPair() pubkey, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyMixed(32) msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey) sig, _ := Sign(msg, seckey)
CompactSigTest(sig) CompactSigTest(sig)
if sig == nil { if sig == nil {
@ -75,7 +75,7 @@ func Test_Secp256_02s(t *testing.T) {
//test signing message //test signing message
func Test_Secp256_02(t *testing.T) { func Test_Secp256_02(t *testing.T) {
pubkey1, seckey := GenerateKeyPair() pubkey1, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyMixed(32) msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey) sig, _ := Sign(msg, seckey)
if sig == nil { if sig == nil {
t.Fatal("Signature nil") t.Fatal("Signature nil")
@ -98,7 +98,7 @@ func Test_Secp256_02(t *testing.T) {
//test pubkey recovery //test pubkey recovery
func Test_Secp256_02a(t *testing.T) { func Test_Secp256_02a(t *testing.T) {
pubkey1, seckey1 := GenerateKeyPair() pubkey1, seckey1 := GenerateKeyPair()
msg := randentropy.GetEntropyMixed(32) msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey1) sig, _ := Sign(msg, seckey1)
if sig == nil { if sig == nil {
@ -127,7 +127,7 @@ func Test_Secp256_02a(t *testing.T) {
func Test_Secp256_03(t *testing.T) { func Test_Secp256_03(t *testing.T) {
_, seckey := GenerateKeyPair() _, seckey := GenerateKeyPair()
for i := 0; i < TESTS; i++ { for i := 0; i < TESTS; i++ {
msg := randentropy.GetEntropyMixed(32) msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey) sig, _ := Sign(msg, seckey)
CompactSigTest(sig) CompactSigTest(sig)
@ -143,7 +143,7 @@ func Test_Secp256_03(t *testing.T) {
func Test_Secp256_04(t *testing.T) { func Test_Secp256_04(t *testing.T) {
for i := 0; i < TESTS; i++ { for i := 0; i < TESTS; i++ {
pubkey1, seckey := GenerateKeyPair() pubkey1, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyMixed(32) msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey) sig, _ := Sign(msg, seckey)
CompactSigTest(sig) CompactSigTest(sig)
@ -166,7 +166,7 @@ func Test_Secp256_04(t *testing.T) {
// -SIPA look at this // -SIPA look at this
func randSig() []byte { func randSig() []byte {
sig := randentropy.GetEntropyMixed(65) sig := randentropy.GetEntropyCSPRNG(65)
sig[32] &= 0x70 sig[32] &= 0x70
sig[64] %= 4 sig[64] %= 4
return sig return sig
@ -174,7 +174,7 @@ func randSig() []byte {
func Test_Secp256_06a_alt0(t *testing.T) { func Test_Secp256_06a_alt0(t *testing.T) {
pubkey1, seckey := GenerateKeyPair() pubkey1, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyMixed(32) msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey) sig, _ := Sign(msg, seckey)
if sig == nil { if sig == nil {
@ -205,12 +205,12 @@ func Test_Secp256_06a_alt0(t *testing.T) {
func Test_Secp256_06b(t *testing.T) { func Test_Secp256_06b(t *testing.T) {
pubkey1, seckey := GenerateKeyPair() pubkey1, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyMixed(32) msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey) sig, _ := Sign(msg, seckey)
fail_count := 0 fail_count := 0
for i := 0; i < TESTS; i++ { for i := 0; i < TESTS; i++ {
msg = randentropy.GetEntropyMixed(32) msg = randentropy.GetEntropyCSPRNG(32)
pubkey2, _ := RecoverPubkey(msg, sig) pubkey2, _ := RecoverPubkey(msg, sig)
if bytes.Equal(pubkey1, pubkey2) == true { if bytes.Equal(pubkey1, pubkey2) == true {
t.Fail() t.Fail()

View File

@ -386,14 +386,17 @@ func (s *Ethereum) StartMining(threads int) error {
func (s *Ethereum) Etherbase() (eb common.Address, err error) { func (s *Ethereum) Etherbase() (eb common.Address, err error) {
eb = s.etherbase eb = s.etherbase
if (eb == common.Address{}) { if (eb == common.Address{}) {
var ebbytes []byte primary, err := s.accountManager.Primary()
ebbytes, err = s.accountManager.Primary() if err != nil {
eb = common.BytesToAddress(ebbytes) return eb, err
if (eb == common.Address{}) {
err = fmt.Errorf("no accounts found")
} }
if (primary == common.Address{}) {
err = fmt.Errorf("no accounts found")
return eb, err
}
eb = primary
} }
return return eb, nil
} }
func (s *Ethereum) StopMining() { s.miner.Stop() } func (s *Ethereum) StopMining() { s.miner.Stop() }
@ -542,7 +545,7 @@ func (self *Ethereum) syncAccounts(tx *types.Transaction) {
return return
} }
if self.accountManager.HasAccount(from.Bytes()) { if self.accountManager.HasAccount(from) {
if self.chainManager.TxState().GetNonce(from) < tx.Nonce() { if self.chainManager.TxState().GetNonce(from) < tx.Nonce() {
self.chainManager.TxState().SetNonce(from, tx.Nonce()) self.chainManager.TxState().SetNonce(from, tx.Nonce())
} }

View File

@ -474,7 +474,7 @@ func gasprice(price *big.Int, pct int64) *big.Int {
func accountAddressesSet(accounts []accounts.Account) *set.Set { func accountAddressesSet(accounts []accounts.Account) *set.Set {
accountSet := set.New() accountSet := set.New()
for _, account := range accounts { for _, account := range accounts {
accountSet.Add(common.BytesToAddress(account.Address)) accountSet.Add(account.Address)
} }
return accountSet return accountSet
} }

View File

@ -18,6 +18,7 @@ package rpc
import ( import (
"encoding/binary" "encoding/binary"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"math/big" "math/big"
@ -117,7 +118,13 @@ func newHexData(input interface{}) *hexdata {
binary.BigEndian.PutUint32(buff, input) binary.BigEndian.PutUint32(buff, input)
d.data = buff d.data = buff
case string: // hexstring case string: // hexstring
d.data = common.Big(input).Bytes() // aaargh ffs TODO: avoid back-and-forth hex encodings where unneeded
bytes, err := hex.DecodeString(strings.TrimPrefix(input, "0x"))
if err != nil {
d.isNil = true
} else {
d.data = bytes
}
default: default:
d.isNil = true d.isNil = true
} }

View File

@ -99,7 +99,7 @@ func runBlockTest(name string, test *BlockTest, t *testing.T) {
} }
func testEthConfig() *eth.Config { func testEthConfig() *eth.Config {
ks := crypto.NewKeyStorePassphrase(filepath.Join(common.DefaultDataDir(), "keys")) ks := crypto.NewKeyStorePassphrase(filepath.Join(common.DefaultDataDir(), "keystore"))
return &eth.Config{ return &eth.Config{
DataDir: common.DefaultDataDir(), DataDir: common.DefaultDataDir(),

View File

@ -113,7 +113,7 @@ func (t *BlockTest) InsertPreState(ethereum *eth.Ethereum) (*state.StateDB, erro
if acct.PrivateKey != "" { if acct.PrivateKey != "" {
privkey, err := hex.DecodeString(strings.TrimPrefix(acct.PrivateKey, "0x")) privkey, err := hex.DecodeString(strings.TrimPrefix(acct.PrivateKey, "0x"))
err = crypto.ImportBlockTestKey(privkey) err = crypto.ImportBlockTestKey(privkey)
err = ethereum.AccountManager().TimedUnlock(addr, "", 999999*time.Second) err = ethereum.AccountManager().TimedUnlock(common.BytesToAddress(addr), "", 999999*time.Second)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -365,7 +365,7 @@ func (self *XEth) Accounts() []string {
accounts, _ := self.backend.AccountManager().Accounts() accounts, _ := self.backend.AccountManager().Accounts()
accountAddresses := make([]string, len(accounts)) accountAddresses := make([]string, len(accounts))
for i, ac := range accounts { for i, ac := range accounts {
accountAddresses[i] = common.ToHex(ac.Address) accountAddresses[i] = ac.Address.Hex()
} }
return accountAddresses return accountAddresses
} }
@ -781,7 +781,7 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st
if err != nil || len(accounts) == 0 { if err != nil || len(accounts) == 0 {
from = statedb.GetOrNewStateObject(common.Address{}) from = statedb.GetOrNewStateObject(common.Address{})
} else { } else {
from = statedb.GetOrNewStateObject(common.BytesToAddress(accounts[0].Address)) from = statedb.GetOrNewStateObject(accounts[0].Address)
} }
} else { } else {
from = statedb.GetOrNewStateObject(common.HexToAddress(fromStr)) from = statedb.GetOrNewStateObject(common.HexToAddress(fromStr))
@ -817,7 +817,7 @@ func (self *XEth) ConfirmTransaction(tx string) bool {
} }
func (self *XEth) doSign(from common.Address, hash common.Hash, didUnlock bool) ([]byte, error) { func (self *XEth) doSign(from common.Address, hash common.Hash, didUnlock bool) ([]byte, error) {
sig, err := self.backend.AccountManager().Sign(accounts.Account{Address: from.Bytes()}, hash.Bytes()) sig, err := self.backend.AccountManager().Sign(accounts.Account{Address: from}, hash.Bytes())
if err == accounts.ErrLocked { if err == accounts.ErrLocked {
if didUnlock { if didUnlock {
return nil, fmt.Errorf("signer account still locked after successful unlock") return nil, fmt.Errorf("signer account still locked after successful unlock")