Implemented HashService and tests

This commit is contained in:
Khwezi Mngoma
2026-06-01 09:15:14 +02:00
parent c4f73fd999
commit 8fe129e19c
13 changed files with 193 additions and 38 deletions
+29 -17
View File
@@ -7,25 +7,37 @@ public sealed partial class HashService(IHashids hasher, IOptions<HasherSettings
{
private readonly HasherSettings settings = options.Value;
[System.Text.RegularExpressions.GeneratedRegex(@"\A\b[0-9a-fA-F]+\b\Z")]
private static partial System.Text.RegularExpressions.Regex HexHashRegex();
[GeneratedRegex(@"\A\b[0-9a-fA-F]+\b\Z")]
private static partial Regex HexHashRegex { get; }
public static readonly Func<string?, string?> StringToSha256Hash = (input) =>
string.IsNullOrEmpty(input) ? null : Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(input)));
[GeneratedRegex(@"\A[0-9a-fA-F]{32}\Z", RegexOptions.None, matchTimeoutMilliseconds: 100)]
private static partial Regex Md5Regex { get; }
public static readonly Func<Stream, string?> StreamToSha256Hash = (stream) =>
[GeneratedRegex(@"\A[0-9a-fA-F]{64}\Z", RegexOptions.None, matchTimeoutMilliseconds: 100)]
private static partial Regex Sha256Regex { get; }
public static bool IsMd5Hash(string? value) =>
!string.IsNullOrWhiteSpace(value) && Md5Regex.IsMatch(value);
public static bool IsSha256Hash(string? value) =>
!string.IsNullOrWhiteSpace(value) && Sha256Regex.IsMatch(value);
public static string? StringToSha256Hash(string? input) =>
string.IsNullOrEmpty(input) ? null : Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(input)));
public static string? StreamToSha256Hash(Stream stream) =>
stream is null ? null : Convert.ToHexString(SHA256.HashData(stream));
public static readonly Func<byte[], string?> BytesToSha256Hash = (bytes) =>
public static string? BytesToSha256Hash(byte[] bytes) =>
bytes is null ? null : Convert.ToHexString(SHA256.HashData(bytes));
public static Result<string> ComputeMd5Hash(string input)
public static Result<string> ToMd5Hash(string input)
{
if (string.IsNullOrEmpty(input))
return Result.Fail<string>("Input content cannot be null or empty for MD5 processing.");
byte[] bytes = MD5.HashData(Encoding.UTF8.GetBytes(input));
return Result.Ok(Convert.ToHexString(bytes).ToLowerInvariant());
return Result.Ok(Convert.ToHexString(bytes).ToLowerInvariant());
}
public Result<bool> VerifyPayfastWebhookSignature(IDictionary<string, string> incomingFormData, string incomingSignature)
@@ -36,16 +48,16 @@ public sealed partial class HashService(IHashids hasher, IOptions<HasherSettings
return Result.Fail<bool>("Validation failed: Missing signature string parameter.");
var sortedFields = incomingFormData
.Where(field => field.Key != "signature")
.OrderBy(field => field.Key)
.Select(field => $"{field.Key}={Uri.EscapeDataString(field.Value).Replace("%20", "+")}");
.Where(field => !string.Equals(field.Key, "signature", StringComparison.OrdinalIgnoreCase))
.OrderBy(field => field.Key, StringComparer.Ordinal)
.Select(field => $"{field.Key}={WebUtility.UrlEncode(field.Value)}");
string payload = string.Join("&", sortedFields);
if (!string.IsNullOrWhiteSpace(settings.PayfastPassphrase))
payload += $"&passphrase={Uri.EscapeDataString(settings.PayfastPassphrase).Replace("%20", "+")}";
payload += $"&passphrase={WebUtility.UrlEncode(settings.PayfastPassphrase)}";
var localHashResult = ComputeMd5Hash(payload);
var localHashResult = ToMd5Hash(payload);
if (!localHashResult.IsSuccess)
return Result.Fail<bool>(localHashResult.Errors);
@@ -60,9 +72,9 @@ public sealed partial class HashService(IHashids hasher, IOptions<HasherSettings
}
}
public Result<string> HashEncodeHex(string input) => string.IsNullOrWhiteSpace(input) || !HexHashRegex().IsMatch(input)
? Result.Fail<string>("Input must be a valid hexadecimal string.")
: Result.Ok(hasher.EncodeHex(input));
public Result<string> HashEncodeHex(string input) => string.IsNullOrWhiteSpace(input) || !HexHashRegex.IsMatch(input)
? Result.Fail<string>("Input must be a valid hexadecimal string.")
: Result.Ok(hasher.EncodeHex(input));
public Result<string> HashEncodeIntId(int id) => id < 0
? Result.Fail<string>("Id cannot be negative.")