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 }