github.com/MetalBlockchain/subnet-evm@v0.4.9/accounts/keystore/passphrase.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2014 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  
    27  /*
    28  
    29  This key store behaves as KeyStorePlain with the difference that
    30  the private key is encrypted and on disk uses another JSON encoding.
    31  
    32  The crypto is documented at https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition
    33  
    34  */
    35  
    36  package keystore
    37  
    38  import (
    39  	"bytes"
    40  	"crypto/aes"
    41  	"crypto/rand"
    42  	"crypto/sha256"
    43  	"encoding/hex"
    44  	"encoding/json"
    45  	"fmt"
    46  	"io"
    47  	"os"
    48  	"path/filepath"
    49  
    50  	"github.com/MetalBlockchain/subnet-evm/accounts"
    51  	"github.com/ethereum/go-ethereum/common"
    52  	"github.com/ethereum/go-ethereum/common/math"
    53  	"github.com/ethereum/go-ethereum/crypto"
    54  	"github.com/google/uuid"
    55  	"golang.org/x/crypto/pbkdf2"
    56  	"golang.org/x/crypto/scrypt"
    57  )
    58  
    59  const (
    60  	keyHeaderKDF = "scrypt"
    61  
    62  	// StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB
    63  	// memory and taking approximately 1s CPU time on a modern processor.
    64  	StandardScryptN = 1 << 18
    65  
    66  	// StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB
    67  	// memory and taking approximately 1s CPU time on a modern processor.
    68  	StandardScryptP = 1
    69  
    70  	// LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB
    71  	// memory and taking approximately 100ms CPU time on a modern processor.
    72  	LightScryptN = 1 << 12
    73  
    74  	// LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB
    75  	// memory and taking approximately 100ms CPU time on a modern processor.
    76  	LightScryptP = 6
    77  
    78  	scryptR     = 8
    79  	scryptDKLen = 32
    80  )
    81  
    82  type keyStorePassphrase struct {
    83  	keysDirPath string
    84  	scryptN     int
    85  	scryptP     int
    86  	// skipKeyFileVerification disables the security-feature which does
    87  	// reads and decrypts any newly created keyfiles. This should be 'false' in all
    88  	// cases except tests -- setting this to 'true' is not recommended.
    89  	skipKeyFileVerification bool
    90  }
    91  
    92  func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) {
    93  	// Load the key from the keystore and decrypt its contents
    94  	keyjson, err := os.ReadFile(filename)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	key, err := DecryptKey(keyjson, auth)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	// Make sure we're really operating on the requested key (no swap attacks)
   103  	if key.Address != addr {
   104  		return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr)
   105  	}
   106  	return key, nil
   107  }
   108  
   109  // StoreKey generates a key, encrypts with 'auth' and stores in the given directory
   110  func StoreKey(dir, auth string, scryptN, scryptP int) (accounts.Account, error) {
   111  	_, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth)
   112  	return a, err
   113  }
   114  
   115  func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error {
   116  	keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)
   117  	if err != nil {
   118  		return err
   119  	}
   120  	// Write into temporary file
   121  	tmpName, err := writeTemporaryKeyFile(filename, keyjson)
   122  	if err != nil {
   123  		return err
   124  	}
   125  	if !ks.skipKeyFileVerification {
   126  		// Verify that we can decrypt the file with the given password.
   127  		_, err = ks.GetKey(key.Address, tmpName, auth)
   128  		if err != nil {
   129  			msg := "An error was encountered when saving and verifying the keystore file. \n" +
   130  				"This indicates that the keystore is corrupted. \n" +
   131  				"The corrupted file is stored at \n%v\n" +
   132  				"Please file a ticket at:\n\n" +
   133  				"https://github.com/ethereum/go-ethereum/issues." +
   134  				"The error was : %s"
   135  			//lint:ignore ST1005 This is a message for the user
   136  			return fmt.Errorf(msg, tmpName, err)
   137  		}
   138  	}
   139  	return os.Rename(tmpName, filename)
   140  }
   141  
   142  func (ks keyStorePassphrase) JoinPath(filename string) string {
   143  	if filepath.IsAbs(filename) {
   144  		return filename
   145  	}
   146  	return filepath.Join(ks.keysDirPath, filename)
   147  }
   148  
   149  // Encryptdata encrypts the data given as 'data' with the password 'auth'.
   150  func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
   151  	salt := make([]byte, 32)
   152  	if _, err := io.ReadFull(rand.Reader, salt); err != nil {
   153  		panic("reading from crypto/rand failed: " + err.Error())
   154  	}
   155  	derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen)
   156  	if err != nil {
   157  		return CryptoJSON{}, err
   158  	}
   159  	encryptKey := derivedKey[:16]
   160  
   161  	iv := make([]byte, aes.BlockSize) // 16
   162  	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
   163  		panic("reading from crypto/rand failed: " + err.Error())
   164  	}
   165  	cipherText, err := aesCTRXOR(encryptKey, data, iv)
   166  	if err != nil {
   167  		return CryptoJSON{}, err
   168  	}
   169  	mac := crypto.Keccak256(derivedKey[16:32], cipherText)
   170  
   171  	scryptParamsJSON := make(map[string]interface{}, 5)
   172  	scryptParamsJSON["n"] = scryptN
   173  	scryptParamsJSON["r"] = scryptR
   174  	scryptParamsJSON["p"] = scryptP
   175  	scryptParamsJSON["dklen"] = scryptDKLen
   176  	scryptParamsJSON["salt"] = hex.EncodeToString(salt)
   177  	cipherParamsJSON := cipherparamsJSON{
   178  		IV: hex.EncodeToString(iv),
   179  	}
   180  
   181  	cryptoStruct := CryptoJSON{
   182  		Cipher:       "aes-128-ctr",
   183  		CipherText:   hex.EncodeToString(cipherText),
   184  		CipherParams: cipherParamsJSON,
   185  		KDF:          keyHeaderKDF,
   186  		KDFParams:    scryptParamsJSON,
   187  		MAC:          hex.EncodeToString(mac),
   188  	}
   189  	return cryptoStruct, nil
   190  }
   191  
   192  // EncryptKey encrypts a key using the specified scrypt parameters into a json
   193  // blob that can be decrypted later on.
   194  func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
   195  	keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
   196  	cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP)
   197  	if err != nil {
   198  		return nil, err
   199  	}
   200  	encryptedKeyJSONV3 := encryptedKeyJSONV3{
   201  		hex.EncodeToString(key.Address[:]),
   202  		cryptoStruct,
   203  		key.Id.String(),
   204  		version,
   205  	}
   206  	return json.Marshal(encryptedKeyJSONV3)
   207  }
   208  
   209  // DecryptKey decrypts a key from a json blob, returning the private key itself.
   210  func DecryptKey(keyjson []byte, auth string) (*Key, error) {
   211  	// Parse the json into a simple map to fetch the key version
   212  	m := make(map[string]interface{})
   213  	if err := json.Unmarshal(keyjson, &m); err != nil {
   214  		return nil, err
   215  	}
   216  	// Depending on the version try to parse one way or another
   217  	var (
   218  		keyBytes, keyId []byte
   219  		err             error
   220  	)
   221  	if version, ok := m["version"].(string); ok && version == "1" {
   222  		k := new(encryptedKeyJSONV1)
   223  		if err := json.Unmarshal(keyjson, k); err != nil {
   224  			return nil, err
   225  		}
   226  		keyBytes, keyId, err = decryptKeyV1(k, auth)
   227  	} else {
   228  		k := new(encryptedKeyJSONV3)
   229  		if err := json.Unmarshal(keyjson, k); err != nil {
   230  			return nil, err
   231  		}
   232  		keyBytes, keyId, err = decryptKeyV3(k, auth)
   233  	}
   234  	// Handle any decryption errors and return the key
   235  	if err != nil {
   236  		return nil, err
   237  	}
   238  	key := crypto.ToECDSAUnsafe(keyBytes)
   239  	id, err := uuid.FromBytes(keyId)
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  	return &Key{
   244  		Id:         id,
   245  		Address:    crypto.PubkeyToAddress(key.PublicKey),
   246  		PrivateKey: key,
   247  	}, nil
   248  }
   249  
   250  func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
   251  	if cryptoJson.Cipher != "aes-128-ctr" {
   252  		return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher)
   253  	}
   254  	mac, err := hex.DecodeString(cryptoJson.MAC)
   255  	if err != nil {
   256  		return nil, err
   257  	}
   258  
   259  	iv, err := hex.DecodeString(cryptoJson.CipherParams.IV)
   260  	if err != nil {
   261  		return nil, err
   262  	}
   263  
   264  	cipherText, err := hex.DecodeString(cryptoJson.CipherText)
   265  	if err != nil {
   266  		return nil, err
   267  	}
   268  
   269  	derivedKey, err := getKDFKey(cryptoJson, auth)
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  
   274  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   275  	if !bytes.Equal(calculatedMAC, mac) {
   276  		return nil, ErrDecrypt
   277  	}
   278  
   279  	plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
   280  	if err != nil {
   281  		return nil, err
   282  	}
   283  	return plainText, err
   284  }
   285  
   286  func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
   287  	if keyProtected.Version != version {
   288  		return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version)
   289  	}
   290  	keyUUID, err := uuid.Parse(keyProtected.Id)
   291  	if err != nil {
   292  		return nil, nil, err
   293  	}
   294  	keyId = keyUUID[:]
   295  	plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
   296  	if err != nil {
   297  		return nil, nil, err
   298  	}
   299  	return plainText, keyId, err
   300  }
   301  
   302  func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
   303  	keyUUID, err := uuid.Parse(keyProtected.Id)
   304  	if err != nil {
   305  		return nil, nil, err
   306  	}
   307  	keyId = keyUUID[:]
   308  	mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
   309  	if err != nil {
   310  		return nil, nil, err
   311  	}
   312  
   313  	iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
   314  	if err != nil {
   315  		return nil, nil, err
   316  	}
   317  
   318  	cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
   319  	if err != nil {
   320  		return nil, nil, err
   321  	}
   322  
   323  	derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
   324  	if err != nil {
   325  		return nil, nil, err
   326  	}
   327  
   328  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   329  	if !bytes.Equal(calculatedMAC, mac) {
   330  		return nil, nil, ErrDecrypt
   331  	}
   332  
   333  	plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv)
   334  	if err != nil {
   335  		return nil, nil, err
   336  	}
   337  	return plainText, keyId, err
   338  }
   339  
   340  func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
   341  	authArray := []byte(auth)
   342  	salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
   343  	if err != nil {
   344  		return nil, err
   345  	}
   346  	dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
   347  
   348  	if cryptoJSON.KDF == keyHeaderKDF {
   349  		n := ensureInt(cryptoJSON.KDFParams["n"])
   350  		r := ensureInt(cryptoJSON.KDFParams["r"])
   351  		p := ensureInt(cryptoJSON.KDFParams["p"])
   352  		return scrypt.Key(authArray, salt, n, r, p, dkLen)
   353  	} else if cryptoJSON.KDF == "pbkdf2" {
   354  		c := ensureInt(cryptoJSON.KDFParams["c"])
   355  		prf := cryptoJSON.KDFParams["prf"].(string)
   356  		if prf != "hmac-sha256" {
   357  			return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf)
   358  		}
   359  		key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
   360  		return key, nil
   361  	}
   362  
   363  	return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF)
   364  }
   365  
   366  // TODO: can we do without this when unmarshalling dynamic JSON?
   367  // why do integers in KDF params end up as float64 and not int after
   368  // unmarshal?
   369  func ensureInt(x interface{}) int {
   370  	res, ok := x.(int)
   371  	if !ok {
   372  		res = int(x.(float64))
   373  	}
   374  	return res
   375  }