|
1 | | -// Reference: https://datatracker.ietf.org/doc/html/rfc8235#page-6 |
2 | | -// Prove the knowledge of [k] given [k]G, G and the curve where the points reside |
| 1 | +// Package dl provides a Schnorr NIZK discrete-log proof. |
| 2 | +// |
| 3 | +// This package implements a Schnorr NIZK discrete-log proof obtained from the |
| 4 | +// interactive Schnorr identification scheme through a Fiat-Shamir transformation. |
| 5 | +// |
| 6 | +// Given (k,G,kG) the Prove function returns a Proof struct attesting that |
| 7 | +// kG = [k]G, which can be validated using the Verify function. |
| 8 | +// |
| 9 | +// The userID label is a unique identifier for the prover. |
| 10 | +// |
| 11 | +// The otherInfo label is defined to allow flexible inclusion of contextual |
| 12 | +// information in the Schnorr NIZK proof. |
| 13 | +// The otherInfo is also used as a domain separation tag (dst) for the hash |
| 14 | +// to scalar function. |
| 15 | +// |
| 16 | +// Reference: https://datatracker.ietf.org/doc/html/rfc8235 |
3 | 17 | package dl |
4 | 18 |
|
5 | 19 | import ( |
| 20 | + "encoding/binary" |
6 | 21 | "io" |
7 | 22 |
|
8 | 23 | "github.com/cloudflare/circl/group" |
9 | 24 | ) |
10 | 25 |
|
11 | | -// Input: myGroup, the group we operate in |
12 | | -// Input: R = [kA]DB |
13 | | -// Input: proverLabel, verifierLabel labels of prover and verifier |
14 | | -// Ouptput: (V,r), the prove such that we know kA without revealing kA |
15 | | -func ProveGen(myGroup group.Group, DB, R group.Element, kA group.Scalar, proverLabel, verifierLabel, dst []byte, rnd io.Reader) (group.Element, group.Scalar) { |
16 | | - v := myGroup.RandomNonZeroScalar(rnd) |
17 | | - V := myGroup.NewElement() |
18 | | - V.Mul(DB, v) |
| 26 | +type Proof struct { |
| 27 | + V group.Element |
| 28 | + R group.Scalar |
| 29 | +} |
19 | 30 |
|
20 | | - // Hash transcript (D_B | V | R | proverLabel | verifierLabel) to get the random coin |
21 | | - DBByte, errByte := DB.MarshalBinary() |
| 31 | +func calcChallenge(myGroup group.Group, G, V, A group.Element, userID, otherInfo []byte) group.Scalar { |
| 32 | + // Hash transcript (G | V | A | UserID | OtherInfo) to get the random coin. |
| 33 | + GByte, errByte := G.MarshalBinary() |
22 | 34 | if errByte != nil { |
23 | 35 | panic(errByte) |
24 | 36 | } |
25 | 37 | VByte, errByte := V.MarshalBinary() |
26 | 38 | if errByte != nil { |
27 | 39 | panic(errByte) |
28 | 40 | } |
29 | | - |
30 | | - RByte, errByte := R.MarshalBinary() |
| 41 | + AByte, errByte := A.MarshalBinary() |
31 | 42 | if errByte != nil { |
32 | 43 | panic(errByte) |
33 | 44 | } |
34 | 45 |
|
35 | | - hashByte := append(DBByte, VByte...) |
36 | | - hashByte = append(hashByte, RByte...) |
37 | | - hashByte = append(hashByte, proverLabel...) |
38 | | - hashByte = append(hashByte, verifierLabel...) |
39 | | - |
40 | | - c := myGroup.HashToScalar(hashByte, dst) |
| 46 | + uPrefix := [4]byte{} |
| 47 | + binary.BigEndian.PutUint32(uPrefix[:], uint32(len(userID))) |
| 48 | + oPrefix := [4]byte{} |
| 49 | + binary.BigEndian.PutUint32(oPrefix[:], uint32(len(otherInfo))) |
41 | 50 |
|
42 | | - kAc := myGroup.NewScalar() |
43 | | - kAc.Mul(c, kA) |
44 | | - r := v.Copy() |
45 | | - r.Sub(r, kAc) |
| 51 | + hashByte := append(append(append(append(append(append( |
| 52 | + GByte, VByte...), AByte...), |
| 53 | + uPrefix[:]...), userID...), |
| 54 | + oPrefix[:]...), otherInfo...) |
46 | 55 |
|
47 | | - return V, r |
| 56 | + return myGroup.HashToScalar(hashByte, otherInfo) |
48 | 57 | } |
49 | 58 |
|
50 | | -// Input: myGroup, the group we operate in |
51 | | -// Input: R = [kA]DB |
52 | | -// Input: (V,r), the prove such that the prover knows kA |
53 | | -// Input: proverLabel, verifierLabel labels of prover and verifier |
54 | | -// Output: V ?= [r]D_B +[c]R |
55 | | -func Verify(myGroup group.Group, DB, R group.Element, V group.Element, r group.Scalar, proverLabel, verifierLabel, dst []byte) bool { |
56 | | - // Hash the transcript (D_B | V | R | proverLabel | verifierLabel) to get the random coin |
57 | | - DBByte, errByte := DB.MarshalBinary() |
58 | | - if errByte != nil { |
59 | | - panic(errByte) |
60 | | - } |
61 | | - VByte, errByte := V.MarshalBinary() |
62 | | - if errByte != nil { |
63 | | - panic(errByte) |
64 | | - } |
| 59 | +// Prove returns a proof attesting that kG = [k]G. |
| 60 | +func Prove(myGroup group.Group, G, kG group.Element, k group.Scalar, userID, otherInfo []byte, rnd io.Reader) Proof { |
| 61 | + v := myGroup.RandomNonZeroScalar(rnd) |
| 62 | + V := myGroup.NewElement() |
| 63 | + V.Mul(G, v) |
65 | 64 |
|
66 | | - RByte, errByte := R.MarshalBinary() |
67 | | - if errByte != nil { |
68 | | - panic(errByte) |
69 | | - } |
70 | | - hashByte := append(DBByte, VByte...) |
71 | | - hashByte = append(hashByte, RByte...) |
72 | | - hashByte = append(hashByte, proverLabel...) |
73 | | - hashByte = append(hashByte, verifierLabel...) |
| 65 | + c := calcChallenge(myGroup, G, V, kG, userID, otherInfo) |
| 66 | + |
| 67 | + r := myGroup.NewScalar() |
| 68 | + r.Sub(v, myGroup.NewScalar().Mul(k, c)) |
| 69 | + |
| 70 | + return Proof{V, r} |
| 71 | +} |
74 | 72 |
|
75 | | - c := myGroup.HashToScalar(hashByte, dst) |
| 73 | +// Verify checks whether the proof attests that kG = [k]G. |
| 74 | +func Verify(myGroup group.Group, G, kG group.Element, p Proof, userID, otherInfo []byte) bool { |
| 75 | + c := calcChallenge(myGroup, G, p.V, kG, userID, otherInfo) |
76 | 76 |
|
77 | | - rDB := myGroup.NewElement() |
78 | | - rDB.Mul(DB, r) |
| 77 | + rG := myGroup.NewElement() |
| 78 | + rG.Mul(G, p.R) |
79 | 79 |
|
80 | | - cR := myGroup.NewElement() |
81 | | - cR.Mul(R, c) |
| 80 | + ckG := myGroup.NewElement() |
| 81 | + ckG.Mul(kG, c) |
82 | 82 |
|
83 | | - rDB.Add(rDB, cR) |
| 83 | + rG.Add(rG, ckG) |
84 | 84 |
|
85 | | - return V.IsEqual(rDB) |
| 85 | + return p.V.IsEqual(rG) |
86 | 86 | } |
0 commit comments