Browse Source

Implement promotion for ECPoint precomputations

- a point is promoted after repeated use (e.g. in ECDSA verification)
- promoted points are eligible for extra precomp => faster algorithms
Ugochukwu Mmaduekwe 6 years ago
parent
commit
c6eaddb1b8

+ 6 - 0
CryptoLib/src/Include/CryptoLib.inc

@@ -136,6 +136,12 @@
    {$DEFINE SHIFT_OVERFLOW_BUG_FIXED}
 {$IFEND}
 
+  // 10.1 Berlin and Above
+{$IF CompilerVersion >= 31.0}
+   {$DEFINE DELPHI10.1_BERLIN_UP}
+   {$DEFINE HAS_VOLATILE}
+{$IFEND}
+
   // 10.2 Tokyo and Above
 {$IF CompilerVersion >= 32.0}
    {$DEFINE DELPHI10.2_TOKYO_UP}

+ 4 - 0
CryptoLib/src/Include/CryptoLibHelper.inc

@@ -24,3 +24,7 @@
 {$IF FPC_FULLVERSION < 30004}
    {$MESSAGE ERROR 'This Library requires FreePascal 3.0.4 or higher.'}
 {$IFEND}
+
+{$IF FPC_FULLVERSION >= 30301}
+   {$DEFINE HAS_VOLATILE}
+{$IFEND}

+ 9 - 0
CryptoLib/src/Interfaces/ClpIWNafPreCompInfo.pas

@@ -43,6 +43,13 @@ type
     function GetWidth: Int32;
     procedure SetWidth(Value: Int32);
 
+    function GetPromotionCountdown: Int32;
+    procedure SetPromotionCountdown(Value: Int32);
+
+    function DecrementPromotionCountdown: Int32;
+
+    function IsPromoted: Boolean;
+
     property PreComp: TCryptoLibGenericArray<IECPoint> read GetPreComp
       write SetPreComp;
     property PreCompNeg: TCryptoLibGenericArray<IECPoint> read GetPreCompNeg
@@ -51,6 +58,8 @@ type
 
     property ConfWidth: Int32 read GetConfWidth write SetConfWidth;
     property Width: Int32 read GetWidth write SetWidth;
+    property PromotionCountdown: Int32 read GetPromotionCountdown
+      write SetPromotionCountdown;
 
   end;
 

+ 226 - 98
CryptoLib/src/Math/EC/ClpECAlgorithms.pas

@@ -26,6 +26,7 @@ uses
   Math,
   ClpCryptoLibTypes,
   ClpBits,
+  ClpNat,
   ClpBigInteger,
   ClpWNafPreCompInfo,
   ClpIPolynomialExtensionField,
@@ -38,6 +39,7 @@ uses
   ClpIPreCompCallBack,
   ClpFixedPointPreCompInfo,
   ClpIFixedPointPreCompInfo,
+  ClpIFixedPointCombMultiplier,
   ClpIECC,
   ClpECCurveConstants,
   ClpIFiniteField;
@@ -50,6 +52,8 @@ resourcestring
   SInvalidResult = 'Invalid Result';
   SInvalidRange = 'Must be in the Range [2, 16], "width"';
   SInvalidRange2 = 'Must be in the Range [2, 8], "width"';
+  SInvalidComputation =
+    'Fixed-Point Comb Doesn''t Support Scalars Larger Than The Curve Order';
 
 type
   TWNafUtilities = class abstract(TObject)
@@ -405,6 +409,10 @@ type
       const infos: TCryptoLibGenericArray<IWNafPreCompInfo>;
       const wnafs: TCryptoLibMatrixByteArray): IECPoint; overload; static;
 
+    class function ImplShamirsTrickFixedPoint(const p: IECPoint;
+      const k: TBigInteger; const q: IECPoint; const l: TBigInteger)
+      : IECPoint; static;
+
   public
     class function IsF2mCurve(const c: IECCurve): Boolean; static;
     class function IsF2mField(const field: IFiniteField): Boolean; static;
@@ -415,7 +423,7 @@ type
       const ks: TCryptoLibGenericArray<TBigInteger>): IECPoint; static;
 
     class function SumOfTwoMultiplies(const p: IECPoint; const a: TBigInteger;
-      const Q: IECPoint; const b: TBigInteger): IECPoint; static;
+      const q: IECPoint; const b: TBigInteger): IECPoint; static;
 
     // /*
     // * "Shamir's Trick", originally due to E. G. Straus
@@ -436,7 +444,7 @@ type
     // * 9: return R
     // */
     class function ShamirsTrick(const p: IECPoint; const k: TBigInteger;
-      const Q: IECPoint; const l: TBigInteger): IECPoint; static;
+      const q: IECPoint; const l: TBigInteger): IECPoint; static;
 
     class function ImportPoint(const c: IECCurve; const p: IECPoint)
       : IECPoint; static;
@@ -470,10 +478,10 @@ type
       : IECPoint; static;
 
     class function ImplShamirsTrickJsf(const p: IECPoint; const k: TBigInteger;
-      const Q: IECPoint; const l: TBigInteger): IECPoint; static;
+      const q: IECPoint; const l: TBigInteger): IECPoint; static;
 
     class function ImplShamirsTrickWNaf(const p: IECPoint; const k: TBigInteger;
-      const Q: IECPoint; const l: TBigInteger): IECPoint; overload; static;
+      const q: IECPoint; const l: TBigInteger): IECPoint; overload; static;
 
     class function ImplShamirsTrickWNaf(const endomorphism: IECEndomorphism;
       const p: IECPoint; const k, l: TBigInteger): IECPoint; overload; static;
@@ -497,6 +505,9 @@ type
 
 implementation
 
+uses
+  ClpFixedPointCombMultiplier; // included here to avoid circular dependency :)
+
 { TECAlgorithms }
 
 class function TECAlgorithms.ImplCheckResult(const p: IECPoint): IECPoint;
@@ -533,33 +544,124 @@ begin
   result := p;
 end;
 
+class function TECAlgorithms.ImplShamirsTrickFixedPoint(const p: IECPoint;
+  const k: TBigInteger; const q: IECPoint; const l: TBigInteger): IECPoint;
+var
+  c: IECCurve;
+  combSize, widthP, widthQ, width, d, fullComb, i, top, j: Int32;
+  infoP, infoQ: IFixedPointPreCompInfo;
+  lookupTableP, lookupTableQ: IECLookupTable;
+  m: IFixedPointCombMultiplier;
+  r1, r2, R, addP, addQ, t: IECPoint;
+  BigK, BigL: TCryptoLibUInt32Array;
+  secretBitK, secretBitL, secretIndexK, secretIndexL: UInt32;
+begin
+  c := p.Curve;
+  combSize := TFixedPointUtilities.GetCombSize(c);
+
+  if (((k.BitLength) > combSize) or (l.BitLength > combSize)) then
+  begin
+    (*
+      * TODO The comb works best when the scalars are less than the (possibly unknown) order.
+      * Still, if we want to handle larger scalars, we could allow customization of the comb
+      * size, or alternatively we could deal with the 'extra' bits either by running the comb
+      * multiple times as necessary, or by using an alternative multiplier as prelude.
+    *)
+    raise EInvalidOperationCryptoLibException.CreateRes(@SInvalidComputation);
+  end;
+
+  infoP := TFixedPointUtilities.Precompute(p);
+  infoQ := TFixedPointUtilities.Precompute(q);
+
+  lookupTableP := infoP.LookupTable;
+  lookupTableQ := infoQ.LookupTable;
+
+  widthP := infoP.width;
+  widthQ := infoQ.width;
+
+  // TODO This shouldn't normally happen, but a better "solution" is desirable anyway
+  if (widthP <> widthQ) then
+  begin
+    m := TFixedPointCombMultiplier.Create();
+    r1 := m.Multiply(p, k);
+    r2 := m.Multiply(q, l);
+    result := r1.Add(r2);
+    Exit;
+  end;
+
+  width := widthP;
+
+  d := ((combSize + width) - 1) div width;
+
+  R := c.Infinity;
+
+  fullComb := d * width;
+  BigK := TNat.FromBigInteger(fullComb, k);
+  BigL := TNat.FromBigInteger(fullComb, l);
+
+  top := fullComb - 1;
+
+  for i := 0 to System.Pred(d) do
+  begin
+    secretIndexK := 0;
+    secretIndexL := 0;
+
+    j := top - i;
+
+    while j >= 0 do
+    begin
+
+      secretBitK := BigK[TBits.Asr32(j, 5)] shr (j and $1F);
+      secretIndexK := secretIndexK xor (secretBitK shr 1);
+      secretIndexK := secretIndexK shl 1;
+      secretIndexK := secretIndexK xor secretBitK;
+
+      secretBitL := BigL[TBits.Asr32(j, 5)] shr (j and $1F);
+      secretIndexL := secretIndexL xor (secretBitL shr 1);
+      secretIndexL := secretIndexL shl 1;
+      secretIndexL := secretIndexL xor secretBitL;
+
+      System.Dec(j, d);
+    end;
+
+    addP := lookupTableP.Lookup(Int32(secretIndexK));
+    addQ := lookupTableQ.Lookup(Int32(secretIndexL));
+
+    t := addP.Add(addQ);
+
+    R := R.TwicePlus(t);
+  end;
+
+  result := R.Add(infoP.Offset).Add(infoQ.Offset);
+end;
+
 class function TECAlgorithms.ImplShamirsTrickJsf(const p: IECPoint;
-  const k: TBigInteger; const Q: IECPoint; const l: TBigInteger): IECPoint;
+  const k: TBigInteger; const q: IECPoint; const l: TBigInteger): IECPoint;
 var
   Curve: IECCurve;
