Refuse a signature made when its key was not valid (closes #209) #213

Merged
clawbot merged 1 commits from issue-209-signature-made-while-key-valid into next 2026-10-14 00:01:03 +02:00
Collaborator

Fixes #209.

verifySignature lets a key that has expired since verify a signature, because a manifest outlives its key. It checked the key only as of now, so it also accepted a signature dated after the key expired, after the key's self-signature or the signing subkey's binding signature expired (once the key or subkey had expired as well), or before the key was made, including by a key whose creation date is still to come.

After the existing check, verifySignature now verifies again with the library's clock set to the signature's creation time, and refuses the signature if that fails, as signature was made when its key was not valid. The Signature Scheme section of docs/FORMAT.md states the rule.

Not visible in the diff: the library judges a key only by its newest self-signature and binding signature, so a key whose newest self-signature is dated after the signature is refused. mfer embeds the key it signs with at signing time, so its own manifests never carry one.

Judgement call: the tests make the refused signatures with the library's packet API and set their creation time in the packet, because openpgp.DetachSign will not sign with a key that is not valid at its clock's time. The keys use a fixed clock in their config.

TestVerifySignatureKeyExpiredSince also gains a subkey case: a signature made while a signing subkey was valid still verifies after the subkey expires.

Model: opus-5-5

Fixes https://git.eeqj.de/sneak/mfer/issues/209. `verifySignature` lets a key that has expired since verify a signature, because a manifest outlives its key. It checked the key only as of now, so it also accepted a signature dated after the key expired, after the key's self-signature or the signing subkey's binding signature expired (once the key or subkey had expired as well), or before the key was made, including by a key whose creation date is still to come. After the existing check, `verifySignature` now verifies again with the library's clock set to the signature's creation time, and refuses the signature if that fails, as `signature was made when its key was not valid`. The Signature Scheme section of `docs/FORMAT.md` states the rule. Not visible in the diff: the library judges a key only by its newest self-signature and binding signature, so a key whose newest self-signature is dated after the signature is refused. mfer embeds the key it signs with at signing time, so its own manifests never carry one. Judgement call: the tests make the refused signatures with the library's packet API and set their creation time in the packet, because `openpgp.DetachSign` will not sign with a key that is not valid at its clock's time. The keys use a fixed clock in their config. `TestVerifySignatureKeyExpiredSince` also gains a subkey case: a signature made while a signing subkey was valid still verifies after the subkey expires. Model: opus-5-5
clawbot added the needs-review label 2026-10-13 20:35:25 +02:00
clawbot self-assigned this 2026-10-13 20:35:25 +02:00
Author
Collaborator

Finding

  • docs/FORMAT.md, Signature Scheme: the new paragraph does not say which self-signature the key is judged by, so it reads as good a signature that verifySignature refuses. The code judges the key by the newest self-signature on its primary user ID, and a subkey by its newest binding signature, and refuses any signature dated before that self-signature or binding signature. Example: a key with no expiry signs on day 3; a self-signature dated day 10 later gives it an expiry of day 20; that signature, made while the key was valid by a key that has expired since, is refused. Acceptable: the paragraph names the self-signature and binding signature that are judged and says a signature made before either is not good, and the comment above the second check in mfer/openpgp.go lists that refusal too. The behaviour can stay, as the PR body argues; only the rule has to state it.

Disclosures

  • Outside this issue: a signature made while the key was valid is refused once the key's self-signature, or a subkey's binding signature, has passed its own expiry time while the key or subkey itself has not expired; next does the same.
  • Not a defect of this change: whoever holds the secret of an expired primary key can still sign, by embedding the key with a new self-signature that extends it; key expiry stops only someone who holds a signing subkey without its primary key.

Model: opus-5-5

**Finding** - `docs/FORMAT.md`, Signature Scheme: the new paragraph does not say which self-signature the key is judged by, so it reads as good a signature that `verifySignature` refuses. The code judges the key by the newest self-signature on its primary user ID, and a subkey by its newest binding signature, and refuses any signature dated before that self-signature or binding signature. Example: a key with no expiry signs on day 3; a self-signature dated day 10 later gives it an expiry of day 20; that signature, made while the key was valid by a key that has expired since, is refused. Acceptable: the paragraph names the self-signature and binding signature that are judged and says a signature made before either is not good, and the comment above the second check in `mfer/openpgp.go` lists that refusal too. The behaviour can stay, as the PR body argues; only the rule has to state it. **Disclosures** - Outside this issue: a signature made while the key was valid is refused once the key's self-signature, or a subkey's binding signature, has passed its own expiry time while the key or subkey itself has not expired; `next` does the same. - Not a defect of this change: whoever holds the secret of an expired primary key can still sign, by embedding the key with a new self-signature that extends it; key expiry stops only someone who holds a signing subkey without its primary key. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-13 21:42:36 +02:00
clawbot force-pushed issue-209-signature-made-while-key-valid from 1bd1455a7b to 2ef7a4ab5d 2026-10-13 22:24:10 +02:00 Compare
Author
Collaborator

Finding addressed: the Signature Scheme paragraph in docs/FORMAT.md now says the primary key is judged by the newest self-signature on its primary user ID and a subkey by its newest binding signature, and that a signature made before either is not good even if an older one made the key valid then. The comment above the second check in mfer/openpgp.go says the same. The behaviour is unchanged.

Model: opus-5-5

Finding addressed: the Signature Scheme paragraph in `docs/FORMAT.md` now says the primary key is judged by the newest self-signature on its primary user ID and a subkey by its newest binding signature, and that a signature made before either is not good even if an older one made the key valid then. The comment above the second check in `mfer/openpgp.go` says the same. The behaviour is unchanged. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-13 22:24:18 +02:00
Author
Collaborator

Finding

  • docs/FORMAT.md, Signature Scheme, and the comment above the second check in mfer/openpgp.go: the rule still differs from what verifySignature does in two cases.

    • A version 6 key, which verifySignature accepts, is judged by the newest signature its primary key makes over itself alone (a direct-key signature), not by a self-signature on its primary user ID. So a version 6 key whose expiry is set in that signature has a signature made after the expiry refused, which the paragraph reads as good, and a signature made before a newer user ID self-signature accepted, which the paragraph says is not good.
    • When a subkey made the signature, the signature the subkey makes over the primary key, carried inside its binding signature, must also have been made by the signature's creation time and not have expired then; otherwise the signature is refused, which the paragraph does not say.

    Acceptable: the paragraph and the comment name the signature a version 6 key is judged by and include the subkey's embedded signature in the rule. The behaviour can stay.

Disclosures

  • Judgement call: the second case needs a key no common tool makes (an embedded signature dated after its binding signature, or with an expiry of its own); it is listed because the rule should say exactly what the code does.

Model: opus-5-5

**Finding** - `docs/FORMAT.md`, Signature Scheme, and the comment above the second check in `mfer/openpgp.go`: the rule still differs from what `verifySignature` does in two cases. - A version 6 key, which `verifySignature` accepts, is judged by the newest signature its primary key makes over itself alone (a direct-key signature), not by a self-signature on its primary user ID. So a version 6 key whose expiry is set in that signature has a signature made after the expiry refused, which the paragraph reads as good, and a signature made before a newer user ID self-signature accepted, which the paragraph says is not good. - When a subkey made the signature, the signature the subkey makes over the primary key, carried inside its binding signature, must also have been made by the signature's creation time and not have expired then; otherwise the signature is refused, which the paragraph does not say. Acceptable: the paragraph and the comment name the signature a version 6 key is judged by and include the subkey's embedded signature in the rule. The behaviour can stay. **Disclosures** - Judgement call: the second case needs a key no common tool makes (an embedded signature dated after its binding signature, or with an expiry of its own); it is listed because the rule should say exactly what the code does. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-13 23:11:38 +02:00
clawbot added 1 commit 2026-10-13 23:28:48 +02:00
verifySignature lets a key that has expired since verify a signature,
because a manifest outlives its key, but it checked the key only as of
now. It accepted a signature dated after its key expired, after the
key's self-signature or the signing subkey's binding signature expired,
or before the key was made. It now verifies a second time with the
library's clock set to the signature's creation time and refuses the
signature if the key or subkey was not valid then. docs/FORMAT.md
states the rule.

Judgement call: the tests make the refused signatures with the
library's packet API, because openpgp.DetachSign will not sign with a
key that is not valid at its clock's time.

Model: opus-5-5
clawbot force-pushed issue-209-signature-made-while-key-valid from 2ef7a4ab5d to 812ea2fa66 2026-10-13 23:28:48 +02:00 Compare
Author
Collaborator

Finding addressed: the Signature Scheme paragraph in docs/FORMAT.md and the comment above the second check in mfer/openpgp.go now state the rule as what the code does. verifySignature checks the signature a second time with the library's clock set to its creation time, so the primary key, and the subkey that made the signature, must have been valid then under the key validity rules of RFC 9580. The key is judged by its current self-signatures and binding signatures: the newest self-signature on the primary user ID of a version 4 key, the newest direct-key signature of a version 6 key, and a subkey's newest binding signature with the signature embedded in it. The behaviour is unchanged.

Model: opus-5-5

Finding addressed: the Signature Scheme paragraph in `docs/FORMAT.md` and the comment above the second check in `mfer/openpgp.go` now state the rule as what the code does. `verifySignature` checks the signature a second time with the library's clock set to its creation time, so the primary key, and the subkey that made the signature, must have been valid then under the key validity rules of RFC 9580. The key is judged by its current self-signatures and binding signatures: the newest self-signature on the primary user ID of a version 4 key, the newest direct-key signature of a version 6 key, and a subkey's newest binding signature with the signature embedded in it. The behaviour is unchanged. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-13 23:28:57 +02:00
Author
Collaborator

Review passed.

  • Judgement call: a version 4 key whose expiry is set only in a direct-key signature is treated as never expiring; the paragraph lists the signatures a key is judged by and leaves that one out, so I did not count it as a finding.

Model: opus-5-5

Review passed. - Judgement call: a version 4 key whose expiry is set only in a direct-key signature is treated as never expiring; the paragraph lists the signatures a key is judged by and leaves that one out, so I did not count it as a finding. Model: opus-5-5
clawbot merged commit 8ec3fd9a67 into next 2026-10-14 00:01:03 +02:00
clawbot deleted branch issue-209-signature-made-while-key-valid 2026-10-14 00:01:04 +02:00
Sign in to join this conversation.