- Add Tauri commands to inspect and adopt the gateway repo root - Retry locked vault commands by prompting for unlock - Improve mobile layout, editor mode toggles, and settings UI
70 lines
2.0 KiB
C#
70 lines
2.0 KiB
C#
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
namespace Journal.WebGateway.Security;
|
|
|
|
public static class GatewayPasswordHasher
|
|
{
|
|
private const string AlgorithmName = "PBKDF2-SHA256";
|
|
private const int SaltSize = 16;
|
|
private const int KeySize = 32;
|
|
private const int IterationCount = 600_000;
|
|
|
|
public static string HashPassword(string password)
|
|
{
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(password);
|
|
|
|
var salt = RandomNumberGenerator.GetBytes(SaltSize);
|
|
var key = Rfc2898DeriveBytes.Pbkdf2(
|
|
Encoding.UTF8.GetBytes(password),
|
|
salt,
|
|
IterationCount,
|
|
HashAlgorithmName.SHA256,
|
|
KeySize);
|
|
|
|
return string.Join(
|
|
'$',
|
|
AlgorithmName,
|
|
IterationCount.ToString(System.Globalization.CultureInfo.InvariantCulture),
|
|
Convert.ToBase64String(salt),
|
|
Convert.ToBase64String(key));
|
|
}
|
|
|
|
public static bool VerifyPassword(string password, string passwordHash)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(password) || string.IsNullOrWhiteSpace(passwordHash))
|
|
return false;
|
|
|
|
var parts = passwordHash.Split('$', StringSplitOptions.None);
|
|
if (parts.Length != 4)
|
|
return false;
|
|
|
|
if (!string.Equals(parts[0], AlgorithmName, StringComparison.Ordinal))
|
|
return false;
|
|
|
|
if (!int.TryParse(parts[1], out var iterations) || iterations <= 0)
|
|
return false;
|
|
|
|
byte[] salt;
|
|
byte[] expectedKey;
|
|
try
|
|
{
|
|
salt = Convert.FromBase64String(parts[2]);
|
|
expectedKey = Convert.FromBase64String(parts[3]);
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var actualKey = Rfc2898DeriveBytes.Pbkdf2(
|
|
Encoding.UTF8.GetBytes(password),
|
|
salt,
|
|
iterations,
|
|
HashAlgorithmName.SHA256,
|
|
expectedKey.Length);
|
|
|
|
return CryptographicOperations.FixedTimeEquals(actualKey, expectedKey);
|
|
}
|
|
}
|