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 }