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