github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/app/crypto/ethkeystore/passphrase_adapter.go (about)

     1  package ethkeystore
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/aes"
     6  	"crypto/cipher"
     7  	"crypto/sha256"
     8  	"encoding/hex"
     9  	"encoding/json"
    10  	"fmt"
    11  
    12  	"github.com/ethereum/go-ethereum/accounts/keystore"
    13  	ethcrypto "github.com/ethereum/go-ethereum/crypto"
    14  	"github.com/google/uuid"
    15  	"golang.org/x/crypto/pbkdf2"
    16  	"golang.org/x/crypto/scrypt"
    17  )
    18  
    19  type encryptedKeyJSONV3ForWeb3 struct {
    20  	Address string              `json:"address"`
    21  	Crypto  keystore.CryptoJSON `json:"crypto"`
    22  	Id      string              `json:"id"`
    23  	Version int                 `json:"version"`
    24  }
    25  
    26  // DecryptKey decrypts a key from a json blob, returning the private key itself.
    27  func DecryptKeyForWeb3(keyjson []byte, auth string) (*keystore.Key, error) {
    28  	// Parse the json into a simple map to fetch the key version
    29  	m := make(map[string]interface{})
    30  	if err := json.Unmarshal(keyjson, &m); err != nil {
    31  		return nil, err
    32  	}
    33  	// Depending on the version try to parse one way or another
    34  	var (
    35  		keyBytes, keyId []byte
    36  		err             error
    37  	)
    38  	if m["version"] == float64(3) {
    39  		k := new(encryptedKeyJSONV3ForWeb3)
    40  		if err := json.Unmarshal(keyjson, k); err != nil {
    41  			return nil, err
    42  		}
    43  		keyBytes, keyId, err = decryptKeyV3ForWeb3(k, auth)
    44  	} else {
    45  		return nil, fmt.Errorf("the veison must be equal to 3, got: %s", m["version"])
    46  	}
    47  	// Handle any decryption errors and return the key
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	key := ethcrypto.ToECDSAUnsafe(keyBytes)
    52  	id, err := uuid.FromBytes(keyId)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	return &keystore.Key{
    57  		Id:         id,
    58  		Address:    ethcrypto.PubkeyToAddress(key.PublicKey),
    59  		PrivateKey: key,
    60  	}, nil
    61  }
    62  
    63  func decryptKeyV3ForWeb3(keyProtected *encryptedKeyJSONV3ForWeb3, auth string) (keyBytes []byte, keyId []byte, err error) {
    64  	if keyProtected.Version != 3 {
    65  		return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version)
    66  	}
    67  	keyUUID, err := uuid.Parse(keyProtected.Id)
    68  	if err != nil {
    69  		return nil, nil, err
    70  	}
    71  	keyId = keyUUID[:]
    72  	plainText, err := DecryptDataV3ForWeb3(keyProtected.Crypto, auth)
    73  	if err != nil {
    74  		return nil, nil, err
    75  	}
    76  	return plainText, keyId, err
    77  }
    78  
    79  func DecryptDataV3ForWeb3(cryptoJson keystore.CryptoJSON, auth string) ([]byte, error) {
    80  	if cryptoJson.Cipher != "aes-128-ctr" {
    81  		return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher)
    82  	}
    83  	mac, err := hex.DecodeString(cryptoJson.MAC)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	iv, err := hex.DecodeString(cryptoJson.CipherParams.IV)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	cipherText, err := hex.DecodeString(cryptoJson.CipherText)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	derivedKey, err := getKDFKey(cryptoJson, auth)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	bufferValue := bytes.Buffer{}
   104  	bufferValue.Write(derivedKey[16:32])
   105  	bufferValue.Write(cipherText)
   106  	calculatedMAC := sha256.Sum256(bufferValue.Bytes())
   107  	if !bytes.Equal(calculatedMAC[:], mac) {
   108  		return nil, keystore.ErrDecrypt
   109  	}
   110  
   111  	plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	return plainText, err
   116  }
   117  
   118  func getKDFKey(cryptoJSON keystore.CryptoJSON, auth string) ([]byte, error) {
   119  	authArray := []byte(auth)
   120  	salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
   125  
   126  	if cryptoJSON.KDF == "scrypt" {
   127  		n := ensureInt(cryptoJSON.KDFParams["n"])
   128  		r := ensureInt(cryptoJSON.KDFParams["r"])
   129  		p := ensureInt(cryptoJSON.KDFParams["p"])
   130  		return scrypt.Key(authArray, salt, n, r, p, dkLen)
   131  	} else if cryptoJSON.KDF == "pbkdf2" {
   132  		c := ensureInt(cryptoJSON.KDFParams["c"])
   133  		prf := cryptoJSON.KDFParams["prf"].(string)
   134  		if prf != "hmac-sha256" {
   135  			return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf)
   136  		}
   137  		key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
   138  		return key, nil
   139  	}
   140  
   141  	return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF)
   142  }
   143  
   144  // TODO: can we do without this when unmarshalling dynamic JSON?
   145  // why do integers in KDF params end up as float64 and not int after
   146  // unmarshal?
   147  func ensureInt(x interface{}) int {
   148  	res, ok := x.(int)
   149  	if !ok {
   150  		res = int(x.(float64))
   151  	}
   152  	return res
   153  }
   154  
   155  func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
   156  	// AES-128 is selected due to size of encryptKey.
   157  	aesBlock, err := aes.NewCipher(key)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	stream := cipher.NewCTR(aesBlock, iv)
   162  	outText := make([]byte, len(inText))
   163  	stream.XORKeyStream(outText, inText)
   164  	return outText, err
   165  }