Argon2

My hash verification method is wrong, can anyone tell me how I can fix it?
c#
using Konscious.Security.Cryptography;
using RegulatorioAuth.Application.Services.Interfaces;
using System.Security.Cryptography;
using System.Text;

public class PasswordHasherService : IPasswordHasherService
{
public (string HashedPassword, byte[] Salt) HashPassword(string password)
{
var salt = GenerateSalt();

var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password))
{
Salt = salt,
DegreeOfParallelism = 8,
MemorySize = 65536,
Iterations = 4
};

byte[] hash = argon2.GetBytes(16);
string hashedPassword = Convert.ToBase64String(hash);


return (hashedPassword, salt);
}

public bool VerifyPassword(string password, string hashedPassword, byte[] salt)
{
byte[] hashToVerify = Convert.FromBase64String(hashedPassword);

var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password))
{
Salt = salt,
DegreeOfParallelism = 8,
MemorySize = 65536,
Iterations = 4
};

return argon2.Equals(hashToVerify);
}

private static byte[] GenerateSalt()
{
byte[] salt = new byte[16];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
}
return salt;
}
}
c#
using Konscious.Security.Cryptography;
using RegulatorioAuth.Application.Services.Interfaces;
using System.Security.Cryptography;
using System.Text;

public class PasswordHasherService : IPasswordHasherService
{
public (string HashedPassword, byte[] Salt) HashPassword(string password)
{
var salt = GenerateSalt();

var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password))
{
Salt = salt,
DegreeOfParallelism = 8,
MemorySize = 65536,
Iterations = 4
};

byte[] hash = argon2.GetBytes(16);
string hashedPassword = Convert.ToBase64String(hash);


return (hashedPassword, salt);
}

public bool VerifyPassword(string password, string hashedPassword, byte[] salt)
{
byte[] hashToVerify = Convert.FromBase64String(hashedPassword);

var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password))
{
Salt = salt,
DegreeOfParallelism = 8,
MemorySize = 65536,
Iterations = 4
};

return argon2.Equals(hashToVerify);
}

private static byte[] GenerateSalt()
{
byte[] salt = new byte[16];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
}
return salt;
}
}
9 Replies
Lisa
Lisa3mo ago
You need to use SequenceEqual, not Equals. Equals for a byte array will only check if they are the same instance, not if they contain the same elements.
Jimmacle
Jimmacle3mo ago
in addition, you should use a constant-time implementation of SequenceEquals to make your code less vulnerable to timing attacks
KaykiLetieri.cs
KaykiLetieri.cs3mo ago
it would be something like that
c#
using Konscious.Security.Cryptography;
using RegulatorioAuth.Application.Services.Interfaces;
using System.Security.Cryptography;
using System.Text;

namespace RegulatorioAuth.Application.Services;

public class PasswordHasherService : IPasswordHasherService
{
public (string HashedPassword, byte[] Salt) HashPassword(string password)
{
var salt = GenerateSalt();

var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password))
{
Salt = salt,
DegreeOfParallelism = 8,
MemorySize = 65536,
Iterations = 4
};

byte[] hash = argon2.GetBytes(16);
string hashedPassword = Convert.ToBase64String(hash);


return (hashedPassword, salt);
}

public bool VerifyPassword(string password, string hashedPassword, byte[] salt)
{
byte[] hashToVerify = Convert.FromBase64String(hashedPassword);

var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password))
{
Salt = salt,
DegreeOfParallelism = 8,
MemorySize = 65536,
Iterations = 4
};

byte[] computedHash = argon2.GetBytes(hashToVerify.Length);

bool isEqual = ConstantTimeEquals(computedHash, hashToVerify);
return isEqual;
}

private static bool ConstantTimeEquals(byte[] a, byte[] b)
{
if (a == null || b == null || a.Length != b.Length)
return false;

int result = 0;
for (int i = 0; i < a.Length; i++)
{
result |= a[i] ^ b[i];
}
return result == 0;
}


private static byte[] GenerateSalt()
{
byte[] salt = new byte[16];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
}
return salt;
}
}
c#
using Konscious.Security.Cryptography;
using RegulatorioAuth.Application.Services.Interfaces;
using System.Security.Cryptography;
using System.Text;

namespace RegulatorioAuth.Application.Services;

public class PasswordHasherService : IPasswordHasherService
{
public (string HashedPassword, byte[] Salt) HashPassword(string password)
{
var salt = GenerateSalt();

var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password))
{
Salt = salt,
DegreeOfParallelism = 8,
MemorySize = 65536,
Iterations = 4
};

byte[] hash = argon2.GetBytes(16);
string hashedPassword = Convert.ToBase64String(hash);


return (hashedPassword, salt);
}

public bool VerifyPassword(string password, string hashedPassword, byte[] salt)
{
byte[] hashToVerify = Convert.FromBase64String(hashedPassword);

var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password))
{
Salt = salt,
DegreeOfParallelism = 8,
MemorySize = 65536,
Iterations = 4
};

byte[] computedHash = argon2.GetBytes(hashToVerify.Length);

bool isEqual = ConstantTimeEquals(computedHash, hashToVerify);
return isEqual;
}

private static bool ConstantTimeEquals(byte[] a, byte[] b)
{
if (a == null || b == null || a.Length != b.Length)
return false;

int result = 0;
for (int i = 0; i < a.Length; i++)
{
result |= a[i] ^ b[i];
}
return result == 0;
}


private static byte[] GenerateSalt()
{
byte[] salt = new byte[16];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
}
return salt;
}
}
Lisa
Lisa3mo ago
Imo I'd opt for a more complete and up to date library that handles most of the easy trip-ups for you
Lisa
Lisa3mo ago
GitHub
GitHub - mheyman/Isopoh.Cryptography.Argon2: Fully managed .Net Cor...
Fully managed .Net Core implementation of Argon2. Contribute to mheyman/Isopoh.Cryptography.Argon2 development by creating an account on GitHub.
KaykiLetieri.cs
KaykiLetieri.cs2mo ago
Thank you very much I will do that Hi Lisa, I wanted to touch base regarding the library we discussed earlier, Isopoh.Cryptography.Argon2. After some thorough investigation and testing, I must advise against using this specific library for our project. The issue lies in how the library handles the salt in Argon2 hashing. It was discovered that the salt wasn't effectively incorporated into the hashing process, rendering it essentially ineffective for its intended purpose of enhancing security.
Lisa
Lisa2mo ago
What makes you say so? I do not think your conclusion is correct.
KaykiLetieri.cs
KaykiLetieri.cs2mo ago
I made a post here on the server called: issue with unit test for Argon2. take a look
MODiX
MODiX2mo ago
Lisa
This is because you are using argon2Config.EncodeString EncodeString includes information about how the hash was computed, including the salt. In this case the provided salt is ignored and the salt from the hashstring is used.
React with ❌ to remove this embed.