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

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 System; 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) { uint[] array = new uint[16]; Scalar25519.Decode(r, array); uint[] array2 = new uint[8]; Scalar25519.Decode(k, array2); uint[] array3 = new uint[8]; Scalar25519.Decode(s, array3); Nat256.MulAddTo(array2, array3, array); byte[] array4 = new byte[64]; Codec.Encode32(array, 0, array.Length, array4, 0); return Scalar25519.Reduce512(array4); } 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(byte[] p) { uint num; uint num2 = num = (Codec.Decode32(p, 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--) { uint num7 = Codec.Decode32(p, num6 * 4); num |= num7; num3 |= (num7 ^ P[num6]); num4 |= (num7 ^ Order8_y1[num6]); num5 |= (num7 ^ Order8_y2[num6]); } uint num8 = Codec.Decode32(p, 0); if (num == 0 && num8 <= 1) return false; if (num3 == 0 && num8 >= P[0] - 1) return false; num4 |= (num8 ^ Order8_y1[0]); num5 |= (num8 ^ 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(byte[] p) { if ((Codec.Decode32(p, 28) & 2147483647) < P[7]) return true; for (int num = 6; num >= 0; num--) { if (Codec.Decode32(p, num * 4) < 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(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 static void Dom2(IDigest d, byte phflag, byte[] ctx) { int num = Dom2Prefix.Length; byte[] array = new byte[num + 2 + ctx.Length]; Dom2Prefix.CopyTo(array, 0); array[num] = phflag; array[num + 1] = (byte)ctx.Length; ctx.CopyTo(array, num + 2); d.BlockUpdate(array, 0, array.Length); } 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); } 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); } private static int EncodeResult(ref PointAccum p, byte[] r, int rOff) { Init(out PointAffine r2); NormalizeToAffine(ref p, ref r2); int result = CheckPoint(ref r2); EncodePoint(ref r2, r, rOff); 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 GeneratePublicKey(byte[] sk, int skOff, byte[] pk, int pkOff) { IDigest digest = CreateDigest(); byte[] array = new byte[64]; digest.BlockUpdate(sk, skOff, SecretKeySize); digest.DoFinal(array, 0); byte[] array2 = new byte[32]; PruneScalar(array, 0, array2); ScalarMultBaseEncoded(array2, pk, pkOff); } public static PublicPoint GeneratePublicKey(byte[] sk, int skOff) { IDigest digest = CreateDigest(); byte[] array = new byte[64]; digest.BlockUpdate(sk, skOff, SecretKeySize); digest.DoFinal(array, 0); byte[] array2 = new byte[32]; PruneScalar(array, 0, array2); Init(out PointAccum r); ScalarMultBase(array2, 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(uint[] x, int n) { int num = (int)((uint)n >> 3); int num2 = (n & 7) << 2; return (x[num] >> num2) & 15; } private static void GroupCombBits(uint[] n) { for (int i = 0; i < n.Length; i++) { n[i] = Interleave.Shuffle2(n[i]); } } private static void ImplSign(IDigest d, byte[] h, byte[] s, byte[] pk, int pkOff, byte[] ctx, byte phflag, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { if (ctx != null) Dom2(d, phflag, ctx); d.BlockUpdate(h, 32, 32); d.BlockUpdate(m, mOff, mLen); d.DoFinal(h, 0); byte[] array = Scalar25519.Reduce512(h); byte[] array2 = new byte[32]; ScalarMultBaseEncoded(array, array2, 0); if (ctx != null) Dom2(d, phflag, ctx); d.BlockUpdate(array2, 0, 32); d.BlockUpdate(pk, pkOff, 32); d.BlockUpdate(m, mOff, mLen); d.DoFinal(h, 0); byte[] k = Scalar25519.Reduce512(h); byte[] sourceArray = CalculateS(array, k, s); Array.Copy(array2, 0, sig, sigOff, 32); Array.Copy(sourceArray, 0, sig, sigOff + 32, 32); } private static void ImplSign(byte[] sk, int skOff, byte[] ctx, byte phflag, byte[] m, int mOff, int mLen, byte[] sig, int sigOff) { if (!CheckContextVar(ctx, phflag)) throw new ArgumentException("ctx"); IDigest digest = CreateDigest(); byte[] array = new byte[64]; digest.BlockUpdate(sk, skOff, SecretKeySize); digest.DoFinal(array, 0); byte[] array2 = new byte[32]; PruneScalar(array, 0, array2); byte[] array3 = new byte[32]; ScalarMultBaseEncoded(array2, array3, 0); ImplSign(digest, array, array2, array3, 0, ctx, phflag, m, mOff, mLen, sig, sigOff); } 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) { if (!CheckContextVar(ctx, phflag)) throw new ArgumentException("ctx"); IDigest digest = CreateDigest(); byte[] array = new byte[64]; digest.BlockUpdate(sk, skOff, SecretKeySize); digest.DoFinal(array, 0); byte[] array2 = new byte[32]; PruneScalar(array, 0, array2); ImplSign(digest, array, array2, pk, pkOff, ctx, phflag, m, mOff, mLen, sig, sigOff); } private static bool ImplVerify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte phflag, byte[] m, int mOff, int mLen) { if (!CheckContextVar(ctx, phflag)) throw new ArgumentException("ctx"); byte[] array = Copy(sig, sigOff, 32); byte[] s = Copy(sig, sigOff + 32, 32); byte[] array2 = Copy(pk, pkOff, PublicKeySize); if (!CheckPointVar(array)) return false; uint[] array3 = new uint[8]; if (!Scalar25519.CheckVar(s, array3)) return false; if (!CheckPointFullVar(array2)) return false; Init(out PointAffine r); if (!DecodePointVar(array, true, ref r)) return false; Init(out PointAffine r2); if (!DecodePointVar(array2, true, ref r2)) return false; IDigest digest = CreateDigest(); byte[] array4 = new byte[64]; if (ctx != null) Dom2(digest, phflag, ctx); digest.BlockUpdate(array, 0, 32); digest.BlockUpdate(array2, 0, 32); digest.BlockUpdate(m, mOff, mLen); digest.DoFinal(array4, 0); byte[] k = Scalar25519.Reduce512(array4); uint[] array5 = new uint[8]; Scalar25519.Decode(k, array5); uint[] array6 = new uint[4]; uint[] array7 = new uint[4]; if (!Scalar25519.ReduceBasisVar(array5, array6, array7)) throw new InvalidOperationException(); Scalar25519.Multiply128Var(array3, array7, array3); Init(out PointAccum r3); ScalarMultStraus128Var(array3, array6, ref r2, array7, 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) { if (!CheckContextVar(ctx, phflag)) throw new ArgumentException("ctx"); byte[] array = Copy(sig, sigOff, 32); byte[] s = Copy(sig, sigOff + 32, 32); if (!CheckPointVar(array)) return false; uint[] array2 = new uint[8]; if (!Scalar25519.CheckVar(s, array2)) return false; Init(out PointAffine r); if (!DecodePointVar(array, true, ref r)) return false; Init(out PointAffine r2); X25519Field.Negate(publicPoint.m_data, r2.x); X25519Field.Copy(publicPoint.m_data, 10, r2.y, 0); byte[] array3 = new byte[PublicKeySize]; EncodePublicPoint(publicPoint, array3, 0); IDigest digest = CreateDigest(); byte[] array4 = new byte[64]; if (ctx != null) Dom2(digest, phflag, ctx); digest.BlockUpdate(array, 0, 32); digest.BlockUpdate(array3, 0, 32); digest.BlockUpdate(m, mOff, mLen); digest.DoFinal(array4, 0); byte[] k = Scalar25519.Reduce512(array4); uint[] array5 = new uint[8]; Scalar25519.Decode(k, array5); uint[] array6 = new uint[4]; uint[] array7 = new uint[4]; if (!Scalar25519.ReduceBasisVar(array5, array6, array7)) throw new InvalidOperationException(); Scalar25519.Multiply128Var(array2, array7, array2); Init(out PointAccum r3); ScalarMultStraus128Var(array2, array6, ref r2, array7, 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(uint[] x, int n, int[] table, ref PointPrecompZ r) { uint window = GetWindow4(x, n); int num = (int)((window >> 3) ^ 1); int num2 = ((int)window ^ -num) & 7; int i = 0; int num3 = 0; for (; i < 8; i++) { int cond = (i ^ num2) - 1 >> 31; X25519Field.CMov(cond, table, num3, r.ymx_h, 0); num3 += 10; X25519Field.CMov(cond, table, num3, r.ypx_h, 0); num3 += 10; X25519Field.CMov(cond, table, num3, r.xyd, 0); num3 += 10; X25519Field.CMov(cond, table, num3, r.z, 0); num3 += 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 ScalarMult(byte[] k, ref PointAffine p, ref PointAccum r) { uint[] array = new uint[8]; Scalar25519.Decode(k, array); Scalar25519.ToSignedDigits(256, array); Init(out PointPrecompZ r2); Init(out PointTemp r3); int[] table = PointPrecomputeZ(ref p, 8, ref r3); PointSetNeutral(ref r); int num = 63; while (true) { PointLookupZ(array, num, table, ref r2); PointAdd(ref r2, ref r, ref r3); if (--num < 0) break; for (int i = 0; i < 4; i++) { PointDouble(ref r); } } } private static void ScalarMultBase(byte[] k, ref PointAccum r) { Precompute(); uint[] array = new uint[8]; Scalar25519.Decode(k, array); Scalar25519.ToSignedDigits(256, array); GroupCombBits(array); 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 = array[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) { Init(out PointAccum r2); ScalarMultBase(k, ref r2); if (EncodeResult(ref r2, r, rOff) == 0) throw new InvalidOperationException(); } internal static void ScalarMultBaseYZ(byte[] k, int kOff, int[] y, int[] z) { byte[] array = new byte[32]; PruneScalar(k, kOff, array); Init(out PointAccum r); ScalarMultBase(array, ref r); if (CheckPoint(r) == 0) throw new InvalidOperationException(); X25519Field.Copy(r.y, 0, y, 0); X25519Field.Copy(r.z, 0, z, 0); } private static void ScalarMultOrderVar(ref PointAffine p, ref PointAccum r) { sbyte[] array = new sbyte[253]; Scalar25519.GetOrderWnafVar(4, array); int num = 4; PointPrecompZ[] array2 = new PointPrecompZ[num]; Init(out PointTemp r2); PointPrecomputeZ(ref p, array2, num, ref r2); PointSetNeutral(ref r); int num2 = 252; while (true) { int num3 = array[num2]; if (num3 != 0) { int num4 = (num3 >> 1) ^ (num3 >> 31); PointAddVar(num3 < 0, ref array2[num4], ref r, ref r2); } if (--num2 < 0) break; PointDouble(ref r); } } private static void ScalarMultStraus128Var(uint[] nb, uint[] np, ref PointAffine p, uint[] nq, ref PointAffine q, ref PointAccum r) { Precompute(); sbyte[] array = new sbyte[256]; sbyte[] array2 = new sbyte[128]; sbyte[] array3 = new sbyte[128]; Wnaf.GetSignedVar(nb, 6, array); Wnaf.GetSignedVar(np, 4, array2); Wnaf.GetSignedVar(nq, 4, array3); int num = 4; PointPrecompZ[] array4 = new PointPrecompZ[num]; PointPrecompZ[] array5 = new PointPrecompZ[num]; Init(out PointTemp r2); PointPrecomputeZ(ref p, array4, num, ref r2); PointPrecomputeZ(ref q, array5, num, ref r2); PointSetNeutral(ref r); int num2 = 128; while (--num2 >= 0 && (array[num2] | array[128 + num2] | array2[num2] | array3[num2]) == 0) { } while (num2 >= 0) { int num3 = array[num2]; if (num3 != 0) { int num4 = (num3 >> 1) ^ (num3 >> 31); PointAddVar(num3 < 0, ref PrecompBaseWnaf[num4], ref r, ref r2); } int num5 = array[128 + num2]; if (num5 != 0) { int num6 = (num5 >> 1) ^ (num5 >> 31); PointAddVar(num5 < 0, ref PrecompBase128Wnaf[num6], ref r, ref r2); } int num7 = array2[num2]; if (num7 != 0) { int num8 = (num7 >> 1) ^ (num7 >> 31); PointAddVar(num7 < 0, ref array4[num8], ref r, ref r2); } int num9 = array3[num2]; if (num9 != 0) { int num10 = (num9 >> 1) ^ (num9 >> 31); PointAddVar(num9 < 0, ref array5[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 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 bool ValidatePublicKeyFull(byte[] pk, int pkOff) { byte[] p = Copy(pk, pkOff, PublicKeySize); if (!CheckPointFullVar(p)) return false; Init(out PointAffine r); if (!DecodePointVar(p, false, ref r)) return false; return CheckPointOrderVar(ref r); } public static PublicPoint ValidatePublicKeyFullExport(byte[] pk, int pkOff) { byte[] p = Copy(pk, pkOff, PublicKeySize); if (!CheckPointFullVar(p)) return null; Init(out PointAffine r); if (!DecodePointVar(p, false, ref r)) return null; if (!CheckPointOrderVar(ref r)) return null; return ExportPoint(ref r); } public static bool ValidatePublicKeyPartial(byte[] pk, int pkOff) { byte[] p = Copy(pk, pkOff, PublicKeySize); if (!CheckPointFullVar(p)) return false; Init(out PointAffine r); return DecodePointVar(p, false, ref r); } public static PublicPoint ValidatePublicKeyPartialExport(byte[] pk, int pkOff) { byte[] p = Copy(pk, pkOff, PublicKeySize); if (!CheckPointFullVar(p)) return null; Init(out PointAffine r); if (!DecodePointVar(p, 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 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); } } }