--require-signature accepts a manifest signed by a key other than the required one #167

Open
opened 2026-10-07 10:38:40 +02:00 by clawbot · 1 comment
Collaborator

Problem

check --require-signature and fetch --require-signature compare the required fingerprint with the first fingerprint gpg lists after importing the manifest's embedded public key block (gpgExtractPubKeyFingerprint in mfer/gpg.go, called from verifyRequiredSigner in internal/cli/check.go). The signature is verified separately (gpgVerify, called from verifyOuterIntegrity in mfer/deserialize.go), and gpg --verify succeeds for a good signature by any key in that block. Nothing ties the key that made the signature to the fingerprint compared.

So an embedded block holding two keys passes: the required signer's public key first, then a second key that made the signature. Anyone who has the required signer's public key can make a manifest that --require-signature accepts as signed by them. The signer field (Checker.Signer) is likewise never compared with the key that signed.

Definition of done

  • Failing test first: a manifest whose embedded block holds the required key followed by a second key that made the signature is refused by check --require-signature and by fetch --require-signature. Test keys are made in the test's own temporary GPG home, as the existing gpg tests do.
  • Verification finds out which key made the signature (the primary key fingerprint on the VALIDSIG line of gpg's --status-fd output) and --require-signature compares the required fingerprint with that key, not with a listing of the embedded block.
  • Loading refuses a signed manifest whose embedded block holds more than one primary key, or whose signer field is not the fingerprint of the key that made the signature.
  • The Signature Scheme section of docs/FORMAT.md states what a verifier checks: a good signature over the canonical string, made by the single embedded key, whose fingerprint is signer.
  • make check passes.

Model: opus-5-5

## Problem `check --require-signature` and `fetch --require-signature` compare the required fingerprint with the first fingerprint gpg lists after importing the manifest's embedded public key block (`gpgExtractPubKeyFingerprint` in `mfer/gpg.go`, called from `verifyRequiredSigner` in `internal/cli/check.go`). The signature is verified separately (`gpgVerify`, called from `verifyOuterIntegrity` in `mfer/deserialize.go`), and `gpg --verify` succeeds for a good signature by any key in that block. Nothing ties the key that made the signature to the fingerprint compared. So an embedded block holding two keys passes: the required signer's public key first, then a second key that made the signature. Anyone who has the required signer's public key can make a manifest that `--require-signature` accepts as signed by them. The `signer` field (`Checker.Signer`) is likewise never compared with the key that signed. ## Definition of done - Failing test first: a manifest whose embedded block holds the required key followed by a second key that made the signature is refused by `check --require-signature` and by `fetch --require-signature`. Test keys are made in the test's own temporary GPG home, as the existing gpg tests do. - Verification finds out which key made the signature (the primary key fingerprint on the `VALIDSIG` line of gpg's `--status-fd` output) and `--require-signature` compares the required fingerprint with that key, not with a listing of the embedded block. - Loading refuses a signed manifest whose embedded block holds more than one primary key, or whose `signer` field is not the fingerprint of the key that made the signature. - The Signature Scheme section of `docs/FORMAT.md` states what a verifier checks: a good signature over the canonical string, made by the single embedded key, whose fingerprint is `signer`. - `make check` passes. Model: opus-5-5
clawbot added the critical label 2026-10-07 10:38:49 +02:00
Author
Collaborator

Implemented in #171. Loading a signed manifest now refuses an embedded public key block holding more than one primary key, and a signer field that is not the fingerprint gpg reports for the key that made the signature. check and fetch with --require-signature compare with that checked signer. Signing now signs with and exports the key by its fingerprint, so a --sign-key matching two keys still writes a manifest that loads. Judgement calls are listed in the PR body.

Model: opus-5-5

Implemented in https://git.eeqj.de/sneak/mfer/pulls/171. Loading a signed manifest now refuses an embedded public key block holding more than one primary key, and a `signer` field that is not the fingerprint gpg reports for the key that made the signature. `check` and `fetch` with `--require-signature` compare with that checked `signer`. Signing now signs with and exports the key by its fingerprint, so a `--sign-key` matching two keys still writes a manifest that loads. Judgement calls are listed in the PR body. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/mfer#167