Coverage Summary for Class: BN128 (co.rsk.crypto.altbn128java)

Class Class, % Method, % Line, %
BN128 0% (0/1) 0% (0/14) 0% (0/82)


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.Objects; 24  25 /** 26  * Implementation of Barreto–Naehrig curve defined over abstract finite field. This curve is one of the keys to zkSNARKs. <br/> 27  * This specific curve was introduced in 28  * <a href="https://github.com/scipr-lab/libff#elliptic-curve-choices">libff</a> 29  * and used by a proving system in 30  * <a href="https://github.com/zcash/zcash/wiki/specification#zcash-protocol">ZCash protocol</a> <br/> 31  * <br/> 32  * 33  * Curve equation: <br/> 34  * Y^2 = X^3 + b, where "b" is a constant number belonging to corresponding specific field <br/> 35  * Point at infinity is encoded as <code>(0, 0, 0)</code> <br/> 36  * <br/> 37  * 38  * This curve has embedding degree 12 with respect to "r" (see {@link Params#R}), which means that "r" is a multiple of "p ^ 12 - 1", 39  * this condition is important for pairing operation implemented in {@link PairingCheck}<br/> 40  * <br/> 41  * 42  * Code of curve arithmetic has been ported from 43  * <a href="https://github.com/scipr-lab/libff/blob/master/libff/algebra/curves/alt_bn128/alt_bn128_g1.cpp">libff</a> <br/> 44  * <br/> 45  * 46  * Current implementation uses Jacobian coordinate system as 47  * <a href="https://github.com/scipr-lab/libff/blob/master/libff/algebra/curves/alt_bn128/alt_bn128_g1.cpp">libff</a> does, 48  * use {@link #toEthNotation()} to convert Jacobian coords to Ethereum encoding <br/> 49  * 50  * @author Mikhail Kalinin 51  * @since 05.09.2017 52  */ 53 public abstract class BN128<T extends Field<T>> { 54  55  protected T x; 56  protected T y; 57  protected T z; 58  59  protected BN128(T x, T y, T z) { 60  this.x = x; 61  this.y = y; 62  this.z = z; 63  } 64  65  /** 66  * Point at infinity in Ethereum notation: should return (0; 0; 0), 67  * {@link #isZero()} method called for that point, also, returns {@code true} 68  */ 69  abstract protected BN128<T> zero(); 70  abstract protected BN128<T> instance(T x, T y, T z); 71  abstract protected T b(); 72  abstract protected T one(); 73  74  /** 75  * Transforms given Jacobian to affine coordinates and then creates a point 76  */ 77  public BN128<T> toAffine() { 78  79  if (isZero()) { 80  BN128<T> zero = zero(); 81  return instance(zero.x, one(), zero.z); // (0; 1; 0) 82  } 83  84  T zInv = z.inverse(); 85  T zInv2 = zInv.squared(); 86  T zInv3 = zInv2.mul(zInv); 87  88  T ax = x.mul(zInv2); 89  T ay = y.mul(zInv3); 90  91  return instance(ax, ay, one()); 92  } 93  94  /** 95  * Runs affine transformation and encodes point at infinity as (0; 0; 0) 96  */ 97  public BN128<T> toEthNotation() { 98  BN128<T> affine = toAffine(); 99  100  // affine zero is (0; 1; 0), convert to Ethereum zero: (0; 0; 0) 101  if (affine.isZero()) { 102  return zero(); 103  } else { 104  return affine; 105  } 106  } 107  108  protected boolean isOnCurve() { 109  110  if (isZero()){ return true;} 111  112  T z6 = z.squared().mul(z).squared(); 113  114  T left = y.squared(); // y^2 115  T right = x.squared().mul(x).add(b().mul(z6)); // x^3 + b * z^6 116  return left.equals(right); 117  } 118  119  public BN128<T> add(BN128<T> o) { 120  121  if (this.isZero()) {return o;} // 0 + P = P 122  if (o.isZero()) {return this;} // P + 0 = P 123  124  T x1 = this.x, y1 = this.y, z1 = this.z; 125  T x2 = o.x, y2 = o.y, z2 = o.z; 126  127  // ported code is started from here 128  // next calculations are done in Jacobian coordinates 129  130  T z1z1 = z1.squared(); 131  T z2z2 = z2.squared(); 132  133  T u1 = x1.mul(z2z2); 134  T u2 = x2.mul(z1z1); 135  136  T z1Cubed = z1.mul(z1z1); 137  T z2Cubed = z2.mul(z2z2); 138  139  T s1 = y1.mul(z2Cubed); // s1 = y1 * Z2^3 140  T s2 = y2.mul(z1Cubed); // s2 = y2 * Z1^3 141  142  if (u1.equals(u2) && s1.equals(s2)) { 143  return dbl(); // P + P = 2P 144  } 145  146  T h = u2.sub(u1); // h = u2 - u1 147  T i = h.dbl().squared(); // i = (2 * h)^2 148  T j = h.mul(i); // j = h * i 149  T r = s2.sub(s1).dbl(); // r = 2 * (s2 - s1) 150  T v = u1.mul(i); // v = u1 * i 151  T zz = z1.add(z2).squared() 152  .sub(z1.squared()).sub(z2.squared()); 153  154  T x3 = r.squared().sub(j).sub(v.dbl()); // x3 = r^2 - j - 2 * v 155  T y3 = v.sub(x3).mul(r).sub(s1.mul(j).dbl()); // y3 = r * (v - x3) - 2 * (s1 * j) 156  T z3 = zz.mul(h); // z3 = ((z1+z2)^2 - z1^2 - z2^2) * h = zz * h 157  158  return instance(x3, y3, z3); 159  } 160  161  public BN128<T> mul(BigInteger s) { 162  163  if (s.compareTo(BigInteger.ZERO) == 0) { // P * 0 = 0 164  return zero(); 165  } 166  167  if (isZero()) {return this;} // 0 * s = 0 168  169  BN128<T> res = zero(); 170  171  for (int i = s.bitLength() - 1; i >= 0; i--) { 172  173  res = res.dbl(); 174  175  if (s.testBit(i)) { 176  res = res.add(this); 177  } 178  } 179  180  return res; 181  } 182  183  private BN128<T> dbl() { 184  185  if (isZero()) {return this;} 186  187  // ported code is started from here 188  // next calculations are done in Jacobian coordinates with z = 1 189  190  T a = x.squared(); // a = x^2 191  T b = y.squared(); // b = y^2 192  T c = b.squared(); // c = b^2 193  T d = x.add(b).squared().sub(a).sub(c); 194  d = d.add(d); // d = 2 * ((x + b)^2 - a - c) 195  T e = a.add(a).add(a); // e = 3 * a 196  T f = e.squared(); // f = e^2 197  198  T x3 = f.sub(d.add(d)); // rx = f - 2 * d 199  T y3 = e.mul(d.sub(x3)).sub(c.dbl().dbl().dbl()); // ry = e * (d - rx) - 8 * c 200  T z3 = y.mul(z).dbl(); // z3 = 2 * y * z 201  202  return instance(x3, y3, z3); 203  } 204  205  public T x() { 206  return x; 207  } 208  209  public T y() { 210  return y; 211  } 212  213  public boolean isZero() { 214  return z.isZero(); 215  } 216  217  protected boolean isValid() { 218  219  // check whether coordinates belongs to the Field 220  if (!x.isValid() || !y.isValid() || !z.isValid()) { 221  return false; 222  } 223  224  // check whether point is on the curve 225  return isOnCurve(); 226  } 227  228  @Override 229  public String toString() { 230  return String.format("(%s; %s; %s)", x.toString(), y.toString(), z.toString()); 231  } 232  233  @Override 234  @SuppressWarnings("all") 235  public boolean equals(Object o) { 236  if (this == o) {return true;} 237  if (!(o instanceof BN128)) {return false;} 238  239  BN128<?> bn128 = (BN128<?>) o; 240  241  if (x != null ? !x.equals(bn128.x) : bn128.x != null) {return false;} 242  if (y != null ? !y.equals(bn128.y) : bn128.y != null) {return false;} 243  return !(z != null ? !z.equals(bn128.z) : bn128.z != null); 244  } 245  246  @Override 247  public int hashCode() { 248  return Objects.hash(x,y,z); 249  } 250 }