Skip to content

Commit 5a32aac

Browse files
committed
SECURITY: Seed the OpenSSL PRNG properly.
This commit makes sure that the OpenSSL PRNG is properly seeded by calling RAND_seed of OpenSSL with 32 bytes generated by the Python os.urandom(32) function, which should generate secure random bytes.
1 parent 83eae66 commit 5a32aac

1 file changed

Lines changed: 73 additions & 1 deletion

File tree

src/primemodule.c

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#include <Python.h>
22
#include <openssl/conf.h>
33
#include <openssl/bn.h>
4+
#include <openssl/rand.h>
5+
6+
// By default, use 32 BYTE of randomness
7+
#define RAND_LEN 32
48

59
// Generate a safe prime using OpenSSL
610
static PyObject * gensafeprime_generate(PyObject *self, PyObject *args) {
@@ -47,5 +51,73 @@ static PyMethodDef GenPrime_Methods[] = {
4751
PyMODINIT_FUNC
4852
initgensafeprime(void)
4953
{
50-
(void) Py_InitModule("gensafeprime", GenPrime_Methods);
54+
PyObject *modname, *mod, *mdict, *func, *args, *rslt;
55+
char * rnd;
56+
// int i;
57+
58+
// first, standard initialization
59+
if (!Py_InitModule("gensafeprime", GenPrime_Methods)) {
60+
PyErr_SetString(PyExc_RuntimeError, "cannot init module");
61+
return;
62+
}
63+
64+
// seed the random number generator of OpenSSL using os.urandom(RAND_LEN)
65+
// According to https://docs.python.org/2.7/library/random.html os.urandom
66+
// generates secure random numbers for cryptographic applications.
67+
68+
// Code based on
69+
// https://www.daniweb.com/software-development/python/threads/31682/calling-python-function-from-cc-
70+
// but has been heavily modified.
71+
72+
// Import os and get a reference to os.urandom
73+
modname = PyString_FromString("os");
74+
mod = PyImport_Import(modname);
75+
if (mod) {
76+
mdict = PyModule_GetDict(mod);
77+
func = PyDict_GetItemString(mdict, "urandom");
78+
if (func) {
79+
// Call urandom(RAND_LEN)
80+
if (PyCallable_Check(func)) {
81+
args = Py_BuildValue("(i)", RAND_LEN);
82+
rslt = PyObject_CallObject(func, args);
83+
Py_XDECREF(args);
84+
// Check the result, when the call failed, rslt will be NULL
85+
if (rslt) {
86+
// Get random bytes as char* representation
87+
rnd = PyString_AsString(rslt);
88+
if (rnd) {
89+
// Check that really RAND_LEN bytes have been returned
90+
if (PyString_Size(rslt) == RAND_LEN) {
91+
// rnd should now contain RAND_LEN securely random characters
92+
// terminated by a 0 byte, pass it to OpenSSL RAND_seed
93+
RAND_seed(rnd, RAND_LEN);
94+
95+
// DEBUG code, uncomment to see the seed
96+
//printf("prng has been seeded with");
97+
//for (i = 0; i < RAND_LEN; i++) {
98+
// printf(" %02x", 0xff & rnd[i]);
99+
//}
100+
//printf("\n");
101+
} else {
102+
PyErr_SetString(PyExc_RuntimeError, "wrong length of random data");
103+
}
104+
} else {
105+
PyErr_SetString(PyExc_RuntimeError, "parsing result failed, rnd == NULL");
106+
}
107+
Py_XDECREF(rslt);
108+
} else {
109+
PyErr_SetString(PyExc_RuntimeError, "call to urandom returned NULL pointer");
110+
}
111+
} else {
112+
PyErr_SetString(PyExc_RuntimeError, "urandom not callable");
113+
}
114+
} else {
115+
PyErr_SetString(PyExc_RuntimeError, "could not get reference to os.urandom");
116+
}
117+
Py_XDECREF(mod);
118+
} else {
119+
PyErr_SetString(PyExc_RuntimeError, "could not import os");
120+
}
121+
Py_XDECREF(modname);
122+
51123
}

0 commit comments

Comments
 (0)