Merge pull request #559 from public/rsa-keys

RSA keys
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index 74faee5..6da90ce 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -58,6 +58,40 @@
 
         self._cipher_registry = {}
         self._register_default_ciphers()
+        self.activate_osrandom_engine()
+
+    def activate_builtin_random(self):
+        # Obtain a new structural reference.
+        e = self._lib.ENGINE_get_default_RAND()
+        if e != self._ffi.NULL:
+            self._lib.ENGINE_unregister_RAND(e)
+            # Reset the RNG to use the new engine.
+            self._lib.RAND_cleanup()
+            # decrement the structural reference from get_default_RAND
+            res = self._lib.ENGINE_finish(e)
+            assert res == 1
+
+    def activate_osrandom_engine(self):
+        # Unregister and free the current engine.
+        self.activate_builtin_random()
+        # Fetches an engine by id and returns it. This creates a structural
+        # reference.
+        e = self._lib.ENGINE_by_id(self._lib.Cryptography_osrandom_engine_id)
+        assert e != self._ffi.NULL
+        # Initialize the engine for use. This adds a functional reference.
+        res = self._lib.ENGINE_init(e)
+        assert res == 1
+        # Set the engine as the default RAND provider.
+        res = self._lib.ENGINE_set_default_RAND(e)
+        assert res == 1
+        # Decrement the structural ref incremented by ENGINE_by_id.
+        res = self._lib.ENGINE_free(e)
+        assert res == 1
+        # Decrement the functional ref incremented by ENGINE_init.
+        res = self._lib.ENGINE_finish(e)
+        assert res == 1
+        # Reset the RNG to use the new engine.
+        self._lib.RAND_cleanup()
 
     def openssl_version_text(self):
         """
diff --git a/cryptography/hazmat/bindings/openssl/binding.py b/cryptography/hazmat/bindings/openssl/binding.py
index cde3bdb..714ecc0 100644
--- a/cryptography/hazmat/bindings/openssl/binding.py
+++ b/cryptography/hazmat/bindings/openssl/binding.py
@@ -60,6 +60,7 @@
         "nid",
         "objects",
         "opensslv",
+        "osrandom_engine",
         "pem",
         "pkcs7",
         "pkcs12",
@@ -86,19 +87,23 @@
         if cls.ffi is not None and cls.lib is not None:
             return
 
-        # platform check to set the right library names
+        # OpenSSL goes by a different library name on different operating
+        # systems.
         if sys.platform != "win32":
             libraries = ["crypto", "ssl"]
         else:  # pragma: no cover
-            libraries = ["libeay32", "ssleay32"]
+            libraries = ["libeay32", "ssleay32", "advapi32"]
 
         cls.ffi, cls.lib = build_ffi(cls._module_prefix, cls._modules,
                                      _OSX_PRE_INCLUDE, _OSX_POST_INCLUDE,
                                      libraries)
+        res = cls.lib.Cryptography_add_osrandom_engine()
+        assert res == 1
 
     @classmethod
     def is_available(cls):
-        # OpenSSL is the only binding so for now it must always be available
+        # For now, OpenSSL is considered our "default" binding, so we treat it
+        # as always available.
         return True
 
     @classmethod
@@ -112,15 +117,15 @@
                     cls._lock_cb
                 )
 
-            # use Python's implementation if available
-
+            # Use Python's implementation if available, importing _ssl triggers
+            # the setup for this.
             __import__("_ssl")
 
             if cls.lib.CRYPTO_get_locking_callback() != cls.ffi.NULL:
                 return
 
-            # otherwise setup our version
-
+            # If nothing else has setup a locking callback already, we set up
+            # our own
             num_locks = cls.lib.CRYPTO_num_locks()
             cls._locks = [threading.Lock() for n in range(num_locks)]
 
diff --git a/cryptography/hazmat/bindings/openssl/osrandom_engine.py b/cryptography/hazmat/bindings/openssl/osrandom_engine.py
new file mode 100644
index 0000000..23f2e17
--- /dev/null
+++ b/cryptography/hazmat/bindings/openssl/osrandom_engine.py
@@ -0,0 +1,205 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+INCLUDES = """
+#ifdef _WIN32
+#include <Wincrypt.h>
+#else
+#include <fcntl.h>
+#include <unistd.h>
+#endif
+"""
+
+TYPES = """
+static const char *const Cryptography_osrandom_engine_name;
+static const char *const Cryptography_osrandom_engine_id;
+"""
+
+FUNCTIONS = """
+int Cryptography_add_osrandom_engine(void);
+"""
+
+MACROS = """
+"""
+
+WIN32_CUSTOMIZATIONS = """
+static HCRYPTPROV hCryptProv = 0;
+
+static int osrandom_init(ENGINE *e) {
+    if (hCryptProv > 0) {
+        return 1;
+    }
+    if (CryptAcquireContext(&hCryptProv, NULL, NULL,
+                            PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+static int osrandom_rand_bytes(unsigned char *buffer, int size) {
+    if (hCryptProv == 0) {
+        return 0;
+    }
+
+    if (!CryptGenRandom(hCryptProv, (DWORD)size, buffer)) {
+        ERR_put_error(
+            ERR_LIB_RAND, 0, ERR_R_RAND_LIB, "osrandom_engine.py", 0
+        );
+        return 0;
+    }
+    return 1;
+}
+
+static int osrandom_finish(ENGINE *e) {
+    if (CryptReleaseContext(hCryptProv, 0)) {
+        hCryptProv = 0;
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+static int osrandom_rand_status(void) {
+    if (hCryptProv == 0) {
+        return 0;
+    } else {
+        return 1;
+    }
+}
+"""
+
+POSIX_CUSTOMIZATIONS = """
+static int urandom_fd = -1;
+
+static int osrandom_finish(ENGINE *e);
+
+static int osrandom_init(ENGINE *e) {
+    if (urandom_fd > -1) {
+        return 1;
+    }
+    urandom_fd = open("/dev/urandom", O_RDONLY);
+    if (urandom_fd > -1) {
+        int flags = fcntl(urandom_fd, F_GETFD);
+        if (flags == -1) {
+            osrandom_finish(e);
+            return 0;
+        } else if (fcntl(urandom_fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
+            osrandom_finish(e);
+            return 0;
+        }
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+static int osrandom_rand_bytes(unsigned char *buffer, int size) {
+    ssize_t n;
+    while (size > 0) {
+        do {
+            n = read(urandom_fd, buffer, (size_t)size);
+        } while (n < 0 && errno == EINTR);
+        if (n <= 0) {
+            ERR_put_error(
+                ERR_LIB_RAND, 0, ERR_R_RAND_LIB, "osrandom_engine.py", 0
+            );
+            return 0;
+        }
+        buffer += n;
+        size -= n;
+    }
+    return 1;
+}
+
+static int osrandom_finish(ENGINE *e) {
+    int n;
+    do {
+        n = close(urandom_fd);
+    } while (n < 0 && errno == EINTR);
+    urandom_fd = -1;
+    if (n < 0) {
+        return 0;
+    } else {
+        return 1;
+    }
+}
+
+static int osrandom_rand_status(void) {
+    if (urandom_fd == -1) {
+        return 0;
+    } else {
+        return 1;
+    }
+}
+"""
+
+CUSTOMIZATIONS = """
+static const char *Cryptography_osrandom_engine_id = "osrandom";
+static const char *Cryptography_osrandom_engine_name = "osrandom_engine";
+
+#if defined(_WIN32)
+%(WIN32_CUSTOMIZATIONS)s
+#else
+%(POSIX_CUSTOMIZATIONS)s
+#endif
+
+/* This replicates the behavior of the OpenSSL FIPS RNG, which returns a
+   -1 in the event that there is an error when calling RAND_pseudo_bytes. */
+static int osrandom_pseudo_rand_bytes(unsigned char *buffer, int size) {
+    int res = osrandom_rand_bytes(buffer, size);
+    if (res == 0) {
+        return -1;
+    } else {
+        return res;
+    }
+}
+
+static RAND_METHOD osrandom_rand = {
+    NULL,
+    osrandom_rand_bytes,
+    NULL,
+    NULL,
+    osrandom_pseudo_rand_bytes,
+    osrandom_rand_status,
+};
+
+int Cryptography_add_osrandom_engine(void) {
+    ENGINE *e = ENGINE_new();
+    if (e == NULL) {
+        return 0;
+    }
+    if(!ENGINE_set_id(e, Cryptography_osrandom_engine_id) ||
+            !ENGINE_set_name(e, Cryptography_osrandom_engine_name) ||
+            !ENGINE_set_RAND(e, &osrandom_rand) ||
+            !ENGINE_set_init_function(e, osrandom_init) ||
+            !ENGINE_set_finish_function(e, osrandom_finish)) {
+        ENGINE_free(e);
+        return 0;
+    }
+    if (!ENGINE_add(e)) {
+        ENGINE_free(e);
+        return 0;
+    }
+    if (!ENGINE_free(e)) {
+        return 0;
+    }
+
+    return 1;
+}
+""" % {
+    "WIN32_CUSTOMIZATIONS": WIN32_CUSTOMIZATIONS,
+    "POSIX_CUSTOMIZATIONS": POSIX_CUSTOMIZATIONS,
+}
+
+CONDITIONAL_NAMES = {}
diff --git a/cryptography/hazmat/bindings/openssl/ssl.py b/cryptography/hazmat/bindings/openssl/ssl.py
index 2b4e54f..6c5795b 100644
--- a/cryptography/hazmat/bindings/openssl/ssl.py
+++ b/cryptography/hazmat/bindings/openssl/ssl.py
@@ -268,6 +268,10 @@
 const SSL_METHOD *TLSv1_server_method(void);
 const SSL_METHOD *TLSv1_client_method(void);
 
+const SSL_METHOD *DTLSv1_method(void);
+const SSL_METHOD *DTLSv1_server_method(void);
+const SSL_METHOD *DTLSv1_client_method(void);
+
 const SSL_METHOD *SSLv23_method(void);
 const SSL_METHOD *SSLv23_server_method(void);
 const SSL_METHOD *SSLv23_client_method(void);
diff --git a/docs/changelog.rst b/docs/changelog.rst
index e322b14..4d459bd 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -17,6 +17,7 @@
 * Added :class:`~cryptography.hazmat.primitives.kdf.pbkdf2.PBKDF2HMAC`.
 * Added :class:`~cryptography.hazmat.primitives.kdf.hkdf.HKDF`.
 * Added :doc:`/hazmat/backends/multibackend`.
+* Set default random for the :doc:`/hazmat/backends/openssl` to the OS random engine.
 
 0.1 - 2014-01-08
 ~~~~~~~~~~~~~~~~
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 74b854b..7f4c77f 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -43,6 +43,8 @@
 
 When in doubt, refer to :pep:`8` for Python code.
 
+`Write comments as complete sentences.`_
+
 Every code file must start with the boilerplate notice of the Apache License.
 Additionally, every Python code file must contain
 
@@ -287,6 +289,7 @@
 
 .. _`GitHub`: https://github.com/pyca/cryptography
 .. _`our mailing list`: https://mail.python.org/mailman/listinfo/cryptography-dev
+.. _`Write comments as complete sentences.`: http://nedbatchelder.com/blog/201401/comments_should_be_sentences.html
 .. _`syntax`: http://sphinx-doc.org/domains.html#info-field-lists
 .. _`pytest`: https://pypi.python.org/pypi/pytest
 .. _`tox`: https://pypi.python.org/pypi/tox
diff --git a/docs/hazmat/backends/openssl.rst b/docs/hazmat/backends/openssl.rst
index 4db3972..ea72af9 100644
--- a/docs/hazmat/backends/openssl.rst
+++ b/docs/hazmat/backends/openssl.rst
@@ -16,10 +16,58 @@
     * :class:`~cryptography.hazmat.backends.interfaces.HMACBackend`
     * :class:`~cryptography.hazmat.backends.interfaces.PBKDF2HMACBackend`
 
-    It has one additional public attribute.
+    It also exposes the following:
 
     .. attribute:: name
 
         The string name of this backend: ``"openssl"``
 
+    .. method:: activate_osrandom_engine()
+
+        Activates the OS random engine. This will effectively disable OpenSSL's
+        default CSPRNG.
+
+    .. method:: activate_builtin_random()
+
+        This will activate the default OpenSSL CSPRNG.
+
+OS Random Engine
+----------------
+
+OpenSSL uses a user-space CSPRNG that is seeded from system random (
+``/dev/urandom`` or ``CryptGenRandom``). This CSPRNG is not reseeded
+automatically when a process calls ``fork()``. This can result in situations
+where two different processes can return similar or identical keys and
+compromise the security of the system.
+
+The approach this project has chosen to mitigate this vulnerability is to
+include an engine that replaces the OpenSSL default CSPRNG with one that sources
+its entropy from ``/dev/urandom`` on UNIX-like operating systems and uses
+``CryptGenRandom`` on Windows. This method of pulling from the system pool
+allows us to avoid potential issues with `initializing the RNG`_ as well as
+protecting us from the ``fork()`` weakness.
+
+This engine is **active** by default when importing the OpenSSL backend. When
+active this engine will be used to generate all the random data OpenSSL
+requests.
+
+When importing only the binding it is added to the engine list but
+**not activated**.
+
+
+OS Random Sources
+-----------------
+
+On OS X and FreeBSD ``/dev/urandom`` is an alias for ``/dev/random`` and
+utilizes the `Yarrow`_ algorithm.
+
+On Windows ``CryptGenRandom`` is backed by `Fortuna`_.
+
+Linux uses its own PRNG design. ``/dev/urandom`` is a non-blocking source seeded
+from the same pool as ``/dev/random``.
+
+
 .. _`OpenSSL`: https://www.openssl.org/
+.. _`initializing the RNG`: http://en.wikipedia.org/wiki/OpenSSL#Vulnerability_in_the_Debian_implementation
+.. _`Yarrow`: http://en.wikipedia.org/wiki/Yarrow_algorithm
+.. _`Fortuna`: http://en.wikipedia.org/wiki/Fortuna_(PRNG)
diff --git a/docs/hazmat/primitives/padding.rst b/docs/hazmat/primitives/padding.rst
index 4d79ac8..da5a95d 100644
--- a/docs/hazmat/primitives/padding.rst
+++ b/docs/hazmat/primitives/padding.rst
@@ -54,11 +54,11 @@
 
 .. class:: PaddingContext
 
-    When calling ``padder()`` or ``unpadder()`` you will receive an a return
-    object conforming to the ``PaddingContext`` interface. You can then call
-    ``update(data)`` with data until you have fed everything into the context.
-    Once that is done call ``finalize()`` to finish the operation and obtain
-    the remainder of the data.
+    When calling ``padder()`` or ``unpadder()`` the result will conform to the
+    ``PaddingContext`` interface. You can then call ``update(data)`` with data
+    until you have fed everything into the context. Once that is done call
+    ``finalize()`` to finish the operation and obtain the remainder of the
+    data.
 
     .. method:: update(data)
 
diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst
index 7d95404..85d8e8e 100644
--- a/docs/hazmat/primitives/symmetric-encryption.rst
+++ b/docs/hazmat/primitives/symmetric-encryption.rst
@@ -388,10 +388,10 @@
 .. class:: CipherContext
 
     When calling ``encryptor()`` or ``decryptor()`` on a ``Cipher`` object
-    you will receive a return object conforming to the ``CipherContext``
-    interface. You can then call ``update(data)`` with data until you have fed
-    everything into the context. Once that is done call ``finalize()`` to
-    finish the operation and obtain the remainder of the data.
+    the result will conform to the ``CipherContext`` interface. You can then
+    call ``update(data)`` with data until you have fed everything into the
+    context. Once that is done call ``finalize()`` to finish the operation and
+    obtain the remainder of the data.
 
     Block ciphers require that plaintext or ciphertext always be a multiple of
     their block size, because of that **padding** is sometimes required to make
@@ -429,14 +429,14 @@
 
     When calling ``encryptor()`` or ``decryptor()`` on a ``Cipher`` object
     with an AEAD mode (e.g.
-    :class:`~cryptography.hazmat.primitives.ciphers.modes.GCM`) you will receive
-    a return object conforming to the ``AEADCipherContext`` and
-    ``CipherContext`` interfaces. If it is an encryption context it will
-    additionally be an ``AEADEncryptionContext`` interface.
-    ``AEADCipherContext`` contains an additional method
-    ``authenticate_additional_data`` for adding additional authenticated but
-    unencrypted data (see note below). You should call this before calls to
-    ``update``. When you are done call ``finalize()`` to finish the operation.
+    :class:`~cryptography.hazmat.primitives.ciphers.modes.GCM`) the result will
+    conform to the ``AEADCipherContext`` and ``CipherContext`` interfaces. If
+    it is an encryption context it will additionally be an
+    ``AEADEncryptionContext`` interface. ``AEADCipherContext`` contains an
+    additional method ``authenticate_additional_data`` for adding additional
+    authenticated but unencrypted data (see note below). You should call this
+    before calls to ``update``. When you are done call ``finalize()`` to finish
+    the operation.
 
     .. note::
 
diff --git a/docs/index.rst b/docs/index.rst
index 49e99be..9114b89 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -59,6 +59,7 @@
     :maxdepth: 2
 
     fernet
+    random-numbers
     exceptions
     glossary
 
diff --git a/docs/random-numbers.rst b/docs/random-numbers.rst
new file mode 100644
index 0000000..cd73a7b
--- /dev/null
+++ b/docs/random-numbers.rst
@@ -0,0 +1,20 @@
+Random number generation
+========================
+
+When generating random data for use in cryptographic operations, such as an
+initialization vector for encryption in
+:class:`~cryptography.hazmat.primitives.ciphers.modes.CBC` mode, you do not
+want to use the standard :mod:`random` module APIs. This is because they do not
+provide a cryptographically secure random number generator, which can result in
+major security issues depending on the algorithms in use.
+
+Therefore, it is our recommendation to always use your operating system's
+provided random number generator, which is available as ``os.urandom()``. For
+example, if you need 16 bytes of random data for an initialization vector, you
+can obtain them with:
+
+.. code-block:: pycon
+
+    >>> import os
+    >>> os.urandom(16)
+    '...'
diff --git a/setup.py b/setup.py
index 57a9575..81a50f4 100644
--- a/setup.py
+++ b/setup.py
@@ -81,7 +81,6 @@
     author_email=about["__email__"],
 
     classifiers=[
-        "Development Status :: 2 - Pre-Alpha",
         "Intended Audience :: Developers",
         "License :: OSI Approved :: Apache Software License",
         "Natural Language :: English",
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index ea04c13..b24808d 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -152,3 +152,72 @@
             pytest.skip("Requires an older OpenSSL")
         with pytest.raises(UnsupportedAlgorithm):
             backend.derive_pbkdf2_hmac(hashes.SHA256(), 10, b"", 1000, b"")
+
+    # This test is not in the next class because to check if it's really
+    # default we don't want to run the setup_method before it
+    def test_osrandom_engine_is_default(self):
+        e = backend._lib.ENGINE_get_default_RAND()
+        name = backend._lib.ENGINE_get_name(e)
+        assert name == backend._lib.Cryptography_osrandom_engine_name
+        res = backend._lib.ENGINE_free(e)
+        assert res == 1
+
+
+class TestOpenSSLRandomEngine(object):
+    def teardown_method(self, method):
+        # we need to reset state to being default. backend is a shared global
+        # for all these tests.
+        backend.activate_osrandom_engine()
+        current_default = backend._lib.ENGINE_get_default_RAND()
+        name = backend._lib.ENGINE_get_name(current_default)
+        assert name == backend._lib.Cryptography_osrandom_engine_name
+
+    def test_osrandom_sanity_check(self):
+        # This test serves as a check against catastrophic failure.
+        buf = backend._ffi.new("char[]", 500)
+        res = backend._lib.RAND_bytes(buf, 500)
+        assert res == 1
+        assert backend._ffi.buffer(buf)[:] != "\x00" * 500
+
+    def test_activate_osrandom_already_default(self):
+        e = backend._lib.ENGINE_get_default_RAND()
+        name = backend._lib.ENGINE_get_name(e)
+        assert name == backend._lib.Cryptography_osrandom_engine_name
+        res = backend._lib.ENGINE_free(e)
+        assert res == 1
+        backend.activate_osrandom_engine()
+        e = backend._lib.ENGINE_get_default_RAND()
+        name = backend._lib.ENGINE_get_name(e)
+        assert name == backend._lib.Cryptography_osrandom_engine_name
+        res = backend._lib.ENGINE_free(e)
+        assert res == 1
+
+    def test_activate_osrandom_no_default(self):
+        backend.activate_builtin_random()
+        e = backend._lib.ENGINE_get_default_RAND()
+        assert e == backend._ffi.NULL
+        backend.activate_osrandom_engine()
+        e = backend._lib.ENGINE_get_default_RAND()
+        name = backend._lib.ENGINE_get_name(e)
+        assert name == backend._lib.Cryptography_osrandom_engine_name
+        res = backend._lib.ENGINE_free(e)
+        assert res == 1
+
+    def test_activate_builtin_random(self):
+        e = backend._lib.ENGINE_get_default_RAND()
+        assert e != backend._ffi.NULL
+        name = backend._lib.ENGINE_get_name(e)
+        assert name == backend._lib.Cryptography_osrandom_engine_name
+        res = backend._lib.ENGINE_free(e)
+        assert res == 1
+        backend.activate_builtin_random()
+        e = backend._lib.ENGINE_get_default_RAND()
+        assert e == backend._ffi.NULL
+
+    def test_activate_builtin_random_already_active(self):
+        backend.activate_builtin_random()
+        e = backend._lib.ENGINE_get_default_RAND()
+        assert e == backend._ffi.NULL
+        backend.activate_builtin_random()
+        e = backend._lib.ENGINE_get_default_RAND()
+        assert e == backend._ffi.NULL