<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.1" />

Ed25519

public static class Ed25519
A low-level implementation of the Ed25519, Ed25519ctx, and Ed25519ph instantiations of the Edwards-Curve Digital Signature Algorithm specified in RFC 8032.
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Math.EC.Rfc7748; using Org.BouncyCastle.Math.Raw; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using System; using System.Runtime.InteropServices; namespace Org.BouncyCastle.Math.EC.Rfc8032 { public static class Ed25519 { public enum Algorithm { Ed25519, Ed25519ctx, Ed25519ph } public sealed class PublicPoint { internal readonly int[] m_data; internal PublicPoint(int[] data) { m_data = data; } } private struct PointAccum { internal int[] x; internal int[] y; internal int[] z; internal int[] u; internal int[] v; } private struct PointAffine { internal int[] x; internal int[] y; } private struct PointExtended { internal int[] x; internal int[] y; internal int[] z; internal int[] t; } private struct PointPrecomp { internal int[] ymx_h; internal int[] ypx_h; internal int[] xyd; } private struct PointPrecompZ { internal int[] ymx_h; internal int[] ypx_h; internal int[] xyd; internal int[] z; } private struct PointTemp { internal int[] r0; internal int[] r1; } private const int CoordUints = 8; private const int PointBytes = 32; private const int ScalarUints = 8; private const int ScalarBytes = 32; public static readonly int PrehashSize = 64; public static readonly int PublicKeySize = 32; public static readonly int SecretKeySize = 32; public static readonly int SignatureSize = 64; private static readonly byte[] Dom2Prefix = new byte[32] { 83, 105, 103, 69, 100, 50, 53, 53, 49, 57, 32, 110, 111, 32, 69, 100, 50, 53, 53, 49, 57, 32, 99, 111, 108, 108, 105, 115, 105, 111, 110, 115 }; private static readonly uint[] P = new uint[8] { 4294967277, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, 2147483647 }; private static readonly uint[] Order8_y1 = new uint[8] { 1886001095, 1339575613, 1980447930, 258412557, 4199751722, 3335272748, 2013120334, 2047061138 }; private static readonly uint[] Order8_y2 = new uint[8] { 2408966182, 2955391682, 2314519365, 4036554738, 95215573, 959694547, 2281846961, 100422509 }; private static readonly int[] B_x = new int[10] { 52811034, 25909283, 8072341, 50637101, 13785486, 30858332, 20483199, 20966410, 43936626, 4379245 }; private static readonly int[] B_y = new int[10] { 40265304, 26843545, 6710886, 53687091, 13421772, 40265318, 26843545, 6710886, 53687091, 13421772 }; private static readonly int[] B128_x = new int[10] { 12052516, 1174424, 4087752, 38672185, 20040971, 21899680, 55468344, 20105554, 66708015, 9981791 }; private static readonly int[] B128_y = new int[10] { 66430571, 45040722, 4842939, 15895846, 18981244, 46308410, 4697481, 8903007, 53646190, 12474675 }; private static readonly int[] C_d = new int[10] { 56195235, 47411844, 25868126, 40503822, 57364, 58321048, 30416477, 31930572, 57760639, 10749657 }; private static readonly int[] C_d2 = new int[10] { 45281625, 27714825, 18181821, 13898781, 114729, 49533232, 60832955, 30306712, 48412415, 4722099 }; private static readonly int[] C_d4 = new int[10] { 23454386, 55429651, 2809210, 27797563, 229458, 31957600, 54557047, 27058993, 29715967, 9444199 }; private const int WnafWidth128 = 4; private const int WnafWidthBase = 6; private const int PrecompBlocks = 8; private const int PrecompTeeth = 4; private const int PrecompSpacing = 8; private const int PrecompRange = 256; private const int PrecompPoints = 8; private const int PrecompMask = 7; private static readonly object PrecompLock = new object(); private static PointPrecomp[] PrecompBaseWnaf = null; private static PointPrecomp[] PrecompBase128Wnaf = null; private static int[] PrecompBaseComb = null; private static byte[] CalculateS(byte[] r, byte[] k, byte[] s) { byte[] array = new byte[32]; CalculateS(r, k, s, array); return array; } private unsafe static void CalculateS(ReadOnlySpan<byte> r, ReadOnlySpan<byte> k, ReadOnlySpan<byte> s, Span<byte> S) { Span<uint> span = new Span<uint>(stackalloc byte[64], 16); Scalar25519.Decode(r, span); Span<uint> span2 = new Span<uint>(stackalloc byte[32], 8); Scalar25519.Decode(k, span2); Span<uint> span3 = new Span<uint>(stackalloc byte[32], 8); Scalar25519.Decode(s, span3); Nat256.MulAddTo(span2, span3, span); if (BitConverter.IsLittleEndian) Scalar25519.Reduce512(MemoryMarshal.AsBytes(span), S); else { Span<byte> span4 = new Span<byte>(stackalloc byte[64], 64); Codec.Encode32(span, span4); Scalar25519.Reduce512(span4, S); } } private static bool CheckContextVar(byte[] ctx, byte phflag) { if (ctx != null || phflag != 0) { if (ctx != null) return ctx.Length < 256; return false; } return true; } private static int CheckPoint(ref PointAffine p) { int[] array = X25519Field.Create(); int[] array2 = X25519Field.Create(); int[] array3 = X25519Field.Create(); X25519Field.Sqr(p.x, array2); X25519Field.Sqr(p.y, array3); X25519Field.Mul(array2, array3, array); X25519Field.Sub(array2, array3, array2); X25519Field.Mul(array, C_d, array); X25519Field.AddOne(array); X25519Field.Add(array, array2, array); X25519Field.Normalize(array); X25519Field.Normalize(array3); return X25519Field.IsZero(array) & ~X25519Field.IsZero(array3); } private static int CheckPoint(PointAccum p) { int[] array = X25519Field.Create(); int[] array2 = X25519Field.Create(); int[] array3 = X25519Field.Create(); int[] array4 = X25519Field.Create(); X25519Field.Sqr(p.x, array2); X25519Field.Sqr(p.y, array3); X25519Field.Sqr(p.z, array4); X25519Field.Mul(array2, array3, array); X25519Field.Sub(array2, array3, array2); X25519Field.Mul(array2, array4, array2); X25519Field.Sqr(array4, array4); X25519Field.Mul(array, C_d, array); X25519Field.Add(array, array4, array); X25519Field.Add(array, array2, array); X25519Field.Normalize(array); X25519Field.Normalize(array3); X25519Field.Normalize(array4); return X25519Field.IsZero(array) & ~X25519Field.IsZero(array3) & ~X25519Field.IsZero(array4); } private static bool CheckPointFullVar(ReadOnlySpan<byte> p) { uint num; uint num2 = num = (Codec.Decode32(p.Slice(28, p.Length - 28)) & 2147483647); uint num3 = num2 ^ P[7]; uint num4 = num2 ^ Order8_y1[7]; uint num5 = num2 ^ Order8_y2[7]; for (int num6 = 6; num6 > 0; num6--) { int num7 = num6 * 4; uint num8 = Codec.Decode32(p.Slice(num7, p.Length - num7)); num |= num8; num3 |= (num8 ^ P[num6]); num4 |= (num8 ^ Order8_y1[num6]); num5 |= (num8 ^ Order8_y2[num6]); } uint num9 = Codec.Decode32(p); if (num == 0 && num9 <= 1) return false; if (num3 == 0 && num9 >= P[0] - 1) return false; num4 |= (num9 ^ Order8_y1[0]); num5 |= (num9 ^ Order8_y2[0]); return (num4 != 0) & (num5 != 0); } private static bool CheckPointOrderVar(ref PointAffine p) { Init(out PointAccum r); ScalarMultOrderVar(ref p, ref r); return NormalizeToNeutralElementVar(ref r); } private static bool CheckPointVar(ReadOnlySpan<byte> p) { if ((Codec.Decode32(p.Slice(28, p.Length - 28)) & 2147483647) < P[7]) return true; for (int num = 6; num >= 0; num--) { int num2 = num * 4; if (Codec.Decode32(p.Slice(num2, p.Length - num2)) < P[num]) return true; } return false; } private static byte[] Copy(byte[] buf, int off, int len) { byte[] array = new byte[len]; Array.Copy(buf, off, array, 0, len); return array; } private static IDigest CreateDigest() { Sha512Digest sha512Digest = new Sha512Digest(); if (sha512Digest.GetDigestSize() != 64) throw new InvalidOperationException(); return sha512Digest; } public static IDigest CreatePrehash() { return CreateDigest(); } private static bool DecodePointVar(ReadOnlySpan<byte> p, bool negate, ref PointAffine r) { int num = (p[31] & 128) >> 7; X25519Field.Decode(p, r.y); int[] array = X25519Field.Create(); int[] array2 = X25519Field.Create(); X25519Field.Sqr(r.y, array); X25519Field.Mul(C_d, array, array2); X25519Field.SubOne(array); X25519Field.AddOne(array2); if (!X25519Field.SqrtRatioVar(array, array2, r.x)) return false; X25519Field.Normalize(r.x); if (num == 1 && X25519Field.IsZeroVar(r.x)) return false; if (negate ^ (num != (r.x[0] & 1))) { X25519Field.Negate(r.x, r.x); X25519Field.Normalize(r.x); } return true; } private unsafe static void Dom2(IDigest d, byte phflag, byte[] ctx) { int num = Dom2Prefix.Length; int num2 = num + 2 + ctx.Length; Span<byte> span = new Span<byte>(stackalloc byte[(int)(uint)num2], num2); Dom2Prefix.CopyTo(span); span[num] = phflag; span[num + 1] = (byte)ctx.Length; ctx.CopyTo(span.Slice(num + 2)); d.BlockUpdate(span); } private static void EncodePoint(ref PointAffine p, byte[] r, int rOff) { X25519Field.Encode(p.y, r, rOff); r[rOff + 32 - 1] |= (byte)((p.x[0] & 1) << 7); } private static void EncodePoint(ref PointAffine p, Span<byte> r) { X25519Field.Encode(p.y, r); r[31] |= (byte)((p.x[0] & 1) << 7); } public static void EncodePublicPoint(PublicPoint publicPoint, byte[] pk, int pkOff) { X25519Field.Encode(publicPoint.m_data, 10, pk, pkOff); pk[pkOff + 32 - 1] |= (byte)((publicPoint.m_data[0] & 1) << 7); } public static void EncodePublicPoint(PublicPoint publicPoint, Span<byte> pk) { X25519Field.Encode(publicPoint.m_data.AsSpan(10), pk); pk[31] |= (byte)((publicPoint.m_data[0] & 1) << 7); } private static int EncodeResult(ref PointAccum p, byte[] r, int rOff) { return EncodeResult(ref p, r.AsSpan(rOff)); } private static int EncodeResult(ref PointAccum p, Span<byte> r) { Init(out PointAffine r2); NormalizeToAffine(ref p, ref r2); int result = CheckPoint(ref r2); EncodePoint(ref r2, r); return result; } private static PublicPoint ExportPoint(ref PointAffine p) { int[] array = new int[20]; X25519Field.Copy(p.x, 0, array, 0); X25519Field.Copy(p.y, 0, array, 10); return new PublicPoint(array); } public static void GeneratePrivateKey(SecureRandom random, byte[] k) { if (k.Length != SecretKeySize) throw new ArgumentException("k"); random.NextBytes(k); } public static void GeneratePrivateKey(SecureRandom random, Span<byte> k) { if (k.Length != SecretKeySize) throw new ArgumentException("k"); random.NextBytes(k); } public static void GeneratePublicKey(byte[] sk, int skOff, byte[] pk, int pkOff) { GeneratePublicKey(sk.AsSpan(skOff), pk.AsSpan(pkOff)); } public unsafe static void GeneratePublicKey(ReadOnlySpan<byte> sk, Span<byte> pk) { IDigest digest = CreateDigest(); Span<byte> span = new Span<byte>(stackalloc byte[64], 64); digest.BlockUpdate(sk.Slice(0, SecretKeySize)); digest.DoFinal(span); Span<byte> span2 = new Span<byte>(stackalloc byte[32], 32); PruneScalar(span, span2); ScalarMultBaseEncoded(span2, pk); } public static PublicPoint GeneratePublicKey(byte[] sk, int skOff) { return GeneratePublicKey(sk.AsSpan(skOff)); } public unsafe static PublicPoint GeneratePublicKey(ReadOnlySpan<byte> sk) { IDigest digest = CreateDigest(); Span<byte> span = new Span<byte>(stackalloc byte[64], 64); digest.BlockUpdate(sk.Slice(0, SecretKeySize)); digest.DoFinal(span); Span<byte> span2 = new Span<byte>(stackalloc byte[32], 32); PruneScalar(span, span2); Init(out PointAccum r); ScalarMultBase(span2, ref r); Init(out PointAffine r2); NormalizeToAffine(ref r, ref r2); if (CheckPoint(ref r2) == 0) throw new InvalidOperationException(); return ExportPoint(ref r2); } private static uint GetWindow4(ReadOnlySpan<uint> x, int n) { int index = (int)((uint)n >> 3); int num = (n & 7) << 2; return (x[index] >> num) & 15; } private static void GroupCombBits(Span<uint> n) { for (int i = 0; i < n.Length; i++) { n[i] = Interleave.Shuffle2(n[i]); } } private unsafe static void ImplSign(IDigest d, Span<byte> h, ReadOnlySpan<byte> s, ReadOnlySpan<byte> pk, byte[] ctx, byte phflag, ReadOnlySpan<byte> m, Span<byte> sig) { if (ctx != null) Dom2(d, phflag, ctx); d.BlockUpdate(h.Slice(32, 32)); d.BlockUpdate(m); d.DoFinal(h); Span<byte> span = new Span<byte>(stackalloc byte[32], 32); Scalar25519.Reduce512(h, span); Span<byte> span2 = sig.Slice(0, 32); ScalarMultBaseEncoded(span, span2); if (ctx != null) Dom2(d, phflag, ctx); d.BlockUpdate(span2); d.BlockUpdate(pk); d.BlockUpdate(m); d.DoFinal(h); Span<byte> span3 = new Span<byte>(stackalloc byte[32], 32); Scalar25519.Reduce512(h, span3); Span<byte> s2 = sig.Slice(32, sig.Length - 32); CalculateS(span, span3, s, s2); } private static void ImplSign(byte[] sk, int skOff, byte[] ctx, byte phflag, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { ImplSign(sk.AsSpan(skOff, SecretKeySize), ctx, phflag, m.AsSpan(mOff, mLen), sig.AsSpan(sigOff, SignatureSize)); } private unsafe static void ImplSign(ReadOnlySpan<byte> sk, byte[] ctx, byte phflag, ReadOnlySpan<byte> m, Span<byte> sig) { if (!CheckContextVar(ctx, phflag)) throw new ArgumentException("ctx"); IDigest digest = CreateDigest(); Span<byte> span = new Span<byte>(stackalloc byte[64], 64); digest.BlockUpdate(sk); digest.DoFinal(span); Span<byte> span2 = new Span<byte>(stackalloc byte[32], 32); PruneScalar(span, span2); Span<byte> span3 = new Span<byte>(stackalloc byte[32], 32); ScalarMultBaseEncoded(span2, span3); ImplSign(digest, span, span2, span3, ctx, phflag, m, sig); } private static void ImplSign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte phflag, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { ImplSign(sk.AsSpan(skOff, SecretKeySize), pk.AsSpan(pkOff, PublicKeySize), ctx, phflag, m.AsSpan(mOff, mLen), sig.AsSpan(sigOff, SignatureSize)); } private unsafe static void ImplSign(ReadOnlySpan<byte> sk, ReadOnlySpan<byte> pk, byte[] ctx, byte phflag, ReadOnlySpan<byte> m, Span<byte> sig) { if (!CheckContextVar(ctx, phflag)) throw new ArgumentException("ctx"); IDigest digest = CreateDigest(); Span<byte> span = new Span<byte>(stackalloc byte[64], 64); digest.BlockUpdate(sk); digest.DoFinal(span); Span<byte> span2 = new Span<byte>(stackalloc byte[32], 32); PruneScalar(span, span2); ImplSign(digest, span, span2, pk, ctx, phflag, m, sig); } private static bool ImplVerify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte phflag, byte[] m, int mOff, int mLen) { return ImplVerify(sig.AsSpan(sigOff, SignatureSize), pk.AsSpan(pkOff, PublicKeySize), ctx, phflag, m.AsSpan(mOff, mLen)); } private unsafe static bool ImplVerify(ReadOnlySpan<byte> sig, ReadOnlySpan<byte> pk, byte[] ctx, byte phflag, ReadOnlySpan<byte> m) { if (!CheckContextVar(ctx, phflag)) throw new ArgumentException("ctx"); ReadOnlySpan<byte> readOnlySpan = sig.Slice(0, 32); ReadOnlySpan<byte> s = sig.Slice(32, sig.Length - 32); if (!CheckPointVar(readOnlySpan)) return false; Span<uint> span = new Span<uint>(stackalloc byte[32], 8); if (!Scalar25519.CheckVar(s, span)) return false; if (!CheckPointFullVar(pk)) return false; Init(out PointAffine r); if (!DecodePointVar(readOnlySpan, true, ref r)) return false; Init(out PointAffine r2); if (!DecodePointVar(pk, true, ref r2)) return false; IDigest digest = CreateDigest(); Span<byte> span2 = new Span<byte>(stackalloc byte[64], 64); if (ctx != null) Dom2(digest, phflag, ctx); digest.BlockUpdate(readOnlySpan); digest.BlockUpdate(pk); digest.BlockUpdate(m); digest.DoFinal(span2); Span<byte> span3 = new Span<byte>(stackalloc byte[32], 32); Scalar25519.Reduce512(span2, span3); Span<uint> span4 = new Span<uint>(stackalloc byte[32], 8); Scalar25519.Decode(span3, span4); Span<uint> span5 = new Span<uint>(stackalloc byte[16], 4); Span<uint> span6 = new Span<uint>(stackalloc byte[16], 4); if (!Scalar25519.ReduceBasisVar(span4, span5, span6)) throw new InvalidOperationException(); Scalar25519.Multiply128Var(span, span6, span); Init(out PointAccum r3); ScalarMultStraus128Var(span, span5, ref r2, span6, ref r, ref r3); return NormalizeToNeutralElementVar(ref r3); } private static bool ImplVerify(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, byte phflag, byte[] m, int mOff, int mLen) { return ImplVerify(sig.AsSpan(sigOff, SignatureSize), publicPoint, ctx, phflag, m.AsSpan(mOff, mLen)); } private unsafe static bool ImplVerify(ReadOnlySpan<byte> sig, PublicPoint publicPoint, byte[] ctx, byte phflag, ReadOnlySpan<byte> m) { if (!CheckContextVar(ctx, phflag)) throw new ArgumentException("ctx"); ReadOnlySpan<byte> readOnlySpan = sig.Slice(0, 32); ReadOnlySpan<byte> s = sig.Slice(32, sig.Length - 32); if (!CheckPointVar(readOnlySpan)) return false; Span<uint> span = new Span<uint>(stackalloc byte[32], 8); if (!Scalar25519.CheckVar(s, span)) return false; Init(out PointAffine r); if (!DecodePointVar(readOnlySpan, true, ref r)) return false; Init(out PointAffine r2); X25519Field.Negate(publicPoint.m_data, r2.x); X25519Field.Copy(publicPoint.m_data.AsSpan(10), r2.y); int publicKeySize = PublicKeySize; Span<byte> span2 = new Span<byte>(stackalloc byte[(int)(uint)publicKeySize], publicKeySize); EncodePublicPoint(publicPoint, span2); IDigest digest = CreateDigest(); Span<byte> span3 = new Span<byte>(stackalloc byte[64], 64); if (ctx != null) Dom2(digest, phflag, ctx); digest.BlockUpdate(readOnlySpan); digest.BlockUpdate(span2); digest.BlockUpdate(m); digest.DoFinal(span3); Span<byte> span4 = new Span<byte>(stackalloc byte[32], 32); Scalar25519.Reduce512(span3, span4); Span<uint> span5 = new Span<uint>(stackalloc byte[32], 8); Scalar25519.Decode(span4, span5); Span<uint> span6 = new Span<uint>(stackalloc byte[16], 4); Span<uint> span7 = new Span<uint>(stackalloc byte[16], 4); if (!Scalar25519.ReduceBasisVar(span5, span6, span7)) throw new InvalidOperationException(); Scalar25519.Multiply128Var(span, span7, span); Init(out PointAccum r3); ScalarMultStraus128Var(span, span6, ref r2, span7, ref r, ref r3); return NormalizeToNeutralElementVar(ref r3); } private static void Init(out PointAccum r) { r.x = X25519Field.Create(); r.y = X25519Field.Create(); r.z = X25519Field.Create(); r.u = X25519Field.Create(); r.v = X25519Field.Create(); } private static void Init(out PointAffine r) { r.x = X25519Field.Create(); r.y = X25519Field.Create(); } private static void Init(out PointExtended r) { r.x = X25519Field.Create(); r.y = X25519Field.Create(); r.z = X25519Field.Create(); r.t = X25519Field.Create(); } private static void Init(out PointPrecomp r) { r.ymx_h = X25519Field.Create(); r.ypx_h = X25519Field.Create(); r.xyd = X25519Field.Create(); } private static void Init(out PointPrecompZ r) { r.ymx_h = X25519Field.Create(); r.ypx_h = X25519Field.Create(); r.xyd = X25519Field.Create(); r.z = X25519Field.Create(); } private static void Init(out PointTemp r) { r.r0 = X25519Field.Create(); r.r1 = X25519Field.Create(); } private static void InvertDoubleZs(PointExtended[] points) { int num = points.Length; int[] array = X25519Field.CreateTable(num); int[] array2 = X25519Field.Create(); X25519Field.Copy(points[0].z, 0, array2, 0); X25519Field.Copy(array2, 0, array, 0); int num2 = 0; while (++num2 < num) { X25519Field.Mul(array2, points[num2].z, array2); X25519Field.Copy(array2, 0, array, num2 * 10); } X25519Field.Add(array2, array2, array2); X25519Field.InvVar(array2, array2); num2--; int[] array3 = X25519Field.Create(); while (num2 > 0) { int num4 = num2--; X25519Field.Copy(array, num2 * 10, array3, 0); X25519Field.Mul(array3, array2, array3); X25519Field.Mul(array2, points[num4].z, array2); X25519Field.Copy(array3, 0, points[num4].z, 0); } X25519Field.Copy(array2, 0, points[0].z, 0); } private static void NormalizeToAffine(ref PointAccum p, ref PointAffine r) { X25519Field.Inv(p.z, r.y); X25519Field.Mul(r.y, p.x, r.x); X25519Field.Mul(r.y, p.y, r.y); X25519Field.Normalize(r.x); X25519Field.Normalize(r.y); } private static bool NormalizeToNeutralElementVar(ref PointAccum p) { X25519Field.Normalize(p.x); X25519Field.Normalize(p.y); X25519Field.Normalize(p.z); if (X25519Field.IsZeroVar(p.x) && !X25519Field.IsZeroVar(p.y)) return X25519Field.AreEqualVar(p.y, p.z); return false; } private static void PointAdd(ref PointExtended p, ref PointExtended q, ref PointExtended r, ref PointTemp t) { int[] x = r.x; int[] y = r.y; int[] r2 = t.r0; int[] r3 = t.r1; int[] array = x; int[] array2 = r2; int[] array3 = r3; int[] array4 = y; X25519Field.Apm(p.y, p.x, y, x); X25519Field.Apm(q.y, q.x, r3, r2); X25519Field.Mul(x, r2, x); X25519Field.Mul(y, r3, y); X25519Field.Mul(p.t, q.t, r2); X25519Field.Mul(r2, C_d2, r2); X25519Field.Add(p.z, p.z, r3); X25519Field.Mul(r3, q.z, r3); X25519Field.Apm(y, x, array4, array); X25519Field.Apm(r3, r2, array3, array2); X25519Field.Mul(array, array4, r.t); X25519Field.Mul(array2, array3, r.z); X25519Field.Mul(array, array2, r.x); X25519Field.Mul(array4, array3, r.y); } private static void PointAdd(ref PointPrecomp p, ref PointAccum r, ref PointTemp t) { int[] x = r.x; int[] y = r.y; int[] r2 = t.r0; int[] u = r.u; int[] array = x; int[] array2 = y; int[] v = r.v; X25519Field.Apm(r.y, r.x, y, x); X25519Field.Mul(x, p.ymx_h, x); X25519Field.Mul(y, p.ypx_h, y); X25519Field.Mul(r.u, r.v, r2); X25519Field.Mul(r2, p.xyd, r2); X25519Field.Apm(y, x, v, u); X25519Field.Apm(r.z, r2, array2, array); X25519Field.Mul(array, array2, r.z); X25519Field.Mul(array, u, r.x); X25519Field.Mul(array2, v, r.y); } private static void PointAdd(ref PointPrecompZ p, ref PointAccum r, ref PointTemp t) { int[] x = r.x; int[] y = r.y; int[] r2 = t.r0; int[] z = r.z; int[] u = r.u; int[] array = x; int[] array2 = y; int[] v = r.v; X25519Field.Apm(r.y, r.x, y, x); X25519Field.Mul(x, p.ymx_h, x); X25519Field.Mul(y, p.ypx_h, y); X25519Field.Mul(r.u, r.v, r2); X25519Field.Mul(r2, p.xyd, r2); X25519Field.Mul(r.z, p.z, z); X25519Field.Apm(y, x, v, u); X25519Field.Apm(z, r2, array2, array); X25519Field.Mul(array, array2, r.z); X25519Field.Mul(array, u, r.x); X25519Field.Mul(array2, v, r.y); } private static void PointAddVar(bool negate, ref PointPrecomp p, ref PointAccum r, ref PointTemp t) { int[] x = r.x; int[] y = r.y; int[] r2 = t.r0; int[] u = r.u; int[] x2 = x; int[] array = y; int[] v = r.v; int[] array2; int[] array3; if (negate) { array2 = y; array3 = x; } else { array2 = x; array3 = y; } int[] zm = array2; int[] zp = array3; X25519Field.Apm(r.y, r.x, y, x); X25519Field.Mul(array2, p.ymx_h, array2); X25519Field.Mul(array3, p.ypx_h, array3); X25519Field.Mul(r.u, r.v, r2); X25519Field.Mul(r2, p.xyd, r2); X25519Field.Apm(y, x, v, u); X25519Field.Apm(r.z, r2, zp, zm); X25519Field.Mul(x2, array, r.z); X25519Field.Mul(x2, u, r.x); X25519Field.Mul(array, v, r.y); } private static void PointAddVar(bool negate, ref PointPrecompZ p, ref PointAccum r, ref PointTemp t) { int[] x = r.x; int[] y = r.y; int[] r2 = t.r0; int[] z = r.z; int[] u = r.u; int[] x2 = x; int[] array = y; int[] v = r.v; int[] array2; int[] array3; if (negate) { array2 = y; array3 = x; } else { array2 = x; array3 = y; } int[] zm = array2; int[] zp = array3; X25519Field.Apm(r.y, r.x, y, x); X25519Field.Mul(array2, p.ymx_h, array2); X25519Field.Mul(array3, p.ypx_h, array3); X25519Field.Mul(r.u, r.v, r2); X25519Field.Mul(r2, p.xyd, r2); X25519Field.Mul(r.z, p.z, z); X25519Field.Apm(y, x, v, u); X25519Field.Apm(z, r2, zp, zm); X25519Field.Mul(x2, array, r.z); X25519Field.Mul(x2, u, r.x); X25519Field.Mul(array, v, r.y); } private static void PointCopy(ref PointAccum p, ref PointExtended r) { X25519Field.Copy(p.x, 0, r.x, 0); X25519Field.Copy(p.y, 0, r.y, 0); X25519Field.Copy(p.z, 0, r.z, 0); X25519Field.Mul(p.u, p.v, r.t); } private static void PointCopy(ref PointAffine p, ref PointExtended r) { X25519Field.Copy(p.x, 0, r.x, 0); X25519Field.Copy(p.y, 0, r.y, 0); X25519Field.One(r.z); X25519Field.Mul(p.x, p.y, r.t); } private static void PointCopy(ref PointExtended p, ref PointPrecompZ r) { X25519Field.Apm(p.y, p.x, r.ypx_h, r.ymx_h); X25519Field.Mul(p.t, C_d2, r.xyd); X25519Field.Add(p.z, p.z, r.z); } private static void PointDouble(ref PointAccum r) { int[] x = r.x; int[] y = r.y; int[] z = r.z; int[] u = r.u; int[] array = x; int[] array2 = y; int[] v = r.v; X25519Field.Add(r.x, r.y, u); X25519Field.Sqr(r.x, x); X25519Field.Sqr(r.y, y); X25519Field.Sqr(r.z, z); X25519Field.Add(z, z, z); X25519Field.Apm(x, y, v, array2); X25519Field.Sqr(u, u); X25519Field.Sub(v, u, u); X25519Field.Add(z, array2, array); X25519Field.Carry(array); X25519Field.Mul(array, array2, r.z); X25519Field.Mul(array, u, r.x); X25519Field.Mul(array2, v, r.y); } private static void PointLookup(int block, int index, ref PointPrecomp p) { int num = block * 8 * 3 * 10; for (int i = 0; i < 8; i++) { int cond = (i ^ index) - 1 >> 31; X25519Field.CMov(cond, PrecompBaseComb, num, p.ymx_h, 0); num += 10; X25519Field.CMov(cond, PrecompBaseComb, num, p.ypx_h, 0); num += 10; X25519Field.CMov(cond, PrecompBaseComb, num, p.xyd, 0); num += 10; } } private static void PointLookupZ(ReadOnlySpan<uint> x, int n, ReadOnlySpan<int> table, ref PointPrecompZ r) { uint window = GetWindow4(x, n); int num = (int)((window >> 3) ^ 1); int num2 = ((int)window ^ -num) & 7; for (int i = 0; i < 8; i++) { int cond = (i ^ num2) - 1 >> 31; X25519Field.CMov(cond, table, r.ymx_h); table = table.Slice(10, table.Length - 10); X25519Field.CMov(cond, table, r.ypx_h); table = table.Slice(10, table.Length - 10); X25519Field.CMov(cond, table, r.xyd); table = table.Slice(10, table.Length - 10); X25519Field.CMov(cond, table, r.z); table = table.Slice(10, table.Length - 10); } X25519Field.CSwap(num, r.ymx_h, r.ypx_h); X25519Field.CNegate(num, r.xyd); } private static void PointPrecompute(ref PointAffine p, PointExtended[] points, int pointsOff, int pointsLen, ref PointTemp t) { Init(out points[pointsOff]); PointCopy(ref p, ref points[pointsOff]); Init(out PointExtended r); PointAdd(ref points[pointsOff], ref points[pointsOff], ref r, ref t); for (int i = 1; i < pointsLen; i++) { Init(out points[pointsOff + i]); PointAdd(ref points[pointsOff + i - 1], ref r, ref points[pointsOff + i], ref t); } } private static int[] PointPrecomputeZ(ref PointAffine p, int count, ref PointTemp t) { Init(out PointExtended r); PointCopy(ref p, ref r); Init(out PointExtended r2); PointAdd(ref r, ref r, ref r2, ref t); Init(out PointPrecompZ r3); int[] array = X25519Field.CreateTable(count * 4); int num = 0; int num2 = 0; while (true) { PointCopy(ref r, ref r3); X25519Field.Copy(r3.ymx_h, 0, array, num); num += 10; X25519Field.Copy(r3.ypx_h, 0, array, num); num += 10; X25519Field.Copy(r3.xyd, 0, array, num); num += 10; X25519Field.Copy(r3.z, 0, array, num); num += 10; if (++num2 == count) break; PointAdd(ref r, ref r2, ref r, ref t); } return array; } private static void PointPrecomputeZ(ref PointAffine p, PointPrecompZ[] points, int count, ref PointTemp t) { Init(out PointExtended r); PointCopy(ref p, ref r); Init(out PointExtended r2); PointAdd(ref r, ref r, ref r2, ref t); int num = 0; while (true) { ref PointPrecompZ r3 = ref points[num]; Init(out r3); PointCopy(ref r, ref r3); if (++num == count) break; PointAdd(ref r, ref r2, ref r, ref t); } } private static void PointSetNeutral(ref PointAccum p) { X25519Field.Zero(p.x); X25519Field.One(p.y); X25519Field.One(p.z); X25519Field.Zero(p.u); X25519Field.One(p.v); } public static void Precompute() { lock (PrecompLock) { if (PrecompBaseComb == null) { int num = 16; int num2 = 64; int num3 = num * 2 + num2; PointExtended[] array = new PointExtended[num3]; Init(out PointTemp r); Init(out PointAffine r2); X25519Field.Copy(B_x, 0, r2.x, 0); X25519Field.Copy(B_y, 0, r2.y, 0); PointPrecompute(ref r2, array, 0, num, ref r); Init(out PointAffine r3); X25519Field.Copy(B128_x, 0, r3.x, 0); X25519Field.Copy(B128_y, 0, r3.y, 0); PointPrecompute(ref r3, array, num, num, ref r); Init(out PointAccum r4); X25519Field.Copy(B_x, 0, r4.x, 0); X25519Field.Copy(B_y, 0, r4.y, 0); X25519Field.One(r4.z); X25519Field.Copy(B_x, 0, r4.u, 0); X25519Field.Copy(B_y, 0, r4.v, 0); int num4 = num * 2; PointExtended[] array2 = new PointExtended[4]; for (int i = 0; i < 4; i++) { Init(out array2[i]); } Init(out PointExtended r5); for (int j = 0; j < 8; j++) { ref PointExtended reference = ref array[num4++]; Init(out reference); for (int k = 0; k < 4; k++) { if (k == 0) PointCopy(ref r4, ref reference); else { PointCopy(ref r4, ref r5); PointAdd(ref reference, ref r5, ref reference, ref r); } PointDouble(ref r4); PointCopy(ref r4, ref array2[k]); if (j + k != 10) { for (int l = 1; l < 8; l++) { PointDouble(ref r4); } } } X25519Field.Negate(reference.x, reference.x); X25519Field.Negate(reference.t, reference.t); for (int m = 0; m < 3; m++) { int num6 = 1 << m; int num7 = 0; while (num7 < num6) { Init(out array[num4]); PointAdd(ref array[num4 - num6], ref array2[m], ref array[num4], ref r); num7++; num4++; } } } InvertDoubleZs(array); PrecompBaseWnaf = new PointPrecomp[num]; for (int n = 0; n < num; n++) { ref PointExtended reference2 = ref array[n]; ref PointPrecomp reference3 = ref PrecompBaseWnaf[n]; Init(out reference3); X25519Field.Mul(reference2.x, reference2.z, reference2.x); X25519Field.Mul(reference2.y, reference2.z, reference2.y); X25519Field.Apm(reference2.y, reference2.x, reference3.ypx_h, reference3.ymx_h); X25519Field.Mul(reference2.x, reference2.y, reference3.xyd); X25519Field.Mul(reference3.xyd, C_d4, reference3.xyd); X25519Field.Normalize(reference3.ymx_h); X25519Field.Normalize(reference3.ypx_h); X25519Field.Normalize(reference3.xyd); } PrecompBase128Wnaf = new PointPrecomp[num]; for (int num8 = 0; num8 < num; num8++) { ref PointExtended reference4 = ref array[num + num8]; ref PointPrecomp reference5 = ref PrecompBase128Wnaf[num8]; Init(out reference5); X25519Field.Mul(reference4.x, reference4.z, reference4.x); X25519Field.Mul(reference4.y, reference4.z, reference4.y); X25519Field.Apm(reference4.y, reference4.x, reference5.ypx_h, reference5.ymx_h); X25519Field.Mul(reference4.x, reference4.y, reference5.xyd); X25519Field.Mul(reference5.xyd, C_d4, reference5.xyd); X25519Field.Normalize(reference5.ymx_h); X25519Field.Normalize(reference5.ypx_h); X25519Field.Normalize(reference5.xyd); } PrecompBaseComb = X25519Field.CreateTable(num2 * 3); Init(out PointPrecomp r6); int num9 = 0; for (int num10 = num * 2; num10 < num3; num10++) { ref PointExtended reference6 = ref array[num10]; X25519Field.Mul(reference6.x, reference6.z, reference6.x); X25519Field.Mul(reference6.y, reference6.z, reference6.y); X25519Field.Apm(reference6.y, reference6.x, r6.ypx_h, r6.ymx_h); X25519Field.Mul(reference6.x, reference6.y, r6.xyd); X25519Field.Mul(r6.xyd, C_d4, r6.xyd); X25519Field.Normalize(r6.ymx_h); X25519Field.Normalize(r6.ypx_h); X25519Field.Normalize(r6.xyd); X25519Field.Copy(r6.ymx_h, 0, PrecompBaseComb, num9); num9 += 10; X25519Field.Copy(r6.ypx_h, 0, PrecompBaseComb, num9); num9 += 10; X25519Field.Copy(r6.xyd, 0, PrecompBaseComb, num9); num9 += 10; } } } } private static void PruneScalar(byte[] n, int nOff, byte[] r) { Array.Copy(n, nOff, r, 0, 32); r[0] &= 248; r[31] &= 127; r[31] |= 64; } private static void PruneScalar(ReadOnlySpan<byte> n, Span<byte> r) { n.Slice(0, 32).CopyTo(r); r[0] &= 248; r[31] &= 127; r[31] |= 64; } private unsafe static void ScalarMult(ReadOnlySpan<byte> k, ref PointAffine p, ref PointAccum r) { Span<uint> span = new Span<uint>(stackalloc byte[32], 8); Scalar25519.Decode(k, span); Scalar25519.ToSignedDigits(256, span); Init(out PointPrecompZ r2); Init(out PointTemp r3); int[] array = PointPrecomputeZ(ref p, 8, ref r3); PointSetNeutral(ref r); int num = 63; while (true) { PointLookupZ(span, num, array, ref r2); PointAdd(ref r2, ref r, ref r3); if (--num < 0) break; for (int i = 0; i < 4; i++) { PointDouble(ref r); } } } private unsafe static void ScalarMultBase(ReadOnlySpan<byte> k, ref PointAccum r) { Precompute(); Span<uint> span = new Span<uint>(stackalloc byte[32], 8); Scalar25519.Decode(k, span); Scalar25519.ToSignedDigits(256, span); GroupCombBits(span); Init(out PointPrecomp r2); Init(out PointTemp r3); PointSetNeutral(ref r); int num = 0; int num2 = 28; while (true) { for (int i = 0; i < 8; i++) { uint num3 = span[i] >> num2; int num4 = (int)((num3 >> 3) & 1); int index = ((int)num3 ^ -num4) & 7; PointLookup(i, index, ref r2); X25519Field.CNegate(num ^ num4, r.x); X25519Field.CNegate(num ^ num4, r.u); num = num4; PointAdd(ref r2, ref r, ref r3); } if ((num2 -= 4) < 0) break; PointDouble(ref r); } X25519Field.CNegate(num, r.x); X25519Field.CNegate(num, r.u); } private static void ScalarMultBaseEncoded(byte[] k, byte[] r, int rOff) { ScalarMultBaseEncoded(k.AsSpan(), r.AsSpan(rOff)); } private static void ScalarMultBaseEncoded(ReadOnlySpan<byte> k, Span<byte> r) { Init(out PointAccum r2); ScalarMultBase(k, ref r2); if (EncodeResult(ref r2, r) == 0) throw new InvalidOperationException(); } internal static void ScalarMultBaseYZ(byte[] k, int kOff, int[] y, int[] z) { ScalarMultBaseYZ(k.AsSpan(kOff), y.AsSpan(), z.AsSpan()); } internal unsafe static void ScalarMultBaseYZ(ReadOnlySpan<byte> k, Span<int> y, Span<int> z) { Span<byte> span = new Span<byte>(stackalloc byte[32], 32); PruneScalar(k, span); Init(out PointAccum r); ScalarMultBase(span, ref r); if (CheckPoint(r) == 0) throw new InvalidOperationException(); X25519Field.Copy(r.y, y); X25519Field.Copy(r.z, z); } private unsafe static void ScalarMultOrderVar(ref PointAffine p, ref PointAccum r) { Span<sbyte> ws = new Span<sbyte>(stackalloc byte[253], 253); Scalar25519.GetOrderWnafVar(4, ws); int num = 4; PointPrecompZ[] array = new PointPrecompZ[num]; Init(out PointTemp r2); PointPrecomputeZ(ref p, array, num, ref r2); PointSetNeutral(ref r); int num2 = 252; while (true) { int num3 = ws[num2]; if (num3 != 0) { int num4 = (num3 >> 1) ^ (num3 >> 31); PointAddVar(num3 < 0, ref array[num4], ref r, ref r2); } if (--num2 < 0) break; PointDouble(ref r); } } private unsafe static void ScalarMultStraus128Var(ReadOnlySpan<uint> nb, ReadOnlySpan<uint> np, ref PointAffine p, ReadOnlySpan<uint> nq, ref PointAffine q, ref PointAccum r) { Precompute(); Span<sbyte> ws = new Span<sbyte>(stackalloc byte[256], 256); Span<sbyte> ws2 = new Span<sbyte>(stackalloc byte[128], 128); Span<sbyte> ws3 = new Span<sbyte>(stackalloc byte[128], 128); Wnaf.GetSignedVar(nb, 6, ws); Wnaf.GetSignedVar(np, 4, ws2); Wnaf.GetSignedVar(nq, 4, ws3); int num = 4; PointPrecompZ[] array = new PointPrecompZ[num]; PointPrecompZ[] array2 = new PointPrecompZ[num]; Init(out PointTemp r2); PointPrecomputeZ(ref p, array, num, ref r2); PointPrecomputeZ(ref q, array2, num, ref r2); PointSetNeutral(ref r); int num2 = 128; while (--num2 >= 0 && (ws[num2] | ws[128 + num2] | ws2[num2] | ws3[num2]) == 0) { } while (num2 >= 0) { int num3 = ws[num2]; if (num3 != 0) { int num4 = (num3 >> 1) ^ (num3 >> 31); PointAddVar(num3 < 0, ref PrecompBaseWnaf[num4], ref r, ref r2); } int num5 = ws[128 + num2]; if (num5 != 0) { int num6 = (num5 >> 1) ^ (num5 >> 31); PointAddVar(num5 < 0, ref PrecompBase128Wnaf[num6], ref r, ref r2); } int num7 = ws2[num2]; if (num7 != 0) { int num8 = (num7 >> 1) ^ (num7 >> 31); PointAddVar(num7 < 0, ref array[num8], ref r, ref r2); } int num9 = ws3[num2]; if (num9 != 0) { int num10 = (num9 >> 1) ^ (num9 >> 31); PointAddVar(num9 < 0, ref array2[num10], ref r, ref r2); } PointDouble(ref r); num2--; } PointDouble(ref r); PointDouble(ref r); } public static void Sign(byte[] sk, int skOff, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { byte[] ctx = null; byte phflag = 0; ImplSign(sk, skOff, ctx, phflag, m, mOff, mLen, sig, sigOff); } public static void Sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { byte[] ctx = null; byte phflag = 0; ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff); } public static void Sign(byte[] sk, int skOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { byte phflag = 0; ImplSign(sk, skOff, ctx, phflag, m, mOff, mLen, sig, sigOff); } public static void Sign(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { byte phflag = 0; ImplSign(sk, skOff, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff); } public static void Sign(ReadOnlySpan<byte> sk, ReadOnlySpan<byte> m, Span<byte> sig) { if (sk.Length != SecretKeySize) throw new ArgumentException("sk"); if (sig.Length != SignatureSize) throw new ArgumentException("sig"); ImplSign(sk, null, 0, m, sig); } public static void Sign(ReadOnlySpan<byte> sk, ReadOnlySpan<byte> pk, ReadOnlySpan<byte> m, Span<byte> sig) { if (sk.Length != SecretKeySize) throw new ArgumentException("sk"); if (pk.Length != PublicKeySize) throw new ArgumentException("pk"); if (sig.Length != SignatureSize) throw new ArgumentException("sig"); ImplSign(sk, pk, null, 0, m, sig); } public static void Sign(ReadOnlySpan<byte> sk, byte[] ctx, ReadOnlySpan<byte> m, Span<byte> sig) { if (sk.Length != SecretKeySize) throw new ArgumentException("sk"); if (sig.Length != SignatureSize) throw new ArgumentException("sig"); ImplSign(sk, ctx, 0, m, sig); } public static void Sign(ReadOnlySpan<byte> sk, ReadOnlySpan<byte> pk, byte[] ctx, ReadOnlySpan<byte> m, Span<byte> sig) { if (sk.Length != SecretKeySize) throw new ArgumentException("sk"); if (pk.Length != PublicKeySize) throw new ArgumentException("pk"); if (sig.Length != SignatureSize) throw new ArgumentException("sig"); ImplSign(sk, pk, ctx, 0, m, sig); } public static void SignPrehash(byte[] sk, int skOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff) { byte phflag = 1; ImplSign(sk, skOff, ctx, phflag, ph, phOff, PrehashSize, sig, sigOff); } public static void SignPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff, byte[] sig, int sigOff) { byte phflag = 1; ImplSign(sk, skOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize, sig, sigOff); } public static void SignPrehash(byte[] sk, int skOff, byte[] ctx, IDigest ph, byte[] sig, int sigOff) { byte[] array = new byte[PrehashSize]; if (PrehashSize != ph.DoFinal(array, 0)) throw new ArgumentException("ph"); byte phflag = 1; ImplSign(sk, skOff, ctx, phflag, array, 0, array.Length, sig, sigOff); } public static void SignPrehash(byte[] sk, int skOff, byte[] pk, int pkOff, byte[] ctx, IDigest ph, byte[] sig, int sigOff) { byte[] array = new byte[PrehashSize]; if (PrehashSize != ph.DoFinal(array, 0)) throw new ArgumentException("ph"); byte phflag = 1; ImplSign(sk, skOff, pk, pkOff, ctx, phflag, array, 0, array.Length, sig, sigOff); } public static void SignPrehash(ReadOnlySpan<byte> sk, byte[] ctx, ReadOnlySpan<byte> ph, Span<byte> sig) { if (sk.Length != SecretKeySize) throw new ArgumentException("sk"); if (ph.Length != PrehashSize) throw new ArgumentException("ph"); if (sig.Length != SignatureSize) throw new ArgumentException("sig"); ImplSign(sk, ctx, 1, ph, sig); } public static void SignPrehash(ReadOnlySpan<byte> sk, ReadOnlySpan<byte> pk, byte[] ctx, ReadOnlySpan<byte> ph, Span<byte> sig) { if (sk.Length != SecretKeySize) throw new ArgumentException("sk"); if (pk.Length != PublicKeySize) throw new ArgumentException("pk"); if (ph.Length != PrehashSize) throw new ArgumentException("ph"); if (sig.Length != SignatureSize) throw new ArgumentException("sig"); ImplSign(sk, pk, ctx, 1, ph, sig); } public unsafe static void SignPrehash(ReadOnlySpan<byte> sk, byte[] ctx, IDigest ph, Span<byte> sig) { if (sk.Length != SecretKeySize) throw new ArgumentException("sk"); if (sig.Length != SignatureSize) throw new ArgumentException("sig"); int prehashSize = PrehashSize; Span<byte> span = new Span<byte>(stackalloc byte[(int)(uint)prehashSize], prehashSize); if (PrehashSize != ph.DoFinal(span)) throw new ArgumentException("ph"); ImplSign(sk, ctx, 1, span, sig); } public unsafe static void SignPrehash(ReadOnlySpan<byte> sk, ReadOnlySpan<byte> pk, byte[] ctx, IDigest ph, Span<byte> sig) { if (sk.Length != SecretKeySize) throw new ArgumentException("sk"); if (pk.Length != PublicKeySize) throw new ArgumentException("pk"); if (sig.Length != SignatureSize) throw new ArgumentException("sig"); int prehashSize = PrehashSize; Span<byte> span = new Span<byte>(stackalloc byte[(int)(uint)prehashSize], prehashSize); if (PrehashSize != ph.DoFinal(span)) throw new ArgumentException("ph"); ImplSign(sk, pk, ctx, 1, span, sig); } public static bool ValidatePublicKeyFull(byte[] pk, int pkOff) { return ValidatePublicKeyFull(pk.AsSpan(pkOff)); } public unsafe static bool ValidatePublicKeyFull(ReadOnlySpan<byte> pk) { int publicKeySize = PublicKeySize; Span<byte> span = new Span<byte>(stackalloc byte[(int)(uint)publicKeySize], publicKeySize); span.CopyFrom(pk); if (!CheckPointFullVar(span)) return false; Init(out PointAffine r); if (!DecodePointVar(span, false, ref r)) return false; return CheckPointOrderVar(ref r); } public static PublicPoint ValidatePublicKeyFullExport(byte[] pk, int pkOff) { return ValidatePublicKeyFullExport(pk.AsSpan(pkOff)); } public unsafe static PublicPoint ValidatePublicKeyFullExport(ReadOnlySpan<byte> pk) { int publicKeySize = PublicKeySize; Span<byte> span = new Span<byte>(stackalloc byte[(int)(uint)publicKeySize], publicKeySize); span.CopyFrom(pk); if (!CheckPointFullVar(span)) return null; Init(out PointAffine r); if (!DecodePointVar(span, false, ref r)) return null; if (!CheckPointOrderVar(ref r)) return null; return ExportPoint(ref r); } public static bool ValidatePublicKeyPartial(byte[] pk, int pkOff) { return ValidatePublicKeyPartial(pk.AsSpan(pkOff)); } public unsafe static bool ValidatePublicKeyPartial(ReadOnlySpan<byte> pk) { int publicKeySize = PublicKeySize; Span<byte> span = new Span<byte>(stackalloc byte[(int)(uint)publicKeySize], publicKeySize); span.CopyFrom(pk); if (!CheckPointFullVar(span)) return false; Init(out PointAffine r); return DecodePointVar(span, false, ref r); } public static PublicPoint ValidatePublicKeyPartialExport(byte[] pk, int pkOff) { return ValidatePublicKeyPartialExport(pk.AsSpan(pkOff)); } public unsafe static PublicPoint ValidatePublicKeyPartialExport(ReadOnlySpan<byte> pk) { int publicKeySize = PublicKeySize; Span<byte> span = new Span<byte>(stackalloc byte[(int)(uint)publicKeySize], publicKeySize); span.CopyFrom(pk); if (!CheckPointFullVar(span)) return null; Init(out PointAffine r); if (!DecodePointVar(span, false, ref r)) return null; return ExportPoint(ref r); } public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen) { byte[] ctx = null; byte phflag = 0; return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen); } public static bool Verify(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] m, int mOff, int mLen) { byte[] ctx = null; byte phflag = 0; return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, m, mOff, mLen); } public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen) { byte phflag = 0; return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen); } public static bool Verify(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, byte[] m, int mOff, int mLen) { byte phflag = 0; return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, m, mOff, mLen); } public static bool Verify(ReadOnlySpan<byte> sig, ReadOnlySpan<byte> pk, ReadOnlySpan<byte> m) { if (sig.Length != SignatureSize) throw new ArgumentException("sig"); if (pk.Length != PublicKeySize) throw new ArgumentException("pk"); return ImplVerify(sig, pk, null, 0, m); } public static bool Verify(ReadOnlySpan<byte> sig, PublicPoint publicPoint, ReadOnlySpan<byte> m) { if (sig.Length != SignatureSize) throw new ArgumentException("sig"); return ImplVerify(sig, publicPoint, null, 0, m); } public static bool Verify(ReadOnlySpan<byte> sig, ReadOnlySpan<byte> pk, byte[] ctx, ReadOnlySpan<byte> m) { if (sig.Length != SignatureSize) throw new ArgumentException("sig"); if (pk.Length != PublicKeySize) throw new ArgumentException("pk"); return ImplVerify(sig, pk, ctx, 0, m); } public static bool Verify(ReadOnlySpan<byte> sig, PublicPoint publicPoint, byte[] ctx, ReadOnlySpan<byte> m) { if (sig.Length != SignatureSize) throw new ArgumentException("sig"); return ImplVerify(sig, publicPoint, ctx, 0, m); } public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff) { byte phflag = 1; return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize); } public static bool VerifyPrehash(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, byte[] ph, int phOff) { byte phflag = 1; return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, ph, phOff, PrehashSize); } public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, IDigest ph) { byte[] array = new byte[PrehashSize]; if (PrehashSize != ph.DoFinal(array, 0)) throw new ArgumentException("ph"); byte phflag = 1; return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, array, 0, array.Length); } public static bool VerifyPrehash(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, IDigest ph) { byte[] array = new byte[PrehashSize]; if (PrehashSize != ph.DoFinal(array, 0)) throw new ArgumentException("ph"); byte phflag = 1; return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, array, 0, array.Length); } public static bool VerifyPrehash(ReadOnlySpan<byte> sig, ReadOnlySpan<byte> pk, byte[] ctx, ReadOnlySpan<byte> ph) { if (sig.Length != SignatureSize) throw new ArgumentException("sig"); if (pk.Length != PublicKeySize) throw new ArgumentException("pk"); if (ph.Length != PrehashSize) throw new ArgumentException("ph"); return ImplVerify(sig, pk, ctx, 1, ph); } public static bool VerifyPrehash(ReadOnlySpan<byte> sig, PublicPoint publicPoint, byte[] ctx, ReadOnlySpan<byte> ph) { if (sig.Length != SignatureSize) throw new ArgumentException("sig"); if (ph.Length != PrehashSize) throw new ArgumentException("ph"); return ImplVerify(sig, publicPoint, ctx, 1, ph); } public unsafe static bool VerifyPrehash(ReadOnlySpan<byte> sig, ReadOnlySpan<byte> pk, byte[] ctx, IDigest ph) { if (sig.Length != SignatureSize) throw new ArgumentException("sig"); if (pk.Length != PublicKeySize) throw new ArgumentException("pk"); int prehashSize = PrehashSize; Span<byte> span = new Span<byte>(stackalloc byte[(int)(uint)prehashSize], prehashSize); if (PrehashSize != ph.DoFinal(span)) throw new ArgumentException("ph"); return ImplVerify(sig, pk, ctx, 1, span); } public unsafe static bool VerifyPrehash(ReadOnlySpan<byte> sig, PublicPoint publicPoint, byte[] ctx, IDigest ph) { if (sig.Length != SignatureSize) throw new ArgumentException("sig"); int prehashSize = PrehashSize; Span<byte> span = new Span<byte>(stackalloc byte[(int)(uint)prehashSize], prehashSize); if (PrehashSize != ph.DoFinal(span)) throw new ArgumentException("ph"); return ImplVerify(sig, publicPoint, ctx, 1, span); } } }