296 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			296 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package crypto
 | |
| 
 | |
| import (
 | |
| 	"crypto/aes"
 | |
| 	"crypto/cipher"
 | |
| 	"crypto/ecdsa"
 | |
| 	"crypto/elliptic"
 | |
| 	"crypto/rand"
 | |
| 	"crypto/sha256"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"io/ioutil"
 | |
| 	"os"
 | |
| 
 | |
| 	"encoding/hex"
 | |
| 	"encoding/json"
 | |
| 	"errors"
 | |
| 
 | |
| 	"code.google.com/p/go-uuid/uuid"
 | |
| 	"github.com/ethereum/go-ethereum/common"
 | |
| 	"github.com/ethereum/go-ethereum/crypto/ecies"
 | |
| 	"github.com/ethereum/go-ethereum/crypto/secp256k1"
 | |
| 	"github.com/ethereum/go-ethereum/crypto/sha3"
 | |
| 	"github.com/ethereum/go-ethereum/rlp"
 | |
| 	"golang.org/x/crypto/pbkdf2"
 | |
| 	"golang.org/x/crypto/ripemd160"
 | |
| )
 | |
| 
 | |
| func init() {
 | |
| 	// specify the params for the s256 curve
 | |
| 	ecies.AddParamsForCurve(S256(), ecies.ECIES_AES128_SHA256)
 | |
| }
 | |
| 
 | |
| func Sha3(data ...[]byte) []byte {
 | |
| 	d := sha3.NewKeccak256()
 | |
| 	for _, b := range data {
 | |
| 		d.Write(b)
 | |
| 	}
 | |
| 	return d.Sum(nil)
 | |
| }
 | |
| 
 | |
| func Sha3Hash(data ...[]byte) (h common.Hash) {
 | |
| 	d := sha3.NewKeccak256()
 | |
| 	for _, b := range data {
 | |
| 		d.Write(b)
 | |
| 	}
 | |
| 	d.Sum(h[:0])
 | |
| 	return h
 | |
| }
 | |
| 
 | |
| // Creates an ethereum address given the bytes and the nonce
 | |
| func CreateAddress(b common.Address, nonce uint64) common.Address {
 | |
| 	data, _ := rlp.EncodeToBytes([]interface{}{b, nonce})
 | |
| 	return common.BytesToAddress(Sha3(data)[12:])
 | |
| 	//return Sha3(common.NewValue([]interface{}{b, nonce}).Encode())[12:]
 | |
| }
 | |
| 
 | |
| func Sha256(data []byte) []byte {
 | |
| 	hash := sha256.Sum256(data)
 | |
| 
 | |
| 	return hash[:]
 | |
| }
 | |
| 
 | |
| func Ripemd160(data []byte) []byte {
 | |
| 	ripemd := ripemd160.New()
 | |
| 	ripemd.Write(data)
 | |
| 
 | |
| 	return ripemd.Sum(nil)
 | |
| }
 | |
| 
 | |
| func Ecrecover(hash, sig []byte) ([]byte, error) {
 | |
| 	return secp256k1.RecoverPubkey(hash, sig)
 | |
| }
 | |
| 
 | |
| // New methods using proper ecdsa keys from the stdlib
 | |
| func ToECDSA(prv []byte) *ecdsa.PrivateKey {
 | |
| 	if len(prv) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	priv := new(ecdsa.PrivateKey)
 | |
| 	priv.PublicKey.Curve = S256()
 | |
| 	priv.D = common.BigD(prv)
 | |
| 	priv.PublicKey.X, priv.PublicKey.Y = S256().ScalarBaseMult(prv)
 | |
| 	return priv
 | |
| }
 | |
| 
 | |
| func FromECDSA(prv *ecdsa.PrivateKey) []byte {
 | |
| 	if prv == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 	return prv.D.Bytes()
 | |
| }
 | |
| 
 | |
| func ToECDSAPub(pub []byte) *ecdsa.PublicKey {
 | |
| 	if len(pub) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 	x, y := elliptic.Unmarshal(S256(), pub)
 | |
| 	return &ecdsa.PublicKey{S256(), x, y}
 | |
| }
 | |
| 
 | |
| func FromECDSAPub(pub *ecdsa.PublicKey) []byte {
 | |
| 	if pub == nil || pub.X == nil || pub.Y == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 	return elliptic.Marshal(S256(), pub.X, pub.Y)
 | |
| }
 | |
