source: python/vendor/current/Python/random.c

Last change on this file was 388, checked in by dmik, 11 years ago

python: Update vendor to 2.7.6.

  • Property svn:eol-style set to native
File size: 8.1 KB
Line 
1#include "Python.h"
2#ifdef MS_WINDOWS
3#include <windows.h>
4#else
5#include <fcntl.h>
6#endif
7
8#ifdef Py_DEBUG
9int _Py_HashSecret_Initialized = 0;
10#else
11static int _Py_HashSecret_Initialized = 0;
12#endif
13
14#ifdef MS_WINDOWS
15typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,\
16 LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\
17 DWORD dwFlags );
18typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,\
19 BYTE *pbBuffer );
20
21static CRYPTGENRANDOM pCryptGenRandom = NULL;
22/* This handle is never explicitly released. Instead, the operating
23 system will release it when the process terminates. */
24static HCRYPTPROV hCryptProv = 0;
25
26static int
27win32_urandom_init(int raise)
28{
29 HINSTANCE hAdvAPI32 = NULL;
30 CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL;
31
32 /* Obtain handle to the DLL containing CryptoAPI. This should not fail. */
33 hAdvAPI32 = GetModuleHandle("advapi32.dll");
34 if(hAdvAPI32 == NULL)
35 goto error;
36
37 /* Obtain pointers to the CryptoAPI functions. This will fail on some early
38 versions of Win95. */
39 pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(
40 hAdvAPI32, "CryptAcquireContextA");
41 if (pCryptAcquireContext == NULL)
42 goto error;
43
44 pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32,
45 "CryptGenRandom");
46 if (pCryptGenRandom == NULL)
47 goto error;
48
49 /* Acquire context */
50 if (! pCryptAcquireContext(&hCryptProv, NULL, NULL,
51 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
52 goto error;
53
54 return 0;
55
56error:
57 if (raise)
58 PyErr_SetFromWindowsErr(0);
59 else
60 Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
61 return -1;
62}
63
64/* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
65 API. Return 0 on success, or -1 on error. */
66static int
67win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
68{
69 Py_ssize_t chunk;
70
71 if (hCryptProv == 0)
72 {
73 if (win32_urandom_init(raise) == -1)
74 return -1;
75 }
76
77 while (size > 0)
78 {
79 chunk = size > INT_MAX ? INT_MAX : size;
80 if (!pCryptGenRandom(hCryptProv, chunk, buffer))
81 {
82 /* CryptGenRandom() failed */
83 if (raise)
84 PyErr_SetFromWindowsErr(0);
85 else
86 Py_FatalError("Failed to initialized the randomized hash "
87 "secret using CryptoGen)");
88 return -1;
89 }
90 buffer += chunk;
91 size -= chunk;
92 }
93 return 0;
94}
95#endif /* MS_WINDOWS */
96
97
98#ifdef __VMS
99/* Use openssl random routine */
100#include <openssl/rand.h>
101static int
102vms_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
103{
104 if (RAND_pseudo_bytes(buffer, size) < 0) {
105 if (raise) {
106 PyErr_Format(PyExc_ValueError,
107 "RAND_pseudo_bytes");
108 } else {
109 Py_FatalError("Failed to initialize the randomized hash "
110 "secret using RAND_pseudo_bytes");
111 }
112 return -1;
113 }
114 return 0;
115}
116#endif /* __VMS */
117
118
119#if !defined(MS_WINDOWS) && !defined(__VMS)
120
121/* Read size bytes from /dev/urandom into buffer.
122 Call Py_FatalError() on error. */
123static void
124dev_urandom_noraise(char *buffer, Py_ssize_t size)
125{
126 int fd;
127 Py_ssize_t n;
128
129 assert (0 < size);
130
131 fd = open("/dev/urandom", O_RDONLY);
132 if (fd < 0)
133 Py_FatalError("Failed to open /dev/urandom");
134
135 while (0 < size)
136 {
137 do {
138 n = read(fd, buffer, (size_t)size);
139 } while (n < 0 && errno == EINTR);
140 if (n <= 0)
141 {
142 /* stop on error or if read(size) returned 0 */
143 Py_FatalError("Failed to read bytes from /dev/urandom");
144 break;
145 }
146 buffer += n;
147 size -= (Py_ssize_t)n;
148 }
149 close(fd);
150}
151
152/* Read size bytes from /dev/urandom into buffer.
153 Return 0 on success, raise an exception and return -1 on error. */
154static int
155dev_urandom_python(char *buffer, Py_ssize_t size)
156{
157 int fd;
158 Py_ssize_t n;
159
160 if (size <= 0)
161 return 0;
162
163 Py_BEGIN_ALLOW_THREADS
164 fd = open("/dev/urandom", O_RDONLY);
165 Py_END_ALLOW_THREADS
166 if (fd < 0)
167 {
168 if (errno == ENOENT || errno == ENXIO ||
169 errno == ENODEV || errno == EACCES)
170 PyErr_SetString(PyExc_NotImplementedError,
171 "/dev/urandom (or equivalent) not found");
172 else
173 PyErr_SetFromErrno(PyExc_OSError);
174 return -1;
175 }
176
177 Py_BEGIN_ALLOW_THREADS
178 do {
179 do {
180 n = read(fd, buffer, (size_t)size);
181 } while (n < 0 && errno == EINTR);
182 if (n <= 0)
183 break;
184 buffer += n;
185 size -= (Py_ssize_t)n;
186 } while (0 < size);
187 Py_END_ALLOW_THREADS
188
189 if (n <= 0)
190 {
191 /* stop on error or if read(size) returned 0 */
192 if (n < 0)
193 PyErr_SetFromErrno(PyExc_OSError);
194 else
195 PyErr_Format(PyExc_RuntimeError,
196 "Failed to read %zi bytes from /dev/urandom",
197 size);
198 close(fd);
199 return -1;
200 }
201 close(fd);
202 return 0;
203}
204#endif /* !defined(MS_WINDOWS) && !defined(__VMS) */
205
206/* Fill buffer with pseudo-random bytes generated by a linear congruent
207 generator (LCG):
208
209 x(n+1) = (x(n) * 214013 + 2531011) % 2^32
210
211 Use bits 23..16 of x(n) to generate a byte. */
212static void
213lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
214{
215 size_t index;
216 unsigned int x;
217
218 x = x0;
219 for (index=0; index < size; index++) {
220 x *= 214013;
221 x += 2531011;
222 /* modulo 2 ^ (8 * sizeof(int)) */
223 buffer[index] = (x >> 16) & 0xff;
224 }
225}
226
227/* Fill buffer with size pseudo-random bytes from the operating system random
228 number generator (RNG). It is suitable for for most cryptographic purposes
229 except long living private keys for asymmetric encryption.
230
231 Return 0 on success, raise an exception and return -1 on error. */
232int
233_PyOS_URandom(void *buffer, Py_ssize_t size)
234{
235 if (size < 0) {
236 PyErr_Format(PyExc_ValueError,
237 "negative argument not allowed");
238 return -1;
239 }
240 if (size == 0)
241 return 0;
242
243#ifdef MS_WINDOWS
244 return win32_urandom((unsigned char *)buffer, size, 1);
245#else
246# ifdef __VMS
247 return vms_urandom((unsigned char *)buffer, size, 1);
248# else
249 return dev_urandom_python((char*)buffer, size);
250# endif
251#endif
252}
253
254void
255_PyRandom_Init(void)
256{
257 char *env;
258 void *secret = &_Py_HashSecret;
259 Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
260
261 if (_Py_HashSecret_Initialized)
262 return;
263 _Py_HashSecret_Initialized = 1;
264
265 /*
266 By default, hash randomization is disabled, and only
267 enabled if PYTHONHASHSEED is set to non-empty or if
268 "-R" is provided at the command line:
269 */
270 if (!Py_HashRandomizationFlag) {
271 /* Disable the randomized hash: */
272 memset(secret, 0, secret_size);
273 return;
274 }
275
276 /*
277 Hash randomization is enabled. Generate a per-process secret,
278 using PYTHONHASHSEED if provided.
279 */
280
281 env = Py_GETENV("PYTHONHASHSEED");
282 if (env && *env != '\0' && strcmp(env, "random") != 0) {
283 char *endptr = env;
284 unsigned long seed;
285 seed = strtoul(env, &endptr, 10);
286 if (*endptr != '\0'
287 || seed > 4294967295UL
288 || (errno == ERANGE && seed == ULONG_MAX))
289 {
290 Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
291 "in range [0; 4294967295]");
292 }
293 if (seed == 0) {
294 /* disable the randomized hash */
295 memset(secret, 0, secret_size);
296 }
297 else {
298 lcg_urandom(seed, (unsigned char*)secret, secret_size);
299 }
300 }
301 else {
302#ifdef MS_WINDOWS
303 (void)win32_urandom((unsigned char *)secret, secret_size, 0);
304#else /* #ifdef MS_WINDOWS */
305# ifdef __VMS
306 vms_urandom((unsigned char *)secret, secret_size, 0);
307# else
308 dev_urandom_noraise((char*)secret, secret_size);
309# endif
310#endif
311 }
312}
Note: See TracBrowser for help on using the repository browser.