using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Options; using StellaOps.Cryptography; using StellaOps.Signer.Core; using StellaOps.Signer.Infrastructure.Options; namespace StellaOps.Signer.Infrastructure.Signing; public sealed class HmacDsseSigner : IDsseSigner { private readonly IOptionsMonitor _options; private readonly ICryptoHmac _cryptoHmac; private readonly TimeProvider _timeProvider; public HmacDsseSigner( IOptionsMonitor options, ICryptoHmac cryptoHmac, TimeProvider timeProvider) { _options = options ?? throw new ArgumentNullException(nameof(options)); _cryptoHmac = cryptoHmac ?? throw new ArgumentNullException(nameof(cryptoHmac)); _timeProvider = timeProvider ?? TimeProvider.System; } public ValueTask SignAsync( SigningRequest request, ProofOfEntitlementResult entitlement, CallerContext caller, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(request); ArgumentNullException.ThrowIfNull(entitlement); ArgumentNullException.ThrowIfNull(caller); var options = _options.CurrentValue; var payloadBytes = SignerStatementBuilder.BuildStatementPayload(request); var secretBytes = Convert.FromBase64String(options.Secret); var signature = _cryptoHmac.ComputeHmacBase64ForPurpose(secretBytes, payloadBytes, HmacPurpose.Signing); var payloadBase64 = Convert.ToBase64String(payloadBytes); var envelope = new DsseEnvelope( payloadBase64, "application/vnd.in-toto+json", new[] { new DsseSignature(signature, options.KeyId), }); var metadata = new SigningMetadata( new SigningIdentity( options.Mode, caller.Subject, caller.Subject, _timeProvider.GetUtcNow().AddMinutes(10)), Array.Empty(), options.ProviderName, options.AlgorithmId); var bundle = new SigningBundle(envelope, metadata); return ValueTask.FromResult(bundle); } }