First Commit

master
Harald Wolff 2021-04-06 20:42:16 +02:00
commit c885719c5b
13 changed files with 1152 additions and 0 deletions

40
.gitignore vendored 100644
View File

@ -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

48
ln.crypto.ec.sln 100644
View File

@ -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

View File

@ -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<testCount; i++)
{
result = G * n;
}
DateTime stop = DateTime.Now;
TestContext.Error.WriteLine("Point Multiplication Performance: needed {0}ms ( {1} multiplications / s )", (stop - start).TotalMilliseconds, (testCount * 1000) / (stop - start).TotalMilliseconds);
TestContext.Error.WriteLine("Resulting Point is: {0}", result.toHexString());
}
[Test]
public void Test_15_GenerateKeyPairs()
{
RandomNumberGenerator random = RandomNumberGenerator.Create();
for (int i=0; i< 10; i++)
{
byte[] random32 = new byte[32];
random.GetBytes(random32);
BigInteger privkey = EllipticCurve.SECP256K1.Fp.Fit(new BigInteger(random32));
CurvePoint publicKey = EllipticCurve.SECP256K1.G * privkey;
TestContext.Error.WriteLine("Generated Key-Pair: {0} : {1}", privkey.ToString("X"), publicKey.toHex());
}
}
[Test]
public void Test_20_LSAG()
{
byte[] message = Encoding.UTF8.GetBytes("I am the first and only to be signed text!");
LSAGDoTest(3, 3, message);
LSAGDoTest(10, 5, message);
LSAGDoTest(100, 5, message);
LSAGDoTest(1000, 5, message);
}
public void LSAGDoTest(int ringsize, int testsize, byte[] message)
{
TestContext.Error.WriteLine("LSAGDotest({0})", ringsize);
RandomNumberGenerator random = RandomNumberGenerator.Create();
BigInteger[] privateKeys = new BigInteger[ringsize];
CurvePoint[] publicKeys = new CurvePoint[ringsize];
byte[] random32 = new byte[32];
for (int n=0;n<ringsize;n++)
{
random.GetBytes(random32);
privateKeys[n] = EllipticCurve.SECP256K1.Fp.Fit(new BigInteger(random32));
publicKeys[n] = EllipticCurve.SECP256K1.G * privateKeys[n];
}
LSAG.Signature[] signatures = new LSAG.Signature[ringsize];
DateTime start = DateTime.Now;
for (int n=0;n<testsize;n++)
{
LSAG.Sign(message, EllipticCurve.SECP256K1, privateKeys[n % privateKeys.Length], publicKeys, out signatures[n]);
}
DateTime stop = DateTime.Now;
TestContext.Error.WriteLine("LSAGDotest({0}): needed {1}ms ( {2}ms / sign )", ringsize, (stop - start).TotalMilliseconds,(stop - start).TotalMilliseconds / testsize);
start = DateTime.Now;
for (int n=0;n<testsize;n++)
{
Assert.IsTrue(LSAG.Verify(EllipticCurve.SECP256K1, message, publicKeys, signatures[n]));
}
stop = DateTime.Now;
TestContext.Error.WriteLine("LSAGDotest({0}): needed {1}ms ( {2}ms / verify )", ringsize, (stop - start).TotalMilliseconds,(stop - start).TotalMilliseconds / testsize);
for (int n=0;n<testsize;n++)
{
Assert.IsFalse(LSAG.Verify(EllipticCurve.SECP256K1, message, publicKeys, signatures[n].C0 + 1, signatures[n].s, signatures[n].Y));
}
}
struct TestVector
{
public BigInteger k,x,y;
public string sk,sx,sy;
public TestVector(BigInteger k, BigInteger x, BigInteger y)
{
this.k = k;
this.x = x;
this.y = y;
this.sk = k.ToString();
this.sx = x.ToString();
this.sy = y.ToString();
}
public TestVector(string k, string x, string y)
{
this.k = BigInteger.Parse(k);
this.x = BigInteger.Parse(x, NumberStyles.HexNumber);
this.y = BigInteger.Parse(y, NumberStyles.HexNumber);
this.sk = k;
this.sx = x;
this.sy = y;
}
}
TestVector[] vectors = new TestVector[]{
new TestVector("1","0079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798","00483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"),
new TestVector("2","00C6047F9441ED7D6D3045406E95C07CD85C778E4B8CEF3CA7ABAC09B95C709EE5","001AE168FEA63DC339A3C58419466CEAEEF7F632653266D0E1236431A950CFE52A"),
new TestVector("3","00F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9","00388F7B0F632DE8140FE337E62A37F3566500A99934C2231B6CB9FD7584B8E672"),
new TestVector("4","00E493DBF1C10D80F3581E4904930B1404CC6C13900EE0758474FA94ABE8C4CD13","0051ED993EA0D455B75642E2098EA51448D967AE33BFBDFE40CFE97BDC47739922"),
new TestVector("5","002F8BDE4D1A07209355B4A7250A5C5128E88B84BDDC619AB7CBA8D569B240EFE4","00D8AC222636E5E3D6D4DBA9DDA6C9C426F788271BAB0D6840DCA87D3AA6AC62D6"),
new TestVector("6","00FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A1460297556","00AE12777AACFBB620F3BE96017F45C560DE80F0F6518FE4A03C870C36B075F297"),
new TestVector("7","005CBDF0646E5DB4EAA398F365F2EA7A0E3D419B7E0330E39CE92BDDEDCAC4F9BC","006AEBCA40BA255960A3178D6D861A54DBA813D0B813FDE7B5A5082628087264DA"),
new TestVector("8","002F01E5E15CCA351DAFF3843FB70F3C2F0A1BDD05E5AF888A67784EF3E10A2A01","005C4DA8A741539949293D082A132D13B4C2E213D6BA5B7617B5DA2CB76CBDE904"),
new TestVector("9","00ACD484E2F0C7F65309AD178A9F559ABDE09796974C57E714C35F110DFC27CCBE","00CC338921B0A7D9FD64380971763B61E9ADD888A4375F8E0F05CC262AC64F9C37"),
new TestVector("10","00A0434D9E47F3C86235477C7B1AE6AE5D3442D49B1943C2B752A68E2A47E247C7","00893ABA425419BC27A3B6C7E693A24C696F794C2ED877A1593CBEE53B037368D7"),
new TestVector("11","00774AE7F858A9411E5EF4246B70C65AAC5649980BE5C17891BBEC17895DA008CB","00D984A032EB6B5E190243DD56D7B7B365372DB1E2DFF9D6A8301D74C9C953C61B"),
new TestVector("12","00D01115D548E7561B15C38F004D734633687CF4419620095BC5B0F47070AFE85A","00A9F34FFDC815E0D7A8B64537E17BD81579238C5DD9A86D526B051B13F4062327"),
new TestVector("13","00F28773C2D975288BC7D1D205C3748651B075FBC6610E58CDDEEDDF8F19405AA8","000AB0902E8D880A89758212EB65CDAF473A1A06DA521FA91F29B5CB52DB03ED81"),
new TestVector("14","00499FDF9E895E719CFD64E67F07D38E3226AA7B63678949E6E49B241A60E823E4","00CAC2F6C4B54E855190F044E4A7B3D464464279C27A3F95BCC65F40D403A13F5B"),
new TestVector("15","00D7924D4F7D43EA965A465AE3095FF41131E5946F3C85F79E44ADBCF8E27E080E","00581E2872A86C72A683842EC228CC6DEFEA40AF2BD896D3A5C504DC9FF6A26B58"),
new TestVector("16","00E60FCE93B59E9EC53011AABC21C23E97B2A31369B87A5AE9C44EE89E2A6DEC0A","00F7E3507399E595929DB99F34F57937101296891E44D23F0BE1F32CCE69616821"),
new TestVector("17","00DEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34","004211AB0694635168E997B0EAD2A93DAECED1F4A04A95C0F6CFB199F69E56EB77"),
new TestVector("18","005601570CB47F238D2B0286DB4A990FA0F3BA28D1A319F5E7CF55C2A2444DA7CC","00C136C1DC0CBEB930E9E298043589351D81D8E0BC736AE2A1F5192E5E8B061D58"),
new TestVector("19","002B4EA0A797A443D293EF5CFF444F4979F06ACFEBD7E86D277475656138385B6C","0085E89BC037945D93B343083B5A1C86131A01F60C50269763B570C854E5C09B7A"),
new TestVector("20","004CE119C96E2FA357200B559B2F7DD5A5F02D5290AFF74B03F3E471B273211C97","0012BA26DCB10EC1625DA61FA10A844C676162948271D96967450288EE9233DC3A"),
new TestVector("112233445566778899","00A90CC3D3F3E146DAADFC74CA1372207CB4B725AE708CEF713A98EDD73D99EF29","005A79D6B289610C68BC3B47F3D72F9788A26A06868B4D8E433E1E2AD76FB7DC76"),
new TestVector("112233445566778899112233445566778899","00E5A2636BCFD412EBF36EC45B19BFB68A1BC5F8632E678132B885F7DF99C5E9B3","00736C1CE161AE27B405CAFD2A7520370153C2C861AC51D6C1D5985D9606B45F39"),
new TestVector("28948022309329048855892746252171976963209391069768726095651290785379540373584","00A6B594B38FB3E77C6EDF78161FADE2041F4E09FD8497DB776E546C41567FEB3C","0071444009192228730CD8237A490FEBA2AFE3D27D7CC1136BC97E439D13330D55"),
new TestVector("57896044618658097711785492504343953926418782139537452191302581570759080747168","0000000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C63","003F3979BF72AE8202983DC989AEC7F2FF2ED91BDD69CE02FC0700CA100E59DDF3"),
new TestVector("86844066927987146567678238756515930889628173209306178286953872356138621120752","00E24CE4BEEE294AA6350FAA67512B99D388693AE4E7F53D19882A6EA169FC1CE1","008B71E83545FC2B5872589F99D948C03108D36797C4DE363EBD3FF6A9E1A95B10"),
new TestVector("115792089237316195423570985008687907852837564279074904382605163141518161494317","004CE119C96E2FA357200B559B2F7DD5A5F02D5290AFF74B03F3E471B273211C97","00ED45D9234EF13E9DA259E05EF57BB3989E9D6B7D8E269698BAFD77106DCC1FF5"),
new TestVector("115792089237316195423570985008687907852837564279074904382605163141518161494318","002B4EA0A797A443D293EF5CFF444F4979F06ACFEBD7E86D277475656138385B6C","007A17643FC86BA26C4CBCF7C4A5E379ECE5FE09F3AFD9689C4A8F37AA1A3F60B5"),
new TestVector("115792089237316195423570985008687907852837564279074904382605163141518161494319","005601570CB47F238D2B0286DB4A990FA0F3BA28D1A319F5E7CF55C2A2444DA7CC","003EC93E23F34146CF161D67FBCA76CAE27E271F438C951D5E0AE6D1A074F9DED7"),
new TestVector("115792089237316195423570985008687907852837564279074904382605163141518161494320","00DEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34","00BDEE54F96B9CAE9716684F152D56C251312E0B5FB56A3F09304E660861A910B8")
};
}
}

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp5.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0"/>
<ProjectReference Include="../ln.crypto.ec/ln.crypto.ec.csproj" />
</ItemGroup>
</Project>