-  infinity, R: IECPoint;
+  Infinity, R: IECPoint;
   PaddQ, PsubQ: IECPoint;
   points, table: TCryptoLibGenericArray<IECPoint>;
   jsf: TCryptoLibByteArray;
   i, jsfi, kDigit, lDigit, index: Int32;
 begin
   Curve := p.Curve;
-  infinity := Curve.infinity;
+  Infinity := Curve.Infinity;
 
   // TODO conjugate co-Z addition (ZADDC) can return both of these
-  PaddQ := p.Add(Q);
-  PsubQ := p.Subtract(Q);
+  PaddQ := p.Add(q);
+  PsubQ := p.Subtract(q);
 
-  points := TCryptoLibGenericArray<IECPoint>.Create(Q, PsubQ, p, PaddQ);
+  points := TCryptoLibGenericArray<IECPoint>.Create(q, PsubQ, p, PaddQ);
   Curve.NormalizeAll(points);
 
   table := TCryptoLibGenericArray<IECPoint>.Create(points[3].Negate(),
-    points[2].Negate(), points[1].Negate(), points[0].Negate(), infinity,
+    points[2].Negate(), points[1].Negate(), points[0].Negate(), Infinity,
     points[0], points[1], points[2], points[3]);
 
   jsf := TWNafUtilities.GenerateJsf(k, l);
 
-  R := infinity;
+  R := Infinity;
 
   i := System.length(jsf);
   System.Dec(i);
@@ -584,7 +686,7 @@ class function TECAlgorithms.ImplShamirsTrickWNaf(const endomorphism
 var
   negK, negL: Boolean;
   minWidth, widthP, widthQ: Int32;
-  Q: IECPoint;
+  q: IECPoint;
   infoP, infoQ: IWNafPreCompInfo;
   preCompP, preCompQ, preCompNegP, preCompNegQ
     : TCryptoLibGenericArray<IECPoint>;
@@ -602,8 +704,8 @@ begin
   minWidth := TWNafUtilities.GetWindowSize(Max(k.BitLength, l.BitLength), 8);
 
   infoP := TWNafUtilities.Precompute(p, minWidth, true);
-  Q := TEndoUtilities.MapPoint(endomorphism, p);
-  infoQ := TWNafUtilities.PrecomputeWithPointMap(Q, endomorphism.pointMap,
+  q := TEndoUtilities.MapPoint(endomorphism, p);
+  infoQ := TWNafUtilities.PrecomputeWithPointMap(q, endomorphism.pointMap,
     infoP, true);
 
   widthP := Min(8, infoP.width);
@@ -651,29 +753,41 @@ begin
 end;
 
 class function TECAlgorithms.ImplShamirsTrickWNaf(const p: IECPoint;
-  const k: TBigInteger; const Q: IECPoint; const l: TBigInteger): IECPoint;
+  const k: TBigInteger; const q: IECPoint; const l: TBigInteger): IECPoint;
 var
   negK, negL: Boolean;
-  minWidthP, minWidthQ, widthP, widthQ: Int32;
+  minWidthP, minWidthQ, widthP, widthQ, combSize: Int32;
   infoP, infoQ: IWNafPreCompInfo;
   preCompP, preCompQ, preCompNegP, preCompNegQ
     : TCryptoLibGenericArray<IECPoint>;
   wnafP, wnafQ: TCryptoLibByteArray;
-  LK, LL: TBigInteger;
+  kAbs, lAbs: TBigInteger;
+  c: IECCurve;
 begin
-  LK := k;
-  LL := l;
-  negK := LK.SignValue < 0;
-  negL := LL.SignValue < 0;
 
-  LK := LK.Abs();
-  LL := LL.Abs();
+  negK := k.SignValue < 0;
+  negL := l.SignValue < 0;
+
+  kAbs := k.Abs();
+  lAbs := l.Abs();
 
-  minWidthP := TWNafUtilities.GetWindowSize(k.BitLength, 8);
-  minWidthQ := TWNafUtilities.GetWindowSize(l.BitLength, 8);
+  minWidthP := TWNafUtilities.GetWindowSize(kAbs.BitLength, 8);
+  minWidthQ := TWNafUtilities.GetWindowSize(lAbs.BitLength, 8);
 
   infoP := TWNafUtilities.Precompute(p, minWidthP, true);
-  infoQ := TWNafUtilities.Precompute(Q, minWidthQ, true);
+  infoQ := TWNafUtilities.Precompute(q, minWidthQ, true);
+
+  // When P, Q are 'promoted' (i.e. reused several times), switch to fixed-point algorithm
+
+  c := p.Curve;
+  combSize := TFixedPointUtilities.GetCombSize(c);
+  if ((not negK) and (not negL) and (k.BitLength <= combSize) and
+    (l.BitLength <= combSize) and (infoP.IsPromoted) and (infoQ.IsPromoted))
+  then
+  begin
+    result := ImplShamirsTrickFixedPoint(p, k, q, l);
+    Exit;
+  end;
 
   widthP := Min(8, infoP.width);
   widthQ := Min(8, infoQ.width);
@@ -714,8 +828,8 @@ begin
     preCompNegQ := infoQ.PreCompNeg
   end;
 
-  wnafP := TWNafUtilities.GenerateWindowNaf(widthP, LK);
-  wnafQ := TWNafUtilities.GenerateWindowNaf(widthQ, LL);
+  wnafP := TWNafUtilities.GenerateWindowNaf(widthP, kAbs);
+  wnafQ := TWNafUtilities.GenerateWindowNaf(widthQ, lAbs);
 
   result := ImplShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ,
     preCompNegQ, wnafQ);
