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