| 
 | |
| // HexToECDSA parses a secp256k1 private key.
 | |
| func HexToECDSA(hexkey string) (*ecdsa.PrivateKey, error) {
 | |
| 	b, err := hex.DecodeString(hexkey)
 | |
| 	if err != nil {
 | |
| 		return nil, errors.New("invalid hex string")
 | |
| 	}
 | |
| 	if len(b) != 32 {
 | |
| 		return nil, errors.New("invalid length, need 256 bits")
 | |
| 	}
 | |
| 	return ToECDSA(b), nil
 | |
| }
 | |
| 
 | |
| // LoadECDSA loads a secp256k1 private key from the given file.
 | |
| // The key data is expected to be hex-encoded.
 | |
| func LoadECDSA(file string) (*ecdsa.PrivateKey, error) {
 | |
| 	buf := make([]byte, 64)
 | |
| 	fd, err := os.Open(file)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	defer fd.Close()
 | |
| 	if _, err := io.ReadFull(fd, buf); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	key, err := hex.DecodeString(string(buf))
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return ToECDSA(key), nil
 | |
| }
 | |
| 
 | |
| // SaveECDSA saves a secp256k1 private key to the given file with
 | |
| // restrictive permissions. The key data is saved hex-encoded.
 | |
| func SaveECDSA(file string, key *ecdsa.PrivateKey) error {
 | |
| 	k := hex.EncodeToString(FromECDSA(key))
 | |
| 	return ioutil.WriteFile(file, []byte(k), 0600)
 | |
| }
 | |
| 
 | |
| func GenerateKey() (*ecdsa.PrivateKey, error) {
 | |
| 	return ecdsa.GenerateKey(S256(), rand.Reader)
 | |
| }
 | |
| 
 | |
| func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
 | |
| 	s, err := Ecrecover(hash, sig)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	x, y := elliptic.Unmarshal(S256(), s)
 | |
| 	return &ecdsa.PublicKey{S256(), x, y}, nil
 | |
| }
 | |
| 
 | |
| func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
 | |
| 	if len(hash) != 32 {
 | |
| 		return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
 | |
| 	}
 | |
| 
 | |
| 	sig, err = secp256k1.Sign(hash, common.LeftPadBytes(prv.D.Bytes(), prv.Params().BitSize/8))
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func Encrypt(pub *ecdsa.PublicKey, message []byte) ([]byte, error) {
 | |
| 	return ecies.Encrypt(rand.Reader, ecies.ImportECDSAPublic(pub), message, nil, nil)
 | |
| }
 | |
| 
 | |
| func Decrypt(prv *ecdsa.PrivateKey, ct []byte) ([]byte, error) {
 | |
| 	key := ecies.ImportECDSA(prv)
 | |
| 	return key.Decrypt(rand.Reader, ct, nil, nil)
 | |
| }
 | |
| 
 | |
| // Used only by block tests.
 | |
| func ImportBlockTestKey(privKeyBytes []byte) error {
 | |
| 	ks := NewKeyStorePassphrase(common.DefaultDataDir() + "/keystore")
 | |
| 	ecKey := ToECDSA(privKeyBytes)
 | |
| 	key := &Key{
 | |
| 		Id:         uuid.NewRandom(),
 | |
| 		Address:    common.BytesToAddress(PubkeyToAddress(ecKey.PublicKey)),
 | |
| 		PrivateKey: ecKey,
 | |
| 	}
 | |
| 	err := ks.StoreKey(key, "")
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // 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
 | |
| 	}
 | |
| 	key.Id = uuid.NewRandom()
 | |
| 	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,
 | |
| 		Address:    common.BytesToAddress(PubkeyToAddress(ecKey.PublicKey)),
 | |
| 		PrivateKey: ecKey,
 | |
| 	}
 | |
| 	derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x"
 | |
| 	expectedAddr := preSaleKeyStruct.EthAddr
 | |
| 	if derivedAddr != expectedAddr {
 | |
| 		err = errors.New(fmt.Sprintf("decrypted addr not equal to expected addr ", derivedAddr, expectedAddr))
 | |
| 	}
 | |
| 	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 AES 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)]
 | |
| }
 | |
| 
 | |
| func PubkeyToAddress(p ecdsa.PublicKey) []byte {
 | |
| 	pubBytes := FromECDSAPub(&p)
 | |
| 	return Sha3(pubBytes[1:])[12:]
 | |
| }
 |