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