/* -*- C -*- */ /* * Uses Windows CryptoAPI CryptGenRandom to get random bytes. * The "new" method returns an object, whose "get_bytes" method * can be called repeatedly to get random bytes, seeded by the * OS. See the description in the comment at the end. * * If you have the Intel Security Driver header files (icsp4ms.h) * for their hardware random number generator in the 810 and 820 chipsets, * then define HAVE_INTEL_RNG. * * ======================================================================= * The contents of this file are dedicated to the public domain. To the * extent that dedication to the public domain is not available, everyone * is granted a worldwide, perpetual, royalty-free, non-exclusive license * to exercise all rights associated with the contents of this file for * any purpose whatsoever. No rights are reserved. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * ======================================================================= * */ /* Author: Mark Moraes */ #include "Python.h" #include "pycrypto_compat.h" #ifdef MS_WIN32 #define _WIN32_WINNT 0x400 #define WINSOCK #include #include #ifdef HAVE_INTEL_RNG # include "icsp4ms.h" #else # define PROV_INTEL_SEC 22 # define INTEL_DEF_PROV "Intel Hardware Cryptographic Service Provider" #endif /* To-Do: store provider name and type for print/repr? */ typedef struct { PyObject_HEAD HCRYPTPROV hcp; } WRobject; /* Please see PEP3123 for a discussion of PyObject_HEAD and changes made in 3.x to make it conform to Standard C. * These changes also dictate using Py_TYPE to check type, and PyVarObject_HEAD_INIT(NULL, 0) to initialize */ #ifdef IS_PY3K static PyTypeObject WRtype; #define is_WRobject(v) (Py_TYPE(v) == &WRtype) #else staticforward PyTypeObject WRtype; #define is_WRobject(v) ((v)->ob_type == &WRtype) #define PyLong_FromLong PyInt_FromLong /* for Python 2.x */ #endif static void WRdealloc(PyObject *ptr) { WRobject *o = (WRobject *)ptr; if (! is_WRobject(ptr)) { PyErr_Format(PyExc_TypeError, "WinRandom trying to dealloc non-WinRandom object"); return; } if (! CryptReleaseContext(o->hcp, 0)) { PyErr_Format(PyExc_SystemError, "CryptReleaseContext failed, error 0x%x", (unsigned int) GetLastError()); return; } /* Overwrite the contents of the object */ o->hcp = 0; PyObject_Del(ptr); } static char winrandom__doc__[] = "new([provider], [provtype]): Returns an object handle to Windows\n\ CryptoAPI that can be used to access a cryptographically strong\n\ pseudo-random generator that uses OS-gathered entropy.\n\ Provider is a string that specifies the Cryptographic Service Provider\n\ to use, default is the default OS CSP.\n\ provtype is an integer specifying the provider type to use, default\n\ is 1 (PROV_RSA_FULL)"; static char WR_get_bytes__doc__[] = "get_bytes(nbytes, [userdata]]): Returns nbytes of random data\n\ from Windows CryptGenRandom.\n\ userdata is a string with any additional entropic data that the\n\ user wishes to provide."; static WRobject * winrandom_new(PyObject *self, PyObject *args, PyObject *kwdict) { HCRYPTPROV hcp = 0; WRobject *res; char *provname = NULL; int provtype = PROV_RSA_FULL; static char *kwlist[] = { "provider", "provtype", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|si", kwlist, &provname, &provtype)) { return NULL; } if (! CryptAcquireContext(&hcp, NULL, (LPCTSTR) provname, (DWORD) provtype, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { PyErr_Format(PyExc_SystemError, "CryptAcquireContext for provider \"%s\" type %i failed, error 0x%x", provname? provname : "(null)", provtype, (unsigned int) GetLastError()); return NULL; } res = PyObject_New(WRobject, &WRtype); res->hcp = hcp; return res; } static PyObject * WR_get_bytes(WRobject *self, PyObject *args) { int n, nbytes, len = 0; PyObject *res; char *buf, *str = NULL; if (! is_WRobject(self)) { PyErr_Format(PyExc_TypeError, "WinRandom trying to get_bytes with non-WinRandom object"); return NULL; } if (!PyArg_ParseTuple(args, "i|s#", &n, &str, &len)) { return NULL; } if (n <= 0) { PyErr_SetString(PyExc_ValueError, "nbytes must be positive number"); return NULL; } /* Just in case char != BYTE, or userdata > desired result */ nbytes = (((n > len) ? n : len) * sizeof(char)) / sizeof(BYTE) + 1; if ((buf = (char *) PyMem_Malloc(nbytes)) == NULL) return PyErr_NoMemory(); if (len > 0) memcpy(buf, str, len); /* * if userdata > desired result, we end up getting * more bytes than we really needed to return. No * easy way to avoid that: we prefer that * CryptGenRandom does the distillation of userdata * down to entropy, rather than trying to do it * ourselves. Since the extra bytes presumably come * from an RC4 stream, they should be relatively * cheap. */ if (! CryptGenRandom(self->hcp, (DWORD) nbytes, (BYTE *) buf)) { PyErr_Format(PyExc_SystemError, "CryptGenRandom failed, error 0x%x", (unsigned int) GetLastError()); PyMem_Free(buf); return NULL; } res = PyBytes_FromStringAndSize(buf, n); PyMem_Free(buf); return res; } /* WinRandom object methods */ static PyMethodDef WRmethods[] = { {"get_bytes", (PyCFunction) WR_get_bytes, METH_VARARGS, WR_get_bytes__doc__}, {NULL, NULL} /* sentinel */ }; /* winrandom module methods */ static PyMethodDef WR_mod_methods[] = { {"new", (PyCFunction) winrandom_new, METH_VARARGS|METH_KEYWORDS, winrandom__doc__}, {NULL, NULL} /* Sentinel */ }; static PyObject * #ifdef IS_PY3K WRgetattro(PyObject *s, PyObject *attr) #else WRgetattr(PyObject *s, char *name) #endif { WRobject *self = (WRobject*)s; if (! is_WRobject(self)) { PyErr_Format(PyExc_TypeError, "WinRandom trying to getattr with non-WinRandom object"); return NULL; } #ifdef IS_PY3K if (!PyUnicode_Check(attr)) goto generic; if (PyUnicode_CompareWithASCIIString(attr, "hcp") == 0) #else if (strcmp(name, "hcp") == 0) #endif return PyLong_FromLong((long) self->hcp); #ifdef IS_PY3K generic: return PyObject_GenericGetAttr(s, attr); #else return Py_FindMethod(WRmethods, (PyObject *) self, name); #endif } static PyTypeObject WRtype = { #ifdef IS_PY3K PyVarObject_HEAD_INIT(NULL, 0) /* deferred type init for compilation on Windows, type will be filled in at runtime */ #else PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ #endif "winrandom.WinRandom", /*tp_name*/ sizeof(WRobject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor) WRdealloc, /*tp_dealloc*/ 0, /*tp_print*/ #ifndef IS_PY3K WRgetattr, /*tp_getattr*/ #else 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number */ 0, /*tp_as_sequence */ 0, /*tp_as_mapping */ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ WRgetattro, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ 0, /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ 0, /*tp_iter*/ 0, /*tp_iternext*/ WRmethods, /*tp_methods*/ #endif }; #ifdef IS_PY3K static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "winrandom", NULL, -1, WR_mod_methods, NULL, NULL, NULL, NULL }; #endif #ifdef IS_PY3K PyMODINIT_FUNC PyInit_winrandom() #else void initwinrandom() #endif { PyObject *m; #ifdef IS_PY3K /* PyType_Ready automatically fills in ob_type with &PyType_Type if it's not already set */ if (PyType_Ready(&WRtype) < 0) return NULL; /* Initialize the module */ m = PyModule_Create(&moduledef); if (m == NULL) return NULL; #else WRtype.ob_type = &PyType_Type; m = Py_InitModule("winrandom", WR_mod_methods); #endif /* define Windows CSP Provider Types */ #ifdef PROV_RSA_FULL PyModule_AddIntConstant(m, "PROV_RSA_FULL", PROV_RSA_FULL); #endif #ifdef PROV_RSA_SIG PyModule_AddIntConstant(m, "PROV_RSA_SIG", PROV_RSA_SIG); #endif #ifdef PROV_DSS PyModule_AddIntConstant(m, "PROV_DSS", PROV_DSS); #endif #ifdef PROV_FORTEZZA PyModule_AddIntConstant(m, "PROV_FORTEZZA", PROV_FORTEZZA); #endif #ifdef PROV_MS_EXCHANGE PyModule_AddIntConstant(m, "PROV_MS_EXCHANGE", PROV_MS_EXCHANGE); #endif #ifdef PROV_SSL PyModule_AddIntConstant(m, "PROV_SSL", PROV_SSL); #endif #ifdef PROV_RSA_SCHANNEL PyModule_AddIntConstant(m, "PROV_RSA_SCHANNEL", PROV_RSA_SCHANNEL); #endif #ifdef PROV_DSS_DH PyModule_AddIntConstant(m, "PROV_DSS_DH", PROV_DSS_DH); #endif #ifdef PROV_EC_ECDSA_SIG PyModule_AddIntConstant(m, "PROV_EC_ECDSA_SIG", PROV_EC_ECDSA_SIG); #endif #ifdef PROV_EC_ECNRA_SIG PyModule_AddIntConstant(m, "PROV_EC_ECNRA_SIG", PROV_EC_ECNRA_SIG); #endif #ifdef PROV_EC_ECDSA_FULL PyModule_AddIntConstant(m, "PROV_EC_ECDSA_FULL", PROV_EC_ECDSA_FULL); #endif #ifdef PROV_EC_ECNRA_FULL PyModule_AddIntConstant(m, "PROV_EC_ECNRA_FULL", PROV_EC_ECNRA_FULL); #endif #ifdef PROV_SPYRUS_LYNKS PyModule_AddIntConstant(m, "PROV_SPYRUS_LYNKS", PROV_SPYRUS_LYNKS); #endif #ifdef PROV_INTEL_SEC PyModule_AddIntConstant(m, "PROV_INTEL_SEC", PROV_INTEL_SEC); #endif /* Define Windows CSP Provider Names */ #ifdef MS_DEF_PROV PyModule_AddStringConstant(m, "MS_DEF_PROV", MS_DEF_PROV); #endif #ifdef MS_ENHANCED_PROV PyModule_AddStringConstant(m, "MS_ENHANCED_PROV", MS_ENHANCED_PROV); #endif #ifdef MS_DEF_RSA_SIG_PROV PyModule_AddStringConstant(m, "MS_DEF_RSA_SIG_PROV", MS_DEF_RSA_SIG_PROV); #endif #ifdef MS_DEF_RSA_SCHANNEL_PROV PyModule_AddStringConstant(m, "MS_DEF_RSA_SCHANNEL_PROV", MS_DEF_RSA_SCHANNEL_PROV); #endif #ifdef MS_ENHANCED_RSA_SCHANNEL_PROV PyModule_AddStringConstant(m, "MS_ENHANCED_RSA_SCHANNEL_PROV", MS_ENHANCED_RSA_SCHANNEL_PROV); #endif #ifdef MS_DEF_DSS_PROV PyModule_AddStringConstant(m, "MS_DEF_DSS_PROV", MS_DEF_DSS_PROV); #endif #ifdef MS_DEF_DSS_DH_PROV PyModule_AddStringConstant(m, "MS_DEF_DSS_DH_PROV", MS_DEF_DSS_DH_PROV); #endif #ifdef INTEL_DEF_PROV PyModule_AddStringConstant(m, "INTEL_DEF_PROV", INTEL_DEF_PROV); #endif if (PyErr_Occurred()) Py_FatalError("can't initialize module winrandom"); #ifdef IS_PY3K return m; #endif } /* CryptGenRandom usage is described in http://msdn.microsoft.com/library/en-us/security/security/cryptgenrandom.asp and many associated pages on Windows Cryptographic Service Providers, which say: With Microsoft CSPs, CryptGenRandom uses the same random number generator used by other security components. This allows numerous processes to contribute to a system-wide seed. CryptoAPI stores an intermediate random seed with every user. To form the seed for the random number generator, a calling application supplies bits it might havefor instance, mouse or keyboard timing inputthat are then added to both the stored seed and various system data and user data such as the process ID and thread ID, the system clock, the system time, the system counter, memory status, free disk clusters, the hashed user environment block. This result is SHA-1 hashed, and the output is used to seed an RC4 stream, which is then used as the random stream and used to update the stored seed. The only other detailed description I've found of the sources of randomness for CryptGenRandom is this excerpt from a posting http://www.der-keiler.de/Newsgroups/comp.security.ssh/2002-06/0169.html From: Jon McClelland (dowot69@hotmail.com) Date: 06/12/02 ... Windows, call a function such as CryptGenRandom, which has two of the properties of a good random number generator, unpredictability and even value distribution. This function, declared in Wincrypt.h, is available on just about every Windows platform, including Windows 95 with Internet Explorer 3.02 or later, Windows 98, Windows Me, Windows CE v3, Windows NT 4, Windows 2000, and Windows XP. CryptGenRandom gets its randomness, also known as entropy, from many sources in Windows 2000, including the following: The current process ID (GetCurrentProcessID). The current thread ID (GetCurrentThreadID). The ticks since boot (GetTickCount). The current time (GetLocalTime). Various high-precision performance counters (QueryPerformanceCounter). A Message Digest 4 (MD4) hash of the user's environment block, which includes username, computer name, and search path. High-precision internal CPU counters, such as RDTSC, RDMSR, RDPMC (x86 only-more information about these counters is at developer.intel.com/software/idap/resources/technical_collateral/pentiumii/RDTSCPM1.HTM ). Low-level system information, such as idle time, kernel time, interrupt times, commit limit, page read count, cache read count, nonpaged pool allocations, alignment fixup count, operating system lookaside information. Such information is added to a buffer, which is hashed using MD4 and used as the key to modify a buffer, using RC4, provided by the user. (Refer to the CryptGenRandom documentation in the Platform SDK for more information about the user-provided buffer.) Hence, if the user provides additional data in the buffer, this is used as an element in the witches brew to generate the random data. The result is a cryptographically random number generator. Also, note that if you plan to sell your software to the United States federal government, you'll need to use FIPS 140-1-approved algorithms. The default versions of CryptGenRandom in Microsoft Windows CE v3, Windows 95, Windows 98, Windows Me, Windows 2000, and Windows XP are FIPS-approved. Obviously FIPS-140 compliance is necessary but not sufficient to provide a properly secure source of random data. */ /* [Update: 2007-11-13] CryptGenRandom does not necessarily provide forward secrecy or reverse secrecy. See the paper by Leo Dorrendorf and Zvi Gutterman and Benny Pinkas, _Cryptanalysis of the Random Number Generator of the Windows Operating System_, Cryptology ePrint Archive, Report 2007/419, http://eprint.iacr.org/2007/419 */ #endif /* MS_WIN32 */