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