github.com/cloudflare/circl@v1.5.0/zk/qndleq/qndleq.go (about) 1 // Package qndleq provides zero-knowledge proofs of Discrete-Logarithm Equivalence (DLEQ) on Qn. 2 // 3 // This package implements proofs on the group Qn (the subgroup of squares in (Z/nZ)*). 4 // 5 // # Notation 6 // 7 // Z/nZ is the ring of integers modulo N. 8 // (Z/nZ)* is the multiplicative group of Z/nZ, a.k.a. the units of Z/nZ, the elements with inverse mod N. 9 // Qn is the subgroup of squares in (Z/nZ)*. 10 // 11 // A number x belongs to Qn if 12 // 13 // gcd(x, N) = 1, and 14 // exists y such that x = y^2 mod N. 15 // 16 // # References 17 // 18 // [DLEQ Proof] "Wallet databases with observers" by Chaum-Pedersen. 19 // https://doi.org/10.1007/3-540-48071-4_7 20 // 21 // [Qn] "Practical Threshold Signatures" by Shoup. 22 // https://www.iacr.org/archive/eurocrypt2000/1807/18070209-new.pdf 23 package qndleq 24 25 import ( 26 "crypto/rand" 27 "io" 28 "math/big" 29 30 "github.com/cloudflare/circl/internal/sha3" 31 ) 32 33 type Proof struct { 34 Z, C *big.Int 35 SecParam uint 36 } 37 38 // SampleQn returns an element of Qn (the subgroup of squares in (Z/nZ)*). 39 // SampleQn will return error for any error returned by crypto/rand.Int. 40 func SampleQn(random io.Reader, N *big.Int) (*big.Int, error) { 41 one := big.NewInt(1) 42 gcd := new(big.Int) 43 x := new(big.Int) 44 45 for { 46 y, err := rand.Int(random, N) 47 if err != nil { 48 return nil, err 49 } 50 // x is a square by construction. 51 x.Mul(y, y).Mod(x, N) 52 gcd.GCD(nil, nil, x, N) 53 // now check whether h is coprime to N. 54 if gcd.Cmp(one) == 0 { 55 return x, nil 56 } 57 } 58 } 59 60 // Prove creates a DLEQ Proof that attests that the pairs (g,gx) 61 // and (h,hx) have the same discrete logarithm equal to x. 62 // 63 // Given g, h in Qn (the subgroup of squares in (Z/nZ)*), it holds 64 // 65 // gx = g^x mod N 66 // hx = h^x mod N 67 // x = Log_g(g^x) = Log_h(h^x) 68 // 69 // Note: this function does not run in constant time because it uses 70 // big.Int arithmetic. 71 func Prove(random io.Reader, x, g, gx, h, hx, N *big.Int, secParam uint) (*Proof, error) { 72 rSizeBits := uint(N.BitLen()) + 2*secParam 73 rSizeBytes := (rSizeBits + 7) / 8 74 75 rBytes := make([]byte, rSizeBytes) 76 _, err := io.ReadFull(random, rBytes) 77 if err != nil { 78 return nil, err 79 } 80 r := new(big.Int).SetBytes(rBytes) 81 82 gP := new(big.Int).Exp(g, r, N) 83 hP := new(big.Int).Exp(h, r, N) 84 85 c := doChallenge(g, gx, h, hx, gP, hP, N, secParam) 86 z := new(big.Int) 87 z.Mul(c, x).Add(z, r) 88 89 return &Proof{Z: z, C: c, SecParam: secParam}, nil 90 } 91 92 // Verify checks whether x = Log_g(g^x) = Log_h(h^x). 93 func (p Proof) Verify(g, gx, h, hx, N *big.Int) bool { 94 gPNum := new(big.Int).Exp(g, p.Z, N) 95 gPDen := new(big.Int).Exp(gx, p.C, N) 96 ok := gPDen.ModInverse(gPDen, N) 97 if ok == nil { 98 return false 99 } 100 gP := gPNum.Mul(gPNum, gPDen) 101 gP.Mod(gP, N) 102 103 hPNum := new(big.Int).Exp(h, p.Z, N) 104 hPDen := new(big.Int).Exp(hx, p.C, N) 105 ok = hPDen.ModInverse(hPDen, N) 106 if ok == nil { 107 return false 108 } 109 hP := hPNum.Mul(hPNum, hPDen) 110 hP.Mod(hP, N) 111 112 c := doChallenge(g, gx, h, hx, gP, hP, N, p.SecParam) 113 114 return p.C.Cmp(c) == 0 115 } 116 117 func doChallenge(g, gx, h, hx, gP, hP, N *big.Int, secParam uint) *big.Int { 118 modulusLenBytes := (N.BitLen() + 7) / 8 119 nBytes := make([]byte, modulusLenBytes) 120 cByteLen := (secParam + 7) / 8 121 cBytes := make([]byte, cByteLen) 122 123 H := sha3.NewShake256() 124 _, _ = H.Write(g.FillBytes(nBytes)) 125 _, _ = H.Write(h.FillBytes(nBytes)) 126 _, _ = H.Write(gx.FillBytes(nBytes)) 127 _, _ = H.Write(hx.FillBytes(nBytes)) 128 _, _ = H.Write(gP.FillBytes(nBytes)) 129 _, _ = H.Write(hP.FillBytes(nBytes)) 130 _, _ = H.Read(cBytes) 131 132 return new(big.Int).SetBytes(cBytes) 133 }