pycryptodome/pct-speedtest.py
Lorenz Quack c575de4f18 getStrongPrime() implementation
From http://lists.dlitz.net/pipermail/pycrypto/2009q4/000167.html, with the
following explanation included in the email:

=== snip ===
Hi there!

Here comes my monster patch.
It includes a python and C version of getStrongPrime, rabinMillerTest and isPrime.
there are also two small unit tests and some helper functions.
They all take a randfunc and propagate them (or so I hope).
The Rabin-Miller-Test uses random bases (non-deterministic).
getStrongPrime and isPrime take an optional parameter "false_positive_prob"
where one can specify the maximum probability that the prime is actually
composite. Internally the functions calculate the Rabin-Miller rounds from
this. It defaults to 1e-6 (1:1000000) which results in 10 rounds of Rabin-Miller
testing.

Please review this carefully. Even though I tried hard to get things right some
bugs always slip through.
maybe you could also review the way I acquire and release the GIL. It felt kind
of ugly the way I did it but I don't see a better way just now.

Concerning the public exponent e:
I now know why it needs to be coprime to p-1 and q-1. The private exponent d is
the inverse of e mod ((p-1)(q-1)).
If e is not coprime to ((p-1)(q-1)) then the inverse does not exist [1].

The getStrongPrime take an optional argument e. if provided the function will
make sure p-1 and e are coprime. if e is even (p-1)/2 will be coprime.
if e is even then there is a additional constraint: p =/= q mod 8.
I can't check for that in getStrongPrime of course but since we hardcoded e to
be odd in _RSA.py this should pose no problem.

The Baillie-PSW-Test is not included.

I tried hard not to use any functionality new than 2.1 but if you find anything
feel free to criticize. Also if I didn't get the coding style right either tell
me or feel free to correct it yourself.

have fun.
//Lorenz

[1] http://mathworld.wolfram.com/ModularInverse.html
=== snip ===
2010-06-10 21:02:07 -04:00

