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