1

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

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Emil Emy
  • 11
  • 1
  • It's hard to tell exactly what's going wrong without any specific error messages or debugging information, but one thing that stands out is that the C# implementation uses a different random number generation algorithm than the PHP and Python implementations. This could potentially lead to differences in the generated salt and verifier values, which could cause the authentication to fail. – Wahyu Kristianto Mar 22 '23 at 13:17
  • For testing and debugging, remove all of the random number generation and switch to fixed values – Chris Haas Mar 22 '23 at 13:26

0 Answers0