Argon2.Bindings 1.20.0 resolves the Argon2 type from the stored hash, so existing Argon2i credentials keep verifying after this switch. Without that bump this change locks out every existing account -- the pinned legacy-hash test fails on 1.19.0 for exactly that reason. 16 MiB t=1 measures 8.51 ms against the old 8 MiB t=3 Argon2i at 10.11 ms: cheaper AND stronger. Memory-hardness resists GPU and ASIC cracking; iterations mostly buy wall-clock, so trade t down for m up. It also stays below the 16-32 MiB L3 inflection, which starts to matter once hashing moves off the game loop and contends with the loop's working set. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
42 lines
2.2 KiB
C#
42 lines
2.2 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();
|
|
|
|
// Argon2id over Argon2i: RFC 9106 recommends Argon2i only where side-channel resistance is
|
|
// required and memory is scarce. 16 MiB at t=1 measures cheaper than the old 8 MiB at t=3
|
|
// (8.5 ms vs 10.1 ms) while doubling memory-hardness, which is the property that resists GPU
|
|
// and ASIC cracking; iterations mostly buy wall-clock. p=1 because native argon2 spawns a
|
|
// thread per lane, which is oversubscription on the 1-2 core hosts this path exists to serve.
|
|
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);
|
|
}
|