BcryptNet / bcrypt.net

BCrypt.Net - Bringing updates to the original bcrypt package

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to validate hash format?

AlirezaAsadi96 opened this issue · comments

Hi.
How to found current hash is valid?
Below method validate hash and replace new password hash:
BCrypt.ValidateAndReplacePassword
But I don't want replace hash, Just validate current hash without password like this BCrypt.ValidateHash(currentHash)
How can I do that?

BCrypt.Net.BCrypt.Verify(givenPassword, hashedPassword)

@AlirezaAsadi96 you just want to validate the format or you want to determine that its a bcrypt hash?

@AlirezaAsadi96 you just want to validate the format or you want to determine that its a bcrypt hash?

Just validate the format of bcrypt hash

It's not really something I'd expose as its more of a utility function than part of the bcrypt public api but you can easily add code like this.

using System;

public class Program
{
	private static readonly HashFormatDescriptor OldFormatDescriptor = new HashFormatDescriptor(versionLength: 1);
	private static readonly HashFormatDescriptor NewFormatDescriptor = new HashFormatDescriptor(versionLength: 2);
	public class HashFormatDescriptor
	{
		public HashFormatDescriptor(int versionLength)
		{
			VersionLength = versionLength;
			WorkfactorOffset = 1 + VersionLength + 1;
			SettingLength = WorkfactorOffset + 2;
			HashOffset = SettingLength + 1;
		}

		public int VersionLength
		{
			get;
		}

		public int WorkfactorOffset
		{
			get;
		}

		public int SettingLength
		{
			get;
		}

		public int HashOffset
		{
			get;
		}
	}

	public static bool IsValidHash(string hash, out HashFormatDescriptor format)
	{
		if (hash is null)
		{
			throw new ArgumentNullException(nameof(hash));
		}

		if (hash.Length != 59 && hash.Length != 60)
		{
			// Incorrect full hash length
			format = null;
			return false;
		}

		if (!hash.StartsWith("$2"))
		{
			// Not a bcrypt hash
			format = null;
			return false;
		}

		// Validate version
		int offset = 2;
		if (IsValidBCryptVersionChar(hash[offset]))
		{
			offset++;
			format = NewFormatDescriptor;
		}
		else
		{
			format = OldFormatDescriptor;
		}

		if (hash[offset++] != '$')
		{
			format = null;
			return false;
		}

		// Validate workfactor
		if (!IsAsciiNumeric(hash[offset++]) || !IsAsciiNumeric(hash[offset++]))
		{
			format = null;
			return false;
		}

		if (hash[offset++] != '$')
		{
			format = null;
			return false;
		}

		// Validate hash
		for (int i = offset; i < hash.Length; ++i)
		{
			if (!IsValidBCryptBase64Char(hash[i]))
			{
				format = null;
				return false;
			}
		}

		return true;
	}

	private static bool IsValidBCryptVersionChar(char value)
	{
		return value == 'a' || value == 'b' || value == 'x' || value == 'y';
	}

	private static bool IsValidBCryptBase64Char(char value)
	{
		// Ordered by ascending ASCII value
		return value == '.' || value == '/' || (value >= '0' && value <= '9') || (value >= 'A' && value <= 'Z') || (value >= 'a' && value <= 'z');
	}

	private static bool IsAsciiNumeric(char value)
	{
		return value >= '0' && value <= '9';
	}

	public static void Main()
	{
		Console.WriteLine(IsValidHash("$2y$12$WDviXbO4Y8VM.aS.vmv7FOt3/KztzzVjLb7U/NOMjdzdXDiBbaHRC", out _));
		Console.WriteLine(IsValidHash("X2y$12$WDviXbO4Y8VM.aS.vmv7FOt3/KztzzVjLb7U/NOMjdzdXDiBbaHRC", out _));
	}
}

You can sling it in https://dotnetfiddle.net/ to test