@@ -733,15 +847,15 @@ class function TECAlgorithms.ImplShamirsTrickWNaf(const preCompP,
 var
   len, zeroes, i, wiP, wiQ, nP, nQ: Int32;
   Curve: IECCurve;
-  infinity, R, point: IECPoint;
+  Infinity, R, point: IECPoint;
   tableP, tableQ: TCryptoLibGenericArray<IECPoint>;
 begin
   len := Max(System.length(wnafP), System.length(wnafQ));
 
   Curve := preCompP[0].Curve;
-  infinity := Curve.infinity;
+  Infinity := Curve.Infinity;
 
-  R := infinity;
+  R := Infinity;
   zeroes := 0;
 
   i := len - 1;
@@ -773,7 +887,7 @@ begin
       continue;
     end;
 
-    point := infinity;
+    point := Infinity;
     if (wiP <> 0) then
     begin
       nP := System.Abs(wiP);
@@ -834,7 +948,7 @@ var
   wnafs: TCryptoLibMatrixByteArray;
   i, j0, j1, minWidth, widthP, widthQ: Int32;
   kj0, kj1: TBigInteger;
-  p, Q: IECPoint;
+  p, q: IECPoint;
   pointMap: IECPointMap;
 begin
   halfCount := System.length(ps);
@@ -862,8 +976,8 @@ begin
 
     p := ps[i];
     infoP := TWNafUtilities.Precompute(p, minWidth, true);
-    Q := TEndoUtilities.MapPoint(endomorphism, p);
-    infoQ := TWNafUtilities.PrecomputeWithPointMap(Q, pointMap, infoP, true);
+    q := TEndoUtilities.MapPoint(endomorphism, p);
+    infoQ := TWNafUtilities.PrecomputeWithPointMap(q, pointMap, infoP, true);
 
     widthP := Min(8, infoP.width);
     widthQ := Min(8, infoQ.width);
@@ -932,9 +1046,9 @@ class function TECAlgorithms.ImplSumOfMultiplies
   const wnafs: TCryptoLibMatrixByteArray): IECPoint;
 var
   len, count, zeroes: Int32;
-  i, J, wi, n: Int32;
+  i, j, wi, n: Int32;
   Curve: IECCurve;
-  infinity, R, point: IECPoint;
+  Infinity, R, point: IECPoint;
   wnaf: TCryptoLibByteArray;
   info: IWNafPreCompInfo;
   table: TCryptoLibGenericArray<IECPoint>;
@@ -948,19 +1062,19 @@ begin
   end;
 
   Curve := infos[0].PreComp[0].Curve;
-  infinity := Curve.infinity;
+  Infinity := Curve.Infinity;
 
-  R := infinity;
+  R := Infinity;
   zeroes := 0;
 
   i := len - 1;
   while (i >= 0) do
   begin
-    point := infinity;
+    point := Infinity;
 
-    for J := 0 to System.Pred(count) do
+    for j := 0 to System.Pred(count) do
     begin
-      wnaf := wnafs[J];
+      wnaf := wnafs[j];
       if i < System.length(wnaf) then
       begin
         wi := Int32(ShortInt(wnaf[i]));
