From 1db4606aded7838fc1858591bbe9e5c235c83294 Mon Sep 17 00:00:00 2001 From: GabrielTofvesson Date: Thu, 29 Mar 2018 01:17:29 +0200 Subject: [PATCH 1/4] Added Diffie Hellman Added PBKDF2-HMAC-SHA1 Added IntX implementation --- MLAPI/MLAPI.csproj | 6 + .../DiffieHellman.cs | 263 ++++++++++++++++++ .../EllipticCurve.cs | 189 +++++++++++++ MLAPI/packages.config | 4 + 4 files changed, 462 insertions(+) create mode 100644 MLAPI/NetworkingManagerComponents/DiffieHellman.cs create mode 100644 MLAPI/NetworkingManagerComponents/EllipticCurve.cs create mode 100644 MLAPI/packages.config diff --git a/MLAPI/MLAPI.csproj b/MLAPI/MLAPI.csproj index 44c7b80..f0f9d50 100644 --- a/MLAPI/MLAPI.csproj +++ b/MLAPI/MLAPI.csproj @@ -44,6 +44,9 @@ Auto + + ..\packages\IntX.1.0.1.0\lib\net20\IntXLib.dll + @@ -78,5 +81,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..50e85a1 --- /dev/null +++ b/MLAPI/NetworkingManagerComponents/DiffieHellman.cs @@ -0,0 +1,263 @@ +using System; +using IntXLib; +using System.Text; + +namespace ECDH +{ + public class EllipticDiffieHellman + { + protected static readonly Random rand = new Random(); + + protected readonly EllipticCurve curve; + public readonly IntX priv; + protected readonly Point generator, pub; + + + public EllipticDiffieHellman(EllipticCurve curve, Point 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.NextBytes(p1); + + if (p1.Length == max.Length) p1[p1.Length - 1] %= max[max.Length - 1]; + else p1[p1.Length - 1] &= 127; + + this.priv = Helper.FromArray(p1); + } while (this.priv<2); + } + else this.priv = Helper.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); + + Point remotePublic = new Point(Helper.FromArray(p1), Helper.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 PBKDF2(HMAC_SHA1, secret, Encoding.UTF8.GetBytes("P1sN0R4inb0wPl5P1sPls"), 1024, 32); + } + + + public delegate byte[] PRF(byte[] key, byte[] salt); + private static byte[] PBKDF2(PRF function, byte[] password, byte[] salt, int iterations, int dklen) + { + byte[] dk = new byte[0]; // Create a placeholder for the derived key + uint iter = 1; // Track the iterations + while (dk.Length < dklen) + { + // F-function + // The F-function (PRF) takes the amount of iterations performed in the opposite endianness format from what C# uses, so we have to swap the endianness + byte[] u = function(password, Concatenate(salt, WriteToArray(new byte[4], SwapEndian(iter), 0))); + byte[] ures = new byte[u.Length]; + Array.Copy(u, ures, u.Length); + for (int i = 1; i < iterations; ++i) + { + // Iteratively apply the PRF + u = function(password, u); + for (int j = 0; j < u.Length; ++j) ures[j] ^= u[j]; + } + + // Concatenate the result to the dk + dk = Concatenate(dk, ures); + + ++iter; + } + + // Clip all bytes past what we needed (yes, that's really what the standard is) + if (dk.Length != dklen) + { + var t1 = new byte[dklen]; + Array.Copy(dk, t1, Math.Min(dklen, dk.Length)); + return t1; + } + return dk; + } + public delegate byte[] HashFunction(byte[] message); + private static byte[] HMAC(byte[] key, byte[] message, HashFunction func, int blockSizeBytes) + { + if (key.Length > blockSizeBytes) key = func(key); + else if (key.Length < blockSizeBytes) + { + byte[] b = new byte[blockSizeBytes]; + Array.Copy(key, b, key.Length); + key = b; + } + + byte[] o_key_pad = new byte[blockSizeBytes]; // Outer padding + byte[] i_key_pad = new byte[blockSizeBytes]; // Inner padding + for (int i = 0; i < blockSizeBytes; ++i) + { + // Combine padding with key + o_key_pad[i] = (byte)(key[i] ^ 0x5c); + i_key_pad[i] = (byte)(key[i] ^ 0x36); + } + return func(Concatenate(o_key_pad, func(Concatenate(message, i_key_pad)))); + } + private static byte[] HMAC_SHA1(byte[] key, byte[] message) => HMAC(key, message, SHA1, 20); + private static byte[] Concatenate(params byte[][] bytes) + { + int alloc = 0; + foreach (byte[] b in bytes) alloc += b.Length; + byte[] result = new byte[alloc]; + alloc = 0; + for (int i = 0; i < bytes.Length; ++i) + { + Array.Copy(bytes[i], 0, result, alloc, bytes[i].Length); + alloc += bytes[i].Length; + } + return result; + } + public static byte[] SHA1(byte[] message) + { + // Initialize buffers + uint h0 = 0x67452301; + uint h1 = 0xEFCDAB89; + uint h2 = 0x98BADCFE; + uint h3 = 0x10325476; + uint h4 = 0xC3D2E1F0; + + // Pad message + int ml = message.Length + 1; + byte[] msg = new byte[ml + ((960 - (ml * 8 % 512)) % 512) / 8 + 8]; + Array.Copy(message, msg, message.Length); + msg[message.Length] = 0x80; + long len = message.Length * 8; + for (int i = 0; i < 8; ++i) msg[msg.Length - 1 - i] = (byte)((len >> (i * 8)) & 255); + //Support.WriteToArray(msg, message.Length * 8, msg.Length - 8); + //for (int i = 0; i <4; ++i) msg[msg.Length - 5 - i] = (byte)(((message.Length*8) >> (i * 8)) & 255); + + int chunks = msg.Length / 64; + + // Perform hashing for each 512-bit block + for (int i = 0; i < chunks; ++i) + { + + // Split block into words + uint[] w = new uint[80]; + for (int j = 0; j < 16; ++j) + w[j] |= (uint)((msg[i * 64 + j * 4] << 24) | (msg[i * 64 + j * 4 + 1] << 16) | (msg[i * 64 + j * 4 + 2] << 8) | (msg[i * 64 + j * 4 + 3] << 0)); + + // Expand words + for (int j = 16; j < 80; ++j) + w[j] = Rot(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); + + // Initialize chunk-hash + uint + a = h0, + b = h1, + c = h2, + d = h3, + e = h4; + + // Do hash rounds + for (int t = 0; t < 80; ++t) + { + uint tmp = ((a << 5) | (a >> (27))) + + ( // Round-function + t < 20 ? (b & c) | ((~b) & d) : + t < 40 ? b ^ c ^ d : + t < 60 ? (b & c) | (b & d) | (c & d) : + /*t<80*/ b ^ c ^ d + ) + + e + + ( // K-function + t < 20 ? 0x5A827999 : + t < 40 ? 0x6ED9EBA1 : + t < 60 ? 0x8F1BBCDC : + /*t<80*/ 0xCA62C1D6 + ) + + w[t]; + e = d; + d = c; + c = Rot(b, 30); + b = a; + a = tmp; + } + h0 += a; + h1 += b; + h2 += c; + h3 += d; + h4 += e; + } + + return WriteContiguous(new byte[20], 0, SwapEndian(h0), SwapEndian(h1), SwapEndian(h2), SwapEndian(h3), SwapEndian(h4)); + } + + private static uint Rot(uint val, int by) => (val << by) | (val >> (32 - by)); + + // Swap endianness of a given integer + private static uint SwapEndian(uint value) => (uint)(((value >> 24) & (255 << 0)) | ((value >> 8) & (255 << 8)) | ((value << 8) & (255 << 16)) | ((value << 24) & (255 << 24))); + + private static byte[] WriteToArray(byte[] target, uint data, int offset) + { + for (int i = 0; i < 4; ++i) + target[i + offset] = (byte)((data >> (i * 8)) & 255); + return target; + } + + private static byte[] WriteContiguous(byte[] target, int offset, params uint[] data) + { + for (int i = 0; i < data.Length; ++i) WriteToArray(target, data[i], offset + i * 4); + return target; + } + } + + public static class Helper + { + 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..45b244f --- /dev/null +++ b/MLAPI/NetworkingManagerComponents/EllipticCurve.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using IntXLib; + +namespace ECDH +{ + public class Point + { + public static readonly Point POINT_AT_INFINITY = new Point(); + public IntX X { get; private set; } + public IntX Y { get; private set; } + private bool pai = false; + public Point(IntX x, IntX y) + { + X = x; + Y = y; + } + private Point() { 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 Point Add(Point p1, Point p2) + { +#if SAFE_MATH + CheckOnCurve(p1); + CheckOnCurve(p2); +#endif + + // Special cases + if (p1 == Point.POINT_AT_INFINITY && p2 == Point.POINT_AT_INFINITY) return Point.POINT_AT_INFINITY; + else if (p1 == Point.POINT_AT_INFINITY) return p2; + else if (p2 == Point.POINT_AT_INFINITY) return p1; + else if (p1.X == p2.X && p1.Y == Inverse(p2).Y) return Point.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 Point(x3, y3); + } + + public Point Multiply(Point p, IntX scalar) + { + if (scalar <= 0) throw new Exception("Cannot multiply by a scalar which is <= 0"); + if (p == Point.POINT_AT_INFINITY) return Point.POINT_AT_INFINITY; + + Point p1 = new Point(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 Point Inverse(Point p) => Inverse(p, modulo); + protected static Point Inverse(Point p, IntX modulo) => new Point(p.X, Mod(-p.Y, modulo)); + + public bool IsOnCurve(Point p) + { + try { CheckOnCurve(p); } + catch { return false; } + return true; + } + protected void CheckOnCurve(Point p) + { + if ( + p!=Point.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 From 944def33e59590669429602b5e15eed0afa8f33c Mon Sep 17 00:00:00 2001 From: GabrielTofvesson Date: Thu, 29 Mar 2018 01:43:03 +0200 Subject: [PATCH 2/4] Removed custom PBKDF2 implementation Switched to cryptographically safe random provider --- .../DiffieHellman.cs | 187 ++---------------- .../EllipticCurve.cs | 36 ++-- 2 files changed, 32 insertions(+), 191 deletions(-) diff --git a/MLAPI/NetworkingManagerComponents/DiffieHellman.cs b/MLAPI/NetworkingManagerComponents/DiffieHellman.cs index 50e85a1..2e675b5 100644 --- a/MLAPI/NetworkingManagerComponents/DiffieHellman.cs +++ b/MLAPI/NetworkingManagerComponents/DiffieHellman.cs @@ -1,19 +1,24 @@ using System; using IntXLib; using System.Text; +using System.Security.Cryptography; namespace ECDH { public class EllipticDiffieHellman { - protected static readonly Random rand = new Random(); + 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 Point generator, pub; + protected readonly CurvePoint generator, pub; - public EllipticDiffieHellman(EllipticCurve curve, Point generator, IntX order, byte[] priv = null) + public EllipticDiffieHellman(EllipticCurve curve, CurvePoint generator, IntX order, byte[] priv = null) { this.curve = curve; this.generator = generator; @@ -26,15 +31,15 @@ namespace ECDH { byte[] p1 = new byte[5 /*rand.Next(max.Length) + 1*/]; - rand.NextBytes(p1); + rand.GetBytes(p1); if (p1.Length == max.Length) p1[p1.Length - 1] %= max[max.Length - 1]; else p1[p1.Length - 1] &= 127; - this.priv = Helper.FromArray(p1); + this.priv = DHHelper.FromArray(p1); } while (this.priv<2); } - else this.priv = Helper.FromArray(priv); + else this.priv = DHHelper.FromArray(priv); // Generate public key pub = curve.Multiply(generator, this.priv); @@ -65,180 +70,16 @@ namespace ECDH Array.Copy(pK, 4, p1, 0, p1.Length); Array.Copy(pK, 4 + p1.Length, p2, 0, p2.Length); - Point remotePublic = new Point(Helper.FromArray(p1), Helper.FromArray(p2)); + 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 PBKDF2(HMAC_SHA1, secret, Encoding.UTF8.GetBytes("P1sN0R4inb0wPl5P1sPls"), 1024, 32); - } - - - public delegate byte[] PRF(byte[] key, byte[] salt); - private static byte[] PBKDF2(PRF function, byte[] password, byte[] salt, int iterations, int dklen) - { - byte[] dk = new byte[0]; // Create a placeholder for the derived key - uint iter = 1; // Track the iterations - while (dk.Length < dklen) - { - // F-function - // The F-function (PRF) takes the amount of iterations performed in the opposite endianness format from what C# uses, so we have to swap the endianness - byte[] u = function(password, Concatenate(salt, WriteToArray(new byte[4], SwapEndian(iter), 0))); - byte[] ures = new byte[u.Length]; - Array.Copy(u, ures, u.Length); - for (int i = 1; i < iterations; ++i) - { - // Iteratively apply the PRF - u = function(password, u); - for (int j = 0; j < u.Length; ++j) ures[j] ^= u[j]; - } - - // Concatenate the result to the dk - dk = Concatenate(dk, ures); - - ++iter; - } - - // Clip all bytes past what we needed (yes, that's really what the standard is) - if (dk.Length != dklen) - { - var t1 = new byte[dklen]; - Array.Copy(dk, t1, Math.Min(dklen, dk.Length)); - return t1; - } - return dk; - } - public delegate byte[] HashFunction(byte[] message); - private static byte[] HMAC(byte[] key, byte[] message, HashFunction func, int blockSizeBytes) - { - if (key.Length > blockSizeBytes) key = func(key); - else if (key.Length < blockSizeBytes) - { - byte[] b = new byte[blockSizeBytes]; - Array.Copy(key, b, key.Length); - key = b; - } - - byte[] o_key_pad = new byte[blockSizeBytes]; // Outer padding - byte[] i_key_pad = new byte[blockSizeBytes]; // Inner padding - for (int i = 0; i < blockSizeBytes; ++i) - { - // Combine padding with key - o_key_pad[i] = (byte)(key[i] ^ 0x5c); - i_key_pad[i] = (byte)(key[i] ^ 0x36); - } - return func(Concatenate(o_key_pad, func(Concatenate(message, i_key_pad)))); - } - private static byte[] HMAC_SHA1(byte[] key, byte[] message) => HMAC(key, message, SHA1, 20); - private static byte[] Concatenate(params byte[][] bytes) - { - int alloc = 0; - foreach (byte[] b in bytes) alloc += b.Length; - byte[] result = new byte[alloc]; - alloc = 0; - for (int i = 0; i < bytes.Length; ++i) - { - Array.Copy(bytes[i], 0, result, alloc, bytes[i].Length); - alloc += bytes[i].Length; - } - return result; - } - public static byte[] SHA1(byte[] message) - { - // Initialize buffers - uint h0 = 0x67452301; - uint h1 = 0xEFCDAB89; - uint h2 = 0x98BADCFE; - uint h3 = 0x10325476; - uint h4 = 0xC3D2E1F0; - - // Pad message - int ml = message.Length + 1; - byte[] msg = new byte[ml + ((960 - (ml * 8 % 512)) % 512) / 8 + 8]; - Array.Copy(message, msg, message.Length); - msg[message.Length] = 0x80; - long len = message.Length * 8; - for (int i = 0; i < 8; ++i) msg[msg.Length - 1 - i] = (byte)((len >> (i * 8)) & 255); - //Support.WriteToArray(msg, message.Length * 8, msg.Length - 8); - //for (int i = 0; i <4; ++i) msg[msg.Length - 5 - i] = (byte)(((message.Length*8) >> (i * 8)) & 255); - - int chunks = msg.Length / 64; - - // Perform hashing for each 512-bit block - for (int i = 0; i < chunks; ++i) - { - - // Split block into words - uint[] w = new uint[80]; - for (int j = 0; j < 16; ++j) - w[j] |= (uint)((msg[i * 64 + j * 4] << 24) | (msg[i * 64 + j * 4 + 1] << 16) | (msg[i * 64 + j * 4 + 2] << 8) | (msg[i * 64 + j * 4 + 3] << 0)); - - // Expand words - for (int j = 16; j < 80; ++j) - w[j] = Rot(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); - - // Initialize chunk-hash - uint - a = h0, - b = h1, - c = h2, - d = h3, - e = h4; - - // Do hash rounds - for (int t = 0; t < 80; ++t) - { - uint tmp = ((a << 5) | (a >> (27))) + - ( // Round-function - t < 20 ? (b & c) | ((~b) & d) : - t < 40 ? b ^ c ^ d : - t < 60 ? (b & c) | (b & d) | (c & d) : - /*t<80*/ b ^ c ^ d - ) + - e + - ( // K-function - t < 20 ? 0x5A827999 : - t < 40 ? 0x6ED9EBA1 : - t < 60 ? 0x8F1BBCDC : - /*t<80*/ 0xCA62C1D6 - ) + - w[t]; - e = d; - d = c; - c = Rot(b, 30); - b = a; - a = tmp; - } - h0 += a; - h1 += b; - h2 += c; - h3 += d; - h4 += e; - } - - return WriteContiguous(new byte[20], 0, SwapEndian(h0), SwapEndian(h1), SwapEndian(h2), SwapEndian(h3), SwapEndian(h4)); - } - - private static uint Rot(uint val, int by) => (val << by) | (val >> (32 - by)); - - // Swap endianness of a given integer - private static uint SwapEndian(uint value) => (uint)(((value >> 24) & (255 << 0)) | ((value >> 8) & (255 << 8)) | ((value << 8) & (255 << 16)) | ((value << 24) & (255 << 24))); - - private static byte[] WriteToArray(byte[] target, uint data, int offset) - { - for (int i = 0; i < 4; ++i) - target[i + offset] = (byte)((data >> (i * 8)) & 255); - return target; - } - - private static byte[] WriteContiguous(byte[] target, int offset, params uint[] data) - { - for (int i = 0; i < data.Length; ++i) WriteToArray(target, data[i], offset + i * 4); - return target; + return new Rfc2898DeriveBytes(secret, Encoding.UTF8.GetBytes("P1sN0R4inb0wPl5P1sPls"), 1000).GetBytes(32); } } - public static class Helper + public static class DHHelper { public static byte[] ToArray(this IntX v) { diff --git a/MLAPI/NetworkingManagerComponents/EllipticCurve.cs b/MLAPI/NetworkingManagerComponents/EllipticCurve.cs index 45b244f..3a37efe 100644 --- a/MLAPI/NetworkingManagerComponents/EllipticCurve.cs +++ b/MLAPI/NetworkingManagerComponents/EllipticCurve.cs @@ -4,18 +4,18 @@ using IntXLib; namespace ECDH { - public class Point + public class CurvePoint { - public static readonly Point POINT_AT_INFINITY = new Point(); + 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 Point(IntX x, IntX y) + public CurvePoint(IntX x, IntX y) { X = x; Y = y; } - private Point() { pai = true; } // Accessing corrdinates causes undocumented behaviour + private CurvePoint() { pai = true; } // Accessing corrdinates causes undocumented behaviour public override string ToString() { return pai ? "(POINT_AT_INFINITY)" : "(" + X + ", " + Y + ")"; @@ -41,7 +41,7 @@ namespace ECDH this.type = type; } - public Point Add(Point p1, Point p2) + public CurvePoint Add(CurvePoint p1, CurvePoint p2) { #if SAFE_MATH CheckOnCurve(p1); @@ -49,10 +49,10 @@ namespace ECDH #endif // Special cases - if (p1 == Point.POINT_AT_INFINITY && p2 == Point.POINT_AT_INFINITY) return Point.POINT_AT_INFINITY; - else if (p1 == Point.POINT_AT_INFINITY) return p2; - else if (p2 == Point.POINT_AT_INFINITY) return p1; - else if (p1.X == p2.X && p1.Y == Inverse(p2).Y) return Point.POINT_AT_INFINITY; + 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) @@ -84,15 +84,15 @@ namespace ECDH y3 = Mod(((2 * p1.X + p2.X + a) * co) - (b * co * co * co) - p1.Y); } - return new Point(x3, y3); + return new CurvePoint(x3, y3); } - public Point Multiply(Point p, IntX scalar) + public CurvePoint Multiply(CurvePoint p, IntX scalar) { if (scalar <= 0) throw new Exception("Cannot multiply by a scalar which is <= 0"); - if (p == Point.POINT_AT_INFINITY) return Point.POINT_AT_INFINITY; + if (p == CurvePoint.POINT_AT_INFINITY) return CurvePoint.POINT_AT_INFINITY; - Point p1 = new Point(p.X, p.Y); + 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) @@ -144,19 +144,19 @@ namespace ECDH return Mod(m, modulo); } - public Point Inverse(Point p) => Inverse(p, modulo); - protected static Point Inverse(Point p, IntX modulo) => new Point(p.X, Mod(-p.Y, 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(Point p) + public bool IsOnCurve(CurvePoint p) { try { CheckOnCurve(p); } catch { return false; } return true; } - protected void CheckOnCurve(Point p) + protected void CheckOnCurve(CurvePoint p) { if ( - p!=Point.POINT_AT_INFINITY && // The point at infinity is asserted to be on the curve + 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"); From a7dfddf621873503c89f6a90418c3eb4fd0c3e48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albin=20Cor=C3=A9n?= <2108U9@gmail.com> Date: Fri, 30 Mar 2018 00:35:20 +0200 Subject: [PATCH 3/4] Added AES Encryption with DiffieHellman --- MLAPI/Data/NetworkedClient.cs | 1 + MLAPI/Data/NetworkingConfiguration.cs | 11 +- MLAPI/MLAPI.csproj | 3 + .../MonoBehaviours/Core/NetworkingManager.cs | 144 +++++++++++++++++- .../CryptographyHelper.cs | 50 ++++++ .../DiffieHellman.cs | 2 +- .../EllipticCurve.cs | 2 +- 7 files changed, 197 insertions(+), 16 deletions(-) create mode 100644 MLAPI/NetworkingManagerComponents/CryptographyHelper.cs diff --git a/MLAPI/Data/NetworkedClient.cs b/MLAPI/Data/NetworkedClient.cs index 6d1dbd2..135e778 100644 --- a/MLAPI/Data/NetworkedClient.cs +++ b/MLAPI/Data/NetworkedClient.cs @@ -8,5 +8,6 @@ namespace MLAPI public int ClientId; public GameObject PlayerObject; public List OwnedObjects = new List(); + public byte[] AesKey; } } diff --git a/MLAPI/Data/NetworkingConfiguration.cs b/MLAPI/Data/NetworkingConfiguration.cs index fe969cb..072543d 100644 --- a/MLAPI/Data/NetworkingConfiguration.cs +++ b/MLAPI/Data/NetworkingConfiguration.cs @@ -13,6 +13,7 @@ namespace MLAPI public List MessageTypes = new List(); public List PassthroughMessageTypes = new List(); internal HashSet RegisteredPassthroughMessageTypes = new HashSet(); + public HashSet EncryptedChannels = new HashSet(); public List RegisteredScenes = new List(); public int MessageBufferSize = 65535; public int ReceiveTickrate = 64; @@ -28,11 +29,8 @@ namespace MLAPI public byte[] ConnectionData = new byte[0]; public float SecondsHistory = 5; public bool HandleObjectSpawning = true; - //TODO - public bool CompressMessages = false; - //Should only be used for dedicated servers and will require the servers RSA keypair being hard coded into clients in order to exchange a AES key - //TODO - public bool EncryptMessages = false; + + public bool EnableEncryption = true; public bool AllowPassthroughMessages = true; public bool EnableSceneSwitching = false; @@ -72,8 +70,7 @@ namespace MLAPI } } writer.Write(HandleObjectSpawning); - writer.Write(CompressMessages); - writer.Write(EncryptMessages); + writer.Write(EnableEncryption); writer.Write(AllowPassthroughMessages); writer.Write(EnableSceneSwitching); } diff --git a/MLAPI/MLAPI.csproj b/MLAPI/MLAPI.csproj index 446cc8e..bd86046 100644 --- a/MLAPI/MLAPI.csproj +++ b/MLAPI/MLAPI.csproj @@ -65,6 +65,9 @@ + + + diff --git a/MLAPI/MonoBehaviours/Core/NetworkingManager.cs b/MLAPI/MonoBehaviours/Core/NetworkingManager.cs index 7d6ff02..96ea2bd 100644 --- a/MLAPI/MonoBehaviours/Core/NetworkingManager.cs +++ b/MLAPI/MonoBehaviours/Core/NetworkingManager.cs @@ -49,6 +49,10 @@ namespace MLAPI public NetworkingConfiguration NetworkConfig; + private EllipticDiffieHellman clientDiffieHellman; + private Dictionary diffieHellmanPublicKeys; + private byte[] clientAesKey; + private void OnValidate() { if (SpawnablePrefabs != null) @@ -86,6 +90,7 @@ namespace MLAPI pendingClients = new HashSet(); connectedClients = new Dictionary(); messageBuffer = new byte[NetworkConfig.MessageBufferSize]; + diffieHellmanPublicKeys = new Dictionary(); MessageManager.channels = new Dictionary(); MessageManager.messageTypes = new Dictionary(); MessageManager.messageCallbacks = new Dictionary>>(); @@ -372,15 +377,29 @@ namespace MLAPI } else { + byte[] diffiePublic = new byte[0]; + if(NetworkConfig.EnableEncryption) + { + clientDiffieHellman = new EllipticDiffieHellman(EllipticDiffieHellman.DEFAULT_CURVE, EllipticDiffieHellman.DEFAULT_GENERATOR, EllipticDiffieHellman.DEFAULT_ORDER); + diffiePublic = clientDiffieHellman.GetPublicKey(); + } + int sizeOfStream = 32; if (NetworkConfig.ConnectionApproval) sizeOfStream += 2 + NetworkConfig.ConnectionData.Length; + if (NetworkConfig.EnableEncryption) + sizeOfStream += 2 + diffiePublic.Length; using (MemoryStream writeStream = new MemoryStream(sizeOfStream)) { using (BinaryWriter writer = new BinaryWriter(writeStream)) { writer.Write(NetworkConfig.GetConfig()); + if (NetworkConfig.EnableEncryption) + { + writer.Write((ushort)diffiePublic.Length); + writer.Write(diffiePublic); + } if (NetworkConfig.ConnectionApproval) { writer.Write((ushort)NetworkConfig.ConnectionData.Length); @@ -469,6 +488,14 @@ namespace MLAPI ushort bytesToRead = reader.ReadUInt16(); byte[] incommingData = reader.ReadBytes(bytesToRead); + if(NetworkConfig.EncryptedChannels.Contains(channelId)) + { + //Encrypted message + if (isServer) + incommingData = CryptographyHelper.Decrypt(incommingData, connectedClients[clientId].AesKey); + else + incommingData = CryptographyHelper.Decrypt(incommingData, clientAesKey); + } if (isServer && isPassthrough && !NetworkConfig.RegisteredPassthroughMessageTypes.Contains(messageType)) { @@ -550,6 +577,18 @@ namespace MLAPI DisconnectClient(clientId); return; } + byte[] aesKey = new byte[0]; + if(NetworkConfig.EnableEncryption) + { + ushort diffiePublicSize = reader.ReadUInt16(); + byte[] diffiePublic = reader.ReadBytes(diffiePublicSize); + diffieHellmanPublicKeys.Add(clientId, diffiePublic); + /* + EllipticDiffieHellman diffieHellman = new EllipticDiffieHellman(EllipticDiffieHellman.DEFAULT_CURVE, EllipticDiffieHellman.DEFAULT_GENERATOR, EllipticDiffieHellman.DEFAULT_ORDER); + aesKey = diffieHellman.GetSharedSecret(diffiePublic); + */ + + } if (NetworkConfig.ConnectionApproval) { ushort bufferSize = messageReader.ReadUInt16(); @@ -578,6 +617,12 @@ namespace MLAPI sceneIndex = messageReader.ReadUInt32(); } + if (NetworkConfig.EnableEncryption) + { + ushort keyLength = reader.ReadUInt16(); + clientAesKey = clientDiffieHellman.GetSharedSecret(reader.ReadBytes(keyLength)); + } + float netTime = messageReader.ReadSingle(); int remoteStamp = messageReader.ReadInt32(); int msDelay = NetworkTransport.GetRemoteDelayTimeMS(hostId, clientId, remoteStamp, out error); @@ -894,8 +939,18 @@ namespace MLAPI writer.Write(orderId.Value); writer.Write(true); writer.Write(sourceId); - writer.Write((ushort)data.Length); - writer.Write(data); + if(NetworkConfig.EncryptedChannels.Contains(channelId)) + { + //Encrypted message + byte[] encrypted = CryptographyHelper.Encrypt(data, connectedClients[targetId].AesKey); + writer.Write((ushort)encrypted.Length); + writer.Write(encrypted); + } + else + { + writer.Write((ushort)data.Length); + writer.Write(data); + } } NetworkTransport.QueueMessageForSending(hostId, targetId, channelId, stream.GetBuffer(), sizeOfStream, out error); } @@ -944,8 +999,25 @@ namespace MLAPI writer.Write(isPassthrough); if (isPassthrough) writer.Write(clientId); - writer.Write((ushort)data.Length); - writer.Write(data); + + if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName])) + { + //This is an encrypted message. + byte[] encrypted; + if (isServer) + encrypted = CryptographyHelper.Encrypt(data, connectedClients[clientId].AesKey); + else + encrypted = CryptographyHelper.Encrypt(data, clientAesKey); + + writer.Write((ushort)encrypted.Length); + writer.Write(encrypted); + } + else + { + //Send in plaintext. + writer.Write((ushort)data.Length); + writer.Write(data); + } } if (isPassthrough) clientId = serverClientId; @@ -958,7 +1030,12 @@ namespace MLAPI internal void Send(int[] clientIds, string messageType, string channelName, byte[] data, uint? networkId = null, ushort? orderId = null) { - int sizeOfStream = 6; + if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName])) + { + Debug.LogWarning("MLAPI: Cannot send messages over encrypted channel to multiple clients."); + return; + } + int sizeOfStream = 6; if (networkId != null) sizeOfStream += 4; if (orderId != null) @@ -1000,6 +1077,12 @@ namespace MLAPI internal void Send(List clientIds, string messageType, string channelName, byte[] data, uint? networkId = null, ushort? orderId = null) { + if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName])) + { + Debug.LogWarning("MLAPI: Cannot send messages over encrypted channel to multiple clients."); + return; + } + //2 bytes for messageType, 2 bytes for buffer length and one byte for target bool int sizeOfStream = 6; if (networkId != null) @@ -1043,6 +1126,12 @@ namespace MLAPI internal void Send(string messageType, string channelName, byte[] data, uint? networkId = null, ushort? orderId = null) { + if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName])) + { + Debug.LogWarning("MLAPI: Cannot send messages over encrypted channel to multiple clients."); + return; + } + //2 bytes for messageType, 2 bytes for buffer length and one byte for target bool int sizeOfStream = 6; if (networkId != null) @@ -1087,6 +1176,12 @@ namespace MLAPI internal void Send(string messageType, string channelName, byte[] data, int clientIdToIgnore, uint? networkId = null, ushort? orderId = null) { + if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName])) + { + Debug.LogWarning("MLAPI: Cannot send messages over encrypted channel to multiple clients."); + return; + } + //2 bytes for messageType, 2 bytes for buffer length and one byte for target bool int sizeOfStream = 5; if (networkId != null) @@ -1134,10 +1229,16 @@ namespace MLAPI { if (!isServer) return; + if (pendingClients.Contains(clientId)) pendingClients.Remove(clientId); + if (connectedClients.ContainsKey(clientId)) connectedClients.Remove(clientId); + + if (diffieHellmanPublicKeys.ContainsKey(clientId)) + diffieHellmanPublicKeys.Remove(clientId); + NetworkTransport.Disconnect(hostId, clientId, out error); } @@ -1180,9 +1281,23 @@ namespace MLAPI //Inform new client it got approved if (pendingClients.Contains(clientId)) pendingClients.Remove(clientId); + + byte[] aesKey = new byte[0]; + byte[] publicKey = new byte[0]; + if (NetworkConfig.EnableEncryption) + { + EllipticDiffieHellman diffieHellman = new EllipticDiffieHellman(EllipticDiffieHellman.DEFAULT_CURVE, EllipticDiffieHellman.DEFAULT_GENERATOR, EllipticDiffieHellman.DEFAULT_ORDER); + aesKey = diffieHellman.GetSharedSecret(diffieHellmanPublicKeys[clientId]); + publicKey = diffieHellman.GetPublicKey(); + + if (diffieHellmanPublicKeys.ContainsKey(clientId)) + diffieHellmanPublicKeys.Remove(clientId); + } + NetworkedClient client = new NetworkedClient() { - ClientId = clientId + ClientId = clientId, + AesKey = aesKey }; connectedClients.Add(clientId, client); @@ -1193,7 +1308,6 @@ namespace MLAPI connectedClients[clientId].PlayerObject = go; } - int sizeOfStream = 16 + ((connectedClients.Count - 1) * 4); int amountOfObjectsToSend = SpawnManager.spawnedObjects.Values.Count(x => x.ServerOnly == false); @@ -1203,6 +1317,10 @@ namespace MLAPI sizeOfStream += 4; sizeOfStream += 14 * amountOfObjectsToSend; } + if(NetworkConfig.EnableEncryption) + { + sizeOfStream += 2 + publicKey.Length; + } if(NetworkConfig.EnableSceneSwitching) { sizeOfStream += 4; @@ -1217,8 +1335,16 @@ namespace MLAPI { writer.Write(NetworkSceneManager.CurrentSceneIndex); } + + if(NetworkConfig.EnableEncryption) + { + writer.Write((ushort)publicKey.Length); + writer.Write(publicKey); + } + writer.Write(NetworkTime); writer.Write(NetworkTransport.GetNetworkTimestamp()); + writer.Write(connectedClients.Count - 1); foreach (KeyValuePair item in connectedClients) { @@ -1284,6 +1410,10 @@ namespace MLAPI { if (pendingClients.Contains(clientId)) pendingClients.Remove(clientId); + + if (diffieHellmanPublicKeys.ContainsKey(clientId)) + diffieHellmanPublicKeys.Remove(clientId); + NetworkTransport.Disconnect(hostId, clientId, out error); } } diff --git a/MLAPI/NetworkingManagerComponents/CryptographyHelper.cs b/MLAPI/NetworkingManagerComponents/CryptographyHelper.cs new file mode 100644 index 0000000..02344e8 --- /dev/null +++ b/MLAPI/NetworkingManagerComponents/CryptographyHelper.cs @@ -0,0 +1,50 @@ +using System; +using System.Security.Cryptography; +using System.IO; + +namespace MLAPI.NetworkingManagerComponents +{ + public static class CryptographyHelper + { + public static byte[] Decrypt(byte[] encryptedBuffer, byte[] key) + { + byte[] iv = new byte[16]; + Array.Copy(encryptedBuffer, 0, iv, 0, 16); + + using (MemoryStream stream = new MemoryStream()) + { + using (RijndaelManaged aes = new RijndaelManaged()) + { + aes.IV = iv; + aes.Key = key; + using (CryptoStream cs = new CryptoStream(stream, aes.CreateDecryptor(), CryptoStreamMode.Write)) + { + cs.Write(encryptedBuffer, 16, encryptedBuffer.Length - 16); + } + return stream.ToArray(); + } + } + } + + public static byte[] Encrypt(byte[] clearBuffer, byte[] key) + { + using (MemoryStream stream = new MemoryStream()) + { + using (RijndaelManaged aes = new RijndaelManaged()) + { + aes.Key = key; + aes.GenerateIV(); + using (CryptoStream cs = new CryptoStream(stream, aes.CreateEncryptor(), CryptoStreamMode.Write)) + { + cs.Write(clearBuffer, 0, clearBuffer.Length); + } + byte[] encrypted = stream.ToArray(); + byte[] final = new byte[encrypted.Length + 16]; + Array.Copy(aes.IV, final, 16); + Array.Copy(encrypted, 0, final, 16, encrypted.Length); + return final; + } + } + } + } +} diff --git a/MLAPI/NetworkingManagerComponents/DiffieHellman.cs b/MLAPI/NetworkingManagerComponents/DiffieHellman.cs index 2e675b5..d7885f9 100644 --- a/MLAPI/NetworkingManagerComponents/DiffieHellman.cs +++ b/MLAPI/NetworkingManagerComponents/DiffieHellman.cs @@ -3,7 +3,7 @@ using IntXLib; using System.Text; using System.Security.Cryptography; -namespace ECDH +namespace MLAPI.NetworkingManagerComponents { public class EllipticDiffieHellman { diff --git a/MLAPI/NetworkingManagerComponents/EllipticCurve.cs b/MLAPI/NetworkingManagerComponents/EllipticCurve.cs index 3a37efe..3be71c8 100644 --- a/MLAPI/NetworkingManagerComponents/EllipticCurve.cs +++ b/MLAPI/NetworkingManagerComponents/EllipticCurve.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using IntXLib; -namespace ECDH +namespace MLAPI.NetworkingManagerComponents { public class CurvePoint { From bd4b685e84f0a119095faa4b44bc11099e91b5a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albin=20Cor=C3=A9n?= <2108U9@gmail.com> Date: Fri, 30 Mar 2018 23:38:10 +0200 Subject: [PATCH 4/4] Fixed DiffieHellman issues --- MLAPI/MonoBehaviours/Core/NetworkingManager.cs | 8 ++++---- MLAPI/NetworkingManagerComponents/DiffieHellman.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/MLAPI/MonoBehaviours/Core/NetworkingManager.cs b/MLAPI/MonoBehaviours/Core/NetworkingManager.cs index 96ea2bd..8dffd93 100644 --- a/MLAPI/MonoBehaviours/Core/NetworkingManager.cs +++ b/MLAPI/MonoBehaviours/Core/NetworkingManager.cs @@ -580,8 +580,8 @@ namespace MLAPI byte[] aesKey = new byte[0]; if(NetworkConfig.EnableEncryption) { - ushort diffiePublicSize = reader.ReadUInt16(); - byte[] diffiePublic = reader.ReadBytes(diffiePublicSize); + ushort diffiePublicSize = messageReader.ReadUInt16(); + byte[] diffiePublic = messageReader.ReadBytes(diffiePublicSize); diffieHellmanPublicKeys.Add(clientId, diffiePublic); /* EllipticDiffieHellman diffieHellman = new EllipticDiffieHellman(EllipticDiffieHellman.DEFAULT_CURVE, EllipticDiffieHellman.DEFAULT_GENERATOR, EllipticDiffieHellman.DEFAULT_ORDER); @@ -619,8 +619,8 @@ namespace MLAPI if (NetworkConfig.EnableEncryption) { - ushort keyLength = reader.ReadUInt16(); - clientAesKey = clientDiffieHellman.GetSharedSecret(reader.ReadBytes(keyLength)); + ushort keyLength = messageReader.ReadUInt16(); + clientAesKey = clientDiffieHellman.GetSharedSecret(messageReader.ReadBytes(keyLength)); } float netTime = messageReader.ReadSingle(); diff --git a/MLAPI/NetworkingManagerComponents/DiffieHellman.cs b/MLAPI/NetworkingManagerComponents/DiffieHellman.cs index d7885f9..4915ddf 100644 --- a/MLAPI/NetworkingManagerComponents/DiffieHellman.cs +++ b/MLAPI/NetworkingManagerComponents/DiffieHellman.cs @@ -85,7 +85,7 @@ namespace MLAPI.NetworkingManagerComponents { v.GetInternalState(out uint[] digits, out bool negative); byte[] b = DigitConverter.ToBytes(digits); - byte[] b1 = new byte[b.Length]; + byte[] b1 = new byte[b.Length + 1]; Array.Copy(b, b1, b.Length); b1[b.Length] = (byte)(negative ? 1 : 0); return b1; @@ -98,7 +98,7 @@ namespace MLAPI.NetworkingManagerComponents 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 bool BitAt(this uint[] data, long index) => (data[index / 32] & (1 << (int)(index % 32))) != 0; public static IntX Abs(this IntX i) => i < 0 ? -i : i; } }