up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
SDK Publish & Sign / sdk-publish (push) Has been cancelled
sdk-generator-smoke / sdk-smoke (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-11-27 08:51:10 +02:00
parent ea970ead2a
commit c34fb7256d
126 changed files with 18553 additions and 693 deletions

View File

@@ -0,0 +1,116 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using StellaOps.Attestation;
using StellaOps.Attestor.Envelope;
using StellaOps.Cryptography;
namespace StellaOps.Authority.Signing;
/// <summary>
/// Signs In-toto statements as DSSE envelopes using Authority's active signing key.
/// Supports SBOM, Graph, VEX, Replay, and other StellaOps predicate types.
/// </summary>
public interface IAuthorityDsseStatementSigner
{
/// <summary>
/// Signs an In-toto statement and returns a DSSE envelope.
/// </summary>
/// <param name="statement">The In-toto statement to sign.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The signed DSSE envelope containing the statement.</returns>
Task<DsseEnvelope> SignStatementAsync(InTotoStatement statement, CancellationToken cancellationToken = default);
/// <summary>
/// Gets the key ID of the active signing key.
/// </summary>
string? ActiveKeyId { get; }
/// <summary>
/// Indicates whether signing is enabled and configured.
/// </summary>
bool IsEnabled { get; }
}
/// <summary>
/// Result of signing an In-toto statement.
/// </summary>
/// <param name="Envelope">The signed DSSE envelope.</param>
/// <param name="KeyId">The key ID used for signing.</param>
/// <param name="Algorithm">The signing algorithm used.</param>
public sealed record DsseStatementSignResult(
DsseEnvelope Envelope,
string KeyId,
string Algorithm);
/// <summary>
/// Implementation of <see cref="IAuthorityDsseStatementSigner"/> that uses Authority's
/// signing key manager to sign In-toto statements with DSSE envelopes.
/// </summary>
internal sealed class AuthorityDsseStatementSigner : IAuthorityDsseStatementSigner
{
private readonly AuthoritySigningKeyManager keyManager;
private readonly ICryptoProviderRegistry registry;
private readonly ILogger<AuthorityDsseStatementSigner> logger;
public AuthorityDsseStatementSigner(
AuthoritySigningKeyManager keyManager,
ICryptoProviderRegistry registry,
ILogger<AuthorityDsseStatementSigner> logger)
{
this.keyManager = keyManager ?? throw new ArgumentNullException(nameof(keyManager));
this.registry = registry ?? throw new ArgumentNullException(nameof(registry));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public string? ActiveKeyId => keyManager.Snapshot.ActiveKeyId;
public bool IsEnabled => !string.IsNullOrWhiteSpace(keyManager.Snapshot.ActiveKeyId);
public async Task<DsseEnvelope> SignStatementAsync(InTotoStatement statement, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(statement);
var snapshot = keyManager.Snapshot;
if (string.IsNullOrWhiteSpace(snapshot.ActiveKeyId))
{
throw new InvalidOperationException("Authority signing is not configured. Enable signing before creating attestations.");
}
if (string.IsNullOrWhiteSpace(snapshot.ActiveProvider))
{
throw new InvalidOperationException("Authority signing provider is not configured.");
}
var signerResolution = registry.ResolveSigner(
CryptoCapability.Signing,
GetAlgorithmForKey(snapshot),
new CryptoKeyReference(snapshot.ActiveKeyId!),
snapshot.ActiveProvider);
var adapter = new AuthoritySignerAdapter(signerResolution.Signer);
logger.LogDebug(
"Signing In-toto statement with predicate type {PredicateType} using key {KeyId}.",
statement.PredicateType,
snapshot.ActiveKeyId);
var envelope = await DsseHelper.WrapAsync(statement, adapter, cancellationToken).ConfigureAwait(false);
logger.LogInformation(
"Created DSSE envelope for predicate type {PredicateType}, key {KeyId}, {SignatureCount} signature(s).",
statement.PredicateType,
snapshot.ActiveKeyId,
envelope.Signatures.Count);
return envelope;
}
private static string GetAlgorithmForKey(SigningKeySnapshot snapshot)
{
// Default to ES256 if not explicitly specified
// The AuthoritySigningKeyManager normalises algorithm during load
return SignatureAlgorithms.Es256;
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using StellaOps.Attestation;
using StellaOps.Cryptography;
namespace StellaOps.Authority.Signing;
/// <summary>
/// Adapts an <see cref="ICryptoSigner"/> to the <see cref="IAuthoritySigner"/> interface
/// used by attestation signing helpers.
/// </summary>
internal sealed class AuthoritySignerAdapter : IAuthoritySigner
{
private readonly ICryptoSigner signer;
public AuthoritySignerAdapter(ICryptoSigner signer)
{
this.signer = signer ?? throw new ArgumentNullException(nameof(signer));
}
public Task<string> GetKeyIdAsync(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult(signer.KeyId);
}
public async Task<byte[]> SignAsync(ReadOnlyMemory<byte> paePayload, CancellationToken cancellationToken = default)
{
return await signer.SignAsync(paePayload, cancellationToken).ConfigureAwait(false);
}
}

View File

@@ -32,6 +32,7 @@
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography.Kms/StellaOps.Cryptography.Kms.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.Configuration/StellaOps.Configuration.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.DependencyInjection/StellaOps.DependencyInjection.csproj" />
<ProjectReference Include="../../../Attestor/StellaOps.Attestation/StellaOps.Attestation.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="..\..\StellaOps.Api.OpenApi\authority\openapi.yaml" Link="OpenApi\authority.yaml">