diff --git a/zk/dl/dl.go b/zk/dl/dl.go new file mode 100644 index 000000000..98ac03d9a --- /dev/null +++ b/zk/dl/dl.go @@ -0,0 +1,86 @@ +// 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 + +import ( + "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) + + // Hash 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) + } + + 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 := myGroup.HashToScalar(hashByte, dst) + + kAc := myGroup.NewScalar() + kAc.Mul(c, kA) + r := v.Copy() + r.Sub(r, kAc) + + return V, r +} + +// 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) + } + + 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 := myGroup.HashToScalar(hashByte, dst) + + rDB := myGroup.NewElement() + rDB.Mul(DB, r) + + cR := myGroup.NewElement() + cR.Mul(R, c) + + rDB.Add(rDB, cR) + + return V.IsEqual(rDB) +} diff --git a/zk/dl/dl_test.go b/zk/dl/dl_test.go new file mode 100644 index 000000000..aaf306e43 --- /dev/null +++ b/zk/dl/dl_test.go @@ -0,0 +1,59 @@ +package dl + +import ( + "crypto/rand" + "testing" + + "github.com/cloudflare/circl/group" +) + +const testzkDLCount = 10 + +func testzkDL(t *testing.T, myGroup group.Group) { + kA := myGroup.RandomNonZeroScalar(rand.Reader) + DB := myGroup.RandomElement(rand.Reader) + + R := myGroup.NewElement() + R.Mul(DB, kA) + + dst := "zeroknowledge" + rnd := rand.Reader + V, r := ProveGen(myGroup, DB, R, kA, []byte("Prover"), []byte("Verifier"), []byte(dst), rnd) + + verify := Verify(myGroup, DB, R, V, r, []byte("Prover"), []byte("Verifier"), []byte(dst)) + if verify == false { + t.Error("zkRDL verification failed") + } +} + +func testzkDLNegative(t *testing.T, myGroup group.Group) { + kA := myGroup.RandomNonZeroScalar(rand.Reader) + DB := myGroup.RandomElement(rand.Reader) + + R := myGroup.RandomElement(rand.Reader) + + dst := "zeroknowledge" + rnd := rand.Reader + V, r := ProveGen(myGroup, DB, R, kA, []byte("Prover"), []byte("Verifier"), []byte(dst), rnd) + + verify := Verify(myGroup, DB, R, V, r, []byte("Prover"), []byte("Verifier"), []byte(dst)) + if verify == true { + t.Error("zkRDL verification should fail") + } +} + +func TestZKDL(t *testing.T) { + t.Run("zkDL", func(t *testing.T) { + for i := 0; i < testzkDLCount; i++ { + currGroup := group.P256 + testzkDL(t, currGroup) + } + }) + + t.Run("zkDLNegative", func(t *testing.T) { + for i := 0; i < testzkDLCount; i++ { + currGroup := group.P256 + testzkDLNegative(t, currGroup) + } + }) +}