diff --git a/group/edwards25519/curve.go b/group/edwards25519/curve.go index 5a20e81ff..379801ab3 100644 --- a/group/edwards25519/curve.go +++ b/group/edwards25519/curve.go @@ -45,16 +45,32 @@ func (c *Curve) Point() kyber.Point { return P } -// NewKey returns a formatted Ed25519 key (avoiding subgroup attack by requiring -// it to be a multiple of 8). NewKey implements the kyber/util/key.Generator interface. -func (c *Curve) NewKey(stream cipher.Stream) kyber.Scalar { +// NewKeyAndSeedWithInput returns a formatted Ed25519 key (avoid subgroup attack by +// requiring it to be a multiple of 8). It also returns the input and the digest used +// to generate the key. +func (c *Curve) NewKeyAndSeedWithInput(buffer []byte) (kyber.Scalar, []byte, []byte) { + digest := sha512.Sum512(buffer[:]) + digest[0] &= 0xf8 + digest[31] &= 0x7f + digest[31] |= 0x40 + + secret := c.Scalar().(*scalar) + copy(secret.v[:], digest[:]) + return secret, buffer, digest[32:] +} + +// NewKeyAndSeed returns a formatted Ed25519 key (avoid subgroup attack by requiring +// it to be a multiple of 8). It also returns the seed and the input used to generate +// the key. +func (c *Curve) NewKeyAndSeed(stream cipher.Stream) (kyber.Scalar, []byte, []byte) { var buffer [32]byte random.Bytes(buffer[:], stream) - scalar := sha512.Sum512(buffer[:]) - scalar[0] &= 0xf8 - scalar[31] &= 0x7f - scalar[31] |= 0x40 + return c.NewKeyAndSeedWithInput(buffer[:]) +} - secret := c.Scalar().SetBytes(scalar[:32]) +// NewKey returns a formatted Ed25519 key (avoiding subgroup attack by requiring +// it to be a multiple of 8). NewKey implements the kyber/util/key.Generator interface. +func (c *Curve) NewKey(stream cipher.Stream) kyber.Scalar { + secret, _, _ := c.NewKeyAndSeed(stream) return secret } diff --git a/group/edwards25519/curve_test.go b/group/edwards25519/curve_test.go index 2ffb66d69..853e46e85 100644 --- a/group/edwards25519/curve_test.go +++ b/group/edwards25519/curve_test.go @@ -1,8 +1,10 @@ package edwards25519 import ( + "math" "testing" + "github.com/stretchr/testify/assert" "go.dedis.ch/kyber/v3/util/test" ) @@ -11,6 +13,19 @@ var groupBench = test.NewGroupBench(tSuite) func TestSuite(t *testing.T) { test.SuiteTest(t, tSuite) } +// Test that NewKey generates correct secret keys +func TestCurve_NewKey(t *testing.T) { + group := Curve{} + stream := tSuite.RandomStream() + + for i := 0.0; i < math.Pow(10, 6); i++ { + s := group.NewKey(stream).(*scalar) + + // little-endian check of a multiple of 8 + assert.Equal(t, uint8(0), s.v[0]&7) + } +} + func BenchmarkScalarAdd(b *testing.B) { groupBench.ScalarAdd(b.N) } func BenchmarkScalarSub(b *testing.B) { groupBench.ScalarSub(b.N) } func BenchmarkScalarNeg(b *testing.B) { groupBench.ScalarNeg(b.N) } diff --git a/sign/eddsa/eddsa.go b/sign/eddsa/eddsa.go index cfeeb78fc..00cc0141e 100644 --- a/sign/eddsa/eddsa.go +++ b/sign/eddsa/eddsa.go @@ -10,7 +10,6 @@ import ( "go.dedis.ch/kyber/v3" "go.dedis.ch/kyber/v3/group/edwards25519" - "go.dedis.ch/kyber/v3/util/random" ) var group = new(edwards25519.Curve) @@ -33,17 +32,13 @@ func NewEdDSA(stream cipher.Stream) *EdDSA { if stream == nil { panic("stream is required") } - var buffer [32]byte - random.Bytes(buffer[:], stream) - scalar := hashSeed(buffer[:]) - - secret := group.Scalar().SetBytes(scalar[:32]) + secret, buffer, prefix := group.NewKeyAndSeed(stream) public := group.Point().Mul(secret, nil) return &EdDSA{ - seed: buffer[:], - prefix: scalar[32:], + seed: buffer, + prefix: prefix, Secret: secret, Public: public, } @@ -69,10 +64,11 @@ func (e *EdDSA) UnmarshalBinary(buff []byte) error { return errors.New("wrong length for decoding EdDSA private") } + secret, _, prefix := group.NewKeyAndSeedWithInput(buff[:32]) + e.seed = buff[:32] - scalar := hashSeed(e.seed) - e.prefix = scalar[32:] - e.Secret = group.Scalar().SetBytes(scalar[:32]) + e.prefix = prefix + e.Secret = secret e.Public = group.Point().Mul(e.Secret, nil) return nil } @@ -161,11 +157,3 @@ func Verify(public kyber.Point, msg, sig []byte) error { } return nil } - -func hashSeed(seed []byte) (hash [64]byte) { - hash = sha512.Sum512(seed) - hash[0] &= 0xf8 - hash[31] &= 0x3f - hash[31] |= 0x40 - return -} diff --git a/sign/eddsa/eddsa_test.go b/sign/eddsa/eddsa_test.go index bacf2c68f..07b0d61d5 100644 --- a/sign/eddsa/eddsa_test.go +++ b/sign/eddsa/eddsa_test.go @@ -6,10 +6,13 @@ import ( "compress/gzip" "crypto/cipher" "encoding/hex" + "math/rand" "os" "strings" "testing" + "go.dedis.ch/kyber/v3/group/edwards25519" + "github.com/stretchr/testify/assert" ) @@ -51,6 +54,9 @@ func TestEdDSAMarshalling(t *testing.T) { stream := ConstantStream(seed) edDSA := NewEdDSA(stream) + + assert.Equal(t, edDSA.Public.String(), vec.public) + marshalled, err := edDSA.MarshalBinary() assert.Nil(t, err) assert.NotNil(t, marshalled) @@ -95,6 +101,25 @@ func TestEdDSASigning(t *testing.T) { } } +// Test the property of a EdDSA signature +func TestEdDSASigningRandom(t *testing.T) { + suite := edwards25519.NewBlakeSHA256Ed25519() + + for i := 0.0; i < 10000; i++ { + ed := NewEdDSA(suite.RandomStream()) + + msg := make([]byte, 32) + _, err := rand.Read(msg) + assert.NoError(t, err) + + sig, err := ed.Sign(msg) + assert.Nil(t, err) + // see https://tools.ietf.org/html/rfc8032#section-5.1.6 (item 6.) + assert.Equal(t, uint8(0), sig[63]&0xe0) + assert.Nil(t, Verify(ed.Public, msg, sig)) + } +} + type constantStream struct { seed []byte }