diff --git a/crypto/src/crypto/ec/CustomNamedCurves.cs b/crypto/src/crypto/ec/CustomNamedCurves.cs
index e58f83d25..5d2369349 100644
--- a/crypto/src/crypto/ec/CustomNamedCurves.cs
+++ b/crypto/src/crypto/ec/CustomNamedCurves.cs
@@ -6,6 +6,7 @@ using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Math.EC.Custom.Djb;
using Org.BouncyCastle.Math.EC.Custom.Sec;
using Org.BouncyCastle.Math.EC.Endo;
using Org.BouncyCastle.Utilities;
@@ -36,6 +37,38 @@ namespace Org.BouncyCastle.Crypto.EC
}
/*
+ * curve25519
+ */
+ internal class Curve25519Holder
+ : X9ECParametersHolder
+ {
+ private Curve25519Holder() { }
+
+ internal static readonly X9ECParametersHolder Instance = new Curve25519Holder();
+
+ protected override X9ECParameters CreateParameters()
+ {
+ byte[] S = null;
+ ECCurve curve = ConfigureCurve(new Curve25519());
+
+ /*
+ * NOTE: Curve25519 was specified in Montgomery form. Rewriting in Weierstrass form
+ * involves substitution of variables, so the base-point x coordinate is 9 + (486662 / 3).
+ *
+ * The Curve25519 paper doesn't say which of the two possible y values the base
+ * point has. The choice here is guided by language in the Ed25519 paper.
+ *
+ * (The other possible y value is 5F51E65E475F794B1FE122D388B72EB36DC2B28192839E4DD6163A5D81312C14)
+ */
+ ECPoint G = curve.DecodePoint(Hex.Decode("04"
+ + "2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD245A"
+ + "20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9"));
+
+ return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S);
+ }
+ }
+
+ /*
* secp192k1
*/
internal class Secp192k1Holder
@@ -239,19 +272,35 @@ namespace Org.BouncyCastle.Crypto.EC
}
}
- private static readonly IDictionary objIds = Platform.CreateHashtable();
- private static readonly IDictionary curves = Platform.CreateHashtable();
- private static readonly IDictionary names = Platform.CreateHashtable();
+ private static readonly IDictionary nameToCurve = Platform.CreateHashtable();
+ private static readonly IDictionary nameToOid = Platform.CreateHashtable();
+ private static readonly IDictionary oidToCurve = Platform.CreateHashtable();
+ private static readonly IDictionary oidToName = Platform.CreateHashtable();
+
+ private static void DefineCurve(string name, X9ECParametersHolder holder)
+ {
+ nameToCurve.Add(name, holder);
+ }
private static void DefineCurve(string name, DerObjectIdentifier oid, X9ECParametersHolder holder)
{
- objIds.Add(name, oid);
- names.Add(oid, name);
- curves.Add(oid, holder);
+ nameToCurve.Add(name, holder);
+ nameToOid.Add(name, oid);
+ oidToName.Add(oid, name);
+ oidToCurve.Add(oid, holder);
+ }
+
+ private static void DefineCurveAlias(string alias, DerObjectIdentifier oid)
+ {
+ alias = Platform.ToLowerInvariant(alias);
+ nameToOid.Add(alias, oid);
+ nameToCurve.Add(alias, oidToCurve[oid]);
}
static CustomNamedCurves()
{
+ DefineCurve("curve25519", Curve25519Holder.Instance);
+
DefineCurve("secp192k1", SecObjectIdentifiers.SecP192k1, Secp192k1Holder.Instance);
DefineCurve("secp192r1", SecObjectIdentifiers.SecP192r1, Secp192r1Holder.Instance);
DefineCurve("secp224k1", SecObjectIdentifiers.SecP224k1, Secp224k1Holder.Instance);
@@ -261,18 +310,17 @@ namespace Org.BouncyCastle.Crypto.EC
DefineCurve("secp384r1", SecObjectIdentifiers.SecP384r1, Secp384r1Holder.Instance);
DefineCurve("secp521r1", SecObjectIdentifiers.SecP521r1, Secp521r1Holder.Instance);
- objIds.Add(Platform.ToLowerInvariant("P-192"), SecObjectIdentifiers.SecP192r1);
- objIds.Add(Platform.ToLowerInvariant("P-224"), SecObjectIdentifiers.SecP224r1);
- objIds.Add(Platform.ToLowerInvariant("P-256"), SecObjectIdentifiers.SecP256r1);
- objIds.Add(Platform.ToLowerInvariant("P-384"), SecObjectIdentifiers.SecP384r1);
- objIds.Add(Platform.ToLowerInvariant("P-521"), SecObjectIdentifiers.SecP521r1);
+ DefineCurveAlias("P-192", SecObjectIdentifiers.SecP192r1);
+ DefineCurveAlias("P-224", SecObjectIdentifiers.SecP224r1);
+ DefineCurveAlias("P-256", SecObjectIdentifiers.SecP256r1);
+ DefineCurveAlias("P-384", SecObjectIdentifiers.SecP384r1);
+ DefineCurveAlias("P-521", SecObjectIdentifiers.SecP521r1);
}
public static X9ECParameters GetByName(string name)
{
- DerObjectIdentifier oid = (DerObjectIdentifier)objIds[Platform.ToLowerInvariant(name)];
-
- return oid == null ? null : GetByOid(oid);
+ X9ECParametersHolder holder = (X9ECParametersHolder)nameToCurve[Platform.ToLowerInvariant(name)];
+ return holder == null ? null : holder.Parameters;
}
/**
@@ -283,8 +331,7 @@ namespace Org.BouncyCastle.Crypto.EC
*/
public static X9ECParameters GetByOid(DerObjectIdentifier oid)
{
- X9ECParametersHolder holder = (X9ECParametersHolder)curves[oid];
-
+ X9ECParametersHolder holder = (X9ECParametersHolder)oidToCurve[oid];
return holder == null ? null : holder.Parameters;
}
@@ -296,7 +343,7 @@ namespace Org.BouncyCastle.Crypto.EC
*/
public static DerObjectIdentifier GetOid(string name)
{
- return (DerObjectIdentifier)objIds[Platform.ToLowerInvariant(name)];
+ return (DerObjectIdentifier)nameToOid[Platform.ToLowerInvariant(name)];
}
/**
@@ -304,7 +351,7 @@ namespace Org.BouncyCastle.Crypto.EC
*/
public static string GetName(DerObjectIdentifier oid)
{
- return (string)names[oid];
+ return (string)oidToName[oid];
}
/**
@@ -313,7 +360,7 @@ namespace Org.BouncyCastle.Crypto.EC
*/
public static IEnumerable Names
{
- get { return new EnumerableProxy(objIds.Keys); }
+ get { return new EnumerableProxy(nameToCurve.Keys); }
}
}
}
diff --git a/crypto/src/math/ec/ECPoint.cs b/crypto/src/math/ec/ECPoint.cs
index cd6811dfa..0430a6110 100644
--- a/crypto/src/math/ec/ECPoint.cs
+++ b/crypto/src/math/ec/ECPoint.cs
@@ -1191,13 +1191,13 @@ namespace Org.BouncyCastle.Math.EC
ECFieldElement W = ZZ[1];
if (W == null)
{
- // NOTE: Rarely, twicePlus will result in the need for a lazy W1 calculation here
+ // NOTE: Rarely, TwicePlus will result in the need for a lazy W1 calculation here
ZZ[1] = W = CalculateJacobianModifiedW(ZZ[0], null);
}
return W;
}
- protected FpPoint TwiceJacobianModified(bool calculateW)
+ protected virtual FpPoint TwiceJacobianModified(bool calculateW)
{
ECFieldElement X1 = this.RawXCoord, Y1 = this.RawYCoord, Z1 = this.RawZCoords[0], W1 = GetJacobianModifiedW();
diff --git a/crypto/src/math/ec/custom/djb/Curve25519.cs b/crypto/src/math/ec/custom/djb/Curve25519.cs
new file mode 100644
index 000000000..3dbdac051
--- /dev/null
+++ b/crypto/src/math/ec/custom/djb/Curve25519.cs
@@ -0,0 +1,100 @@
+using System;
+
+using Org.BouncyCastle.Math.EC.Custom.Sec;
+using Org.BouncyCastle.Math.Field;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Djb
+{
+ internal class Curve25519
+ : ECCurve
+ {
+ public static readonly BigInteger q = Nat256.ToBigInteger(Curve25519Field.P);
+
+ private const int Curve25519_DEFAULT_COORDS = COORD_JACOBIAN_MODIFIED;
+
+ protected readonly Curve25519Point m_infinity;
+
+ public Curve25519()
+ : base(FiniteFields.GetPrimeField(q))
+ {
+ this.m_infinity = new Curve25519Point(this, null, null);
+
+ this.m_a = FromBigInteger(new BigInteger(1,
+ Hex.Decode("2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA984914A144")));
+ this.m_b = FromBigInteger(new BigInteger(1,
+ Hex.Decode("7B425ED097B425ED097B425ED097B425ED097B425ED097B4260B5E9C7710C864")));
+ this.m_order = new BigInteger(1, Hex.Decode("1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED"));
+ this.m_cofactor = BigInteger.ValueOf(8);
+ this.m_coord = Curve25519_DEFAULT_COORDS;
+ }
+
+ protected override ECCurve CloneCurve()
+ {
+ return new Curve25519();
+ }
+
+ public override bool SupportsCoordinateSystem(int coord)
+ {
+ switch (coord)
+ {
+ case COORD_JACOBIAN_MODIFIED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public virtual BigInteger Q
+ {
+ get { return q; }
+ }
+
+ public override ECPoint Infinity
+ {
+ get { return m_infinity; }
+ }
+
+ public override int FieldSize
+ {
+ get { return q.BitLength; }
+ }
+
+ public override ECFieldElement FromBigInteger(BigInteger x)
+ {
+ return new Curve25519FieldElement(x);
+ }
+
+ protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression)
+ {
+ return new Curve25519Point(this, x, y, withCompression);
+ }
+
+ protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+ {
+ return new Curve25519Point(this, x, y, zs, withCompression);
+ }
+
+ protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
+ {
+ ECFieldElement x = FromBigInteger(X1);
+ ECFieldElement alpha = x.Square().Add(A).Multiply(x).Add(B);
+ ECFieldElement beta = alpha.Sqrt();
+
+ //
+ // if we can't find a sqrt we haven't got a point on the
+ // curve - run!
+ //
+ if (beta == null)
+ throw new ArithmeticException("Invalid point compression");
+
+ if (beta.TestBitZero() != (yTilde == 1))
+ {
+ // Use the other root
+ beta = beta.Negate();
+ }
+
+ return new Curve25519Point(this, x, beta, true);
+ }
+ }
+}
diff --git a/crypto/src/math/ec/custom/sec/Curve25519Field.cs b/crypto/src/math/ec/custom/djb/Curve25519Field.cs
index ee0f88311..084ca96af 100644
--- a/crypto/src/math/ec/custom/sec/Curve25519Field.cs
+++ b/crypto/src/math/ec/custom/djb/Curve25519Field.cs
@@ -1,7 +1,9 @@
using System;
using System.Diagnostics;
-namespace Org.BouncyCastle.Math.EC.Custom.Sec
+using Org.BouncyCastle.Math.EC.Custom.Sec;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Djb
{
internal class Curve25519Field
{
@@ -190,7 +192,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
c >>= 32;
if (c != 0)
{
- Nat.IncAt(7, z, 1);
+ Nat.IncAt(8, z, 1);
}
z[7] &= P7;
}
@@ -202,7 +204,7 @@ namespace Org.BouncyCastle.Math.EC.Custom.Sec
c >>= 32;
if (c != 0)
{
- Nat.DecAt(7, z, 1);
+ Nat.DecAt(8, z, 1);
}
z[7] &= P7;
}
diff --git a/crypto/src/math/ec/custom/djb/Curve25519FieldElement.cs b/crypto/src/math/ec/custom/djb/Curve25519FieldElement.cs
new file mode 100644
index 000000000..8d5a80326
--- /dev/null
+++ b/crypto/src/math/ec/custom/djb/Curve25519FieldElement.cs
@@ -0,0 +1,233 @@
+using System;
+
+using Org.BouncyCastle.Math.EC.Custom.Sec;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Djb
+{
+ internal class Curve25519FieldElement
+ : ECFieldElement
+ {
+ public static readonly BigInteger Q = Curve25519.q;
+
+ // Calculated as ECConstants.TWO.modPow(Q.shiftRight(2), Q)
+ private static readonly uint[] PRECOMP_POW2 = new uint[]{ 0x4a0ea0b0, 0xc4ee1b27, 0xad2fe478, 0x2f431806,
+ 0x3dfbd7a7, 0x2b4d0099, 0x4fc1df0b, 0x2b832480 };
+
+ protected internal readonly uint[] x;
+
+ public Curve25519FieldElement(BigInteger x)
+ {
+ if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0)
+ throw new ArgumentException("value invalid for Curve25519FieldElement", "x");
+
+ this.x = Curve25519Field.FromBigInteger(x);
+ }
+
+ public Curve25519FieldElement()
+ {
+ this.x = Nat256.Create();
+ }
+
+ protected internal Curve25519FieldElement(uint[] x)
+ {
+ this.x = x;
+ }
+
+ public override bool IsZero
+ {
+ get { return Nat256.IsZero(x); }
+ }
+
+ public override bool IsOne
+ {
+ get { return Nat256.IsOne(x); }
+ }
+
+ public override bool TestBitZero()
+ {
+ return Nat256.GetBit(x, 0) == 1;
+ }
+
+ public override BigInteger ToBigInteger()
+ {
+ return Nat256.ToBigInteger(x);
+ }
+
+ public override string FieldName
+ {
+ get { return "Curve25519Field"; }
+ }
+
+ public override int FieldSize
+ {
+ get { return Q.BitLength; }
+ }
+
+ public override ECFieldElement Add(ECFieldElement b)
+ {
+ uint[] z = Nat256.Create();
+ Curve25519Field.Add(x, ((Curve25519FieldElement)b).x, z);
+ return new Curve25519FieldElement(z);
+ }
+
+ public override ECFieldElement AddOne()
+ {
+ uint[] z = Nat256.Create();
+ Curve25519Field.AddOne(x, z);
+ return new Curve25519FieldElement(z);
+ }
+
+ public override ECFieldElement Subtract(ECFieldElement b)
+ {
+ uint[] z = Nat256.Create();
+ Curve25519Field.Subtract(x, ((Curve25519FieldElement)b).x, z);
+ return new Curve25519FieldElement(z);
+ }
+
+ public override ECFieldElement Multiply(ECFieldElement b)
+ {
+ uint[] z = Nat256.Create();
+ Curve25519Field.Multiply(x, ((Curve25519FieldElement)b).x, z);
+ return new Curve25519FieldElement(z);
+ }
+
+ public override ECFieldElement Divide(ECFieldElement b)
+ {
+ //return Multiply(b.Invert());
+ uint[] z = Nat256.Create();
+ Mod.Invert(Curve25519Field.P, ((Curve25519FieldElement)b).x, z);
+ Curve25519Field.Multiply(z, x, z);
+ return new Curve25519FieldElement(z);
+ }
+
+ public override ECFieldElement Negate()
+ {
+ uint[] z = Nat256.Create();
+ Curve25519Field.Negate(x, z);
+ return new Curve25519FieldElement(z);
+ }
+
+ public override ECFieldElement Square()
+ {
+ uint[] z = Nat256.Create();
+ Curve25519Field.Square(x, z);
+ return new Curve25519FieldElement(z);
+ }
+
+ public override ECFieldElement Invert()
+ {
+ //return new Curve25519FieldElement(ToBigInteger().ModInverse(Q));
+ uint[] z = Nat256.Create();
+ Mod.Invert(Curve25519Field.P, x, z);
+ return new Curve25519FieldElement(z);
+ }
+
+ /**
+ * return a sqrt root - the routine verifies that the calculation returns the right value - if
+ * none exists it returns null.
+ */
+ public override ECFieldElement Sqrt()
+ {
+ /*
+ * Q == 8m + 5, so we use Pocklington's method for this case.
+ *
+ * First, raise this element to the exponent 2^252 - 2^1 (i.e. m + 1)
+ *
+ * Breaking up the exponent's binary representation into "repunits", we get:
+ * { 251 1s } { 1 0s }
+ *
+ * Therefore we need an addition chain containing 251 (the lengths of the repunits)
+ * We use: 1, 2, 3, 4, 7, 11, 15, 30, 60, 120, 131, [251]
+ */
+
+ uint[] x1 = this.x;
+ if (Nat256.IsZero(x1) || Nat256.IsOne(x1))
+ return this;
+
+ uint[] x2 = Nat256.Create();
+ Curve25519Field.Square(x1, x2);
+ Curve25519Field.Multiply(x2, x1, x2);
+ uint[] x3 = x2;
+ Curve25519Field.Square(x2, x3);
+ Curve25519Field.Multiply(x3, x1, x3);
+ uint[] x4 = Nat256.Create();
+ Curve25519Field.Square(x3, x4);
+ Curve25519Field.Multiply(x4, x1, x4);
+ uint[] x7 = Nat256.Create();
+ Curve25519Field.SquareN(x4, 3, x7);
+ Curve25519Field.Multiply(x7, x3, x7);
+ uint[] x11 = x3;
+ Curve25519Field.SquareN(x7, 4, x11);
+ Curve25519Field.Multiply(x11, x4, x11);
+ uint[] x15 = x7;
+ Curve25519Field.SquareN(x11, 4, x15);
+ Curve25519Field.Multiply(x15, x4, x15);
+ uint[] x30 = x4;
+ Curve25519Field.SquareN(x15, 15, x30);
+ Curve25519Field.Multiply(x30, x15, x30);
+ uint[] x60 = x15;
+ Curve25519Field.SquareN(x30, 30, x60);
+ Curve25519Field.Multiply(x60, x30, x60);
+ uint[] x120 = x30;
+ Curve25519Field.SquareN(x60, 60, x120);
+ Curve25519Field.Multiply(x120, x60, x120);
+ uint[] x131 = x60;
+ Curve25519Field.SquareN(x120, 11, x131);
+ Curve25519Field.Multiply(x131, x11, x131);
+ uint[] x251 = x11;
+ Curve25519Field.SquareN(x131, 120, x251);
+ Curve25519Field.Multiply(x251, x120, x251);
+
+ uint[] t1 = x251;
+ Curve25519Field.Square(t1, t1);
+
+ uint[] t2 = x120;
+ Curve25519Field.Square(t1, t2);
+
+ if (Nat256.Eq(x1, t2))
+ {
+ return new Curve25519FieldElement(t1);
+ }
+
+ /*
+ * If the first guess is incorrect, we multiply by a precomputed power of 2 to get the second guess,
+ * which is ((4x)^(m + 1))/2 mod Q
+ */
+ Curve25519Field.Multiply(t1, PRECOMP_POW2, t1);
+
+ Curve25519Field.Square(t1, t2);
+
+ if (Nat256.Eq(x1, t2))
+ {
+ return new Curve25519FieldElement(t1);
+ }
+
+ return null;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as Curve25519FieldElement);
+ }
+
+ public override bool Equals(ECFieldElement other)
+ {
+ return Equals(other as Curve25519FieldElement);
+ }
+
+ public virtual bool Equals(Curve25519FieldElement other)
+ {
+ if (this == other)
+ return true;
+ if (null == other)
+ return false;
+ return Nat256.Eq(x, other.x);
+ }
+
+ public override int GetHashCode()
+ {
+ return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 8);
+ }
+ }
+}
diff --git a/crypto/src/math/ec/custom/djb/Curve25519Point.cs b/crypto/src/math/ec/custom/djb/Curve25519Point.cs
new file mode 100644
index 000000000..65b6792eb
--- /dev/null
+++ b/crypto/src/math/ec/custom/djb/Curve25519Point.cs
@@ -0,0 +1,264 @@
+using System;
+
+using Org.BouncyCastle.Math.EC.Custom.Sec;
+
+namespace Org.BouncyCastle.Math.EC.Custom.Djb
+{
+ internal class Curve25519Point
+ : ECPointBase
+ {
+ /**
+ * Create a point which encodes with point compression.
+ *
+ * @param curve the curve to use
+ * @param x affine x co-ordinate
+ * @param y affine y co-ordinate
+ *
+ * @deprecated Use ECCurve.createPoint to construct points
+ */
+ public Curve25519Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
+ : this(curve, x, y, false)
+ {
+ }
+
+ /**
+ * Create a point that encodes with or without point compresion.
+ *
+ * @param curve the curve to use
+ * @param x affine x co-ordinate
+ * @param y affine y co-ordinate
+ * @param withCompression if true encode with point compression
+ *
+ * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)}
+ */
+ public Curve25519Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression)
+ : base(curve, x, y, withCompression)
+ {
+ if ((x == null) != (y == null))
+ throw new ArgumentException("Exactly one of the field elements is null");
+ }
+
+ internal Curve25519Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+ : base(curve, x, y, zs, withCompression)
+ {
+ }
+
+ protected override ECPoint Detach()
+ {
+ return new Curve25519Point(null, AffineXCoord, AffineYCoord);
+ }
+
+ protected internal override bool CompressionYTilde
+ {
+ get { return this.AffineYCoord.TestBitZero(); }
+ }
+
+ public override ECFieldElement GetZCoord(int index)
+ {
+ if (index == 1)
+ {
+ return GetJacobianModifiedW();
+ }
+
+ return base.GetZCoord(index);
+ }
+
+ public override ECPoint Add(ECPoint b)
+ {
+ if (this.IsInfinity)
+ return b;
+ if (b.IsInfinity)
+ return this;
+ if (this == b)
+ return Twice();
+
+ ECCurve curve = this.Curve;
+
+ ECFieldElement X1 = this.RawXCoord, Y1 = this.RawYCoord;
+ ECFieldElement X2 = b.RawXCoord, Y2 = b.RawYCoord;
+
+ ECFieldElement Z1 = this.RawZCoords[0];
+ ECFieldElement Z2 = b.RawZCoords[0];
+
+ bool Z1IsOne = Z1.IsOne;
+
+ ECFieldElement Z1Squared, U2, S2;
+ if (Z1IsOne)
+ {
+ Z1Squared = Z1; U2 = X2; S2 = Y2;
+ }
+ else
+ {
+ Z1Squared = Z1.Square();
+ U2 = Z1Squared.Multiply(X2);
+ ECFieldElement Z1Cubed = Z1Squared.Multiply(Z1);
+ S2 = Z1Cubed.Multiply(Y2);
+ }
+
+ bool Z2IsOne = Z2.IsOne;
+ ECFieldElement Z2Squared, U1, S1;
+ if (Z2IsOne)
+ {
+ Z2Squared = Z2; U1 = X1; S1 = Y1;
+ }
+ else
+ {
+ Z2Squared = Z2.Square();
+ U1 = Z2Squared.Multiply(X1);
+ ECFieldElement Z2Cubed = Z2Squared.Multiply(Z2);
+ S1 = Z2Cubed.Multiply(Y1);
+ }
+
+ ECFieldElement H = U1.Subtract(U2);
+ ECFieldElement R = S1.Subtract(S2);
+
+ // Check if b == this or b == -this
+ if (H.IsZero)
+ {
+ if (R.IsZero)
+ {
+ // this == b, i.e. this must be doubled
+ return this.Twice();
+ }
+
+ // this == -b, i.e. the result is the point at infinity
+ return curve.Infinity;
+ }
+
+ ECFieldElement HSquared = H.Square();
+ ECFieldElement G = HSquared.Multiply(H);
+ ECFieldElement V = HSquared.Multiply(U1);
+
+ ECFieldElement X3 = R.Square().Add(G).Subtract(Two(V));
+ ECFieldElement Y3 = V.Subtract(X3).MultiplyMinusProduct(R, G, S1);
+
+ ECFieldElement Z3 = H;
+ if (!Z1IsOne)
+ {
+ Z3 = Z3.Multiply(Z1);
+ }
+ if (!Z2IsOne)
+ {
+ Z3 = Z3.Multiply(Z2);
+ }
+
+ ECFieldElement Z3Squared = (Z3 == H) ? HSquared : null;
+
+ // TODO If the result will only be used in a subsequent addition, we don't need W3
+ ECFieldElement W3 = CalculateJacobianModifiedW(Z3, Z3Squared);
+
+ ECFieldElement[] zs = new ECFieldElement[]{ Z3, W3 };
+
+ return new Curve25519Point(curve, X3, Y3, zs, IsCompressed);
+ }
+
+ public override ECPoint Twice()
+ {
+ if (this.IsInfinity)
+ return this;
+
+ ECCurve curve = this.Curve;
+
+ ECFieldElement Y1 = this.RawYCoord;
+ if (Y1.IsZero)
+ return curve.Infinity;
+
+ return TwiceJacobianModified(true);
+ }
+
+ public override ECPoint TwicePlus(ECPoint b)
+ {
+ if (this == b)
+ return ThreeTimes();
+ if (this.IsInfinity)
+ return b;
+ if (b.IsInfinity)
+ return Twice();
+
+ ECFieldElement Y1 = this.RawYCoord;
+ if (Y1.IsZero)
+ return b;
+
+ return TwiceJacobianModified(false).Add(b);
+ }
+
+ public override ECPoint ThreeTimes()
+ {
+ if (this.IsInfinity || this.RawYCoord.IsZero)
+ return this;
+
+ return TwiceJacobianModified(false).Add(this);
+ }
+
+ protected virtual ECFieldElement Two(ECFieldElement x)
+ {
+ return x.Add(x);
+ }
+
+ protected virtual ECFieldElement Three(ECFieldElement x)
+ {
+ return Two(x).Add(x);
+ }
+
+ public override ECPoint Subtract(ECPoint b)
+ {
+ if (b.IsInfinity)
+ return this;
+
+ return Add(b.Negate());
+ }
+
+ public override ECPoint Negate()
+ {
+ if (IsInfinity)
+ return this;
+
+ return new Curve25519Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed);
+ }
+
+ protected virtual ECFieldElement CalculateJacobianModifiedW(ECFieldElement Z, ECFieldElement ZSquared)
+ {
+ ECFieldElement a4 = this.Curve.A;
+ if (Z.IsOne)
+ return a4;
+
+ if (ZSquared == null)
+ {
+ ZSquared = Z.Square();
+ }
+
+ return ZSquared.Square().Multiply(a4);
+ }
+
+ protected virtual ECFieldElement GetJacobianModifiedW()
+ {
+ ECFieldElement[] ZZ = this.RawZCoords;
+ ECFieldElement W = ZZ[1];
+ if (W == null)
+ {
+ // NOTE: Rarely, TwicePlus will result in the need for a lazy W1 calculation here
+ ZZ[1] = W = CalculateJacobianModifiedW(ZZ[0], null);
+ }
+ return W;
+ }
+
+ protected virtual Curve25519Point TwiceJacobianModified(bool calculateW)
+ {
+ ECFieldElement X1 = this.RawXCoord, Y1 = this.RawYCoord, Z1 = this.RawZCoords[0], W1 = GetJacobianModifiedW();
+
+ ECFieldElement X1Squared = X1.Square();
+ ECFieldElement M = Three(X1Squared).Add(W1);
+ ECFieldElement _2Y1 = Two(Y1);
+ ECFieldElement _2Y1Squared = _2Y1.Multiply(Y1);
+ ECFieldElement S = Two(X1.Multiply(_2Y1Squared));
+ ECFieldElement X3 = M.Square().Subtract(Two(S));
+ ECFieldElement _4T = _2Y1Squared.Square();
+ ECFieldElement _8T = Two(_4T);
+ ECFieldElement Y3 = M.Multiply(S.Subtract(X3)).Subtract(_8T);
+ ECFieldElement W3 = calculateW ? Two(_8T.Multiply(W1)) : null;
+ ECFieldElement Z3 = Z1.IsOne ? _2Y1 : _2Y1.Multiply(Z1);
+
+ return new Curve25519Point(this.Curve, X3, Y3, new ECFieldElement[] { Z3, W3 }, IsCompressed);
+ }
+ }
+}
|