You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
4.7 KiB

4 years ago
  1. # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
  2. # Copyright (C) 2009-2017 Nominum, Inc.
  3. #
  4. # Permission to use, copy, modify, and distribute this software and its
  5. # documentation for any purpose with or without fee is hereby granted,
  6. # provided that the above copyright notice and this permission notice
  7. # appear in all copies.
  8. #
  9. # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
  10. # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
  12. # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14. # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
  15. # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. import os
  17. import random
  18. import time
  19. from ._compat import long, binary_type
  20. try:
  21. import threading as _threading
  22. except ImportError:
  23. import dummy_threading as _threading
  24. class EntropyPool(object):
  25. # This is an entropy pool for Python implementations that do not
  26. # have a working SystemRandom. I'm not sure there are any, but
  27. # leaving this code doesn't hurt anything as the library code
  28. # is used if present.
  29. def __init__(self, seed=None):
  30. self.pool_index = 0
  31. self.digest = None
  32. self.next_byte = 0
  33. self.lock = _threading.Lock()
  34. try:
  35. import hashlib
  36. self.hash = hashlib.sha1()
  37. self.hash_len = 20
  38. except ImportError:
  39. try:
  40. import sha
  41. self.hash = sha.new()
  42. self.hash_len = 20
  43. except ImportError:
  44. import md5 # pylint: disable=import-error
  45. self.hash = md5.new()
  46. self.hash_len = 16
  47. self.pool = bytearray(b'\0' * self.hash_len)
  48. if seed is not None:
  49. self.stir(bytearray(seed))
  50. self.seeded = True
  51. self.seed_pid = os.getpid()
  52. else:
  53. self.seeded = False
  54. self.seed_pid = 0
  55. def stir(self, entropy, already_locked=False):
  56. if not already_locked:
  57. self.lock.acquire()
  58. try:
  59. for c in entropy:
  60. if self.pool_index == self.hash_len:
  61. self.pool_index = 0
  62. b = c & 0xff
  63. self.pool[self.pool_index] ^= b
  64. self.pool_index += 1
  65. finally:
  66. if not already_locked:
  67. self.lock.release()
  68. def _maybe_seed(self):
  69. if not self.seeded or self.seed_pid != os.getpid():
  70. try:
  71. seed = os.urandom(16)
  72. except Exception:
  73. try:
  74. r = open('/dev/urandom', 'rb', 0)
  75. try:
  76. seed = r.read(16)
  77. finally:
  78. r.close()
  79. except Exception:
  80. seed = str(time.time())
  81. self.seeded = True
  82. self.seed_pid = os.getpid()
  83. self.digest = None
  84. seed = bytearray(seed)
  85. self.stir(seed, True)
  86. def random_8(self):
  87. self.lock.acquire()
  88. try:
  89. self._maybe_seed()
  90. if self.digest is None or self.next_byte == self.hash_len:
  91. self.hash.update(binary_type(self.pool))
  92. self.digest = bytearray(self.hash.digest())
  93. self.stir(self.digest, True)
  94. self.next_byte = 0
  95. value = self.digest[self.next_byte]
  96. self.next_byte += 1
  97. finally:
  98. self.lock.release()
  99. return value
  100. def random_16(self):
  101. return self.random_8() * 256 + self.random_8()
  102. def random_32(self):
  103. return self.random_16() * 65536 + self.random_16()
  104. def random_between(self, first, last):
  105. size = last - first + 1
  106. if size > long(4294967296):
  107. raise ValueError('too big')
  108. if size > 65536:
  109. rand = self.random_32
  110. max = long(4294967295)
  111. elif size > 256:
  112. rand = self.random_16
  113. max = 65535
  114. else:
  115. rand = self.random_8
  116. max = 255
  117. return first + size * rand() // (max + 1)
  118. pool = EntropyPool()
  119. try:
  120. system_random = random.SystemRandom()
  121. except Exception:
  122. system_random = None
  123. def random_16():
  124. if system_random is not None:
  125. return system_random.randrange(0, 65536)
  126. else:
  127. return pool.random_16()
  128. def between(first, last):
  129. if system_random is not None:
  130. return system_random.randrange(first, last + 1)
  131. else:
  132. return pool.random_between(first, last)