Coverage Summary for Class: PairingCheck (co.rsk.crypto.altbn128java)
Class |
Method, %
|
Line, %
|
PairingCheck |
0%
(0/11)
|
0%
(0/111)
|
PairingCheck$EllCoeffs |
0%
(0/2)
|
0%
(0/5)
|
PairingCheck$Precomputed |
0%
(0/3)
|
0%
(0/5)
|
Total |
0%
(0/16)
|
0%
(0/121)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2019 RSK Labs Ltd.
4 * (derived from ethereumJ library, Copyright (c) 2016 <ether.camp>)
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 package co.rsk.crypto.altbn128java;
21
22 import java.math.BigInteger;
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import static co.rsk.crypto.altbn128java.Params.B_Fp2;
27 import static co.rsk.crypto.altbn128java.Params.PAIRING_FINAL_EXPONENT_Z;
28 import static co.rsk.crypto.altbn128java.Params.TWIST;
29
30 /**
31 * Implementation of a Pairing Check operation over points of two twisted Barreto–Naehrig curves {@link BN128Fp}, {@link BN128Fp2}<br/>
32 * <br/>
33 *
34 * The Pairing itself is a transformation of the form G1 x G2 -> Gt, <br/>
35 * where G1 and G2 are members of {@link BN128G1} {@link BN128G2} respectively, <br/>
36 * Gt is a subgroup of roots of unity in {@link Fp12} field, root degree equals to {@link Params#R} <br/>
37 * <br/>
38 *
39 * Pairing Check input is a sequence of point pairs, the result is either 1 or 0, 1 is considered as success, 0 as fail <br/>
40 * <br/>
41 *
42 * Usage:
43 * <ul>
44 * <li>add pairs sequentially with {@link #addPair(BN128G1, BN128G2)}</li>
45 * <li>run check with {@link #run()} after all paris have been added</li>
46 * <li>get result with {@link #result()}</li>
47 * </ul>
48 *
49 * Arithmetic has been ported from <a href="https://github.com/scipr-lab/libff/blob/master/libff/algebra/curves/alt_bn128/alt_bn128_pairing.cpp">libff</a>
50 * Ate pairing algorithms
51 *
52 * @author Mikhail Kalinin
53 * @since 01.09.2017
54 */
55 public class PairingCheck {
56
57 private static final BigInteger LOOP_COUNT = new BigInteger("29793968203157093288");
58
59 private List<BN128Pair> pairs = new ArrayList<>();
60 private Fp12 product = Fp12._1;
61
62 private PairingCheck() {}
63
64 public static PairingCheck create() {
65 return new PairingCheck();
66 }
67
68 public void addPair(BN128G1 g1, BN128G2 g2) {
69 pairs.add(BN128Pair.of(g1, g2));
70 }
71
72 public void run() {
73
74 for (BN128Pair pair : pairs) {
75
76 Fp12 miller = pair.millerLoop();
77
78 if (!miller.equals(Fp12._1)) { // run mul code only if necessary
79 product = product.mul(miller);
80 }
81 }
82
83 // finalize
84 product = finalExponentiation(product);
85 }
86
87 public int result() {
88 return product.equals(Fp12._1) ? 1 : 0;
89 }
90
91 public static Fp12 millerLoop(BN128G1 g1, BN128G2 g2) {
92
93 // convert to affine coordinates
94 g1 = g1.toAffine();
95 g2 = g2.toAffine();
96
97 // calculate Ell coefficients
98 List<EllCoeffs> coeffs = calcEllCoeffs(g2);
99
100 Fp12 f = Fp12._1;
101 int idx = 0;
102
103 // for each bit except most significant one
104 for (int i = LOOP_COUNT.bitLength() - 2; i >=0; i--) {
105
106 EllCoeffs c = coeffs.get(idx++);
107 f = f.squared();
108 f = f.mulBy024(c.ell0, g1.y.mul(c.ellVW), g1.x.mul(c.ellVV));
109
110 if (LOOP_COUNT.testBit(i)) {
111 c = coeffs.get(idx++);
112 f = f.mulBy024(c.ell0, g1.y.mul(c.ellVW), g1.x.mul(c.ellVV));
113 }
114
115 }
116
117 EllCoeffs c = coeffs.get(idx++);
118 f = f.mulBy024(c.ell0, g1.y.mul(c.ellVW), g1.x.mul(c.ellVV));
119
120 c = coeffs.get(idx);
121 f = f.mulBy024(c.ell0, g1.y.mul(c.ellVW), g1.x.mul(c.ellVV));
122
123 return f;
124 }
125
126 private static List<EllCoeffs> calcEllCoeffs(BN128G2 base) {
127
128 List<EllCoeffs> coeffs = new ArrayList<>();
129
130 BN128G2 addend = base;
131
132 // for each bit except most significant one
133 for (int i = LOOP_COUNT.bitLength() - 2; i >=0; i--) {
134
135 Precomputed doubling = flippedMillerLoopDoubling(addend);
136
137 addend = doubling.g2;
138 coeffs.add(doubling.coeffs);
139
140 if (LOOP_COUNT.testBit(i)) {
141 Precomputed addition = flippedMillerLoopMixedAddition(base, addend);
142 addend = addition.g2;
143 coeffs.add(addition.coeffs);
144 }
145 }
146
147 BN128G2 q1 = base.mulByP();
148 BN128G2 q2 = q1.mulByP();
149
150 q2 = new BN128G2(q2.x, q2.y.negate(), q2.z) ; // q2.y = -q2.y
151
152 Precomputed addition = flippedMillerLoopMixedAddition(q1, addend);
153 addend = addition.g2;
154 coeffs.add(addition.coeffs);
155
156 addition = flippedMillerLoopMixedAddition(q2, addend);
157 coeffs.add(addition.coeffs);
158
159 return coeffs;
160 }
161
162 private static Precomputed flippedMillerLoopMixedAddition(BN128G2 base, BN128G2 addend) {
163
164 Fp2 x1 = addend.x, y1 = addend.y, z1 = addend.z;
165 Fp2 x2 = base.x, y2 = base.y;
166
167 Fp2 d = x1.sub(x2.mul(z1)); // d = x1 - x2 * z1
168 Fp2 e = y1.sub(y2.mul(z1)); // e = y1 - y2 * z1
169 Fp2 f = d.squared(); // f = d^2
170 Fp2 g = e.squared(); // g = e^2
171 Fp2 h = d.mul(f); // h = d * f
172 Fp2 i = x1.mul(f); // i = x1 * f
173 Fp2 j = h.add(z1.mul(g)).sub(i.dbl()); // j = h + z1 * g - 2 * i
174
175 Fp2 x3 = d.mul(j); // x3 = d * j
176 Fp2 y3 = e.mul(i.sub(j)).sub(h.mul(y1)); // y3 = e * (i - j) - h * y1)
177 Fp2 z3 = z1.mul(h); // z3 = Z1*H
178
179 Fp2 ell0 = TWIST.mul(e.mul(x2).sub(d.mul(y2))); // ell_0 = TWIST * (e * x2 - d * y2)
180 Fp2 ellVV = e.negate(); // ell_VV = -e
181 Fp2 ellVW = d; // ell_VW = d
182
183 return Precomputed.of(
184 new BN128G2(x3, y3, z3),
185 new EllCoeffs(ell0, ellVW, ellVV)
186 );
187 }
188
189 private static Precomputed flippedMillerLoopDoubling(BN128G2 g2) {
190
191 Fp2 x = g2.x, y = g2.y, z = g2.z;
192
193 Fp2 a = Fp._2_INV.mul(x.mul(y)); // a = x * y / 2
194 Fp2 b = y.squared(); // b = y^2
195 Fp2 c = z.squared(); // c = z^2
196 Fp2 d = c.add(c).add(c); // d = 3 * c
197 Fp2 e = B_Fp2.mul(d); // e = twist_b * d
198 Fp2 f = e.add(e).add(e); // f = 3 * e
199 Fp2 g = Fp._2_INV.mul(b.add(f)); // g = (b + f) / 2
200 Fp2 h = y.add(z).squared().sub(b.add(c)); // h = (y + z)^2 - (b + c)
201 Fp2 i = e.sub(b); // i = e - b
202 Fp2 j = x.squared(); // j = x^2
203 Fp2 e2 = e.squared(); // e2 = e^2
204
205 Fp2 rx = a.mul(b.sub(f)); // rx = a * (b - f)
206 Fp2 ry = g.squared().sub(e2.add(e2).add(e2)); // ry = g^2 - 3 * e^2
207 Fp2 rz = b.mul(h); // rz = b * h
208
209 Fp2 ell0 = TWIST.mul(i); // ell_0 = twist * i
210 Fp2 ellVW = h.negate(); // ell_VW = -h
211 Fp2 ellVV = j.add(j).add(j); // ell_VV = 3 * j
212
213 return Precomputed.of(
214 new BN128G2(rx, ry, rz),
215 new EllCoeffs(ell0, ellVW, ellVV)
216 );
217 }
218
219 public static Fp12 finalExponentiation(Fp12 el) {
220
221 // first chunk
222 Fp12 w = new Fp12(el.a(), el.b().negate()); // el.b = -el.b
223 Fp12 x = el.inverse();
224 Fp12 y = w.mul(x);
225 Fp12 z = y.frobeniusMap(2);
226 Fp12 pre = z.mul(y);
227
228 // last chunk
229 Fp12 a = pre.negExp(PAIRING_FINAL_EXPONENT_Z);
230 Fp12 b = a.cyclotomicSquared();
231 Fp12 c = b.cyclotomicSquared();
232 Fp12 d = c.mul(b);
233 Fp12 e = d.negExp(PAIRING_FINAL_EXPONENT_Z);
234 Fp12 f = e.cyclotomicSquared();
235 Fp12 g = f.negExp(PAIRING_FINAL_EXPONENT_Z);
236 Fp12 h = d.unitaryInverse();
237 Fp12 i = g.unitaryInverse();
238 Fp12 j = i.mul(e);
239 Fp12 k = j.mul(h);
240 Fp12 l = k.mul(b);
241 Fp12 m = k.mul(e);
242 Fp12 n = m.mul(pre);
243 Fp12 o = l.frobeniusMap(1);
244 Fp12 p = o.mul(n);
245 Fp12 q = k.frobeniusMap(2);
246 Fp12 r = q.mul(p);
247 Fp12 s = pre.unitaryInverse();
248 Fp12 t = s.mul(l);
249 Fp12 u = t.frobeniusMap(3);
250 Fp12 v = u.mul(r);
251
252 return v;
253 }
254
255 static class Precomputed {
256
257 private BN128G2 g2;
258 private EllCoeffs coeffs;
259
260 static Precomputed of(BN128G2 g2, EllCoeffs coeffs) {
261 return new Precomputed(g2, coeffs);
262 }
263
264 Precomputed(BN128G2 g2, EllCoeffs coeffs) {
265 this.g2 = g2;
266 this.coeffs = coeffs;
267 }
268 }
269
270 static class EllCoeffs {
271 private Fp2 ell0;
272 private Fp2 ellVW;
273 private Fp2 ellVV;
274
275 EllCoeffs(Fp2 ell0, Fp2 ellVW, Fp2 ellVV) {
276 this.ell0 = ell0;
277 this.ellVW = ellVW;
278 this.ellVV = ellVV;
279 }
280 }
281 }