@@ -973,8 +1087,8 @@ begin
       if (wi <> 0) then
       begin
         n := System.Abs(wi);
-        info := infos[J];
-        if (wi < 0 = negs[J]) then
+        info := infos[j];
+        if (wi < 0 = negs[j]) then
         begin
           table := info.PreComp;
         end
@@ -987,7 +1101,7 @@ begin
       end;
     end;
 
-    if (point = infinity) then
+    if (point = Infinity) then
     begin
       System.Inc(zeroes);
       System.Dec(i);
@@ -1020,10 +1134,10 @@ class function TECAlgorithms.ImplSumOfMultipliesGlv
   const glvEndomorphism: IGlvEndomorphism): IECPoint;
 var
   n: TBigInteger;
-  len, i, J: Int32;
+  len, i, j: Int32;
   &abs, ab: TCryptoLibGenericArray<TBigInteger>;
   pqs: TCryptoLibGenericArray<IECPoint>;
-  p, Q: IECPoint;
+  p, q: IECPoint;
 begin
   n := ps[0].Curve.Order;
 
@@ -1032,16 +1146,16 @@ begin
   System.SetLength(Abs, len shl 1);
 
   i := 0;
-  J := 0;
+  j := 0;
 
   while (i < len) do
   begin
     ab := glvEndomorphism.DecomposeScalar(ks[i].&Mod(n));
 
-    Abs[J] := ab[0];
-    System.Inc(J);
-    Abs[J] := ab[1];
-    System.Inc(J);
+    Abs[j] := ab[0];
+    System.Inc(j);
+    Abs[j] := ab[1];
+    System.Inc(j);
     System.Inc(i);
   end;
 
@@ -1054,17 +1168,17 @@ begin
   System.SetLength(pqs, len shl 1);
 
   i := 0;
-  J := 0;
+  j := 0;
 
   while (i < len) do
   begin
     p := ps[i];
-    Q := TEndoUtilities.MapPoint(glvEndomorphism, p);
+    q := TEndoUtilities.MapPoint(glvEndomorphism, p);
 
-    pqs[J] := p;
-    System.Inc(J);
-    pqs[J] := Q;
-    System.Inc(J);
+    pqs[j] := p;
+    System.Inc(j);
+    pqs[j] := q;
+    System.Inc(j);
     System.Inc(i);
   end;
 
@@ -1112,7 +1226,7 @@ class procedure TECAlgorithms.MontgomeryTrick
   const scale: IECFieldElement);
 var
   c: TCryptoLibGenericArray<IECFieldElement>;
-  i, J: Int32;
+  i, j: Int32;
   u, tmp: IECFieldElement;
 begin
   // /*
@@ -1144,10 +1258,10 @@ begin
 
   while (i > 0) do
   begin
-    J := off + i;
+    j := off + i;
     System.Dec(i);
-    tmp := zs[J];
-    zs[J] := c[i].Multiply(u);
+    tmp := zs[j];
+    zs[j] := c[i].Multiply(u);
     u := u.Multiply(tmp);
   end;
 
@@ -1164,18 +1278,18 @@ class function TECAlgorithms.ReferenceMultiply(const p: IECPoint;
   const k: TBigInteger): IECPoint;
 var
   x: TBigInteger;
-  Q, LP: IECPoint;
+  q, LP: IECPoint;
   t, i: Int32;
 begin
   LP := p;
   x := k.Abs();
-  Q := LP.Curve.infinity;
+  q := LP.Curve.Infinity;
   t := x.BitLength;
   if (t > 0) then
   begin
     if (x.TestBit(0)) then
     begin
-      Q := LP;
+      q := LP;
     end;
     i := 1;
     while (i < t) do
@@ -1183,7 +1297,7 @@ begin
       LP := LP.Twice();
       if (x.TestBit(i)) then
       begin
-        Q := Q.Add(LP);
+        q := q.Add(LP);
       end;
       System.Inc(i);
     end;
@@ -1192,23 +1306,23 @@ begin
 
   if k.SignValue < 0 then
   begin
-    result := Q.Negate();
+    result := q.Negate();
   end
   else
   begin
-    result := Q;
+    result := q;
   end;
 
 end;
 
 class function TECAlgorithms.ShamirsTrick(const p: IECPoint;
-  const k: TBigInteger; const Q: IECPoint; const l: TBigInteger): IECPoint;
+  const k: TBigInteger; const q: IECPoint; const l: TBigInteger): IECPoint;
 var
   cp: IECCurve;
   LQ: IECPoint;
 begin
   cp := p.Curve;
-  LQ := Q;
+  LQ := q;
   LQ := ImportPoint(cp, LQ);
 
   result := ImplCheckResult(ImplShamirsTrickJsf(p, k, LQ, l));
