Ed25519/Ed25519/Signer.cs
2020-01-03 21:37:05 +01:00

99 lines
3.8 KiB
C#

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)
{
if(privateKey.Length != Constants.BIT_LENGTH / 8)
throw new ArgumentException($"Private key length is wrong. Got {publicKey.Length} instead of {Constants.BIT_LENGTH / 8}.");
if (publicKey.Length != Constants.BIT_LENGTH / 8)
throw new ArgumentException($"Public key length is wrong. Got {publicKey.Length} instead of {Constants.BIT_LENGTH / 8}.");
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[(Constants.BIT_LENGTH / 8)..]);
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());
nOut.Flush();
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. Got {signature.Length} instead of {Constants.BIT_LENGTH / 4}.");
if (publicKey.Length != Constants.BIT_LENGTH / 8)
throw new ArgumentException($"Public key length is wrong. Got {publicKey.Length} instead of {Constants.BIT_LENGTH / 8}.");
var signatureSliceLeft = signature[..(Constants.BIT_LENGTH / 8)];
var pointSignatureLeft = EdPoint.DecodePoint(signatureSliceLeft);
var pointPublicKey = EdPoint.DecodePoint(publicKey);
var signatureSliceRight = signature[(Constants.BIT_LENGTH / 8)..];
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);
}
}
}