Skip to content

Commit 1db4606

Browse files
Added Diffie Hellman
Added PBKDF2-HMAC-SHA1 Added IntX implementation
1 parent 3d6795e commit 1db4606

File tree

4 files changed

+462
-0
lines changed

4 files changed

+462
-0
lines changed

MLAPI/MLAPI.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
4545
</PropertyGroup>
4646
<ItemGroup>
47+
<Reference Include="IntXLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1744b76c74eaee1e, processorArchitecture=MSIL">
48+
<HintPath>..\packages\IntX.1.0.1.0\lib\net20\IntXLib.dll</HintPath>
49+
</Reference>
4750
<Reference Include="System" />
4851
<Reference Include="System.Core" />
4952
<Reference Include="System.Xml.Linq" />
@@ -78,5 +81,8 @@
7881
<Compile Include="Data\ClientIdKey.cs" />
7982
<Compile Include="NetworkingManagerComponents\MessageChunker.cs" />
8083
</ItemGroup>
84+
<ItemGroup>
85+
<None Include="packages.config" />
86+
</ItemGroup>
8187
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
8288
</Project>
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
using System;
2+
using IntXLib;
3+
using System.Text;
4+
5+
namespace ECDH
6+
{
7+
public class EllipticDiffieHellman
8+
{
9+
protected static readonly Random rand = new Random();
10+
11+
protected readonly EllipticCurve curve;
12+
public readonly IntX priv;
13+
protected readonly Point generator, pub;
14+
15+
16+
public EllipticDiffieHellman(EllipticCurve curve, Point generator, IntX order, byte[] priv = null)
17+
{
18+
this.curve = curve;
19+
this.generator = generator;
20+
21+
// Generate private key
22+
if (priv == null)
23+
{
24+
byte[] max = order.ToArray();
25+
do
26+
{
27+
byte[] p1 = new byte[5 /*rand.Next(max.Length) + 1*/];
28+
29+
rand.NextBytes(p1);
30+
31+
if (p1.Length == max.Length) p1[p1.Length - 1] %= max[max.Length - 1];
32+
else p1[p1.Length - 1] &= 127;
33+
34+
this.priv = Helper.FromArray(p1);
35+
} while (this.priv<2);
36+
}
37+
else this.priv = Helper.FromArray(priv);
38+
39+
// Generate public key
40+
pub = curve.Multiply(generator, this.priv);
41+
}
42+
43+
public byte[] GetPublicKey()
44+
{
45+
byte[] p1 = pub.X.ToArray();
46+
byte[] p2 = pub.Y.ToArray();
47+
48+
byte[] ser = new byte[4 + p1.Length + p2.Length];
49+
ser[0] = (byte)(p1.Length & 255);
50+
ser[1] = (byte)((p1.Length >> 8) & 255);
51+
ser[2] = (byte)((p1.Length >> 16) & 255);
52+
ser[3] = (byte)((p1.Length >> 24) & 255);
53+
Array.Copy(p1, 0, ser, 4, p1.Length);
54+
Array.Copy(p2, 0, ser, 4 + p1.Length, p2.Length);
55+
56+
return ser;
57+
}
58+
59+
public byte[] GetPrivateKey() => priv.ToArray();
60+
61+
public byte[] GetSharedSecret(byte[] pK)
62+
{
63+
byte[] p1 = new byte[pK[0] | (pK[1]<<8) | (pK[2]<<16) | (pK[3]<<24)]; // Reconstruct x-axis size
64+
byte[] p2 = new byte[pK.Length - p1.Length - 4];
65+
Array.Copy(pK, 4, p1, 0, p1.Length);
66+
Array.Copy(pK, 4 + p1.Length, p2, 0, p2.Length);
67+
68+
Point remotePublic = new Point(Helper.FromArray(p1), Helper.FromArray(p2));
69+
70+
byte[] secret = curve.Multiply(remotePublic, priv).X.ToArray(); // Use the x-coordinate as the shared secret
71+
72+
// PBKDF2-HMAC-SHA1 (Common shared secret generation method)
73+
return PBKDF2(HMAC_SHA1, secret, Encoding.UTF8.GetBytes("P1sN0R4inb0wPl5P1sPls"), 1024, 32);
74+
}
75+
76+
77+
public delegate byte[] PRF(byte[] key, byte[] salt);
78+
private static byte[] PBKDF2(PRF function, byte[] password, byte[] salt, int iterations, int dklen)
79+
{
80+
byte[] dk = new byte[0]; // Create a placeholder for the derived key
81+
uint iter = 1; // Track the iterations
82+
while (dk.Length < dklen)
83+
{
84+
// F-function
85+
// 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
86+
byte[] u = function(password, Concatenate(salt, WriteToArray(new byte[4], SwapEndian(iter), 0)));
87+
byte[] ures = new byte[u.Length];
88+
Array.Copy(u, ures, u.Length);
89+
for (int i = 1; i < iterations; ++i)
90+
{
91+
// Iteratively apply the PRF
92+
u = function(password, u);
93+
for (int j = 0; j < u.Length; ++j) ures[j] ^= u[j];
94+
}
95+
96+
// Concatenate the result to the dk
97+
dk = Concatenate(dk, ures);
98+
99+
++iter;
100+
}
101+
102+
// Clip all bytes past what we needed (yes, that's really what the standard is)
103+
if (dk.Length != dklen)
104+
{
105+
var t1 = new byte[dklen];
106+
Array.Copy(dk, t1, Math.Min(dklen, dk.Length));
107+
return t1;
108+
}
109+
return dk;
110+
}
111+
public delegate byte[] HashFunction(byte[] message);
112+
private static byte[] HMAC(byte[] key, byte[] message, HashFunction func, int blockSizeBytes)
113+
{
114+
if (key.Length > blockSizeBytes) key = func(key);
115+
else if (key.Length < blockSizeBytes)
116+
{
117+
byte[] b = new byte[blockSizeBytes];
118+
Array.Copy(key, b, key.Length);
119+
key = b;
120+
}
121+
122+
byte[] o_key_pad = new byte[blockSizeBytes]; // Outer padding
123+
byte[] i_key_pad = new byte[blockSizeBytes]; // Inner padding
124+
for (int i = 0; i < blockSizeBytes; ++i)
125+
{
126+
// Combine padding with key
127+
o_key_pad[i] = (byte)(key[i] ^ 0x5c);
128+
i_key_pad[i] = (byte)(key[i] ^ 0x36);
129+
}
130+
return func(Concatenate(o_key_pad, func(Concatenate(message, i_key_pad))));
131+
}
132+
private static byte[] HMAC_SHA1(byte[] key, byte[] message) => HMAC(key, message, SHA1, 20);
133+
private static byte[] Concatenate(params byte[][] bytes)
134+
{
135+
int alloc = 0;
136+
foreach (byte[] b in bytes) alloc += b.Length;
137+
byte[] result = new byte[alloc];
138+
alloc = 0;
139+
for (int i = 0; i < bytes.Length; ++i)
140+
{
141+
Array.Copy(bytes[i], 0, result, alloc, bytes[i].Length);
142+
alloc += bytes[i].Length;
143+
}
144+
return result;
145+
}
146+
public static byte[] SHA1(byte[] message)
147+
{
148+
// Initialize buffers
149+
uint h0 = 0x67452301;
150+
uint h1 = 0xEFCDAB89;
151+
uint h2 = 0x98BADCFE;
152+
uint h3 = 0x10325476;
153+
uint h4 = 0xC3D2E1F0;
154+
155+
// Pad message
156+
int ml = message.Length + 1;
157+
byte[] msg = new byte[ml + ((960 - (ml * 8 % 512)) % 512) / 8 + 8];
158+
Array.Copy(message, msg, message.Length);
159+
msg[message.Length] = 0x80;
160+
long len = message.Length * 8;
161+
for (int i = 0; i < 8; ++i) msg[msg.Length - 1 - i] = (byte)((len >> (i * 8)) & 255);
162+
//Support.WriteToArray(msg, message.Length * 8, msg.Length - 8);
163+
//for (int i = 0; i <4; ++i) msg[msg.Length - 5 - i] = (byte)(((message.Length*8) >> (i * 8)) & 255);
164+
165+
int chunks = msg.Length / 64;
166+
167+
// Perform hashing for each 512-bit block
168+
for (int i = 0; i < chunks; ++i)
169+
{
170+
171+
// Split block into words
172+
uint[] w = new uint[80];
173+
for (int j = 0; j < 16; ++j)
174+
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));
175+
176+
// Expand words
177+
for (int j = 16; j < 80; ++j)
178+
w[j] = Rot(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
179+
180+
// Initialize chunk-hash
181+
uint
182+
a = h0,
183+
b = h1,
184+
c = h2,
185+
d = h3,
186+
e = h4;
187+
188+
// Do hash rounds
189+
for (int t = 0; t < 80; ++t)
190+
{
191+
uint tmp = ((a << 5) | (a >> (27))) +
192+
( // Round-function
193+
t < 20 ? (b & c) | ((~b) & d) :
194+
t < 40 ? b ^ c ^ d :
195+
t < 60 ? (b & c) | (b & d) | (c & d) :
196+
/*t<80*/ b ^ c ^ d
197+
) +
198+
e +
199+
( // K-function
200+
t < 20 ? 0x5A827999 :
201+
t < 40 ? 0x6ED9EBA1 :
202+
t < 60 ? 0x8F1BBCDC :
203+
/*t<80*/ 0xCA62C1D6
204+
) +
205+
w[t];
206+
e = d;
207+
d = c;
208+
c = Rot(b, 30);
209+
b = a;
210+
a = tmp;
211+
}
212+
h0 += a;
213+
h1 += b;
214+
h2 += c;
215+
h3 += d;
216+
h4 += e;
217+
}
218+
219+
return WriteContiguous(new byte[20], 0, SwapEndian(h0), SwapEndian(h1), SwapEndian(h2), SwapEndian(h3), SwapEndian(h4));
220+
}
221+
222+
private static uint Rot(uint val, int by) => (val << by) | (val >> (32 - by));
223+
224+
// Swap endianness of a given integer
225+
private static uint SwapEndian(uint value) => (uint)(((value >> 24) & (255 << 0)) | ((value >> 8) & (255 << 8)) | ((value << 8) & (255 << 16)) | ((value << 24) & (255 << 24)));
226+
227+
private static byte[] WriteToArray(byte[] target, uint data, int offset)
228+
{
229+
for (int i = 0; i < 4; ++i)
230+
target[i + offset] = (byte)((data >> (i * 8)) & 255);
231+
return target;
232+
}
233+
234+
private static byte[] WriteContiguous(byte[] target, int offset, params uint[] data)
235+
{
236+
for (int i = 0; i < data.Length; ++i) WriteToArray(target, data[i], offset + i * 4);
237+
return target;
238+
}
239+
}
240+
241+
public static class Helper
242+
{
243+
public static byte[] ToArray(this IntX v)
244+
{
245+
v.GetInternalState(out uint[] digits, out bool negative);
246+
byte[] b = DigitConverter.ToBytes(digits);
247+
byte[] b1 = new byte[b.Length];
248+
Array.Copy(b, b1, b.Length);
249+
b1[b.Length] = (byte)(negative ? 1 : 0);
250+
return b1;
251+
}
252+
public static IntX FromArray(byte[] b)
253+
{
254+
if (b.Length == 0) return new IntX();
255+
byte[] b1 = new byte[b.Length - 1];
256+
Array.Copy(b, b1, b1.Length);
257+
uint[] u = DigitConverter.FromBytes(b1);
258+
return new IntX(u, b[b.Length - 1]==1);
259+
}
260+
public static bool BitAt(this uint[] data, long index) => (data[index/8]&(1<<(int)(index%8)))!=0;
261+
public static IntX Abs(this IntX i) => i < 0 ? -i : i;
262+
}
263+
}

0 commit comments

Comments
 (0)