From 27a50c8f4bc69f98e20db361859bfbb6cf371c00 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Mon, 28 Sep 2015 11:19:23 +0200 Subject: [PATCH 1/2] core/secp256k1: update libsecp256k1 Go wrapper and tests --- crypto/secp256k1/secp256.go | 4 ++-- crypto/secp256k1/secp256_test.go | 31 +++++++++++++++---------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/crypto/secp256k1/secp256.go b/crypto/secp256k1/secp256.go index 41a5608a5..7f26f307c 100644 --- a/crypto/secp256k1/secp256.go +++ b/crypto/secp256k1/secp256.go @@ -96,7 +96,7 @@ func GenerateKeyPair() ([]byte, []byte) { var output_len C.size_t - C.secp256k1_ec_pubkey_serialize( // always returns 1 + _ = C.secp256k1_ec_pubkey_serialize( // always returns 1 context, pubkey65_ptr, &output_len, @@ -163,7 +163,7 @@ func Sign(msg []byte, seckey []byte) ([]byte, error) { sig_serialized_ptr := (*C.uchar)(unsafe.Pointer(&sig_serialized[0])) var recid C.int - C.secp256k1_ecdsa_recoverable_signature_serialize_compact( + _ = C.secp256k1_ecdsa_recoverable_signature_serialize_compact( context, sig_serialized_ptr, // 64 byte compact signature &recid, diff --git a/crypto/secp256k1/secp256_test.go b/crypto/secp256k1/secp256_test.go index cb71ea5e7..d3ff2223d 100644 --- a/crypto/secp256k1/secp256_test.go +++ b/crypto/secp256k1/secp256_test.go @@ -86,10 +86,7 @@ func TestSignAndRecover(t *testing.T) { func TestRandomMessagesWithSameKey(t *testing.T) { pubkey, seckey := GenerateKeyPair() keys := func() ([]byte, []byte) { - // Sign function zeroes the privkey so we need a new one in each call - newkey := make([]byte, len(seckey)) - copy(newkey, seckey) - return pubkey, newkey + return pubkey, seckey } signAndRecoverWithRandomMessages(t, keys) } @@ -209,30 +206,32 @@ func compactSigCheck(t *testing.T, sig []byte) { } } -// godep go test -v -run=XXX -bench=BenchmarkSignRandomInputEachRound +// godep go test -v -run=XXX -bench=BenchmarkSign // add -benchtime=10s to benchmark longer for more accurate average -func BenchmarkSignRandomInputEachRound(b *testing.B) { + +// to avoid compiler optimizing the benchmarked function call +var err error + +func BenchmarkSign(b *testing.B) { for i := 0; i < b.N; i++ { - b.StopTimer() _, seckey := GenerateKeyPair() msg := randentropy.GetEntropyCSPRNG(32) b.StartTimer() - if _, err := Sign(msg, seckey); err != nil { - b.Fatal(err) - } + _, e := Sign(msg, seckey) + err = e + b.StopTimer() } } -//godep go test -v -run=XXX -bench=BenchmarkRecoverRandomInputEachRound -func BenchmarkRecoverRandomInputEachRound(b *testing.B) { +//godep go test -v -run=XXX -bench=BenchmarkECRec +func BenchmarkRecover(b *testing.B) { for i := 0; i < b.N; i++ { - b.StopTimer() _, seckey := GenerateKeyPair() msg := randentropy.GetEntropyCSPRNG(32) sig, _ := Sign(msg, seckey) b.StartTimer() - if _, err := RecoverPubkey(msg, sig); err != nil { - b.Fatal(err) - } + _, e := RecoverPubkey(msg, sig) + err = e + b.StopTimer() } } From c8ad64f33cd04fc10ac6681260ea06e464908c91 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Tue, 29 Sep 2015 19:37:44 +0200 Subject: [PATCH 2/2] crypto, crypto/ecies, crypto/secp256k1: libsecp256k1 scalar mult thanks to Felix Lange (fjl) for help with design & impl --- crypto/crypto.go | 26 ++--- crypto/crypto_test.go | 8 +- crypto/ecies/asn1.go | 7 ++ crypto/ecies/ecies.go | 1 + crypto/ecies/ecies_test.go | 123 +++++++++++++++++--- crypto/ecies/params.go | 14 +-- crypto/key.go | 5 +- crypto/{ => secp256k1}/curve.go | 166 +++++++++------------------ crypto/secp256k1/curve_test.go | 39 +++++++ crypto/secp256k1/pubkey_scalar_mul.h | 56 +++++++++ crypto/secp256k1/secp256.go | 28 ++++- crypto/secp256k1/secp256_test.go | 2 +- p2p/discover/node.go | 2 +- p2p/rlpx.go | 4 +- p2p/rlpx_test.go | 2 + whisper/message_test.go | 9 +- 16 files changed, 321 insertions(+), 171 deletions(-) rename crypto/{ => secp256k1}/curve.go (67%) create mode 100644 crypto/secp256k1/curve_test.go create mode 100644 crypto/secp256k1/pubkey_scalar_mul.h diff --git a/crypto/crypto.go b/crypto/crypto.go index 8685d62d3..7d7623753 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -43,14 +43,6 @@ import ( "golang.org/x/crypto/ripemd160" ) -var secp256k1n *big.Int - -func init() { - // specify the params for the s256 curve - ecies.AddParamsForCurve(S256(), ecies.ECIES_AES128_SHA256) - secp256k1n = common.String2Big("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141") -} - func Sha3(data ...[]byte) []byte { d := sha3.NewKeccak256() for _, b := range data { @@ -99,9 +91,9 @@ func ToECDSA(prv []byte) *ecdsa.PrivateKey { } priv := new(ecdsa.PrivateKey) - priv.PublicKey.Curve = S256() + priv.PublicKey.Curve = secp256k1.S256() priv.D = common.BigD(prv) - priv.PublicKey.X, priv.PublicKey.Y = S256().ScalarBaseMult(prv) + priv.PublicKey.X, priv.PublicKey.Y = secp256k1.S256().ScalarBaseMult(prv) return priv } @@ -116,15 +108,15 @@ func ToECDSAPub(pub []byte) *ecdsa.PublicKey { if len(pub) == 0 { return nil } - x, y := elliptic.Unmarshal(S256(), pub) - return &ecdsa.PublicKey{S256(), x, y} + x, y := elliptic.Unmarshal(secp256k1.S256(), pub) + return &ecdsa.PublicKey{secp256k1.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) + return elliptic.Marshal(secp256k1.S256(), pub.X, pub.Y) } // HexToECDSA parses a secp256k1 private key. @@ -168,7 +160,7 @@ func SaveECDSA(file string, key *ecdsa.PrivateKey) error { } func GenerateKey() (*ecdsa.PrivateKey, error) { - return ecdsa.GenerateKey(S256(), rand.Reader) + return ecdsa.GenerateKey(secp256k1.S256(), rand.Reader) } func ValidateSignatureValues(v byte, r, s *big.Int) bool { @@ -176,7 +168,7 @@ func ValidateSignatureValues(v byte, r, s *big.Int) bool { return false } vint := uint32(v) - if r.Cmp(secp256k1n) < 0 && s.Cmp(secp256k1n) < 0 && (vint == 27 || vint == 28) { + if r.Cmp(secp256k1.N) < 0 && s.Cmp(secp256k1.N) < 0 && (vint == 27 || vint == 28) { return true } else { return false @@ -189,8 +181,8 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { return nil, err } - x, y := elliptic.Unmarshal(S256(), s) - return &ecdsa.PublicKey{S256(), x, y}, nil + x, y := elliptic.Unmarshal(secp256k1.S256(), s) + return &ecdsa.PublicKey{secp256k1.S256(), x, y}, nil } func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) { diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index fdd9c1ee8..d5e19a4bb 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -181,7 +181,7 @@ func TestValidateSignatureValues(t *testing.T) { minusOne := big.NewInt(-1) one := common.Big1 zero := common.Big0 - secp256k1nMinus1 := new(big.Int).Sub(secp256k1n, common.Big1) + secp256k1nMinus1 := new(big.Int).Sub(secp256k1.N, common.Big1) // correct v,r,s check(true, 27, one, one) @@ -208,9 +208,9 @@ func TestValidateSignatureValues(t *testing.T) { // correct sig with max r,s check(true, 27, secp256k1nMinus1, secp256k1nMinus1) // correct v, combinations of incorrect r,s at upper limit - check(false, 27, secp256k1n, secp256k1nMinus1) - check(false, 27, secp256k1nMinus1, secp256k1n) - check(false, 27, secp256k1n, secp256k1n) + check(false, 27, secp256k1.N, secp256k1nMinus1) + check(false, 27, secp256k1nMinus1, secp256k1.N) + check(false, 27, 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 6eaf3d2ca..40dabd329 100644 --- a/crypto/ecies/asn1.go +++ b/crypto/ecies/asn1.go @@ -41,6 +41,8 @@ import ( "fmt" "hash" "math/big" + + "github.com/ethereum/go-ethereum/crypto/secp256k1" ) var ( @@ -81,6 +83,7 @@ func doScheme(base, v []int) asn1.ObjectIdentifier { type secgNamedCurve asn1.ObjectIdentifier var ( + secgNamedCurveS256 = secgNamedCurve{1, 3, 132, 0, 10} secgNamedCurveP256 = secgNamedCurve{1, 2, 840, 10045, 3, 1, 7} secgNamedCurveP384 = secgNamedCurve{1, 3, 132, 0, 34} secgNamedCurveP521 = secgNamedCurve{1, 3, 132, 0, 35} @@ -116,6 +119,8 @@ func (curve secgNamedCurve) Equal(curve2 secgNamedCurve) bool { func namedCurveFromOID(curve secgNamedCurve) elliptic.Curve { switch { + case curve.Equal(secgNamedCurveS256): + return secp256k1.S256() case curve.Equal(secgNamedCurveP256): return elliptic.P256() case curve.Equal(secgNamedCurveP384): @@ -134,6 +139,8 @@ func oidFromNamedCurve(curve elliptic.Curve) (secgNamedCurve, bool) { return secgNamedCurveP384, true case elliptic.P521(): return secgNamedCurveP521, true + case secp256k1.S256(): + return secgNamedCurveS256, true } return nil, false diff --git a/crypto/ecies/ecies.go b/crypto/ecies/ecies.go index a3b520dd5..65dc5b38b 100644 --- a/crypto/ecies/ecies.go +++ b/crypto/ecies/ecies.go @@ -125,6 +125,7 @@ func (prv *PrivateKey) GenerateShared(pub *PublicKey, skLen, macLen int) (sk []b if skLen+macLen > MaxSharedKeyLength(pub) { return nil, ErrSharedKeyTooBig } + x, _ := pub.Curve.ScalarMult(pub.X, pub.Y, prv.D.Bytes()) if x == nil { return nil, ErrSharedKeyIsPointAtInfinity diff --git a/crypto/ecies/ecies_test.go b/crypto/ecies/ecies_test.go index 1c391f938..6a0ea3f02 100644 --- a/crypto/ecies/ecies_test.go +++ b/crypto/ecies/ecies_test.go @@ -31,13 +31,18 @@ package ecies import ( "bytes" + "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" + "encoding/hex" "flag" "fmt" "io/ioutil" + "math/big" "testing" + + "github.com/ethereum/go-ethereum/crypto/secp256k1" ) var dumpEnc bool @@ -65,7 +70,6 @@ func TestKDF(t *testing.T) { } } -var skLen int var ErrBadSharedKeys = fmt.Errorf("ecies: shared keys don't match") // cmpParams compares a set of ECIES parameters. We assume, as per the @@ -117,7 +121,7 @@ func TestSharedKey(t *testing.T) { fmt.Println(err.Error()) t.FailNow() } - skLen = MaxSharedKeyLength(&prv1.PublicKey) / 2 + skLen := MaxSharedKeyLength(&prv1.PublicKey) / 2 prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil) if err != nil { @@ -143,6 +147,44 @@ func TestSharedKey(t *testing.T) { } } +func TestSharedKeyPadding(t *testing.T) { + // sanity checks + prv0 := hexKey("1adf5c18167d96a1f9a0b1ef63be8aa27eaf6032c233b2b38f7850cf5b859fd9") + prv1 := hexKey("97a076fc7fcd9208240668e31c9abee952cbb6e375d1b8febc7499d6e16f1a") + x0, _ := new(big.Int).SetString("1a8ed022ff7aec59dc1b440446bdda5ff6bcb3509a8b109077282b361efffbd8", 16) + x1, _ := new(big.Int).SetString("6ab3ac374251f638d0abb3ef596d1dc67955b507c104e5f2009724812dc027b8", 16) + y0, _ := new(big.Int).SetString("e040bd480b1deccc3bc40bd5b1fdcb7bfd352500b477cb9471366dbd4493f923", 16) + y1, _ := new(big.Int).SetString("8ad915f2b503a8be6facab6588731fefeb584fd2dfa9a77a5e0bba1ec439e4fa", 16) + + if prv0.PublicKey.X.Cmp(x0) != 0 { + t.Errorf("mismatched prv0.X:\nhave: %x\nwant: %x\n", prv0.PublicKey.X.Bytes(), x0.Bytes()) + } + if prv0.PublicKey.Y.Cmp(y0) != 0 { + t.Errorf("mismatched prv0.Y:\nhave: %x\nwant: %x\n", prv0.PublicKey.Y.Bytes(), y0.Bytes()) + } + if prv1.PublicKey.X.Cmp(x1) != 0 { + t.Errorf("mismatched prv1.X:\nhave: %x\nwant: %x\n", prv1.PublicKey.X.Bytes(), x1.Bytes()) + } + if prv1.PublicKey.Y.Cmp(y1) != 0 { + t.Errorf("mismatched prv1.Y:\nhave: %x\nwant: %x\n", prv1.PublicKey.Y.Bytes(), y1.Bytes()) + } + + // test shared secret generation + sk1, err := prv0.GenerateShared(&prv1.PublicKey, 16, 16) + if err != nil { + fmt.Println(err.Error()) + } + + sk2, err := prv1.GenerateShared(&prv0.PublicKey, 16, 16) + if err != nil { + t.Fatal(err.Error()) + } + + if !bytes.Equal(sk1, sk2) { + t.Fatal(ErrBadSharedKeys.Error()) + } +} + // Verify that the key generation code fails when too much key data is // requested. func TestTooBigSharedKey(t *testing.T) { @@ -158,13 +200,13 @@ func TestTooBigSharedKey(t *testing.T) { t.FailNow() } - _, err = prv1.GenerateShared(&prv2.PublicKey, skLen*2, skLen*2) + _, err = prv1.GenerateShared(&prv2.PublicKey, 32, 32) if err != ErrSharedKeyTooBig { fmt.Println("ecdh: shared key should be too large for curve") t.FailNow() } - _, err = prv2.GenerateShared(&prv1.PublicKey, skLen*2, skLen*2) + _, err = prv2.GenerateShared(&prv1.PublicKey, 32, 32) if err != ErrSharedKeyTooBig { fmt.Println("ecdh: shared key should be too large for curve") t.FailNow() @@ -176,25 +218,21 @@ func TestTooBigSharedKey(t *testing.T) { func TestMarshalPublic(t *testing.T) { prv, err := GenerateKey(rand.Reader, DefaultCurve, nil) if err != nil { - fmt.Println(err.Error()) - t.FailNow() + t.Fatalf("GenerateKey error: %s", err) } out, err := MarshalPublic(&prv.PublicKey) if err != nil { - fmt.Println(err.Error()) - t.FailNow() + t.Fatalf("MarshalPublic error: %s", err) } pub, err := UnmarshalPublic(out) if err != nil { - fmt.Println(err.Error()) - t.FailNow() + t.Fatalf("UnmarshalPublic error: %s", err) } if !cmpPublic(prv.PublicKey, *pub) { - fmt.Println("ecies: failed to unmarshal public key") - t.FailNow() + t.Fatal("ecies: failed to unmarshal public key") } } @@ -304,9 +342,26 @@ func BenchmarkGenSharedKeyP256(b *testing.B) { fmt.Println(err.Error()) b.FailNow() } - + b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := prv.GenerateShared(&prv.PublicKey, skLen, skLen) + _, err := prv.GenerateShared(&prv.PublicKey, 16, 16) + if err != nil { + fmt.Println(err.Error()) + b.FailNow() + } + } +} + +// Benchmark the generation of S256 shared keys. +func BenchmarkGenSharedKeyS256(b *testing.B) { + prv, err := GenerateKey(rand.Reader, secp256k1.S256(), nil) + if err != nil { + fmt.Println(err.Error()) + b.FailNow() + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := prv.GenerateShared(&prv.PublicKey, 16, 16) if err != nil { fmt.Println(err.Error()) b.FailNow() @@ -511,3 +566,43 @@ func TestBasicKeyValidation(t *testing.T) { } } } + +// Verify GenerateShared against static values - useful when +// debugging changes in underlying libs +func TestSharedKeyStatic(t *testing.T) { + prv1 := hexKey("7ebbc6a8358bc76dd73ebc557056702c8cfc34e5cfcd90eb83af0347575fd2ad") + prv2 := hexKey("6a3d6396903245bba5837752b9e0348874e72db0c4e11e9c485a81b4ea4353b9") + + skLen := MaxSharedKeyLength(&prv1.PublicKey) / 2 + + sk1, err := prv1.GenerateShared(&prv2.PublicKey, skLen, skLen) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + sk2, err := prv2.GenerateShared(&prv1.PublicKey, skLen, skLen) + if err != nil { + fmt.Println(err.Error()) + t.FailNow() + } + + if !bytes.Equal(sk1, sk2) { + fmt.Println(ErrBadSharedKeys.Error()) + t.FailNow() + } + + sk, _ := hex.DecodeString("167ccc13ac5e8a26b131c3446030c60fbfac6aa8e31149d0869f93626a4cdf62") + if !bytes.Equal(sk1, sk) { + t.Fatalf("shared secret mismatch: want: %x have: %x", sk, sk1) + } +} + +// 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) +} diff --git a/crypto/ecies/params.go b/crypto/ecies/params.go index 97ddb0973..511c53ebc 100644 --- a/crypto/ecies/params.go +++ b/crypto/ecies/params.go @@ -41,13 +41,12 @@ import ( "crypto/sha512" "fmt" "hash" + + "github.com/ethereum/go-ethereum/crypto/secp256k1" ) -// The default curve for this package is the NIST P256 curve, which -// provides security equivalent to AES-128. -var DefaultCurve = elliptic.P256() - var ( + DefaultCurve = secp256k1.S256() ErrUnsupportedECDHAlgorithm = fmt.Errorf("ecies: unsupported ECDH algorithm") ErrUnsupportedECIESParameters = fmt.Errorf("ecies: unsupported ECIES parameters") ) @@ -101,9 +100,10 @@ var ( ) var paramsFromCurve = map[elliptic.Curve]*ECIESParams{ - elliptic.P256(): ECIES_AES128_SHA256, - elliptic.P384(): ECIES_AES256_SHA384, - elliptic.P521(): ECIES_AES256_SHA512, + secp256k1.S256(): ECIES_AES128_SHA256, + elliptic.P256(): ECIES_AES128_SHA256, + elliptic.P384(): ECIES_AES256_SHA384, + elliptic.P521(): ECIES_AES256_SHA512, } func AddParamsForCurve(curve elliptic.Curve, params *ECIESParams) { diff --git a/crypto/key.go b/crypto/key.go index 4ec43dfd7..ccf284ad8 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -25,6 +25,7 @@ import ( "strings" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/pborman/uuid" ) @@ -137,7 +138,7 @@ func NewKey(rand io.Reader) *Key { panic("key generation: could not read from random source: " + err.Error()) } reader := bytes.NewReader(randBytes) - privateKeyECDSA, err := ecdsa.GenerateKey(S256(), reader) + privateKeyECDSA, err := ecdsa.GenerateKey(secp256k1.S256(), reader) if err != nil { panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) } @@ -155,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(S256(), reader) + privateKeyECDSA, err := ecdsa.GenerateKey(secp256k1.S256(), reader) if err != nil { panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) } diff --git a/crypto/curve.go b/crypto/secp256k1/curve.go similarity index 67% rename from crypto/curve.go rename to crypto/secp256k1/curve.go index 48f3f5e9c..6e44a6771 100644 --- a/crypto/curve.go +++ b/crypto/secp256k1/curve.go @@ -29,15 +29,22 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -package crypto +package secp256k1 import ( "crypto/elliptic" "io" "math/big" "sync" + "unsafe" ) +/* +#include "libsecp256k1/include/secp256k1.h" +extern int secp256k1_pubkey_scalar_mul(const secp256k1_context* ctx, const unsigned char *point, const unsigned char *scalar); +*/ +import "C" + // This code is from https://github.com/ThePiachu/GoBit and implements // several Koblitz elliptic curves over prime fields. // @@ -211,44 +218,37 @@ func (BitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, return x3, y3, z3 } -//TODO: double check if it is okay -// ScalarMult returns k*(Bx,By) where k is a number in big-endian form. -func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) { - // We have a slight problem in that the identity of the group (the - // point at infinity) cannot be represented in (x, y) form on a finite - // machine. Thus the standard add/double algorithm has to be tweaked - // slightly: our initial state is not the identity, but x, and we - // ignore the first true bit in |k|. If we don't find any true bits in - // |k|, then we return nil, nil, because we cannot return the identity - // element. - - Bz := new(big.Int).SetInt64(1) - x := Bx - y := By - z := Bz - - seenFirstTrue := false - for _, byte := range k { - for bitNum := 0; bitNum < 8; bitNum++ { - if seenFirstTrue { - x, y, z = BitCurve.doubleJacobian(x, y, z) - } - if byte&0x80 == 0x80 { - if !seenFirstTrue { - seenFirstTrue = true - } else { - x, y, z = BitCurve.addJacobian(Bx, By, Bz, x, y, z) - } - } - byte <<= 1 - } +func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) { + // Ensure scalar is exactly 32 bytes. We pad always, even if + // scalar is 32 bytes long, to avoid a timing side channel. + if len(scalar) > 32 { + panic("can't handle scalars > 256 bits") } + padded := make([]byte, 32) + copy(padded[32-len(scalar):], scalar) + scalar = padded - if !seenFirstTrue { + // Do the multiplication in C, updating point. + point := make([]byte, 64) + readBits(point[:32], Bx) + readBits(point[32:], By) + pointPtr := (*C.uchar)(unsafe.Pointer(&point[0])) + scalarPtr := (*C.uchar)(unsafe.Pointer(&scalar[0])) + res := C.secp256k1_pubkey_scalar_mul(context, pointPtr, scalarPtr) + + // Unpack the result and clear temporaries. + x := new(big.Int).SetBytes(point[:32]) + y := new(big.Int).SetBytes(point[32:]) + for i := range point { + point[i] = 0 + } + for i := range padded { + scalar[i] = 0 + } + if res != 1 { return nil, nil } - - return BitCurve.affineFromJacobian(x, y, z) + return x, y } // ScalarBaseMult returns k*G, where G is the base point of the group and k is @@ -312,86 +312,24 @@ func (BitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) { return } -//curve parameters taken from: -//http://www.secg.org/collateral/sec2_final.pdf - -var initonce sync.Once -var ecp160k1 *BitCurve -var ecp192k1 *BitCurve -var ecp224k1 *BitCurve -var ecp256k1 *BitCurve - -func initAll() { - initS160() - initS192() - initS224() - initS256() -} - -func initS160() { - // See SEC 2 section 2.4.1 - ecp160k1 = new(BitCurve) - ecp160k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", 16) - ecp160k1.N, _ = new(big.Int).SetString("0100000000000000000001B8FA16DFAB9ACA16B6B3", 16) - ecp160k1.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000007", 16) - ecp160k1.Gx, _ = new(big.Int).SetString("3B4C382CE37AA192A4019E763036F4F5DD4D7EBB", 16) - ecp160k1.Gy, _ = new(big.Int).SetString("938CF935318FDCED6BC28286531733C3F03C4FEE", 16) - ecp160k1.BitSize = 160 -} - -func initS192() { - // See SEC 2 section 2.5.1 - ecp192k1 = new(BitCurve) - ecp192k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37", 16) - ecp192k1.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D", 16) - ecp192k1.B, _ = new(big.Int).SetString("000000000000000000000000000000000000000000000003", 16) - ecp192k1.Gx, _ = new(big.Int).SetString("DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D", 16) - ecp192k1.Gy, _ = new(big.Int).SetString("9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D", 16) - ecp192k1.BitSize = 192 -} - -func initS224() { - // See SEC 2 section 2.6.1 - ecp224k1 = new(BitCurve) - ecp224k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D", 16) - ecp224k1.N, _ = new(big.Int).SetString("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7", 16) - ecp224k1.B, _ = new(big.Int).SetString("00000000000000000000000000000000000000000000000000000005", 16) - ecp224k1.Gx, _ = new(big.Int).SetString("A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C", 16) - ecp224k1.Gy, _ = new(big.Int).SetString("7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5", 16) - ecp224k1.BitSize = 224 -} - -func initS256() { - // See SEC 2 section 2.7.1 - ecp256k1 = new(BitCurve) - ecp256k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16) - ecp256k1.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16) - ecp256k1.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000000000000000000000000000007", 16) - ecp256k1.Gx, _ = new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16) - ecp256k1.Gy, _ = new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16) - ecp256k1.BitSize = 256 -} - -// S160 returns a BitCurve which implements secp160k1 (see SEC 2 section 2.4.1) -func S160() *BitCurve { - initonce.Do(initAll) - return ecp160k1 -} - -// S192 returns a BitCurve which implements secp192k1 (see SEC 2 section 2.5.1) -func S192() *BitCurve { - initonce.Do(initAll) - return ecp192k1 -} - -// S224 returns a BitCurve which implements secp224k1 (see SEC 2 section 2.6.1) -func S224() *BitCurve { - initonce.Do(initAll) - return ecp224k1 -} +var ( + initonce sync.Once + theCurve *BitCurve +) // S256 returns a BitCurve which implements secp256k1 (see SEC 2 section 2.7.1) func S256() *BitCurve { - initonce.Do(initAll) - return ecp256k1 + initonce.Do(func() { + // See SEC 2 section 2.7.1 + // curve parameters taken from: + // http://www.secg.org/collateral/sec2_final.pdf + theCurve = new(BitCurve) + theCurve.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16) + theCurve.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16) + theCurve.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000000000000000000000000000007", 16) + theCurve.Gx, _ = new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16) + theCurve.Gy, _ = new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16) + theCurve.BitSize = 256 + }) + return theCurve } diff --git a/crypto/secp256k1/curve_test.go b/crypto/secp256k1/curve_test.go new file mode 100644 index 000000000..d915ee852 --- /dev/null +++ b/crypto/secp256k1/curve_test.go @@ -0,0 +1,39 @@ +// Copyright 2015 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 secp256k1 + +import ( + "bytes" + "encoding/hex" + "math/big" + "testing" +) + +func TestReadBits(t *testing.T) { + check := func(input string) { + want, _ := hex.DecodeString(input) + int, _ := new(big.Int).SetString(input, 16) + buf := make([]byte, len(want)) + readBits(buf, int) + if !bytes.Equal(buf, want) { + t.Errorf("have: %x\nwant: %x", buf, want) + } + } + check("000000000000000000000000000000000000000000000000000000FEFCF3F8F0") + check("0000000000012345000000000000000000000000000000000000FEFCF3F8F0") + check("18F8F8F1000111000110011100222004330052300000000000000000FEFCF3F8F0") +} diff --git a/crypto/secp256k1/pubkey_scalar_mul.h b/crypto/secp256k1/pubkey_scalar_mul.h new file mode 100644 index 000000000..0511545ec --- /dev/null +++ b/crypto/secp256k1/pubkey_scalar_mul.h @@ -0,0 +1,56 @@ +// Copyright 2015 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 . + +/** Multiply point by scalar in constant time. + * Returns: 1: multiplication was successful + * 0: scalar was invalid (zero or overflow) + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: point: the multiplied point (usually secret) + * In: point: pointer to a 64-byte bytepublic point, + encoded as two 256bit big-endian numbers. + * scalar: a 32-byte scalar with which to multiply the point + */ +int secp256k1_pubkey_scalar_mul(const secp256k1_context* ctx, unsigned char *point, const unsigned char *scalar) { + int ret = 0; + int overflow = 0; + secp256k1_fe feX, feY; + secp256k1_gej res; + secp256k1_ge ge; + secp256k1_scalar s; + ARG_CHECK(point != NULL); + ARG_CHECK(scalar != NULL); + (void)ctx; + + secp256k1_fe_set_b32(&feX, point); + secp256k1_fe_set_b32(&feY, point+32); + secp256k1_ge_set_xy(&ge, &feX, &feY); + secp256k1_scalar_set_b32(&s, scalar, &overflow); + if (overflow || secp256k1_scalar_is_zero(&s)) { + ret = 0; + } else { + secp256k1_ecmult_const(&res, &ge, &s); + secp256k1_ge_set_gej(&ge, &res); + /* Note: can't use secp256k1_pubkey_save here because it is not constant time. */ + secp256k1_fe_normalize(&ge.x); + secp256k1_fe_normalize(&ge.y); + secp256k1_fe_get_b32(point, &ge.x); + secp256k1_fe_get_b32(point+32, &ge.y); + ret = 1; + } + secp256k1_scalar_clear(&s); + return ret; +} + diff --git a/crypto/secp256k1/secp256.go b/crypto/secp256k1/secp256.go index 7f26f307c..8dc248145 100644 --- a/crypto/secp256k1/secp256.go +++ b/crypto/secp256k1/secp256.go @@ -20,6 +20,7 @@ package secp256k1 /* #cgo CFLAGS: -I./libsecp256k1 +#cgo CFLAGS: -I./libsecp256k1/src/ #cgo darwin CFLAGS: -I/usr/local/include #cgo freebsd CFLAGS: -I/usr/local/include #cgo linux,arm CFLAGS: -I/usr/local/arm/include @@ -35,6 +36,7 @@ package secp256k1 #define NDEBUG #include "./libsecp256k1/src/secp256k1.c" #include "./libsecp256k1/src/modules/recovery/main_impl.h" +#include "pubkey_scalar_mul.h" typedef void (*callbackFunc) (const char* msg, void* data); extern void secp256k1GoPanicIllegal(const char* msg, void* data); @@ -44,6 +46,7 @@ import "C" import ( "errors" + "math/big" "unsafe" "github.com/ethereum/go-ethereum/crypto/randentropy" @@ -56,13 +59,16 @@ import ( > store private keys in buffer and shuffle (deters persistance on swap disc) > byte permutation (changing) > xor with chaning random block (to deter scanning memory for 0x63) (stream cipher?) - > on disk: store keys in wallets */ // holds ptr to secp256k1_context_struct (see secp256k1/include/secp256k1.h) -var context *C.secp256k1_context +var ( + context *C.secp256k1_context + N *big.Int +) func init() { + N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16) // around 20 ms on a modern CPU. context = C.secp256k1_context_create(3) // SECP256K1_START_SIGN | SECP256K1_START_VERIFY C.secp256k1_context_set_illegal_callback(context, C.callbackFunc(C.secp256k1GoPanicIllegal), nil) @@ -78,7 +84,6 @@ var ( func GenerateKeyPair() ([]byte, []byte) { var seckey []byte = randentropy.GetEntropyCSPRNG(32) var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0])) - var pubkey64 []byte = make([]byte, 64) // secp256k1_pubkey var pubkey65 []byte = make([]byte, 65) // 65 byte uncompressed pubkey pubkey64_ptr := (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey64[0])) @@ -96,7 +101,7 @@ func GenerateKeyPair() ([]byte, []byte) { var output_len C.size_t - _ = C.secp256k1_ec_pubkey_serialize( // always returns 1 + C.secp256k1_ec_pubkey_serialize( // always returns 1 context, pubkey65_ptr, &output_len, @@ -163,7 +168,7 @@ func Sign(msg []byte, seckey []byte) ([]byte, error) { sig_serialized_ptr := (*C.uchar)(unsafe.Pointer(&sig_serialized[0])) var recid C.int - _ = C.secp256k1_ecdsa_recoverable_signature_serialize_compact( + C.secp256k1_ecdsa_recoverable_signature_serialize_compact( context, sig_serialized_ptr, // 64 byte compact signature &recid, @@ -254,3 +259,16 @@ func checkSignature(sig []byte) error { } return nil } + +// reads num into buf as big-endian bytes. +func readBits(buf []byte, num *big.Int) { + const wordLen = int(unsafe.Sizeof(big.Word(0))) + i := len(buf) + for _, d := range num.Bits() { + for j := 0; j < wordLen && i > 0; j++ { + i-- + buf[i] = byte(d) + d >>= 8 + } + } +} diff --git a/crypto/secp256k1/secp256_test.go b/crypto/secp256k1/secp256_test.go index d3ff2223d..fc6fc9b32 100644 --- a/crypto/secp256k1/secp256_test.go +++ b/crypto/secp256k1/secp256_test.go @@ -24,7 +24,7 @@ import ( "github.com/ethereum/go-ethereum/crypto/randentropy" ) -const TestCount = 10000 +const TestCount = 1000 func TestPrivkeyGenerate(t *testing.T) { _, seckey := GenerateKeyPair() diff --git a/p2p/discover/node.go b/p2p/discover/node.go index a14f29424..dd19df3a2 100644 --- a/p2p/discover/node.go +++ b/p2p/discover/node.go @@ -210,7 +210,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: crypto.S256(), X: new(big.Int), Y: new(big.Int)} + p := &ecdsa.PublicKey{Curve: secp256k1.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 aaa733854..8f429d6ec 100644 --- a/p2p/rlpx.go +++ b/p2p/rlpx.go @@ -277,7 +277,7 @@ func newInitiatorHandshake(remoteID discover.NodeID) (*encHandshake, error) { return nil, err } // generate random keypair to use for signing - randpriv, err := ecies.GenerateKey(rand.Reader, crypto.S256(), nil) + randpriv, err := ecies.GenerateKey(rand.Reader, secp256k1.S256(), nil) if err != nil { return nil, err } @@ -376,7 +376,7 @@ func decodeAuthMsg(prv *ecdsa.PrivateKey, token []byte, auth []byte) (*encHandsh var err error h := new(encHandshake) // generate random keypair for session - h.randomPrivKey, err = ecies.GenerateKey(rand.Reader, crypto.S256(), nil) + h.randomPrivKey, err = ecies.GenerateKey(rand.Reader, secp256k1.S256(), nil) if err != nil { return nil, err } diff --git a/p2p/rlpx_test.go b/p2p/rlpx_test.go index 900353f0e..7cc7548e2 100644 --- a/p2p/rlpx_test.go +++ b/p2p/rlpx_test.go @@ -93,6 +93,7 @@ func testEncHandshake(token []byte) error { go func() { r := result{side: "initiator"} defer func() { output <- r }() + defer fd0.Close() dest := &discover.Node{ID: discover.PubkeyID(&prv1.PublicKey)} r.id, r.err = c0.doEncHandshake(prv0, dest) @@ -107,6 +108,7 @@ func testEncHandshake(token []byte) error { go func() { r := result{side: "receiver"} defer func() { output <- r }() + defer fd1.Close() r.id, r.err = c1.doEncHandshake(prv1, nil) if r.err != nil { diff --git a/whisper/message_test.go b/whisper/message_test.go index 6ff95efff..d70da40a4 100644 --- a/whisper/message_test.go +++ b/whisper/message_test.go @@ -23,6 +23,7 @@ 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. @@ -72,8 +73,8 @@ func TestMessageCleartextSignRecover(t *testing.T) { if pubKey == nil { t.Fatalf("failed to recover public key") } - p1 := elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y) - p2 := elliptic.Marshal(crypto.S256(), pubKey.X, pubKey.Y) + p1 := elliptic.Marshal(secp256k1.S256(), key.PublicKey.X, key.PublicKey.Y) + p2 := elliptic.Marshal(secp256k1.S256(), pubKey.X, pubKey.Y) if !bytes.Equal(p1, p2) { t.Fatalf("public key mismatch: have 0x%x, want 0x%x", p2, p1) } @@ -150,8 +151,8 @@ func TestMessageFullCrypto(t *testing.T) { if pubKey == nil { t.Fatalf("failed to recover public key") } - p1 := elliptic.Marshal(crypto.S256(), fromKey.PublicKey.X, fromKey.PublicKey.Y) - p2 := elliptic.Marshal(crypto.S256(), pubKey.X, pubKey.Y) + p1 := elliptic.Marshal(secp256k1.S256(), fromKey.PublicKey.X, fromKey.PublicKey.Y) + p2 := elliptic.Marshal(secp256k1.S256(), pubKey.X, pubKey.Y) if !bytes.Equal(p1, p2) { t.Fatalf("public key mismatch: have 0x%x, want 0x%x", p2, p1) }