mirror of
https://github.com/autistic-symposium/sec-pentesting-toolkit.git
synced 2025-04-27 19:16:08 -04:00
473 lines
14 KiB
C
473 lines
14 KiB
C
/* -*- 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 */
|