From ae2b6ff21510610de5a26789aa4501d13485c3a5 Mon Sep 17 00:00:00 2001 From: Gaurav Narula Date: Wed, 12 Aug 2020 14:05:46 +0200 Subject: [PATCH] Schnorr Verification Checks Added `schnorr.VerifyWithChecks` which checks if the scalars and points are canonical and ensures the points do not have a small order for Edwards25519 Group. Builds on top of #432 and closes #431 Co-authored-by: David Cerezo --- sign/schnorr/schnorr.go | 32 +++++++++++++++++++++++++++++--- sign/schnorr/schnorr_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/sign/schnorr/schnorr.go b/sign/schnorr/schnorr.go index def76095b..9d0dae6b8 100644 --- a/sign/schnorr/schnorr.go +++ b/sign/schnorr/schnorr.go @@ -18,6 +18,8 @@ import ( "fmt" "go.dedis.ch/kyber/v3" + "go.dedis.ch/kyber/v3/group/edwards25519" + "go.dedis.ch/kyber/v3/sign/eddsa" ) // Suite represents the set of functionalities needed by the package schnorr. @@ -57,9 +59,16 @@ func Sign(s Suite, private kyber.Scalar, msg []byte) ([]byte, error) { return b.Bytes(), nil } -// Verify verifies a given Schnorr signature. It returns nil iff the -// given signature is valid. -func Verify(g kyber.Group, public kyber.Point, msg, sig []byte) error { +// VerifyWithChecks uses a public key buffer, a message and a signature. +// It will return nil if sig is a valid signature for msg created by +// key public, or an error otherwise. Compared to `Verify`, it performs +// additional checks around the canonicality and ensures the public key +// does not have a small order when using `edwards25519` group. +func VerifyWithChecks(g kyber.Group, pub, msg, sig []byte) error { + if _, ok := g.(*edwards25519.SuiteEd25519); ok { + return eddsa.VerifyWithChecks(pub, msg, sig) + } + R := g.Point() s := g.Scalar() pointSize := R.MarshalSize() @@ -74,6 +83,12 @@ func Verify(g kyber.Group, public kyber.Point, msg, sig []byte) error { if err := s.UnmarshalBinary(sig[pointSize:]); err != nil { return err } + + public := g.Point() + err := public.UnmarshalBinary(pub) + if err != nil { + return fmt.Errorf("schnorr: error unmarshalling public key") + } // recompute hash(public || R || msg) h, err := hash(g, public, R, msg) if err != nil { @@ -91,6 +106,17 @@ func Verify(g kyber.Group, public kyber.Point, msg, sig []byte) error { } return nil + +} + +// Verify verifies a given Schnorr signature. It returns nil iff the +// given signature is valid. +func Verify(g kyber.Group, public kyber.Point, msg, sig []byte) error { + PBuf, err := public.MarshalBinary() + if err != nil { + return fmt.Errorf("error unmarshalling public key: %s", err) + } + return VerifyWithChecks(g, PBuf, msg, sig) } func hash(g kyber.Group, public, r kyber.Point, msg []byte) (kyber.Scalar, error) { diff --git a/sign/schnorr/schnorr_test.go b/sign/schnorr/schnorr_test.go index 43d9855ba..e669c2ce1 100644 --- a/sign/schnorr/schnorr_test.go +++ b/sign/schnorr/schnorr_test.go @@ -96,3 +96,32 @@ func TestQuickSchnorrSignature(t *testing.T) { t.Error(err) } } + +func TestSchnorrMalleability(t *testing.T) { + /* l = 2^252+27742317777372353535851937790883648493, prime order of the base point */ + var L []uint16 = []uint16{0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, + 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10} + var c uint16 = 0 + + msg := []byte("Hello Schnorr") + suite := edwards25519.NewBlakeSHA256Ed25519() + kp := key.NewKeyPair(suite) + + s, err := Sign(suite, kp.Private, msg) + assert.NoErrorf(t, err, "Couldn't sign msg: %s: %v", msg, err) + + err = Verify(suite, kp.Public, msg, s) + assert.NoErrorf(t, err, "Couldn't verify signature (schnorr.Verify): \n%+v\nfor msg:'%s'. Error:\n%v", s, msg, err) + + // Add l to signature + for i := 0; i < 32; i++ { + c += uint16(s[32+i]) + L[i] + s[32+i] = byte(c) + c >>= 8 + } + assert.Error(t, eddsa.Verify(kp.Public, msg, s)) + + err = Verify(suite, kp.Public, msg, s) + assert.Error(t, err, "schnorr signature malleable") +}