]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - Crypto/src/winrand.c
8c82bd45ea245940d13c76eb7f45f2f6ca66616f
[tahoe-lafs/tahoe-lafs.git] / Crypto / src / winrand.c
1 /* -*- C -*- */
2 /*
3  * Uses Windows CryptoAPI CryptGenRandom to get random bytes.
4  * The "new" method returns an object, whose "get_bytes" method
5  * can be called repeatedly to get random bytes, seeded by the
6  * OS.  See the description in the comment at the end.
7  * 
8  * If you have the Intel Security Driver header files (icsp4ms.h)
9  * for their hardware random number generator in the 810 and 820 chipsets,
10  * then define HAVE_INTEL_RNG.
11  *
12  * Distribute and use freely; there are no restrictions on further 
13  * dissemination and usage except those imposed by the laws of your 
14  * country of residence.  This software is provided "as is" without
15  * warranty of fitness for use or suitability for any purpose, express
16  * or implied. Use at your own risk or not at all. 
17  *
18  */
19
20 /* Author: Mark Moraes */
21
22 #include "Python.h"
23
24 #ifdef MS_WIN32
25
26 #define _WIN32_WINNT 0x400
27 #define WINSOCK
28
29 #include <windows.h>
30 #include <wincrypt.h>
31
32 #ifdef HAVE_INTEL_RNG
33 # include "icsp4ms.h"
34 #else
35 # define PROV_INTEL_SEC 22
36 # define INTEL_DEF_PROV "Intel Hardware Cryptographic Service Provider"
37 #endif
38
39 /* To-Do: store provider name and type for print/repr? */
40 typedef struct
41 {
42     PyObject_HEAD
43     HCRYPTPROV hcp;
44 } WRobject;
45
46 staticforward PyTypeObject WRtype;
47
48 #define is_WRobject(v) ((v)->ob_type == &WRtype)
49
50 static void
51 WRdealloc(PyObject *ptr)
52 {               
53         WRobject *o = (WRobject *)ptr;
54
55         if (! is_WRobject(ptr)) {
56                 PyErr_Format(PyExc_TypeError,
57                     "WinRandom trying to dealloc non-WinRandom object");
58                 return;
59         }
60         if (! CryptReleaseContext(o->hcp, 0)) {
61                 PyErr_Format(PyExc_SystemError,
62                              "CryptReleaseContext failed, error 0x%x",
63                              GetLastError());
64                 return;
65         }
66         /* Overwrite the contents of the object */
67         o->hcp = 0;
68         PyObject_Del(ptr);
69 }
70
71 static char winrandom__doc__[] =
72 "new([provider], [provtype]): Returns an object handle to Windows\n\
73 CryptoAPI that can be used to access a cryptographically strong\n\
74 pseudo-random generator that uses OS-gathered entropy.\n\
75 Provider is a string that specifies the Cryptographic Service Provider\n\
76 to use, default is the default OS CSP.\n\
77 provtype is an integer specifying the provider type to use, default\n\
78 is 1 (PROV_RSA_FULL)";
79
80 static char WR_get_bytes__doc__[] =
81 "get_bytes(nbytes, [userdata]]): Returns nbytes of random data\n\
82 from Windows CryptGenRandom.\n\
83 userdata is a string with any additional entropic data that the\n\
84 user wishes to provide.";
85
86 static WRobject *
87 winrandom_new(PyObject *self, PyObject *args, PyObject *kwdict)
88 {
89         HCRYPTPROV hcp = 0;
90         WRobject *res;
91         char *provname = NULL;
92         int provtype = PROV_RSA_FULL;
93         static char *kwlist[] = { "provider", "provtype", NULL};
94         
95         if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|si", kwlist,
96                                          &provname, &provtype)) {
97                 return NULL;
98         }
99         if (! CryptAcquireContext(&hcp, NULL, (LPCTSTR) provname,
100                                   (DWORD) provtype, 0)) {
101                 PyErr_Format(PyExc_SystemError,
102                              "CryptAcquireContext for provider \"%s\" type %i failed, error 0x%x",
103                              provname? provname : "(null)", provtype,
104                              GetLastError());
105                 return NULL;
106         }
107         res = PyObject_New(WRobject, &WRtype);
108         res->hcp = hcp;
109         return res;
110 }
111
112 static PyObject *
113 WR_get_bytes(WRobject *self, PyObject *args)
114 {
115         HCRYPTPROV hcp = 0;
116         int n, nbytes, len = 0;
117         PyObject *res;
118         char *buf, *str = NULL;
119         
120         if (! is_WRobject(self)) {
121                 PyErr_Format(PyExc_TypeError,
122                     "WinRandom trying to get_bytes with non-WinRandom object");
123                 return NULL;
124         }
125         if (!PyArg_ParseTuple(args, "i|s#", &n, &str, &len)) {
126                 return NULL;
127         }
128         if (n <= 0) {
129                 PyErr_SetString(PyExc_ValueError, "nbytes must be positive number");
130                 return NULL;
131         }
132         /* Just in case char != BYTE, or userdata > desired result */
133         nbytes = (((n > len) ? n : len) * sizeof(char)) / sizeof(BYTE) + 1;
134         if ((buf = (char *) PyMem_Malloc(nbytes)) == NULL)
135             return PyErr_NoMemory();
136         if (len > 0)
137                 memcpy(buf, str, len);
138         /*
139          * if userdata > desired result, we end up getting
140          * more bytes than we really needed to return.  No
141          * easy way to avoid that: we prefer that
142          * CryptGenRandom does the distillation of userdata
143          * down to entropy, rather than trying to do it
144          * ourselves.  Since the extra bytes presumably come
145          * from an RC4 stream, they should be relatively
146          * cheap.
147          */
148         if (! CryptGenRandom(self->hcp, (DWORD) nbytes, (BYTE *) buf)) {
149                 PyErr_Format(PyExc_SystemError,
150                              "CryptGenRandom failed, error 0x%x",
151                              GetLastError());
152                 PyMem_Free(buf);
153                 return NULL;
154         }
155         res = PyString_FromStringAndSize(buf, n);
156         PyMem_Free(buf);
157         return res;
158 }
159
160 /* WinRandom object methods */
161
162 static PyMethodDef WRmethods[] =
163 {
164         {"get_bytes", (PyCFunction) WR_get_bytes, METH_VARARGS,
165                 WR_get_bytes__doc__},
166         {NULL, NULL}                    /* sentinel */
167 };
168
169 /* winrandom module methods */
170
171 static PyMethodDef WR_mod_methods[] = {
172         {"new", (PyCFunction) winrandom_new, METH_VARARGS|METH_KEYWORDS,
173                 winrandom__doc__},
174         {NULL,      NULL}        /* Sentinel */
175 };
176
177
178 static PyObject *
179 WRgetattr(PyObject *s, char *name)
180 {
181         WRobject *self = (WRobject*)s;
182         if (! is_WRobject(self)) {
183                 PyErr_Format(PyExc_TypeError,
184                     "WinRandom trying to getattr with non-WinRandom object");
185                 return NULL;
186         }
187         if (strcmp(name, "hcp") == 0)
188                 return PyInt_FromLong((long) self->hcp);
189         return Py_FindMethod(WRmethods, (PyObject *) self, name);
190 }
191
192 static PyTypeObject WRtype =
193 {
194         PyObject_HEAD_INIT(NULL)
195         0,                      /*ob_size*/
196         "winrandom.WinRandom",  /*tp_name*/
197         sizeof(WRobject),       /*tp_size*/
198         0,                      /*tp_itemsize*/
199         /* methods */
200         WRdealloc,              /*tp_dealloc*/
201         0,                      /*tp_print*/
202         WRgetattr,              /*tp_getattr*/
203 };
204
205 void
206 initwinrandom()
207 {
208         PyObject *m;
209         WRtype.ob_type = &PyType_Type;
210         m = Py_InitModule("winrandom", WR_mod_methods);
211
212         /* define Windows CSP Provider Types */
213 #ifdef PROV_RSA_FULL
214         PyModule_AddIntConstant(m, "PROV_RSA_FULL", PROV_RSA_FULL);
215 #endif
216 #ifdef PROV_RSA_SIG
217         PyModule_AddIntConstant(m, "PROV_RSA_SIG", PROV_RSA_SIG);
218 #endif
219 #ifdef PROV_DSS
220         PyModule_AddIntConstant(m, "PROV_DSS", PROV_DSS);
221 #endif
222 #ifdef PROV_FORTEZZA
223         PyModule_AddIntConstant(m, "PROV_FORTEZZA", PROV_FORTEZZA);
224 #endif
225 #ifdef PROV_MS_EXCHANGE
226         PyModule_AddIntConstant(m, "PROV_MS_EXCHANGE", PROV_MS_EXCHANGE);
227 #endif
228 #ifdef PROV_SSL
229         PyModule_AddIntConstant(m, "PROV_SSL", PROV_SSL);
230 #endif
231 #ifdef PROV_RSA_SCHANNEL
232         PyModule_AddIntConstant(m, "PROV_RSA_SCHANNEL", PROV_RSA_SCHANNEL);
233 #endif
234 #ifdef PROV_DSS_DH
235         PyModule_AddIntConstant(m, "PROV_DSS_DH", PROV_DSS_DH);
236 #endif
237 #ifdef PROV_EC_ECDSA_SIG
238         PyModule_AddIntConstant(m, "PROV_EC_ECDSA_SIG", PROV_EC_ECDSA_SIG);
239 #endif
240 #ifdef PROV_EC_ECNRA_SIG
241         PyModule_AddIntConstant(m, "PROV_EC_ECNRA_SIG", PROV_EC_ECNRA_SIG);
242 #endif
243 #ifdef PROV_EC_ECDSA_FULL
244         PyModule_AddIntConstant(m, "PROV_EC_ECDSA_FULL", PROV_EC_ECDSA_FULL);
245 #endif
246 #ifdef PROV_EC_ECNRA_FULL
247         PyModule_AddIntConstant(m, "PROV_EC_ECNRA_FULL", PROV_EC_ECNRA_FULL);
248 #endif
249 #ifdef PROV_SPYRUS_LYNKS
250         PyModule_AddIntConstant(m, "PROV_SPYRUS_LYNKS", PROV_SPYRUS_LYNKS);
251 #endif
252 #ifdef PROV_INTEL_SEC
253         PyModule_AddIntConstant(m, "PROV_INTEL_SEC", PROV_INTEL_SEC);
254 #endif
255
256         /* Define Windows CSP Provider Names */
257 #ifdef MS_DEF_PROV
258         PyModule_AddStringConstant(m, "MS_DEF_PROV", MS_DEF_PROV);
259 #endif
260 #ifdef MS_ENHANCED_PROV
261         PyModule_AddStringConstant(m, "MS_ENHANCED_PROV", MS_ENHANCED_PROV);
262 #endif
263 #ifdef MS_DEF_RSA_SIG_PROV
264         PyModule_AddStringConstant(m, "MS_DEF_RSA_SIG_PROV",
265                                    MS_DEF_RSA_SIG_PROV);
266 #endif
267 #ifdef MS_DEF_RSA_SCHANNEL_PROV
268         PyModule_AddStringConstant(m, "MS_DEF_RSA_SCHANNEL_PROV",
269                                    MS_DEF_RSA_SCHANNEL_PROV);
270 #endif
271 #ifdef MS_ENHANCED_RSA_SCHANNEL_PROV
272         PyModule_AddStringConstant(m, "MS_ENHANCED_RSA_SCHANNEL_PROV",
273                                    MS_ENHANCED_RSA_SCHANNEL_PROV);
274 #endif
275 #ifdef MS_DEF_DSS_PROV
276         PyModule_AddStringConstant(m, "MS_DEF_DSS_PROV", MS_DEF_DSS_PROV);
277 #endif
278 #ifdef MS_DEF_DSS_DH_PROV
279         PyModule_AddStringConstant(m, "MS_DEF_DSS_DH_PROV",
280                                    MS_DEF_DSS_DH_PROV);
281 #endif
282 #ifdef INTEL_DEF_PROV
283         PyModule_AddStringConstant(m, "INTEL_DEF_PROV", INTEL_DEF_PROV);
284 #endif
285
286         if (PyErr_Occurred())
287                 Py_FatalError("can't initialize module winrandom");
288 }
289
290 /*
291
292 CryptGenRandom usage is described in
293 http://msdn.microsoft.com/library/en-us/security/security/cryptgenrandom.asp
294 and many associated pages on Windows Cryptographic Service
295 Providers, which say:
296
297         With Microsoft CSPs, CryptGenRandom uses the same
298         random number generator used by other security
299         components. This allows numerous processes to
300         contribute to a system-wide seed. CryptoAPI stores
301         an intermediate random seed with every user. To form
302         the seed for the random number generator, a calling
303         application supplies bits it might havefor instance,
304         mouse or keyboard timing inputthat are then added to
305         both the stored seed and various system data and
306         user data such as the process ID and thread ID, the
307         system clock, the system time, the system counter,
308         memory status, free disk clusters, the hashed user
309         environment block. This result is SHA-1 hashed, and
310         the output is used to seed an RC4 stream, which is
311         then used as the random stream and used to update
312         the stored seed.
313
314 The only other detailed description I've found of the
315 sources of randomness for CryptGenRandom is this excerpt
316 from a posting
317 http://www.der-keiler.de/Newsgroups/comp.security.ssh/2002-06/0169.html
318
319 From: Jon McClelland (dowot69@hotmail.com) 
320 Date: 06/12/02 
321 ... 
322  
323 Windows, call a function such as CryptGenRandom, which has two of 
324 the properties of a good random number generator, unpredictability and 
325 even value distribution. This function, declared in Wincrypt.h, is 
326 available on just about every Windows platform, including Windows 95 
327 with Internet Explorer 3.02 or later, Windows 98, Windows Me, Windows 
328 CE v3, Windows NT 4, Windows 2000, and Windows XP. 
329  
330 CryptGenRandom gets its randomness, also known as entropy, from many 
331 sources in Windows 2000, including the following: 
332 The current process ID (GetCurrentProcessID). 
333 The current thread ID (GetCurrentThreadID). 
334 The ticks since boot (GetTickCount). 
335 The current time (GetLocalTime). 
336 Various high-precision performance counters (QueryPerformanceCounter). 
337 A Message Digest 4 (MD4) hash of the user's environment block, which 
338 includes username, computer name, and search path. 
339  
340 High-precision internal CPU counters, such as RDTSC, RDMSR, RDPMC (x86 
341 only-more information about these counters is at 
342 developer.intel.com/software/idap/resources/technical_collateral/pentiumii/RDTSCPM1.HTM 
343 <http://developer.intel.com>). 
344  
345 Low-level system information, such as idle time, kernel time, 
346 interrupt times, commit limit, page read count, cache read count, 
347 nonpaged pool allocations, alignment fixup count, operating system 
348 lookaside information. 
349  
350 Such information is added to a buffer, which is hashed using MD4 and 
351 used as the key to modify a buffer, using RC4, provided by the user. 
352 (Refer to the CryptGenRandom documentation in the Platform SDK for 
353 more information about the user-provided buffer.) Hence, if the user 
354 provides additional data in the buffer, this is used as an element in 
355 the witches brew to generate the random data. The result is a 
356 cryptographically random number generator. 
357 Also, note that if you plan to sell your software to the United States 
358 federal government, you'll need to use FIPS 140-1-approved algorithms. 
359 The default versions of CryptGenRandom in Microsoft Windows CE v3, 
360 Windows 95, Windows 98, Windows Me, Windows 2000, and Windows XP are 
361 FIPS-approved. Obviously FIPS-140 compliance is necessary but not 
362 sufficient to provide a properly secure source of random data. 
363  
364 */
365
366 #endif /* MS_WIN32 */