Hello community,

here is the log from the commit of package python-python-gnupg for 
openSUSE:Factory checked in at 2018-06-19 11:59:36
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-python-gnupg (Old)
 and      /work/SRC/openSUSE:Factory/.python-python-gnupg.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-python-gnupg"

Tue Jun 19 11:59:36 2018 rev:3 rq:616620 version:0.4.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-python-gnupg/python-python-gnupg.changes  
2017-10-13 14:16:16.523733298 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-python-gnupg.new/python-python-gnupg.changes 
    2018-06-19 11:59:37.467029956 +0200
@@ -1,0 +2,18 @@
+Wed Jun 13 19:37:41 UTC 2018 - [email protected]
+
+- uppate to 0.4.3:
+  * Add --no-verbose to the gpg command line, in case verbose is
+    specified is gpg.conf.
+  * This is mitigation against CVE-2018-12020 boo#1096745
+- includes changes from 0.4.2:
+  * Subkey information is now collected and returned in a
+    subkey_info dictionary keyed by the subkey's ID
+  * Add expect_passphrase password for use on GnuPG >= 2.1 when
+    passing passphrase to gpg via pinentry
+  * Provid a trust_keys method to allow setting the trust level
+    for keys
+  * When the gpg executable is not found, note the path used in the
+    exception message
+  * Make error messages nor informational
+
+-------------------------------------------------------------------

Old:
----
  python-gnupg-0.4.1.tar.gz

New:
----
  python-gnupg-0.4.3.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-python-gnupg.spec ++++++
--- /var/tmp/diff_new_pack.kjmc2T/_old  2018-06-19 11:59:38.067007679 +0200
+++ /var/tmp/diff_new_pack.kjmc2T/_new  2018-06-19 11:59:38.075007382 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-python-gnupg
 #
-# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -20,21 +20,22 @@
 %define         oldpython python
 %bcond_with     test
 Name:           python-python-gnupg
-Version:        0.4.1
+Version:        0.4.3
 Release:        0
 Summary:        A wrapper for the GNU Privacy Guard (GPG or GnuPG)
 License:        BSD-3-Clause
 Group:          Development/Languages/Python
-Url:            http://pythonhosted.org/python-gnupg/index.html
+URL:            http://pythonhosted.org/python-gnupg/index.html
 Source:         
https://files.pythonhosted.org/packages/source/p/python-gnupg/python-gnupg-%{version}.tar.gz
 BuildRequires:  %{python_module devel}
 BuildRequires:  %{python_module setuptools}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
+Requires:       gpg2
+BuildArch:      noarch
 %if %{with test}
 BuildRequires:  gpg2
 %endif
-Requires:       gpg2
 %ifpython2
 Obsoletes:      %{oldpython}-gnupg < %{version}
 Provides:       %{oldpython}-gnupg = %{version}
@@ -43,8 +44,6 @@
 Obsoletes:      python3-gnupg < %{version}
 Provides:       python3-gnupg = %{version}
 %endif
-BuildArch:      noarch
-
 %python_subpackages
 
 %description
@@ -67,8 +66,8 @@
 %endif
 
 %files %{python_files}
