Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-python-gnupg for openSUSE:Factory checked in at 2021-12-09 19:45:02 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-python-gnupg (Old) and /work/SRC/openSUSE:Factory/.python-python-gnupg.new.2520 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-python-gnupg" Thu Dec 9 19:45:02 2021 rev:11 rq:934532 version:0.4.8 Changes: -------- --- /work/SRC/openSUSE:Factory/python-python-gnupg/python-python-gnupg.changes 2021-08-04 22:28:31.965827955 +0200 +++ /work/SRC/openSUSE:Factory/.python-python-gnupg.new.2520/python-python-gnupg.changes 2021-12-09 19:45:05.257118188 +0100 @@ -1,0 +2,10 @@ +Mon Nov 29 11:57:11 UTC 2021 - Dirk M??ller <dmuel...@suse.com> + +- update to 0.4.8: + * Return gpg's return code in all result instances. + * Add check for invalid file objects. + * Provide more useful status message when a secret key is absent. + * Added a get_recipients() API to find the recipients of an encrypted + message without decrypting it. + +------------------------------------------------------------------- Old: ---- python-gnupg-0.4.7.tar.gz New: ---- python-gnupg-0.4.8.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-python-gnupg.spec ++++++ --- /var/tmp/diff_new_pack.NMufFG/_old 2021-12-09 19:45:05.869118482 +0100 +++ /var/tmp/diff_new_pack.NMufFG/_new 2021-12-09 19:45:05.869118482 +0100 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define oldpython python Name: python-python-gnupg -Version: 0.4.7 +Version: 0.4.8 Release: 0 Summary: A wrapper for the GNU Privacy Guard (GPG or GnuPG) License: BSD-3-Clause ++++++ python-gnupg-0.4.7.tar.gz -> python-gnupg-0.4.8.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.4.7/PKG-INFO new/python-gnupg-0.4.8/PKG-INFO --- old/python-gnupg-0.4.7/PKG-INFO 2021-03-11 09:23:20.000000000 +0100 +++ new/python-gnupg-0.4.8/PKG-INFO 2021-11-24 10:39:28.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: python-gnupg -Version: 0.4.7 +Version: 0.4.8 Summary: A wrapper for the Gnu Privacy Guard (GPG or GnuPG) Home-page: https://docs.red-dove.com/python-gnupg/ Author: Vinay Sajip @@ -8,7 +8,7 @@ Maintainer: Vinay Sajip Maintainer-email: vinay_sa...@yahoo.co.uk License: Copyright (C) 2008-2021 by Vinay Sajip. All Rights Reserved. See LICENSE.txt for license. -Download-URL: https://pypi.io/packages/source/p/python-gnupg/python-gnupg-0.4.7.tar.gz +Download-URL: https://pypi.io/packages/source/p/python-gnupg/python-gnupg-0.4.8.tar.gz Description: This module allows easy access to GnuPG's key management, encryption and signature functionality from Python programs. It is intended for use with Python 2.4 or greater. Releases are normally signed using a GnuPG key with the user id vinay_sa...@yahoo.co.uk and the following fingerprint: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.4.7/README.rst new/python-gnupg-0.4.8/README.rst --- old/python-gnupg-0.4.7/README.rst 2021-03-11 07:59:24.000000000 +0100 +++ new/python-gnupg-0.4.8/README.rst 2021-11-24 10:11:43.000000000 +0100 @@ -64,11 +64,26 @@ .. note:: GCnn refers to an issue nn on Google Code. -0.4.8 (future) +0.4.9 (future) -------------- Released: Not yet. +0.4.8 +----- + +Released: 2021-11-24 + +* Fixed #147: Return gpg's return code in all result instances. + +* Fixed #152: Add check for invalid file objects. + +* Fixed #157: Provide more useful status message when a secret key is absent. + +* Fixed #158: Added a get_recipients() API to find the recipients of an encrypted + message without decrypting it. + + 0.4.7 ----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.4.7/gnupg.py new/python-gnupg-0.4.8/gnupg.py --- old/python-gnupg-0.4.7/gnupg.py 2021-03-11 08:01:14.000000000 +0100 +++ new/python-gnupg-0.4.8/gnupg.py 2021-11-24 10:13:33.000000000 +0100 @@ -32,9 +32,9 @@ A unittest harness (test_gnupg.py) has also been added. """ -__version__ = "0.4.7" +__version__ = "0.4.8" __author__ = "Vinay Sajip" -__date__ = "$11-Mar-2021 07:01:14$" +__date__ = "$24-Nov-2021 09:13:33$" try: from io import StringIO @@ -230,6 +230,8 @@ 11: 'incorrect passphrase', } + returncode = None + def __init__(self, gpg): self.gpg = gpg self.valid = False @@ -326,6 +328,10 @@ self.valid = False self.key_id = value self.status = 'no public key' + elif key == "NO_SECKEY": # pragma: no cover + self.valid = False + self.key_id = value + self.status = 'no secret key' elif key in ("EXPKEYSIG", "REVKEYSIG"): # pragma: no cover # signed with expired or revoked key self.valid = False @@ -366,7 +372,7 @@ if not self.status: self.status = message elif key in ("DECRYPTION_INFO", "PLAINTEXT", "PLAINTEXT_LENGTH", - "NO_SECKEY", "BEGIN_SIGNING"): + "BEGIN_SIGNING"): pass else: # pragma: no cover logger.debug('message ignored: %s, %s', key, value) @@ -377,6 +383,9 @@ counts = '''count no_user_id imported imported_rsa unchanged n_uids n_subk n_sigs n_revoc sec_read sec_imported sec_dups not_imported'''.split() + + returncode = None + def __init__(self, gpg): self.gpg = gpg self.results = [] @@ -469,6 +478,9 @@ } class SendResult(object): + + returncode = None + def __init__(self, gpg): self.gpg = gpg @@ -492,6 +504,7 @@ UID_INDEX = 1 FIELDS = 'type keyid algo length date expires'.split() + returncode = None def __init__(self, gpg): self.gpg = gpg @@ -637,15 +650,19 @@ if key in ("WARNING", "ERROR"): logger.warning('potential problem: %s: %s', key, value) elif key == "NODATA": - self.status = "no data was provided" + if self.status not in ("decryption failed",): + self.status = "no data was provided" elif key in ("NEED_PASSPHRASE", "BAD_PASSPHRASE", "GOOD_PASSPHRASE", - "MISSING_PASSPHRASE", "DECRYPTION_FAILED", - "KEY_NOT_CREATED", "NEED_PASSPHRASE_PIN"): + "MISSING_PASSPHRASE", "KEY_NOT_CREATED", "NEED_PASSPHRASE_PIN"): self.status = key.replace("_", " ").lower() + elif key == "DECRYPTION_FAILED": + if self.status != 'no secret key': # don't overwrite more useful message + self.status = 'decryption failed' elif key == "NEED_PASSPHRASE_SYM": self.status = 'need symmetric passphrase' elif key == "BEGIN_DECRYPTION": - self.status = 'decryption incomplete' + if self.status != 'no secret key': # don't overwrite more useful message + self.status = 'decryption incomplete' elif key == "BEGIN_ENCRYPTION": self.status = 'encryption incomplete' elif key == "DECRYPTION_OKAY": @@ -675,6 +692,9 @@ class GenKey(object): "Handle status messages for --gen-key" + + returncode = None + def __init__(self, gpg): self.gpg = gpg self.type = None @@ -713,6 +733,9 @@ class DeleteResult(object): "Handle status messages for --delete-key and --delete-secret-key" + + returncode = None + def __init__(self, gpg): self.gpg = gpg self.status = 'ok' @@ -745,6 +768,9 @@ class Sign(TextHandler): "Handle status messages for --sign" + + returncode = None + def __init__(self, gpg): self.gpg = gpg self.type = None @@ -782,6 +808,7 @@ VERSION_RE = re.compile(r'gpg \(GnuPG(?:/MacGPG2)?\) (\d+(\.\d+)*)'.encode('ascii'), re.I) HEX_DIGITS_RE = re.compile(r'[0-9a-f]+$', re.I) +PUBLIC_KEY_RE = re.compile(r'gpg: public key is (\w+)') class GPG(object): @@ -1010,7 +1037,7 @@ if writer is not None: writer.join() process.wait() - rc = process.returncode + result.returncode = rc = process.returncode if rc != 0: logger.warning('gpg returned a non-zero error code: %d', rc) if stdin is not None: @@ -1020,11 +1047,20 @@ pass stderr.close() stdout.close() + return rc + + def is_valid_file(self, fileobj): + """ + Simplistic check for a file object + """ + return hasattr(fileobj, 'read') def _handle_io(self, args, fileobj, result, passphrase=None, binary=False): "Handle a call to GPG - pass input data, collect output data" # Handle a basic data call - pass data to GPG, handle the output # including status information. Garbage In, Garbage Out :) + if not self.is_valid_file(fileobj): + raise TypeError('Not a valid file: %s' % fileobj) p = self._open_subprocess(args, passphrase is not None) if not binary: # pragma: no cover stdin = codecs.getwriter(self.encoding)(p.stdin) @@ -1618,6 +1654,23 @@ logger.debug('decrypt result[:100]: %r', result.data[:100]) return result + def get_recipients(self, message, **kwargs): + data = _make_binary_stream(message, self.encoding) + result = self.get_recipients_file(data, **kwargs) + data.close() + return result + + def get_recipients_file(self, file, extra_args=None): + args = ['--decrypt', '--list-only', '-v'] + if extra_args: + args.extend(extra_args) + result = self.result_map['crypt'](self) + self._handle_io(args, file, result, binary=True) + ids = [] + for m in PUBLIC_KEY_RE.finditer(result.stderr): + ids.append(m.group(1)) + return ids + def trust_keys(self, fingerprints, trustlevel): levels = Verify.TRUST_LEVELS if trustlevel not in levels: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.4.7/python_gnupg.egg-info/PKG-INFO new/python-gnupg-0.4.8/python_gnupg.egg-info/PKG-INFO --- old/python-gnupg-0.4.7/python_gnupg.egg-info/PKG-INFO 2021-03-11 09:23:20.000000000 +0100 +++ new/python-gnupg-0.4.8/python_gnupg.egg-info/PKG-INFO 2021-11-24 10:39:27.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: python-gnupg -Version: 0.4.7 +Version: 0.4.8 Summary: A wrapper for the Gnu Privacy Guard (GPG or GnuPG) Home-page: https://docs.red-dove.com/python-gnupg/ Author: Vinay Sajip @@ -8,7 +8,7 @@ Maintainer: Vinay Sajip Maintainer-email: vinay_sa...@yahoo.co.uk License: Copyright (C) 2008-2021 by Vinay Sajip. All Rights Reserved. See LICENSE.txt for license. -Download-URL: https://pypi.io/packages/source/p/python-gnupg/python-gnupg-0.4.7.tar.gz +Download-URL: https://pypi.io/packages/source/p/python-gnupg/python-gnupg-0.4.8.tar.gz Description: This module allows easy access to GnuPG's key management, encryption and signature functionality from Python programs. It is intended for use with Python 2.4 or greater. Releases are normally signed using a GnuPG key with the user id vinay_sa...@yahoo.co.uk and the following fingerprint: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.4.7/test_gnupg.py new/python-gnupg-0.4.8/test_gnupg.py --- old/python-gnupg-0.4.7/test_gnupg.py 2021-03-11 08:01:41.000000000 +0100 +++ new/python-gnupg-0.4.8/test_gnupg.py 2021-11-24 10:14:09.000000000 +0100 @@ -30,7 +30,7 @@ import gnupg __author__ = "Vinay Sajip" -__date__ = "$11-Mar-2020 07:01:41$" +__date__ = "$24-Nov-2021 09:14:09$" ALL_TESTS = True @@ -257,6 +257,7 @@ "Test that initially there are no keys" logger.debug("test_list_keys_initial begins") public_keys = self.gpg.list_keys() + self.assertEqual(0, public_keys.returncode, "Non-zero return code") self.assertTrue(is_list_with_len(public_keys, 0), "Empty list expected") private_keys = self.gpg.list_keys(secret=True) @@ -310,6 +311,7 @@ result = self.gpg.gen_key(cmd) self.assertFalse(result.data, 'Null data result') self.assertEqual(None, result.fingerprint, 'Null fingerprint result') + self.assertEqual(2, result.returncode, "Unexpected return code") def test_key_generation_with_colons(self): "Test that key generation handles colons in key fields" @@ -323,7 +325,9 @@ params['passphrase'] = 'foo' cmd = self.gpg.gen_key_input(**params) result = self.gpg.gen_key(cmd) + self.assertEqual(0, result.returncode, "Non-zero return code") keys = self.gpg.list_keys() + self.assertEqual(0, keys.returncode, "Non-zero return code") self.assertEqual(len(keys), 1) key = keys[0] uids = key['uids'] @@ -343,7 +347,9 @@ params['passphrase'] = 'foo' cmd = self.gpg.gen_key_input(**params) result = self.gpg.gen_key(cmd) + self.assertEqual(0, result.returncode, "Non-zero return code") keys = self.gpg.list_keys() + self.assertEqual(0, keys.returncode, "Non-zero return code") self.assertEqual(len(keys), 1) key = keys[0] uids = key['uids'] @@ -381,6 +387,7 @@ self.test_list_keys_initial() self.do_key_generation() public_keys = self.gpg.list_keys() + self.assertEqual(0, public_keys.returncode, "Non-zero return code") self.assertTrue(is_list_with_len(public_keys, 1), "1-element list expected") key_info = public_keys[0] @@ -399,6 +406,7 @@ # now test with sigs=True public_keys_sigs = self.gpg.list_keys(sigs=True) + self.assertEqual(0, public_keys_sigs.returncode, "Non-zero return code") self.assertTrue(is_list_with_len(public_keys_sigs, 1), "1-element list expected") key_info = public_keys_sigs[0] @@ -419,6 +427,7 @@ self.assertTrue(public_keys_sigs.key_map[sfp] is key_info) private_keys = self.gpg.list_keys(secret=True) + self.assertEqual(0, private_keys.returncode, "Non-zero return code") self.assertTrue(is_list_with_len(private_keys, 1), "1-element list expected") self.assertEqual(len(private_keys.fingerprints), 1) @@ -448,8 +457,10 @@ keyring=pkn, secret_keyring=skn) logger.debug('Using keyring and secret_keyring arguments') public_keys_2 = gpg.list_keys() + self.assertEqual(0, public_keys_2.returncode, "Non-zero return code") self.assertEqual(public_keys_2, public_keys) private_keys_2 = gpg.list_keys(secret=True) + self.assertEqual(0, private_keys_2.returncode, "Non-zero return code") self.assertEqual(private_keys_2, private_keys) # generate additional keys so that we can test listing a subset of @@ -464,9 +475,12 @@ result = self.generate_key("Charlie", "Clark", "gamma.com") self.assertNotEqual(None, result, "Non-null result") + self.assertEqual(0, result.returncode, "Non-zero return code") result = self.generate_key("Donna", "Davis", "delta.com") self.assertNotEqual(None, result, "Non-null result") + self.assertEqual(0, result.returncode, "Non-zero return code") public_keys = gpg.list_keys() + self.assertEqual(0, public_keys.returncode, "Non-zero return code") self.assertEqual(len(public_keys), 3) actual = get_names(public_keys.key_map) expected = set(['Barbara Brown <barbara.br...@beta.com>', @@ -475,11 +489,13 @@ self.assertEqual(actual, expected) # specify a single key as a string public_keys = gpg.list_keys(keys='Donna Davis') + self.assertEqual(0, public_keys.returncode, "Non-zero return code") actual = get_names(public_keys.key_map) expected = set(['Donna Davis <donna.da...@delta.com>']) self.assertEqual(actual, expected) # specify multiple keys public_keys = gpg.list_keys(keys=['Donna', 'Barbara']) + self.assertEqual(0, public_keys.returncode, "Non-zero return code") actual = get_names(public_keys.key_map) expected = set(['Barbara Brown <barbara.br...@beta.com>', 'Donna Davis <donna.da...@delta.com>']) @@ -488,8 +504,10 @@ def test_key_trust(self): "Test that trusting keys works" gpg = self.gpg - gpg.import_keys(KEYS_TO_IMPORT) + result = gpg.import_keys(KEYS_TO_IMPORT) + self.assertEqual(0, result.returncode, "Non-zero return code") keys = gpg.list_keys() + self.assertEqual(0, keys.returncode, "Non-zero return code") fingerprints = [] for key in keys: self.assertEqual(key['ownertrust'], '-') @@ -516,7 +534,9 @@ def test_list_signatures(self): logger.debug("test_list_signatures begins") imported = self.gpg.import_keys(SIGNED_KEYS) + self.assertEqual(0, imported.returncode, "Non-zero return code") keys = self.gpg.list_keys(keys=["18897CA2"]) + self.assertEqual(0, keys.returncode, "Non-zero return code") self.assertTrue(is_list_with_len(keys, 1), "importing test signed key") sigs = self.gpg.list_keys(keys=["18897CA2"], sigs=True)[0]['sigs'] logger.debug("testing self-signature") @@ -539,6 +559,7 @@ for fn in ('test_pubring.gpg', 'test_secring.gpg'): logger.debug('scanning keys in %s', fn) data = self.gpg.scan_keys(fn) + self.assertEqual(0, data.returncode, "Non-zero return code") uids = set() for d in data: uids.add(d['uids'][0]) @@ -549,8 +570,10 @@ logger.debug("test_encryption_and_decryption begins") key = self.generate_key("Andrew", "Able", "alpha.com", passphrase="andy") + self.assertEqual(0, key.returncode, "Non-zero return code") andrew = key.fingerprint key = self.generate_key("Barbara", "Brown", "beta.com") + self.assertEqual(0, key.returncode, "Non-zero return code") barbara = key.fingerprint gpg = self.gpg if gnupg._py3k: @@ -558,23 +581,28 @@ else: data = unicode('Hello, Andr??', gpg.encoding) data = data.encode(gpg.encoding) - edata = str(gpg.encrypt(data, barbara)) + result = gpg.encrypt(data, barbara) + self.assertEqual(0, result.returncode, "Non-zero return code") + edata = str(result) self.assertNotEqual(data, edata, "Data must have changed") self.assertRaises(ValueError, gpg.decrypt, edata, passphrase="bbr\x00own") self.assertRaises(ValueError, gpg.decrypt, edata, passphrase="bbr\rown") self.assertRaises(ValueError, gpg.decrypt, edata, passphrase="bbr\nown") ddata = gpg.decrypt(edata, passphrase="bbrown") + self.assertEqual(0, ddata.returncode, "Non-zero return code") if data != ddata.data: # pragma: no cover logger.debug("was: %r", data) logger.debug("new: %r", ddata.data) self.assertEqual(data, ddata.data, "Round-trip must work") - edata = str(gpg.encrypt(data, [andrew, barbara])) + result = gpg.encrypt(data, [andrew, barbara]) + self.assertEqual(0, result.returncode, "Non-zero return code") + edata = str(result) self.assertNotEqual(data, edata, "Data must have changed") ddata = gpg.decrypt(edata, passphrase="andy") + self.assertEqual(0, ddata.returncode, "Non-zero return code") self.assertEqual(data, ddata.data, "Round-trip must work") ddata = gpg.decrypt(edata, passphrase="bbrown") self.assertEqual(data, ddata.data, "Round-trip must work") - logger.debug("test_encryption_and_decryption ends") # Test symmetric encryption data = "chippy was here" self.assertRaises(ValueError, gpg.encrypt, data, None, @@ -583,18 +611,25 @@ passphrase='bbr\rown', symmetric=True) self.assertRaises(ValueError, gpg.encrypt, data, None, passphrase='bbr\nown', symmetric=True) - edata = str(gpg.encrypt(data, None, passphrase='bbrown', symmetric=True)) + result = gpg.encrypt(data, None, passphrase='bbrown', symmetric=True) + self.assertEqual(0, result.returncode, "Non-zero return code") + edata = str(result) ddata = gpg.decrypt(edata, passphrase='bbrown') + self.assertEqual(0, ddata.returncode, "Non-zero return code") self.assertEqual(data, str(ddata)) # Test symmetric encryption with non-default cipher - edata = str(gpg.encrypt(data, None, passphrase='bbrown', - symmetric='AES256')) + result = gpg.encrypt(data, None, passphrase='bbrown', symmetric='AES256') + self.assertEqual(0, result.returncode, "Non-zero return code") + edata = str(result) ddata = gpg.decrypt(edata, passphrase='bbrown') + self.assertEqual(0, ddata.returncode, "Non-zero return code") self.assertEqual(data, str(ddata)) # Test that you can't encrypt with no recipients self.assertRaises(ValueError, self.gpg.encrypt, data, []) # Test extra_args parameter - edata = str(gpg.encrypt(data, barbara, extra_args=['-z', '0'])) + result = gpg.encrypt(data, barbara, extra_args=['-z', '0']) + self.assertEqual(0, result.returncode, "Non-zero return code") + edata = str(result) ddata = gpg.decrypt(edata, passphrase='bbrown') self.assertEqual(data.encode('ascii'), ddata.data, 'Round-trip must work') # Test on_data functionality @@ -605,12 +640,15 @@ chunks.append(data) gpg.on_data = collector - edata = str(gpg.encrypt(data, barbara)) + result = gpg.encrypt(data, barbara) + self.assertEqual(0, result.returncode, "Non-zero return code") + edata = str(result) self.assertTrue(chunks) expected = type(chunks[0])().join(chunks) self.assertEqual(expected.decode('ascii'), edata) chunks = [] ddata = gpg.decrypt(edata, passphrase='bbrown') + self.assertEqual(0, ddata.returncode, "Non-zero return code") self.assertEqual(data.encode('ascii'), ddata.data, 'Round-trip must work') expected = type(chunks[0])().join(chunks) self.assertEqual(expected.decode('ascii'), data) @@ -618,15 +656,19 @@ # test signing with encryption and verification during decryption logger.debug('encrypting with signature') gpg.on_data = None - edata = str(gpg.encrypt(data, barbara, sign=andrew, passphrase='andy')) + result = gpg.encrypt(data, barbara, sign=andrew, passphrase='andy') + self.assertEqual(0, result.returncode, "Non-zero return code") + edata = str(result) logger.debug('decrypting with verification') ddata = gpg.decrypt(edata, passphrase='bbrown') + self.assertEqual(0, ddata.returncode, "Non-zero return code") self.assertEqual(data.encode('ascii'), ddata.data, 'Round-trip must work') sig_values = list(ddata.sig_info.values()) self.assertTrue(sig_values) sig_info = sig_values[0] self.assertEqual(sig_info['fingerprint'], andrew) logger.debug('decrypting with verification succeeded') + logger.debug("test_encryption_and_decryption ends") def test_import_and_export(self): "Test that key import and export works" @@ -634,11 +676,14 @@ self.test_list_keys_initial() gpg = self.gpg result = gpg.import_keys(KEYS_TO_IMPORT) + self.assertEqual(0, result.returncode, "Non-zero return code") self.assertEqual(result.summary(), '2 imported') public_keys = gpg.list_keys() + self.assertEqual(0, public_keys.returncode, "Non-zero return code") self.assertTrue(is_list_with_len(public_keys, 2), "2-element list expected") private_keys = gpg.list_keys(secret=True) + self.assertEqual(0, private_keys.returncode, "Non-zero return code") self.assertTrue(is_list_with_len(private_keys, 0), "Empty list expected") ascii = gpg.export_keys([k['keyid'] for k in public_keys]) @@ -666,6 +711,7 @@ # import a secret key, and confirm that it's found in the list of # secret keys. result = gpg.import_keys(SECRET_KEY) + self.assertEqual(0, result.returncode, "Non-zero return code") self.assertEqual(result.summary(), '1 imported') private_keys = gpg.list_keys(secret=True) self.assertTrue(is_list_with_len(private_keys, 2)) @@ -682,11 +728,14 @@ "Test that key import works" logger.debug("test_import_only begins") self.test_list_keys_initial() - self.gpg.import_keys(KEYS_TO_IMPORT) + result = self.gpg.import_keys(KEYS_TO_IMPORT) + self.assertEqual(0, result.returncode, "Non-zero return code") public_keys = self.gpg.list_keys() + self.assertEqual(0, public_keys.returncode, "Non-zero return code") self.assertTrue(is_list_with_len(public_keys, 2), "2-element list expected") private_keys = self.gpg.list_keys(secret=True) + self.assertEqual(0, private_keys.returncode, "Non-zero return code") self.assertTrue(is_list_with_len(private_keys, 0), "Empty list expected") ascii = self.gpg.export_keys([k['keyid'] for k in public_keys]) @@ -715,6 +764,7 @@ sig = self.gpg.sign(data, keyid=key.fingerprint, passphrase='bbrown') self.assertFalse(sig, "Bad passphrase should fail") sig = self.gpg.sign(data, keyid=key.fingerprint, passphrase='aable') + self.assertEqual(0, sig.returncode, "Non-zero return code") self.assertTrue(sig, "Good passphrase should succeed") if sig.username: # not set in recent versions of GnuPG e.g. 2.2.5 self.assertTrue(sig.username.startswith('Andrew Able')) @@ -723,6 +773,7 @@ self.assertTrue(sig.hash_algo) logger.debug('verification start') verified = self.gpg.verify(sig.data) + self.assertEqual(0, verified.returncode, "Non-zero return code") logger.debug('verification end') if key.fingerprint != verified.fingerprint: # pragma: no cover logger.debug("key: %r", key.fingerprint) @@ -734,6 +785,7 @@ data_file = open(self.test_fn, 'rb') sig = self.gpg.sign_file(data_file, keyid=key.fingerprint, passphrase='aable') + self.assertEqual(0, sig.returncode, "Non-zero return code") data_file.close() self.assertTrue(sig, "File signing should succeed") self.assertTrue(sig.hash_algo) @@ -744,6 +796,7 @@ # sometimes happens in Python 2.6 from io import BytesIO verified = self.gpg.verify_file(BytesIO(sig.data)) + self.assertEqual(0, verified.returncode, "Non-zero return code") if key.fingerprint != verified.fingerprint: # pragma: no cover logger.debug("key: %r", key.fingerprint) logger.debug("ver: %r", verified.fingerprint) @@ -752,6 +805,7 @@ data_file = open(self.test_fn, 'rb') sig = self.gpg.sign_file(data_file, keyid=key.fingerprint, passphrase='aable', detach=True) + self.assertEqual(0, sig.returncode, "Non-zero return code") data_file.close() self.assertTrue(sig, "File signing should succeed") self.assertTrue(sig.hash_algo) @@ -762,6 +816,7 @@ # sometimes happens in Python 2.6 from io import BytesIO verified = self.gpg.verify_file(BytesIO(sig.data)) + self.assertEqual(0, verified.returncode, "Non-zero return code") if key.fingerprint != verified.fingerprint: # pragma: no cover logger.debug("key: %r", key.fingerprint) logger.debug("ver: %r", verified.fingerprint) @@ -778,6 +833,7 @@ verified = self.gpg.verify_data(fn, data) finally: os.unlink(fn) + self.assertEqual(0, verified.returncode, "Non-zero return code") if key.fingerprint != verified.fingerprint: # pragma: no cover logger.debug("key: %r", key.fingerprint) logger.debug("ver: %r", verified.fingerprint) @@ -794,6 +850,7 @@ sig = self.gpg.sign_file(data_file, keyid=key.fingerprint, passphrase='aable', detach=True, output=sig_file) + self.assertEqual(0, sig.returncode, "Non-zero return code") data_file.close() self.assertTrue(sig, "File signing should succeed") self.assertTrue(sig.hash_algo) @@ -808,6 +865,7 @@ self.assertTrue(key.fingerprint.endswith(verified.key_id)) finally: os.unlink(sig_file) + self.assertEqual(0, verified.returncode, "Non-zero return code") if key.fingerprint != verified.fingerprint: logger.debug("key: %r", key.fingerprint) logger.debug("ver: %r", verified.fingerprint) @@ -818,12 +876,16 @@ def test_deletion(self): "Test that key deletion works" logger.debug("test_deletion begins") - self.gpg.import_keys(KEYS_TO_IMPORT) + result = self.gpg.import_keys(KEYS_TO_IMPORT) + self.assertEqual(0, result.returncode, "Non-zero return code") public_keys = self.gpg.list_keys() + self.assertEqual(0, public_keys.returncode, "Non-zero return code") self.assertTrue(is_list_with_len(public_keys, 2), "2-element list expected") - self.gpg.delete_keys(public_keys[0]['fingerprint']) + result = self.gpg.delete_keys(public_keys[0]['fingerprint']) + self.assertEqual(0, result.returncode, "Non-zero return code") public_keys = self.gpg.list_keys() + self.assertEqual(0, public_keys.returncode, "Non-zero return code") self.assertTrue(is_list_with_len(public_keys, 1), "1-element list expected") logger.debug("test_deletion ends") @@ -864,17 +926,21 @@ try: key = self.generate_key("Andrew", "Able", "alpha.com", passphrase="andy") + self.assertEqual(0, key.returncode, "Non-zero return code") andrew = key.fingerprint key = self.generate_key("Barbara", "Brown", "beta.com") + self.assertEqual(0, key.returncode, "Non-zero return code") barbara = key.fingerprint data = "Hello, world!" file = gnupg._make_binary_stream(data, self.gpg.encoding) edata = self.gpg.encrypt_file(file, barbara, armor=False, output=encfname) + self.assertEqual(0, edata.returncode, "Non-zero return code") efile = open(encfname, 'rb') ddata = self.gpg.decrypt_file(efile, passphrase="bbrown", output=decfname) + self.assertEqual(0, ddata.returncode, "Non-zero return code") efile.seek(0, 0) # can't use os.SEEK_SET in 2.4 edata = efile.read() efile.close() @@ -894,6 +960,7 @@ efile = open(encfname, 'r') ddata = self.gpg.decrypt_file(efile, passphrase="bbrown", output=decfname) + self.assertEqual(2, ddata.returncode, "Unexpected return code") self.assertFalse(ddata) self.assertEqual(ddata.status, "no data was provided") efile.close() @@ -934,6 +1001,7 @@ stream = gnupg._make_binary_stream(data, self.gpg.encoding) edata = self.gpg.encrypt_file(stream, barbara, armor=False, output=badout) + self.assertEqual(2, edata.returncode, "Unexpecteds return code") # on GnuPG 1.4, you sometimes don't get any FAILURE messages, in # which case status will not be set if edata.status: @@ -963,6 +1031,7 @@ stream = gnupg._make_binary_stream(data, self.gpg.encoding) edata = self.gpg.encrypt_file(stream, barbara, armor=False, output=badout) + self.assertEqual(2, edata.returncode, "Unexpected return code") # on GnuPG 1.4, you sometimes don't get any FAILURE messages, in # which case status will not be set if edata.status: @@ -988,9 +1057,11 @@ "Test that searching for keys works" if 'NO_EXTERNAL_TESTS' not in os.environ: r = self.gpg.search_keys('<vinay_sa...@hotmail.com>') + self.assertEqual(0, r.returncode, "Non-zero return code") self.assertTrue(r) self.assertTrue('Vinay Sajip <vinay_sa...@hotmail.com>' in r[0]['uids']) r = self.gpg.search_keys('92905378') + self.assertEqual(0, r.returncode, "Non-zero return code") self.assertTrue(r) self.assertTrue('Vinay Sajip <vinay_sa...@hotmail.com>' in r[0]['uids']) @@ -1031,6 +1102,7 @@ detach=True) finally: signfile.close() + self.assertEqual(0, signed.returncode, "Non-zero return code") self.assertTrue(signed.data) logger.debug("test_signing_with_uid ends") @@ -1090,6 +1162,7 @@ fp1 = result.fingerprint inp = gpg.gen_key_input(name_email='user2@test', passphrase='pp2') result = gpg.gen_key(inp) + self.assertEqual(0, result.returncode, "Non-zero return code") fp2 = result.fingerprint pubkey1 = gpg.export_keys(fp1) if gpg.version >= (2, 1): @@ -1098,23 +1171,31 @@ passphrase = None seckey1 = gpg.export_keys(fp1, secret=True, passphrase=passphrase) seckeys = gpg.list_keys(secret=True) + self.assertEqual(0, seckeys.returncode, "Non-zero return code") pubkeys = gpg.list_keys() + self.assertEqual(0, pubkeys.returncode, "Non-zero return code") # avoid assertIn, etc. as absent in older Python versions self.assertTrue(fp1 in seckeys.fingerprints) self.assertTrue(fp1 in pubkeys.fingerprints) result = gpg.delete_keys(fp1) + self.assertEqual(2, result.returncode, "Unexpected return code") self.assertEqual(str(result), 'Must delete secret key first') if gpg.version < (2, 1): # Doesn't work on 2.1, and can't use SkipTest due to having # to support older Pythons result = gpg.delete_keys(fp1, secret=True, passphrase=passphrase) + self.assertEqual(0, result.returncode, "Non-zero return code") self.assertEqual(str(result), 'ok') result = gpg.delete_keys(fp1) + self.assertEqual(0, result.returncode, "Non-zero return code") self.assertEqual(str(result), 'ok') result = gpg.delete_keys('nosuchkey') + self.assertEqual(2, result.returncode, "Unexpected return code") self.assertEqual(str(result), 'No such key') seckeys = gpg.list_keys(secret=True) + self.assertEqual(0, seckeys.returncode, "Non-zero return code") pubkeys = gpg.list_keys() + self.assertEqual(0, pubkeys.returncode, "Non-zero return code") self.assertFalse(fp1 in seckeys.fingerprints) self.assertFalse(fp1 in pubkeys.fingerprints) result = gpg.import_keys('foo') @@ -1123,15 +1204,74 @@ def test_recv_keys_no_server(self): result = self.gpg.recv_keys('foo.bar.baz', '92905378') + self.assertEqual(2, result.returncode, "Unexpected return code") self.assertEqual(result.summary(), '0 imported') + def test_invalid_fileobject(self): + # accidentally on purpose pass in a filename rather than the file itself + with self.assertRaises(TypeError) as ec: + self.gpg.decrypt_file('foobar.txt', passphrase='', + output='/tmp/decrypted.txt') + self.assertEqual(str(ec.exception), 'Not a valid file: foobar.txt') + + def remove_all_existing_keys(self): + for root, dirs, files in os.walk(self.homedir): + for d in dirs: + p = os.path.join(root, d) + shutil.rmtree(p) + for f in files: + if f.endswith('.conf'): + continue + p = os.path.join(root, f) + os.remove(p) + + def test_no_such_key(self): + logger.debug("test_no_such_key begins") + key = self.generate_key("Barbara", "Brown", "beta.com") + barbara = key.fingerprint + gpg = self.gpg + if gnupg._py3k: + data = 'Hello, Andr??!' + else: + data = unicode('Hello, Andr??', gpg.encoding) + try: + data = data.encode(gpg.encoding) + encrypted = gpg.encrypt(data, barbara) + self.remove_all_existing_keys() + decrypted = gpg.decrypt(str(encrypted), passphrase='bbrown') + self.assertFalse(decrypted.ok) + expected = {'decryption failed', 'no secret key', 'no data was provided'} + self.assertIn(decrypted.status, expected) + finally: + logger.debug("test_no_such_key ends") + + def test_get_recipients(self): + logger.debug("test_get_recipients begins") + try: + gpg = self.gpg + inp = gpg.gen_key_input(name_email='user1@test', passphrase='pp1') + key1 = gpg.gen_key(inp) + inp = gpg.gen_key_input(name_email='user2@test', passphrase='pp2') + key2 = gpg.gen_key(inp) + data = 'super secret'.encode(gpg.encoding) + edata = gpg.encrypt(data, (key1.fingerprint, key2.fingerprint)) + logger.debug('Getting recipients') + ids = gpg.get_recipients(edata.data.decode(gpg.encoding)) + self.assertGreater(len(ids), 0) + idlen = len(ids[0]) + ids = set(ids) + expected = set((key1.fingerprint[-idlen:], key2.fingerprint[-idlen:])) + self.assertEqual(expected, ids) + finally: + logger.debug("test_get_recipients ends") TEST_GROUPS = { 'sign' : set(['test_signature_verification', 'test_signature_file']), 'crypt' : set(['test_encryption_and_decryption', 'test_file_encryption_and_decryption', - 'test_filenames_with_spaces', 'test_invalid_outputs']), + 'test_filenames_with_spaces', 'test_invalid_outputs', + 'test_no_such_key']), 'key' : set(['test_deletion', 'test_import_and_export', 'test_list_keys_after_generation', 'test_list_signatures', @@ -1144,7 +1284,7 @@ 'basic' : set(['test_environment', 'test_list_keys_initial', 'test_nogpg', 'test_make_args', 'test_quote_with_shell']), - 'test': set(['test_invalid_outputs']), + 'test': set(['test_get_recipients']), } def suite(args=None):