github.com/xfond/eth-implementation@v1.8.9-0.20180514135602-f6bc65fc6811/accounts/keystore/keystore_passphrase.go (about)

     1  // Copyright 2014 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  /*
    18  
    19  This key store behaves as KeyStorePlain with the difference that
    20  the private key is encrypted and on disk uses another JSON encoding.
    21  
    22  The crypto is documented at https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition
    23  
    24  */
    25  
    26  package keystore
    27  
    28  import (
    29  	"bytes"
    30  	"crypto/aes"
    31  	crand "crypto/rand"
    32  	"crypto/sha256"
    33  	"encoding/hex"
    34  	"encoding/json"
    35  	"fmt"
    36  	"io/ioutil"
    37  	"path/filepath"
    38  
    39  	"github.com/ethereum/go-ethereum/common"
    40  	"github.com/ethereum/go-ethereum/common/math"
    41  	"github.com/ethereum/go-ethereum/crypto"
    42  	"github.com/ethereum/go-ethereum/crypto/randentropy"
    43  	"github.com/pborman/uuid"
    44  	"golang.org/x/crypto/pbkdf2"
    45  	"golang.org/x/crypto/scrypt"
    46  )
    47  
    48  const (
    49  	keyHeaderKDF = "scrypt"
    50  
    51  	// StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB
    52  	// memory and taking approximately 1s CPU time on a modern processor.
    53  	StandardScryptN = 1 << 18
    54  
    55  	// StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB
    56  	// memory and taking approximately 1s CPU time on a modern processor.
    57  	StandardScryptP = 1
    58  
    59  	// LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB
    60  	// memory and taking approximately 100ms CPU time on a modern processor.
    61  	LightScryptN = 1 << 12
    62  
    63  	// LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB
    64  	// memory and taking approximately 100ms CPU time on a modern processor.
    65  	LightScryptP = 6
    66  
    67  	scryptR     = 8
    68  	scryptDKLen = 32
    69  )
    70  
    71  type keyStorePassphrase struct {
    72  	keysDirPath string
    73  	scryptN     int
    74  	scryptP     int
    75  }
    76  
    77  func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) {
    78  	// Load the key from the keystore and decrypt its contents
    79  	keyjson, err := ioutil.ReadFile(filename)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	key, err := DecryptKey(keyjson, auth)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	// Make sure we're really operating on the requested key (no swap attacks)
    88  	if key.Address != addr {
    89  		return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr)
    90  	}
    91  	return key, nil
    92  }
    93  
    94  // StoreKey generates a key, encrypts with 'auth' and stores in the given directory
    95  func StoreKey(dir, auth string, scryptN, scryptP int) (common.Address, error) {
    96  	_, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP}, crand.Reader, auth)
    97  	return a.Address, err
    98  }
    99  
   100  func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error {
   101  	keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)
   102  	if err != nil {
   103  		return err
   104  	}
   105  	return writeKeyFile(filename, keyjson)
   106  }
   107  
   108  func (ks keyStorePassphrase) JoinPath(filename string) string {
   109  	if filepath.IsAbs(filename) {
   110  		return filename
   111  	}
   112  	return filepath.Join(ks.keysDirPath, filename)
   113  }
   114  
   115  // EncryptKey encrypts a key using the specified scrypt parameters into a json
   116  // blob that can be decrypted later on.
   117  func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
   118  	authArray := []byte(auth)
   119  	salt := randentropy.GetEntropyCSPRNG(32)
   120  	derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	encryptKey := derivedKey[:16]
   125  	keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
   126  
   127  	iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16
   128  	cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	mac := crypto.Keccak256(derivedKey[16:32], cipherText)
   133  
   134  	scryptParamsJSON := make(map[string]interface{}, 5)
   135  	scryptParamsJSON["n"] = scryptN
   136  	scryptParamsJSON["r"] = scryptR
   137  	scryptParamsJSON["p"] = scryptP
   138  	scryptParamsJSON["dklen"] = scryptDKLen
   139  	scryptParamsJSON["salt"] = hex.EncodeToString(salt)
   140  
   141  	cipherParamsJSON := cipherparamsJSON{
   142  		IV: hex.EncodeToString(iv),
   143  	}
   144  
   145  	cryptoStruct := cryptoJSON{
   146  		Cipher:       "aes-128-ctr",
   147  		CipherText:   hex.EncodeToString(cipherText),
   148  		CipherParams: cipherParamsJSON,
   149  		KDF:          keyHeaderKDF,
   150  		KDFParams:    scryptParamsJSON,
   151  		MAC:          hex.EncodeToString(mac),
   152  	}
   153  	encryptedKeyJSONV3 := encryptedKeyJSONV3{
   154  		hex.EncodeToString(key.Address[:]),
   155  		cryptoStruct,
   156  		key.Id.String(),
   157  		version,
   158  	}
   159  	return json.Marshal(encryptedKeyJSONV3)
   160  }
   161  
   162  // DecryptKey decrypts a key from a json blob, returning the private key itself.
   163  func DecryptKey(keyjson []byte, auth string) (*Key, error) {
   164  	// Parse the json into a simple map to fetch the key version
   165  	m := make(map[string]interface{})
   166  	if err := json.Unmarshal(keyjson, &m); err != nil {
   167  		return nil, err
   168  	}
   169  	// Depending on the version try to parse one way or another
   170  	var (
   171  		keyBytes, keyId []byte
   172  		err             error
   173  	)
   174  	if version, ok := m["version"].(string); ok && version == "1" {
   175  		k := new(encryptedKeyJSONV1)
   176  		if err := json.Unmarshal(keyjson, k); err != nil {
   177  			return nil, err
   178  		}
   179  		keyBytes, keyId, err = decryptKeyV1(k, auth)
   180  	} else {
   181  		k := new(encryptedKeyJSONV3)
   182  		if err := json.Unmarshal(keyjson, k); err != nil {
   183  			return nil, err
   184  		}
   185  		keyBytes, keyId, err = decryptKeyV3(k, auth)
   186  	}
   187  	// Handle any decryption errors and return the key
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  	key := crypto.ToECDSAUnsafe(keyBytes)
   192  
   193  	return &Key{
   194  		Id:         uuid.UUID(keyId),
   195  		Address:    crypto.PubkeyToAddress(key.PublicKey),
   196  		PrivateKey: key,
   197  	}, nil
   198  }
   199  
   200  func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
   201  	if keyProtected.Version != version {
   202  		return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
   203  	}
   204  
   205  	if keyProtected.Crypto.Cipher != "aes-128-ctr" {
   206  		return nil, nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher)
   207  	}
   208  
   209  	keyId = uuid.Parse(keyProtected.Id)
   210  	mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
   211  	if err != nil {
   212  		return nil, nil, err
   213  	}
   214  
   215  	iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
   216  	if err != nil {
   217  		return nil, nil, err
   218  	}
   219  
   220  	cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
   221  	if err != nil {
   222  		return nil, nil, err
   223  	}
   224  
   225  	derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
   226  	if err != nil {
   227  		return nil, nil, err
   228  	}
   229  
   230  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   231  	if !bytes.Equal(calculatedMAC, mac) {
   232  		return nil, nil, ErrDecrypt
   233  	}
   234  
   235  	plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
   236  	if err != nil {
   237  		return nil, nil, err
   238  	}
   239  	return plainText, keyId, err
   240  }
   241  
   242  func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
   243  	keyId = uuid.Parse(keyProtected.Id)
   244  	mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
   245  	if err != nil {
   246  		return nil, nil, err
   247  	}
   248  
   249  	iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
   250  	if err != nil {
   251  		return nil, nil, err
   252  	}
   253  
   254  	cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
   255  	if err != nil {
   256  		return nil, nil, err
   257  	}
   258  
   259  	derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
   260  	if err != nil {
   261  		return nil, nil, err
   262  	}
   263  
   264  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   265  	if !bytes.Equal(calculatedMAC, mac) {
   266  		return nil, nil, ErrDecrypt
   267  	}
   268  
   269  	plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv)
   270  	if err != nil {
   271  		return nil, nil, err
   272  	}
   273  	return plainText, keyId, err
   274  }
   275  
   276  func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) {
   277  	authArray := []byte(auth)
   278  	salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
   279  	if err != nil {
   280  		return nil, err
   281  	}
   282  	dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
   283  
   284  	if cryptoJSON.KDF == keyHeaderKDF {
   285  		n := ensureInt(cryptoJSON.KDFParams["n"])
   286  		r := ensureInt(cryptoJSON.KDFParams["r"])
   287  		p := ensureInt(cryptoJSON.KDFParams["p"])
   288  		return scrypt.Key(authArray, salt, n, r, p, dkLen)
   289  
   290  	} else if cryptoJSON.KDF == "pbkdf2" {
   291  		c := ensureInt(cryptoJSON.KDFParams["c"])
   292  		prf := cryptoJSON.KDFParams["prf"].(string)
   293  		if prf != "hmac-sha256" {
   294  			return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf)
   295  		}
   296  		key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
   297  		return key, nil
   298  	}
   299  
   300  	return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF)
   301  }
   302  
   303  // TODO: can we do without this when unmarshalling dynamic JSON?
   304  // why do integers in KDF params end up as float64 and not int after
   305  // unmarshal?
   306  func ensureInt(x interface{}) int {
   307  	res, ok := x.(int)
   308  	if !ok {
   309  		res = int(x.(float64))
   310  	}
   311  	return res
   312  }