Repository: pyca/pyopenssl (python)

Issue:
Security Implications of #675 (Delete rand.py)
In #675, the rand module was removed from the codebase, and users of the module were instructed to use os.urandom instead. However, using os.urandom is not an alternative for the whole rand module, just for some functions like rand.bytes.

There are other functions in the module, such as rand.add, that are changing the PRNG state of OpenSSL. Using such functions is of particular importance for fork safety, as described here: https://wiki.openssl.org/index.php/Random_fork-safety

and here:
https://docs.python.org/2/library/ssl.html#multi-processing

Please consider reverting #675, or at least bringing back the minimum subset of the rand module that will allow pyopenssl users to deal with fork safety.

Pull request description:
restore a subset of the rand module
fixes #706 

We should bikeshed on the changelog language probably.

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 0eb7f81..0f7e890 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -24,6 +24,9 @@ Changes:
 ^^^^^^^^
 
 
+- Re-added a subset of the ``OpenSSL.rand`` module.
+  This subset allows conscientious users to reseed the OpenSSL CSPRNG after fork.
+  `#708 <https://github.com/pyca/pyopenssl/pull/708>`_
 - Corrected a use-after-free when reusing an issuer or subject from an ``X509`` object after the underlying object has been mutated.
   `#709 <https://github.com/pyca/pyopenssl/pull/709>`_
 
diff --git a/src/OpenSSL/rand.py b/src/OpenSSL/rand.py
new file mode 100644
index 0000000..d2c1767
--- /dev/null
+++ b/src/OpenSSL/rand.py
@@ -0,0 +1,40 @@
+"""
+PRNG management routines, thin wrappers.
+"""
+
+from OpenSSL._util import lib as _lib
+
+
+def add(buffer, entropy):
+    """
+    Mix bytes from *string* into the PRNG state.
+
+    The *entropy* argument is (the lower bound of) an estimate of how much
+    randomness is contained in *string*, measured in bytes.
+
+    For more information, see e.g. :rfc:`1750`.
+
+    This function is only relevant if you are forking Python processes and
+    need to reseed the CSPRNG after fork.
+
+    :param buffer: Buffer with random data.
+    :param entropy: The entropy (in bytes) measurement of the buffer.
+
+    :return: :obj:`None`
+    """
+    if not isinstance(buffer, bytes):
+        raise TypeError("buffer must be a byte string")
+
+    if not isinstance(entropy, int):
+        raise TypeError("entropy must be an integer")
+
+    _lib.RAND_add(buffer, len(buffer), entropy)
+
+
+def status():
+    """
+    Check whether the PRNG has been seeded with enough data.
+
+    :return: 1 if the PRNG is seeded enough, 0 otherwise.
+    """
+    return _lib.RAND_status()


Test patch:
diff --git a/tests/test_rand.py b/tests/test_rand.py
new file mode 100644
index 0000000..e04a24c
--- /dev/null
+++ b/tests/test_rand.py
@@ -0,0 +1,38 @@
+# Copyright (c) Frederick Dean
+# See LICENSE for details.
+
+"""
+Unit tests for `OpenSSL.rand`.
+"""
+
+import pytest
+
+from OpenSSL import rand
+
+
+class TestRand(object):
+
+    @pytest.mark.parametrize('args', [
+        (b"foo", None),
+        (None, 3),
+    ])
+    def test_add_wrong_args(self, args):
+        """
+        `OpenSSL.rand.add` raises `TypeError` if called with arguments not of
+        type `str` and `int`.
+        """
+        with pytest.raises(TypeError):
+            rand.add(*args)
+
+    def test_add(self):
+        """
+        `OpenSSL.rand.add` adds entropy to the PRNG.
+        """
+        rand.add(b'hamburger', 3)
+
+    def test_status(self):
+        """
+        `OpenSSL.rand.status` returns `1` if the PRNG has sufficient entropy,
+        `0` otherwise.
+        """
+        assert rand.status() == 1