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 }