Packages · supply-chain

k2gl/sigstore-sign

Produce Sigstore signatures and bundles from PHP — keyful or keyless (Fulcio/OIDC).

Sign artifacts and attestations the Sigstore way, then hand off a bundle any verifier accepts.

k2gl/sigstore-sign version on Packagist k2gl/sigstore-sign total downloads k2gl/sigstore-sign license

Install

composer require k2gl/sigstore-sign
PHP
>=8.1
k2gl dependencies
k2gl/dsse , k2gl/sigstore-bundle , k2gl/rekor-client

Reach for it when

  • You need to produce Sigstore bundles from PHP, keyless (Fulcio + OIDC) or with your own key.
  • You’re building a signing step into a PHP tool or release pipeline.

Look elsewhere when

  • You only need to verify — use sigstore-verify.
  • You’re signing release artifacts in GitHub Actions — composer-attest-action wraps this.

Sign with Sigstore from PHP

The Sigstore signing flow, end to end, in PHP: sign an artifact or an attestation, log the entry to a Rekor v2 transparency log, timestamp the signature, and assemble the .sigstore.json bundle — the emit counterpart to k2gl/sigstore-verify.

It ties the family together: k2gl/dsse signs, k2gl/rekor-client logs, k2gl/sigstore-bundle is emitted. It does both keyful signing (you bring the key and its certificate or a public-key hint) and keyless signing (an ephemeral key certified by Fulcio against a CI OIDC identity).

Requirements

  • PHP 8.1+
  • A PSR-18 HTTP client and PSR-17 factory (for Rekor and the timestamp authority)
  • k2gl/dsse, k2gl/rekor-client, k2gl/sigstore-bundle

Installation

composer require k2gl/sigstore-sign

Usage

use K2gl\Dsse\EcdsaP256Signer;
use K2gl\RekorClient\{RekorClient, KeyDetails};
use K2gl\SigstoreSign\{SigstoreSigner, SigningKey, TsaClient};

$rekor = new RekorClient($psr18, $psr17, $psr17, 'https://rekor.sigstore.dev');
$tsa   = new TsaClient($psr18, $psr17, $psr17, 'https://timestamp.sigstore.dev');
$signer = new SigstoreSigner($rekor, $tsa);

// The key: a DSSE signer for the private half, plus the public half — here a
// public-key hint (or use SigningKey::certificate() with a Fulcio leaf).
$key = SigningKey::publicKey(
    signer:     EcdsaP256Signer::fromPem(file_get_contents('signing-key.pem'), null),
    publicKeyDer: $publicKeyDer,
    keyDetails: KeyDetails::PKIX_ECDSA_P256_SHA_256,
    hint:       $hexSha256OfThePublicKey,
);

// Sign an artifact → a message-signature bundle.
$bundleJson = $signer->signArtifact(file_get_contents('release.tar.gz'), $key)->toJson();

// …or sign an attestation payload (e.g. an in-toto Statement) → a DSSE bundle.
$bundleJson = $signer->signAttestation($statementJson, 'application/vnd.in-toto+json', $key)->toJson();

Keyless signing (Fulcio + CI OIDC)

In CI, sign without a long-lived key: read the ambient OIDC identity, let Fulcio certify an ephemeral key against it, and sign with that.

use K2gl\SigstoreSign\{AmbientCredentials, FulcioClient, FulcioSigningKey, SigstoreSigner};

// The CI identity token (GitHub Actions needs `id-token: write`).
$oidcToken = AmbientCredentials::githubActions($psr18, $psr17);
// …or AmbientCredentials::gitlabCi() for a GitLab id_token.

$fulcio = new FulcioClient($psr18, $psr17, $psr17, 'https://fulcio.sigstore.dev');
$key = FulcioSigningKey::create($fulcio, $oidcToken); // ephemeral key + Fulcio certificate

$bundleJson = (new SigstoreSigner($rekor, $tsa))->signArtifact($artifact, $key)->toJson();

FulcioSigningKey generates the ephemeral P-256 key, proves possession of it to Fulcio by signing the token's sub, and returns a SigningKey bound to the issued certificate — the same type keyful signing uses, so the rest of the flow is identical.

Why the timestamp authority matters

A Rekor v2 entry has no integrated time, so a bundle needs a trusted RFC 3161 timestamp to have a verifiable signing time. Pass a TsaClient (Sigstore's public-good TSA is timestamp.sigstore.dev) when signing against Rekor v2 — without it, the bundle logs and assembles but will not verify for lack of a time source.

What gets signed

  • Artifact — the signature is over the artifact's SHA-256 digest (the message-signature convention). Use an attestation for Ed25519 keys, which the message-signature path does not cover on the verify side.
  • Attestation — the payload is wrapped in a DSSE envelope and the signature is over the PAE; the Rekor entry binds the PAE digest, as Rekor v2 records DSSE attestations.

Errors

Everything thrown implements K2gl\SigstoreSign\Exception\SigstoreSignException: SigningException (the signing flow), TimestampException (the timestamp authority), and FulcioException (the OIDC credential or the Fulcio certificate step).

Pull requests are always welcome

Collaborate with pull requests

API

Public classes and methods, generated from the source.

K2gl\SigstoreSign\AmbientCredentials class

  • githubActions( ClientInterface $httpClient, RequestFactoryInterface $requestFactory, string $audience = 'sigstore', ): string
  • gitlabCi(string $variable = 'SIGSTORE_ID_TOKEN'): string

K2gl\SigstoreSign\Exception\FulcioException class

K2gl\SigstoreSign\Exception\SigningException class

K2gl\SigstoreSign\Exception\SigstoreSignException interface

K2gl\SigstoreSign\Exception\TimestampException class

K2gl\SigstoreSign\FulcioClient class

  • __construct( private readonly ClientInterface $httpClient, private readonly RequestFactoryInterface $requestFactory, private readonly StreamFactoryInterface $streamFactory, string $baseUrl, )
  • requestCertificate( string $oidcToken, string $publicKeyPem, string $proofOfPossession, string $algorithm = 'ECDSA', ): string

K2gl\SigstoreSign\FulcioSigningKey class

  • create(FulcioClient $fulcio, string $oidcToken): SigningKey

K2gl\SigstoreSign\SigningKey class

  • publicKey(Signer $signer, string $publicKeyDer, KeyDetails $keyDetails, string $hint): self
  • certificate(Signer $signer, string $certificateDer, KeyDetails $keyDetails): self
  • sign(string $message): string
  • keyId(): ?string
  • rekorVerifier(): Verifier
  • bundleIdentity(): SigningIdentity

K2gl\SigstoreSign\SigstoreSigner class

  • __construct( private readonly RekorClient $rekor, private readonly ?TsaClient $tsa = null, )
  • signArtifact(string $artifact, SigningKey $key): Bundle
  • signAttestation(string $payload, string $payloadType, SigningKey $key): Bundle

K2gl\SigstoreSign\TsaClient class

  • __construct( private readonly ClientInterface $httpClient, private readonly RequestFactoryInterface $requestFactory, private readonly StreamFactoryInterface $streamFactory, string $url, )
  • timestamp(string $signature): string