public ECDomainParameters( ECCurve curve, ECPoint g, BigInteger n) : this(curve, g, n, BigInteger.One) { }
public X9ECParameters( ECCurve curve, ECPoint g, BigInteger n) : this(curve, g, n, BigInteger.One, null) { }
public static ECPoint ImportPoint(ECCurve c, ECPoint p) { ECCurve cp = p.Curve; if (!c.Equals(cp)) throw new ArgumentException("Point must be on the same curve"); return c.ImportPoint(p); }
public ECDomainParameters( ECCurve curve, ECPoint g, BigInteger n, BigInteger h) : this(curve, g, n, h, null) { }
public ECPublicKeyParameters( ECPoint q, DerObjectIdentifier publicKeyParamSet) : base("ECGOST3410", false, publicKeyParamSet) { if (q == null) throw new ArgumentNullException("q"); this.q = q.Normalize(); }
public ECPublicKeyParameters( string algorithm, ECPoint q, DerObjectIdentifier publicKeyParamSet) : base(algorithm, false, publicKeyParamSet) { if (q == null) throw new ArgumentNullException("q"); this.q = q.Normalize(); }
public ECPublicKeyParameters( string algorithm, ECPoint q, ECDomainParameters parameters) : base(algorithm, false, parameters) { if (q == null) throw new ArgumentNullException("q"); this.q = q.Normalize(); }
public X9ECParameters( Asn1Sequence seq) { if (!(seq[0] is DerInteger) || !((DerInteger) seq[0]).Value.Equals(BigInteger.One)) { throw new ArgumentException("bad version in X9ECParameters"); } X9Curve x9c = null; if (seq[2] is X9Curve) { x9c = (X9Curve) seq[2]; } else { x9c = new X9Curve( new X9FieldID( (Asn1Sequence) seq[1]), (Asn1Sequence) seq[2]); } this.curve = x9c.Curve; if (seq[3] is X9ECPoint) { this.g = ((X9ECPoint) seq[3]).Point; } else { this.g = new X9ECPoint(curve, (Asn1OctetString) seq[3]).Point; } this.n = ((DerInteger) seq[4]).Value; this.seed = x9c.GetSeed(); if (seq.Count == 6) { this.h = ((DerInteger) seq[5]).Value; } }
public ECDomainParameters( ECCurve curve, ECPoint g, BigInteger n, BigInteger h, byte[] seed) { if (curve == null) throw new ArgumentNullException("curve"); if (g == null) throw new ArgumentNullException("g"); if (n == null) throw new ArgumentNullException("n"); if (h == null) throw new ArgumentNullException("h"); this.curve = curve; this.g = g.Normalize(); this.n = n; this.h = h; this.seed = Arrays.Clone(seed); }
/** * Simple shift-and-add multiplication. Serves as reference implementation * to verify (possibly faster) implementations, and for very small scalars. * * @param p * The point to multiply. * @param k * The multiplier. * @return The result of the point multiplication <code>kP</code>. */ public static ECPoint ReferenceMultiply(ECPoint p, BigInteger k) { BigInteger x = k.Abs(); ECPoint q = p.Curve.Infinity; int t = x.BitLength; if (t > 0) { if (x.TestBit(0)) { q = p; } for (int i = 1; i < t; i++) { p = p.Twice(); if (x.TestBit(i)) { q = q.Add(p); } } } return k.SignValue < 0 ? q.Negate() : q; }
// The ECMQV Primitive as described in SEC-1, 3.4 private static ECPoint CalculateMqvAgreement( ECDomainParameters parameters, ECPrivateKeyParameters d1U, ECPrivateKeyParameters d2U, ECPublicKeyParameters Q2U, ECPublicKeyParameters Q1V, ECPublicKeyParameters Q2V) { BigInteger n = parameters.N; int e = (n.BitLength + 1) / 2; BigInteger powE = BigInteger.One.ShiftLeft(e); ECCurve curve = parameters.Curve; ECPoint[] points = new ECPoint[]{ // The Q2U public key is optional ECAlgorithms.ImportPoint(curve, Q2U == null ? parameters.G.Multiply(d2U.D) : Q2U.Q), ECAlgorithms.ImportPoint(curve, Q1V.Q), ECAlgorithms.ImportPoint(curve, Q2V.Q) }; curve.NormalizeAll(points); ECPoint q2u = points[0], q1v = points[1], q2v = points[2]; BigInteger x = q2u.AffineXCoord.ToBigInteger(); BigInteger xBar = x.Mod(powE); BigInteger Q2UBar = xBar.SetBit(e); BigInteger s = d1U.D.Multiply(Q2UBar).Add(d2U.D).Mod(n); BigInteger xPrime = q2v.AffineXCoord.ToBigInteger(); BigInteger xPrimeBar = xPrime.Mod(powE); BigInteger Q2VBar = xPrimeBar.SetBit(e); BigInteger hs = parameters.H.Multiply(s).Mod(n); return ECAlgorithms.SumOfTwoMultiplies( q1v, Q2VBar.Multiply(hs).Mod(n), q2v, hs); }
internal static ECPoint ImplSumOfMultiplies(ECPoint[] ps, BigInteger[] ks) { int count = ps.Length; bool[] negs = new bool[count]; WNafPreCompInfo[] infos = new WNafPreCompInfo[count]; byte[][] wnafs = new byte[count][]; for (int i = 0; i < count; ++i) { BigInteger ki = ks[i]; negs[i] = ki.SignValue < 0; ki = ki.Abs(); int width = System.Math.Max(2, System.Math.Min(16, WNafUtilities.GetWindowSize(ki.BitLength))); infos[i] = WNafUtilities.Precompute(ps[i], width, true); wnafs[i] = WNafUtilities.GenerateWindowNaf(width, ki); } return ImplSumOfMultiplies(negs, infos, wnafs); }
internal static ECPoint ImplShamirsTrickWNaf(ECPoint P, BigInteger k, ECPointMap pointMapQ, BigInteger l) { bool negK = k.SignValue < 0, negL = l.SignValue < 0; k = k.Abs(); l = l.Abs(); int width = System.Math.Max(2, System.Math.Min(16, WNafUtilities.GetWindowSize(System.Math.Max(k.BitLength, l.BitLength)))); ECPoint Q = WNafUtilities.MapPointWithPrecomp(P, width, true, pointMapQ); WNafPreCompInfo infoP = WNafUtilities.GetWNafPreCompInfo(P); WNafPreCompInfo infoQ = WNafUtilities.GetWNafPreCompInfo(Q); ECPoint[] preCompP = negK ? infoP.PreCompNeg : infoP.PreComp; ECPoint[] preCompQ = negL ? infoQ.PreCompNeg : infoQ.PreComp; ECPoint[] preCompNegP = negK ? infoP.PreComp : infoP.PreCompNeg; ECPoint[] preCompNegQ = negL ? infoQ.PreComp : infoQ.PreCompNeg; byte[] wnafP = WNafUtilities.GenerateWindowNaf(width, k); byte[] wnafQ = WNafUtilities.GenerateWindowNaf(width, l); return ImplShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ, preCompNegQ, wnafQ); }
internal static ECPoint ImplShamirsTrickJsf(ECPoint P, BigInteger k, ECPoint Q, BigInteger l) { ECCurve curve = P.Curve; ECPoint infinity = curve.Infinity; // TODO conjugate co-Z addition (ZADDC) can return both of these ECPoint PaddQ = P.Add(Q); ECPoint PsubQ = P.Subtract(Q); ECPoint[] points = new ECPoint[] { Q, PsubQ, P, PaddQ }; curve.NormalizeAll(points); ECPoint[] table = new ECPoint[] { points[3].Negate(), points[2].Negate(), points[1].Negate(), points[0].Negate(), infinity, points[0], points[1], points[2], points[3] }; byte[] jsf = WNafUtilities.GenerateJsf(k, l); ECPoint R = infinity; int i = jsf.Length; while (--i >= 0) { int jsfi = jsf[i]; // NOTE: The shifting ensures the sign is extended correctly int kDigit = ((jsfi << 24) >> 28), lDigit = ((jsfi << 28) >> 28); int index = 4 + (kDigit * 3) + lDigit; R = R.TwicePlus(table[index]); } return R; }
public static ECPoint ValidatePoint(ECPoint p) { if (!p.IsValid()) throw new ArgumentException("Invalid point", "p"); return p; }
public static ECPoint SumOfTwoMultiplies(ECPoint P, BigInteger a, ECPoint Q, BigInteger b) { ECCurve cp = P.Curve; Q = ImportPoint(cp, Q); // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick if (cp is F2mCurve) { F2mCurve f2mCurve = (F2mCurve) cp; if (f2mCurve.IsKoblitz) { return ValidatePoint(P.Multiply(a).Add(Q.Multiply(b))); } } GlvEndomorphism glvEndomorphism = cp.GetEndomorphism() as GlvEndomorphism; if (glvEndomorphism != null) { return ValidatePoint( ImplSumOfMultipliesGlv(new ECPoint[] { P, Q }, new BigInteger[] { a, b }, glvEndomorphism)); } return ValidatePoint(ImplShamirsTrickWNaf(P, a, Q, b)); }
public abstract ECPoint Add(ECPoint b);
private static ECPoint ImplShamirsTrickWNaf(ECPoint[] preCompP, ECPoint[] preCompNegP, byte[] wnafP, ECPoint[] preCompQ, ECPoint[] preCompNegQ, byte[] wnafQ) { int len = System.Math.Max(wnafP.Length, wnafQ.Length); ECCurve curve = preCompP[0].Curve; ECPoint infinity = curve.Infinity; ECPoint R = infinity; int zeroes = 0; for (int i = len - 1; i >= 0; --i) { int wiP = i < wnafP.Length ? (int)(sbyte)wnafP[i] : 0; int wiQ = i < wnafQ.Length ? (int)(sbyte)wnafQ[i] : 0; if ((wiP | wiQ) == 0) { ++zeroes; continue; } ECPoint r = infinity; if (wiP != 0) { int nP = System.Math.Abs(wiP); ECPoint[] tableP = wiP < 0 ? preCompNegP : preCompP; r = r.Add(tableP[nP >> 1]); } if (wiQ != 0) { int nQ = System.Math.Abs(wiQ); ECPoint[] tableQ = wiQ < 0 ? preCompNegQ : preCompQ; r = r.Add(tableQ[nQ >> 1]); } if (zeroes > 0) { R = R.TimesPow2(zeroes); zeroes = 0; } R = R.TwicePlus(r); } if (zeroes > 0) { R = R.TimesPow2(zeroes); } return R; }
/* * "Shamir's Trick", originally due to E. G. Straus * (Addition chains of vectors. American Mathematical Monthly, * 71(7):806-808, Aug./Sept. 1964) * * Input: The points P, Q, scalar k = (km?, ... , k1, k0) * and scalar l = (lm?, ... , l1, l0). * Output: R = k * P + l * Q. * 1: Z <- P + Q * 2: R <- O * 3: for i from m-1 down to 0 do * 4: R <- R + R {point doubling} * 5: if (ki = 1) and (li = 0) then R <- R + P end if * 6: if (ki = 0) and (li = 1) then R <- R + Q end if * 7: if (ki = 1) and (li = 1) then R <- R + Z end if * 8: end for * 9: return R */ public static ECPoint ShamirsTrick(ECPoint P, BigInteger k, ECPoint Q, BigInteger l) { ECCurve cp = P.Curve; Q = ImportPoint(cp, Q); return ValidatePoint(ImplShamirsTrickJsf(P, k, Q, l)); }
// B.3 pg 62 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; int coord = curve.CoordinateSystem; ECFieldElement X1 = this.RawXCoord, Y1 = this.RawYCoord; ECFieldElement X2 = b.RawXCoord, Y2 = b.RawYCoord; switch (coord) { case ECCurve.COORD_AFFINE: { ECFieldElement dx = X2.Subtract(X1), dy = Y2.Subtract(Y1); if (dx.IsZero) { if (dy.IsZero) { // this == b, i.e. this must be doubled return Twice(); } // this == -b, i.e. the result is the point at infinity return Curve.Infinity; } ECFieldElement gamma = dy.Divide(dx); ECFieldElement X3 = gamma.Square().Subtract(X1).Subtract(X2); ECFieldElement Y3 = gamma.Multiply(X1.Subtract(X3)).Subtract(Y1); return new FpPoint(Curve, X3, Y3, IsCompressed); } case ECCurve.COORD_HOMOGENEOUS: { ECFieldElement Z1 = this.RawZCoords[0]; ECFieldElement Z2 = b.RawZCoords[0]; bool Z1IsOne = Z1.IsOne; bool Z2IsOne = Z2.IsOne; ECFieldElement u1 = Z1IsOne ? Y2 : Y2.Multiply(Z1); ECFieldElement u2 = Z2IsOne ? Y1 : Y1.Multiply(Z2); ECFieldElement u = u1.Subtract(u2); ECFieldElement v1 = Z1IsOne ? X2 : X2.Multiply(Z1); ECFieldElement v2 = Z2IsOne ? X1 : X1.Multiply(Z2); ECFieldElement v = v1.Subtract(v2); // Check if b == this or b == -this if (v.IsZero) { if (u.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; } // TODO Optimize for when w == 1 ECFieldElement w = Z1IsOne ? Z2 : Z2IsOne ? Z1 : Z1.Multiply(Z2); ECFieldElement vSquared = v.Square(); ECFieldElement vCubed = vSquared.Multiply(v); ECFieldElement vSquaredV2 = vSquared.Multiply(v2); ECFieldElement A = u.Square().Multiply(w).Subtract(vCubed).Subtract(Two(vSquaredV2)); ECFieldElement X3 = v.Multiply(A); ECFieldElement Y3 = vSquaredV2.Subtract(A).MultiplyMinusProduct(u, u2, vCubed); ECFieldElement Z3 = vCubed.Multiply(w); return new FpPoint(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed); } case ECCurve.COORD_JACOBIAN: case ECCurve.COORD_JACOBIAN_MODIFIED: { ECFieldElement Z1 = this.RawZCoords[0]; ECFieldElement Z2 = b.RawZCoords[0]; bool Z1IsOne = Z1.IsOne; ECFieldElement X3, Y3, Z3, Z3Squared = null; if (!Z1IsOne && Z1.Equals(Z2)) { // TODO Make this available as public method coZAdd? ECFieldElement dx = X1.Subtract(X2), dy = Y1.Subtract(Y2); if (dx.IsZero) { if (dy.IsZero) { return Twice(); } return curve.Infinity; } ECFieldElement C = dx.Square(); ECFieldElement W1 = X1.Multiply(C), W2 = X2.Multiply(C); ECFieldElement A1 = W1.Subtract(W2).Multiply(Y1); X3 = dy.Square().Subtract(W1).Subtract(W2); Y3 = W1.Subtract(X3).Multiply(dy).Subtract(A1); Z3 = dx; if (Z1IsOne) { Z3Squared = C; } else { Z3 = Z3.Multiply(Z1); } } else { 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); X3 = R.Square().Add(G).Subtract(Two(V)); Y3 = V.Subtract(X3).MultiplyMinusProduct(R, G, S1); Z3 = H; if (!Z1IsOne) { Z3 = Z3.Multiply(Z1); } if (!Z2IsOne) { Z3 = Z3.Multiply(Z2); } // Alternative calculation of Z3 using fast square //X3 = four(X3); //Y3 = eight(Y3); //Z3 = doubleProductFromSquares(Z1, Z2, Z1Squared, Z2Squared).Multiply(H); if (Z3 == H) { Z3Squared = HSquared; } } ECFieldElement[] zs; if (coord == ECCurve.COORD_JACOBIAN_MODIFIED) { // TODO If the result will only be used in a subsequent addition, we don't need W3 ECFieldElement W3 = CalculateJacobianModifiedW(Z3, Z3Squared); zs = new ECFieldElement[] { Z3, W3 }; } else { zs = new ECFieldElement[] { Z3 }; } return new FpPoint(curve, X3, Y3, zs, IsCompressed); } default: { throw new InvalidOperationException("unsupported coordinate system"); } } }
/* (non-Javadoc) * @see BitcoinKit.BouncyCastle.math.ec.ECPoint#subtract(BitcoinKit.BouncyCastle.math.ec.ECPoint) */ public override ECPoint Subtract( ECPoint b) { CheckPoints(this, b); return SubtractSimple((F2mPoint) b); }
public virtual ECPoint TwicePlus(ECPoint b) { return Twice().Add(b); }
public abstract ECPoint Subtract(ECPoint b);
public virtual bool Equals(ECPoint other) { if (this == other) return true; if (null == other) return false; ECCurve c1 = this.Curve, c2 = other.Curve; bool n1 = (null == c1), n2 = (null == c2); bool i1 = IsInfinity, i2 = other.IsInfinity; if (i1 || i2) { return (i1 && i2) && (n1 || n2 || c1.Equals(c2)); } ECPoint p1 = this, p2 = other; if (n1 && n2) { // Points with null curve are in affine form, so already normalized } else if (n1) { p2 = p2.Normalize(); } else if (n2) { p1 = p1.Normalize(); } else if (!c1.Equals(c2)) { return false; } else { // TODO Consider just requiring already normalized, to avoid silent performance degradation ECPoint[] points = new ECPoint[] { this, c1.ImportPoint(p2) }; // TODO This is a little strong, really only requires coZNormalizeAll to get Zs equal c1.NormalizeAll(points); p1 = points[0]; p2 = points[1]; } return p1.XCoord.Equals(p2.XCoord) && p1.YCoord.Equals(p2.YCoord); }
internal static ECPoint ImplSumOfMultiplies(ECPoint[] ps, ECPointMap pointMap, BigInteger[] ks) { int halfCount = ps.Length, fullCount = halfCount << 1; bool[] negs = new bool[fullCount]; WNafPreCompInfo[] infos = new WNafPreCompInfo[fullCount]; byte[][] wnafs = new byte[fullCount][]; for (int i = 0; i < halfCount; ++i) { int j0 = i << 1, j1 = j0 + 1; BigInteger kj0 = ks[j0]; negs[j0] = kj0.SignValue < 0; kj0 = kj0.Abs(); BigInteger kj1 = ks[j1]; negs[j1] = kj1.SignValue < 0; kj1 = kj1.Abs(); int width = System.Math.Max(2, System.Math.Min(16, WNafUtilities.GetWindowSize(System.Math.Max(kj0.BitLength, kj1.BitLength)))); ECPoint P = ps[i], Q = WNafUtilities.MapPointWithPrecomp(P, width, true, pointMap); infos[j0] = WNafUtilities.GetWNafPreCompInfo(P); infos[j1] = WNafUtilities.GetWNafPreCompInfo(Q); wnafs[j0] = WNafUtilities.GenerateWindowNaf(width, kj0); wnafs[j1] = WNafUtilities.GenerateWindowNaf(width, kj1); } return ImplSumOfMultiplies(negs, infos, wnafs); }
/** * Check, if two <code>ECPoint</code>s can be added or subtracted. * @param a The first <code>ECPoint</code> to check. * @param b The second <code>ECPoint</code> to check. * @throws IllegalArgumentException if <code>a</code> and <code>b</code> * cannot be added. */ private static void CheckPoints( ECPoint a, ECPoint b) { // Check, if points are on the same curve if (!a.Curve.Equals(b.Curve)) throw new ArgumentException("Only points on the same curve can be added or subtracted"); // F2mFieldElement.CheckFieldElements(a.x, b.x); }
internal static ECPoint ImplSumOfMultipliesGlv(ECPoint[] ps, BigInteger[] ks, GlvEndomorphism glvEndomorphism) { BigInteger n = ps[0].Curve.Order; int len = ps.Length; BigInteger[] abs = new BigInteger[len << 1]; for (int i = 0, j = 0; i < len; ++i) { BigInteger[] ab = glvEndomorphism.DecomposeScalar(ks[i].Mod(n)); abs[j++] = ab[0]; abs[j++] = ab[1]; } ECPointMap pointMap = glvEndomorphism.PointMap; if (glvEndomorphism.HasEfficientPointMap) { return ECAlgorithms.ImplSumOfMultiplies(ps, pointMap, abs); } ECPoint[] pqs = new ECPoint[len << 1]; for (int i = 0, j = 0; i < len; ++i) { ECPoint p = ps[i], q = pointMap.Map(p); pqs[j++] = p; pqs[j++] = q; } return ECAlgorithms.ImplSumOfMultiplies(pqs, abs); }
public static ECPoint SumOfMultiplies(ECPoint[] ps, BigInteger[] ks) { if (ps == null || ks == null || ps.Length != ks.Length || ps.Length < 1) throw new ArgumentException("point and scalar arrays should be non-null, and of equal, non-zero, length"); int count = ps.Length; switch (count) { case 1: return ps[0].Multiply(ks[0]); case 2: return SumOfTwoMultiplies(ps[0], ks[0], ps[1], ks[1]); default: break; } ECPoint p = ps[0]; ECCurve c = p.Curve; ECPoint[] imported = new ECPoint[count]; imported[0] = p; for (int i = 1; i < count; ++i) { imported[i] = ImportPoint(c, ps[i]); } GlvEndomorphism glvEndomorphism = c.GetEndomorphism() as GlvEndomorphism; if (glvEndomorphism != null) { return ValidatePoint(ImplSumOfMultipliesGlv(imported, ks, glvEndomorphism)); } return ValidatePoint(ImplSumOfMultiplies(imported, ks)); }
/* (non-Javadoc) * @see BitcoinKit.BouncyCastle.math.ec.ECPoint#add(BitcoinKit.BouncyCastle.math.ec.ECPoint) */ public override ECPoint Add(ECPoint b) { CheckPoints(this, b); return AddSimple((F2mPoint) b); }
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; ECCurve curve = this.Curve; int coord = curve.CoordinateSystem; switch (coord) { case ECCurve.COORD_AFFINE: { ECFieldElement X1 = this.RawXCoord; ECFieldElement X2 = b.RawXCoord, Y2 = b.RawYCoord; ECFieldElement dx = X2.Subtract(X1), dy = Y2.Subtract(Y1); if (dx.IsZero) { if (dy.IsZero) { // this == b i.e. the result is 3P return ThreeTimes(); } // this == -b, i.e. the result is P return this; } /* * Optimized calculation of 2P + Q, as described in "Trading Inversions for * Multiplications in Elliptic Curve Cryptography", by Ciet, Joye, Lauter, Montgomery. */ ECFieldElement X = dx.Square(), Y = dy.Square(); ECFieldElement d = X.Multiply(Two(X1).Add(X2)).Subtract(Y); if (d.IsZero) { return Curve.Infinity; } ECFieldElement D = d.Multiply(dx); ECFieldElement I = D.Invert(); ECFieldElement L1 = d.Multiply(I).Multiply(dy); ECFieldElement L2 = Two(Y1).Multiply(X).Multiply(dx).Multiply(I).Subtract(L1); ECFieldElement X4 = (L2.Subtract(L1)).Multiply(L1.Add(L2)).Add(X2); ECFieldElement Y4 = (X1.Subtract(X4)).Multiply(L2).Subtract(Y1); return new FpPoint(Curve, X4, Y4, IsCompressed); } case ECCurve.COORD_JACOBIAN_MODIFIED: { return TwiceJacobianModified(false).Add(b); } default: { return Twice().Add(b); } } }