using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; namespace Nuuru.Server.Services.BBCode { public interface IQuoteChecksumService { string GenerateHash(string sourceType, string sourceId, string content); bool VerifyHash(string sourceType, string sourceId, string content, string providedHash); string NormalizeContent(string content); } public partial class QuoteChecksumService : IQuoteChecksumService { private readonly byte[] _key; public QuoteChecksumService(IConfiguration configuration) { var keyString = configuration["BBCode:QuoteSigningKey"] ?? throw new InvalidOperationException("BBCode:QuoteSigningKey not configured"); _key = Encoding.UTF8.GetBytes(keyString); } public string GenerateHash(string sourceType, string sourceId, string content) { var normalized = NormalizeContent(content); var input = $"{sourceType}:{sourceId}:{normalized}"; var hash = HMACSHA256.HashData(_key, Encoding.UTF8.GetBytes(input)); return Convert.ToHexString(hash)[..8].ToLowerInvariant(); } public bool VerifyHash(string sourceType, string sourceId, string content, string providedHash) { var expectedHash = GenerateHash(sourceType, sourceId, content); return string.Equals(expectedHash, providedHash, StringComparison.OrdinalIgnoreCase); } public string NormalizeContent(string content) { // Normalize whitespace: collapse multiple whitespace chars to single space, trim return WhitespaceRegex().Replace(content.Trim(), " "); } [GeneratedRegex(@"\s+")] private static partial Regex WhitespaceRegex(); } }