using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Org.BouncyCastle.Math.EC.Endo;
using Org.BouncyCastle.Math.EC.Multiplier;
using Org.BouncyCastle.Math.Field;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Math.EC
{
/// Base class for an elliptic curve.
public abstract class ECCurve
{
public const int COORD_AFFINE = 0;
public const int COORD_HOMOGENEOUS = 1;
public const int COORD_JACOBIAN = 2;
public const int COORD_JACOBIAN_CHUDNOVSKY = 3;
public const int COORD_JACOBIAN_MODIFIED = 4;
public const int COORD_LAMBDA_AFFINE = 5;
public const int COORD_LAMBDA_PROJECTIVE = 6;
public const int COORD_SKEWED = 7;
public static int[] GetAllCoordinateSystems()
{
return new int[]{ COORD_AFFINE, COORD_HOMOGENEOUS, COORD_JACOBIAN, COORD_JACOBIAN_CHUDNOVSKY,
COORD_JACOBIAN_MODIFIED, COORD_LAMBDA_AFFINE, COORD_LAMBDA_PROJECTIVE, COORD_SKEWED };
}
public class Config
{
protected ECCurve outer;
protected int coord;
protected ECEndomorphism endomorphism;
protected ECMultiplier multiplier;
internal Config(ECCurve outer, int coord, ECEndomorphism endomorphism, ECMultiplier multiplier)
{
this.outer = outer;
this.coord = coord;
this.endomorphism = endomorphism;
this.multiplier = multiplier;
}
public Config SetCoordinateSystem(int coord)
{
this.coord = coord;
return this;
}
public Config SetEndomorphism(ECEndomorphism endomorphism)
{
this.endomorphism = endomorphism;
return this;
}
public Config SetMultiplier(ECMultiplier multiplier)
{
this.multiplier = multiplier;
return this;
}
public ECCurve Create()
{
if (!outer.SupportsCoordinateSystem(coord))
{
throw new InvalidOperationException("unsupported coordinate system");
}
ECCurve c = outer.CloneCurve();
if (c == outer)
{
throw new InvalidOperationException("implementation returned current curve");
}
c.m_coord = coord;
c.m_endomorphism = endomorphism;
c.m_multiplier = multiplier;
return c;
}
}
protected readonly IFiniteField m_field;
protected ECFieldElement m_a, m_b;
protected BigInteger m_order, m_cofactor;
protected int m_coord = COORD_AFFINE;
protected ECEndomorphism m_endomorphism = null;
protected ECMultiplier m_multiplier = null;
private IDictionary m_preCompTable = null;
protected ECCurve(IFiniteField field)
{
this.m_field = field;
}
public abstract int FieldSize { get; }
public abstract ECFieldElement FromBigInteger(BigInteger x);
public abstract bool IsValidFieldElement(BigInteger x);
public abstract ECFieldElement RandomFieldElement(SecureRandom r);
public abstract ECFieldElement RandomFieldElementMult(SecureRandom r);
public virtual Config Configure()
{
return new Config(this, this.m_coord, this.m_endomorphism, this.m_multiplier);
}
public virtual ECPoint ValidatePoint(BigInteger x, BigInteger y)
{
ECPoint p = CreatePoint(x, y);
if (!p.IsValid())
throw new ArgumentException("Invalid point coordinates");
return p;
}
public virtual ECPoint CreatePoint(BigInteger x, BigInteger y)
{
return CreateRawPoint(FromBigInteger(x), FromBigInteger(y));
}
protected abstract ECCurve CloneCurve();
protected internal abstract ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y);
protected internal abstract ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs);
protected virtual ECMultiplier CreateDefaultMultiplier()
{
if (m_endomorphism is GlvEndomorphism glvEndomorphism)
return new GlvMultiplier(this, glvEndomorphism);
return new WNafL2RMultiplier();
}
public virtual bool SupportsCoordinateSystem(int coord)
{
return coord == COORD_AFFINE;
}
public virtual PreCompInfo GetPreCompInfo(ECPoint point, string name)
{
CheckPoint(point);
IDictionary table;
lock (point)
{
table = point.m_preCompTable;
}
if (null == table)
return null;
lock (table)
{
return table.TryGetValue(name, out var preCompInfo) ? preCompInfo : null;
}
}
internal virtual PreCompInfo Precompute(string name, IPreCompCallback callback)
{
IDictionary table;
lock (this)
{
table = m_preCompTable;
if (null == table)
{
m_preCompTable = table = new Dictionary();
}
}
lock (table)
{
PreCompInfo existing = table.TryGetValue(name, out var preCompInfo) ? preCompInfo : null;
PreCompInfo result = callback.Precompute(existing);
if (result != existing)
{
table[name] = result;
}
return result;
}
}
/**
* Compute a PreCompInfo
for a point on this curve, under a given name. Used by
* ECMultiplier
s to save the precomputation for this ECPoint
for use
* by subsequent multiplication.
*
* @param point
* The ECPoint
to store precomputations for.
* @param name
* A String
used to index precomputations of different types.
* @param callback
* Called to calculate the PreCompInfo
.
*/
public virtual PreCompInfo Precompute(ECPoint point, string name, IPreCompCallback callback)
{
CheckPoint(point);
IDictionary table;
lock (point)
{
table = point.m_preCompTable;
if (null == table)
{
point.m_preCompTable = table = new Dictionary();
}
}
lock (table)
{
PreCompInfo existing = table.TryGetValue(name, out var preCompInfo) ? preCompInfo : null;
PreCompInfo result = callback.Precompute(existing);
if (result != existing)
{
table[name] = result;
}
return result;
}
}
public virtual ECPoint ImportPoint(ECPoint p)
{
if (this == p.Curve)
{
return p;
}
if (p.IsInfinity)
{
return Infinity;
}
// TODO Default behaviour could be improved if the two curves have the same coordinate system by copying any Z coordinates.
p = p.Normalize();
return CreatePoint(p.XCoord.ToBigInteger(), p.YCoord.ToBigInteger());
}
/**
* Normalization ensures that any projective coordinate is 1, and therefore that the x, y
* coordinates reflect those of the equivalent point in an affine coordinate system. Where more
* than one point is to be normalized, this method will generally be more efficient than
* normalizing each point separately.
*
* @param points
* An array of points that will be updated in place with their normalized versions,
* where necessary
*/
public virtual void NormalizeAll(ECPoint[] points)
{
NormalizeAll(points, 0, points.Length, null);
}
/**
* Normalization ensures that any projective coordinate is 1, and therefore that the x, y
* coordinates reflect those of the equivalent point in an affine coordinate system. Where more
* than one point is to be normalized, this method will generally be more efficient than
* normalizing each point separately. An (optional) z-scaling factor can be applied; effectively
* each z coordinate is scaled by this value prior to normalization (but only one
* actual multiplication is needed).
*
* @param points
* An array of points that will be updated in place with their normalized versions,
* where necessary
* @param off
* The start of the range of points to normalize
* @param len
* The length of the range of points to normalize
* @param iso
* The (optional) z-scaling factor - can be null
*/
public virtual void NormalizeAll(ECPoint[] points, int off, int len, ECFieldElement iso)
{
CheckPoints(points, off, len);
switch (this.CoordinateSystem)
{
case ECCurve.COORD_AFFINE:
case ECCurve.COORD_LAMBDA_AFFINE:
{
if (iso != null)
throw new ArgumentException("not valid for affine coordinates", "iso");
return;
}
}
/*
* Figure out which of the points actually need to be normalized
*/
ECFieldElement[] zs = new ECFieldElement[len];
int[] indices = new int[len];
int count = 0;
for (int i = 0; i < len; ++i)
{
ECPoint p = points[off + i];
if (null != p && (iso != null || !p.IsNormalized()))
{
zs[count] = p.GetZCoord(0);
indices[count++] = off + i;
}
}
if (count == 0)
{
return;
}
ECAlgorithms.MontgomeryTrick(zs, 0, count, iso);
for (int j = 0; j < count; ++j)
{
int index = indices[j];
points[index] = points[index].Normalize(zs[j]);
}
}
public abstract ECPoint Infinity { get; }
public virtual IFiniteField Field
{
get { return m_field; }
}
public virtual ECFieldElement A
{
get { return m_a; }
}
public virtual ECFieldElement B
{
get { return m_b; }
}
public virtual BigInteger Order
{
get { return m_order; }
}
public virtual BigInteger Cofactor
{
get { return m_cofactor; }
}
public virtual int CoordinateSystem
{
get { return m_coord; }
}
/**
* Create a cache-safe lookup table for the specified sequence of points. All the points MUST
* belong to this ECCurve
instance, and MUST already be normalized.
*/
public virtual ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
{
int FE_BYTES = (FieldSize + 7) / 8;
byte[] table = new byte[len * FE_BYTES * 2];
{
int pos = 0;
for (int i = 0; i < len; ++i)
{
ECPoint p = points[off + i];
byte[] px = p.RawXCoord.ToBigInteger().ToByteArray();
byte[] py = p.RawYCoord.ToBigInteger().ToByteArray();
int pxStart = px.Length > FE_BYTES ? 1 : 0, pxLen = px.Length - pxStart;
int pyStart = py.Length > FE_BYTES ? 1 : 0, pyLen = py.Length - pyStart;
Array.Copy(px, pxStart, table, pos + FE_BYTES - pxLen, pxLen); pos += FE_BYTES;
Array.Copy(py, pyStart, table, pos + FE_BYTES - pyLen, pyLen); pos += FE_BYTES;
}
}
return new DefaultLookupTable(this, table, len);
}
protected virtual void CheckPoint(ECPoint point)
{
if (null == point || (this != point.Curve))
throw new ArgumentException("must be non-null and on this curve", "point");
}
protected virtual void CheckPoints(ECPoint[] points)
{
CheckPoints(points, 0, points.Length);
}
protected virtual void CheckPoints(ECPoint[] points, int off, int len)
{
if (points == null)
throw new ArgumentNullException("points");
if (off < 0 || len < 0 || (off > (points.Length - len)))
throw new ArgumentException("invalid range specified", "points");
for (int i = 0; i < len; ++i)
{
ECPoint point = points[off + i];
if (null != point && this != point.Curve)
throw new ArgumentException("entries must be null or on this curve", "points");
}
}
public virtual bool Equals(ECCurve other)
{
if (this == other)
return true;
if (null == other)
return false;
return Field.Equals(other.Field)
&& A.ToBigInteger().Equals(other.A.ToBigInteger())
&& B.ToBigInteger().Equals(other.B.ToBigInteger());
}
public override bool Equals(object obj)
{
return Equals(obj as ECCurve);
}
public override int GetHashCode()
{
return Field.GetHashCode()
^ Integers.RotateLeft(A.ToBigInteger().GetHashCode(), 8)
^ Integers.RotateLeft(B.ToBigInteger().GetHashCode(), 16);
}
protected abstract ECPoint DecompressPoint(int yTilde, BigInteger X1);
public virtual ECEndomorphism GetEndomorphism()
{
return m_endomorphism;
}
/**
* Sets the default ECMultiplier
, unless already set.
*
* We avoid locking for performance reasons, so there is no uniqueness guarantee.
*/
public virtual ECMultiplier GetMultiplier()
{
if (this.m_multiplier == null)
{
this.m_multiplier = CreateDefaultMultiplier();
}
return this.m_multiplier;
}
/**
* Decode a point on this curve from its ASN.1 encoding. The different
* encodings are taken account of, including point compression for
* Fp
(X9.62 s 4.2.1 pg 17).
* @return The decoded point.
*/
public virtual ECPoint DecodePoint(byte[] encoded)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
return DecodePoint(encoded.AsSpan());
#else
ECPoint p;
int expectedLength = (FieldSize + 7) / 8;
byte type = encoded[0];
switch (type)
{
case 0x00: // infinity
{
if (encoded.Length != 1)
throw new ArgumentException("Incorrect length for infinity encoding", "encoded");
p = Infinity;
break;
}
case 0x02: // compressed
case 0x03: // compressed
{
if (encoded.Length != (expectedLength + 1))
throw new ArgumentException("Incorrect length for compressed encoding", "encoded");
int yTilde = type & 1;
BigInteger X = new BigInteger(1, encoded, 1, expectedLength);
p = DecompressPoint(yTilde, X);
if (!p.ImplIsValid(true, true))
throw new ArgumentException("Invalid point");
break;
}
case 0x04: // uncompressed
{
if (encoded.Length != (2 * expectedLength + 1))
throw new ArgumentException("Incorrect length for uncompressed encoding", "encoded");
BigInteger X = new BigInteger(1, encoded, 1, expectedLength);
BigInteger Y = new BigInteger(1, encoded, 1 + expectedLength, expectedLength);
p = ValidatePoint(X, Y);
break;
}
case 0x06: // hybrid
case 0x07: // hybrid
{
if (encoded.Length != (2 * expectedLength + 1))
throw new ArgumentException("Incorrect length for hybrid encoding", "encoded");
BigInteger X = new BigInteger(1, encoded, 1, expectedLength);
BigInteger Y = new BigInteger(1, encoded, 1 + expectedLength, expectedLength);
if (Y.TestBit(0) != (type == 0x07))
throw new ArgumentException("Inconsistent Y coordinate in hybrid encoding", "encoded");
p = ValidatePoint(X, Y);
break;
}
default:
throw new FormatException("Invalid point encoding " + type);
}
if (type != 0x00 && p.IsInfinity)
throw new ArgumentException("Invalid infinity encoding", "encoded");
return p;
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
public virtual ECPoint DecodePoint(ReadOnlySpan encoded)
{
ECPoint p;
int expectedLength = (FieldSize + 7) / 8;
byte type = encoded[0];
switch (type)
{
case 0x00: // infinity
{
if (encoded.Length != 1)
throw new ArgumentException("Incorrect length for infinity encoding", "encoded");
p = Infinity;
break;
}
case 0x02: // compressed
case 0x03: // compressed
{
if (encoded.Length != (expectedLength + 1))
throw new ArgumentException("Incorrect length for compressed encoding", "encoded");
int yTilde = type & 1;
BigInteger X = new BigInteger(1, encoded[1..]);
p = DecompressPoint(yTilde, X);
if (!p.ImplIsValid(true, true))
throw new ArgumentException("Invalid point");
break;
}
case 0x04: // uncompressed
{
if (encoded.Length != (2 * expectedLength + 1))
throw new ArgumentException("Incorrect length for uncompressed encoding", "encoded");
BigInteger X = new BigInteger(1, encoded[1..(1 + expectedLength)]);
BigInteger Y = new BigInteger(1, encoded[(1 + expectedLength)..]);
p = ValidatePoint(X, Y);
break;
}
case 0x06: // hybrid
case 0x07: // hybrid
{
if (encoded.Length != (2 * expectedLength + 1))
throw new ArgumentException("Incorrect length for hybrid encoding", "encoded");
BigInteger X = new BigInteger(1, encoded[1..(1 + expectedLength)]);
BigInteger Y = new BigInteger(1, encoded[(1 + expectedLength)..]);
if (Y.TestBit(0) != (type == 0x07))
throw new ArgumentException("Inconsistent Y coordinate in hybrid encoding", "encoded");
p = ValidatePoint(X, Y);
break;
}
default:
throw new FormatException("Invalid point encoding " + type);
}
if (type != 0x00 && p.IsInfinity)
throw new ArgumentException("Invalid infinity encoding", "encoded");
return p;
}
#endif
private class DefaultLookupTable
: AbstractECLookupTable
{
private readonly ECCurve m_outer;
private readonly byte[] m_table;
private readonly int m_size;
internal DefaultLookupTable(ECCurve outer, byte[] table, int size)
{
this.m_outer = outer;
this.m_table = table;
this.m_size = size;
}
public override int Size
{
get { return m_size; }
}
public override ECPoint Lookup(int index)
{
int FE_BYTES = (m_outer.FieldSize + 7) / 8;
byte[] x = new byte[FE_BYTES], y = new byte[FE_BYTES];
int pos = 0;
for (int i = 0; i < m_size; ++i)
{
byte MASK = (byte)(((i ^ index) - 1) >> 31);
for (int j = 0; j < FE_BYTES; ++j)
{
x[j] ^= (byte)(m_table[pos + j] & MASK);
y[j] ^= (byte)(m_table[pos + FE_BYTES + j] & MASK);
}
pos += (FE_BYTES * 2);
}
return CreatePoint(x, y);
}
public override ECPoint LookupVar(int index)
{
int FE_BYTES = (m_outer.FieldSize + 7) / 8;
byte[] x = new byte[FE_BYTES], y = new byte[FE_BYTES];
int pos = index * FE_BYTES * 2;
for (int j = 0; j < FE_BYTES; ++j)
{
x[j] = m_table[pos + j];
y[j] = m_table[pos + FE_BYTES + j];
}
return CreatePoint(x, y);
}
private ECPoint CreatePoint(byte[] x, byte[] y)
{
ECFieldElement X = m_outer.FromBigInteger(new BigInteger(1, x));
ECFieldElement Y = m_outer.FromBigInteger(new BigInteger(1, y));
return m_outer.CreateRawPoint(X, Y);
}
}
}
public abstract class AbstractFpCurve
: ECCurve
{
private static readonly ConcurrentDictionary KnownPrimes =
new ConcurrentDictionary();
protected AbstractFpCurve(BigInteger q)
: this(q, isInternal: false)
{
}
internal AbstractFpCurve(BigInteger q, bool isInternal)
: base(FiniteFields.GetPrimeField(q))
{
if (isInternal)
{
KnownPrimes.AddOrUpdate(q, true, (key, value) => true);
}
else if (!KnownPrimes.ContainsKey(q))
{
ImplCheckQ(q);
KnownPrimes.TryAdd(q, false);
}
}
public override bool IsValidFieldElement(BigInteger x)
{
return x != null && x.SignValue >= 0 && x.CompareTo(Field.Characteristic) < 0;
}
public override ECFieldElement RandomFieldElement(SecureRandom r)
{
/*
* NOTE: BigInteger comparisons in the rejection sampling are not constant-time, so we
* use the product of two independent elements to mitigate side-channels.
*/
BigInteger p = Field.Characteristic;
ECFieldElement fe1 = FromBigInteger(ImplRandomFieldElement(r, p));
ECFieldElement fe2 = FromBigInteger(ImplRandomFieldElement(r, p));
return fe1.Multiply(fe2);
}
public override ECFieldElement RandomFieldElementMult(SecureRandom r)
{
/*
* NOTE: BigInteger comparisons in the rejection sampling are not constant-time, so we
* use the product of two independent elements to mitigate side-channels.
*/
BigInteger p = Field.Characteristic;
ECFieldElement fe1 = FromBigInteger(ImplRandomFieldElementMult(r, p));
ECFieldElement fe2 = FromBigInteger(ImplRandomFieldElementMult(r, p));
return fe1.Multiply(fe2);
}
protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
{
ECFieldElement x = FromBigInteger(X1);
ECFieldElement rhs = x.Square().Add(A).Multiply(x).Add(B);
ECFieldElement y = rhs.Sqrt();
/*
* If y is not a square, then we haven't got a point on the curve
*/
if (y == null)
throw new ArgumentException("Invalid point compression");
if (y.TestBitZero() != (yTilde == 1))
{
// Use the other root
y = y.Negate();
}
return CreateRawPoint(x, y);
}
private static void ImplCheckQ(BigInteger q)
{
int maxBitLength = ImplGetInteger("Org.BouncyCastle.EC.Fp_MaxSize", 1042); // 2 * 521
if (q.BitLength > maxBitLength)
throw new ArgumentException("Fp q value out of range");
if (!ImplIsPrime(q))
throw new ArgumentException("Fp q value not prime");
}
private static int ImplGetInteger(string envVariable, int defaultValue)
{
string property = Platform.GetEnvironmentVariable(envVariable);
return int.TryParse(property, out int value) ? value : defaultValue;
}
private static int ImplGetIterations(int bits, int certainty)
{
/*
* NOTE: We enforce a minimum 'certainty' of 100 for bits >= 1024 (else 80). Where the
* certainty is higher than the FIPS 186-4 tables (C.2/C.3) cater to, extra iterations
* are added at the "worst case rate" for the excess.
*/
if (bits >= 1536)
{
return certainty <= 100 ? 3
: certainty <= 128 ? 4
: 4 + (certainty - 128 + 1) / 2;
}
else if (bits >= 1024)
{
return certainty <= 100 ? 4
: certainty <= 112 ? 5
: 5 + (certainty - 112 + 1) / 2;
}
else if (bits >= 512)
{
return certainty <= 80 ? 5
: certainty <= 100 ? 7
: 7 + (certainty - 100 + 1) / 2;
}
else
{
return certainty <= 80 ? 40
: 40 + (certainty - 80 + 1) / 2;
}
}
private static bool ImplIsPrime(BigInteger q)
{
if (Primes.HasAnySmallFactors(q))
return false;
int certainty = ImplGetInteger("Org.BouncyCastle.EC.Fp_Certainty", 100);
int iterations = ImplGetIterations(q.BitLength, certainty);
return Primes.IsMRProbablePrime(q, SecureRandom.ArbitraryRandom, iterations);
}
private static BigInteger ImplRandomFieldElement(SecureRandom r, BigInteger p)
{
BigInteger x;
do
{
x = BigIntegers.CreateRandomBigInteger(p.BitLength, r);
}
while (x.CompareTo(p) >= 0);
return x;
}
private static BigInteger ImplRandomFieldElementMult(SecureRandom r, BigInteger p)
{
BigInteger x;
do
{
x = BigIntegers.CreateRandomBigInteger(p.BitLength, r);
}
while (x.SignValue <= 0 || x.CompareTo(p) >= 0);
return x;
}
}
/**
* Elliptic curve over Fp
*/
public class FpCurve
: AbstractFpCurve
{
private const int FP_DEFAULT_COORDS = COORD_JACOBIAN_MODIFIED;
protected readonly BigInteger m_q, m_r;
protected readonly FpPoint m_infinity;
[Obsolete("Use constructor taking order/cofactor")]
public FpCurve(BigInteger q, BigInteger a, BigInteger b)
: this(q, a, b, null, null)
{
}
public FpCurve(BigInteger q, BigInteger a, BigInteger b, BigInteger order, BigInteger cofactor)
: this(q, a, b, order, cofactor, isInternal: false)
{
}
internal FpCurve(BigInteger q, BigInteger a, BigInteger b, BigInteger order, BigInteger cofactor,
bool isInternal)
: base(q, isInternal)
{
this.m_q = q;
this.m_r = FpFieldElement.CalculateResidue(q);
this.m_infinity = new FpPoint(this, null, null);
this.m_a = FromBigInteger(a);
this.m_b = FromBigInteger(b);
this.m_order = order;
this.m_cofactor = cofactor;
this.m_coord = FP_DEFAULT_COORDS;
}
internal FpCurve(BigInteger q, BigInteger r, ECFieldElement a, ECFieldElement b, BigInteger order,
BigInteger cofactor)
: base(q, isInternal: true)
{
this.m_q = q;
this.m_r = r;
this.m_infinity = new FpPoint(this, null, null);
this.m_a = a;
this.m_b = b;
this.m_order = order;
this.m_cofactor = cofactor;
this.m_coord = FP_DEFAULT_COORDS;
}
protected override ECCurve CloneCurve()
{
return new FpCurve(m_q, m_r, m_a, m_b, m_order, m_cofactor);
}
public override bool SupportsCoordinateSystem(int coord)
{
switch (coord)
{
case COORD_AFFINE:
case COORD_HOMOGENEOUS:
case COORD_JACOBIAN:
case COORD_JACOBIAN_MODIFIED:
return true;
default:
return false;
}
}
public virtual BigInteger Q
{
get { return m_q; }
}
public override ECPoint Infinity
{
get { return m_infinity; }
}
public override int FieldSize
{
get { return m_q.BitLength; }
}
public override ECFieldElement FromBigInteger(BigInteger x)
{
if (x == null || x.SignValue < 0 || x.CompareTo(m_q) >= 0)
throw new ArgumentException("value invalid for Fp field element", "x");
return new FpFieldElement(this.m_q, this.m_r, x);
}
protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y)
{
return new FpPoint(this, x, y);
}
protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs)
{
return new FpPoint(this, x, y, zs);
}
public override ECPoint ImportPoint(ECPoint p)
{
if (this != p.Curve && this.CoordinateSystem == COORD_JACOBIAN && !p.IsInfinity)
{
switch (p.Curve.CoordinateSystem)
{
case COORD_JACOBIAN:
case COORD_JACOBIAN_CHUDNOVSKY:
case COORD_JACOBIAN_MODIFIED:
return new FpPoint(this,
FromBigInteger(p.RawXCoord.ToBigInteger()),
FromBigInteger(p.RawYCoord.ToBigInteger()),
new ECFieldElement[] { FromBigInteger(p.GetZCoord(0).ToBigInteger()) });
default:
break;
}
}
return base.ImportPoint(p);
}
}
public abstract class AbstractF2mCurve
: ECCurve
{
public static BigInteger Inverse(int m, int[] ks, BigInteger x)
{
return new LongArray(x).ModInverse(m, ks).ToBigInteger();
}
private static IFiniteField BuildField(int m, int k1, int k2, int k3)
{
int[] exponents = (k2 | k3) == 0
? new int[]{ 0, k1, m }
: new int[]{ 0, k1, k2, k3, m };
return FiniteFields.GetBinaryExtensionField(exponents);
}
protected AbstractF2mCurve(int m, int k1, int k2, int k3)
: base(BuildField(m, k1, k2, k3))
{
}
public override ECPoint CreatePoint(BigInteger x, BigInteger y)
{
ECFieldElement X = FromBigInteger(x), Y = FromBigInteger(y);
switch (this.CoordinateSystem)
{
case COORD_LAMBDA_AFFINE:
case COORD_LAMBDA_PROJECTIVE:
{
if (X.IsZero)
{
if (!Y.Square().Equals(B))
throw new ArgumentException();
}
else
{
// Y becomes Lambda (X + Y/X) here
Y = Y.Divide(X).Add(X);
}
break;
}
default:
{
break;
}
}
return CreateRawPoint(X, Y);
}
public override bool IsValidFieldElement(BigInteger x)
{
return x != null && x.SignValue >= 0 && x.BitLength <= FieldSize;
}
public override ECFieldElement RandomFieldElement(SecureRandom r)
{
int m = FieldSize;
return FromBigInteger(BigIntegers.CreateRandomBigInteger(m, r));
}
public override ECFieldElement RandomFieldElementMult(SecureRandom r)
{
/*
* NOTE: BigInteger comparisons in the rejection sampling are not constant-time, so we
* use the product of two independent elements to mitigate side-channels.
*/
int m = FieldSize;
ECFieldElement fe1 = FromBigInteger(ImplRandomFieldElementMult(r, m));
ECFieldElement fe2 = FromBigInteger(ImplRandomFieldElementMult(r, m));
return fe1.Multiply(fe2);
}
protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
{
ECFieldElement xp = FromBigInteger(X1), yp = null;
if (xp.IsZero)
{
yp = B.Sqrt();
}
else
{
ECFieldElement beta = xp.Square().Invert().Multiply(B).Add(A).Add(xp);
ECFieldElement z = SolveQuadraticEquation(beta);
if (z != null)
{
if (z.TestBitZero() != (yTilde == 1))
{
z = z.AddOne();
}
switch (this.CoordinateSystem)
{
case COORD_LAMBDA_AFFINE:
case COORD_LAMBDA_PROJECTIVE:
{
yp = z.Add(xp);
break;
}
default:
{
yp = z.Multiply(xp);
break;
}
}
}
}
if (yp == null)
throw new ArgumentException("Invalid point compression");
return CreateRawPoint(xp, yp);
}
/**
* Solves a quadratic equation z2 + z = beta
(X9.62
* D.1.6) The other solution is z + 1
.
*
* @param beta
* The value to solve the quadratic equation for.
* @return the solution for z2 + z = beta
or
* null
if no solution exists.
*/
internal ECFieldElement SolveQuadraticEquation(ECFieldElement beta)
{
AbstractF2mFieldElement betaF2m = (AbstractF2mFieldElement)beta;
bool fastTrace = betaF2m.HasFastTrace;
if (fastTrace && 0 != betaF2m.Trace())
return null;
int m = FieldSize;
// For odd m, use the half-trace
if (0 != (m & 1))
{
ECFieldElement r = betaF2m.HalfTrace();
if (fastTrace || r.Square().Add(r).Add(beta).IsZero)
return r;
return null;
}
if (beta.IsZero)
return beta;
ECFieldElement gamma, z, zeroElement = FromBigInteger(BigInteger.Zero);
do
{
ECFieldElement t = FromBigInteger(BigInteger.Arbitrary(m));
z = zeroElement;
ECFieldElement w = beta;
for (int i = 1; i < m; i++)
{
ECFieldElement w2 = w.Square();
z = z.Square().Add(w2.Multiply(t));
w = w2.Add(beta);
}
if (!w.IsZero)
{
return null;
}
gamma = z.Square().Add(z);
}
while (gamma.IsZero);
return z;
}
/**
* Returns true if this is a Koblitz curve (ABC curve).
* @return true if this is a Koblitz curve (ABC curve), false otherwise
*/
public virtual bool IsKoblitz
{
get
{
return m_order != null && m_cofactor != null && m_b.IsOne && (m_a.IsZero || m_a.IsOne);
}
}
private static BigInteger ImplRandomFieldElementMult(SecureRandom r, int m)
{
BigInteger x;
do
{
x = BigIntegers.CreateRandomBigInteger(m, r);
}
while (x.SignValue <= 0);
return x;
}
}
/**
* Elliptic curves over F2m. The Weierstrass equation is given by
* y2 + xy = x3 + ax2 + b
.
*/
public class F2mCurve
: AbstractF2mCurve
{
private const int F2M_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
/**
* The exponent m
of F2m
.
*/
private readonly int m;
/**
* TPB: The integer k
where xm +
* xk + 1
represents the reduction polynomial
* f(z)
.
* PPB: The integer k1
where xm +
* xk3 + xk2 + xk1 + 1
* represents the reduction polynomial f(z)
.
*/
private readonly int k1;
/**
* TPB: Always set to 0
* PPB: The integer k2
where xm +
* xk3 + xk2 + xk1 + 1
* represents the reduction polynomial f(z)
.
*/
private readonly int k2;
/**
* TPB: Always set to 0
* PPB: The integer k3
where xm +
* xk3 + xk2 + xk1 + 1
* represents the reduction polynomial f(z)
.
*/
private readonly int k3;
/**
* The point at infinity on this curve.
*/
protected readonly F2mPoint m_infinity;
/**
* Constructor for Trinomial Polynomial Basis (TPB).
* @param m The exponent m
of
* F2m
.
* @param k The integer k
where xm +
* xk + 1
represents the reduction
* polynomial f(z)
.
* @param a The coefficient a
in the Weierstrass equation
* for non-supersingular elliptic curves over
* F2m
.
* @param b The coefficient b
in the Weierstrass equation
* for non-supersingular elliptic curves over
* F2m
.
*/
[Obsolete("Use constructor taking order/cofactor")]
public F2mCurve(
int m,
int k,
BigInteger a,
BigInteger b)
: this(m, k, 0, 0, a, b, null, null)
{
}
/**
* Constructor for Trinomial Polynomial Basis (TPB).
* @param m The exponent m
of
* F2m
.
* @param k The integer k
where xm +
* xk + 1
represents the reduction
* polynomial f(z)
.
* @param a The coefficient a
in the Weierstrass equation
* for non-supersingular elliptic curves over
* F2m
.
* @param b The coefficient b
in the Weierstrass equation
* for non-supersingular elliptic curves over
* F2m
.
* @param order The order of the main subgroup of the elliptic curve.
* @param cofactor The cofactor of the elliptic curve, i.e.
* #Ea(F2m) = h * n
.
*/
public F2mCurve(
int m,
int k,
BigInteger a,
BigInteger b,
BigInteger order,
BigInteger cofactor)
: this(m, k, 0, 0, a, b, order, cofactor)
{
}
/**
* Constructor for Pentanomial Polynomial Basis (PPB).
* @param m The exponent m
of
* F2m
.
* @param k1 The integer k1
where xm +
* xk3 + xk2 + xk1 + 1
* represents the reduction polynomial f(z)
.
* @param k2 The integer k2
where xm +
* xk3 + xk2 + xk1 + 1
* represents the reduction polynomial f(z)
.
* @param k3 The integer k3
where xm +
* xk3 + xk2 + xk1 + 1
* represents the reduction polynomial f(z)
.
* @param a The coefficient a
in the Weierstrass equation
* for non-supersingular elliptic curves over
* F2m
.
* @param b The coefficient b
in the Weierstrass equation
* for non-supersingular elliptic curves over
* F2m
.
*/
[Obsolete("Use constructor taking order/cofactor")]
public F2mCurve(
int m,
int k1,
int k2,
int k3,
BigInteger a,
BigInteger b)
: this(m, k1, k2, k3, a, b, null, null)
{
}
/**
* Constructor for Pentanomial Polynomial Basis (PPB).
* @param m The exponent m
of
* F2m
.
* @param k1 The integer k1
where xm +
* xk3 + xk2 + xk1 + 1
* represents the reduction polynomial f(z)
.
* @param k2 The integer k2
where xm +
* xk3 + xk2 + xk1 + 1
* represents the reduction polynomial f(z)
.
* @param k3 The integer k3
where xm +
* xk3 + xk2 + xk1 + 1
* represents the reduction polynomial f(z)
.
* @param a The coefficient a
in the Weierstrass equation
* for non-supersingular elliptic curves over
* F2m
.
* @param b The coefficient b
in the Weierstrass equation
* for non-supersingular elliptic curves over
* F2m
.
* @param order The order of the main subgroup of the elliptic curve.
* @param cofactor The cofactor of the elliptic curve, i.e.
* #Ea(F2m) = h * n
.
*/
public F2mCurve(int m, int k1, int k2, int k3, BigInteger a, BigInteger b, BigInteger order,
BigInteger cofactor)
: base(m, k1, k2, k3)
{
this.m = m;
this.k1 = k1;
this.k2 = k2;
this.k3 = k3;
this.m_order = order;
this.m_cofactor = cofactor;
this.m_infinity = new F2mPoint(this, null, null);
this.m_a = FromBigInteger(a);
this.m_b = FromBigInteger(b);
this.m_coord = F2M_DEFAULT_COORDS;
}
internal F2mCurve(int m, int k1, int k2, int k3, ECFieldElement a, ECFieldElement b, BigInteger order,
BigInteger cofactor)
: base(m, k1, k2, k3)
{
this.m = m;
this.k1 = k1;
this.k2 = k2;
this.k3 = k3;
this.m_order = order;
this.m_cofactor = cofactor;
this.m_infinity = new F2mPoint(this, null, null);
this.m_a = a;
this.m_b = b;
this.m_coord = F2M_DEFAULT_COORDS;
}
protected override ECCurve CloneCurve()
{
return new F2mCurve(m, k1, k2, k3, m_a, m_b, m_order, m_cofactor);
}
public override bool SupportsCoordinateSystem(int coord)
{
switch (coord)
{
case COORD_AFFINE:
case COORD_HOMOGENEOUS:
case COORD_LAMBDA_PROJECTIVE:
return true;
default:
return false;
}
}
protected override ECMultiplier CreateDefaultMultiplier()
{
if (IsKoblitz)
{
return new WTauNafMultiplier();
}
return base.CreateDefaultMultiplier();
}
public override int FieldSize
{
get { return m; }
}
public override ECFieldElement FromBigInteger(BigInteger x)
{
if (x == null || x.SignValue < 0 || x.BitLength > m)
throw new ArgumentException("value invalid for F2m field element", "x");
int[] ks = (k2 | k3) == 0
? new int[]{ k1 }
: new int[]{ k1, k2, k3 };
return new F2mFieldElement(m, ks, new LongArray(x));
}
protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y)
{
return new F2mPoint(this, x, y);
}
protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs)
{
return new F2mPoint(this, x, y, zs);
}
public override ECPoint Infinity
{
get { return m_infinity; }
}
public int M
{
get { return m; }
}
/**
* Return true if curve uses a Trinomial basis.
*
* @return true if curve Trinomial, false otherwise.
*/
public bool IsTrinomial()
{
return k2 == 0 && k3 == 0;
}
public int K1
{
get { return k1; }
}
public int K2
{
get { return k2; }
}
public int K3
{
get { return k3; }
}
public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
{
int FE_LONGS = (m + 63) / 64;
ulong[] table = new ulong[len * FE_LONGS * 2];
{
int pos = 0;
for (int i = 0; i < len; ++i)
{
ECPoint p = points[off + i];
((F2mFieldElement)p.RawXCoord).x.CopyTo(table, pos); pos += FE_LONGS;
((F2mFieldElement)p.RawYCoord).x.CopyTo(table, pos); pos += FE_LONGS;
}
}
return new DefaultF2mLookupTable(this, table, len);
}
private class DefaultF2mLookupTable
: AbstractECLookupTable
{
private readonly F2mCurve m_outer;
private readonly ulong[] m_table;
private readonly int m_size;
internal DefaultF2mLookupTable(F2mCurve outer, ulong[] table, int size)
{
this.m_outer = outer;
this.m_table = table;
this.m_size = size;
}
public override int Size
{
get { return m_size; }
}
public override ECPoint Lookup(int index)
{
int FE_LONGS = (m_outer.m + 63) / 64;
ulong[] x = new ulong[FE_LONGS], y = new ulong[FE_LONGS];
int pos = 0;
for (int i = 0; i < m_size; ++i)
{
ulong MASK = (ulong)(long)(((i ^ index) - 1) >> 31);
for (int j = 0; j < FE_LONGS; ++j)
{
x[j] ^= m_table[pos + j] & MASK;
y[j] ^= m_table[pos + FE_LONGS + j] & MASK;
}
pos += (FE_LONGS * 2);
}
return CreatePoint(x, y);
}
public override ECPoint LookupVar(int index)
{
int FE_LONGS = (m_outer.m + 63) / 64;
ulong[] x = new ulong[FE_LONGS], y = new ulong[FE_LONGS];
int pos = index * FE_LONGS * 2;
for (int j = 0; j < FE_LONGS; ++j)
{
x[j] = m_table[pos + j];
y[j] = m_table[pos + FE_LONGS + j];
}
return CreatePoint(x, y);
}
private ECPoint CreatePoint(ulong[] x, ulong[] y)
{
int m = m_outer.m;
int[] ks = m_outer.IsTrinomial()
? new int[]{ m_outer.k1 }
: new int[]{ m_outer.k1, m_outer.k2, m_outer.k3 };
ECFieldElement X = new F2mFieldElement(m, ks, new LongArray(x));
ECFieldElement Y = new F2mFieldElement(m, ks, new LongArray(y));
return m_outer.CreateRawPoint(X, Y);
}
}
}
}