From 263e51c2fdf220cedd4d82e4fe884e4996e11d6d Mon Sep 17 00:00:00 2001 From: Lukas Prediger Date: Thu, 2 Jul 2020 21:56:04 +0300 Subject: [PATCH 1/8] Preliminary implementation of Montgomery curves WORK IN PROGRESS for traditional coordinates as well as x-only algorithm with projected coordinates. --- .../MontgomeryCurveAlgebraTests.cs | 135 +++++++ .../ProjectedMontgomeryCurveAlgebraTests.cs | 105 +++++ .../EllipticCurves/BigIntegerField.cs | 5 + .../EllipticCurves/MontgomeryCurveAlgebra.cs | 152 +++++++ .../ProjectedMontgomeryCurveAlgebra.cs | 373 ++++++++++++++++++ 5 files changed, 770 insertions(+) create mode 100644 CompactCryptoGroupAlgebra.Tests/EllipticCurves/MontgomeryCurveAlgebraTests.cs create mode 100644 CompactCryptoGroupAlgebra.Tests/EllipticCurves/ProjectedMontgomeryCurveAlgebraTests.cs create mode 100644 CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs create mode 100644 CompactCryptoGroupAlgebra/EllipticCurves/ProjectedMontgomeryCurveAlgebra.cs diff --git a/CompactCryptoGroupAlgebra.Tests/EllipticCurves/MontgomeryCurveAlgebraTests.cs b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/MontgomeryCurveAlgebraTests.cs new file mode 100644 index 0000000..8b159a3 --- /dev/null +++ b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/MontgomeryCurveAlgebraTests.cs @@ -0,0 +1,135 @@ +using System; +using System.Numerics; +using System.Security.Cryptography; +using NUnit.Framework; + +namespace CompactCryptoGroupAlgebra.EllipticCurves.Tests +{ + [TestFixture] + public class MontgomeryCurveAlgebraTests + { + private CurveParameters ecParams; + + public MontgomeryCurveAlgebraTests() + { + ecParams = new CurveParameters( + p: BigPrime.CreateWithoutChecks(41), + a: 4, + b: 3, + generator: new CurvePoint(2, 6), + order: BigPrime.CreateWithoutChecks(11), + cofactor: 4 + ); + // generated points + //1: (2, 6) + //2: (6, 9) + //3: (23, 9) + //4: (38, 24) + //5: (8, 32) + //6: (15, 6) + //7: (20, 35) + //8: (30, 40) + //9: (18, 39) + //10: (28, 7) + //11: (atInf) + + // all curve points + //(44, + // 11, + // 4, + // 3, + // [(0, (array([0]),)), + // (1, (array([17, 24]),)), + // (2, (array([6, 35]),)), + // (6, (array([9, 32]),)), + // (7, (array([10, 31]),)), + // (8, (array([9, 32]),)), + // (11, (array([12, 29]),)), + // (15, (array([6, 35]),)), + // (16, (array([20, 21]),)), + // (18, (array([2, 39]),)), + // (20, (array([6, 35]),)), + // (21, (array([19, 22]),)), + // (22, (array([15, 26]),)), + // (23, (array([9, 32]),)), + // (25, (array([8, 33]),)), + // (26, (array([20, 21]),)), + // (27, (array([11, 30]),)), + // (28, (array([7, 34]),)), + // (30, (array([1, 40]),)), + // (36, (array([20, 21]),)), + // (38, (array([17, 24]),)), + // (39, (array([17, 24]),))] + //) + + + } + + [Test] + public void TestAddSame() + { + var point = new CurvePoint(2, 6); + var algebra = new MontgomeryCurveAlgebra(ecParams); + + var expected = new CurvePoint(6, 9); + var result = algebra.Add(point, point); + Assert.AreEqual(expected, result); + } + + [Test] + public void TestAddDifferent() + { + var point = new CurvePoint(2, 6); + var otherPoint = new CurvePoint(8, 32); + var algebra = new MontgomeryCurveAlgebra(ecParams); + + var expected = new CurvePoint(15, 6); + var result = algebra.Add(otherPoint, point); + + Assert.AreEqual(expected, result); + } + + + [Test] + [TestCase(1, 2, 6, 2, 6)] + [TestCase(2, 2, 6, 6, 9)] + [TestCase(6, 2, 6, 15, 6)] + [TestCase(8, 2, 6, 30, 40)] + [TestCase(9, 2, 6, 18, 39)] + [TestCase(10, 2, 6, 28, 7)] + public void TestMultiplyScalar(int k, int x, int y, int expectedX, int expectedY) + { + var algebra = new MontgomeryCurveAlgebra(ecParams); + var expected = new CurvePoint(new BigInteger(expectedX), new BigInteger(expectedY)); + + var p = new CurvePoint(new BigInteger(x), new BigInteger(y)); + var result = algebra.MultiplyScalar(p, k); + + Assert.AreEqual(expected, result); + } + + //[Test] + //public void GetOrder() + //{ + // var point = new CurvePoint(2, 6); + // var algebra = new MontgomeryCurveAlgebra(ecParams); + + // for (int i = 1; i < ecParams.Order; ++i) + // { + // var r = algebra.MultiplyScalar(point, i); + // Console.WriteLine(String.Format("{0}: {1}", i, r)); + // //Assert.IsTrue(algebra.IsElement(r)); + // } + + // //Assert.IsTrue(algebra.IsElement(point), "not valid"); + // var point2 = algebra.MultiplyScalar(point, 2); + // Assert.AreNotEqual(point2, CurvePoint.PointAtInfinity, "order 2"); + // var point4 = algebra.MultiplyScalar(point, 4); + // Assert.AreNotEqual(point4, CurvePoint.PointAtInfinity, "order 4"); + // var point11 = algebra.MultiplyScalar(point, 11); + // Assert.AreEqual(point11, CurvePoint.PointAtInfinity, "order not 11"); + + //} + + } +} diff --git a/CompactCryptoGroupAlgebra.Tests/EllipticCurves/ProjectedMontgomeryCurveAlgebraTests.cs b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/ProjectedMontgomeryCurveAlgebraTests.cs new file mode 100644 index 0000000..f5fb6d9 --- /dev/null +++ b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/ProjectedMontgomeryCurveAlgebraTests.cs @@ -0,0 +1,105 @@ +using System; +using System.Numerics; +using System.Security.Cryptography; +using NUnit.Framework; + +namespace CompactCryptoGroupAlgebra.EllipticCurves.Tests +{ + [TestFixture] + public class ProjectedMontgomeryCurveAlgebraTests + { + private CurveParameters ecParams; + + public ProjectedMontgomeryCurveAlgebraTests() + { + ecParams = new CurveParameters( + p: BigPrime.CreateWithoutChecks(41), + a: 4, + b: 3, + generator: new CurvePoint(2, 6), + order: BigPrime.CreateWithoutChecks(11), + cofactor: 4 + ); + // generated points + //1: (2, 6) + //2: (6, 9) + //3: (23, 9) + //4: (38, 24) + //5: (8, 32) + //6: (15, 6) + //7: (20, 35) + //8: (30, 40) + //9: (18, 39) + //10: (28, 7) + //11: (atInf) + + // all curve points + //(44, + // 11, + // 4, + // 3, + // [(0, (array([0]),)), + // (1, (array([17, 24]),)), + // (2, (array([6, 35]),)), + // (6, (array([9, 32]),)), + // (7, (array([10, 31]),)), + // (8, (array([9, 32]),)), + // (11, (array([12, 29]),)), + // (15, (array([6, 35]),)), + // (16, (array([20, 21]),)), + // (18, (array([2, 39]),)), + // (20, (array([6, 35]),)), + // (21, (array([19, 22]),)), + // (22, (array([15, 26]),)), + // (23, (array([9, 32]),)), + // (25, (array([8, 33]),)), + // (26, (array([20, 21]),)), + // (27, (array([11, 30]),)), + // (28, (array([7, 34]),)), + // (30, (array([1, 40]),)), + // (36, (array([20, 21]),)), + // (38, (array([17, 24]),)), + // (39, (array([17, 24]),))] + //) + + + } + + [Test] + [TestCase(0, 2, 0)] + [TestCase(1, 2, 2)] + [TestCase(2, 2, 6)] + [TestCase(3, 2, 23)] + [TestCase(4, 2, 38)] + [TestCase(5, 2, 8)] + [TestCase(6, 2, 15)] + [TestCase(7, 2, 20)] + [TestCase(8, 2, 30)] + [TestCase(9, 2, 18)] + [TestCase(10, 2, 28)] + [TestCase(11, 2, 0)] + public void TestMultiplyScalar(int k, int x, int expectedX) + { + var algebra = new ProjectedMontgomeryCurveAlgebra(ecParams); + + var p = new MontgomeryPoint(new BigInteger(x)); + var result = algebra.MultiplyScalar(p, k); + result = algebra.RenormalizePoint(result); + + Assert.AreEqual(new BigInteger(expectedX), result.X); + } + + [Test] + [TestCase(3, 17, 5)] + [TestCase(4, 1, 4)] + [TestCase(1, 40, 40)] + public void TestRenormalizePoint(int x, int z, int expectedX) + { + var algebra = new ProjectedMontgomeryCurveAlgebra(ecParams); + var p = new MontgomeryPoint(new BigInteger(x), new BigInteger(z)); + var result = algebra.RenormalizePoint(p); + + Assert.AreEqual(new BigInteger(expectedX), result.X); + } + } +} diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/BigIntegerField.cs b/CompactCryptoGroupAlgebra/EllipticCurves/BigIntegerField.cs index aa46ef6..9f939f4 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/BigIntegerField.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/BigIntegerField.cs @@ -82,5 +82,10 @@ public BigInteger Mod(BigInteger x) { return (x % Modulo + Modulo) % Modulo; // to prevent negative results of BigInteger modulo } + + public bool IsElement(BigInteger x) + { + return x >= 0 && x < Modulo; + } } } diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs b/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs new file mode 100644 index 0000000..36f9a81 --- /dev/null +++ b/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Security.Cryptography; + +namespace CompactCryptoGroupAlgebra.EllipticCurves +{ + public class MontgomeryCurveAlgebra : CryptoGroupAlgebra + { + private readonly CurveParameters _parameters; + private readonly BigIntegerField _field; + + public MontgomeryCurveAlgebra(CurveParameters parameters) + : base(parameters.Generator, parameters.Order) + { + _parameters = parameters; + _field = new BigIntegerField(_parameters.P); + } + + /// + public override BigInteger Cofactor { get { return _parameters.Cofactor; } } + + /// + public override int ElementBitLength { get { return 2 * NumberLength.GetLength(_parameters.P).InBits; } } + + /// + public override CurvePoint NeutralElement { get { return CurvePoint.PointAtInfinity; } } + + /// + public override CurvePoint Add(CurvePoint left, CurvePoint right) + { + BigInteger x1 = left.X; + BigInteger x2 = right.X; + BigInteger y1 = left.Y; + BigInteger y2 = right.Y; + + BigInteger lambdaSame = _field.Mod((3 * _field.Square(x1) + 2 * _parameters.A * x1+ 1) * _field.InvertMult(2 * _parameters.B * y1)); + BigInteger lambdaDiff = _field.Mod((y2 - y1) * _field.InvertMult(x2 - x1)); + BigInteger lambda; + // note: branching is side-channel vulnerable + if (left.Equals(right)) // Equals probably not constant time + { + lambda = lambdaSame; + } + else + { + lambda = lambdaDiff; + } + BigInteger x3 = _field.Mod(_parameters.B * _field.Square(lambda) - x1 - x2 - _parameters.A); + BigInteger y3 = _field.Mod(lambda * (x1 - x3) - y1); + + CurvePoint result = CurvePoint.PointAtInfinity; + bool pointsAreNegations = AreNegations(left, right); + // note: branching is side-channel vulnerable + if (left.IsAtInfinity && right.IsAtInfinity) + result = CurvePoint.PointAtInfinity; + if (left.IsAtInfinity && !right.IsAtInfinity) + result = right.Clone(); + if (right.IsAtInfinity && !left.IsAtInfinity) + result = left.Clone(); + if (!left.IsAtInfinity && !right.IsAtInfinity && pointsAreNegations) + result = CurvePoint.PointAtInfinity; + if (!left.IsAtInfinity && !right.IsAtInfinity && !pointsAreNegations) + result = new CurvePoint(x3, y3); + return result; + } + + /// + /// Checks whether two given points are negations of each other, i.e., + /// adding them results in the zero element (point at infinity). + /// + /// true, if the given points are negations of each other, false otherwise. + /// Curve point + /// Curve point + public bool AreNegations(CurvePoint left, CurvePoint right) + { + var inv = Negate(right); + return left.Equals(inv); + } + + /// + public override CurvePoint FromBytes(byte[] buffer) + { + throw new NotImplementedException(); + } + + /// + public override byte[] ToBytes(CurvePoint element) + { + throw new NotImplementedException(); + } + + /// + public override CurvePoint Negate(CurvePoint e) + { + if (e.IsAtInfinity) + return e; + return new CurvePoint(e.X, -e.Y); + } + + /// + protected override bool IsElementDerived(CurvePoint point) + { + if (!_field.IsElement(point.X)) + return false; + if (!_field.IsElement(point.Y)) + return false; + + // verifying that the point satisfies the curve equation + BigInteger r = _field.Mod(_field.Pow(point.X, 3) + _parameters.A * _field.Square(point.X) + point.X); + BigInteger ySquared = _field.Mod(_parameters.B * _field.Square(point.Y)); + return (r == ySquared); + } + + /// + /// Selects one of two given curve points. + /// + /// This allows side-channel resistant selection by avoiding branching. + /// The selection is made based on the value of the parameter + /// . A value of BigInteger.Zeroselects the curve point + /// given as , a value of BigInteger.One selects . + /// + /// The selected boolean. + /// Selection indicator. + /// First selection option. + /// First selection option. + protected override CurvePoint Multiplex(BigInteger selection, CurvePoint first, CurvePoint second) + { + return CurvePoint.Multiplex(selection, first, second); + } + + /// + protected override CurvePoint MultiplyScalarUnchecked(CurvePoint e, BigInteger k, int factorBitLength) + { + return base.MultiplyScalarUnchecked(e, k, factorBitLength); + } + + /// + public override bool Equals(CryptoGroupAlgebra? other) + { + var algebra = other as MontgomeryCurveAlgebra; + return other != null && + EqualityComparer.Default.Equals(_parameters, algebra!._parameters); + } + + /// + public override int GetHashCode() + { + return -2051777468 + EqualityComparer.Default.GetHashCode(_parameters); + } + } +} diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/ProjectedMontgomeryCurveAlgebra.cs b/CompactCryptoGroupAlgebra/EllipticCurves/ProjectedMontgomeryCurveAlgebra.cs new file mode 100644 index 0000000..0313c51 --- /dev/null +++ b/CompactCryptoGroupAlgebra/EllipticCurves/ProjectedMontgomeryCurveAlgebra.cs @@ -0,0 +1,373 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Security.Cryptography; + +namespace CompactCryptoGroupAlgebra.EllipticCurves +{ + public readonly struct MontgomeryPoint + { + public BigInteger X { get; } + public BigInteger Z { get; } + public bool IsAtInfinity { get { return Z.IsZero; } } + + public MontgomeryPoint(BigInteger x, BigInteger z) + { + X = x; + Z = z; + } + + public MontgomeryPoint(BigInteger x) + : this(x, BigInteger.One) { } + + public static MontgomeryPoint AtInfinity = new MontgomeryPoint(); + + public bool Equals(MontgomeryPoint other) + { + return X == other.X && Z == other.Z; + } + + public bool IsNormalized { get { return Z.IsOne; } } + } + /// + /// An implementation of for + /// Montgomery curves using x-only arithmetic on projected coordinates. + /// + /// Note: Does not implements the standard addition of + /// but only . + /// + /// + /// Implementation based on https://eprint.iacr.org/2017/212.pdf . + /// + public class ProjectedMontgomeryCurveAlgebra : CryptoGroupAlgebra + { + private readonly CurveParameters _parameters; + private BigIntegerField _field; + private BigInteger _aConstant; + + /// + /// Initializes a new instance of + /// with given curve parameters. + /// + /// Curve parameters. + public ProjectedMontgomeryCurveAlgebra(CurveParameters parameters) + : base( + new MontgomeryPoint(parameters.Generator.X), + parameters.Order + ) + { + _parameters = parameters; + _field = new BigIntegerField(_parameters.P); + _aConstant = _field.Mod((_parameters.A + 2) * _field.InvertMult(4)); + } + + /// + public override BigInteger Cofactor { get { return _parameters.Cofactor; } } + + /// + public override int ElementBitLength { get { return NumberLength.GetLength(_parameters.P).InBits; } } + + /// + public override MontgomeryPoint NeutralElement { get { return MontgomeryPoint.AtInfinity; } } + + /// + /// Sums two projected Montgomery point using only x- and z-coordinates and knowledge of + /// the difference. + /// + /// The sum of and + /// . + /// Curve point to add. + /// Curve point to add. + /// Difference of and + /// . + private MontgomeryPoint AddInternal(MontgomeryPoint left, MontgomeryPoint right, MontgomeryPoint diff) + { + if (right.IsAtInfinity) + return left; + if (left.IsAtInfinity) + return right; + + BigInteger xp = left.X; + BigInteger xq = right.X; + BigInteger zp = left.Z; + BigInteger zq = right.Z; + + var xpPlusZp = _field.Mod(xp + zp); + var xpMinusZp = _field.Mod(xp - zp); + + var xqPlusZq = _field.Mod(xq + zq); + var xqMinusZq = _field.Mod(xq - zq); + + var firstProduct = _field.Mod(xpMinusZp * xqPlusZq); + var secondProduct = _field.Mod(xpPlusZp * xqMinusZq); + + var xNew = _field.Mod(diff.Z * _field.Square( + firstProduct + secondProduct + )); + + var zNew = _field.Mod(diff.X * _field.Square( + firstProduct - secondProduct + )); + + //var v0 = _ring.Mod(xp + zp); + //var v1 = _ring.Mod(xq - zq); + //v1 = _ring.Mod(v0 * v1); + //v0 = _ring.Mod(xp - zp); + //var v2 = _ring.Mod(xq + zq); + //v2 = _ring.Mod(v2 * v0); + //var v3 = _ring.Mod(v1 + v2); + + //v3 = _ring.Square(v3); + //var v4 = _ring.Mod(v1 - v2); + //v4 = _ring.Square(v4); + + //var xNew = _ring.Mod(diff.Z * v3); + //var zNew = _ring.Mod(diff.X * v4); + + return new MontgomeryPoint(xNew, zNew); + } + + /// + /// Doubles a projected Montgomery point using only x- and z-coordinates. + /// + /// The curve point. + /// The doubled curve point. + private MontgomeryPoint DoubleInternal(MontgomeryPoint point) + { + BigInteger xp = point.X; + BigInteger zp = point.Z; + + var xpPlusZpSquared = _field.Square(xp + zp); + var xpMinusZpSquared = _field.Square(xp - zp); + var xpzp4 = xpPlusZpSquared - xpMinusZpSquared; + + var xNew = _field.Mod(xpPlusZpSquared * xpMinusZpSquared); + var zNew = _field.Mod(xpzp4 * (xpMinusZpSquared + _aConstant * xpzp4)); + + //var v1 = _ring.Mod(xp + zp); + //v1 = _ring.Square(v1); + //var v2 = _ring.Mod(xp - zp); + //v2 = _ring.Square(v2); + //var xNew = _ring.Mod(v1 * v2); + + + + //v1 = _ring.Mod(v1 - v2); + //var v3 = _ring.Mod((_parameters.A + 2) * _ring.InvertMult(4) * v1); + //v3 = _ring.Mod(v3 + v2); + //var zNew = _ring.Mod(v1 * v3); + return new MontgomeryPoint(xNew, zNew); + } + + /// + /// Renormalizes a Montgomery point by setting its z-coordinate to 1 + /// and scaling its x-coordinate accordingly. + /// + /// The curve point point. + /// The renormalized curve point. + public MontgomeryPoint RenormalizePoint(MontgomeryPoint point) + { + return new MontgomeryPoint( + _field.Mod(_field.InvertMult(point.Z) * point.X) + ); + } + + /// + public override MontgomeryPoint Add(MontgomeryPoint left, MontgomeryPoint right) + { + throw new NotSupportedException("A projected Montgomery curve" + + "has no definition for the standard addition. Use the" + + "standard Montgomery curve implementation instead."); + } + + /// + /// Checks whether two given points are negations of each other, i.e., + /// adding them results in the zero element (point at infinity). + /// + /// + /// true, if the given points are negations of each other; + /// false otherwise. + /// + /// Curve point + /// Curve point + public bool AreNegations(MontgomeryPoint left, MontgomeryPoint right) + { + var inv = Negate(right); + return left.Equals(inv); + } + + /// + public override MontgomeryPoint FromBytes(byte[] buffer) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer)); + if (buffer.Length < _field.ElementByteLength) + throw new ArgumentException("The given buffer is too short to" + + "contain a valid element representation.", nameof(buffer)); + + byte[] xBytes = new byte[_field.ElementByteLength]; + + Buffer.BlockCopy(buffer, 0, xBytes, 0, _field.ElementByteLength); + + BigInteger x = new BigInteger(xBytes); + return new MontgomeryPoint(x); + } + + /// + public override byte[] ToBytes(MontgomeryPoint element) + { + if (!element.IsNormalized) + element = RenormalizePoint(element); + + byte[] xBytes = element.X.ToByteArray(); + + Debug.Assert(xBytes.Length <= _field.ElementByteLength); + + return xBytes; + } + + /// + public override MontgomeryPoint Negate(MontgomeryPoint point) + { + return point; + } + + /// + protected override bool IsElementDerived(MontgomeryPoint point) + { + if (!_field.IsElement(point.X)) + return false; + if (!_field.IsElement(point.Z)) + return false; + + // In Montgomery form, every x coordinate corresponds to a valid + // point either on the curve or on its twist, i.e., another valid + // curve. We do not really care about which of these two we compute + // on (the projected x-only addition and multiplication work for + // both), so we do not need to perform additional validity checks + // here. + // Note that subgroup attacks could be problematic, but those + // are caught already in CryptoGroupAlgebra's IsElement implementation. + return true; + } + + /// + /// Selects one of two given BigInteger scalars. + /// + /// This allows side-channel resistant selection by avoiding branching. + /// The selection is made based on the value of the parameter + /// . A value of BigInteger.Zero selects the BigInteger + /// given as , a value of BigInteger.One selects . + /// + /// The selected BigInteger. + /// Selection indicator. + /// First selection option. + /// Second selection option. + protected BigInteger Multiplex(BigInteger selection, BigInteger first, BigInteger second) + { + Debug.Assert(selection.IsOne || selection.IsZero); + return first + selection * (second - first); + } + + /// + /// Selects one of two given curve points. + /// + /// This allows side-channel resistant selection by avoiding branching. + /// The selection is made based on the value of the parameter + /// . A value of BigInteger.Zero selects the curve point + /// given as , a value of BigInteger.One selects . + /// + /// The selected boolean. + /// Selection indicator. + /// First selection option. + /// First selection option. + protected override MontgomeryPoint Multiplex( + BigInteger selection, MontgomeryPoint first, MontgomeryPoint second + ) + { + Debug.Assert(selection.IsOne || selection.IsZero); + var sel = !selection.IsZero; + return new MontgomeryPoint( + Multiplex(selection, first.X, second.X), + Multiplex(selection, first.Z, second.Z) + ); + } + + /// + /// Conditionally swaps the two given curve points. + /// + /// This allows side-channel resistant selection by avoiding branching. + /// The swap is made based on the value of the parameter + /// . A value of BigInteger.Zero means + /// no swapping takes place, a value of BigInteger.One causes + /// a swap. + /// + /// + /// (, ) + /// if is 0; otherwise + /// (, ) + /// + /// Swapping indicator. + /// Curve point. + /// Curve point. + private (MontgomeryPoint, MontgomeryPoint) ConditionalSwap( + BigInteger selector, MontgomeryPoint first, MontgomeryPoint second + ) + { + Debug.Assert(selector.IsOne || selector.IsZero); + return ( + Multiplex(selector, first, second), + Multiplex(selector, second, first) + ); + } + + /// + protected override MontgomeryPoint MultiplyScalarUnchecked(MontgomeryPoint e, BigInteger k, int factorBitLength) + { + BigInteger maxFactor = BigInteger.One << factorBitLength; + int i = factorBitLength - 1; + + MontgomeryPoint r0 = NeutralElement; + MontgomeryPoint r1 = e; + + // Montgomery ladder maintains invariant r1 = r0 + e + // and can thus use x-only addition that requires knowledge of r1-r0 (= e) + for (BigInteger mask = maxFactor >> 1; !mask.IsZero; mask = mask >> 1, --i) + { + BigInteger bitI = (k & mask) >> i; + + (r0, r1) = ConditionalSwap(bitI, r0, r1); + r1 = AddInternal(r0, r1, e); + r0 = DoubleInternal(r0); + (r0, r1) = ConditionalSwap(bitI, r0, r1); + //if (bitI.IsZero) + //{ + // r1 = AddInternal(r0, r1, e); + // r0 = DoubleInternal(r0); + //} + //else + //{ + // r0 = AddInternal(r0, r1, e); + // r1 = DoubleInternal(r1); + //} + } + Debug.Assert(i == -1); + return r0; + } + + /// + public override bool Equals(CryptoGroupAlgebra? other) + { + var algebra = other as ProjectedMontgomeryCurveAlgebra; + return other != null && + EqualityComparer.Default.Equals(_parameters, algebra!._parameters); + } + + /// + public override int GetHashCode() + { + var hashCode = -2051777468 + EqualityComparer.Default.GetHashCode(_parameters); + return hashCode; + } + } +} From 30be30414c571020d3836924bfbbf4b85679edc0 Mon Sep 17 00:00:00 2001 From: Lukas Prediger Date: Sun, 5 Jul 2020 12:34:14 +0300 Subject: [PATCH 2/8] Addings tests for and separating MontgomeryCurvePoint --- .../EllipticCurves/BigIntegerFieldTests.cs | 20 +++++ .../MontgomeryCurvePointTests.cs | 90 +++++++++++++++++++ .../EllipticCurves/CurveGroupAlgebra.cs | 4 +- .../EllipticCurves/CurvePoint.cs | 10 +-- .../EllipticCurves/MontgomeryCurvePoint.cs | 69 ++++++++++++++ .../ProjectedMontgomeryCurveAlgebra.cs | 26 +----- 6 files changed, 186 insertions(+), 33 deletions(-) create mode 100644 CompactCryptoGroupAlgebra.Tests/EllipticCurves/MontgomeryCurvePointTests.cs create mode 100644 CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurvePoint.cs diff --git a/CompactCryptoGroupAlgebra.Tests/EllipticCurves/BigIntegerFieldTests.cs b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/BigIntegerFieldTests.cs index 0321cce..4b69096 100644 --- a/CompactCryptoGroupAlgebra.Tests/EllipticCurves/BigIntegerFieldTests.cs +++ b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/BigIntegerFieldTests.cs @@ -75,5 +75,25 @@ public void TestMod(int value, int expectedRaw) var expected = new BigInteger(expectedRaw); Assert.AreEqual(expected, result); } + + [Test] + public void TestIsElementTrue() + { + var prime = BigPrime.CreateWithoutChecks(11); + var field = new BigIntegerField(prime); + + Assert.IsTrue(field.IsElement(7)); + } + + [Test] + [TestCase(-2)] + [TestCase(12)] + public void TestIsElementFalse(int value) + { + var prime = BigPrime.CreateWithoutChecks(11); + var field = new BigIntegerField(prime); + + Assert.IsFalse(field.IsElement(new BigInteger(value))); + } } } \ No newline at end of file diff --git a/CompactCryptoGroupAlgebra.Tests/EllipticCurves/MontgomeryCurvePointTests.cs b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/MontgomeryCurvePointTests.cs new file mode 100644 index 0000000..b75d16d --- /dev/null +++ b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/MontgomeryCurvePointTests.cs @@ -0,0 +1,90 @@ +using System; +using System.Numerics; + +using NUnit.Framework; + +namespace CompactCryptoGroupAlgebra.EllipticCurves.Tests +{ + [TestFixture] + public class MontgomeryCurvePointTests + { + [Test] + public void TestPointAtInfinity() + { + var point = MontgomeryCurvePoint.PointAtInfinity; + + Assert.True(point.IsAtInfinity); + } + + [Test] + public void TestConstructor() + { + var x = new BigInteger(2); + var z = new BigInteger(7); + var point = new MontgomeryCurvePoint(x, z); + + Assert.AreEqual(x, point.X); + Assert.AreEqual(z, point.Z); + Assert.IsFalse(point.IsNormalized); + Assert.IsFalse(point.IsAtInfinity); + } + + [Test] + public void TestConstructorXOnly() + { + var x = new BigInteger(5); + var point = new MontgomeryCurvePoint(x); + + Assert.AreEqual(x, point.X); + Assert.AreEqual(BigInteger.One, point.Z); + Assert.IsTrue(point.IsNormalized); + Assert.IsFalse(point.IsAtInfinity); + } + + [Test] + public void TestEqualsTrueForEqual() + { + var x = new BigInteger(3); + var z = new BigInteger(67); + var point = new MontgomeryCurvePoint(x, z); + var otherPoint = new MontgomeryCurvePoint(x, z); + + Assert.IsTrue(point.Equals(otherPoint)); + } + + [Test] + public void TestGetHashCodeSameForEqual() + { + var x = new BigInteger(3); + var z = new BigInteger(67); + var point = new MontgomeryCurvePoint(x, z); + var otherPoint = new MontgomeryCurvePoint(x, z); + + Assert.AreEqual(point.GetHashCode(), otherPoint.GetHashCode()); + } + + [Test] + public void TestEqualsFalseForDifferent() + { + var x = new BigInteger(3); + var z = new BigInteger(67); + var point = new MontgomeryCurvePoint(x, z); + + var otherPoint = new MontgomeryCurvePoint(1, z); + Assert.IsFalse(point.Equals(otherPoint)); + + otherPoint = new MontgomeryCurvePoint(8, z); + Assert.IsFalse(point.Equals(otherPoint)); + } + + [Test] + public void TestEqualsFalseForX0AndPointAtInfinity() + { + var point = new MontgomeryCurvePoint(0); + var otherPoint = MontgomeryCurvePoint.PointAtInfinity; + + Assert.IsFalse(point.Equals(otherPoint)); + } + + } +} \ No newline at end of file diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/CurveGroupAlgebra.cs b/CompactCryptoGroupAlgebra/EllipticCurves/CurveGroupAlgebra.cs index ac9b412..f168a5a 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/CurveGroupAlgebra.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/CurveGroupAlgebra.cs @@ -151,9 +151,7 @@ protected override CurvePoint Multiplex(BigInteger selection, CurvePoint first, /// protected override bool IsElementDerived(CurvePoint point) { - if (!(BigInteger.Zero <= point.X && point.X < _parameters.P)) - return false; - if (!(BigInteger.Zero <= point.Y && point.Y < _parameters.P)) + if (!_field.IsElement(point.X) || !_field.IsElement(point.Y)) return false; // verifying that the point satisfies the curve equation diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/CurvePoint.cs b/CompactCryptoGroupAlgebra/EllipticCurves/CurvePoint.cs index b8800ac..60e7684 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/CurvePoint.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/CurvePoint.cs @@ -18,9 +18,9 @@ namespace CompactCryptoGroupAlgebra.EllipticCurves public readonly struct CurvePoint : IEquatable { /// - /// Creates a point at infninity. + /// Creates a point at infinity. /// - /// A CurvePoint instance representing a point at infinity. + /// A instance representing a point at infinity. public static readonly CurvePoint PointAtInfinity = new CurvePoint(0, 0, true); /// @@ -39,7 +39,7 @@ namespace CompactCryptoGroupAlgebra.EllipticCurves public BigInteger Y { get; } /// - /// Instantiates a new CurvePoint with the given values. + /// Instantiates a new with the given values. /// /// The integer coordinates are only relevant is the point is not at infinity. /// @@ -54,7 +54,7 @@ private CurvePoint(BigInteger x, BigInteger y, bool isAtInfinity) } /// - /// Instantiates a new CurvePoint with the given coordinates. + /// Instantiates a new with the given coordinates. /// /// X-coordinate of the point. /// Y-coordinate of the point. @@ -65,7 +65,7 @@ public CurvePoint(BigInteger x, BigInteger y) /// /// Clones this point. /// - /// A new CurvePoint instance that is an identical copy of the original instance. + /// A new instance that is an identical copy of the original instance. public CurvePoint Clone() { return new CurvePoint(X, Y, IsAtInfinity); diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurvePoint.cs b/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurvePoint.cs new file mode 100644 index 0000000..a076a68 --- /dev/null +++ b/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurvePoint.cs @@ -0,0 +1,69 @@ +using System; +using System.Numerics; + +namespace CompactCryptoGroupAlgebra.EllipticCurves +{ + /// + /// A point on a Montgomery elliptic curve in projected coordinates + /// and without y-coordinate. + /// + /// Raw group element of . This struct + /// is mainly a information container and implements no algebraic operations. + /// + public readonly struct MontgomeryCurvePoint + { + /// + /// X-coordinate of this point. + /// + public BigInteger X { get; } + + /// + /// Z-coordinate of this point. + /// + public BigInteger Z { get; } + + /// + /// Whether this point is a point at infinity. + /// + public bool IsAtInfinity { get { return Z.IsZero; } } + + /// + /// Instantiates a new with the given coordinates. + /// + /// X-coordinate of the point. + /// Z-coordinate of the point. + public MontgomeryCurvePoint(BigInteger x, BigInteger z) + { + X = x; + Z = z; + } + + /// + /// Instantiates a new with the given X-coordinate. + /// + /// X-coordinate of the point. + public MontgomeryCurvePoint(BigInteger x) + : this(x, BigInteger.One) { } + + /// + /// Creates a point at infinity. + /// + /// A CurvePoint instance representing a point at infinity. + public static MontgomeryCurvePoint PointAtInfinity = new MontgomeryCurvePoint(); + + /// + /// Compares this point for equality with another one. + /// + /// The point to compare this one to. + /// True, if either both points are points at infinity or have identical coordinates. + public bool Equals(MontgomeryCurvePoint other) + { + return X == other.X && Z == other.Z; + } + + /// + /// Whether this point is normalized (i.e. has z-coordinate 1) + /// + public bool IsNormalized { get { return Z.IsOne; } } + } +} \ No newline at end of file diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/ProjectedMontgomeryCurveAlgebra.cs b/CompactCryptoGroupAlgebra/EllipticCurves/ProjectedMontgomeryCurveAlgebra.cs index 0313c51..9e106d8 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/ProjectedMontgomeryCurveAlgebra.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/ProjectedMontgomeryCurveAlgebra.cs @@ -1,34 +1,10 @@ -using System; +using System; using System.Collections.Generic; using System.Numerics; -using System.Security.Cryptography; namespace CompactCryptoGroupAlgebra.EllipticCurves { - public readonly struct MontgomeryPoint - { - public BigInteger X { get; } - public BigInteger Z { get; } - public bool IsAtInfinity { get { return Z.IsZero; } } - - public MontgomeryPoint(BigInteger x, BigInteger z) - { - X = x; - Z = z; - } - - public MontgomeryPoint(BigInteger x) - : this(x, BigInteger.One) { } - public static MontgomeryPoint AtInfinity = new MontgomeryPoint(); - - public bool Equals(MontgomeryPoint other) - { - return X == other.X && Z == other.Z; - } - - public bool IsNormalized { get { return Z.IsOne; } } - } /// /// An implementation of for /// Montgomery curves using x-only arithmetic on projected coordinates. From d403be5e26a955b550274e3fd89953acc0a097f5 Mon Sep 17 00:00:00 2001 From: Lukas Prediger Date: Sun, 5 Jul 2020 12:37:43 +0300 Subject: [PATCH 3/8] refactor: ProjectedMontgomery... -> XOnlyMontgomery... & made its generic type BigInteger --- ...cs => XOnlyMontgomeryCurveAlgebraTests.cs} | 23 +--- .../EllipticCurves/MontgomeryCurveAlgebra.cs | 24 +++- .../EllipticCurves/MontgomeryCurvePoint.cs | 2 +- ...ebra.cs => XOnlyMontgomeryCurveAlgebra.cs} | 123 ++++++------------ 4 files changed, 65 insertions(+), 107 deletions(-) rename CompactCryptoGroupAlgebra.Tests/EllipticCurves/{ProjectedMontgomeryCurveAlgebraTests.cs => XOnlyMontgomeryCurveAlgebraTests.cs} (74%) rename CompactCryptoGroupAlgebra/EllipticCurves/{ProjectedMontgomeryCurveAlgebra.cs => XOnlyMontgomeryCurveAlgebra.cs} (71%) diff --git a/CompactCryptoGroupAlgebra.Tests/EllipticCurves/ProjectedMontgomeryCurveAlgebraTests.cs b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/XOnlyMontgomeryCurveAlgebraTests.cs similarity index 74% rename from CompactCryptoGroupAlgebra.Tests/EllipticCurves/ProjectedMontgomeryCurveAlgebraTests.cs rename to CompactCryptoGroupAlgebra.Tests/EllipticCurves/XOnlyMontgomeryCurveAlgebraTests.cs index f5fb6d9..6c3f4c3 100644 --- a/CompactCryptoGroupAlgebra.Tests/EllipticCurves/ProjectedMontgomeryCurveAlgebraTests.cs +++ b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/XOnlyMontgomeryCurveAlgebraTests.cs @@ -6,11 +6,11 @@ namespace CompactCryptoGroupAlgebra.EllipticCurves.Tests { [TestFixture] - public class ProjectedMontgomeryCurveAlgebraTests + public class XOnlyMontgomeryCurveAlgebraTests { private CurveParameters ecParams; - public ProjectedMontgomeryCurveAlgebraTests() + public XOnlyMontgomeryCurveAlgebraTests() { ecParams = new CurveParameters( p: BigPrime.CreateWithoutChecks(41), @@ -80,26 +80,13 @@ public ProjectedMontgomeryCurveAlgebraTests() [TestCase(11, 2, 0)] public void TestMultiplyScalar(int k, int x, int expectedX) { - var algebra = new ProjectedMontgomeryCurveAlgebra(ecParams); + var algebra = new XOnlyMontgomeryCurveAlgebra(ecParams); - var p = new MontgomeryPoint(new BigInteger(x)); + var p = new BigInteger(x); var result = algebra.MultiplyScalar(p, k); - result = algebra.RenormalizePoint(result); - Assert.AreEqual(new BigInteger(expectedX), result.X); + Assert.AreEqual(new BigInteger(expectedX), result); } - [Test] - [TestCase(3, 17, 5)] - [TestCase(4, 1, 4)] - [TestCase(1, 40, 40)] - public void TestRenormalizePoint(int x, int z, int expectedX) - { - var algebra = new ProjectedMontgomeryCurveAlgebra(ecParams); - var p = new MontgomeryPoint(new BigInteger(x), new BigInteger(z)); - var result = algebra.RenormalizePoint(p); - - Assert.AreEqual(new BigInteger(expectedX), result.X); - } } } diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs b/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs index 36f9a81..02015c2 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs @@ -81,13 +81,33 @@ public bool AreNegations(CurvePoint left, CurvePoint right) /// public override CurvePoint FromBytes(byte[] buffer) { - throw new NotImplementedException(); + if (buffer.Length < 2 * _field.ElementByteLength) + throw new ArgumentException("The given buffer is too short to contain a valid element representation.", nameof(buffer)); + + byte[] xBytes = new byte[_field.ElementByteLength]; + byte[] yBytes = new byte[_field.ElementByteLength]; + + Buffer.BlockCopy(buffer, 0, xBytes, 0, _field.ElementByteLength); + Buffer.BlockCopy(buffer, _field.ElementByteLength, yBytes, 0, _field.ElementByteLength); + + BigInteger x = new BigInteger(xBytes); + BigInteger y = new BigInteger(yBytes); + return new CurvePoint(x, y); } /// public override byte[] ToBytes(CurvePoint element) { - throw new NotImplementedException(); + byte[] xBytes = element.X.ToByteArray(); + byte[] yBytes = element.Y.ToByteArray(); + + Debug.Assert(xBytes.Length <= _field.ElementByteLength); + Debug.Assert(yBytes.Length <= _field.ElementByteLength); + + byte[] result = new byte[2 * _field.ElementByteLength]; + Buffer.BlockCopy(xBytes, 0, result, 0, xBytes.Length); + Buffer.BlockCopy(yBytes, 0, result, _field.ElementByteLength, yBytes.Length); + return result; } /// diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurvePoint.cs b/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurvePoint.cs index a076a68..fdeacd8 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurvePoint.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurvePoint.cs @@ -7,7 +7,7 @@ namespace CompactCryptoGroupAlgebra.EllipticCurves /// A point on a Montgomery elliptic curve in projected coordinates /// and without y-coordinate. /// - /// Raw group element of . This struct + /// Raw group element of . This struct /// is mainly a information container and implements no algebraic operations. /// public readonly struct MontgomeryCurvePoint diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/ProjectedMontgomeryCurveAlgebra.cs b/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs similarity index 71% rename from CompactCryptoGroupAlgebra/EllipticCurves/ProjectedMontgomeryCurveAlgebra.cs rename to CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs index 9e106d8..ea72074 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/ProjectedMontgomeryCurveAlgebra.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs @@ -1,10 +1,10 @@ -using System; +using System; using System.Collections.Generic; using System.Numerics; namespace CompactCryptoGroupAlgebra.EllipticCurves { - + /// /// An implementation of for /// Montgomery curves using x-only arithmetic on projected coordinates. @@ -15,20 +15,20 @@ namespace CompactCryptoGroupAlgebra.EllipticCurves /// /// Implementation based on https://eprint.iacr.org/2017/212.pdf . /// - public class ProjectedMontgomeryCurveAlgebra : CryptoGroupAlgebra + public class XOnlyMontgomeryCurveAlgebra : CryptoGroupAlgebra { private readonly CurveParameters _parameters; private BigIntegerField _field; private BigInteger _aConstant; /// - /// Initializes a new instance of + /// Initializes a new instance of /// with given curve parameters. /// /// Curve parameters. - public ProjectedMontgomeryCurveAlgebra(CurveParameters parameters) + public XOnlyMontgomeryCurveAlgebra(CurveParameters parameters) : base( - new MontgomeryPoint(parameters.Generator.X), + parameters.Generator.X, parameters.Order ) { @@ -44,7 +44,7 @@ public ProjectedMontgomeryCurveAlgebra(CurveParameters parameters) public override int ElementBitLength { get { return NumberLength.GetLength(_parameters.P).InBits; } } /// - public override MontgomeryPoint NeutralElement { get { return MontgomeryPoint.AtInfinity; } } + public override BigInteger NeutralElement { get { return BigInteger.Zero; } } /// /// Sums two projected Montgomery point using only x- and z-coordinates and knowledge of @@ -56,7 +56,7 @@ public ProjectedMontgomeryCurveAlgebra(CurveParameters parameters) /// Curve point to add. /// Difference of and /// . - private MontgomeryPoint AddInternal(MontgomeryPoint left, MontgomeryPoint right, MontgomeryPoint diff) + private MontgomeryCurvePoint XOnlyAdd(MontgomeryCurvePoint left, MontgomeryCurvePoint right, MontgomeryCurvePoint diff) { if (right.IsAtInfinity) return left; @@ -85,22 +85,7 @@ private MontgomeryPoint AddInternal(MontgomeryPoint left, MontgomeryPoint right, firstProduct - secondProduct )); - //var v0 = _ring.Mod(xp + zp); - //var v1 = _ring.Mod(xq - zq); - //v1 = _ring.Mod(v0 * v1); - //v0 = _ring.Mod(xp - zp); - //var v2 = _ring.Mod(xq + zq); - //v2 = _ring.Mod(v2 * v0); - //var v3 = _ring.Mod(v1 + v2); - - //v3 = _ring.Square(v3); - //var v4 = _ring.Mod(v1 - v2); - //v4 = _ring.Square(v4); - - //var xNew = _ring.Mod(diff.Z * v3); - //var zNew = _ring.Mod(diff.X * v4); - - return new MontgomeryPoint(xNew, zNew); + return new MontgomeryCurvePoint(xNew, zNew); } /// @@ -108,7 +93,7 @@ private MontgomeryPoint AddInternal(MontgomeryPoint left, MontgomeryPoint right, /// /// The curve point. /// The doubled curve point. - private MontgomeryPoint DoubleInternal(MontgomeryPoint point) + private MontgomeryCurvePoint XOnlyDouble(MontgomeryCurvePoint point) { BigInteger xp = point.X; BigInteger zp = point.Z; @@ -120,19 +105,7 @@ private MontgomeryPoint DoubleInternal(MontgomeryPoint point) var xNew = _field.Mod(xpPlusZpSquared * xpMinusZpSquared); var zNew = _field.Mod(xpzp4 * (xpMinusZpSquared + _aConstant * xpzp4)); - //var v1 = _ring.Mod(xp + zp); - //v1 = _ring.Square(v1); - //var v2 = _ring.Mod(xp - zp); - //v2 = _ring.Square(v2); - //var xNew = _ring.Mod(v1 * v2); - - - - //v1 = _ring.Mod(v1 - v2); - //var v3 = _ring.Mod((_parameters.A + 2) * _ring.InvertMult(4) * v1); - //v3 = _ring.Mod(v3 + v2); - //var zNew = _ring.Mod(v1 * v3); - return new MontgomeryPoint(xNew, zNew); + return new MontgomeryCurvePoint(xNew, zNew); } /// @@ -141,39 +114,23 @@ private MontgomeryPoint DoubleInternal(MontgomeryPoint point) /// /// The curve point point. /// The renormalized curve point. - public MontgomeryPoint RenormalizePoint(MontgomeryPoint point) + private MontgomeryCurvePoint RenormalizePoint(MontgomeryCurvePoint point) { - return new MontgomeryPoint( + return new MontgomeryCurvePoint( _field.Mod(_field.InvertMult(point.Z) * point.X) ); } /// - public override MontgomeryPoint Add(MontgomeryPoint left, MontgomeryPoint right) + public override BigInteger Add(BigInteger left,BigInteger right) { throw new NotSupportedException("A projected Montgomery curve" + "has no definition for the standard addition. Use the" + "standard Montgomery curve implementation instead."); } - /// - /// Checks whether two given points are negations of each other, i.e., - /// adding them results in the zero element (point at infinity). - /// - /// - /// true, if the given points are negations of each other; - /// false otherwise. - /// - /// Curve point - /// Curve point - public bool AreNegations(MontgomeryPoint left, MontgomeryPoint right) - { - var inv = Negate(right); - return left.Equals(inv); - } - /// - public override MontgomeryPoint FromBytes(byte[] buffer) + public override BigInteger FromBytes(byte[] buffer) { if (buffer == null) throw new ArgumentNullException(nameof(buffer)); @@ -186,16 +143,13 @@ public override MontgomeryPoint FromBytes(byte[] buffer) Buffer.BlockCopy(buffer, 0, xBytes, 0, _field.ElementByteLength); BigInteger x = new BigInteger(xBytes); - return new MontgomeryPoint(x); + return x; } /// - public override byte[] ToBytes(MontgomeryPoint element) + public override byte[] ToBytes(BigInteger element) { - if (!element.IsNormalized) - element = RenormalizePoint(element); - - byte[] xBytes = element.X.ToByteArray(); + byte[] xBytes = element.ToByteArray(); Debug.Assert(xBytes.Length <= _field.ElementByteLength); @@ -203,19 +157,16 @@ public override byte[] ToBytes(MontgomeryPoint element) } /// - public override MontgomeryPoint Negate(MontgomeryPoint point) + public override BigInteger Negate(BigInteger point) { return point; } /// - protected override bool IsElementDerived(MontgomeryPoint point) + protected override bool IsElementDerived(BigInteger point) { - if (!_field.IsElement(point.X)) - return false; - if (!_field.IsElement(point.Z)) - return false; - + return _field.IsElement(point); + // In Montgomery form, every x coordinate corresponds to a valid // point either on the curve or on its twist, i.e., another valid // curve. We do not really care about which of these two we compute @@ -224,7 +175,6 @@ protected override bool IsElementDerived(MontgomeryPoint point) // here. // Note that subgroup attacks could be problematic, but those // are caught already in CryptoGroupAlgebra's IsElement implementation. - return true; } /// @@ -239,7 +189,7 @@ protected override bool IsElementDerived(MontgomeryPoint point) /// Selection indicator. /// First selection option. /// Second selection option. - protected BigInteger Multiplex(BigInteger selection, BigInteger first, BigInteger second) + protected override BigInteger Multiplex(BigInteger selection, BigInteger first, BigInteger second) { Debug.Assert(selection.IsOne || selection.IsZero); return first + selection * (second - first); @@ -257,13 +207,13 @@ protected BigInteger Multiplex(BigInteger selection, BigInteger first, BigIntege /// Selection indicator. /// First selection option. /// First selection option. - protected override MontgomeryPoint Multiplex( - BigInteger selection, MontgomeryPoint first, MontgomeryPoint second + private MontgomeryCurvePoint Multiplex( + BigInteger selection, MontgomeryCurvePoint first, MontgomeryCurvePoint second ) { Debug.Assert(selection.IsOne || selection.IsZero); var sel = !selection.IsZero; - return new MontgomeryPoint( + return new MontgomeryCurvePoint( Multiplex(selection, first.X, second.X), Multiplex(selection, first.Z, second.Z) ); @@ -286,8 +236,8 @@ protected override MontgomeryPoint Multiplex( /// Swapping indicator. /// Curve point. /// Curve point. - private (MontgomeryPoint, MontgomeryPoint) ConditionalSwap( - BigInteger selector, MontgomeryPoint first, MontgomeryPoint second + private (MontgomeryCurvePoint, MontgomeryCurvePoint) ConditionalSwap( + BigInteger selector, MontgomeryCurvePoint first, MontgomeryCurvePoint second ) { Debug.Assert(selector.IsOne || selector.IsZero); @@ -298,13 +248,14 @@ protected override MontgomeryPoint Multiplex( } /// - protected override MontgomeryPoint MultiplyScalarUnchecked(MontgomeryPoint e, BigInteger k, int factorBitLength) + protected override BigInteger MultiplyScalarUnchecked(BigInteger e, BigInteger k, int factorBitLength) { BigInteger maxFactor = BigInteger.One << factorBitLength; int i = factorBitLength - 1; - MontgomeryPoint r0 = NeutralElement; - MontgomeryPoint r1 = e; + MontgomeryCurvePoint point = new MontgomeryCurvePoint(e); + MontgomeryCurvePoint r0 = MontgomeryCurvePoint.PointAtInfinity; + MontgomeryCurvePoint r1 = point; // Montgomery ladder maintains invariant r1 = r0 + e // and can thus use x-only addition that requires knowledge of r1-r0 (= e) @@ -313,8 +264,8 @@ protected override MontgomeryPoint MultiplyScalarUnchecked(MontgomeryPoint e, Bi BigInteger bitI = (k & mask) >> i; (r0, r1) = ConditionalSwap(bitI, r0, r1); - r1 = AddInternal(r0, r1, e); - r0 = DoubleInternal(r0); + r1 = XOnlyAdd(r0, r1, point); + r0 = XOnlyDouble(r0); (r0, r1) = ConditionalSwap(bitI, r0, r1); //if (bitI.IsZero) //{ @@ -328,13 +279,13 @@ protected override MontgomeryPoint MultiplyScalarUnchecked(MontgomeryPoint e, Bi //} } Debug.Assert(i == -1); - return r0; + return RenormalizePoint(r0).X; } /// - public override bool Equals(CryptoGroupAlgebra? other) + public override bool Equals(CryptoGroupAlgebra? other) { - var algebra = other as ProjectedMontgomeryCurveAlgebra; + var algebra = other as XOnlyMontgomeryCurveAlgebra; return other != null && EqualityComparer.Default.Equals(_parameters, algebra!._parameters); } From d8ba9949e5bbfd0d9b8e83b0529ef44f63424d77 Mon Sep 17 00:00:00 2001 From: Lukas Prediger Date: Sun, 5 Jul 2020 17:25:51 +0300 Subject: [PATCH 4/8] Added tests for MontgomeryCurveAlgebra --- .../EllipticCurves/CurveGroupAlgebraTests.cs | 2 +- .../MontgomeryCurveAlgebraTests.cs | 276 ++++++++++++++++-- .../EllipticCurves/CurveGroupAlgebra.cs | 8 +- .../EllipticCurves/CurvePoint.cs | 1 - .../EllipticCurves/MontgomeryCurveAlgebra.cs | 28 +- 5 files changed, 285 insertions(+), 30 deletions(-) diff --git a/CompactCryptoGroupAlgebra.Tests/EllipticCurves/CurveGroupAlgebraTests.cs b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/CurveGroupAlgebraTests.cs index 7c58480..1182f0b 100644 --- a/CompactCryptoGroupAlgebra.Tests/EllipticCurves/CurveGroupAlgebraTests.cs +++ b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/CurveGroupAlgebraTests.cs @@ -156,7 +156,7 @@ public void TestAddAffine() [TestCase(13, 8)] [TestCase(18, 3)] [TestCase(0, 20)] - public void TestPointValidTrueForValidPoint(int xRaw, int yRaw) + public void TestIsElementTrueForValidPoint(int xRaw, int yRaw) { var curve = new CurveGroupAlgebra(ecParams); var point = new CurvePoint(xRaw, yRaw); diff --git a/CompactCryptoGroupAlgebra.Tests/EllipticCurves/MontgomeryCurveAlgebraTests.cs b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/MontgomeryCurveAlgebraTests.cs index 8b159a3..68b89a1 100644 --- a/CompactCryptoGroupAlgebra.Tests/EllipticCurves/MontgomeryCurveAlgebraTests.cs +++ b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/MontgomeryCurveAlgebraTests.cs @@ -8,7 +8,8 @@ namespace CompactCryptoGroupAlgebra.EllipticCurves.Tests [TestFixture] public class MontgomeryCurveAlgebraTests { - private CurveParameters ecParams; + private readonly CurveParameters ecParams; + private readonly CurveParameters largeParams; public MontgomeryCurveAlgebraTests() { @@ -62,7 +63,40 @@ public MontgomeryCurveAlgebraTests() // (39, (array([17, 24]),))] //) + largeParams = new CurveParameters( + p: BigPrime.CreateWithoutChecks(18392027), // 25 bits + generator: CurvePoint.PointAtInfinity, + order: BigPrime.CreateWithoutChecks(3), + a: 0, b: 0, cofactor: 1 + ); + } + [Test] + public void TestConstructorAndProperties() + { + var algebra = new MontgomeryCurveAlgebra(ecParams); + + Assert.AreEqual(ecParams.Cofactor, algebra.Cofactor); + Assert.AreEqual(CurvePoint.PointAtInfinity, algebra.NeutralElement); + Assert.AreEqual(2*NumberLength.GetLength(ecParams.P).InBits, algebra.ElementBitLength); + } + + [Test] + public void TestInvalidElementRejectedAsGenerator() + { + var invalidGenerator = new CurvePoint(0, 0); + CurveParameters invalidParams = new CurveParameters( + generator: invalidGenerator, + p: ecParams.P, + a: ecParams.A, + b: ecParams.B, + order: ecParams.Order, + cofactor: ecParams.Cofactor + ); + + Assert.Throws( + () => new MontgomeryCurveAlgebra(invalidParams) + ); } [Test] @@ -89,6 +123,58 @@ public void TestAddDifferent() Assert.AreEqual(expected, result); } + [Test] + public void TestAddNeutralElementRight() + { + var point = new CurvePoint(2, 6); + var otherPoint = CurvePoint.PointAtInfinity; + var algebra = new MontgomeryCurveAlgebra(ecParams); + + var expected = point; + var result = algebra.Add(point, otherPoint); + + Assert.AreEqual(expected, result); + } + + [Test] + public void TestAddNeutralElementLeft() + { + var point = new CurvePoint(2, 6); + var otherPoint = CurvePoint.PointAtInfinity; + var algebra = new MontgomeryCurveAlgebra(ecParams); + + var expected = point; + var result = algebra.Add(otherPoint, point); + + Assert.AreEqual(expected, result); + } + + [Test] + public void TestAddNeutralElements() + { + var point = CurvePoint.PointAtInfinity; + var otherPoint = CurvePoint.PointAtInfinity; + var algebra = new MontgomeryCurveAlgebra(ecParams); + + var expected = CurvePoint.PointAtInfinity; + var result = algebra.Add(otherPoint, point); + + Assert.AreEqual(expected, result); + } + + [Test] + public void TestAddNegatedElements() + { + var point = new CurvePoint(2, 6); + var otherPoint = new CurvePoint(2, 41 - 6); + var algebra = new MontgomeryCurveAlgebra(ecParams); + + var expected = CurvePoint.PointAtInfinity; + var result = algebra.Add(otherPoint, point); + + Assert.AreEqual(expected, result); + } + [Test] [TestCase(1, 2, 6, 2, 6)] @@ -108,28 +194,172 @@ public void TestMultiplyScalar(int k, int x, int y, int expectedX, int expectedY Assert.AreEqual(expected, result); } - //[Test] - //public void GetOrder() - //{ - // var point = new CurvePoint(2, 6); - // var algebra = new MontgomeryCurveAlgebra(ecParams); - - // for (int i = 1; i < ecParams.Order; ++i) - // { - // var r = algebra.MultiplyScalar(point, i); - // Console.WriteLine(String.Format("{0}: {1}", i, r)); - // //Assert.IsTrue(algebra.IsElement(r)); - // } - - // //Assert.IsTrue(algebra.IsElement(point), "not valid"); - // var point2 = algebra.MultiplyScalar(point, 2); - // Assert.AreNotEqual(point2, CurvePoint.PointAtInfinity, "order 2"); - // var point4 = algebra.MultiplyScalar(point, 4); - // Assert.AreNotEqual(point4, CurvePoint.PointAtInfinity, "order 4"); - // var point11 = algebra.MultiplyScalar(point, 11); - // Assert.AreEqual(point11, CurvePoint.PointAtInfinity, "order not 11"); - - //} + [Test] + public void TestNegate() + { + var point = new CurvePoint(2, 6); + var algebra = new MontgomeryCurveAlgebra(ecParams); + + var expected = new CurvePoint(2, 41 - 6); + var result = algebra.Negate(point); + + Assert.AreEqual(expected, result); + } + + [Test] + public void TestNegateNeutralElement() + { + var algebra = new MontgomeryCurveAlgebra(ecParams); + var point = algebra.NeutralElement; + + var expected = point; + var result = algebra.Negate(point); + + Assert.AreEqual(expected, result); + } + + [Test] + public void TestEqualsFalseForNull() + { + var groupAlgebra = new MontgomeryCurveAlgebra(ecParams); + + bool result = groupAlgebra.Equals(null); + Assert.IsFalse(result); + } + + [Test] + public void TestEqualsFalseForUnrelatedObject() + { + var groupAlgebra = new MontgomeryCurveAlgebra(ecParams); + var otherAlgebra = new object { }; + + bool result = groupAlgebra.Equals(otherAlgebra); + Assert.IsFalse(result); + } + + [Test] + public void TestEqualsFalseForOtherAlgebra() + { + var groupAlgebra = new MontgomeryCurveAlgebra(ecParams); + + var otherParams = largeParams; + var otherAlgebra = new MontgomeryCurveAlgebra(otherParams); + + bool result = groupAlgebra.Equals(otherAlgebra); + Assert.IsFalse(result); + } + + [Test] + public void TestGetHashCodeSameForEqual() + { + var groupAlgebra = new MontgomeryCurveAlgebra(ecParams); + var otherAlgebra = new MontgomeryCurveAlgebra(ecParams); + + Assert.AreEqual(groupAlgebra.GetHashCode(), otherAlgebra.GetHashCode()); + } + + [Test] + public void TestFromBytes() + { + var curve = new MontgomeryCurveAlgebra(largeParams); + var expected = new CurvePoint(5, 3); + var buffer = new byte[] { 5, 0, 0, 0, 3, 0, 0, 0 }; + + var result = curve.FromBytes(buffer); + Assert.AreEqual(expected, result); + } + + [Test] + public void TestToBytes() + { + var curve = new MontgomeryCurveAlgebra(largeParams); + var p = new CurvePoint(5, 3); + var expected = new byte[] { 5, 0, 0, 0, 3, 0, 0, 0 }; + + var result = curve.ToBytes(p); + CollectionAssert.AreEqual(expected, result); + } + + [Test] + public void TestFromBytesWithLessThanOneByteLargeElements() + { + var curve = new MontgomeryCurveAlgebra(ecParams); + var expected = new CurvePoint(5, 3); + var buffer = new byte[] { 5, 3 }; + + var result = curve.FromBytes(buffer); + Assert.AreEqual(expected, result); + } + + [Test] + public void TestToBytesWithLessThanOneByteLargeElements() + { + var curve = new MontgomeryCurveAlgebra(ecParams); + var p = new CurvePoint(5, 5); + var expected = new byte[] { 5, 5 }; + + var result = curve.ToBytes(p); + CollectionAssert.AreEqual(expected, result); + } + + [Test] + public void TestFromBytesRejectsTooShortBuffer() + { + var curve = new MontgomeryCurveAlgebra(largeParams); + var buffer = new byte[7]; + Assert.Throws( + () => curve.FromBytes(buffer) + ); + } + + + [Test] + [TestCase(38, 24)] + [TestCase(2, 6)] + [TestCase(18, 39)] + public void TestIsElementTrueForValidPoint(int xRaw, int yRaw) + { + var curve = new MontgomeryCurveAlgebra(ecParams); + var point = new CurvePoint(xRaw, yRaw); + Assert.IsTrue(curve.IsElement(point)); + } + + [Test] + public void TestIsElementFalseForPointAtInfinity() + { + var curve = new MontgomeryCurveAlgebra(ecParams); + Assert.IsFalse(curve.IsElement(CurvePoint.PointAtInfinity)); + } + + [Test] + [TestCase(3, 1)] + [TestCase(1, 6)] + [TestCase(-2, 1)] + [TestCase(1, -4)] + [TestCase(25, 7)] + public void TestIsElementFalseForPointNotOnCurve(int xRaw, int yRaw) + { + var curve = new MontgomeryCurveAlgebra(ecParams); + var point = new CurvePoint(xRaw, yRaw); + Assert.IsFalse(curve.IsElement(point)); + } + + [Test] + [TestCase(0, 0)] + public void TestIsElementFalseForLowOrderCurvePoint(int xRaw, int yRaw) + { + var curve = new MontgomeryCurveAlgebra(ecParams); + var point = new CurvePoint(xRaw, yRaw); + Assert.IsFalse(curve.IsElement(point)); + } + + [Test] + public void TestCreateCryptoGroup() + { + var groupAlgebra = new MontgomeryCurveAlgebra(ecParams); + var group = MontgomeryCurveAlgebra.CreateCryptoGroup(ecParams); + Assert.AreEqual(groupAlgebra, group.Algebra); + } } } diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/CurveGroupAlgebra.cs b/CompactCryptoGroupAlgebra/EllipticCurves/CurveGroupAlgebra.cs index f168a5a..a72d428 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/CurveGroupAlgebra.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/CurveGroupAlgebra.cs @@ -7,8 +7,8 @@ namespace CompactCryptoGroupAlgebra.EllipticCurves /// /// Cryptographic group based on point addition in elliptic curves in Weierstrass form. /// - /// Weierstrass curves are of form y² = x³ + Ax + B, with all numbers from finite field defined - /// by a prime number P. Elements of the groups are all points (x mod P, y mod P) that satisfy + /// Weierstrass curves are of form y² = x³ + Ax + B, with all numbers from finite field + /// with characteristic P. Elements of the groups are all points (x mod P, y mod P) that satisfy /// the curve equation (and the addtional "point at infinity" as neutral element). /// /// The exact parameters of the curve (A, B, P) are encoded in a object. @@ -48,7 +48,7 @@ public CurveGroupAlgebra(CurveParameters parameters) _parameters = parameters; _field = new BigIntegerField(_parameters.P); if (!IsElement(Generator)) - throw new ArgumentException("The point given as generator is" + + throw new ArgumentException("The point given as generator is " + "not a valid point on the curve.", nameof(parameters)); } @@ -217,7 +217,7 @@ public override int GetHashCode() /// Creates a instance using a /// instance with the given parameters. /// - /// The prime modulo of the group. + /// Parameters for the curve. public static CryptoGroup CreateCryptoGroup(CurveParameters curveParameters) { return new CryptoGroup(new CurveGroupAlgebra(curveParameters)); diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/CurvePoint.cs b/CompactCryptoGroupAlgebra/EllipticCurves/CurvePoint.cs index 60e7684..c345d16 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/CurvePoint.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/CurvePoint.cs @@ -92,7 +92,6 @@ public bool Equals(CurvePoint other) return (IsAtInfinity && other.IsAtInfinity) || (!IsAtInfinity && !other.IsAtInfinity && (X == other.X) && (Y == other.Y)); } - /// /// Selects one of two given scalars. /// diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs b/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs index 02015c2..12ecb82 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs @@ -5,16 +5,32 @@ namespace CompactCryptoGroupAlgebra.EllipticCurves { + /// + /// Cryptographic group based on point addition in elliptic curves in Montgomery form. + /// + /// Montgomery curves are of form By² = x³ + Ax² + x, with all numbers from the finite field with + /// characteristic P. Elements of the groups are all points (x mod P, y mod P) that satisfy + /// the curve equation (and the addtional "point at infinity" as neutral element). + /// + /// The exact parameters of the curve (A, B, P) are encoded in a object. + /// public class MontgomeryCurveAlgebra : CryptoGroupAlgebra { private readonly CurveParameters _parameters; private readonly BigIntegerField _field; + /// + /// Initializes a new instance of the class. + /// + /// Parameters for the curve. public MontgomeryCurveAlgebra(CurveParameters parameters) : base(parameters.Generator, parameters.Order) { _parameters = parameters; _field = new BigIntegerField(_parameters.P); + if (!IsElement(Generator)) + throw new ArgumentException("The point given as generator is " + + "not a valid point on the curve.", nameof(parameters)); } /// @@ -115,7 +131,7 @@ public override CurvePoint Negate(CurvePoint e) { if (e.IsAtInfinity) return e; - return new CurvePoint(e.X, -e.Y); + return new CurvePoint(e.X, _field.Mod(-e.Y)); } /// @@ -168,5 +184,15 @@ public override int GetHashCode() { return -2051777468 + EqualityComparer.Default.GetHashCode(_parameters); } + + /// + /// Creates a instance using a + /// instance with the given parameters. + /// + /// Parameters for the curve. + public static CryptoGroup CreateCryptoGroup(CurveParameters curveParameters) + { + return new CryptoGroup(new MontgomeryCurveAlgebra(curveParameters)); + } } } From ad31f9479b0fe01aa5de8723637e235993b8143a Mon Sep 17 00:00:00 2001 From: Lukas Prediger Date: Sun, 5 Jul 2020 17:30:25 +0300 Subject: [PATCH 5/8] Updates to docstrings of curve algebra implementations --- .../EllipticCurves/CurveGroupAlgebra.cs | 2 +- .../EllipticCurves/MontgomeryCurveAlgebra.cs | 2 +- .../XOnlyMontgomeryCurveAlgebra.cs | 17 +++++++++++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/CurveGroupAlgebra.cs b/CompactCryptoGroupAlgebra/EllipticCurves/CurveGroupAlgebra.cs index a72d428..e582c75 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/CurveGroupAlgebra.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/CurveGroupAlgebra.cs @@ -9,7 +9,7 @@ namespace CompactCryptoGroupAlgebra.EllipticCurves /// /// Weierstrass curves are of form y² = x³ + Ax + B, with all numbers from finite field /// with characteristic P. Elements of the groups are all points (x mod P, y mod P) that satisfy - /// the curve equation (and the addtional "point at infinity" as neutral element). + /// the curve equation (and the additional "point at infinity" as neutral element). /// /// The exact parameters of the curve (A, B, P) are encoded in a object. /// diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs b/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs index 12ecb82..b3865b5 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs @@ -10,7 +10,7 @@ namespace CompactCryptoGroupAlgebra.EllipticCurves /// /// Montgomery curves are of form By² = x³ + Ax² + x, with all numbers from the finite field with /// characteristic P. Elements of the groups are all points (x mod P, y mod P) that satisfy - /// the curve equation (and the addtional "point at infinity" as neutral element). + /// the curve equation (and the additional "point at infinity" as neutral element). /// /// The exact parameters of the curve (A, B, P) are encoded in a object. /// diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs b/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs index ea72074..1381287 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs @@ -6,11 +6,20 @@ namespace CompactCryptoGroupAlgebra.EllipticCurves { /// - /// An implementation of for - /// Montgomery curves using x-only arithmetic on projected coordinates. + /// Cryptographic group based on point addition in elliptic curves in Montgomery form + /// using x-coordinate-only arithmetics on projected coordinates. /// - /// Note: Does not implements the standard addition of - /// but only . + /// Montgomery curves are of form By² = x³ + Ax² + x, with all numbers from the finite field with + /// characteristic P. Elements of the groups are all points (x mod P, y mod P) that satisfy + /// the curve equation (and the additional "point at infinity" as neutral element). + /// + /// The exact parameters of the curve (A, B, P) are encoded in a object. + /// + /// The x-coordinate-only specification is computationally more efficient (and elements require less + /// storage) but does not have a well-defined addition for arbitrary points. + /// is therefore not implemented (however, + /// is). If you require full + /// addition on arbitrary points, use . /// /// /// Implementation based on https://eprint.iacr.org/2017/212.pdf . From 7a0fb8813c15886ff98863f7a21f2faaf29afb28 Mon Sep 17 00:00:00 2001 From: Lukas Prediger Date: Mon, 6 Jul 2020 13:31:22 +0300 Subject: [PATCH 6/8] Full tests for XOnlyMongomeryCurveAlgebra --- .../XOnlyMontgomeryCurveAlgebraTests.cs | 144 +++++++++++++++++- .../XOnlyMontgomeryCurveAlgebra.cs | 39 ++--- 2 files changed, 155 insertions(+), 28 deletions(-) diff --git a/CompactCryptoGroupAlgebra.Tests/EllipticCurves/XOnlyMontgomeryCurveAlgebraTests.cs b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/XOnlyMontgomeryCurveAlgebraTests.cs index 6c3f4c3..a28d966 100644 --- a/CompactCryptoGroupAlgebra.Tests/EllipticCurves/XOnlyMontgomeryCurveAlgebraTests.cs +++ b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/XOnlyMontgomeryCurveAlgebraTests.cs @@ -8,7 +8,8 @@ namespace CompactCryptoGroupAlgebra.EllipticCurves.Tests [TestFixture] public class XOnlyMontgomeryCurveAlgebraTests { - private CurveParameters ecParams; + private readonly CurveParameters ecParams; + private readonly CurveParameters largeParams; public XOnlyMontgomeryCurveAlgebraTests() { @@ -62,7 +63,12 @@ public XOnlyMontgomeryCurveAlgebraTests() // (39, (array([17, 24]),))] //) - + largeParams = new CurveParameters( + p: BigPrime.CreateWithoutChecks(18392027), // 25 bits + generator: CurvePoint.PointAtInfinity, + order: BigPrime.CreateWithoutChecks(3), + a: 0, b: 0, cofactor: 1 + ); } [Test] @@ -88,5 +94,139 @@ public void TestMultiplyScalar(int k, int x, int expectedX) Assert.AreEqual(new BigInteger(expectedX), result); } + [Test] + public void TestConstructorAndProperties() + { + var algebra = new XOnlyMontgomeryCurveAlgebra(ecParams); + + Assert.AreEqual(ecParams.Generator.X, algebra.Generator); + Assert.AreEqual(ecParams.Order, algebra.Order); + Assert.AreEqual(ecParams.Cofactor, algebra.Cofactor); + Assert.AreEqual(NumberLength.GetLength(ecParams.P).InBits, algebra.ElementBitLength); + } + + [Test] + public void TestAddThrowsNotSupported() + { + var point = new BigInteger(2); + var otherPoint = new BigInteger(6); + var algebra = new XOnlyMontgomeryCurveAlgebra(ecParams); + + Assert.Throws( + () => algebra.Add(point, otherPoint) + ); + } + + [Test] + public void TestNegateThrowsNotSupported() + { + var point = new BigInteger(2); + var algebra = new XOnlyMontgomeryCurveAlgebra(ecParams); + + Assert.Throws( + () => algebra.Negate(point) + ); + } + + + [Test] + public void TestFromBytes() + { + var curve = new XOnlyMontgomeryCurveAlgebra(largeParams); + var expected = new BigInteger(5); + var buffer = new byte[] { 5 }; + + var result = curve.FromBytes(buffer); + Assert.AreEqual(expected, result); + } + + [Test] + public void TestToBytes() + { + var curve = new XOnlyMontgomeryCurveAlgebra(largeParams); + var p = new BigInteger(5); + var expected = new byte[] { 5 }; + + var result = curve.ToBytes(p); + CollectionAssert.AreEqual(expected, result); + } + + [Test] + public void TestFromBytesWithLessThanOneByteLargeElements() + { + var curve = new XOnlyMontgomeryCurveAlgebra(ecParams); + var expected = new BigInteger(5); + var buffer = new byte[] { 5 }; + + var result = curve.FromBytes(buffer); + Assert.AreEqual(expected, result); + } + + [Test] + public void TestToBytesWithLessThanOneByteLargeElements() + { + var curve = new XOnlyMontgomeryCurveAlgebra(ecParams); + var p = new BigInteger(5); + var expected = new byte[] { 5 }; + + var result = curve.ToBytes(p); + CollectionAssert.AreEqual(expected, result); + } + + [Test] + public void TestIsElement() + { + var curve = new XOnlyMontgomeryCurveAlgebra(ecParams); + var p = new BigInteger(7); + + Assert.IsTrue(curve.IsElement(p)); + } + + [Test] + public void TestIsElementFalseForLowOrderElements() + { + var curve = new XOnlyMontgomeryCurveAlgebra(ecParams); + var p = new BigInteger(0); + + Assert.IsFalse(curve.IsElement(p)); + } + + [Test] + public void TestEqualsTrueForEqual() + { + var curve = new XOnlyMontgomeryCurveAlgebra(ecParams); + var otherCurve = new XOnlyMontgomeryCurveAlgebra(ecParams); + + Assert.IsTrue(curve.Equals(otherCurve)); + } + + [Test] + public void TestEqualsFalseForDifferent() + { + var curve = new XOnlyMontgomeryCurveAlgebra(ecParams); + var otherCurve = new XOnlyMontgomeryCurveAlgebra(largeParams); + + Assert.IsFalse(curve.Equals(otherCurve)); + } + + [Test] + public void TestEqualsFalseForNull() + { + var curve = new XOnlyMontgomeryCurveAlgebra(ecParams); + + Assert.IsFalse(curve.Equals(null)); + } + + [Test] + public void TestGetHashCodeSameForEqual() + { + var curve = new XOnlyMontgomeryCurveAlgebra(ecParams); + var otherCurve = new XOnlyMontgomeryCurveAlgebra(ecParams); + + Assert.AreEqual(curve.GetHashCode(), otherCurve.GetHashCode()); + } + + + } } diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs b/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs index 1381287..c5f4854 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs @@ -17,9 +17,10 @@ namespace CompactCryptoGroupAlgebra.EllipticCurves /// /// The x-coordinate-only specification is computationally more efficient (and elements require less /// storage) but does not have a well-defined addition for arbitrary points. - /// is therefore not implemented (however, - /// is). If you require full - /// addition on arbitrary points, use . + /// and + /// are therefore not implemented (however, + /// is). + /// If you require full addition on arbitrary points, use . /// /// /// Implementation based on https://eprint.iacr.org/2017/212.pdf . @@ -133,42 +134,29 @@ private MontgomeryCurvePoint RenormalizePoint(MontgomeryCurvePoint point) /// public override BigInteger Add(BigInteger left,BigInteger right) { - throw new NotSupportedException("A projected Montgomery curve" + - "has no definition for the standard addition. Use the" + + throw new NotSupportedException("An x-only Montgomery curve " + + "has no definition for the standard addition. Use the " + "standard Montgomery curve implementation instead."); } /// public override BigInteger FromBytes(byte[] buffer) { - if (buffer == null) - throw new ArgumentNullException(nameof(buffer)); - if (buffer.Length < _field.ElementByteLength) - throw new ArgumentException("The given buffer is too short to" + - "contain a valid element representation.", nameof(buffer)); - - byte[] xBytes = new byte[_field.ElementByteLength]; - - Buffer.BlockCopy(buffer, 0, xBytes, 0, _field.ElementByteLength); - - BigInteger x = new BigInteger(xBytes); - return x; + return new BigInteger(buffer); } /// public override byte[] ToBytes(BigInteger element) { - byte[] xBytes = element.ToByteArray(); - - Debug.Assert(xBytes.Length <= _field.ElementByteLength); - - return xBytes; + return element.ToByteArray(); } /// public override BigInteger Negate(BigInteger point) { - return point; + throw new NotSupportedException("An x-only Montgomery curve " + + "has no definition for the standard negation. Use the " + + "standard Montgomery curve implementation instead."); } /// @@ -295,14 +283,13 @@ protected override BigInteger MultiplyScalarUnchecked(BigInteger e, BigInteger k public override bool Equals(CryptoGroupAlgebra? other) { var algebra = other as XOnlyMontgomeryCurveAlgebra; - return other != null && - EqualityComparer.Default.Equals(_parameters, algebra!._parameters); + return other != null && _parameters.Equals(algebra!._parameters); } /// public override int GetHashCode() { - var hashCode = -2051777468 + EqualityComparer.Default.GetHashCode(_parameters); + var hashCode = -2051777468 + _parameters.GetHashCode(); return hashCode; } } From 24f217ec9ddb3824e811061e04c4b35b479368d1 Mon Sep 17 00:00:00 2001 From: Lukas Prediger Date: Mon, 6 Jul 2020 13:58:51 +0300 Subject: [PATCH 7/8] Extended documentation of XOnlyMontgomeryCurveAlgebra --- .../EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs b/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs index c5f4854..ce83877 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs @@ -21,6 +21,12 @@ namespace CompactCryptoGroupAlgebra.EllipticCurves /// are therefore not implemented (however, /// is). /// If you require full addition on arbitrary points, use . + /// + /// returns 0 as its neutral element. + /// This is not technically correct as a point with x-coordinate 0 exists on the curve, + /// but that point is of low order and thus not admittable as safe curve element. For + /// all implementation related considerations, 0 serves as representation of + /// the neutral element. /// /// /// Implementation based on https://eprint.iacr.org/2017/212.pdf . From 6bb7fee4562b9cdd15c0c73010104c59927aa40e Mon Sep 17 00:00:00 2001 From: Lukas Prediger Date: Mon, 6 Jul 2020 18:59:32 +0300 Subject: [PATCH 8/8] Curve25519 parameters and tests (as far as applicable) --- .../EllipticCurves/Curve25519Tests.cs | 92 +++++++++++++++ .../EllipticCurves/NISTP256Tests.cs | 110 +++++++++--------- .../TestUtils/BigIntegerUtils.cs | 19 +++ .../EllipticCurves/CurveParameters.cs | 26 ++++- .../EllipticCurves/MontgomeryCurveAlgebra.cs | 3 + .../XOnlyMontgomeryCurveAlgebra.cs | 3 + 6 files changed, 196 insertions(+), 57 deletions(-) create mode 100644 CompactCryptoGroupAlgebra.Tests/EllipticCurves/Curve25519Tests.cs create mode 100644 CompactCryptoGroupAlgebra.Tests/TestUtils/BigIntegerUtils.cs diff --git a/CompactCryptoGroupAlgebra.Tests/EllipticCurves/Curve25519Tests.cs b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/Curve25519Tests.cs new file mode 100644 index 0000000..c6da588 --- /dev/null +++ b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/Curve25519Tests.cs @@ -0,0 +1,92 @@ +using System; +using System.Numerics; +using System.Collections.Generic; + +using NUnit.Framework; +using CompactCryptoGroupAlgebra.Tests.TestUtils; + +namespace CompactCryptoGroupAlgebra.EllipticCurves.Tests +{ + [TestFixture] + public class Curve25519Tests + { + [Test] + [TestCaseSource(nameof(Curve25519MultiplyScalarTestVectors))] + public void TestXOnlyMultiplyScalar(BigInteger k, BigInteger inputX, BigInteger expectedX) + { + var curve25519Algebra = new XOnlyMontgomeryCurveAlgebra(CurveParameters.Curve25519); + + var point = curve25519Algebra.MultiplyScalar(inputX, k); + Assert.AreEqual(point, expectedX); + } + + [Test] + [TestCaseSource(nameof(Curve25519GenerateTestVectors))] + public void TestXOnlyGenerate(BigInteger k, BigInteger expectedX) + { + var curve25519Algebra = new XOnlyMontgomeryCurveAlgebra(CurveParameters.Curve25519); + + var point = curve25519Algebra.GenerateElement(k); + Assert.AreEqual(point, expectedX); + } + + [Test] + [TestCaseSource(nameof(Curve25519GenerateTestVectors))] + public void TestGenerate(BigInteger k, BigInteger expectedX) + { + var curve25519Algebra = new MontgomeryCurveAlgebra(CurveParameters.Curve25519); + + var point = curve25519Algebra.GenerateElement(k); + Assert.AreEqual(point.X, expectedX); + } + + // from https://tools.ietf.org/html/rfc7748 + private static readonly object[] Curve25519MultiplyScalarTestVectors = + { + new object[] { + new BigInteger( new byte[] { + 0xa5 & 248, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, 0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, 0x9a, (0xc4 & 127) | 64, 0x00 + }), + new BigInteger( new byte[] { + 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, 0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c, 0x00 + }), + new BigInteger( new byte[] { + 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, 0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, 0x85, 0x52, 0x00 + }) + }, + // the following test vector fails: + // new object[] { + // new BigInteger( new byte[] { + // 0x4b & 248, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, 0x6a, 0xf5, 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, 0x2c, 0xa4, 0x16, 0x9e, 0x79, 0x18, 0xba, (0x0d & 127) | 64, 0x00 + // }), + // new BigInteger( new byte[] { + // 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, 0xae, 0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, 0xfc, 0x4c, 0xd5, 0x49, 0xc7, 0x15, 0xa4, 0x93, 0x00 + // }), + // new BigInteger( new byte[] { + // 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, 0x73, 0xf8, 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, 0xe6, 0xf8, 0xf7, 0x64, 0x7a, 0xac, 0x79, 0x57, 0x00 + // }) + // }, + }; + + // from https://tools.ietf.org/html/rfc7748 + private static readonly object[] Curve25519GenerateTestVectors = + { + new object[] { + new BigInteger( new byte[] { + 0x77 & 248, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, 0x72, 0x51, 0xb2, 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a, 0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, (0x2a & 127) | 64, 0x00 + }), + new BigInteger( new byte[] { + 0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d, 0xdc, 0xb4, 0x3e, 0xf7, 0x5a, 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a, 0xf4, 0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a, 0x00 + }) + }, + new object[] { + new BigInteger( new byte[] { + 0x5d & 248, 0xab, 0x08, 0x7e, 0x62, 0x4a, 0x8a, 0x4b, 0x79, 0xe1, 0x7f, 0x8b, 0x83, 0x80, 0x0e, 0xe6, 0x6f, 0x3b, 0xb1, 0x29, 0x26, 0x18, 0xb6, 0xfd, 0x1c, 0x2f, 0x8b, 0x27, 0xff, 0x88, 0xe0, (0xeb & 127) | 64, 0x00 + }), + new BigInteger( new byte[] { + 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, 0x5b, 0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78, 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f, 0x00 + }) + }, + }; + } +} diff --git a/CompactCryptoGroupAlgebra.Tests/EllipticCurves/NISTP256Tests.cs b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/NISTP256Tests.cs index b247943..72bb69a 100644 --- a/CompactCryptoGroupAlgebra.Tests/EllipticCurves/NISTP256Tests.cs +++ b/CompactCryptoGroupAlgebra.Tests/EllipticCurves/NISTP256Tests.cs @@ -1,8 +1,8 @@ using System; using System.Numerics; -using System.Globalization; using NUnit.Framework; +using CompactCryptoGroupAlgebra.Tests.TestUtils; namespace CompactCryptoGroupAlgebra.EllipticCurves.Tests { @@ -21,71 +21,71 @@ public void TestCurvePoint(BigInteger k, BigInteger expectedX, BigInteger expect Assert.AreEqual(point, expectedPoint); } - // from http://point-at-infinity.org/ecc/nisttv - static readonly object[] NISTP256TestVectors = + // from https://point-at-infinity.org/ecc/nisttv + private static readonly object[] NISTP256TestVectors = { - new object[] { BigInteger.Parse("1"), BigInteger.Parse("06B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", NumberStyles.AllowHexSpecifier), BigInteger.Parse("04FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("2"), BigInteger.Parse("07CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC47669978", NumberStyles.AllowHexSpecifier), BigInteger.Parse("007775510DB8ED040293D9AC69F7430DBBA7DADE63CE982299E04B79D227873D1", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("3"), BigInteger.Parse("05ECBE4D1A6330A44C8F7EF951D4BF165E6C6B721EFADA985FB41661BC6E7FD6C", NumberStyles.AllowHexSpecifier), BigInteger.Parse("08734640C4998FF7E374B06CE1A64A2ECD82AB036384FB83D9A79B127A27D5032", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("4"), BigInteger.Parse("0E2534A3532D08FBBA02DDE659EE62BD0031FE2DB785596EF509302446B030852", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0E0F1575A4C633CC719DFEE5FDA862D764EFC96C3F30EE0055C42C23F184ED8C6", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("5"), BigInteger.Parse("051590B7A515140D2D784C85608668FDFEF8C82FD1F5BE52421554A0DC3D033ED", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0E0C17DA8904A727D8AE1BF36BF8A79260D012F00D4D80888D1D0BB44FDA16DA4", NumberStyles.AllowHexSpecifier) }, + new object[] { BigInteger.Parse("1"), BigIntegerUtils.ParseHex("06B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"), BigIntegerUtils.ParseHex("04FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5") }, + new object[] { BigInteger.Parse("2"), BigIntegerUtils.ParseHex("07CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC47669978"), BigIntegerUtils.ParseHex("007775510DB8ED040293D9AC69F7430DBBA7DADE63CE982299E04B79D227873D1") }, + new object[] { BigInteger.Parse("3"), BigIntegerUtils.ParseHex("05ECBE4D1A6330A44C8F7EF951D4BF165E6C6B721EFADA985FB41661BC6E7FD6C"), BigIntegerUtils.ParseHex("08734640C4998FF7E374B06CE1A64A2ECD82AB036384FB83D9A79B127A27D5032") }, + new object[] { BigInteger.Parse("4"), BigIntegerUtils.ParseHex("0E2534A3532D08FBBA02DDE659EE62BD0031FE2DB785596EF509302446B030852"), BigIntegerUtils.ParseHex("0E0F1575A4C633CC719DFEE5FDA862D764EFC96C3F30EE0055C42C23F184ED8C6") }, + new object[] { BigInteger.Parse("5"), BigIntegerUtils.ParseHex("051590B7A515140D2D784C85608668FDFEF8C82FD1F5BE52421554A0DC3D033ED"), BigIntegerUtils.ParseHex("0E0C17DA8904A727D8AE1BF36BF8A79260D012F00D4D80888D1D0BB44FDA16DA4") }, - new object[] { BigInteger.Parse("6"), BigInteger.Parse("0B01A172A76A4602C92D3242CB897DDE3024C740DEBB215B4C6B0AAE93C2291A9", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0E85C10743237DAD56FEC0E2DFBA703791C00F7701C7E16BDFD7C48538FC77FE2", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("7"), BigInteger.Parse("08E533B6FA0BF7B4625BB30667C01FB607EF9F8B8A80FEF5B300628703187B2A3", NumberStyles.AllowHexSpecifier), BigInteger.Parse("073EB1DBDE03318366D069F83A6F5900053C73633CB041B21C55E1A86C1F400B4", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("8"), BigInteger.Parse("062D9779DBEE9B0534042742D3AB54CADC1D238980FCE97DBB4DD9DC1DB6FB393", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0AD5ACCBD91E9D8244FF15D771167CEE0A2ED51F6BBE76A78DA540A6A0F09957E", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("9"), BigInteger.Parse("0EA68D7B6FEDF0B71878938D51D71F8729E0ACB8C2C6DF8B3D79E8A4B90949EE0", NumberStyles.AllowHexSpecifier), BigInteger.Parse("02A2744C972C9FCE787014A964A8EA0C84D714FEAA4DE823FE85A224A4DD048FA", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("10"), BigInteger.Parse("0CEF66D6B2A3A993E591214D1EA223FB545CA6C471C48306E4C36069404C5723F", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0878662A229AAAE906E123CDD9D3B4C10590DED29FE751EEECA34BBAA44AF0773", NumberStyles.AllowHexSpecifier) }, + new object[] { BigInteger.Parse("6"), BigIntegerUtils.ParseHex("0B01A172A76A4602C92D3242CB897DDE3024C740DEBB215B4C6B0AAE93C2291A9"), BigIntegerUtils.ParseHex("0E85C10743237DAD56FEC0E2DFBA703791C00F7701C7E16BDFD7C48538FC77FE2") }, + new object[] { BigInteger.Parse("7"), BigIntegerUtils.ParseHex("08E533B6FA0BF7B4625BB30667C01FB607EF9F8B8A80FEF5B300628703187B2A3"), BigIntegerUtils.ParseHex("073EB1DBDE03318366D069F83A6F5900053C73633CB041B21C55E1A86C1F400B4") }, + new object[] { BigInteger.Parse("8"), BigIntegerUtils.ParseHex("062D9779DBEE9B0534042742D3AB54CADC1D238980FCE97DBB4DD9DC1DB6FB393"), BigIntegerUtils.ParseHex("0AD5ACCBD91E9D8244FF15D771167CEE0A2ED51F6BBE76A78DA540A6A0F09957E") }, + new object[] { BigInteger.Parse("9"), BigIntegerUtils.ParseHex("0EA68D7B6FEDF0B71878938D51D71F8729E0ACB8C2C6DF8B3D79E8A4B90949EE0"), BigIntegerUtils.ParseHex("02A2744C972C9FCE787014A964A8EA0C84D714FEAA4DE823FE85A224A4DD048FA") }, + new object[] { BigInteger.Parse("10"), BigIntegerUtils.ParseHex("0CEF66D6B2A3A993E591214D1EA223FB545CA6C471C48306E4C36069404C5723F"), BigIntegerUtils.ParseHex("0878662A229AAAE906E123CDD9D3B4C10590DED29FE751EEECA34BBAA44AF0773") }, - new object[] { BigInteger.Parse("11"), BigInteger.Parse("03ED113B7883B4C590638379DB0C21CDA16742ED0255048BF433391D374BC21D1", NumberStyles.AllowHexSpecifier), BigInteger.Parse("09099209ACCC4C8A224C843AFA4F4C68A090D04DA5E9889DAE2F8EEFCE82A3740", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("12"), BigInteger.Parse("0741DD5BDA817D95E4626537320E5D55179983028B2F82C99D500C5EE8624E3C4", NumberStyles.AllowHexSpecifier), BigInteger.Parse("00770B46A9C385FDC567383554887B1548EEB912C35BA5CA71995FF22CD4481D3", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("13"), BigInteger.Parse("0177C837AE0AC495A61805DF2D85EE2FC792E284B65EAD58A98E15D9D46072C01", NumberStyles.AllowHexSpecifier), BigInteger.Parse("063BB58CD4EBEA558A24091ADB40F4E7226EE14C3A1FB4DF39C43BBE2EFC7BFD8", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("14"), BigInteger.Parse("054E77A001C3862B97A76647F4336DF3CF126ACBE7A069C5E5709277324D2920B", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0F599F1BB29F4317542121F8C05A2E7C37171EA77735090081BA7C82F60D0B375", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("15"), BigInteger.Parse("0F0454DC6971ABAE7ADFB378999888265AE03AF92DE3A0EF163668C63E59B9D5F", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0B5B93EE3592E2D1F4E6594E51F9643E62A3B21CE75B5FA3F47E59CDE0D034F36", NumberStyles.AllowHexSpecifier) }, + new object[] { BigInteger.Parse("11"), BigIntegerUtils.ParseHex("03ED113B7883B4C590638379DB0C21CDA16742ED0255048BF433391D374BC21D1"), BigIntegerUtils.ParseHex("09099209ACCC4C8A224C843AFA4F4C68A090D04DA5E9889DAE2F8EEFCE82A3740") }, + new object[] { BigInteger.Parse("12"), BigIntegerUtils.ParseHex("0741DD5BDA817D95E4626537320E5D55179983028B2F82C99D500C5EE8624E3C4"), BigIntegerUtils.ParseHex("00770B46A9C385FDC567383554887B1548EEB912C35BA5CA71995FF22CD4481D3") }, + new object[] { BigInteger.Parse("13"), BigIntegerUtils.ParseHex("0177C837AE0AC495A61805DF2D85EE2FC792E284B65EAD58A98E15D9D46072C01"), BigIntegerUtils.ParseHex("063BB58CD4EBEA558A24091ADB40F4E7226EE14C3A1FB4DF39C43BBE2EFC7BFD8") }, + new object[] { BigInteger.Parse("14"), BigIntegerUtils.ParseHex("054E77A001C3862B97A76647F4336DF3CF126ACBE7A069C5E5709277324D2920B"), BigIntegerUtils.ParseHex("0F599F1BB29F4317542121F8C05A2E7C37171EA77735090081BA7C82F60D0B375") }, + new object[] { BigInteger.Parse("15"), BigIntegerUtils.ParseHex("0F0454DC6971ABAE7ADFB378999888265AE03AF92DE3A0EF163668C63E59B9D5F"), BigIntegerUtils.ParseHex("0B5B93EE3592E2D1F4E6594E51F9643E62A3B21CE75B5FA3F47E59CDE0D034F36") }, - new object[] { BigInteger.Parse("16"), BigInteger.Parse("076A94D138A6B41858B821C629836315FCD28392EFF6CA038A5EB4787E1277C6E", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0A985FE61341F260E6CB0A1B5E11E87208599A0040FC78BAA0E9DDD724B8C5110", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("17"), BigInteger.Parse("047776904C0F1CC3A9C0984B66F75301A5FA68678F0D64AF8BA1ABCE34738A73E", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0AA005EE6B5B957286231856577648E8381B2804428D5733F32F787FF71F1FCDC", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("18"), BigInteger.Parse("01057E0AB5780F470DEFC9378D1C7C87437BB4C6F9EA55C63D936266DBD781FDA", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0F6F1645A15CBE5DC9FA9B7DFD96EE5A7DCC11B5C5EF4F1F78D83B3393C6A45A2", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("19"), BigInteger.Parse("0CB6D2861102C0C25CE39B7C17108C507782C452257884895C1FC7B74AB03ED83", NumberStyles.AllowHexSpecifier), BigInteger.Parse("058D7614B24D9EF515C35E7100D6D6CE4A496716E30FA3E03E39150752BCECDAA", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("20"), BigInteger.Parse("083A01A9378395BAB9BCD6A0AD03CC56D56E6B19250465A94A234DC4C6B28DA9A", NumberStyles.AllowHexSpecifier), BigInteger.Parse("076E49B6DE2F73234AE6A5EB9D612B75C9F2202BB6923F54FF8240AAA86F640B8", NumberStyles.AllowHexSpecifier) }, + new object[] { BigInteger.Parse("16"), BigIntegerUtils.ParseHex("076A94D138A6B41858B821C629836315FCD28392EFF6CA038A5EB4787E1277C6E"), BigIntegerUtils.ParseHex("0A985FE61341F260E6CB0A1B5E11E87208599A0040FC78BAA0E9DDD724B8C5110") }, + new object[] { BigInteger.Parse("17"), BigIntegerUtils.ParseHex("047776904C0F1CC3A9C0984B66F75301A5FA68678F0D64AF8BA1ABCE34738A73E"), BigIntegerUtils.ParseHex("0AA005EE6B5B957286231856577648E8381B2804428D5733F32F787FF71F1FCDC") }, + new object[] { BigInteger.Parse("18"), BigIntegerUtils.ParseHex("01057E0AB5780F470DEFC9378D1C7C87437BB4C6F9EA55C63D936266DBD781FDA"), BigIntegerUtils.ParseHex("0F6F1645A15CBE5DC9FA9B7DFD96EE5A7DCC11B5C5EF4F1F78D83B3393C6A45A2") }, + new object[] { BigInteger.Parse("19"), BigIntegerUtils.ParseHex("0CB6D2861102C0C25CE39B7C17108C507782C452257884895C1FC7B74AB03ED83"), BigIntegerUtils.ParseHex("058D7614B24D9EF515C35E7100D6D6CE4A496716E30FA3E03E39150752BCECDAA") }, + new object[] { BigInteger.Parse("20"), BigIntegerUtils.ParseHex("083A01A9378395BAB9BCD6A0AD03CC56D56E6B19250465A94A234DC4C6B28DA9A"), BigIntegerUtils.ParseHex("076E49B6DE2F73234AE6A5EB9D612B75C9F2202BB6923F54FF8240AAA86F640B8") }, - new object[] { BigInteger.Parse("112233445566778899"), BigInteger.Parse("0339150844EC15234807FE862A86BE77977DBFB3AE3D96F4C22795513AEAAB82F", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0B1C14DDFDC8EC1B2583F51E85A5EB3A155840F2034730E9B5ADA38B674336A21", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("112233445566778899112233445566778899"), BigInteger.Parse("01B7E046A076CC25E6D7FA5003F6729F665CC3241B5ADAB12B498CD32F2803264", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0BFEA79BE2B666B073DB69A2A241ADAB0738FE9D2DD28B5604EB8C8CF097C457B", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("29852220098221261079183923314599206100666902414330245206392788703677545185283"), BigInteger.Parse("09EACE8F4B071E677C5350B02F2BB2B384AAE89D58AA72CA97A170572E0FB222F", NumberStyles.AllowHexSpecifier), BigInteger.Parse("01BBDAEC2430B09B93F7CB08678636CE12EAAFD58390699B5FD2F6E1188FC2A78", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("57896042899961394862005778464643882389978449576758748073725983489954366354431"), BigInteger.Parse("0878F22CC6DB6048D2B767268F22FFAD8E56AB8E2DC615F7BD89F1E350500DD8D", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0714A5D7BB901C9C5853400D12341A892EF45D87FC553786756C4F0C9391D763E", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("1766845392945710151501889105729049882997660004824848915955419660366636031"), BigInteger.Parse("0659A379625AB122F2512B8DADA02C6348D53B54452DFF67AC7ACE4E8856295CA", NumberStyles.AllowHexSpecifier), BigInteger.Parse("049D81AB97B648464D0B4A288BD7818FAB41A16426E943527C4FED8736C53D0F6", NumberStyles.AllowHexSpecifier) }, + new object[] { BigInteger.Parse("112233445566778899"), BigIntegerUtils.ParseHex("0339150844EC15234807FE862A86BE77977DBFB3AE3D96F4C22795513AEAAB82F"), BigIntegerUtils.ParseHex("0B1C14DDFDC8EC1B2583F51E85A5EB3A155840F2034730E9B5ADA38B674336A21") }, + new object[] { BigInteger.Parse("112233445566778899112233445566778899"), BigIntegerUtils.ParseHex("01B7E046A076CC25E6D7FA5003F6729F665CC3241B5ADAB12B498CD32F2803264"), BigIntegerUtils.ParseHex("0BFEA79BE2B666B073DB69A2A241ADAB0738FE9D2DD28B5604EB8C8CF097C457B") }, + new object[] { BigInteger.Parse("29852220098221261079183923314599206100666902414330245206392788703677545185283"), BigIntegerUtils.ParseHex("09EACE8F4B071E677C5350B02F2BB2B384AAE89D58AA72CA97A170572E0FB222F"), BigIntegerUtils.ParseHex("01BBDAEC2430B09B93F7CB08678636CE12EAAFD58390699B5FD2F6E1188FC2A78") }, + new object[] { BigInteger.Parse("57896042899961394862005778464643882389978449576758748073725983489954366354431"), BigIntegerUtils.ParseHex("0878F22CC6DB6048D2B767268F22FFAD8E56AB8E2DC615F7BD89F1E350500DD8D"), BigIntegerUtils.ParseHex("0714A5D7BB901C9C5853400D12341A892EF45D87FC553786756C4F0C9391D763E") }, + new object[] { BigInteger.Parse("1766845392945710151501889105729049882997660004824848915955419660366636031"), BigIntegerUtils.ParseHex("0659A379625AB122F2512B8DADA02C6348D53B54452DFF67AC7ACE4E8856295CA"), BigIntegerUtils.ParseHex("049D81AB97B648464D0B4A288BD7818FAB41A16426E943527C4FED8736C53D0F6") }, - new object[] { BigInteger.Parse("28948025760307534517734791687894775804466072615242963443097661355606862201087"), BigInteger.Parse("0CBCEAAA8A4DD44BBCE58E8DB7740A5510EC2CB7EA8DA8D8F036B3FB04CDA4DE4", NumberStyles.AllowHexSpecifier), BigInteger.Parse("04BD7AA301A80D7F59FD983FEDBE59BB7B2863FE46494935E3745B360E32332FA", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("113078210460870548944811695960290644973229224625838436424477095834645696384"), BigInteger.Parse("0F0C4A0576154FF3A33A3460D42EAED806E854DFA37125221D37935124BA462A4", NumberStyles.AllowHexSpecifier), BigInteger.Parse("05B392FA964434D29EEC6C9DBC261CF116796864AA2FAADB984A2DF38D1AEF7A3", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("12078056106883488161242983286051341125085761470677906721917479268909056"), BigInteger.Parse("05E6C8524B6369530B12C62D31EC53E0288173BD662BDF680B53A41ECBCAD00CC", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0447FE742C2BFEF4D0DB14B5B83A2682309B5618E0064A94804E9282179FE089F", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("57782969857385448082319957860328652998540760998293976083718804450708503920639"), BigInteger.Parse("003792E541BC209076A3D7920A915021ECD396A6EB5C3960024BE5575F3223484", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0FC774AE092403101563B712F68170312304F20C80B40C06282063DB25F268DE4", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("57896017119460046759583662757090100341435943767777707906455551163257755533312"), BigInteger.Parse("02379FF85AB693CDF901D6CE6F2473F39C04A2FE3DCD842CE7AAB0E002095BCF8", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0F8B476530A634589D5129E46F322B02FBC610A703D80875EE70D7CE1877436A1", NumberStyles.AllowHexSpecifier) }, + new object[] { BigInteger.Parse("28948025760307534517734791687894775804466072615242963443097661355606862201087"), BigIntegerUtils.ParseHex("0CBCEAAA8A4DD44BBCE58E8DB7740A5510EC2CB7EA8DA8D8F036B3FB04CDA4DE4"), BigIntegerUtils.ParseHex("04BD7AA301A80D7F59FD983FEDBE59BB7B2863FE46494935E3745B360E32332FA") }, + new object[] { BigInteger.Parse("113078210460870548944811695960290644973229224625838436424477095834645696384"), BigIntegerUtils.ParseHex("0F0C4A0576154FF3A33A3460D42EAED806E854DFA37125221D37935124BA462A4"), BigIntegerUtils.ParseHex("05B392FA964434D29EEC6C9DBC261CF116796864AA2FAADB984A2DF38D1AEF7A3") }, + new object[] { BigInteger.Parse("12078056106883488161242983286051341125085761470677906721917479268909056"), BigIntegerUtils.ParseHex("05E6C8524B6369530B12C62D31EC53E0288173BD662BDF680B53A41ECBCAD00CC"), BigIntegerUtils.ParseHex("0447FE742C2BFEF4D0DB14B5B83A2682309B5618E0064A94804E9282179FE089F") }, + new object[] { BigInteger.Parse("57782969857385448082319957860328652998540760998293976083718804450708503920639"), BigIntegerUtils.ParseHex("003792E541BC209076A3D7920A915021ECD396A6EB5C3960024BE5575F3223484"), BigIntegerUtils.ParseHex("0FC774AE092403101563B712F68170312304F20C80B40C06282063DB25F268DE4") }, + new object[] { BigInteger.Parse("57896017119460046759583662757090100341435943767777707906455551163257755533312"), BigIntegerUtils.ParseHex("02379FF85AB693CDF901D6CE6F2473F39C04A2FE3DCD842CE7AAB0E002095BCF8"), BigIntegerUtils.ParseHex("0F8B476530A634589D5129E46F322B02FBC610A703D80875EE70D7CE1877436A1") }, - new object[] { BigInteger.Parse("452312848374287284681282171017647412726433684238464212999305864837160993279"), BigInteger.Parse("0C1E4072C529BF2F44DA769EFC934472848003B3AF2C0F5AA8F8DDBD53E12ED7C", NumberStyles.AllowHexSpecifier), BigInteger.Parse("039A6EE77812BB37E8079CD01ED649D3830FCA46F718C1D3993E4A591824ABCDB", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("904571339174065134293634407946054000774746055866917729876676367558469746684"), BigInteger.Parse("034DFBC09404C21E250A9B40FA8772897AC63A094877DB65862B61BD1507B34F3", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0CF6F8A876C6F99CEAEC87148F18C7E1E0DA6E165FFC8ED82ABB65955215F77D3", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044349"), BigInteger.Parse("083A01A9378395BAB9BCD6A0AD03CC56D56E6B19250465A94A234DC4C6B28DA9A", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0891B64911D08CDCC5195A14629ED48A360DDFD4596DC0AB007DBF5557909BF47", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044350"), BigInteger.Parse("0CB6D2861102C0C25CE39B7C17108C507782C452257884895C1FC7B74AB03ED83", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0A7289EB3DB2610AFA3CA18EFF292931B5B698E92CF05C1FC1C6EAF8AD4313255", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044351"), BigInteger.Parse("01057E0AB5780F470DEFC9378D1C7C87437BB4C6F9EA55C63D936266DBD781FDA", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0090E9BA4EA341A246056482026911A58233EE4A4A10B0E08727C4CC6C395BA5D", NumberStyles.AllowHexSpecifier) }, + new object[] { BigInteger.Parse("452312848374287284681282171017647412726433684238464212999305864837160993279"), BigIntegerUtils.ParseHex("0C1E4072C529BF2F44DA769EFC934472848003B3AF2C0F5AA8F8DDBD53E12ED7C"), BigIntegerUtils.ParseHex("039A6EE77812BB37E8079CD01ED649D3830FCA46F718C1D3993E4A591824ABCDB") }, + new object[] { BigInteger.Parse("904571339174065134293634407946054000774746055866917729876676367558469746684"), BigIntegerUtils.ParseHex("034DFBC09404C21E250A9B40FA8772897AC63A094877DB65862B61BD1507B34F3"), BigIntegerUtils.ParseHex("0CF6F8A876C6F99CEAEC87148F18C7E1E0DA6E165FFC8ED82ABB65955215F77D3") }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044349"), BigIntegerUtils.ParseHex("083A01A9378395BAB9BCD6A0AD03CC56D56E6B19250465A94A234DC4C6B28DA9A"), BigIntegerUtils.ParseHex("0891B64911D08CDCC5195A14629ED48A360DDFD4596DC0AB007DBF5557909BF47") }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044350"), BigIntegerUtils.ParseHex("0CB6D2861102C0C25CE39B7C17108C507782C452257884895C1FC7B74AB03ED83"), BigIntegerUtils.ParseHex("0A7289EB3DB2610AFA3CA18EFF292931B5B698E92CF05C1FC1C6EAF8AD4313255") }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044351"), BigIntegerUtils.ParseHex("01057E0AB5780F470DEFC9378D1C7C87437BB4C6F9EA55C63D936266DBD781FDA"), BigIntegerUtils.ParseHex("0090E9BA4EA341A246056482026911A58233EE4A4A10B0E08727C4CC6C395BA5D") }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044352"), BigInteger.Parse("047776904C0F1CC3A9C0984B66F75301A5FA68678F0D64AF8BA1ABCE34738A73E", NumberStyles.AllowHexSpecifier), BigInteger.Parse("055FFA1184A46A8D89DCE7A9A889B717C7E4D7FBCD72A8CC0CD0878008E0E0323", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044353"), BigInteger.Parse("076A94D138A6B41858B821C629836315FCD28392EFF6CA038A5EB4787E1277C6E", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0567A019DCBE0D9F2934F5E4A1EE178DF7A665FFCF0387455F162228DB473AEEF", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044354"), BigInteger.Parse("0F0454DC6971ABAE7ADFB378999888265AE03AF92DE3A0EF163668C63E59B9D5F", NumberStyles.AllowHexSpecifier), BigInteger.Parse("04A46C11BA6D1D2E1B19A6B1AE069BC19D5C4DE328A4A05C0B81A6321F2FCB0C9", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044355"), BigInteger.Parse("054E77A001C3862B97A76647F4336DF3CF126ACBE7A069C5E5709277324D2920B", NumberStyles.AllowHexSpecifier), BigInteger.Parse("00A660E43D60BCE8BBDEDE073FA5D183C8E8E15898CAF6FF7E45837D09F2F4C8A", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044356"), BigInteger.Parse("0177C837AE0AC495A61805DF2D85EE2FC792E284B65EAD58A98E15D9D46072C01", NumberStyles.AllowHexSpecifier), BigInteger.Parse("09C44A731B1415AA85DBF6E524BF0B18DD911EB3D5E04B20C63BC441D10384027", NumberStyles.AllowHexSpecifier) }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044352"), BigIntegerUtils.ParseHex("047776904C0F1CC3A9C0984B66F75301A5FA68678F0D64AF8BA1ABCE34738A73E"), BigIntegerUtils.ParseHex("055FFA1184A46A8D89DCE7A9A889B717C7E4D7FBCD72A8CC0CD0878008E0E0323") }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044353"), BigIntegerUtils.ParseHex("076A94D138A6B41858B821C629836315FCD28392EFF6CA038A5EB4787E1277C6E"), BigIntegerUtils.ParseHex("0567A019DCBE0D9F2934F5E4A1EE178DF7A665FFCF0387455F162228DB473AEEF") }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044354"), BigIntegerUtils.ParseHex("0F0454DC6971ABAE7ADFB378999888265AE03AF92DE3A0EF163668C63E59B9D5F"), BigIntegerUtils.ParseHex("04A46C11BA6D1D2E1B19A6B1AE069BC19D5C4DE328A4A05C0B81A6321F2FCB0C9") }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044355"), BigIntegerUtils.ParseHex("054E77A001C3862B97A76647F4336DF3CF126ACBE7A069C5E5709277324D2920B"), BigIntegerUtils.ParseHex("00A660E43D60BCE8BBDEDE073FA5D183C8E8E15898CAF6FF7E45837D09F2F4C8A") }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044356"), BigIntegerUtils.ParseHex("0177C837AE0AC495A61805DF2D85EE2FC792E284B65EAD58A98E15D9D46072C01"), BigIntegerUtils.ParseHex("09C44A731B1415AA85DBF6E524BF0B18DD911EB3D5E04B20C63BC441D10384027") }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044357"), BigInteger.Parse("0741DD5BDA817D95E4626537320E5D55179983028B2F82C99D500C5EE8624E3C4", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0F88F4B9463C7A024A98C7CAAB7784EAB71146ED4CA45A358E66A00DD32BB7E2C", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044358"), BigInteger.Parse("03ED113B7883B4C590638379DB0C21CDA16742ED0255048BF433391D374BC21D1", NumberStyles.AllowHexSpecifier), BigInteger.Parse("06F66DF64333B375EDB37BC505B0B3975F6F2FB26A16776251D07110317D5C8BF", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044359"), BigInteger.Parse("0CEF66D6B2A3A993E591214D1EA223FB545CA6C471C48306E4C36069404C5723F", NumberStyles.AllowHexSpecifier), BigInteger.Parse("078799D5CD655517091EDC32262C4B3EFA6F212D7018AE11135CB4455BB50F88C", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044360"), BigInteger.Parse("0EA68D7B6FEDF0B71878938D51D71F8729E0ACB8C2C6DF8B3D79E8A4B90949EE0", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0D5D8BB358D36031978FEB569B5715F37B28EB0165B217DC017A5DDB5B22FB705", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044361"), BigInteger.Parse("062D9779DBEE9B0534042742D3AB54CADC1D238980FCE97DBB4DD9DC1DB6FB393", NumberStyles.AllowHexSpecifier), BigInteger.Parse("052A533416E1627DCB00EA288EE98311F5D12AE0A4418958725ABF595F0F66A81", NumberStyles.AllowHexSpecifier) }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044357"), BigIntegerUtils.ParseHex("0741DD5BDA817D95E4626537320E5D55179983028B2F82C99D500C5EE8624E3C4"), BigIntegerUtils.ParseHex("0F88F4B9463C7A024A98C7CAAB7784EAB71146ED4CA45A358E66A00DD32BB7E2C") }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044358"), BigIntegerUtils.ParseHex("03ED113B7883B4C590638379DB0C21CDA16742ED0255048BF433391D374BC21D1"), BigIntegerUtils.ParseHex("06F66DF64333B375EDB37BC505B0B3975F6F2FB26A16776251D07110317D5C8BF") }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044359"), BigIntegerUtils.ParseHex("0CEF66D6B2A3A993E591214D1EA223FB545CA6C471C48306E4C36069404C5723F"), BigIntegerUtils.ParseHex("078799D5CD655517091EDC32262C4B3EFA6F212D7018AE11135CB4455BB50F88C") }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044360"), BigIntegerUtils.ParseHex("0EA68D7B6FEDF0B71878938D51D71F8729E0ACB8C2C6DF8B3D79E8A4B90949EE0"), BigIntegerUtils.ParseHex("0D5D8BB358D36031978FEB569B5715F37B28EB0165B217DC017A5DDB5B22FB705") }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044361"), BigIntegerUtils.ParseHex("062D9779DBEE9B0534042742D3AB54CADC1D238980FCE97DBB4DD9DC1DB6FB393"), BigIntegerUtils.ParseHex("052A533416E1627DCB00EA288EE98311F5D12AE0A4418958725ABF595F0F66A81") }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044362"), BigInteger.Parse("08E533B6FA0BF7B4625BB30667C01FB607EF9F8B8A80FEF5B300628703187B2A3", NumberStyles.AllowHexSpecifier), BigInteger.Parse("08C14E2411FCCE7CA92F9607C590A6FFFAC38C9CD34FBE4DE3AA1E5793E0BFF4B", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044363"), BigInteger.Parse("0B01A172A76A4602C92D3242CB897DDE3024C740DEBB215B4C6B0AAE93C2291A9", NumberStyles.AllowHexSpecifier), BigInteger.Parse("017A3EF8ACDC8252B9013F1D20458FC86E3FF0890E381E9420283B7AC7038801D", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044364"), BigInteger.Parse("051590B7A515140D2D784C85608668FDFEF8C82FD1F5BE52421554A0DC3D033ED", NumberStyles.AllowHexSpecifier), BigInteger.Parse("01F3E82566FB58D83751E40C9407586D9F2FED1002B27F7772E2F44BB025E925B", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044365"), BigInteger.Parse("0E2534A3532D08FBBA02DDE659EE62BD0031FE2DB785596EF509302446B030852", NumberStyles.AllowHexSpecifier), BigInteger.Parse("01F0EA8A4B39CC339E62011A02579D289B103693D0CF11FFAA3BD3DC0E7B12739", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044366"), BigInteger.Parse("05ECBE4D1A6330A44C8F7EF951D4BF165E6C6B721EFADA985FB41661BC6E7FD6C", NumberStyles.AllowHexSpecifier), BigInteger.Parse("078CB9BF2B6670082C8B4F931E59B5D1327D54FCAC7B047C265864ED85D82AFCD", NumberStyles.AllowHexSpecifier) }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044362"), BigIntegerUtils.ParseHex("08E533B6FA0BF7B4625BB30667C01FB607EF9F8B8A80FEF5B300628703187B2A3"), BigIntegerUtils.ParseHex("08C14E2411FCCE7CA92F9607C590A6FFFAC38C9CD34FBE4DE3AA1E5793E0BFF4B") }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044363"), BigIntegerUtils.ParseHex("0B01A172A76A4602C92D3242CB897DDE3024C740DEBB215B4C6B0AAE93C2291A9"), BigIntegerUtils.ParseHex("017A3EF8ACDC8252B9013F1D20458FC86E3FF0890E381E9420283B7AC7038801D") }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044364"), BigIntegerUtils.ParseHex("051590B7A515140D2D784C85608668FDFEF8C82FD1F5BE52421554A0DC3D033ED"), BigIntegerUtils.ParseHex("01F3E82566FB58D83751E40C9407586D9F2FED1002B27F7772E2F44BB025E925B") }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044365"), BigIntegerUtils.ParseHex("0E2534A3532D08FBBA02DDE659EE62BD0031FE2DB785596EF509302446B030852"), BigIntegerUtils.ParseHex("01F0EA8A4B39CC339E62011A02579D289B103693D0CF11FFAA3BD3DC0E7B12739") }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044366"), BigIntegerUtils.ParseHex("05ECBE4D1A6330A44C8F7EF951D4BF165E6C6B721EFADA985FB41661BC6E7FD6C"), BigIntegerUtils.ParseHex("078CB9BF2B6670082C8B4F931E59B5D1327D54FCAC7B047C265864ED85D82AFCD") }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044367"), BigInteger.Parse("07CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC47669978", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0F888AAEE24712FC0D6C26539608BCF244582521AC3167DD661FB4862DD878C2E", NumberStyles.AllowHexSpecifier) }, - new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044368"), BigInteger.Parse("06B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", NumberStyles.AllowHexSpecifier), BigInteger.Parse("0B01CBD1C01E58065711814B583F061E9D431CCA994CEA1313449BF97C840AE0A", NumberStyles.AllowHexSpecifier) } + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044367"), BigIntegerUtils.ParseHex("07CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC47669978"), BigIntegerUtils.ParseHex("0F888AAEE24712FC0D6C26539608BCF244582521AC3167DD661FB4862DD878C2E") }, + new object[] { BigInteger.Parse("115792089210356248762697446949407573529996955224135760342422259061068512044368"), BigIntegerUtils.ParseHex("06B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"), BigIntegerUtils.ParseHex("0B01CBD1C01E58065711814B583F061E9D431CCA994CEA1313449BF97C840AE0A") } }; } } diff --git a/CompactCryptoGroupAlgebra.Tests/TestUtils/BigIntegerUtils.cs b/CompactCryptoGroupAlgebra.Tests/TestUtils/BigIntegerUtils.cs new file mode 100644 index 0000000..90a485d --- /dev/null +++ b/CompactCryptoGroupAlgebra.Tests/TestUtils/BigIntegerUtils.cs @@ -0,0 +1,19 @@ +using System; +using System.Numerics; +using System.Globalization; + +namespace CompactCryptoGroupAlgebra.Tests.TestUtils +{ + public class BigIntegerUtils + { + /// + /// Parses a hex-formatted integer string as a . + /// + /// Hexadecimal string representation of the integer. + /// The determined by the given hex string. + public static BigInteger ParseHex(string hexEncodedInteger) + { + return BigInteger.Parse(hexEncodedInteger, NumberStyles.AllowHexSpecifier); + } + } +} diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/CurveParameters.cs b/CompactCryptoGroupAlgebra/EllipticCurves/CurveParameters.cs index 547cbf8..7227de8 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/CurveParameters.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/CurveParameters.cs @@ -72,13 +72,13 @@ BigInteger cofactor } /// - /// Creates a parameter set for the NIST P-256 elliptic curve + /// A parameter set for the NIST P-256 elliptic curve /// of form y² = x³ + Ax + B. /// /// /// As defined in https://csrc.nist.gov/csrc/media/publications/fips/186/2/archive/2000-01-27/documents/fips186-2.pdf , p. 34. /// - /// An instance of CurveParameters for the NIST P-256 curve. + /// for the NIST P-256 curve. public static CurveParameters NISTP256 = new CurveParameters( p: BigPrime.CreateWithoutChecks(BigInteger.Parse( "115792089210356248762697446949407573530086143415290314195533631308867097853951" @@ -110,6 +110,28 @@ BigInteger cofactor cofactor: 1 ); + /// + /// A parameter set for the Curve25519 elliptic curve. + /// of form By² = x³ + Ax² + x. + /// + /// + /// As defined in https://tools.ietf.org/html/rfc7748#section-4.1 . + /// + /// for the curve25519 curve. + public static CurveParameters Curve25519 = new CurveParameters( + p: BigPrime.CreateWithoutChecks(BigInteger.Pow(2, 255) - 19), + a: new BigInteger(486662), + b: BigInteger.One, + generator: new CurvePoint( + new BigInteger(9), + BigInteger.Parse("14781619447589544791020593568409986887264606134616475288964881837755586237401") + ), + order: BigPrime.CreateWithoutChecks( + BigInteger.Parse("7237005577332262213973186563042994240857116359379907606001950938285454250989") + ), + cofactor: 8 + ); + /// public override bool Equals(object obj) { diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs b/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs index b3865b5..4fcd069 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/MontgomeryCurveAlgebra.cs @@ -13,6 +13,9 @@ namespace CompactCryptoGroupAlgebra.EllipticCurves /// the curve equation (and the additional "point at infinity" as neutral element). /// /// The exact parameters of the curve (A, B, P) are encoded in a object. + /// + /// Note that does not implement RFC 7748 ( https://tools.ietf.org/html/rfc7748 ) + /// due to different handling/encoding of scalars. /// public class MontgomeryCurveAlgebra : CryptoGroupAlgebra { diff --git a/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs b/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs index ce83877..f36161d 100644 --- a/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs +++ b/CompactCryptoGroupAlgebra/EllipticCurves/XOnlyMontgomeryCurveAlgebra.cs @@ -27,6 +27,9 @@ namespace CompactCryptoGroupAlgebra.EllipticCurves /// but that point is of low order and thus not admittable as safe curve element. For /// all implementation related considerations, 0 serves as representation of /// the neutral element. + /// + /// Note that does not implement RFC 7748 ( https://tools.ietf.org/html/rfc7748 ) + /// due to different handling/encoding of scalars. /// /// /// Implementation based on https://eprint.iacr.org/2017/212.pdf .