-%defattr(-,root,root,-)
-%doc LICENSE.txt README.rst
+%license LICENSE.txt
+%doc README.rst
 %{python_sitelib}/*
 
 %changelog

++++++ python-gnupg-0.4.1.tar.gz -> python-gnupg-0.4.3.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.4.1/PKG-INFO 
new/python-gnupg-0.4.3/PKG-INFO
--- old/python-gnupg-0.4.1/PKG-INFO     2017-07-06 18:01:33.000000000 +0200
+++ new/python-gnupg-0.4.3/PKG-INFO     2018-06-13 16:42:22.000000000 +0200
@@ -1,12 +1,12 @@
 Metadata-Version: 1.1
 Name: python-gnupg
-Version: 0.4.1
+Version: 0.4.3
 Summary: A wrapper for the Gnu Privacy Guard (GPG or GnuPG)
-Home-page: http://pythonhosted.org/python-gnupg/index.html
+Home-page: http://gnupg.readthedocs.io/en/latest/
 Author: Vinay Sajip
 Author-email: [email protected]
-License: Copyright (C) 2008-2017 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.1.tar.gz
+License: Copyright (C) 2008-2018 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.3.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.
 Platform: No particular restrictions
 Classifier: Development Status :: 5 - Production/Stable
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.4.1/README.rst 
new/python-gnupg-0.4.3/README.rst
--- old/python-gnupg-0.4.1/README.rst   2017-07-06 15:49:11.000000000 +0200
+++ new/python-gnupg-0.4.3/README.rst   2018-06-13 13:50:26.000000000 +0200
@@ -26,6 +26,15 @@
 
     pip install python-gnupg
 
+.. important::
+   There is at least one fork of this project, which was apparently created
+   because an earlier version of this software used the ``subprocess`` module
+   with ``shell=True``, making it vulnerable to shell injection. **This is no
+   longer the case**.
+
+   Forks may not be drop-in compatible with this software, so take care to use
+   the correct version, as indicated in the ``pip install`` command above.
+
 
 Installing from a source distribution archive
 ---------------------------------------------
@@ -54,12 +63,46 @@
 
 .. note:: GCnn refers to an issue nn on Google Code.
 
-0.4.2 (future)
+0.4.4 (future)
 --------------
 
 Released: Not yet.
 
 
+0.4.3
+-----
+
+Released: 2018-06-13
+
+* Added --no-verbose to the gpg command line, in case verbose is specified in
+  gpg.conf - we don't need verbose output.
+
+
+0.4.2
+-----
+
+Released: 2018-03-28
+
+* Fixed #81: Subkey information is now collected and returned in a 
``subkey_info``
+  dictionary keyed by the subkey's ID.
+
+* Fixed #84: GPG2 version is now correctly detected on OS X.
+
+* Fixed #94: Added ``expect_passphrase`` password for use on GnuPG >= 2.1 when
+  passing passphrase to ``gpg`` via pinentry.
+
+* Fixed #95: Provided a ``trust_keys`` method to allow setting the trust level
+  for keys. Thanks to William Foster for a suggested implementation.
+
+* Made the exception message when the gpg executable is not found contain the
+  path of the executable that was tried. Thanks to Kostis Anagnostopoulos for
+  the suggestion.
+
+* Fixed #100: Made the error message less categorical in the case of a failure
+  with an unspecified reason, adding some information from gpg error codes when
+  available.
+
+
 0.4.1
 -----
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.4.1/gnupg.py 
new/python-gnupg-0.4.3/gnupg.py
--- old/python-gnupg-0.4.1/gnupg.py     2017-07-06 16:09:20.000000000 +0200
+++ new/python-gnupg-0.4.3/gnupg.py     2018-06-13 13:11:43.000000000 +0200
@@ -27,14 +27,14 @@
 and so does not work on Windows). Renamed to gnupg.py to avoid confusion with
 the previous versions.
 
-Modifications Copyright (C) 2008-2017 Vinay Sajip. All rights reserved.
+Modifications Copyright (C) 2008-2018 Vinay Sajip. All rights reserved.
 
 A unittest harness (test_gnupg.py) has also been added.
 """
 
-__version__ = "0.4.1"
+__version__ = "0.4.3"
 __author__ = "Vinay Sajip"
-__date__  = "$07-Jul-2017 15:09:20$"
+__date__  = "$13-Jun-2018 12:11:43$"
 
 try:
     from io import StringIO
@@ -217,12 +217,26 @@
         "TRUST_ULTIMATE" : TRUST_ULTIMATE,
     }
 
+    # for now, just the most common error codes. This can be expanded as and
+    # when reports come in of other errors.
+    GPG_SYSTEM_ERROR_CODES = {
+        1: 'permission denied',
+        35: 'file exists',
+        81: 'file not found',
+        97: 'not a directory',
+    }
+
+    GPG_ERROR_CODES = {
+        11: 'incorrect passphrase',
+    }
+
     def __init__(self, gpg):
         self.gpg = gpg
         self.valid = False
         self.fingerprint = self.creation_date = self.timestamp = None
         self.signature_id = self.key_id = None
         self.username = None
+        self.key_id = None
         self.key_status = None
         self.status = None
         self.pubkey_fingerprint = None
@@ -295,9 +309,26 @@
             if key == "UNEXPECTED":
                 self.status = 'unexpected data'
             else:
-                # N.B. there might be other reasons
+                # N.B. there might be other reasons. For example, if an output
+                # file can't  be created - /dev/null/foo will lead to a
+                # "not a directory" error, but which is not sent as a status
+                # message with the [GNUPG:] prefix. Similarly if you try to
+                # write to "/etc/foo" as a non-root user, a "permission denied"
+                # error will be sent as a non-status message.
+                message = 'error - %s' % value
+                parts = value.split()
+                if parts[-1].isdigit():
+                    code = int(parts[-1])
+                    system_error = bool(code & 0x8000)
+                    code = code & 0x7FFF
+                    if system_error:
+                        mapping = self.GPG_SYSTEM_ERROR_CODES
+                    else:
+                        mapping = self.GPG_ERROR_CODES
+                    if code in mapping:
+                        message = mapping[code]
                 if not self.status:
-                    self.status = 'incorrect passphrase'
+                    self.status = message
         elif key in ("DECRYPTION_INFO", "PLAINTEXT", "PLAINTEXT_LENGTH",
                      "NO_SECKEY", "BEGIN_SIGNING"):
             pass
@@ -409,6 +440,13 @@
     def handle_status(self, key, value):
         logger.debug('SendResult: %s: %s', key, value)
 
+def _set_fields(target, fieldnames, args):
+    for i, var in enumerate(fieldnames):
+        if i < len(args):
+            target[var] = args[i]
+        else:
+            target[var] = 'unavailable'
+
 class SearchKeys(list):
     ''' Handle status messages for --search-keys.
 
@@ -428,11 +466,7 @@
 
     def get_fields(self, args):
         result = {}
-        for i, var in enumerate(self.FIELDS):
-            if i < len(args):
-                result[var] = args[i]
-            else:
-                result[var] = 'unavailable'
+        _set_fields(result, self.FIELDS, args)
         result['uids'] = []
         result['sigs'] = []
         return result
@@ -470,7 +504,7 @@
     '''
 
     UID_INDEX = 9
-    FIELDS = 'type trust length algo keyid date expires dummy ownertrust uid 
sig'.split()
+    FIELDS = 'type trust length algo keyid date expires dummy ownertrust uid 
sig cap issuer flag token hash curve compliance updated origin'.split()
 
     def __init__(self, gpg):
         super(ListKeys, self).__init__(gpg)
@@ -500,14 +534,25 @@
             self.curkey['subkeys'][-1].append(fp)
             self.key_map[fp] = self.curkey
 
+    def _collect_subkey_info(self, curkey, args):
+        info_map = curkey.setdefault('subkey_info', {})
+        info = {}
+        _set_fields(info, self.FIELDS, args)
+        info_map[args[4]] = info
+
     def sub(self, args):
+        # See issue #81. We create a dict with more information about
+        # subkeys, but for backward compatibility reason, have to add it in
+        # as a separate entry 'subkey_info'
         subkey = [args[4], args[11]]    # keyid, type
         self.curkey['subkeys'].append(subkey)
+        self._collect_subkey_info(self.curkey, args)
         self.in_subkey = True
 
     def ssb(self, args):
         subkey = [args[4], None]    # keyid, type
         self.curkey['subkeys'].append(subkey)
+        self._collect_subkey_info(self.curkey, args)
         self.in_subkey = True
 
     def sig(self, args):
@@ -522,6 +567,7 @@
         # use the last value args[-1] instead of args[11]
         subkey = [args[4], args[-1]]
         self.curkey['subkeys'].append(subkey)
+        self._collect_subkey_info(self.curkey, args)
         self.in_subkey = True
 
 class TextHandler(object):
@@ -544,6 +590,7 @@
         self.data = ''
         self.ok = False
         self.status = ''
+        self.key_id = None
 
     def __nonzero__(self):
         if self.ok: return True
@@ -580,7 +627,10 @@
             self.status = 'sig created'
         elif key == "SIGEXPIRED":  # pragma: no cover
             self.status = 'sig expired'
-        elif key in ("ENC_TO", "USERID_HINT", "GOODMDC",
+        elif key == "ENC_TO":  # pragma: no cover
+            # ENC_TO <long_keyid> <keytype> <keylength>
+            self.key_id = value.split(' ', 1)[0]
+        elif key in ("USERID_HINT", "GOODMDC",
                      "END_DECRYPTION", "CARDCTRL", "BADMDC",
                      "SC_OP_FAILURE", "SC_OP_SUCCESS",
                      "PINENTRY_LAUNCHED", "KEY_CONSIDERED"):
@@ -662,6 +712,8 @@
         self.hash_algo = None
         self.fingerprint = None
         self.status = None
+        self.key_id = None
+        self.username = None
 
     def __nonzero__(self):
         return self.fingerprint is not None
@@ -677,17 +729,19 @@
             self.status = 'key revoked'
         elif key == "SIG_CREATED":
             (self.type,
-             algo, self.hash_algo, cls,
-             self.timestamp, self.fingerprint
-             ) = value.split()
+             algo, self.hash_algo, cls, self.timestamp, self.fingerprint
+            ) = value.split()
             self.status = 'signature created'
-        elif key in ("USERID_HINT", "NEED_PASSPHRASE", "GOOD_PASSPHRASE",
-                     "BAD_PASSPHRASE", "BEGIN_SIGNING"):
+        elif key == "USERID_HINT":  # pragma: no cover
+            self.key_id, self.username = value.split(' ', 1)
+        elif key == "BAD_PASSPHRASE":
+            self.status = 'bad passphrase'
+        elif key in ("NEED_PASSPHRASE", "GOOD_PASSPHRASE", "BEGIN_SIGNING"):
             pass
         else:  # pragma: no cover
             logger.debug('message ignored: %s, %s', key, value)
 
-VERSION_RE = re.compile(r'gpg \(GnuPG\) (\d+(\.\d+)*)'.encode('ascii'), re.I)
+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)
 
 class GPG(object):
@@ -754,7 +808,7 @@
         try:
             p = self._open_subprocess(["--version"])
         except OSError:
-            msg = 'Unable to run gpg - it may not be available.'
+            msg = 'Unable to run gpg (%s) - it may not be available.' % 
self.gpgbinary
             logger.exception(msg)
             raise OSError(msg)
         result = self.result_map['verify'](self) # any result will do for this
@@ -775,8 +829,9 @@
         will be appended. The ``passphrase`` argument needs to be True if
         a passphrase will be sent to GPG, else False.
         """
-        cmd = [self.gpgbinary, '--status-fd', '2', '--no-tty']
-        cmd.extend(['--debug', 'ipc'])
+        cmd = [self.gpgbinary, '--status-fd', '2', '--no-tty', '--no-verbose']
+        if 'DEBUG_IPC' in os.environ:
+            cmd.extend(['--debug', 'ipc'])
         if passphrase and hasattr(self, 'version'):
             if self.version >= (2, 1):
                 cmd[1:1] = ['--pinentry-mode', 'loopback']
@@ -1094,10 +1149,20 @@
         data.close()
         return result
 
-    def delete_keys(self, fingerprints, secret=False, passphrase=None):
+    def delete_keys(self, fingerprints, secret=False, passphrase=None,
+                    expect_passphrase=True):
+        """
+        Delete the indicated keys.
+
+        Since GnuPG 2.1, you can't delete secret keys without providing a
+        passphrase. However, if you're expecting the passphrase to go to gpg
+        via pinentry, you should specify expect_passphrase=False. (It's only
+        checked for GnuPG >= 2.1).
+        """
         which='key'
         if secret:  # pragma: no cover
-            if self.version >= (2, 1) and passphrase is None:
+            if (self.version >= (2, 1) and passphrase is None and
+                expect_passphrase):
                 raise ValueError('For GnuPG >= 2.1, deleting secret keys '
                                  'needs a passphrase to be provided')
             which='secret-key'
@@ -1122,18 +1187,21 @@
         return result
 
     def export_keys(self, keyids, secret=False, armor=True, minimal=False,
-                    passphrase=None):
+                    passphrase=None, expect_passphrase=True):
         """
         Export the indicated keys. A 'keyid' is anything gpg accepts.
 
         Since GnuPG 2.1, you can't export secret keys without providing a
-        passphrase.
+        passphrase. However, if you're expecting the passphrase to go to gpg
+        via pinentry, you should specify expect_passphrase=False. (It's only
+        checked for GnuPG >= 2.1).
         """
 
         which=''
         if secret:
             which='-secret-key'
-            if self.version >= (2, 1) and passphrase is None:
+            if (self.version >= (2, 1) and passphrase is None and
+                expect_passphrase):
                 raise ValueError('For GnuPG >= 2.1, exporting secret keys '
                                  'needs a passphrase to be provided')
         if _is_sequence(keyids):
@@ -1458,3 +1526,30 @@
         self._handle_io(args, file, result, passphrase, binary=True)
         logger.debug('decrypt result: %r', result.data)
         return result
+
+    def trust_keys(self, fingerprints, trustlevel):
+        levels = Verify.TRUST_LEVELS
+        if trustlevel not in levels:
+            poss = ', '.join(sorted(levels))
+            raise ValueError('Invalid trust level: "%s" (must be one of %s)' %
+                             (trustlevel, poss))
+        trustlevel = levels[trustlevel] + 2
+        import tempfile
+        try:
+            fd, fn = tempfile.mkstemp()
+            lines = []
+            if isinstance(fingerprints, string_types):
+                fingerprints = [fingerprints]
+            for f in fingerprints:
+                lines.append('%s:%s:' % (f, trustlevel))
+            # The trailing newline is required!
+            s = os.linesep.join(lines) + os.linesep
+            logger.debug('writing ownertrust info: %s', s);
+            os.write(fd, s.encode(self.encoding))
+            os.close(fd)
+            result = self.result_map['delete'](self)
+            p = self._open_subprocess(['--import-ownertrust', fn])
+            self._collect_output(p, result, stdin=p.stdin)
+        finally:
+            os.remove(fn)
+        return result
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.4.1/setup.py 
new/python-gnupg-0.4.3/setup.py
--- old/python-gnupg-0.4.1/setup.py     2017-01-29 19:30:28.000000000 +0100
+++ new/python-gnupg-0.4.3/setup.py     2018-03-28 16:21:20.000000000 +0200
@@ -7,13 +7,13 @@
     long_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.",
-    license="""Copyright (C) 2008-2017 by Vinay Sajip. All Rights Reserved. 
See LICENSE.txt for license.""",
+    license="""Copyright (C) 2008-2018 by Vinay Sajip. All Rights Reserved. 
See LICENSE.txt for license.""",
     version=version,
     author="Vinay Sajip",
     author_email="[email protected]",
     maintainer="Vinay Sajip",
     maintainer_email="[email protected]",
-    url="http://pythonhosted.org/python-gnupg/index.html";,
+    url="http://gnupg.readthedocs.io/en/latest/";,
     py_modules=["gnupg"],
     platforms="No particular restrictions",
     
download_url="https://pypi.io/packages/source/p/python-gnupg/python-gnupg-%s.tar.gz";
 % version,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.4.1/test_gnupg.py 
new/python-gnupg-0.4.3/test_gnupg.py
--- old/python-gnupg-0.4.1/test_gnupg.py        2017-07-06 16:09:39.000000000 
+0200
+++ new/python-gnupg-0.4.3/test_gnupg.py        2018-06-13 13:12:12.000000000 
+0200
@@ -2,7 +2,7 @@
 """
 A test harness for gnupg.py.
 
-Copyright (C) 2008-2017 Vinay Sajip. All rights reserved.
+Copyright (C) 2008-2018 Vinay Sajip. All rights reserved.
 """
 import doctest
 import logging
@@ -28,7 +28,7 @@
 import gnupg
 
 __author__ = "Vinay Sajip"
-__date__  = "$06-Jul-2017 15:09:39$"
+__date__  = "$13-Jun-2018 12:12:12$"
 
 ALL_TESTS = True
 
@@ -321,7 +321,13 @@
         fp = key_info['fingerprint']
         self.assertTrue(fp in public_keys.key_map)
         self.assertTrue(public_keys.key_map[fp] is key_info)
-        for _, _, sfp in key_info['subkeys']:
+        self.assertTrue('subkey_info' in key_info)
+        skinfo = key_info['subkey_info']
+        for skid, _, sfp in key_info['subkeys']:
+            self.assertTrue(skid in skinfo)
+            info = skinfo[skid]
+            self.assertEqual(info['keyid'], skid)
+            self.assertEqual(info['type'], 'sub')
             self.assertTrue(sfp in public_keys.key_map)
             self.assertTrue(public_keys.key_map[sfp] is key_info)
 
@@ -334,9 +340,15 @@
         self.assertTrue(fp in public_keys_sigs.key_map)
         self.assertTrue(public_keys_sigs.key_map[fp] is key_info)
         self.assertTrue(is_list_with_len(key_info['sigs'], 2))
+        self.assertTrue('subkey_info' in key_info)
+        skinfo = key_info['subkey_info']
         for siginfo in key_info['sigs']:
             self.assertTrue(len(siginfo), 3)
-        for _, _, sfp in key_info['subkeys']:
+        for skid, _, sfp in key_info['subkeys']:
+            self.assertTrue(skid in skinfo)
+            info = skinfo[skid]
+            self.assertEqual(info['keyid'], skid)
+            self.assertEqual(info['type'], 'sub')
             self.assertTrue(sfp in public_keys_sigs.key_map)
             self.assertTrue(public_keys_sigs.key_map[sfp] is key_info)
 
@@ -344,6 +356,14 @@
         self.assertTrue(is_list_with_len(private_keys, 1),
                         "1-element list expected")
         self.assertEqual(len(private_keys.fingerprints), 1)
+        key_info = private_keys[0]
+        self.assertTrue('subkey_info' in key_info)
+        skinfo = key_info['subkey_info']
+        self.assertTrue(skid in skinfo)
+        info = skinfo[skid]
+        self.assertEqual(info['keyid'], skid)
+        self.assertEqual(info['type'], 'ssb')
+
         # Now do the same test, but using keyring and secret_keyring arguments
         if self.gpg.version < (2, 1):
             pkn = 'pubring.gpg'
@@ -399,6 +419,29 @@
                         'Donna Davis <[email protected]>'])
         self.assertEqual(actual, expected)
 
+    def test_key_trust(self):
+        "Test that trusting keys works"
+        gpg = self.gpg
+        gpg.import_keys(KEYS_TO_IMPORT)
+        keys = gpg.list_keys()
+        fingerprints = []
+        for key in keys:
+            self.assertEqual(key['ownertrust'], '-')
+            fingerprints.append(key['fingerprint'])
+        cases = (
+            ('TRUST_NEVER', 'n'),
+            ('TRUST_MARGINAL', 'm'),
+            ('TRUST_FULLY', 'f'),
+            ('TRUST_ULTIMATE', 'u'),
+            ('TRUST_UNDEFINED', 'q'),
+        )
+        for param, expected in cases:
+            gpg.trust_keys(fingerprints, param)
+            keys = gpg.list_keys(keys=fingerprints)
+            for key in keys:
+                self.assertEqual(key['ownertrust'], expected)
+        self.assertRaises(ValueError, gpg.trust_keys, fingerprints, 
'TRUST_FOOBAR')
+
     def test_list_signatures(self):
         logger.debug("test_list_signatures begins")
         imported = self.gpg.import_keys(SIGNED_KEYS)
@@ -564,8 +607,14 @@
         self.assertFalse(sig, "Bad passphrase should fail")
         sig = self.gpg.sign(data, keyid=key.fingerprint, passphrase='aable')
         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'))
+        if sig.key_id:
+            self.assertTrue(key.fingerprint.endswith(sig.key_id))
         self.assertTrue(sig.hash_algo)
+        logger.debug('verification start')
         verified = self.gpg.verify(sig.data)
+        logger.debug('verification end')
         if key.fingerprint != verified.fingerprint:  # pragma: no cover
             logger.debug("key: %r", key.fingerprint)
             logger.debug("ver: %r", verified.fingerprint)
@@ -646,6 +695,8 @@
         data_file.close()
         try:
             verified = self.gpg.verify_data(sig_file, data)
+            self.assertTrue(verified.username.startswith('Andrew Able'))
+            self.assertTrue(key.fingerprint.endswith(verified.key_id))
         finally:
             os.unlink(sig_file)
         if key.fingerprint != verified.fingerprint:
@@ -670,8 +721,9 @@
 
     def test_nogpg(self):
         "Test that absence of gpg is handled correctly"
-        self.assertRaises(OSError, gnupg.GPG, gnupghome=self.homedir,
-                          gpgbinary='frob')
+        with self.assertRaises(OSError) as ar:
+            gnupg.GPG(gnupghome=self.homedir, gpgbinary='frob')
+        self.assertIn('frob', str(ar.exception))
 
     def test_make_args(self):
         "Test argument line construction"
@@ -748,6 +800,27 @@
         self.do_file_encryption_and_decryption(encfname, decfname)
         logger.debug("test_file_encryption_and_decryption ends")
 
+    #@skipIf(os.name == 'nt', 'Test not suitable for Windows')
+    def test_invalid_outputs(self):
+        "Test encrypting to invalid output files"
+        encfno, encfname = tempfile.mkstemp()
+        os.close(encfno)
+        cases = (
+            ('/dev/null/foo', 'not a directory'),
+            ('/etc/foo', 'permission denied'),
+        )
+        key = self.generate_key("Barbara", "Brown", "beta.com")
+        barbara = key.fingerprint
+        data = "Hello, world!"
+        for badout, message in cases:
+            stream = gnupg._make_binary_stream(data, self.gpg.encoding)
+            edata = self.gpg.encrypt_file(stream,
+                                          barbara, armor=False, output=badout)
+            # on GnuPG 1.4, you sometimes don't get any FAILURE messages, in
+            # which case status will not be set
+            if edata.status:
+                self.assertEqual(edata.status, message)
+
     def test_filenames_with_spaces(self):       # See Issue #16
         "Test that filenames with spaces are correctly handled"
         logger.debug("test_filename_with_spaces begins")
@@ -899,10 +972,11 @@
 
 
 TEST_GROUPS = {
-    'sign' : set(['test_signature_verification']),
+    'sign' : set(['test_signature_verification',
+                  'test_signature_file']),
     'crypt' : set(['test_encryption_and_decryption',
                    'test_file_encryption_and_decryption',
-                   'test_filenames_with_spaces']),
+                   'test_filenames_with_spaces', 'test_invalid_outputs']),
     'key' : set(['test_deletion', 'test_import_and_export',
                  'test_list_keys_after_generation',
                  'test_list_signatures',
@@ -910,12 +984,12 @@
                  'test_key_generation_with_escapes',
                  'test_key_generation_with_empty_value',
                  'test_key_generation_with_colons',
-                 'test_search_keys', 'test_scan_keys']),
+                 'test_search_keys', 'test_scan_keys', 'test_key_trust']),
     'import' : set(['test_import_only', 'test_doctest_import_keys']),
     'basic' : set(['test_environment', 'test_list_keys_initial',
                    'test_nogpg', 'test_make_args',
                    'test_quote_with_shell']),
-    'test': set(['test_doctest_import_keys']),
+    'test': set(['test_signature_verification']),
 }
 
 def suite(args=None):


Reply via email to