github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/accounts/keystore/keystore_passphrase.go (about)

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