public static (HashingPreferences Prefs, byte[] Salt, byte[] Hash) DecodeHash(string encodedHash) { if (!encodedHash.StartsWith("$argon2id")) { throw new Exception("not an argon2id encoded hash"); } var split = encodedHash.Split("$"); if (split.Length != 6) { throw new Exception("invalid hash format"); } if (GetKvValue("v", split[2]) != ARGON2ID_VERSION) { throw new Exception("invalid hash version"); } var prefs = new HashingPreferences(); var paramsSplit = split[3].Split(","); prefs.MemorySize = GetKvValue("m", paramsSplit[0]); prefs.Iterations = GetKvValue("t", paramsSplit[1]); prefs.DegreeOfParallelism = GetKvValue("p", paramsSplit[2]); var salt = Convert.FromBase64String(split[4]); prefs.SaltLength = salt.Length; var hash = Convert.FromBase64String(split[5]); prefs.KeyLength = hash.Length; return(prefs, salt, hash); }
public async Task <bool> CompareEncodedHash(string password, string encodedHash) { if (password == null) { throw new ArgumentNullException(nameof(password)); } if (password.Length == 0) { throw new ArgumentException("invalid length", nameof(password)); } if (encodedHash == null) { throw new ArgumentNullException(nameof(encodedHash)); } if (encodedHash.Length == 0) { throw new ArgumentException("invalid length", nameof(encodedHash)); } var(prefs, salt, hash) = HashingPreferences.DecodeHash(encodedHash); var hasher = prefs.MakeUnsaltedHasher(password); hasher.Salt = salt; var compHash = await hasher.GetBytesAsync(prefs.KeyLength); return(compHash.SequenceEqual(hash)); }