File: flp.go

package info (click to toggle)
golang-github-cloudflare-circl 1.6.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 18,060 kB
  • sloc: asm: 20,492; ansic: 1,292; makefile: 68
file content (149 lines) | stat: -rw-r--r-- 4,598 bytes parent folder | download | duplicates (2)
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
// Package flp provides a Fully-Linear Proof (FLP) system.
package flp

import (
	"errors"

	"github.com/cloudflare/circl/vdaf/prio3/arith"
	"github.com/cloudflare/circl/vdaf/prio3/internal/cursor"
)

// FLP is an instance of a FLP by Boneh et al. Crypto, 2019 paper
// "Zero-Knowledge Proofs on Secret-Shared Data via Fully Linear PCPs",
// https://ia.cr/2019/188
// plus some changes described in VDAF specification.
//
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vdaf-13#section-7.1
type FLP[
	G Gadget[P, V, E, F],
	P arith.Poly[P, E], V arith.Vec[V, E], E arith.Elt, F arith.Fp[E],
] struct {
	// Eval evaluates the arithmetic circuit on a measurement and joint randomness.
	Eval func(out V, g Gadget[P, V, E, F], numCalls uint, meas, jointRand V, numShares uint8)
	Valid[G, P, V, E, F]
}

// Prove returns a proof attesting validity to the given measurement.
// Prove randomness must be provided.
// Some statements may require joint randomness too.
//
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vdaf-13#section-7.3.3
func (f *FLP[G, P, V, E, F]) Prove(meas, proveRand, jointRand V) V {
	proof := arith.NewVec[V](f.ProofLength())
	proofCur := cursor.New(proof)

	g := f.Valid.wrapProve(proveRand)
	out := arith.NewVec[V](f.EvalOutputLength())
	f.Eval(out, g, f.Valid.NumGadgetCalls, meas, jointRand, 1)

	// invN is the inverse of N = g.p.
	// Also, since g.p is always a power of two, we call a faster inversion
	// method that receives the log2 of g.p.
	invN := F(new(E))
	invN.InvTwoN(g.log2p)

	arity := g.Arity()
	wirePoly := make([]P, arity)
	wirePolyN := arith.NewVec[V](arity * g.p)
	wirePolyNCur := cursor.New(wirePolyN)
	wiresCur := cursor.New(g.wires)
	for i := range wirePoly {
		wire := wiresCur.Next(g.p)
		wirePolyI := wirePolyNCur.Next(g.p)
		wirePolyI.InvNTT(wire, g.p)
		wirePolyI.ScalarMul(invN)
		wirePoly[i] = P(wirePolyI)
		proofCur.Next(1)[0] = wire[0]
	}

	gadgetPoly := P(proofCur.Next(f.gadgetPolyLen()))
	g.EvalPoly(gadgetPoly, wirePoly)
	return proof
}

// Query is the linear Query algorithm run by each verifier on a share of the
// measurement and proof.
// Query randomness must be provided.
// Some statements may require joint randomness too.
//
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vdaf-13#section-7.3.4
func (f *FLP[G, P, V, E, F]) Query(
	measShare, proofShare, queryRand, jointRand V, numShares uint8,
) (verifierMsg V, err error) {
	verifierMsg = arith.NewVec[V](f.VerifierLength())
	verifierCur := cursor.New(verifierMsg)
	queryRandCur := cursor.New(queryRand)

	g := f.Valid.wrapQuery(proofShare)
	outLen := f.EvalOutputLength()
	out := arith.NewVec[V](outLen)
	f.Eval(out, g, f.Valid.NumGadgetCalls, measShare, jointRand, numShares)

	v := verifierCur.Next(1)
	if outLen > 1 {
		v[0] = out.DotProduct(queryRandCur.Next(outLen))
	} else {
		v[0] = out[0]
	}

	// Check that t^p != 1. Since p=2^log2p, this requires log2P squares.
	t := &queryRandCur.Next(1)[0]
	tp := F(new(E))
	*tp = *t
	for range g.log2p {
		tp.Sqr(tp)
	}

	if tp.IsOne() {
		return nil, ErrInvalidEval
	}

	// invN is the inverse of N = g.p.
	// Also, since g.p is always a power of two, we call a faster inversion
	// method that receives the log2 of g.p.
	invN := F(new(E))
	invN.InvTwoN(g.log2p)

	wireChecks := verifierCur.Next(g.Arity())
	wirePoly := arith.NewPoly[P](g.p - 1)
	wirei := cursor.New(g.wires)
	for i := range wireChecks {
		V(wirePoly).InvNTT(wirei.Next(g.p), g.p)
		wireChecks[i] = wirePoly.Evaluate(t)
		// Extracts the constant factor (1/N) to be multiplied after
		// polynomial interpolation and evaluation.
		F(&wireChecks[i]).MulAssign(invN)
	}

	gadgetCheck := &verifierCur.Next(1)[0]
	*gadgetCheck = g.poly.Evaluate(t)
	return verifierMsg, nil
}

// Decide returns true if the measurement from which it was generated is valid.
//
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vdaf-13#section-7.3.5
func (f *FLP[G, P, V, E, F]) Decide(verifierMsg V) bool {
	if len(verifierMsg) != int(f.VerifierLength()) {
		return false
	}

	verifierMsgCur := cursor.New(verifierMsg)
	v := F(&verifierMsgCur.Next(1)[0])
	if !v.IsZero() {
		return false
	}

	wireChecks := verifierMsgCur.Next(f.Valid.Gadget.Arity())
	gadgetCheck := &verifierMsgCur.Next(1)[0]
	check := F(new(E))
	f.Valid.Gadget.Eval(check, wireChecks)
	return check.IsEqual(gadgetCheck)
}

var (
	ErrOutputLen        = errors.New("wrong output length")
	ErrMeasurementLen   = errors.New("invalid measurement length")
	ErrMeasurementValue = errors.New("invalid measurement value")
	ErrInvalidEval      = errors.New("invalid evaluation point")
)