134 lines
5.1 KiB
C#
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;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
}
|