From 6eccbe704841cca90573dc554019712528d927a0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 28 Jul 2013 11:28:49 +0000 Subject: [PATCH 1/3] Make default gen_key_input() Key-Type conditional on GnuPG version. * ADD functions _util._match_version_string(), _util._is_gpg2(), and _util._is_gpg1() for determining whether to set the default Key-Type to 'default' (for GnuPG v2.x) or to 'RSA' (for GnuPG v1.x). * ADD class attribute GPG.binary_version, which stores the GnuPG executable's version string, which should be in the form x.x.x, where 'x' is an integer. --- gnupg/_util.py | 34 ++++++++++++++++++++++++++++++++++ gnupg/gnupg.py | 16 +++++++++++----- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/gnupg/_util.py b/gnupg/_util.py index ddf0764..77f847c 100644 --- a/gnupg/_util.py +++ b/gnupg/_util.py @@ -33,6 +33,7 @@ import encodings import os import threading import random +import re import string import sys @@ -295,6 +296,28 @@ def _is_list_or_tuple(instance): """ return isinstance(instance, (list, tuple,)) +def _is_gpg1(version): + """Returns True if using GnuPG version 1.x. + + :param tuple version: A tuple of three integers indication major, minor, + and micro version numbers. + """ + (major, minor, micro) = _match_version_string(version) + if major == 1: + return True + return False + +def _is_gpg2(version): + """Returns True if using GnuPG version 2.x. + + :param tuple version: A tuple of three integers indication major, minor, + and micro version numbers. + """ + (major, minor, micro) = _match_version_string(version) + if major == 2: + return True + return False + def _make_binary_stream(s, encoding): """ xxx fill me in @@ -356,6 +379,17 @@ def _make_random_string(length): chars = string.ascii_lowercase + string.ascii_uppercase + string.digits return ''.join(random.choice(chars) for x in range(length)) +def _match_version_string(version): + """Sort a binary version string into major, minor, and micro integers. + + :param str version: A version string in the form x.x.x + """ + regex = re.compile('(\d)*(\.)*(\d)*(\.)*(\d)*') + matched = regex.match(version) + g = matched.groups() + major, minor, micro = int(g[0]), int(g[2]), int(g[4]) + return (major, minor, micro) + def _next_year(): """Get the date of today plus one year. diff --git a/gnupg/gnupg.py b/gnupg/gnupg.py index efa9b1f..75b5431 100644 --- a/gnupg/gnupg.py +++ b/gnupg/gnupg.py @@ -137,14 +137,20 @@ class GPG(GPGBase): self.temp_keyring = None #: The secring used in the most recently created batch file self.temp_secring = None + #: The version string of our GnuPG binary + self.binary_version = str() - ## check that everything runs alright: + ## check that everything runs alright, and grab the gpg binary's + ## version number while we're at it: proc = self._open_subprocess(["--list-config", "--with-colons"]) result = self._result_map['list'](self) - self._collect_output(proc, result, stdin=proc.stdin) - if proc.returncode != 0: - raise RuntimeError("Error invoking gpg: %s: %s" - % (proc.returncode, result.stderr)) + self._read_data(proc.stdout, result) + if proc.returncode: + raise RuntimeError("Error invoking gpg: %s" % result.data) + + version_line = str(result.data).partition(':version:')[2] + self.binary_version = version_line.split('\n')[0] + log.debug("Using GnuPG version %s" % self.binary_version) def sign(self, data, **kwargs): """Create a signature for a message string or file. From 1e867d6e117f603cd9f2238641a1fb9cb87d4b51 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 28 Jul 2013 11:38:43 +0000 Subject: [PATCH 2/3] If using GnuPG v1.x, use {'Key-Type': 'RSA'} in GPG.gen_key_input(). * CHANGE default key type to RSA for older GnuPG versions, since they seems to not understand using a 'default' key type. * UPDATE docstring for GPG.gen_key_input(). --- gnupg/gnupg.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/gnupg/gnupg.py b/gnupg/gnupg.py index 75b5431..4ec7879 100644 --- a/gnupg/gnupg.py +++ b/gnupg/gnupg.py @@ -609,13 +609,14 @@ class GPG(GPGBase): user ID is created. :param str key_type: One of 'RSA', 'DSA', 'ELG-E', or 'default'. - (default: 'default') Starts a new parameter block by giving the - type of the primary key. The algorithm must be capable of - signing. This is a required parameter. The algorithm may either be - an OpenPGP algorithm number or a string with the algorithm - name. The special value ‘default’ may be used for algo to create - the default key type; in this case a ``key_usage`` should not be - given and 'default' must also be used for ``subkey_type``. + (default: 'RSA', if using GnuPG v1.x, otherwise 'default') Starts + a new parameter block by giving the type of the primary key. The + algorithm must be capable of signing. This is a required + parameter. The algorithm may either be an OpenPGP algorithm number + or a string with the algorithm name. The special value ‘default’ + may be used for algo to create the default key type; in this case + a ``key_usage`` should not be given and 'default' must also be + used for ``subkey_type``. :param int key_length: The requested length of the generated key in bits. (Default: 4096) @@ -700,18 +701,24 @@ class GPG(GPGBase): http://www.gnupg.org/documentation/manuals/gnupg-devel/Unattended-GPG-key-generation.html for more details. """ - parms = {} - #: A boolean for determining whether to set subkey_type to 'default' default_type = False - name_email = kwargs.get('name_email') - uidemail = _util.create_uid_email(name_email) + parms = {} + ## if using GnuPG version 1.x, then set the default 'Key-Type' to + ## 'RSA' because it doesn't understand 'default' parms.setdefault('Key-Type', 'default') + if _util._is_gpg1(self.binary_version): + parms.setdefault('Key-Type', 'RSA') + log.debug("GnuPG v%s detected: setting default key type to %s." + % (self.binary_version, parms['Key-Type'])) parms.setdefault('Key-Length', 4096) parms.setdefault('Name-Real', "Autogenerated Key") parms.setdefault('Expire-Date', _util._next_year()) + + name_email = kwargs.get('name_email') + uidemail = _util.create_uid_email(name_email) parms.setdefault('Name-Email', uidemail) if testing: From bbeba9263ebb2157bd3ebe38872f3756c37100bb Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 28 Jul 2013 20:38:49 +0000 Subject: [PATCH 3/3] Update TODO file with tasks done and further work needed. --- TODO | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/TODO b/TODO index 49c59e4..44e8f30 100644 --- a/TODO +++ b/TODO @@ -19,7 +19,15 @@ see :compatibility:gen__key_input: * Compatibility between GnuPG versions :compatibility: ** TODO GnuPG>=2.1.0 won't allow key generation with preset passphrase -*** TODO in GPG.gen__key_input() :compatibility:gen_key_input: +*** DONE add version detection for GPG.gen_key_input() :compatibility:gen_key_input: +DONE: use gnupg._util._is_gpg2() and gnupg.util._is_gpg1() to check versions + +If we add a GnuPG version detection feature (the version string is already +obtained in GPG.___init___() [[gnupg.py:407]]), then we can automatically chain +GPG.gen__key_input() to another new feature for '--edit-key'. This chaining +would likely need to happen here [[gnupg.py:1146]]. + +*** TODO passphase setting for GnuPGv2.x in GPG.gen__key_input() :compatibility:gen_key_input: In the docstring of GPG.gen__key_input() [[gnupg.py:1068]], for the parameter 'passphrase', it is explained that: @@ -34,16 +42,16 @@ In the docstring of GPG.gen__key_input() [[gnupg.py:1068]], for the parameter passwordless key to be created, which can then have its passphrase set later with '--edit-key'. -If we add a GnuPG version detection feature (the version string is already -obtained in GPG.___init___() [[gnupg.py:407]]), then we can automatically chain -GPG.gen__key_input() to another new feature for '--edit-key'. This chaining -would likely need to happen here [[gnupg.py:1146]]. - *** TODO add '--edit-key' feature :editkey: This would be necessary for adding a passphrase to the key after passwordless generation in GnuPG>=2.1.0. -** TODO GnuPG==1.4.12 doesn't process "Key-Type: default" in batch files +** DONE GnuPG==1.4.12 doesn't process "Key-Type: default" in batch files + +DONE: GnuPG version detection in gen_key_input() was added in commits +6eccbe704841cca90573dc554019712528d927a0 and +1e867d6e117f603cd9f2238641a1fb9cb87d4b51 + (python-gnupg)∃!isisⒶwintermute:~/code/riseup/python-gnupg ∴ ipython WARNING: Attempting to work in a virtualenv. If you encounter problems, please install IPython inside the virtualenv.