github.com/aiyaya188/klaytn@v0.0.0-20220629133911-2c66fd5546f4/accounts/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  	"os"
    38  	"path/filepath"
    39  
    40  	"github.com/aiyaya188/klaytn/accounts"
    41  	"github.com/aiyaya188/klaytn/common"
    42  	"github.com/aiyaya188/klaytn/common/math"
    43  	"github.com/aiyaya188/klaytn/crypto"
    44  	"github.com/google/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 := os.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) (accounts.Account, error) {
   101  	_, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth)
   102  	return a, 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/aiyaya188/klaytn/issues." +
   124  				"The error was : %s"
   125  			//lint:ignore ST1005 This is a message for the user
   126  			return fmt.Errorf(msg, tmpName, err)
   127  		}
   128  	}
   129  	return os.Rename(tmpName, filename)
   130  }
   131  
   132  func (ks keyStorePassphrase) JoinPath(filename string) string {
   133  	if filepath.IsAbs(filename) {
   134  		return filename
   135  	}
   136  	return filepath.Join(ks.keysDirPath, filename)
   137  }
   138  
   139  // Encryptdata encrypts the data given as 'data' with the password 'auth'.
   140  func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
   141  
   142  	salt := make([]byte, 32)
   143  	if _, err := io.ReadFull(rand.Reader, salt); err != nil {
   144  		panic("reading from crypto/rand failed: " + err.Error())
   145  	}
   146  	derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen)
   147  	if err != nil {
   148  		return CryptoJSON{}, err
   149  	}
   150  	encryptKey := derivedKey[:16]
   151  
   152  	iv := make([]byte, aes.BlockSize) // 16
   153  	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
   154  		panic("reading from crypto/rand failed: " + err.Error())
   155  	}
   156  	cipherText, err := aesCTRXOR(encryptKey, data, iv)
   157  	if err != nil {
   158  		return CryptoJSON{}, err
   159  	}
   160  	mac := crypto.Keccak256(derivedKey[16:32], cipherText)
   161  
   162  	scryptParamsJSON := make(map[string]interface{}, 5)
   163  	scryptParamsJSON["n"] = scryptN
   164  	scryptParamsJSON["r"] = scryptR
   165  	scryptParamsJSON["p"] = scryptP
   166  	scryptParamsJSON["dklen"] = scryptDKLen
   167  	scryptParamsJSON["salt"] = hex.EncodeToString(salt)
   168  	cipherParamsJSON := cipherparamsJSON{
   169  		IV: hex.EncodeToString(iv),
   170  	}
   171  
   172  	cryptoStruct := CryptoJSON{
   173  		Cipher:       "aes-128-ctr",
   174  		CipherText:   hex.EncodeToString(cipherText),
   175  		CipherParams: cipherParamsJSON,
   176  		KDF:          keyHeaderKDF,
   177  		KDFParams:    scryptParamsJSON,
   178  		MAC:          hex.EncodeToString(mac),
   179  	}
   180  	return cryptoStruct, nil
   181  }
   182  
   183  // EncryptKey encrypts a key using the specified scrypt parameters into a json
   184  // blob that can be decrypted later on.
   185  func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
   186  	keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
   187  	cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  	encryptedKeyJSONV3 := encryptedKeyJSONV3{
   192  		hex.EncodeToString(key.Address[:]),
   193  		cryptoStruct,
   194  		key.Id.String(),
   195  		version,
   196  	}
   197  	return json.Marshal(encryptedKeyJSONV3)
   198  }
   199  
   200  // DecryptKey decrypts a key from a json blob, returning the private key itself.
   201  func DecryptKey(keyjson []byte, auth string) (*Key, error) {
   202  	// Parse the json into a simple map to fetch the key version
   203  	m := make(map[string]interface{})
   204  	if err := json.Unmarshal(keyjson, &m); err != nil {
   205  		return nil, err
   206  	}
   207  	// Depending on the version try to parse one way or another
   208  	var (
   209  		keyBytes, keyId []byte
   210  		err             error
   211  	)
   212  	if version, ok := m["version"].(string); ok && version == "1" {
   213  		k := new(encryptedKeyJSONV1)
   214  		if err := json.Unmarshal(keyjson, k); err != nil {
   215  			return nil, err
   216  		}
   217  		keyBytes, keyId, err = decryptKeyV1(k, auth)
   218  	} else {
   219  		k := new(encryptedKeyJSONV3)
   220  		if err := json.Unmarshal(keyjson, k); err != nil {
   221  			return nil, err
   222  		}
   223  		keyBytes, keyId, err = decryptKeyV3(k, auth)
   224  	}
   225  	// Handle any decryption errors and return the key
   226  	if err != nil {
   227  		return nil, err
   228  	}
   229  	key := crypto.ToECDSAUnsafe(keyBytes)
   230  	id, err := uuid.FromBytes(keyId)
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  	return &Key{
   235  		Id:         id,
   236  		Address:    crypto.PubkeyToAddress(key.PublicKey),
   237  		PrivateKey: key,
   238  	}, nil
   239  }
   240  
   241  func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
   242  	if cryptoJson.Cipher != "aes-128-ctr" {
   243  		return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher)
   244  	}
   245  	mac, err := hex.DecodeString(cryptoJson.MAC)
   246  	if err != nil {
   247  		return nil, err
   248  	}
   249  
   250  	iv, err := hex.DecodeString(cryptoJson.CipherParams.IV)
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  
   255  	cipherText, err := hex.DecodeString(cryptoJson.CipherText)
   256  	if err != nil {
   257  		return nil, err
   258  	}
   259  
   260  	derivedKey, err := getKDFKey(cryptoJson, auth)
   261  	if err != nil {
   262  		return nil, err
   263  	}
   264  
   265  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   266  	if !bytes.Equal(calculatedMAC, mac) {
   267  		return nil, ErrDecrypt
   268  	}
   269  
   270  	plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  	return plainText, err
   275  }
   276  
   277  func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
   278  	if keyProtected.Version != version {
   279  		return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version)
   280  	}
   281  	keyUUID, err := uuid.Parse(keyProtected.Id)
   282  	if err != nil {
   283  		return nil, nil, err
   284  	}
   285  	keyId = keyUUID[:]
   286  	plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
   287  	if err != nil {
   288  		return nil, nil, err
   289  	}
   290  	return plainText, keyId, err
   291  }
   292  
   293  func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
   294  	keyUUID, err := uuid.Parse(keyProtected.Id)
   295  	if err != nil {
   296  		return nil, nil, err
   297  	}
   298  	keyId = keyUUID[:]
   299  	mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
   300  	if err != nil {
   301  		return nil, nil, err
   302  	}
   303  
   304  	iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
   305  	if err != nil {
   306  		return nil, nil, err
   307  	}
   308  
   309  	cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
   310  	if err != nil {
   311  		return nil, nil, err
   312  	}
   313  
   314  	derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
   315  	if err != nil {
   316  		return nil, nil, err
   317  	}
   318  
   319  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   320  	if !bytes.Equal(calculatedMAC, mac) {
   321  		return nil, nil, ErrDecrypt
   322  	}
   323  
   324  	plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv)
   325  	if err != nil {
   326  		return nil, nil, err
   327  	}
   328  	return plainText, keyId, err
   329  }
   330  
   331  func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
   332  	authArray := []byte(auth)
   333  	salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
   334  	if err != nil {
   335  		return nil, err
   336  	}
   337  	dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
   338  
   339  	if cryptoJSON.KDF == keyHeaderKDF {
   340  		n := ensureInt(cryptoJSON.KDFParams["n"])
   341  		r := ensureInt(cryptoJSON.KDFParams["r"])
   342  		p := ensureInt(cryptoJSON.KDFParams["p"])
   343  		return scrypt.Key(authArray, salt, n, r, p, dkLen)
   344  
   345  	} else if cryptoJSON.KDF == "pbkdf2" {
   346  		c := ensureInt(cryptoJSON.KDFParams["c"])
   347  		prf := cryptoJSON.KDFParams["prf"].(string)
   348  		if prf != "hmac-sha256" {
   349  			return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf)
   350  		}
   351  		key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
   352  		return key, nil
   353  	}
   354  
   355  	return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF)
   356  }
   357  
   358  // TODO: can we do without this when unmarshalling dynamic JSON?
   359  // why do integers in KDF params end up as float64 and not int after
   360  // unmarshal?
   361  func ensureInt(x interface{}) int {
   362  	res, ok := x.(int)
   363  	if !ok {
   364  		res = int(x.(float64))
   365  	}
   366  	return res
   367  }