add GPG 2.1 compability

Issue #32 open
Tim Grocki
created an issue

The changes made in GPG breaks some calls made in python-gnupg. Since GPG 2.1 is used in Archlinux now, python-gnupg breaks in Archlinux.

Problems are:

  • Pinentry is used for inputing a password and console input of passwords is allowed only after a configuration change in GPG
  • A new flag has to be given to GPG in order to input a password via console. An other way exists but breaks other programs like thunderbird.
  • creation of keys via console is only allowed without a passphrase.
  • exporting keys without passphrase is prohibited.
  • New parameters are not supported in the old GPG so some kind of version switch would be needed.

Since Archlinux already adopted GPG 2.1 and other distributions will probably do so too, this is critical for future usefullness of python-gnupg.

I made quick fixes for some Problems but coordination and advise on how to approach the Situation would be great.

Comments (11)

  1. Tim Grocki reporter

    Sorry for the late repy.

    for the fixes to work

    allow-loopback-pinentry
    

    has to be added to the file

    ~/.gnupg/gpg-agent.conf
    

    The changes are (relative to 0.3.6):

    I added some dummy objects, which are used later. You can probably ignore this.

    519a520,538
    > class Change(object):
    >     "Handle status messages for changing a key"
    >     def __init__(self,gpg):
    >         pass
    >   
    >     def handle_status(self, key, value):
    >         pass
    > 
    > class Export(object):
    >     "Handle status messages for changing a key"
    >     def __init__(self,gpg):
    >         self.gpg = gpg
    > 
    >     def __str__(self):
    >         return self.data.decode(self.gpg.encoding, self.gpg.decode_errors)
    >   
    >     def handle_status(self, key, value):
    >         pass
    > 
    611a631,632
    >         'change': Change,
    >         'export': Export,
    

    I added the loopback mode parameter to the make_args function

    683c704
    <         cmd = [self.gpgbinary, '--status-fd', '2', '--no-tty']
    ---
    >         cmd = [self.gpgbinary, '--pinentry-mode', 'loopback', '--status-fd', '2', '--no-tty']
    

    Problem:

    I added a passphrase parameter to the export function and passed it to gpg

    1012,1030c1033,1061
    <     def export_keys(self, keyids, secret=False):
    <         "export the indicated keys. 'keyid' is anything gpg accepts"
    <         which=''
    <         if secret:
    <             which='-secret-key'
    <         if _is_sequence(keyids):
    <             keyids = [shell_quote(k) for k in keyids]
    <         else:
    <             keyids = [shell_quote(keyids)]
    <         args = ['--armor', '--export%s' % which]
    <         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['delete'](self) # any result will do
    <         self._collect_output(p, result, stdin=p.stdin)
    <         logger.debug('export_keys result: %r', result.data)
    <         return result.data.decode(self.encoding, self.decode_errors)
    ---
    >     def export_keys(self, keyids, secret=False, passphrase=None):
    >         if secret == False:
    >             "export the indicated keys. 'keyid' is anything gpg accepts"
    >             which=''
    >             if secret:
    >                 which='-secret-key'
    >             if _is_sequence(keyids):
    >                 keyids = [shell_quote(k) for k in keyids]
    >             else:
    >                 keyids = [shell_quote(keyids)]
    >             args = ['--armor', '--export%s' % which]
    >             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['delete'](self) # any result will do
    >             self._collect_output(p, result, stdin=p.stdin)
    >             logger.debug('export_keys result: %r', result.data)
    >             return result.data.decode(self.encoding, self.decode_errors)
    >         else:
    >             args = ["--pinentry-mode","loopback",'--passphrase-fd','0',"--armor","--export-secret-keys",keyids]
    >             f = _make_binary_stream("", self.encoding)
    >             result2 = self.result_map['export'](self)
    >             self._handle_io(args, f, result2, passphrase=passphrase, binary=True)
    >             f.close()
    > 
    >             return result2
    >   
    

    created key without password first then set passphrase. The whole approch is a bad solution obviosly.

    1118a1150,1151
    >         #Hack:generate Key without pass first, then change Key
    > 
    1130c1163
    <         args = ["--gen-key", "--batch"]
    ---
    >         args = ["--full-gen-key", "--batch"]
    1134a1168,1181
    > 
    >         passphrase = None
    >         for line in input.split("\n"):
    >              if line[:12] == "Passphrase: ":
    >                   passphrase = line[12:]
    >                   break
    >       
    > 
    >         args = ["--pinentry-mode","loopback",'--passphrase-fd','0',"--edit-key",str(result),"passwd"]
    >         f = _make_binary_stream("", self.encoding)
    >         result2 = self.result_map['change'](self)
    >         self._handle_io(args, f, result2, passphrase=passphrase, binary=True)
    >         f.close()
    > 
    1158a1206
    >         out += "%no-protection\n"
    1167a1216
    >         # %no-protection
    1181a1231
    >         # %no-protection
    
  2. HLFH HLFH

    I'm using hash-slinger 2.7 and python2-gnupg master version. I'm using GnuPG 2.1.11.

    I got the issue

    hlfh@arch-server ~]$ openpgpkey --output rfc user@example.com
    Exception in thread Thread-5:
    Traceback (most recent call last):
      File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
        self.run()
      File "/usr/lib/python2.7/threading.py", line 754, in run
        self.__target(*self.__args, **self.__kwargs)
      File "/usr/lib/python2.7/site-packages/gnupg.py", line 825, in _read_response
        result.handle_status(keyword, value)
      File "/usr/lib/python2.7/site-packages/gnupg.py", line 612, in handle_status
        raise ValueError("Unknown status message: %r" % key)
    ValueError: Unknown status message: u'EXPORTED'
    
    Exception in thread Thread-7:
    Traceback (most recent call last):
      File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
        self.run()
      File "/usr/lib/python2.7/threading.py", line 754, in run
        self.__target(*self.__args, **self.__kwargs)
      File "/usr/lib/python2.7/site-packages/gnupg.py", line 825, in _read_response
        result.handle_status(keyword, value)
      File "/usr/lib/python2.7/site-packages/gnupg.py", line 612, in handle_status
        raise ValueError("Unknown status message: %r" % key)
    ValueError: Unknown status message: u'EXPORTED'
    
  3. Vinay Sajip repo owner

    Fixes #32: Improved support for GnuPG 2.1.

    → <<cset 9e90361bfc2f>>

    Note that not all versions of 2.1 are well-behaved. For example, tests fail with the distro-provided gpg 2.1.11 on Ubuntu 16.04, but pass with a gpg 2.1.15 compiled from source on the same machine.

  4. brent

    not even with --yes (and perhaps --batch)?

    • --delete-secret-keys name Remove key from the secret keyring. In batch mode the key must be specified by fingerprint. The option --yes can be used to advice gpg-agent not to request a confirmation. This extra precaution is done because gpg can't be sure that the secret key (as controlled by gpg-agent) is only used for the given OpenPGP public key. *

    in other news, creating a detached signature appears to be impossible with gnupg 2.1.16. the most i've gotten is a file to create, but it's utterly empty. i've tried writing a file containing str() of the .sign_file() output, and i've also tried output='/path/to/file'. neither works.

  5. Vinay Sajip repo owner

    in other news, creating a detached signature appears to be impossible with gnupg 2.1.16

    Is that a regression? Does it work with e.g. 2.1.15 or 1.x?

  6. Log in to comment