github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/accounts/presale.go (about)

     1  // Copyright 2016 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  package accounts
    18  
    19  import (
    20  	"crypto/aes"
    21  	"crypto/cipher"
    22  	"crypto/sha256"
    23  	"encoding/hex"
    24  	"encoding/json"
    25  	"fmt"
    26  
    27  	"github.com/ethereumproject/go-ethereum/crypto"
    28  	"golang.org/x/crypto/pbkdf2"
    29  )
    30  
    31  // creates a Key and stores that in the given KeyStore by decrypting a presale key JSON
    32  func importPreSaleKey(keyStore *keyStore, keyJSON []byte, password string) (Account, *key, error) {
    33  	key, err := decryptPreSaleKey(keyJSON, password)
    34  	if err != nil {
    35  		return Account{}, nil, err
    36  	}
    37  
    38  	key.UUID, err = newKeyUUID()
    39  	if err != nil {
    40  		return Account{}, nil, err
    41  	}
    42  
    43  	file, err := keyStore.Insert(key, password)
    44  	if err != nil {
    45  		return Account{}, nil, err
    46  	}
    47  
    48  	return Account{Address: key.Address, File: file}, key, nil
    49  }
    50  
    51  func decryptPreSaleKey(fileContent []byte, password string) (*key, error) {
    52  	preSaleKeyStruct := struct {
    53  		EncSeed string
    54  		EthAddr string
    55  		Email   string
    56  		BtcAddr string
    57  	}{}
    58  	if err := json.Unmarshal(fileContent, &preSaleKeyStruct); err != nil {
    59  		return nil, err
    60  	}
    61  	encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed)
    62  	iv := encSeedBytes[:16]
    63  	cipherText := encSeedBytes[16:]
    64  	/*
    65  		See https://github.com/ethereum/pyethsaletool
    66  
    67  		pyethsaletool generates the encryption key from password by
    68  		2000 rounds of PBKDF2 with HMAC-SHA-256 using password as salt (:().
    69  		16 byte key length within PBKDF2 and resulting key is used as AES key
    70  	*/
    71  	passBytes := []byte(password)
    72  	derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New)
    73  	plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	ethPriv := crypto.Keccak256(plainText)
    78  	ecKey := crypto.ToECDSA(ethPriv)
    79  	k := &key{
    80  		Address:    crypto.PubkeyToAddress(ecKey.PublicKey),
    81  		PrivateKey: ecKey,
    82  	}
    83  	derivedAddr := hex.EncodeToString(k.Address.Bytes()) // needed because .Hex() gives leading "0x"
    84  	expectedAddr := preSaleKeyStruct.EthAddr
    85  	if derivedAddr != expectedAddr {
    86  		err = fmt.Errorf("decrypted addr '%s' not equal to expected addr '%s'", derivedAddr, expectedAddr)
    87  	}
    88  	return k, err
    89  }
    90  
    91  func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
    92  	// AES-128 is selected due to size of encryptKey.
    93  	aesBlock, err := aes.NewCipher(key)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	stream := cipher.NewCTR(aesBlock, iv)
    98  	outText := make([]byte, len(inText))
    99  	stream.XORKeyStream(outText, inText)
   100  	return outText, err
   101  }
   102  
   103  func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) {
   104  	aesBlock, err := aes.NewCipher(key)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
   109  	paddedPlaintext := make([]byte, len(cipherText))
   110  	decrypter.CryptBlocks(paddedPlaintext, cipherText)
   111  	plaintext := pkcs7Unpad(paddedPlaintext)
   112  	if plaintext == nil {
   113  		return nil, ErrDecrypt
   114  	}
   115  	return plaintext, err
   116  }
   117  
   118  // From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
   119  func pkcs7Unpad(in []byte) []byte {
   120  	if len(in) == 0 {
   121  		return nil
   122  	}
   123  
   124  	padding := in[len(in)-1]
   125  	if int(padding) > len(in) || padding > aes.BlockSize {
   126  		return nil
   127  	} else if padding == 0 {
   128  		return nil
   129  	}
   130  
   131  	for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
   132  		if in[i] != padding {
   133  			return nil
   134  		}
   135  	}
   136  	return in[:len(in)-int(padding)]
   137  }