@@ -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 ;
0 commit comments