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 2022-09-17 20:10:48 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-python-gnupg (Old) and /work/SRC/openSUSE:Factory/.python-python-gnupg.new.2083 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-python-gnupg" Sat Sep 17 20:10:48 2022 rev:13 rq:1004263 version:0.5.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-python-gnupg/python-python-gnupg.changes 2022-06-06 11:10:35.995314344 +0200 +++ /work/SRC/openSUSE:Factory/.python-python-gnupg.new.2083/python-python-gnupg.changes 2022-09-17 20:10:59.561269837 +0200 @@ -1,0 +2,18 @@ +Sat Sep 17 07:21:39 UTC 2022 - Dirk M??ller <dmuel...@suse.com> + +- update to 0.5.0: + * Fixed #181: Added the ability to pass file paths to encrypt_file, decrypt_file, + sign_file, verify_file, get_recipients_file and added import_keys_file. + * Fixed #183: Handle FAILURE and UNEXPECTED conditions correctly. Thanks to sebbASF for + the patch. + * Fixed #185: Handle VALIDSIG arguments more robustly. + * Fixed #188: Remove handling of DECRYPTION_FAILED from Verify code, as not required + there. Thanks to sebbASF for the patch. + * Fixed #190: Handle KEY_CREATED more robustly. + * Fixed #191: Handle NODATA messages during verification. + * Fixed #196: Don't log chunk data by default, as it could contain sensitive + information (during decryption, for example). + * Added the ability to pass an environment to the gpg executable. Thanks to Edvard + Rejthar for the patch. + +------------------------------------------------------------------- Old: ---- python-gnupg-0.4.9.tar.gz New: ---- python-gnupg-0.5.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-python-gnupg.spec ++++++ --- /var/tmp/diff_new_pack.oJqALp/_old 2022-09-17 20:11:00.025271176 +0200 +++ /var/tmp/diff_new_pack.oJqALp/_new 2022-09-17 20:11:00.029271187 +0200 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define oldpython python Name: python-python-gnupg -Version: 0.4.9 +Version: 0.5.0 Release: 0 Summary: A wrapper for the GNU Privacy Guard (GPG or GnuPG) License: BSD-3-Clause ++++++ python-gnupg-0.4.9.tar.gz -> python-gnupg-0.5.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.4.9/PKG-INFO new/python-gnupg-0.5.0/PKG-INFO --- old/python-gnupg-0.4.9/PKG-INFO 2022-05-20 10:08:10.000000000 +0200 +++ new/python-gnupg-0.5.0/PKG-INFO 2022-08-23 17:52:00.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: python-gnupg -Version: 0.4.9 +Version: 0.5.0 Summary: A wrapper for the Gnu Privacy Guard (GPG or GnuPG) Home-page: https://github.com/vsajip/python-gnupg Author: Vinay Sajip diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.4.9/README.rst new/python-gnupg-0.5.0/README.rst --- old/python-gnupg-0.4.9/README.rst 2022-05-20 09:14:52.000000000 +0200 +++ new/python-gnupg-0.5.0/README.rst 2022-08-23 10:08:24.000000000 +0200 @@ -4,6 +4,7 @@ :alt: GitHub test status .. |badge2| image:: https://img.shields.io/codecov/c/github/vsajip/python-gnupg + :target: https://app.codecov.io/gh/vsajip/python-gnupg :alt: GitHub coverage status @@ -71,6 +72,27 @@ Released: Not yet. +* Fixed #181: Added the ability to pass file paths to encrypt_file, decrypt_file, + sign_file, verify_file, get_recipients_file and added import_keys_file. + +* Fixed #183: Handle FAILURE and UNEXPECTED conditions correctly. Thanks to sebbASF for + the patch. + +* Fixed #185: Handle VALIDSIG arguments more robustly. + +* Fixed #188: Remove handling of DECRYPTION_FAILED from Verify code, as not required + there. Thanks to sebbASF for the patch. + +* Fixed #190: Handle KEY_CREATED more robustly. + +* Fixed #191: Handle NODATA messages during verification. + +* Fixed #196: Don't log chunk data by default, as it could contain sensitive + information (during decryption, for example). + +* Added the ability to pass an environment to the gpg executable. Thanks to Edvard + Rejthar for the patch. + 0.4.9 ----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.4.9/gnupg.py new/python-gnupg-0.5.0/gnupg.py --- old/python-gnupg-0.4.9/gnupg.py 2022-05-20 10:01:43.000000000 +0200 +++ new/python-gnupg-0.5.0/gnupg.py 2022-08-23 17:36:40.000000000 +0200 @@ -42,9 +42,9 @@ import sys import threading -__version__ = '0.4.9' +__version__ = '0.5.0' __author__ = 'Vinay Sajip' -__date__ = '$20-May-2022 09:01:43$' +__date__ = '$23-Aug-2022 16:36:40$' STARTUPINFO = None if os.name == 'nt': # pragma: no cover @@ -58,15 +58,24 @@ _py3k = False string_types = basestring text_type = unicode + path_types = (bytes, str) except NameError: _py3k = True string_types = str text_type = str + path_types = (str,) logger = logging.getLogger(__name__) if not logger.handlers: logger.addHandler(logging.NullHandler()) +# See gh-196: Logging could show sensitive data. It also produces some voluminous +# output. Hence, split into two tiers - stuff that's always logged, and stuff that's +# only logged if log_everything is True. (This is set by the test script.) +# +# For now, only debug logging of chunks falls into the optionally-logged category. +log_everything = False + # We use the test below because it works for Jython as well as CPython if os.path.__name__ == 'ntpath': # pragma: no cover # On Windows, we don't need shell quoting, other than worrying about @@ -135,7 +144,7 @@ # for what is actually a binary file try: data = instream.read(1024) - except UnicodeError: + except Exception: # pragma: no cover logger.warning('Exception occurred while reading', exc_info=1) break if not data: @@ -146,7 +155,7 @@ outstream.write(data) except UnicodeError: # pragma: no cover outstream.write(data.encode(enc)) - except Exception: + except Exception: # pragma: no cover # Can sometimes get 'broken pipe' errors even when the data has all # been sent logger.exception('Error sending data') @@ -291,11 +300,13 @@ self.key_id, self.username = value.split(None, 1) update_sig_info(keyid=self.key_id, username=self.username, status=self.status) elif key == 'VALIDSIG': - fingerprint, creation_date, sig_ts, expire_ts = value.split()[:4] + parts = value.split() + fingerprint, creation_date, sig_ts, expire_ts = parts[:4] (self.fingerprint, self.creation_date, self.sig_timestamp, self.expire_timestamp) = (fingerprint, creation_date, sig_ts, expire_ts) # may be different if signature is made with a subkey - self.pubkey_fingerprint = value.split()[-1] + if len(parts) >= 10: + self.pubkey_fingerprint = parts[9] self.status = 'signature valid' update_sig_info(fingerprint=fingerprint, creation_date=creation_date, @@ -308,10 +319,6 @@ self.sig_info[sig_id] = {'creation_date': creation_date, 'timestamp': timestamp} (self.signature_id, self.creation_date, self.timestamp) = (sig_id, creation_date, timestamp) - elif key == 'DECRYPTION_FAILED': # pragma: no cover - self.valid = False - self.key_id = value - self.status = 'decryption failed' elif key == 'NO_PUBKEY': # pragma: no cover self.valid = False self.key_id = value @@ -332,7 +339,6 @@ update_sig_info(status=self.status, keyid=self.key_id) elif key in ('UNEXPECTED', 'FAILURE'): # pragma: no cover self.valid = False - self.key_id = value if key == 'UNEXPECTED': self.status = 'unexpected data' else: @@ -359,6 +365,10 @@ message = '%s: %s' % (operation, mapping[code]) if not self.status: self.status = message + elif key == 'NODATA': + # See issue GH-191 + self.valid = False + self.status = 'signature expected but not found' elif key in ('DECRYPTION_INFO', 'PLAINTEXT', 'PLAINTEXT_LENGTH', 'BEGIN_SIGNING'): pass else: # pragma: no cover @@ -382,7 +392,7 @@ setattr(self, result, 0) def __nonzero__(self): - if self.not_imported or not self.fingerprints: + if self.not_imported or not self.fingerprints: # pragma: no cover return False return True @@ -406,7 +416,7 @@ } def handle_status(self, key, value): - if key in ('WARNING', 'ERROR'): + if key in ('WARNING', 'ERROR'): # pragma: no cover logger.warning('potential problem: %s: %s', key, value) elif key in ('IMPORTED', 'KEY_CONSIDERED'): # this duplicates info we already see in import_ok & import_problem @@ -718,7 +728,8 @@ if key in ('WARNING', 'ERROR'): # pragma: no cover logger.warning('potential problem: %s: %s', key, value) elif key == 'KEY_CREATED': - (self.type, self.fingerprint) = value.split() + parts = value.split() + (self.type, self.fingerprint) = parts[:2] self.status = 'ok' elif key == 'KEY_NOT_CREATED': self.status = key.replace('_', ' ').lower() @@ -880,7 +891,8 @@ use_agent=False, keyring=None, options=None, - secret_keyring=None): + secret_keyring=None, + env=None): """Initialize a GPG process wrapper. Options are: gpgbinary -- full pathname for GPG binary. @@ -892,9 +904,11 @@ options =-- a list of additional options to pass to the GPG binary. secret_keyring -- name of alternative secret keyring file to use, or list of such keyrings. + env -- a dict of environment variables """ self.gpgbinary = gpgbinary self.gnupghome = gnupghome + self.env = env # issue 112: fail if the specified value isn't a directory if gnupghome and not os.path.isdir(gnupghome): raise ValueError('gnupghome should be a directory (it isn\'t): %s' % gnupghome) @@ -990,7 +1004,7 @@ si = STARTUPINFO() si.dwFlags = STARTF_USESHOWWINDOW si.wShowWindow = SW_HIDE - result = Popen(cmd, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE, startupinfo=si) + result = Popen(cmd, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE, startupinfo=si, env=self.env) logger.debug('%s: %s', result.pid, debug_print(cmd)) return result @@ -1031,7 +1045,8 @@ if on_data: on_data(data) break - logger.debug('chunk: %r' % data[:256]) + if log_everything: + logger.debug('chunk: %r' % data[:256]) append = True if on_data: append = on_data(data) is not False @@ -1065,7 +1080,7 @@ dr.join() rr.join() if writer is not None: - writer.join() + writer.join(0.01) process.wait() result.returncode = rc = process.returncode if rc != 0: @@ -1085,23 +1100,37 @@ """ 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) + def _get_fileobj(self, fileobj_or_path): + if self.is_valid_file(fileobj_or_path): + result = fileobj_or_path + elif not isinstance(fileobj_or_path, path_types): + raise TypeError('Not a valid file or path: %s' % fileobj_or_path) + elif not os.path.exists(fileobj_or_path): + raise ValueError('No such file: %s' % fileobj_or_path) else: - stdin = p.stdin - if passphrase: - _write_passphrase(stdin, passphrase, self.encoding) - writer = _threaded_copy_data(fileobj, stdin) - self._collect_output(p, result, writer, stdin) + result = open(fileobj_or_path, 'rb') return result + def _handle_io(self, args, fileobj_or_path, 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 :) + fileobj = self._get_fileobj(fileobj_or_path) + try: + p = self._open_subprocess(args, passphrase is not None) + if not binary: # pragma: no cover + stdin = codecs.getwriter(self.encoding)(p.stdin) + else: + stdin = p.stdin + if passphrase: + _write_passphrase(stdin, passphrase, self.encoding) + writer = _threaded_copy_data(fileobj, stdin) + self._collect_output(p, result, writer, stdin) + return result + finally: + writer.join(0.01) + if fileobj is not fileobj_or_path: + fileobj.close() # # SIGNATURE METHODS # @@ -1128,7 +1157,7 @@ return ('\n' not in passphrase and '\r' not in passphrase and '\x00' not in passphrase) def sign_file(self, - file, + fileobj_or_path, keyid=None, passphrase=None, clearsign=True, @@ -1139,7 +1168,7 @@ """sign file""" if passphrase and not self.is_valid_passphrase(passphrase): raise ValueError('Invalid passphrase') - logger.debug('sign_file: %s', file) + logger.debug('sign_file: %s', fileobj_or_path) if binary: # pragma: no cover args = ['-s'] else: @@ -1160,15 +1189,21 @@ result = self.result_map['sign'](self) # We could use _handle_io here except for the fact that if the # passphrase is bad, gpg bails and you can't write the message. + fileobj = self._get_fileobj(fileobj_or_path) p = self._open_subprocess(args, passphrase is not None) try: stdin = p.stdin if passphrase: _write_passphrase(stdin, passphrase, self.encoding) - writer = _threaded_copy_data(file, stdin) + writer = _threaded_copy_data(fileobj, stdin) except IOError: # pragma: no cover logging.exception('error writing message') writer = None + finally: + if writer: + writer.join(0.01) + if fileobj is not fileobj_or_path: + fileobj.close() self._collect_output(p, result, writer, stdin) return result @@ -1194,22 +1229,22 @@ f.close() return result - def verify_file(self, file, data_filename=None, close_file=True, extra_args=None): + def verify_file(self, fileobj_or_path, data_filename=None, close_file=True, extra_args=None): "Verify the signature on the contents of the file-like object 'file'" - logger.debug('verify_file: %r, %r', file, data_filename) + logger.debug('verify_file: %r, %r', fileobj_or_path, data_filename) result = self.result_map['verify'](self) args = ['--verify'] if extra_args: args.extend(extra_args) if data_filename is None: - self._handle_io(args, file, result, binary=True) + self._handle_io(args, fileobj_or_path, result, binary=True) else: logger.debug('Handling detached verification') import tempfile - fd, fn = tempfile.mkstemp(prefix='pygpg') - s = file.read() + fd, fn = tempfile.mkstemp(prefix='pygpg-') + s = fileobj_or_path.read() if close_file: - file.close() + fileobj_or_path.close() logger.debug('Wrote to temp file: %r', s) os.write(fd, s) os.close(fd) @@ -1219,7 +1254,7 @@ p = self._open_subprocess(args) self._collect_output(p, result, stdin=p.stdin) finally: - os.unlink(fn) + os.remove(fn) return result def verify_data(self, sig_filename, data, extra_args=None): @@ -1253,6 +1288,13 @@ data.close() return result + def import_keys_file(self, key_path, **kwargs): + """ + Import the key data in key_path into our keyring. + """ + with open(key_path, 'rb') as f: + return self.import_keys(f.read(), **kwargs) + def recv_keys(self, keyserver, *keyids, **kwargs): """Import a key from a keyserver @@ -1617,7 +1659,7 @@ # ENCRYPTION # def encrypt_file(self, - file, + fileobj_or_path, recipients, sign=None, always_trust=False, @@ -1658,7 +1700,7 @@ if extra_args: args.extend(extra_args) result = self.result_map['crypt'](self) - self._handle_io(args, file, result, passphrase=passphrase, binary=True) + self._handle_io(args, fileobj_or_path, result, passphrase=passphrase, binary=True) logger.debug('encrypt result[:100]: %r', result.data[:100]) return result @@ -1717,7 +1759,7 @@ return result def decrypt_file(self, - file, + fileobj_or_path, always_trust=False, passphrase=None, output=None, @@ -1732,7 +1774,7 @@ if extra_args: args.extend(extra_args) result = self.result_map['crypt'](self) - self._handle_io(args, file, result, passphrase, binary=True) + self._handle_io(args, fileobj_or_path, result, passphrase, binary=True) logger.debug('decrypt result[:100]: %r', result.data[:100]) return result @@ -1742,12 +1784,12 @@ data.close() return result - def get_recipients_file(self, file, extra_args=None): + def get_recipients_file(self, fileobj_or_path, 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) + self._handle_io(args, fileobj_or_path, result, binary=True) ids = [] for m in PUBLIC_KEY_RE.finditer(result.stderr): ids.append(m.group(1)) @@ -1761,7 +1803,7 @@ trustlevel = levels[trustlevel] + 2 import tempfile try: - fd, fn = tempfile.mkstemp() + fd, fn = tempfile.mkstemp(prefix='pygpg-') lines = [] if isinstance(fingerprints, string_types): fingerprints = [fingerprints] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.4.9/python_gnupg.egg-info/PKG-INFO new/python-gnupg-0.5.0/python_gnupg.egg-info/PKG-INFO --- old/python-gnupg-0.4.9/python_gnupg.egg-info/PKG-INFO 2022-05-20 10:08:10.000000000 +0200 +++ new/python-gnupg-0.5.0/python_gnupg.egg-info/PKG-INFO 2022-08-23 17:52:00.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: python-gnupg -Version: 0.4.9 +Version: 0.5.0 Summary: A wrapper for the Gnu Privacy Guard (GPG or GnuPG) Home-page: https://github.com/vsajip/python-gnupg Author: Vinay Sajip diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.4.9/test_gnupg.py new/python-gnupg-0.5.0/test_gnupg.py --- old/python-gnupg-0.4.9/test_gnupg.py 2022-05-20 10:02:15.000000000 +0200 +++ new/python-gnupg-0.5.0/test_gnupg.py 2022-08-23 17:37:02.000000000 +0200 @@ -30,10 +30,12 @@ import gnupg __author__ = 'Vinay Sajip' -__date__ = '$20-May-2022 09:02:15$' +__date__ = '$23-Aug-2022 16:37:02$' ALL_TESTS = True +gnupg.log_everything = True + logger = logging.getLogger(__name__) GPGBINARY = os.environ.get('GPGBINARY', 'gpg') @@ -979,8 +981,8 @@ self.assertTrue(sig, 'File signing should succeed') self.assertTrue(sig.hash_algo) try: - file = gnupg._make_binary_stream(sig.data, self.gpg.encoding) - verified = self.gpg.verify_file(file) + stream = gnupg._make_binary_stream(sig.data, self.gpg.encoding) + verified = self.gpg.verify_file(stream) except UnicodeDecodeError: # pragma: no cover # sometimes happens in Python 2.6 from io import BytesIO @@ -1012,13 +1014,13 @@ data_file = open(self.test_fn, 'rb') data = data_file.read() data_file.close() - fd, fn = tempfile.mkstemp() + fd, fn = tempfile.mkstemp(prefix='pygpg-test-') os.write(fd, sig.data) os.close(fd) try: verified = self.gpg.verify_data(fn, data) finally: - os.unlink(fn) + os.remove(fn) self.assertEqual(0, verified.returncode, 'Non-zero return code') if key.fingerprint != verified.fingerprint: # pragma: no cover logger.debug('key: %r', key.fingerprint) @@ -1049,7 +1051,7 @@ self.assertTrue(verified.username.startswith('Andrew Able')) self.assertTrue(key.fingerprint.endswith(verified.key_id)) finally: - os.unlink(sig_file) + os.remove(sig_file) self.assertEqual(0, verified.returncode, 'Non-zero return code') if key.fingerprint != verified.fingerprint: logger.debug('key: %r', key.fingerprint) @@ -1096,7 +1098,7 @@ self.assertTrue(verified.username.startswith('Charlie Clark')) self.assertTrue(subkey.fingerprint.endswith(verified.key_id)) finally: - os.unlink(sig_file) + os.remove(sig_file) self.assertEqual(0, verified.returncode, 'Non-zero return code') if subkey.fingerprint != verified.fingerprint: logger.debug('key: %r', subkey.fingerprint) @@ -1157,8 +1159,8 @@ 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, [andrew, barbara], armor=False, output=encfname) + stream = gnupg._make_binary_stream(data, self.gpg.encoding) + edata = self.gpg.encrypt_file(stream, [andrew, 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) @@ -1196,8 +1198,8 @@ def test_file_encryption_and_decryption(self): "Test that encryption/decryption to/from file works" - encfno, encfname = tempfile.mkstemp() - decfno, decfname = tempfile.mkstemp() + encfno, encfname = tempfile.mkstemp(prefix='pygpg-test-') + decfno, decfname = tempfile.mkstemp(prefix='pygpg-test-') # On Windows, if the handles aren't closed, the files can't be deleted os.close(encfno) os.close(decfno) @@ -1206,7 +1208,7 @@ @skipIf(os.name == 'nt', 'Test not suitable for Windows') def test_invalid_outputs(self): "Test encrypting to invalid output files" - encfno, encfname = tempfile.mkstemp() + encfno, encfname = tempfile.mkstemp(prefix='pygpg-test-') os.close(encfno) os.chmod(encfname, 0o400) cases = ( @@ -1235,7 +1237,7 @@ self.gpg.error_map = messages - encfno, encfname = tempfile.mkstemp() + encfno, encfname = tempfile.mkstemp(prefix='pygpg-test-') os.close(encfno) os.chmod(encfname, 0o400) @@ -1421,9 +1423,14 @@ 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') + bad = b'foobar.txt' + with self.assertRaises((TypeError, ValueError)) as ec: + self.gpg.decrypt_file(bad, passphrase='', output='/tmp/decrypted.txt') + if gnupg._py3k: + expected = 'Not a valid file or path: %s' % bad + else: + expected = 'No such file: %s' % bad + self.assertEqual(str(ec.exception), expected) def remove_all_existing_keys(self): for root, dirs, files in os.walk(self.homedir): @@ -1468,6 +1475,58 @@ expected = set((key1.fingerprint[-idlen:], key2.fingerprint[-idlen:])) self.assertEqual(expected, ids) + def test_passing_paths(self): + key1 = self.generate_key('Andrew', 'Able', 'alpha.com', passphrase='andy') + self.assertEqual(0, key1.returncode, 'Non-zero return code') + andrew = key1.fingerprint + key2 = self.generate_key('Barbara', 'Brown', 'beta.com') + self.assertEqual(0, key2.returncode, 'Non-zero return code') + barbara = key2.fingerprint + data = b'Hello, world!' + fd, fn = tempfile.mkstemp(prefix='pygpg-test-') + os.write(fd, data) + os.close(fd) + gpg = self.gpg + try: + # Check encryption + edata = gpg.encrypt_file(fn, [andrew, barbara], armor=False) + self.assertEqual(0, edata.returncode, 'Non-zero return code') + self.assertEqual(edata.status, 'encryption ok') + with open(fn, 'wb') as f: + f.write(edata.data) + # Check getting recipients + ids = gpg.get_recipients_file(fn) + idlen = len(ids[0]) + keys = gpg.list_keys() + expected = set(d['subkeys'][0][0][-idlen:] for d in keys) + self.assertEqual(set(ids), expected) + # Check decryption + ddata = gpg.decrypt_file(fn, passphrase='andy') + self.assertEqual(0, ddata.returncode, 'Non-zero return code') + self.assertEqual(ddata.status, 'decryption ok') + self.assertEqual(ddata.data, data) + # Check signing + with open(fn, 'wb') as f: + f.write(data) + sig = gpg.sign_file(fn, keyid=andrew, passphrase='andy', binary=True) + self.assertEqual(0, sig.returncode, 'Non-zero return code') + self.assertEqual(sig.status, 'signature created') + # Check verification + with open(fn, 'wb') as f: + f.write(sig.data) + verified = gpg.verify_file(fn) + self.assertEqual(0, verified.returncode, 'Non-zero return code') + self.assertEqual(verified.status, 'signature valid') + self.assertTrue(verified.valid) + # Check importing keys + with open(fn, 'wb') as f: + f.write(KEYS_TO_IMPORT.encode('ascii')) + result = gpg.import_keys_file(fn) + self.assertEqual(0, result.returncode, 'Non-zero return code') + self.assertEqual(result.imported, 2) + finally: + os.remove(fn) + TEST_GROUPS = { 'sign': @@ -1494,7 +1553,7 @@ 'test_quote_with_shell' ]), 'test': - set(['test_key_generation_failure']), + set(['test_passing_paths']), }