Skip to content

Add Sigstore signer package for skill OCI artifacts - #6023

Open
samuv wants to merge 2 commits into
mainfrom
skills-sig/09-signer
Open

Add Sigstore signer package for skill OCI artifacts#6023
samuv wants to merge 2 commits into
mainfrom
skills-sig/09-signer

Conversation

@samuv

@samuv samuv commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Note

Stack 2 of RFC THV-0080 (tracking issue #5899) — PR 9 of the Sigstore signing/verification series. Follows #6015 (provenance schema, merged). Parallel with the verifier wrapper PR, which blocks on stacklok/toolhive-core#192. This package has no consumers yet; push-signing and install-verification PRs later in the stack wire it in.

Summary

Skills pushed to OCI registries need signatures so installs can be verified against the lock file's recorded provenance (RFC THV-0080). This PR adds pkg/skills/signer, the signing half of that trust model:

  • Cosign key-pair signing: loads a cosign PEM private key (COSIGN_PASSWORD honored for encrypted keys, matching the cosign CLI), signs the artifact via sigstore-go.
  • Signs the simple-signing payload, not the raw digest: the payload embeds the artifact's manifest digest, binding the signature to the exact content — this is the cosign convention that cosign verify --key and toolhive-core's bundle reconstruction both expect. The POC (Add project-level skills lock file (POC) #5715) signed the manifest-digest bytes directly and produced two different signatures (attached vs. bundled); here the signature is computed once and reused for both representations, so a verifier reconstructing the bundle from the registry checks exactly the signature that was attached.
  • Classic cosign signature layout: the signature manifest is pushed at the sha256-<hex>.sig tag with the simple-signing payload as its single layer and the signature in the layer annotation — a deliberate interop choice so existing Sigstore registry tooling can discover and verify it.
  • Returns the serialized Sigstore bundle for durable storage (SQLite, later in the stack) and offline re-verification during sync.
  • Hardened key-path handling: resolveKeyPath canonicalizes the user-supplied --key path (absolute, cleaned, symlinks resolved, regular-file check) before opening — no #nosec/string-inspection shortcuts.

Keyless (OIDC) signing is intentionally not implemented (ErrKeyRequired); it is a tracked follow-up per the Stack 1 review decision.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Test improvements
  • CI/CD or build system changes

Test plan

  • Unit tests pass locally (task test)
  • Linting passes (task lint-fix, 0 issues)
  • New unit tests added: round-trip against an in-process registry (httptest + registry.New(), zero network) proving the returned bundle verifies against the signing public key over the payload digest, the .sig manifest layer is the exact signed payload, and the annotation signature equals the bundle's message signature; wrong-key rejection; encrypted-key decryption via COSIGN_PASSWORD (right and wrong password); ErrKeyRequired; resolveKeyPath matrix (regular file / directory / missing / empty).

Does this introduce a user-facing change?

No — the package has no callers yet. The thv skill push --key flag lands with the push-signing PR at the end of the stack.

Special notes for reviewers

  • The round-trip test's verifyKeyBundle helper mirrors toolhive-core's upcoming VerifyBundleWithKey (Export Sigstore bundle retrieval, offline verification, and identity API toolhive-core#192) using sigstore-go directly; once the core release lands and the verifier wrapper PR bumps go.mod, that helper is the contract the install-verification E2E relies on.
  • go.mod: sigstore-go, sigstore, and protobuf-specs promoted from indirect to direct (already in the dependency graph via toolhive-core — no new modules).

Generated with Claude Code

Skills pushed to OCI registries need signatures so installs can be
verified against the lock file's recorded provenance (RFC THV-0080).
This adds pkg/skills/signer: cosign key-pair signing that signs the
simple-signing payload (binding the artifact's manifest digest), pushes
the classic cosign signature manifest at the sha256-<hex>.sig tag for
interop with `cosign verify --key`, and returns the serialized Sigstore
bundle for durable storage and offline re-verification.

The signature is computed once over the payload and reused for both the
registry manifest annotation and the returned bundle, so a verifier
reconstructing the bundle from the registry checks exactly the
signature that was attached. Encrypted keys are decrypted via
COSIGN_PASSWORD, matching the cosign CLI. Key paths are canonicalized
(absolute, symlinks resolved, regular-file check) before opening.

Part of #5899; consumed by the push-signing and install-verification
PRs in this stack.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@samuv
samuv requested a review from JAORMX as a code owner July 27, 2026 14:04
@github-actions github-actions Bot added the size/L Large PR: 600-999 lines changed label Jul 27, 2026
@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 71.73913% with 39 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.17%. Comparing base (6ef7820) to head (6c104c8).
⚠️ Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
pkg/skills/signer/key.go 68.33% 9 Missing and 10 partials ⚠️
pkg/skills/signer/cosign_attach.go 80.00% 5 Missing and 5 partials ⚠️
pkg/skills/signer/signer.go 64.28% 5 Missing and 5 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6023      +/-   ##
==========================================
+ Coverage   72.14%   72.17%   +0.02%     
==========================================
  Files         720      724       +4     
  Lines       74772    75129     +357     
==========================================
+ Hits        53945    54221     +276     
- Misses      16953    17027      +74     
- Partials     3874     3881       +7     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Cover input-validation failures (garbage key, bad reference, malformed
digest), bare-hex digest normalization, the payload's digest/repository
binding, and the sigstore Keypair metadata accessors, raising patch
coverage flagged by codecov.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added size/L Large PR: 600-999 lines changed and removed size/L Large PR: 600-999 lines changed labels Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L Large PR: 600-999 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant