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  }