Longfellow ZK 0290cb32
Loading...
Searching...
No Matches
verify_circuit.h
1// Copyright 2025 Google LLC.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifndef PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ECDSA_VERIFY_CIRCUIT_H_
16#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ECDSA_VERIFY_CIRCUIT_H_
17
18#include <stddef.h>
19
20#include "circuits/compiler/compiler.h"
21#include "circuits/logic/bit_plucker.h"
22
23namespace proofs {
24// Verify ECDSA signature using triple scalar mult form.
25//
26// The field used by sumcheck is the base field of the elliptic curve.
27// Compiled circuit: ecdsa verify
28// d: 7 wires: 21099 in: 1038 out:764 use:13911 ovh:7188 t:42963 cse:11351
29// notn:34724
30//
31template <class LogicCircuit, class Field, class EC>
32class VerifyCircuit {
33 using EltW = typename LogicCircuit::EltW;
34 using BitW = typename LogicCircuit::BitW;
35 using Elt = typename LogicCircuit::Elt;
36 using Nat = typename Field::N;
37 static constexpr size_t kBits = EC::kBits;
38 using Bitvec = typename LogicCircuit::v256;
39
40 public:
41 struct Witness {
42 EltW rx, ry;
43 EltW pre[8];
44 EltW rx_inv, s_inv, pk_inv;
45 EltW bi[kBits];
46 EltW int_x[kBits - 1];
47 EltW int_y[kBits - 1];
48 EltW int_z[kBits - 1];
49
50 void input(QuadCircuit<Field>& Q) {
51 rx = Q.input();
52 ry = Q.input();
53 rx_inv = Q.input();
54 s_inv = Q.input();
55 pk_inv = Q.input();
56 for (size_t i = 0; i < 8; ++i) {
57 pre[i] = Q.input();
58 }
59 for (size_t i = 0; i < kBits; ++i) {
60 bi[i] = Q.input();
61 if (i < kBits - 1) {
62 int_x[i] = Q.input();
63 int_y[i] = Q.input();
64 int_z[i] = Q.input();
65 }
66 }
67 }
68 };
69
70 VerifyCircuit(const LogicCircuit& lc, const EC& ec, const Nat& order)
71 : lc_(lc), ec_(ec), k2_(lc_.elt(2)), k3_(lc_.elt(3)) {
72 // Compute the bit representation of the order of the curve.
73 for (size_t i = 0; i < ec.kBits; ++i) {
74 bits_n_[i] = lc_.bit(order.bit(i));
75 }
76 }
77
78 // This verify takes the triple (pkx,pky,e) and checks that there exists
79 // (r=rx, ry, s) such that:
80 // identity = g*e + pk*r + (rx,ry)*-s
81 // It performs this check using a witness table that includes
82 // (g+pk, g+r, r+pk, g+r+pk), a correction element,
83 // bits of exponents (e, r, -s) s.t. each triple of bits is packed into {0,7},
84 // and intermediate ec points in (x,y,z) form. The bits are used to index the
85 // witness table in order to compute the right-hand side in a loop. The loop
86 // is sliced by providing the intermediate results in order reduce depth.
87 //
88 // An external constraint will need to ensure that e \neq 0 (e.g.,
89 // either the verifier checks this as part of the public input, or
90 // the hash that defines e is produced in the circuit). In our mdoc case,
91 // we use the later checks for both signatures.
92 //
93 // Other checks:
94 // r is interpreted as both in the base field and the scalar field.
95 // As a result, rx_inv is provided to ensure that r != 0.
96 // Similarly, s_inv is provided to ensure that s != 0.
97 //
98 // (rx,ry) is verified to be on the curve.
99 //
100 // (pkx, pky) \neq identity, because we set pk_z=1, we verify that
101 // pkx != 0, and we ensure that (pkx,pky) is on the curve.
102 //
103 void verify_signature3(EltW pk_x, EltW pk_y, EltW e, const Witness& w) const {
104 EltW zero = lc_.konst(lc_.zero());
105 EltW one = lc_.konst(lc_.one());
106 EltW gx = lc_.konst(ec_.gx_), gy = lc_.konst(ec_.gy_);
107
108 // indices for the pre[] table, don't change order
109 enum PreIndex {
110 GPK_X = 0,
111 GPK_Y,
112 GR_X,
113 GR_Y,
114 RPK_X,
115 RPK_Y,
116 GRPK_X,
117 GRPK_Y
118 };
119
120 // These variables hold the (e,r) exponents which are computed from the
121 // bits of advice (e,r,-s). They are compared with their expected values.
122 EltW est = zero, rst = zero, sst = zero;
123
124 // initialize at the 0 point, but these indices are reset on each loop
125 EltW ax = zero, ay = one, az = zero;
126
127 // =========
128 // Verify the values received in the table are correct.
129 // By verifying these values in parallel with using them, we can reduce
130 // the depth of the resulting circuit.
131 EltW cg_pkx, cg_pky, cg_pkz;
132 EltW cr_pkx, cr_pky, cr_pkz;
133 EltW cr_gx, cr_gy, cr_gz;
134 EltW cr_g_pkx, cr_g_pky, cr_g_pkz;
135 addE(cg_pkx, cg_pky, cg_pkz, gx, gy, one, pk_x, pk_y, one);
136 addE(cr_gx, cr_gy, cr_gz, w.rx, w.ry, one, gx, gy, one);
137 addE(cr_pkx, cr_pky, cr_pkz, w.rx, w.ry, one, pk_x, pk_y, one);
138 addE(cr_g_pkx, cr_g_pky, cr_g_pkz, gx, gy, one, w.pre[RPK_X], w.pre[RPK_Y],
139 one);
140 point_equality(cg_pkx, cg_pky, cg_pkz, w.pre[GPK_X], w.pre[GPK_Y]);
141 point_equality(cr_gx, cr_gy, cr_gz, w.pre[GR_X], w.pre[GR_Y]);
142 point_equality(cr_pkx, cr_pky, cr_pkz, w.pre[RPK_X], w.pre[RPK_Y]);
143 point_equality(cr_g_pkx, cr_g_pky, cr_g_pkz, w.pre[GRPK_X], w.pre[GRPK_Y]);
144
145 EltW arr_x[] = {zero, gx, pk_x, w.pre[GPK_X],
146 w.rx, w.pre[GR_X], w.pre[RPK_X], w.pre[GRPK_X]};
147 EltW arr_y[] = {one, gy, pk_y, w.pre[GPK_Y],
148 w.ry, w.pre[GR_Y], w.pre[RPK_Y], w.pre[GRPK_Y]};
149 EltW arr_z[] = {zero, one, one, one, one, one, one, one};
150 EltW arr_e[] = {zero, one, zero, one, zero, one, zero, one};
151 EltW arr_r[] = {zero, zero, one, one, zero, zero, one, one};
152 EltW arr_s[] = {zero, zero, zero, zero, one, one, one, one};
153 EltW arr_v[] = {one, one, one, one, one, one, one, one};
154
155 EltMuxer<LogicCircuit, 3> xx(lc_, arr_x);
156 EltMuxer<LogicCircuit, 3> yy(lc_, arr_y);
157 EltMuxer<LogicCircuit, 3> zz(lc_, arr_z);
158 EltMuxer<LogicCircuit, 3> ee(lc_, arr_e);
159 EltMuxer<LogicCircuit, 3> rr(lc_, arr_r);
160 EltMuxer<LogicCircuit, 3> ss(lc_, arr_s);
161 EltMuxer<LogicCircuit, 3> vv(lc_, arr_v);
162
163 Bitvec r_bits, s_bits;
164
165 // Traverses the bits of the scalar from high-order to low-order.
166 for (size_t i = 0; i < kBits; ++i) {
167 // Use the arr{X..V} arrays and the muxer to pick the correct point
168 // slice based on the bits of advice in the witness.
169 EltW tx = xx.mux(w.bi[i]);
170 EltW ty = yy.mux(w.bi[i]);
171 EltW tz = zz.mux(w.bi[i]);
172
173 // Update the exponent.
174 EltW e_bi = ee.mux(w.bi[i]);
175 EltW r_bi = rr.mux(w.bi[i]);
176 EltW s_bi = ss.mux(w.bi[i]);
177 auto k2 = lc_.konst(k2_);
178 est = lc_.add(&e_bi, lc_.mul(&k2, est));
179 rst = lc_.add(&r_bi, lc_.mul(&k2, rst));
180 sst = lc_.add(&s_bi, lc_.mul(&k2, sst));
181 r_bits[kBits - i - 1] = BitW(r_bi, ec_.f_);
182 s_bits[kBits - i - 1] = BitW(s_bi, ec_.f_);
183
184 // Verify that the advice bit is in [0,7].
185 EltW range = vv.mux(w.bi[i]);
186 lc_.assert_eq(&range, one);
187
188 // Perform the basic add-dbl step in repeated squaring using the
189 // muxed point {tx, ty, tz}.
190 if (i > 0) {
191 doubleE(ax, ay, az, ax, ay, az);
192 }
193 addE(ax, ay, az, ax, ay, az, tx, ty, tz);
194
195 if (i < kBits - 1) {
196 // Ensure that the resulting point is equal to the intermediate
197 // point provided as input. Performing an explicit equality check
198 // ensures that all intermediate witness points are on the curve.
199 // This follows by induction. The first (ax,ay,az) is on the curve.
200 // The addition formula ensures that the i-th (ax,ay,az) is on the
201 // curve; equality ensures that the i-th witness is on the curve.
202 lc_.assert_eq(&ax, w.int_x[i]);
203 lc_.assert_eq(&ay, w.int_y[i]);
204 lc_.assert_eq(&az, w.int_z[i]);
205
206 // Use the intermediate (x,y,z) point as the next input.
207 ax = w.int_x[i];
208 ay = w.int_y[i];
209 az = w.int_z[i];
210 }
211 }
212
213 // Check that the aX,aZ points are 0.
214 lc_.assert0(ax);
215 lc_.assert0(az);
216
217 // Check that the bits used for {e,rx} correspond to the input {e, rx}.
218 lc_.assert_eq(&est, e);
219 lc_.assert_eq(&rst, w.rx);
220
221 // Check that (pk,py), (rx,ry) satisfy the curve equation.
222 is_on_curve(pk_x, pk_y);
223 is_on_curve(w.rx, w.ry);
224
225 // Verify that exponents (r,s) are not zero.
226 // A witness is provided to ensure that both values have inverses in F.
227 // A bitwise comparison is done to ensure both are < |order| of EC.
228 assert_nonzero(w.rx, w.rx_inv);
229 assert_nonzero(sst, w.s_inv);
230 assert_nonzero(pk_x, w.pk_inv);
231 auto r_range = lc_.vlt(&r_bits, bits_n_);
232 auto s_range = lc_.vlt(&s_bits, bits_n_);
233 lc_.assert1(r_range);
234 lc_.assert1(s_range);
235 }
236
237 private:
238 void assert_nonzero(EltW x, EltW witness) const {
239 auto maybe_one = lc_.mul(&x, witness);
240 auto one = lc_.konst(lc_.one());
241 lc_.assert_eq(&maybe_one, one);
242 }
243
244 void point_equality(EltW x, EltW y, EltW z, EltW p_x, EltW p_y) const {
245 lc_.assert_eq(&x, lc_.mul(&z, p_x));
246 lc_.assert_eq(&y, lc_.mul(&z, p_y));
247 }
248
249 void is_on_curve(EltW x, EltW y) const {
250 // Check that y^2 = x^3 + ax + b
251 auto yy = lc_.mul(&y, y);
252 auto xx = lc_.mul(&x, x);
253 auto xxx = lc_.mul(&x, xx);
254 auto ax = lc_.mul(ec_.a_, x);
255 auto b = lc_.konst(ec_.b_);
256 auto axb = lc_.add(&ax, b);
257 auto rhs = lc_.add(&axb, xxx);
258 lc_.assert_eq(&yy, rhs);
259 }
260
261 void addE(EltW& X3, EltW& Y3, EltW& Z3, EltW X1, EltW Y1, EltW Z1, EltW X2,
262 EltW Y2, EltW Z2) const {
263 // The general case.
264 // Algorithm 1: Complete, projective point addition for arbitrary prime
265 // order short Weierstrass curves E/Fq : y^2 = x^3 + ax + b
266 // The compiler seems to optimize the cases when Z1,Z2=1.
267 EltW t0 = lc_.mul(&X1, X2);
268 EltW t1 = lc_.mul(&Y1, Y2);
269 EltW t2 = lc_.mul(&Z1, Z2);
270 EltW t3 = lc_.add(&X1, Y1);
271 EltW t4 = lc_.add(&X2, Y2);
272 t3 = lc_.mul(&t3, t4);
273 t4 = lc_.add(&t0, t1);
274 t3 = lc_.sub(&t3, t4);
275 t4 = lc_.add(&X1, Z1);
276 EltW t5 = lc_.add(&X2, Z2);
277 t4 = lc_.mul(&t4, t5);
278 t5 = lc_.add(&t0, t2);
279 t4 = lc_.sub(&t4, t5);
280 t5 = lc_.add(&Y1, Z1);
281 EltW X3t = lc_.add(&Y2, Z2);
282 t5 = lc_.mul(&t5, X3t);
283 X3t = lc_.add(&t1, t2);
284 t5 = lc_.sub(&t5, X3t);
285 auto a = lc_.konst(ec_.a_);
286 EltW Z3t = lc_.mul(&a, t4);
287 auto k3b = lc_.konst(ec_.k3b);
288 X3t = lc_.mul(&k3b, t2);
289 Z3t = lc_.add(&X3t, Z3t);
290 X3t = lc_.sub(&t1, Z3t);
291 Z3t = lc_.add(&t1, Z3t);
292 EltW Y3t = lc_.mul(&X3t, Z3t);
293 t1 = lc_.add(&t0, t0);
294 t1 = lc_.add(&t1, t0);
295 t2 = lc_.mul(&a, t2);
296 t4 = lc_.mul(&k3b, t4);
297 t1 = lc_.add(&t1, t2);
298 t2 = lc_.sub(&t0, t2);
299 t2 = lc_.mul(&a, t2);
300 t4 = lc_.add(&t4, t2);
301 t0 = lc_.mul(&t1, t4);
302 Y3t = lc_.add(&Y3t, t0);
303 t0 = lc_.mul(&t5, t4);
304 X3t = lc_.mul(&t3, X3t);
305 X3t = lc_.sub(&X3t, t0);
306 t0 = lc_.mul(&t3, t1);
307 Z3t = lc_.mul(&t5, Z3t);
308 Z3t = lc_.add(&Z3t, t0);
309
310 X3 = X3t;
311 Y3 = Y3t;
312 Z3 = Z3t;
313 }
314
315 void doubleE(EltW& X3, EltW& Y3, EltW& Z3, EltW X, EltW Y, EltW Z) const {
316 // The general case.
317 // Algorithm 3: Exception-free point doubling for arbitrary prime order
318 // short Weierstrass curves E/Fq : y^2 = x^3 + ax + b.
319 // The compiler will presumably optimize away 0 mults when a=0 and 1
320 // mults when Z = 1.
321 EltW t0 = lc_.mul(&X, X);
322 EltW t1 = lc_.mul(&Y, Y);
323 EltW t2 = lc_.mul(&Z, Z);
324 EltW t3 = lc_.mul(&X, Y);
325 t3 = lc_.add(&t3, t3);
326 EltW Z3t = lc_.mul(&X, Z);
327 Z3t = lc_.add(&Z3t, Z3t);
328 auto a = lc_.konst(ec_.a_);
329 auto k3b = lc_.konst(ec_.k3b);
330 EltW X3t = lc_.mul(&a, Z3t);
331 EltW Y3t = lc_.mul(&k3b, t2);
332 Y3t = lc_.add(&X3t, Y3t);
333 X3t = lc_.sub(&t1, Y3t);
334 Y3t = lc_.add(&t1, Y3t);
335 Y3t = lc_.mul(&X3t, Y3t);
336 X3t = lc_.mul(&t3, X3t);
337 Z3t = lc_.mul(&k3b, Z3t);
338 t2 = lc_.mul(&a, t2);
339 t3 = lc_.sub(&t0, t2);
340 t3 = lc_.mul(&a, t3);
341 t3 = lc_.add(&t3, Z3t);
342 Z3t = lc_.add(&t0, t0);
343 t0 = lc_.add(&Z3t, t0);
344 t0 = lc_.add(&t0, t2);
345 t0 = lc_.mul(&t0, t3);
346 Y3t = lc_.add(&Y3t, t0);
347 t2 = lc_.mul(&Y, Z);
348 t2 = lc_.add(&t2, t2);
349 t0 = lc_.mul(&t2, t3);
350 X3t = lc_.sub(&X3t, t0);
351 Z3t = lc_.mul(&t2, t1);
352 Z3t = lc_.add(&Z3t, Z3t);
353 Z3t = lc_.add(&Z3t, Z3t);
354
355 X3 = X3t;
356 Y3 = Y3t;
357 Z3 = Z3t;
358 }
359
360 const LogicCircuit& lc_;
361 const EC& ec_;
362
363 Elt k2_, k3_;
364 Bitvec bits_n_;
365};
366} // namespace proofs
367
368#endif // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_ECDSA_VERIFY_CIRCUIT_H_
Definition compiler.h:50
Definition verify_circuit.h:41