Initial
This commit is contained in:
parent
a39c5b0a6a
commit
9d1236a41a
42
Ed25519/Constants.cs
Normal file
42
Ed25519/Constants.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
namespace Ed25519
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
//
|
||||
// Compiler-time constants:
|
||||
//
|
||||
internal const int BIT_LENGTH = 256;
|
||||
|
||||
//
|
||||
// Run-time constants:
|
||||
//
|
||||
internal static readonly BigInteger Q = BigInteger.Parse("57896044618658097711785492504343953926634992332820282019728792003956564819949");
|
||||
internal static readonly BigInteger QM2 = BigInteger.Parse("57896044618658097711785492504343953926634992332820282019728792003956564819947");
|
||||
internal static readonly BigInteger QP3 = BigInteger.Parse("57896044618658097711785492504343953926634992332820282019728792003956564819952");
|
||||
internal static readonly BigInteger L = BigInteger.Parse("7237005577332262213973186563042994240857116359379907606001950938285454250989");
|
||||
internal static readonly BigInteger D = BigInteger.Parse("-4513249062541557337682894930092624173785641285191125241628941591882900924598840740");
|
||||
internal static readonly BigInteger I = BigInteger.Parse("19681161376707505956807079304988542015446066515923890162744021073123829784752");
|
||||
internal static readonly BigInteger U_N = BigInteger.Parse("57896044618658097711785492504343953926634992332820282019728792003956564819967");
|
||||
internal static readonly BigInteger TWO = new BigInteger(2);
|
||||
internal static readonly BigInteger EIGHT = new BigInteger(8);
|
||||
|
||||
//
|
||||
// Pre-calculated values:
|
||||
//
|
||||
internal static readonly EdPoint B = new EdPoint
|
||||
{
|
||||
X = BigInteger.Parse("15112221349535400772501151409588531511454012693041857206046113283949847762202").Mod(Q),
|
||||
Y = BigInteger.Parse("46316835694926478169428394003475163141307993866256225615783033603165251855960").Mod(Q),
|
||||
};
|
||||
|
||||
internal static readonly BigInteger RECOVER_X_EXP = Constants.QP3 / Constants.EIGHT;
|
||||
internal static readonly BigInteger TWO_POW_BIT_LENGTH_MINUS_TWO = BigInteger.Pow(2, BIT_LENGTH - 2);
|
||||
internal static readonly BigInteger[] TWO_POW_CACHE = Enumerable.Range(0, 2 * BIT_LENGTH).Select(i => BigInteger.Pow(2, i)).ToArray();
|
||||
}
|
||||
}
|
101
Ed25519/EdPoint.cs
Normal file
101
Ed25519/EdPoint.cs
Normal file
@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
namespace Ed25519
|
||||
{
|
||||
internal struct EdPoint
|
||||
{
|
||||
public BigInteger X { get; set; }
|
||||
|
||||
public BigInteger Y { get; set; }
|
||||
|
||||
public static EdPoint DecodePoint(ReadOnlySpan<byte> pointBytes)
|
||||
{
|
||||
var y = new BigInteger(pointBytes) & Constants.U_N;
|
||||
var x = y.RecoverX();
|
||||
|
||||
if ((x.IsEven ? 0 : 1) != pointBytes.GetBit(Constants.BIT_LENGTH - 1))
|
||||
{
|
||||
x = Constants.Q - x;
|
||||
}
|
||||
|
||||
var point = new EdPoint
|
||||
{
|
||||
X = x,
|
||||
Y = y,
|
||||
};
|
||||
|
||||
if (!point.IsOnCurve())
|
||||
throw new ArgumentException("Decoding point that is not on curve");
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> EncodePoint()
|
||||
{
|
||||
var nout = this.Y.EncodeInt();
|
||||
nout[^1] |= this.X.IsEven ? (byte)0 : (byte)0x80;
|
||||
return nout;
|
||||
}
|
||||
|
||||
public EdPoint Edwards(EdPoint point2)
|
||||
{
|
||||
var xx12 = this.X * point2.X;
|
||||
var yy12 = this.Y * point2.Y;
|
||||
var dTemp = Constants.D * xx12 * yy12;
|
||||
|
||||
var x3 = (this.X * point2.Y + point2.X * this.Y) * (1 + dTemp).Inv();
|
||||
var y3 = (this.Y * point2.Y + xx12) * (1 - dTemp).Inv();
|
||||
|
||||
return new EdPoint
|
||||
{
|
||||
X = x3.Mod(Constants.Q),
|
||||
Y = y3.Mod(Constants.Q),
|
||||
};
|
||||
}
|
||||
|
||||
public EdPoint ScalarMul(BigInteger e)
|
||||
{
|
||||
if (e.Equals(BigInteger.Zero))
|
||||
{
|
||||
return new EdPoint
|
||||
{
|
||||
X = BigInteger.Zero,
|
||||
Y = BigInteger.One,
|
||||
};
|
||||
}
|
||||
|
||||
var q = this.ScalarMul(e / Constants.TWO);
|
||||
q = q.EdwardsSquare();
|
||||
|
||||
return e.IsEven ? q : q.Edwards(this);
|
||||
}
|
||||
|
||||
public EdPoint EdwardsSquare()
|
||||
{
|
||||
var xx = this.X * this.X;
|
||||
var yy = this.Y * this.Y;
|
||||
var dTemp = Constants.D * xx * yy;
|
||||
|
||||
var x3 = 2 * this.X * this.Y * (1 + dTemp).Inv();
|
||||
var y3 = (yy + xx) * (1 - dTemp).Inv();
|
||||
|
||||
return new EdPoint
|
||||
{
|
||||
X = x3.Mod(Constants.Q),
|
||||
Y = y3.Mod(Constants.Q),
|
||||
};
|
||||
}
|
||||
|
||||
public bool IsOnCurve()
|
||||
{
|
||||
var xx = this.X * this.X;
|
||||
var yy = this.Y * this.Y;
|
||||
var dxxyy = Constants.D * yy * xx;
|
||||
|
||||
return (yy - xx - dxxyy - 1).Mod(Constants.Q).Equals(BigInteger.Zero);
|
||||
}
|
||||
}
|
||||
}
|
107
Ed25519/Extensions.cs
Normal file
107
Ed25519/Extensions.cs
Normal file
@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Ed25519
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
internal static ReadOnlySpan<byte> ComputeHash(this ReadOnlySpan<byte> data)
|
||||
{
|
||||
using var sha512 = SHA512.Create();
|
||||
return sha512.ComputeHash(data.ToArray());
|
||||
}
|
||||
|
||||
internal static ReadOnlySpan<byte> ComputeHash(this Stream inputStream)
|
||||
{
|
||||
using var sha512 = SHA512.Create();
|
||||
return sha512.ComputeHash(inputStream);
|
||||
}
|
||||
|
||||
internal static BigInteger Mod(this BigInteger number, BigInteger modulo)
|
||||
{
|
||||
var result = number % modulo;
|
||||
return result < 0 ? result + modulo : result;
|
||||
}
|
||||
|
||||
internal static BigInteger Inv(this BigInteger number)
|
||||
{
|
||||
return number.ExpMod(2, Constants.Q);
|
||||
}
|
||||
|
||||
internal static BigInteger RecoverX(this BigInteger y)
|
||||
{
|
||||
var y2 = y * y;
|
||||
var xx = (y2 - 1) * (Constants.D * y2 + 1).Inv();
|
||||
var x = xx.ExpMod(Constants.RECOVER_X_EXP, Constants.Q);
|
||||
|
||||
if (!(x * x - xx).Mod(Constants.Q).Equals(BigInteger.Zero))
|
||||
{
|
||||
x = (x * Constants.I).Mod(Constants.Q);
|
||||
}
|
||||
|
||||
if (!x.IsEven)
|
||||
{
|
||||
x = Constants.Q - x;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static BigInteger ExpMod(this BigInteger number, BigInteger exponent, BigInteger modulo)
|
||||
{
|
||||
if (exponent.Equals(BigInteger.Zero))
|
||||
{
|
||||
return BigInteger.One;
|
||||
}
|
||||
|
||||
var result = BigInteger.Pow(number.ExpMod(exponent / Constants.TWO, modulo), 2).Mod(modulo);
|
||||
|
||||
if (exponent.IsEven)
|
||||
return result;
|
||||
|
||||
result *= number;
|
||||
result = result.Mod(modulo);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static Span<byte> EncodeInt(this BigInteger number)
|
||||
{
|
||||
var nin = number.ToByteArray();
|
||||
var nout = new byte[Math.Max(nin.Length, 32)];
|
||||
|
||||
Array.Copy(nin, nout, nin.Length);
|
||||
return nout;
|
||||
}
|
||||
|
||||
internal static BigInteger DecodeInt(this ReadOnlySpan<byte> data)
|
||||
{
|
||||
return new BigInteger(data) & Constants.U_N;
|
||||
}
|
||||
|
||||
internal static BigInteger HashInt(this MemoryStream data)
|
||||
{
|
||||
var hash = data.ComputeHash();
|
||||
var hashSum = BigInteger.Zero;
|
||||
|
||||
for (var i = 0; i < 2 * Constants.BIT_LENGTH; i++)
|
||||
{
|
||||
var bit = hash.GetBit(i);
|
||||
if (bit != 0)
|
||||
{
|
||||
hashSum += Constants.TWO_POW_CACHE[i];
|
||||
}
|
||||
}
|
||||
|
||||
return hashSum;
|
||||
}
|
||||
|
||||
internal static int GetBit(this ReadOnlySpan<byte> data, int index)
|
||||
{
|
||||
return data[index / 8] >> (index % 8) & 1;
|
||||
}
|
||||
}
|
||||
}
|
90
Ed25519/Signer.cs
Normal file
90
Ed25519/Signer.cs
Normal file
@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
namespace Ed25519
|
||||
{
|
||||
public static class Signer
|
||||
{
|
||||
public static ReadOnlySpan<byte> Sign(ReadOnlySpan<byte> message, ReadOnlySpan<byte> privateKey, ReadOnlySpan<byte> publicKey)
|
||||
{
|
||||
var privateKeyHash = privateKey.ComputeHash();
|
||||
var privateKeyBits = Constants.TWO_POW_BIT_LENGTH_MINUS_TWO;
|
||||
for (var i = 3; i < Constants.BIT_LENGTH - 2; i++)
|
||||
{
|
||||
var bit = privateKeyHash.GetBit(i);
|
||||
if (bit != 0)
|
||||
{
|
||||
privateKeyBits += Constants.TWO_POW_CACHE[i];
|
||||
}
|
||||
}
|
||||
|
||||
BigInteger r;
|
||||
using (var rSub = new MemoryStream((Constants.BIT_LENGTH / 8) + message.Length))
|
||||
{
|
||||
rSub.Write(privateKeyHash[(privateKeyHash.Length/2)..]);
|
||||
rSub.Write(message);
|
||||
rSub.Flush();
|
||||
|
||||
r = rSub.HashInt();
|
||||
}
|
||||
|
||||
var bigR = Constants.B.ScalarMul(r);
|
||||
|
||||
BigInteger s;
|
||||
var encodedBigR = bigR.EncodePoint();
|
||||
using (var sTemp = new MemoryStream(encodedBigR.Length + publicKey.Length + message.Length))
|
||||
{
|
||||
sTemp.Write(encodedBigR);
|
||||
sTemp.Write(publicKey);
|
||||
sTemp.Write(message);
|
||||
sTemp.Flush();
|
||||
|
||||
s = (r + sTemp.HashInt() * privateKeyBits).Mod(Constants.L);
|
||||
}
|
||||
|
||||
using (var nOut = new MemoryStream(64))
|
||||
{
|
||||
nOut.Write(encodedBigR);
|
||||
nOut.Write(s.EncodeInt());
|
||||
return nOut.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Validate(ReadOnlySpan<byte> signature, ReadOnlySpan<byte> message, ReadOnlySpan<byte> publicKey)
|
||||
{
|
||||
if (signature.Length != Constants.BIT_LENGTH / 4)
|
||||
throw new ArgumentException("Signature length is wrong");
|
||||
|
||||
if (publicKey.Length != Constants.BIT_LENGTH / 8)
|
||||
throw new ArgumentException("Public key length is wrong");
|
||||
|
||||
var signatureSliceLeft = signature[..(Constants.BIT_LENGTH / 8)];
|
||||
var pointSignatureLeft = EdPoint.DecodePoint(signatureSliceLeft);
|
||||
var pointPublicKey = EdPoint.DecodePoint(publicKey);
|
||||
|
||||
var signatureSliceRight = signature[(signature.Length/2)..];
|
||||
var signatureRight = signatureSliceRight.DecodeInt();
|
||||
var encodedSignatureLeftPoint = pointSignatureLeft.EncodePoint();
|
||||
|
||||
BigInteger h;
|
||||
using (var sTemp = new MemoryStream(encodedSignatureLeftPoint.Length + publicKey.Length + message.Length))
|
||||
{
|
||||
sTemp.Write(encodedSignatureLeftPoint);
|
||||
sTemp.Write(publicKey);
|
||||
sTemp.Write(message);
|
||||
sTemp.Flush();
|
||||
|
||||
h = sTemp.HashInt();
|
||||
}
|
||||
|
||||
var ra = Constants.B.ScalarMul(signatureRight);
|
||||
var ah = pointPublicKey.ScalarMul(h);
|
||||
var rb = pointSignatureLeft.Edwards(ah);
|
||||
|
||||
return ra.X.Equals(rb.X) && ra.Y.Equals(rb.Y);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user