/// <summary>
/// Sign
/// </summary>
/// <param name="privateKey">private key</param>
/// <param name="data">data to be signed</param>
/// <param name="signature">signature returned</param>
/// <returns>true if the signing succeeded, otherwise false.</returns>
public override bool Sign(byte[] privateKey, byte[] data, out byte[] signature)
{
// based on draft-irtf-cfrg-eddsa-08 "Edwards-curve Digital Signature Algorithm (EdDSA)"
if (privateKey.Length != 32) {
signature = null;
return false;
}
using (var sha512 = new SHA512CryptoServiceProvider()) {
byte[] hash;
hash = sha512.ComputeHash(privateKey);
byte[] sdata = new byte[32];
Buffer.BlockCopy(hash, 0, sdata, 0, 32);
sdata[0] &= (byte)((0xff << _c) & 0xff); // clear lower bits
sdata[31] &= (byte)((1 << (_n % 8)) - 1); // clear higher bits
sdata[31] |= (byte)(1 << (_n % 8)); // set top bit
Array.Reverse(sdata); // to big endian
var s = new BigInteger(sdata);
var G = GetBasePoint();
byte[] A;
if (!EncodePoint(PointMul(s, G), out A)) {
signature = null;
return false;
}
sha512.Initialize();
sha512.TransformBlock(hash, 32, 32, null, 0);
sha512.TransformFinalBlock(data, 0, data.Length);
byte[] rdata = sha512.Hash;
Array.Reverse(rdata); // to big endian
var r = new BigInteger(rdata) % this._l;
byte[] R;
if (!EncodePoint(PointMul(r, G), out R)) {
signature = null;
return false;
}
sha512.Initialize();
sha512.TransformBlock(R, 0, R.Length, null, 0);
sha512.TransformBlock(A, 0, A.Length, null, 0);
sha512.TransformFinalBlock(data, 0, data.Length);
byte[] kdata = sha512.Hash;
Array.Reverse(kdata); // to big endian
var k = new BigInteger(kdata) % this._l;
var S = (r + k * s) % this._l;
byte[] sig = new byte[64];
Buffer.BlockCopy(R, 0, sig, 0, R.Length); // copy 32 bytes
byte[] wS = S.GetBytes();
Array.Reverse(wS); // to little endian
Buffer.BlockCopy(wS, 0, sig, 32, wS.Length);
signature = sig;
return true;
}
}