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.

96 lines
2.6 KiB

  1. # Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # https://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Functions for generating random numbers."""
  15. # Source inspired by code by Yesudeep Mangalapilly <yesudeep@gmail.com>
  16. import os
  17. import struct
  18. from rsa import common, transform
  19. def read_random_bits(nbits: int) -> bytes:
  20. """Reads 'nbits' random bits.
  21. If nbits isn't a whole number of bytes, an extra byte will be appended with
  22. only the lower bits set.
  23. """
  24. nbytes, rbits = divmod(nbits, 8)
  25. # Get the random bytes
  26. randomdata = os.urandom(nbytes)
  27. # Add the remaining random bits
  28. if rbits > 0:
  29. randomvalue = ord(os.urandom(1))
  30. randomvalue >>= (8 - rbits)
  31. randomdata = struct.pack("B", randomvalue) + randomdata
  32. return randomdata
  33. def read_random_int(nbits: int) -> int:
  34. """Reads a random integer of approximately nbits bits.
  35. """
  36. randomdata = read_random_bits(nbits)
  37. value = transform.bytes2int(randomdata)
  38. # Ensure that the number is large enough to just fill out the required
  39. # number of bits.
  40. value |= 1 << (nbits - 1)
  41. return value
  42. def read_random_odd_int(nbits: int) -> int:
  43. """Reads a random odd integer of approximately nbits bits.
  44. >>> read_random_odd_int(512) & 1
  45. 1
  46. """
  47. value = read_random_int(nbits)
  48. # Make sure it's odd
  49. return value | 1
  50. def randint(maxvalue: int) -> int:
  51. """Returns a random integer x with 1 <= x <= maxvalue
  52. May take a very long time in specific situations. If maxvalue needs N bits
  53. to store, the closer maxvalue is to (2 ** N) - 1, the faster this function
  54. is.
  55. """
  56. bit_size = common.bit_size(maxvalue)
  57. tries = 0
  58. while True:
  59. value = read_random_int(bit_size)
  60. if value <= maxvalue:
  61. break
  62. if tries % 10 == 0 and tries:
  63. # After a lot of tries to get the right number of bits but still
  64. # smaller than maxvalue, decrease the number of bits by 1. That'll
  65. # dramatically increase the chances to get a large enough number.
  66. bit_size -= 1
  67. tries += 1
  68. return value