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