Anulum Overview Host analysis AMP simulation Validation Python API Rust API C/C++ API

Source release signatures

The manually dispatched source-release-signatures.yml workflow verifies and signs the two existing GitHub v0.1.0 source assets. It reconstructs the ZIP from source commit 1d12e0342d5320f62182fef41709773f3c555b50 using git archive and requires byte-identical SHA-256 digests for the archive and the original release-provenance.json. A different tag target, damaged asset or unqualified workflow revision refuses signing.

The original ZIP was created with TZ=Europe/Zurich. Git stores local DOS timestamps in ZIP entries, so reconstruction must declare the same timezone:

TZ=Europe/Zurich git archive --format=zip --prefix=loop-timing-witness-0.1.0/ \
  1d12e0342d5320f62182fef41709773f3c555b50 --output=reconstructed.zip
sha256sum reconstructed.zip

The expected digest is 3e0c83e09be127ef3fc30c0043c377f20dcfed3df58e70a49e9ebebdd6f7880e. A UTC reconstruction has identical member contents but different ZIP timestamp bytes; verification rejects it. Both the signing workflow and test fixture set the release timezone explicitly instead of inheriting the host's timezone.

Verification runs with read permissions. A separate job obtains a short-lived Sigstore certificate using GitHub OIDC and signs an in-toto statement covering both asset digests. This job can publish attestations but cannot change release assets. The final job verifies the certificate, signature, transparency-log proof, both subject digests, predicate type and exact workflow identity before attaching source-v0.1.0.sigstore.json. It has no OIDC signing permission. The source-signatures environment admits only main; all seven source validation workflows must have succeeded at the exact workflow revision.

The custom predicate records artifact_source_commit separately from verification_workflow_commit. This is an authenticated verification of existing source assets, not retrospective SLSA build provenance. The original ZIP, manifest, tag, Zenodo files and registry packages remain unchanged. A repeat dispatch refuses overwriting an existing signature attachment.

Verify downloaded assets

Use a current GitHub CLI with gh attestation verify support:

gh release download v0.1.0 --repo anulum/loop-timing-witness \
  --pattern loop-timing-witness-0.1.0.zip --pattern release-provenance.json \
  --pattern source-v0.1.0.sigstore.json

for asset in loop-timing-witness-0.1.0.zip release-provenance.json; do
  gh attestation verify "$asset" --repo anulum/loop-timing-witness \
    --bundle source-v0.1.0.sigstore.json \
    --predicate-type https://github.com/anulum/loop-timing-witness/source-release-verification/v1 \
    --signer-workflow anulum/loop-timing-witness/.github/workflows/source-release-signatures.yml \
    --source-ref refs/heads/main --deny-self-hosted-runners --format json
done

Check the verified statement's predicate: artifact_source_commit must be 1d12e0342d5320f62182fef41709773f3c555b50, and verification_workflow_commit must identify the reviewed signing workflow revision. For a pinned review, also supply --source-digest with that workflow revision. The certificate's source digest names the signing workflow's commit, not the older source archive's commit. Never accept an arbitrary workflow or repository merely because its certificate is valid.

The bundle can also verify identical files downloaded from Zenodo; the signature sidecar is distributed through GitHub. A modified file, unrelated signer or wrong predicate fails verification. A signature establishes artifact integrity and issuer identity; physical timing qualification remains governed by the measurement domain and its evidence.

Subsequent versions

This workflow is deliberately bound to the original v0.1.0 assets. A new source version requires separately reviewed artifact digests, a new source identity, complete validation and its own signature. It cannot replace existing versions or infer a package publisher's identity from the source signature.