github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/accounts/keystore.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package accounts
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/aes"
    22  	"crypto/ecdsa"
    23  	"crypto/rand"
    24  	"crypto/sha256"
    25  	"encoding/hex"
    26  	"encoding/json"
    27  	"fmt"
    28  	"io/ioutil"
    29  	"os"
    30  	"path/filepath"
    31  	"time"
    32  
    33  	"golang.org/x/crypto/pbkdf2"
    34  	"golang.org/x/crypto/scrypt"
    35  
    36  	"github.com/ethereumproject/go-ethereum/common"
    37  	"github.com/ethereumproject/go-ethereum/crypto"
    38  	"github.com/ethereumproject/go-ethereum/crypto/randentropy"
    39  	"github.com/ethereumproject/go-ethereum/crypto/secp256k1"
    40  )
    41  
    42  type key struct {
    43  	UUID string
    44  	// to simplify lookups we also store the address
    45  	Address common.Address
    46  	// we only store privkey as pubkey/address can be derived from it
    47  	// privkey in this struct is always in plaintext
    48  	PrivateKey *ecdsa.PrivateKey
    49  }
    50  
    51  type plainKeyJSON struct {
    52  	ID         string `json:"id"`
    53  	Address    string `json:"address"`
    54  	PrivateKey string `json:"privatekey"`
    55  	Version    int    `json:"version"`
    56  }
    57  
    58  func (k *key) MarshalJSON() (j []byte, err error) {
    59  	jStruct := plainKeyJSON{
    60  		ID:         k.UUID,
    61  		Address:    hex.EncodeToString(k.Address[:]),
    62  		PrivateKey: hex.EncodeToString(crypto.FromECDSA(k.PrivateKey)),
    63  		Version:    3,
    64  	}
    65  	j, err = json.Marshal(jStruct)
    66  	return j, err
    67  }
    68  
    69  func (k *key) UnmarshalJSON(j []byte) (err error) {
    70  	keyJSON := new(plainKeyJSON)
    71  	err = json.Unmarshal(j, &keyJSON)
    72  	if err != nil {
    73  		return err
    74  	}
    75  
    76  	k.UUID = keyJSON.ID
    77  	addr, err := hex.DecodeString(keyJSON.Address)
    78  	if err != nil {
    79  		return err
    80  	}
    81  	k.Address = common.BytesToAddress(addr)
    82  
    83  	privkey, err := hex.DecodeString(keyJSON.PrivateKey)
    84  	if err != nil {
    85  		return err
    86  	}
    87  	k.PrivateKey = crypto.ToECDSA(privkey)
    88  
    89  	return nil
    90  }
    91  
    92  // newKeyUUID returns an identifier for key.
    93  func newKeyUUID() (string, error) {
    94  	var u [16]byte
    95  	if _, err := rand.Read(u[:]); err != nil {
    96  		return "", err
    97  	}
    98  
    99  	u[6] = (u[6] & 0x0f) | 0x40 // version 4
   100  	u[8] = (u[8] & 0x3f) | 0x80 // variant 10
   101  
   102  	return fmt.Sprintf("%x-%x-%x-%x-%x", u[:4], u[4:6], u[6:8], u[8:10], u[10:]), nil
   103  }
   104  
   105  func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) (*key, error) {
   106  	id, err := newKeyUUID()
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  
   111  	return &key{
   112  		UUID:       id,
   113  		Address:    crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
   114  		PrivateKey: privateKeyECDSA,
   115  	}, nil
   116  }
   117  
   118  func storeNewKey(store *keyStore, secret string) (*key, Account, error) {
   119  	privateKeyECDSA, err := ecdsa.GenerateKey(secp256k1.S256(), rand.Reader)
   120  	if err != nil {
   121  		return nil, Account{}, err
   122  	}
   123  	key, err := newKeyFromECDSA(privateKeyECDSA)
   124  	if err != nil {
   125  		return nil, Account{}, err
   126  	}
   127  
   128  	file, err := store.Insert(key, secret)
   129  	if err != nil {
   130  		return nil, Account{}, err
   131  	}
   132  
   133  	return key, Account{Address: key.Address, File: file}, err
   134  }
   135  
   136  type keyStore struct {
   137  	baseDir string // absolute filepath to default/flagged value for eg datadir/mainnet/keystore
   138  	scryptN int
   139  	scryptP int
   140  }
   141  
   142  func newKeyStore(dir string, scryptN, scryptP int) (*keyStore, error) {
   143  	if !filepath.IsAbs(dir) {
   144  		var err error
   145  		dir, err = filepath.Abs(dir)
   146  		if err != nil {
   147  			return nil, err
   148  		}
   149  	}
   150  	if err := os.MkdirAll(dir, 0700); err != nil {
   151  		return nil, err
   152  	}
   153  
   154  	return &keyStore{
   155  		baseDir: dir,
   156  		scryptN: scryptN,
   157  		scryptP: scryptP,
   158  	}, nil
   159  }
   160  
   161  func (store *keyStore) DecryptKey(data []byte, secret string) (*key, error) {
   162  	key, err := decryptKey(data, secret)
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  	return key, nil
   167  }
   168  
   169  func (store *keyStore) Lookup(file string, secret string) (*key, error) {
   170  	if !filepath.IsAbs(file) {
   171  		file = filepath.Join(store.baseDir, file)
   172  	}
   173  
   174  	data, err := ioutil.ReadFile(file)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  
   179  	key, err := decryptKey(data, secret)
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  
   184  	return key, nil
   185  }
   186  
   187  func (store *keyStore) Insert(key *key, secret string) (file string, err error) {
   188  	data, err := encryptKey(key, secret, store.scryptN, store.scryptP)
   189  	if err != nil {
   190  		return "", err
   191  	}
   192  
   193  	timestamp := time.Now().UTC().Format("2006-01-02T15-04-05.999999999")
   194  	file = fmt.Sprintf("UTC--%sZ--%x", timestamp, key.Address[:])
   195  	file = filepath.Join(store.baseDir, file)
   196  
   197  	if err := writeKeyFile(file, data); err != nil {
   198  		return "", err
   199  	}
   200  	return file, nil
   201  }
   202  
   203  func (store keyStore) Update(file string, key *key, secret string) error {
   204  	data, err := encryptKey(key, secret, store.scryptN, store.scryptP)
   205  	if err != nil {
   206  		return err
   207  	}
   208  
   209  	if !filepath.IsAbs(file) {
   210  		file = filepath.Join(store.baseDir, file)
   211  	}
   212  	return writeKeyFile(file, data)
   213  }
   214  
   215  // https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition
   216  
   217  // web3v3 is a version 3 encrypted key store record.
   218  type web3v3 struct {
   219  	ID      string     `json:"id"`
   220  	Address string     `json:"address"`
   221  	Crypto  cryptoJSON `json:"crypto"`
   222  	Version int        `json:"version"`
   223  }
   224  
   225  // web3v3 is a version 1 encrypted key store record.
   226  type web3v1 struct {
   227  	ID      string     `json:"id"`
   228  	Address string     `json:"address"`
   229  	Crypto  cryptoJSON `json:"crypto"`
   230  	Version string     `json:"version"`
   231  }
   232  
   233  type cryptoJSON struct {
   234  	Cipher       string                 `json:"cipher"`
   235  	CipherText   string                 `json:"ciphertext"`
   236  	CipherParams cipherparamsJSON       `json:"cipherparams"`
   237  	KDF          string                 `json:"kdf"`
   238  	KDFParams    map[string]interface{} `json:"kdfparams"`
   239  	MAC          string                 `json:"mac"`
   240  }
   241  
   242  type cipherparamsJSON struct {
   243  	IV string `json:"iv"`
   244  }
   245  
   246  // encryptKey encrypts key as version 3.
   247  func encryptKey(key *key, secret string, scryptN, scryptP int) ([]byte, error) {
   248  	salt := randentropy.GetEntropyCSPRNG(32)
   249  	derivedKey, err := scrypt.Key([]byte(secret), salt, scryptN, scryptR, scryptP, scryptDKLen)
   250  	if err != nil {
   251  		return nil, err
   252  	}
   253  	encryptKey := derivedKey[:16]
   254  	keyBytes := crypto.FromECDSA(key.PrivateKey)
   255  
   256  	iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16
   257  	cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv)
   258  	if err != nil {
   259  		return nil, err
   260  	}
   261  	mac := crypto.Keccak256(derivedKey[16:32], cipherText)
   262  
   263  	return json.Marshal(web3v3{
   264  		ID:      key.UUID,
   265  		Address: hex.EncodeToString(key.Address[:]),
   266  		Crypto: cryptoJSON{
   267  			Cipher:     "aes-128-ctr",
   268  			CipherText: hex.EncodeToString(cipherText),
   269  			CipherParams: cipherparamsJSON{
   270  				IV: hex.EncodeToString(iv),
   271  			},
   272  			KDF: "scrypt",
   273  			KDFParams: map[string]interface{}{
   274  				"n":     scryptN,
   275  				"r":     scryptR,
   276  				"p":     scryptP,
   277  				"dklen": scryptDKLen,
   278  				"salt":  hex.EncodeToString(salt),
   279  			},
   280  			MAC: hex.EncodeToString(mac),
   281  		},
   282  		Version: 3,
   283  	})
   284  }
   285  
   286  // Web3PrivateKey decrypts the record with secret and returns the private key.
   287  func Web3PrivateKey(web3JSON []byte, secret string) (*ecdsa.PrivateKey, error) {
   288  	k, err := decryptKey(web3JSON, secret)
   289  	if err != nil {
   290  		return nil, err
   291  	}
   292  	return k.PrivateKey, nil
   293  }
   294  
   295  // decryptKey decrypts a key from a JSON blob, returning the private key itself.
   296  func decryptKey(web3JSON []byte, secret string) (*key, error) {
   297  	// Parse the JSON into a simple map to fetch the key version
   298  	m := make(map[string]interface{})
   299  	if err := json.Unmarshal(web3JSON, &m); err != nil {
   300  		return nil, err
   301  	}
   302  
   303  	// Depending on the version try to parse one way or another
   304  	var (
   305  		keyBytes []byte
   306  		keyUUID  string
   307  	)
   308  	if version, ok := m["version"].(string); ok && version == "1" {
   309  		w := new(web3v1)
   310  		if err := json.Unmarshal(web3JSON, w); err != nil {
   311  			return nil, err
   312  		}
   313  
   314  		keyUUID = w.ID
   315  
   316  		var err error
   317  		keyBytes, err = decryptKeyV1(w, secret)
   318  		if err != nil {
   319  			return nil, err
   320  		}
   321  	} else {
   322  		w := new(web3v3)
   323  		if err := json.Unmarshal(web3JSON, w); err != nil {
   324  			return nil, err
   325  		}
   326  		if w.Version != 3 {
   327  			return nil, fmt.Errorf("unsupported Web3 version: %v", version)
   328  		}
   329  
   330  		keyUUID = w.ID
   331  
   332  		var err error
   333  		keyBytes, err = decryptKeyV3(w, secret)
   334  		if err != nil {
   335  			return nil, err
   336  		}
   337  	}
   338  
   339  	k := crypto.ToECDSA(keyBytes)
   340  	return &key{
   341  		UUID:       keyUUID,
   342  		Address:    crypto.PubkeyToAddress(k.PublicKey),
   343  		PrivateKey: k,
   344  	}, nil
   345  }
   346  
   347  func decryptKeyV3(keyProtected *web3v3, secret string) (keyBytes []byte, err error) {
   348  	if keyProtected.Crypto.Cipher != "aes-128-ctr" {
   349  		return nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher)
   350  	}
   351  
   352  	mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
   353  	if err != nil {
   354  		return nil, err
   355  	}
   356  
   357  	iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
   358  	if err != nil {
   359  		return nil, err
   360  	}
   361  
   362  	cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
   363  	if err != nil {
   364  		return nil, err
   365  	}
   366  
   367  	derivedKey, err := getKDFKey(keyProtected.Crypto, secret)
   368  	if err != nil {
   369  		return nil, err
   370  	}
   371  
   372  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   373  	if !bytes.Equal(calculatedMAC, mac) {
   374  		return nil, ErrDecrypt
   375  	}
   376  
   377  	plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
   378  	if err != nil {
   379  		return nil, err
   380  	}
   381  	return plainText, err
   382  }
   383  
   384  func decryptKeyV1(keyProtected *web3v1, secret string) (keyBytes []byte, err error) {
   385  	mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
   386  	if err != nil {
   387  		return nil, err
   388  	}
   389  
   390  	iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
   391  	if err != nil {
   392  		return nil, err
   393  	}
   394  
   395  	cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
   396  	if err != nil {
   397  		return nil, err
   398  	}
   399  
   400  	derivedKey, err := getKDFKey(keyProtected.Crypto, secret)
   401  	if err != nil {
   402  		return nil, err
   403  	}
   404  
   405  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   406  	if !bytes.Equal(calculatedMAC, mac) {
   407  		return nil, ErrDecrypt
   408  	}
   409  
   410  	plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv)
   411  	if err != nil {
   412  		return nil, err
   413  	}
   414  	return plainText, err
   415  }
   416  
   417  func getKDFKey(cryptoJSON cryptoJSON, secret string) ([]byte, error) {
   418  	salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
   419  	if err != nil {
   420  		return nil, err
   421  	}
   422  	dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
   423  
   424  	if cryptoJSON.KDF == "scrypt" {
   425  		n := ensureInt(cryptoJSON.KDFParams["n"])
   426  		r := ensureInt(cryptoJSON.KDFParams["r"])
   427  		p := ensureInt(cryptoJSON.KDFParams["p"])
   428  		return scrypt.Key([]byte(secret), salt, n, r, p, dkLen)
   429  
   430  	} else if cryptoJSON.KDF == "pbkdf2" {
   431  		c := ensureInt(cryptoJSON.KDFParams["c"])
   432  		prf := cryptoJSON.KDFParams["prf"].(string)
   433  		if prf != "hmac-sha256" {
   434  			return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf)
   435  		}
   436  		key := pbkdf2.Key([]byte(secret), salt, c, dkLen, sha256.New)
   437  		return key, nil
   438  	}
   439  
   440  	return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF)
   441  }
   442  
   443  // TODO: can we do without this when unmarshalling dynamic JSON?
   444  // why do integers in KDF params end up as float64 and not int after
   445  // unmarshal?
   446  func ensureInt(x interface{}) int {
   447  	res, ok := x.(int)
   448  	if !ok {
   449  		res = int(x.(float64))
   450  	}
   451  	return res
   452  }
   453  
   454  func writeKeyFile(file string, content []byte) error {
   455  	dir, basename := filepath.Split(file)
   456  
   457  	// Atomic write: create a temporary hidden file first
   458  	// then move it into place. TempFile assigns mode 0600.
   459  	f, err := ioutil.TempFile(dir, "."+basename+".tmp")
   460  	if err != nil {
   461  		return err
   462  	}
   463  
   464  	if _, err := f.Write(content); err != nil {
   465  		f.Close()
   466  		os.Remove(f.Name())
   467  		return err
   468  	}
   469  	if err := f.Close(); err != nil {
   470  		return err
   471  	}
   472  
   473  	// BUG(pascaldekloe): Windows won't allow updates to a keyfile when it is being read.
   474  	return os.Rename(f.Name(), file)
   475  }