Skip to content

Instantly share code, notes, and snippets.

@Rochet2
Last active December 27, 2022 11:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • 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 = BigInteger.Parse("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7", NumberStyles.AllowHexSpecifier);
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();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment