| /* -*- 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 <windows.h> |
| #include <wincrypt.h> |
| |
| #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 |
| <http://developer.intel.com>). |
| |
| 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 */ |