Software Supply Chain Security / Binary Assurance

Code Signing, Binary Integrity & Sigstore

Traditional code signing requires software developers or build systems to manage long-lived private signing keys. If a private key is leaked, stolen, or improperly stored in a CI/CD secret store, attackers can sign malicious software binaries with trusted vendor identities. Sigstore removes long-lived signing keys from the model entirely by introducing keyless code signing: short-lived X.509 certificates issued against an OpenID Connect identity (Fulcio), an append-only, tamper-evident Merkle transparency log (Rekor), and OCI container image signing and verification (Cosign).

Three component panels and a case study. Fulcio authenticates via OpenID Connect and issues an ephemeral certificate valid about ten minutes, with expiry replacing revocation. Rekor is an append-only tamper-evident Merkle log that stores signature, certificate and artifact digest and returns an inclusion proof against a co-signed checkpoint, noting v2 issues no Signed Entry Timestamp so an RFC 3161 timestamp authority is used. Cosign signs images and blobs, stores signatures as OCI artifacts, and pins an exact or anchored identity. The case study panel records that the xz payload fixtures were in Git while only the modified build-to-host.m4 shipped in the tarball, and that the maintainer account was earned rather than stolen.

Sigstore keyless signing architecture: Fulcio, Rekor, Cosign, and the xz-utils case study

The failure modes of traditional key management

