Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 3500e78e9d
Fetching contributors…

Cannot retrieve contributors at this time

221 lines (178 sloc) 6.084 kb
"""Module containing a cryptographic-quality source of randomness and
other cryptographically useful functionality
Python 2.4 needs no external support for this module, nor does Python
2.3 on a system with /dev/urandom.
Other configurations will need a quality source of random bytes and
access to a function that will convert binary strings to long
integers. This module will work with the Python Cryptography Toolkit
(pycrypto) if it is present. pycrypto can be found with a search
engine, but is currently found at:
http://www.amk.ca/python/code/crypto
"""
__all__ = [
'base64ToLong',
'binaryToLong',
'hmacSha1',
'hmacSha256',
'longToBase64',
'longToBinary',
'randomString',
'randrange',
'sha1',
'sha256',
]
import hmac
import os
import random
from openid.oidutil import toBase64, fromBase64
try:
import hashlib
except ImportError:
import sha as sha1_module
try:
from Crypto.Hash import SHA256 as sha256_module
except ImportError:
sha256_module = None
else:
class HashContainer(object):
def __init__(self, hash_constructor):
self.new = hash_constructor
self.digest_size = hash_constructor().digest_size
sha1_module = HashContainer(hashlib.sha1)
sha256_module = HashContainer(hashlib.sha256)
def hmacSha1(key, text):
return hmac.new(key, text, sha1_module).digest()
def sha1(s):
return sha1_module.new(s).digest()
if sha256_module is not None:
def hmacSha256(key, text):
return hmac.new(key, text, sha256_module).digest()
def sha256(s):
return sha256_module.new(s).digest()
SHA256_AVAILABLE = True
else:
_no_sha256 = NotImplementedError(
'Use Python 2.5, install pycrypto or install hashlib to use SHA256')
def hmacSha256(unused_key, unused_text):
raise _no_sha256
def sha256(s):
raise _no_sha256
SHA256_AVAILABLE = False
try:
from Crypto.Util.number import long_to_bytes, bytes_to_long
except ImportError:
import pickle
try:
# Check Python compatiblity by raising an exception on import
# if the needed functionality is not present. Present in
# Python >= 2.3
pickle.encode_long
pickle.decode_long
except AttributeError:
raise ImportError(
'No functionality for serializing long integers found')
# Present in Python >= 2.4
try:
reversed
except NameError:
def reversed(seq):
return map(seq.__getitem__, xrange(len(seq) - 1, -1, -1))
def longToBinary(l):
if l == 0:
return '\x00'
return ''.join(reversed(pickle.encode_long(l)))
def binaryToLong(s):
return pickle.decode_long(''.join(reversed(s)))
else:
# We have pycrypto
def longToBinary(l):
if l < 0:
raise ValueError('This function only supports positive integers')
bytes = long_to_bytes(l)
if ord(bytes[0]) > 127:
return '\x00' + bytes
else:
return bytes
def binaryToLong(bytes):
if not bytes:
raise ValueError('Empty string passed to strToLong')
if ord(bytes[0]) > 127:
raise ValueError('This function only supports positive integers')
return bytes_to_long(bytes)
# A cryptographically safe source of random bytes
try:
getBytes = os.urandom
except AttributeError:
try:
from Crypto.Util.randpool import RandomPool
except ImportError:
# Fall back on /dev/urandom, if present. It would be nice to
# have Windows equivalent here, but for now, require pycrypto
# on Windows.
try:
_urandom = file('/dev/urandom', 'rb')
except IOError:
raise ImportError('No adequate source of randomness found!')
else:
def getBytes(n):
bytes = []
while n:
chunk = _urandom.read(n)
n -= len(chunk)
bytes.append(chunk)
assert n >= 0
return ''.join(bytes)
else:
_pool = RandomPool()
def getBytes(n, pool=_pool):
if pool.entropy < n:
pool.randomize()
return pool.get_bytes(n)
# A randrange function that works for longs
try:
randrange = random.SystemRandom().randrange
except AttributeError:
# In Python 2.2's random.Random, randrange does not support
# numbers larger than sys.maxint for randrange. For simplicity,
# use this implementation for any Python that does not have
# random.SystemRandom
from math import log, ceil
_duplicate_cache = {}
def randrange(start, stop=None, step=1):
if stop is None:
stop = start
start = 0
r = (stop - start) // step
try:
(duplicate, nbytes) = _duplicate_cache[r]
except KeyError:
rbytes = longToBinary(r)
if rbytes[0] == '\x00':
nbytes = len(rbytes) - 1
else:
nbytes = len(rbytes)
mxrand = (256 ** nbytes)
# If we get a number less than this, then it is in the
# duplicated range.
duplicate = mxrand % r
if len(_duplicate_cache) > 10:
_duplicate_cache.clear()
_duplicate_cache[r] = (duplicate, nbytes)
while 1:
bytes = '\x00' + getBytes(nbytes)
n = binaryToLong(bytes)
# Keep looping if this value is in the low duplicated range
if n >= duplicate:
break
return start + (n % r) * step
def longToBase64(l):
return toBase64(longToBinary(l))
def base64ToLong(s):
return binaryToLong(fromBase64(s))
def randomString(length, chrs=None):
"""Produce a string of length random bytes, chosen from chrs."""
if chrs is None:
return getBytes(length)
else:
n = len(chrs)
return ''.join([chrs[randrange(n)] for _ in xrange(length)])
Jump to Line
Something went wrong with that request. Please try again.