| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- /* -*- 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 */
|