crypto, crypto/ecies, crypto/secp256k1: libsecp256k1 scalar mult
thanks to Felix Lange (fjl) for help with design & impl
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+109
-14
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user