diff options
Diffstat (limited to 'lib/dnspython/dns/entropy.py')
-rw-r--r-- | lib/dnspython/dns/entropy.py | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/lib/dnspython/dns/entropy.py b/lib/dnspython/dns/entropy.py new file mode 100644 index 0000000000..fd9d4f8cdf --- /dev/null +++ b/lib/dnspython/dns/entropy.py @@ -0,0 +1,123 @@ +# Copyright (C) 2009 Nominum, Inc. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose with or without fee is hereby granted, +# provided that the above copyright notice and this permission notice +# appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import os +import time +try: + import threading as _threading +except ImportError: + import dummy_threading as _threading + +class EntropyPool(object): + def __init__(self, seed=None): + self.pool_index = 0 + self.digest = None + self.next_byte = 0 + self.lock = _threading.Lock() + try: + import hashlib + self.hash = hashlib.sha1() + self.hash_len = 20 + except: + try: + import sha + self.hash = sha.new() + self.hash_len = 20 + except: + import md5 + self.hash = md5.new() + self.hash_len = 16 + self.pool = '\0' * self.hash_len + if not seed is None: + self.stir(seed) + self.seeded = True + else: + self.seeded = False + + def stir(self, entropy, already_locked=False): + if not already_locked: + self.lock.acquire() + try: + bytes = [ord(c) for c in self.pool] + for c in entropy: + if self.pool_index == self.hash_len: + self.pool_index = 0 + b = ord(c) & 0xff + bytes[self.pool_index] ^= b + self.pool_index += 1 + self.pool = ''.join([chr(c) for c in bytes]) + finally: + if not already_locked: + self.lock.release() + + def _maybe_seed(self): + if not self.seeded: + try: + seed = os.urandom(16) + except: + try: + r = file('/dev/urandom', 'r', 0) + try: + seed = r.read(16) + finally: + r.close() + except: + seed = str(time.time()) + self.seeded = True + self.stir(seed, True) + + def random_8(self): + self.lock.acquire() + self._maybe_seed() + try: + if self.digest is None or self.next_byte == self.hash_len: + self.hash.update(self.pool) + self.digest = self.hash.digest() + self.stir(self.digest, True) + self.next_byte = 0 + value = ord(self.digest[self.next_byte]) + self.next_byte += 1 + finally: + self.lock.release() + return value + + def random_16(self): + return self.random_8() * 256 + self.random_8() + + def random_32(self): + return self.random_16() * 65536 + self.random_16() + + def random_between(self, first, last): + size = last - first + 1 + if size > 4294967296L: + raise ValueError('too big') + if size > 65536: + rand = self.random_32 + max = 4294967295L + elif size > 256: + rand = self.random_16 + max = 65535 + else: + rand = self.random_8 + max = 255 + return (first + size * rand() // (max + 1)) + +pool = EntropyPool() + +def random_16(): + return pool.random_16() + +def between(first, last): + return pool.random_between(first, last) |