using System;
using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Math.EC.Multiplier
{
public abstract class WNafUtilities
{
public static readonly string PRECOMP_NAME = "bc_wnaf";
private static readonly int[] DEFAULT_WINDOW_SIZE_CUTOFFS = new int[]{ 13, 41, 121, 337, 897, 2305 };
private static readonly int MAX_WIDTH = 16;
private static readonly ECPoint[] EMPTY_POINTS = new ECPoint[0];
public static void ConfigureBasepoint(ECPoint p)
{
ECCurve c = p.Curve;
if (null == c)
return;
BigInteger n = c.Order;
int bits = (null == n) ? c.FieldSize + 1 : n.BitLength;
int confWidth = System.Math.Min(MAX_WIDTH, GetWindowSize(bits) + 3);
c.Precompute(p, PRECOMP_NAME, new ConfigureBasepointCallback(c, confWidth));
}
public static int[] GenerateCompactNaf(BigInteger k)
{
if ((k.BitLength >> 16) != 0)
throw new ArgumentException("must have bitlength < 2^16", "k");
if (k.SignValue == 0)
return Arrays.EmptyInts;
BigInteger _3k = k.ShiftLeft(1).Add(k);
int bits = _3k.BitLength;
int[] naf = new int[bits >> 1];
BigInteger diff = _3k.Xor(k);
int highBit = bits - 1, length = 0, zeroes = 0;
for (int i = 1; i < highBit; ++i)
{
if (!diff.TestBit(i))
{
++zeroes;
continue;
}
int digit = k.TestBit(i) ? -1 : 1;
naf[length++] = (digit << 16) | zeroes;
zeroes = 1;
++i;
}
naf[length++] = (1 << 16) | zeroes;
if (naf.Length > length)
{
naf = Trim(naf, length);
}
return naf;
}
public static int[] GenerateCompactWindowNaf(int width, BigInteger k)
{
if (width == 2)
{
return GenerateCompactNaf(k);
}
if (width < 2 || width > 16)
throw new ArgumentException("must be in the range [2, 16]", "width");
if ((k.BitLength >> 16) != 0)
throw new ArgumentException("must have bitlength < 2^16", "k");
if (k.SignValue == 0)
return Arrays.EmptyInts;
int[] wnaf = new int[k.BitLength / width + 1];
// 2^width and a mask and sign bit set accordingly
int pow2 = 1 << width;
int mask = pow2 - 1;
int sign = pow2 >> 1;
bool carry = false;
int length = 0, pos = 0;
while (pos <= k.BitLength)
{
if (k.TestBit(pos) == carry)
{
++pos;
continue;
}
k = k.ShiftRight(pos);
int digit = k.IntValue & mask;
if (carry)
{
++digit;
}
carry = (digit & sign) != 0;
if (carry)
{
digit -= pow2;
}
int zeroes = length > 0 ? pos - 1 : pos;
wnaf[length++] = (digit << 16) | zeroes;
pos = width;
}
// Reduce the WNAF array to its actual length
if (wnaf.Length > length)
{
wnaf = Trim(wnaf, length);
}
return wnaf;
}
public static byte[] GenerateJsf(BigInteger g, BigInteger h)
{
int digits = System.Math.Max(g.BitLength, h.BitLength) + 1;
byte[] jsf = new byte[digits];
BigInteger k0 = g, k1 = h;
int j = 0, d0 = 0, d1 = 0;
int offset = 0;
while ((d0 | d1) != 0 || k0.BitLength > offset || k1.BitLength > offset)
{
int n0 = ((int)((uint)k0.IntValue >> offset) + d0) & 7;
int n1 = ((int)((uint)k1.IntValue >> offset) + d1) & 7;
int u0 = n0 & 1;
if (u0 != 0)
{
u0 -= (n0 & 2);
if ((n0 + u0) == 4 && (n1 & 3) == 2)
{
u0 = -u0;
}
}
int u1 = n1 & 1;
if (u1 != 0)
{
u1 -= (n1 & 2);
if ((n1 + u1) == 4 && (n0 & 3) == 2)
{
u1 = -u1;
}
}
if ((d0 << 1) == 1 + u0)
{
d0 ^= 1;
}
if ((d1 << 1) == 1 + u1)
{
d1 ^= 1;
}
if (++offset == 30)
{
offset = 0;
k0 = k0.ShiftRight(30);
k1 = k1.ShiftRight(30);
}
jsf[j++] = (byte)((u0 << 4) | (u1 & 0xF));
}
// Reduce the JSF array to its actual length
if (jsf.Length > j)
{
jsf = Trim(jsf, j);
}
return jsf;
}
public static byte[] GenerateNaf(BigInteger k)
{
if (k.SignValue == 0)
return Arrays.EmptyBytes;
BigInteger _3k = k.ShiftLeft(1).Add(k);
int digits = _3k.BitLength - 1;
byte[] naf = new byte[digits];
BigInteger diff = _3k.Xor(k);
for (int i = 1; i < digits; ++i)
{
if (diff.TestBit(i))
{
naf[i - 1] = (byte)(k.TestBit(i) ? -1 : 1);
++i;
}
}
naf[digits - 1] = 1;
return naf;
}
/**
* Computes the Window NAF (non-adjacent Form) of an integer.
* @param width The width w
of the Window NAF. The width is
* defined as the minimal number w
, such that for any
* w
consecutive digits in the resulting representation, at
* most one is non-zero.
* @param k The integer of which the Window NAF is computed.
* @return The Window NAF of the given width, such that the following holds:
* k = ∑i=0l-1 ki2i
*
, where the ki
denote the elements of the
* returned byte[]
.
*/
public static byte[] GenerateWindowNaf(int width, BigInteger k)
{
if (width == 2)
{
return GenerateNaf(k);
}
if (width < 2 || width > 8)
throw new ArgumentException("must be in the range [2, 8]", "width");
if (k.SignValue == 0)
return Arrays.EmptyBytes;
byte[] wnaf = new byte[k.BitLength + 1];
// 2^width and a mask and sign bit set accordingly
int pow2 = 1 << width;
int mask = pow2 - 1;
int sign = pow2 >> 1;
bool carry = false;
int length = 0, pos = 0;
while (pos <= k.BitLength)
{
if (k.TestBit(pos) == carry)
{
++pos;
continue;
}
k = k.ShiftRight(pos);
int digit = k.IntValue & mask;
if (carry)
{
++digit;
}
carry = (digit & sign) != 0;
if (carry)
{
digit -= pow2;
}
length += (length > 0) ? pos - 1 : pos;
wnaf[length++] = (byte)digit;
pos = width;
}
// Reduce the WNAF array to its actual length
if (wnaf.Length > length)
{
wnaf = Trim(wnaf, length);
}
return wnaf;
}
public static int GetNafWeight(BigInteger k)
{
if (k.SignValue == 0)
return 0;
BigInteger _3k = k.ShiftLeft(1).Add(k);
BigInteger diff = _3k.Xor(k);
return diff.BitCount;
}
public static WNafPreCompInfo GetWNafPreCompInfo(ECPoint p)
{
return GetWNafPreCompInfo(p.Curve.GetPreCompInfo(p, PRECOMP_NAME));
}
public static WNafPreCompInfo GetWNafPreCompInfo(PreCompInfo preCompInfo)
{
return preCompInfo as WNafPreCompInfo;
}
/**
* Determine window width to use for a scalar multiplication of the given size.
*
* @param bits the bit-length of the scalar to multiply by
* @return the window size to use
*/
public static int GetWindowSize(int bits)
{
return GetWindowSize(bits, DEFAULT_WINDOW_SIZE_CUTOFFS, MAX_WIDTH);
}
/**
* Determine window width to use for a scalar multiplication of the given size.
*
* @param bits the bit-length of the scalar to multiply by
* @param maxWidth the maximum window width to return
* @return the window size to use
*/
public static int GetWindowSize(int bits, int maxWidth)
{
return GetWindowSize(bits, DEFAULT_WINDOW_SIZE_CUTOFFS, maxWidth);
}
/**
* Determine window width to use for a scalar multiplication of the given size.
*
* @param bits the bit-length of the scalar to multiply by
* @param windowSizeCutoffs a monotonically increasing list of bit sizes at which to increment the window width
* @return the window size to use
*/
public static int GetWindowSize(int bits, int[] windowSizeCutoffs)
{
return GetWindowSize(bits, windowSizeCutoffs, MAX_WIDTH);
}
/**
* Determine window width to use for a scalar multiplication of the given size.
*
* @param bits the bit-length of the scalar to multiply by
* @param windowSizeCutoffs a monotonically increasing list of bit sizes at which to increment the window width
* @param maxWidth the maximum window width to return
* @return the window size to use
*/
public static int GetWindowSize(int bits, int[] windowSizeCutoffs, int maxWidth)
{
int w = 0;
for (; w < windowSizeCutoffs.Length; ++w)
{
if (bits < windowSizeCutoffs[w])
{
break;
}
}
return System.Math.Max(2, System.Math.Min(maxWidth, w + 2));
}
public static WNafPreCompInfo Precompute(ECPoint p, int minWidth, bool includeNegated)
{
return (WNafPreCompInfo)p.Curve.Precompute(p, PRECOMP_NAME,
new PrecomputeCallback(p, minWidth, includeNegated));
}
public static WNafPreCompInfo PrecomputeWithPointMap(ECPoint p, ECPointMap pointMap, WNafPreCompInfo fromWNaf,
bool includeNegated)
{
return (WNafPreCompInfo)p.Curve.Precompute(p, PRECOMP_NAME,
new PrecomputeWithPointMapCallback(p, pointMap, fromWNaf, includeNegated));
}
private static byte[] Trim(byte[] a, int length)
{
byte[] result = new byte[length];
Array.Copy(a, 0, result, 0, result.Length);
return result;
}
private static int[] Trim(int[] a, int length)
{
int[] result = new int[length];
Array.Copy(a, 0, result, 0, result.Length);
return result;
}
private static ECPoint[] ResizeTable(ECPoint[] a, int length)
{
ECPoint[] result = new ECPoint[length];
Array.Copy(a, 0, result, 0, a.Length);
return result;
}
private class ConfigureBasepointCallback
: IPreCompCallback
{
private readonly ECCurve m_curve;
private readonly int m_confWidth;
internal ConfigureBasepointCallback(ECCurve curve, int confWidth)
{
this.m_curve = curve;
this.m_confWidth = confWidth;
}
public PreCompInfo Precompute(PreCompInfo existing)
{
WNafPreCompInfo existingWNaf = existing as WNafPreCompInfo;
if (null != existingWNaf && existingWNaf.ConfWidth == m_confWidth)
{
existingWNaf.PromotionCountdown = 0;
return existingWNaf;
}
WNafPreCompInfo result = new WNafPreCompInfo();
result.PromotionCountdown = 0;
result.ConfWidth = m_confWidth;
if (null != existingWNaf)
{
result.PreComp = existingWNaf.PreComp;
result.PreCompNeg = existingWNaf.PreCompNeg;
result.Twice = existingWNaf.Twice;
result.Width = existingWNaf.Width;
}
return result;
}
}
private class MapPointCallback
: IPreCompCallback
{
private readonly WNafPreCompInfo m_infoP;
private readonly bool m_includeNegated;
private readonly ECPointMap m_pointMap;
internal MapPointCallback(WNafPreCompInfo infoP, bool includeNegated, ECPointMap pointMap)
{
this.m_infoP = infoP;
this.m_includeNegated = includeNegated;
this.m_pointMap = pointMap;
}
public PreCompInfo Precompute(PreCompInfo existing)
{
WNafPreCompInfo result = new WNafPreCompInfo();
result.ConfWidth = m_infoP.ConfWidth;
ECPoint twiceP = m_infoP.Twice;
if (null != twiceP)
{
ECPoint twiceQ = m_pointMap.Map(twiceP);
result.Twice = twiceQ;
}
ECPoint[] preCompP = m_infoP.PreComp;
ECPoint[] preCompQ = new ECPoint[preCompP.Length];
for (int i = 0; i < preCompP.Length; ++i)
{
preCompQ[i] = m_pointMap.Map(preCompP[i]);
}
result.PreComp = preCompQ;
result.Width = m_infoP.Width;
if (m_includeNegated)
{
ECPoint[] preCompNegQ = new ECPoint[preCompQ.Length];
for (int i = 0; i < preCompNegQ.Length; ++i)
{
preCompNegQ[i] = preCompQ[i].Negate();
}
result.PreCompNeg = preCompNegQ;
}
return result;
}
}
private class PrecomputeCallback
: IPreCompCallback
{
private readonly ECPoint m_p;
private readonly int m_minWidth;
private readonly bool m_includeNegated;
internal PrecomputeCallback(ECPoint p, int minWidth, bool includeNegated)
{
this.m_p = p;
this.m_minWidth = minWidth;
this.m_includeNegated = includeNegated;
}
public PreCompInfo Precompute(PreCompInfo existing)
{
WNafPreCompInfo existingWNaf = existing as WNafPreCompInfo;
int width = System.Math.Max(2, System.Math.Min(MAX_WIDTH, m_minWidth));
int reqPreCompLen = 1 << (width - 2);
if (CheckExisting(existingWNaf, width, reqPreCompLen, m_includeNegated))
{
existingWNaf.DecrementPromotionCountdown();
return existingWNaf;
}
WNafPreCompInfo result = new WNafPreCompInfo();
ECCurve c = m_p.Curve;
ECPoint[] preComp = null, preCompNeg = null;
ECPoint twiceP = null;
if (null != existingWNaf)
{
int promotionCountdown = existingWNaf.DecrementPromotionCountdown();
result.PromotionCountdown = promotionCountdown;
int confWidth = existingWNaf.ConfWidth;
result.ConfWidth = confWidth;
preComp = existingWNaf.PreComp;
preCompNeg = existingWNaf.PreCompNeg;
twiceP = existingWNaf.Twice;
}
width = System.Math.Min(MAX_WIDTH, System.Math.Max(result.ConfWidth, width));
reqPreCompLen = 1 << (width - 2);
int iniPreCompLen = 0;
if (null == preComp)
{
preComp = EMPTY_POINTS;
}
else
{
iniPreCompLen = preComp.Length;
}
if (iniPreCompLen < reqPreCompLen)
{
preComp = WNafUtilities.ResizeTable(preComp, reqPreCompLen);
if (reqPreCompLen == 1)
{
preComp[0] = m_p.Normalize();
}
else
{
int curPreCompLen = iniPreCompLen;
if (curPreCompLen == 0)
{
preComp[0] = m_p;
curPreCompLen = 1;
}
ECFieldElement iso = null;
if (reqPreCompLen == 2)
{
preComp[1] = m_p.ThreeTimes();
}
else
{
ECPoint isoTwiceP = twiceP, last = preComp[curPreCompLen - 1];
if (null == isoTwiceP)
{
isoTwiceP = preComp[0].Twice();
twiceP = isoTwiceP;
/*
* For Fp curves with Jacobian projective coordinates, use a (quasi-)isomorphism
* where 'twiceP' is "affine", so that the subsequent additions are cheaper. This
* also requires scaling the initial point's X, Y coordinates, and reversing the
* isomorphism as part of the subsequent normalization.
*
* NOTE: The correctness of this optimization depends on:
* 1) additions do not use the curve's A, B coefficients.
* 2) no special cases (i.e. Q +/- Q) when calculating 1P, 3P, 5P, ...
*/
if (!twiceP.IsInfinity && ECAlgorithms.IsFpCurve(c) && c.FieldSize >= 64)
{
switch (c.CoordinateSystem)
{
case ECCurve.COORD_JACOBIAN:
case ECCurve.COORD_JACOBIAN_CHUDNOVSKY:
case ECCurve.COORD_JACOBIAN_MODIFIED:
{
iso = twiceP.GetZCoord(0);
isoTwiceP = c.CreatePoint(twiceP.XCoord.ToBigInteger(),
twiceP.YCoord.ToBigInteger());
ECFieldElement iso2 = iso.Square(), iso3 = iso2.Multiply(iso);
last = last.ScaleX(iso2).ScaleY(iso3);
if (iniPreCompLen == 0)
{
preComp[0] = last;
}
break;
}
}
}
}
while (curPreCompLen < reqPreCompLen)
{
/*
* Compute the new ECPoints for the precomputation array. The values 1, 3,
* 5, ..., 2^(width-1)-1 times p are computed
*/
preComp[curPreCompLen++] = last = last.Add(isoTwiceP);
}
}
/*
* Having oft-used operands in affine form makes operations faster.
*/
c.NormalizeAll(preComp, iniPreCompLen, reqPreCompLen - iniPreCompLen, iso);
}
}
if (m_includeNegated)
{
int pos;
if (null == preCompNeg)
{
pos = 0;
preCompNeg = new ECPoint[reqPreCompLen];
}
else
{
pos = preCompNeg.Length;
if (pos < reqPreCompLen)
{
preCompNeg = WNafUtilities.ResizeTable(preCompNeg, reqPreCompLen);
}
}
while (pos < reqPreCompLen)
{
preCompNeg[pos] = preComp[pos].Negate();
++pos;
}
}
result.PreComp = preComp;
result.PreCompNeg = preCompNeg;
result.Twice = twiceP;
result.Width = width;
return result;
}
private bool CheckExisting(WNafPreCompInfo existingWNaf, int width, int reqPreCompLen, bool includeNegated)
{
return null != existingWNaf
&& existingWNaf.Width >= System.Math.Max(existingWNaf.ConfWidth, width)
&& CheckTable(existingWNaf.PreComp, reqPreCompLen)
&& (!includeNegated || CheckTable(existingWNaf.PreCompNeg, reqPreCompLen));
}
private bool CheckTable(ECPoint[] table, int reqLen)
{
return null != table && table.Length >= reqLen;
}
}
private class PrecomputeWithPointMapCallback
: IPreCompCallback
{
private readonly ECPoint m_point;
private readonly ECPointMap m_pointMap;
private readonly WNafPreCompInfo m_fromWNaf;
private readonly bool m_includeNegated;
internal PrecomputeWithPointMapCallback(ECPoint point, ECPointMap pointMap, WNafPreCompInfo fromWNaf,
bool includeNegated)
{
this.m_point = point;
this.m_pointMap = pointMap;
this.m_fromWNaf = fromWNaf;
this.m_includeNegated = includeNegated;
}
public PreCompInfo Precompute(PreCompInfo existing)
{
WNafPreCompInfo existingWNaf = existing as WNafPreCompInfo;
int width = m_fromWNaf.Width;
int reqPreCompLen = m_fromWNaf.PreComp.Length;
if (CheckExisting(existingWNaf, width, reqPreCompLen, m_includeNegated))
{
existingWNaf.DecrementPromotionCountdown();
return existingWNaf;
}
/*
* TODO Ideally this method would support incremental calculation, but given the
* existing use-cases it would be of little-to-no benefit.
*/
WNafPreCompInfo result = new WNafPreCompInfo();
result.PromotionCountdown = m_fromWNaf.PromotionCountdown;
ECPoint twiceFrom = m_fromWNaf.Twice;
if (null != twiceFrom)
{
ECPoint twice = m_pointMap.Map(twiceFrom);
result.Twice = twice;
}
ECPoint[] preCompFrom = m_fromWNaf.PreComp;
ECPoint[] preComp = new ECPoint[preCompFrom.Length];
for (int i = 0; i < preCompFrom.Length; ++i)
{
preComp[i] = m_pointMap.Map(preCompFrom[i]);
}
result.PreComp = preComp;
result.Width = width;
if (m_includeNegated)
{
ECPoint[] preCompNeg = new ECPoint[preComp.Length];
for (int i = 0; i < preCompNeg.Length; ++i)
{
preCompNeg[i] = preComp[i].Negate();
}
result.PreCompNeg = preCompNeg;
}
return result;
}
private bool CheckExisting(WNafPreCompInfo existingWNaf, int width, int reqPreCompLen, bool includeNegated)
{
return null != existingWNaf
&& existingWNaf.Width >= width
&& CheckTable(existingWNaf.PreComp, reqPreCompLen)
&& (!includeNegated || CheckTable(existingWNaf.PreCompNeg, reqPreCompLen));
}
private bool CheckTable(ECPoint[] table, int reqLen)
{
return null != table && table.Length >= reqLen;
}
}
}
}