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
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

View File

@ -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 {

View File

@ -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.")

View File

@ -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,

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
// 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)
}

View File

@ -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"

View File

@ -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)
}

View File

@ -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(`{

View File

@ -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
}

View File

@ -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
}
type cipherJSON struct {
Salt []byte
IV []byte
CipherText []byte
Address string `json:"address"`
PrivateKey string `json:"privatekey"`
Id string `json:"id"`
Version string `json:"version"`
}
type encryptedKeyJSON struct {
Id []byte
Address []byte
Crypto cipherJSON
Address string `json:"address"`
Crypto cryptoJSON
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) {
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
}
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 {
id := uuid.NewRandom()
key := &Key{
Id: id,
Address: PubkeyToAddress(privateKeyECDSA.PublicKey),
Address: common.BytesToAddress(PubkeyToAddress(privateKeyECDSA.PublicKey)),
PrivateKey: privateKeyECDSA,
}
return key

View File

@ -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
}
keyBytes := FromECDSA(key.PrivateKey)
keyBytesHash := Sha3(keyBytes)
toEncrypt := PKCS7Pad(append(keyBytes, keyBytesHash...))
encryptKey := Sha3(derivedKey[:16])[:16]
AES256Block, err := aes.NewCipher(derivedKey)
keyBytes := FromECDSA(key.PrivateKey)
toEncrypt := PKCS7Pad(keyBytes)
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)
cipherStruct := cipherJSON{
salt,
iv,
cipherText,
mac := Sha3(derivedKey[16:32], 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)
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
}
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, scryptN, scryptr, scryptp, scryptdkLen)
derivedKey, err := scrypt.Key(authArray, salt, n, r, p, dkLen)
if err != nil {
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 {
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")
return nil, nil, err
}
return keyBytes, keyId, err
return plainText, keyId, err
}

View File

@ -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
}

View File

@ -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"
)

View File

@ -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
}

View File

@ -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

View File

@ -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()

View File

@ -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{}) {
err = fmt.Errorf("no accounts found")
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())
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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(),

View File

@ -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
}

View File

@ -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")