semi implemented and features implemented save checkpoint

This commit is contained in:
master
2026-02-08 18:00:49 +02:00
parent 04360dff63
commit 1bf6bbf395
20895 changed files with 716795 additions and 64 deletions

View File

@@ -0,0 +1,28 @@
# CI/CD Keyless Signing Workflow Templates (GitHub/GitLab/Gitea)
## Module
Signer
## Status
IMPLEMENTED
## Description
Production-ready reusable CI/CD workflow templates for keyless signing integration across GitHub Actions (stellaops-sign.yml, stellaops-verify.yml), GitLab CI (.gitlab-ci-stellaops.yml), and Gitea. Enables zero-configuration OIDC-based keyless signing with identity verification gates and cross-platform signature verification.
## Implementation Details
- **SigstoreSigningService**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Infrastructure/Sigstore/SigstoreSigningService.cs` -- orchestrates complete Sigstore keyless signing: (1) generate ephemeral ECDSA P-256 key pair, (2) compute SHA-256 artifact hash, (3) create proof-of-possession by signing OIDC token, (4) request certificate from Fulcio, (5) sign artifact with ephemeral key, (6) upload to Rekor transparency log; VerifyKeylessAsync validates signature, certificate, and Rekor entry timestamp
- **SigstoreServiceCollectionExtensions**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Infrastructure/Sigstore/SigstoreServiceCollectionExtensions.cs` -- DI registration for Sigstore services
- **SigstoreOptions**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Infrastructure/Sigstore/SigstoreOptions.cs` -- configurable Fulcio URL, Rekor URL, RequireRekorEntry flag, retry/backoff settings
- **SignerEndpoints**: `src/Signer/StellaOps.Signer/StellaOps.Signer.WebService/Endpoints/SignerEndpoints.cs` -- signing API endpoints consumed by CI/CD workflow templates
- **AmbientOidcTokenProvider**: `src/Signer/__Libraries/StellaOps.Signer.Keyless/AmbientOidcTokenProvider.cs` -- detects OIDC tokens from CI runner environment (GitHub Actions, GitLab CI, Gitea)
- **KeylessDsseSigner**: `src/Signer/__Libraries/StellaOps.Signer.Keyless/KeylessDsseSigner.cs` -- DSSE signer used by workflow templates for in-toto statement signing
- **Source**: SPRINT_20251226_004_BE_cicd_signing_templates.md
## E2E Test Plan
- [ ] Verify signing endpoint accepts OIDC identity token and returns signed DSSE envelope with certificate chain
- [ ] Verify verification endpoint validates signature, certificate chain, and Rekor entry
- [ ] Test ambient OIDC token detection for GitHub Actions, GitLab CI, and Gitea CI environments
- [ ] Verify Rekor transparency log entry is created when RequireRekorEntry is enabled
- [ ] Verify signing fails gracefully when Fulcio is unavailable (proper error response)
- [ ] Test cross-platform signature verification: sign on GitHub Actions, verify on GitLab CI
- [ ] Verify signed artifacts include proper in-toto statement format with subject digests

View File

@@ -0,0 +1,32 @@
# Dual-Control Signing Ceremonies (M-of-N Threshold)
## Module
Signer
## Status
IMPLEMENTED
## Description
Orchestrator for M-of-N threshold signing ceremonies requiring multiple authorized participants to approve key operations, with API endpoints for ceremony initiation, participant enrollment, share submission, and ceremony completion.
## Implementation Details
- **CeremonyOrchestrator**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Core/Ceremonies/CeremonyOrchestrator.cs` -- full M-of-N orchestration: CreateCeremonyAsync (configurable threshold/expiration per operation type), ApproveCeremonyAsync (duplicate detection, approver validation via ICeremonyApproverValidator, signature verification), ExecuteCeremonyAsync (only from Approved state), CancelCeremonyAsync, ProcessExpiredCeremoniesAsync (batch expiry); ICeremonyAuditSink for all lifecycle events
- **CeremonyStateMachine**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Core/Ceremonies/CeremonyStateMachine.cs` -- enforces valid state transitions: Pending -> PartiallyApproved -> Approved -> Executed; terminal states (Executed/Expired/Cancelled); CanAcceptApproval, CanExecute, CanCancel guards; ComputeStateAfterApproval for threshold-based transitions
- **CeremonyModels**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Core/Ceremonies/CeremonyModels.cs` -- Ceremony, CeremonyApproval, CeremonyResult, CeremonyFilter, CeremonyState enum (Pending/PartiallyApproved/Approved/Executed/Expired/Cancelled), CeremonyOperationType enum (KeyGeneration/KeyRotation/KeyRevocation/KeyExport/KeyImport/KeyRecovery), CeremonyErrorCode enum
- **CeremonyOptions**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Core/Ceremonies/CeremonyOptions.cs` -- configurable thresholds and expiration per operation type
- **CeremonyAuditEvents**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Core/Ceremonies/CeremonyAuditEvents.cs` -- Initiated, Approved, ApprovalRejected, ThresholdReached, Executed, Cancelled, Expired audit event types
- **ICeremonyOrchestrator**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Core/Ceremonies/ICeremonyOrchestrator.cs` -- interface contract
- **ICeremonyRepository**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Core/Ceremonies/ICeremonyRepository.cs` -- persistence interface: CreateAsync, GetByIdAsync, HasApprovedAsync, AddApprovalAsync, UpdateStateAsync, ListAsync, GetExpiredCeremoniesAsync, MarkExpiredAsync
- **CeremonyEndpoints**: `src/Signer/StellaOps.Signer/StellaOps.Signer.WebService/Endpoints/CeremonyEndpoints.cs` -- REST API at `/api/v1/ceremonies`: POST / (create, ceremony:create), GET / (list with state/operationType/initiatedBy/tenantId/limit/offset filters), GET /{ceremonyId} (get by ID), POST /{ceremonyId}/approve (ceremony:approve, base64 signature required), POST /{ceremonyId}/execute (ceremony:execute), DELETE /{ceremonyId} (ceremony:cancel); all require ceremony:read authorization
- **Tests**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Tests/Ceremonies/CeremonyOrchestratorIntegrationTests.cs`, `CeremonyStateMachineTests.cs`
- **Source**: SPRINT_20260112_018_SIGNER_dual_control_ceremonies.md
## E2E Test Plan
- [ ] POST /api/v1/ceremonies creates a new ceremony with threshold, expiration, and operation type; verify 201 response with ceremonyId
- [ ] POST /{ceremonyId}/approve accepts approval with base64 signature; verify duplicate approval returns 409, unauthorized approver returns 403
- [ ] Verify state transitions: Pending -> PartiallyApproved (after first approval) -> Approved (when threshold reached) -> Executed (after execution)
- [ ] POST /{ceremonyId}/execute succeeds only when state is Approved; verify 409 for non-approved states
- [ ] DELETE /{ceremonyId} cancels ceremony; verify only non-terminal ceremonies can be cancelled
- [ ] Verify expired ceremonies cannot accept approvals or be executed (409)
- [ ] GET / returns filtered list with pagination (limit/offset) and state/operationType filters
- [ ] Verify audit events are recorded for all lifecycle transitions (Initiated, Approved, Executed, Cancelled, Expired)

View File

@@ -0,0 +1,36 @@
# Fulcio/Sigstore Keyless Signing Client
## Module
Signer
## Status
IMPLEMENTED
## Description
Fulcio-based keyless signing using OIDC tokens from CI runners, ephemeral key pairs, short-lived X.509 certificates, DSSE signing, and certificate chain validation. Tests exist for all components.
## Implementation Details
- **KeylessDsseSigner**: `src/Signer/__Libraries/StellaOps.Signer.Keyless/KeylessDsseSigner.cs` -- DSSE signer implementing Sigstore keyless workflow: (1) acquire OIDC token via IOidcTokenProvider, (2) generate ephemeral key pair via IEphemeralKeyGenerator, (3) serialize in-toto statement, (4) create proof-of-possession (SHA-256 hash signed with ephemeral key), (5) request short-lived certificate from Fulcio, (6) create DSSE signature using ephemeral key; returns SigningBundle with DsseEnvelope + certificate chain + signing identity metadata
- **EphemeralKeyGenerator**: `src/Signer/__Libraries/StellaOps.Signer.Keyless/EphemeralKeyGenerator.cs` -- generates ECDSA P-256 key pairs using .NET crypto APIs; supports EcdsaP256 algorithm; Ed25519 placeholder for future .NET 9+ support
- **HttpFulcioClient**: `src/Signer/__Libraries/StellaOps.Signer.Keyless/HttpFulcioClient.cs` -- HTTP client for Fulcio v2 API (`/api/v2/signingCert`): sends OIDC token + public key + proof-of-possession; parses PEM certificate chain from response; extracts OIDC issuer from Fulcio extension OID 1.3.6.1.4.1.57264.1.1; retry logic with exponential backoff; non-retryable errors (400/401/403) vs retryable (5xx)
- **SigstoreSigningService**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Infrastructure/Sigstore/SigstoreSigningService.cs` -- orchestrates full Sigstore keyless flow: ephemeral ECDSA P-256 key generation, Fulcio certificate request, artifact signing, Rekor transparency log upload; VerifyKeylessAsync validates signature, certificate validity, and Rekor entry timestamp within certificate window
- **IOidcTokenProvider**: `src/Signer/__Libraries/StellaOps.Signer.Keyless/IOidcTokenProvider.cs` -- interface for OIDC token acquisition
- **AmbientOidcTokenProvider**: `src/Signer/__Libraries/StellaOps.Signer.Keyless/AmbientOidcTokenProvider.cs` -- CI runner ambient OIDC token detection
- **EphemeralKeyPair**: `src/Signer/__Libraries/StellaOps.Signer.Keyless/EphemeralKeyPair.cs` -- disposable key pair model with Sign method
- **ICertificateChainValidator**: `src/Signer/__Libraries/StellaOps.Signer.Keyless/ICertificateChainValidator.cs` -- certificate chain validation interface
- **FulcioHttpClient**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Infrastructure/Sigstore/FulcioHttpClient.cs` -- infrastructure-level Fulcio client
- **RekorHttpClient**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Infrastructure/Sigstore/RekorHttpClient.cs` -- Rekor transparency log HTTP client
- **ISigstoreClients**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Infrastructure/Sigstore/ISigstoreClients.cs` -- IFulcioClient and IRekorClient interfaces
- **SigstoreOptions**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Infrastructure/Sigstore/SigstoreOptions.cs` -- Fulcio URL, Rekor URL, RequireRekorEntry, retry/backoff config
- **Tests**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Tests/Keyless/KeylessDsseSignerTests.cs`, `EphemeralKeyGeneratorTests.cs`, `HttpFulcioClientTests.cs`, `CertificateChainValidatorTests.cs`, `KeylessSigningIntegrationTests.cs`
- **Source**: Feature matrix scan
## E2E Test Plan
- [ ] Verify keyless signing produces a valid DSSE envelope with base64-encoded payload and signature
- [ ] Verify certificate chain includes leaf certificate from Fulcio and intermediate/root certificates
- [ ] Verify proof-of-possession is computed as SHA-256 hash of statement signed with ephemeral key
- [ ] Test Fulcio client retry logic with exponential backoff on 5xx errors
- [ ] Verify non-retryable Fulcio errors (400/401/403) fail immediately
- [ ] Test keyless verification validates signature, certificate chain, and Rekor timestamp
- [ ] Verify signing identity metadata includes OIDC issuer, subject, and certificate expiry
- [ ] Test ephemeral key disposal after signing completes

View File

@@ -0,0 +1,34 @@
# Key Rotation Service with Temporal Validity
## Module
Signer
## Status
IMPLEMENTED
## Description
Automated key rotation service with temporal key validity windows, key history tracking (key_history and key_audit_log tables), trust anchor management with PURL pattern matching, and CLI commands for key lifecycle operations. Ensures proof verification uses the correct key for the attestation timestamp.
## Implementation Details
- **KeyRotationService**: `src/Signer/__Libraries/StellaOps.Signer.KeyManagement/KeyRotationService.cs` -- implements advisory section 8.2: AddKeyAsync (validates algorithm against AllowedAlgorithms, creates KeyHistoryEntity + KeyAuditLogEntity, updates anchor AllowedKeyIds), RevokeKeyAsync (sets RevokedAt + RevokeReason, moves key from AllowedKeyIds to RevokedKeyIds), CheckKeyValidityAsync (temporal validation: NotYetValid if signedAt < AddedAt, Revoked if signedAt >= RevokedAt, Expired if signedAt >= ExpiresAt), GetRotationWarningsAsync (ExpiryApproaching within ExpiryWarningDays, LongLived exceeding MaxKeyAgeDays, AlgorithmDeprecating for deprecated algorithms), GetKeyHistoryAsync; EF Core transactions with InMemory provider detection
- **TrustAnchorManager**: `src/Signer/__Libraries/StellaOps.Signer.KeyManagement/TrustAnchorManager.cs` -- implements advisory section 8.3: GetAnchorAsync, FindAnchorForPurlAsync (glob-style PURL pattern matching with specificity scoring, most-specific-match-wins), CreateAnchorAsync (validates PURL pattern), UpdateAnchorAsync (AllowedPredicateTypes/PolicyRef/PolicyVersion), DeactivateAnchorAsync, VerifySignatureAuthorizationAsync (combines temporal key validity + predicate type authorization), GetActiveAnchorsAsync
- **PurlPatternMatcher**: `src/Signer/__Libraries/StellaOps.Signer.KeyManagement/TrustAnchorManager.cs` -- glob-to-regex pattern matching for PURLs (e.g., pkg:npm/*, pkg:maven/org.apache/*); specificity scoring (segments * 10 - wildcards * 5)
- **KeyRotationOptions**: configurable AllowedAlgorithms (ES256, ES384, ES512, RS256, EdDSA, SM2, GOST12-256, DILITHIUM3, FALCON512, etc.), ExpiryWarningDays (60), MaxKeyAgeDays (365), DeprecatedAlgorithms (RSA-2048, SHA1-RSA)
- **KeyManagementDbContext**: `src/Signer/__Libraries/StellaOps.Signer.KeyManagement/KeyManagementDbContext.cs` -- EF Core context with TrustAnchors, KeyHistory, KeyAuditLog DbSets
- **KeyEntities**: `src/Signer/__Libraries/StellaOps.Signer.KeyManagement/Entities/KeyEntities.cs` -- KeyHistoryEntity (HistoryId, AnchorId, KeyId, PublicKey, Algorithm, AddedAt, ExpiresAt, RevokedAt, RevokeReason), KeyAuditLogEntity (LogId, Operation enum: Add/Revoke/Rotate, Actor, Reason)
- **TrustAnchorEntity**: `src/Signer/__Libraries/StellaOps.Signer.KeyManagement/Entities/TrustAnchorEntity.cs` -- AnchorId, PurlPattern, AllowedKeyIds, RevokedKeyIds, AllowedPredicateTypes, PolicyRef, PolicyVersion, IsActive
- **KeyRotationEndpoints**: `src/Signer/StellaOps.Signer/StellaOps.Signer.WebService/Endpoints/KeyRotationEndpoints.cs` -- REST API at `/api/v1/anchors`: POST /{anchorId}/keys (add key), POST /{anchorId}/keys/{keyId}/revoke, GET /{anchorId}/keys/{keyId}/validity?signedAt=, GET /{anchorId}/keys/history, GET /{anchorId}/keys/warnings; all require KeyManagement authorization
- **IKeyRotationService**: `src/Signer/__Libraries/StellaOps.Signer.KeyManagement/IKeyRotationService.cs` -- interface contract
- **ITrustAnchorManager**: `src/Signer/__Libraries/StellaOps.Signer.KeyManagement/ITrustAnchorManager.cs` -- interface contract
- **Tests**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Tests/KeyManagement/KeyRotationServiceTests.cs`, `TemporalKeyVerificationTests.cs`, `TrustAnchorManagerTests.cs`, `Integration/KeyRotationWorkflowIntegrationTests.cs`
- **Source**: SPRINT_0501_0008_0001_proof_chain_key_rotation.md
## E2E Test Plan
- [ ] POST /api/v1/anchors/{anchorId}/keys adds a key and returns updated AllowedKeyIds with audit log ID
- [ ] POST /{anchorId}/keys/{keyId}/revoke sets RevokedAt and moves key from allowed to revoked list
- [ ] GET /{anchorId}/keys/{keyId}/validity returns correct temporal validity (Active, NotYetValid, Revoked, Expired) for a given signedAt timestamp
- [ ] Verify temporal key validation: key added at T1 is invalid for signatures before T1, valid between T1 and revocation/expiry
- [ ] GET /{anchorId}/keys/warnings returns ExpiryApproaching, LongLived, and AlgorithmDeprecating warnings
- [ ] Verify PURL pattern matching finds most-specific anchor for a given PURL
- [ ] Verify VerifySignatureAuthorizationAsync combines key validity + predicate type check
- [ ] Verify algorithm validation rejects keys with unsupported algorithms

View File

@@ -0,0 +1,32 @@
# Shamir Secret Sharing Key Escrow
## Module
Signer
## Status
IMPLEMENTED
## Description
Key escrow system using Shamir's Secret Sharing over GF(256) to split signing keys into M-of-N shares distributed to escrow agents, with ceremony-authorized recovery requiring quorum approval.
## Implementation Details
- **ShamirSecretSharing**: `src/Cryptography/StellaOps.Cryptography/KeyEscrow/ShamirSecretSharing.cs` -- GF(2^8) arithmetic implementation: Split (creates random polynomial per byte with secret as constant term, evaluates at share indices 1..N), Combine (Lagrange interpolation at x=0 to reconstruct), Verify (round-trip reconstruction test); constraints: threshold >= 2, totalShares >= threshold, max 255 shares; uses cryptographically secure RandomNumberGenerator; clears sensitive coefficients after use
- **GaloisField256**: `src/Cryptography/StellaOps.Cryptography/KeyEscrow/GaloisField256.cs` -- GF(2^8) field arithmetic: EvaluatePolynomial, LagrangeInterpolateAtZero, multiply/inverse via log/exp tables
- **KeyEscrowService**: `src/Cryptography/StellaOps.Cryptography/KeyEscrow/KeyEscrowService.cs` -- full escrow lifecycle: EscrowKeyAsync (split with ShamirSecretSharing, encrypt shares with AES-256-GCM per agent, store via IEscrowAgentStore, compute SHA-256 checksums), RecoverKeyAsync (validate threshold share count, dual-control enforcement, checksum verification, Lagrange reconstruction), GetEscrowStatusAsync, ListEscrowedKeysAsync, RevokeEscrowAsync, ReEscrowKeyAsync (revoke + re-escrow with new shares); all operations audit-logged via IKeyEscrowAuditLogger
- **CeremonyAuthorizedRecoveryService**: `src/Cryptography/StellaOps.Cryptography/KeyEscrow/CeremonyAuthorizedRecoveryService.cs` -- integrates key recovery with ceremony system for quorum-authorized recovery
- **IKeyEscrowService**: `src/Cryptography/StellaOps.Cryptography/KeyEscrow/IKeyEscrowService.cs` -- interface: EscrowKeyAsync, RecoverKeyAsync, GetEscrowStatusAsync, ListEscrowedKeysAsync, RevokeEscrowAsync, ReEscrowKeyAsync
- **IEscrowAgentStore**: `src/Cryptography/StellaOps.Cryptography/KeyEscrow/IEscrowAgentStore.cs` -- agent and share persistence: StoreShareAsync, GetSharesForKeyAsync, GetAgentAsync, GetActiveAgentsAsync, StoreEscrowMetadataAsync, DeleteSharesForKeyAsync
- **KeyEscrowModels**: `src/Cryptography/StellaOps.Cryptography/KeyEscrow/KeyEscrowModels.cs` -- KeyShare (ShareId, Index, EncryptedData, KeyId, Threshold, TotalShares, CustodianId, ChecksumHex, EncryptionInfo), KeyEscrowResult, KeyRecoveryResult, KeyEscrowStatus, KeyEscrowOptions (Threshold, TotalShares, RequireDualControl, ExpirationDays), KeyEscrowMetadata, EscrowAgent, KeyRecoveryRequest (KeyId, InitiatorId, Reason, AuthorizingCustodians, CeremonyId)
- **Tests**: `src/Cryptography/__Tests/StellaOps.Cryptography.Tests/ShamirSecretSharingTests.cs`, `KeyEscrow/KeyEscrowRecoveryIntegrationTests.cs`, `KeyEscrow/KeyEscrowRecoveryIntegrationTests.Fixed.cs`
- **Source**: SPRINT_20260112_018_CRYPTO_key_escrow_shamir.md
## E2E Test Plan
- [ ] Verify M-of-N split produces N shares and any M shares can reconstruct the original secret
- [ ] Verify fewer than M shares cannot reconstruct the secret (information-theoretic security)
- [ ] Verify duplicate share indices are rejected during reconstruction
- [ ] Test key escrow flow: escrow key -> retrieve status -> recover with threshold shares
- [ ] Verify dual-control enforcement requires at least 2 authorizing custodians when enabled
- [ ] Verify share checksums (SHA-256) are validated during recovery
- [ ] Verify escrow revocation deletes all shares and audit-logs the action
- [ ] Test re-escrow preserves original parameters when no new options provided
- [ ] Verify maximum 255 shares constraint from GF(2^8) field

View File

@@ -0,0 +1,30 @@
# TUF Client for Trust Root Management
## Module
Signer
## Status
IMPLEMENTED
## Description
Full TUF (The Update Framework) client implementation for secure trust root management, including root rotation, timestamp verification, target hash validation, cached state management, and offline mode support. Provides the foundation for Sigstore trust root bootstrapping.
## Implementation Details
- **TrustAnchorManager**: `src/Signer/__Libraries/StellaOps.Signer.KeyManagement/TrustAnchorManager.cs` -- trust anchor CRUD with PURL pattern matching: CreateAnchorAsync (validates PURL pattern format), FindAnchorForPurlAsync (glob-style matching with specificity scoring: segments*10 - wildcards*5, most-specific-match-wins), GetActiveAnchorsAsync, DeactivateAnchorAsync; VerifySignatureAuthorizationAsync combines temporal key validity check with predicate type authorization; each anchor has AllowedKeyIds, RevokedKeyIds, AllowedPredicateTypes, PolicyRef, PolicyVersion
- **PurlPatternMatcher**: `src/Signer/__Libraries/StellaOps.Signer.KeyManagement/TrustAnchorManager.cs` -- validates PURL patterns (must start with pkg:), converts glob patterns to regex (*/? wildcards), computes specificity scores for best-match resolution
- **KeyRotationService**: `src/Signer/__Libraries/StellaOps.Signer.KeyManagement/KeyRotationService.cs` -- trust anchor key lifecycle: AddKeyAsync, RevokeKeyAsync, CheckKeyValidityAsync (temporal validation), GetRotationWarningsAsync (expiry/age/algorithm warnings), GetKeyHistoryAsync; supports key rotation while preserving historical key validity for signature verification at signing time
- **KeyRotationAuditRepository**: `src/Signer/__Libraries/StellaOps.Signer.KeyManagement/KeyRotationAuditRepository.cs` -- audit trail for all key operations
- **SigstoreModels**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Infrastructure/Sigstore/SigstoreModels.cs` -- Sigstore trust root data models
- **DefaultSigningKeyResolver**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Infrastructure/Signing/DefaultSigningKeyResolver.cs` -- resolves signing keys from trust anchors
- **Tests**: `src/Signer/StellaOps.Signer/StellaOps.Signer.Tests/KeyManagement/TrustAnchorManagerTests.cs`
- **Source**: batch_38/file_08.md
## E2E Test Plan
- [ ] Verify trust anchor creation with valid PURL pattern succeeds
- [ ] Verify trust anchor creation with invalid PURL pattern (missing pkg: prefix) is rejected
- [ ] Test PURL pattern matching: exact match, wildcard match (pkg:npm/*), namespace wildcard (pkg:maven/org.apache/*)
- [ ] Verify most-specific pattern wins when multiple patterns match a PURL
- [ ] Verify VerifySignatureAuthorizationAsync returns IsAuthorized=false when key is not valid at signing time
- [ ] Verify predicate type authorization restricts signing to allowed predicate types
- [ ] Test trust anchor deactivation prevents matching
- [ ] Verify key rotation updates AllowedKeyIds on the anchor while preserving historical validity