Longfellow ZK 0290cb32

Loading...
Searching...
No Matches
elliptic_curve.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_EC_ELLIPTIC_CURVE_H_
16#define PRIVACY_PROOFS_ZK_LIB_EC_ELLIPTIC_CURVE_H_
17
18#include <cstddef>
19#include <cstdint>
20
21#include "algebra/nat.h"
22#include "util/panic.h"
23
24 namespace proofs {
25// Elliptic curve class that supports basic operations such as addition,
26// doubling. The algorithms are described in
27// https://eprint.iacr.org/2015/1060.pdf.
28// The const Field parameter is meant as a type check to keep different
29// elliptic curves from interacting. The convention is to use the last 5
30// digits of the coordinate field prime in base-10 to name a curve.
31// The kN template parameter describes the number of bits in the curve
32// order, e.g., to handle curves like P-521, K-283, etc.
33 template <class Field_, size_t W, size_t kN>
34 class EllipticCurve {
35 public:
36 using Field = Field_;
37 using Elt = typename Field::Elt;
38 using N = Nat<W>;
39
40 static constexpr const size_t kBits = kN; /* # bits in size of the group */
41
42 const Field& f_;
43 Elt a_;
44 Elt b_;
45 Elt gx_, gy_, gz_; // generator of the group
46 const Elt k2, k3, k8, k3b, k9b, k24b;
47
48 struct ECPoint {
49 Elt x;
50 Elt y;
51 Elt z;
52
53 ECPoint() = default;
54 ECPoint(const Elt& x, const Elt& y, const Elt& z) : x(x), y(y), z(z) {}
55 };
56
57 EllipticCurve(const Elt& a, const Elt& b, const Elt& gX, const Elt& gY,
58 const Field_& F)
59 : f_(F),
60 a_(a),
61 b_(b),
62 gx_(gX),
63 gy_(gY),
64 gz_(F.one()),
65 k2(F.of_scalar(2)),
66 k3(F.of_scalar(3)),
67 k8(F.of_scalar(8)),
68 k3b(F.mulf(k3, b_)),
69 k9b(F.mulf(F.of_scalar(9), b_)),
70 k24b(F.mulf(F.of_scalar(24), b_)) {
71 is_minus_3_a_ = (a_ == F.negf(k3));
72 is_zero_a_ = (a_ == F.zero());
73 }
74
75 // This equality method makes no assumptions about whether the inputs
76 // are valid points on the curve. Just verifying cross-mult is not
77 // enough if one of the points is invalid. This method is not constant
78 // time and can return early if any point is infinity.
79 bool equal(const ECPoint& p, const ECPoint& q) const {
80 // handle inf point, then point equality, and finally projective eq
81 if (q.x == f_.zero() && q.z == f_.zero() && q.y != f_.zero() &&
82 p.x == f_.zero() && p.z == f_.zero() && p.y != f_.zero()) {
83 return true;
84 }
85 if (q.x == p.x && q.z == p.z && q.y == p.y) {
86 return true;
87 }
88
89 return (f_.mulf(p.x, q.z) == f_.mulf(q.x, p.z) &&
90 f_.mulf(p.y, q.z) == f_.mulf(q.y, p.z));
91 }
92
93 // This method assumes a point is either zero or has z=1 coordinate,
94 // so it does not implement the full mathematical notion of Jacobian-form
95 // ec point.
96 bool is_on_curve(const ECPoint& p) const {
97 if (equal(p, zero())) {
98 return true;
99 }
100 // Do not support Jacobian coordinate with z != 1"
101 if (p.z != f_.one()) {
102 return false;
103 }
104 return is_on_curve(p.x, p.y);
105 }
106
107 // This caller of the constructor must first verify that (x,y) is on the
108 // curve using the isOnCurve() method.
109 ECPoint point(const Elt& x, const Elt& y) const {
110 ECPoint p(x, y, f_.one());
111 check(is_on_curve(p), "Invalid curve point");
112 return p;
113 }
114
115 void normalize(ECPoint& p) const {
116 if (p.z == f_.zero()) return;
117 f_.invert(p.z);
118 f_.mul(p.x, p.z);
119 f_.mul(p.y, p.z);
120 p.z = f_.one();
121 }
122
123 void addE(ECPoint& p3, const ECPoint& p2) const {
124 addE(p3.x, p3.y, p3.z, p3.x, p3.y, p3.z, p2.x, p2.y, p2.z);
125 }
126
127 void doubleE(ECPoint& p3) const {
128 doubleE(p3.x, p3.y, p3.z, p3.x, p3.y, p3.z);
129 }
130
131 // Functional interface.
132 ECPoint addEf(ECPoint p1, const ECPoint& p2) const {
133 addE(p1, p2);
134 return p1;
135 }
136
137 ECPoint doubleEf(ECPoint p1) const {
138 doubleE(p1);
139 return p1;
140 }
141
142 // Computes the elliptic curve point p * scalar.
143 // This method is not constant time, but that is not necessary in the current
144 // zk implementation.
145 ECPoint scalar_multf(const ECPoint& p, const N& scalar) const {
146 ECPoint x = p;
147 ECPoint p3 = zero();
148 for (size_t d = 0; d < N::kLimbs; ++d) {
149 auto nd = scalar.limb_[d];
150 for (size_t i = 0; i < N::kBitsPerLimb; ++i) {
151 if (nd & 1) {
152 addE(p3, x);
153 }
154 doubleE(x);
155 nd >>= 1;
156 }
157 }
158 return p3;
159 }
160
161 // Computes the multi-scalar elliptic curve point multiplication.
162 // Input: p1, p2, ..., pn, and scalars s1, s2, ..., sn
163 // Output: p1 * s1 + p2 * s2 + ... + pn * sn
164 // This method is not a constant time operation.
165 ECPoint scalar_multf(size_t n, ECPoint p[/*n*/], N scalar[/*n*/]) const {
166 if (n == 0) {
167 return zero();
168 } else if (n == 1) {
169 return scalar_multf(p[0], scalar[0]);
170 } else {
171 return bos_coster(n, p, scalar);
172 }
173 }
174
175 ECPoint zero() const { return ECPoint(f_.zero(), f_.one(), f_.zero()); }
176 ECPoint generator() const { return ECPoint(gx_, gy_, gz_); }
177
178 // Check whether Y^2 = X^3 + aX + b.
179 bool is_on_curve(const Elt& X, const Elt& Y) const {
180 Elt left = f_.mulf(Y, Y);
181 Elt X3 = f_.mulf(X, f_.mulf(X, X));
182 Elt right = f_.addf(f_.addf(X3, f_.mulf(a_, X)), b_);
183 return left == right;
184 }
185
186 void addE(Elt& X3o, Elt& Y3o, Elt& Z3o, const Elt& X1, const Elt& Y1,
187 const Elt& Z1, const Elt& X2, const Elt& Y2, const Elt& Z2) const {
188 // Optimized special cases.
189 if (is_zero_a_) return addEZeroA(X3o, Y3o, Z3o, X1, Y1, Z1, X2, Y2, Z2);
190 if (is_minus_3_a_)
191 return addEMinus3A(X3o, Y3o, Z3o, X1, Y1, Z1, X2, Y2, Z2);
192
193 /*
194 Source: 1998 Cohen–Miyaji–Ono "Efficient elliptic curve exponentiation using
195 mixed coordinates", formula (3), plus common-subexpression elimination.
196 These equations are taken from the Hyperelliptic curve formula database.
197 This could have special short-cuts for addition with inf and self-addition,
198 which speeds up all multi-exponentiation computations that involve a lot of
199 small exponents.
200 */
201 if (X1 == f_.zero() && Z1 == f_.zero()) {
202 X3o = X2;
203 Y3o = Y2;
204 Z3o = Z2;
205 return;
206 }
207
208 if (X2 == f_.zero() && Z2 == f_.zero()) {
209 X3o = X1;
210 Y3o = Y1;
211 Z3o = Z1;
212 return;
213 }
214
215 Elt Y1Z2 = f_.mulf(Y1, Z2);
216 Elt X1Z2 = f_.mulf(X1, Z2);
217 Elt u = f_.subf(f_.mulf(Y2, Z1), Y1Z2);
218 Elt v = f_.subf(f_.mulf(X2, Z1), X1Z2);
219 if (u == f_.zero()) {
220 doubleE(X3o, Y3o, Z3o, X1, Y1, Z1);
221 return;
222 // Self addition, invoke Double method.
223 }
224 /* This check occurs after the u check.
225 If u!=0, but v=0, then the points are inverses.
226 */
227 if (v == f_.zero()) {
228 X3o = f_.zero();
229 Y3o = f_.one();
230 Z3o = f_.zero();
231 return;
232 }
233
234 Elt Z1Z2 = f_.mulf(Z1, Z2);
235 Elt uu = f_.mulf(u, u);
236 Elt vv = f_.mulf(v, v);
237 Elt vvv = f_.mulf(v, vv);
238 Elt R = f_.mulf(vv, X1Z2);
239 Elt A = f_.subf(f_.subf(f_.mulf(uu, Z1Z2), vvv), f_.mulf(k2, R));
240 Elt X3 = f_.mulf(v, A);
241 Elt Y3 = f_.subf(f_.mulf(u, f_.subf(R, A)), f_.mulf(vvv, Y1Z2));
242 Elt Z3 = f_.mulf(vvv, Z1Z2);
243
244 X3o = X3;
245 Y3o = Y3;
246 Z3o = Z3;
247 }
248
249 void doubleE(Elt& X3o, Elt& Y3o, Elt& Z3o, const Elt& X, const Elt& Y,
250 const Elt& Z) const {
251 // Optimized special cases.
252 if (is_zero_a_) return doubleEZeroA(X3o, Y3o, Z3o, X, Y, Z);
253 if (is_minus_3_a_) return doubleEMinus3A(X3o, Y3o, Z3o, X, Y, Z);
254
255 /*
256 // 1998 Cohen–Miyaji–Ono "Efficient elliptic curve exponentiation using
257 mixed coordinates", formula (4), This version of the double formula trades
258 general mults for mults by 2,4,8 which can be implemented with additions.
259 This results in savings of 200ns on double.
260 */
261 if (X == f_.zero() && Z == f_.zero()) {
262 X3o = X;
263 Y3o = f_.one();
264 Z3o = Z;
265 return;
266 }
267
268 Elt Z2 = f_.mulf(Z, Z);
269 Elt X2 = f_.mulf(X, X);
270 Elt X2_3 = f_.addf(f_.addf(X2, X2), X2);
271 Elt s = f_.mulf(Y, Z);
272 Elt ss = f_.mulf(s, s);
273 Elt sss = f_.mulf(s, ss);
274 Elt sss_2 = f_.addf(sss, sss);
275 Elt w = f_.addf(f_.mulf(a_, Z2), X2_3);
276 Elt R = f_.mulf(Y, s);
277 Elt sss_4 = f_.addf(sss_2, sss_2);
278 Elt B = f_.mulf(X, R);
279 Elt sss_8 = f_.addf(sss_4, sss_4);
280 Elt B_2 = f_.addf(B, B);
281 Elt R2 = f_.mulf(R, R);
282 Elt B_4 = f_.addf(B_2, B_2);
283 Elt B_8 = f_.addf(B_4, B_4);
284 Elt w2 = f_.mulf(w, w);
285 Elt h = f_.subf(w2, B_8);
286 Elt s_2 = f_.addf(s, s);
287 Elt X3 = f_.mulf(h, s_2);
288 Elt R2_2 = f_.addf(R2, R2);
289 Elt R2_4 = f_.addf(R2_2, R2_2);
290 Elt R2_8 = f_.addf(R2_4, R2_4);
291 Elt Y3 = f_.subf(f_.mulf(w, f_.subf(B_4, h)), R2_8);
292 Elt Z3 = sss_8;
293
294 X3o = X3;
295 Y3o = Y3;
296 Z3o = Z3;
297 }
298
299 private:
300 /* From Algorithm 7: Complete, projective point addition for prime order
301 j-invariant 0 short Weierstrass curves E/Fq : y^2 = x^3 + b.
302
303 X3 = (X1 Y2 + X2 Y1)(Y1 Y2 - 3b Z1 Z2) - 3b(Y1 Z2 + Y2 Z1)(X1 Z2 + X2 Z1)
304 Y3 = (Y1 Y2 + 3b Z1 Z2)(Y1 Y2 - 3b Z1 Z2) + 9b X1 X2 (X1 Z2 + X2 Z1)
305 Z3 = (Y1 Z2 + Y2 Z1)(Y1 Y2 + 3b Z1 Z2) + 3 X1 X2(X1 Y2 + X2 Y1)
306 */
307 void addEZeroA(Elt& X3o, Elt& Y3o, Elt& Z3o, const Elt& X1, const Elt& Y1,
308 const Elt& Z1, const Elt& X2, const Elt& Y2,
309 const Elt& Z2) const {
310 Elt t0 = f_.mulf(X2, Y1);
311 Elt t1 = f_.mulf(X1, Y2);
312 Elt t2 = f_.addf(t1, t0);
313 Elt t3 = f_.mulf(Y1, Y2);
314 Elt t4 = f_.mulf(Z1, Z2);
315 Elt t5 = f_.mulf(Y1, Z2);
316 Elt t6 = f_.mulf(Y2, Z1);
317 Elt t7 = f_.addf(t5, t6);
318 Elt t8 = f_.mulf(X1, Z2);
319 Elt t9 = f_.mulf(X2, Z1);
320 Elt t10 = f_.addf(t8, t9);
321 Elt t11 = f_.mulf(X1, X2);
322 Elt t12 = f_.mulf(k3b, t4);
323 Elt t13 = f_.addf(t3, t12);
324 Elt t14 = f_.subf(t3, t12);
325
326 X3o = f_.subf(f_.mulf(t2, t14), f_.mulf(k3b, f_.mulf(t7, t10)));
327 Y3o = f_.addf(f_.mulf(t13, t14), f_.mulf(k9b, f_.mulf(t11, t10)));
328 Z3o = f_.addf(f_.mulf(t7, t13), f_.mulf(k3, f_.mulf(t11, t2)));
329 }
330
331 /*Algorithm 4: Complete, projective point addition for prime order short
332 * Weierstrass curves E/Fq : y^2 = x^33 + ax + b with a = −3.
333 */
334 void addEMinus3A(Elt& X3o, Elt& Y3o, Elt& Z3o, const Elt& X1, const Elt& Y1,
335 const Elt& Z1, const Elt& X2, const Elt& Y2,
336 const Elt& Z2) const {
337 Elt t0 = f_.mulf(X1, X2);
338 Elt t1 = f_.mulf(Y1, Y2);
339 Elt t2 = f_.mulf(Z1, Z2);
340 Elt t3 = f_.addf(X1, Y1);
341 Elt t4 = f_.addf(X2, Y2);
342 t3 = f_.mulf(t3, t4);
343 t4 = f_.addf(t0, t1);
344 t3 = f_.subf(t3, t4);
345 t4 = f_.addf(Y1, Z1);
346 Elt X3 = f_.addf(Y2, Z2);
347 t4 = f_.mulf(t4, X3);
348 X3 = f_.addf(t1, t2);
349 t4 = f_.subf(t4, X3);
350 X3 = f_.addf(X1, Z1);
351 Elt Y3 = f_.addf(X2, Z2);
352 X3 = f_.mulf(X3, Y3);
353 Y3 = f_.addf(t0, t2);
354 Y3 = f_.subf(X3, Y3);
355 Elt Z3 = f_.mulf(b_, t2);
356 X3 = f_.subf(Y3, Z3);
357 Z3 = f_.addf(X3, X3);
358 X3 = f_.addf(X3, Z3);
359 Z3 = f_.subf(t1, X3);
360 X3 = f_.addf(t1, X3);
361 Y3 = f_.mulf(b_, Y3);
362 t1 = f_.addf(t2, t2);
363 t2 = f_.addf(t1, t2);
364 Y3 = f_.subf(Y3, t2);
365 Y3 = f_.subf(Y3, t0);
366 t1 = f_.addf(Y3, Y3);
367 Y3 = f_.addf(t1, Y3);
368 t1 = f_.addf(t0, t0);
369 t0 = f_.addf(t1, t0);
370 t0 = f_.subf(t0, t2);
371 t1 = f_.mulf(t4, Y3);
372 t2 = f_.mulf(t0, Y3);
373 Y3 = f_.mulf(X3, Z3);
374 Y3 = f_.addf(Y3, t2);
375 X3 = f_.mulf(t3, X3);
376 X3 = f_.subf(X3, t1);
377 Z3 = f_.mulf(t4, Z3);
378 t1 = f_.mulf(t3, t0);
379 Z3 = f_.addf(Z3, t1);
380
381 X3o = X3;
382 Y3o = Y3;
383 Z3o = Z3;
384 }
385
386 /* From Algorithm 6: Exception-free point doubling for prime order short
387 * Weierstrass curves E/Fq : y^2 = x^3 + ax + b with a = −3.
388 */
389 void doubleEMinus3A(Elt& X3o, Elt& Y3o, Elt& Z3o, const Elt& X, const Elt& Y,
390 const Elt& Z) const {
391 Elt t0 = f_.mulf(X, X);
392 Elt t1 = f_.mulf(Y, Y);
393 Elt t2 = f_.mulf(Z, Z);
394 Elt t3 = f_.mulf(X, Y);
395 t3 = f_.addf(t3, t3);
396 Elt Z3 = f_.mulf(X, Z);
397 Z3 = f_.addf(Z3, Z3);
398 Elt Y3 = f_.mulf(b_, t2);
399 Y3 = f_.subf(Y3, Z3);
400 Elt X3 = f_.addf(Y3, Y3);
401 Y3 = f_.addf(X3, Y3);
402 X3 = f_.subf(t1, Y3);
403 Y3 = f_.addf(t1, Y3);
404 Y3 = f_.mulf(X3, Y3);
405 X3 = f_.mulf(X3, t3);
406 t3 = f_.addf(t2, t2);
407 t2 = f_.addf(t2, t3);
408 Z3 = f_.mulf(b_, Z3);
409 Z3 = f_.subf(Z3, t2);
410 Z3 = f_.subf(Z3, t0);
411 t3 = f_.addf(Z3, Z3);
412 Z3 = f_.addf(Z3, t3);
413 t3 = f_.addf(t0, t0);
414 t0 = f_.addf(t3, t0);
415 t0 = f_.subf(t0, t2);
416 t0 = f_.mulf(t0, Z3);
417 Y3 = f_.addf(Y3, t0);
418 t0 = f_.mulf(Y, Z);
419 t0 = f_.addf(t0, t0);
420 Z3 = f_.mulf(t0, Z3);
421 X3 = f_.subf(X3, Z3);
422 Z3 = f_.mulf(t0, t1);
423 Z3 = f_.addf(Z3, Z3);
424 Z3 = f_.addf(Z3, Z3);
425
426 X3o = X3;
427 Y3o = Y3;
428 Z3o = Z3;
429 }
430
431 /* From Algorithm 9: Exception-free point doubling for prime order
432 j-invariant 0 short Weierstrass curves E/Fq y^2 = x^3 + b
433
434 X3 = 2XY (YY − 9bZZ)
435 Y3 = (YY − 9bZZ)(YY + 3bZZ) + 24bYYZZ
436 Z3 = 8YYYZ.
437 */
438 void doubleEZeroA(Elt& X3o, Elt& Y3o, Elt& Z3o, const Elt& X, const Elt& Y,
439 const Elt& Z) const {
440 Elt t0 = f_.mulf(X, Y);
441 Elt t1 = f_.mulf(Y, Y);
442 Elt t2 = f_.mulf(Z, Z);
443 Elt t4 = f_.mulf(Y, Z);
444 Elt t5 = f_.mulf(k9b, t2); // 9bZZ
445 Elt t6 = f_.subf(t1, t5); // YY - 9bZZ
446 Elt t7 = f_.mulf(k3b, t2); // 3bZZ
447 Elt t8 = f_.addf(t1, t7); // YY + 3bZZ
448 X3o = f_.mulf(k2, f_.mulf(t0, t6));
449 Y3o = f_.addf(f_.mulf(t6, t8), f_.mulf(k24b, f_.mulf(t1, t2)));
450 Z3o = f_.mulf(k8, f_.mulf(t1, t4));
451 }
452
453 //------------------------------------------------------------
454 // Multi-exponentiation SUM_i scalarMult(p[i], s[i])
455
456 // We follow the basic strategy outlined in Daniel J. Bernstein,
457 // Niels Duif, Tanja Lange, Peter Schwabe, and Bo-Yin Yang,
458 // "High-speed high-security signatures",
459 // https://eprint.iacr.org/2011/368, where Bernstein et al. credit
460 // the method to Bos and Coster via a reference to Peter de Rooij,
461 // "Efficient exponentiation using precomputation and vector
462 // addition chains", in Eurocrypt ’94. In my opinion [matteof@]
463 // the method of Bernstein et al. is not quite the same as the papers
464 // that they reference, but either way we follow Bernstein et al.
465 // with a few modifications, and call the method bos_coster() in
466 // this file.
467
468 // The basic idea is to keep the list (p[i], s[i]) of pairs
469 // (point, scalar) in descending order of scalar, so that s[0]
470 // is the maximum.
471
472 // Bernstein's method repeatedly replaces p[0]*s[0]+p[1]*s[1]
473 // by p[0]*(s[0]-s[1])+(p[0]+p[1])*s[1], where now the first
474 // scalar becomes smaller. Eventually all the scalars s[i] become
475 // 0 for i>=1.
476
477 // For random scalars, one can roughly expect s[0]-s[1] to be
478 // about |F|/n, so the method can be expected to set approximately
479 // log n scalar bits to zero in each iteration. However, its worst
480 // case is horrific. E.g., if s[1]=1 and s[0] is O(2**256), the
481 // method will require O(2**256) iterations to converge.
482
483 // To avoid this worst-case behavior, we depart from Bernstein and
484 // perform either a Bernstein step or a double-and-add step,
485 // whichever one decreases s[0] the most. A double-and-add writes
486 // s[0] = 2*a+b and replaces p[0]*s[0] with (2*p[0])*(s[0]/2) *
487 // b*p[0], where the second multiplication is trivial because b \in
488 // {0,1}. With this choice, we are guaranteed to eliminate at least
489 // one scalar bit per iteration, so the method is no worse than a
490 // loop of single scalar multiplications. Bernstein et al., page
491 // 17, are aware of the problem, but they say that doing anything
492 // about it is "not worthwhile". On the other hand, the fix doesn't
493 // cost anything either, so may as well do it. (Bernstein et
494 // al. propose a different fix.)
495
496 // For easy access to the largest s[0] and second-largest s[1], we
497 // keep the (p[i], s[i]) terms in a heap. Here, "heap" denotes a
498 // variant of the standard heap where the root node H[0] has one
499 // child H[1] (as opposed to two children), and every other node i>0
500 // has two children H[2*i] and H[2*i+1]. A heap comprises at least
501 // two elements H[0] and H[1]. In this way, the two largest scalars
502 // are always available directly.
503
504 // The following function is equivalent to assigning (p[i], s[i]) =
505 // (tp, ts) followed by restoring heap order. However, the logic is
506 // a bit complicated by our desire to avoid unnecessary copies of
507 // large objects (e.g., swap p[i] with a child, followed by another
508 // swap of the child with its child.)
509 //
510 // Warning: tp and ts MUST NOT be references to p[i] and s[i], since
511 // the algorithm overwrites the p and s arrays. This complication
512 // ensues because we are trying to avoid unnecessary copies.
513 void bury(size_t i, size_t n, ECPoint p[/*n*/], N s[/*n*/], const ECPoint& tp,
514 const N& ts) const {
515 while (2 * i < n) {
516 // at least one child
517 size_t cld = 2 * i;
518 if (2 * i + 1 < n && s[cld] < s[2 * i + 1]) {
519 // right child exists and is larger
520 cld = 2 * i + 1;
521 }
522
523 if (ts < s[cld]) {
524 // I is out of order with CLD. Bubble CLD up the tree and
525 // continue as in BURY(CLD, N, P, S, TP, TS).
526 s[i] = s[cld];
527 p[i] = p[cld];
528 i = cld;
529 } else {
530 // already in heap order, stop here.
531 break;
532 }
533 }
534
535 p[i] = tp;
536 s[i] = ts;
537 }
538
539 // bury (p[0], s[0]) to its rightful place in the heap.
540 void bury0(size_t n, ECPoint p[/*n*/], N s[/*n*/]) const {
541 if (s[0] < s[1]) {
542 // equivalent to swap(s[0], s[1]); bury(s[1]);
543 // but without the possibly unnecessary assignment of (s,p)[1]
544 ECPoint tp = p[0];
545 N ts = s[0];
546 p[0] = p[1];
547 s[0] = s[1];
548 bury(1, n, p, s, tp, ts);
549 }
550 }
551
552 // The main Bernstein/Bos-Coster algorithm.
553 ECPoint bos_coster(size_t n, ECPoint p[/*n*/], N s[/*n*/]) const {
554 check(n >= 2, "n >= 2");
555 ECPoint res = zero();
556
557 // build a heap on [1..n)
558 for (size_t i = /*floor*/ (n / 2); i >= 1; --i) {
559 // create temporary copies of p[i], s[i], which are passed by
560 // const &.
561 bury(i, n, p, s, ECPoint(p[i]), N(s[i]));
562 }
563 // finish the heap on [0..n)
564 bury0(n, p, s);
565
566 while (s[0] != /*zero*/ N{}) {
567 // ns0 = s[0] - s[1]
568 N ns0(s[0]);
569 ns0.sub(s[1]);
570
571 // Double-and-add is (locally) better than Bernstein iff s[0]/2
572 // < s[0] - s[1], equivalent to s[0] > 2*s[1], equivalent to
573 // ns0 = s[0] - s[1] > s[1]:
574 if (s[1] < ns0) {
575 // res += (s[0] & 1) * p[0]; s[0] /= 2; p[0] *= 2;
576 uint64_t lsb = s[0].shiftr(1);
577 if (lsb != 0) {
578 addE(res, p[0]);
579 }
580 doubleE(p[0]);
581 } else {
582 // s[0] -= s[1], p[1] += p[0]
583 s[0] = ns0;
584 addE(p[1], p[0]);
585 }
586 bury0(n, p, s);
587 }
588 return res;
589 }
590
591 bool is_zero_a_;
592 bool is_minus_3_a_;
593};
594} // namespace proofs
595
596#endif // PRIVACY_PROOFS_ZK_LIB_EC_ELLIPTIC_CURVE_H_
proofs::Nat
Definition nat.h:60
proofs::EllipticCurve::ECPoint
Definition elliptic_curve.h:48
proofs::GF2_128::Elt
Definition gf2_128.h:63