Merge branch 'Gustav-Simonsson-import_presale_keys' into develop

This commit is contained in:
obscuren 2015-01-22 22:42:52 +01:00
commit d792e95c21
5 changed files with 125 additions and 50 deletions

View File

@ -46,7 +46,7 @@ Go Ethereum comes with several binaries found in
* `mist` Official Ethereum Browser
* `ethereum` Ethereum CLI
* `ethtest` test tool which runs with the [tests](https://github.com/ethereum/testes) suit:
`ethtest "`cat myfile.json`"`.
`cat file | ethtest`.
* `evm` is a generic Ethereum Virtual Machine: `evm -code 60ff60ff -gas
10000 -price 0 -dump`. See `-h` for a detailed description.

View File

@ -1,12 +1,20 @@
package crypto
import (
"crypto/aes"
"crypto/cipher"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"fmt"
"encoding/hex"
"encoding/json"
"errors"
"code.google.com/p/go-uuid/uuid"
"code.google.com/p/go.crypto/pbkdf2"
"code.google.com/p/go.crypto/ripemd160"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/crypto/sha3"
@ -118,3 +126,100 @@ func Decrypt(prv *ecdsa.PrivateKey, ct []byte) ([]byte, error) {
key := ecies.ImportECDSA(prv)
return key.Decrypt(rand.Reader, ct, nil, nil)
}
// creates a Key and stores that in the given KeyStore by decrypting a presale key JSON
func ImportPreSaleKey(keyStore KeyStore2, keyJSON []byte, password string) (*Key, error) {
key, err := decryptPreSaleKey(keyJSON, password)
if err != nil {
return nil, err
}
id := uuid.NewRandom()
key.Id = &id
err = keyStore.StoreKey(key, password)
return key, err
}
func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error) {
preSaleKeyStruct := struct {
EncSeed string
EthAddr string
Email string
BtcAddr string
}{}
err = json.Unmarshal(fileContent, &preSaleKeyStruct)
if err != nil {
return nil, err
}
encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed)
iv := encSeedBytes[:16]
cipherText := encSeedBytes[16:]
/*
See https://github.com/ethereum/pyethsaletool
pyethsaletool generates the encryption key from password by
2000 rounds of PBKDF2 with HMAC-SHA-256 using password as salt (:().
16 byte key length within PBKDF2 and resulting key is used as AES key
*/
passBytes := []byte(password)
derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New)
plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
ethPriv := Sha3(plainText)
ecKey := ToECDSA(ethPriv)
key = &Key{
Id: nil,
PrivateKey: ecKey,
}
derivedAddr := ethutil.Bytes2Hex(key.Address())
expectedAddr := preSaleKeyStruct.EthAddr
if derivedAddr != expectedAddr {
err = errors.New("decrypted addr not equal to expected addr")
}
return key, err
}
func aesCBCDecrypt(key []byte, cipherText []byte, iv []byte) (plainText []byte, err error) {
aesBlock, err := aes.NewCipher(key)
if err != nil {
return plainText, err
}
decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
paddedPlainText := make([]byte, len(cipherText))
decrypter.CryptBlocks(paddedPlainText, cipherText)
plainText = PKCS7Unpad(paddedPlainText)
if plainText == nil {
err = errors.New("Decryption failed: PKCS7Unpad failed after decryption")
}
return plainText, err
}
// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
func PKCS7Pad(in []byte) []byte {
padding := 16 - (len(in) % 16)
if padding == 0 {
padding = 16
}
for i := 0; i < padding; i++ {
in = append(in, byte(padding))
}
return in
}
func PKCS7Unpad(in []byte) []byte {
if len(in) == 0 {
return nil
}
padding := in[len(in)-1]
if int(padding) > len(in) || padding > aes.BlockSize {
return nil
} else if padding == 0 {
return nil
}
for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
if in[i] != padding {
return nil
}
}
return in[:len(in)-int(padding)]
}

View File

@ -57,7 +57,7 @@ type encryptedKeyJSON struct {
func (k *Key) Address() []byte {
pubBytes := FromECDSAPub(&k.PrivateKey.PublicKey)
return Sha3(pubBytes)[12:]
return Sha3(pubBytes[1:])[12:]
}
func (k *Key) MarshalJSON() (j []byte, err error) {
@ -99,9 +99,10 @@ func NewKey(rand io.Reader) *Key {
privateKeyMarshalled := elliptic.Marshal(S256(), x, y)
privateKeyECDSA := ToECDSA(privateKeyMarshalled)
key := new(Key)
id := uuid.NewRandom()
key.Id = &id
key.PrivateKey = privateKeyECDSA
key := &Key{
Id: &id,
PrivateKey: privateKeyECDSA,
}
return key
}

View File

@ -178,22 +178,10 @@ func DecryptKey(ks keyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes
if err != nil {
return nil, err
}
AES256Block, err := aes.NewCipher(derivedKey)
plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
if err != nil {
return nil, err
}
AES256CBCDecrypter := cipher.NewCBCDecrypter(AES256Block, iv)
paddedPlainText := make([]byte, len(cipherText))
AES256CBCDecrypter.CryptBlocks(paddedPlainText, cipherText)
plainText := PKCS7Unpad(paddedPlainText)
if plainText == nil {
err = errors.New("Decryption failed: PKCS7Unpad failed after decryption")
return nil, err
}
keyBytes = plainText[:len(plainText)-32]
keyBytesHash := plainText[len(plainText)-32:]
if !bytes.Equal(Sha3(keyBytes), keyBytesHash) {
@ -211,35 +199,3 @@ func getEntropyCSPRNG(n int) []byte {
}
return mainBuff
}
// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
func PKCS7Pad(in []byte) []byte {
padding := 16 - (len(in) % 16)
if padding == 0 {
padding = 16
}
for i := 0; i < padding; i++ {
in = append(in, byte(padding))
}
return in
}
func PKCS7Unpad(in []byte) []byte {
if len(in) == 0 {
return nil
}
padding := in[len(in)-1]
if int(padding) > len(in) || padding > aes.BlockSize {
return nil
} else if padding == 0 {
return nil
}
for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
if in[i] != padding {
return nil
}
}
return in[:len(in)-int(padding)]
}

View File

@ -83,3 +83,16 @@ func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
t.Fatal(err)
}
}
func TestImportPreSaleKey(t *testing.T) {
// file content of a presale key file generated with:
// python pyethsaletool.py genwallet
// with password "foo"
fileContent := "{\"encseed\": \"26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba\", \"ethaddr\": \"d4584b5f6229b7be90727b0fc8c6b91bb427821f\", \"email\": \"gustav.simonsson@gmail.com\", \"btcaddr\": \"1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx\"}"
ks := NewKeyStorePassphrase(DefaultDataDir())
pass := "foo"
_, err := ImportPreSaleKey(ks, []byte(fileContent), pass)
if err != nil {
t.Fatal(err)
}
}