getting file size for all dict files to be downloaded. coming to be 400mb or so.
[worddb.git] / libs / openid / cryptutil.py
blob52c2c619de687da9537a0443a81fb137c6441389
1 """Module containing a cryptographic-quality source of randomness and
2 other cryptographically useful functionality
4 Python 2.4 needs no external support for this module, nor does Python
5 2.3 on a system with /dev/urandom.
7 Other configurations will need a quality source of random bytes and
8 access to a function that will convert binary strings to long
9 integers. This module will work with the Python Cryptography Toolkit
10 (pycrypto) if it is present. pycrypto can be found with a search
11 engine, but is currently found at:
13 http://www.amk.ca/python/code/crypto
14 """
16 __all__ = [
17 'base64ToLong',
18 'binaryToLong',
19 'hmacSha1',
20 'hmacSha256',
21 'longToBase64',
22 'longToBinary',
23 'randomString',
24 'randrange',
25 'sha1',
26 'sha256',
29 import hmac
30 import os
31 import random
33 from openid.oidutil import toBase64, fromBase64
35 try:
36 import hashlib
37 except ImportError:
38 import sha as sha1_module
40 try:
41 from Crypto.Hash import SHA256 as sha256_module
42 except ImportError:
43 sha256_module = None
45 else:
46 class HashContainer(object):
47 def __init__(self, hash_constructor):
48 self.new = hash_constructor
50 sha1_module = HashContainer(hashlib.sha1)
51 sha256_module = HashContainer(hashlib.sha256)
53 def hmacSha1(key, text):
54 return hmac.new(key, text, sha1_module).digest()
56 def sha1(s):
57 return sha1_module.new(s).digest()
59 if sha256_module is not None:
60 def hmacSha256(key, text):
61 return hmac.new(key, text, sha256_module).digest()
63 def sha256(s):
64 return sha256_module.new(s).digest()
66 SHA256_AVAILABLE = True
68 else:
69 _no_sha256 = NotImplementedError(
70 'Use Python 2.5, install pycrypto or install hashlib to use SHA256')
72 def hmacSha256(unused_key, unused_text):
73 raise _no_sha256
75 def sha256(s):
76 raise _no_sha256
78 SHA256_AVAILABLE = False
80 try:
81 from Crypto.Util.number import long_to_bytes, bytes_to_long
82 except ImportError:
83 import pickle
84 try:
85 # Check Python compatiblity by raising an exception on import
86 # if the needed functionality is not present. Present in
87 # Python >= 2.3
88 pickle.encode_long
89 pickle.decode_long
90 except AttributeError:
91 raise ImportError(
92 'No functionality for serializing long integers found')
94 # Present in Python >= 2.4
95 try:
96 reversed
97 except NameError:
98 def reversed(seq):
99 return map(seq.__getitem__, xrange(len(seq) - 1, -1, -1))
101 def longToBinary(l):
102 if l == 0:
103 return '\x00'
105 return ''.join(reversed(pickle.encode_long(l)))
107 def binaryToLong(s):
108 return pickle.decode_long(''.join(reversed(s)))
109 else:
110 # We have pycrypto
112 def longToBinary(l):
113 if l < 0:
114 raise ValueError('This function only supports positive integers')
116 bytes = long_to_bytes(l)
117 if ord(bytes[0]) > 127:
118 return '\x00' + bytes
119 else:
120 return bytes
122 def binaryToLong(bytes):
123 if not bytes:
124 raise ValueError('Empty string passed to strToLong')
126 if ord(bytes[0]) > 127:
127 raise ValueError('This function only supports positive integers')
129 return bytes_to_long(bytes)
131 # A cryptographically safe source of random bytes
132 try:
133 getBytes = os.urandom
134 except AttributeError:
135 try:
136 from Crypto.Util.randpool import RandomPool
137 except ImportError:
138 # Fall back on /dev/urandom, if present. It would be nice to
139 # have Windows equivalent here, but for now, require pycrypto
140 # on Windows.
141 try:
142 _urandom = file('/dev/urandom', 'rb')
143 except IOError:
144 raise ImportError('No adequate source of randomness found!')
145 else:
146 def getBytes(n):
147 bytes = []
148 while n:
149 chunk = _urandom.read(n)
150 n -= len(chunk)
151 bytes.append(chunk)
152 assert n >= 0
153 return ''.join(bytes)
154 else:
155 _pool = RandomPool()
156 def getBytes(n, pool=_pool):
157 if pool.entropy < n:
158 pool.randomize()
159 return pool.get_bytes(n)
161 # A randrange function that works for longs
162 try:
163 randrange = random.SystemRandom().randrange
164 except AttributeError:
165 # In Python 2.2's random.Random, randrange does not support
166 # numbers larger than sys.maxint for randrange. For simplicity,
167 # use this implementation for any Python that does not have
168 # random.SystemRandom
169 from math import log, ceil
171 _duplicate_cache = {}
172 def randrange(start, stop=None, step=1):
173 if stop is None:
174 stop = start
175 start = 0
177 r = (stop - start) // step
178 try:
179 (duplicate, nbytes) = _duplicate_cache[r]
180 except KeyError:
181 rbytes = longToBinary(r)
182 if rbytes[0] == '\x00':
183 nbytes = len(rbytes) - 1
184 else:
185 nbytes = len(rbytes)
187 mxrand = (256 ** nbytes)
189 # If we get a number less than this, then it is in the
190 # duplicated range.
191 duplicate = mxrand % r
193 if len(_duplicate_cache) > 10:
194 _duplicate_cache.clear()
196 _duplicate_cache[r] = (duplicate, nbytes)
198 while 1:
199 bytes = '\x00' + getBytes(nbytes)
200 n = binaryToLong(bytes)
201 # Keep looping if this value is in the low duplicated range
202 if n >= duplicate:
203 break
205 return start + (n % r) * step
207 def longToBase64(l):
208 return toBase64(longToBinary(l))
210 def base64ToLong(s):
211 return binaryToLong(fromBase64(s))
213 def randomString(length, chrs=None):
214 """Produce a string of length random bytes, chosen from chrs."""
215 if chrs is None:
216 return getBytes(length)
217 else:
218 n = len(chrs)
219 return ''.join([chrs[randrange(n)] for _ in xrange(length)])