github.com/status-im/status-go@v1.1.0/eth-node/keystore/passphrase.go (about)

     1  // Imported from github.com/ethereum/go-ethereum/accounts/keystore/passphrase.go
     2  // and github.com/ethereum/go-ethereum/accounts/keystore/presale.go
     3  
     4  package keystore
     5  
     6  import (
     7  	"bytes"
     8  	"crypto/aes"
     9  	"crypto/cipher"
    10  	"crypto/sha256"
    11  	"encoding/hex"
    12  	"encoding/json"
    13  	"fmt"
    14  
    15  	"github.com/google/uuid"
    16  	"golang.org/x/crypto/pbkdf2"
    17  	"golang.org/x/crypto/scrypt"
    18  
    19  	"github.com/status-im/status-go/eth-node/crypto"
    20  	"github.com/status-im/status-go/eth-node/types"
    21  	"github.com/status-im/status-go/extkeys"
    22  )
    23  
    24  const (
    25  	keyHeaderKDF = "scrypt"
    26  )
    27  
    28  type EncryptedKeyJSONV3 struct {
    29  	Address         string     `json:"address"`
    30  	Crypto          CryptoJSON `json:"crypto"`
    31  	Id              string     `json:"id"`
    32  	Version         int        `json:"version"`
    33  	ExtendedKey     CryptoJSON `json:"extendedkey"`
    34  	SubAccountIndex uint32     `json:"subaccountindex"`
    35  }
    36  
    37  type encryptedKeyJSONV1 struct {
    38  	Address string     `json:"address"`
    39  	Crypto  CryptoJSON `json:"crypto"`
    40  	Id      string     `json:"id"`
    41  	Version string     `json:"version"`
    42  }
    43  
    44  type CryptoJSON struct {
    45  	Cipher       string                 `json:"cipher"`
    46  	CipherText   string                 `json:"ciphertext"`
    47  	CipherParams cipherparamsJSON       `json:"cipherparams"`
    48  	KDF          string                 `json:"kdf"`
    49  	KDFParams    map[string]interface{} `json:"kdfparams"`
    50  	MAC          string                 `json:"mac"`
    51  }
    52  
    53  type cipherparamsJSON struct {
    54  	IV string `json:"iv"`
    55  }
    56  
    57  // DecryptKey decrypts a key from a json blob, returning the private key itself.
    58  func DecryptKey(keyjson []byte, auth string) (*types.Key, error) {
    59  	// Parse the json into a simple map to fetch the key version
    60  	m := make(map[string]interface{})
    61  	if err := json.Unmarshal(keyjson, &m); err != nil {
    62  		return nil, err
    63  	}
    64  	// Depending on the version try to parse one way or another
    65  	var (
    66  		keyBytes, keyId []byte
    67  		err             error
    68  		extKeyBytes     []byte
    69  		extKey          *extkeys.ExtendedKey
    70  	)
    71  
    72  	subAccountIndex, ok := m["subaccountindex"].(float64)
    73  	if !ok {
    74  		subAccountIndex = 0
    75  	}
    76  
    77  	if version, ok := m["version"].(string); ok && version == "1" {
    78  		k := new(encryptedKeyJSONV1)
    79  		if err := json.Unmarshal(keyjson, k); err != nil {
    80  			return nil, err
    81  		}
    82  		keyBytes, keyId, err = decryptKeyV1(k, auth)
    83  		if err != nil {
    84  			return nil, err
    85  		}
    86  
    87  		extKey, err = extkeys.NewKeyFromString(extkeys.EmptyExtendedKeyString)
    88  	} else {
    89  		k := new(EncryptedKeyJSONV3)
    90  		if err := json.Unmarshal(keyjson, k); err != nil {
    91  			return nil, err
    92  		}
    93  		keyBytes, keyId, err = decryptKeyV3(k, auth)
    94  		if err != nil {
    95  			return nil, err
    96  		}
    97  
    98  		extKeyBytes, err = decryptExtendedKey(k, auth)
    99  		if err != nil {
   100  			return nil, err
   101  		}
   102  		extKey, err = extkeys.NewKeyFromString(string(extKeyBytes))
   103  	}
   104  	// Handle any decryption errors and return the key
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	key := crypto.ToECDSAUnsafe(keyBytes)
   109  
   110  	id, err := uuid.FromBytes(keyId)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	return &types.Key{
   116  		ID:              id,
   117  		Address:         crypto.PubkeyToAddress(key.PublicKey),
   118  		PrivateKey:      key,
   119  		ExtendedKey:     extKey,
   120  		SubAccountIndex: uint32(subAccountIndex),
   121  	}, nil
   122  }
   123  
   124  func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
   125  	if cryptoJson.Cipher != "aes-128-ctr" {
   126  		return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher)
   127  	}
   128  	mac, err := hex.DecodeString(cryptoJson.MAC)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  
   133  	iv, err := hex.DecodeString(cryptoJson.CipherParams.IV)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	cipherText, err := hex.DecodeString(cryptoJson.CipherText)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	derivedKey, err := getKDFKey(cryptoJson, auth)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   149  	if !bytes.Equal(calculatedMAC, mac) {
   150  		return nil, ErrDecrypt
   151  	}
   152  
   153  	plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  	return plainText, err
   158  }
   159  
   160  func decryptKeyV3(keyProtected *EncryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
   161  	if keyProtected.Version != version {
   162  		return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
   163  	}
   164  	id, err := uuid.Parse(keyProtected.Id)
   165  	if err != nil {
   166  		return nil, nil, err
   167  	}
   168  	keyId = id[:]
   169  	plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
   170  	if err != nil {
   171  		return nil, nil, err
   172  	}
   173  	return plainText, keyId, err
   174  }
   175  
   176  func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
   177  	id, err := uuid.Parse(keyProtected.Id)
   178  	if err != nil {
   179  		return nil, nil, err
   180  	}
   181  	keyId = id[:]
   182  
   183  	mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
   184  	if err != nil {
   185  		return nil, nil, err
   186  	}
   187  
   188  	iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
   189  	if err != nil {
   190  		return nil, nil, err
   191  	}
   192  
   193  	cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
   194  	if err != nil {
   195  		return nil, nil, err
   196  	}
   197  
   198  	derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
   199  	if err != nil {
   200  		return nil, nil, err
   201  	}
   202  
   203  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   204  	if !bytes.Equal(calculatedMAC, mac) {
   205  		return nil, nil, ErrDecrypt
   206  	}
   207  
   208  	plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv)
   209  	if err != nil {
   210  		return nil, nil, err
   211  	}
   212  	return plainText, keyId, err
   213  }
   214  
   215  func decryptExtendedKey(keyProtected *EncryptedKeyJSONV3, auth string) (plainText []byte, err error) {
   216  	if len(keyProtected.ExtendedKey.CipherText) == 0 {
   217  		return []byte(extkeys.EmptyExtendedKeyString), nil
   218  	}
   219  
   220  	if keyProtected.Version != version {
   221  		return nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
   222  	}
   223  
   224  	if keyProtected.ExtendedKey.Cipher != "aes-128-ctr" {
   225  		return nil, fmt.Errorf("Cipher not supported: %v", keyProtected.ExtendedKey.Cipher)
   226  	}
   227  
   228  	mac, err := hex.DecodeString(keyProtected.ExtendedKey.MAC)
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  
   233  	iv, err := hex.DecodeString(keyProtected.ExtendedKey.CipherParams.IV)
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  
   238  	cipherText, err := hex.DecodeString(keyProtected.ExtendedKey.CipherText)
   239  	if err != nil {
   240  		return nil, err
   241  	}
   242  
   243  	derivedKey, err := getKDFKey(keyProtected.ExtendedKey, auth)
   244  	if err != nil {
   245  		return nil, err
   246  	}
   247  
   248  	calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
   249  	if !bytes.Equal(calculatedMAC, mac) {
   250  		return nil, ErrDecrypt
   251  	}
   252  
   253  	plainText, err = aesCTRXOR(derivedKey[:16], cipherText, iv)
   254  	if err != nil {
   255  		return nil, err
   256  	}
   257  	return plainText, err
   258  }
   259  
   260  func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
   261  	authArray := []byte(auth)
   262  	salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
   267  
   268  	if cryptoJSON.KDF == keyHeaderKDF {
   269  		n := ensureInt(cryptoJSON.KDFParams["n"])
   270  		r := ensureInt(cryptoJSON.KDFParams["r"])
   271  		p := ensureInt(cryptoJSON.KDFParams["p"])
   272  		return scrypt.Key(authArray, salt, n, r, p, dkLen)
   273  
   274  	} else if cryptoJSON.KDF == "pbkdf2" {
   275  		c := ensureInt(cryptoJSON.KDFParams["c"])
   276  		prf := cryptoJSON.KDFParams["prf"].(string)
   277  		if prf != "hmac-sha256" {
   278  			return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf)
   279  		}
   280  		key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
   281  		return key, nil
   282  	}
   283  
   284  	return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF)
   285  }
   286  
   287  // TODO: can we do without this when unmarshalling dynamic JSON?
   288  // why do integers in KDF params end up as float64 and not int after
   289  // unmarshal?
   290  func ensureInt(x interface{}) int {
   291  	res, ok := x.(int)
   292  	if !ok {
   293  		res = int(x.(float64))
   294  	}
   295  	return res
   296  }
   297  
   298  func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
   299  	// AES-128 is selected due to size of encryptKey.
   300  	aesBlock, err := aes.NewCipher(key)
   301  	if err != nil {
   302  		return nil, err
   303  	}
   304  	stream := cipher.NewCTR(aesBlock, iv)
   305  	outText := make([]byte, len(inText))
   306  	stream.XORKeyStream(outText, inText)
   307  	return outText, err
   308  }
   309  
   310  func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) {
   311  	aesBlock, err := aes.NewCipher(key)
   312  	if err != nil {
   313  		return nil, err
   314  	}
   315  	decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
   316  	paddedPlaintext := make([]byte, len(cipherText))
   317  	decrypter.CryptBlocks(paddedPlaintext, cipherText)
   318  	plaintext := pkcs7Unpad(paddedPlaintext)
   319  	if plaintext == nil {
   320  		return nil, ErrDecrypt
   321  	}
   322  	return plaintext, err
   323  }
   324  
   325  // From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
   326  func pkcs7Unpad(in []byte) []byte {
   327  	if len(in) == 0 {
   328  		return nil
   329  	}
   330  
   331  	padding := in[len(in)-1]
   332  	if int(padding) > len(in) || padding > aes.BlockSize {
   333  		return nil
   334  	} else if padding == 0 {
   335  		return nil
   336  	}
   337  
   338  	for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
   339  		if in[i] != padding {
   340  			return nil
   341  		}
   342  	}
   343  	return in[:len(in)-int(padding)]
   344  }
   345  
   346  func RawKeyToCryptoJSON(rawKeyFile []byte) (cj CryptoJSON, e error) {
   347  	var keyJSON EncryptedKeyJSONV3
   348  	if e := json.Unmarshal(rawKeyFile, &keyJSON); e != nil {
   349  		return cj, fmt.Errorf("failed to read key file: %s", e)
   350  	}
   351  
   352  	return keyJSON.Crypto, e
   353  }