github.com/luckypickle/go-ethereum-vet@v1.14.2/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  	"crypto/rand"
    32  	"crypto/sha256"
    33  	"encoding/hex"
    34  	"encoding/json"
    35  	"fmt"
    36  	"io"
    37  	"io/ioutil"
    38  	"path/filepath"
    39  
    40  	"github.com/luckypickle/go-ethereum-vet/common"
    41  	"github.com/luckypickle/go-ethereum-vet/common/math"
    42  	"github.com/luckypickle/go-ethereum-vet/crypto"
    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}, rand.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  	}
   112  	return filepath.Join(ks.keysDirPath, filename)
   113  }
   114  
   115  // EncryptKey encrypts a key using the specified scrypt parameters into a json
   116  // blob that can be decrypted later on.
   117  func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
   118  	authArray := []byte(auth)
   119  
   120  	salt := make([]byte, 32)
   121  	if _, err := io.ReadFull(rand.Reader, salt); err != nil {
   122  		panic("reading from crypto/rand failed: " + err.Error())
   123  	}
   124  	derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	encryptKey := derivedKey[:16]
   129  	keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
   130  
   131  	iv := make([]byte, aes.BlockSize) // 16
   132  	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
   133  		panic("reading from crypto/rand failed: " + err.Error())
   134  	}
   135  	cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv)
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  	mac := crypto.Keccak256(derivedKey[16:32], cipherText)
   140  
   141  	scryptParamsJSON := make(map[string]interface{}, 5)
   142  	scryptParamsJSON["n"] = scryptN
   143  	scryptParamsJSON["r"] = scryptR
   144  	scryptParamsJSON["p"] = scryptP
   145  	scryptParamsJSON["dklen"] = scryptDKLen
   146  	scryptParamsJSON["salt"] = hex.EncodeToString(salt)
   147  
   148  	cipherParamsJSON := cipherparamsJSON{
   149  		IV: hex.EncodeToString(iv),
   150  	}
   151  
   152  	cryptoStruct := cryptoJSON{
   153  		Cipher:       "aes-128-ctr",
   154  		CipherText:   hex.EncodeToString(cipherText),
   155  		CipherParams: cipherParamsJSON,
   156  		KDF:          keyHeaderKDF,
   157  		KDFParams:    scryptParamsJSON,
   158  		MAC:          hex.EncodeToString(mac),
   159  	}
   160  	encryptedKeyJSONV3 := encryptedKeyJSONV3{
   161  		hex.EncodeToString(key.Address[:]),
   162  		cryptoStruct,
   163  		key.Id.String(),
   164  		version,
   165  	}
   166  	return json.Marshal(encryptedKeyJSONV3)
   167  }
   168  
   169  // DecryptKey decrypts a key from a json blob, returning the private key itself.
   170  func DecryptKey(keyjson []byte, auth string) (*Key, error) {
   171  	// Parse the json into a simple map to fetch the key version
   172  	m := make(map[string]interface{})
   173  	if err := json.Unmarshal(keyjson, &m); err != nil {
   174  		return nil, err
   175  	}
   176  	// Depending on the version try to parse one way or another
   177  	var (
   178  		keyBytes, keyId []byte
   179  		err             error
   180  	)
   181  	if version, ok := m["version"].(string); ok && version == "1" {
   182  		k := new(encryptedKeyJSONV1)
   183  		if err := json.Unmarshal(keyjson, k); err != nil {
   184  			return nil, err
   185  		}
   186  		keyBytes, keyId, err = decryptKeyV1(k, auth)
   187  	} else {
   188  		k := new(encryptedKeyJSONV3)
   189  		if err := json.Unmarshal(keyjson, k); err != nil {
   190  			return nil, err
   191  		}
   192  		keyBytes, keyId, err = decryptKeyV3(k, auth)
   193  	}
   194  	// Handle any decryption errors and return the key
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  	key := crypto.ToECDSAUnsafe(keyBytes)
   199  
   200  	return &Key{
   201  		Id:         uuid.UUID(keyId),
   202  		Address:    crypto.PubkeyToAddress(key.PublicKey),
   203  		PrivateKey: key,
   204  	}, nil
   205  }
   206  
   207  func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
   208  	if keyProtected.Version != version {
   209  		return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
   210  	}
   211  
   212  	if keyProtected.Crypto.Cipher != "aes-128-ctr" {
   213  		return nil, nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher)
   214  	}
   215  
   216  	keyId = uuid.Parse(keyProtected.Id)
   217  	mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
   218  	if err != nil {
   219  		return nil, nil, err
   220  	}
   221  
   222  	iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
   223  	if err != nil {
   224  		return nil, nil, err
   225  	}
   226  
   227  	cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
   228  	if err != nil {
   229  		return nil, nil, err
   230  	}
   231  
   232  	derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
   233  	if err != nil {
   234  		return nil, nil, err
   235  	}
   236  
   237  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   238  	if !bytes.Equal(calculatedMAC, mac) {
   239  		return nil, nil, ErrDecrypt
   240  	}
   241  
   242  	plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
   243  	if err != nil {
   244  		return nil, nil, err
   245  	}
   246  	return plainText, keyId, err
   247  }
   248  
   249  func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
   250  	keyId = uuid.Parse(keyProtected.Id)
   251  	mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
   252  	if err != nil {
   253  		return nil, nil, err
   254  	}
   255  
   256  	iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
   257  	if err != nil {
   258  		return nil, nil, err
   259  	}
   260  
   261  	cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
   262  	if err != nil {
   263  		return nil, nil, err
   264  	}
   265  
   266  	derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
   267  	if err != nil {
   268  		return nil, nil, err
   269  	}
   270  
   271  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   272  	if !bytes.Equal(calculatedMAC, mac) {
   273  		return nil, nil, ErrDecrypt
   274  	}
   275  
   276  	plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv)
   277  	if err != nil {
   278  		return nil, nil, err
   279  	}
   280  	return plainText, keyId, err
   281  }
   282  
   283  func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) {
   284  	authArray := []byte(auth)
   285  	salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
   286  	if err != nil {
   287  		return nil, err
   288  	}
   289  	dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
   290  
   291  	if cryptoJSON.KDF == keyHeaderKDF {
   292  		n := ensureInt(cryptoJSON.KDFParams["n"])
   293  		r := ensureInt(cryptoJSON.KDFParams["r"])
   294  		p := ensureInt(cryptoJSON.KDFParams["p"])
   295  		return scrypt.Key(authArray, salt, n, r, p, dkLen)
   296  
   297  	} else if cryptoJSON.KDF == "pbkdf2" {
   298  		c := ensureInt(cryptoJSON.KDFParams["c"])
   299  		prf := cryptoJSON.KDFParams["prf"].(string)
   300  		if prf != "hmac-sha256" {
   301  			return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf)
   302  		}
   303  		key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
   304  		return key, nil
   305  	}
   306  
   307  	return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF)
   308  }
   309  
   310  // TODO: can we do without this when unmarshalling dynamic JSON?
   311  // why do integers in KDF params end up as float64 and not int after
   312  // unmarshal?
   313  func ensureInt(x interface{}) int {
   314  	res, ok := x.(int)
   315  	if !ok {
   316  		res = int(x.(float64))
   317  	}
   318  	return res
   319  }