• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.bouncycastle.math.ec;
2 
3 import java.math.BigInteger;
4 
5 import org.bouncycastle.math.ec.endo.ECEndomorphism;
6 import org.bouncycastle.math.ec.endo.GLVEndomorphism;
7 import org.bouncycastle.math.field.FiniteField;
8 import org.bouncycastle.math.field.PolynomialExtensionField;
9 
10 public class ECAlgorithms
11 {
isF2mCurve(ECCurve c)12     public static boolean isF2mCurve(ECCurve c)
13     {
14         return isF2mField(c.getField());
15     }
16 
isF2mField(FiniteField field)17     public static boolean isF2mField(FiniteField field)
18     {
19         return field.getDimension() > 1 && field.getCharacteristic().equals(ECConstants.TWO)
20             && field instanceof PolynomialExtensionField;
21     }
22 
isFpCurve(ECCurve c)23     public static boolean isFpCurve(ECCurve c)
24     {
25         return isFpField(c.getField());
26     }
27 
isFpField(FiniteField field)28     public static boolean isFpField(FiniteField field)
29     {
30         return field.getDimension() == 1;
31     }
32 
sumOfMultiplies(ECPoint[] ps, BigInteger[] ks)33     public static ECPoint sumOfMultiplies(ECPoint[] ps, BigInteger[] ks)
34     {
35         if (ps == null || ks == null || ps.length != ks.length || ps.length < 1)
36         {
37             throw new IllegalArgumentException("point and scalar arrays should be non-null, and of equal, non-zero, length");
38         }
39 
40         int count = ps.length;
41         switch (count)
42         {
43         case 1:
44             return ps[0].multiply(ks[0]);
45         case 2:
46             return sumOfTwoMultiplies(ps[0], ks[0], ps[1], ks[1]);
47         default:
48             break;
49         }
50 
51         ECPoint p = ps[0];
52         ECCurve c = p.getCurve();
53 
54         ECPoint[] imported = new ECPoint[count];
55         imported[0] = p;
56         for (int i = 1; i < count; ++i)
57         {
58             imported[i] = importPoint(c, ps[i]);
59         }
60 
61         ECEndomorphism endomorphism = c.getEndomorphism();
62         if (endomorphism instanceof GLVEndomorphism)
63         {
64             return validatePoint(implSumOfMultipliesGLV(imported, ks, (GLVEndomorphism)endomorphism));
65         }
66 
67         return validatePoint(implSumOfMultiplies(imported, ks));
68     }
69 
sumOfTwoMultiplies(ECPoint P, BigInteger a, ECPoint Q, BigInteger b)70     public static ECPoint sumOfTwoMultiplies(ECPoint P, BigInteger a,
71         ECPoint Q, BigInteger b)
72     {
73         ECCurve cp = P.getCurve();
74         Q = importPoint(cp, Q);
75 
76         // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick
77         if (cp instanceof ECCurve.AbstractF2m)
78         {
79             ECCurve.AbstractF2m f2mCurve = (ECCurve.AbstractF2m)cp;
80             if (f2mCurve.isKoblitz())
81             {
82                 return validatePoint(P.multiply(a).add(Q.multiply(b)));
83             }
84         }
85 
86         ECEndomorphism endomorphism = cp.getEndomorphism();
87         if (endomorphism instanceof GLVEndomorphism)
88         {
89             return validatePoint(
90                 implSumOfMultipliesGLV(new ECPoint[]{ P, Q }, new BigInteger[]{ a, b }, (GLVEndomorphism)endomorphism));
91         }
92 
93         return validatePoint(implShamirsTrickWNaf(P, a, Q, b));
94     }
95 
96     /*
97      * "Shamir's Trick", originally due to E. G. Straus
98      * (Addition chains of vectors. American Mathematical Monthly,
99      * 71(7):806-808, Aug./Sept. 1964)
100      * <pre>
101      * Input: The points P, Q, scalar k = (km?, ... , k1, k0)
102      * and scalar l = (lm?, ... , l1, l0).
103      * Output: R = k * P + l * Q.
104      * 1: Z <- P + Q
105      * 2: R <- O
106      * 3: for i from m-1 down to 0 do
107      * 4:        R <- R + R        {point doubling}
108      * 5:        if (ki = 1) and (li = 0) then R <- R + P end if
109      * 6:        if (ki = 0) and (li = 1) then R <- R + Q end if
110      * 7:        if (ki = 1) and (li = 1) then R <- R + Z end if
111      * 8: end for
112      * 9: return R
113      * </pre>
114      */
shamirsTrick(ECPoint P, BigInteger k, ECPoint Q, BigInteger l)115     public static ECPoint shamirsTrick(ECPoint P, BigInteger k,
116         ECPoint Q, BigInteger l)
117     {
118         ECCurve cp = P.getCurve();
119         Q = importPoint(cp, Q);
120 
121         return validatePoint(implShamirsTrickJsf(P, k, Q, l));
122     }
123 
importPoint(ECCurve c, ECPoint p)124     public static ECPoint importPoint(ECCurve c, ECPoint p)
125     {
126         ECCurve cp = p.getCurve();
127         if (!c.equals(cp))
128         {
129             throw new IllegalArgumentException("Point must be on the same curve");
130         }
131         return c.importPoint(p);
132     }
133 
montgomeryTrick(ECFieldElement[] zs, int off, int len)134     public static void montgomeryTrick(ECFieldElement[] zs, int off, int len)
135     {
136         montgomeryTrick(zs, off, len, null);
137     }
138 
montgomeryTrick(ECFieldElement[] zs, int off, int len, ECFieldElement scale)139     public static void montgomeryTrick(ECFieldElement[] zs, int off, int len, ECFieldElement scale)
140     {
141         /*
142          * Uses the "Montgomery Trick" to invert many field elements, with only a single actual
143          * field inversion. See e.g. the paper:
144          * "Fast Multi-scalar Multiplication Methods on Elliptic Curves with Precomputation Strategy Using Montgomery Trick"
145          * by Katsuyuki Okeya, Kouichi Sakurai.
146          */
147 
148         ECFieldElement[] c = new ECFieldElement[len];
149         c[0] = zs[off];
150 
151         int i = 0;
152         while (++i < len)
153         {
154             c[i] = c[i - 1].multiply(zs[off + i]);
155         }
156 
157         --i;
158 
159         if (scale != null)
160         {
161             c[i] = c[i].multiply(scale);
162         }
163 
164         ECFieldElement u = c[i].invert();
165 
166         while (i > 0)
167         {
168             int j = off + i--;
169             ECFieldElement tmp = zs[j];
170             zs[j] = c[i].multiply(u);
171             u = u.multiply(tmp);
172         }
173 
174         zs[off] = u;
175     }
176 
177     /**
178      * Simple shift-and-add multiplication. Serves as reference implementation
179      * to verify (possibly faster) implementations, and for very small scalars.
180      *
181      * @param p
182      *            The point to multiply.
183      * @param k
184      *            The multiplier.
185      * @return The result of the point multiplication <code>kP</code>.
186      */
referenceMultiply(ECPoint p, BigInteger k)187     public static ECPoint referenceMultiply(ECPoint p, BigInteger k)
188     {
189         BigInteger x = k.abs();
190         ECPoint q = p.getCurve().getInfinity();
191         int t = x.bitLength();
192         if (t > 0)
193         {
194             if (x.testBit(0))
195             {
196                 q = p;
197             }
198             for (int i = 1; i < t; i++)
199             {
200                 p = p.twice();
201                 if (x.testBit(i))
202                 {
203                     q = q.add(p);
204                 }
205             }
206         }
207         return k.signum() < 0 ? q.negate() : q;
208     }
209 
validatePoint(ECPoint p)210     public static ECPoint validatePoint(ECPoint p)
211     {
212         if (!p.isValid())
213         {
214             throw new IllegalArgumentException("Invalid point");
215         }
216 
217         return p;
218     }
219 
implShamirsTrickJsf(ECPoint P, BigInteger k, ECPoint Q, BigInteger l)220     static ECPoint implShamirsTrickJsf(ECPoint P, BigInteger k,
221         ECPoint Q, BigInteger l)
222     {
223         ECCurve curve = P.getCurve();
224         ECPoint infinity = curve.getInfinity();
225 
226         // TODO conjugate co-Z addition (ZADDC) can return both of these
227         ECPoint PaddQ = P.add(Q);
228         ECPoint PsubQ = P.subtract(Q);
229 
230         ECPoint[] points = new ECPoint[]{ Q, PsubQ, P, PaddQ };
231         curve.normalizeAll(points);
232 
233         ECPoint[] table = new ECPoint[] {
234             points[3].negate(), points[2].negate(), points[1].negate(),
235             points[0].negate(), infinity, points[0],
236             points[1], points[2], points[3] };
237 
238         byte[] jsf = WNafUtil.generateJSF(k, l);
239 
240         ECPoint R = infinity;
241 
242         int i = jsf.length;
243         while (--i >= 0)
244         {
245             int jsfi = jsf[i];
246 
247             // NOTE: The shifting ensures the sign is extended correctly
248             int kDigit = ((jsfi << 24) >> 28), lDigit = ((jsfi << 28) >> 28);
249 
250             int index = 4 + (kDigit * 3) + lDigit;
251             R = R.twicePlus(table[index]);
252         }
253 
254         return R;
255     }
256 
implShamirsTrickWNaf(ECPoint P, BigInteger k, ECPoint Q, BigInteger l)257     static ECPoint implShamirsTrickWNaf(ECPoint P, BigInteger k,
258         ECPoint Q, BigInteger l)
259     {
260         boolean negK = k.signum() < 0, negL = l.signum() < 0;
261 
262         k = k.abs();
263         l = l.abs();
264 
265         int widthP = Math.max(2, Math.min(16, WNafUtil.getWindowSize(k.bitLength())));
266         int widthQ = Math.max(2, Math.min(16, WNafUtil.getWindowSize(l.bitLength())));
267 
268         WNafPreCompInfo infoP = WNafUtil.precompute(P, widthP, true);
269         WNafPreCompInfo infoQ = WNafUtil.precompute(Q, widthQ, true);
270 
271         ECPoint[] preCompP = negK ? infoP.getPreCompNeg() : infoP.getPreComp();
272         ECPoint[] preCompQ = negL ? infoQ.getPreCompNeg() : infoQ.getPreComp();
273         ECPoint[] preCompNegP = negK ? infoP.getPreComp() : infoP.getPreCompNeg();
274         ECPoint[] preCompNegQ = negL ? infoQ.getPreComp() : infoQ.getPreCompNeg();
275 
276         byte[] wnafP = WNafUtil.generateWindowNaf(widthP, k);
277         byte[] wnafQ = WNafUtil.generateWindowNaf(widthQ, l);
278 
279         return implShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ, preCompNegQ, wnafQ);
280     }
281 
282     static ECPoint implShamirsTrickWNaf(ECPoint P, BigInteger k, ECPointMap pointMapQ, BigInteger l)
283     {
284         boolean negK = k.signum() < 0, negL = l.signum() < 0;
285 
286         k = k.abs();
287         l = l.abs();
288 
289         int width = Math.max(2, Math.min(16, WNafUtil.getWindowSize(Math.max(k.bitLength(), l.bitLength()))));
290 
291         ECPoint Q = WNafUtil.mapPointWithPrecomp(P, width, true, pointMapQ);
292         WNafPreCompInfo infoP = WNafUtil.getWNafPreCompInfo(P);
293         WNafPreCompInfo infoQ = WNafUtil.getWNafPreCompInfo(Q);
294 
295         ECPoint[] preCompP = negK ? infoP.getPreCompNeg() : infoP.getPreComp();
296         ECPoint[] preCompQ = negL ? infoQ.getPreCompNeg() : infoQ.getPreComp();
297         ECPoint[] preCompNegP = negK ? infoP.getPreComp() : infoP.getPreCompNeg();
298         ECPoint[] preCompNegQ = negL ? infoQ.getPreComp() : infoQ.getPreCompNeg();
299 
300         byte[] wnafP = WNafUtil.generateWindowNaf(width, k);
301         byte[] wnafQ = WNafUtil.generateWindowNaf(width, l);
302 
303         return implShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ, preCompNegQ, wnafQ);
304     }
305 
306     private static ECPoint implShamirsTrickWNaf(ECPoint[] preCompP, ECPoint[] preCompNegP, byte[] wnafP,
307         ECPoint[] preCompQ, ECPoint[] preCompNegQ, byte[] wnafQ)
308     {
309         int len = Math.max(wnafP.length, wnafQ.length);
310 
311         ECCurve curve = preCompP[0].getCurve();
312         ECPoint infinity = curve.getInfinity();
313 
314         ECPoint R = infinity;
315         int zeroes = 0;
316 
317         for (int i = len - 1; i >= 0; --i)
318         {
319             int wiP = i < wnafP.length ? wnafP[i] : 0;
320             int wiQ = i < wnafQ.length ? wnafQ[i] : 0;
321 
322             if ((wiP | wiQ) == 0)
323             {
324                 ++zeroes;
325                 continue;
326             }
327 
328             ECPoint r = infinity;
329             if (wiP != 0)
330             {
331                 int nP = Math.abs(wiP);
332                 ECPoint[] tableP = wiP < 0 ? preCompNegP : preCompP;
333                 r = r.add(tableP[nP >>> 1]);
334             }
335             if (wiQ != 0)
336             {
337                 int nQ = Math.abs(wiQ);
338                 ECPoint[] tableQ = wiQ < 0 ? preCompNegQ : preCompQ;
339                 r = r.add(tableQ[nQ >>> 1]);
340             }
341 
342             if (zeroes > 0)
343             {
344                 R = R.timesPow2(zeroes);
345                 zeroes = 0;
346             }
347 
348             R = R.twicePlus(r);
349         }
350 
351         if (zeroes > 0)
352         {
353             R = R.timesPow2(zeroes);
354         }
355 
356         return R;
357     }
358 
implSumOfMultiplies(ECPoint[] ps, BigInteger[] ks)359     static ECPoint implSumOfMultiplies(ECPoint[] ps, BigInteger[] ks)
360     {
361         int count = ps.length;
362         boolean[] negs = new boolean[count];
363         WNafPreCompInfo[] infos = new WNafPreCompInfo[count];
364         byte[][] wnafs = new byte[count][];
365 
366         for (int i = 0; i < count; ++i)
367         {
368             BigInteger ki = ks[i]; negs[i] = ki.signum() < 0; ki = ki.abs();
369 
370             int width = Math.max(2, Math.min(16, WNafUtil.getWindowSize(ki.bitLength())));
371             infos[i] = WNafUtil.precompute(ps[i], width, true);
372             wnafs[i] = WNafUtil.generateWindowNaf(width, ki);
373         }
374 
375         return implSumOfMultiplies(negs, infos, wnafs);
376     }
377 
378     static ECPoint implSumOfMultipliesGLV(ECPoint[] ps, BigInteger[] ks, GLVEndomorphism glvEndomorphism)
379     {
380         BigInteger n = ps[0].getCurve().getOrder();
381 
382         int len = ps.length;
383 
384         BigInteger[] abs = new BigInteger[len << 1];
385         for (int i = 0, j = 0; i < len; ++i)
386         {
387             BigInteger[] ab = glvEndomorphism.decomposeScalar(ks[i].mod(n));
388             abs[j++] = ab[0];
389             abs[j++] = ab[1];
390         }
391 
392         ECPointMap pointMap = glvEndomorphism.getPointMap();
393         if (glvEndomorphism.hasEfficientPointMap())
394         {
395             return ECAlgorithms.implSumOfMultiplies(ps, pointMap, abs);
396         }
397 
398         ECPoint[] pqs = new ECPoint[len << 1];
399         for (int i = 0, j = 0; i < len; ++i)
400         {
401             ECPoint p = ps[i], q = pointMap.map(p);
402             pqs[j++] = p;
403             pqs[j++] = q;
404         }
405 
406         return ECAlgorithms.implSumOfMultiplies(pqs, abs);
407 
408     }
409 
410     static ECPoint implSumOfMultiplies(ECPoint[] ps, ECPointMap pointMap, BigInteger[] ks)
411     {
412         int halfCount = ps.length, fullCount = halfCount << 1;
413 
414         boolean[] negs = new boolean[fullCount];
415         WNafPreCompInfo[] infos = new WNafPreCompInfo[fullCount];
416         byte[][] wnafs = new byte[fullCount][];
417 
418         for (int i = 0; i < halfCount; ++i)
419         {
420             int j0 = i << 1, j1 = j0 + 1;
421 
422             BigInteger kj0 = ks[j0]; negs[j0] = kj0.signum() < 0; kj0 = kj0.abs();
423             BigInteger kj1 = ks[j1]; negs[j1] = kj1.signum() < 0; kj1 = kj1.abs();
424 
425             int width = Math.max(2, Math.min(16, WNafUtil.getWindowSize(Math.max(kj0.bitLength(), kj1.bitLength()))));
426 
427             ECPoint P = ps[i], Q = WNafUtil.mapPointWithPrecomp(P, width, true, pointMap);
428             infos[j0] = WNafUtil.getWNafPreCompInfo(P);
429             infos[j1] = WNafUtil.getWNafPreCompInfo(Q);
430             wnafs[j0] = WNafUtil.generateWindowNaf(width, kj0);
431             wnafs[j1] = WNafUtil.generateWindowNaf(width, kj1);
432         }
433 
434         return implSumOfMultiplies(negs, infos, wnafs);
435     }
436 
437     private static ECPoint implSumOfMultiplies(boolean[] negs, WNafPreCompInfo[] infos, byte[][] wnafs)
438     {
439         int len = 0, count = wnafs.length;
440         for (int i = 0; i < count; ++i)
441         {
442             len = Math.max(len, wnafs[i].length);
443         }
444 
445         ECCurve curve = infos[0].getPreComp()[0].getCurve();
446         ECPoint infinity = curve.getInfinity();
447 
448         ECPoint R = infinity;
449         int zeroes = 0;
450 
451         for (int i = len - 1; i >= 0; --i)
452         {
453             ECPoint r = infinity;
454 
455             for (int j = 0; j < count; ++j)
456             {
457                 byte[] wnaf = wnafs[j];
458                 int wi = i < wnaf.length ? wnaf[i] : 0;
459                 if (wi != 0)
460                 {
461                     int n = Math.abs(wi);
462                     WNafPreCompInfo info = infos[j];
463                     ECPoint[] table = (wi < 0 == negs[j]) ? info.getPreComp() : info.getPreCompNeg();
464                     r = r.add(table[n >>> 1]);
465                 }
466             }
467 
468             if (r == infinity)
469             {
470                 ++zeroes;
471                 continue;
472             }
473 
474             if (zeroes > 0)
475             {
476                 R = R.timesPow2(zeroes);
477                 zeroes = 0;
478             }
479 
480             R = R.twicePlus(r);
481         }
482 
483         if (zeroes > 0)
484         {
485             R = R.timesPow2(zeroes);
486         }
487 
488         return R;
489     }
490 }
491