Hello community,
here is the log from the commit of package python-python-gnupg for openSUSE:Factory checked in at 2019-01-25 22:44:11
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-python-gnupg (Old)
and /work/SRC/openSUSE:Factory/.python-python-gnupg.new.28833 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-python-gnupg"
Fri Jan 25 22:44:11 2019 rev:5 rq:668267 version:0.4.4
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-python-gnupg/python-python-gnupg.changes 2018-12-24 11:42:35.473388325 +0100
+++ /work/SRC/openSUSE:Factory/.python-python-gnupg.new.28833/python-python-gnupg.changes 2019-01-25 22:44:12.807165479 +0100
@@ -1,0 +2,30 @@
+Thu Jan 24 10:07:27 UTC 2019 - Tomáš Chvátal
+
+- Enable tests
+
+-------------------------------------------------------------------
+Thu Jan 24 09:31:19 UTC 2019 - Karol Babioch
+
+- Update to 0.4.4:
+ * Changed how any return value from the ``on_data`` callable is processed. In
+ earlier versions, the return value was ignored. In this version, if the
+ return value is ``False``, the data received from ``gpg`` is not buffered.
+ Otherwise (if the value is ``None`` or ``True``, for example), the data is
+ buffered as normal. This functionality can be used to do your own
+ buffering, or to prevent buffering altogether. The ``on_data`` callable is
+ also called once with an empty byte-string to signal the end of data from
+ ``gpg``.
+ * Added an additional attribute ``check_fingerprint_collisions`` to
+ ``GPG`` instances, which defaults to ``False``. It seems that ``gpg`` is
+ happy to have duplicate keys and fingerprints in a keyring, so we can't be
+ too strict. A user can set this attribute of an instance to ``True`` to
+ trigger a check for collisions.
+ * With GnuPG 2.2.7 or later, provide the fingerprint of a signing key for a
+ failed signature verification, if available.
+ * For verification where multiple signatures are involved, a mapping of
+ signature_ids to fingerprint, keyid, username, creation date, creation
+ timestamp and expiry timestamp is provided.
+ * Added a check to disallow certain control characters ('\r', '\n', NUL) in
+ passphrases.
+
+-------------------------------------------------------------------
Old:
----
python-gnupg-0.4.3.tar.gz
New:
----
python-gnupg-0.4.4.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-python-gnupg.spec ++++++
--- /var/tmp/diff_new_pack.lOdi8C/_old 2019-01-25 22:44:13.263164918 +0100
+++ /var/tmp/diff_new_pack.lOdi8C/_new 2019-01-25 22:44:13.267164912 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-python-gnupg
#
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 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
@@ -18,9 +18,8 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define oldpython python
-%bcond_with test
Name: python-python-gnupg
-Version: 0.4.3
+Version: 0.4.4
Release: 0
Summary: A wrapper for the GNU Privacy Guard (GPG or GnuPG)
License: BSD-3-Clause
@@ -29,20 +28,16 @@
Source: https://files.pythonhosted.org/packages/source/p/python-gnupg/python-gnupg-%{version}.tar.gz
BuildRequires: %{python_module setuptools}
BuildRequires: fdupes
+BuildRequires: gpg2
BuildRequires: python-rpm-macros
Requires: gpg2
+Obsoletes: python-gnupg < %{version}
+Provides: python-gnupg = %{version}
BuildArch: noarch
-%if %{with test}
-BuildRequires: gpg2
-%endif
%ifpython2
Obsoletes: %{oldpython}-gnupg < %{version}
Provides: %{oldpython}-gnupg = %{version}
%endif
-%ifpython3
-Obsoletes: python3-gnupg < %{version}
-Provides: python3-gnupg = %{version}
-%endif
%python_subpackages
%description
@@ -59,10 +54,9 @@
%python_install
%python_expand %fdupes %{buildroot}%{$python_sitelib}
-%if %{with test}
%check
+export NO_EXTERNAL_TESTS=true
%python_exec test_gnupg.py
-%endif
%files %{python_files}
%license LICENSE.txt
++++++ python-gnupg-0.4.3.tar.gz -> python-gnupg-0.4.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.4.3/PKG-INFO new/python-gnupg-0.4.4/PKG-INFO
--- old/python-gnupg-0.4.3/PKG-INFO 2018-06-13 16:42:22.000000000 +0200
+++ new/python-gnupg-0.4.4/PKG-INFO 2019-01-24 09:48:36.000000000 +0100
@@ -1,12 +1,12 @@
Metadata-Version: 1.1
Name: python-gnupg
-Version: 0.4.3
+Version: 0.4.4
Summary: A wrapper for the Gnu Privacy Guard (GPG or GnuPG)
Home-page: http://gnupg.readthedocs.io/en/latest/
Author: Vinay Sajip
Author-email: vinay_sajip@red-dove.com
-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
+License: Copyright (C) 2008-2019 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.4.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
@@ -24,5 +24,6 @@
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.4.3/README.rst new/python-gnupg-0.4.4/README.rst
--- old/python-gnupg-0.4.3/README.rst 2018-06-13 13:50:26.000000000 +0200
+++ new/python-gnupg-0.4.4/README.rst 2019-01-23 20:36:19.000000000 +0100
@@ -63,12 +63,44 @@
.. note:: GCnn refers to an issue nn on Google Code.
-0.4.4 (future)
+0.4.5 (future)
--------------
Released: Not yet.
+0.4.4
+-----
+
+Released: 2019-01-24
+
+* Fixed #108: Changed how any return value from the ``on_data`` callable is
+ processed. In earlier versions, the return value was ignored. In this version,
+ if the return value is ``False``, the data received from ``gpg`` is not
+ buffered. Otherwise (if the value is ``None`` or ``True``, for example), the
+ data is buffered as normal. This functionality can be used to do your own
+ buffering, or to prevent buffering altogether.
+
+ The ``on_data`` callable is also called once with an empty byte-string to
+ signal the end of data from ``gpg``.
+
+* Fixed #97: Added an additional attribute ``check_fingerprint_collisions`` to
+ ``GPG`` instances, which defaults to ``False``. It seems that ``gpg`` is happy
+ to have duplicate keys and fingerprints in a keyring, so we can't be too
+ strict. A user can set this attribute of an instance to ``True`` to trigger a
+ check for collisions.
+
+* Fixed #111: With GnuPG 2.2.7 or later, provide the fingerprint of a signing
+ key for a failed signature verification, if available.
+
+* Fixed #21: For verification where multiple signatures are involved, a
+ mapping of signature_ids to fingerprint, keyid, username, creation date,
+ creation timestamp and expiry timestamp is provided.
+
+* Added a check to disallow certain control characters ('\r', '\n', NUL) in
+ passphrases.
+
+
0.4.3
-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.4.3/gnupg.py new/python-gnupg-0.4.4/gnupg.py
--- old/python-gnupg-0.4.3/gnupg.py 2018-06-13 13:11:43.000000000 +0200
+++ new/python-gnupg-0.4.4/gnupg.py 2019-01-24 09:43:25.000000000 +0100
@@ -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-2018 Vinay Sajip. All rights reserved.
+Modifications Copyright (C) 2008-2019 Vinay Sajip. All rights reserved.
A unittest harness (test_gnupg.py) has also been added.
"""
-__version__ = "0.4.3"
+__version__ = "0.4.4"
__author__ = "Vinay Sajip"
-__date__ = "$13-Jun-2018 12:11:43$"
+__date__ = "$24-Jan-2019 08:43:25$"
try:
from io import StringIO
@@ -244,6 +244,7 @@
self.sig_timestamp = None
self.trust_text = None
self.trust_level = None
+ self.sig_info = {}
def __nonzero__(self):
return self.valid
@@ -251,41 +252,72 @@
__bool__ = __nonzero__
def handle_status(self, key, value):
+
+ def update_sig_info(**kwargs):
+ sig_id = self.signature_id
+ if sig_id:
+ info = self.sig_info[sig_id]
+ info.update(kwargs)
+
if key in self.TRUST_LEVELS:
self.trust_text = key
self.trust_level = self.TRUST_LEVELS[key]
+ update_sig_info(trust_level=self.trust_level,
+ trust_text=self.trust_text)
elif key in ("WARNING", "ERROR"):
logger.warning('potential problem: %s: %s', key, value)
elif key == "BADSIG": # pragma: no cover
self.valid = False
self.status = 'signature bad'
self.key_id, self.username = value.split(None, 1)
+ update_sig_info(keyid=self.key_id, username=self.username,
+ status=self.status)
elif key == "ERRSIG": # pragma: no cover
self.valid = False
+ parts = value.split()
(self.key_id,
algo, hash_algo,
cls,
- self.timestamp) = value.split()[:5]
+ self.timestamp) = parts[:5]
+ # Since GnuPG 2.2.7, a fingerprint is tacked on
+ if len(parts) >= 7:
+ self.fingerprint = parts[6]
self.status = 'signature error'
+ update_sig_info(keyid=self.key_id, timestamp=self.timestamp,
+ fingerprint=self.fingerprint, status=self.status)
elif key == "EXPSIG": # pragma: no cover
self.valid = False
self.status = 'signature expired'
self.key_id, self.username = value.split(None, 1)
+ update_sig_info(keyid=self.key_id, username=self.username,
+ status=self.status)
elif key == "GOODSIG":
self.valid = True
self.status = 'signature good'
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]
(self.fingerprint,
self.creation_date,
self.sig_timestamp,
- self.expire_timestamp) = value.split()[:4]
+ 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]
self.status = 'signature valid'
+ update_sig_info(fingerprint=fingerprint, creation_date=creation_date,
+ timestamp=sig_ts, expiry=expire_ts,
+ pubkey_fingerprint=self.pubkey_fingerprint,
+ status=self.status)
elif key == "SIG_ID":
+ sig_id, creation_date, timestamp = value.split()
+ self.sig_info[sig_id] = {'creation_date': creation_date,
+ 'timestamp': timestamp}
(self.signature_id,
- self.creation_date, self.timestamp) = value.split()
+ self.creation_date, self.timestamp) = (sig_id, creation_date,
+ timestamp)
elif key == "DECRYPTION_FAILED": # pragma: no cover
self.valid = False
self.key_id = value
@@ -303,6 +335,7 @@
else:
self.key_status = 'signing key was revoked'
self.status = self.key_status
+ 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
@@ -524,7 +557,7 @@
def fpr(self, args):
fp = args[9]
- if fp in self.key_map: # pragma: no cover
+ if fp in self.key_map and self.gpg.check_fingerprint_collisions: # pragma: no cover
raise ValueError('Unexpected fingerprint collision: %s' % fp)
if not self.in_subkey:
self.curkey['fingerprint'] = fp
@@ -823,6 +856,10 @@
dot = '.'.encode('ascii')
self.version = tuple([int(s) for s in m.groups()[0].split(dot)])
+ # See issue #97. It seems gpg allow duplicate keys in keyrings, so we
+ # can't be too strict.
+ self.check_fingerprint_collisions = False
+
def make_args(self, args, passphrase):
"""
Make a list of command line elements for GPG. The value of ``args``
@@ -921,11 +958,15 @@
while True:
data = stream.read(1024)
if len(data) == 0:
+ if on_data:
+ on_data(data)
break
logger.debug("chunk: %r" % data[:256])
- chunks.append(data)
+ append = True
if on_data:
- on_data(data)
+ append = on_data(data) != False
+ if append:
+ chunks.append(data)
if _py3k:
# Join using b'' or '', as appropriate
result.data = type(data)().join(chunks)
@@ -996,9 +1037,20 @@
args.extend(['--yes'])
args.extend(['--output', no_quote(output)])
+ def is_valid_passphrase(self, passphrase):
+ """
+ Confirm that the passphrase doesn't contain newline-type characters -
+ it is passed in a pipe to gpg, and so not checking could lead to
+ spoofing attacks by passing arbitrary text after passphrase and newline.
+ """
+ return ('\n' not in passphrase and '\r' not in passphrase and
+ '\x00' not in passphrase)
+
def sign_file(self, file, keyid=None, passphrase=None, clearsign=True,
detach=False, binary=False, output=None, extra_args=None):
"""sign file"""
+ if passphrase and not self.is_valid_passphrase(passphrase):
+ raise ValueError('Invalid passphrase')
logger.debug("sign_file: %s", file)
if binary: # pragma: no cover
args = ['-s']
@@ -1118,7 +1170,7 @@
>>> gpg = GPG(gpgbinary=GPGBINARY, gnupghome="keys")
>>> os.chmod('keys', 0x1C0)
>>> result = gpg.recv_keys('pgp.mit.edu', '92905378')
- >>> assert result
+ >>> if 'NO_EXTERNAL_TESTS' not in os.environ: assert result
"""
result = self.result_map['import'](self)
@@ -1159,6 +1211,8 @@
via pinentry, you should specify expect_passphrase=False. (It's only
checked for GnuPG >= 2.1).
"""
+ if passphrase and not self.is_valid_passphrase(passphrase):
+ raise ValueError('Invalid passphrase')
which='key'
if secret: # pragma: no cover
if (self.version >= (2, 1) and passphrase is None and
@@ -1196,7 +1250,8 @@
via pinentry, you should specify expect_passphrase=False. (It's only
checked for GnuPG >= 2.1).
"""
-
+ if passphrase and not self.is_valid_passphrase(passphrase):
+ raise ValueError('Invalid passphrase')
which=''
if secret:
which='-secret-key'
@@ -1277,7 +1332,8 @@
if sigs:
which = 'sigs'
- else: which='keys'
+ else:
+ which = 'keys'
if secret:
which='secret-keys'
args = ['--list-%s' % which,
@@ -1321,7 +1377,7 @@
>>> gpg = GPG(gpgbinary=GPGBINARY, gnupghome='keys')
>>> os.chmod('keys', 0x1C0)
>>> result = gpg.search_keys('')
- >>> assert result, 'Failed using default keyserver'
+ >>> if 'NO_EXTERNAL_TESTS' not in os.environ: assert result, 'Failed using default keyserver'
>>> #keyserver = 'keyserver.ubuntu.com'
>>> #result = gpg.search_keys('', keyserver)
>>> #assert result, 'Failed using keyserver.ubuntu.com'
@@ -1427,6 +1483,8 @@
always_trust=False, passphrase=None,
armor=True, output=None, symmetric=False, extra_args=None):
"Encrypt the message read from the file-like object 'file'"
+ if passphrase and not self.is_valid_passphrase(passphrase):
+ raise ValueError('Invalid passphrase')
args = ['--encrypt']
if symmetric:
# can't be False or None - could be True or a cipher algo value
@@ -1515,6 +1573,8 @@
def decrypt_file(self, file, always_trust=False, passphrase=None,
output=None, extra_args=None):
+ if passphrase and not self.is_valid_passphrase(passphrase):
+ raise ValueError('Invalid passphrase')
args = ["--decrypt"]
if output: # write the output to a file with the specified name
self.set_output_without_confirmation(args, output)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.4.3/setup.py new/python-gnupg-0.4.4/setup.py
--- old/python-gnupg-0.4.3/setup.py 2018-03-28 16:21:20.000000000 +0200
+++ new/python-gnupg-0.4.4/setup.py 2019-01-24 09:25:30.000000000 +0100
@@ -7,7 +7,7 @@
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-2018 by Vinay Sajip. All Rights Reserved. See LICENSE.txt for license.""",
+ license="""Copyright (C) 2008-2019 by Vinay Sajip. All Rights Reserved. See LICENSE.txt for license.""",
version=version,
author="Vinay Sajip",
author_email="vinay_sajip@red-dove.com",
@@ -33,6 +33,7 @@
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
+ "Programming Language :: Python :: 3.7",
"Operating System :: OS Independent",
"Topic :: Software Development :: Libraries :: Python Modules"
]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.4.3/test_gnupg.py new/python-gnupg-0.4.4/test_gnupg.py
--- old/python-gnupg-0.4.3/test_gnupg.py 2018-06-13 13:12:12.000000000 +0200
+++ new/python-gnupg-0.4.4/test_gnupg.py 2019-01-24 09:43:59.000000000 +0100
@@ -28,7 +28,7 @@
import gnupg
__author__ = "Vinay Sajip"
-__date__ = "$13-Jun-2018 12:12:12$"
+__date__ = "$24-Jan-2019 08:43:59$"
ALL_TESTS = True
@@ -173,11 +173,14 @@
class GPGTestCase(unittest.TestCase):
def setUp(self):
- hd = os.path.join(os.getcwd(), 'keys')
- if os.path.exists(hd):
- self.assertTrue(os.path.isdir(hd),
- "Not a directory: %s" % hd)
- shutil.rmtree(hd, ignore_errors=True)
+ if 'STATIC_TEST_HOMEDIR' not in os.environ:
+ hd = tempfile.mkdtemp(prefix='keys-')
+ else:
+ hd = os.path.join(os.getcwd(), 'keys')
+ if os.path.exists(hd):
+ self.assertTrue(os.path.isdir(hd),
+ "Not a directory: %s" % hd)
+ shutil.rmtree(hd, ignore_errors=True)
prepare_homedir(hd)
self.homedir = hd
self.gpg = gpg = gnupg.GPG(gnupghome=hd, gpgbinary=GPGBINARY)
@@ -193,6 +196,10 @@
data_file.write(os.urandom(5120 * 1024))
data_file.close()
+ def tearDown(self):
+ if 'STATIC_TEST_HOMEDIR' not in os.environ:
+ shutil.rmtree(self.homedir, ignore_errors=True)
+
def test_environment(self):
"Test the environment by ensuring that setup worked"
hd = self.homedir
@@ -373,7 +380,7 @@
# and the keyring file name has changed.
pkn = 'pubring.kbx'
skn = None
- hd = os.path.join(os.getcwd(), 'keys')
+ hd = self.homedir
if os.name == 'posix':
pkn = os.path.join(hd, pkn)
if skn:
@@ -489,6 +496,9 @@
data = data.encode(gpg.encoding)
edata = str(gpg.encrypt(data, barbara))
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")
if data != ddata.data: # pragma: no cover
logger.debug("was: %r", data)
@@ -503,6 +513,12 @@
logger.debug("test_encryption_and_decryption ends")
# Test symmetric encryption
data = "chippy was here"
+ self.assertRaises(ValueError, gpg.encrypt, data, None,
+ passphrase='bbr\x00own', symmetric=True)
+ self.assertRaises(ValueError, gpg.encrypt, data, None,
+ 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))
ddata = gpg.decrypt(edata, passphrase='bbrown')
self.assertEqual(data, str(ddata))
@@ -603,6 +619,9 @@
else:
data = unicode('Hello, André', self.gpg.encoding)
data = data.encode(self.gpg.encoding)
+ self.assertRaises(ValueError, self.gpg.sign, data, keyid=key.fingerprint, passphrase="bbr\x00own")
+ self.assertRaises(ValueError, self.gpg.sign, data, keyid=key.fingerprint, passphrase="bbr\rown")
+ self.assertRaises(ValueError, self.gpg.sign, data, keyid=key.fingerprint, passphrase="bbr\nown")
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')
@@ -836,12 +855,13 @@
#@skipIf(os.name == 'nt', 'Test not suitable for Windows')
def test_search_keys(self):
"Test that searching for keys works"
- r = self.gpg.search_keys('')
- self.assertTrue(r)
- self.assertTrue('Vinay Sajip ' in r[0]['uids'])
- r = self.gpg.search_keys('92905378')
- self.assertTrue(r)
- self.assertTrue('Vinay Sajip ' in r[0]['uids'])
+ if 'NO_EXTERNAL_TESTS' not in os.environ:
+ r = self.gpg.search_keys('')
+ self.assertTrue(r)
+ self.assertTrue('Vinay Sajip ' in r[0]['uids'])
+ r = self.gpg.search_keys('92905378')
+ self.assertTrue(r)
+ self.assertTrue('Vinay Sajip ' in r[0]['uids'])
def test_quote_with_shell(self):
"Test shell quoting with a real shell"