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 time
     18  try:
     19      import threading as _threading
     20  except ImportError:
     21      import dummy_threading as _threading
     22
     23 -class EntropyPool(object):
     24 -    def __init__(self, seed=None):
     25          self.pool_index = 0
     26          self.digest = None
     27          self.next_byte = 0
     28          self.lock = _threading.Lock()
     29          try:
     30              import hashlib
     31              self.hash = hashlib.sha1()
     32              self.hash_len = 20
     33          except:
     34              try:
     35                  import sha
     36                  self.hash = sha.new()
     37                  self.hash_len = 20
     38              except:
     39                  import md5
     40                  self.hash = md5.new()
     41                  self.hash_len = 16
     42          self.pool = '\0' * self.hash_len
     43          if not seed is None:
     44              self.stir(seed)
     45              self.seeded = True
     46          else:
     47              self.seeded = False
     48
     49 -    def stir(self, entropy, already_locked=False):
     50          if not already_locked:
     51              self.lock.acquire()
     52          try:
     53              bytes = [ord(c) for c in self.pool]
     54              for c in entropy:
     55                  if self.pool_index == self.hash_len:
     56                      self.pool_index = 0
     57                  b = ord(c) & 0xff
     58                  bytes[self.pool_index] ^= b
     59                  self.pool_index += 1
     60              self.pool = ''.join([chr(c) for c in bytes])
     61          finally:
     62              if not already_locked:
     63                  self.lock.release()
     64
     65 -    def _maybe_seed(self):
     66          if not self.seeded:
     67              try:
     68                  seed = os.urandom(16)
     69              except:
     70                  try:
     71                      r = file('/dev/urandom', 'r', 0)
     72                      try:
     73                          seed = r.read(16)
     74                      finally:
     75                          r.close()
     76                  except:
     77                      seed = str(time.time())
     78              self.seeded = True
     79              self.stir(seed, True)
     80
     81 -    def random_8(self):
     82          self.lock.acquire()
     83          self._maybe_seed()
     84          try:
     85              if self.digest is None or self.next_byte == self.hash_len:
     86                  self.hash.update(self.pool)
     87                  self.digest = self.hash.digest()
     88                  self.stir(self.digest, True)
     89                  self.next_byte = 0
     90              value = ord(self.digest[self.next_byte])
     91              self.next_byte += 1
     92          finally:
     93              self.lock.release()
     94          return value
     95
     96 -    def random_16(self):
     97          return self.random_8() * 256 + self.random_8()
     98
     99 -    def random_32(self):
    100          return self.random_16() * 65536 + self.random_16()
    101
    102 -    def random_between(self, first, last):
    103          size = last - first + 1
    104          if size > 4294967296L:
    105              raise ValueError('too big')
    106          if size > 65536:
    107              rand = self.random_32
    108              max = 4294967295L
    109          elif size > 256:
    110              rand = self.random_16
    111              max = 65535
    112          else:
    113              rand = self.random_8
    114              max = 255
    115          return (first + size * rand() // (max + 1))
    116
    117  pool = EntropyPool()
    118
    119 -def random_16():
    120      return pool.random_16()
    121
    122 -def between(first, last):
    123      return pool.random_between(first, last)
    124