Page MenuHome GnuPG

Kleopatra: "no trusted certification" should have precedence over "expired" in signature verification
Open, HighPublic

Description

When verifying signatures we currently first check for expiration of the signing certificate.
But if the certificate has no certification you trust, that fact should have precedence over expiration and reported to the user in the result of a signature check.

Please change the verification logic accordingly.

The corresponding result messages from T7786: Draft: Kleopatra: improvements of signature verification result messages require that change.

Event Timeline

ebo triaged this task as Normal priority.Aug 21 2025, 12:38 PM
ebo created this task.
ebo created this object with edit policy "Contributor (Project)".
ebo raised the priority of this task from Normal to High.Oct 20 2025, 4:22 PM
ikloecker moved this task from Backlog to WIP on the gpd5x board.

How I reproduced this:

  • Create new test key
  • Detached-sign some text with the new test key
  • Change trust of test key to "unknown"
  • Expire the test key (e.g. with gpg --quick-set-expire FPR seconds=1)

It would have saved me some time if suitable test data would have been provided.

Using gpg to verify the signature (gpg --status-fd 2 --verify bla.txt.sig bla.txt) I get

[GNUPG:] NEWSIG
gpg: Signature made Di 06 Jan 2026 16:35:20 CET
gpg:                using EDDSA key 98FB8E8F8E5F58FA653E17A6FC9B2EF2C62AC7BE
[GNUPG:] KEYEXPIRED 1767714195
[GNUPG:] KEY_CONSIDERED 98FB8E8F8E5F58FA653E17A6FC9B2EF2C62AC7BE 0
[GNUPG:] KEYEXPIRED 1767714195
[GNUPG:] SIG_ID mmuLNgiB0C7AfTaVYpNjZbcVQok 2026-01-06 1767713720
[GNUPG:] EXPKEYSIG FC9B2EF2C62AC7BE t7790-expired
gpg: Good signature from "t7790-expired" [expired]
[GNUPG:] VALIDSIG 98FB8E8F8E5F58FA653E17A6FC9B2EF2C62AC7BE 2026-01-06 1767713720 0 4 0 22 10 00 98FB8E8F8E5F58FA653E17A6FC9B2EF2C62AC7BE
gpg: Note: This key has expired!
      98FB8E8F8E5F58FA653E17A6FC9B2EF2C62AC7BE

Using the test runner from gpgmeqt I get

Diagnostics: <pre>gpg: Signature made Di 06 Jan 2026 16:35:20 CET
gpg:                using EDDSA key 98FB8E8F8E5F58FA653E17A6FC9B2EF2C62AC7BE
gpg: Good signature from &quot;t7790-expired&quot; [expired]
gpg: Note: This key has expired!
      98FB8E8F8E5F58FA653E17A6FC9B2EF2C62AC7BE
</pre>
Verification Result: GpgME::VerificationResult(
 error:      GpgME::Error(0 (Success))
 fileName:   
 signatures:
GpgME::Signature(
 Summary:                   GpgME::Signature::Summary(KeyExpired )
 Fingerprint:               98FB8E8F8E5F58FA653E17A6FC9B2EF2C62AC7BE
 Status:                    GpgME::Error(117440665 (Key expired))
 creationTime:              1767713720
 expirationTime:            0
 isWrongKeyUsage:           0
 isVerifiedUsingChainModel: 0
 pkaStatus:                 GpgME::Signature::PKAStatus(UnknownPKAStatus)
 pkaAddress:                <null>
 validity:                  ?
 nonValidityReason:         GpgME::Error(0 (Success))
 publicKeyAlgorithm:        EdDSA
 hashAlgorithm:             SHA512
 policyURL:                 <null>
 isDeVs:                    0
 isBetaCompliance:          0
 notations:
)
)

In particular, we see that gpgme reports GpgME::Error(117440665 (Key expired)) as status of the signature and the summary is GpgME::Signature::Summary(KeyExpired ). I don't know how Kleopatra is supposed to derive from this information that the signature is actually "GOOD". I guess because the signature status is "just" expired and not "BAD" and the summary doesn't contain the "RED" flag. I'll check what we get in the case of a revoked signing key.

Oh, I just noticed that gpg doesn't say anything about the trust of the key if the key is expired. Compare this to the following output of gpg in case of a not-expired signing key without trusted certifications.

[GNUPG:] NEWSIG
gpg: Signature made Di 06 Jan 2026 16:35:20 CET
gpg:                using EDDSA key 98FB8E8F8E5F58FA653E17A6FC9B2EF2C62AC7BE
[GNUPG:] KEY_CONSIDERED 98FB8E8F8E5F58FA653E17A6FC9B2EF2C62AC7BE 0
[GNUPG:] SIG_ID mmuLNgiB0C7AfTaVYpNjZbcVQok 2026-01-06 1767713720
[GNUPG:] GOODSIG FC9B2EF2C62AC7BE t7790-expired
gpg: Good signature from "t7790-expired" [unknown]
[GNUPG:] VALIDSIG 98FB8E8F8E5F58FA653E17A6FC9B2EF2C62AC7BE 2026-01-06 1767713720 0 4 0 22 10 00 98FB8E8F8E5F58FA653E17A6FC9B2EF2C62AC7BE
[GNUPG:] TRUST_UNDEFINED 0 pgp
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
      98FB8E8F8E5F58FA653E17A6FC9B2EF2C62AC7BE

