launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #23375
[Merge] lp:~cjwatson/launchpad/no-auto-key-retrieve into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/no-auto-key-retrieve into lp:launchpad.
Commit message:
Use our own GPG key retrieval implementation when verifying signatures rather than relying on auto-key-retrieve.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/no-auto-key-retrieve/+merge/364100
It's hard to verify this in the test suite, but this should avoid a current problem where we sometimes seem to tickle SKS into returning old keys with old expiration dates as a result of auto-key-retrieve using slightly different URLs.
This doesn't quite fix bug 3052 (!), but it should make it easier to fix.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/no-auto-key-retrieve into lp:launchpad.
=== modified file 'lib/lp/services/gpg/doc/gpg-signatures.txt'
--- lib/lp/services/gpg/doc/gpg-signatures.txt 2012-06-29 08:40:05 +0000
+++ lib/lp/services/gpg/doc/gpg-signatures.txt 2019-03-07 15:39:32 +0000
@@ -7,6 +7,10 @@
>>> transaction.commit()
+ >>> from lp.testing.keyserver import KeyServerTac
+ >>> keyserver = KeyServerTac()
+ >>> keyserver.setUp()
+
>>> from lp.testing import login
>>> from lp.services.webapp.interfaces import ILaunchBag
@@ -164,7 +168,8 @@
>>> gpghandler.getVerifiedSignature(content)
Traceback (most recent call last):
...
- GPGVerificationError: (7, 9, u'No public key')
+ GPGKeyDoesNotExistOnServer: GPG key E192C0543B1BB2EB does not exist on the
+ keyserver.
Due to unpredictable behaviour between the production system and
the external keyserver, we have a resilient signature verifier,
@@ -178,9 +183,9 @@
Traceback (most recent call last):
...
GPGVerificationError: Verification failed 3 times:
- ["(7, 9, u'No public key')",
- "(7, 9, u'No public key')",
- "(7, 9, u'No public key')"]
+ ['GPG key E192C0543B1BB2EB does not exist on the keyserver.',
+ 'GPG key E192C0543B1BB2EB does not exist on the keyserver.',
+ 'GPG key E192C0543B1BB2EB does not exist on the keyserver.']
Debugging exceptions
@@ -227,3 +232,4 @@
[<gpgme.Signature object at ...>]
7
+ >>> keyserver.tearDown()
=== modified file 'lib/lp/services/gpg/handler.py'
--- lib/lp/services/gpg/handler.py 2018-03-26 14:29:32 +0000
+++ lib/lp/services/gpg/handler.py 2019-03-07 15:39:32 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2019 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
__metaclass__ = type
@@ -99,13 +99,8 @@
"""
self.home = tempfile.mkdtemp(prefix='gpg-')
confpath = os.path.join(self.home, 'gpg.conf')
- # set needed GPG options, 'auto-key-retrieve' is necessary for
- # automatically retrieve from the keyserver unknown key when
- # verifying signatures and 'no-auto-check-trustdb' avoid wasting
- # time verifying the local keyring consistence.
with open(confpath, 'w') as conf:
- conf.write('keyserver hkp://%s\n' % config.gpghandler.host)
- conf.write('keyserver-options auto-key-retrieve\n')
+ # Avoid wasting time verifying the local keyring's consistency.
conf.write('no-auto-check-trustdb\n')
# Prefer a SHA-2 hash where possible, otherwise GPG will fall
# back to a hash it can use.
@@ -157,7 +152,7 @@
for i in range(3):
try:
signature = self.getVerifiedSignature(content, signature)
- except GPGVerificationError as info:
+ except GPGKeyNotFoundError as info:
errors.append(info)
else:
return signature
@@ -167,14 +162,13 @@
raise GPGVerificationError(
"Verification failed 3 times: %s " % stored_errors)
- def getVerifiedSignature(self, content, signature=None):
- """See IGPGHandler."""
-
- assert not isinstance(content, unicode)
- assert not isinstance(signature, unicode)
-
- ctx = self._getContext()
-
+ def _rawVerifySignature(self, ctx, content, signature=None):
+ """Internals of `getVerifiedSignature`.
+
+ This is called twice during a typical verification: once to work out
+ the correct fingerprint, and once after retrieving the corresponding
+ key from the keyserver.
+ """
# from `info gpgme` about gpgme_op_verify(SIG, SIGNED_TEXT, PLAIN):
#
# If SIG is a detached signature, then the signed text should be
@@ -228,22 +222,34 @@
raise GPGVerificationError('Single signature expected, '
'found multiple signatures')
- signature = signatures[0]
+ return plain, signatures[0]
+
+ def getVerifiedSignature(self, content, signature=None):
+ """See IGPGHandler."""
+
+ assert not isinstance(content, unicode)
+ assert not isinstance(signature, unicode)
+
+ ctx = self._getContext()
+
+ # We may not yet have the public key, so find out the fingerprint we
+ # need to fetch.
+ _, sig = self._rawVerifySignature(ctx, content, signature=signature)
+
+ # Fetch the full key from the keyserver now that we know its
+ # fingerprint, and then verify the signature again. (This also lets
+ # us support subkeys by using the master key fingerprint.)
+ key = self.retrieveKey(sig.fpr)
+ plain, sig = self._rawVerifySignature(
+ ctx, content, signature=signature)
+
expired = False
- # signature.status == 0 means "Ok"
- if signature.status is not None:
- if signature.status.code == gpgme.ERR_KEY_EXPIRED:
+ # sig.status == 0 means "Ok"
+ if sig.status is not None:
+ if sig.status.code == gpgme.ERR_KEY_EXPIRED:
expired = True
else:
- raise GPGVerificationError(signature.status.args)
-
- # Support subkeys by retrieving the full key from the keyserver and
- # using the master key fingerprint.
- try:
- key = self.retrieveKey(signature.fpr)
- except GPGKeyNotFoundError:
- raise GPGVerificationError(
- "Unable to map subkey: %s" % signature.fpr)
+ raise GPGVerificationError(sig.status.args)
if expired:
# This should already be set, but let's make sure.
@@ -254,7 +260,7 @@
return PymeSignature(
fingerprint=key.fingerprint,
plain_data=plain.getvalue(),
- timestamp=signature.timestamp)
+ timestamp=sig.timestamp)
def importPublicKey(self, content):
"""See IGPGHandler."""
Follow ups