Browse Source

Auth works!

master
Gregory Rudolph 3 years ago
parent
commit
4e88939137
Signed by: rudi
GPG Key ID: EF64F3CBD1A1EBDD
  1. 53
      Account.cs
  2. 292
      Extensions.cs
  3. 73
      SRP6.cs

53
Account.cs

@ -1,9 +1,6 @@ @@ -1,9 +1,6 @@
using System;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Collections.Generic;
using System.Security.Cryptography;
using MySql.Data.MySqlClient;
using System.Globalization;
@ -163,55 +160,7 @@ namespace NightmareCoreWeb2 @@ -163,55 +160,7 @@ namespace NightmareCoreWeb2
catch (Exception) { }
}
return AuthenticateWithToken(password) || VerifySRP6Login(this.Username, password, salt, verifier);
}
public bool VerifySRP6Login(string username, string password, byte[] salt, byte[] verifier)
{
// re-calculate the verifier using the provided username + password and the stored salt
byte[] checkVerifier = CalculateVerifier(username, password, salt);
Console.WriteLine($"{Encoding.ASCII.GetString(verifier)} {verifier.Length} bytes\n{Encoding.ASCII.GetString(checkVerifier)} {checkVerifier.Length} bytes");
Console.WriteLine($"DB {new BigInteger(verifier)}\nTC {new BigInteger(CalculateVerifier(username, password, salt))}");
// compare it against the stored verifier
return verifier.SequenceEqual(checkVerifier.Reverse().ToArray());
}
public byte[] Hash(byte[] componentOne, byte[] componentTwo)
{
if (componentOne == null) throw new ArgumentNullException(nameof(componentOne));
if (componentTwo == null) throw new ArgumentNullException(nameof(componentTwo));
return Hash(componentOne.Concat(componentTwo).ToArray());
}
public byte[] Hash(byte[] bytes)
{
if (bytes == null) throw new ArgumentNullException(nameof(bytes));
//WoW expects non-secure SHA1 hashing. SRP6 is deprecated too. We need to do it anyway
using (SHA1 shaProvider = SHA1.Create())
{
return shaProvider.ComputeHash(bytes);
}
}
public byte[] CalculateVerifier(string username, string password, byte[] salt)
{
using (SHA1 shaProvider = SHA1.Create())
{
if (BitConverter.IsLittleEndian)
{
return BigInteger.ModPow(
g,
new BigInteger(Hash(salt, Hash(Encoding.UTF8.GetBytes($"{username.ToUpper()}:{password.ToUpper()}")))),
N
).ToByteArray();
}
else
{
return BigInteger.ModPow(
g,
new BigInteger(Hash(salt, Hash(Encoding.UTF8.GetBytes($"{username.ToUpper()}:{password.ToUpper()}")).Reverse().ToArray())),
N
).ToByteArray();
}
}
return Framework.Cryptography.SRP6.CheckLogin(this.Username, password, salt, verifier);
}
}

292
Extensions.cs