As one can clearly see here we get a WARNING about the untrusted signing key, but we didn't get this WARNING for the expired key. To me this doesn't look as if for gpg "no trusted certification" had precedence over "expired".

Verification results for a few more cases (to help with the correct implementation):

Expired key with trust signature:

Verification Result: GpgME::VerificationResult(
 error:      GpgME::Error(0 (Success))
 fileName:   
 signatures:
GpgME::Signature(
 Summary:                   GpgME::Signature::Summary(KeyExpired )
 Fingerprint:               4C5031D011841EC1D8541D993D6358093C97015A
 Status:                    GpgME::Error(117440665 (Key expired))
 creationTime:              1767781629
 expirationTime:            0
 isWrongKeyUsage:           0
 isVerifiedUsingChainModel: 0
 pkaStatus:                 GpgME::Signature::PKAStatus(UnknownPKAStatus)
 pkaAddress:                <null>
 validity:                  ?
 nonValidityReason:         GpgME::Error(0 (Success))
 publicKeyAlgorithm:        EdDSA
 hashAlgorithm:             SHA512
 policyURL:                 <null>
 isDeVs:                    0
 isBetaCompliance:          0
 notations:
)
)

-> This is basically identical to the verification result for an expired key without a trust signature (see above T7790#210738)

Revoked key without trust signature:

Verification Result: GpgME::VerificationResult(
 error:      GpgME::Error(0 (Success))
 fileName:   
 signatures:
GpgME::Signature(
 Summary:                   GpgME::Signature::Summary(KeyRevoked )
 Fingerprint:               70709DA5588B9BC41D90845D73D10B0634EE46BA
 Status:                    GpgME::Error(117440606 (Certificate revoked))
 creationTime:              1767779318
 expirationTime:            0
 isWrongKeyUsage:           0
 isVerifiedUsingChainModel: 0
 pkaStatus:                 GpgME::Signature::PKAStatus(UnknownPKAStatus)
 pkaAddress:                <null>
 validity:                  ?
 nonValidityReason:         GpgME::Error(0 (Success))
 publicKeyAlgorithm:        EdDSA
 hashAlgorithm:             SHA512
 policyURL:                 <null>
 isDeVs:                    0
 isBetaCompliance:          0
 notations:
)
)

Revoked key with trust signature:

Verification Result: GpgME::VerificationResult(
 error:      GpgME::Error(0 (Success))
 fileName:   
 signatures:
GpgME::Signature(
 Summary:                   GpgME::Signature::Summary(KeyRevoked )
 Fingerprint:               BD05F2F741051D331567C2617967963A07F223E4
 Status:                    GpgME::Error(117440606 (Certificate revoked))
 creationTime:              1767781644
 expirationTime:            0
 isWrongKeyUsage:           0
 isVerifiedUsingChainModel: 0
 pkaStatus:                 GpgME::Signature::PKAStatus(UnknownPKAStatus)
 pkaAddress:                <null>
 validity:                  ?
 nonValidityReason:         GpgME::Error(0 (Success))
 publicKeyAlgorithm:        EdDSA
 hashAlgorithm:             SHA512
 policyURL:                 <null>
 isDeVs:                    0
 isBetaCompliance:          0
 notations:
)
)

-> Again this is basically identical to the verification result for an revoked key without a trust signature (see above).

Output of gpg in the above cases:

Expired key with trust signature:

gpg: Signature made Mi 07 Jan 2026 11:27:09 CET
gpg:                using EDDSA key 4C5031D011841EC1D8541D993D6358093C97015A
gpg: Good signature from "t7790-expired-with-trust-signature" [expired]
gpg: Note: This key has expired!
      4C5031D011841EC1D8541D993D6358093C97015A

-> Same as for an expired key without a trust signature (see above T7790#210738), but for the case of a key without trust signature see T8019: gpg does not print warning about untrusted key when verifying signatures made by expired (and untrusted) keys.

Revoked key without trust signature:

gpg: Signature made Mi 07 Jan 2026 10:48:38 CET
gpg:                using EDDSA key 70709DA5588B9BC41D90845D73D10B0634EE46BA
gpg: Good signature from "t7790-revoked" [unknown]
gpg: WARNING: This key has been revoked by its owner!
gpg:          This could mean that the signature is forged.
gpg: reason for revocation: No reason specified
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
      70709DA5588B9BC41D90845D73D10B0634EE46BA

Revoked key with trust signature:

gpg: Signature made Mi 07 Jan 2026 11:27:24 CET
gpg:                using EDDSA key BD05F2F741051D331567C2617967963A07F223E4
gpg: Good signature from "t7790-revoked-with-trust-signature" [unknown]
gpg: WARNING: This key has been revoked by its owner!
gpg:          This could mean that the signature is forged.
gpg: reason for revocation: No reason specified
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
      BD05F2F741051D331567C2617967963A07F223E4

-> Output of gpg for revoked keys with and without trust signature is basically identical (which is a bit surprising because the claim that the revoked key with the trust signature isn't "certified with a trusted signature!" is false (unless for revoked keys all certifications are considered invalid, e.g. because the user ID could be forged).

Concluding we have:

  • The signature should be considered as good if it's not explicitly reported as BAD.
  • We have to check the validity of the signing key separately; the GpgME::Signature lacks information about this. The validity of the signature is the validity of the signing key.