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