131 lines
4.4 KiB
C#
131 lines
4.4 KiB
C#
using Konscious.Security.Cryptography;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
public class Hashers
|
|
{
|
|
struct settings
|
|
{
|
|
public short parallel;
|
|
public short memory;
|
|
public short iterations;
|
|
public short bc;
|
|
}
|
|
|
|
static settings[] all_versions = new settings[]
|
|
{
|
|
new settings { }, // Custom
|
|
new settings { parallel = 4, memory = 64, iterations = 3, bc = 32 },
|
|
};
|
|
|
|
const int use_version = 2;
|
|
|
|
public static string Argon2id_hash(string password)
|
|
{
|
|
byte[] raw_salt = new byte[16];
|
|
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
|
|
{
|
|
rng.GetBytes(raw_salt);
|
|
}
|
|
settings complexity = all_versions[use_version - 1];
|
|
|
|
byte[] raw_hash = argon2id_hash(password, raw_salt, complexity);
|
|
|
|
string base64_hash = Convert.ToBase64String(raw_hash);
|
|
string base64_salt = Convert.ToBase64String(raw_salt);
|
|
|
|
return String.Format("{0}${1}${2}${3}", use_version, FormatComplexity(complexity), base64_salt, base64_hash);
|
|
}
|
|
|
|
public static string Argon2id_hash_custom(string password, short parallel, short memory, short iterations, short bc)
|
|
{
|
|
byte[] raw_salt = new byte[16];
|
|
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
|
|
{
|
|
rng.GetBytes(raw_salt);
|
|
}
|
|
settings complexity = new settings { parallel = parallel, memory = memory, iterations = iterations, bc = bc };
|
|
|
|
byte[] raw_hash = argon2id_hash(password, raw_salt, complexity);
|
|
|
|
string base64_hash = Convert.ToBase64String(raw_hash);
|
|
string base64_salt = Convert.ToBase64String(raw_salt);
|
|
|
|
return String.Format("{0}${1}${2}${3}", 1, FormatComplexity(complexity), base64_salt, base64_hash);
|
|
}
|
|
|
|
public static bool Argon2id_verify(string password, string hash)
|
|
{
|
|
string[] split = hash.Split('$');
|
|
if (split.Length == 0)
|
|
{
|
|
throw new Exception("Invalid or unknown Argon2id hash");
|
|
}
|
|
short version = Convert.ToInt16(split[0]);
|
|
if (version != 1 && version != 2)
|
|
{
|
|
throw new Exception("Invalid or unknown Argon2id hash version");
|
|
}
|
|
// version$settings$salt$hash
|
|
if (split.Length != 4)
|
|
{
|
|
throw new Exception("Invalid or unknown Argon2id hash format");
|
|
}
|
|
|
|
settings s = ParseComplexity(version, split[1]);
|
|
byte[] raw_salt = Convert.FromBase64String(split[2]);
|
|
|
|
byte[] raw_hash = argon2id_hash(password, raw_salt, s);
|
|
string new_hash = Convert.ToBase64String(raw_hash);
|
|
Console.WriteLine("{0} == {1}", new_hash, split[3]);
|
|
return new_hash == split[3];
|
|
}
|
|
|
|
private static byte[] argon2id_hash(string password, byte[] salt, settings complexity)
|
|
{
|
|
using (var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password)))
|
|
{
|
|
argon2.DegreeOfParallelism = complexity.parallel;
|
|
argon2.MemorySize = 1024 * complexity.memory;
|
|
argon2.Iterations = complexity.iterations;
|
|
argon2.Salt = salt;
|
|
|
|
return argon2.GetBytes(complexity.bc);
|
|
}
|
|
}
|
|
|
|
private static string FormatComplexity(settings complexity)
|
|
{
|
|
return String.Format("p={0};m={1};i={2};bc={3}", complexity.parallel, complexity.memory, complexity.iterations, complexity.bc);
|
|
}
|
|
|
|
private static settings ParseComplexity(int version, string raw_complexity)
|
|
{
|
|
if (version != 1 && version != 2)
|
|
{
|
|
throw new Exception("Invalid or unknown Argon2id hash version in ParseComplexity");
|
|
}
|
|
settings s = new settings();
|
|
|
|
foreach (string item in raw_complexity.Split(';'))
|
|
{
|
|
string[] key_value = item.Split('=');
|
|
if (key_value.Length != 2) { continue; }
|
|
|
|
if (key_value[0] == "p") { s.parallel = Convert.ToInt16(key_value[1]); }
|
|
if (key_value[0] == "m") { s.memory = Convert.ToInt16(key_value[1]); }
|
|
if (key_value[0] == "i") { s.iterations = Convert.ToInt16(key_value[1]); }
|
|
if (key_value[0] == "bc") { s.bc = Convert.ToInt16(key_value[1]); }
|
|
}
|
|
|
|
if (s.parallel == 0 || s.memory == 0 || s.iterations == 0 || s.bc == 0)
|
|
{
|
|
throw new Exception("Invalid or missing Argon2id hash settings");
|
|
}
|
|
return s;
|
|
}
|
|
}
|