|
|
|
@ -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)] |
|
|
|
|
} |
|
|
|
|