Skip to content

Instantly share code, notes, and snippets.

@Rochet2
Last active December 27, 2024 05:53
Show Gist options
  • Save Rochet2/3bb0adaf6f3e9a9fbc78ba5ce9a43e09 to your computer and use it in GitHub Desktop.
Save Rochet2/3bb0adaf6f3e9a9fbc78ba5ce9a43e09 to your computer and use it in GitHub Desktop.
SRP6 for TC in C#/csharp
using System;
using System.Security.Cryptography;
using System.Text;
using System.Numerics;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
public static class SRP6CryptoHasher {
private static readonly RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
// algorithm constants
private static readonly BigInteger g = 7;
private static readonly BigInteger N = new BigInteger("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7".ToByteArray(true), true);
public static(byte[] Salt, byte[] Verifier) GetSRP6RegistrationData(string username, string password) {
// generate a random salt
byte[] salt = new byte[32];
rngCsp.GetBytes(salt);
// calculate verifier using this salt
byte[] verifier = CalculateSRP6Verifier(username, password, salt);
// done - this is what you put in the account table!
return (salt, verifier);
}
public static byte[] CalculateSRP6Verifier(string username, string password, byte[] salt_bytes) {
SHA1 sha1 = SHA1.Create();
// calculate first hash
byte[] login_bytes = Encoding.UTF8.GetBytes((username + ':' + password).ToUpper());
byte[] h1_bytes = sha1.ComputeHash(login_bytes);
// calculate second hash
byte[] h2_bytes = sha1.ComputeHash(salt_bytes.Concat(h1_bytes).ToArray());
// convert to integer (little-endian)
BigInteger h2 = new BigInteger(h2_bytes, true);
// g^h2 mod N
BigInteger verifier = BigInteger.ModPow(g, h2, N);
// convert back to a byte array (little-endian)
byte[] verifier_bytes = verifier.ToByteArray();
// pad to 32 bytes, remember that zeros go on the end in little-endian!
byte[] verifier_bytes_padded = new byte[Math.Max(32, verifier_bytes.Length)];
Buffer.BlockCopy(verifier_bytes, 0, verifier_bytes_padded, 0, verifier_bytes.Length);
// done!
return verifier_bytes_padded;
}
public static 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 = CalculateSRP6Verifier(username, password, salt);
// compare it against the stored verifier
return verifier.SequenceEqual(checkVerifier);
}
public static void Main(string[] args) {
// Example code
// When creating account
// store the returned data to database
// Never store the actual password
var data = GetSRP6RegistrationData("username", "password");
// When logging in
// verify login
bool validLoginInfo = VerifySRP6Login("username", "password", data.Salt, data.Verifier);
Console.WriteLine("Username and password match the stored data: " + validLoginInfo);
// When program should close
rngCsp.Dispose();
}
}
@0775123
Copy link

0775123 commented Dec 26, 2024

Can't login latest TC,,invalid password!

Result result = GetSRP6RegistrationData(UserName, Pass);
...
cmd.Parameters.Add(new MySqlParameter("@username", MySqlDbType.VarChar) { Value = UserName.ToUpper() });
cmd.Parameters.Add(new MySqlParameter("@binarydata1", MySqlDbType.Binary) { Value = result.salt });
cmd.Parameters.Add(new MySqlParameter("@binaryData2", MySqlDbType.Binary) { Value = result.verifier });
cmd.ExecuteNonQuery();

@Rochet2
Copy link
Author

Rochet2 commented Dec 26, 2024

@0775123 Can you try now?
I originally coded this for someone else, and I was not able to compile or run it at that time. They tested and proposed a fix back then, which I now applied. Seems @Rudi9719 figured the same issue years ago here 👏

@Rochet2
Copy link
Author

Rochet2 commented Dec 26, 2024

Also, for further reference, this was coded based on the php examples here: TrinityCore/TrinityCore#25157

@0775123
Copy link

0775123 commented Dec 26, 2024

i can try now.

string query2 = "INSERT INTO account (username, salt, verifier) VALUES (@username,@binarydata1,@binaryData2)";
AuthConn.Open();

byte[] salt = new byte[32];
rngCsp.GetBytes(salt);

// calculate verifier using this salt
byte[] verifier = CalculateSRP6Verifier("TELLMEWHY", "TELLMEWHY", salt);

MySqlCommand cmd = new MySqlCommand(query2, AuthConn);

cmd.Parameters.Add(new MySqlParameter("@username", MySqlDbType.VarChar) { Value = UserName.ToUpper() });
cmd.Parameters.Add(new MySqlParameter("@binarydata1", MySqlDbType.Binary) { Value = salt });
cmd.Parameters.Add(new MySqlParameter("@binaryData2", MySqlDbType.Binary) { Value = verifier });

cmd.ExecuteNonQuery();//here insert db successful,

// my test here
var data = GetSRP6RegistrationData("TELLMEWHY", "TELLMEWHY");

// When logging in
// verify login
bool validLoginInfo = VerifySRP6Login("TELLMEWHY", "TELLMEWHY", data.salt, data.verifier);
ConsoleHelper.WriteErr(true, "Username and password match the stored data: " + validLoginInfo);
// validLoginInfo value is :True , but can't login TC
AuthConn.Close();

@Rochet2
Copy link
Author

Rochet2 commented Dec 26, 2024

Based on this commit: TrinityCore/TrinityCore@623202d
there might be new variants of the algorithm that need to be implemented or the implementation may need tweaking to work since a year ago.
What is the srp_version set to for the login info in battlenet_accounts? If it is 2, try setting it to 1.

@0775123
Copy link

0775123 commented Dec 26, 2024

thx, i'll try

@Rochet2
Copy link
Author

Rochet2 commented Dec 26, 2024

@0775123
Copy link

0775123 commented Dec 26, 2024

I tried all of the above and failed. test on tc3.3.5. .gave up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment