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)
|
---|