Merge branch 'fix/2131-unittests' into develop

testing/mmn/mktime_takes_localtime_not_gmtime
Isis Lovecruft 2013-04-17 00:37:39 +00:00
commit 4706932275
No known key found for this signature in database
GPG Key ID: A3ADB67A2CDB8B35
6 changed files with 366 additions and 203 deletions

View File

@ -11,7 +11,7 @@ cleantest: clean
rm gnupg/tests/*.log rm gnupg/tests/*.log
test: cleantest test: cleantest
python gnupg/tests/test_gnupg.py basic genkey sign python gnupg/tests/test_gnupg.py parsers basic genkey sign
install: install:
python setup.py install --record installed-files.txt python setup.py install --record installed-files.txt

View File

@ -137,26 +137,25 @@ def _copy_data(instream, outstream):
if len(data) == 0: if len(data) == 0:
break break
sent += len(data) sent += len(data)
logger.debug("sending chunk (%d): %r", sent, data[:256]) logger.debug("_copy_data(): sending chunk (%d):\n%s" % (sent, data[:256]))
try: try:
outstream.write(data) outstream.write(data)
except UnicodeError: except UnicodeError:
try: try:
outstream.write(data.encode(enc)) outstream.write(data.encode(enc))
except IOError: except IOError:
logger.exception('Error sending data: Broken pipe') logger.exception('_copy_data(): Error sending data: Broken pipe')
break break
except IOError: except IOError:
# Can sometimes get 'broken pipe' errors even when the # Can get 'broken pipe' errors even when all data was sent
# data has all been sent logger.exception('_copy_data(): Error sending data: Broken pipe')
logger.exception('Error sending data: Broken pipe')
break break
try: try:
outstream.close() outstream.close()
except IOError: except IOError:
logger.exception('Got IOError while trying to close FD outstream') logger.exception('_copy_data(): Got IOError while closing %s' % outstream)
else: else:
logger.debug("closed output, %d bytes sent", sent) logger.debug("_copy_data(): Closed output, %d bytes sent." % sent)
def _threaded_copy_data(instream, outstream): def _threaded_copy_data(instream, outstream):
"""Copy data from one stream to another in a separate thread. """Copy data from one stream to another in a separate thread.
@ -413,10 +412,6 @@ class GPG(object):
if isinstance(message, file): if isinstance(message, file):
result = self._sign_file(message, **kwargs) result = self._sign_file(message, **kwargs)
elif not util._is_stream(message): elif not util._is_stream(message):
if isinstance(message, str):
if not util._py3k:
message = unicode(message, self.encoding)
message = message.encode(self.encoding)
f = util._make_binary_stream(message, self.encoding) f = util._make_binary_stream(message, self.encoding)
result = self._sign_file(f, **kwargs) result = self._sign_file(f, **kwargs)
f.close() f.close()
@ -429,7 +424,7 @@ class GPG(object):
def _sign_file(self, file, keyid=None, passphrase=None, clearsign=True, def _sign_file(self, file, keyid=None, passphrase=None, clearsign=True,
detach=False, binary=False): detach=False, binary=False):
"""Create a signature for a file.""" """Create a signature for a file."""
logger.debug("GPG._sign_file(): %s", file) logger.debug("_sign_file(): %s", file)
if binary: if binary:
args = ['--sign'] args = ['--sign']
else: else:
@ -438,10 +433,8 @@ class GPG(object):
if clearsign: if clearsign:
args.append("--clearsign") args.append("--clearsign")
if detach: if detach:
logger.warn( logger.warn("Cannot use both --clearsign and --detach-sign.")
"Cannot use --clearsign and --detach-sign simultaneously.") logger.warn("Using default GPG behaviour: --clearsign only.")
logger.warn(
"Using default GPG behaviour: --clearsign only.")
elif detach and not clearsign: elif detach and not clearsign:
args.append("--detach-sign") args.append("--detach-sign")
@ -449,8 +442,8 @@ class GPG(object):
args.append(str("--default-key %s" % keyid)) args.append(str("--default-key %s" % keyid))
result = self._result_map['sign'](self) result = self._result_map['sign'](self)
#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.
p = self._open_subprocess(args, passphrase is not None) p = self._open_subprocess(args, passphrase is not None)
try: try:
stdin = p.stdin stdin = p.stdin
@ -458,7 +451,7 @@ class GPG(object):
_write_passphrase(stdin, passphrase, self.encoding) _write_passphrase(stdin, passphrase, self.encoding)
writer = _threaded_copy_data(file, stdin) writer = _threaded_copy_data(file, stdin)
except IOError: except IOError:
logging.exception("error writing message") logger.exception("_sign_file(): Error writing message")
writer = None writer = None
self._collect_output(p, result, writer, stdin) self._collect_output(p, result, writer, stdin)
return result return result
@ -483,50 +476,43 @@ class GPG(object):
f.close() f.close()
return result return result
def verify_file(self, file, data_filename=None): def verify_file(self, file, sig_file=None):
""" """Verify the signature on the contents of a file or file-like
Verify the signature on the contents of a file or file-like
object. Can handle embedded signatures as well as detached object. Can handle embedded signatures as well as detached
signatures. If using detached signatures, the file containing the signatures. If using detached signatures, the file containing the
detached signature should be specified as the :param:data_filename. detached signature should be specified as the ``sig_file``.
:param file file: A file descriptor object. Its type will be checked :param file file: A file descriptor object. Its type will be checked
with :func:util._is_file. with :func:`util._is_file`.
:param file data_filename: A file containing the GPG signature data for :param str sig_file: A file containing the GPG signature data for
:param:file. If given, :param:file is ``file``. If given, ``file`` is verified via this
verified via this detached signature. detached signature.
""" """
## attempt to wrap any escape characters in quotes:
safe_file = _fix_unsafe(file)
## check that :param:file is actually a file: fn = None
util._is_file(safe_file)
logger.debug('verify_file: %r, %r', safe_file, data_filename)
result = self._result_map['verify'](self) result = self._result_map['verify'](self)
args = ['--verify']
if data_filename is None: if sig_file is None:
self._handle_io(args, safe_file, result, binary=True) logger.debug("verify_file(): Handling embedded signature")
args = ["--verify"]
proc = self._open_subprocess(args)
writer = _threaded_copy_data(file, proc.stdin)
self._collect_output(proc, result, writer, stdin=proc.stdin)
else: else:
safe_data_filename = _fix_unsafe(data_filename) if not util._is_file(sig_file):
logger.debug("verify_file(): '%r' is not a file" % sig_file)
logger.debug('Handling detached verification') return result
fd, fn = tempfile.mkstemp(prefix='pygpg') logger.debug('verify_file(): Handling detached verification')
sig_fh = None
with open(safe_file) as sf: try:
contents = sf.read() sig_fh = open(sig_file)
os.write(fd, s) args = ["--verify %s - " % sig_fh.name]
os.close(fd) proc = self._open_subprocess(args)
logger.debug('Wrote to temp file: %r', contents) writer = _threaded_copy_data(file, proc.stdin)
args.append(fn) self._collect_output(proc, result, stdin=proc.stdin)
args.append('"%s"' % safe_data_filename) finally:
if sig_fh and not sig_fh.closed:
try: sig_fh.close()
p = self._open_subprocess(args)
self._collect_output(p, result, stdin=p.stdin)
finally:
os.unlink(fn)
return result return result
# #
@ -659,13 +645,12 @@ class GPG(object):
>>> pubkeys = gpg.list_keys() >>> pubkeys = gpg.list_keys()
>>> assert print1 in pubkeys.fingerprints >>> assert print1 in pubkeys.fingerprints
>>> assert print2 in pubkeys.fingerprints >>> assert print2 in pubkeys.fingerprints
""" """
which='public-keys' which='public-keys'
if secret: if secret:
which='secret-keys' which='secret-keys'
args = "--list-%s --fixed-list-mode --fingerprint --with-colons" % (which,) args = "--list-%s --fixed-list-mode --fingerprint --with-colons --no-show-photos" % (which,)
args = [args] args = [args]
p = self._open_subprocess(args) p = self._open_subprocess(args)
@ -692,6 +677,17 @@ class GPG(object):
getattr(result, keyword)(L) getattr(result, keyword)(L)
return result return result
def list_sigs(self, *keyids):
"""xxx implement me
The GnuPG option '--show-photos', according to the GnuPG manual, "does
not work with --with-colons", but since we can't rely on all versions
of GnuPG to explicitly handle this correctly, we should probably
include it in the args.
"""
## we will want to include "--no-show-photos" in the args
raise NotImplemented("Functionality for '--list-sigs' not implemented.")
def gen_key(self, input): def gen_key(self, input):
""" """
Generate a key; you might use gen_key_input() to create the control Generate a key; you might use gen_key_input() to create the control
@ -871,8 +867,7 @@ class GPG(object):
return result return result
def decrypt(self, message, **kwargs): def decrypt(self, message, **kwargs):
""" """Decrypt the contents of a string or file-like object ``message``.
Decrypt the contents of a string or file-like object :param:message .
:param message: A string or file-like object to decrypt. :param message: A string or file-like object to decrypt.
""" """
@ -967,9 +962,7 @@ class GPGWrapper(GPG):
passphrase=passphrase) passphrase=passphrase)
def send_keys(self, keyserver, *keyids): def send_keys(self, keyserver, *keyids):
""" """Send keys to a keyserver."""
Send keys to a keyserver
"""
result = self._result_map['list'](self) result = self._result_map['list'](self)
gnupg.logger.debug('send_keys: %r', keyids) gnupg.logger.debug('send_keys: %r', keyids)
data = gnupg.util._make_binary_stream("", self.encoding) data = gnupg.util._make_binary_stream("", self.encoding)

View File

@ -34,6 +34,7 @@ import util
ESCAPE_PATTERN = re.compile(r'\\x([0-9a-f][0-9a-f])', re.I) ESCAPE_PATTERN = re.compile(r'\\x([0-9a-f][0-9a-f])', re.I)
HEXIDECIMAL = re.compile('([0-9A-F]{2})+')
class ProtectedOption(Exception): class ProtectedOption(Exception):
@ -268,16 +269,13 @@ def _is_allowed(input):
## assertion will check that GPG will recognise them ## assertion will check that GPG will recognise them
## ##
## xxx checkout the --store option for creating rfc1991 data packets ## xxx checkout the --store option for creating rfc1991 data packets
## xxx also --multifile use with verify encrypt & decrypt
## xxx key fetching/retrieving options: [fetch_keys, merge_only, recv_keys] ## xxx key fetching/retrieving options: [fetch_keys, merge_only, recv_keys]
## ##
## xxx which ones do we want as defaults?
## eg, --no-show-photos would mitigate things like
## https://www-01.ibm.com/support/docview.wss?uid=swg21620982
_allowed = frozenset( _allowed = frozenset(
['--list-keys', '--list-key', '--fixed-list-mode', ['--list-keys', '--list-key', '--fixed-list-mode',
'--list-secret-keys', '--list-public-keys', '--list-secret-keys', '--list-public-keys',
'--list-packets', '--with-colons', '--list-packets', '--with-colons',
'--no-show-photos',
'--delete-keys', '--delete-secret-keys', '--delete-keys', '--delete-secret-keys',
'--encrypt', '--encrypt-files', '--encrypt', '--encrypt-files',
'--decrypt', '--decrypt-files', '--decrypt', '--decrypt-files',
@ -327,6 +325,17 @@ def _is_allowed(input):
return input return input
return None return None
def _is_hex(string):
"""Check that a string is hexidecimal, with alphabetic characters
capitalized and without whitespace.
:param str string: The string to check.
"""
matched = HEXIDECIMAL.match(string)
if matched is not None and len(matched.group()) >= 2:
return True
return False
def _sanitise(*args): def _sanitise(*args):
"""Take an arg or the key portion of a kwarg and check that it is in the """Take an arg or the key portion of a kwarg and check that it is in the
set of allowed GPG options and flags, and that it has the correct set of allowed GPG options and flags, and that it has the correct
@ -388,65 +397,93 @@ def _sanitise(*args):
if flag in ['--encrypt', '--encrypt-files', '--decrypt', if flag in ['--encrypt', '--encrypt-files', '--decrypt',
'--decrypt-file', '--import', '--verify']: '--decrypt-file', '--import', '--verify']:
## Place checks here: ## Place checks here:
if _is_file(val): if util._is_file(val):
safe_option += (val + " ") safe_option += (val + " ")
else: else:
logger.debug("_check_option(): %s not file: %s" logger.debug("_check_option(): %s not file: %s"
% (flag, val)) % (flag, val))
elif flag in ['--default-key']:
if _is_hex(val):
safe_option += (val + " ")
else:
logger.debug("_check_option(): '%s %s' not hex."
% (flag, val))
else: else:
safe_option += (val + " ") safe_option += (val + " ")
logger.debug("_check_option(): No checks for %s" logger.debug("_check_option(): No checks for %s"
% val) % val)
return safe_option return safe_option
is_flag = lambda x: x.startswith('-') is_flag = lambda x: x.startswith('--')
checked = []
def _make_filo(args_string):
filo = arg.split(' ')
filo.reverse()
logger.debug("_make_filo(): Converted to reverse list: %s" % filo)
return filo
def _make_groups(filo):
groups = {}
while len(filo) >= 1:
last = filo.pop()
if is_flag(last):
logger.debug("_make_groups(): Got arg: %s" % last)
if last == '--verify':
groups[last] = str(filo.pop())
## accept the read-from-stdin arg:
if len(filo) >= 1 and filo[len(filo)-1] == '-':
groups[last] += str(' - \'\'') ## gross hack
else:
groups[last] = str()
while len(filo) > 1 and not is_flag(filo[len(filo)-1]):
logger.debug("_make_groups(): Got value: %s"
% filo[len(filo)-1])
groups[last] += (filo.pop() + " ")
else:
if len(filo) == 1 and not is_flag(filo[0]):
logger.debug("_make_groups(): Got value: %s" % filo[0])
groups[last] += filo.pop()
else:
logger.debug("_make_groups(): Got solitary value: %s" % last)
groups["xxx"] = last
return groups
def _check_groups(groups):
logger.debug("_check_groups(): Got groups: %s" % groups)
checked_groups = []
for a,v in groups.items():
v = None if len(v) == 0 else v
safe = _check_option(a, v)
if safe is not None and not safe.strip() == "":
logger.debug("_check_groups(): appending option: %s" % safe)
checked_groups.append(safe)
else:
logger.debug("_check_groups(): dropped option '%s %s'" % (a,v))
return checked_groups
if args is not None: if args is not None:
option_groups = {}
for arg in args: for arg in args:
## if we're given a string with a bunch of options in it split them
## up and deal with them separately
if isinstance(arg, str): if isinstance(arg, str):
logger.debug("_sanitise(): Got arg string: %s" % arg) logger.debug("_sanitise(): Got arg string: %s" % arg)
## if we're given a string with a bunch of options in it split
## them up and deal with them separately
if arg.find(' ') > 0: if arg.find(' ') > 0:
filo = arg.split() filo = _make_filo(arg)
filo.reverse() option_groups.update(_make_groups(filo))
new_arg, new_value = str(), str()
while len(filo) > 0:
if not is_flag(filo[0]):
logger.debug("_sanitise(): Got non-flag arg %s"
% filo[0])
new_value += (filo.pop() + " ")
else:
logger.debug("_sanitise(): Got arg: %s" % filo[0])
new_arg = filo.pop()
if len(filo) > 0:
while not is_flag(filo[0]):
logger.debug("_sanitise(): Got value: %s"
% filo[0])
new_value += (filo.pop() + " ")
safe = _check_option(new_arg, new_value)
if safe is not None and not safe.strip() == "":
logger.debug("_sanitise(): appending option: %s"
% safe)
checked.append(safe)
else: else:
safe = _check_option(arg, None) option_groups.update({ arg: "" })
if safe is not None:
logger.debug("_sanitise(): appending args: %s" % safe)
checked.append(safe)
else:
logger.debug("_sanitise(): got None for safe")
elif isinstance(arg, list): elif isinstance(arg, list):
logger.debug("_sanitise(): Got arg list: %s" % arg) logger.debug("_sanitise(): Got arg list: %s" % arg)
allow = _one_flag(arg) arg.reverse()
if allow is not None: option_groups.update(_make_groups(arg))
checked.append(allow)
else: else:
logger.debug("_sanitise(): got non string or list arg: %s" logger.debug("_sanitise(): Got non str or list arg: %s" % arg)
% arg) checked = _check_groups(option_groups)
sanitised = ' '.join(x for x in checked)
sanitised = ' '.join(x for x in checked) return sanitised
return sanitised else:
logger.debug("_sanitise(): Got None for args")
def _sanitise_list(arg_list): def _sanitise_list(arg_list):
"""A generator for iterating through a list of gpg options and sanitising """A generator for iterating through a list of gpg options and sanitising
@ -474,30 +511,48 @@ class Verify(object):
TRUST_FULLY = 3 TRUST_FULLY = 3
TRUST_ULTIMATE = 4 TRUST_ULTIMATE = 4
TRUST_LEVELS = { TRUST_LEVELS = { "TRUST_UNDEFINED" : TRUST_UNDEFINED,
"TRUST_UNDEFINED" : TRUST_UNDEFINED, "TRUST_NEVER" : TRUST_NEVER,
"TRUST_NEVER" : TRUST_NEVER, "TRUST_MARGINAL" : TRUST_MARGINAL,
"TRUST_MARGINAL" : TRUST_MARGINAL, "TRUST_FULLY" : TRUST_FULLY,
"TRUST_FULLY" : TRUST_FULLY, "TRUST_ULTIMATE" : TRUST_ULTIMATE, }
"TRUST_ULTIMATE" : TRUST_ULTIMATE,
} #: True if the signature is valid, False otherwise.
valid = False
#: A string describing the status of the signature verification.
#: Can be one of ``'signature bad'``, ``'signature good'``,
#: ``'signature valid'``, ``'signature error'``, ``'decryption failed'``,
#: ``'no public key'``, ``'key exp'``, or ``'key rev'``.
status = None
#: The fingerprint of the signing keyid.
fingerprint = None
#: The fingerprint of the corresponding public key, which may be different
#: if the signature was created with a subkey.
pubkey_fingerprint = None
#: The keyid of the signing key.
key_id = None
#: xxx I'm not sure how this is different to key_id.
signature_id = None
#: The creation date of the signing key.
creation_date = None
#: The timestamp of the purported signature, if we are unable to parse it.
timestamp = None
#: The userid of the signing key which was used to create the signature.
username = None
#: When the signing key is due to expire.
expire_timestamp = None
#: The timestamp for when the signature was created.
sig_timestamp = None
#: A number 0-4 describing the trust level of the signature.
trust_level = None
#: The string corresponding to the ``trust_level`` number.
trust_text = None
def __init__(self, gpg): def __init__(self, gpg):
self.gpg = gpg self.gpg = gpg
self.valid = False
self.fingerprint = self.creation_date = self.timestamp = None
self.signature_id = self.key_id = None
self.username = None
self.status = None
self.pubkey_fingerprint = None
self.expire_timestamp = None
self.sig_timestamp = None
self.trust_text = None
self.trust_level = None
def __nonzero__(self): def __nonzero__(self):
return self.valid return self.valid
__bool__ = __nonzero__ __bool__ = __nonzero__
def handle_status(self, key, value): def handle_status(self, key, value):
@ -521,7 +576,6 @@ class Verify(object):
self.creation_date, self.creation_date,
self.sig_timestamp, self.sig_timestamp,
self.expire_timestamp) = value.split()[:4] self.expire_timestamp) = value.split()[:4]
# may be different if signature is made with a subkey
self.pubkey_fingerprint = value.split()[-1] self.pubkey_fingerprint = value.split()[-1]
self.status = 'signature valid' self.status = 'signature valid'
elif key == "SIG_ID": elif key == "SIG_ID":
@ -574,7 +628,7 @@ class Crypt(Verify):
__bool__ = __nonzero__ __bool__ = __nonzero__
def __str__(self): def __str__(self):
return self.data.decode(self.gpg.encoding, self.gpg.decode_errors) return self.data.decode(self.gpg.encoding, self.gpg._decode_errors)
def handle_status(self, key, value): def handle_status(self, key, value):
"""Parse a status code from the attached GnuPG process. """Parse a status code from the attached GnuPG process.
@ -676,19 +730,14 @@ class Sign(object):
#: The type of signature created. #: The type of signature created.
sig_type = None sig_type = None
#: The algorithm used to create the signature. #: The algorithm used to create the signature.
sig_algo = None sig_algo = None
#: The hash algorithm used to create the signature. #: The hash algorithm used to create the signature.
sig_hash_also = None sig_hash_also = None
#: The fingerprint of the signing keyid. #: The fingerprint of the signing keyid.
fingerprint = None fingerprint = None
#: The timestamp on the signature. #: The timestamp on the signature.
timestamp = None timestamp = None
#: xxx fill me in #: xxx fill me in
what = None what = None
@ -705,7 +754,7 @@ class Sign(object):
__bool__ = __nonzero__ __bool__ = __nonzero__
def __str__(self): def __str__(self):
return self.data.decode(self.gpg.encoding, self.gpg.decode_errors) return self.data.decode(self.gpg.encoding, self.gpg._decode_errors)
def handle_status(self, key, value): def handle_status(self, key, value):
"""Parse a status code from the attached GnuPG process. """Parse a status code from the attached GnuPG process.

View File

@ -0,0 +1,87 @@
Privacy is necessary for an open society in the electronic age. Privacy is not
secrecy. A private matter is something one doesn't want the whole world to
know, but a secret matter is something one doesn't want anybody to
know. Privacy is the power to selectively reveal oneself to the world.
If two parties have some sort of dealings, then each has a memory of their
interaction. Each party can speak about their own memory of this; how could
anyone prevent it? One could pass laws against it, but the freedom of speech,
even more than privacy, is fundamental to an open society; we seek not to
restrict any speech at all. If many parties speak together in the same forum,
each can speak to all the others and aggregate together knowledge about
individuals and other parties. The power of electronic communications has
enabled such group speech, and it will not go away merely because we might want
it to.
Since we desire privacy, we must ensure that each party to a transaction have
knowledge only of that which is directly necessary for that transaction. Since
any information can be spoken of, we must ensure that we reveal as little as
possible. In most cases personal identity is not salient. When I purchase a
magazine at a store and hand cash to the clerk, there is no need to know who I
am. When I ask my electronic mail provider to send and receive messages, my
provider need not know to whom I am speaking or what I am saying or what others
are saying to me; my provider only need know how to get the message there and
how much I owe them in fees. When my identity is revealed by the underlying
mechanism of the transaction, I have no privacy. I cannot here selectively
reveal myself; I must always reveal myself.
Therefore, privacy in an open society requires anonymous transaction
systems. Until now, cash has been the primary such system. An anonymous
transaction system is not a secret transaction system. An anonymous system
empowers individuals to reveal their identity when desired and only when
desired; this is the essence of privacy.
Privacy in an open society also requires cryptography. If I say something, I
want it heard only by those for whom I intend it. If the content of my speech
is available to the world, I have no privacy. To encrypt is to indicate the
desire for privacy, and to encrypt with weak cryptography is to indicate not
too much desire for privacy. Furthermore, to reveal one's identity with
assurance when the default is anonymity requires the cryptographic signature.
We cannot expect governments, corporations, or other large, faceless
organizations to grant us privacy out of their beneficence. It is to their
advantage to speak of us, and we should expect that they will speak. To try to
prevent their speech is to fight against the realities of
information. Information does not just want to be free, it longs to be
free. Information expands to fill the available storage space. Information is
Rumor's younger, stronger cousin; Information is fleeter of foot, has more
eyes, knows more, and understands less than Rumor.
We must defend our own privacy if we expect to have any. We must come together
and create systems which allow anonymous transactions to take place. People
have been defending their own privacy for centuries with whispers, darkness,
envelopes, closed doors, secret handshakes, and couriers. The technologies of
the past did not allow for strong privacy, but electronic technologies do.
We the Cypherpunks are dedicated to building anonymous systems. We are
defending our privacy with cryptography, with anonymous mail forwarding
systems, with digital signatures, and with electronic money.
Cypherpunks write code. We know that someone has to write software to defend
privacy, and since we can't get privacy unless we all do, we're going to write
it. We publish our code so that our fellow Cypherpunks may practice and play
with it. Our code is free for all to use, worldwide. We don't much care if you
don't approve of the software we write. We know that software can't be
destroyed and that a widely dispersed system can't be shut down.
Cypherpunks deplore regulations on cryptography, for encryption is
fundamentally a private act. The act of encryption, in fact, removes
information from the public realm. Even laws against cryptography reach only so
far as a nation's border and the arm of its violence. Cryptography will
ineluctably spread over the whole globe, and with it the anonymous transactions
systems that it makes possible.
For privacy to be widespread it must be part of a social contract. People must
come and together deploy these systems for the common good. Privacy only
extends so far as the cooperation of one's fellows in society. We the
Cypherpunks seek your questions and your concerns and hope we may engage you so
that we do not deceive ourselves. We will not, however, be moved out of our
course because some may disagree with our goals.
The Cypherpunks are actively engaged in making the networks safer for
privacy. Let us proceed together apace.
Onward.
Eric Hughes <hughes@soda.berkeley.edu>
9 March 1993

View File

@ -17,6 +17,7 @@ import os
import shutil import shutil
import sys import sys
import tempfile import tempfile
import time
## Use unittest2 if we're on Python2.6 or less: ## Use unittest2 if we're on Python2.6 or less:
if sys.version_info.major == 2 and sys.version_info.minor <= 6: if sys.version_info.major == 2 and sys.version_info.minor <= 6:
@ -45,6 +46,8 @@ def _make_tempfile(*args, **kwargs):
*args, **kwargs) *args, **kwargs)
logger = logging.getLogger('gnupg') logger = logging.getLogger('gnupg')
_here = os.path.join(os.path.join(util._repo, 'gnupg'), 'tests')
_files = os.path.join(_here, 'files')
KEYS_TO_IMPORT = """-----BEGIN PGP PUBLIC KEY BLOCK----- KEYS_TO_IMPORT = """-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.9 (MingW32) Version: GnuPG v1.4.9 (MingW32)
@ -146,6 +149,26 @@ class GPGTestCase(unittest.TestCase):
self.pubring = os.path.join(self.homedir, 'pubring.gpg') self.pubring = os.path.join(self.homedir, 'pubring.gpg')
self.secring = os.path.join(self.homedir, 'secring.gpg') self.secring = os.path.join(self.homedir, 'secring.gpg')
def test_parsers_fix_unsafe(self):
"""Test that unsafe inputs are quoted out and then ignored."""
shell_input = "\"&coproc /bin/sh\""
fixed = parsers._fix_unsafe(shell_input)
print fixed
test_file = os.path.join(_files, 'cypherpunk_manifesto')
self.assertTrue(os.path.isfile(test_file))
has_shell = self.gpg.verify_file(test_file, fixed)
self.assertFalse(has_shell.valid)
def test_parsers_is_hex_valid(self):
"""Test that valid hexidecimal passes the parsers._is_hex() check"""
valid_hex = '0A6A58A14B5946ABDE18E207A3ADB67A2CDB8B35'
self.assertTrue(parsers._is_hex(valid_hex))
def test_parsers_is_hex_invalid(self):
"""Test that invalid hexidecimal fails the parsers._is_hex() check"""
invalid_hex = 'cipherpunks write code'
self.assertFalse(parsers._is_hex(invalid_hex))
def test_gpghome_creation(self): def test_gpghome_creation(self):
"""Test the environment by ensuring that setup worked.""" """Test the environment by ensuring that setup worked."""
hd = self.homedir hd = self.homedir
@ -271,51 +294,39 @@ class GPGTestCase(unittest.TestCase):
self.assertGreater(key_input.find('Francisco Ferrer'), 0) self.assertGreater(key_input.find('Francisco Ferrer'), 0)
def test_rsa_key_generation(self): def test_rsa_key_generation(self):
""" """Test that RSA key generation succeeds."""
Test that RSA key generation succeeds. key = self.generate_key("Ralph Merkle", "xerox.com")
"""
key = self.generate_key("Barbara Brown", "beta.com")
self.assertIsNotNone(key.type) self.assertIsNotNone(key.type)
self.assertIsNotNone(key.fingerprint) self.assertIsNotNone(key.fingerprint)
def test_rsa_key_generation_with_unicode(self): def test_rsa_key_generation_with_unicode(self):
""" """Test that RSA key generation succeeds with unicode characters."""
Test that RSA key generation succeeds with unicode characters.
"""
key = self.generate_key("Anaïs de Flavigny", "êtrerien.fr") key = self.generate_key("Anaïs de Flavigny", "êtrerien.fr")
self.assertIsNotNone(key.type) self.assertIsNotNone(key.type)
self.assertIsNotNone(key.fingerprint) self.assertIsNotNone(key.fingerprint)
def test_rsa_key_generation_with_subkey(self): def test_rsa_key_generation_with_subkey(self):
""" """Test that RSA key generation succeeds with additional subkey."""
Test that RSA key generation succeeds with additional subkey. key = self.generate_key("John Gilmore", "isapu.nk",
"""
key = self.generate_key("Need Caffeine", "nowplea.se",
subkey_type='RSA') subkey_type='RSA')
self.assertIsNotNone(key.type) self.assertIsNotNone(key.type)
self.assertIsNotNone(key.fingerprint) self.assertIsNotNone(key.fingerprint)
def test_dsa_key_generation(self): def test_dsa_key_generation(self):
""" """Test that DSA key generation succeeds."""
Test that DSA key generation succeeds. key = self.generate_key("Ross Anderson", "bearli.on")
"""
key = self.generate_key("DSA Signonly", "test.com")
self.assertIsNotNone(key.type) self.assertIsNotNone(key.type)
self.assertIsNotNone(key.fingerprint) self.assertIsNotNone(key.fingerprint)
def test_dsa_key_generation_with_unicode(self): def test_dsa_key_generation_with_unicode(self):
""" """Test that DSA key generation succeeds with unicode characters."""
Test that DSA key generation succeeds with unicode characters.
"""
key = self.generate_key("破壊合計する", "破壊合計する.日本") key = self.generate_key("破壊合計する", "破壊合計する.日本")
self.assertIsNotNone(key.type) self.assertIsNotNone(key.type)
self.assertIsNotNone(key.fingerprint) self.assertIsNotNone(key.fingerprint)
def test_dsa_key_generation_with_subkey(self): def test_dsa_key_generation_with_subkey(self):
""" """Test that RSA key generation succeeds with additional subkey."""
Test that RSA key generation succeeds with additional subkey. key = self.generate_key("Eli Biham", "bearli.on",
"""
key = self.generate_key("OMG Moar Coffee", "giveitto.me",
subkey_type='ELG-E') subkey_type='ELG-E')
self.assertIsNotNone(key.type) self.assertIsNotNone(key.type)
self.assertIsNotNone(key.fingerprint) self.assertIsNotNone(key.fingerprint)
@ -505,49 +516,45 @@ class GPGTestCase(unittest.TestCase):
def test_signature_algorithm(self): def test_signature_algorithm(self):
"""Test that determining the signing algorithm works.""" """Test that determining the signing algorithm works."""
key = self.generate_key("Werner Koch", "gnupg.org") key = self.generate_key("Ron Rivest", "rsa.com")
message = "Damn, I really wish GnuPG had ECC support." message = "Someone should add GCM block cipher mode to PyCrypto."
sig = self.gpg.sign(message, keyid=key.fingerprint, sig = self.gpg.sign(message, keyid=key.fingerprint,
passphrase='wernerkoch') passphrase='ronrivest')
print "ALGORITHM:\n", sig.sig_algo print "ALGORITHM:\n", sig.sig_algo
self.assertIsNotNone(sig.sig_algo) self.assertIsNotNone(sig.sig_algo)
def test_signature_string_bad_passphrase(self): def test_signature_string_bad_passphrase(self):
"""Test that signing and verification works.""" """Test that signing and verification works."""
key = self.generate_key("Ron Rivest", "rsa.com") key = self.generate_key("Taher ElGamal", "cryto.me")
message = 'Hello, André!' message = 'أصحاب المصالح لا يحبون الثوراتز'
sig = self.gpg.sign(message, keyid=key.fingerprint, passphrase='foo') sig = self.gpg.sign(message, keyid=key.fingerprint, passphrase='foo')
self.assertFalse(sig, "Bad passphrase should fail") self.assertFalse(sig, "Bad passphrase should fail")
def test_signature_string_alternate_encoding(self): def test_signature_string_alternate_encoding(self):
key = self.generate_key("Adi Shamir", "rsa.com") key = self.generate_key("Nos Oignons", "nos-oignons.net")
self.gpg.encoding = 'latin-1' self.gpg.encoding = 'latin-1'
message = 'Hello, André!' message = "Mêle-toi de tes oignons"
sig = self.gpg.sign(message, keyid=key.fingerprint, sig = self.gpg.sign(message, keyid=key.fingerprint,
passphrase='adishamir') passphrase='nosoignons')
self.assertTrue(sig) self.assertTrue(sig)
def test_signature_file(self): def test_signature_file(self):
"""Test that signing a message file works.""" """Test that signing a message file works."""
key = self.generate_key("Leonard Adleman", "rsa.com") key = self.generate_key("Leonard Adleman", "rsa.com")
message = "Someone should add GCM block cipher mode to PyCrypto." message_file = os.path.join(_files, 'cypherpunk_manifesto')
message_fn = os.path.join(tempfile.gettempdir(), 'test_signature_file') with open(message_file) as msg:
with open(message_fn, 'w+b') as msg: sig = self.gpg.sign(msg, keyid=key.fingerprint,
msg.write(message) passphrase='leonardadleman')
self.assertTrue(sig, "I thought I typed my password correctly...")
message_file = buffer(open(message_fn, "rb").read())
mf = io.BytesIO(message_file)
sig = self.gpg.sign(mf, keyid=key.fingerprint,
passphrase='leonardadleman')
self.assertTrue(sig, "Good passphrase should succeed")
def test_signature_string_verification(self): def test_signature_string_verification(self):
"""Test verification of a signature from a message string.""" """Test verification of a signature from a message string."""
key = self.generate_key("Andrew Able", "alpha.com") key = self.generate_key("Bruce Schneier", "schneier.com")
message = 'Hello, André!' message = '...the government uses the general fear of '
message += '[hackers in popular culture] to push for more power'
sig = self.gpg.sign(message, keyid=key.fingerprint, sig = self.gpg.sign(message, keyid=key.fingerprint,
passphrase='andrewable') passphrase='bruceschneier')
now = time.mktime(time.gmtime())
self.assertTrue(sig, "Good passphrase should succeed") self.assertTrue(sig, "Good passphrase should succeed")
verified = self.gpg.verify(sig.data) verified = self.gpg.verify(sig.data)
self.assertIsNotNone(verified.fingerprint) self.assertIsNotNone(verified.fingerprint)
@ -556,15 +563,18 @@ class GPGTestCase(unittest.TestCase):
logger.debug("ver: %r", verified.fingerprint) logger.debug("ver: %r", verified.fingerprint)
self.assertEqual(key.fingerprint, verified.fingerprint, self.assertEqual(key.fingerprint, verified.fingerprint,
"Fingerprints must match") "Fingerprints must match")
self.assertEqual(verified.trust_level, verified.TRUST_ULTIMATE) self.assertEqual(verified.status, 'signature valid')
self.assertEqual(verified.trust_text, 'TRUST_ULTIMATE') self.assertAlmostEqual(int(now), int(verified.timestamp), delta=1000)
self.assertEqual(
verified.username,
u'Bruce Schneier (python-gnupg tester) <bruceschneier@schneier.com>')
def test_signature_file_verification(self): def test_signature_verification_clearsign(self):
"""Test verfication of a signature on a message file.""" """Test verfication of an embedded signature."""
key = self.generate_key("Taher ElGamal", "cryto.me") key = self.generate_key("Johan Borst", "rijnda.el")
message = 'أصحاب المصالح لا يحبون الثوراتز' message = "You're *still* using AES? Really?"
sig = self.gpg.sign(message, keyid=key.fingerprint, sig = self.gpg.sign(message, keyid=key.fingerprint,
passphrase='taherelgamal') passphrase='johanborst')
self.assertTrue(sig, "Good passphrase should succeed") self.assertTrue(sig, "Good passphrase should succeed")
try: try:
file = util._make_binary_stream(sig.data, self.gpg.encoding) file = util._make_binary_stream(sig.data, self.gpg.encoding)
@ -576,22 +586,41 @@ class GPGTestCase(unittest.TestCase):
logger.debug("ver: %r", verified.fingerprint) logger.debug("ver: %r", verified.fingerprint)
self.assertEqual(key.fingerprint, verified.fingerprint, self.assertEqual(key.fingerprint, verified.fingerprint,
"Fingerprints must match") "Fingerprints must match")
data_file = open('random_binary_data', 'rb')
sig = self.gpg._sign_file(data_file, keyid=key.fingerprint, def test_signature_verification_detached(self):
passphrase='andrewable', detach=True) """Test that verification of a detached signature of a file works."""
data_file.close() key = self.generate_key("Paulo S.L.M. Barreto", "anub.is")
self.assertTrue(sig, "File signing should succeed") with open(os.path.join(_files, 'cypherpunk_manifesto'),
try: 'rb') as manifesto:
file = gnupg._make_binary_stream(sig.data, self.gpg.encoding) sig = self.gpg.sign(manifesto, keyid=key.fingerprint,
verified = self.gpg.verify_file(file, 'random_binary_data') passphrase='paulos.l.m.barreto',
except UnicodeDecodeError: #happens in Python 2.6 detach=True, clearsign=False)
verified = self.gpg.verify_file(io.BytesIO(sig.data)) self.assertTrue(sig.data, "File signing should succeed")
if key.fingerprint != verified.fingerprint: sigfilename = os.path.join(_files, 'cypherpunk_manifesto.sig')
logger.debug("key: %r", key.fingerprint) with open(sigfilename,'w') as sigfile:
logger.debug("ver: %r", verified.fingerprint) sigfile.write(sig.data)
self.assertEqual(key.fingerprint, verified.fingerprint, sigfile.seek(0)
"Fingerprints must match")
logger.debug("test_signature_verification ends") verified = self.gpg.verify_file(manifesto, sigfilename)
if key.fingerprint != verified.fingerprint:
logger.debug("key: %r", key.fingerprint)
logger.debug("ver: %r", verified.fingerprint)
self.assertEqual(key.fingerprint, verified.fingerprint,
"Fingerprints must match")
def test_signature_verification_detached_binary(self):
"""Test that detached signature verification in binary mode fails."""
key = self.generate_key("Adi Shamir", "rsa.com")
with open(os.path.join(_files, 'cypherpunk_manifesto'),
'rb') as manifesto:
sig = self.gpg.sign(manifesto, keyid=key.fingerprint,
passphrase='adishamir',
detach=True, binary=True, clearsign=False)
self.assertTrue(sig.data, "File signing should succeed")
with self.assertRaises(UnicodeDecodeError):
print "SIG=", sig
def test_deletion(self): def test_deletion(self):
"""Test that key deletion works.""" """Test that key deletion works."""
@ -639,7 +668,10 @@ class GPGTestCase(unittest.TestCase):
logger.debug("test_file_encryption_and_decryption ends") logger.debug("test_file_encryption_and_decryption ends")
suites = { 'basic': set(['test_gpghome_creation', suites = { 'parsers': set(['test_parsers_fix_unsafe',
'test_parsers_is_hex_valid',
'test_parsers_is_hex_invalid',]),
'basic': set(['test_gpghome_creation',
'test_gpg_binary', 'test_gpg_binary',
'test_gpg_binary_not_abs', 'test_gpg_binary_not_abs',
'test_gpg_binary_version_str', 'test_gpg_binary_version_str',
@ -659,7 +691,9 @@ suites = { 'basic': set(['test_gpghome_creation',
'test_key_generation_with_empty_value', 'test_key_generation_with_empty_value',
'test_key_generation_override_default_value', 'test_key_generation_override_default_value',
'test_key_generation_with_colons']), 'test_key_generation_with_colons']),
'sign': set(['test_signature_file_verification', 'sign': set(['test_signature_verification_clearsign',
'test_signature_verification_detached',
'test_signature_verification_detached_binary',
'test_signature_file', 'test_signature_file',
'test_signature_string_bad_passphrase', 'test_signature_string_bad_passphrase',
'test_signature_string_alternate_encoding', 'test_signature_string_alternate_encoding',

View File

@ -140,7 +140,7 @@ def _is_file(input):
""" """
try: try:
assert os.lstat(input).st_size > 0, "not a file: %s" % input assert os.lstat(input).st_size > 0, "not a file: %s" % input
except (AssertionError, TypeError) as error: except (AssertionError, TypeError, IOError, OSError) as error:
logger.debug(error.message) logger.debug(error.message)
return False return False
else: else: