github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/accounts/keystore/presale.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:31</date>
    10  //</624450064772763648>
    11  
    12  
    13  package keystore
    14  
    15  import (
    16  	"crypto/aes"
    17  	"crypto/cipher"
    18  	"crypto/sha256"
    19  	"encoding/hex"
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  
    24  	"github.com/ethereum/go-ethereum/accounts"
    25  	"github.com/ethereum/go-ethereum/crypto"
    26  	"github.com/pborman/uuid"
    27  	"golang.org/x/crypto/pbkdf2"
    28  )
    29  
    30  //通过解密预售密钥JSON,创建密钥并将其存储在给定的密钥库中。
    31  func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accounts.Account, *Key, error) {
    32  	key, err := decryptPreSaleKey(keyJSON, password)
    33  	if err != nil {
    34  		return accounts.Account{}, nil, err
    35  	}
    36  	key.Id = uuid.NewRandom()
    37  	a := accounts.Account{
    38  		Address: key.Address,
    39  		URL: accounts.URL{
    40  			Scheme: KeyStoreScheme,
    41  			Path:   keyStore.JoinPath(keyFileName(key.Address)),
    42  		},
    43  	}
    44  	err = keyStore.StoreKey(a.URL.Path, key, password)
    45  	return a, key, err
    46  }
    47  
    48  func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error) {
    49  	preSaleKeyStruct := struct {
    50  		EncSeed string
    51  		EthAddr string
    52  		Email   string
    53  		BtcAddr string
    54  	}{}
    55  	err = json.Unmarshal(fileContent, &preSaleKeyStruct)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  	encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed)
    60  	if err != nil {
    61  		return nil, errors.New("invalid hex in encSeed")
    62  	}
    63  	if len(encSeedBytes) < 16 {
    64  		return nil, errors.New("invalid encSeed, too short")
    65  	}
    66  	iv := encSeedBytes[:16]
    67  	cipherText := encSeedBytes[16:]
    68   /*
    69    请参阅https://github.com/ethereum/pyethsaletool
    70  
    71    pyethsaletool根据密码生成加密密钥
    72    2000轮PBKdf2,HMAC-SHA-256,使用密码作为salt(:()。
    73    pbkdf2内的16字节密钥长度,生成的密钥用作aes密钥。
    74   **/
    75  
    76  	passBytes := []byte(password)
    77  	derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New)
    78  	plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	ethPriv := crypto.Keccak256(plainText)
    83  	ecKey := crypto.ToECDSAUnsafe(ethPriv)
    84  
    85  	key = &Key{
    86  		Id:         nil,
    87  		Address:    crypto.PubkeyToAddress(ecKey.PublicKey),
    88  		PrivateKey: ecKey,
    89  	}
    90  derivedAddr := hex.EncodeToString(key.Address.Bytes()) //需要,因为.hex()给出前导“0x”
    91  	expectedAddr := preSaleKeyStruct.EthAddr
    92  	if derivedAddr != expectedAddr {
    93  		err = fmt.Errorf("decrypted addr '%s' not equal to expected addr '%s'", derivedAddr, expectedAddr)
    94  	}
    95  	return key, err
    96  }
    97  
    98  func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
    99  //由于加密密钥的大小,选择了AES-128。
   100  	aesBlock, err := aes.NewCipher(key)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  	stream := cipher.NewCTR(aesBlock, iv)
   105  	outText := make([]byte, len(inText))
   106  	stream.XORKeyStream(outText, inText)
   107  	return outText, err
   108  }
   109  
   110  func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) {
   111  	aesBlock, err := aes.NewCipher(key)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
   116  	paddedPlaintext := make([]byte, len(cipherText))
   117  	decrypter.CryptBlocks(paddedPlaintext, cipherText)
   118  	plaintext := pkcs7Unpad(paddedPlaintext)
   119  	if plaintext == nil {
   120  		return nil, ErrDecrypt
   121  	}
   122  	return plaintext, err
   123  }
   124  
   125  //来自https://leanpub.com/gocrypto/read leanpub自动分组密码模式
   126  func pkcs7Unpad(in []byte) []byte {
   127  	if len(in) == 0 {
   128  		return nil
   129  	}
   130  
   131  	padding := in[len(in)-1]
   132  	if int(padding) > len(in) || padding > aes.BlockSize {
   133  		return nil
   134  	} else if padding == 0 {
   135  		return nil
   136  	}
   137  
   138  	for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
   139  		if in[i] != padding {
   140  			return nil
   141  		}
   142  	}
   143  	return in[:len(in)-int(padding)]
   144  }
   145