Implement VEX document verification system with issuer management and signature verification
- Added IIssuerDirectory interface for managing VEX document issuers, including methods for registration, revocation, and trust validation. - Created InMemoryIssuerDirectory class as an in-memory implementation of IIssuerDirectory for testing and single-instance deployments. - Introduced ISignatureVerifier interface for verifying signatures on VEX documents, with support for multiple signature formats. - Developed SignatureVerifier class as the default implementation of ISignatureVerifier, allowing extensibility for different signature formats. - Implemented handlers for DSSE and JWS signature formats, including methods for verification and signature extraction. - Defined various records and enums for issuer and signature metadata, enhancing the structure and clarity of the verification process.
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Policy.Engine.AirGap;
|
||||
|
||||
namespace StellaOps.Policy.Engine.Endpoints;
|
||||
|
||||
/// <summary>
|
||||
/// Endpoints for sealed-mode operations per CONTRACT-SEALED-MODE-004.
|
||||
/// </summary>
|
||||
public static class SealedModeEndpoints
|
||||
{
|
||||
public static IEndpointRouteBuilder MapSealedMode(this IEndpointRouteBuilder routes)
|
||||
{
|
||||
var group = routes.MapGroup("/system/airgap");
|
||||
|
||||
group.MapPost("/seal", SealAsync)
|
||||
.WithName("AirGap.Seal")
|
||||
.WithDescription("Seal the environment")
|
||||
.RequireAuthorization(policy => policy.RequireClaim("scope", "airgap:seal"))
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.ProducesProblem(StatusCodes.Status500InternalServerError);
|
||||
|
||||
group.MapPost("/unseal", UnsealAsync)
|
||||
.WithName("AirGap.Unseal")
|
||||
.WithDescription("Unseal the environment")
|
||||
.RequireAuthorization(policy => policy.RequireClaim("scope", "airgap:seal"))
|
||||
.ProducesProblem(StatusCodes.Status500InternalServerError);
|
||||
|
||||
group.MapGet("/status", GetStatusAsync)
|
||||
.WithName("AirGap.GetStatus")
|
||||
.WithDescription("Get sealed-mode status")
|
||||
.RequireAuthorization(policy => policy.RequireClaim("scope", "airgap:status:read"));
|
||||
|
||||
group.MapPost("/verify", VerifyBundleAsync)
|
||||
.WithName("AirGap.VerifyBundle")
|
||||
.WithDescription("Verify a bundle against trust roots")
|
||||
.RequireAuthorization(policy => policy.RequireClaim("scope", "airgap:verify"))
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.ProducesProblem(StatusCodes.Status422UnprocessableEntity);
|
||||
|
||||
return routes;
|
||||
}
|
||||
|
||||
private static async Task<IResult> SealAsync(
|
||||
[FromHeader(Name = "X-Tenant-Id")] string? tenantId,
|
||||
[FromBody] SealRequest request,
|
||||
ISealedModeService service,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tenantId))
|
||||
{
|
||||
tenantId = "default";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var response = await service.SealAsync(tenantId, request, cancellationToken).ConfigureAwait(false);
|
||||
return Results.Ok(response);
|
||||
}
|
||||
catch (SealedModeException ex)
|
||||
{
|
||||
return SealedModeResultHelper.ToProblem(ex);
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
return SealedModeResultHelper.ToProblem(
|
||||
SealedModeErrorCodes.SealFailed,
|
||||
ex.Message,
|
||||
"Ensure all required parameters are provided");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return SealedModeResultHelper.ToProblem(
|
||||
SealedModeErrorCodes.SealFailed,
|
||||
$"Seal operation failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<IResult> UnsealAsync(
|
||||
[FromHeader(Name = "X-Tenant-Id")] string? tenantId,
|
||||
ISealedModeService service,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tenantId))
|
||||
{
|
||||
tenantId = "default";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var response = await service.UnsealAsync(tenantId, cancellationToken).ConfigureAwait(false);
|
||||
return Results.Ok(response);
|
||||
}
|
||||
catch (SealedModeException ex)
|
||||
{
|
||||
return SealedModeResultHelper.ToProblem(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return SealedModeResultHelper.ToProblem(
|
||||
SealedModeErrorCodes.UnsealFailed,
|
||||
$"Unseal operation failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetStatusAsync(
|
||||
[FromHeader(Name = "X-Tenant-Id")] string? tenantId,
|
||||
ISealedModeService service,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tenantId))
|
||||
{
|
||||
tenantId = "default";
|
||||
}
|
||||
|
||||
var status = await service.GetStatusAsync(tenantId, cancellationToken).ConfigureAwait(false);
|
||||
return Results.Ok(status);
|
||||
}
|
||||
|
||||
private static async Task<IResult> VerifyBundleAsync(
|
||||
[FromBody] BundleVerifyRequest request,
|
||||
ISealedModeService service,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await service.VerifyBundleAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// Return problem details if verification failed
|
||||
if (!response.Valid && response.VerificationResult.Error is not null)
|
||||
{
|
||||
return SealedModeResultHelper.ToProblem(
|
||||
SealedModeErrorCodes.SignatureInvalid,
|
||||
response.VerificationResult.Error,
|
||||
"Verify bundle integrity and trust root configuration",
|
||||
422);
|
||||
}
|
||||
|
||||
return Results.Ok(response);
|
||||
}
|
||||
catch (SealedModeException ex)
|
||||
{
|
||||
return SealedModeResultHelper.ToProblem(ex);
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
return SealedModeResultHelper.ToProblem(
|
||||
SealedModeErrorCodes.BundleInvalid,
|
||||
ex.Message,
|
||||
"Ensure bundle path is valid and accessible");
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
return SealedModeResultHelper.ToProblem(
|
||||
SealedModeErrorCodes.BundleInvalid,
|
||||
$"Bundle file not found: {ex.FileName ?? ex.Message}",
|
||||
"Verify the bundle path is correct");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user