Rename public affiliate route to /support with localized nav labels, and include affiliate/social/wallpaper admin, 2FA login, and related site updates. Co-authored-by: Cursor <cursoragent@cursor.com>
105 lines
3.1 KiB
C#
105 lines
3.1 KiB
C#
using System;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
namespace atakanozbancom.Models.classes
|
|
{
|
|
public static class PasswordHasher
|
|
{
|
|
private const string Prefix = "pbkdf2";
|
|
private const int DefaultIterations = 100000;
|
|
private const int SaltSize = 16;
|
|
private const int HashSize = 32;
|
|
|
|
public static string Hash(string password)
|
|
{
|
|
if (password == null)
|
|
throw new ArgumentNullException("password");
|
|
|
|
var salt = new byte[SaltSize];
|
|
using (var rng = RandomNumberGenerator.Create())
|
|
{
|
|
rng.GetBytes(salt);
|
|
}
|
|
|
|
var hash = Pbkdf2(password, salt, DefaultIterations, HashSize);
|
|
return string.Format(
|
|
"{0}${1}${2}${3}",
|
|
Prefix,
|
|
DefaultIterations,
|
|
Convert.ToBase64String(salt),
|
|
Convert.ToBase64String(hash));
|
|
}
|
|
|
|
public static bool IsHashed(string stored)
|
|
{
|
|
return !string.IsNullOrWhiteSpace(stored)
|
|
&& stored.StartsWith(Prefix + "$", StringComparison.Ordinal);
|
|
}
|
|
|
|
public static bool Verify(string password, string stored)
|
|
{
|
|
if (string.IsNullOrEmpty(password) || string.IsNullOrEmpty(stored))
|
|
return false;
|
|
|
|
if (!IsHashed(stored))
|
|
return FixedTimeEquals(password, stored);
|
|
|
|
var parts = stored.Split('$');
|
|
if (parts.Length != 4 || !string.Equals(parts[0], Prefix, StringComparison.Ordinal))
|
|
return false;
|
|
|
|
int iterations;
|
|
if (!int.TryParse(parts[1], out iterations) || iterations < 1000)
|
|
return false;
|
|
|
|
byte[] salt;
|
|
byte[] expected;
|
|
try
|
|
{
|
|
salt = Convert.FromBase64String(parts[2]);
|
|
expected = Convert.FromBase64String(parts[3]);
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var actual = Pbkdf2(password, salt, iterations, expected.Length);
|
|
return FixedTimeEquals(actual, expected);
|
|
}
|
|
|
|
private static byte[] Pbkdf2(string password, byte[] salt, int iterations, int length)
|
|
{
|
|
using (var derive = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256))
|
|
{
|
|
return derive.GetBytes(length);
|
|
}
|
|
}
|
|
|
|
private static bool FixedTimeEquals(byte[] a, byte[] b)
|
|
{
|
|
if (a == null || b == null || a.Length != b.Length)
|
|
return false;
|
|
|
|
var diff = 0;
|
|
for (var i = 0; i < a.Length; i++)
|
|
diff |= a[i] ^ b[i];
|
|
|
|
return diff == 0;
|
|
}
|
|
|
|
private static bool FixedTimeEquals(string a, string b)
|
|
{
|
|
if (a == null || b == null || a.Length != b.Length)
|
|
return false;
|
|
|
|
var diff = 0;
|
|
for (var i = 0; i < a.Length; i++)
|
|
diff |= a[i] ^ b[i];
|
|
|
|
return diff == 0;
|
|
}
|
|
}
|
|
}
|