Traditional code signing relies on long-lived asymmetric key pairs (e.g. RSA or ECDSA private keys stored in PKCS#12 files or hardware security modules):

A long-lived RSA or ECDSA private signing key, valid for months or years, branches to three exposure surfaces: a CI secret store reachable by any job that runs, a developer workstation one endpoint compromise away, and a build script or log pipeline where masking can fail. All three converge on one outcome: an attacker signs malicious binaries under the vendor's own identity and clients accept them because the key is the only thing checked. A footer explains that revocation calls every past signature into question unless a timestamping authority recorded when each signature was made.

One long-lived key, three places it can leave from, and one indistinguishable outcome

Sigstore keyless signing architecture

Sigstore (an OpenSSF project under the Linux Foundation) eliminates long-lived signing keys by introducing keyless signing:

Six steps. The runner authenticates and receives an OIDC identity token naming the repository, workflow file, and ref. It generates an ephemeral key pair in memory. Fulcio validates the token and issues a certificate valid about ten minutes binding the public key to the identity in the SAN. The runner signs the artifact digest and Rekor returns an inclusion proof against a co-signed checkpoint. The client obtains an RFC 3161 timestamp from a timestamp authority because Rekor v2 no longer returns Signed Entry Timestamps. The result is a bundle carrying certificate, signature, inclusion proof, and timestamp, with the private key discarded.

Keyless signing end to end: the private key exists only for the length of one build step

The three core Sigstore components

  1. Fulcio (short-lived certificate authority):
    • Authenticates the developer or build runner using OpenID Connect (OIDC) (e.g. a GitHub Actions identity token, or a Google OIDC identity).
    • The client generates an ephemeral asymmetric key pair locally, submits the public key to Fulcio, and receives a short-lived X.509 certificate valid for roughly 10 minutes.
    • The certificate binds the ephemeral public key to the authenticated OIDC identity string in its subject alternative name (e.g. https://github.com/example/repo/.github/workflows/release.yml@refs/heads/main).
    • Because the certificate expires in minutes, expiry does the work that revocation lists and OCSP do elsewhere.
  2. Rekor (transparency log):
    • An append-only, tamper-evident Merkle tree transparency log, structurally similar to Certificate Transparency.
    • Records the artifact digest, signature, and Fulcio certificate, and returns an inclusion proof against a co-signed checkpoint on upload.
    • Rekor v2 (generally available since October 2025) no longer returns Signed Entry Timestamps or integrated time. Clients obtain an RFC 3161 signed timestamp from a trusted timestamp authority — Sigstore operates one at timestamp.sigstore.dev — and carry it in the bundle. Together the inclusion proof and the timestamp establish that the signature existed while the short-lived certificate was still valid.
    • Makes signature events publicly discoverable, so misuse of a signing identity is detectable by anyone monitoring the log. As with Certificate Transparency, that detection depends on monitors and auditors comparing checkpoints; the log structure alone does not prevent an operator from serving a split view.
  3. Cosign (container & blob signing tool):
    • Command-line tool used to sign and verify OCI container images, SBOMs, SLSA attestations, and arbitrary binary blobs.
    • Stores signatures and attestations directly inside OCI container registries as OCI artifacts.

Verification workflow via Cosign

When a Kubernetes cluster or server pulls a container image, Cosign verifies its signature and the identity that produced it:

# Verify an OCI container image signed keylessly via GitHub Actions.
# Pin the digest, not the tag: a tag resolves at verification time and can move.
cosign verify \
  --certificate-identity "https://github.com/example/repo/.github/workflows/release.yml@refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/example/app@sha256:3f79bb7b435b05321651daefd374cdc681dc06faa65e374e38337b88ca046dea
  1. Extract the short-lived X.509 certificate attached to the container artifact.
  2. Verify that the certificate chains to Fulcio and that its OIDC issuer and SAN identity match what the policy expects.
  3. Verify the Rekor inclusion proof against a co-signed checkpoint, and verify the RFC 3161 timestamp, establishing that the signature was produced while the certificate was valid.

Prefer --certificate-identity over --certificate-identity-regexp. Cosign documents the plain flag as the strict matcher; the regexp variant is the non-strict alternative and its pattern is applied unanchored, so it matches any identity merely containing the pattern. If a regexp is genuinely needed, anchor it:

--certificate-identity-regexp "^https://github\.com/example/repo/\.github/workflows/release\.yml@refs/heads/.+$"

What this proves and what it does not. These steps establish which identity produced the artifact and that the signature was logged while the certificate was valid. They prove nothing about the code’s behavior — which is exactly the boundary the next section illustrates.

Case study: the xz-utils backdoor (CVE-2024-3094)

In March 2024, a supply chain backdoor was discovered in xz-utils versions 5.6.0 and 5.6.1, a compression library present across Linux distributions:

Two panels compare the public Git tree with the release tarball. Both contained the payload test fixtures bad-3-corrupt_lzma2.xz and good-large_compressed.lzma, committed openly as corrupt-input test data. The Git tree held a benign build-to-host.m4; the tarball held a modified one that existed nowhere in Git. A four-step chain follows: configure runs the modified macro, a sed pipeline extracts the staged script, liblzma is patched during compilation on glibc x86-64 RPM and DEB builds only, and distribution-patched sshd reaches it because it links libsystemd. A footer notes the maintainer account was earned over roughly two years rather than stolen.

The divergence was one file: the payload sat in Git openly, and only the macro that activated it shipped in the tarball

Code signing & Sigstore diagnostic checklist

The checklist below is a journal working model, not a published audit standard. When auditing code signing architectures and binary deployment integrity, evaluate these six criteria:

Diagnostic area Architectural evaluation question Verification & audit evidence
Keyless Sigstore adoption Are container images and release binaries signed using keyless Sigstore rather than long-lived keys? CI/CD build scripts & Cosign sign commands.
Timestamp verification Does the verification policy check the Rekor inclusion proof and an RFC 3161 timestamp, rather than a Signed Entry Timestamp that Rekor v2 no longer issues? Deployment policy files & Kyverno admission controller rules.
Identity match strictness Does verification pin an exact certificate identity, or an anchored regexp, rather than an unanchored pattern? Cosign verify flags (--certificate-identity, or an anchored --certificate-identity-regexp).
Digest-pinned verification Are images verified and admitted by digest rather than by a mutable tag? Deployment manifests and admission policy referencing @sha256:.
Kubernetes admission control Is an admission controller (Kyverno / Policy Controller) deployed to block unsigned container images? Kubernetes admission webhook manifests & block logs.
Release artifact reproducibility Are release tarballs produced from the tagged commit inside CI, and diffed against the source tree before publishing? Release workflow definitions, provenance attestations & CI diff check logs.
What I need to remember

Sigstore replaces long-lived signing keys with a 10-minute OIDC-bound certificate (Fulcio) plus an append-only, tamper-evident log (Rekor). Since Rekor v2 there is no Signed Entry Timestamp — verification uses an inclusion proof plus an RFC 3161 timestamp. Pin an exact identity and a digest, not a regexp and a tag. The xz-utils backdoor shows signing proves who produced an artifact, not that its contents are safe.

Primary references