Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/Crypto/Random/random.py: 30%

80 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 07:03 +0000

1# -*- coding: utf-8 -*- 

2# 

3# Random/random.py : Strong alternative for the standard 'random' module 

4# 

5# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net> 

6# 

7# =================================================================== 

8# The contents of this file are dedicated to the public domain. To 

9# the extent that dedication to the public domain is not available, 

10# everyone is granted a worldwide, perpetual, royalty-free, 

11# non-exclusive license to exercise all rights associated with the 

12# contents of this file for any purpose whatsoever. 

13# No rights are reserved. 

14# 

15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 

16# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 

17# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 

18# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 

19# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 

20# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 

21# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 

22# SOFTWARE. 

23# =================================================================== 

24 

25"""A cryptographically strong version of Python's standard "random" module.""" 

26 

27__revision__ = "$Id$" 

28__all__ = ['StrongRandom', 'getrandbits', 'randrange', 'randint', 'choice', 'shuffle', 'sample'] 

29 

30from Crypto import Random 

31import sys 

32if sys.version_info[0] == 2 and sys.version_info[1] == 1: 

33 from Crypto.Util.py21compat import * 

34 

35class StrongRandom(object): 

36 def __init__(self, rng=None, randfunc=None): 

37 if randfunc is None and rng is None: 

38 self._randfunc = None 

39 elif randfunc is not None and rng is None: 

40 self._randfunc = randfunc 

41 elif randfunc is None and rng is not None: 

42 self._randfunc = rng.read 

43 else: 

44 raise ValueError("Cannot specify both 'rng' and 'randfunc'") 

45 

46 def getrandbits(self, k): 

47 """Return a python long integer with k random bits.""" 

48 if self._randfunc is None: 

49 self._randfunc = Random.new().read 

50 mask = (1 << k) - 1 

51 return mask & bytes_to_long(self._randfunc(ceil_div(k, 8))) 

52 

53 def randrange(self, *args): 

54 """randrange([start,] stop[, step]): 

55 Return a randomly-selected element from range(start, stop, step).""" 

56 if len(args) == 3: 

57 (start, stop, step) = args 

58 elif len(args) == 2: 

59 (start, stop) = args 

60 step = 1 

61 elif len(args) == 1: 

62 (stop,) = args 

63 start = 0 

64 step = 1 

65 else: 

66 raise TypeError("randrange expected at most 3 arguments, got %d" % (len(args),)) 

67 if (not isinstance(start, int) 

68 or not isinstance(stop, int) 

69 or not isinstance(step, int)): 

70 raise TypeError("randrange requires integer arguments") 

71 if step == 0: 

72 raise ValueError("randrange step argument must not be zero") 

73 

74 num_choices = ceil_div(stop - start, step) 

75 if num_choices < 0: 

76 num_choices = 0 

77 if num_choices < 1: 

78 raise ValueError("empty range for randrange(%r, %r, %r)" % (start, stop, step)) 

79 

80 # Pick a random number in the range of possible numbers 

81 r = num_choices 

82 while r >= num_choices: 

83 r = self.getrandbits(size(num_choices)) 

84 

85 return start + (step * r) 

86 

87 def randint(self, a, b): 

88 """Return a random integer N such that a <= N <= b.""" 

89 if not isinstance(a, int) or not isinstance(b, int): 

90 raise TypeError("randint requires integer arguments") 

91 N = self.randrange(a, b+1) 

92 assert a <= N <= b 

93 return N 

94 

95 def choice(self, seq): 

96 """Return a random element from a (non-empty) sequence. 

97 

98 If the seqence is empty, raises IndexError. 

99 """ 

100 if len(seq) == 0: 

101 raise IndexError("empty sequence") 

102 return seq[self.randrange(len(seq))] 

103 

104 def shuffle(self, x): 

105 """Shuffle the sequence in place.""" 

106 # Fisher-Yates shuffle. O(n) 

107 # See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle 

108 # Working backwards from the end of the array, we choose a random item 

109 # from the remaining items until all items have been chosen. 

110 for i in range(len(x)-1, 0, -1): # iterate from len(x)-1 downto 1 

111 j = self.randrange(0, i+1) # choose random j such that 0 <= j <= i 

112 x[i], x[j] = x[j], x[i] # exchange x[i] and x[j] 

113 

114 def sample(self, population, k): 

115 """Return a k-length list of unique elements chosen from the population sequence.""" 

116 

117 num_choices = len(population) 

118 if k > num_choices: 

119 raise ValueError("sample larger than population") 

120 

121 retval = [] 

122 selected = {} # we emulate a set using a dict here 

123 for i in range(k): 

124 r = None 

125 while r is None or r in selected: 

126 r = self.randrange(num_choices) 

127 retval.append(population[r]) 

128 selected[r] = 1 

129 return retval 

130 

131_r = StrongRandom() 

132getrandbits = _r.getrandbits 

133randrange = _r.randrange 

134randint = _r.randint 

135choice = _r.choice 

136shuffle = _r.shuffle 

137sample = _r.sample 

138 

139# These are at the bottom to avoid problems with recursive imports 

140from Crypto.Util.number import ceil_div, bytes_to_long, long_to_bytes, size 

141 

142# vim:set ts=4 sw=4 sts=4 expandtab: