mysimulation/server/FSO.Server.Common/PasswordHasher.cs
Tony Bark 22191ce648 Removed NioTSO client and server
- NioTSO client isn't needed because we're using RayLib
- Added FreeSO's API server to handle most backend operations
2024-05-01 02:55:43 -04:00

103 lines
2.9 KiB
C#
Executable file

using System;
using System.Linq;
using System.Security.Cryptography;
namespace FSO.Server.Common
{
public class PasswordHasher
{
public static PasswordHash Hash(string password)
{
return Hash(password, "Rfc2898");
}
public static PasswordHash Hash(string password, string scheme)
{
var schemeImpl = GetScheme(scheme);
return schemeImpl.Hash(password);
}
public static bool Verify(string password, PasswordHash hash)
{
return Verify(password, hash, "Rfc2898");
}
public static bool Verify(string password, PasswordHash hash, string scheme)
{
var schemeImpl = GetScheme(scheme);
return schemeImpl.Verify(password, hash);
}
private static IPasswordHashScheme GetScheme(string scheme)
{
switch (scheme)
{
case "Rfc2898":
return new DefaultPasswordHashScheme();
}
throw new Exception("Unknown password hash scheme: " + scheme);
}
}
public class PasswordHash
{
public byte[] data;
public string scheme;
}
public class DefaultPasswordHashScheme : IPasswordHashScheme
{
public PasswordHash Hash(string password)
{
var salt_input = GetStrongRandomBytes(16);
return new PasswordHash()
{
scheme = "Rfc2898",
data = Hash(salt_input, password)
};
}
private byte[] Hash(byte[] salt_input, string password)
{
var hasher = new Rfc2898DeriveBytes(System.Text.Encoding.UTF8.GetBytes(password), salt_input, 1000);
var hash = hasher.GetBytes(64);
//Encode the salt + hash together
var result = new byte[1 + 16 + hash.Length];
result[0] = (byte)16;
Array.Copy(salt_input, 0, result, 1, salt_input.Length);
Array.Copy(hash, 0, result, salt_input.Length + 1, hash.Length);
return result;
}
public bool Verify(string password, PasswordHash hash)
{
var salt_length = hash.data[0];
var salt_input = new byte[salt_length];
Array.Copy(hash.data, 1, salt_input, 0, salt_length);
var expected = Hash(salt_input, password);
return expected.SequenceEqual(hash.data);
}
private byte[] GetStrongRandomBytes(int numBytes)
{
var random_bytes = new byte[numBytes];
using (RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider())
{
rngCsp.GetBytes(random_bytes);
}
return random_bytes;
}
}
public interface IPasswordHashScheme
{
PasswordHash Hash(string password);
bool Verify(string password, PasswordHash hash);
}
}