From 34a257546cdcf56918e8acc39a1272df47d4f3aa Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 15 Feb 2017 23:39:05 +0100 Subject: [PATCH] crypto: add btcec fallback for sign/recover without cgo This commit adds a non-cgo fallback implementation of secp256k1 operations. --- accounts/keystore/key.go | 5 +- crypto/crypto.go | 79 ++++--------------- crypto/crypto_test.go | 8 +- crypto/ecies/asn1.go | 6 +- crypto/ecies/ecies_test.go | 41 +++++++--- crypto/ecies/params.go | 6 +- crypto/encrypt_decrypt_test.go | 56 ------------- crypto/secp256k1/secp256.go | 10 +-- crypto/signature_cgo.go | 64 +++++++++++++++ crypto/signature_nocgo.go | 77 ++++++++++++++++++ .../crypto.go => crypto/signature_test.go | 25 +++--- p2p/discover/node.go | 2 +- p2p/discv5/node.go | 2 +- p2p/rlpx.go | 4 +- whisper/whisperv2/message.go | 6 +- whisper/whisperv2/message_test.go | 9 +-- whisper/whisperv5/message.go | 5 +- 17 files changed, 231 insertions(+), 174 deletions(-) delete mode 100644 crypto/encrypt_decrypt_test.go create mode 100644 crypto/signature_cgo.go create mode 100644 crypto/signature_nocgo.go rename p2p/discv5/crypto.go => crypto/signature_test.go (53%) diff --git a/accounts/keystore/key.go b/accounts/keystore/key.go index e2bdf09fee..292b47b446 100644 --- a/accounts/keystore/key.go +++ b/accounts/keystore/key.go @@ -32,7 +32,6 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/pborman/uuid" ) @@ -157,7 +156,7 @@ func NewKeyForDirectICAP(rand io.Reader) *Key { panic("key generation: could not read from random source: " + err.Error()) } reader := bytes.NewReader(randBytes) - privateKeyECDSA, err := ecdsa.GenerateKey(secp256k1.S256(), reader) + privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader) if err != nil { panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) } @@ -169,7 +168,7 @@ func NewKeyForDirectICAP(rand io.Reader) *Key { } func newKey(rand io.Reader) (*Key, error) { - privateKeyECDSA, err := ecdsa.GenerateKey(secp256k1.S256(), rand) + privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand) if err != nil { return nil, err } diff --git a/crypto/crypto.go b/crypto/crypto.go index ce45ebd389..3a6896951b 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -21,23 +21,24 @@ import ( "crypto/elliptic" "crypto/rand" "crypto/sha256" - "fmt" + "encoding/hex" + "errors" "io" "io/ioutil" "math/big" "os" - "encoding/hex" - "errors" - "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/ripemd160" ) +var ( + secp256k1_N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16) + secp256k1_halfN = new(big.Int).Div(secp256k1_N, big.NewInt(2)) +) + func Keccak256(data ...[]byte) []byte { d := sha3.NewKeccak256() for _, b := range data { @@ -78,26 +79,16 @@ func Ripemd160(data []byte) []byte { return ripemd.Sum(nil) } -// Ecrecover returns the public key for the private key that was used to -// calculate the signature. -// -// Note: secp256k1 expects the recover id to be either 0, 1. Ethereum -// signatures have a recover id with an offset of 27. Callers must take -// this into account and if "recovering" from an Ethereum signature adjust. -func Ecrecover(hash, sig []byte) ([]byte, error) { - return secp256k1.RecoverPubkey(hash, sig) -} - -// New methods using proper ecdsa keys from the stdlib +// ToECDSA creates a private key with the given D value. func ToECDSA(prv []byte) *ecdsa.PrivateKey { if len(prv) == 0 { return nil } priv := new(ecdsa.PrivateKey) - priv.PublicKey.Curve = secp256k1.S256() + priv.PublicKey.Curve = S256() priv.D = common.BigD(prv) - priv.PublicKey.X, priv.PublicKey.Y = secp256k1.S256().ScalarBaseMult(prv) + priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(prv) return priv } @@ -112,15 +103,15 @@ func ToECDSAPub(pub []byte) *ecdsa.PublicKey { if len(pub) == 0 { return nil } - x, y := elliptic.Unmarshal(secp256k1.S256(), pub) - return &ecdsa.PublicKey{Curve: secp256k1.S256(), X: x, Y: y} + x, y := elliptic.Unmarshal(S256(), pub) + return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y} } func FromECDSAPub(pub *ecdsa.PublicKey) []byte { if pub == nil || pub.X == nil || pub.Y == nil { return nil } - return elliptic.Marshal(secp256k1.S256(), pub.X, pub.Y) + return elliptic.Marshal(S256(), pub.X, pub.Y) } // HexToECDSA parses a secp256k1 private key. @@ -164,7 +155,7 @@ func SaveECDSA(file string, key *ecdsa.PrivateKey) error { } func GenerateKey() (*ecdsa.PrivateKey, error) { - return ecdsa.GenerateKey(secp256k1.S256(), rand.Reader) + return ecdsa.GenerateKey(S256(), rand.Reader) } // ValidateSignatureValues verifies whether the signature values are valid with @@ -175,49 +166,11 @@ func ValidateSignatureValues(v byte, r, s *big.Int, homestead bool) bool { } // reject upper range of s values (ECDSA malleability) // see discussion in secp256k1/libsecp256k1/include/secp256k1.h - if homestead && s.Cmp(secp256k1.HalfN) > 0 { + if homestead && s.Cmp(secp256k1_halfN) > 0 { return false } // Frontier: allow s to be in full N range - return r.Cmp(secp256k1.N) < 0 && s.Cmp(secp256k1.N) < 0 && (v == 0 || v == 1) -} - -func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { - s, err := Ecrecover(hash, sig) - if err != nil { - return nil, err - } - - x, y := elliptic.Unmarshal(secp256k1.S256(), s) - return &ecdsa.PublicKey{Curve: secp256k1.S256(), X: x, Y: y}, nil -} - -// Sign calculates an ECDSA signature. -// -// This function is susceptible to chosen plaintext attacks that can leak -// information about the private key that is used for signing. Callers must -// be aware that the given hash cannot be chosen by an adversery. Common -// solution is to hash any input before calculating the signature. -// -// The produced signature is in the [R || S || V] format where V is 0 or 1. -func Sign(data []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) { - if len(data) != 32 { - return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(data)) - } - - seckey := common.LeftPadBytes(prv.D.Bytes(), prv.Params().BitSize/8) - defer zeroBytes(seckey) - sig, err = secp256k1.Sign(data, seckey) - 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) + return r.Cmp(secp256k1_N) < 0 && s.Cmp(secp256k1_N) < 0 && (v == 0 || v == 1) } func PubkeyToAddress(p ecdsa.PublicKey) common.Address { diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index d4d3098494..73425a6b62 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -170,7 +170,7 @@ func TestValidateSignatureValues(t *testing.T) { minusOne := big.NewInt(-1) one := common.Big1 zero := common.Big0 - secp256k1nMinus1 := new(big.Int).Sub(secp256k1.N, common.Big1) + secp256k1nMinus1 := new(big.Int).Sub(secp256k1_N, common.Big1) // correct v,r,s check(true, 0, one, one) @@ -197,9 +197,9 @@ func TestValidateSignatureValues(t *testing.T) { // correct sig with max r,s check(true, 0, secp256k1nMinus1, secp256k1nMinus1) // correct v, combinations of incorrect r,s at upper limit - check(false, 0, secp256k1.N, secp256k1nMinus1) - check(false, 0, secp256k1nMinus1, secp256k1.N) - check(false, 0, secp256k1.N, secp256k1.N) + check(false, 0, secp256k1_N, secp256k1nMinus1) + check(false, 0, secp256k1nMinus1, secp256k1_N) + check(false, 0, secp256k1_N, secp256k1_N) // current callers ensures r,s cannot be negative, but let's test for that too // as crypto package could be used stand-alone diff --git a/crypto/ecies/asn1.go b/crypto/ecies/asn1.go index 508a645cdf..d3e77d8490 100644 --- a/crypto/ecies/asn1.go +++ b/crypto/ecies/asn1.go @@ -42,7 +42,7 @@ import ( "hash" "math/big" - "github.com/ethereum/go-ethereum/crypto/secp256k1" + ethcrypto "github.com/ethereum/go-ethereum/crypto" ) var ( @@ -120,7 +120,7 @@ func (curve secgNamedCurve) Equal(curve2 secgNamedCurve) bool { func namedCurveFromOID(curve secgNamedCurve) elliptic.Curve { switch { case curve.Equal(secgNamedCurveS256): - return secp256k1.S256() + return ethcrypto.S256() case curve.Equal(secgNamedCurveP256): return elliptic.P256() case curve.Equal(secgNamedCurveP384): @@ -139,7 +139,7 @@ func oidFromNamedCurve(curve elliptic.Curve) (secgNamedCurve, bool) { return secgNamedCurveP384, true case elliptic.P521(): return secgNamedCurveP521, true - case secp256k1.S256(): + case ethcrypto.S256(): return secgNamedCurveS256, true } diff --git a/crypto/ecies/ecies_test.go b/crypto/ecies/ecies_test.go index 3b3517baf3..7c454aa730 100644 --- a/crypto/ecies/ecies_test.go +++ b/crypto/ecies/ecies_test.go @@ -31,7 +31,6 @@ package ecies import ( "bytes" - "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" @@ -42,7 +41,7 @@ import ( "math/big" "testing" - "github.com/ethereum/go-ethereum/crypto/secp256k1" + "github.com/ethereum/go-ethereum/crypto" ) var dumpEnc bool @@ -150,7 +149,7 @@ func TestSharedKey(t *testing.T) { func TestSharedKeyPadding(t *testing.T) { // sanity checks prv0 := hexKey("1adf5c18167d96a1f9a0b1ef63be8aa27eaf6032c233b2b38f7850cf5b859fd9") - prv1 := hexKey("97a076fc7fcd9208240668e31c9abee952cbb6e375d1b8febc7499d6e16f1a") + prv1 := hexKey("0097a076fc7fcd9208240668e31c9abee952cbb6e375d1b8febc7499d6e16f1a") x0, _ := new(big.Int).SetString("1a8ed022ff7aec59dc1b440446bdda5ff6bcb3509a8b109077282b361efffbd8", 16) x1, _ := new(big.Int).SetString("6ab3ac374251f638d0abb3ef596d1dc67955b507c104e5f2009724812dc027b8", 16) y0, _ := new(big.Int).SetString("e040bd480b1deccc3bc40bd5b1fdcb7bfd352500b477cb9471366dbd4493f923", 16) @@ -354,7 +353,7 @@ func BenchmarkGenSharedKeyP256(b *testing.B) { // Benchmark the generation of S256 shared keys. func BenchmarkGenSharedKeyS256(b *testing.B) { - prv, err := GenerateKey(rand.Reader, secp256k1.S256(), nil) + prv, err := GenerateKey(rand.Reader, crypto.S256(), nil) if err != nil { fmt.Println(err.Error()) b.FailNow() @@ -597,6 +596,29 @@ func TestBasicKeyValidation(t *testing.T) { } } +func TestBox(t *testing.T) { + prv1 := hexKey("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f") + prv2 := hexKey("d0b043b4c5d657670778242d82d68a29d25d7d711127d17b8e299f156dad361a") + pub2 := &prv2.PublicKey + + message := []byte("Hello, world.") + ct, err := Encrypt(rand.Reader, pub2, message, nil, nil) + if err != nil { + t.Fatal(err) + } + + pt, err := prv2.Decrypt(rand.Reader, ct, nil, nil) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(pt, message) { + t.Fatal("ecies: plaintext doesn't match message") + } + if _, err = prv1.Decrypt(rand.Reader, ct, nil, nil); err == nil { + t.Fatal("ecies: encryption should not have succeeded") + } +} + // Verify GenerateShared against static values - useful when // debugging changes in underlying libs func TestSharedKeyStatic(t *testing.T) { @@ -628,11 +650,10 @@ func TestSharedKeyStatic(t *testing.T) { } } -// TODO: remove after refactoring packages crypto and crypto/ecies func hexKey(prv string) *PrivateKey { - priv := new(ecdsa.PrivateKey) - priv.PublicKey.Curve = secp256k1.S256() - priv.D, _ = new(big.Int).SetString(prv, 16) - priv.PublicKey.X, priv.PublicKey.Y = secp256k1.S256().ScalarBaseMult(priv.D.Bytes()) - return ImportECDSA(priv) + key, err := crypto.HexToECDSA(prv) + if err != nil { + panic(err) + } + return ImportECDSA(key) } diff --git a/crypto/ecies/params.go b/crypto/ecies/params.go index 511c53ebc0..826d90c845 100644 --- a/crypto/ecies/params.go +++ b/crypto/ecies/params.go @@ -42,11 +42,11 @@ import ( "fmt" "hash" - "github.com/ethereum/go-ethereum/crypto/secp256k1" + ethcrypto "github.com/ethereum/go-ethereum/crypto" ) var ( - DefaultCurve = secp256k1.S256() + DefaultCurve = ethcrypto.S256() ErrUnsupportedECDHAlgorithm = fmt.Errorf("ecies: unsupported ECDH algorithm") ErrUnsupportedECIESParameters = fmt.Errorf("ecies: unsupported ECIES parameters") ) @@ -100,7 +100,7 @@ var ( ) var paramsFromCurve = map[elliptic.Curve]*ECIESParams{ - secp256k1.S256(): ECIES_AES128_SHA256, + ethcrypto.S256(): ECIES_AES128_SHA256, elliptic.P256(): ECIES_AES128_SHA256, elliptic.P384(): ECIES_AES256_SHA384, elliptic.P521(): ECIES_AES256_SHA512, diff --git a/crypto/encrypt_decrypt_test.go b/crypto/encrypt_decrypt_test.go deleted file mode 100644 index fcf40b70fd..0000000000 --- a/crypto/encrypt_decrypt_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package crypto - -import ( - "bytes" - "fmt" - "testing" - - "github.com/ethereum/go-ethereum/common" -) - -func TestBox(t *testing.T) { - prv1 := ToECDSA(common.Hex2Bytes("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f")) - prv2 := ToECDSA(common.Hex2Bytes("d0b043b4c5d657670778242d82d68a29d25d7d711127d17b8e299f156dad361a")) - pub2 := ToECDSAPub(common.Hex2Bytes("04bd27a63c91fe3233c5777e6d3d7b39204d398c8f92655947eb5a373d46e1688f022a1632d264725cbc7dc43ee1cfebde42fa0a86d08b55d2acfbb5e9b3b48dc5")) - - message := []byte("Hello, world.") - ct, err := Encrypt(pub2, message) - if err != nil { - fmt.Println(err.Error()) - t.FailNow() - } - - pt, err := Decrypt(prv2, ct) - if err != nil { - fmt.Println(err.Error()) - t.FailNow() - } - - if !bytes.Equal(pt, message) { - fmt.Println("ecies: plaintext doesn't match message") - t.FailNow() - } - - _, err = Decrypt(prv1, pt) - if err == nil { - fmt.Println("ecies: encryption should not have succeeded") - t.FailNow() - } - -} diff --git a/crypto/secp256k1/secp256.go b/crypto/secp256k1/secp256.go index 4284115e2d..1a152a6706 100644 --- a/crypto/secp256k1/secp256.go +++ b/crypto/secp256k1/secp256.go @@ -42,17 +42,9 @@ import ( "unsafe" ) -var ( - context *C.secp256k1_context - N *big.Int - HalfN *big.Int -) +var context *C.secp256k1_context func init() { - N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16) - // N / 2 == 57896044618658097711785492504343953926418782139537452191302581570759080747168 - HalfN, _ = new(big.Int).SetString("7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", 16) - // around 20 ms on a modern CPU. context = C.secp256k1_context_create_sign_verify() C.secp256k1_context_set_illegal_callback(context, C.callbackFunc(C.secp256k1GoPanicIllegal), nil) diff --git a/crypto/signature_cgo.go b/crypto/signature_cgo.go new file mode 100644 index 0000000000..5faa6061fd --- /dev/null +++ b/crypto/signature_cgo.go @@ -0,0 +1,64 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build !nacl,!js,!nocgo + +package crypto + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/secp256k1" +) + +func Ecrecover(hash, sig []byte) ([]byte, error) { + return secp256k1.RecoverPubkey(hash, sig) +} + +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{Curve: S256(), X: x, Y: y}, nil +} + +// Sign calculates an ECDSA signature. +// +// This function is susceptible to chosen plaintext attacks that can leak +// information about the private key that is used for signing. Callers must +// be aware that the given hash cannot be chosen by an adversery. Common +// solution is to hash any input before calculating the signature. +// +// The produced signature is in the [R || S || V] format where V is 0 or 1. +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)) + } + seckey := common.LeftPadBytes(prv.D.Bytes(), prv.Params().BitSize/8) + defer zeroBytes(seckey) + return secp256k1.Sign(hash, seckey) +} + +// S256 returns an instance of the secp256k1 curve. +func S256() elliptic.Curve { + return secp256k1.S256() +} diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go new file mode 100644 index 0000000000..47880aaf4d --- /dev/null +++ b/crypto/signature_nocgo.go @@ -0,0 +1,77 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// +build nacl js nocgo + +package crypto + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "fmt" + + "github.com/btcsuite/btcd/btcec" +) + +func Ecrecover(hash, sig []byte) ([]byte, error) { + pub, err := SigToPub(hash, sig) + if err != nil { + return nil, err + } + bytes := (*btcec.PublicKey)(pub).SerializeUncompressed() + return bytes, err +} + +func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { + // Convert to btcec input format with 'recovery id' v at the beginning. + btcsig := make([]byte, 65) + btcsig[0] = sig[64] + 27 + copy(btcsig[1:], sig) + + pub, _, err := btcec.RecoverCompact(btcec.S256(), btcsig, hash) + return (*ecdsa.PublicKey)(pub), err +} + +// Sign calculates an ECDSA signature. +// +// This function is susceptible to chosen plaintext attacks that can leak +// information about the private key that is used for signing. Callers must +// be aware that the given hash cannot be chosen by an adversery. Common +// solution is to hash any input before calculating the signature. +// +// The produced signature is in the [R || S || V] format where V is 0 or 1. +func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) { + if len(hash) != 32 { + return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash)) + } + if prv.Curve != btcec.S256() { + return nil, fmt.Errorf("private key curve is not secp256k1") + } + sig, err := btcec.SignCompact(btcec.S256(), (*btcec.PrivateKey)(prv), hash, false) + if err != nil { + return nil, err + } + // Convert to Ethereum signature format with 'recovery id' v at the end. + v := sig[0] - 27 + copy(sig, sig[1:]) + sig[64] = v + return sig, nil +} + +// S256 returns an instance of the secp256k1 curve. +func S256() elliptic.Curve { + return btcec.S256() +} diff --git a/p2p/discv5/crypto.go b/crypto/signature_test.go similarity index 53% rename from p2p/discv5/crypto.go rename to crypto/signature_test.go index 48b2a8a729..ca7eefe992 100644 --- a/p2p/discv5/crypto.go +++ b/crypto/signature_test.go @@ -14,18 +14,23 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package discv5 +package crypto import ( - //"github.com/btcsuite/btcd/btcec" - "github.com/ethereum/go-ethereum/crypto/secp256k1" + "bytes" + "encoding/hex" + "testing" ) -func S256() *secp256k1.BitCurve { - return secp256k1.S256() +func TestRecoverSanity(t *testing.T) { + msg, _ := hex.DecodeString("ce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008") + sig, _ := hex.DecodeString("90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc9301") + pubkey1, _ := hex.DecodeString("04e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652") + pubkey2, err := Ecrecover(msg, sig) + if err != nil { + t.Fatalf("recover error: %s", err) + } + if !bytes.Equal(pubkey1, pubkey2) { + t.Errorf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2) + } } - -// This version should be used for NaCl compilation -/*func S256() *btcec.KoblitzCurve { - return S256() -}*/ diff --git a/p2p/discover/node.go b/p2p/discover/node.go index 8b1062d87b..f0262762ef 100644 --- a/p2p/discover/node.go +++ b/p2p/discover/node.go @@ -259,7 +259,7 @@ func PubkeyID(pub *ecdsa.PublicKey) NodeID { // Pubkey returns the public key represented by the node ID. // It returns an error if the ID is not a point on the curve. func (id NodeID) Pubkey() (*ecdsa.PublicKey, error) { - p := &ecdsa.PublicKey{Curve: secp256k1.S256(), X: new(big.Int), Y: new(big.Int)} + p := &ecdsa.PublicKey{Curve: crypto.S256(), X: new(big.Int), Y: new(big.Int)} half := len(id) / 2 p.X.SetBytes(id[:half]) p.Y.SetBytes(id[half:]) diff --git a/p2p/discv5/node.go b/p2p/discv5/node.go index cfc833ff55..c99b4da145 100644 --- a/p2p/discv5/node.go +++ b/p2p/discv5/node.go @@ -297,7 +297,7 @@ func PubkeyID(pub *ecdsa.PublicKey) NodeID { // Pubkey returns the public key represented by the node ID. // It returns an error if the ID is not a point on the curve. func (id NodeID) Pubkey() (*ecdsa.PublicKey, error) { - p := &ecdsa.PublicKey{Curve: S256(), X: new(big.Int), Y: new(big.Int)} + p := &ecdsa.PublicKey{Curve: crypto.S256(), X: new(big.Int), Y: new(big.Int)} half := len(id) / 2 p.X.SetBytes(id[:half]) p.Y.SetBytes(id[half:]) diff --git a/p2p/rlpx.go b/p2p/rlpx.go index c6fd841f7c..b2775cacdc 100644 --- a/p2p/rlpx.go +++ b/p2p/rlpx.go @@ -303,7 +303,7 @@ func (h *encHandshake) makeAuthMsg(prv *ecdsa.PrivateKey, token []byte) (*authMs return nil, err } // Generate random keypair to for ECDH. - h.randomPrivKey, err = ecies.GenerateKey(rand.Reader, secp256k1.S256(), nil) + h.randomPrivKey, err = ecies.GenerateKey(rand.Reader, crypto.S256(), nil) if err != nil { return nil, err } @@ -381,7 +381,7 @@ func (h *encHandshake) handleAuthMsg(msg *authMsgV4, prv *ecdsa.PrivateKey) erro // Generate random keypair for ECDH. // If a private key is already set, use it instead of generating one (for testing). if h.randomPrivKey == nil { - h.randomPrivKey, err = ecies.GenerateKey(rand.Reader, secp256k1.S256(), nil) + h.randomPrivKey, err = ecies.GenerateKey(rand.Reader, crypto.S256(), nil) if err != nil { return err } diff --git a/whisper/whisperv2/message.go b/whisper/whisperv2/message.go index 7ef9d0912a..e55544ffc9 100644 --- a/whisper/whisperv2/message.go +++ b/whisper/whisperv2/message.go @@ -21,11 +21,13 @@ package whisperv2 import ( "crypto/ecdsa" + crand "crypto/rand" "math/rand" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" ) @@ -131,13 +133,13 @@ func (self *Message) Recover() *ecdsa.PublicKey { // encrypt encrypts a message payload with a public key. func (self *Message) encrypt(key *ecdsa.PublicKey) (err error) { - self.Payload, err = crypto.Encrypt(key, self.Payload) + self.Payload, err = ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(key), self.Payload, nil, nil) return } // decrypt decrypts an encrypted payload with a private key. func (self *Message) decrypt(key *ecdsa.PrivateKey) error { - cleartext, err := crypto.Decrypt(key, self.Payload) + cleartext, err := ecies.ImportECDSA(key).Decrypt(crand.Reader, self.Payload, nil, nil) if err == nil { self.Payload = cleartext } diff --git a/whisper/whisperv2/message_test.go b/whisper/whisperv2/message_test.go index d3b307d2ab..c760ac54c4 100644 --- a/whisper/whisperv2/message_test.go +++ b/whisper/whisperv2/message_test.go @@ -23,7 +23,6 @@ import ( "time" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/crypto/secp256k1" ) // Tests whether a message can be wrapped without any identity or encryption. @@ -73,8 +72,8 @@ func TestMessageCleartextSignRecover(t *testing.T) { if pubKey == nil { t.Fatalf("failed to recover public key") } - p1 := elliptic.Marshal(secp256k1.S256(), key.PublicKey.X, key.PublicKey.Y) - p2 := elliptic.Marshal(secp256k1.S256(), pubKey.X, pubKey.Y) + p1 := elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y) + p2 := elliptic.Marshal(crypto.S256(), pubKey.X, pubKey.Y) if !bytes.Equal(p1, p2) { t.Fatalf("public key mismatch: have 0x%x, want 0x%x", p2, p1) } @@ -151,8 +150,8 @@ func TestMessageFullCrypto(t *testing.T) { if pubKey == nil { t.Fatalf("failed to recover public key") } - p1 := elliptic.Marshal(secp256k1.S256(), fromKey.PublicKey.X, fromKey.PublicKey.Y) - p2 := elliptic.Marshal(secp256k1.S256(), pubKey.X, pubKey.Y) + p1 := elliptic.Marshal(crypto.S256(), fromKey.PublicKey.X, fromKey.PublicKey.Y) + p2 := elliptic.Marshal(crypto.S256(), pubKey.X, pubKey.Y) if !bytes.Equal(p1, p2) { t.Fatalf("public key mismatch: have 0x%x, want 0x%x", p2, p1) } diff --git a/whisper/whisperv5/message.go b/whisper/whisperv5/message.go index a095f7c0cd..255ad380d5 100644 --- a/whisper/whisperv5/message.go +++ b/whisper/whisperv5/message.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "golang.org/x/crypto/pbkdf2" @@ -163,7 +164,7 @@ func (msg *SentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error { if !ValidatePublicKey(key) { return fmt.Errorf("Invalid public key provided for asymmetric encryption") } - encrypted, err := crypto.Encrypt(key, msg.Raw) + encrypted, err := ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(key), msg.Raw, nil, nil) if err == nil { msg.Raw = encrypted } @@ -293,7 +294,7 @@ func (msg *ReceivedMessage) decryptSymmetric(key []byte, salt []byte, nonce []by // decryptAsymmetric decrypts an encrypted payload with a private key. func (msg *ReceivedMessage) decryptAsymmetric(key *ecdsa.PrivateKey) error { - decrypted, err := crypto.Decrypt(key, msg.Raw) + decrypted, err := ecies.ImportECDSA(key).Decrypt(crand.Reader, msg.Raw, nil, nil) if err == nil { msg.Raw = decrypted }