root / trunk / twisted / python / randbytes.py

Revision 23950, 4.7 kB (checked in by exarkun, 1 year ago)

Merge basicauth-web-696-3

Author: dreid, exarkun
Reviewer: therve
Fixes: #696

Add HTTP Basic and Digest authentication support for twisted.web
in the form of a guard-style resource wrapper with a twisted.cred
backend.

DummyRequest has been expanded slightly to more closely resemble
the real request class. The long-since deprecated guard code in
twisted.web.guard has been removed and replaced with the new guard
API.

Line 
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
Note: See TracBrowser for help on using the browser.