Package openid :: Module cryptutil
[frames] | no frames]

Source Code for Module openid.cryptutil

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