openpgp: prevent panic when candidate identity has no self-signature#313
Open
lexfrei wants to merge 1 commit into
Open
openpgp: prevent panic when candidate identity has no self-signature#313lexfrei wants to merge 1 commit into
lexfrei wants to merge 1 commit into
Conversation
A user ID can be stored with a nil SelfSignature, for example when it only carries a revocation or when its self-signature fails to verify. shouldPreferIdentity already guards the case where the existing primary has no self-signature, but dereferences the candidate's SelfSignature unconditionally. Because PrimaryIdentity iterates the Identities map in a non-deterministic order, the candidate is the empty identity in a fraction of runs, which panics. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
shouldPreferIdentitypanics with a nil pointer dereference when an identity inEntity.Identitieshas a nilSelfSignature. It already guards the case where the existing primary has a nilSelfSignature, but dereferences the candidate'sSelfSignatureunconditionally a few lines later.A user ID is stored with a nil
SelfSignaturewhen it carries only a revocation, or when its self-signature fails to verify —addUserIDstill inserts the identity into the map in the revocation-only case. BecausePrimaryIdentityranges over theIdentitiesmap, whose iteration order is randomized, that empty identity lands in the candidate slot in a fraction of runs and panics. This is the same class of failure fixed for the existing-identity side in #99; the candidate side was left unguarded.Fix
Add the symmetric nil check: if the candidate has no self-signature, keep the existing identity (which is known to have a valid one). An identity with a real self-signature is always preferred over one without, regardless of map iteration order.
Impact
go-cryptois the OpenPGP implementation OpenTofu uses to authenticate provider packages, and the panic is reachable fromPrimarySelfSignature→EntityList.KeysByIdduring signature verification. When it fires,tofu initaborts with a Go panic and stack trace instead of installing the provider, so that provider — and therefore every subsequent OpenTofu command — cannot be used.Because the crash depends on the randomized map iteration order, it is intermittent: re-running
initoften succeeds. That makes it a flaky CI failure, which is costly to diagnose and easy to misattribute to infrastructure rather than to a library bug.The trigger is not a contrived edge case. Any caller verifying signatures with a key whose identities include a user ID that has no usable self-signature is affected — for example a revoked user ID, or one whose self-signature fails to verify. Long-lived signing keys commonly accumulate such user IDs (e.g. revoked addresses from a previous employer), so real-world provider keys hit this.
Reproduction
Observed with OpenTofu (which pins go-crypto v1.4.1) installing a provider whose signing key carried a user ID without a usable self-signature:
tofu initpanicked in a large fraction of runs during signature authentication, while succeeding on others.Tests
shouldPreferIdentitycovering the candidate-nil, existing-nil, and both-nil cases.TestPrimaryIdentityWithNilSelfSignaturebuilds anEntitywith one valid and one self-signature-less identity and callsPrimaryIdentityin a loop to defeat the randomized map order, asserting the identity with a valid self-signature is always chosen and that no panic occurs.Both tests panic at
keys.go:115without the fix and pass with it.