Workaround possible upstream GnuPG bug sign fail when passphrase=''.

If passed `--passphrase-fd 0` and piped the passphrase `''` (an empty
string), GnuPG will truncate the message being signed. The truncation
appears to only affect messages which contain newlines; for those
messages, the first line (up unto and including the newline character)
will be truncated and thus missing from the output signed message or
signature, causing verification for signatures on the original
non-truncated message to fail.

 * FIXES #82: https://github.com/isislovecruft/python-gnupg/issues/82
fix/82-passphrase-empty-str
Isis Lovecruft 2015-03-18 19:33:09 +00:00
parent 9be01ec6df
commit 16107bc8a8
No known key found for this signature in database
GPG Key ID: 18C16EC5F9F1D673
2 changed files with 67 additions and 0 deletions

View File

@ -46,6 +46,8 @@ except ImportError:
from . import _parsers from . import _parsers
from . import _util from . import _util
from ._util import b
from ._util import s
from ._parsers import _check_preferences from ._parsers import _check_preferences
from ._parsers import _sanitise_list from ._parsers import _sanitise_list
@ -804,6 +806,19 @@ class GPGBase(object):
## We could use _handle_io here except for the fact that if the ## We could use _handle_io here except for the fact that if the
## passphrase is bad, gpg bails and you can't write the message. ## passphrase is bad, gpg bails and you can't write the message.
result = self._result_map['sign'](self) result = self._result_map['sign'](self)
## If the passphrase is an empty string, the message up to and
## including its first newline will be cut off before making it to the
## GnuPG process. Therefore, if the passphrase='' or passphrase=b'',
## we set passphrase=None. See Issue #82:
## https://github.com/isislovecruft/python-gnupg/issues/82
if _util._is_string(passphrase):
passphrase = passphrase if len(passphrase) > 0 else None
elif _util._is_bytes(passphrase):
passphrase = s(passphrase) if len(passphrase) > 0 else None
else:
passphrase = None
proc = self._open_subprocess(args, passphrase is not None) proc = self._open_subprocess(args, passphrase is not None)
try: try:
if passphrase: if passphrase:

View File

@ -165,6 +165,33 @@ def find_encodings(enc=None, system=False):
return coder return coder
if _py3k:
def b(x):
"""See http://python3porting.com/problems.html#nicer-solutions"""
return x
def s(x):
if isinstance(x, str):
return x
elif isinstance(x, (bytes, bytearray)):
return x.decode(find_encodings().name)
else:
raise NotImplemented
else:
def b(x):
"""See http://python3porting.com/problems.html#nicer-solutions"""
return find_encodings().encode(x)[0]
def s(x):
if isinstance(x, basestring):
return x
elif isinstance(x, (bytes, bytearray)):
return x.decode(find_encodings().name)
else:
raise NotImplemented
def author_info(name, contact=None, public_key=None): def author_info(name, contact=None, public_key=None):
"""Easy object-oriented representation of contributor info. """Easy object-oriented representation of contributor info.
@ -440,6 +467,31 @@ def _is_stream(input):
""" """
return isinstance(input, tuple(_STREAMLIKE_TYPES)) return isinstance(input, tuple(_STREAMLIKE_TYPES))
def _is_string(thing):
"""Check that **thing** is a string. The definition of the latter depends
upon the Python version.
:param thing: The thing to check if it's a string.
:rtype: bool
:returns: ``True`` if **thing** is string (or unicode in Python2).
"""
if (_py3k and isinstance(thing, str)):
return True
if (not _py3k and isinstance(thing, basestring)):
return True
return False
def _is_bytes(thing):
"""Check that **thing** is bytes.
:param thing: The thing to check if it's bytes.
:rtype: bool
:returns: ``True`` if **thing** is bytes or a bytearray.
"""
if isinstance(thing, (bytes, bytearray)):
return True
return False
def _is_list_or_tuple(instance): def _is_list_or_tuple(instance):
"""Check that ``instance`` is a list or tuple. """Check that ``instance`` is a list or tuple.