@@ -1269,7 +1383,7 @@ begin
 end;
 
 class function TECAlgorithms.SumOfTwoMultiplies(const p: IECPoint;
-  const a: TBigInteger; const Q: IECPoint; const b: TBigInteger): IECPoint;
+  const a: TBigInteger; const q: IECPoint; const b: TBigInteger): IECPoint;
 var
   cp: IECCurve;
   f2mCurve: IAbstractF2mCurve;
@@ -1277,7 +1391,7 @@ var
   LQ: IECPoint;
 begin
   cp := p.Curve;
-  LQ := Q;
+  LQ := q;
   LQ := ImportPoint(cp, LQ);
 
   // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick
@@ -1549,7 +1663,7 @@ end;
 class function TWNafUtilities.GenerateJsf(const g, h: TBigInteger)
   : TCryptoLibByteArray;
 var
-  digits, J, d0, d1, offset, n0, n1, u0, u1: Int32;
+  digits, j, d0, d1, Offset, n0, n1, u0, u1: Int32;
   jsf: TCryptoLibByteArray;
   k0, k1: TBigInteger;
 begin
@@ -1559,17 +1673,17 @@ begin
 
   k0 := g;
   k1 := h;
-  J := 0;
+  j := 0;
   d0 := 0;
   d1 := 0;
 
-  offset := 0;
+  Offset := 0;
 
-  while (((d0 or d1) <> 0) or (k0.BitLength > offset) or
-    (k1.BitLength > offset)) do
+  while (((d0 or d1) <> 0) or (k0.BitLength > Offset) or
+    (k1.BitLength > Offset)) do
   begin
-    n0 := (Int32(UInt32(k0.Int32Value) shr offset) + d0) and 7;
-    n1 := (Int32(UInt32(k1.Int32Value) shr offset) + d1) and 7;
+    n0 := (Int32(UInt32(k0.Int32Value) shr Offset) + d0) and 7;
+    n1 := (Int32(UInt32(k1.Int32Value) shr Offset) + d1) and 7;
 
     u0 := n0 and 1;
     if (u0 <> 0) then
@@ -1600,22 +1714,22 @@ begin
       d1 := d1 xor 1;
     end;
 
-    System.Inc(offset);
-    if (offset = 30) then
+    System.Inc(Offset);
+    if (Offset = 30) then
     begin
-      offset := 0;
+      Offset := 0;
       k0 := k0.ShiftRight(30);
       k1 := k1.ShiftRight(30);
     end;
 
-    jsf[J] := Byte((u0 shl 4) or (u1 and $F));
-    System.Inc(J);
+    jsf[j] := Byte((u0 shl 4) or (u1 and $F));
+    System.Inc(j);
   end;
 
   // Reduce the JSF array to its actual length
-  if (System.length(jsf) > J) then
+  if (System.length(jsf) > j) then
   begin
-    jsf := Trim(jsf, J);
+    jsf := Trim(jsf, j);
   end;
 
   result := jsf;
@@ -1890,7 +2004,8 @@ var
   c: IECCurve;
   PreComp, PreCompNeg, EMPTY_POINTS: TCryptoLibGenericArray<IECPoint>;
   tempRes, existingWNaf: IWNafPreCompInfo;
-  reqPreCompLen, iniPreCompLen, curPreCompLen, pos, width: Int32;
+  reqPreCompLen, iniPreCompLen, curPreCompLen, pos, width, PromotionCountdown,
+    ConfWidth: Int32;
   iso, iso2, iso3: IECFieldElement;
 begin
   c := Fm_p.Curve;
@@ -1902,6 +2017,7 @@ begin
 
   if (CheckExisting(existingWNaf, width, reqPreCompLen, Fm_includeNegated)) then
   begin
+    existingWNaf.DecrementPromotionCountdown;
     result := existingWNaf;
     Exit;
   end;
@@ -1910,6 +2026,13 @@ begin
 
   if (existingWNaf <> Nil) then
   begin
+
+    PromotionCountdown := existingWNaf.DecrementPromotionCountdown;
+    tempRes.PromotionCountdown := PromotionCountdown;
+
+    ConfWidth := existingWNaf.ConfWidth;
+    tempRes.ConfWidth := ConfWidth;
+
     PreComp := existingWNaf.PreComp;
     PreCompNeg := existingWNaf.PreCompNeg;
     twiceP := existingWNaf.Twice;
@@ -2074,12 +2197,14 @@ begin
 
   if ((existingWNaf <> Nil) and (existingWNaf.ConfWidth = FConfWidth)) then
   begin