220 lines
7.6 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# pct-speedtest.py: Speed test for the Python Cryptography Toolkit
#
# Written in 2009 by Dwayne C. Litzenberger <dlitz@dlitz.net>
#
# ===================================================================
# The contents of this file are dedicated to the public domain. To
# the extent that dedication to the public domain is not available,
# everyone is granted a worldwide, perpetual, royalty-free,
# non-exclusive license to exercise all rights associated with the
# contents of this file for any purpose whatsoever.
# No rights are reserved.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# ===================================================================
import time
import os
import sys
from Crypto.PublicKey import RSA
from Crypto.Cipher import AES, ARC2, ARC4, Blowfish, CAST, DES3, DES, XOR
from Crypto.Hash import MD2, MD4, MD5, SHA256, SHA
try:
from Crypto.Hash import RIPEMD
except ImportError: # Some builds of PyCrypto don't have the RIPEMD module
RIPEMD = None
class Benchmark:
def __init__(self):
self.__random_data = None
def random_keys(self, bytes):
"""Return random keys of the specified number of bytes.
If this function has been called before with the same number of bytes,
cached keys are used instead of randomly generating new ones.
"""
return self.random_blocks(bytes, 10**5) # 100k
def random_blocks(self, bytes_per_block, blocks):
bytes = bytes_per_block * blocks
data = self.random_data(bytes)
retval = []
for i in xrange(blocks):
p = i * bytes_per_block
retval.append(data[p:p+bytes_per_block])
return retval
def random_data(self, bytes):
if self.__random_data is None:
self.__random_data = self._random_bytes(bytes)
return self.__random_data
elif bytes == len(self.__random_data):
return self.__random_data
elif bytes < len(self.__random_data):
return self.__random_data[:bytes]
else:
self.__random_data += self._random_bytes(bytes - len(self.__random_data))
return self.__random_data
def _random_bytes(self, b):
return os.urandom(b)
def announce_start(self, test_name):
sys.stdout.write("%s: " % (test_name,))
sys.stdout.flush()
def announce_result(self, value, units):
sys.stdout.write("%.2f %s\n" % (value, units))
sys.stdout.flush()
def test_pubkey_setup(self, pubkey_name, module, key_bytes):
self.announce_start("%s pubkey setup" % (pubkey_name,))
keys = self.random_keys(key_bytes)[:5]
t0 = time.time()
for k in keys:
module.generate(key_bytes*8)
t = time.time()
pubkey_setups_per_second = len(keys) / (t - t0)
self.announce_result(pubkey_setups_per_second, "Keys/sec")
def test_key_setup(self, cipher_name, module, key_bytes, mode):
self.announce_start("%s key setup" % (cipher_name,))
# Generate random keys for use with the tests
keys = self.random_keys(key_bytes)
# Perform key setups
if mode is None:
t0 = time.time()
for k in keys:
module.new(k)
t = time.time()
else:
t0 = time.time()
for k in keys:
module.new(k, module.MODE_ECB)
t = time.time()
key_setups_per_second = len(keys) / (t - t0)
self.announce_result(key_setups_per_second/1000, "kKeys/sec")
def test_encryption(self, cipher_name, module, key_bytes, mode):
self.announce_start("%s encryption" % (cipher_name,))
# Generate random keys for use with the tests
rand = self.random_data(key_bytes + module.block_size)
key, iv = rand[:key_bytes], rand[key_bytes:]
blocks = self.random_blocks(16384, 1000)
if mode is None:
cipher = module.new(key)
else:
cipher = module.new(key, mode, iv)
# Perform encryption
t0 = time.time()
for b in blocks:
cipher.encrypt(b)
t = time.time()
encryption_speed = (len(blocks) * len(blocks[0])) / (t - t0)
self.announce_result(encryption_speed / 10**6, "MBps")
def test_hash_small(self, hash_name, module):
self.announce_start("%s (%d-byte inputs)" % (hash_name, module.digest_size))
blocks = self.random_blocks(module.digest_size, 10000)
# Initialize hashes
t0 = time.time()
for b in blocks:
module.new(b).digest()
t = time.time()
hashes_per_second = len(blocks) / (t - t0)
self.announce_result(hashes_per_second / 1000, "kHashes/sec")
def test_hash_large(self, hash_name, module):
self.announce_start("%s (single large input)" % (hash_name,))
blocks = self.random_blocks(16384, 10000)
# Perform hashing
t0 = time.time()
h = module.new()
for b in blocks:
h.update(b)
h.digest()
t = time.time()
hash_speed = len(blocks) * len(blocks[0]) / (t - t0)
self.announce_result(hash_speed / 10**6, "MBps")
def run(self):
pubkey_specs = [
("RSA(1024)", RSA, 1024/8),
("RSA(2048)", RSA, 2048/8),
("RSA(4096)", RSA, 4096/8),
]
block_specs = [
("DES", DES, 8),
("DES3", DES3, 24),
("AES128", AES, 16),
("AES192", AES, 24),
("AES256", AES, 32),
("Blowfish(256)", Blowfish, 32),
("CAST(40)", CAST, 5),
("CAST(80)", CAST, 10),
("CAST(128)", CAST, 16),
]
stream_specs = [
("ARC2(128)", ARC2, 16),
("ARC4(128)", ARC4, 16),
("XOR(24)", XOR, 3),
("XOR(256)", XOR, 32),
]
hash_specs = [
("MD2", MD2),
("MD4", MD4),
("MD5", MD5),
("SHA", SHA),
("SHA256", SHA256),
]
if RIPEMD is not None:
hash_specs += [("RIPEMD", RIPEMD)]
for pubkey_name, module, key_bytes in pubkey_specs:
self.test_pubkey_setup(pubkey_name, module, key_bytes)
for cipher_name, module, key_bytes in block_specs:
self.test_key_setup(cipher_name, module, key_bytes, module.MODE_CBC)
self.test_encryption("%s-CBC" % (cipher_name,), module, key_bytes, module.MODE_CBC)
self.test_encryption("%s-CFB" % (cipher_name,), module, key_bytes, module.MODE_CFB)
self.test_encryption("%s-PGP" % (cipher_name,), module, key_bytes, module.MODE_PGP)
self.test_encryption("%s-OFB" % (cipher_name,), module, key_bytes, module.MODE_OFB)
for cipher_name, module, key_bytes in stream_specs:
self.test_key_setup(cipher_name, module, key_bytes, None)
self.test_encryption(cipher_name, module, key_bytes, None)
for hash_name, module in hash_specs:
self.test_hash_small(hash_name, module)
self.test_hash_large(hash_name, module)
if __name__ == '__main__':
Benchmark().run()
# vim:set ts=4 sw=4 sts=4 expandtab: