diff --git a/MLAPI/MLAPI.csproj b/MLAPI/MLAPI.csproj index 757c7d5..446cc8e 100644 --- a/MLAPI/MLAPI.csproj +++ b/MLAPI/MLAPI.csproj @@ -44,6 +44,9 @@ Auto + + ..\packages\IntX.1.0.1.0\lib\net20\IntXLib.dll + @@ -79,5 +82,8 @@ + + + \ No newline at end of file diff --git a/MLAPI/NetworkingManagerComponents/DiffieHellman.cs b/MLAPI/NetworkingManagerComponents/DiffieHellman.cs new file mode 100644 index 0000000..2e675b5 --- /dev/null +++ b/MLAPI/NetworkingManagerComponents/DiffieHellman.cs @@ -0,0 +1,104 @@ +using System; +using IntXLib; +using System.Text; +using System.Security.Cryptography; + +namespace ECDH +{ + public class EllipticDiffieHellman + { + protected static readonly RNGCryptoServiceProvider rand = new RNGCryptoServiceProvider(); + public static readonly IntX DEFAULT_PRIME = (new IntX(1) << 255) - 19; + public static readonly IntX DEFAULT_ORDER = (new IntX(1) << 252) + IntX.Parse("27742317777372353535851937790883648493"); + public static readonly EllipticCurve DEFAULT_CURVE = new EllipticCurve(486662, 1, DEFAULT_PRIME, EllipticCurve.CurveType.Montgomery); + public static readonly CurvePoint DEFAULT_GENERATOR = new CurvePoint(9, IntX.Parse("14781619447589544791020593568409986887264606134616475288964881837755586237401")); + + protected readonly EllipticCurve curve; + public readonly IntX priv; + protected readonly CurvePoint generator, pub; + + + public EllipticDiffieHellman(EllipticCurve curve, CurvePoint generator, IntX order, byte[] priv = null) + { + this.curve = curve; + this.generator = generator; + + // Generate private key + if (priv == null) + { + byte[] max = order.ToArray(); + do + { + byte[] p1 = new byte[5 /*rand.Next(max.Length) + 1*/]; + + rand.GetBytes(p1); + + if (p1.Length == max.Length) p1[p1.Length - 1] %= max[max.Length - 1]; + else p1[p1.Length - 1] &= 127; + + this.priv = DHHelper.FromArray(p1); + } while (this.priv<2); + } + else this.priv = DHHelper.FromArray(priv); + + // Generate public key + pub = curve.Multiply(generator, this.priv); + } + + public byte[] GetPublicKey() + { + byte[] p1 = pub.X.ToArray(); + byte[] p2 = pub.Y.ToArray(); + + byte[] ser = new byte[4 + p1.Length + p2.Length]; + ser[0] = (byte)(p1.Length & 255); + ser[1] = (byte)((p1.Length >> 8) & 255); + ser[2] = (byte)((p1.Length >> 16) & 255); + ser[3] = (byte)((p1.Length >> 24) & 255); + Array.Copy(p1, 0, ser, 4, p1.Length); + Array.Copy(p2, 0, ser, 4 + p1.Length, p2.Length); + + return ser; + } + + public byte[] GetPrivateKey() => priv.ToArray(); + + public byte[] GetSharedSecret(byte[] pK) + { + byte[] p1 = new byte[pK[0] | (pK[1]<<8) | (pK[2]<<16) | (pK[3]<<24)]; // Reconstruct x-axis size + byte[] p2 = new byte[pK.Length - p1.Length - 4]; + Array.Copy(pK, 4, p1, 0, p1.Length); + Array.Copy(pK, 4 + p1.Length, p2, 0, p2.Length); + + CurvePoint remotePublic = new CurvePoint(DHHelper.FromArray(p1), DHHelper.FromArray(p2)); + + byte[] secret = curve.Multiply(remotePublic, priv).X.ToArray(); // Use the x-coordinate as the shared secret + + // PBKDF2-HMAC-SHA1 (Common shared secret generation method) + return new Rfc2898DeriveBytes(secret, Encoding.UTF8.GetBytes("P1sN0R4inb0wPl5P1sPls"), 1000).GetBytes(32); + } + } + + public static class DHHelper + { + public static byte[] ToArray(this IntX v) + { + v.GetInternalState(out uint[] digits, out bool negative); + byte[] b = DigitConverter.ToBytes(digits); + byte[] b1 = new byte[b.Length]; + Array.Copy(b, b1, b.Length); + b1[b.Length] = (byte)(negative ? 1 : 0); + return b1; + } + public static IntX FromArray(byte[] b) + { + if (b.Length == 0) return new IntX(); + byte[] b1 = new byte[b.Length - 1]; + Array.Copy(b, b1, b1.Length); + uint[] u = DigitConverter.FromBytes(b1); + return new IntX(u, b[b.Length - 1]==1); + } + public static bool BitAt(this uint[] data, long index) => (data[index/8]&(1<<(int)(index%8)))!=0; + public static IntX Abs(this IntX i) => i < 0 ? -i : i; + } +} diff --git a/MLAPI/NetworkingManagerComponents/EllipticCurve.cs b/MLAPI/NetworkingManagerComponents/EllipticCurve.cs new file mode 100644 index 0000000..3a37efe --- /dev/null +++ b/MLAPI/NetworkingManagerComponents/EllipticCurve.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using IntXLib; + +namespace ECDH +{ + public class CurvePoint + { + public static readonly CurvePoint POINT_AT_INFINITY = new CurvePoint(); + public IntX X { get; private set; } + public IntX Y { get; private set; } + private bool pai = false; + public CurvePoint(IntX x, IntX y) + { + X = x; + Y = y; + } + private CurvePoint() { pai = true; } // Accessing corrdinates causes undocumented behaviour + public override string ToString() + { + return pai ? "(POINT_AT_INFINITY)" : "(" + X + ", " + Y + ")"; + } + } + + public class EllipticCurve + { + public enum CurveType { Weierstrass, Montgomery } + + protected readonly IntX a, b, modulo; + protected readonly CurveType type; + + public EllipticCurve(IntX a, IntX b, IntX modulo, CurveType type = CurveType.Weierstrass) + { + if ( + (type==CurveType.Weierstrass && (4 * a * a * a) + (27 * b * b) == 0) || // Unfavourable Weierstrass curves + (type==CurveType.Montgomery && b * (a * a - 4)==0) // Unfavourable Montgomery curves + ) throw new Exception("Unfavourable curve"); + this.a = a; + this.b = b; + this.modulo = modulo; + this.type = type; + } + + public CurvePoint Add(CurvePoint p1, CurvePoint p2) + { +#if SAFE_MATH + CheckOnCurve(p1); + CheckOnCurve(p2); +#endif + + // Special cases + if (p1 == CurvePoint.POINT_AT_INFINITY && p2 == CurvePoint.POINT_AT_INFINITY) return CurvePoint.POINT_AT_INFINITY; + else if (p1 == CurvePoint.POINT_AT_INFINITY) return p2; + else if (p2 == CurvePoint.POINT_AT_INFINITY) return p1; + else if (p1.X == p2.X && p1.Y == Inverse(p2).Y) return CurvePoint.POINT_AT_INFINITY; + + IntX x3 = 0, y3 = 0; + if (type == CurveType.Weierstrass) + { + IntX slope = p1.X == p2.X && p1.Y == p2.Y ? Mod((3 * p1.X * p1.X + a) * MulInverse(2 * p1.Y)) : Mod(Mod(p2.Y - p1.Y) * MulInverse(p2.X - p1.X)); + x3 = Mod((slope * slope) - p1.X - p2.X); + y3 = Mod(-((slope * x3) + p1.Y - (slope * p1.X))); + } + else if (type == CurveType.Montgomery) + { + if ((p1.X == p2.X && p1.Y == p2.Y)) + { + IntX q = 3 * p1.X; + IntX w = q * p1.X; + + IntX e = 2 * a; + IntX r = e * p1.X; + + IntX t = 2 * b; + IntX y = t * p1.Y; + + IntX u = MulInverse(y); + + IntX o = w + e + 1; + IntX p = o * u; + } + IntX co = p1.X == p2.X && p1.Y == p2.Y ? Mod((3 * p1.X * p1.X + 2 * a * p1.X + 1) * MulInverse(2 * b * p1.Y)) : Mod(Mod(p2.Y - p1.Y) * MulInverse(p2.X - p1.X)); // Compute a commonly used coefficient + x3 = Mod(b * co * co - a - p1.X - p2.X); + y3 = Mod(((2 * p1.X + p2.X + a) * co) - (b * co * co * co) - p1.Y); + } + + return new CurvePoint(x3, y3); + } + + public CurvePoint Multiply(CurvePoint p, IntX scalar) + { + if (scalar <= 0) throw new Exception("Cannot multiply by a scalar which is <= 0"); + if (p == CurvePoint.POINT_AT_INFINITY) return CurvePoint.POINT_AT_INFINITY; + + CurvePoint p1 = new CurvePoint(p.X, p.Y); + scalar.GetInternalState(out uint[] u, out bool b); + long high_bit = -1; + for (int i = u.Length - 1; i>=0; --i) + if (u[i] != 0) + { + for(int j = 31; j>=0; --j) + if ((u[i] & (1<= 0) + { + p1 = Add(p1, p1); // Double + if ((u.BitAt(high_bit))) + p1 = Add(p1, p); // Add + --high_bit; + } + + return p1; + } + + protected IntX MulInverse(IntX eq) => MulInverse(eq, modulo); + public static IntX MulInverse(IntX eq, IntX modulo) + { + eq = Mod(eq, modulo); + Stack collect = new Stack(); + IntX v = modulo; // Copy modulo + IntX m; + while((m = v % eq) != 0) + { + collect.Push(-v/eq/*-(m.l_div)*/); + v = eq; + eq = m; + } + if (collect.Count == 0) return 1; + v = 1; + m = collect.Pop(); + while (collect.Count > 0) + { + eq = m; + m = v + (m * collect.Pop()); + v = eq; + } + return Mod(m, modulo); + } + + public CurvePoint Inverse(CurvePoint p) => Inverse(p, modulo); + protected static CurvePoint Inverse(CurvePoint p, IntX modulo) => new CurvePoint(p.X, Mod(-p.Y, modulo)); + + public bool IsOnCurve(CurvePoint p) + { + try { CheckOnCurve(p); } + catch { return false; } + return true; + } + protected void CheckOnCurve(CurvePoint p) + { + if ( + p!=CurvePoint.POINT_AT_INFINITY && // The point at infinity is asserted to be on the curve + (type == CurveType.Weierstrass && Mod(p.Y * p.Y) != Mod((p.X * p.X * p.X) + (p.X * a) + b)) || // Weierstrass formula + (type == CurveType.Montgomery && Mod(b * p.Y * p.Y) != Mod((p.X * p.X * p.X) + (p.X * p.X * a) + p.X)) // Montgomery formula + ) throw new Exception("Point is not on curve"); + } + + protected IntX Mod(IntX b) => Mod(b, modulo); + + private static IntX Mod(IntX x, IntX m) + { + IntX r = x.Abs() > m ? x % m : x; + return r < 0 ? r + m : r; + } + + protected static IntX ModPow(IntX x, IntX power, IntX prime) + { + IntX result = 1; + bool setBit = false; + while(power > 0) + { + x %= prime; + setBit = (power & 1) == 1; + power >>= 1; + if (setBit) result *= x; + x *= x; + } + + return result; + } + } +} diff --git a/MLAPI/packages.config b/MLAPI/packages.config new file mode 100644 index 0000000..f41bab7 --- /dev/null +++ b/MLAPI/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file