ln.crypto.ec/ln.crypto.ec/lsag/LSAG.cs

134 lines
5.1 KiB
C#

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