/*************************************************************************
* ModernUO *
* Copyright 2019-2026 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: HashAlgorithmPasswordProtection.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
*************************************************************************/
using System;
using System.Security.Cryptography;
using Server.Text;
namespace Server.Accounting.Security;
///
/// The obsolete unsalted digests, kept only so imported accounts can log in once and be upgraded.
///
/// Hashing goes through the one-shot static APIs rather than a retained .
/// A instance carries the running digest across HashCore/HashFinal, so
/// two threads sharing one corrupt each other's result -- and these are process-wide singletons.
/// The static form has no such state, allocates nothing, and produces identical bytes.
///
public class HashAlgorithmPasswordProtection : IPasswordProtection
{
private enum Kind
{
MD5,
SHA1,
SHA512
}
public static readonly IPasswordProtection MD5Instance = new HashAlgorithmPasswordProtection(Kind.MD5);
public static readonly IPasswordProtection SHA1Instance = new HashAlgorithmPasswordProtection(Kind.SHA1);
public static readonly IPasswordProtection SHA2Instance = new HashAlgorithmPasswordProtection(Kind.SHA512);
private const int MaxDigestLength = 64; // SHA512, the largest of the three.
private readonly Kind _kind;
private HashAlgorithmPasswordProtection(Kind kind) => _kind = kind;
public string EncryptPassword(string plainPassword)
{
var bytes = plainPassword.AsSpan(0, Math.Min(256, plainPassword.Length)).GetBytesAscii();
Span digest = stackalloc byte[MaxDigestLength];
var written = _kind switch
{
Kind.MD5 => MD5.HashData(bytes, digest),
Kind.SHA1 => SHA1.HashData(bytes, digest),
_ => SHA512.HashData(bytes, digest)
};
return digest[..written].ToHexString();
}
public bool ValidatePassword(string encryptedPassword, string plainPassword) =>
EncryptPassword(plainPassword) == encryptedPassword;
}