Re-implement OpenSSL.rand in Python using opentls.
diff --git a/OpenSSL/rand.py b/OpenSSL/rand.py
new file mode 100644
index 0000000..efdbd4b
--- /dev/null
+++ b/OpenSSL/rand.py
@@ -0,0 +1,167 @@
+"""
+PRNG management routines, thin wrappers.
+
+See the file RATIONALE for a short explanation of why this module was written.
+"""
+
+import __builtin__
+
+from tls.c import api as _api
+
+# TODO Nothing tests the existence or use of this
+class Error(Exception):
+ pass
+
+
+_unspecified = object()
+
+def bytes(num_bytes):
+ """
+ Get some random bytes as a string.
+
+ :param num_bytes: The number of bytes to fetch
+ :return: A string of random bytes
+ """
+ if not isinstance(num_bytes, int):
+ raise TypeError("num_bytes must be an integer")
+
+ if num_bytes < 0:
+ raise ValueError("num_bytes must not be negative")
+
+ result_buffer = _api.new("char[]", num_bytes)
+ result_code = _api.RAND_bytes(result_buffer, num_bytes)
+ if result_code == -1:
+ raise Exception("zoops") # TODO
+ return _api.buffer(result_buffer)[:]
+
+
+
+def add(buffer, entropy):
+ """
+ Add data with a given entropy to the PRNG
+
+ :param buffer: Buffer with random data
+ :param entropy: The entropy (in bytes) measurement of the buffer
+ :return: None
+ """
+ if not isinstance(buffer, __builtin__.bytes):
+ raise TypeError("buffer must be a byte string")
+
+ if not isinstance(entropy, int):
+ raise TypeError("entropy must be an integer")
+
+ # TODO Nothing tests this call actually being made, or made properly.
+ _api.RAND_add(buffer, len(buffer), entropy)
+
+
+
+def seed(buffer):
+ """
+ Alias for rand_add, with entropy equal to length
+
+ :param buffer: Buffer with random data
+ :return: None
+ """
+ if not isinstance(buffer, __builtin__.bytes):
+ raise TypeError("buffer must be a byte string")
+
+ # TODO Nothing tests this call actually being made, or made properly.
+ _api.RAND_seed(buffer, len(buffer))
+
+
+
+def status():
+ """
+ Retrieve the status of the PRNG
+
+ :return: True if the PRNG is seeded enough, false otherwise
+ """
+ return _api.RAND_status()
+
+
+
+def egd(path, bytes=_unspecified):
+ """
+ Query an entropy gathering daemon (EGD) for random data and add it to the
+ PRNG. I haven't found any problems when the socket is missing, the function
+ just returns 0.
+
+ :param path: The path to the EGD socket
+ :param bytes: (optional) The number of bytes to read, default is 255
+ :returns: The number of bytes read (NB: a value of 0 isn't necessarily an
+ error, check rand.status())
+ """
+ if not isinstance(path, str):
+ raise TypeError("path must be a string")
+
+ if bytes is _unspecified:
+ bytes = 255
+ elif not isinstance(bytes, int):
+ raise TypeError("bytes must be an integer")
+
+ return _api.RAND_egd_bytes(path, bytes)
+
+
+
+def cleanup():
+ """
+ Erase the memory used by the PRNG.
+
+ :return: None
+ """
+ # TODO Nothing tests this call actually being made, or made properly.
+ _api.RAND_cleanup()
+
+
+
+def load_file(filename, maxbytes=_unspecified):
+ """
+ Seed the PRNG with data from a file
+
+ :param filename: The file to read data from
+ :param maxbytes: (optional) The number of bytes to read, default is
+ to read the entire file
+ :return: The number of bytes read
+ """
+ if not isinstance(filename, str):
+ raise TypeError("filename must be a string")
+
+ if maxbytes is _unspecified:
+ maxbytes = -1
+ elif not isinstance(maxbytes, int):
+ raise TypeError("maxbytes must be an integer")
+
+ return _api.RAND_load_file(filename, maxbytes)
+
+
+
+def write_file(filename):
+ """
+ Save PRNG state to a file
+
+ :param filename: The file to write data to
+ :return: The number of bytes written
+ """
+ if not isinstance(filename, str):
+ raise TypeError("filename must be a string")
+
+ return _api.RAND_write_file(filename)
+
+
+# TODO There are no tests for screen at all
+def screen():
+ """
+ Add the current contents of the screen to the PRNG state. Availability:
+ Windows.
+
+ :return: None
+ """
+ _api.RAND_screen()
+
+if getattr(_api, 'RAND_screen', None) is None:
+ del screen
+
+
+# TODO There are no tests for the RAND strings being loaded, whatever that
+# means.
+_api.ERR_load_RAND_strings()
diff --git a/OpenSSL/rand/rand.c b/OpenSSL/rand/rand.c
deleted file mode 100644
index 712ad56..0000000
--- a/OpenSSL/rand/rand.c
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * rand.c
- *
- * Copyright (C) AB Strakt
- * See LICENSE file for details.
- *
- * PRNG management routines, thin wrappers.
- * See the file RATIONALE for a short explanation of why this module was written.
- *
- */
-#include <Python.h>
-
-/*
- * In order to get the RAND_screen definition from the rand.h
- * WIN32 or WINDOWS needs to be defined, otherwise we get a
- * warning.
- */
-#ifdef MS_WINDOWS
-# ifndef WIN32
-# define WIN32
-# endif
-#endif
-#include <openssl/rand.h>
-#include "../util.h"
-
-PyObject *rand_Error;
-
-static char rand_doc[] = "\n\
-PRNG management routines, thin wrappers.\n\
-See the file RATIONALE for a short explanation of why this module was written.\n\
-";
-
-static char rand_add_doc[] = "\n\
-Add data with a given entropy to the PRNG\n\
-\n\
-:param buffer: Buffer with random data\n\
-:param entropy: The entropy (in bytes) measurement of the buffer\n\
-:return: None\n\
-";
-
-static PyObject *
-rand_add(PyObject *spam, PyObject *args)
-{
- char *buf;
- int size;
- double entropy;
-
- if (!PyArg_ParseTuple(args, BYTESTRING_FMT "#d:add", &buf, &size, &entropy))
- return NULL;
-
- RAND_add(buf, size, entropy);
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static char rand_seed_doc[] = "\n\
-Alias for rand_add, with entropy equal to length\n\
-\n\
-:param buffer: Buffer with random data\n\
-:return: None\n\
-";
-
-static PyObject *
-rand_seed(PyObject *spam, PyObject *args)
-{
- char *buf;
- int size;
-
- if (!PyArg_ParseTuple(args, BYTESTRING_FMT "#:seed", &buf, &size))
- return NULL;
-
- RAND_seed(buf, size);
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static char rand_status_doc[] = "\n\
-Retrieve the status of the PRNG\n\
-\n\
-:return: True if the PRNG is seeded enough, false otherwise\n\
-";
-
-static PyObject *
-rand_status(PyObject *spam, PyObject *args)
-{
- if (!PyArg_ParseTuple(args, ":status"))
- return NULL;
-
- return PyLong_FromLong((long)RAND_status());
-}
-
-#ifdef MS_WINDOWS
-static char rand_screen_doc[] = "\n\
-Add the current contents of the screen to the PRNG state. Availability:\n\
-Windows.\n\
-\n\
-:return: None\n\
-";
-
-static PyObject *
-rand_screen(PyObject *spam, PyObject *args)
-{
- if (!PyArg_ParseTuple(args, ":screen"))
- return NULL;
-
- RAND_screen();
- Py_INCREF(Py_None);
- return Py_None;
-}
-#endif
-
-static char rand_egd_doc[] = "\n\
-Query an entropy gathering daemon (EGD) for random data and add it to the\n\
-PRNG. I haven't found any problems when the socket is missing, the function\n\
-just returns 0.\n\
-\n\
-:param path: The path to the EGD socket\n\
-:param bytes: (optional) The number of bytes to read, default is 255\n\
-:returns: The number of bytes read (NB: a value of 0 isn't necessarily an\n\
- error, check rand.status())\n\
-";
-
-static PyObject *
-rand_egd(PyObject *spam, PyObject *args)
-{
- char *path;
- int bytes = 255;
-
- if (!PyArg_ParseTuple(args, "s|i:egd", &path, &bytes))
- return NULL;
-
- return PyLong_FromLong((long)RAND_egd_bytes(path, bytes));
-}
-
-static char rand_cleanup_doc[] = "\n\
-Erase the memory used by the PRNG.\n\
-\n\
-:return: None\n\
-";
-
-static PyObject *
-rand_cleanup(PyObject *spam, PyObject *args)
-{
- if (!PyArg_ParseTuple(args, ":cleanup"))
- return NULL;
-
- RAND_cleanup();
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static char rand_load_file_doc[] = "\n\
-Seed the PRNG with data from a file\n\
-\n\
-:param filename: The file to read data from\n\
-:param maxbytes: (optional) The number of bytes to read, default is\n\
- to read the entire file\n\
-:return: The number of bytes read\n\
-";
-
-static PyObject *
-rand_load_file(PyObject *spam, PyObject *args)
-{
- char *filename;
- int maxbytes = -1;
-
- if (!PyArg_ParseTuple(args, "s|i:load_file", &filename, &maxbytes))
- return NULL;
-
- return PyLong_FromLong((long)RAND_load_file(filename, maxbytes));
-}
-
-static char rand_write_file_doc[] = "\n\
-Save PRNG state to a file\n\
-\n\
-:param filename: The file to write data to\n\
-:return: The number of bytes written\n\
-";
-
-static PyObject *
-rand_write_file(PyObject *spam, PyObject *args)
-{
- char *filename;
-
- if (!PyArg_ParseTuple(args, "s:write_file", &filename))
- return NULL;
-
- return PyLong_FromLong((long)RAND_write_file(filename));
-}
-
-static char rand_bytes_doc[] = "\n\
-Get some randomm bytes as a string.\n\
-\n\
-:param num_bytes: The number of bytes to fetch\n\
-:return: A string of random bytes\n\
-";
-
-#if PY_VERSION_HEX < 0x02050000
-#define Py_ssize_t int
-#define PY_SSIZE_FMT "i"
-#else
-#define PY_SSIZE_FMT "n"
-#endif
-
-static PyObject *
-rand_bytes(PyObject *spam, PyObject *args, PyObject *keywds) {
- Py_ssize_t num_bytes;
- static char *kwlist[] = {"num_bytes", NULL};
- char *buf;
- unsigned int rc;
- PyObject *obj = NULL;
-
- if (!PyArg_ParseTupleAndKeywords(
- args, keywds, PY_SSIZE_FMT ":bytes", kwlist, &num_bytes)) {
- return NULL;
- }
-
- if(num_bytes < 0) {
- PyErr_SetString(PyExc_ValueError, "num_bytes must not be negative");
- return NULL;
- }
- buf = malloc(num_bytes);
- if (buf == NULL) /* out of memory */
- return NULL;
- rc = RAND_bytes((unsigned char *) buf, num_bytes);
- if(rc != 1) { /* if unsuccessful */
- exception_from_error_queue(rand_Error);
- goto done;
- }
- obj = PyBytes_FromStringAndSize(buf, (unsigned) num_bytes);
- done:
- free(buf);
- return obj;
-}
-
-
-/* Methods in the OpenSSL.rand module */
-static PyMethodDef rand_methods[] = {
- { "add", (PyCFunction)rand_add, METH_VARARGS, rand_add_doc },
- { "seed", (PyCFunction)rand_seed, METH_VARARGS, rand_seed_doc },
- { "status", (PyCFunction)rand_status, METH_VARARGS, rand_status_doc },
-#ifdef MS_WINDOWS
- { "screen", (PyCFunction)rand_screen, METH_VARARGS, rand_screen_doc },
-#endif
- { "egd", (PyCFunction)rand_egd, METH_VARARGS, rand_egd_doc },
- { "cleanup", (PyCFunction)rand_cleanup, METH_VARARGS, rand_cleanup_doc },
- { "load_file", (PyCFunction)rand_load_file, METH_VARARGS, rand_load_file_doc },
- { "write_file",(PyCFunction)rand_write_file, METH_VARARGS, rand_write_file_doc },
- { "bytes", (PyCFunction)rand_bytes, METH_VARARGS|METH_KEYWORDS, rand_bytes_doc },
- { NULL, NULL }
-};
-
-
-#ifdef PY3
-static struct PyModuleDef randmodule = {
- PyModuleDef_HEAD_INIT,
- "rand",
- rand_doc,
- -1,
- rand_methods
-};
-#endif
-
-/*
- * Initialize the rand sub module
- *
- * Arguments: None
- * Returns: None
- */
-PyOpenSSL_MODINIT(rand) {
- PyObject *module;
-
-#ifdef PY3
- module = PyModule_Create(&randmodule);
-#else
- module = Py_InitModule3("rand", rand_methods, rand_doc);
-#endif
- if (module == NULL) {
- PyOpenSSL_MODRETURN(NULL);
- }
-
- rand_Error = PyErr_NewException("OpenSSL.rand.Error", NULL, NULL);
-
- if (rand_Error == NULL) {
- goto error;
- }
-
- /* PyModule_AddObject steals a reference.
- */
- Py_INCREF(rand_Error);
- if (PyModule_AddObject(module, "Error", rand_Error) != 0) {
- goto error;
- }
-
- ERR_load_RAND_strings();
-
- PyOpenSSL_MODRETURN(module);
-
-error:
- PyOpenSSL_MODRETURN(NULL);
- ;
-}
-
diff --git a/setup.py b/setup.py
index 511c60c..142d03c 100755
--- a/setup.py
+++ b/setup.py
@@ -31,7 +31,6 @@
'OpenSSL/crypto/pkcs12.h', 'OpenSSL/crypto/netscape_spki.h',
'OpenSSL/crypto/revoked.h', 'OpenSSL/crypto/crl.h',
'OpenSSL/util.h']
-rand_src = ['OpenSSL/rand/rand.c', 'OpenSSL/util.c']
rand_dep = ['OpenSSL/util.h']
ssl_src = ['OpenSSL/ssl/connection.c', 'OpenSSL/ssl/context.c', 'OpenSSL/ssl/ssl.c',
'OpenSSL/ssl/session.c', 'OpenSSL/util.c']
@@ -195,9 +194,9 @@
setup(name='pyOpenSSL', version=__version__,
packages = ['OpenSSL'],
package_dir = {'OpenSSL': 'OpenSSL'},
- ext_modules = [mkExtension('crypto'), mkExtension('rand'),
- mkExtension('SSL')],
+ ext_modules = [mkExtension('crypto'), mkExtension('SSL')],
py_modules = ['OpenSSL.__init__', 'OpenSSL.tsafe',
+ 'OpenSSL.rand',
'OpenSSL.version', 'OpenSSL.test.__init__',
'OpenSSL.test.util',
'OpenSSL.test.test_crypto',