+    existingWNaf.PromotionCountdown := 0;
     result := existingWNaf;
     Exit;
   end;
 
   tempResult := TWNafPreCompInfo.Create();
 
+  tempResult.PromotionCountdown := 0;
   tempResult.ConfWidth := FConfWidth;
 
   if (existingWNaf <> Nil) then
@@ -2143,6 +2268,7 @@ begin
 
   if (CheckExisting(existingWNaf, width, reqPreCompLen, FIncludeNegated)) then
   begin
+    existingWNaf.DecrementPromotionCountdown;
     result := existingWNaf;
     Exit;
   end;
@@ -2151,7 +2277,9 @@ begin
     * TODO Ideally this method would support incremental calculation, but given the
     * existing use-cases it would be of little-to-no benefit.
   *)
-  tempResult := TWNafPreCompInfo.Create() as IWNafPreCompInfo;
+  tempResult := TWNafPreCompInfo.Create();
+
+  tempResult.PromotionCountdown := FfromWNaf.PromotionCountdown;
 
   twiceFrom := FfromWNaf.Twice;
   if (twiceFrom <> Nil) then
@@ -2301,7 +2429,7 @@ end;
 class function TFixedPointUtilities.TFixedPointCallback.CheckExisting
   (const existingFP: IFixedPointPreCompInfo; n: Int32): Boolean;
 begin
-  result := (existingFP <> Nil) and CheckTable(existingFP.LookUpTable, n);
+  result := (existingFP <> Nil) and CheckTable(existingFP.LookupTable, n);
 end;
 
 class function TFixedPointUtilities.GetCombSize(const c: IECCurve): Int32;
