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