View File

@ -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<byte>(result, 0, bytesPerValue), out int bytesWritten);
Y.TryWriteBytes(new Span<byte>(result, bytesPerValue, bytesPerValue), out bytesWritten);
return result;
}
}
}

View File

@ -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<n;i++)
{
hash = SHA256.Create().ComputeHash(hash);
results[i] = Fp.Fit(new BigInteger(hash));
}
return results;
}
public BigInteger HashToField(byte[] message) => 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();
}
}

View File

@ -0,0 +1,73 @@
using System;
using System.Numerics;
namespace ln.crypto.ec
{
public static class Euclid
{
public static Tuple<BigInteger,BigInteger,BigInteger> extended(BigInteger a, BigInteger b)
{
if (b.IsZero){
if (a != BigInteger.One){
throw new ArgumentException("Euclid found GCD>1!");
}
return new Tuple<BigInteger,BigInteger,BigInteger>(a, BigInteger.One, BigInteger.Zero);
}
Tuple<BigInteger,BigInteger,BigInteger> r = extended(b, a % b);
return new Tuple<BigInteger,BigInteger,BigInteger>(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;
}
}
}
}

View File

@ -0,0 +1,49 @@
using System;
using System.Globalization;
using System.Numerics;
namespace ln.crypto.ec
{
public static class Extensions
{
public static T[] Segment<T>(this T[] a,int start) => Segment(a, start, a.Length);
public static T[] Segment<T>(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<a.Length;n++)
Array.Copy(BitConverter.GetBytes(a[n]),0,r,(n*4),4);
return r;
}
public static byte[] GetBytes(this int[] a)
{
byte[] r = new byte[a.Length * 4];
for (int n=0;n<a.Length;n++)
Array.Copy(BitConverter.GetBytes(a[n]),0,r,(n*4),4);
return r;
}
public static T[] Reverse<T>(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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
// }
// }
// }

View File

@ -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);
}
}

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../ln.biginteger/ln.biginteger/ln.biginteger.csproj" />
</ItemGroup>
</Project>

View File

@ -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<publicKeys.Length; i++)
{
if (signPubKey.Equals(publicKeys[i]))
signPubKeyIndex = i;
}
if (signPubKeyIndex == -1)
throw new ArgumentException("public key of signer not found");
byte[] publicKeysHash = HashCurvePoints(publicKeys);
CurvePoint H = curve.HashToPoint(publicKeysHash);
Y = H * signKey;
BigInteger u = GetRandom(curve.n);
c[(signPubKeyIndex + 1) % publicKeys.Length] = H1(curve, publicKeysHash, Y, message, curve.G * u, H * u);
for (int i = (signPubKeyIndex+1) % publicKeys.Length; i != signPubKeyIndex; i = (i+1) % publicKeys.Length)
{
s[i] = GetRandom(curve.n);
CurvePoint z1 = (curve.G * s[i]) + (publicKeys[i] * c[i]);
CurvePoint z2 = (H * s[i]) + (Y * c[i]);
c[(i+1)%publicKeys.Length] = H1(curve, publicKeysHash, Y, message, z1, z2);
}
s[signPubKeyIndex] = u - (signKey * c[signPubKeyIndex]);
s[signPubKeyIndex] = s[signPubKeyIndex] % curve.n;
if (s[signPubKeyIndex] < 0)
s[signPubKeyIndex] += curve.n;
C0 = c[0];
}
public static bool Verify(EllipticCurve curve, byte[] message, CurvePoint[] publicKeys, Signature signature) => 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<publicKeys.Length; i++)
{
CurvePoint z1 = (curve.G * s[i]) + (publicKeys[i] * c[i]);
CurvePoint z2 = (H * s[i]) + (Y * c[i]);
c[i+1] = H1(curve, publicKeysHash, Y, message, z1, z2);
}
return c[0] == c[publicKeys.Length];
}
public static BigInteger H1(EllipticCurve curve, CurvePoint[] publicKeys, CurvePoint Y, byte[] message, CurvePoint a, CurvePoint b) => 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;
}
}
}
}