/
deniable.go
297 lines (248 loc) · 7.66 KB
/
deniable.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
package proof
import (
"bytes"
"errors"
"fmt"
"go.dedis.ch/kyber/v3"
)
// DeniableProver is a Protocol implementing an interactive Sigma-protocol
// to prove a particular statement to the other participants.
// Optionally the Protocol participant can also verify
// the Sigma-protocol proofs of any or all of the other participants.
// Different participants may produce different proofs of varying sizes,
// and may even consist of different numbers of steps.
func DeniableProver(suite Suite, self int, prover Prover,
verifiers []Verifier) Protocol {
return Protocol(func(ctx Context) []error {
dp := deniableProver{}
return dp.run(suite, self, prover, verifiers, ctx)
})
}
type deniableProver struct {
suite Suite // Agreed-on ciphersuite for protocol
self int // Our own node number
sc Context // Clique protocol context
// verifiers for other nodes' proofs
dv []*deniableVerifier
// per-step state
key []byte // Secret pre-challenge we committed to
msg *bytes.Buffer // Buffer in which to build prover msg
msgs [][]byte // All messages from last proof step
pubrand kyber.XOF
prirand kyber.XOF
// Error/success indicators for all participants
err []error
}
func (dp *deniableProver) run(suite Suite, self int, prv Prover,
vrf []Verifier, sc Context) []error {
dp.suite = suite
dp.self = self
dp.sc = sc
dp.prirand = sc.Random()
nnodes := len(vrf)
if self < 0 || self >= nnodes {
return []error{errors.New("out-of-range self node")}
}
// Initialize error slice entries to a default error indicator,
// so that forgetting to run a verifier won't look like "success"
verr := errors.New("prover or verifier not run")
dp.err = make([]error, nnodes)
for i := range dp.err {
if i != self {
dp.err[i] = verr
}
}
// Launch goroutines to run whichever verifiers the caller requested
dp.dv = make([]*deniableVerifier, nnodes)
for i := range vrf {
if vrf[i] != nil {
dv := deniableVerifier{}
dv.start(suite, vrf[i])
dp.dv[i] = &dv
}
}
// Run the prover, which will also drive the verifiers.
dp.initStep()
if err := (func(ProverContext) error)(prv)(dp); err != nil {
dp.err[self] = err
}
// Send the last prover message.
// Make sure the verifiers get to run to completion as well
for {
stragglers, err := dp.proofStep()
if err != nil {
dp.err[self] = err
break
}
if !stragglers {
break
}
if err = dp.challengeStep(); err != nil {
dp.err[self] = err
break
}
}
return dp.err
}
// keySize is arbitrary, make it long enough to seed the XOF
const keySize = 128
// Start the message buffer off in each step with a randomness commitment
func (dp *deniableProver) initStep() {
key := make([]byte, keySize) // secret random key
_, _ = dp.prirand.Read(key)
dp.key = key
msg := make([]byte, keySize) // send commitment to it
xof := dp.suite.XOF(key)
xof.Read(msg)
dp.msg = bytes.NewBuffer(msg)
// The Sigma-Prover will now append its proof content to dp.msg...
}
func (dp *deniableProver) proofStep() (bool, error) {
// Send the randomness commit and accumulated message to the leader,
// and get all participants' commits, via our star-protocol context.
msgs, err := dp.sc.Step(dp.msg.Bytes())
if err != nil {
return false, err
}
if !bytes.Equal(msgs[dp.self], dp.msg.Bytes()) {
return false, errors.New("own messages were corrupted")
}
dp.msgs = msgs
// Distribute this step's prover messages
// to the relevant verifiers as well,
// waking them up in the process so they can proceed.
for i := range dp.dv {
dv := dp.dv[i]
if dv != nil && i < len(msgs) {
dv.inbox <- msgs[i][keySize:] // send to verifier
}
}
// Collect the verifiers' responses,
// collecting error indicators from verifiers that are done.
stragglers := false
for i := range dp.dv { // collect verifier responses
dv := dp.dv[i]
if dv != nil {
done := <-dv.done // get verifier response
if done { // verifier is done
dp.err[i] = dv.err
dp.dv[i] = nil
} else { // verifier needs next challenge
stragglers = true
}
}
}
return stragglers, nil
}
func (dp *deniableProver) challengeStep() error {
// Send our challenge randomness to the leader, and collect all.
keys, err := dp.sc.Step(dp.key)
if err != nil {
return err
}
// XOR together all the participants' randomness contributions,
// check them against the respective commits,
// and ensure ours is included to ensure deniability
// (even if all others turn out to be maliciously generated).
mix := make([]byte, keySize)
for i := range keys {
com := dp.msgs[i][:keySize] // node i's randomness commitment
key := keys[i] // node i's committed random key
if len(com) < keySize || len(key) < keySize {
continue // ignore participants who dropped out
}
chk := make([]byte, keySize)
dp.suite.XOF(key).Read(chk)
if !bytes.Equal(com, chk) {
return errors.New("wrong key for commit")
}
for j := 0; j < keySize; j++ { // mix in this key
mix[j] ^= key[j]
}
}
if len(keys) <= dp.self || !bytes.Equal(keys[dp.self], dp.key) {
return errors.New("our own message was corrupted")
}
// Use the mix to produce the public randomness needed by the prover
dp.pubrand = dp.suite.XOF(mix)
// Distribute the master challenge to any verifiers waiting for it
for i := range dp.dv {
dv := dp.dv[i]
if dv != nil {
dv.inbox <- mix // so send it
}
}
// Setup for the next proof step
dp.initStep()
return nil
}
func (dp *deniableProver) Put(message interface{}) error {
// Add onto accumulated prover message
return dp.suite.Write(dp.msg, message)
}
// Prover will call this after Put()ing all commits for a given step,
// to get the master challenge to be used in its challenge/responses.
func (dp *deniableProver) PubRand(data ...interface{}) error {
if _, err := dp.proofStep(); err != nil { // finish proof step
return err
}
if err := dp.challengeStep(); err != nil { // run challenge step
return err
}
return dp.suite.Read(dp.pubrand, data...)
}
// Get private randomness
func (dp *deniableProver) PriRand(data ...interface{}) error {
if err := dp.suite.Read(dp.prirand, data...); err != nil {
return fmt.Errorf("error reading random stream: %v", err.Error())
}
return nil
}
// Interactive Sigma-protocol verifier context.
// Acts as a slave to a deniableProver instance.
type deniableVerifier struct {
suite Suite
inbox chan []byte // Channel for receiving proofs and challenges
prbuf *bytes.Buffer // Buffer with which to read proof messages
done chan bool // Channel for sending done status indicators
err error // When done indicates verify error if non-nil
pubrand kyber.XOF
}
func (dv *deniableVerifier) start(suite Suite, vrf Verifier) {
dv.suite = suite
dv.inbox = make(chan []byte)
dv.done = make(chan bool)
// Launch a concurrent goroutine to run this verifier
go func() {
// Await the prover's first message
dv.getProof()
// Run the verifier, providing dv as its context
dv.err = (func(VerifierContext) error)(vrf)(dv)
// Signal verifier termination
dv.done <- true
}()
}
func (dv *deniableVerifier) getProof() {
// Get the next message from the prover
prbuf := <-dv.inbox
dv.prbuf = bytes.NewBuffer(prbuf)
}
// Read structured data from the proof
func (dv *deniableVerifier) Get(message interface{}) error {
return dv.suite.Read(dv.prbuf, message)
}
// Get the next public random challenge.
func (dv *deniableVerifier) PubRand(data ...interface{}) error {
// Signal that we need the next challenge
dv.done <- false
// Wait for it
chal := <-dv.inbox
// Produce the appropriate publicly random stream
dv.pubrand = dv.suite.XOF(chal)
if err := dv.suite.Read(dv.pubrand, data...); err != nil {
return err
}
// Get the next proof message
dv.getProof()
return nil
}