@ -0,0 +1,292 @@ @@ -0,0 +1,292 @@
/*
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Runtime.CompilerServices;
namespace System
{
public static class Extensions
{
public static bool HasAnyFlag<T>(this T value, T flag) where T : struct
{
long lValue = Convert.ToInt64(value);
long lFlag = Convert.ToInt64(flag);
return (lValue & lFlag) != 0;
}
public static string ToHexString(this byte[] byteArray, bool reverse = false)
{
if (reverse)
return byteArray.Reverse().Aggregate("", (current, b) => current + b.ToString("X2"));
else
return byteArray.Aggregate("", (current, b) => current + b.ToString("X2"));
}
public static byte[] ToByteArray(this string str)
{
str = str.Replace(" ", String.Empty);
var res = new byte[str.Length / 2];
for (int i = 0; i < res.Length; ++i)
{
string temp = String.Concat(str[i * 2], str[i * 2 + 1]);
res[i] = Convert.ToByte(temp, 16);
}
return res;
}
public static byte[] ToByteArray(this string value, char separator)
{
return Array.ConvertAll(value.Split(separator), byte.Parse);
}
static uint LeftRotate(this uint value, int shiftCount)
{
return (value << shiftCount) | (value >> (0x20 - shiftCount));
}
public static byte[] GenerateRandomKey(this byte[] s, int length)
{
var random = new Random((int)((uint)(Guid.NewGuid().GetHashCode() ^ 1 >> 89 << 2 ^ 42)).LeftRotate(13));
var key = new byte[length];
for (int i = 0; i < length; i++)
{
int randValue;
do
{
randValue = (int)((uint)random.Next(0xFF)).LeftRotate(1) ^ i;
} while (randValue > 0xFF && randValue <= 0);
key[i] = (byte)randValue;
}
return key;
}
public static bool Compare(this byte[] b, byte[] b2)
{
for (int i = 0; i < b2.Length; i++)
if (b[i] != b2[i])
return false;
return true;
}
public static byte[] Combine(this byte[] data, params byte[][] pData)
{
var combined = data;
foreach (var arr in pData)
{
var currentSize = combined.Length;
Array.Resize(ref combined, currentSize + arr.Length);
Buffer.BlockCopy(arr, 0, combined, currentSize, arr.Length);
}
return combined;
}
public static object[] Combine(this object[] data, params object[][] pData)
{
var combined = data;
foreach (var arr in pData)
{
var currentSize = combined.Length;
Array.Resize(ref combined, currentSize + arr.Length);
Array.Copy(arr, 0, combined, currentSize, arr.Length);
}
return combined;
}
public static void Swap<T>(ref T left, ref T right)
{
T temp = left;
left = right;
right = temp;
}
public static uint[] SerializeObject<T>(this T obj)
{
//if (obj.GetType()<StructLayoutAttribute>() == null)
//return null;
var size = Marshal.SizeOf(typeof(T));
var ptr = Marshal.AllocHGlobal(size);
byte[] array = new byte[size];
Marshal.StructureToPtr(obj, ptr, true);
Marshal.Copy(ptr, array, 0, size);
Marshal.FreeHGlobal(ptr);
uint[] result = new uint[size / 4];
Buffer.BlockCopy(array, 0, result, 0, array.Length);
return result;
}
public static List<T> DeserializeObjects<T>(this ICollection<uint> data)
{
List<T> list = new List<T>();
if (data.Count == 0)
return list;
if (typeof(T).GetCustomAttribute<StructLayoutAttribute>() == null)
return list;
byte[] result = new byte[data.Count * sizeof(uint)];
Buffer.BlockCopy(data.ToArray(), 0, result, 0, result.Length);
var typeSize = Marshal.SizeOf(typeof(T));
var objCount = data.Count / (typeSize / sizeof(uint));
for (var i = 0; i < objCount; ++i)
{
var ptr = Marshal.AllocHGlobal(typeSize);
Marshal.Copy(result, typeSize * i, ptr, typeSize);
list.Add((T)Marshal.PtrToStructure(ptr, typeof(T)));
Marshal.FreeHGlobal(ptr);
}
return list;
}
#region Strings
public static bool IsEmpty(this string str)
{
return string.IsNullOrEmpty(str);
}
public static T ToEnum<T>(this string str) where T : struct
{
T value;
if (!Enum.TryParse(str, out value))
return default;
return value;
}
public static string ConvertFormatSyntax(this string str)
{
string pattern = @"(%\W*\d*[a-zA-Z]*)";
int count = 0;
string result = Regex.Replace(str, pattern, m => string.Concat("{", count++, "}"));
return result;
}
public static bool Like(this string toSearch, string toFind)
{
return toSearch.ToLower().Contains(toFind.ToLower());
}
public static bool IsNumber(this string str)
{
double value;
return double.TryParse(str, out value);
}
public static int GetByteCount(this string str)
{
if (str.IsEmpty())
return 0;
return Encoding.UTF8.GetByteCount(str);
}
public static bool isExtendedLatinCharacter(char wchar)
{
if (isBasicLatinCharacter(wchar))
return true;
if (wchar >= 0x00C0 && wchar <= 0x00D6) // LATIN CAPITAL LETTER A WITH GRAVE - LATIN CAPITAL LETTER O WITH DIAERESIS
return true;
if (wchar >= 0x00D8 && wchar <= 0x00DE) // LATIN CAPITAL LETTER O WITH STROKE - LATIN CAPITAL LETTER THORN
return true;
if (wchar == 0x00DF) // LATIN SMALL LETTER SHARP S
return true;
if (wchar >= 0x00E0 && wchar <= 0x00F6) // LATIN SMALL LETTER A WITH GRAVE - LATIN SMALL LETTER O WITH DIAERESIS
return true;
if (wchar >= 0x00F8 && wchar <= 0x00FE) // LATIN SMALL LETTER O WITH STROKE - LATIN SMALL LETTER THORN
return true;
if (wchar >= 0x0100 && wchar <= 0x012F) // LATIN CAPITAL LETTER A WITH MACRON - LATIN SMALL LETTER I WITH OGONEK
return true;
if (wchar == 0x1E9E) // LATIN CAPITAL LETTER SHARP S
return true;
return false;
}
public static bool isBasicLatinCharacter(char wchar)
{
if (wchar >= 'a' && wchar <= 'z') // LATIN SMALL LETTER A - LATIN SMALL LETTER Z
return true;
if (wchar >= 'A' && wchar <= 'Z') // LATIN CAPITAL LETTER A - LATIN CAPITAL LETTER Z
return true;
return false;
}
#endregion
#region BinaryReader
public static string ReadCString(this BinaryReader reader)
{
byte num;
List<byte> temp = new List<byte>();
while ((num = reader.ReadByte()) != 0)
temp.Add(num);
return Encoding.UTF8.GetString(temp.ToArray());
}
public static string ReadString(this BinaryReader reader, int count)
{
var array = reader.ReadBytes(count);
return Encoding.ASCII.GetString(array);
}
public static string ReadStringFromChars(this BinaryReader reader, int count)
{
return new string(reader.ReadChars(count));
}
public static T Read<T>(this BinaryReader reader) where T : struct
{
byte[] result = reader.ReadBytes(Unsafe.SizeOf<T>());
return Unsafe.ReadUnaligned<T>(ref result[0]);
}
#endregion
}
}

73
SRP6.cs

@ -0,0 +1,73 @@ @@ -0,0 +1,73 @@
/*
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;
namespace Framework.Cryptography
{
public class SRP6
{
static SHA1 _sha1;
static BigInteger _g;
static BigInteger _N;
static SRP6()
{
_sha1 = new SHA1Managed();
_g = new BigInteger(7);
_N = new BigInteger(new byte[]
{
0x89, 0x4B, 0x64, 0x5E, 0x89, 0xE1, 0x53, 0x5B, 0xBD, 0xAD, 0x5B, 0x8B, 0x29, 0x06, 0x50, 0x53,
0x08, 0x01, 0xB1, 0x8E, 0xBF, 0xBF, 0x5E, 0x8F, 0xAB, 0x3C, 0x82, 0x87, 0x2A, 0x3E, 0x9B, 0xB7,
}, true, true);
}
public static (byte[] Salt, byte[] Verifier) MakeRegistrationData(string username, string password)
{
var salt = new byte[0].GenerateRandomKey(32); // random salt
return (salt, CalculateVerifier(username, password, salt));
}
[Obsolete]
public static (byte[] Salt, byte[] Verifier) MakeRegistrationDataFromHash(byte[] hash)
{
var salt = new byte[0].GenerateRandomKey(32); // random salt
return (salt, CalculateVerifierFromHash(hash, salt));
}
public static byte[] CalculateVerifier(string username, string password, byte[] salt)
{
// v = g ^ H(s || H(u || ':' || p)) mod N
return CalculateVerifierFromHash(_sha1.ComputeHash(Encoding.UTF8.GetBytes(username.ToUpperInvariant() + ":" + password.ToUpperInvariant())), salt);
}
// merge this into CalculateVerifier once the sha_pass hack finally gets nuked from orbit
public static byte[] CalculateVerifierFromHash(byte[] hash, byte[] salt)
{
// v = BigInteger.ModPow(gBN, x, BN);
return BigInteger.ModPow(_g, new BigInteger(_sha1.ComputeHash(salt.Combine(hash)), true), _N).ToByteArray();
}
public static bool CheckLogin(string username, string password, byte[] salt, byte[] verifier)
{
return verifier.Compare(CalculateVerifier(username, password, salt));
}
}
}
Loading…
Cancel
Save