From b15da9006b7ca8d4b6d5e61dbf768cb0be773389 Mon Sep 17 00:00:00 2001 From: armfazh Date: Thu, 26 Jan 2023 16:26:26 -0800 Subject: [PATCH] Adds prefixed labels and updates nomenclature. --- zk/dl/dl.go | 110 +++++++++++++++++++++++------------------------ zk/dl/dl_test.go | 17 ++++---- 2 files changed, 64 insertions(+), 63 deletions(-) diff --git a/zk/dl/dl.go b/zk/dl/dl.go index 98ac03d9..abecf3cc 100644 --- a/zk/dl/dl.go +++ b/zk/dl/dl.go @@ -1,24 +1,36 @@ -// Reference: https://datatracker.ietf.org/doc/html/rfc8235#page-6 -// Prove the knowledge of [k] given [k]G, G and the curve where the points reside +// Package dl provides a Schnorr NIZK discrete-log proof. +// +// This package implements a Schnorr NIZK discrete-log proof obtained from the +// interactive Schnorr identification scheme through a Fiat-Shamir transformation. +// +// Given (k,G,kG) the Prove function returns a Proof struct attesting that +// kG = [k]G, which can be validated using the Verify function. +// +// The userID label is a unique identifier for the prover. +// +// The otherInfo label is defined to allow flexible inclusion of contextual +// information in the Schnorr NIZK proof. +// The otherInfo is also used as a domain separation tag (dst) for the hash +// to scalar function. +// +// Reference: https://datatracker.ietf.org/doc/html/rfc8235 package dl import ( + "encoding/binary" "io" "github.com/cloudflare/circl/group" ) -// Input: myGroup, the group we operate in -// Input: R = [kA]DB -// Input: proverLabel, verifierLabel labels of prover and verifier -// Ouptput: (V,r), the prove such that we know kA without revealing kA -func ProveGen(myGroup group.Group, DB, R group.Element, kA group.Scalar, proverLabel, verifierLabel, dst []byte, rnd io.Reader) (group.Element, group.Scalar) { - v := myGroup.RandomNonZeroScalar(rnd) - V := myGroup.NewElement() - V.Mul(DB, v) +type Proof struct { + V group.Element + R group.Scalar +} - // Hash transcript (D_B | V | R | proverLabel | verifierLabel) to get the random coin - DBByte, errByte := DB.MarshalBinary() +func calcChallenge(myGroup group.Group, G, V, A group.Element, userID, otherInfo []byte) group.Scalar { + // Hash transcript (G | V | A | UserID | OtherInfo) to get the random coin. + GByte, errByte := G.MarshalBinary() if errByte != nil { panic(errByte) } @@ -26,61 +38,49 @@ func ProveGen(myGroup group.Group, DB, R group.Element, kA group.Scalar, proverL if errByte != nil { panic(errByte) } - - RByte, errByte := R.MarshalBinary() + AByte, errByte := A.MarshalBinary() if errByte != nil { panic(errByte) } - hashByte := append(DBByte, VByte...) - hashByte = append(hashByte, RByte...) - hashByte = append(hashByte, proverLabel...) - hashByte = append(hashByte, verifierLabel...) - - c := myGroup.HashToScalar(hashByte, dst) + uPrefix := [4]byte{} + binary.BigEndian.PutUint32(uPrefix[:], uint32(len(userID))) + oPrefix := [4]byte{} + binary.BigEndian.PutUint32(oPrefix[:], uint32(len(otherInfo))) - kAc := myGroup.NewScalar() - kAc.Mul(c, kA) - r := v.Copy() - r.Sub(r, kAc) + hashByte := append(append(append(append(append(append( + GByte, VByte...), AByte...), + uPrefix[:]...), userID...), + oPrefix[:]...), otherInfo...) - return V, r + return myGroup.HashToScalar(hashByte, otherInfo) } -// Input: myGroup, the group we operate in -// Input: R = [kA]DB -// Input: (V,r), the prove such that the prover knows kA -// Input: proverLabel, verifierLabel labels of prover and verifier -// Output: V ?= [r]D_B +[c]R -func Verify(myGroup group.Group, DB, R group.Element, V group.Element, r group.Scalar, proverLabel, verifierLabel, dst []byte) bool { - // Hash the transcript (D_B | V | R | proverLabel | verifierLabel) to get the random coin - DBByte, errByte := DB.MarshalBinary() - if errByte != nil { - panic(errByte) - } - VByte, errByte := V.MarshalBinary() - if errByte != nil { - panic(errByte) - } +// Prove returns a proof attesting that kG = [k]G. +func Prove(myGroup group.Group, G, kG group.Element, k group.Scalar, userID, otherInfo []byte, rnd io.Reader) Proof { + v := myGroup.RandomNonZeroScalar(rnd) + V := myGroup.NewElement() + V.Mul(G, v) - RByte, errByte := R.MarshalBinary() - if errByte != nil { - panic(errByte) - } - hashByte := append(DBByte, VByte...) - hashByte = append(hashByte, RByte...) - hashByte = append(hashByte, proverLabel...) - hashByte = append(hashByte, verifierLabel...) + c := calcChallenge(myGroup, G, V, kG, userID, otherInfo) + + r := myGroup.NewScalar() + r.Sub(v, myGroup.NewScalar().Mul(k, c)) + + return Proof{V, r} +} - c := myGroup.HashToScalar(hashByte, dst) +// Verify checks whether the proof attests that kG = [k]G. +func Verify(myGroup group.Group, G, kG group.Element, p Proof, userID, otherInfo []byte) bool { + c := calcChallenge(myGroup, G, p.V, kG, userID, otherInfo) - rDB := myGroup.NewElement() - rDB.Mul(DB, r) + rG := myGroup.NewElement() + rG.Mul(G, p.R) - cR := myGroup.NewElement() - cR.Mul(R, c) + ckG := myGroup.NewElement() + ckG.Mul(kG, c) - rDB.Add(rDB, cR) + rG.Add(rG, ckG) - return V.IsEqual(rDB) + return p.V.IsEqual(rG) } diff --git a/zk/dl/dl_test.go b/zk/dl/dl_test.go index aaf306e4..4773d76e 100644 --- a/zk/dl/dl_test.go +++ b/zk/dl/dl_test.go @@ -1,13 +1,14 @@ -package dl +package dl_test import ( "crypto/rand" "testing" "github.com/cloudflare/circl/group" + "github.com/cloudflare/circl/zk/dl" ) -const testzkDLCount = 10 +const testzkDLCount = 1 << 8 func testzkDL(t *testing.T, myGroup group.Group) { kA := myGroup.RandomNonZeroScalar(rand.Reader) @@ -18,11 +19,11 @@ func testzkDL(t *testing.T, myGroup group.Group) { dst := "zeroknowledge" rnd := rand.Reader - V, r := ProveGen(myGroup, DB, R, kA, []byte("Prover"), []byte("Verifier"), []byte(dst), rnd) + proof := dl.Prove(myGroup, DB, R, kA, []byte("Prover"), []byte(dst), rnd) - verify := Verify(myGroup, DB, R, V, r, []byte("Prover"), []byte("Verifier"), []byte(dst)) + verify := dl.Verify(myGroup, DB, R, proof, []byte("Prover"), []byte(dst)) if verify == false { - t.Error("zkRDL verification failed") + t.Error("zk/dl verification failed") } } @@ -34,11 +35,11 @@ func testzkDLNegative(t *testing.T, myGroup group.Group) { dst := "zeroknowledge" rnd := rand.Reader - V, r := ProveGen(myGroup, DB, R, kA, []byte("Prover"), []byte("Verifier"), []byte(dst), rnd) + proof := dl.Prove(myGroup, DB, R, kA, []byte("Prover"), []byte(dst), rnd) - verify := Verify(myGroup, DB, R, V, r, []byte("Prover"), []byte("Verifier"), []byte(dst)) + verify := dl.Verify(myGroup, DB, R, proof, []byte("Prover"), []byte(dst)) if verify == true { - t.Error("zkRDL verification should fail") + t.Error("zk/dl verification should fail") } }