@@ -2349,7 +2477,7 @@ function TFixedPointUtilities.TFixedPointCallback.Precompute(const existing
 var
   bit, bits, minWidth, n, d, i, step: Int32;
   existingFP: IFixedPointPreCompInfo;
-  pow2Table, LookUpTable: TCryptoLibGenericArray<IECPoint>;
+  pow2Table, LookupTable: TCryptoLibGenericArray<IECPoint>;
   pow2: IECPoint;
   c: IECCurve;
   tempResult: IFixedPointPreCompInfo;
@@ -2396,8 +2524,8 @@ begin
 
   c.NormalizeAll(pow2Table);
 
-  System.SetLength(LookUpTable, n);
-  LookUpTable[0] := pow2Table[0];
+  System.SetLength(LookupTable, n);
+  LookupTable[0] := pow2Table[0];
 
   bit := minWidth - 1;
   while bit >= 0 do
@@ -2410,7 +2538,7 @@ begin
 
     while i < n do
     begin
-      LookUpTable[i] := LookUpTable[i - step].Add(pow2);
+      LookupTable[i] := LookupTable[i - step].Add(pow2);
 
       System.Inc(i, step shl 1);
     end;
@@ -2418,12 +2546,12 @@ begin
     System.Dec(bit);
   end;
 
-  c.NormalizeAll(LookUpTable);
+  c.NormalizeAll(LookupTable);
 
   tempResult := TFixedPointPreCompInfo.Create();
-  tempResult.LookUpTable := c.CreateCacheSafeLookupTable(LookUpTable, 0,
-    System.length(LookUpTable));
-  tempResult.offset := pow2Table[minWidth];
+  tempResult.LookupTable := c.CreateCacheSafeLookupTable(LookupTable, 0,
+    System.length(LookupTable));
+  tempResult.Offset := pow2Table[minWidth];
   tempResult.width := minWidth;
   result := tempResult;
 end;

+ 1 - 1
CryptoLib/src/Math/EC/Multiplier/ClpFixedPointCombMultiplier.pas

@@ -25,9 +25,9 @@ uses
   ClpBits,
   ClpBigInteger,
   ClpNat,
+  ClpECAlgorithms,
   ClpCryptoLibTypes,
   ClpIECC,
-  ClpECAlgorithms,
   ClpIFixedPointPreCompInfo,
   ClpAbstractECMultiplier,
   ClpIFixedPointCombMultiplier;

+ 65 - 19
CryptoLib/src/Math/EC/Multiplier/ClpWNafPreCompInfo.pas

@@ -33,24 +33,10 @@ type
   /// Class holding precomputation data for the WNAF (Window Non-Adjacent
   /// Form) algorithm.
   /// </summary>
-  TWNafPreCompInfo = class(TInterfacedObject, IPreCompInfo, IWNafPreCompInfo)
+  TWNafPreCompInfo = class sealed(TInterfacedObject, IPreCompInfo,
+    IWNafPreCompInfo)
 
   strict private
-    function GetPreComp: TCryptoLibGenericArray<IECPoint>; virtual;
-    procedure SetPreComp(const Value
-      : TCryptoLibGenericArray<IECPoint>); virtual;
-    function GetPreCompNeg: TCryptoLibGenericArray<IECPoint>; virtual;
-    procedure SetPreCompNeg(const Value
-      : TCryptoLibGenericArray<IECPoint>); virtual;
-    function GetTwice: IECPoint; virtual;
-    procedure SetTwice(const Value: IECPoint); virtual;
-
-    function GetConfWidth: Int32; virtual;
-    procedure SetConfWidth(Value: Int32); virtual;
-
-    function GetWidth: Int32; virtual;
-    procedure SetWidth(Value: Int32); virtual;
-  strict protected
   var
     /// <summary>
     /// Array holding the precomputed <c>ECPoint</c>s used for a Window NAF
@@ -72,10 +58,35 @@ type
 
     FConfWidth, FWidth: Int32;
 
+{$IFNDEF FPC}
+{$IFDEF HAS_VOLATILE}[volatile]
+{$ENDIF}
+{$ENDIF}
+    FPromotionCountdown: Int32;
+
+    function GetPreComp: TCryptoLibGenericArray<IECPoint>; inline;
+    procedure SetPreComp(const Value: TCryptoLibGenericArray<IECPoint>); inline;
+    function GetPreCompNeg: TCryptoLibGenericArray<IECPoint>; inline;
+    procedure SetPreCompNeg(const Value
+      : TCryptoLibGenericArray<IECPoint>); inline;
+    function GetTwice: IECPoint; inline;
+    procedure SetTwice(const Value: IECPoint); inline;
+
+    function GetConfWidth: Int32; inline;
+    procedure SetConfWidth(Value: Int32); inline;
+
+    function GetWidth: Int32; inline;
+    procedure SetWidth(Value: Int32); inline;
+
+    function GetPromotionCountdown: Int32; inline;
+    procedure SetPromotionCountdown(Value: Int32); inline;
+
+    function DecrementPromotionCountdown: Int32; inline;
+    function IsPromoted: Boolean; inline;
+
   public
 
     constructor Create();
-    destructor Destroy; override;
     property PreComp: TCryptoLibGenericArray<IECPoint> read GetPreComp
       write SetPreComp;
     property PreCompNeg: TCryptoLibGenericArray<IECPoint> read GetPreCompNeg
@@ -85,6 +96,9 @@ type
     property ConfWidth: Int32 read GetConfWidth write SetConfWidth;
     property Width: Int32 read GetWidth write SetWidth;
 
+    property PromotionCountdown: Int32 read GetPromotionCountdown
+      write SetPromotionCountdown;
+
   end;
 
 implementation
@@ -96,11 +110,20 @@ begin
   inherited Create();
   FConfWidth := -1;
   FWidth := -1;
+  FPromotionCountdown := 4;
 end;
 
-destructor TWNafPreCompInfo.Destroy;
+function TWNafPreCompInfo.DecrementPromotionCountdown: Int32;
+var
+  t: Int32;
 begin
-  inherited Destroy;
+  t := PromotionCountdown;
+  if (t > 0) then
+  begin
+    System.Dec(t);
+    PromotionCountdown := t;
+  end;
+  result := t;
 end;
 
 function TWNafPreCompInfo.GetConfWidth: Int32;
@@ -118,6 +141,15 @@ begin
   result := FPreCompNeg;
 end;
 
+function TWNafPreCompInfo.GetPromotionCountdown: Int32;
+begin
+{$IFDEF FPC}
+  result := {$IFDEF HAS_VOLATILE}volatile{$ENDIF}(FPromotionCountdown);
+{$ELSE}
+    result := FPromotionCountdown;
+{$ENDIF}
+end;
+
 function TWNafPreCompInfo.GetTwice: IECPoint;
 begin
   result := FTwice;
@@ -128,6 +160,11 @@ begin
   result := FWidth;
 end;
 
+function TWNafPreCompInfo.IsPromoted: Boolean;
+begin
+  result := PromotionCountdown <= 0;
+end;
+
 procedure TWNafPreCompInfo.SetConfWidth(Value: Int32);
 begin
   FConfWidth := Value;
@@ -145,6 +182,15 @@ begin
   FPreCompNeg := Value;
 end;
 
+procedure TWNafPreCompInfo.SetPromotionCountdown(Value: Int32);
+begin
+{$IFDEF FPC}
+  FPromotionCountdown := {$IFDEF HAS_VOLATILE}volatile{$ENDIF}(Value);
+{$ELSE}
+    FPromotionCountdown := Value;
+{$ENDIF}
+end;
+
 procedure TWNafPreCompInfo.SetTwice(const Value: IECPoint);
 begin
   FTwice := Value;