WHATSNEW: Update release notes.
[Samba.git] / lib / dnspython / dns / entropy.py
blobd380cf8c0d0d609f911c18ee39642ef23cd6981b
1 # Copyright (C) 2009, 2011 Nominum, Inc.
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.
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.
16 import os
17 import time
18 try:
19 import threading as _threading
20 except ImportError:
21 import dummy_threading as _threading
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
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()
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)
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
96 def random_16(self):
97 return self.random_8() * 256 + self.random_8()
99 def random_32(self):
100 return self.random_16() * 65536 + self.random_16()
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))
117 pool = EntropyPool()
119 def random_16():
120 return pool.random_16()
122 def between(first, last):
123 return pool.random_between(first, last)