From a52cfc68fa5c443ca5e2602049e2a1ec31e6db19 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Sat, 25 Jan 2014 17:39:43 +0700 Subject: Implement homogeneous and lambda-projective coordinate systems in F2m curves --- crypto/src/math/ec/ECCurve.cs | 67 +++++-- crypto/src/math/ec/ECPoint.cs | 428 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 428 insertions(+), 67 deletions(-) (limited to 'crypto/src/math') diff --git a/crypto/src/math/ec/ECCurve.cs b/crypto/src/math/ec/ECCurve.cs index 02c676994..832145e2e 100644 --- a/crypto/src/math/ec/ECCurve.cs +++ b/crypto/src/math/ec/ECCurve.cs @@ -721,6 +721,19 @@ namespace Org.BouncyCastle.Math.EC return new F2mCurve(m, k1, k2, k3, m_a, m_b, n, h); } + 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) @@ -805,26 +818,39 @@ namespace Org.BouncyCastle.Math.EC return si; } - public override ECPoint CreatePoint( - BigInteger X1, - BigInteger Y1, - bool withCompression) + public override ECPoint CreatePoint(BigInteger x, BigInteger y, bool withCompression) { - // TODO Validation of X1, Y1? - return new F2mPoint( - this, - FromBigInteger(X1), - FromBigInteger(Y1), - withCompression); + ECFieldElement X = FromBigInteger(x), Y = FromBigInteger(y); + + switch (this.CoordinateSystem) + { + case COORD_LAMBDA_AFFINE: + case COORD_LAMBDA_PROJECTIVE: + { + if (!X.IsZero) + { + // Y becomes Lambda (X + Y/X) here + Y = Y.Divide(X).Add(X); + } + break; + } + default: + { + break; + } + } + + return CreateRawPoint(X, Y, withCompression); } protected override ECPoint DecompressPoint( int yTilde, BigInteger X1) { + ECFieldElement xp = FromBigInteger(X1); ECFieldElement yp = null; - if (xp.ToBigInteger().SignValue == 0) + if (xp.IsZero) { yp = (F2mFieldElement)m_b; for (int i = 0; i < m - 1; i++) @@ -840,13 +866,26 @@ namespace Org.BouncyCastle.Math.EC if (z == null) throw new ArithmeticException("Invalid point compression"); - int zBit = z.ToBigInteger().TestBit(0) ? 1 : 0; - if (zBit != yTilde) + if (z.TestBitZero() != (yTilde == 1)) { - z = z.Add(FromBigInteger(BigInteger.One)); + z = z.AddOne(); } yp = xp.Multiply(z); + + switch (this.CoordinateSystem) + { + case COORD_LAMBDA_AFFINE: + case COORD_LAMBDA_PROJECTIVE: + { + yp = yp.Divide(xp).Add(xp); + break; + } + default: + { + break; + } + } } return new F2mPoint(this, xp, yp, true); diff --git a/crypto/src/math/ec/ECPoint.cs b/crypto/src/math/ec/ECPoint.cs index 86134f80c..5dbffda09 100644 --- a/crypto/src/math/ec/ECPoint.cs +++ b/crypto/src/math/ec/ECPoint.cs @@ -179,12 +179,12 @@ namespace Org.BouncyCastle.Math.EC return copy; } - protected virtual ECFieldElement RawXCoord + protected internal ECFieldElement RawXCoord { get { return m_x; } } - protected virtual ECFieldElement RawYCoord + protected internal ECFieldElement RawYCoord { get { return m_y; } } @@ -550,8 +550,8 @@ namespace Org.BouncyCastle.Math.EC ECCurve curve = this.Curve; int coord = curve.CoordinateSystem; - ECFieldElement X1 = this.XCoord, Y1 = this.YCoord; - ECFieldElement X2 = b.XCoord, Y2 = b.YCoord; + ECFieldElement X1 = this.RawXCoord, Y1 = this.RawYCoord; + ECFieldElement X2 = b.RawXCoord, Y2 = b.RawYCoord; switch (coord) { @@ -637,7 +637,7 @@ namespace Org.BouncyCastle.Math.EC ECCurve curve = this.Curve; - ECFieldElement Y1 = this.YCoord; + ECFieldElement Y1 = this.RawYCoord; if (Y1.IsZero) { return curve.Infinity; @@ -645,7 +645,7 @@ namespace Org.BouncyCastle.Math.EC int coord = curve.CoordinateSystem; - ECFieldElement X1 = this.XCoord; + ECFieldElement X1 = this.RawXCoord; switch (coord) { @@ -711,7 +711,7 @@ namespace Org.BouncyCastle.Math.EC return Twice(); } - ECFieldElement Y1 = this.YCoord; + ECFieldElement Y1 = this.RawYCoord; if (Y1.IsZero) { return b; @@ -724,8 +724,8 @@ namespace Org.BouncyCastle.Math.EC { case ECCurve.COORD_AFFINE: { - ECFieldElement X1 = this.XCoord; - ECFieldElement X2 = b.XCoord, Y2 = b.YCoord; + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord, Y2 = b.RawYCoord; ECFieldElement dx = X2.Subtract(X1), dy = Y2.Subtract(Y1); @@ -771,10 +771,12 @@ namespace Org.BouncyCastle.Math.EC public override ECPoint ThreeTimes() { - if (IsInfinity || this.YCoord.IsZero) - { + if (IsInfinity) + return this; + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) return this; - } ECCurve curve = this.Curve; int coord = curve.CoordinateSystem; @@ -783,7 +785,7 @@ namespace Org.BouncyCastle.Math.EC { case ECCurve.COORD_AFFINE: { - ECFieldElement X1 = this.XCoord, Y1 = this.YCoord; + ECFieldElement X1 = this.RawXCoord; ECFieldElement _2Y1 = Two(Y1); ECFieldElement X = _2Y1.Square(); @@ -938,6 +940,43 @@ namespace Org.BouncyCastle.Math.EC { } + public override ECFieldElement YCoord + { + get + { + int coord = this.CurveCoordinateSystem; + + switch (coord) + { + case ECCurve.COORD_LAMBDA_AFFINE: + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + // TODO The X == 0 stuff needs further thought + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Subtract(X).Multiply(X); + if (ECCurve.COORD_LAMBDA_PROJECTIVE == coord) + { + ECFieldElement Z = GetZCoord(0); + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + } + return Y; + } + default: + { + return RawYCoord; + } + } + } + } + protected internal override bool CompressionYTilde { get @@ -1007,36 +1046,154 @@ namespace Org.BouncyCastle.Math.EC { if (this.IsInfinity) return b; - if (b.IsInfinity) return this; - F2mFieldElement x2 = (F2mFieldElement) b.XCoord; - F2mFieldElement y2 = (F2mFieldElement) b.YCoord; + ECCurve curve = this.Curve; + int coord = curve.CoordinateSystem; - // Check if b == this or b == -this - if (this.XCoord.Equals(x2)) + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + switch (coord) { - // this == b, i.e. this must be doubled - if (this.YCoord.Equals(y2)) - return (F2mPoint) this.Twice(); + case ECCurve.COORD_AFFINE: + { + ECFieldElement Y1 = this.RawYCoord; + ECFieldElement Y2 = b.RawYCoord; - // this = -other, i.e. the result is the point at infinity - return (F2mPoint) Curve.Infinity; - } + if (X1.Equals(X2)) + { + if (Y1.Equals(Y2)) + { + return (F2mPoint)Twice(); + } + + return (F2mPoint)curve.Infinity; + } + + ECFieldElement sumX = X1.Add(X2); + ECFieldElement L = Y1.Add(Y2).Divide(sumX); + + ECFieldElement X3 = L.Square().Add(L).Add(sumX).Add(curve.A); + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + + return new F2mPoint(curve, X3, Y3, IsCompressed); + } + case ECCurve.COORD_HOMOGENEOUS: + { + ECFieldElement Y1 = this.RawYCoord, Z1 = this.GetZCoord(0); + ECFieldElement Y2 = b.RawYCoord, Z2 = b.GetZCoord(0); + + bool Z2IsOne = Z2.IsOne; + + ECFieldElement U1 = Z1.Multiply(Y2); + ECFieldElement U2 = Z2IsOne ? Y1 : Y1.Multiply(Z2); + ECFieldElement U = U1.Subtract(U2); + ECFieldElement V1 = Z1.Multiply(X2); + ECFieldElement V2 = Z2IsOne ? X1 : X1.Multiply(Z2); + ECFieldElement V = V1.Subtract(V2); + + if (V1.Equals(V2)) + { + if (U1.Equals(U2)) + { + return (F2mPoint)Twice(); + } + + return (F2mPoint)curve.Infinity; + } + + ECFieldElement VSq = V.Square(); + ECFieldElement W = Z2IsOne ? Z1 : Z1.Multiply(Z2); + ECFieldElement A = U.Square().Add(U.Multiply(V).Add(VSq.Multiply(curve.A))).Multiply(W).Add(V.Multiply(VSq)); + + ECFieldElement X3 = V.Multiply(A); + ECFieldElement VSqZ2 = Z2IsOne ? VSq : VSq.Multiply(Z2); + ECFieldElement Y3 = VSqZ2.Multiply(U.Multiply(X1).Add(Y1.Multiply(V))).Add(A.Multiply(U.Add(V))); + ECFieldElement Z3 = VSq.Multiply(V).Multiply(W); + + return new F2mPoint(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed); + } + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + if (X1.IsZero) + return b.AddSimple(this); + + ECFieldElement L1 = this.RawYCoord, Z1 = this.GetZCoord(0); + ECFieldElement L2 = b.RawYCoord, Z2 = b.GetZCoord(0); + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + { + return (F2mPoint)Twice(); + } + + return (F2mPoint)curve.Infinity; + } - ECFieldElement xSum = this.XCoord.Add(x2); + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit - F2mFieldElement lambda - = (F2mFieldElement)(this.YCoord.Add(y2)).Divide(xSum); + ECFieldElement Y1 = this.RawYCoord, Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); - F2mFieldElement x3 - = (F2mFieldElement)lambda.Square().Add(lambda).Add(xSum).Add(Curve.A); + X3 = L.Square().Add(L).Add(X1).Add(curve.A); + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = X3.IsZero ? Y3 : Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } - F2mFieldElement y3 - = (F2mFieldElement)lambda.Multiply(this.XCoord.Add(x3)).Add(x3).Add(this.YCoord); + X3 = AU1.Multiply(AU2); + L3 = AU2.Add(B).Square().Add(ABZ2.Multiply(L1.Add(Z1))); - return new F2mPoint(Curve, x3, y3, IsCompressed); + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new F2mPoint(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + default: + { + throw new InvalidOperationException("unsupported coordinate system"); + } + } } /* (non-Javadoc) @@ -1078,20 +1235,20 @@ namespace Org.BouncyCastle.Math.EC ECCurve curve = this.Curve; int coord = curve.CoordinateSystem; - ECFieldElement X1 = this.XCoord; + ECFieldElement X1 = this.RawXCoord; switch (coord) { case ECCurve.COORD_AFFINE: case ECCurve.COORD_LAMBDA_AFFINE: { - ECFieldElement Y1 = this.YCoord; + ECFieldElement Y1 = this.RawYCoord; return new F2mPoint(curve, X1.Square(), Y1.Square(), IsCompressed); } case ECCurve.COORD_HOMOGENEOUS: case ECCurve.COORD_LAMBDA_PROJECTIVE: { - ECFieldElement Y1 = this.YCoord, Z1 = this.GetZCoord(0); + ECFieldElement Y1 = this.RawYCoord, Z1 = this.GetZCoord(0); return new F2mPoint(curve, X1.Square(), Y1.Square(), new ECFieldElement[] { Z1.Square() }, IsCompressed); } default: @@ -1106,40 +1263,205 @@ namespace Org.BouncyCastle.Math.EC */ public override ECPoint Twice() { - // Twice identity element (point at infinity) is identity if (this.IsInfinity) return this; - // if x1 == 0, then (x1, y1) == (x1, x1 + y1) - // and hence this = -this and thus 2(x1, y1) == infinity - if (this.XCoord.IsZero) + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) { - return Curve.Infinity; + // A point with X == 0 is it's own additive inverse + return curve.Infinity; } - F2mFieldElement lambda = (F2mFieldElement) this.XCoord.Add(this.YCoord.Divide(this.XCoord)); - F2mFieldElement x2 = (F2mFieldElement)lambda.Square().Add(lambda).Add(Curve.A); - ECFieldElement ONE = Curve.FromBigInteger(BigInteger.One); - F2mFieldElement y2 = (F2mFieldElement)this.XCoord.Square().Add( - x2.Multiply(lambda.Add(ONE))); + int coord = curve.CoordinateSystem; - return new F2mPoint(Curve, x2, y2, IsCompressed); + switch (coord) + { + case ECCurve.COORD_AFFINE: + { + ECFieldElement Y1 = this.RawYCoord; + + ECFieldElement L1 = Y1.Divide(X1).Add(X1); + + ECFieldElement X3 = L1.Square().Add(L1).Add(curve.A); + ECFieldElement Y3 = X1.Square().Add(X3.Multiply(L1.AddOne())); + + return new F2mPoint(curve, X3, Y3, IsCompressed); + } + case ECCurve.COORD_HOMOGENEOUS: + { + ECFieldElement Y1 = this.RawYCoord, Z1 = this.GetZCoord(0); + + bool Z1IsOne = Z1.IsOne; + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1); + ECFieldElement Y1Z1 = Z1IsOne ? Y1 : Y1.Multiply(Z1); + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement S = X1Sq.Add(Y1Z1); + ECFieldElement V = X1Z1; + ECFieldElement vSquared = V.Square(); + ECFieldElement h = S.Square().Add(S.Multiply(V)).Add(curve.A.Multiply(vSquared)); + + ECFieldElement X3 = V.Multiply(h); + ECFieldElement Y3 = h.Multiply(S.Add(V)).Add(X1Sq.Square().Multiply(V)); + ECFieldElement Z3 = V.Multiply(vSquared); + + return new F2mPoint(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed); + } + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + ECFieldElement L1 = this.RawYCoord, Z1 = this.GetZCoord(0); + + bool Z1IsOne = Z1.IsOne; + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.Multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement a = curve.A; + ECFieldElement aZ1Sq = Z1IsOne ? a : a.Multiply(Z1Sq); + ECFieldElement T = L1.Square().Add(L1Z1).Add(aZ1Sq); + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement b = curve.B; + ECFieldElement L3; + if (b.BitLength < (curve.FieldSize >> 1)) + { + ECFieldElement t1 = L1.Add(X1).Square(); + ECFieldElement t4; + if (b.IsOne) + { + t4 = aZ1Sq.Add(Z1Sq).Square(); + } + else + { + // TODO t2/t3 can be calculated with one square if we pre-compute sqrt(b) + ECFieldElement t2 = aZ1Sq.Square(); + ECFieldElement t3 = b.Multiply(Z1Sq.Square()); + t4 = t2.Add(t3); + } + L3 = t1.Add(T).Add(Z1Sq).Multiply(t1).Add(t4).Add(X3); + if (a.IsZero) + { + L3 = L3.Add(Z3); + } + else if (!a.IsOne) + { + L3 = L3.Add(a.AddOne().Multiply(Z3)); + } + } + else + { + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1); + L3 = X1Z1.Square().Add(X3).Add(T.Multiply(L1Z1)).Add(Z3); + } + + return new F2mPoint(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + default: + { + throw new InvalidOperationException("unsupported coordinate system"); + } + } } - public override ECPoint Negate() + public override ECPoint TwicePlus(ECPoint b) { - if (IsInfinity) + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) { - return this; + // A point with X == 0 is it's own additive inverse + return b; } - ECFieldElement X1 = this.XCoord; - if (X1.IsZero) + int coord = curve.CoordinateSystem; + + switch (coord) { - return this; + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + // NOTE: twicePlus() only optimized for lambda-affine argument + ECFieldElement X2 = b.RawXCoord, Z2 = b.GetZCoord(0); + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.GetZCoord(0); + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).Multiply(T).Add(X1Sq.Multiply(Z1Sq)); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().Multiply(T).Add(L2plus1.Multiply(Z3)); + + return new F2mPoint(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + default: + { + return Twice().Add(b); + } } + } + + public override ECPoint Negate() + { + if (this.IsInfinity) + return this; - return new F2mPoint(Curve, X1, X1.Add(this.YCoord), IsCompressed); + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + ECCurve curve = this.Curve; + int coord = curve.CoordinateSystem; + + switch (coord) + { + case ECCurve.COORD_AFFINE: + { + ECFieldElement Y = this.RawYCoord; + return new F2mPoint(curve, X, Y.Add(X), IsCompressed); + } + case ECCurve.COORD_HOMOGENEOUS: + { + ECFieldElement Y = this.RawYCoord, Z = this.GetZCoord(0); + return new F2mPoint(curve, X, Y.Add(X), new ECFieldElement[] { Z }, IsCompressed); + } + case ECCurve.COORD_LAMBDA_AFFINE: + { + ECFieldElement L = this.RawYCoord; + return new F2mPoint(curve, X, L.AddOne(), IsCompressed); + } + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.GetZCoord(0); + return new F2mPoint(curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + default: + { + throw new InvalidOperationException("unsupported coordinate system"); + } + } } } } -- cgit 1.4.1