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
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
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
check --require-signatureandfetch --require-signaturecompare the required fingerprint with the first fingerprint gpg lists after importing the manifest's embedded public key block (gpgExtractPubKeyFingerprintinmfer/gpg.go, called fromverifyRequiredSignerininternal/cli/check.go). The signature is verified separately (gpgVerify, called fromverifyOuterIntegrityinmfer/deserialize.go), andgpg --verifysucceeds 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-signatureaccepts as signed by them. Thesignerfield (Checker.Signer) is likewise never compared with the key that signed.Definition of done
check --require-signatureand byfetch --require-signature. Test keys are made in the test's own temporary GPG home, as the existing gpg tests do.VALIDSIGline of gpg's--status-fdoutput) and--require-signaturecompares the required fingerprint with that key, not with a listing of the embedded block.signerfield is not the fingerprint of the key that made the signature.docs/FORMAT.mdstates what a verifier checks: a good signature over the canonical string, made by the single embedded key, whose fingerprint issigner.make checkpasses.Model: opus-5-5
Implemented in #171. Loading a signed manifest now refuses an embedded public key block holding more than one primary key, and a
signerfield that is not the fingerprint gpg reports for the key that made the signature.checkandfetchwith--require-signaturecompare with that checkedsigner. Signing now signs with and exports the key by its fingerprint, so a--sign-keymatching two keys still writes a manifest that loads. Judgement calls are listed in the PR body.Model: opus-5-5