I want to write a function in C# that do the same thing as the function shown here; PHP and Python are both working, but the last one in C# does not work. I do not know what I am doing wrong, I cannot even debug because the hash is always different, please help me if do you know.
Thanks
PHP:
function calculateSRP6Verifier($username, $password, $salt)
{
$g = gmp_init(7);
$N = gmp_init('894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7', 16);
$h1 = sha1(strtoupper($username . ':' . $password), TRUE);
$h2 = sha1(strrev($salt) . $h1, TRUE);
$h2 = gmp_import($h2, 1, GMP_LSW_FIRST);
$verifier = gmp_powm($g, $h2, $N);
$verifier = gmp_export($verifier, 1, GMP_LSW_FIRST);
$verifier = str_pad($verifier, 32, chr(0), STR_PAD_RIGHT);
$verifier = strrev($verifier);
return $verifier;
}
function getRegistrationData($username, $password)
{
$salt = random_bytes(32);
$verifier = calculateSRP6Verifier($username, $password, $salt);
$salt = strtoupper(bin2hex($salt)); // From haukw
$verifier = strtoupper(bin2hex($verifier));
return array($salt, $verifier);
}
Python:
def CalculateSRP6Verifier(username, password, salt):
g = int(7)
N = int("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7", 16)
userpassupper = f'{username}:{password}'.upper()
h1 = hashlib.sha1(userpassupper.encode('utf-8')).digest()
salt = salt[::-1]
h2 = hashlib.sha1(salt + h1)
h2 = int.from_bytes(h2.digest(), 'little')
verifier = pow(g,h2,N)
verifier = verifier.to_bytes(32, 'little')
verifier = verifier.ljust(32, b'\x00')
verifier = verifier[::-1]
return verifier
def GetSRP6RegistrationData(username, password):
salt = secrets.token_bytes(32)
verifier = CalculateSRP6Verifier(username, password, salt)
print(salt.hex().upper())
print(verifier.hex().upper())
return [salt.hex().upper(), verifier.hex().upper()]
C#:
public static string ToHexString(this byte[] s)
{
StringBuilder sub = new StringBuilder();
foreach (var x in s)
{
sub.Append(x.ToString("X2"));
}
return sub.ToString();
}
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 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 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 class SRP6
{
public static (string Salt, string Verifier) MakeRegistrationData(string username, string password)
{
var s = new byte[0].GenerateRandomKey(32); // random salt
var v = CalculateVerifier(username, password, s);
var salt = s.ToHexString().ToUpper();
var verifier = v.ToHexString().ToUpper();
return (salt, verifier);
}
public static byte[] CalculateVerifier(string username, string password, byte[] salt)
{
SHA1 _sha1 = SHA1.Create();
BigInteger _g = new BigInteger(7);
BigInteger _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, false);
string userPassUpper = username.ToUpper() + ":" + password.ToUpper();
var h1 = _sha1.ComputeHash(Encoding.UTF8.GetBytes(userPassUpper));
Array.Reverse(salt);
var h2 = salt.Combine(h1);
var h2int = new BigInteger(h2, false);
var v = BigInteger.ModPow(_g, h2int, _N);
byte[] verifier = v.ToByteArray();
if (verifier.Length < 32)
{
var paddedVerifier = new byte[32];
Array.Copy(verifier, 0, paddedVerifier, 32 - verifier.Length, verifier.Length);
verifier = paddedVerifier;
}
Array.Reverse(verifier);
return verifier;
}
public static string Reverse( string s )
{
char[] charArray = s.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
}
I tried around everything that google showed me in first 3 pages, even in GitHub repositories and from many implementation I tried but it do not work, I spend more that 50 hours and no success