Skip to content

Sigstore Interop — in-toto/DSSE Output (REQ-030)

Diogenes attestations can be emitted as in-toto Statements wrapped in DSSE envelopes verifiable by cosign verify-attestation and any in-toto-compatible tooling, with no Diogenes-specific verifier required.

This page documents:

  • The supported predicate types
  • The SDK and CLI surfaces
  • The cosign verify-attestation reference workflow
  • The round-trip guarantee and the _diogenes_extensions field
  • The Diogenes Key Registry vs. Fulcio cert chain trade-off

Supported predicate types

Five predicate types are first-class on output:

Predicate type URL Spec
https://slsa.dev/provenance/v1 https://slsa.dev/spec/v1.0/provenance
https://in-toto.io/attestation/release/v0.1 in-toto/attestation spec/predicates/release.md
https://in-toto.io/attestation/test-result/v0.1 in-toto/attestation spec/predicates/test-result.md
https://in-toto.io/attestation/vulns/v0.2 in-toto/attestation spec/predicates/vuln.md
https://trustdiogenes.com/attestation/v1 Diogenes-native passthrough

Unknown predicate types are rejected at diogenes sign and DiogenesSDK.sign time with a clear error listing the supported set.

SDK

from diogenes.sdk import DiogenesSDK, generate_key_pair

sdk = DiogenesSDK(transparency_log=log)
private_key, _ = generate_key_pair()

result = sdk.sign(
    content=b"...artifact bytes...",
    private_key=private_key,
    output_format="in-toto-dsse",
    predicate_type="https://slsa.dev/provenance/v1",
    predicate={"buildDefinition": {...}, "runDetails": {...}},
    subject_name="myapp.tar.gz",
)
# result is a SignedAttestationBundle:
result.manifest             # Diogenes Manifest (with native attestation in .attestations)
result.attestation          # Native Diogenes Attestation
result.bundle               # DSSE envelope as a dict
result.bundle_json          # DSSE envelope JSON string
result.statement_bytes      # JCS-canonical in-toto Statement bytes
result.signer_fingerprint   # Diogenes Key Registry fingerprint

The default output_format="native" returns the historical (manifest, attestation) 2-tuple unchanged.

CLI

diogenes sign \
  --subject ./myapp.tar.gz \
  --predicate-type https://slsa.dev/provenance/v1 \
  --predicate ./provenance.json \
  --format in-toto-dsse \
  --output ./myapp.intoto.jsonl \
  --key-file ./signer.pem

The output file is a single-line DSSE envelope. The subject SHA-256 digest is computed from the artifact at --subject. The predicate body in --predicate (a JSON file with an object at the top level) is emitted as the typed in-toto predicate body alongside the _diogenes_extensions envelope.

If --predicate is omitted, the typed predicate body is empty (the extension envelope alone carries the Diogenes-side data).

Reference verification with cosign

The Diogenes Phase-3 verification workflow uses cosign with two flags that disable the Sigstore-specific transparency log lookup and Fulcio chain validation, since Diogenes signs with long-lived keys. The canonical invocation pattern is:

cosign verify-attestation --insecure-ignore-tlog --certificate <diogenes-cert>

A concrete example:

cosign verify-attestation \
  --insecure-ignore-tlog \
  --certificate diogenes-signer.cert.pem \
  --type slsaprovenance \
  myapp.tar.gz
  • --insecure-ignore-tlog — Diogenes does not register attestations in Rekor (the public Sigstore transparency log) by default. The Diogenes log is a separate transparency layer; cosign cannot consult it. Skipping the Rekor lookup is the documented Phase-3 behavior. (Dual-log witnessing — REQ-031 — adds optional Rekor co-anchoring.)
  • --certificate diogenes-signer.cert.pem — the Diogenes Key Registry certificate for the signing key. This replaces the implicit Fulcio chain validation with explicit caller-supplied trust.

The certificate file is a PEM that holds the signer's public key. Diogenes does not mint short-lived Fulcio-style certificates; the signer key is a long-lived registered key in the Diogenes Key Registry.

Long-term goal: cosign or sibling tooling gains native Diogenes-issuer recognition, eliminating the --insecure-ignore-tlog flag. Tracked under REQ-029-post-GA, not in Phase 3.

Round-trip guarantee

Diogenes attestations preserve bitwise identity across a serialize → deserialize round trip through the in-toto-DSSE format, modulo a documented set of non-canonical fields (currently empty — every native field round-trips).

Mechanism: every native :class:Attestation is parked under a _diogenes_extensions sub-object inside the predicate:

{
  "_type": "https://in-toto.io/Statement/v1",
  "subject": [{"name": "myapp.tar.gz", "digest": {"sha256": "..."}}],
  "predicateType": "https://slsa.dev/provenance/v1",
  "predicate": {
    "buildDefinition": { ... },
    "runDetails":      { ... },
    "_diogenes_extensions": {
      "version": "1",
      "attestation": { ...full native Attestation JSON... }
    }
  }
}

The extension envelope is part of the JCS-canonicalized bytes that the DSSE signature covers, so it cannot be tampered with without breaking verification. Verifiers that do not understand the extension transparently ignore it (cosign verifies the bytes, not the predicate schema).

To recover the native attestation:

from diogenes.interop.sigstore.output import deserialize_from_in_toto_dsse

native = deserialize_from_in_toto_dsse(envelope_json)
# `native` is a fully-populated Attestation pydantic model

deserialize_from_in_toto_dsse raises InTotoDSSEDeserializeError on non-Diogenes envelopes, on envelopes with a wrong payloadType, on unknown predicate types, or on malformed extension envelopes.

DSSE conformance

The DSSE envelope follows the DSSE v1 spec exactly:

{
  "payloadType": "application/vnd.in-toto+json",
  "payload": "<base64(canonical-statement-bytes)>",
  "signatures": [
    {"keyid": "<diogenes-fingerprint>", "sig": "<base64(signature)>"}
  ]
}

The bytes signed are the DSSE Pre-Authentication Encoding (PAE) of the canonical Statement bytes:

PAE = "DSSEv1" SP LEN(type) SP type SP LEN(body) SP body

The Statement is canonicalized via RFC 8785 (JCS) prior to signing, so the signed bytes are deterministic across implementations.

Trust-model preservation

Diogenes remains long-lived-key. The DSSE envelope is signed by the same long-lived Diogenes key as the native attestation. The certificate chain in the verification flow references the Diogenes Key Registry, not Fulcio. Sigstore is additive to rather than competitive with Diogenes; this output format makes Diogenes attestations consumable by the existing OSS supply-chain ecosystem without changing the Diogenes trust model.

See also

  • User-facing guide — publisher-oriented walk-through of the diogenes export-dsse CLI and the manifest-level workflow (issue #308)
  • design/phase3/requirements.md — REQ-030 / SI-02 source spec
  • design/sigstore-interop-prd.md — Phase-3 PRD
  • tasks/in-toto-dsse-output/ — implementation nano-spec
  • tasks/dsse-intoto-export-adapter/ — manifest-level export nano-spec (#308)
  • tests/features/233-in-toto-dsse-output.feature — BDD scenarios (per-attestation)
  • tests/features/308-dsse-export-round-trips-via-cosign.feature — BDD scenarios (manifest-level + real cosign)