aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeffrey Wilcke <jeffrey@ethereum.org>2015-05-13 01:00:35 +0800
committerJeffrey Wilcke <jeffrey@ethereum.org>2015-05-13 01:00:35 +0800
commitd6357aa616715df1e98cfb90c3aa5372e15cc24b (patch)
tree3f1792e7d71a82de0ceb6047eaf87f2f72aa82ff
parent58d6ec689ff44232cd5d6a7cbbaad2d7a2cb44bd (diff)
parente389585f1f2e77fd7cd507499015bf3754581e4e (diff)
downloaddexon-d6357aa616715df1e98cfb90c3aa5372e15cc24b.tar.gz
dexon-d6357aa616715df1e98cfb90c3aa5372e15cc24b.tar.zst
dexon-d6357aa616715df1e98cfb90c3aa5372e15cc24b.zip
Merge pull request #631 from Gustav-Simonsson/improve_key_store_crypto
Improve key store crypto
-rw-r--r--accounts/account_manager.go42
-rw-r--r--cmd/geth/admin.go6
-rw-r--r--cmd/geth/js.go3
-rw-r--r--cmd/geth/js_test.go4
-rw-r--r--cmd/geth/main.go9
-rw-r--r--cmd/mist/gui.go2
-rw-r--r--cmd/utils/flags.go2
-rw-r--r--common/natspec/natspec_e2e_test.go9
-rw-r--r--crypto/crypto.go12
-rw-r--r--crypto/key.go75
-rw-r--r--crypto/key_store_passphrase.go131
-rw-r--r--crypto/key_store_plain.go29
-rw-r--r--crypto/key_store_test.go2
-rw-r--r--crypto/randentropy/rand_entropy.go51
-rw-r--r--crypto/secp256k1/secp256.go4
-rw-r--r--crypto/secp256k1/secp256_test.go20
-rw-r--r--eth/backend.go15
-rw-r--r--miner/worker.go2
-rw-r--r--rpc/types.go9
-rw-r--r--tests/block_test.go2
-rw-r--r--tests/block_test_util.go2
-rw-r--r--xeth/xeth.go6
22 files changed, 241 insertions, 196 deletions
diff --git a/accounts/account_manager.go b/accounts/account_manager.go
index e9eb8f816..6cbd23c4e 100644
--- a/accounts/account_manager.go
+++ b/accounts/account_manager.go
@@ -33,7 +33,6 @@ and accounts persistence is derived from stored keys' addresses
package accounts
import (
- "bytes"
"crypto/ecdsa"
crand "crypto/rand"
"errors"
@@ -41,6 +40,7 @@ import (
"sync"
"time"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
@@ -50,12 +50,12 @@ var (
)
type Account struct {
- Address []byte
+ Address common.Address
}
type Manager struct {
keyStore crypto.KeyStore2
- unlocked map[string]*unlocked
+ unlocked map[common.Address]*unlocked
mutex sync.RWMutex
}
@@ -67,40 +67,40 @@ type unlocked struct {
func NewManager(keyStore crypto.KeyStore2) *Manager {
return &Manager{
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()
for _, acct := range accounts {
- if bytes.Compare(acct.Address, addr) == 0 {
+ if acct.Address == addr {
return true
}
}
return false
}
-func (am *Manager) Primary() (addr []byte, err error) {
+func (am *Manager) Primary() (addr common.Address, err error) {
addrs, err := am.keyStore.GetKeyAddresses()
if os.IsNotExist(err) {
- return nil, ErrNoKeys
+ return common.Address{}, ErrNoKeys
} else if err != nil {
- return nil, err
+ return common.Address{}, err
}
if len(addrs) == 0 {
- return nil, ErrNoKeys
+ return common.Address{}, ErrNoKeys
}
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)
}
func (am *Manager) Sign(a Account, toSign []byte) (signature []byte, err error) {
am.mutex.RLock()
- unlockedKey, found := am.unlocked[string(a.Address)]
+ unlockedKey, found := am.unlocked[a.Address]
am.mutex.RUnlock()
if !found {
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.
// 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)
if err != nil {
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
// stays unlocked until the program exits or until a TimedUnlock
// 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)
if err != nil {
return err
@@ -157,10 +157,10 @@ func (am *Manager) Accounts() ([]Account, error) {
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{})}
am.mutex.Lock()
- prev, found := am.unlocked[string(addr)]
+ prev, found := am.unlocked[addr]
if found {
// terminate dropLater for this key to avoid unexpected drops.
close(prev.abort)
@@ -169,12 +169,12 @@ func (am *Manager) addUnlocked(addr []byte, key *crypto.Key) *unlocked {
// key, i.e. when Unlock was used.
zeroKey(prev.PrivateKey)
}
- am.unlocked[string(addr)] = u
+ am.unlocked[addr] = u
am.mutex.Unlock()
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)
defer t.Stop()
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
// because the map stores a new pointer every time the key is
// unlocked.
- if am.unlocked[string(addr)] == u {
+ if am.unlocked[addr] == u {
zeroKey(u.PrivateKey)
- delete(am.unlocked, string(addr))
+ delete(am.unlocked, addr)
}
am.mutex.Unlock()
}
@@ -204,7 +204,7 @@ func zeroKey(k *ecdsa.PrivateKey) {
// USE WITH CAUTION = this will save an unencrypted private key on disk
// 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)
if err != nil {
return err
diff --git a/cmd/geth/admin.go b/cmd/geth/admin.go
index b0cb7507a..15923c366 100644
--- a/cmd/geth/admin.go
+++ b/cmd/geth/admin.go
@@ -126,7 +126,7 @@ func (js *jsre) pendingTransactions(call otto.FunctionCall) otto.Value {
// Add the accouns to a new set
accountSet := set.New()
for _, account := range accounts {
- accountSet.Add(common.BytesToAddress(account.Address))
+ accountSet.Add(account.Address)
}
//ltxs := make([]*tx, len(txs))
@@ -391,7 +391,7 @@ func (js *jsre) unlock(call otto.FunctionCall) otto.Value {
}
}
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 {
fmt.Printf("Unlock account failed '%v'\n", err)
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)
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 {
diff --git a/cmd/geth/js.go b/cmd/geth/js.go
index 61e97433a..4ddb3bd9c 100644
--- a/cmd/geth/js.go
+++ b/cmd/geth/js.go
@@ -26,6 +26,7 @@ import (
"strings"
"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/natspec"
"github.com/ethereum/go-ethereum/eth"
@@ -164,7 +165,7 @@ func (self *jsre) UnlockAccount(addr []byte) bool {
return false
}
// 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
} else {
fmt.Println("Account is now unlocked for this session.")
diff --git a/cmd/geth/js_test.go b/cmd/geth/js_test.go
index 9b6d503ab..c2a0e2fe2 100644
--- a/cmd/geth/js_test.go
+++ b/cmd/geth/js_test.go
@@ -45,7 +45,7 @@ type testjethre struct {
}
func (self *testjethre) UnlockAccount(acc []byte) bool {
- err := self.ethereum.AccountManager().Unlock(acc, "")
+ err := self.ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "")
if err != nil {
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
core.GenesisData = []byte(testGenesis)
- ks := crypto.NewKeyStorePassphrase(filepath.Join(tmp, "keys"))
+ ks := crypto.NewKeyStorePassphrase(filepath.Join(tmp, "keystore"))
am := accounts.NewManager(ks)
ethereum, err := eth.New(&eth.Config{
DataDir: tmp,
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index 5fe83a2a0..dbcfe8175 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -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
// Attempt to unlock the account
passphrase = getPassPhrase(ctx, "", false)
- accbytes := common.FromHex(account)
- if len(accbytes) == 0 {
+ if len(account) == 0 {
utils.Fatalf("Invalid account address '%s'", account)
}
- err = am.Unlock(accbytes, passphrase)
+ err = am.Unlock(common.StringToAddress(account), passphrase)
if err != nil {
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)
if len(account) > 0 {
if account == "primary" {
- accbytes, err := am.Primary()
+ primaryAcc, err := am.Primary()
if err != nil {
utils.Fatalf("no primary account: %v", err)
}
- account = common.ToHex(accbytes)
+ account = primaryAcc.Hex()
}
unlockAccount(ctx, am, account)
}
diff --git a/cmd/mist/gui.go b/cmd/mist/gui.go
index 53194ae50..f443bacbc 100644
--- a/cmd/mist/gui.go
+++ b/cmd/mist/gui.go
@@ -232,7 +232,7 @@ func (self *Gui) loadMergedMiningOptions() {
func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
var inout string
from, _ := tx.From()
- if gui.eth.AccountManager().HasAccount(common.Hex2Bytes(from.Hex())) {
+ if gui.eth.AccountManager().HasAccount(from) {
inout = "send"
} else {
inout = "recv"
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 339dd3f47..ddbd36b5c 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -346,7 +346,7 @@ func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Dat
func GetAccountManager(ctx *cli.Context) *accounts.Manager {
dataDir := ctx.GlobalString(DataDirFlag.Name)
- ks := crypto.NewKeyStorePassphrase(filepath.Join(dataDir, "keys"))
+ ks := crypto.NewKeyStorePassphrase(filepath.Join(dataDir, "keystore"))
return accounts.NewManager(ks)
}
diff --git a/common/natspec/natspec_e2e_test.go b/common/natspec/natspec_e2e_test.go
index f9b0c1dcc..a8d318b57 100644
--- a/common/natspec/natspec_e2e_test.go
+++ b/common/natspec/natspec_e2e_test.go
@@ -4,6 +4,7 @@ import (
"fmt"
"io/ioutil"
"os"
+ "strings"
"testing"
"github.com/ethereum/go-ethereum/accounts"
@@ -84,7 +85,7 @@ type testFrontend struct {
}
func (self *testFrontend) UnlockAccount(acc []byte) bool {
- self.ethereum.AccountManager().Unlock(acc, "password")
+ self.ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "password")
return true
}
@@ -103,19 +104,19 @@ func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) {
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 {
panic(err)
}
// create a testAddress
- ks := crypto.NewKeyStorePassphrase("/tmp/eth-natspec/keys")
+ ks := crypto.NewKeyStorePassphrase("/tmp/eth-natspec/keystore")
am := accounts.NewManager(ks)
testAccount, err := am.NewAccount("password")
if err != nil {
panic(err)
}
- testAddress := common.Bytes2Hex(testAccount.Address)
+ testAddress := strings.TrimPrefix(testAccount.Address.Hex(), "0x")
// set up mock genesis with balance on the testAddress
core.GenesisData = []byte(`{
diff --git a/crypto/crypto.go b/crypto/crypto.go
index 3c5783014..4bbd62f7f 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -181,11 +181,11 @@ func Decrypt(prv *ecdsa.PrivateKey, ct []byte) ([]byte, error) {
// Used only by block tests.
func ImportBlockTestKey(privKeyBytes []byte) error {
- ks := NewKeyStorePassphrase(common.DefaultDataDir() + "/keys")
+ ks := NewKeyStorePassphrase(common.DefaultDataDir() + "/keystore")
ecKey := ToECDSA(privKeyBytes)
key := &Key{
Id: uuid.NewRandom(),
- Address: PubkeyToAddress(ecKey.PublicKey),
+ Address: common.BytesToAddress(PubkeyToAddress(ecKey.PublicKey)),
PrivateKey: ecKey,
}
err := ks.StoreKey(key, "")
@@ -231,13 +231,13 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
ecKey := ToECDSA(ethPriv)
key = &Key{
Id: nil,
- Address: PubkeyToAddress(ecKey.PublicKey),
+ Address: common.BytesToAddress(PubkeyToAddress(ecKey.PublicKey)),
PrivateKey: ecKey,
}
- derivedAddr := common.Bytes2Hex(key.Address)
+ derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x"
expectedAddr := preSaleKeyStruct.EthAddr
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
}
@@ -252,7 +252,7 @@ func aesCBCDecrypt(key []byte, cipherText []byte, iv []byte) (plainText []byte,
decrypter.CryptBlocks(paddedPlainText, cipherText)
plainText = PKCS7Unpad(paddedPlainText)
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
}
diff --git a/crypto/key.go b/crypto/key.go
index 0b84bfec1..0c5ce4254 100644
--- a/crypto/key.go
+++ b/crypto/key.go
@@ -26,44 +26,69 @@ package crypto
import (
"bytes"
"crypto/ecdsa"
+ "encoding/hex"
"encoding/json"
"io"
"code.google.com/p/go-uuid/uuid"
+ "github.com/ethereum/go-ethereum/common"
+)
+
+const (
+ version = "1"
)
type Key struct {
Id uuid.UUID // Version 4 "random" for unique id not derived from key data
// 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
// privkey in this struct is always in plaintext
PrivateKey *ecdsa.PrivateKey
}
type plainKeyJSON struct {
- Id []byte
- Address []byte
- PrivateKey []byte
+ Address string `json:"address"`
+ PrivateKey string `json:"privatekey"`
+ Id string `json:"id"`
+ Version string `json:"version"`
}
-type cipherJSON struct {
- Salt []byte
- IV []byte
- CipherText []byte
+type encryptedKeyJSON struct {
+ Address string `json:"address"`
+ Crypto cryptoJSON
+ Id string `json:"id"`
+ Version string `json:"version"`
}
-type encryptedKeyJSON struct {
- Id []byte
- Address []byte
- Crypto cipherJSON
+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) {
jStruct := plainKeyJSON{
- k.Id,
- k.Address,
- FromECDSA(k.PrivateKey),
+ hex.EncodeToString(k.Address[:]),
+ hex.EncodeToString(FromECDSA(k.PrivateKey)),
+ k.Id.String(),
+ version,
}
j, err = json.Marshal(jStruct)
return j, err
@@ -77,19 +102,29 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) {
}
u := new(uuid.UUID)
- *u = keyJSON.Id
+ *u = uuid.Parse(keyJSON.Id)
k.Id = *u
- k.Address = keyJSON.Address
- k.PrivateKey = ToECDSA(keyJSON.PrivateKey)
+ addr, err := hex.DecodeString(keyJSON.Address)
+ if err != nil {
+ return err
+ }
+
+ privkey, err := hex.DecodeString(keyJSON.PrivateKey)
+ if err != nil {
+ return err
+ }
+
+ k.Address = common.BytesToAddress(addr)
+ k.PrivateKey = ToECDSA(privkey)
- return err
+ return nil
}
func NewKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
id := uuid.NewRandom()
key := &Key{
Id: id,
- Address: PubkeyToAddress(privateKeyECDSA.PublicKey),
+ Address: common.BytesToAddress(PubkeyToAddress(privateKeyECDSA.PublicKey)),
PrivateKey: privateKeyECDSA,
}
return key
diff --git a/crypto/key_store_passphrase.go b/crypto/key_store_passphrase.go
index 9d36d7b03..d9a5a81f9 100644
--- a/crypto/key_store_passphrase.go
+++ b/crypto/key_store_passphrase.go
@@ -28,24 +28,25 @@ the private key is encrypted and on disk uses another JSON encoding.
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.
-2. Scrypt salt is 32 random bytes from CSPRNG. It is appended to ciphertext.
-3. Checksum is SHA3 of the private key bytes.
-4. Plaintext is concatenation of private key bytes and checksum.
-5. Encryption algo is AES 256 CBC [3][4]
-6. CBC IV is 16 random bytes from CSPRNG. It is appended to ciphertext.
+2. Scrypt salt is 32 random bytes from CSPRNG.
+ It's stored in plain next to ciphertext in key file.
+3. MAC is SHA3-256 of concatenation of ciphertext and last 16 bytes of scrypt derived key.
+4. Plaintext is the EC private key bytes.
+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]
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.
2. byte arrays are base64 JSON strings.
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].
-4. The checksum is the last 32 bytes of the plaintext byte array and the
- private key is the preceeding bytes.
References:
@@ -75,11 +76,14 @@ import (
"path/filepath"
"code.google.com/p/go-uuid/uuid"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/randentropy"
"golang.org/x/crypto/scrypt"
)
const (
+ keyHeaderVersion = "1"
+ keyHeaderKDF = "scrypt"
// 2^18 / 8 / 1 uses 256MB memory and approx 1s CPU time on a modern CPU.
scryptN = 1 << 18
scryptr = 8
@@ -99,7 +103,7 @@ func (ks keyStorePassphrase) GenerateNewKey(rand io.Reader, auth string) (key *K
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)
if err != nil {
return nil, err
@@ -112,43 +116,63 @@ func (ks keyStorePassphrase) GetKey(keyAddr []byte, auth string) (key *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)
}
func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
authArray := []byte(auth)
- salt := randentropy.GetEntropyMixed(32)
+ salt := randentropy.GetEntropyCSPRNG(32)
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen)
if err != nil {
return err
}
+ encryptKey := Sha3(derivedKey[:16])[:16]
+
keyBytes := FromECDSA(key.PrivateKey)
- keyBytesHash := Sha3(keyBytes)
- toEncrypt := PKCS7Pad(append(keyBytes, keyBytesHash...))
+ toEncrypt := PKCS7Pad(keyBytes)
- AES256Block, err := aes.NewCipher(derivedKey)
+ AES128Block, err := aes.NewCipher(encryptKey)
if err != nil {
return err
}
- iv := randentropy.GetEntropyMixed(aes.BlockSize) // 16
- AES256CBCEncrypter := cipher.NewCBCEncrypter(AES256Block, iv)
+ iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16
+ AES128CBCEncrypter := cipher.NewCBCEncrypter(AES128Block, iv)
cipherText := make([]byte, len(toEncrypt))
- AES256CBCEncrypter.CryptBlocks(cipherText, toEncrypt)
+ AES128CBCEncrypter.CryptBlocks(cipherText, toEncrypt)
+
+ mac := Sha3(derivedKey[16:32], cipherText)
- cipherStruct := cipherJSON{
- salt,
- iv,
- cipherText,
+ scryptParamsJSON := scryptParamsJSON{
+ N: scryptN,
+ R: scryptr,
+ P: scryptp,
+ DkLen: scryptdkLen,
+ Salt: hex.EncodeToString(salt),
}
- keyStruct := encryptedKeyJSON{
- key.Id,
- key.Address,
- cipherStruct,
+
+ cipherParamsJSON := cipherparamsJSON{
+ IV: hex.EncodeToString(iv),
}
- 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 {
return err
}
@@ -156,18 +180,18 @@ func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
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
_, _, err = DecryptKey(ks, keyAddr, auth)
if err != nil {
return err
}
- keyDirPath := filepath.Join(ks.keysDirPath, hex.EncodeToString(keyAddr))
+ keyDirPath := filepath.Join(ks.keysDirPath, hex.EncodeToString(keyAddr[:]))
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)
if err != nil {
return nil, nil, err
@@ -176,25 +200,48 @@ func DecryptKey(ks keyStorePassphrase, keyAddr []byte, auth string) (keyBytes []
keyProtected := new(encryptedKeyJSON)
err = json.Unmarshal(fileContent, keyProtected)
- keyId = keyProtected.Id
- salt := keyProtected.Crypto.Salt
- iv := keyProtected.Crypto.IV
- cipherText := keyProtected.Crypto.CipherText
+ keyId = uuid.Parse(keyProtected.Id)
- authArray := []byte(auth)
- derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen)
+ mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
+ 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
}
- plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
+
+ 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)
+ derivedKey, err := scrypt.Key(authArray, salt, n, r, p, dkLen)
if err != nil {
return nil, nil, err
}
- keyBytes = plainText[:len(plainText)-32]
- keyBytesHash := plainText[len(plainText)-32:]
- if !bytes.Equal(Sha3(keyBytes), keyBytesHash) {
- err = errors.New("Decryption failed: checksum mismatch")
+
+ 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 {
return nil, nil, err
}
- return keyBytes, keyId, err
+ return plainText, keyId, err
}
diff --git a/crypto/key_store_plain.go b/crypto/key_store_plain.go
index 581968d7c..6a8afe27d 100644
--- a/crypto/key_store_plain.go
+++ b/crypto/key_store_plain.go
@@ -27,6 +27,7 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
+ "github.com/ethereum/go-ethereum/common"
"io"
"io/ioutil"
"os"
@@ -37,10 +38,10 @@ import (
type KeyStore2 interface {
// create new key using io.Reader entropy source and optionally using auth string
GenerateNewKey(io.Reader, string) (*Key, error)
- GetKey([]byte, string) (*Key, error) // key from addr and auth string
- GetKeyAddresses() ([][]byte, error) // get all addresses
- StoreKey(*Key, string) error // store key optionally using auth string
- DeleteKey([]byte, string) error // delete key by addr and auth string
+ GetKey(common.Address, string) (*Key, error) // 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
}
type keyStorePlain struct {
@@ -66,7 +67,7 @@ func GenerateNewKeyDefault(ks KeyStore2, rand io.Reader, auth string) (key *Key,
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)
if err != nil {
return nil, err
@@ -77,7 +78,7 @@ func (ks keyStorePlain) GetKey(keyAddr []byte, auth string) (key *Key, err error
return key, err
}
-func (ks keyStorePlain) GetKeyAddresses() (addresses [][]byte, err error) {
+func (ks keyStorePlain) GetKeyAddresses() (addresses []common.Address, err error) {
return GetKeyAddresses(ks.keysDirPath)
}
@@ -90,19 +91,19 @@ func (ks keyStorePlain) StoreKey(key *Key, auth string) (err error) {
return err
}
-func (ks keyStorePlain) DeleteKey(keyAddr []byte, auth string) (err error) {
- keyDirPath := filepath.Join(ks.keysDirPath, hex.EncodeToString(keyAddr))
+func (ks keyStorePlain) DeleteKey(keyAddr common.Address, auth string) (err error) {
+ keyDirPath := filepath.Join(ks.keysDirPath, keyAddr.Hex())
err = os.RemoveAll(keyDirPath)
return err
}
-func GetKeyFile(keysDirPath string, keyAddr []byte) (fileContent []byte, err error) {
- fileName := hex.EncodeToString(keyAddr)
+func GetKeyFile(keysDirPath string, keyAddr common.Address) (fileContent []byte, err error) {
+ fileName := hex.EncodeToString(keyAddr[:])
return ioutil.ReadFile(filepath.Join(keysDirPath, fileName, fileName))
}
-func WriteKeyFile(addr []byte, keysDirPath string, content []byte) (err error) {
- addrHex := hex.EncodeToString(addr)
+func WriteKeyFile(addr common.Address, keysDirPath string, content []byte) (err error) {
+ addrHex := hex.EncodeToString(addr[:])
keyDirPath := filepath.Join(keysDirPath, addrHex)
keyFilePath := filepath.Join(keyDirPath, addrHex)
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
}
-func GetKeyAddresses(keysDirPath string) (addresses [][]byte, err error) {
+func GetKeyAddresses(keysDirPath string) (addresses []common.Address, err error) {
fileInfos, err := ioutil.ReadDir(keysDirPath)
if err != nil {
return nil, err
@@ -122,7 +123,7 @@ func GetKeyAddresses(keysDirPath string) (addresses [][]byte, err error) {
if err != nil {
continue
}
- addresses = append(addresses, address)
+ addresses = append(addresses, common.BytesToAddress(address))
}
return addresses, err
}
diff --git a/crypto/key_store_test.go b/crypto/key_store_test.go
index f0a1e567b..6e50afe34 100644
--- a/crypto/key_store_test.go
+++ b/crypto/key_store_test.go
@@ -1,8 +1,8 @@
package crypto
import (
- "github.com/ethereum/go-ethereum/crypto/randentropy"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto/randentropy"
"reflect"
"testing"
)
diff --git a/crypto/randentropy/rand_entropy.go b/crypto/randentropy/rand_entropy.go
index b87fa564e..68bb8808b 100644
--- a/crypto/randentropy/rand_entropy.go
+++ b/crypto/randentropy/rand_entropy.go
@@ -2,12 +2,8 @@ package randentropy
import (
crand "crypto/rand"
- "encoding/binary"
"github.com/ethereum/go-ethereum/crypto/sha3"
"io"
- "os"
- "strings"
- "time"
)
var Reader io.Reader = &randEntropy{}
@@ -16,7 +12,7 @@ type randEntropy struct {
}
func (*randEntropy) Read(bytes []byte) (n int, err error) {
- readBytes := GetEntropyMixed(len(bytes))
+ readBytes := GetEntropyCSPRNG(len(bytes))
copy(bytes, readBytes)
return len(bytes), nil
}
@@ -29,40 +25,6 @@ func Sha3(data []byte) []byte {
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 {
mainBuff := make([]byte, n)
_, err := io.ReadFull(crand.Reader, mainBuff)
@@ -71,14 +33,3 @@ func GetEntropyCSPRNG(n int) []byte {
}
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
-}
diff --git a/crypto/secp256k1/secp256.go b/crypto/secp256k1/secp256.go
index f8cc60e82..8ed81a1ed 100644
--- a/crypto/secp256k1/secp256.go
+++ b/crypto/secp256k1/secp256.go
@@ -59,7 +59,7 @@ func GenerateKeyPair() ([]byte, []byte) {
const seckey_len = 32
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 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) {
- nonce := randentropy.GetEntropyMixed(32)
+ nonce := randentropy.GetEntropyCSPRNG(32)
var sig []byte = make([]byte, 65)
var recid C.int
diff --git a/crypto/secp256k1/secp256_test.go b/crypto/secp256k1/secp256_test.go
index 3599fde38..14d260beb 100644
--- a/crypto/secp256k1/secp256_test.go
+++ b/crypto/secp256k1/secp256_test.go
@@ -14,7 +14,7 @@ const SigSize = 65 //64+1
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 {
t.Fatal()
@@ -52,7 +52,7 @@ func Test_Secp256_01(t *testing.T) {
//test size of messages
func Test_Secp256_02s(t *testing.T) {
pubkey, seckey := GenerateKeyPair()
- msg := randentropy.GetEntropyMixed(32)
+ msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
CompactSigTest(sig)
if sig == nil {
@@ -75,7 +75,7 @@ func Test_Secp256_02s(t *testing.T) {
//test signing message
func Test_Secp256_02(t *testing.T) {
pubkey1, seckey := GenerateKeyPair()
- msg := randentropy.GetEntropyMixed(32)
+ msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
if sig == nil {
t.Fatal("Signature nil")
@@ -98,7 +98,7 @@ func Test_Secp256_02(t *testing.T) {
//test pubkey recovery
func Test_Secp256_02a(t *testing.T) {
pubkey1, seckey1 := GenerateKeyPair()
- msg := randentropy.GetEntropyMixed(32)
+ msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey1)
if sig == nil {
@@ -127,7 +127,7 @@ func Test_Secp256_02a(t *testing.T) {
func Test_Secp256_03(t *testing.T) {
_, seckey := GenerateKeyPair()
for i := 0; i < TESTS; i++ {
- msg := randentropy.GetEntropyMixed(32)
+ msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
CompactSigTest(sig)
@@ -143,7 +143,7 @@ func Test_Secp256_03(t *testing.T) {
func Test_Secp256_04(t *testing.T) {
for i := 0; i < TESTS; i++ {
pubkey1, seckey := GenerateKeyPair()
- msg := randentropy.GetEntropyMixed(32)
+ msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
CompactSigTest(sig)
@@ -166,7 +166,7 @@ func Test_Secp256_04(t *testing.T) {
// -SIPA look at this
func randSig() []byte {
- sig := randentropy.GetEntropyMixed(65)
+ sig := randentropy.GetEntropyCSPRNG(65)
sig[32] &= 0x70
sig[64] %= 4
return sig
@@ -174,7 +174,7 @@ func randSig() []byte {
func Test_Secp256_06a_alt0(t *testing.T) {
pubkey1, seckey := GenerateKeyPair()
- msg := randentropy.GetEntropyMixed(32)
+ msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
if sig == nil {
@@ -205,12 +205,12 @@ func Test_Secp256_06a_alt0(t *testing.T) {
func Test_Secp256_06b(t *testing.T) {
pubkey1, seckey := GenerateKeyPair()
- msg := randentropy.GetEntropyMixed(32)
+ msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey)
fail_count := 0
for i := 0; i < TESTS; i++ {
- msg = randentropy.GetEntropyMixed(32)
+ msg = randentropy.GetEntropyCSPRNG(32)
pubkey2, _ := RecoverPubkey(msg, sig)
if bytes.Equal(pubkey1, pubkey2) == true {
t.Fail()
diff --git a/eth/backend.go b/eth/backend.go
index 7960a0e61..362a7eab7 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -386,14 +386,17 @@ func (s *Ethereum) StartMining(threads int) error {
func (s *Ethereum) Etherbase() (eb common.Address, err error) {
eb = s.etherbase
if (eb == common.Address{}) {
- var ebbytes []byte
- ebbytes, err = s.accountManager.Primary()
- eb = common.BytesToAddress(ebbytes)
- if (eb == common.Address{}) {
+ primary, err := s.accountManager.Primary()
+ if err != nil {
+ return eb, err
+ }
+ 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() }
@@ -542,7 +545,7 @@ func (self *Ethereum) syncAccounts(tx *types.Transaction) {
return
}
- if self.accountManager.HasAccount(from.Bytes()) {
+ if self.accountManager.HasAccount(from) {
if self.chainManager.TxState().GetNonce(from) < tx.Nonce() {
self.chainManager.TxState().SetNonce(from, tx.Nonce())
}
diff --git a/miner/worker.go b/miner/worker.go
index 8698bb90d..f737be507 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -474,7 +474,7 @@ func gasprice(price *big.Int, pct int64) *big.Int {
func accountAddressesSet(accounts []accounts.Account) *set.Set {
accountSet := set.New()
for _, account := range accounts {
- accountSet.Add(common.BytesToAddress(account.Address))
+ accountSet.Add(account.Address)
}
return accountSet
}
diff --git a/rpc/types.go b/rpc/types.go
index e6eb4f856..1f49a3dea 100644
--- a/rpc/types.go
+++ b/rpc/types.go
@@ -18,6 +18,7 @@ package rpc
import (
"encoding/binary"
+ "encoding/hex"
"encoding/json"
"fmt"
"math/big"
@@ -117,7 +118,13 @@ func newHexData(input interface{}) *hexdata {
binary.BigEndian.PutUint32(buff, input)
d.data = buff
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:
d.isNil = true
}
diff --git a/tests/block_test.go b/tests/block_test.go
index b5724a1e1..0ba0aefa2 100644
--- a/tests/block_test.go
+++ b/tests/block_test.go
@@ -99,7 +99,7 @@ func runBlockTest(name string, test *BlockTest, t *testing.T) {
}
func testEthConfig() *eth.Config {
- ks := crypto.NewKeyStorePassphrase(filepath.Join(common.DefaultDataDir(), "keys"))
+ ks := crypto.NewKeyStorePassphrase(filepath.Join(common.DefaultDataDir(), "keystore"))
return &eth.Config{
DataDir: common.DefaultDataDir(),
diff --git a/tests/block_test_util.go b/tests/block_test_util.go
index 093c9be0c..ae2ae4033 100644
--- a/tests/block_test_util.go
+++ b/tests/block_test_util.go
@@ -113,7 +113,7 @@ func (t *BlockTest) InsertPreState(ethereum *eth.Ethereum) (*state.StateDB, erro
if acct.PrivateKey != "" {
privkey, err := hex.DecodeString(strings.TrimPrefix(acct.PrivateKey, "0x"))
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 {
return nil, err
}
diff --git a/xeth/xeth.go b/xeth/xeth.go
index 11dc506b8..0fe68d175 100644
--- a/xeth/xeth.go
+++ b/xeth/xeth.go
@@ -365,7 +365,7 @@ func (self *XEth) Accounts() []string {
accounts, _ := self.backend.AccountManager().Accounts()
accountAddresses := make([]string, len(accounts))
for i, ac := range accounts {
- accountAddresses[i] = common.ToHex(ac.Address)
+ accountAddresses[i] = ac.Address.Hex()
}
return accountAddresses
}
@@ -781,7 +781,7 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st
if err != nil || len(accounts) == 0 {
from = statedb.GetOrNewStateObject(common.Address{})
} else {
- from = statedb.GetOrNewStateObject(common.BytesToAddress(accounts[0].Address))
+ from = statedb.GetOrNewStateObject(accounts[0].Address)
}
} else {
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) {
- 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 didUnlock {
return nil, fmt.Errorf("signer account still locked after successful unlock")