EU Signing Payload Registry v1
Contract ID: CONTRACT-EU-SIGNING-PAYLOAD-REGISTRY-V1-203 Status: Active Signer contract Owner: Attestor/Signer guild Runtime library: src/Attestor/StellaOps.Signer/StellaOps.Signer.Core/EuSigningContracts.cs Adjacent tests: src/Attestor/StellaOps.Signer/StellaOps.Signer.Tests/Signing/EuArtifactOfflineVerifierTests.cs Sprint: SPRINT_20260430_203_Signer_eu_signing_verifier_contracts.md
Purpose
This registry is the authoritative allow-list for engineers integrating with the Stella Ops Signer: it defines the only EU compliance artifact payload types that the Signer offline verifier accepts. The registry is fail-closed — unknown payload ids, mismatched DSSE payload media types, wrong schema pins, or missing replay hashes all yield an invalid verification result.
The registry distinguishes Stella-as-manufacturer artifacts from tenant/operator artifacts so verifiers can require the correct signer profile.
Registry Entries
| Payload id | DSSE payload type | Schema pin id | Signer profile | Audience |
|---|---|---|---|---|
product-csaf-advisory-v1 | application/vnd.stellaops.product.csaf-advisory.v1+json | 2.0 | stella-manufacturer-release | Product manufacturer |
product-update-manifest-v1 | application/vnd.in-toto+json | stella.ops/productUpdateManifest@v1 | stella-manufacturer-release | Product manufacturer |
cra-tech-file-v1 | application/vnd.stellaops.cra.tech-file.index.v1+json | cra-tech-file-v1 | tenant-regulatory-export | Tenant/operator |
conformity-dossier-v1 | application/vnd.stellaops.cra.conformity-dossier.index.v1+json | conformity-dossier-v1 | tenant-regulatory-export | Tenant/operator |
nis2-soa-v1 | application/vnd.stellaops.nis2.soa.v1+json | nis2-soa-v1 | tenant-regulatory-export | Tenant/operator |
dora-major-incident-report-v1 | application/vnd.stellaops.dora.major-incident-report.envelope.v1+json | dora-major-incident-report.v1 | tenant-regulatory-reporting | Tenant/operator |
dora-roi-v1 | application/vnd.stellaops.dora.roi.bundle-index.v1+json | dora-roi.v1 | tenant-regulatory-reporting | Tenant/operator |
dora-info-sharing-event-v1 | application/vnd.stellaops.dora-info-sharing.stix-batch.v1+json | dora-info-sharing-event-v1 | tenant-regulatory-sharing | Tenant/operator |
tlpt-evidence-pack-v1 | application/vnd.stellaops.tlpt-evidence-pack+json | tlpt-evidence-pack.v1 | tenant-regulatory-evidence | Tenant/operator |
Schema Pin Locations
Direct JSON payloads carry the schema pin at a fixed JSON path:
| Payload id | JSON path |
|---|---|
product-csaf-advisory-v1 | document.csaf_version |
cra-tech-file-v1 | schemaVersion |
conformity-dossier-v1 | schemaVersion |
nis2-soa-v1 | schemaVersion |
dora-major-incident-report-v1 | contractVersion |
dora-roi-v1 | schemaVersion |
dora-info-sharing-event-v1 | x_stella_dora_info_sharing.contract_version |
tlpt-evidence-pack-v1 | schemaVersion |
product-update-manifest-v1 is an in-toto statement (PayloadShape = InTotoStatement). The verifier checks predicateType = stella.ops/product-update-manifest@v1, then reads the schema pin from predicate.schemaVersion (the registry SchemaPinJsonPath is schemaVersion, evaluated against the predicate object). If the predicate type does not match or the predicate object is absent, no schema pin is resolved and verification fails with schema_pin_invalid rather than a distinct predicate-type code.
Signer Production Path
Direct JSON registry entries are signed through SigningRequest.DirectPayload (a DirectDssePayload). SignerPipeline.ValidateRequest calls ValidateDirectPayload, which checks (in order) the registry id, registry payload shape (DirectJson), DSSE media type, schema pin id, presence of payload bytes, the sha256:<64 lowercase hex> form of the supplied canonical hash, the recomputed canonical payload hash, that the payload bytes are already canonical JSON, and that the schema pin value at the registry JSON path matches. These run before entitlement introspection, release-integrity verification of the scanner image digest, quota enforcement, the provider-backed sign step, and the audit-sink write. Direct JSON payloads must not be wrapped in an in-toto statement unless their registry entry declares PayloadShape = InTotoStatement.
Validation failures throw SignerValidationException with stable codes including direct_payload_type_id_missing, direct_payload_type_unknown, direct_payload_shape_invalid, direct_payload_type_mismatch, direct_payload_schema_pin_mismatch, direct_payload_missing, direct_payload_hash_invalid, direct_payload_hash_mismatch, direct_payload_json_invalid, direct_payload_not_canonical, and direct_payload_schema_pin_invalid.
Signer pins the primary DSSE signing algorithm from the active crypto profile before resolving key material. Built-in pins are fips -> ES256, eidas -> ES256, gost -> GOST12-256, sm -> SM2 (the sm pin honours the optional SmAlgorithm override and otherwise defaults to SM2), and pq -> DILITHIUM3; explicit ProfileAlgorithmPins (DsseSignerOptions) may override the built-in map for deployment-specific provider packs. The sm and pq algorithms are additionally gated by environment flags: SM2 requires SM_SOFT_ALLOWED=1 and DILITHIUM3/FALCON512 require PQ_SOFT_ALLOWED=1, otherwise the sign step throws InvalidOperationException (fail-closed).
Offline Verification Contract
Verifier callers populate EuOfflineVerificationRequest with:
EnvelopeJson: the DSSE envelope.ExpectedPayloadTypeId: one of the registry ids above.ExpectedSchemaPinId: the exact schema pin from the registry.ExpectedCanonicalPayloadSha256: lowercasesha256:<64 hex>over the canonical JSON payload bytes.TrustRoots: one or more local trust roots (EuVerifierTrustRoot) with trust root id, key id, public key PEM, fingerprint, validity window (NotBeforeUtc/NotAfterUtc), and signer profiles.TrustRootMode: anEuTrustRootMode(Online,Sealed, orAirGap). This field is required on the request record but is currently informational — theEuArtifactOfflineVerifierdoes not branch on it; verification is always offline regardless of the value.VerificationTimeUtc: the verification time.
Optional request fields:
ExpectedSignerKeyId: pins the accepted signer key id; a non-empty value that does not match a signature’s key id yieldssigner_key_mismatch:<keyId>.AllowHistoricalVerificationandSignedAtUtc: when historical verification is allowed and a signed time is supplied, a trust root that is expired atVerificationTimeUtcis still accepted ifSignedAtUtcfalls inside its validity window.
The verifier must not perform network access. It validates only local envelope bytes, local trust roots, local schema pin data, and caller-provided replay metadata.
Fail-Closed Reasons
Current stable issue codes include:
payload_type_unknownpayload_type_mismatchschema_pin_registry_mismatchschema_pin_invalidreplay_canonical_payload_hash_invalidcanonical_payload_hash_mismatchpayload_not_canonicalpayload_invalid_base64trust_roots_requiredsignature_key_id_missingsigner_key_mismatch:<keyId>signature_invalid_base64:<keyId>signer_profile_not_allowed:<keyId>trust_root_not_valid_at_time:<trustRootId>signature_untrusted:<keyId>envelope_missing_signatures(the envelope parsed but itssignaturesarray was empty)no_valid_signaturepayload_json_invalid:<message>- DSSE structure failures surfaced while parsing the envelope:
payload_type_missing,payload_missing,signatures_missing(thesignaturesproperty was absent or not a JSON array), andenvelope_parse_error:<message>(the envelope JSON failed to parse).
Audit Linkage Status
EuSigningAuditLinkageBuilder.FromVerification projects a verification request/result pair into an EuSigningAuditLinkage record carrying the shared EvidenceLocker audit fields:
- audit event name:
evidence.eu.artifact.verified(ArtifactVerifiedEventName) - artifact type (caller-supplied, trimmed)
- content hash (
result.CanonicalPayloadSha256, falling back to the request’sExpectedCanonicalPayloadSha256) - schema pin id
- payload type id
- verifier result (
validwhenresult.IsValid, otherwiseinvalid) - signer key id (
result.VerifiedKeyId, falling back torequest.ExpectedSignerKeyId) - trust root id
- trust root fingerprint
- deterministic issue list (deduplicated and ordinal-sorted)
The builder also exposes ArtifactSignedEventName = evidence.eu.artifact.signed for the production/sign path.
Downstream sign/export flows record these fields in evidence_locker.regulatory_audit_events and can link the output from evidence_locker.regulatory_artifact_ledger (both created by EvidenceLocker migration 007_regulatory_audit_retention_ledger.sql). The Signer verifier remains a local/offline contract and does not write EvidenceLocker tables directly.
