Skip to content

Commit

Permalink
Add unit test for AllOrNothing
Browse files Browse the repository at this point in the history
Note that AllOrNothing fails occasionally. This has always been the case;
the unit test merely forces the flaw to be exposed.
  • Loading branch information
thorsteneb committed Jan 4, 2011
1 parent 560874f commit 83cd648
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 22 deletions.
57 changes: 48 additions & 9 deletions Doc/pycrypt.rst
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ CAST Variable/8 bytes
DES 8 bytes/8 bytes
DES3 (Triple DES) 16 bytes/8 bytes
IDEA 16 bytes/8 bytes
RC5 Variable/8 bytes
[RC5 Variable/8 bytes]
================= ============================


Expand Down Expand Up @@ -438,9 +438,15 @@ encrypt() will return a bytes object.
Algorithm-specific Notes for Encryption Algorithms
=======================================================

[RC5 is not currently implemented in pycrypto]

RC5 has a bunch of parameters; see Ronald Rivest's paper at
<http://theory.lcs.mit.edu/~rivest/rc5rev.ps> for the
implementation details. The keyword parameters are:
implementation details. RC5 is patented by RSA Laboratories.
RC5 supports 32-bit, 64-bit and 128-bit block sizes. RSA suggests a block size
of 64-bit, a 128-bit key and 18-20 rounds.

The keyword parameters are:

* ``version``: The version of the RC5 algorithm to use; currently
the only legal value is ``0x10`` for RC5 1.0.
Expand All @@ -451,7 +457,9 @@ implementation details. The keyword parameters are:

* ``rounds``: The number of rounds to apply, the larger the more
secure: this can be any value from 0 to 255, so you will have to
choose a value balanced between speed and security.
choose a value balanced between speed and security. 12-round RC5
is susceptible to a differential attack. 18-20 rounds are suggested
as sufficient protection.


Security Notes
Expand All @@ -467,6 +475,10 @@ encrypted and forwarded to someone else. This is a
possible to choose plaintexts that reveal something about the key when
encrypted.

Stream ciphers are only secure if any given key is never used twice.
If two (or more) messages are encrypted using the same key in a stream
cipher, the cipher can be broken fairly easily.

DES (5100 K/sec) has a 56-bit key; this is starting to become too small
for safety. It has been shown in 2009 that a ~$10,000 machine can break
DES in under a day on average. NIST has withdrawn FIPS 46-3 in 2005.
Expand All @@ -479,21 +491,48 @@ study applied against it. It is, however, slow.
There are no publicly known attacks against the full-round IDEA (3050 K/sec),
and it's been around long enough to have been examined. IDEA is patented but
free for non-commercial use. Patents are expected to expire in 2011/2012.
There are no known attacks against ARC2 (2160 K/sec), ARC4 (8830 K/sec),
Blowfish (9250 K/sec), CAST (2960 K/sec), or RC5 (2060 K/sec), but they're all
relatively new algorithms and there hasn't been time for much analysis
to be performed; use them for serious applications only after careful
research.
IDEA is one of the strongest symmetric ciphers available to the public, alongside
AES and AES candidates.

There are no known attacks against Blowfish (9250 K/sec), CAST (2960 K/sec),
or RC5 (2060 K/sec), but they're all relatively new algorithms and there hasn't
been time for much analysis to be performed; use them for serious applications
only after careful research.

pycrypto implements CAST with up to 128 bits key length (CAST-128). This
algorithm is considered obsoleted by CAST-256. CAST is patented by Entrust
Technologies and free for non-commercial use.

Bruce Schneier recommends his newer Twofish algorithm over Blowfish where
a fast, secure symmetric cipher is desired. Twofish was an AES candidate. It
is slightly slower than Rijndael (the chose algorithm for AES) for 128-bit
is slightly slower than Rijndael (the chosen algorithm for AES) for 128-bit
keys, and slightly faster for 256-bit keys.

AES, the Advanced Encryption Standard, was chosen by the US National
Institute of Standards and Technology from among 6 competitors, and is
probably your best choice. It runs at 7060 K/sec, so it's among the
faster algorithms around.

ARC4 ("Alleged" RC4) (8830 K/sec) has been weakened. Specifically, it has been
shown that the first few bytes of the ARC4 keystream are strongly non-random,
leaking information about the key. When the long-term key and nonce are merely
concatenated to form the ARC4 key, such as is done in WEP, this weakness can be
used to discover the long-term key by observing a large number of messages
encrypted with this key.
Because of these possible related-key attacks, ARC4 should only be used with
keys generated by a strong RNG, or from a source of sufficiently uncorrelated
bits, such as the output of a hash function.
A further possible defense is to discard the initial portion of the keystream.
This altered algorithm is called RC4-drop(n).
While ARC4 is in wide-spread use in several protocols, its use in new protocols
or applications is discouraged.
RC4 is patented by RSA Laboratories.

