Files
git.stella-ops.org/src/__Libraries/StellaOps.Audit.ReplayToken/Sha256ReplayTokenGenerator.cs

72 lines
2.6 KiB
C#

using StellaOps.Cryptography;
namespace StellaOps.Audit.ReplayToken;
/// <summary>
/// Generates replay tokens using SHA-256 hashing with deterministic canonicalization.
/// </summary>
public sealed partial class Sha256ReplayTokenGenerator : IReplayTokenGenerator
{
private readonly ICryptoHash _cryptoHash;
private readonly TimeProvider _timeProvider;
public Sha256ReplayTokenGenerator(ICryptoHash cryptoHash, TimeProvider timeProvider)
{
_cryptoHash = cryptoHash ?? throw new ArgumentNullException(nameof(cryptoHash));
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
}
public ReplayToken Generate(ReplayTokenRequest request)
{
ArgumentNullException.ThrowIfNull(request);
var hashHex = ComputeTokenValue(request, ReplayToken.DefaultVersion);
return new ReplayToken(hashHex, _timeProvider.GetUtcNow());
}
public ReplayToken GenerateWithExpiration(ReplayTokenRequest request, TimeSpan? expiration = null)
{
ArgumentNullException.ThrowIfNull(request);
var effectiveExpiration = expiration ?? ReplayToken.DefaultExpiration;
if (effectiveExpiration <= TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(expiration), "Expiration must be positive.");
}
var hashHex = ComputeTokenValue(request, ReplayToken.VersionWithExpiration);
var now = _timeProvider.GetUtcNow();
var expiresAt = now + effectiveExpiration;
return new ReplayToken(hashHex, now, expiresAt, ReplayToken.DefaultAlgorithm, ReplayToken.VersionWithExpiration);
}
public bool Verify(ReplayToken token, ReplayTokenRequest request)
{
ArgumentNullException.ThrowIfNull(token);
ArgumentNullException.ThrowIfNull(request);
var computed = ComputeTokenValue(request, token.Version);
return string.Equals(token.Value, computed, StringComparison.OrdinalIgnoreCase);
}
public ReplayTokenVerificationResult VerifyWithExpiration(ReplayToken token, ReplayTokenRequest request)
{
ArgumentNullException.ThrowIfNull(token);
ArgumentNullException.ThrowIfNull(request);
var computed = ComputeTokenValue(request, token.Version);
if (!string.Equals(token.Value, computed, StringComparison.OrdinalIgnoreCase))
{
return ReplayTokenVerificationResult.Invalid;
}
if (token.IsExpired(_timeProvider.GetUtcNow()))
{
return ReplayTokenVerificationResult.Expired;
}
return ReplayTokenVerificationResult.Valid;
}
}