Package dns :: Module entropy
[hide private]
[frames] | no frames]

Source Code for Module dns.entropy

  1  # Copyright (C) 2009, 2011 Nominum, Inc. 
  2  # 
  3  # Permission to use, copy, modify, and distribute this software and its 
  4  # documentation for any purpose with or without fee is hereby granted, 
  5  # provided that the above copyright notice and this permission notice 
  6  # appear in all copies. 
  7  # 
  8  # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES 
  9  # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 
 10  # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR 
 11  # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 
 12  # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
 13  # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 
 14  # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 
 15   
 16  import os 
 17  import random 
 18  import time 
 19  from ._compat import long, binary_type 
 20  try: 
 21      import threading as _threading 
 22  except ImportError: 
 23      import dummy_threading as _threading 
 24   
 25   
26 -class EntropyPool(object):
27
28 - def __init__(self, seed=None):
29 self.pool_index = 0 30 self.digest = None 31 self.next_byte = 0 32 self.lock = _threading.Lock() 33 try: 34 import hashlib 35 self.hash = hashlib.sha1() 36 self.hash_len = 20 37 except ImportError: 38 try: 39 import sha 40 self.hash = sha.new() 41 self.hash_len = 20 42 except ImportError: 43 import md5 # pylint: disable=import-error 44 self.hash = md5.new() 45 self.hash_len = 16 46 self.pool = bytearray(b'\0' * self.hash_len) 47 if seed is not None: 48 self.stir(bytearray(seed)) 49 self.seeded = True 50 self.seed_pid = os.getpid() 51 else: 52 self.seeded = False 53 self.seed_pid = 0
54
55 - def stir(self, entropy, already_locked=False):
56 if not already_locked: 57 self.lock.acquire() 58 try: 59 for c in entropy: 60 if self.pool_index == self.hash_len: 61 self.pool_index = 0 62 b = c & 0xff 63 self.pool[self.pool_index] ^= b 64 self.pool_index += 1 65 finally: 66 if not already_locked: 67 self.lock.release()
68
69 - def _maybe_seed(self):
70 if not self.seeded or self.seed_pid != os.getpid(): 71 try: 72 seed = os.urandom(16) 73 except Exception: 74 try: 75 r = open('/dev/urandom', 'rb', 0) 76 try: 77 seed = r.read(16) 78 finally: 79 r.close() 80 except Exception: 81 seed = str(time.time()) 82 self.seeded = True 83 self.seed_pid = os.getpid() 84 self.digest = None 85 seed = bytearray(seed) 86 self.stir(seed, True)
87
88 - def random_8(self):
89 self.lock.acquire() 90 try: 91 self._maybe_seed() 92 if self.digest is None or self.next_byte == self.hash_len: 93 self.hash.update(binary_type(self.pool)) 94 self.digest = bytearray(self.hash.digest()) 95 self.stir(self.digest, True) 96 self.next_byte = 0 97 value = self.digest[self.next_byte] 98 self.next_byte += 1 99 finally: 100 self.lock.release() 101 return value
102
103 - def random_16(self):
104 return self.random_8() * 256 + self.random_8()
105
106 - def random_32(self):
107 return self.random_16() * 65536 + self.random_16()
108
109 - def random_between(self, first, last):
110 size = last - first + 1 111 if size > long(4294967296): 112 raise ValueError('too big') 113 if size > 65536: 114 rand = self.random_32 115 max = long(4294967295) 116 elif size > 256: 117 rand = self.random_16 118 max = 65535 119 else: 120 rand = self.random_8 121 max = 255 122 return first + size * rand() // (max + 1)
123 124 pool = EntropyPool() 125 126 try: 127 system_random = random.SystemRandom() 128 except Exception: 129 system_random = None 130
131 -def random_16():
132 if system_random is not None: 133 return system_random.randrange(0, 65536) 134 else: 135 return pool.random_16()
136
137 -def between(first, last):
138 if system_random is not None: 139 return system_random.randrange(first, last + 1) 140 else: 141 return pool.random_between(first, last)
142