ARC2 ("Alleged" RC2) is vulnerable to a related-key attack, 2^34 chosen
plaintexts are needed.
Because of these possible related-key attacks, ARC2 should only be used with
keys generated by a strong RNG, or from a source of sufficiently uncorrelated
bits, such as the output of a hash function.

Credits
=============
Expand Down
19 changes: 9 additions & 10 deletions lib/Crypto/Protocol/AllOrNothing.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def digest(self, text):
# the undigest() step.
block_size = self.__ciphermodule.block_size
padbytes = block_size - (len(text) % block_size)
text = text + ' ' * padbytes
text = text + b(' ') * padbytes

# Run through the algorithm:
# s: number of message blocks (size of text / block_size)
Expand All @@ -128,7 +128,7 @@ def digest(self, text):
# The one complication I add is that the last message block is hard
# coded to the number of padbytes added, so that these can be stripped
# during the undigest() step
s = len(text) / block_size
s = divmod(len(text), block_size)[0]
blocks = []
hashes = []
for i in range(1, s+1):
Expand Down Expand Up @@ -219,8 +219,7 @@ def undigest(self, blocks):
# of the cipher's block_size. This number should be small enough that
# the conversion from long integer to integer should never overflow
padbytes = int(parts[-1])
# PY3K: This is meant to be text, do not change to bytes (data)
text = ''.join(map(long_to_bytes, parts[:-1]))
text = b('').join(map(long_to_bytes, parts[:-1]))
return text[:-padbytes]

def _inventkey(self, key_size):
Expand Down Expand Up @@ -291,13 +290,13 @@ def usage(code, msg=None):
# ugly hack to force __import__ to give us the end-path module
module = __import__('Crypto.Cipher.'+ciphermodule, None, None, ['new'])

a = AllOrNothing(module)
x = AllOrNothing(module)
print 'Original text:\n=========='
print __doc__
print '=========='
msgblocks = a.digest(__doc__)
msgblocks = x.digest(b(__doc__))
print 'message blocks:'
for i, blk in map(None, range(len(msgblocks)), msgblocks):
for i, blk in zip(range(len(msgblocks)), msgblocks):
# base64 adds a trailing newline
print ' %3d' % i,
if aslong:
Expand All @@ -306,9 +305,9 @@ def usage(code, msg=None):
print base64.encodestring(blk)[:-1]
#
# get a new undigest-only object so there's no leakage
b = AllOrNothing(module)
text = b.undigest(msgblocks)
if text == __doc__:
y = AllOrNothing(module)
text = y.undigest(msgblocks)
if text == b(__doc__):
print 'They match!'
else:
print 'They differ!'
1 change: 1 addition & 0 deletions lib/Crypto/SelfTest/Protocol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def get_tests(config={}):
tests = []
import test_chaffing; tests += test_chaffing.get_tests(config=config)
import test_rfc1751; tests += test_rfc1751.get_tests(config=config)
import test_AllOrNothing; tests += test_AllOrNothing.get_tests(config=config)
return tests

if __name__ == '__main__':
Expand Down
11 changes: 8 additions & 3 deletions python-3-changes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ Use instead assertEqual(expr,True) for assert_ and assertEqual(expr,False) for
failIf

Added unit tests for Crypto.Random.random. Fixed random.shuffle().
Not changed: random.sample() fails on Python 2.1. This is now exposed through
the unit test.

Added unit test for Crypto.Protocol.AllOrNothing.
Not changed: AllOrNothing fails when called a few times (<10, usually). This
is now exposed through the unit test.

C code:

Expand Down Expand Up @@ -95,12 +101,11 @@ getattr cannot be used to check against custom attributes in 3.x. For 3.x,
hexdigest() needed to be changed to return a Unicode object with 3.x


TODO:
TODO for extra credit:
- Check for type of string in functions and throw an error when it's not
correct. While at it, ensure that functions observe the guidelines below
re type.
This is friendlier than just relying on Python's errors.
- Need additional unit tests for Protocol/AllOrNothing
- Make sure DerSequence slicing is tested, since I took the explicit slice
functions away in 3.x
- Test install on all tested Python versions

0 comments on commit 83cd648

Please sign in to comment.