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