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):