| 1 | # Copyright (C) 2009 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)
|
|---|