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