github.com/klaytn/klaytn@v1.10.2/accounts/keystore/keystore_passphrase.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2014 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from accounts/keystore/keystore_passphrase.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package keystore
    22  
    23  import (
    24  	"bytes"
    25  	"crypto/aes"
    26  	"crypto/ecdsa"
    27  	"crypto/rand"
    28  	"crypto/sha256"
    29  	"encoding/hex"
    30  	"encoding/json"
    31  	"fmt"
    32  	"io"
    33  	"io/ioutil"
    34  	"os"
    35  	"path/filepath"
    36  
    37  	"github.com/klaytn/klaytn/common"
    38  	"github.com/klaytn/klaytn/common/math"
    39  	"github.com/klaytn/klaytn/crypto"
    40  	"github.com/pborman/uuid"
    41  	"golang.org/x/crypto/pbkdf2"
    42  	"golang.org/x/crypto/scrypt"
    43  )
    44  
    45  const (
    46  	keyHeaderKDF = "scrypt"
    47  
    48  	// StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB
    49  	// memory and taking approximately 1s CPU time on a modern processor.
    50  	StandardScryptN = 1 << 18
    51  
    52  	// StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB
    53  	// memory and taking approximately 1s CPU time on a modern processor.
    54  	StandardScryptP = 1
    55  
    56  	// LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB
    57  	// memory and taking approximately 100ms CPU time on a modern processor.
    58  	LightScryptN = 1 << 12
    59  
    60  	// LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB
    61  	// memory and taking approximately 100ms CPU time on a modern processor.
    62  	LightScryptP = 6
    63  
    64  	scryptR     = 8
    65  	scryptDKLen = 32
    66  )
    67  
    68  type keyStorePassphrase struct {
    69  	keysDirPath string
    70  	scryptN     int
    71  	scryptP     int
    72  	// skipKeyFileVerification disables the security-feature which does
    73  	// reads and decrypts any newly created keyfiles. This should be 'false' in all
    74  	// cases except tests -- setting this to 'true' is not recommended.
    75  	skipKeyFileVerification bool
    76  }
    77  
    78  func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (Key, error) {
    79  	// Load the key from the keystore and decrypt its contents
    80  	keyjson, err := ioutil.ReadFile(filename)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	key, err := DecryptKey(keyjson, auth)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	// Make sure we're really operating on the requested key (no swap attacks)
    89  	if key.GetAddress() != addr {
    90  		return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.GetAddress(), addr)
    91  	}
    92  	return key, nil
    93  }
    94  
    95  // StoreKey generates a key, encrypts with 'auth' and stores in the given directory
    96  func StoreKey(dir, auth string, scryptN, scryptP int) (common.Address, error) {
    97  	_, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth)
    98  	return a.Address, err
    99  }
   100  
   101  func (ks keyStorePassphrase) StoreKey(filename string, key Key, auth string) error {
   102  	keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)
   103  	if err != nil {
   104  		return err
   105  	}
   106  	// Write into temporary file
   107  	tmpName, err := writeTemporaryKeyFile(filename, keyjson)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	if ks.skipKeyFileVerification == false { // do not skip file verification
   112  		// Verify that we can decrypt the file with the given password.
   113  		_, err = ks.GetKey(key.GetAddress(), tmpName, auth)
   114  		if err != nil {
   115  			msg := "An error was encountered when saving and verifying the keystore file. \n" +
   116  				"This indicates that the keystore is corrupted. \n" +
   117  				"The corrupted file is stored at \n%v\n" +
   118  				"Please file a ticket at:\n\n" +
   119  				"https://github.com/klaytn/klaytn/issues" +
   120  				"The error was : %w"
   121  			return fmt.Errorf(msg, tmpName, err)
   122  		}
   123  	}
   124  	return os.Rename(tmpName, filename)
   125  }
   126  
   127  func (ks keyStorePassphrase) JoinPath(filename string) string {
   128  	if filepath.IsAbs(filename) {
   129  		return filename
   130  	}
   131  	return filepath.Join(ks.keysDirPath, filename)
   132  }
   133  
   134  // encryptCrypto encrypts a private key to a cryptoJSON object.
   135  func encryptCrypto(keyBytes []byte, auth string, scryptN, scryptP int) (*cryptoJSON, error) {
   136  	authArray := []byte(auth)
   137  
   138  	salt := make([]byte, 32)
   139  	if _, err := io.ReadFull(rand.Reader, salt); err != nil {
   140  		panic("reading from crypto/rand failed: " + err.Error())
   141  	}
   142  	derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen)
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  	encryptKey := derivedKey[:16]
   147  
   148  	iv := make([]byte, aes.BlockSize) // 16
   149  	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
   150  		panic("reading from crypto/rand failed: " + err.Error())
   151  	}
   152  	cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	mac := crypto.Keccak256(derivedKey[16:32], cipherText)
   157  
   158  	scryptParamsJSON := make(map[string]interface{}, 5)
   159  	scryptParamsJSON["n"] = scryptN
   160  	scryptParamsJSON["r"] = scryptR
   161  	scryptParamsJSON["p"] = scryptP
   162  	scryptParamsJSON["dklen"] = scryptDKLen
   163  	scryptParamsJSON["salt"] = hex.EncodeToString(salt)
   164  
   165  	cipherParamsJSON := cipherparamsJSON{
   166  		IV: hex.EncodeToString(iv),
   167  	}
   168  
   169  	return &cryptoJSON{
   170  		Cipher:       "aes-128-ctr",
   171  		CipherText:   hex.EncodeToString(cipherText),
   172  		CipherParams: cipherParamsJSON,
   173  		KDF:          keyHeaderKDF,
   174  		KDFParams:    scryptParamsJSON,
   175  		MAC:          hex.EncodeToString(mac),
   176  	}, nil
   177  }
   178  
   179  // EncryptKey encrypts a key using the specified scrypt parameters into a json
   180  // blob that can be decrypted later on. It uses the keystore v4 format.
   181  func EncryptKey(key Key, auth string, scryptN, scryptP int) ([]byte, error) {
   182  	pks := key.GetPrivateKeys()
   183  	crypto := make([][]cryptoJSON, len(pks))
   184  	for i, keys := range pks {
   185  		crypto[i] = make([]cryptoJSON, len(keys))
   186  		for j, k := range keys {
   187  			keyBytes := math.PaddedBigBytes(k.D, 32)
   188  			c, err := encryptCrypto(keyBytes, auth, scryptN, scryptP)
   189  			if err != nil {
   190  				return nil, err
   191  			}
   192  			crypto[i][j] = *c
   193  		}
   194  	}
   195  	encryptedKeyJSONV4 := encryptedKeyJSONV4{
   196  		hex.EncodeToString(key.GetAddress().Bytes()),
   197  		crypto,
   198  		key.GetId().String(),
   199  		4,
   200  	}
   201  	return json.Marshal(encryptedKeyJSONV4)
   202  }
   203  
   204  // EncryptKeyV3 encrypts a key using the specified scrypt parameters into a json
   205  // blob that can be decrypted later on. It uses the keystore v3 format.
   206  func EncryptKeyV3(key Key, auth string, scryptN, scryptP int) ([]byte, error) {
   207  	authArray := []byte(auth)
   208  
   209  	salt := make([]byte, 32)
   210  	if _, err := io.ReadFull(rand.Reader, salt); err != nil {
   211  		panic("reading from crypto/rand failed: " + err.Error())
   212  	}
   213  	derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen)
   214  	if err != nil {
   215  		return nil, err
   216  	}
   217  	encryptKey := derivedKey[:16]
   218  	keyBytes := math.PaddedBigBytes(key.GetPrivateKey().D, 32)
   219  
   220  	iv := make([]byte, aes.BlockSize) // 16
   221  	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
   222  		panic("reading from crypto/rand failed: " + err.Error())
   223  	}
   224  	cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv)
   225  	if err != nil {
   226  		return nil, err
   227  	}
   228  	mac := crypto.Keccak256(derivedKey[16:32], cipherText)
   229  
   230  	scryptParamsJSON := make(map[string]interface{}, 5)
   231  	scryptParamsJSON["n"] = scryptN
   232  	scryptParamsJSON["r"] = scryptR
   233  	scryptParamsJSON["p"] = scryptP
   234  	scryptParamsJSON["dklen"] = scryptDKLen
   235  	scryptParamsJSON["salt"] = hex.EncodeToString(salt)
   236  
   237  	cipherParamsJSON := cipherparamsJSON{
   238  		IV: hex.EncodeToString(iv),
   239  	}
   240  
   241  	cryptoStruct := cryptoJSON{
   242  		Cipher:       "aes-128-ctr",
   243  		CipherText:   hex.EncodeToString(cipherText),
   244  		CipherParams: cipherParamsJSON,
   245  		KDF:          keyHeaderKDF,
   246  		KDFParams:    scryptParamsJSON,
   247  		MAC:          hex.EncodeToString(mac),
   248  	}
   249  	encryptedKeyJSONV3 := encryptedKeyJSONV3{
   250  		hex.EncodeToString(key.GetAddress().Bytes()),
   251  		cryptoStruct,
   252  		key.GetId().String(),
   253  		3,
   254  	}
   255  	return json.Marshal(encryptedKeyJSONV3)
   256  }
   257  
   258  // DecryptKey decrypts a key from a json blob, returning the private key itself.
   259  // TODO: use encryptedKeyJSON object directly instead of double unmarshalling.
   260  func DecryptKey(keyjson []byte, auth string) (Key, error) {
   261  	// Parse the json into a simple map to fetch the key version
   262  	m := make(map[string]interface{})
   263  	if err := json.Unmarshal(keyjson, &m); err != nil {
   264  		return nil, err
   265  	}
   266  	// Depending on the version try to parse one way or another
   267  	var (
   268  		keyBytes [][][]byte
   269  		keyId    []byte
   270  		err      error
   271  		address  common.Address
   272  	)
   273  
   274  	switch v := m["version"].(type) {
   275  	case string:
   276  		if v == "1" {
   277  			k := new(encryptedKeyJSONV1)
   278  			if err := json.Unmarshal(keyjson, k); err != nil {
   279  				return nil, err
   280  			}
   281  			keyBytes = make([][][]byte, 1)
   282  			keyBytes[0] = make([][]byte, 1)
   283  			keyBytes[0][0], keyId, err = decryptKeyV1(k, auth)
   284  			address = common.HexToAddress(k.Address)
   285  		}
   286  
   287  	case float64:
   288  		switch v {
   289  		case 3:
   290  			k := new(encryptedKeyJSONV3)
   291  			if err := json.Unmarshal(keyjson, k); err != nil {
   292  				return nil, err
   293  			}
   294  			keyBytes = make([][][]byte, 1)
   295  			keyBytes[0] = make([][]byte, 1)
   296  			keyBytes[0][0], keyId, err = decryptKeyV3(k, auth)
   297  			address = common.HexToAddress(k.Address)
   298  		case 4:
   299  			// At first, try to decrypt using encryptedKeyJSONV4
   300  			k := new(encryptedKeyJSONV4)
   301  			if err := json.Unmarshal(keyjson, k); err != nil {
   302  				// If it fails, try to decrypt using encryptedKeyJSONV4Single
   303  				kSingle := new(encryptedKeyJSONV4Single)
   304  				if err = json.Unmarshal(keyjson, kSingle); err != nil {
   305  					return nil, err
   306  				}
   307  
   308  				// If succeeded, copy the values of kSingle to k
   309  				k.Id = kSingle.Id
   310  				k.Address = kSingle.Address
   311  				k.Keyring = [][]cryptoJSON{kSingle.Keyring}
   312  				k.Version = kSingle.Version
   313  			}
   314  
   315  			keyBytes, keyId, err = decryptKeyV4(k, auth)
   316  			address = common.HexToAddress(k.Address)
   317  		default:
   318  			return nil, fmt.Errorf("undefined version: %f", v)
   319  		}
   320  
   321  	default:
   322  		return nil, fmt.Errorf("undefined type of version: %s", m)
   323  	}
   324  
   325  	// Handle any decryption errors and return the key
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  	privateKeys := make([][]*ecdsa.PrivateKey, len(keyBytes))
   330  	for i, keys := range keyBytes {
   331  		privateKeys[i] = make([]*ecdsa.PrivateKey, len(keys))
   332  		for j, key := range keys {
   333  			privateKeys[i][j], err = crypto.ToECDSA(key)
   334  			if err != nil {
   335  				return nil, err
   336  			}
   337  		}
   338  	}
   339  
   340  	return &KeyV4{
   341  		Id:          uuid.UUID(keyId),
   342  		Address:     address,
   343  		PrivateKeys: privateKeys,
   344  	}, nil
   345  }
   346  
   347  func decryptKeyV4(keyProtected *encryptedKeyJSONV4, auth string) (keyBytes [][][]byte, keyId []byte, err error) {
   348  	if keyProtected.Version != 4 {
   349  		return nil, nil, fmt.Errorf("version not supported: %v (should be 4)", keyProtected.Version)
   350  	}
   351  
   352  	keyId = uuid.Parse(keyProtected.Id)
   353  	keyBytes = make([][][]byte, len(keyProtected.Keyring))
   354  
   355  	for i, keys := range keyProtected.Keyring {
   356  		keyBytes[i] = make([][]byte, len(keys))
   357  		for j, key := range keys {
   358  			keyBytes[i][j], err = decryptKey(key, auth)
   359  			if err != nil {
   360  				return nil, nil, err
   361  			}
   362  		}
   363  	}
   364  
   365  	return keyBytes, keyId, err
   366  }
   367  
   368  func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
   369  	if keyProtected.Version != 3 {
   370  		return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
   371  	}
   372  	keyId = uuid.Parse(keyProtected.Id)
   373  
   374  	plainText, err := decryptKey(keyProtected.Crypto, auth)
   375  	if err != nil {
   376  		return nil, nil, err
   377  	}
   378  	return plainText, keyId, err
   379  }
   380  
   381  func decryptKey(cryptoJson cryptoJSON, auth string) (keyBytes []byte, err error) {
   382  	if cryptoJson.Cipher != "aes-128-ctr" {
   383  		return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher)
   384  	}
   385  
   386  	mac, err := hex.DecodeString(cryptoJson.MAC)
   387  	if err != nil {
   388  		return nil, err
   389  	}
   390  
   391  	iv, err := hex.DecodeString(cryptoJson.CipherParams.IV)
   392  	if err != nil {
   393  		return nil, err
   394  	}
   395  
   396  	cipherText, err := hex.DecodeString(cryptoJson.CipherText)
   397  	if err != nil {
   398  		return nil, err
   399  	}
   400  
   401  	derivedKey, err := getKDFKey(cryptoJson, auth)
   402  	if err != nil {
   403  		return nil, err
   404  	}
   405  
   406  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   407  	if !bytes.Equal(calculatedMAC, mac) {
   408  		return nil, ErrDecrypt
   409  	}
   410  
   411  	plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
   412  	if err != nil {
   413  		return nil, err
   414  	}
   415  
   416  	return plainText, err
   417  }
   418  
   419  func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
   420  	keyId = uuid.Parse(keyProtected.Id)
   421  	mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
   422  	if err != nil {
   423  		return nil, nil, err
   424  	}
   425  
   426  	iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
   427  	if err != nil {
   428  		return nil, nil, err
   429  	}
   430  
   431  	cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
   432  	if err != nil {
   433  		return nil, nil, err
   434  	}
   435  
   436  	derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
   437  	if err != nil {
   438  		return nil, nil, err
   439  	}
   440  
   441  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   442  	if !bytes.Equal(calculatedMAC, mac) {
   443  		return nil, nil, ErrDecrypt
   444  	}
   445  
   446  	plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv)
   447  	if err != nil {
   448  		return nil, nil, err
   449  	}
   450  	return plainText, keyId, err
   451  }
   452  
   453  func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) {
   454  	authArray := []byte(auth)
   455  	salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
   456  	if err != nil {
   457  		return nil, err
   458  	}
   459  	dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
   460  
   461  	if cryptoJSON.KDF == keyHeaderKDF {
   462  		n := ensureInt(cryptoJSON.KDFParams["n"])
   463  		r := ensureInt(cryptoJSON.KDFParams["r"])
   464  		p := ensureInt(cryptoJSON.KDFParams["p"])
   465  		return scrypt.Key(authArray, salt, n, r, p, dkLen)
   466  
   467  	} else if cryptoJSON.KDF == "pbkdf2" {
   468  		c := ensureInt(cryptoJSON.KDFParams["c"])
   469  		prf := cryptoJSON.KDFParams["prf"].(string)
   470  		if prf != "hmac-sha256" {
   471  			return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf)
   472  		}
   473  		key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
   474  		return key, nil
   475  	}
   476  
   477  	return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF)
   478  }
   479  
   480  // TODO: can we do without this when unmarshalling dynamic JSON?
   481  // why do integers in KDF params end up as float64 and not int after
   482  // unmarshal?
   483  func ensureInt(x interface{}) int {
   484  	res, ok := x.(int)
   485  	if !ok {
   486  		res = int(x.(float64))
   487  	}
   488  	return res
   489  }