| 24 | |
| 25 | |
| 26 | class EntropyPool: |
| 27 | # This is an entropy pool for Python implementations that do not |
| 28 | # have a working SystemRandom. I'm not sure there are any, but |
| 29 | # leaving this code doesn't hurt anything as the library code |
| 30 | # is used if present. |
| 31 | |
| 32 | def __init__(self, seed: bytes | None = None): |
| 33 | self.pool_index = 0 |
| 34 | self.digest: bytearray | None = None |
| 35 | self.next_byte = 0 |
| 36 | self.lock = threading.Lock() |
| 37 | self.hash = hashlib.sha1() |
| 38 | self.hash_len = 20 |
| 39 | self.pool = bytearray(b"\0" * self.hash_len) |
| 40 | if seed is not None: |
| 41 | self._stir(seed) |
| 42 | self.seeded = True |
| 43 | self.seed_pid = os.getpid() |
| 44 | else: |
| 45 | self.seeded = False |
| 46 | self.seed_pid = 0 |
| 47 | |
| 48 | def _stir(self, entropy: bytes | bytearray) -> None: |
| 49 | for c in entropy: |
| 50 | if self.pool_index == self.hash_len: |
| 51 | self.pool_index = 0 |
| 52 | b = c & 0xFF |
| 53 | self.pool[self.pool_index] ^= b |
| 54 | self.pool_index += 1 |
| 55 | |
| 56 | def stir(self, entropy: bytes | bytearray) -> None: |
| 57 | with self.lock: |
| 58 | self._stir(entropy) |
| 59 | |
| 60 | def _maybe_seed(self) -> None: |
| 61 | if not self.seeded or self.seed_pid != os.getpid(): |
| 62 | try: |
| 63 | seed = os.urandom(16) |
| 64 | except Exception: # pragma: no cover |
| 65 | try: |
| 66 | with open("/dev/urandom", "rb", 0) as r: |
| 67 | seed = r.read(16) |
| 68 | except Exception: |
| 69 | seed = str(time.time()).encode() |
| 70 | self.seeded = True |
| 71 | self.seed_pid = os.getpid() |
| 72 | self.digest = None |
| 73 | seed = bytearray(seed) |
| 74 | self._stir(seed) |
| 75 | |
| 76 | def random_8(self) -> int: |
| 77 | with self.lock: |
| 78 | self._maybe_seed() |
| 79 | if self.digest is None or self.next_byte == self.hash_len: |
| 80 | self.hash.update(bytes(self.pool)) |
| 81 | self.digest = bytearray(self.hash.digest()) |
| 82 | self._stir(self.digest) |
| 83 | self.next_byte = 0 |
no outgoing calls
no test coverage detected
searching dependent graphs…