Skip to content

Commit 6d458bb

Browse files
committed
First version with immutable GroupElement. Needs some fixing up of formatting, comments. Maybe add test for precomputed.
1 parent 581c6a9 commit 6d458bb

File tree

7 files changed

+120
-97
lines changed

7 files changed

+120
-97
lines changed

src/net/i2p/crypto/eddsa/math/Curve.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class Curve implements Serializable {
2828

2929
private final GroupElement zeroP2;
3030
private final GroupElement zeroP3;
31+
private final GroupElement zeroP3PrecomputedDouble;
3132
private final GroupElement zeroPrecomp;
3233

3334
public Curve(Field f, byte[] d, FieldElement I) {
@@ -39,7 +40,8 @@ public Curve(Field f, byte[] d, FieldElement I) {
3940
FieldElement zero = f.ZERO;
4041
FieldElement one = f.ONE;
4142
zeroP2 = GroupElement.p2(this, zero, one, one);
42-
zeroP3 = GroupElement.p3(this, zero, one, one, zero);
43+
zeroP3 = GroupElement.p3(this, zero, one, one, zero, false);
44+
zeroP3PrecomputedDouble = GroupElement.p3(this, zero, one, one, zero, true);
4345
zeroPrecomp = GroupElement.precomp(this, one, one, zero);
4446
}
4547

@@ -65,6 +67,8 @@ public GroupElement getZero(GroupElement.Representation repr) {
6567
return zeroP2;
6668
case P3:
6769
return zeroP3;
70+
case P3PrecomputedDouble:
71+
return zeroP3PrecomputedDouble;
6872
case PRECOMP:
6973
return zeroPrecomp;
7074
default:
@@ -73,9 +77,7 @@ public GroupElement getZero(GroupElement.Representation repr) {
7377
}
7478

7579
public GroupElement createPoint(byte[] P, boolean precompute) {
76-
GroupElement ge = new GroupElement(this, P);
77-
if (precompute)
78-
ge.precompute(true);
80+
GroupElement ge = new GroupElement(this, P, precompute);
7981
return ge;
8082
}
8183

src/net/i2p/crypto/eddsa/math/GroupElement.java

Lines changed: 85 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public enum Representation {
4949
P2,
5050
/** Extended ($P^3$): $(X:Y:Z:T)$ satisfying $x=X/Z, y=Y/Z, XY=ZT$ */
5151
P3,
52+
/** P3 but also populate dblPrecmp */
53+
P3PrecomputedDouble,
5254
/** Completed ($P \times P$): $((X:Z),(Y:T))$ satisfying $x=X/Z, y=Y/T$ */
5355
P1P1,
5456
/** Precomputed (Duif): $(y+x,y-x,2dxy)$ */
@@ -71,7 +73,7 @@ public static GroupElement p2(
7173
final FieldElement X,
7274
final FieldElement Y,
7375
final FieldElement Z) {
74-
return new GroupElement(curve, Representation.P2, X, Y, Z, null);
76+
return new GroupElement(curve, Representation.P2, X, Y, Z, null, false);
7577
}
7678

7779
/**
@@ -89,8 +91,9 @@ public static GroupElement p3(
8991
final FieldElement X,
9092
final FieldElement Y,
9193
final FieldElement Z,
92-
final FieldElement T) {
93-
return new GroupElement(curve, Representation.P3, X, Y, Z, T);
94+
final FieldElement T,
95+
final boolean precomputeDoubleOnly) {
96+
return new GroupElement(curve, Representation.P3, X, Y, Z, T, precomputeDoubleOnly);
9497
}
9598

9699
/**
@@ -109,7 +112,7 @@ public static GroupElement p1p1(
109112
final FieldElement Y,
110113
final FieldElement Z,
111114
final FieldElement T) {
112-
return new GroupElement(curve, Representation.P1P1, X, Y, Z, T);
115+
return new GroupElement(curve, Representation.P1P1, X, Y, Z, T, false);
113116
}
114117

115118
/**
@@ -126,7 +129,7 @@ public static GroupElement precomp(
126129
final FieldElement ypx,
127130
final FieldElement ymx,
128131
final FieldElement xy2d) {
129-
return new GroupElement(curve, Representation.PRECOMP, ypx, ymx, xy2d, null);
132+
return new GroupElement(curve, Representation.PRECOMP, ypx, ymx, xy2d, null, false);
130133
}
131134

132135
/**
@@ -145,7 +148,7 @@ public static GroupElement cached(
145148
final FieldElement YmX,
146149
final FieldElement Z,
147150
final FieldElement T2d) {
148-
return new GroupElement(curve, Representation.CACHED, YpX, YmX, Z, T2d);
151+
return new GroupElement(curve, Representation.CACHED, YpX, YmX, Z, T2d, false);
149152
}
150153

151154
/**
@@ -184,15 +187,15 @@ public static GroupElement cached(
184187
* <p>
185188
* Variable is package private only so that tests run.
186189
*/
187-
GroupElement[][] precmp;
190+
final GroupElement[][] precmp;
188191

189192
/**
190193
* Precomputed table for {@link #doubleScalarMultiplyVariableTime(GroupElement, byte[], byte[])},
191194
* filled if necessary.
192195
* <p>
193196
* Variable is package private only so that tests run.
194197
*/
195-
GroupElement[] dblPrecmp;
198+
final GroupElement[] dblPrecmp;
196199

197200
/**
198201
* Creates a group element for a curve.
@@ -210,13 +213,16 @@ public GroupElement(
210213
final FieldElement X,
211214
final FieldElement Y,
212215
final FieldElement Z,
213-
final FieldElement T) {
216+
final FieldElement T,
217+
final boolean precomputeDouble) {
214218
this.curve = curve;
215219
this.repr = repr;
216220
this.X = X;
217221
this.Y = Y;
218222
this.Z = Z;
219223
this.T = T;
224+
this.precmp = null;
225+
this.dblPrecmp = precomputeDouble ? precomputeDouble() : null;
220226
}
221227

222228
/**
@@ -236,6 +242,11 @@ public GroupElement(
236242
* @param s The encoded point.
237243
*/
238244
public GroupElement(final Curve curve, final byte[] s) {
245+
this(curve, s, false);
246+
}
247+
248+
// TODO
249+
public GroupElement(final Curve curve, final byte[] s, boolean precomputeSingleAndDouble) {
239250
FieldElement x, y, yy, u, v, v3, vxx, check;
240251
y = curve.getField().fromByteArray(s);
241252
yy = y.square();
@@ -278,6 +289,13 @@ public GroupElement(final Curve curve, final byte[] s) {
278289
this.Y = y;
279290
this.Z = curve.getField().ONE;
280291
this.T = this.X.multiply(this.Y);
292+
if(precomputeSingleAndDouble) {
293+
precmp = precomputeSingle();
294+
dblPrecmp = precomputeDouble();
295+
} else {
296+
precmp = null;
297+
dblPrecmp = null;
298+
}
281299
}
282300

283301
/**
@@ -376,6 +394,11 @@ public GroupElement toP3() {
376394
return toRep(Representation.P3);
377395
}
378396

397+
// TODO
398+
public GroupElement toP3PrecomputeDouble() {
399+
return toRep(Representation.P3PrecomputedDouble);
400+
}
401+
379402
/**
380403
* Converts the group element to the CACHED representation.
381404
*
@@ -414,7 +437,7 @@ private GroupElement toRep(final Representation repr) {
414437
case P2:
415438
return p2(this.curve, this.X, this.Y, this.Z);
416439
case P3:
417-
return p3(this.curve, this.X, this.Y, this.Z, this.T);
440+
return p3(this.curve, this.X, this.Y, this.Z, this.T, false);
418441
case CACHED:
419442
return cached(this.curve, this.Y.add(this.X), this.Y.subtract(this.X), this.Z, this.T.multiply(this.curve.get2D()));
420443
default:
@@ -425,7 +448,9 @@ private GroupElement toRep(final Representation repr) {
425448
case P2:
426449
return p2(this.curve, this.X.multiply(this.T), Y.multiply(this.Z), this.Z.multiply(this.T));
427450
case P3:
428-
return p3(this.curve, this.X.multiply(this.T), Y.multiply(this.Z), this.Z.multiply(this.T), this.X.multiply(this.Y));
451+
return p3(this.curve, this.X.multiply(this.T), Y.multiply(this.Z), this.Z.multiply(this.T), this.X.multiply(this.Y), false);
452+
case P3PrecomputedDouble:
453+
return p3(this.curve, this.X.multiply(this.T), Y.multiply(this.Z), this.Z.multiply(this.T), this.X.multiply(this.Y), true);
429454
case P1P1:
430455
return p1p1(this.curve, this.X, this.Y, this.Z, this.T);
431456
default:
@@ -451,51 +476,47 @@ private GroupElement toRep(final Representation repr) {
451476
}
452477

453478
/**
454-
* Precomputes several tables.
455-
* <p>
456-
* The precomputed tables are used for {@link #scalarMultiply(byte[])}
457-
* and {@link #doubleScalarMultiplyVariableTime(GroupElement, byte[], byte[])}.
458-
*
459-
* @param precomputeSingle should the matrix for scalarMultiply() be precomputed?
479+
* Precomputes table for {@link #scalarMultiply(byte[])}.
460480
*/
461-
public synchronized void precompute(final boolean precomputeSingle) {
462-
GroupElement Bi;
463-
464-
if (precomputeSingle && this.precmp == null) {
465-
// Precomputation for single scalar multiplication.
466-
this.precmp = new GroupElement[32][8];
467-
// TODO-CR BR: check that this == base point when the method is called.
468-
Bi = this;
469-
for (int i = 0; i < 32; i++) {
470-
GroupElement Bij = Bi;
471-
for (int j = 0; j < 8; j++) {
472-
final FieldElement recip = Bij.Z.invert();
473-
final FieldElement x = Bij.X.multiply(recip);
474-
final FieldElement y = Bij.Y.multiply(recip);
475-
this.precmp[i][j] = precomp(this.curve, y.add(x), y.subtract(x), x.multiply(y).multiply(this.curve.get2D()));
476-
Bij = Bij.add(Bi.toCached()).toP3();
477-
}
478-
// Only every second summand is precomputed (16^2 = 256)
479-
for (int k = 0; k < 8; k++) {
480-
Bi = Bi.add(Bi.toCached()).toP3();
481-
}
481+
private GroupElement[][] precomputeSingle() {
482+
// Precomputation for single scalar multiplication.
483+
GroupElement[][] precmp = new GroupElement[32][8];
484+
// TODO-CR BR: check that this == base point when the method is called.
485+
GroupElement Bi = this;
486+
for (int i = 0; i < 32; i++) {
487+
GroupElement Bij = Bi;
488+
for (int j = 0; j < 8; j++) {
489+
final FieldElement recip = Bij.Z.invert();
490+
final FieldElement x = Bij.X.multiply(recip);
491+
final FieldElement y = Bij.Y.multiply(recip);
492+
precmp[i][j] = precomp(this.curve, y.add(x), y.subtract(x), x.multiply(y).multiply(this.curve.get2D()));
493+
Bij = Bij.add(Bi.toCached()).toP3();
494+
}
495+
// Only every second summand is precomputed (16^2 = 256)
496+
for (int k = 0; k < 8; k++) {
497+
Bi = Bi.add(Bi.toCached()).toP3();
482498
}
483499
}
500+
return precmp;
501+
}
484502

503+
/**
504+
* Precomputes table for {@link #doubleScalarMultiplyVariableTime(GroupElement, byte[], byte[])}.
505+
*/
506+
private GroupElement[] precomputeDouble() {
485507
// Precomputation for double scalar multiplication.
486508
// P,3P,5P,7P,9P,11P,13P,15P
487-
if (this.dblPrecmp != null)
488-
return;
489-
this.dblPrecmp = new GroupElement[8];
490-
Bi = this;
509+
GroupElement[] dblPrecmp = new GroupElement[8];
510+
GroupElement Bi = this;
491511
for (int i = 0; i < 8; i++) {
492512
final FieldElement recip = Bi.Z.invert();
493513
final FieldElement x = Bi.X.multiply(recip);
494514
final FieldElement y = Bi.Y.multiply(recip);
495-
this.dblPrecmp[i] = precomp(this.curve, y.add(x), y.subtract(x), x.multiply(y).multiply(this.curve.get2D()));
515+
dblPrecmp[i] = precomp(this.curve, y.add(x), y.subtract(x), x.multiply(y).multiply(this.curve.get2D()));
496516
// Bi = edwards(B,edwards(B,Bi))
497517
Bi = this.add(this.add(Bi.toCached()).toP3().toCached()).toP3();
498518
}
519+
return dblPrecmp;
499520
}
500521

501522
/**
@@ -721,7 +742,7 @@ public GroupElement sub(GroupElement q) {
721742
public GroupElement negate() {
722743
if (this.repr != Representation.P3)
723744
throw new UnsupportedOperationException();
724-
return this.curve.getZero(Representation.P3).sub(toCached()).toP3();
745+
return this.curve.getZero(Representation.P3).sub(toCached()).toP3PrecomputeDouble();
725746
}
726747

727748
@Override
@@ -877,22 +898,20 @@ public GroupElement scalarMultiply(final byte[] a) {
877898
final byte[] e = toRadix16(a);
878899

879900
GroupElement h = this.curve.getZero(Representation.P3);
880-
synchronized(this) {
881-
// TODO: Get opinion from a crypto professional.
882-
// This should in practice never be necessary, the only point that
883-
// this should get called on is EdDSA's B.
884-
//precompute();
885-
for (i = 1; i < 64; i += 2) {
886-
t = select(i/2, e[i]);
887-
h = h.madd(t).toP3();
888-
}
901+
// TODO: Get opinion from a crypto professional.
902+
// This should in practice never be necessary, the only point that
903+
// this should get called on is EdDSA's B.
904+
//precompute();
905+
for (i = 1; i < 64; i += 2) {
906+
t = select(i/2, e[i]);
907+
h = h.madd(t).toP3();
908+
}
889909

890-
h = h.dbl().toP2().dbl().toP2().dbl().toP2().dbl().toP3();
910+
h = h.dbl().toP2().dbl().toP2().dbl().toP2().dbl().toP3();
891911

892-
for (i = 0; i < 64; i += 2) {
893-
t = select(i/2, e[i]);
894-
h = h.madd(t).toP3();
895-
}
912+
for (i = 0; i < 64; i += 2) {
913+
t = select(i/2, e[i]);
914+
h = h.madd(t).toP3();
896915
}
897916

898917
return h;
@@ -969,14 +988,13 @@ public GroupElement doubleScalarMultiplyVariableTime(final GroupElement A, final
969988
if (aslide[i] != 0 || bslide[i] != 0) break;
970989
}
971990

972-
synchronized(this) {
973-
// TODO-CR BR strange comment below.
974-
// TODO: Get opinion from a crypto professional.
975-
// This should in practice never be necessary, the only point that
976-
// this should get called on is EdDSA's B.
977-
//precompute();
978-
for (; i >= 0; --i) {
979-
GroupElement t = r.dbl();
991+
// TODO-CR BR strange comment below.
992+
// TODO: Get opinion from a crypto professional.
993+
// This should in practice never be necessary, the only point that
994+
// this should get called on is EdDSA's B.
995+
//precompute();
996+
for (; i >= 0; --i) {
997+
GroupElement t = r.dbl();
980998

981999
if (aslide[i] > 0) {
9821000
t = t.toP3().madd(A.dblPrecmp[aslide[i]/2]);
@@ -990,8 +1008,7 @@ public GroupElement doubleScalarMultiplyVariableTime(final GroupElement A, final
9901008
t = t.toP3().msub(this.dblPrecmp[(-bslide[i])/2]);
9911009
}
9921010

993-
r = t.toP2();
994-
}
1011+
r = t.toP2();
9951012
}
9961013

9971014
return r;

src/net/i2p/crypto/eddsa/spec/EdDSANamedCurveTable.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*/
1212
package net.i2p.crypto.eddsa.spec;
1313

14-
import java.util.Hashtable;
14+
import java.util.HashMap;
1515
import java.util.Locale;
1616

1717
import net.i2p.crypto.eddsa.Utils;
@@ -46,18 +46,24 @@ public class EdDSANamedCurveTable {
4646
Utils.hexToBytes("5866666666666666666666666666666666666666666666666666666666666666"),
4747
true)); // Precompute tables for B
4848

49-
private static final Hashtable<String, EdDSANamedCurveSpec> curves = new Hashtable<String, EdDSANamedCurveSpec>();
49+
private static volatile HashMap<String, EdDSANamedCurveSpec> curves = new HashMap<String, EdDSANamedCurveSpec>();
50+
51+
private static synchronized void putCurve(String name, EdDSANamedCurveSpec curve) {
52+
HashMap<String, EdDSANamedCurveSpec> newCurves = new HashMap<String, EdDSANamedCurveSpec>(curves);
53+
newCurves.put(name, curve);
54+
curves = newCurves;
55+
}
5056

5157
public static void defineCurve(EdDSANamedCurveSpec curve) {
52-
curves.put(curve.getName().toLowerCase(Locale.ENGLISH), curve);
58+
putCurve(curve.getName().toLowerCase(Locale.ENGLISH), curve);
5359
}
5460

5561
static void defineCurveAlias(String name, String alias) {
5662
EdDSANamedCurveSpec curve = curves.get(name.toLowerCase(Locale.ENGLISH));
5763
if (curve == null) {
5864
throw new IllegalStateException();
5965
}
60-
curves.put(alias.toLowerCase(Locale.ENGLISH), curve);
66+
putCurve(alias.toLowerCase(Locale.ENGLISH), curve);
6167
}
6268

6369
static {

src/net/i2p/crypto/eddsa/spec/EdDSAPublicKeySpec.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,12 @@ public EdDSAPublicKeySpec(byte[] pk, EdDSAParameterSpec spec) {
3636
this.A = new GroupElement(spec.getCurve(), pk);
3737
// Precompute -A for use in verification.
3838
this.Aneg = A.negate();
39-
Aneg.precompute(false);
4039
this.spec = spec;
4140
}
4241

4342
public EdDSAPublicKeySpec(GroupElement A, EdDSAParameterSpec spec) {
4443
this.A = A;
4544
this.Aneg = A.negate();
46-
Aneg.precompute(false);
4745
this.spec = spec;
4846
}
4947

0 commit comments

Comments
 (0)