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