diff options
-rw-r--r-- | .travis.yml | 1 | ||||
-rw-r--r-- | doc/api/rand.rst | 74 | ||||
-rw-r--r-- | src/OpenSSL/__init__.py | 2 | ||||
-rw-r--r-- | src/OpenSSL/rand.py | 97 | ||||
-rw-r--r-- | src/OpenSSL/version.py | 2 | ||||
-rw-r--r-- | tests/test_rand.py | 51 |
6 files changed, 106 insertions, 121 deletions
diff --git a/.travis.yml b/.travis.yml index 55bffb0..c621fd4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -129,6 +129,7 @@ script: ~/.venv/bin/tox -v after_script: + ~/.venv/bin/coverage combine - ./.travis/upload_coverage.sh notifications: diff --git a/doc/api/rand.rst b/doc/api/rand.rst index 18789b8..6c2a5f3 100644 --- a/doc/api/rand.rst +++ b/doc/api/rand.rst @@ -1,79 +1,43 @@ .. _openssl-rand: -:py:mod:`rand` --- An interface to the OpenSSL pseudo random number generator -============================================================================= +:mod:`rand` --- An interface to the OpenSSL pseudo random number generator +========================================================================== -.. py:module:: OpenSSL.rand - :synopsis: An interface to the OpenSSL pseudo random number generator - - -This module handles the OpenSSL pseudo random number generator (PRNG) and -declares the following: - -.. py:function:: add(string, entropy) +.. warning:: + Functions from this methods shouldn't be used. + `Use urandom <http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/>`_ instead. - 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`. - -.. py:function:: bytes(num_bytes) - - Get some random bytes from the PRNG as a string. - - This is a wrapper for the C function :py:func:`RAND_bytes`. +.. py:module:: OpenSSL.rand + :synopsis: An interface to the OpenSSL pseudo random number generator -.. py:function:: cleanup() +This module handles the OpenSSL pseudo random number generator (PRNG) and declares the following: - Erase the memory used by the PRNG. +.. autofunction:: add - This is a wrapper for the C function :py:func:`RAND_cleanup`. +.. autofunction:: bytes +.. autofunction:: cleanup -.. py:function:: egd(path[, bytes]) +.. autofunction:: egd(path[, bytes]) - Query the `Entropy Gathering Daemon <http://www.lothar.com/tech/crypto/>`_ on - socket *path* for *bytes* bytes of random data and uses :py:func:`add` to - seed the PRNG. The default value of *bytes* is 255. +.. autofunction:: load_file(filename[, bytes]) +.. autofunction:: seed -.. py:function:: load_file(path[, bytes]) +.. autofunction:: status - Read *bytes* bytes (or all of it, if *bytes* is negative) of data from the - file *path* to seed the PRNG. The default value of *bytes* is -1. +.. autofunction:: write_file -.. py:function:: screen() +.. function:: screen Add the current contents of the screen to the PRNG state. Availability: Windows. + :return: :obj:`None` -.. py:function:: seed(string) - - This is equivalent to calling :py:func:`add` with *entropy* as the length - of the string. - - -.. py:function:: status() - - Returns true if the PRNG has been seeded with enough data, and false otherwise. - - -.. py:function:: write_file(path) - - Write a number of random bytes (currently 1024) to the file *path*. This - file can then be used with :py:func:`load_file` to seed the PRNG again. - - -.. py:exception:: Error - - If the current RAND method supports any errors, this is raised when needed. - The default method does not raise this when the entropy pool is depleted. - Whenever this exception is raised directly, it has a list of error messages - from the OpenSSL error queue, where each item is a tuple *(lib, function, - reason)*. Here *lib*, *function* and *reason* are all strings, describing - where and what the problem is. See :manpage:`err(3)` for more information. +.. autoexception:: Error diff --git a/src/OpenSSL/__init__.py b/src/OpenSSL/__init__.py index 3c80e54..b827e3c 100644 --- a/src/OpenSSL/__init__.py +++ b/src/OpenSSL/__init__.py @@ -13,7 +13,7 @@ from OpenSSL.version import ( __all__ = [ - "SSL", "crypto", "rand", "tsafe", + "SSL", "crypto", "rand", "__author__", "__copyright__", "__email__", "__license__", "__summary__", "__title__", "__uri__", "__version__", diff --git a/src/OpenSSL/rand.py b/src/OpenSSL/rand.py index 65cd597..c12638f 100644 --- a/src/OpenSSL/rand.py +++ b/src/OpenSSL/rand.py @@ -1,9 +1,10 @@ """ PRNG management routines, thin wrappers. - -See the file RATIONALE for a short explanation of why this module was written. """ +import os +import warnings + from functools import partial from six import integer_types as _integer_types @@ -17,7 +18,17 @@ from OpenSSL._util import ( class Error(Exception): """ - An error occurred in an `OpenSSL.rand` API. + An error occurred in an :mod:`OpenSSL.rand` API. + + If the current RAND method supports any errors, this is raised when needed. + The default method does not raise this when the entropy pool is depleted. + + Whenever this exception is raised directly, it has a list of error messages + from the OpenSSL error queue, where each item is a tuple *(lib, function, + reason)*. Here *lib*, *function* and *reason* are all strings, describing + where and what the problem is. + + See :manpage:`err(3)` for more information. """ _raise_current_error = partial(_exception_from_error_queue, Error) @@ -29,10 +40,13 @@ _builtin_bytes = bytes def bytes(num_bytes): """ - Get some random bytes as a string. + Get some random bytes from the PRNG as a string. + + This is a wrapper for the C function ``RAND_bytes``. - :param num_bytes: The number of bytes to fetch - :return: A string of random bytes + :param num_bytes: The number of bytes to fetch. + + :return: A string of random bytes. """ if not isinstance(num_bytes, _integer_types): raise TypeError("num_bytes must be an integer") @@ -52,11 +66,17 @@ def bytes(num_bytes): def add(buffer, entropy): """ - Add data with a given entropy to the PRNG + Mix bytes from *string* into the PRNG state. - :param buffer: Buffer with random data - :param entropy: The entropy (in bytes) measurement of the buffer - :return: None + 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`. + + :param buffer: Buffer with random data. + :param entropy: The entropy (in bytes) measurement of the buffer. + + :return: :obj:`None` """ if not isinstance(buffer, _builtin_bytes): raise TypeError("buffer must be a byte string") @@ -70,10 +90,11 @@ def add(buffer, entropy): def seed(buffer): """ - Alias for rand_add, with entropy equal to length + Equivalent to calling :func:`add` with *entropy* as the length of *buffer*. :param buffer: Buffer with random data - :return: None + + :return: :obj:`None` """ if not isinstance(buffer, _builtin_bytes): raise TypeError("buffer must be a byte string") @@ -84,24 +105,33 @@ def seed(buffer): def status(): """ - Retrieve the status of the PRNG + Check whether the PRNG has been seeded with enough data. - :return: True if the PRNG is seeded enough, false otherwise + :return: :obj:`True` if the PRNG is seeded enough, :obj:`False` otherwise. """ return _lib.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. + Query the system random source and seed the PRNG. + + Does *not* actually query the EGD. + + .. deprecated:: 15.2.0 + EGD was only necessary for some commercial UNIX systems that all + reached their ends of life more than a decade ago. See + `pyca/cryptography#1636 + <https://github.com/pyca/cryptography/pull/1636>`_. + + :param path: Ignored. + :param bytes: (optional) The number of bytes to read, default is 255. - :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()) + :returns: ``len(bytes)`` or 255 if not specified. """ + warnings.warn("OpenSSL.rand.egd() is deprecated as of 15.2.0.", + DeprecationWarning) + if not isinstance(path, _builtin_bytes): raise TypeError("path must be a byte string") @@ -110,14 +140,17 @@ def egd(path, bytes=_unspecified): elif not isinstance(bytes, int): raise TypeError("bytes must be an integer") - return _lib.RAND_egd_bytes(path, bytes) + seed(os.urandom(bytes)) + return bytes def cleanup(): """ Erase the memory used by the PRNG. - :return: None + This is a wrapper for the C function ``RAND_cleanup``. + + :return: :obj:`None` """ # TODO Nothing tests this call actually being made, or made properly. _lib.RAND_cleanup() @@ -125,11 +158,13 @@ def cleanup(): def load_file(filename, maxbytes=_unspecified): """ - Seed the PRNG with data from a file + Read *maxbytes* of data from *filename* and seed the PRNG with it. + + Read the whole file if *maxbytes* is not specified or negative. :param filename: The file to read data from (``bytes`` or ``unicode``). - :param maxbytes: (optional) The number of bytes to read, default is to read - the entire file + :param maxbytes: (optional) The number of bytes to read. Default is to + read the entire file. :return: The number of bytes read """ @@ -145,11 +180,12 @@ def load_file(filename, maxbytes=_unspecified): def write_file(filename): """ - Save PRNG state to a file + Write a number of random bytes (currently 1024) to the file *path*. This + file can then be used with :func:`load_file` to seed the PRNG again. :param filename: The file to write data to (``bytes`` or ``unicode``). - :return: The number of bytes written + :return: The number of bytes written. """ filename = _path_string(filename) return _lib.RAND_write_file(filename) @@ -158,8 +194,9 @@ def 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. + Add the current contents of the screen to the PRNG state. + + Availability: Windows. :return: None """ diff --git a/src/OpenSSL/version.py b/src/OpenSSL/version.py index f284b04..02c5fc3 100644 --- a/src/OpenSSL/version.py +++ b/src/OpenSSL/version.py @@ -11,7 +11,7 @@ __all__ = [ "__title__", "__uri__", "__version__", ] -__version__ = "0.16.dev0" +__version__ = "15.2.0.dev0" __title__ = "pyOpenSSL" __uri__ = "https://github.com/pyca/pyopenssl" diff --git a/tests/test_rand.py b/tests/test_rand.py index 8064011..ece0935 100644 --- a/tests/test_rand.py +++ b/tests/test_rand.py @@ -5,11 +5,12 @@ Unit tests for :py:obj:`OpenSSL.rand`. """ -from unittest import main import os import stat import sys +import pytest + from OpenSSL import rand from .util import NON_ASCII, TestCase, b @@ -97,40 +98,26 @@ class RandTests(TestCase): # entropy or not. self.assertTrue(rand.status() in (1, 2)) - def test_egd_wrong_args(self): - """ - :py:obj:`OpenSSL.rand.egd` raises :py:obj:`TypeError` when called with - the wrong number of arguments or with arguments not of type - :py:obj:`str` and :py:obj:`int`. - """ - self.assertRaises(TypeError, rand.egd) - self.assertRaises(TypeError, rand.egd, None) - self.assertRaises(TypeError, rand.egd, "foo", None) - self.assertRaises(TypeError, rand.egd, None, 3) - self.assertRaises(TypeError, rand.egd, "foo", 3, None) - - def test_egd_missing(self): + def test_egd_warning(self): """ - :py:obj:`OpenSSL.rand.egd` returns :py:obj:`0` or :py:obj:`-1` if the - EGD socket passed to it does not exist. + Calling egd raises :exc:`DeprecationWarning`. """ - result = rand.egd(self.mktemp()) - expected = (-1, 0) - self.assertTrue( - result in expected, - "%r not in %r" % (result, expected)) + pytest.deprecated_call(rand.egd, b"foo", 255) + pytest.deprecated_call(rand.egd, b"foo") - def test_egd_missing_and_bytes(self): + def test_egd_wrong_args(self): """ - :py:obj:`OpenSSL.rand.egd` returns :py:obj:`0` or :py:obj:`-1` if the - EGD socket passed to it does not exist even if a size argument is - explicitly passed. + :meth:`OpenSSL.rand.egd` raises :exc:`TypeError` when called with the + wrong number of arguments or with arguments not of type :obj:`str` and + :obj:`int`. """ - result = rand.egd(self.mktemp(), 1024) - expected = (-1, 0) - self.assertTrue( - result in expected, - "%r not in %r" % (result, expected)) + for args in [(), + (None,), + ("foo", None), + (None, 3), + ("foo", 3, None)]: + with pytest.raises(TypeError): + rand.egd(*args) def test_cleanup_wrong_args(self): """ @@ -206,7 +193,3 @@ class RandTests(TestCase): """ path = self.mktemp().decode('utf-8') + NON_ASCII self._read_write_test(path) - - -if __name__ == '__main__': - main() |