Skip to content

manifest: extend signing payload to cover the whole manifest - #34

Merged
TeoSlayer merged 1 commit into
fix/supervisor-signature-backofffrom
sec/findings-app-store
Jul 26, 2026
Merged

manifest: extend signing payload to cover the whole manifest#34
TeoSlayer merged 1 commit into
fix/supervisor-signature-backofffrom
sec/findings-app-store

Conversation

@TeoSlayer

Copy link
Copy Markdown
Contributor

What

The manifest signing payload (signingPayload, manifest.go:188-197) was built from only five inputs:

Store.Publisher : ID : ManifestVersion : Binary.SHA256 : sha256(Grants)

Everything else in the manifest sat outside the signed bytes and could be edited without disturbing the signature:

Field Why it matters
Binary.Path which executable the supervisor launches
Binary.Runtime which interpreter runs it
Exposes the app's IPC entrypoints
Protection shareable vs guarded storage/process isolation
Affiliates peers whose calls skip per-call user consent
Depends declared cross-app method calls
Extends daemon hook points + dispatched methods + contributed CLI flags
DynamicExtends the bound on runtime hook registration
AppVersion publisher-visible version

Change

Adds a v2 payload that commits to the entire manifest:

"pilot.app-manifest.v2" : <publisher> : sha256(canonical JSON of manifest with store.signature blanked)

Blanking store.signature is what makes the hash reproducible on both sides. The domain-separation prefix keeps v1 and v2 payload bytes disjoint, so a signature for one scheme can never verify under the other.

New exported helpers: Manifest.SigningPayload() (returns v2 bytes) and Manifest.Sign(priv) (signs v2, refuses a key that does not match Store.Publisher).

Compatibility — nothing breaks now

VerifySignature tries v2 first, then falls back to v1 while AllowLegacySignaturePayload is true. That flag defaults to true, so every already-signed manifest in the wild keeps verifying and no installed app is disturbed. Existing signing code paths (including plugin/appstore test helpers, which still sign v1) are untouched and still pass. Flipping the flag to false moves to v2-only.

Tests

New pkg/manifest/zz3_signing_payload_test.go:

  • TestV2PayloadCoversWholeManifest — 16 sub-cases, one per field listed above plus the fields v1 already covered; each mutation must break verification.
  • TestV1SignatureStillVerifies — v1 signatures verify with the default flag.
  • TestLegacyFallbackCanBeDisabled — flag off rejects v1, still accepts v2.
  • TestV1PayloadLeavesFieldsUnsigned — pins the exact v1/v2 delta.
  • TestSigningPayloadIndependentOfSignatureField — payload stable regardless of store.signature, and computing it does not mutate the manifest.
  • TestSignRejectsMismatchedPublisher, TestV2SignatureIsNotAcceptedForAnotherPublisher.

go build ./..., go vet ./... and go test ./... all green.

🤖 Generated with Claude Code

The v1 signing payload was built from Store.Publisher, ID, ManifestVersion,
Binary.SHA256 and a hash of Grants. Every other field — Binary.Path,
Binary.Runtime, Exposes, Protection, Affiliates, Depends, Extends,
DynamicExtends, AppVersion — sat outside the signed bytes and could be edited
without disturbing the signature, even though several of them decide what the
app is allowed to do (exec path, IPC entrypoints, hook points, co-trusted
peers, storage protection).

Add a v2 payload that commits to the publisher key plus a sha256 over the
canonical JSON of the manifest with Store.Signature blanked, under the
domain-separation tag "pilot.app-manifest.v2".

Compatibility: VerifySignature tries v2 first and falls back to v1 while
AllowLegacySignaturePayload is true, which is the default, so every manifest
signed under the old scheme keeps verifying and no installed app is affected.
Setting the flag to false accepts v2 only. New signatures are produced by the
new Manifest.Sign / Manifest.SigningPayload helpers, which emit v2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@TeoSlayer
TeoSlayer merged commit 4634bfa into fix/supervisor-signature-backoff Jul 26, 2026
3 checks passed
@TeoSlayer
TeoSlayer deleted the sec/findings-app-store branch July 26, 2026 14:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants