OLD | NEW |
| (Empty) |
1 # -*- test-case-name: twisted.test.test_randbytes -*- | |
2 # Copyright (c) 2007 Twisted Matrix Laboratories. | |
3 # See LICENSE for details. | |
4 | |
5 """ | |
6 Cryptographically secure random implementation, with fallback on normal random. | |
7 """ | |
8 | |
9 # System imports | |
10 import warnings, os, random | |
11 | |
12 getrandbits = getattr(random, 'getrandbits', None) | |
13 | |
14 try: | |
15 from Crypto.Util import randpool | |
16 except ImportError: | |
17 randpool = None | |
18 | |
19 | |
20 | |
21 class SecureRandomNotAvailable(RuntimeError): | |
22 """ | |
23 Exception raised when no secure random algorithm is found. | |
24 """ | |
25 | |
26 | |
27 | |
28 class SourceNotAvailable(RuntimeError): | |
29 """ | |
30 Internal exception used when a specific random source is not available. | |
31 """ | |
32 | |
33 | |
34 | |
35 class RandomFactory(object): | |
36 """ | |
37 Factory providing L{secureRandom} and L{insecureRandom} methods. | |
38 | |
39 You shouldn't have to instantiate this class, use the module level | |
40 functions instead: it is an implementation detail and could be removed or | |
41 changed arbitrarily. | |
42 | |
43 @ivar randomPool: pool of random data. | |
44 @type randomPool: C{randpool.RandomPool} | |
45 | |
46 @cvar randomSources: list of file sources used when os.urandom is not | |
47 available. | |
48 @type randomSources: C{tuple} | |
49 """ | |
50 randpool = randpool | |
51 randomPool = None | |
52 randomSources = ('/dev/urandom',) | |
53 getrandbits = getrandbits | |
54 | |
55 | |
56 def _osUrandom(self, nbytes): | |
57 """ | |
58 Wrapper around C{os.urandom} that cleanly manage its absence. | |
59 """ | |
60 try: | |
61 return os.urandom(nbytes) | |
62 except (AttributeError, NotImplementedError), e: | |
63 raise SourceNotAvailable(e) | |
64 | |
65 | |
66 def _fileUrandom(self, nbytes): | |
67 """ | |
68 Wrapper around random file sources. | |
69 | |
70 This method isn't meant to be call out of the class and could be | |
71 removed arbitrarily. | |
72 """ | |
73 for src in self.randomSources: | |
74 try: | |
75 f = file(src, 'rb') | |
76 except (IOError, OSError): | |
77 pass | |
78 else: | |
79 bytes = f.read(nbytes) | |
80 f.close() | |
81 return bytes | |
82 raise SourceNotAvailable("File sources not available: %s" % | |
83 (self.randomSources,)) | |
84 | |
85 | |
86 def _cryptoRandom(self, nbytes): | |
87 """ | |
88 Wrapper around Crypo random pool. | |
89 """ | |
90 if self.randpool is not None: | |
91 if self.randomPool is None: | |
92 self.randomPool = self.randpool.RandomPool() | |
93 self.randomPool.stir() | |
94 return self.randomPool.get_bytes(nbytes) | |
95 raise SourceNotAvailable("PyCrypto isn't installed") | |
96 | |
97 | |
98 def secureRandom(self, nbytes, fallback=False): | |
99 """ | |
100 Return a number of secure random bytes. | |
101 | |
102 @param nbytes: number of bytes to generate. | |
103 @type nbytes: C{int}. | |
104 @param fallback: whether the function should fallback on non secure | |
105 random or not. Default to C{false}. | |
106 @type fallback: C{bool} | |
107 | |
108 @return: a string of random bytes. | |
109 @rtype: C{str}. | |
110 """ | |
111 for src in ("_osUrandom", "_fileUrandom", "_cryptoRandom"): | |
112 try: | |
113 return getattr(self, src)(nbytes) | |
114 except SourceNotAvailable: | |
115 pass | |
116 if fallback: | |
117 warnings.warn( | |
118 "Neither PyCrypto nor urandom available - " | |
119 "proceeding with non-cryptographically secure random source", | |
120 category=RuntimeWarning, | |
121 stacklevel=2 | |
122 ) | |
123 return self.insecureRandom(nbytes) | |
124 else: | |
125 raise SecureRandomNotAvailable("No secure random source available") | |
126 | |
127 | |
128 def _randBits(self, nbytes): | |
129 """ | |
130 Wrapper around C{os.getrandbits}. | |
131 """ | |
132 if self.getrandbits is not None: | |
133 n = self.getrandbits(nbytes * 8) | |
134 hexBytes = ("%%0%dx" % (nbytes * 2)) % n | |
135 return hexBytes.decode('hex') | |
136 raise SourceNotAvailable("random.getrandbits is not available") | |
137 | |
138 | |
139 def _randRange(self, nbytes): | |
140 """ | |
141 Wrapper around C{random.randrange}. | |
142 """ | |
143 bytes = "" | |
144 for i in xrange(nbytes): | |
145 bytes += chr(random.randrange(0, 255)) | |
146 return bytes | |
147 | |
148 | |
149 def insecureRandom(self, nbytes): | |
150 """ | |
151 Return a number of non secure random bytes. | |
152 | |
153 @param nbytes: number of bytes to generate. | |
154 @type nbytes: C{int}. | |
155 | |
156 @return: a string of random bytes. | |
157 @rtype: C{str}. | |
158 """ | |
159 for src in ("_randBits", "_randRange"): | |
160 try: | |
161 return getattr(self, src)(nbytes) | |
162 except SourceNotAvailable: | |
163 pass | |
164 | |
165 | |
166 | |
167 factory = RandomFactory() | |
168 | |
169 secureRandom = factory.secureRandom | |
170 | |
171 insecureRandom = factory.insecureRandom | |
172 | |
173 del factory | |
174 | |
175 | |
176 __all__ = ["secureRandom", "insecureRandom", "SecureRandomNotAvailable"] | |
177 | |
OLD | NEW |