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