Hello community, here is the log from the commit of package python3-gnupg for openSUSE:Factory checked in at 2017-02-09 11:15:08 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python3-gnupg (Old) and /work/SRC/openSUSE:Factory/.python3-gnupg.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python3-gnupg" Changes: -------- --- /work/SRC/openSUSE:Factory/python3-gnupg/python3-gnupg.changes 2016-09-21 18:49:00.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.python3-gnupg.new/python3-gnupg.changes 2017-02-09 11:15:10.610790801 +0100 @@ -1,0 +2,18 @@ +Fri Feb 3 03:28:05 UTC 2017 - [email protected] + +- specfile: + * update copyright year + +- update to version 0.4.0: + * Added support for KEY_CONSIDERED in more places - encryption / + decryption, signing, key generation and key import. + * Partial fix for #32 (GPG 2.1 compatibility). Unfortunately, better + support cannot be provided at this point, unless there are certain + changes (relating to pinentry popups) in how GPG 2.1 works. + * Fixed #60: An IndexError was being thrown by scan_keys(). + * Ensured that utf-8 encoding is used when the --with-column mode is + used. Thanks to Yann Leboulanger for the patch. + * list_keys() now uses --fixed-list-mode. Thanks to Werner Koch for + the pointer. + +------------------------------------------------------------------- Old: ---- python-gnupg-0.3.9.tar.gz New: ---- python-gnupg-0.4.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python3-gnupg.spec ++++++ --- /var/tmp/diff_new_pack.3187Tp/_old 2017-02-09 11:15:11.230703232 +0100 +++ /var/tmp/diff_new_pack.3187Tp/_new 2017-02-09 11:15:11.234702666 +0100 @@ -1,7 +1,7 @@ # # spec file for package python3-gnupg # -# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2017 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 @@ -17,7 +17,7 @@ Name: python3-gnupg -Version: 0.3.9 +Version: 0.4.0 Release: 0 Url: https://bitbucket.org/vinay.sajip/python-gnupg Summary: A wrapper for the Gnu Privacy Guard (GPG or GnuPG) ++++++ python-gnupg-0.3.9.tar.gz -> python-gnupg-0.4.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.3.9/PKG-INFO new/python-gnupg-0.4.0/PKG-INFO --- old/python-gnupg-0.3.9/PKG-INFO 2016-09-10 09:41:21.000000000 +0200 +++ new/python-gnupg-0.4.0/PKG-INFO 2017-01-29 19:30:36.000000000 +0100 @@ -1,12 +1,12 @@ Metadata-Version: 1.1 Name: python-gnupg -Version: 0.3.9 +Version: 0.4.0 Summary: A wrapper for the Gnu Privacy Guard (GPG or GnuPG) -Home-page: http://packages.python.org/python-gnupg/index.html +Home-page: http://pythonhosted.org/python-gnupg/index.html Author: Vinay Sajip Author-email: [email protected] -License: Copyright (C) 2008-2016 by Vinay Sajip. All Rights Reserved. See LICENSE.txt for license. -Download-URL: https://pypi.python.org/packages/source/p/python-gnupg/python-gnupg-0.3.9.tar.gz +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.0.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 @@ -23,5 +23,6 @@ Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 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.3.9/README.rst new/python-gnupg-0.4.0/README.rst --- old/python-gnupg-0.3.9/README.rst 2016-09-10 09:37:53.000000000 +0200 +++ new/python-gnupg-0.4.0/README.rst 2017-01-29 18:15:10.000000000 +0100 @@ -1,353 +1,375 @@ -.. image:: https://travis-ci.org/vsajip/python-gnupg.svg - :target: https://travis-ci.org/vsajip/python-gnupg - -.. image:: https://coveralls.io/repos/vsajip/python-gnupg/badge.svg - :target: https://coveralls.io/github/vsajip/python-gnupg - - -What is it? -=========== - -The GNU Privacy Guard (gpg, or gpg.exe on Windows) is a command-line program -which provides support for programmatic access via spawning a separate process -to run it and then communicating with that process from your program. - -This project, ``python-gnupg``, implements a Python library which takes care -of the internal details and allows its users to generate and manage keys, -encrypt and decrypt data, and sign and verify messages. - -Installation -============ - -Installing from PyPI --------------------- - -You can install this package from the Python Package Index (pyPI) by running:: - - pip install python-gnupg - - -Installing from a source distribution archive ---------------------------------------------- -To install this package from a source distribution archive, do the following: - -1. Extract all the files in the distribution archive to some directory on your - system. -2. In that directory, run ``python setup.py install``. -3. Optionally, run ``python test_gnupg.py`` to ensure that the package is - working as expected. - -Credits -======= - -* The developers of the GNU Privacy Guard. -* The original version of this module was developed by Andrew Kuchling. -* It was improved by Richard Jones. -* It was further improved by Steve Traugott. - -The present incarnation, based on the earlier versions, uses the ``subprocess`` -module and so works on Windows as well as Unix/Linux platforms. It's not, -however, 100% backwards-compatible with earlier incarnations. - -Change log -========== - -N.B: GCnn refers to an issue nn on Google Code. - -0.4.0 (future) --------------- - -Released: Not yet - -0.3.9 ------ - -Released: 2016-09-10 - -* Fixed #38: You can now request information about signatures against - keys. Thanks to SunDwarf for the suggestion and patch, which was used - as a basis for this change. - -* Fixed #49: When exporting keys, no attempt is made to decode the output when - armor=False is specified. - -* Fixed #53: A ``FAILURE`` message caused by passing an incorrect passphrase - is handled. - -* Handled ``EXPORTED`` and ``EXPORT_RES`` messages while exporting keys. Thanks - to Marcel Pörner for the patch. - -* Fixed #54: Improved error message shown when gpg is not available. - -* Fixed #55: Added support for ``KEY_CONSIDERED`` while verifying. - -* Avoided encoding problems with filenames under Windows. Thanks to Kévin - Bernard-Allies for the patch. - -* Fixed #57: Used a better mechanism for comparing keys. - - -0.3.8 ------ - -Released: 2015-09-24 - -* Fixed #22: handled ``PROGRESS`` messages during verification and signing. - -* Fixed #26: handled ``PINENTRY_LAUNCHED`` messages during verification, - decryption and key generation. - -* Fixed #28: Allowed a default Name-Email to be computed even when neither of - ``LOGNAME`` and ``USERNAME`` are in the environment. - -* Fixed #29: Included test files missing from the tarball in previous versions. - -* Fixed #39: On Python 3.x, passing a text instead of a binary stream caused - file decryption to hang due to a ``UnicodeDecodeError``. This has now been - correctly handled: The decryption fails with a "no data" status. - -* Fixed #41: Handled Unicode filenames correctly by encoding them on 2.x using - the file system encoding. - -* Fixed #43: handled ``PINENTRY_LAUNCHED`` messages during key export. Thanks - to Ian Denhardt for looking into this. - -* Hide the console window which appears on Windows when gpg is spawned. - Thanks to Kévin Bernard-Allies for the patch. - -* Subkey fingerprints are now captured. - -* The returned value from the ``list_keys`` method now has a new attribute, - ``key_map``, which is a dictionary mapping key and subkey fingerprints to - the corresponding key's dictionary. With this change, you don't need to - iterate over the (potentially large) returned list to search for a key with - a given fingerprint - the ``key_map`` dict will take you straight to the key - info, whether the fingerprint you have is for a key or a subkey. Thanks to - Nick Daly for the initial suggestion. - -0.3.7 ------ - -Released: 2014-12-07 - -Signed with PGP key: Vinay Sajip (CODE SIGNING KEY) <[email protected]> - -Key Fingerprint : CA74 9061 914E AC13 8E66 EADB 9147 B477 339A 9B86 - -* Added an ``output`` keyword parameter to the ``sign`` and - ``sign_file`` methods, to allow writing the signature to a file. - Thanks to Jannis Leidel for the patch. - -* Allowed specifying ``True`` for the ``sign`` keyword parameter, - which allows use of the default key for signing and avoids having to - specify a key id when it's desired to use the default. Thanks to - Fabian Beutel for the patch. - -* Used a uniform approach with subprocess on Windows and POSIX: shell=True - is not used on either. - -* When signing/verifying, the status is updated to reflect any expired or - revoked keys or signatures. - -* Handled 'NOTATION_NAME' and 'NOTATION_DATA' during verification. - -* Fixed #1, #16, #18, #20: Quoting approach changed, since now shell=False. - -* Fixed #14: Handled 'NEED_PASSPHRASE_PIN' message. - -* Fixed #8: Added a scan_keys method to allow scanning of keys without the - need to import into a keyring. Thanks to Venzen Khaosan for the suggestion. - -* Fixed #5: Added '0x' prefix when searching for keys. Thanks to Aaron Toponce - for the report. - -* Fixed #4: Handled 'PROGRESS' message during encryption. Thanks to Daniel - Mills for the report. - -* Fixed #3: Changed default encoding to Latin-1. - -* Fixed #2: Raised ValueError if no recipients were specified - for an asymmetric encryption request. - -* Handled 'UNEXPECTED' message during verification. Thanks to - David Andersen for the patch. - -* Replaced old range(len(X)) idiom with enumerate(). - -* Refactored ``ListKeys`` / ``SearchKeys`` classes to maximise use of common - functions. - -* Fixed GC94: Added ``export-minimal`` and ``armor`` options when exporting - keys. This addition was inadvertently left out of 0.3.6. - -0.3.6 ------ - -Released: 2014-02-05 - -* Fixed GC82: Enabled fast random tests on gpg as well as gpg2. -* Fixed GC85: Avoided deleting temporary file to preserve its permissions. -* Fixed GC87: Avoided writing passphrase to log. -* Fixed GC95: Added ``verify_data()`` method to allow verification of - signatures in memory. -* Fixed GC96: Regularised end-of-line characters. -* Fixed GC98: Rectified problems with earlier fix for shell injection. - -0.3.5 ------ - -Released: 2013-08-30 - -* Added improved shell quoting to guard against shell injection. -* Fixed GC76: Added ``search_keys()`` and ``send_keys()`` methods. -* Fixed GC77: Allowed specifying a symmetric cipher algorithm. -* Fixed GC78: Fell back to utf-8 encoding when no other could be determined. -* Fixed GC79: Default key length is now 2048 bits. -* Fixed GC80: Removed the Name-Comment default in key generation. - -0.3.4 ------ - -Released: 2013-06-05 - -* Fixed GC65: Fixed encoding exception when getting version. -* Fixed GC66: Now accepts sets and frozensets where appropriate. -* Fixed GC67: Hash algorithm now captured in sign result. -* Fixed GC68: Added support for ``--secret-keyring``. -* Fixed GC70: Added support for multiple keyrings. - -0.3.3 ------ - -Released: 2013-03-11 - -* Fixed GC57: Handled control characters in ``list_keys()``. -* Fixed GC61: Enabled fast random for testing. -* Fixed GC62: Handled ``KEYEXPIRED`` status. -* Fixed GC63: Handled ``NO_SGNR`` status. - -0.3.2 ------ - -Released: 2013-01-17 - -* Fixed GC56: Disallowed blank values in key generation. -* Fixed GC57: Handled colons and other characters in ``list_keys()``. -* Fixed GC59/GC60: Handled ``INV_SGNR`` status during verification and removed - calls requiring interactive password input from doctests. - -0.3.1 ------ - -Released: 2012-09-01 - -* Fixed GC45: Allowed additional arguments to gpg executable. -* Fixed GC50: Used latin-1 encoding in tests when it's known to be required. -* Fixed GC51: Test now returns non-zero exit status on test failure. -* Fixed GC53: Now handles ``INV_SGNR`` and ``KEY_NOT_CREATED`` statuses. -* Fixed GC55: Verification and decryption now return trust level of signer in - integer and text form. - -0.3.0 ------ - -Released: 2012-05-12 - -* Fixed GC49: Reinstated Yann Leboulanger's change to support subkeys - (accidentally left out in 0.2.7). - -0.2.9 ------ - -Released: 2012-03-29 - -* Fixed GC36: Now handles ``CARDCTRL`` and ``POLICY_URL`` messages. -* Fixed GC40: Now handles ``DECRYPTION_INFO``, ``DECRYPTION_FAILED`` and - ``DECRYPTION_OKAY`` messages. -* The ``random_binary_data file`` is no longer shipped, but constructed by the - test suite if needed. - -0.2.8 ------ - -Released: 2011-09-02 - -* Fixed GC29: Now handles ``IMPORT_RES`` while verifying. -* Fixed GC30: Fixed an encoding problem. -* Fixed GC33: Quoted arguments for added safety. - -0.2.7 ------ - -Released: 2011-04-10 - -* Fixed GC24: License is clarified as BSD. -* Fixed GC25: Incorporated Daniel Folkinshteyn's changes. -* Fixed GC26: Incorporated Yann Leboulanger's subkey change. -* Fixed GC27: Incorporated hysterix's support for symmetric encryption. -* Did some internal cleanups of Unicode handling. - -0.2.6 ------ - -Released: 2011-01-25 - -* Fixed GC14: Should be able to accept passphrases from GPG-Agent. -* Fixed GC19: Should be able to create a detached signature. -* Fixed GC21/GC23: Better handling of less common responses from GPG. - -0.2.5 ------ - -Released: 2010-10-13 - -* Fixed GC11/GC16: Detached signatures can now be created. -* Fixed GC3: Detached signatures can be verified. -* Fixed GC12: Better support for RSA and IDEA. -* Fixed GC15/GC17: Better support for non-ASCII input. - -0.2.4 ------ - -Released: 2010-03-01 - -* Fixed GC9: Now allows encryption without armor and the ability to encrypt - and decrypt directly to/from files. - -0.2.3 ------ - -Released: 2010-01-07 - -* Fixed GC7: Made sending data to process threaded and added a test case. - With a test data file used by the test case, the archive size has gone up - to 5MB (the size of the test file). - -0.2.2 ------ - -Released: 2009-10-06 - -* Fixed GC5/GC6: Added ``--batch`` when specifying ``--passphrase-fd`` and - changed the name of the distribution file to add the ``python-`` prefix. - -0.2.1 ------ - -Released: 2009-08-07 - -* Fixed GC2: Added ``handle_status()`` method to the ``ListKeys`` class. - -0.2.0 ------ - -Released: 2009-07-16 - -* Various changes made to support Python 3.0. - -0.1.0 ------ - -Released: 2009-07-04 - -* Initial release. +.. image:: https://travis-ci.org/vsajip/python-gnupg.svg + :target: https://travis-ci.org/vsajip/python-gnupg + +.. image:: https://coveralls.io/repos/vsajip/python-gnupg/badge.svg + :target: https://coveralls.io/github/vsajip/python-gnupg + + +What is it? +=========== + +The GNU Privacy Guard (gpg, or gpg.exe on Windows) is a command-line program +which provides support for programmatic access via spawning a separate process +to run it and then communicating with that process from your program. + +This project, ``python-gnupg``, implements a Python library which takes care +of the internal details and allows its users to generate and manage keys, +encrypt and decrypt data, and sign and verify messages. + +Installation +============ + +Installing from PyPI +-------------------- + +You can install this package from the Python Package Index (pyPI) by running:: + + pip install python-gnupg + + +Installing from a source distribution archive +--------------------------------------------- +To install this package from a source distribution archive, do the following: + +1. Extract all the files in the distribution archive to some directory on your + system. +2. In that directory, run ``python setup.py install``. +3. Optionally, run ``python test_gnupg.py`` to ensure that the package is + working as expected. + +Credits +======= + +* The developers of the GNU Privacy Guard. +* The original version of this module was developed by Andrew Kuchling. +* It was improved by Richard Jones. +* It was further improved by Steve Traugott. + +The present incarnation, based on the earlier versions, uses the ``subprocess`` +module and so works on Windows as well as Unix/Linux platforms. It's not, +however, 100% backwards-compatible with earlier incarnations. + +Change log +========== + +N.B: GCnn refers to an issue nn on Google Code. + +0.4.1 (future) +-------------- + +Released: Not yet. + + +0.4.0 +----- + +Released: 2017-01-29 + +* Added support for ``KEY_CONSIDERED`` in more places - encryption / + decryption, signing, key generation and key import. + +* Partial fix for #32 (GPG 2.1 compatibility). Unfortunately, better + support cannot be provided at this point, unless there are certain + changes (relating to pinentry popups) in how GPG 2.1 works. + +* Fixed #60: An IndexError was being thrown by ``scan_keys()``. + +* Ensured that utf-8 encoding is used when the ``--with-column`` mode is + used. Thanks to Yann Leboulanger for the patch. + +* ``list_keys()`` now uses ``--fixed-list-mode``. Thanks to Werner Koch + for the pointer. + + +0.3.9 +----- + +Released: 2016-09-10 + +* Fixed #38: You can now request information about signatures against + keys. Thanks to SunDwarf for the suggestion and patch, which was used + as a basis for this change. + +* Fixed #49: When exporting keys, no attempt is made to decode the output when + armor=False is specified. + +* Fixed #53: A ``FAILURE`` message caused by passing an incorrect passphrase + is handled. + +* Handled ``EXPORTED`` and ``EXPORT_RES`` messages while exporting keys. Thanks + to Marcel Pörner for the patch. + +* Fixed #54: Improved error message shown when gpg is not available. + +* Fixed #55: Added support for ``KEY_CONSIDERED`` while verifying. + +* Avoided encoding problems with filenames under Windows. Thanks to Kévin + Bernard-Allies for the patch. + +* Fixed #57: Used a better mechanism for comparing keys. + + +0.3.8 +----- + +Released: 2015-09-24 + +* Fixed #22: handled ``PROGRESS`` messages during verification and signing. + +* Fixed #26: handled ``PINENTRY_LAUNCHED`` messages during verification, + decryption and key generation. + +* Fixed #28: Allowed a default Name-Email to be computed even when neither of + ``LOGNAME`` and ``USERNAME`` are in the environment. + +* Fixed #29: Included test files missing from the tarball in previous versions. + +* Fixed #39: On Python 3.x, passing a text instead of a binary stream caused + file decryption to hang due to a ``UnicodeDecodeError``. This has now been + correctly handled: The decryption fails with a "no data" status. + +* Fixed #41: Handled Unicode filenames correctly by encoding them on 2.x using + the file system encoding. + +* Fixed #43: handled ``PINENTRY_LAUNCHED`` messages during key export. Thanks + to Ian Denhardt for looking into this. + +* Hide the console window which appears on Windows when gpg is spawned. + Thanks to Kévin Bernard-Allies for the patch. + +* Subkey fingerprints are now captured. + +* The returned value from the ``list_keys`` method now has a new attribute, + ``key_map``, which is a dictionary mapping key and subkey fingerprints to + the corresponding key's dictionary. With this change, you don't need to + iterate over the (potentially large) returned list to search for a key with + a given fingerprint - the ``key_map`` dict will take you straight to the key + info, whether the fingerprint you have is for a key or a subkey. Thanks to + Nick Daly for the initial suggestion. + +0.3.7 +----- + +Released: 2014-12-07 + +Signed with PGP key: Vinay Sajip (CODE SIGNING KEY) <[email protected]> + +Key Fingerprint : CA74 9061 914E AC13 8E66 EADB 9147 B477 339A 9B86 + +* Added an ``output`` keyword parameter to the ``sign`` and + ``sign_file`` methods, to allow writing the signature to a file. + Thanks to Jannis Leidel for the patch. + +* Allowed specifying ``True`` for the ``sign`` keyword parameter, + which allows use of the default key for signing and avoids having to + specify a key id when it's desired to use the default. Thanks to + Fabian Beutel for the patch. + +* Used a uniform approach with subprocess on Windows and POSIX: shell=True + is not used on either. + +* When signing/verifying, the status is updated to reflect any expired or + revoked keys or signatures. + +* Handled 'NOTATION_NAME' and 'NOTATION_DATA' during verification. + +* Fixed #1, #16, #18, #20: Quoting approach changed, since now shell=False. + +* Fixed #14: Handled 'NEED_PASSPHRASE_PIN' message. + +* Fixed #8: Added a scan_keys method to allow scanning of keys without the + need to import into a keyring. Thanks to Venzen Khaosan for the suggestion. + +* Fixed #5: Added '0x' prefix when searching for keys. Thanks to Aaron Toponce + for the report. + +* Fixed #4: Handled 'PROGRESS' message during encryption. Thanks to Daniel + Mills for the report. + +* Fixed #3: Changed default encoding to Latin-1. + +* Fixed #2: Raised ValueError if no recipients were specified + for an asymmetric encryption request. + +* Handled 'UNEXPECTED' message during verification. Thanks to + David Andersen for the patch. + +* Replaced old range(len(X)) idiom with enumerate(). + +* Refactored ``ListKeys`` / ``SearchKeys`` classes to maximise use of common + functions. + +* Fixed GC94: Added ``export-minimal`` and ``armor`` options when exporting + keys. This addition was inadvertently left out of 0.3.6. + +0.3.6 +----- + +Released: 2014-02-05 + +* Fixed GC82: Enabled fast random tests on gpg as well as gpg2. +* Fixed GC85: Avoided deleting temporary file to preserve its permissions. +* Fixed GC87: Avoided writing passphrase to log. +* Fixed GC95: Added ``verify_data()`` method to allow verification of + signatures in memory. +* Fixed GC96: Regularised end-of-line characters. +* Fixed GC98: Rectified problems with earlier fix for shell injection. + +0.3.5 +----- + +Released: 2013-08-30 + +* Added improved shell quoting to guard against shell injection. +* Fixed GC76: Added ``search_keys()`` and ``send_keys()`` methods. +* Fixed GC77: Allowed specifying a symmetric cipher algorithm. +* Fixed GC78: Fell back to utf-8 encoding when no other could be determined. +* Fixed GC79: Default key length is now 2048 bits. +* Fixed GC80: Removed the Name-Comment default in key generation. + +0.3.4 +----- + +Released: 2013-06-05 + +* Fixed GC65: Fixed encoding exception when getting version. +* Fixed GC66: Now accepts sets and frozensets where appropriate. +* Fixed GC67: Hash algorithm now captured in sign result. +* Fixed GC68: Added support for ``--secret-keyring``. +* Fixed GC70: Added support for multiple keyrings. + +0.3.3 +----- + +Released: 2013-03-11 + +* Fixed GC57: Handled control characters in ``list_keys()``. +* Fixed GC61: Enabled fast random for testing. +* Fixed GC62: Handled ``KEYEXPIRED`` status. +* Fixed GC63: Handled ``NO_SGNR`` status. + +0.3.2 +----- + +Released: 2013-01-17 + +* Fixed GC56: Disallowed blank values in key generation. +* Fixed GC57: Handled colons and other characters in ``list_keys()``. +* Fixed GC59/GC60: Handled ``INV_SGNR`` status during verification and removed + calls requiring interactive password input from doctests. + +0.3.1 +----- + +Released: 2012-09-01 + +* Fixed GC45: Allowed additional arguments to gpg executable. +* Fixed GC50: Used latin-1 encoding in tests when it's known to be required. +* Fixed GC51: Test now returns non-zero exit status on test failure. +* Fixed GC53: Now handles ``INV_SGNR`` and ``KEY_NOT_CREATED`` statuses. +* Fixed GC55: Verification and decryption now return trust level of signer in + integer and text form. + +0.3.0 +----- + +Released: 2012-05-12 + +* Fixed GC49: Reinstated Yann Leboulanger's change to support subkeys + (accidentally left out in 0.2.7). + +0.2.9 +----- + +Released: 2012-03-29 + +* Fixed GC36: Now handles ``CARDCTRL`` and ``POLICY_URL`` messages. +* Fixed GC40: Now handles ``DECRYPTION_INFO``, ``DECRYPTION_FAILED`` and + ``DECRYPTION_OKAY`` messages. +* The ``random_binary_data file`` is no longer shipped, but constructed by the + test suite if needed. + +0.2.8 +----- + +Released: 2011-09-02 + +* Fixed GC29: Now handles ``IMPORT_RES`` while verifying. +* Fixed GC30: Fixed an encoding problem. +* Fixed GC33: Quoted arguments for added safety. + +0.2.7 +----- + +Released: 2011-04-10 + +* Fixed GC24: License is clarified as BSD. +* Fixed GC25: Incorporated Daniel Folkinshteyn's changes. +* Fixed GC26: Incorporated Yann Leboulanger's subkey change. +* Fixed GC27: Incorporated hysterix's support for symmetric encryption. +* Did some internal cleanups of Unicode handling. + +0.2.6 +----- + +Released: 2011-01-25 + +* Fixed GC14: Should be able to accept passphrases from GPG-Agent. +* Fixed GC19: Should be able to create a detached signature. +* Fixed GC21/GC23: Better handling of less common responses from GPG. + +0.2.5 +----- + +Released: 2010-10-13 + +* Fixed GC11/GC16: Detached signatures can now be created. +* Fixed GC3: Detached signatures can be verified. +* Fixed GC12: Better support for RSA and IDEA. +* Fixed GC15/GC17: Better support for non-ASCII input. + +0.2.4 +----- + +Released: 2010-03-01 + +* Fixed GC9: Now allows encryption without armor and the ability to encrypt + and decrypt directly to/from files. + +0.2.3 +----- + +Released: 2010-01-07 + +* Fixed GC7: Made sending data to process threaded and added a test case. + With a test data file used by the test case, the archive size has gone up + to 5MB (the size of the test file). + +0.2.2 +----- + +Released: 2009-10-06 + +* Fixed GC5/GC6: Added ``--batch`` when specifying ``--passphrase-fd`` and + changed the name of the distribution file to add the ``python-`` prefix. + +0.2.1 +----- + +Released: 2009-08-07 + +* Fixed GC2: Added ``handle_status()`` method to the ``ListKeys`` class. + +0.2.0 +----- + +Released: 2009-07-16 + +* Various changes made to support Python 3.0. + +0.1.0 +----- + +Released: 2009-07-04 + +* Initial release. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.3.9/gnupg.py new/python-gnupg-0.4.0/gnupg.py --- old/python-gnupg-0.3.9/gnupg.py 2016-09-10 09:38:35.000000000 +0200 +++ new/python-gnupg-0.4.0/gnupg.py 2017-01-29 18:09:41.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-2016 Vinay Sajip. All rights reserved. +Modifications Copyright (C) 2008-2017 Vinay Sajip. All rights reserved. A unittest harness (test_gnupg.py) has also been added. """ -__version__ = "0.3.9" +__version__ = "0.4.0" __author__ = "Vinay Sajip" -__date__ = "$10-Sep-2016 08:38:35$" +__date__ = "$29-Jan-2017 17:09:41$" try: from io import StringIO @@ -308,7 +308,8 @@ self.status = 'unexpected data' else: # N.B. there might be other reasons - self.status = 'incorrect passphrase' + if not self.status: + self.status = 'incorrect passphrase' else: raise ValueError("Unknown status message: %r" % key) @@ -351,7 +352,7 @@ } def handle_status(self, key, value): - if key == "IMPORTED": + if key in ("IMPORTED", "KEY_CONSIDERED"): # this duplicates info we already see in import_ok & import_problem pass elif key == "NODATA": # pragma: no cover @@ -432,7 +433,10 @@ def get_fields(self, args): result = {} for i, var in enumerate(self.FIELDS): - result[var] = args[i] + if i < len(args): + result[var] = args[i] + else: + result[var] = 'unavailable' result['uids'] = [] result['sigs'] = [] return result @@ -555,7 +559,7 @@ if key in ("ENC_TO", "USERID_HINT", "GOODMDC", "END_DECRYPTION", "BEGIN_SIGNING", "NO_SECKEY", "ERROR", "NODATA", "PROGRESS", "CARDCTRL", "BADMDC", "SC_OP_FAILURE", "SC_OP_SUCCESS", - "PINENTRY_LAUNCHED"): + "PINENTRY_LAUNCHED", "KEY_CONSIDERED"): # in the case of ERROR, this is because a more specific error # message will have come first if key == "NODATA": @@ -605,7 +609,7 @@ def handle_status(self, key, value): if key in ("PROGRESS", "GOOD_PASSPHRASE", "NODATA", "KEY_NOT_CREATED", - "PINENTRY_LAUNCHED"): + "PINENTRY_LAUNCHED", "ERROR", "KEY_CONSIDERED"): pass elif key == "KEY_CREATED": (self.type,self.fingerprint) = value.split() @@ -643,6 +647,8 @@ if key == "DELETE_PROBLEM": # pragma: no cover self.status = self.problem_reason.get(value, "Unknown error: %r" % value) + elif key == "KEY_CONSIDERED": + pass else: # pragma: no cover raise ValueError("Unknown status message: %r" % key) @@ -670,7 +676,7 @@ "GOOD_PASSPHRASE", "BEGIN_SIGNING", "CARDCTRL", "INV_SGNR", "NO_SGNR", "MISSING_PASSPHRASE", "NEED_PASSPHRASE_PIN", "SC_OP_FAILURE", "SC_OP_SUCCESS", "PROGRESS", - "PINENTRY_LAUNCHED"): + "PINENTRY_LAUNCHED", "FAILURE", "ERROR", "KEY_CONSIDERED"): pass elif key in ("KEYEXPIRED", "SIGEXPIRED"): # pragma: no cover self.status = 'key expired' @@ -772,6 +778,10 @@ a passphrase will be sent to GPG, else False. """ cmd = [self.gpgbinary, '--status-fd', '2', '--no-tty'] + cmd.extend(['--debug', 'ipc']) + if passphrase and hasattr(self, 'version'): + if self.version >= (2, 1): + cmd[1:1] = ['--pinentry-mode', 'loopback'] if self.gnupghome: cmd.extend(['--homedir', no_quote(self.gnupghome)]) if self.keyring: @@ -793,19 +803,35 @@ def _open_subprocess(self, args, passphrase=False): # Internal method: open a pipe to a GPG subprocess and return # the file objects for communicating with it. + + # def debug_print(cmd): + # result = [] + # for c in cmd: + # if ' ' not in c: + # result.append(c) + # else: + # if '"' not in c: + # result.append('"%s"' % c) + # elif "'" not in c: + # result.append("'%s'" % c) + # else: + # result.append(c) # give up + # return ' '.join(cmd) + from subprocess import list2cmdline as debug_print + cmd = self.make_args(args, passphrase) if self.verbose: # pragma: no cover - pcmd = ' '.join(cmd) - print(pcmd) - logger.debug("%s", cmd) + print(debug_print(cmd)) if not STARTUPINFO: si = None else: # pragma: no cover si = STARTUPINFO() si.dwFlags = STARTF_USESHOWWINDOW si.wShowWindow = SW_HIDE - return 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) + logger.debug("%s: %s", result.pid, debug_print(cmd)) + return result def _read_response(self, stream, result): # Internal method: reads all the stderr output from GPG, taking notice @@ -951,8 +977,9 @@ def verify(self, data): """Verify the signature on the contents of the string 'data' - >>> gpg = GPG(gnupghome="keys") - >>> input = gpg.gen_key_input(Passphrase='foo') + >>> GPGBINARY = os.environ.get('GPGBINARY', 'gpg') + >>> gpg = GPG(gpgbinary=GPGBINARY, gnupghome="keys") + >>> input = gpg.gen_key_input(passphrase='foo') >>> key = gpg.gen_key(input) >>> assert key >>> sig = gpg.sign('hello',keyid=key.fingerprint,passphrase='bar') @@ -1007,49 +1034,8 @@ # def import_keys(self, key_data): - """ import the key_data into our keyring - - >>> import shutil - >>> shutil.rmtree("keys") - >>> gpg = GPG(gnupghome="keys") - >>> input = gpg.gen_key_input() - >>> result = gpg.gen_key(input) - >>> print1 = result.fingerprint - >>> result = gpg.gen_key(input) - >>> print2 = result.fingerprint - >>> pubkey1 = gpg.export_keys(print1) - >>> seckey1 = gpg.export_keys(print1,secret=True) - >>> seckeys = gpg.list_keys(secret=True) - >>> pubkeys = gpg.list_keys() - >>> assert print1 in seckeys.fingerprints - >>> assert print1 in pubkeys.fingerprints - >>> str(gpg.delete_keys(print1)) - 'Must delete secret key first' - >>> str(gpg.delete_keys(print1,secret=True)) - 'ok' - >>> str(gpg.delete_keys(print1)) - 'ok' - >>> str(gpg.delete_keys("nosuchkey")) - 'No such key' - >>> seckeys = gpg.list_keys(secret=True) - >>> pubkeys = gpg.list_keys() - >>> assert not print1 in seckeys.fingerprints - >>> assert not print1 in pubkeys.fingerprints - >>> result = gpg.import_keys('foo') - >>> assert not result - >>> result = gpg.import_keys(pubkey1) - >>> pubkeys = gpg.list_keys() - >>> seckeys = gpg.list_keys(secret=True) - >>> assert not print1 in seckeys.fingerprints - >>> assert print1 in pubkeys.fingerprints - >>> result = gpg.import_keys(seckey1) - >>> assert result - >>> seckeys = gpg.list_keys(secret=True) - >>> pubkeys = gpg.list_keys() - >>> assert print1 in seckeys.fingerprints - >>> assert print1 in pubkeys.fingerprints - >>> assert print2 in pubkeys.fingerprints - + """ + Import the key_data into our keyring. """ result = self.result_map['import'](self) logger.debug('import_keys: %r', key_data[:256]) @@ -1064,9 +1050,10 @@ >>> import shutil >>> shutil.rmtree("keys") - >>> gpg = GPG(gnupghome="keys") + >>> GPGBINARY = os.environ.get('GPGBINARY', 'gpg') + >>> gpg = GPG(gpgbinary=GPGBINARY, gnupghome="keys") >>> os.chmod('keys', 0x1C0) - >>> result = gpg.recv_keys('keyserver.ubuntu.com', '92905378') + >>> result = gpg.recv_keys('pgp.mit.edu', '92905378') >>> assert result """ @@ -1098,9 +1085,12 @@ data.close() return result - def delete_keys(self, fingerprints, secret=False): + def delete_keys(self, fingerprints, secret=False, passphrase=None): which='key' if secret: # pragma: no cover + if self.version >= (2, 1) and passphrase is None: + raise ValueError('For GnuPG >= 2.1, deleting secret keys ' + 'needs a passphrase to be provided') which='secret-key' if _is_sequence(fingerprints): # pragma: no cover fingerprints = [no_quote(s) for s in fingerprints] @@ -1109,15 +1099,34 @@ args = ['--batch', '--delete-%s' % which] args.extend(fingerprints) result = self.result_map['delete'](self) - p = self._open_subprocess(args) - self._collect_output(p, result, stdin=p.stdin) + if not secret or self.version < (2, 1): + p = self._open_subprocess(args) + self._collect_output(p, result, stdin=p.stdin) + else: + # Need to send in a passphrase. + f = _make_binary_stream('', self.encoding) + try: + self._handle_io(args, f, result, passphrase=passphrase, + binary=True) + finally: + f.close() return result - def export_keys(self, keyids, secret=False, armor=True, minimal=False): - "export the indicated keys. 'keyid' is anything gpg accepts" + def export_keys(self, keyids, secret=False, armor=True, minimal=False, + passphrase=None): + """ + Export the indicated keys. A 'keyid' is anything gpg accepts. + + Since GnuPG 2.1, you can't export secret keys without providing a + passphrase. + """ + which='' if secret: which='-secret-key' + if self.version >= (2, 1) and passphrase is None: + raise ValueError('For GnuPG >= 2.1, exporting secret keys ' + 'needs a passphrase to be provided') if _is_sequence(keyids): keyids = [no_quote(k) for k in keyids] else: @@ -1128,12 +1137,21 @@ if minimal: # pragma: no cover args.extend(['--export-options','export-minimal']) args.extend(keyids) - p = self._open_subprocess(args) # gpg --export produces no status-fd output; stdout will be # empty in case of failure #stdout, stderr = p.communicate() result = self.result_map['export'](self) - self._collect_output(p, result, stdin=p.stdin) + if not secret or self.version < (2, 1): + p = self._open_subprocess(args) + self._collect_output(p, result, stdin=p.stdin) + else: + # Need to send in a passphrase. + f = _make_binary_stream('', self.encoding) + try: + self._handle_io(args, f, result, passphrase=passphrase, + binary=True) + finally: + f.close() logger.debug('export_keys result: %r', result.data) # Issue #49: Return bytes if armor not specified, else text result = result.data @@ -1145,7 +1163,7 @@ # Get the response information result = self.result_map[kind](self) self._collect_output(p, result, stdin=p.stdin) - lines = result.data.decode(self.encoding, + lines = result.data.decode('utf-8', self.decode_errors).splitlines() valid_keywords = 'pub uid sec fpr sub ssb sig'.split() for line in lines: @@ -1167,22 +1185,22 @@ >>> import shutil >>> shutil.rmtree("keys") - >>> gpg = GPG(gnupghome="keys") - >>> input = gpg.gen_key_input() + >>> GPGBINARY = os.environ.get('GPGBINARY', 'gpg') + >>> gpg = GPG(gpgbinary=GPGBINARY, gnupghome="keys") + >>> input = gpg.gen_key_input(passphrase='foo') >>> result = gpg.gen_key(input) - >>> print1 = result.fingerprint + >>> fp1 = result.fingerprint >>> result = gpg.gen_key(input) - >>> print2 = result.fingerprint + >>> fp2 = result.fingerprint >>> pubkeys = gpg.list_keys() - >>> assert print1 in pubkeys.fingerprints - >>> assert print2 in pubkeys.fingerprints + >>> assert fp1 in pubkeys.fingerprints + >>> assert fp2 in pubkeys.fingerprints """ if sigs: which = 'sigs' - else: - which='keys' + else: which='keys' if secret: which='secret-keys' args = ['--list-%s' % which, '--fixed-list-mode', @@ -1203,7 +1221,7 @@ The function achieves this by running: $ gpg --with-fingerprint --with-colons filename """ - args = ['--with-fingerprint', '--with-colons'] + args = ['--with-fingerprint', '--with-colons', '--fixed-list-mode'] args.append(no_quote(filename)) p = self._open_subprocess(args) return self._get_list_output(p, 'scan') @@ -1213,13 +1231,14 @@ >>> import shutil >>> shutil.rmtree('keys') - >>> gpg = GPG(gnupghome='keys') + >>> GPGBINARY = os.environ.get('GPGBINARY', 'gpg') + >>> gpg = GPG(gpgbinary=GPGBINARY, gnupghome='keys') >>> os.chmod('keys', 0x1C0) >>> result = gpg.search_keys('<[email protected]>') >>> assert result, 'Failed using default keyserver' - >>> keyserver = 'keyserver.ubuntu.com' - >>> result = gpg.search_keys('<[email protected]>', keyserver) - >>> assert result, 'Failed using keyserver.ubuntu.com' + >>> #keyserver = 'keyserver.ubuntu.com' + >>> #result = gpg.search_keys('<[email protected]>', keyserver) + >>> #assert result, 'Failed using keyserver.ubuntu.com' """ query = query.strip() @@ -1233,7 +1252,7 @@ # Get the response information result = self.result_map['search'](self) self._collect_output(p, result, stdin=p.stdin) - lines = result.data.decode(self.encoding, + lines = result.data.decode('utf-8', self.decode_errors).splitlines() valid_keywords = ['pub', 'uid'] for line in lines: @@ -1254,8 +1273,9 @@ """Generate a key; you might use gen_key_input() to create the control input. - >>> gpg = GPG(gnupghome="keys") - >>> input = gpg.gen_key_input() + >>> GPGBINARY = os.environ.get('GPGBINARY', 'gpg') + >>> gpg = GPG(gpgbinary=GPGBINARY, gnupghome="keys") + >>> input = gpg.gen_key_input(passphrase='foo') >>> result = gpg.gen_key(input) >>> assert result >>> result = gpg.gen_key('foo') @@ -1358,39 +1378,40 @@ >>> import shutil >>> if os.path.exists("keys"): ... shutil.rmtree("keys") - >>> gpg = GPG(gnupghome="keys") - >>> input = gpg.gen_key_input(passphrase='foo') + >>> GPGBINARY = os.environ.get('GPGBINARY', 'gpg') + >>> gpg = GPG(gpgbinary=GPGBINARY, gnupghome="keys") + >>> input = gpg.gen_key_input(name_email='user1@test', passphrase='pp1') >>> result = gpg.gen_key(input) - >>> print1 = result.fingerprint - >>> input = gpg.gen_key_input() + >>> fp1 = result.fingerprint + >>> input = gpg.gen_key_input(name_email='user2@test', passphrase='pp2') >>> result = gpg.gen_key(input) - >>> print2 = result.fingerprint - >>> result = gpg.encrypt("hello",print2) + >>> fp2 = result.fingerprint + >>> result = gpg.encrypt("hello",fp2) >>> message = str(result) >>> assert message != 'hello' - >>> result = gpg.decrypt(message) + >>> result = gpg.decrypt(message, passphrase='pp2') >>> assert result >>> str(result) 'hello' - >>> result = gpg.encrypt("hello again",print1) + >>> result = gpg.encrypt("hello again", fp1) >>> message = str(result) - >>> result = gpg.decrypt(message,passphrase='bar') + >>> result = gpg.decrypt(message, passphrase='bar') >>> result.status in ('decryption failed', 'bad passphrase') True >>> assert not result - >>> result = gpg.decrypt(message,passphrase='foo') + >>> result = gpg.decrypt(message, passphrase='pp1') >>> result.status == 'decryption ok' True >>> str(result) 'hello again' - >>> result = gpg.encrypt("signed hello",print2,sign=print1,passphrase='foo') + >>> result = gpg.encrypt("signed hello", fp2, sign=fp1, passphrase='pp1') >>> result.status == 'encryption ok' True >>> message = str(result) - >>> result = gpg.decrypt(message) + >>> result = gpg.decrypt(message, passphrase='pp2') >>> result.status == 'decryption ok' True - >>> assert result.fingerprint == print1 + >>> assert result.fingerprint == fp1 """ data = _make_binary_stream(data, self.encoding) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.3.9/setup.py new/python-gnupg-0.4.0/setup.py --- old/python-gnupg-0.3.9/setup.py 2016-09-09 00:28:05.000000000 +0200 +++ new/python-gnupg-0.4.0/setup.py 2017-01-29 19:30:28.000000000 +0100 @@ -7,16 +7,16 @@ 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-2016 by Vinay Sajip. All Rights Reserved. See LICENSE.txt for license.""", + license="""Copyright (C) 2008-2017 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://packages.python.org/python-gnupg/index.html", + url="http://pythonhosted.org/python-gnupg/index.html", py_modules=["gnupg"], platforms="No particular restrictions", - download_url="https://pypi.python.org/packages/source/p/python-gnupg/python-gnupg-%s.tar.gz" % version, + download_url="https://pypi.io/packages/source/p/python-gnupg/python-gnupg-%s.tar.gz" % version, classifiers=[ 'Development Status :: 5 - Production/Stable', "Intended Audience :: Developers", @@ -32,6 +32,7 @@ "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", "Operating System :: OS Independent", "Topic :: Software Development :: Libraries :: Python Modules" ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.3.9/test_gnupg.py new/python-gnupg-0.4.0/test_gnupg.py --- old/python-gnupg-0.3.9/test_gnupg.py 2016-09-10 09:38:57.000000000 +0200 +++ new/python-gnupg-0.4.0/test_gnupg.py 2017-01-29 18:09:59.000000000 +0100 @@ -2,7 +2,7 @@ """ A test harness for gnupg.py. -Copyright (C) 2008-2016 Vinay Sajip. All rights reserved. +Copyright (C) 2008-2017 Vinay Sajip. All rights reserved. """ import doctest import logging @@ -15,10 +15,20 @@ import tempfile import unittest +try: + from unittest import skipIf +except ImportError: + # For now, for Python < 2.7 + def skipIf(condition, message): + if not condition: + return lambda x: x + else: + return lambda x: None + import gnupg __author__ = "Vinay Sajip" -__date__ = "$10-Sep-2016 08:38:57$" +__date__ = "$29-Jan-2017 17:09:59$" ALL_TESTS = True @@ -147,6 +157,20 @@ # ignoring things like spurious blank lines return get_key_data(k1) != get_key_data(k2) +AGENT_CONFIG = b'''allow-loopback-pinentry +log-file socket:///tmp/S.my-gnupg-log +verbose +debug ipc +''' + +def prepare_homedir(hd): + if not os.path.isdir(hd): + os.makedirs(hd) + os.chmod(hd, 0x1C0) + fn = os.path.join(hd, 'gpg-agent.conf') + with open(fn, 'wb') as f: + f.write(AGENT_CONFIG) + class GPGTestCase(unittest.TestCase): def setUp(self): hd = os.path.join(os.getcwd(), 'keys') @@ -154,6 +178,7 @@ self.assertTrue(os.path.isdir(hd), "Not a directory: %s" % hd) shutil.rmtree(hd) + prepare_homedir(hd) self.homedir = hd self.gpg = gpg = gnupg.GPG(gnupghome=hd, gpgbinary=GPGBINARY) v = gpg.version @@ -240,6 +265,8 @@ 'name_comment': 'dummy comment', 'name_email': '[email protected]', } + if self.gpg.version >= (2, 1): + params['passphrase'] = 'foo' cmd = self.gpg.gen_key_input(**params) result = self.gpg.gen_key(cmd) keys = self.gpg.list_keys() @@ -253,10 +280,14 @@ def test_key_generation_with_escapes(self): "Test that key generation handles escape characters" - cmd = self.gpg.gen_key_input(name_comment='Funny chars: ' - '\\r\\n\\f\\v\\0\\b', - name_real='Test Name', - name_email='[email protected]') + params = { + 'name_real': 'Test Name', + 'name_comment': 'Funny chars: \\r\\n\\f\\v\\0\\b', + 'name_email': '[email protected]', + } + if self.gpg.version >= (2, 1): + params['passphrase'] = 'foo' + cmd = self.gpg.gen_key_input(**params) result = self.gpg.gen_key(cmd) keys = self.gpg.list_keys() self.assertEqual(len(keys), 1) @@ -314,12 +345,19 @@ "1-element list expected") self.assertEqual(len(private_keys.fingerprints), 1) # Now do the same test, but using keyring and secret_keyring arguments - pkn = 'pubring.gpg' - skn = 'secring.gpg' + if self.gpg.version < (2, 1): + pkn = 'pubring.gpg' + skn = 'secring.gpg' + else: + # On GnuPG >= 2.1, --secret-keyring is obsolete and ignored, + # and the keyring file name has changed. + pkn = 'pubring.kbx' + skn = None hd = os.path.join(os.getcwd(), 'keys') if os.name == 'posix': pkn = os.path.join(hd, pkn) - skn = os.path.join(hd, skn) + if skn: + skn = os.path.join(hd, skn) gpg = gnupg.GPG(gnupghome=hd, gpgbinary=GPGBINARY, keyring=pkn, secret_keyring=skn) logger.debug('Using keyring and secret_keyring arguments') @@ -377,17 +415,20 @@ def test_scan_keys(self): "Test that external key files can be scanned" - expected = set([ - 'Andrew Able (A test user) <[email protected]>', - 'Barbara Brown (A test user) <[email protected]>', - 'Charlie Clark (A test user) <[email protected]>', - ]) - for fn in ('test_pubring.gpg', 'test_secring.gpg'): - data = self.gpg.scan_keys(fn) - uids = set() - for d in data: - uids.add(d['uids'][0]) - self.assertEqual(uids, expected) + # Don't use SkipTest for now, as not available for Python < 2.7 + if self.gpg.version < (2, 1): + expected = set([ + 'Andrew Able (A test user) <[email protected]>', + 'Barbara Brown (A test user) <[email protected]>', + 'Charlie Clark (A test user) <[email protected]>', + ]) + for fn in ('test_pubring.gpg', 'test_secring.gpg'): + logger.debug('scanning keys in %s', fn) + data = self.gpg.scan_keys(fn) + uids = set() + for d in data: + uids.add(d['uids'][0]) + self.assertEqual(uids, expected) def test_encryption_and_decryption(self): "Test that encryption and decryption works" @@ -455,11 +496,16 @@ self.assertEqual(0, match, "Keys must match") #Generate a key so we can test exporting private keys key = self.do_key_generation() - ascii = gpg.export_keys(key.fingerprint, True) + if self.gpg.version < (2, 1): + passphrase = None + else: + passphrase = 'bbrown' + ascii = gpg.export_keys(key.fingerprint, True, passphrase=passphrase) self.assertTrue(isinstance(ascii, gnupg.text_type)) self.assertTrue(ascii.find("PGP PRIVATE KEY BLOCK") >= 0, "Exported key should be private") - binary = gpg.export_keys(key.fingerprint, True, armor=False) + binary = gpg.export_keys(key.fingerprint, True, armor=False, + passphrase=passphrase) self.assertFalse(isinstance(binary, gnupg.text_type)) logger.debug("test_import_and_export ends") @@ -616,7 +662,7 @@ self.assertEqual(args[-4:], ['--foo', '--bar', 'a', 'b']) def do_file_encryption_and_decryption(self, encfname, decfname): - "Do the actual encryption.decryptin test using given filenames" + "Do the actual encryption/decryption test using given filenames" mode = None if os.name == 'posix': # pick a mode that won't be already in effect via umask @@ -644,6 +690,7 @@ efile.seek(0, 0) # can't use os.SEEK_SET in 2.4 edata = efile.read() efile.close() + self.assertTrue(os.path.exists(decfname)) dfile = open(decfname, 'rb') ddata = dfile.read() dfile.close() @@ -694,6 +741,7 @@ shutil.rmtree(d) logger.debug("test_filename_with_spaces ends") + #@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('<[email protected]>') @@ -743,6 +791,94 @@ self.assertTrue(signed.data) logger.debug("test_signing_with_uid ends") + def test_doctest_import_keys(self): + """ + Because GnuPG 2.1 requires passphrases for exporting and deleting + secret keys, and because console-mode passphrase entry requires + configuration changes, doctests can't always be used. This test + replicates the original doctest for import_keys as a regular test. + + >>> import shutil + >>> shutil.rmtree("keys") + >>> GPGBINARY = os.environ.get('GPGBINARY', 'gpg') + >>> gpg = GPG(gpgbinary=GPGBINARY, gnupghome="keys") + >>> input = gpg.gen_key_input(name_email='user1@test', passphrase='pp1') + >>> result = gpg.gen_key(input) + >>> fp1 = result.fingerprint + >>> result = gpg.gen_key(input) + >>> fp2 = result.fingerprint + >>> pubkey1 = gpg.export_keys(fp1) + >>> seckey1 = gpg.export_keys(fp1, secret=True, passphrase='pp1') + >>> seckeys = gpg.list_keys(secret=True) + >>> pubkeys = gpg.list_keys() + >>> assert fp1 in seckeys.fingerprints + >>> assert fp1 in pubkeys.fingerprints + >>> str(gpg.delete_keys(fp1)) + 'Must delete secret key first' + >>> str(gpg.delete_keys(fp1, secret=True, passphrase='pp1')) + 'ok' + >>> str(gpg.delete_keys(fp1)) + 'ok' + >>> str(gpg.delete_keys("nosuchkey")) + 'No such key' + >>> seckeys = gpg.list_keys(secret=True) + >>> pubkeys = gpg.list_keys() + >>> assert not fp1 in seckeys.fingerprints + >>> assert not fp1 in pubkeys.fingerprints + >>> result = gpg.import_keys('foo') + >>> assert not result + >>> result = gpg.import_keys(pubkey1) + >>> pubkeys = gpg.list_keys() + >>> seckeys = gpg.list_keys(secret=True) + >>> assert not fp1 in seckeys.fingerprints + >>> assert fp1 in pubkeys.fingerprints + >>> result = gpg.import_keys(seckey1) + >>> assert result + >>> seckeys = gpg.list_keys(secret=True) + >>> pubkeys = gpg.list_keys() + >>> assert fp1 in seckeys.fingerprints + >>> assert fp1 in pubkeys.fingerprints + >>> assert fp2 in pubkeys.fingerprints + """ + logger.debug("test_doctest_import_keys begins") + gpg = self.gpg + inp = gpg.gen_key_input(name_email='user1@test', passphrase='pp1') + result = gpg.gen_key(inp) + fp1 = result.fingerprint + inp = gpg.gen_key_input(name_email='user2@test', passphrase='pp2') + result = gpg.gen_key(inp) + fp2 = result.fingerprint + pubkey1 = gpg.export_keys(fp1) + if gpg.version >= (2, 1): + passphrase = 'pp1' + else: + passphrase = None + seckey1 = gpg.export_keys(fp1, secret=True, passphrase=passphrase) + seckeys = gpg.list_keys(secret=True) + pubkeys = gpg.list_keys() + # avoid assertIn, etc. as absent in older Python versions + self.assertTrue(fp1 in seckeys.fingerprints) + self.assertTrue(fp1 in pubkeys.fingerprints) + result = gpg.delete_keys(fp1) + self.assertEqual(str(result), 'Must delete secret key first') + if gpg.version < (2, 1): + # Doesn't work on 2.1, and can't use SkipTest due to having + # to support older Pythons + result = gpg.delete_keys(fp1, secret=True, passphrase=passphrase) + self.assertEqual(str(result), 'ok') + result = gpg.delete_keys(fp1) + self.assertEqual(str(result), 'ok') + result = gpg.delete_keys('nosuchkey') + self.assertEqual(str(result), 'No such key') + seckeys = gpg.list_keys(secret=True) + pubkeys = gpg.list_keys() + self.assertFalse(fp1 in seckeys.fingerprints) + self.assertFalse(fp1 in pubkeys.fingerprints) + result = gpg.import_keys('foo') + self.assertFalse(result) + logger.debug("test_doctest_import_keys ends") + + TEST_GROUPS = { 'sign' : set(['test_signature_verification']), 'crypt' : set(['test_encryption_and_decryption', @@ -756,11 +892,11 @@ 'test_key_generation_with_empty_value', 'test_key_generation_with_colons', 'test_search_keys', 'test_scan_keys']), - 'import' : set(['test_import_only']), + '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_search_keys']), + 'test': set(['test_doctest_import_keys']), } def suite(args=None): @@ -791,7 +927,7 @@ def main(): init_logging() tests = suite() - results = unittest.TextTestRunner(verbosity=2).run(tests) + results = unittest.TextTestRunner(verbosity=1).run(tests) return not results.wasSuccessful()
