commit c885719c5b9199860edc2930a6c11ff1518675bc Author: Harald Wolff Date: Tue Apr 6 20:42:16 2021 +0200 First Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4e82d27 --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +# Autosave files +*~ + +# build +[Oo]bj/ +[Bb]in/ +packages/ +TestResults/ + +# globs +Makefile.in +*.DS_Store +*.sln.cache +*.suo +*.cache +*.pidb +*.userprefs +*.usertasks +config.log +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.user +*.tar.gz +tarballs/ +test-results/ +Thumbs.db + +# Mac bundle stuff +*.dmg +*.app + +# resharper +*_Resharper.* +*.Resharper + +# dotCover +*.dotCover diff --git a/ln.crypto.ec.sln b/ln.crypto.ec.sln new file mode 100644 index 0000000..59b0649 --- /dev/null +++ b/ln.crypto.ec.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.crypto.ec", "ln.crypto.ec\ln.crypto.ec.csproj", "{4D57CD8B-AA87-47E3-A7CC-01E9C5DF7E28}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.crypto.ec.test", "ln.crypto.ec.test\ln.crypto.ec.test.csproj", "{83A9ED0C-94F6-4E45-AA3C-D39DE427A13C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4D57CD8B-AA87-47E3-A7CC-01E9C5DF7E28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D57CD8B-AA87-47E3-A7CC-01E9C5DF7E28}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D57CD8B-AA87-47E3-A7CC-01E9C5DF7E28}.Debug|x64.ActiveCfg = Debug|Any CPU + {4D57CD8B-AA87-47E3-A7CC-01E9C5DF7E28}.Debug|x64.Build.0 = Debug|Any CPU + {4D57CD8B-AA87-47E3-A7CC-01E9C5DF7E28}.Debug|x86.ActiveCfg = Debug|Any CPU + {4D57CD8B-AA87-47E3-A7CC-01E9C5DF7E28}.Debug|x86.Build.0 = Debug|Any CPU + {4D57CD8B-AA87-47E3-A7CC-01E9C5DF7E28}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D57CD8B-AA87-47E3-A7CC-01E9C5DF7E28}.Release|Any CPU.Build.0 = Release|Any CPU + {4D57CD8B-AA87-47E3-A7CC-01E9C5DF7E28}.Release|x64.ActiveCfg = Release|Any CPU + {4D57CD8B-AA87-47E3-A7CC-01E9C5DF7E28}.Release|x64.Build.0 = Release|Any CPU + {4D57CD8B-AA87-47E3-A7CC-01E9C5DF7E28}.Release|x86.ActiveCfg = Release|Any CPU + {4D57CD8B-AA87-47E3-A7CC-01E9C5DF7E28}.Release|x86.Build.0 = Release|Any CPU + {83A9ED0C-94F6-4E45-AA3C-D39DE427A13C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83A9ED0C-94F6-4E45-AA3C-D39DE427A13C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {83A9ED0C-94F6-4E45-AA3C-D39DE427A13C}.Debug|x64.ActiveCfg = Debug|Any CPU + {83A9ED0C-94F6-4E45-AA3C-D39DE427A13C}.Debug|x64.Build.0 = Debug|Any CPU + {83A9ED0C-94F6-4E45-AA3C-D39DE427A13C}.Debug|x86.ActiveCfg = Debug|Any CPU + {83A9ED0C-94F6-4E45-AA3C-D39DE427A13C}.Debug|x86.Build.0 = Debug|Any CPU + {83A9ED0C-94F6-4E45-AA3C-D39DE427A13C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {83A9ED0C-94F6-4E45-AA3C-D39DE427A13C}.Release|Any CPU.Build.0 = Release|Any CPU + {83A9ED0C-94F6-4E45-AA3C-D39DE427A13C}.Release|x64.ActiveCfg = Release|Any CPU + {83A9ED0C-94F6-4E45-AA3C-D39DE427A13C}.Release|x64.Build.0 = Release|Any CPU + {83A9ED0C-94F6-4E45-AA3C-D39DE427A13C}.Release|x86.ActiveCfg = Release|Any CPU + {83A9ED0C-94F6-4E45-AA3C-D39DE427A13C}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/ln.crypto.ec.test/UnitTest1.cs b/ln.crypto.ec.test/UnitTest1.cs new file mode 100644 index 0000000..6e9b071 --- /dev/null +++ b/ln.crypto.ec.test/UnitTest1.cs @@ -0,0 +1,224 @@ +using System; +using System.Security.Cryptography; +using System.Text; +using Crypto.EC; +using ln.crypto.ec.lsag; +using NUnit.Framework; +using System.Numerics; +using System.Globalization; + +namespace ln.crypto.ec.test +{ + public class Tests + { + [SetUp] + public void Setup() + { + } + + [Test] + public void Test_05_BigInteger() + { + foreach (TestVector testVector in vectors) + { + BigInteger bi = BigInteger.Parse(testVector.sx, System.Globalization.NumberStyles.HexNumber); + string si = bi.ToString("X"); + BigInteger bi2 = BigInteger.Parse(si, System.Globalization.NumberStyles.HexNumber); + + TestContext.Error.WriteLine("BigInteger Test: SRC={0}", bi.ToString("X")); + TestContext.Error.WriteLine("BigInteger Test: DST={0}", bi2.ToString("X")); + Assert.IsTrue(bi == bi2); + } + } + + [Test] + public void Test_10_Curve() + { + EllipticCurve secp256k1 = EllipticCurve.createSecp256k1(); + + /* Minimum check security... TODO: */ + Assert.IsFalse(new CurvePoint(secp256k1, 4, 3).isOnCurve()); + + + foreach (TestVector testVector in vectors) + { + TestContext.Error.WriteLine("TestCurve: k={0}", testVector.k); + + CurvePoint testPoint = new CurvePoint(secp256k1, testVector.x, testVector.y); + CurvePoint checkPoint = secp256k1.G * testVector.k; + + if (!Object.Equals(checkPoint, testPoint)) + { + TestContext.Error.WriteLine("TestCurve: modulus: {0}", secp256k1.Fp.FieldModulo.ToString("X")); + TestContext.Error.WriteLine("TestCurve: test x: {0}", testPoint.X.ToString("X")); + TestContext.Error.WriteLine("TestCurve: test y: {0}", testPoint.Y.ToString("X")); + TestContext.Error.WriteLine("TestCurve: check x: {0}", checkPoint.X.ToString("X")); + TestContext.Error.WriteLine("TestCurve: check y: {0}", checkPoint.Y.ToString("X")); + } + Assert.AreEqual(testPoint, checkPoint); + Assert.IsTrue( checkPoint.isOnCurve() ); + } + + + Assert.Pass(); + } + + [Test] + public void Test_11_Performance_Point_Mul() + { + int testCount = 2000; + BigInteger n = BigInteger.Parse("115792089237316195423570985008687907852837564279074904382605163141518161494317", NumberStyles.HexNumber); + CurvePoint G = EllipticCurve.SECP256K1.G; + CurvePoint result = null; + + DateTime start = DateTime.Now; + + for (int i=0; i + + + netcoreapp5.0 + + false + + + + + + + + + + + diff --git a/ln.crypto.ec/CurvePoint.cs b/ln.crypto.ec/CurvePoint.cs new file mode 100644 index 0000000..503fa5e --- /dev/null +++ b/ln.crypto.ec/CurvePoint.cs @@ -0,0 +1,159 @@ +using System; +using System.Text; +using System.Numerics; + +namespace Crypto.EC +{ + public class CurvePoint + { + public static readonly CurvePoint INFINITY = new CurvePoint(); + + public CurvePoint(EllipticCurve curve,BigInteger x,BigInteger y){ + Curve = curve; + X = x; + Y = y; + } + + private CurvePoint(){ + this.Curve = null; + this.X = 0; + this.Y = 0; + } + + public string toHexString() + { + StringBuilder sb = new StringBuilder(); + sb.Append(X.ToString("X")); + sb.Append("."); + sb.Append(Y.ToString("X")); + return sb.ToString(); + } + + public string toHex() + { + StringBuilder sb = new StringBuilder(); + sb.Append(X.ToString("X32")); + sb.Append(Y.ToString("X32")); + return sb.ToString(); + } + + public override string ToString() + { + if ((object)this == (object)INFINITY){ + return String.Format("[CurvePoint INFINITY]"); + } + return String.Format("[CurvePoint X={0} Y={1}]",X.ToString("X"),Y.ToString("X")); + } + + public EllipticCurve Curve { get; private set; } + public BigInteger X { get; private set; } + public BigInteger Y { get; private set; } + + public bool isOnCurve(){ + return this.Curve.isOnCurve(this); + } + + public bool isInfinity(){ + return ((object)this == (object)INFINITY); + } + + public CurvePoint Negated(){ + return new CurvePoint(Curve, X, Curve.Fp.AdditiveInverse(Y)); + } + + public static CurvePoint operator +(CurvePoint p1, CurvePoint p2) + { + if ((object)p1 == (object)INFINITY){ + return p2; + } + if ((object)p2 == (object)INFINITY){ + return p1; + } + + return p1.Curve.Add(p1, p2); + } + + public static CurvePoint operator -(CurvePoint p1, CurvePoint p2) + { + if ((object)p1 == (object)INFINITY){ + return p2; + } + if ((object)p2 == (object)INFINITY){ + return p1; + } + return p1.Curve.Add(p1, p2.Negated()); + } + + public static CurvePoint operator *(CurvePoint p1, BigInteger n) + { + if ((object)p1 == (object)INFINITY){ + return INFINITY; + } + + if (p1.Y == BigInteger.Zero){ + return INFINITY; + } + + if (n.IsZero){ + throw new NotImplementedException("CurvePoint * 0"); + } + + CurvePoint result = null; + CurvePoint p = p1; + + int i; + byte[] nBytes = n.ToByteArray(); + + for (i = 0; i < nBytes.Length<<3; i++) + { + if ((nBytes[i >> 3] & (1 << (i & 0x07))) != 0) + break; + + p += p; + } + + result = p; + + for (i++; i < nBytes.Length<<3; i++) + { + p += p; + if ((nBytes[i >> 3] & (1 << (i & 0x07))) != 0) + { + result = p + result; + } + } + + return result; + } + + public static bool operator ==(CurvePoint p1, CurvePoint p2) + { + if ((p1 is null)||(p2 is null)){ + return false; + } + return (p1.X == p2.X) && (p1.Y == p2.Y); + } + public static bool operator !=(CurvePoint p1, CurvePoint p2) + { + if ((p1 is null)||(p2 is null)){ + return false; + } + return (p1.X != p2.X) || (p1.Y != p2.Y); + } + + public override bool Equals(object obj) => (obj is CurvePoint cp2) && X.Equals(cp2.X) && Y.Equals(cp2.Y); + public override int GetHashCode() => X.GetHashCode() ^ Y.GetHashCode(); + + public byte[] ToByteArray() + { + int bytesPerValue = Curve.Fp.FieldModulo.GetByteCount(); + byte[] result = new byte[bytesPerValue*2]; + X.TryWriteBytes(new Span(result, 0, bytesPerValue), out int bytesWritten); + Y.TryWriteBytes(new Span(result, bytesPerValue, bytesPerValue), out bytesWritten); + return result; + } + + + + } +} diff --git a/ln.crypto.ec/EllipticCurve.cs b/ln.crypto.ec/EllipticCurve.cs new file mode 100644 index 0000000..4f85570 --- /dev/null +++ b/ln.crypto.ec/EllipticCurve.cs @@ -0,0 +1,146 @@ +using System; +using System.Text; +using ln.crypto.ec; +using System.Security.Cryptography; +using System.Numerics; +using System.Globalization; + +namespace Crypto.EC +{ + public class EllipticCurve + { + public BigInteger a { get; private set; } + public BigInteger b { get; private set; } + public BigInteger n { get; private set; } + public int h { get; private set; } + + public CurvePoint G { get; private set; } + public Field Fp { get; private set; } + + public EllipticCurve(BigInteger p,BigInteger a,BigInteger b,BigInteger xG,BigInteger yG,BigInteger n,int h) + { + this.Fp = new Field(p); + this.a = a; + this.b = b; + this.n = n; + this.h = h; + this.G = new CurvePoint(this, xG, yG); + + if (!isOnCurve(this.G)){ + throw new ArgumentException("G is not part of the curve"); + } + } + + public BigInteger Y2(BigInteger x) + { + BigInteger yy = BigInteger.ModPow(x, 3, Fp.FieldModulo); + yy += (a * x); + yy += b; + yy %= this.Fp.FieldModulo; + return yy; + } + + public bool isOnCurve(CurvePoint p){ + BigInteger py2 = BigInteger.ModPow(p.Y, 2, this.Fp.FieldModulo); + BigInteger pcy2 = Y2(p.X); + + //Console.WriteLine("CHECK A: {0}",py2.toHexString()); + //Console.WriteLine("CHECK B: {0}",pcy2.toHexString()); + + return (py2 == pcy2); + } + + public CurvePoint Add(CurvePoint p1, CurvePoint p2) + { + if ((p1 is null) || (p2 is null)) + { + throw new ArgumentException("SignedCurvePoints to be added must not be null"); + } + + if (p1.Curve != p2.Curve) + { + throw new ArgumentException("SignedCurvePoints to be added must belong to the same curve!"); + } + + if ((p1 != p2) && (p1.X == p2.X)){ + return CurvePoint.INFINITY; + } + + BigInteger lambda,divlambda; + + if (p1 == p2){ + lambda = Fp.Fit(3 * BigInteger.ModPow(p1.X, 2, Fp.FieldModulo) + a); + divlambda = Fp.Fit(2 * p1.Y); + } else { + lambda = Fp.Fit(p2.Y - p1.Y); + divlambda = Fp.Fit(p2.X - p1.X); + } + + + + lambda = lambda * Euclid.inverse(divlambda, Fp.FieldModulo); + + BigInteger xR = (BigInteger.ModPow(lambda, 2, Fp.FieldModulo) - p1.X - p2.X); + BigInteger yR = (lambda * (p1.X - xR)) - p1.Y; + + xR = Fp.Fit(xR); + yR = Fp.Fit(yR); + + return new CurvePoint(this, xR, yR); + } + + public CurvePoint HashToPoint(byte[] message) + { + byte[] hash = SHA256.Create().ComputeHash(message); + BigInteger bi = new BigInteger(hash) * 16; + return MapToCurve(bi); + } + + public BigInteger[] HashToField(byte[] message, int n) + { + byte[] hash = SHA256.Create().ComputeHash(message); + BigInteger[] results = new BigInteger[n]; + for (int i=0;i HashToField(message, 1)[0]; + + public CurvePoint MapToCurve(BigInteger u) => G * Fp.Fit(u); + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendFormat("[EllipticCurve] Fp={0}\n",(Fp == null) ? "" : Fp.ToString()); + sb.AppendFormat("[EllipticCurve] a={0}\n",a.ToString("X")); + sb.AppendFormat("[EllipticCurve] b={0}\n",b.ToString("X")); + sb.AppendFormat("[EllipticCurve] Gx={0}\n",G.X.ToString("X")); + sb.AppendFormat("[EllipticCurve] Gy={0}\n",G.Y.ToString("X")); + sb.AppendFormat("[EllipticCurve] G on curve? {0}",isOnCurve(G)); + + return sb.ToString(); + } + + public static EllipticCurve createSecp256k1(){ + BigInteger p = (BigInteger.One << 256); + p -= BigInteger.One; + p -= (BigInteger.One << 32); + p -= (BigInteger.One << 9); + p -= (BigInteger.One << 8); + p -= (BigInteger.One << 7); + p -= (BigInteger.One << 6); + p -= (BigInteger.One << 4); + + BigInteger xG = BigInteger.Parse("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", NumberStyles.HexNumber); + BigInteger yG = BigInteger.Parse("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", NumberStyles.HexNumber); + + BigInteger n = BigInteger.Parse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", NumberStyles.HexNumber); + return new EllipticCurve(p, BigInteger.Zero, 7, xG, yG, n, 1); + } + + public static readonly EllipticCurve SECP256K1 = createSecp256k1(); + } +} diff --git a/ln.crypto.ec/Euclid.cs b/ln.crypto.ec/Euclid.cs new file mode 100644 index 0000000..a66cb5f --- /dev/null +++ b/ln.crypto.ec/Euclid.cs @@ -0,0 +1,73 @@ +using System; +using System.Numerics; + +namespace ln.crypto.ec +{ +public static class Euclid + { + + public static Tuple extended(BigInteger a, BigInteger b) + { + if (b.IsZero){ + if (a != BigInteger.One){ + throw new ArgumentException("Euclid found GCD>1!"); + } + return new Tuple(a, BigInteger.One, BigInteger.Zero); + } + Tuple r = extended(b, a % b); + return new Tuple(r.Item1,r.Item3,r.Item2 - ((a / b) * r.Item3) ); + } + + /** + * Implementation of inverse() like found at + * https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm (14 Oct 2017) + **/ + public static BigInteger inverse(BigInteger a,BigInteger m){ + BigInteger one, zero; + + rt act, next, future; + + one = BigInteger.One; + zero = BigInteger.Zero; + + act = new rt(m, zero); + next = new rt(a, one); + + while (!next.r.IsZero) + { + BigInteger q = act.r / next.r; + future = new rt( + act.r - (q * next.r), + act.t - (q * next.t) + ); + + //Console.WriteLine("EUCLID: q = {0}",q); + //Console.WriteLine("EUCLID: act: r = {0} / t = {1}",act.r,act.t); + //Console.WriteLine("EUCLID: next: r = {0} / t = {1}",next.r,next.t); + //Console.WriteLine("EUCLID: future: r = {0} / t = {1}",future.r,future.t); + + act = next; + next = future; + } + + if (act.r > one){ + throw new NotSupportedException(); + } + if (act.t < 0){ + act.t += m; + } + + return act.t; + } + + private struct rt { + public BigInteger r, t; + + public rt(BigInteger r,BigInteger t){ + this.r = r; + this.t = t; + } + } + + } +} \ No newline at end of file diff --git a/ln.crypto.ec/Extensions.cs b/ln.crypto.ec/Extensions.cs new file mode 100644 index 0000000..28d425d --- /dev/null +++ b/ln.crypto.ec/Extensions.cs @@ -0,0 +1,49 @@ +using System; +using System.Globalization; +using System.Numerics; + +namespace ln.crypto.ec +{ + public static class Extensions + { + + public static T[] Segment(this T[] a,int start) => Segment(a, start, a.Length); + public static T[] Segment(this T[] a,int start, int length) + { + T[] r = new T[length]; + Array.Copy(a, start, r, 0, length > a.Length ? a.Length : length); + return r; + } + + public static byte[] GetBytes(this uint[] a) + { + byte[] r = new byte[a.Length * 4]; + for (int n=0;n(T[] a) + { + T[] r = (T[])a.Clone(); + Array.Reverse(r); + return r; + } + + public static int CreateHashCode(this byte[] bytes) + { + int hash = 0x22447799; + foreach (byte by in bytes) + hash *= by; + return hash; + } + + } +} \ No newline at end of file diff --git a/ln.crypto.ec/Field.cs b/ln.crypto.ec/Field.cs new file mode 100644 index 0000000..b7417fd --- /dev/null +++ b/ln.crypto.ec/Field.cs @@ -0,0 +1,44 @@ +using System; +using System.Numerics; + +namespace ln.crypto.ec +{ + public class Field + { + public static Field Default { get; set; } = null; // new IntField(UBigInteger.ZERO.Resize(256) - 1); + public static Field INFINITY { get; private set; } = new Field(); + + public BigInteger FieldModulo { get; private set; } + + public Field(BigInteger p){ + this.FieldModulo = p; + } + + private Field(){ + this.FieldModulo = 0; + } + + public BigInteger Fit(BigInteger value) + { + if (FieldModulo.IsZero){ + return value; + } + + value %= FieldModulo; + if (value.Sign < 0) + { + value += FieldModulo; + } + return value; + } + + public BigInteger AdditiveInverse(BigInteger value){ + return FieldModulo - value; + } + + public override string ToString(){ + return String.Format("[IntField p={0}]",this.FieldModulo); + } + + } +} \ No newline at end of file diff --git a/ln.crypto.ec/ProjectiveCurvePoint.cs b/ln.crypto.ec/ProjectiveCurvePoint.cs new file mode 100644 index 0000000..decc35a --- /dev/null +++ b/ln.crypto.ec/ProjectiveCurvePoint.cs @@ -0,0 +1,198 @@ +using System; +using System.Numerics; +using ln.crypto.ec; + +// namespace Crypto.EC +// { +// public class ProjectiveCurvePoint +// { +// public static readonly ProjectiveCurvePoint INFINITY = new ProjectiveCurvePoint(null,1,0,0); + +// public BigInteger X { get; set; } +// public BigInteger Y { get; set; } +// public BigInteger Z { get; set; } + +// public EllipticCurve Curve { get; private set; } + +// public ProjectiveCurvePoint(CurvePoint p) +// { +// this.Curve = p.Curve; +// this.X = p.X; +// this.Y = p.Y; +// this.Z = 1; +// } + +// public ProjectiveCurvePoint(EllipticCurve curve,BigInteger x,BigInteger y,BigInteger z) +// { +// this.Curve = curve; +// this.X = x; +// this.Y = y; +// this.Z = z; +// } + +// public CurvePoint toCurvePoint(){ +// if (Z.IsZero){ +// return CurvePoint.INFINITY; +// } +// BigInteger iz = Euclid.inverse(Z, Curve.Fp.FieldModulo); + +// BigInteger nx = Curve.Fp.Fit(X * iz); +// BigInteger ny = Curve.Fp.Fit(Y * iz); + +// return new CurvePoint(Curve, nx, ny); +// } + +// public override bool Equals(object obj) +// { +// ProjectiveCurvePoint p = INFINITY; + +// return new BooleanConditional() +// .requires(obj != null) +// .requires(GetType().IsInstanceOfType(obj)) +// .does( () => { p = (ProjectiveCurvePoint)obj; } ) +// .requires(this.Curve == p.Curve) +// .requires(this.X == p.X) +// .requires(this.Y == p.Y) +// .requires(this.Z == p.Z) +// .isSuccess(); +// } + +// public ProjectiveCurvePoint doubled() +// { +// BigInteger s = Curve.Fp.Fit(Y * Z); +// BigInteger B = Curve.Fp.Fit(X * Y * s); +// BigInteger w = Curve.Fp.Fit( +// (Curve.a * BigInteger.Pow(Z,2)) + +// (BigInteger.Pow(X,2) * 3) +// ); +// BigInteger h = Curve.Fp.Fit(w.Pow(2) - (B * 8)); + +// BigInteger xd = h * s * 2; +// BigInteger yd = ( +// (w * ((B * 4) - h)) - +// (Y.Pow(2) * s.Pow(2) * 8) +// ); +// BigInteger zd = s.Pow(3) * 8; + +// xd = Curve.Fp.Fit(xd); +// yd = Curve.Fp.Fit(yd); +// zd = Curve.Fp.Fit(zd); + +// return new ProjectiveCurvePoint(Curve,xd,yd,zd); +// } + +// public ProjectiveCurvePoint add(ProjectiveCurvePoint p){ +// BigInteger u = Curve.Fp.Fit( (p.Y * Z) - (Y * p.Z) ); +// BigInteger v = Curve.Fp.Fit( (p.X * Z) - (X * p.Z) ); +// BigInteger A = Curve.Fp.Fit( +// (Z * p.Z * u.Pow(2)) - v.Pow(3) - (v.Pow(2) * X * p.Z * 2) +// ); + +// BigInteger xd = v * A; +// BigInteger yd = ( +// (u * ((X * p.Z * v.Pow(2)) - A)) - +// (v.Pow(3) * Y * p.Z) +// ); +// BigInteger zd = Z * p.Z * v.Pow(3); + +// xd = Curve.Fp.Fit(xd); +// yd = Curve.Fp.Fit(yd); +// zd = Curve.Fp.Fit(zd); + +// if (zd == 0){ +// return INFINITY; +// } + +// return new ProjectiveCurvePoint(Curve, xd, yd, zd); +// } + +// public ProjectiveCurvePoint Negated(){ +// return new ProjectiveCurvePoint(Curve, X, Curve.Fp.AdditiveInverse(Y), Z); +// } + + +// public override string ToString() +// { +// return string.Format("[ProjectiveCurvePoint: Curve=... X={1} Y={2} Z={3}]", Curve,X,Y,Z); +// } + +// public static implicit operator ProjectiveCurvePoint(CurvePoint p){ +// return new ProjectiveCurvePoint(p); +// } + +// public static ProjectiveCurvePoint operator +(ProjectiveCurvePoint p1, ProjectiveCurvePoint p2) +// { +// if ((object)p1 == INFINITY){ +// return p2; +// } else if ((object)p2 == INFINITY){ +// return p1; +// } else if (p1.Equals(p2)){ +// return p1.doubled(); +// } else { +// return p1.add(p2); +// } +// } +// public static ProjectiveCurvePoint operator -(ProjectiveCurvePoint p1, ProjectiveCurvePoint p2) +// { +// if ((object)p1 == INFINITY) +// { +// return p2; +// } +// else if ((object)p2 == INFINITY) +// { +// return p1; +// } +// return p1 + p2.Negated(); +// } +// public static ProjectiveCurvePoint operator *(ProjectiveCurvePoint p1, Integer n) +// { +// if ((object)p1 == (object)INFINITY) +// { +// return INFINITY; +// } + +// if (p1.Y == BigInteger.Zero) +// { +// return INFINITY; +// } + +// if (n.isZero()) +// { +// throw new NotImplementedException("CurevPoint * 0"); +// } + +// ProjectiveCurvePoint result = null; +// ProjectiveCurvePoint p = p1; + +// int i; +// int bitwidth = n.Log2(); + +// if (bitwidth < 0) +// { +// bitwidth = -bitwidth; +// } + +// for (i = 0; i < bitwidth; i++) +// { +// if (n[i]) +// break; + +// p += p; +// } + +// result = p; + +// for (i++; i <= bitwidth; i++) +// { +// p += p; +// if (n[i]) +// { +// result = p + result; +// } +// } + +// return result; +// } + +// } +// } diff --git a/ln.crypto.ec/SHA256Extensions.cs b/ln.crypto.ec/SHA256Extensions.cs new file mode 100644 index 0000000..fa94ec7 --- /dev/null +++ b/ln.crypto.ec/SHA256Extensions.cs @@ -0,0 +1,9 @@ +using System.Security.Cryptography; + +namespace ln.crypto.ec +{ + public static class SHA256Extensions + { + public static void TransformBlock(this SHA256 sha256, byte[] input) => sha256.TransformBlock(input,0,input.Length, null, 0); + } +} \ No newline at end of file diff --git a/ln.crypto.ec/ln.crypto.ec.csproj b/ln.crypto.ec/ln.crypto.ec.csproj new file mode 100644 index 0000000..bebf901 --- /dev/null +++ b/ln.crypto.ec/ln.crypto.ec.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp5.0 + + + + + + + + diff --git a/ln.crypto.ec/lsag/LSAG.cs b/ln.crypto.ec/lsag/LSAG.cs new file mode 100644 index 0000000..f1ba729 --- /dev/null +++ b/ln.crypto.ec/lsag/LSAG.cs @@ -0,0 +1,133 @@ +using System; +using System.Security.Cryptography; +using Crypto.EC; +using System.Numerics; + +namespace ln.crypto.ec.lsag +{ + public static class LSAG + { + public static RandomNumberGenerator RandomNumberGenerator { get; } = RandomNumberGenerator.Create(); + + public static void Sign(byte[] message, EllipticCurve curve, BigInteger signKey, CurvePoint[] publicKeys, out Signature signature) + { + Sign(message, curve, signKey, publicKeys, out BigInteger c0, out BigInteger[] s, out CurvePoint Y); + signature = new Signature(c0, s, Y); + } + public static void Sign(byte[] message, EllipticCurve curve, BigInteger signKey, CurvePoint[] publicKeys, out BigInteger C0, out BigInteger[] s,out CurvePoint Y) + { + if (message.Length > 32) + message = SHA256.Create().ComputeHash(message); + + BigInteger[] c = new BigInteger[publicKeys.Length]; + s = new BigInteger[publicKeys.Length]; + CurvePoint signPubKey = curve.G * signKey; + int signPubKeyIndex = -1; + + for (int i=0;i Verify(curve, message, publicKeys, signature.C0, signature.s, signature.Y); + public static bool Verify(EllipticCurve curve, byte[] message, CurvePoint[] publicKeys, BigInteger C0, BigInteger[] s, CurvePoint Y) + { + if (message.Length > 32) + message = SHA256.Create().ComputeHash(message); + + BigInteger[] c = new BigInteger[publicKeys.Length+1]; + c[0] = C0; + + byte[] publicKeysHash = HashCurvePoints(publicKeys); + + CurvePoint H = curve.HashToPoint(publicKeysHash); + + for (int i=0;i H1(curve, HashCurvePoints(publicKeys), Y, message, a, b); + public static BigInteger H1(EllipticCurve curve, byte[] publicKeysHash, CurvePoint Y, byte[] message, CurvePoint a, CurvePoint b) + { + SHA256 sha256 = SHA256.Create(); + sha256.TransformBlock(publicKeysHash); + sha256.TransformBlock(Y.ToByteArray()); + sha256.TransformBlock(message); + sha256.TransformBlock(a.ToByteArray()); + sha256.TransformBlock(b.ToByteArray()); + sha256.TransformFinalBlock(new byte[0],0,0); + return curve.Fp.Fit(new BigInteger(sha256.Hash)); + } + + public static byte[] HashCurvePoints(CurvePoint[] curvePoints) + { + SHA256 sha256 = SHA256.Create(); + foreach (CurvePoint cp in curvePoints) + sha256.TransformBlock(cp.ToByteArray()); + return sha256.TransformFinalBlock(new byte[0],0,0); + } + + + public static BigInteger GetRandom(BigInteger maxExclusive) + { + byte[] bytes = new byte[maxExclusive.GetByteCount()]; + RandomNumberGenerator.GetBytes(bytes); + BigInteger result = new BigInteger(bytes) % maxExclusive; + if (result < 0) + result += maxExclusive; + return result; + } + + + public class Signature + { + public BigInteger C0 { get; } + public BigInteger[] s { get; } + public CurvePoint Y { get; } + + public Signature(BigInteger C0, BigInteger[] s, CurvePoint Y) + { + this.C0 = C0; + this.s = (BigInteger[])s.Clone(); + this.Y = Y; + } + + } + + + } +}