feat: Implement DefaultCryptoHmac for compliance-aware HMAC operations
- Added DefaultCryptoHmac class implementing ICryptoHmac interface. - Introduced purpose-based HMAC computation methods. - Implemented verification methods for HMACs with constant-time comparison. - Created HmacAlgorithms and HmacPurpose classes for well-known identifiers. - Added compliance profile support for HMAC algorithms. - Included asynchronous methods for HMAC computation from streams.
This commit is contained in:
@@ -8,6 +8,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Cryptography;
|
||||
using StellaOps.Scanner.Surface.Env;
|
||||
using StellaOps.Scanner.Surface.Secrets;
|
||||
using StellaOps.Scanner.Worker.Options;
|
||||
@@ -18,20 +19,23 @@ namespace StellaOps.Scanner.Worker.Processing.Surface;
|
||||
/// DSSE envelope signer that prefers an HMAC key (deterministic) and falls back to
|
||||
/// the deterministic hash-only signer when no key is configured.
|
||||
/// </summary>
|
||||
internal sealed class HmacDsseEnvelopeSigner : IDsseEnvelopeSigner, IDisposable
|
||||
internal sealed class HmacDsseEnvelopeSigner : IDsseEnvelopeSigner
|
||||
{
|
||||
private readonly ILogger<HmacDsseEnvelopeSigner> _logger;
|
||||
private readonly ScannerWorkerOptions _options;
|
||||
private readonly ICryptoHmac _cryptoHmac;
|
||||
private readonly DeterministicDsseEnvelopeSigner _deterministic = new();
|
||||
private readonly HMACSHA256? _hmac;
|
||||
private readonly byte[]? _secretBytes;
|
||||
private readonly string _keyId;
|
||||
|
||||
public HmacDsseEnvelopeSigner(
|
||||
IOptions<ScannerWorkerOptions> options,
|
||||
ICryptoHmac cryptoHmac,
|
||||
ILogger<HmacDsseEnvelopeSigner> logger,
|
||||
IServiceProvider serviceProvider)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
_cryptoHmac = cryptoHmac ?? throw new ArgumentNullException(nameof(cryptoHmac));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_options = options.Value ?? throw new ArgumentNullException(nameof(options));
|
||||
|
||||
@@ -52,8 +56,8 @@ internal sealed class HmacDsseEnvelopeSigner : IDsseEnvelopeSigner, IDisposable
|
||||
|
||||
if (secretBytes is not null && secretBytes.Length > 0)
|
||||
{
|
||||
_hmac = new HMACSHA256(secretBytes);
|
||||
_logger.LogInformation("DSSE signing enabled using HMAC-SHA256 with key id {KeyId}", _keyId);
|
||||
_secretBytes = secretBytes;
|
||||
_logger.LogInformation("DSSE signing enabled using HMAC with key id {KeyId}", _keyId);
|
||||
}
|
||||
else if (!signing.AllowDeterministicFallback)
|
||||
{
|
||||
@@ -67,13 +71,13 @@ internal sealed class HmacDsseEnvelopeSigner : IDsseEnvelopeSigner, IDisposable
|
||||
|
||||
public Task<DsseEnvelope> SignAsync(string payloadType, ReadOnlyMemory<byte> content, string suggestedKind, string merkleRoot, string? view, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_hmac is null)
|
||||
if (_secretBytes is null)
|
||||
{
|
||||
return _deterministic.SignAsync(payloadType, content, suggestedKind, merkleRoot, view, cancellationToken);
|
||||
}
|
||||
|
||||
var pae = BuildPae(payloadType, content.Span);
|
||||
var signatureBytes = _hmac.ComputeHash(pae);
|
||||
var signatureBytes = _cryptoHmac.ComputeHmacForPurpose(_secretBytes, pae, HmacPurpose.Signing);
|
||||
var envelope = new
|
||||
{
|
||||
payloadType,
|
||||
@@ -96,11 +100,6 @@ internal sealed class HmacDsseEnvelopeSigner : IDsseEnvelopeSigner, IDisposable
|
||||
return Task.FromResult(new DsseEnvelope("application/vnd.dsse+json", uri, digest, bytes));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_hmac?.Dispose();
|
||||
}
|
||||
|
||||
private static byte[]? LoadSecret(ScannerWorkerOptions.SigningOptions signing)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(signing.SharedSecretFile) && File.Exists(signing.SharedSecretFile))
|
||||
|
||||
Reference in New Issue
Block a user