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