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
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:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user