feat: Add DigestUpsertRequest and LockEntity models
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled

- Introduced DigestUpsertRequest for handling digest upsert requests with properties like ChannelId, Recipient, DigestKey, Events, and CollectUntil.
- Created LockEntity to represent a lightweight distributed lock entry with properties such as Id, TenantId, Resource, Owner, ExpiresAt, and CreatedAt.

feat: Implement ILockRepository interface and LockRepository class

- Defined ILockRepository interface with methods for acquiring and releasing locks.
- Implemented LockRepository class with methods to try acquiring a lock and releasing it, using SQL for upsert operations.

feat: Add SurfaceManifestPointer record for manifest pointers

- Introduced SurfaceManifestPointer to represent a minimal pointer to a Surface.FS manifest associated with an image digest.

feat: Create PolicySimulationInputLock and related validation logic

- Added PolicySimulationInputLock record to describe policy simulation inputs and expected digests.
- Implemented validation logic for policy simulation inputs, including checks for digest drift and shadow mode requirements.

test: Add unit tests for ReplayVerificationService and ReplayVerifier

- Created ReplayVerificationServiceTests to validate the behavior of the ReplayVerificationService under various scenarios.
- Developed ReplayVerifierTests to ensure the correctness of the ReplayVerifier logic.

test: Implement PolicySimulationInputLockValidatorTests

- Added tests for PolicySimulationInputLockValidator to verify the validation logic against expected inputs and conditions.

chore: Add cosign key example and signing scripts

- Included a placeholder cosign key example for development purposes.
- Added a script for signing Signals artifacts using cosign with support for both v2 and v3.

chore: Create script for uploading evidence to the evidence locker

- Developed a script to upload evidence to the evidence locker, ensuring required environment variables are set.
This commit is contained in:
StellaOps Bot
2025-12-03 07:51:50 +02:00
parent 37cba83708
commit e923880694
171 changed files with 6567 additions and 2952 deletions

View File

@@ -0,0 +1,14 @@
using System.Text.Json.Serialization;
namespace StellaOps.AirGap.Importer.Contracts;
/// <summary>
/// Replay enforcement depth for offline kit verification.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ReplayDepth
{
HashOnly,
FullRecompute,
PolicyFreeze
}

View File

@@ -0,0 +1,21 @@
namespace StellaOps.AirGap.Importer.Contracts;
/// <summary>
/// Inputs required to enforce replay depth for an offline kit.
/// </summary>
public sealed record ReplayVerificationRequest(
string ExpectedManifestSha256,
string ExpectedBundleSha256,
string ComputedManifestSha256,
string ComputedBundleSha256,
DateTimeOffset ManifestCreatedAt,
int StalenessWindowHours,
string? BundlePolicyHash,
string? SealedPolicyHash,
ReplayDepth Depth);
public sealed record ReplayVerificationResult(bool IsValid, string Reason)
{
public static ReplayVerificationResult Success(string reason = "ok") => new(true, reason);
public static ReplayVerificationResult Failure(string reason) => new(false, reason);
}

View File

@@ -0,0 +1,60 @@
using StellaOps.AirGap.Importer.Contracts;
namespace StellaOps.AirGap.Importer.Validation;
/// <summary>
/// Enforces replay depth semantics for offline kit validation.
/// </summary>
public sealed class ReplayVerifier
{
public ReplayVerificationResult Verify(ReplayVerificationRequest request, DateTimeOffset nowUtc)
{
if (string.IsNullOrWhiteSpace(request.ExpectedManifestSha256) ||
string.IsNullOrWhiteSpace(request.ExpectedBundleSha256) ||
string.IsNullOrWhiteSpace(request.ComputedManifestSha256) ||
string.IsNullOrWhiteSpace(request.ComputedBundleSha256))
{
return ReplayVerificationResult.Failure("hash-missing");
}
if (!string.Equals(request.ExpectedManifestSha256, request.ComputedManifestSha256, StringComparison.OrdinalIgnoreCase))
{
return ReplayVerificationResult.Failure("manifest-hash-drift");
}
if (!string.Equals(request.ExpectedBundleSha256, request.ComputedBundleSha256, StringComparison.OrdinalIgnoreCase))
{
return ReplayVerificationResult.Failure("bundle-hash-drift");
}
if (request.StalenessWindowHours >= 0)
{
var age = nowUtc - request.ManifestCreatedAt;
if (age > TimeSpan.FromHours(request.StalenessWindowHours))
{
return ReplayVerificationResult.Failure("manifest-stale");
}
}
if (request.Depth == ReplayDepth.PolicyFreeze)
{
if (string.IsNullOrWhiteSpace(request.SealedPolicyHash) || string.IsNullOrWhiteSpace(request.BundlePolicyHash))
{
return ReplayVerificationResult.Failure("policy-hash-missing");
}
if (!string.Equals(request.BundlePolicyHash, request.SealedPolicyHash, StringComparison.OrdinalIgnoreCase))
{
return ReplayVerificationResult.Failure("policy-hash-drift");
}
}
return request.Depth switch
{
ReplayDepth.HashOnly => ReplayVerificationResult.Success("hash-only-passed"),
ReplayDepth.FullRecompute => ReplayVerificationResult.Success("full-recompute-passed"),
ReplayDepth.PolicyFreeze => ReplayVerificationResult.Success("policy-freeze-passed"),
_ => ReplayVerificationResult.Failure("unknown-depth")
};
}
}