cryptutil.py 5.9 KB

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