github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/accounts/keystore/presale.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 package keystore 19 20 import ( 21 "crypto/aes" 22 "crypto/cipher" 23 "crypto/sha256" 24 "encoding/hex" 25 "encoding/json" 26 "errors" 27 "fmt" 28 29 "github.com/AigarNetwork/aigar/accounts" 30 "github.com/AigarNetwork/aigar/crypto" 31 "github.com/pborman/uuid" 32 "golang.org/x/crypto/pbkdf2" 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 = uuid.NewRandom() 42 a := accounts.Account{ 43 Address: key.Address, 44 URL: accounts.URL{ 45 Scheme: KeyStoreScheme, 46 Path: keyStore.JoinPath(keyFileName(key.Address)), 47 }, 48 } 49 err = keyStore.StoreKey(a.URL.Path, key, password) 50 return a, key, err 51 } 52 53 func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error) { 54 preSaleKeyStruct := struct { 55 EncSeed string 56 EthAddr string 57 Email string 58 BtcAddr string 59 }{} 60 err = json.Unmarshal(fileContent, &preSaleKeyStruct) 61 if err != nil { 62 return nil, err 63 } 64 encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed) 65 if err != nil { 66 return nil, errors.New("invalid hex in encSeed") 67 } 68 if len(encSeedBytes) < 16 { 69 return nil, errors.New("invalid encSeed, too short") 70 } 71 iv := encSeedBytes[:16] 72 cipherText := encSeedBytes[16:] 73 /* 74 See https://github.com/ethereum/pyethsaletool 75 76 pyethsaletool generates the encryption key from password by 77 2000 rounds of PBKDF2 with HMAC-SHA-256 using password as salt (:(). 78 16 byte key length within PBKDF2 and resulting key is used as AES key 79 */ 80 passBytes := []byte(password) 81 derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New) 82 plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv) 83 if err != nil { 84 return nil, err 85 } 86 ethPriv := crypto.Keccak256(plainText) 87 ecKey := crypto.ToECDSAUnsafe(ethPriv) 88 89 key = &Key{ 90 Id: nil, 91 Address: crypto.PubkeyToAddress(ecKey.PublicKey), 92 PrivateKey: ecKey, 93 } 94 derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x" 95 expectedAddr := preSaleKeyStruct.EthAddr 96 if derivedAddr != expectedAddr { 97 err = fmt.Errorf("decrypted addr '%s' not equal to expected addr '%s'", derivedAddr, expectedAddr) 98 } 99 return key, err 100 } 101 102 func aesCTRXOR(key, inText, iv []byte) ([]byte, error) { 103 // AES-128 is selected due to size of encryptKey. 104 aesBlock, err := aes.NewCipher(key) 105 if err != nil { 106 return nil, err 107 } 108 stream := cipher.NewCTR(aesBlock, iv) 109 outText := make([]byte, len(inText)) 110 stream.XORKeyStream(outText, inText) 111 return outText, err 112 } 113 114 func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) { 115 aesBlock, err := aes.NewCipher(key) 116 if err != nil { 117 return nil, err 118 } 119 decrypter := cipher.NewCBCDecrypter(aesBlock, iv) 120 paddedPlaintext := make([]byte, len(cipherText)) 121 decrypter.CryptBlocks(paddedPlaintext, cipherText) 122 plaintext := pkcs7Unpad(paddedPlaintext) 123 if plaintext == nil { 124 return nil, ErrDecrypt 125 } 126 return plaintext, err 127 } 128 129 // From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes 130 func pkcs7Unpad(in []byte) []byte { 131 if len(in) == 0 { 132 return nil 133 } 134 135 padding := in[len(in)-1] 136 if int(padding) > len(in) || padding > aes.BlockSize { 137 return nil 138 } else if padding == 0 { 139 return nil 140 } 141 142 for i := len(in) - 1; i > len(in)-int(padding)-1; i-- { 143 if in[i] != padding { 144 return nil 145 } 146 } 147 return in[:len(in)-int(padding)] 148 }