github.com/Gessiux/neatchain@v1.3.1/chain/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/Gessiux/neatchain/utilities/common"
    40  	"github.com/Gessiux/neatchain/utilities/common/math"
    41  	"github.com/Gessiux/neatchain/utilities/crypto"
    42  	"github.com/Gessiux/neatchain/utilities/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  		key.Address.String(),
   157  		cryptoStruct,
   158  		key.Id.String(),
   159  		version,
   160  	}
   161  	return json.Marshal(encryptedKeyJSONV3)
   162  }
   163  
   164  // DecryptKey decrypts a key from a json blob, returning the private key itself.
   165  func DecryptKey(keyjson []byte, auth string) (*Key, error) {
   166  	// Parse the json into a simple map to fetch the key version
   167  	m := make(map[string]interface{})
   168  	if err := json.Unmarshal(keyjson, &m); err != nil {
   169  		return nil, err
   170  	}
   171  	// Depending on the version try to parse one way or another
   172  	var (
   173  		keyBytes, keyId []byte
   174  		err             error
   175  	)
   176  	if version, ok := m["version"].(string); ok && version == "1" {
   177  		k := new(encryptedKeyJSONV1)
   178  		if err := json.Unmarshal(keyjson, k); err != nil {
   179  			return nil, err
   180  		}
   181  		keyBytes, keyId, err = decryptKeyV1(k, auth)
   182  	} else {
   183  		k := new(encryptedKeyJSONV3)
   184  		if err := json.Unmarshal(keyjson, k); err != nil {
   185  			return nil, err
   186  		}
   187  		keyBytes, keyId, err = decryptKeyV3(k, auth)
   188  	}
   189  	// Handle any decryption errors and return the key
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  	key := crypto.ToECDSAUnsafe(keyBytes)
   194  
   195  	return &Key{
   196  		Id:         uuid.UUID(keyId),
   197  		Address:    crypto.PubkeyToAddress(key.PublicKey),
   198  		PrivateKey: key,
   199  	}, nil
   200  }
   201  
   202  func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
   203  	if keyProtected.Version != version {
   204  		return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
   205  	}
   206  
   207  	if keyProtected.Crypto.Cipher != "aes-128-ctr" {
   208  		return nil, nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher)
   209  	}
   210  
   211  	keyId = uuid.Parse(keyProtected.Id)
   212  	mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
   213  	if err != nil {
   214  		return nil, nil, err
   215  	}
   216  
   217  	iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
   218  	if err != nil {
   219  		return nil, nil, err
   220  	}
   221  
   222  	cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
   223  	if err != nil {
   224  		return nil, nil, err
   225  	}
   226  
   227  	derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
   228  	if err != nil {
   229  		return nil, nil, err
   230  	}
   231  
   232  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   233  	if !bytes.Equal(calculatedMAC, mac) {
   234  		return nil, nil, ErrDecrypt
   235  	}
   236  
   237  	plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
   238  	if err != nil {
   239  		return nil, nil, err
   240  	}
   241  	return plainText, keyId, err
   242  }
   243  
   244  func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
   245  	keyId = uuid.Parse(keyProtected.Id)
   246  	mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
   247  	if err != nil {
   248  		return nil, nil, err
   249  	}
   250  
   251  	iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
   252  	if err != nil {
   253  		return nil, nil, err
   254  	}
   255  
   256  	cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
   257  	if err != nil {
   258  		return nil, nil, err
   259  	}
   260  
   261  	derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
   262  	if err != nil {
   263  		return nil, nil, err
   264  	}
   265  
   266  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   267  	if !bytes.Equal(calculatedMAC, mac) {
   268  		return nil, nil, ErrDecrypt
   269  	}
   270  
   271  	plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv)
   272  	if err != nil {
   273  		return nil, nil, err
   274  	}
   275  	return plainText, keyId, err
   276  }
   277  
   278  func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) {
   279  	authArray := []byte(auth)
   280  	salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
   281  	if err != nil {
   282  		return nil, err
   283  	}
   284  	dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
   285  
   286  	if cryptoJSON.KDF == keyHeaderKDF {
   287  		n := ensureInt(cryptoJSON.KDFParams["n"])
   288  		r := ensureInt(cryptoJSON.KDFParams["r"])
   289  		p := ensureInt(cryptoJSON.KDFParams["p"])
   290  		return scrypt.Key(authArray, salt, n, r, p, dkLen)
   291  
   292  	} else if cryptoJSON.KDF == "pbkdf2" {
   293  		c := ensureInt(cryptoJSON.KDFParams["c"])
   294  		prf := cryptoJSON.KDFParams["prf"].(string)
   295  		if prf != "hmac-sha256" {
   296  			return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf)
   297  		}
   298  		key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
   299  		return key, nil
   300  	}
   301  
   302  	return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF)
   303  }
   304  
   305  // TODO: can we do without this when unmarshalling dynamic JSON?
   306  // why do integers in KDF params end up as float64 and not int after
   307  // unmarshal?
   308  func ensureInt(x interface{}) int {
   309  	res, ok := x.(int)
   310  	if !ok {
   311  		res = int(x.(float64))
   312  	}
   313  	return res
   314  }