github.com/cloudflare/circl@v1.5.0/zk/dl/dl.go (about) 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 17 package dl 18 19 import ( 20 "encoding/binary" 21 "io" 22 23 "github.com/cloudflare/circl/group" 24 ) 25 26 type Proof struct { 27 V group.Element 28 R group.Scalar 29 } 30 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() 34 if errByte != nil { 35 panic(errByte) 36 } 37 VByte, errByte := V.MarshalBinary() 38 if errByte != nil { 39 panic(errByte) 40 } 41 AByte, errByte := A.MarshalBinary() 42 if errByte != nil { 43 panic(errByte) 44 } 45 46 uPrefix := [4]byte{} 47 binary.BigEndian.PutUint32(uPrefix[:], uint32(len(userID))) 48 oPrefix := [4]byte{} 49 binary.BigEndian.PutUint32(oPrefix[:], uint32(len(otherInfo))) 50 51 hashByte := append(append(append(append(append(append( 52 GByte, VByte...), AByte...), 53 uPrefix[:]...), userID...), 54 oPrefix[:]...), otherInfo...) 55 56 return myGroup.HashToScalar(hashByte, otherInfo) 57 } 58 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) 64 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 } 72 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 77 rG := myGroup.NewElement() 78 rG.Mul(G, p.R) 79 80 ckG := myGroup.NewElement() 81 ckG.Mul(kG, c) 82 83 rG.Add(rG, ckG) 84 85 return p.V.IsEqual(rG) 86 }