ModernUO/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs
Kamron Batman c25a644a33 refactor(accounts): drop the migrated-password repair path
Every ModernUO shard has always run Argon2, so the RunUO/ServUO migration
shapes the repair path existed to recover do not occur in practice. It was
never free: it retried a failed verify against the other phrase rule, and
nothing in a stored hash separates a mis-migrated credential from a password
that merely begins with the username -- which is why it needed a per-account
tag on top of the config switch to be safe at all.

Removing it takes accountSecurity.repairMigratedPasswords, the RepairPasswordTag
opt-in and the forced rehash with it. The rehash on a successful login is now
implicit: stale algorithm or stale parameters, nothing else.

Also trims the development narrative out of the comments left behind.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 23:51:43 -07:00

58 lines
2.8 KiB
C#

/*************************************************************************
* ModernUO *
* Copyright 2019-2026 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Argon2PasswordProtection.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 <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System.Security.Cryptography;
namespace Server.Accounting.Security;
public class Argon2PasswordProtection : IPasswordProtection
{
public static IPasswordProtection Instance = new Argon2PasswordProtection();
// 16 MiB at t=1 is cheaper than 8 MiB at t=3 (8.5 ms vs 10.1 ms) and twice as memory-hard, which
// is what resists GPU and ASIC cracking. p=1: native argon2 spawns a thread per lane.
private readonly Argon2PasswordHasher _passwordHasher = new(
time: 1,
memory: 16384,
parallel: 1,
type: Argon2Type.Argon2id,
rng: RandomNumberGenerator.Create()
);
public string EncryptPassword(string plainPassword) =>
_passwordHasher.Hash(plainPassword);
public bool ValidatePassword(string encryptedPassword, string plainPassword) =>
_passwordHasher.Verify(encryptedPassword, plainPassword);
// The PHC string carries the parameters it was hashed with, so verification uses those rather
// than the configured ones. Comparing them is what lets a parameter change reach existing
// accounts.
public bool NeedsRehash(string encryptedPassword)
{
// Unparseable but verified: a format this build does not understand, so rewrite it.
if (!Argon2PasswordHasher.TryExtractMetadataValues(encryptedPassword, out var values))
{
return true;
}
return values.ArgonType != _passwordHasher.ArgonType
|| values.MemoryCost != _passwordHasher.MemoryCost
|| values.TimeCost != _passwordHasher.TimeCost
|| values.Parallelism != _passwordHasher.Parallelism
|| values.HashLength != (int)_passwordHasher.HashLength
|| values.SaltLength != (int)_passwordHasher.SaltLength;
}
}