gpgv does not handle expired or revoked keys
Open, NormalPublic

Description

gpgv is meant as a tool that does stricter key verification.

It currently does not seem to handle revoked or expired GPG keys, returned
SUCCESS for verification with them.

It is not clear from the manual page if this is a missing feature or
intentional. If intentional it should probably clarified in the manpage.

(spotted by Michael Schroeder at SUSE)

Details

Version
2.1.12
meissner set Version to 2.0.19.
meissner added a subscriber: meissner.
werner added a subscriber: werner.Oct 4 2013, 9:28 PM

Done for 1.4.15 and 2.0.22.

werner closed this task as Resolved.Oct 4 2013, 9:28 PM
werner claimed this task.
dkg added a subscriber: dkg.May 23 2016, 4:40 PM

I don't think this is actually resolved.

As noted in https://lists.gnupg.org/pipermail/gnupg-devel/2016-April/031032.html
, gpgv accepts signatures made from revoked or expired keys.

It should reject signatures made from keys it believes to be revoked or expired.

The attached tarball contains:

     pubkey.gpg -- a binary-format 2048-bit RSA OpenPGP certificate

     C47D9EDFF117EE2AA11B162D017D715B3D0C4AF2.key -- the corresponding
                                                     secret key (for
                                                     reference/experimentation
                                                     only)

     before.txt.asc -- clearsigned message made by the key before
                       certificate creation time

     during.txt.asc -- clearsigned message made by the key between
                       certificate creation and certificate expiration

     after.txt.asc -- clearsigned message made by the key after certificate
                      expiration

of these, gpg approves of during.txt.asc and after.txt.asc, but not before.txt.asc.

dkg added a comment.May 23 2016, 4:40 PM

dkg changed Version from 2.0.19 to 2.1.12.May 23 2016, 4:40 PM
dkg reopened this task as Open.

By resolved, I meant that the man page now states:

  gpgv  assumes  that  all  keys in the keyring are trustworthy.  That does also
  mean that it does not check for expired or revoked keys.

Your wish is to change this behaviour. This would be an API break and thus I
hestitate to do this for 1.4 and 2.0. However, 2.1 has a lot of changes anyway
and I think it is okay to change it for 2.1.

werner lowered the priority of this task from Normal to Low.May 30 2016, 8:05 AM
werner removed a project: Bug Report.
werner raised the priority of this task from Low to Normal.
werner added a project: Feature Request.
werner removed a project: Documentation.
werner added a comment.Jan 6 2017, 6:25 PM

I do not think that an expired key should be ignored. The reason is that it
won't be possible to verify an old package because it is common that keys expire
at some time. This does not say anything on whether the key has been compromised.

However, if a key has been revoked, that might be be an indication that the key
has been comprimised and that old signature may have been replaced by faked
ones. I would agree to return failure in this case.

justus moved this task from Backlog to Wishlist on the gnupg (gpg22) board.May 24 2017, 1:17 PM
justus claimed this task.Jun 21 2017, 12:18 PM

So both gpg and gpgv seem to return success (as in the exit code is 0) if the signature is correct, even if the key is revoked or expired:

$ gpg --verify /home/teythoon/repos/g10/gnupg/obj/fortune.expired.gpg && echo success || echo failure
gpg: Signature made Sun Jul 12 15:09:25 2015 CEST
gpg:                using RSA key 4918AE3FE7E95EE73C770F86E94E684C62CD31CE
gpg:                issuer "expired@example.org"
gpg: Good signature from "expired@example.org" [expired]
gpg: Note: This key has expired!
Primary key fingerprint: 4918 AE3F E7E9 5EE7 3C77  0F86 E94E 684C 62CD 31CE
success
$ gpg --verify /home/teythoon/repos/g10/gnupg/obj/fortune.revoked.gpg && echo success || echo failure
gpg: Signature made Sun Jul 12 15:09:26 2015 CEST
gpg:                using RSA key E890AD8E6416356194E20B54B3B1C7CF2961F62D
gpg:                issuer "revoked@example.org"
gpg: Good signature from "revoked@example.org" [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.
Primary key fingerprint: E890 AD8E 6416 3561 94E2  0B54 B3B1 C7CF 2961 F62D
success

(gpgv does the same, trust me (tm)).

Is that what we want?

dkg added a comment.Jul 12 2017, 12:00 AM

I don't think that's what we want. An OpenPGP certificate has a claimed temporal validity window: from the creation date of the certificate to its expiration or revocation date.

in the tarball i attached above, we reject signatures that have been made before the key was claimed to be valid.

so gpgv and gpg are *not* ignoring the key's claimed temporal validity window entirely -- they respect the start of the claimed temporal validity window.

But they ignore the *end* of the temporal validity window. That seems like a mistake.

Sorry, I expressed my concern poorly. gpg does recognize the keys as being expired/revoked, but this is not reflected in the exit code of the gpg/gpgv process.

Again, this time with status-fd=1:

$ g10/gpg --status-fd=1 --verify /home/teythoon/repos/g10/gnupg/obj/fortune.expired.gpg && echo success || echo failure
[GNUPG:] NEWSIG expired@example.org
gpg: Signature made Sun 12 Jul 2015 03:09:25 PM CEST
gpg:                using RSA key 4918AE3FE7E95EE73C770F86E94E684C62CD31CE
gpg:                issuer "expired@example.org"
[GNUPG:] KEY_CONSIDERED 4918AE3FE7E95EE73C770F86E94E684C62CD31CE 0
[GNUPG:] KEYEXPIRED 1468242504
[GNUPG:] SIG_ID Vki15u2E/yG0CZyWmmmI6FjeLec 2015-07-12 1436706565
[GNUPG:] KEY_CONSIDERED 4918AE3FE7E95EE73C770F86E94E684C62CD31CE 0
[GNUPG:] EXPKEYSIG E94E684C62CD31CE expired@example.org
gpg: Good signature from "expired@example.org" [expired]
[GNUPG:] VALIDSIG 4918AE3FE7E95EE73C770F86E94E684C62CD31CE 2015-07-12 1436706565 0 4 0 1 8 00 4918AE3FE7E95EE73C770F86E94E684C62CD31CE
gpg: Note: This key has expired!
Primary key fingerprint: 4918 AE3F E7E9 5EE7 3C77  0F86 E94E 684C 62CD 31CE
[GNUPG:] VERIFICATION_COMPLIANCE_MODE 23
success
$ g10/gpg --status-fd=1 --verify /home/teythoon/repos/g10/gnupg/obj/fortune.revoked.gpg && echo success || echo failure
[GNUPG:] NEWSIG revoked@example.org
gpg: Signature made Sun 12 Jul 2015 03:09:26 PM CEST
gpg:                using RSA key E890AD8E6416356194E20B54B3B1C7CF2961F62D
gpg:                issuer "revoked@example.org"
[GNUPG:] KEY_CONSIDERED E890AD8E6416356194E20B54B3B1C7CF2961F62D 0
[GNUPG:] SIG_ID 8JYnRuhVXsMg12y9sDnVTl3KWSo 2015-07-12 1436706566
[GNUPG:] KEY_CONSIDERED E890AD8E6416356194E20B54B3B1C7CF2961F62D 0
[GNUPG:] REVKEYSIG B3B1C7CF2961F62D revoked@example.org
gpg: Good signature from "revoked@example.org" [unknown]
[GNUPG:] VALIDSIG E890AD8E6416356194E20B54B3B1C7CF2961F62D 2015-07-12 1436706566 0 4 0 1 8 00 E890AD8E6416356194E20B54B3B1C7CF2961F62D
[GNUPG:] KEYREVOKED
gpg: WARNING: This key has been revoked by its owner!
gpg:          This could mean that the signature is forged.
[GNUPG:] KEY_CONSIDERED E890AD8E6416356194E20B54B3B1C7CF2961F62D 0
gpg: reason for revocation: No reason specified
[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.
Primary key fingerprint: E890 AD8E 6416 3561 94E2  0B54 B3B1 C7CF 2961 F62D
[GNUPG:] VERIFICATION_COMPLIANCE_MODE 23
success

When I do the same using GPGME, it clearly communicates the problems:

$ python3 -c "import gpg; print(gpg.Context().verify(open('/home/teythoon/repos/g10/gnupg/obj/fortune.expired.gpg')))"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/teythoon/repos/g10/local/lib/python3.5/site-packages/gpg/core.py", line 490, in verify
    raise errors.BadSignatures(results[1], results=results)
gpg.errors.BadSignatures: 4918AE3FE7E95EE73C770F86E94E684C62CD31CE: Key expired
$ python3 -c "import gpg; print(gpg.Context().verify(open('/home/teythoon/repos/g10/gnupg/obj/fortune.revoked.gpg')))"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/teythoon/repos/g10/local/lib/python3.5/site-packages/gpg/core.py", line 490, in verify
    raise errors.BadSignatures(results[1], results=results)
gpg.errors.BadSignatures: E890AD8E6416356194E20B54B3B1C7CF2961F62D: Certificate revoked
dkg added a comment.Jul 14 2017, 12:26 PM

for expiration (or for revocations flagged "key was superseded" instead of "compromised"), you can have a signature made *before* the key's expiration/revocation, but you might be verifying it *after* the key was revoked/expired.

that is:

  • t=0 create key K
  • t=1 use K to make signature S
  • t=2 key K expires
  • t=3 try to verify signature S

In this circumstance, the signature is valid (but old), even though the key is expired at time of signature verification. compare that with:

  • t=0 create key K
  • t=1 key K expires
  • t=2 use K to make signature S
  • t=3 try to verify signature S

In this circumstance, signature S is invalid (and can only be made valid if the key's expiration date is updated to a point later than t=2).

dkg added a comment.Jul 14 2017, 12:29 PM

Thinking about it more broadly, i think that gpgv (and gpg, when used in signature verification mode) should have a return code that is as close to the true/false underlying semantics that users will want, rather than relying on status messages to distinguish between these cases.

obviously, there's more nuance to this than true/false can provide, but users *want* a simple verification step, so they're likely to use the return code. we should make sure it works as clearly and cleanly as we can.

justus removed justus as the assignee of this task.Aug 8 2017, 11:14 AM
justus added a subscriber: justus.

We are in feature freeze and changing the status code of gpgv will likely cause problems for gpgme. We need to defer this.

dkg added a comment.Aug 8 2017, 7:47 PM

Can you describe the problems it would cause for gpgme? gpgme already currently expects that gpgv will return a failure for signatures made before the validity window of the key. so gpgme won't break just because gpgv is capable of returning a non-zero response.

for example (using the gpgv-dates above) we can see it returning 2, as well as the ERRSIG status on status-fd:

0 dkg@host:gpgv-dates$ gpgv --status-fd 3 --keyring pubkey.gpg before.txt.asc  3>status.out
gpgv: Signature made Mon 09 Nov 2015 12:00:03 AM EST
gpgv:                using RSA key 655C6990848F642B
gpgv: public key 655C6990848F642B is 165 days newer than the signature
gpgv: Can't check signature: Time conflict
2 dkg@alice:/tmp/cdtemp.9CTcwC/gpgv-dates$ cat status.out 
[GNUPG:] NEWSIG
[GNUPG:] KEY_CONSIDERED C0A429B45292C8C082E5E31A655C6990848F642B 0
[GNUPG:] ERRSIG 655C6990848F642B 1 8 00 1447045203 39
0 dkg@host:gpgv-dates$

I also note that back in 2016 Werner identified this as a candidate for 2.1. Please help make revocation and expiration actually meaningful.

werner added a comment.Aug 8 2017, 9:09 PM

GPGME does not use gpgv. What Justus likely meant is that we would need to change the common code used by gpgv and gpg. That may give problems in GPGME.

I did not re-read this thread but I do not think that this would affect GPGME. However, we need to stop fixning bugs at some point and do a release for a stable maintained branch (LTS they call it these times). This is why we can't fix this now. I am really sorry about this. Many other things took too much time and thus the 2.2 release is more than a year late. We need to live with some bugs for now and find a way later to fix them without an invasive patch.