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