Merge pull request #1862 from Gustav-Simonsson/libsecp256k1_ecdh

crypto, crypto/secp256k1: use libsecp256k1 for scalar multiplication
This commit is contained in:
Jeffrey Wilcke 2015-12-02 13:19:33 +01:00
commit 888e7bc765
16 changed files with 334 additions and 185 deletions

View File

@ -43,14 +43,6 @@ import (
"golang.org/x/crypto/ripemd160" "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 { func Sha3(data ...[]byte) []byte {
d := sha3.NewKeccak256() d := sha3.NewKeccak256()
for _, b := range data { for _, b := range data {
@ -99,9 +91,9 @@ func ToECDSA(prv []byte) *ecdsa.PrivateKey {
} }
priv := new(ecdsa.PrivateKey) priv := new(ecdsa.PrivateKey)
priv.PublicKey.Curve = S256() priv.PublicKey.Curve = secp256k1.S256()
priv.D = common.BigD(prv) 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 return priv
} }
@ -116,15 +108,15 @@ func ToECDSAPub(pub []byte) *ecdsa.PublicKey {
if len(pub) == 0 { if len(pub) == 0 {
return nil return nil
} }
x, y := elliptic.Unmarshal(S256(), pub) x, y := elliptic.Unmarshal(secp256k1.S256(), pub)
return &ecdsa.PublicKey{S256(), x, y} return &ecdsa.PublicKey{secp256k1.S256(), x, y}
} }
func FromECDSAPub(pub *ecdsa.PublicKey) []byte { func FromECDSAPub(pub *ecdsa.PublicKey) []byte {
if pub == nil || pub.X == nil || pub.Y == nil { if pub == nil || pub.X == nil || pub.Y == nil {
return 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. // HexToECDSA parses a secp256k1 private key.
@ -168,7 +160,7 @@ func SaveECDSA(file string, key *ecdsa.PrivateKey) error {
} }
func GenerateKey() (*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 { func ValidateSignatureValues(v byte, r, s *big.Int) bool {
@ -176,7 +168,7 @@ func ValidateSignatureValues(v byte, r, s *big.Int) bool {
return false return false
} }
vint := uint32(v) 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 return true
} else { } else {
return false return false
@ -189,8 +181,8 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
return nil, err return nil, err
} }
x, y := elliptic.Unmarshal(S256(), s) x, y := elliptic.Unmarshal(secp256k1.S256(), s)
return &ecdsa.PublicKey{S256(), x, y}, nil return &ecdsa.PublicKey{secp256k1.S256(), x, y}, nil
} }
func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) { func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {

View File

@ -181,7 +181,7 @@ func TestValidateSignatureValues(t *testing.T) {
minusOne := big.NewInt(-1) minusOne := big.NewInt(-1)
one := common.Big1 one := common.Big1
zero := common.Big0 zero := common.Big0
secp256k1nMinus1 := new(big.Int).Sub(secp256k1n, common.Big1) secp256k1nMinus1 := new(big.Int).Sub(secp256k1.N, common.Big1)
// correct v,r,s // correct v,r,s
check(true, 27, one, one) check(true, 27, one, one)
@ -208,9 +208,9 @@ func TestValidateSignatureValues(t *testing.T) {
// correct sig with max r,s // correct sig with max r,s
check(true, 27, secp256k1nMinus1, secp256k1nMinus1) check(true, 27, secp256k1nMinus1, secp256k1nMinus1)
// correct v, combinations of incorrect r,s at upper limit // correct v, combinations of incorrect r,s at upper limit
check(false, 27, secp256k1n, secp256k1nMinus1) check(false, 27, secp256k1.N, secp256k1nMinus1)
check(false, 27, secp256k1nMinus1, secp256k1n) check(false, 27, secp256k1nMinus1, secp256k1.N)
check(false, 27, secp256k1n, secp256k1n) check(false, 27, secp256k1.N, secp256k1.N)
// current callers ensures r,s cannot be negative, but let's test for that too // current callers ensures r,s cannot be negative, but let's test for that too
// as crypto package could be used stand-alone // as crypto package could be used stand-alone

View File

@ -41,6 +41,8 @@ import (
"fmt" "fmt"
"hash" "hash"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
) )
var ( var (
@ -81,6 +83,7 @@ func doScheme(base, v []int) asn1.ObjectIdentifier {
type secgNamedCurve asn1.ObjectIdentifier type secgNamedCurve asn1.ObjectIdentifier
var ( var (
secgNamedCurveS256 = secgNamedCurve{1, 3, 132, 0, 10}
secgNamedCurveP256 = secgNamedCurve{1, 2, 840, 10045, 3, 1, 7} secgNamedCurveP256 = secgNamedCurve{1, 2, 840, 10045, 3, 1, 7}
secgNamedCurveP384 = secgNamedCurve{1, 3, 132, 0, 34} secgNamedCurveP384 = secgNamedCurve{1, 3, 132, 0, 34}
secgNamedCurveP521 = secgNamedCurve{1, 3, 132, 0, 35} secgNamedCurveP521 = secgNamedCurve{1, 3, 132, 0, 35}
@ -116,6 +119,8 @@ func (curve secgNamedCurve) Equal(curve2 secgNamedCurve) bool {
func namedCurveFromOID(curve secgNamedCurve) elliptic.Curve { func namedCurveFromOID(curve secgNamedCurve) elliptic.Curve {
switch { switch {
case curve.Equal(secgNamedCurveS256):
return secp256k1.S256()
case curve.Equal(secgNamedCurveP256): case curve.Equal(secgNamedCurveP256):
return elliptic.P256() return elliptic.P256()
case curve.Equal(secgNamedCurveP384): case curve.Equal(secgNamedCurveP384):
@ -134,6 +139,8 @@ func oidFromNamedCurve(curve elliptic.Curve) (secgNamedCurve, bool) {
return secgNamedCurveP384, true return secgNamedCurveP384, true
case elliptic.P521(): case elliptic.P521():
return secgNamedCurveP521, true return secgNamedCurveP521, true
case secp256k1.S256():
return secgNamedCurveS256, true
} }
return nil, false return nil, false

View File

@ -125,6 +125,7 @@ func (prv *PrivateKey) GenerateShared(pub *PublicKey, skLen, macLen int) (sk []b
if skLen+macLen > MaxSharedKeyLength(pub) { if skLen+macLen > MaxSharedKeyLength(pub) {
return nil, ErrSharedKeyTooBig return nil, ErrSharedKeyTooBig
} }
x, _ := pub.Curve.ScalarMult(pub.X, pub.Y, prv.D.Bytes()) x, _ := pub.Curve.ScalarMult(pub.X, pub.Y, prv.D.Bytes())
if x == nil { if x == nil {
return nil, ErrSharedKeyIsPointAtInfinity return nil, ErrSharedKeyIsPointAtInfinity

View File

@ -31,13 +31,18 @@ package ecies
import ( import (
"bytes" "bytes"
"crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/sha256" "crypto/sha256"
"encoding/hex"
"flag" "flag"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/big"
"testing" "testing"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
) )
var dumpEnc bool 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") var ErrBadSharedKeys = fmt.Errorf("ecies: shared keys don't match")
// cmpParams compares a set of ECIES parameters. We assume, as per the // 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()) fmt.Println(err.Error())
t.FailNow() t.FailNow()
} }
skLen = MaxSharedKeyLength(&prv1.PublicKey) / 2 skLen := MaxSharedKeyLength(&prv1.PublicKey) / 2
prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil) prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != 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 // Verify that the key generation code fails when too much key data is
// requested. // requested.
func TestTooBigSharedKey(t *testing.T) { func TestTooBigSharedKey(t *testing.T) {
@ -158,13 +200,13 @@ func TestTooBigSharedKey(t *testing.T) {
t.FailNow() t.FailNow()
} }
_, err = prv1.GenerateShared(&prv2.PublicKey, skLen*2, skLen*2) _, err = prv1.GenerateShared(&prv2.PublicKey, 32, 32)
if err != ErrSharedKeyTooBig { if err != ErrSharedKeyTooBig {
fmt.Println("ecdh: shared key should be too large for curve") fmt.Println("ecdh: shared key should be too large for curve")
t.FailNow() t.FailNow()
} }
_, err = prv2.GenerateShared(&prv1.PublicKey, skLen*2, skLen*2) _, err = prv2.GenerateShared(&prv1.PublicKey, 32, 32)
if err != ErrSharedKeyTooBig { if err != ErrSharedKeyTooBig {
fmt.Println("ecdh: shared key should be too large for curve") fmt.Println("ecdh: shared key should be too large for curve")
t.FailNow() t.FailNow()
@ -176,25 +218,21 @@ func TestTooBigSharedKey(t *testing.T) {
func TestMarshalPublic(t *testing.T) { func TestMarshalPublic(t *testing.T) {
prv, err := GenerateKey(rand.Reader, DefaultCurve, nil) prv, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil { if err != nil {
fmt.Println(err.Error()) t.Fatalf("GenerateKey error: %s", err)
t.FailNow()
} }
out, err := MarshalPublic(&prv.PublicKey) out, err := MarshalPublic(&prv.PublicKey)
if err != nil { if err != nil {
fmt.Println(err.Error()) t.Fatalf("MarshalPublic error: %s", err)
t.FailNow()
} }
pub, err := UnmarshalPublic(out) pub, err := UnmarshalPublic(out)
if err != nil { if err != nil {
fmt.Println(err.Error()) t.Fatalf("UnmarshalPublic error: %s", err)
t.FailNow()
} }
if !cmpPublic(prv.PublicKey, *pub) { if !cmpPublic(prv.PublicKey, *pub) {
fmt.Println("ecies: failed to unmarshal public key") t.Fatal("ecies: failed to unmarshal public key")
t.FailNow()
} }
} }
@ -304,9 +342,26 @@ func BenchmarkGenSharedKeyP256(b *testing.B) {
fmt.Println(err.Error()) fmt.Println(err.Error())
b.FailNow() b.FailNow()
} }
b.ResetTimer()
for i := 0; i < b.N; i++ { 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 { if err != nil {
fmt.Println(err.Error()) fmt.Println(err.Error())
b.FailNow() 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)
}

View File

@ -41,13 +41,12 @@ import (
"crypto/sha512" "crypto/sha512"
"fmt" "fmt"
"hash" "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 ( var (
DefaultCurve = secp256k1.S256()
ErrUnsupportedECDHAlgorithm = fmt.Errorf("ecies: unsupported ECDH algorithm") ErrUnsupportedECDHAlgorithm = fmt.Errorf("ecies: unsupported ECDH algorithm")
ErrUnsupportedECIESParameters = fmt.Errorf("ecies: unsupported ECIES parameters") ErrUnsupportedECIESParameters = fmt.Errorf("ecies: unsupported ECIES parameters")
) )
@ -101,9 +100,10 @@ var (
) )
var paramsFromCurve = map[elliptic.Curve]*ECIESParams{ var paramsFromCurve = map[elliptic.Curve]*ECIESParams{
elliptic.P256(): ECIES_AES128_SHA256, secp256k1.S256(): ECIES_AES128_SHA256,
elliptic.P384(): ECIES_AES256_SHA384, elliptic.P256(): ECIES_AES128_SHA256,
elliptic.P521(): ECIES_AES256_SHA512, elliptic.P384(): ECIES_AES256_SHA384,
elliptic.P521(): ECIES_AES256_SHA512,
} }
func AddParamsForCurve(curve elliptic.Curve, params *ECIESParams) { func AddParamsForCurve(curve elliptic.Curve, params *ECIESParams) {

View File

@ -25,6 +25,7 @@ import (
"strings" "strings"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/pborman/uuid" "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()) panic("key generation: could not read from random source: " + err.Error())
} }
reader := bytes.NewReader(randBytes) reader := bytes.NewReader(randBytes)
privateKeyECDSA, err := ecdsa.GenerateKey(S256(), reader) privateKeyECDSA, err := ecdsa.GenerateKey(secp256k1.S256(), reader)
if err != nil { if err != nil {
panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) 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()) panic("key generation: could not read from random source: " + err.Error())
} }
reader := bytes.NewReader(randBytes) reader := bytes.NewReader(randBytes)
privateKeyECDSA, err := ecdsa.GenerateKey(S256(), reader) privateKeyECDSA, err := ecdsa.GenerateKey(secp256k1.S256(), reader)
if err != nil { if err != nil {
panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
} }

View File

@ -29,15 +29,22 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package crypto package secp256k1
import ( import (
"crypto/elliptic" "crypto/elliptic"
"io" "io"
"math/big" "math/big"
"sync" "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 // This code is from https://github.com/ThePiachu/GoBit and implements
// several Koblitz elliptic curves over prime fields. // 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 return x3, y3, z3
} }
//TODO: double check if it is okay func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
// ScalarMult returns k*(Bx,By) where k is a number in big-endian form. // Ensure scalar is exactly 32 bytes. We pad always, even if
func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) { // scalar is 32 bytes long, to avoid a timing side channel.
// We have a slight problem in that the identity of the group (the if len(scalar) > 32 {
// point at infinity) cannot be represented in (x, y) form on a finite panic("can't handle scalars > 256 bits")
// 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
}
} }
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 nil, nil
} }
return x, y
return BitCurve.affineFromJacobian(x, y, z)
} }
// ScalarBaseMult returns k*G, where G is the base point of the group and k is // 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 return
} }
//curve parameters taken from: var (
//http://www.secg.org/collateral/sec2_final.pdf initonce sync.Once
theCurve *BitCurve
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
}
// S256 returns a BitCurve which implements secp256k1 (see SEC 2 section 2.7.1) // S256 returns a BitCurve which implements secp256k1 (see SEC 2 section 2.7.1)
func S256() *BitCurve { func S256() *BitCurve {
initonce.Do(initAll) initonce.Do(func() {
return ecp256k1 // 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
} }

View File

@ -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 <http://www.gnu.org/licenses/>.
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")
}

View File

@ -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 <http://www.gnu.org/licenses/>.
/** 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;
}

View File

@ -20,6 +20,7 @@ package secp256k1
/* /*
#cgo CFLAGS: -I./libsecp256k1 #cgo CFLAGS: -I./libsecp256k1
#cgo CFLAGS: -I./libsecp256k1/src/
#cgo darwin CFLAGS: -I/usr/local/include #cgo darwin CFLAGS: -I/usr/local/include
#cgo freebsd CFLAGS: -I/usr/local/include #cgo freebsd CFLAGS: -I/usr/local/include
#cgo linux,arm CFLAGS: -I/usr/local/arm/include #cgo linux,arm CFLAGS: -I/usr/local/arm/include
@ -35,6 +36,7 @@ package secp256k1
#define NDEBUG #define NDEBUG
#include "./libsecp256k1/src/secp256k1.c" #include "./libsecp256k1/src/secp256k1.c"
#include "./libsecp256k1/src/modules/recovery/main_impl.h" #include "./libsecp256k1/src/modules/recovery/main_impl.h"
#include "pubkey_scalar_mul.h"
typedef void (*callbackFunc) (const char* msg, void* data); typedef void (*callbackFunc) (const char* msg, void* data);
extern void secp256k1GoPanicIllegal(const char* msg, void* data); extern void secp256k1GoPanicIllegal(const char* msg, void* data);
@ -44,6 +46,7 @@ import "C"
import ( import (
"errors" "errors"
"math/big"
"unsafe" "unsafe"
"github.com/ethereum/go-ethereum/crypto/randentropy" "github.com/ethereum/go-ethereum/crypto/randentropy"
@ -56,13 +59,16 @@ import (
> store private keys in buffer and shuffle (deters persistance on swap disc) > store private keys in buffer and shuffle (deters persistance on swap disc)
> byte permutation (changing) > byte permutation (changing)
> xor with chaning random block (to deter scanning memory for 0x63) (stream cipher?) > 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) // 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() { func init() {
N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16)
// around 20 ms on a modern CPU. // around 20 ms on a modern CPU.
context = C.secp256k1_context_create(3) // SECP256K1_START_SIGN | SECP256K1_START_VERIFY context = C.secp256k1_context_create(3) // SECP256K1_START_SIGN | SECP256K1_START_VERIFY
C.secp256k1_context_set_illegal_callback(context, C.callbackFunc(C.secp256k1GoPanicIllegal), nil) C.secp256k1_context_set_illegal_callback(context, C.callbackFunc(C.secp256k1GoPanicIllegal), nil)
@ -78,7 +84,6 @@ var (
func GenerateKeyPair() ([]byte, []byte) { func GenerateKeyPair() ([]byte, []byte) {
var seckey []byte = randentropy.GetEntropyCSPRNG(32) var seckey []byte = randentropy.GetEntropyCSPRNG(32)
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0])) var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
var pubkey64 []byte = make([]byte, 64) // secp256k1_pubkey var pubkey64 []byte = make([]byte, 64) // secp256k1_pubkey
var pubkey65 []byte = make([]byte, 65) // 65 byte uncompressed pubkey var pubkey65 []byte = make([]byte, 65) // 65 byte uncompressed pubkey
pubkey64_ptr := (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey64[0])) pubkey64_ptr := (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey64[0]))
@ -254,3 +259,16 @@ func checkSignature(sig []byte) error {
} }
return nil 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
}
}
}

View File

@ -24,7 +24,7 @@ import (
"github.com/ethereum/go-ethereum/crypto/randentropy" "github.com/ethereum/go-ethereum/crypto/randentropy"
) )
const TestCount = 10000 const TestCount = 1000
func TestPrivkeyGenerate(t *testing.T) { func TestPrivkeyGenerate(t *testing.T) {
_, seckey := GenerateKeyPair() _, seckey := GenerateKeyPair()
@ -86,10 +86,7 @@ func TestSignAndRecover(t *testing.T) {
func TestRandomMessagesWithSameKey(t *testing.T) { func TestRandomMessagesWithSameKey(t *testing.T) {
pubkey, seckey := GenerateKeyPair() pubkey, seckey := GenerateKeyPair()
keys := func() ([]byte, []byte) { keys := func() ([]byte, []byte) {
// Sign function zeroes the privkey so we need a new one in each call return pubkey, seckey
newkey := make([]byte, len(seckey))
copy(newkey, seckey)
return pubkey, newkey
} }
signAndRecoverWithRandomMessages(t, keys) 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 // 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++ { for i := 0; i < b.N; i++ {
b.StopTimer()
_, seckey := GenerateKeyPair() _, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32) msg := randentropy.GetEntropyCSPRNG(32)
b.StartTimer() b.StartTimer()
if _, err := Sign(msg, seckey); err != nil { _, e := Sign(msg, seckey)
b.Fatal(err) err = e
} b.StopTimer()
} }
} }
//godep go test -v -run=XXX -bench=BenchmarkRecoverRandomInputEachRound //godep go test -v -run=XXX -bench=BenchmarkECRec
func BenchmarkRecoverRandomInputEachRound(b *testing.B) { func BenchmarkRecover(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
b.StopTimer()
_, seckey := GenerateKeyPair() _, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32) msg := randentropy.GetEntropyCSPRNG(32)
sig, _ := Sign(msg, seckey) sig, _ := Sign(msg, seckey)
b.StartTimer() b.StartTimer()
if _, err := RecoverPubkey(msg, sig); err != nil { _, e := RecoverPubkey(msg, sig)
b.Fatal(err) err = e
} b.StopTimer()
} }
} }

View File

@ -210,7 +210,7 @@ func PubkeyID(pub *ecdsa.PublicKey) NodeID {
// Pubkey returns the public key represented by the node ID. // Pubkey returns the public key represented by the node ID.
// It returns an error if the ID is not a point on the curve. // It returns an error if the ID is not a point on the curve.
func (id NodeID) Pubkey() (*ecdsa.PublicKey, error) { 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 half := len(id) / 2
p.X.SetBytes(id[:half]) p.X.SetBytes(id[:half])
p.Y.SetBytes(id[half:]) p.Y.SetBytes(id[half:])

View File

@ -277,7 +277,7 @@ func newInitiatorHandshake(remoteID discover.NodeID) (*encHandshake, error) {
return nil, err return nil, err
} }
// generate random keypair to use for signing // 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 { if err != nil {
return nil, err return nil, err
} }
@ -376,7 +376,7 @@ func decodeAuthMsg(prv *ecdsa.PrivateKey, token []byte, auth []byte) (*encHandsh
var err error var err error
h := new(encHandshake) h := new(encHandshake)
// generate random keypair for session // 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 { if err != nil {
return nil, err return nil, err
} }

View File

@ -93,6 +93,7 @@ func testEncHandshake(token []byte) error {
go func() { go func() {
r := result{side: "initiator"} r := result{side: "initiator"}
defer func() { output <- r }() defer func() { output <- r }()
defer fd0.Close()
dest := &discover.Node{ID: discover.PubkeyID(&prv1.PublicKey)} dest := &discover.Node{ID: discover.PubkeyID(&prv1.PublicKey)}
r.id, r.err = c0.doEncHandshake(prv0, dest) r.id, r.err = c0.doEncHandshake(prv0, dest)
@ -107,6 +108,7 @@ func testEncHandshake(token []byte) error {
go func() { go func() {
r := result{side: "receiver"} r := result{side: "receiver"}
defer func() { output <- r }() defer func() { output <- r }()
defer fd1.Close()
r.id, r.err = c1.doEncHandshake(prv1, nil) r.id, r.err = c1.doEncHandshake(prv1, nil)
if r.err != nil { if r.err != nil {

View File

@ -23,6 +23,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/crypto" "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. // Tests whether a message can be wrapped without any identity or encryption.
@ -72,8 +73,8 @@ func TestMessageCleartextSignRecover(t *testing.T) {
if pubKey == nil { if pubKey == nil {
t.Fatalf("failed to recover public key") t.Fatalf("failed to recover public key")
} }
p1 := elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y) p1 := elliptic.Marshal(secp256k1.S256(), key.PublicKey.X, key.PublicKey.Y)
p2 := elliptic.Marshal(crypto.S256(), pubKey.X, pubKey.Y) p2 := elliptic.Marshal(secp256k1.S256(), pubKey.X, pubKey.Y)
if !bytes.Equal(p1, p2) { if !bytes.Equal(p1, p2) {
t.Fatalf("public key mismatch: have 0x%x, want 0x%x", p2, p1) 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 { if pubKey == nil {
t.Fatalf("failed to recover public key") t.Fatalf("failed to recover public key")
} }
p1 := elliptic.Marshal(crypto.S256(), fromKey.PublicKey.X, fromKey.PublicKey.Y) p1 := elliptic.Marshal(secp256k1.S256(), fromKey.PublicKey.X, fromKey.PublicKey.Y)
p2 := elliptic.Marshal(crypto.S256(), pubKey.X, pubKey.Y) p2 := elliptic.Marshal(secp256k1.S256(), pubKey.X, pubKey.Y)
if !bytes.Equal(p1, p2) { if !bytes.Equal(p1, p2) {
t.Fatalf("public key mismatch: have 0x%x, want 0x%x", p2, p1) t.Fatalf("public key mismatch: have 0x%x, want 0x%x", p2, p1)
} }