OpenPGP Signature Verification ============================== >>> from canonical.launchpad.ftests import import_public_test_keys >>> import transaction >>> import_public_test_keys() >>> transaction.commit() >>> from canonical.launchpad.ftests import login >>> from canonical.launchpad.webapp.interfaces import ILaunchBag >>> bag = getUtility(ILaunchBag) >>> bag.user is None True >>> login('test@canonical.com') >>> bag.user.name u'name12' >>> from zope.component import getUtility >>> from canonical.launchpad.interfaces.gpghandler import IGPGHandler >>> gpghandler = getUtility(IGPGHandler) The text below was "clear signed" by 0xDFD20543 master key: >>> content = """-----BEGIN PGP SIGNED MESSAGE----- ... Hash: SHA1 ... ... Test Message. ... -----BEGIN PGP SIGNATURE----- ... Version: GnuPG v1.2.5 (GNU/Linux) ... ... iD8DBQFC7ZYY2yWXVgK6XvYRAgcwAJ43g/8X6DguixRucoXN86No67/W2wCgjFDj ... jLeauuXDPTcnzDmDzCaQLXo= ... =ettP ... -----END PGP SIGNATURE----- ... """ >>> master_sig = gpghandler.verifySignature(content) >>> print master_sig.fingerprint A419AE861E88BC9E04B9C26FBA2B9389DFD20543 The text below was "clear signed" by a 0x02BA5EF6, a subkey of 0xDFD20543 >>> content = """-----BEGIN PGP SIGNED MESSAGE----- ... Hash: SHA1 ... ... Test Message. ... -----BEGIN PGP SIGNATURE----- ... Version: GnuPG v1.2.5 (GNU/Linux) ... ... iD8DBQFC7ZVH2yWXVgK6XvYRAqWEAJsF9I1MK2tRPPcxjXR2QclSPiQEsgCgtwst ... dZ7wZYeW68bk6GuuadabsSY= ... =ioKn ... -----END PGP SIGNATURE----- ... """ >>> >>> subkey_sig = gpghandler.verifySignature(content) >>> print subkey_sig.fingerprint A419AE861E88BC9E04B9C26FBA2B9389DFD20543 >>> master_sig.fingerprint == subkey_sig.fingerprint True The text below was "clear signed" by 0xDFD20543 master key but tampered with: >>> content = """-----BEGIN PGP SIGNED MESSAGE----- ... Hash: SHA1 ... ... Test Message ... -----BEGIN PGP SIGNATURE----- ... Version: GnuPG v1.2.5 (GNU/Linux) ... ... iD8DBQFC7ZYY2yWXVgK6XvYRAgcwAJ43g/8X6DguixRucoXN86No67/W2wCgjFDj ... jLeauuXDPTcnzDmDzCaQLXo= ... =ettP ... -----END PGP SIGNATURE----- ... """ >>> master_sig = gpghandler.getVerifiedSignature(content) Traceback (most recent call last): ... GPGVerificationError: (7, 8, u'Bad signature') If no signed content is found, an exception is raised: >>> content = """-----BEGIN PGP SIGNED MESSAGE----- ... Hash: SHA1 ... ... Test Message ... -----BEGIN PGP SIGNATURE----- ... -----END PGP SIGNATURE----- ... """ >>> master_sig = gpghandler.getVerifiedSignature(content) Traceback (most recent call last): ... GPGVerificationError: No signatures found The text below contains two clear signed sections. As there are two signing keys involved here, we raise a verification error, since the signed text can not be attributed solely to either key: >>> content = """ ... -----BEGIN PGP SIGNED MESSAGE----- ... Hash: SHA1 ... ... Test Message. ... -----BEGIN PGP SIGNATURE----- ... Version: GnuPG v1.4.1 (GNU/Linux) ... ... iD8DBQFD3xV52yWXVgK6XvYRAtJQAJ4ojuLC4aap4R9T0og17RkPYoND+ACfbCA3 ... yrZD6MZcqzyaGNy1s28Co2Q= ... =5QGd ... -----END PGP SIGNATURE----- ... -----BEGIN PGP SIGNED MESSAGE----- ... Hash: SHA1 ... ... Some data appended by foo.bar@canonical.com ... -----BEGIN PGP SIGNATURE----- ... Version: GnuPG v1.4.1 (GNU/Linux) ... ... iD8DBQFD3xWpjn63CGxkqMURAmi6AJ4yHAnhIpt49VlYDG1uxpGy9BmHwwCeKbFM ... aHIJLqhWVf8bGLHZBIH5odw= ... =iUSC ... -----END PGP SIGNATURE----- ... """ XXX: See discussion on the launchpad@ mailing list for an explanation to this change. -- Guilherme Salgado, 2007-04-05 (https://lists.ubuntu.com/mailman/private/launchpad/2007-April/015085.html) >>> master_sig = gpghandler.getVerifiedSignature(content) Traceback (most recent call last): ... GPGVerificationError... # >>> master_sig = gpghandler.getVerifiedSignature(content) # Traceback (most recent call last): # ... # GPGVerificationError: Single signature expected, found multiple signatures The text below was signed by key that's is not part of the imported keyring. Note that we have extra debug information containing the GPGME error codes (they may be helpful). >>> content = """-----BEGIN PGP SIGNED MESSAGE----- ... Hash: SHA1 ... ... Text Message ... -----BEGIN PGP SIGNATURE----- ... Version: GnuPG v1.4.2.2 (GNU/Linux) ... ... iD8DBQFFo+jp4ZLAVDsbsusRAhcYAJ9OOo7+tAxK94xGDu5yIUQG1LEY+wCeJvxr ... bOpYlIQD8vo7f9Y6LGqJbCc= ... =ds3K ... -----END PGP SIGNATURE----- ... """ >>> gpghandler.getVerifiedSignature(content) Traceback (most recent call last): ... GPGVerificationError: (7, 9, u'No public key') Due to unpredictable behaviour between the production system and the external keyserver, we have a resilient signature verifier, encapsulated in 'getVerifiedSignatureResilient'. It retries the failed verification 2 other times before raising an exception. The exception raised by this method will contain debug information for the 3 failures. >>> gpghandler.getVerifiedSignatureResilient(content) 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')"]