Merge pull request #1016 from Ayrx/hkdf-expand-only

HKDF Expand Only implementation
diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py
index 753f4fc..c5c652d 100644
--- a/cryptography/hazmat/backends/multibackend.py
+++ b/cryptography/hazmat/backends/multibackend.py
@@ -146,6 +146,24 @@
         raise UnsupportedAlgorithm("RSA is not supported by the backend",
                                    _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
 
+    def mgf1_hash_supported(self, algorithm):
+        for b in self._filtered_backends(RSABackend):
+            return b.mgf1_hash_supported(algorithm)
+        raise UnsupportedAlgorithm("RSA is not supported by the backend",
+                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
+    def decrypt_rsa(self, private_key, ciphertext, padding):
+        for b in self._filtered_backends(RSABackend):
+            return b.decrypt_rsa(private_key, ciphertext, padding)
+        raise UnsupportedAlgorithm("RSA is not supported by the backend",
+                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
+    def encrypt_rsa(self, public_key, plaintext, padding):
+        for b in self._filtered_backends(RSABackend):
+            return b.encrypt_rsa(public_key, plaintext, padding)
+        raise UnsupportedAlgorithm("RSA is not supported by the backend",
+                                   _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
     def generate_dsa_parameters(self, key_size):
         for b in self._filtered_backends(DSABackend):
             return b.generate_dsa_parameters(key_size)
diff --git a/cryptography/hazmat/bindings/openssl/ssl.py b/cryptography/hazmat/bindings/openssl/ssl.py
index 0b15411..cd8fa1c 100644
--- a/cryptography/hazmat/bindings/openssl/ssl.py
+++ b/cryptography/hazmat/bindings/openssl/ssl.py
@@ -15,6 +15,8 @@
 
 INCLUDES = """
 #include <openssl/ssl.h>
+
+typedef STACK_OF(SSL_CIPHER) Cryptography_STACK_OF_SSL_CIPHER;
 """
 
 TYPES = """
@@ -24,6 +26,7 @@
 static const long Cryptography_HAS_SSL2;
 static const long Cryptography_HAS_TLSv1_1;
 static const long Cryptography_HAS_TLSv1_2;
+static const long Cryptography_HAS_SECURE_RENEGOTIATION;
 
 /* Internally invented symbol to tell us if SNI is supported */
 static const long Cryptography_HAS_TLSEXT_HOSTNAME;
@@ -84,6 +87,8 @@
 static const long SSL_OP_NO_TICKET;
 static const long SSL_OP_ALL;
 static const long SSL_OP_SINGLE_ECDH_USE;
+static const long SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
+static const long SSL_OP_LEGACY_SERVER_CONNECT;
 static const long SSL_VERIFY_PEER;
 static const long SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
 static const long SSL_VERIFY_CLIENT_ONCE;
@@ -153,6 +158,7 @@
 static const long TLSEXT_NAMETYPE_host_name;
 
 typedef ... SSL_CIPHER;
+typedef ... Cryptography_STACK_OF_SSL_CIPHER;
 """
 
 FUNCTIONS = """
@@ -190,6 +196,7 @@
 int SSL_do_handshake(SSL *);
 int SSL_shutdown(SSL *);
 const char *SSL_get_cipher_list(const SSL *, int);
+Cryptography_STACK_OF_SSL_CIPHER *SSL_get_ciphers(const SSL *);
 
 /*  context */
 void SSL_CTX_free(SSL_CTX *);
@@ -248,6 +255,7 @@
 int SSL_want_write(const SSL *);
 
 long SSL_total_renegotiations(SSL *);
+long SSL_get_secure_renegotiation_support(SSL *);
 
 /* Defined as unsigned long because SSL_OP_ALL is greater than signed 32-bit
    and Windows defines long as 32-bit. */
@@ -351,9 +359,23 @@
                           const unsigned char *, unsigned int);
 void SSL_get0_next_proto_negotiated(const SSL *,
                                     const unsigned char **, unsigned *);
+
+int sk_SSL_CIPHER_num(Cryptography_STACK_OF_SSL_CIPHER *);
+SSL_CIPHER *sk_SSL_CIPHER_value(Cryptography_STACK_OF_SSL_CIPHER *, int);
 """
 
 CUSTOMIZATIONS = """
+/** Secure renegotiation is supported in OpenSSL >= 0.9.8m
+ *  But some Linux distributions have back ported some features.
+ */
+#ifndef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
+static const long Cryptography_HAS_SECURE_RENEGOTIATION = 0;
+long (*SSL_get_secure_renegotiation_support)(SSL *) = NULL;
+const long SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = 0;
+const long SSL_OP_LEGACY_SERVER_CONNECT = 0;
+#else
+static const long Cryptography_HAS_SECURE_RENEGOTIATION = 1;
+#endif
 #ifdef OPENSSL_NO_SSL2
 static const long Cryptography_HAS_SSL2 = 0;
 SSL_METHOD* (*SSLv2_method)(void) = NULL;
@@ -551,5 +573,11 @@
         "SSL_CTX_set_next_proto_select_cb",
         "SSL_select_next_proto",
         "SSL_get0_next_proto_negotiated",
-    ]
+    ],
+
+    "Cryptography_HAS_SECURE_RENEGOTIATION": [
+        "SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION",
+        "SSL_OP_LEGACY_SERVER_CONNECT",
+        "SSL_get_secure_renegotiation_support",
+    ],
 }
diff --git a/docs/faq.rst b/docs/faq.rst
index 0b7bdce..d06c1d0 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -14,5 +14,18 @@
 
 If you prefer NaCl's design, we highly recommend `PyNaCl`_.
 
+When I try to use ``cryptography`` on Windows I get a ``cffi.ffiplatform.VerificationError``
+--------------------------------------------------------------------------------------------
+
+This error looks something like:
+
+.. code-block:: console
+
+    cffi.ffiplatform.VerificationError: importing '<some_path>.pyd': DLL load failed:
+
+It typically occurs on Windows when you have not installed OpenSSL. Download
+a `pre-compiled binary`_ to resolve this issue.
+
 .. _`NaCl`: http://nacl.cr.yp.to/
 .. _`PyNaCl`: https://pynacl.readthedocs.org
+.. _`pre-compiled binary`: https://www.openssl.org/related/binaries.html
diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst
index f363b54..1e1a6b2 100644
--- a/docs/hazmat/backends/interfaces.rst
+++ b/docs/hazmat/backends/interfaces.rst
@@ -275,6 +275,14 @@
             :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding`
             provider.
 
+        :return bytes: The decrypted data.
+
+        :raises cryptography.exceptions.UnsupportedAlgorithm: If an unsupported
+            MGF, hash function, or padding is chosen.
+
+        :raises ValueError: When decryption fails or key size does not match
+            ciphertext length.
+
     .. method:: encrypt_rsa(public_key, plaintext, padding)
 
         :param public_key: An instance of an
@@ -287,6 +295,12 @@
             :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding`
             provider.
 
+        :return bytes: The encrypted data.
+
+        :raises cryptography.exceptions.UnsupportedAlgorithm: If an unsupported
+            MGF, hash function, or padding is chosen.
+
+        :raises ValueError: When plaintext is too long for the key size.
 
 .. class:: TraditionalOpenSSLSerializationBackend
 
diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst
index 2700154..7bae714 100644
--- a/docs/hazmat/primitives/asymmetric/rsa.rst
+++ b/docs/hazmat/primitives/asymmetric/rsa.rst
@@ -154,21 +154,39 @@
             :class:`~cryptography.hazmat.primitives.asymmetric.padding.OAEP`
             it may also be raised for invalid label values.
 
-        .. code-block:: python
+        .. doctest::
 
-            from cryptography.hazmat.backends import default_backend
-            from cryptography.hazmat.primitives import hashes
-            from cryptography.hazmat.primitives.asymmetric import padding
+            >>> from cryptography.hazmat.backends import default_backend
+            >>> from cryptography.hazmat.primitives import hashes
+            >>> from cryptography.hazmat.primitives.asymmetric import padding
 
-            plaintext = private_key.decrypt(
-                ciphertext,
-                padding.OAEP(
-                    mgf=padding.MGF1(algorithm=hashes.SHA1()),
-                    algorithm=hashes.SHA1(),
-                    label=None
-                ),
-                default_backend()
-            )
+            >>> # Generate a key
+            >>> private_key = rsa.RSAPrivateKey.generate(
+            ...     public_exponent=65537,
+            ...     key_size=2048,
+            ...     backend=default_backend()
+            ... )
+            >>> public_key = private_key.public_key()
+            >>> # encrypt some data
+            >>> ciphertext = public_key.encrypt(
+            ...     b"encrypted data",
+            ...     padding.OAEP(
+            ...         mgf=padding.MGF1(algorithm=hashes.SHA1()),
+            ...         algorithm=hashes.SHA1(),
+            ...         label=None
+            ...     ),
+            ...     default_backend()
+            ... )
+            >>> # Now do the actual decryption
+            >>> plaintext = private_key.decrypt(
+            ...     ciphertext,
+            ...     padding.OAEP(
+            ...         mgf=padding.MGF1(algorithm=hashes.SHA1()),
+            ...         algorithm=hashes.SHA1(),
+            ...         label=None
+            ...     ),
+            ...     default_backend()
+            ... )
 
 
 .. class:: RSAPublicKey(public_exponent, modulus)
@@ -306,27 +324,29 @@
             :class:`~cryptography.hazmat.primitives.asymmetric.padding.OAEP`
             it may also be raised for invalid label values.
 
-        .. code-block:: python
+        .. doctest::
 
-            from cryptography.hazmat.backends import default_backend
-            from cryptography.hazmat.primitives import hashes
-            from cryptography.hazmat.primitives.asymmetric import padding, rsa
+            >>> from cryptography.hazmat.backends import default_backend
+            >>> from cryptography.hazmat.primitives import hashes
+            >>> from cryptography.hazmat.primitives.asymmetric import padding
 
-            private_key = rsa.RSAPrivateKey.generate(
-                public_exponent=65537,
-                key_size=2048,
-                backend=default_backend()
-            )
-            public_key = private_key.public_key()
-            ciphertext = public_key.encrypt(
-                plaintext,
-                padding.OAEP(
-                    mgf=padding.MGF1(algorithm=hashes.SHA1()),
-                    algorithm=hashes.SHA1(),
-                    label=None
-                ),
-                default_backend()
-            )
+            >>> # Generate a key
+            >>> private_key = rsa.RSAPrivateKey.generate(
+            ...     public_exponent=65537,
+            ...     key_size=2048,
+            ...     backend=default_backend()
+            ... )
+            >>> public_key = private_key.public_key()
+            >>> # encrypt some data
+            >>> ciphertext = public_key.encrypt(
+            ...     b"encrypted data",
+            ...     padding.OAEP(
+            ...         mgf=padding.MGF1(algorithm=hashes.SHA1()),
+            ...         algorithm=hashes.SHA1(),
+            ...         label=None
+            ...     ),
+            ...     default_backend()
+            ... )
 
 
 Handling partial RSA private keys
diff --git a/docs/hazmat/primitives/key-derivation-functions.rst b/docs/hazmat/primitives/key-derivation-functions.rst
index fdc540b..de6bf5f 100644
--- a/docs/hazmat/primitives/key-derivation-functions.rst
+++ b/docs/hazmat/primitives/key-derivation-functions.rst
@@ -194,7 +194,7 @@
     .. method:: derive(key_material)
 
         :param bytes key_material: The input key material.
-        :retunr bytes: The derived key.
+        :return bytes: The derived key.
 
         Derives a new key from the input key material by performing both the
         extract and expand operations.
diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst
index 78bf663..e5d8c65 100644
--- a/docs/hazmat/primitives/symmetric-encryption.rst
+++ b/docs/hazmat/primitives/symmetric-encryption.rst
@@ -20,7 +20,7 @@
 message but an attacker can create bogus messages and force the application to
 decrypt them.
 
-For this reason it is *strongly* recommended to combine encryption with a
+For this reason it is **strongly** recommended to combine encryption with a
 message authentication code, such as :doc:`HMAC </hazmat/primitives/mac/hmac>`, in
 an "encrypt-then-MAC" formulation as `described by Colin Percival`_.
 
diff --git a/docs/installation.rst b/docs/installation.rst
index e2b3589..1efe1af 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -128,13 +128,21 @@
 --------------------------------
 
 Because of a `bug in conda`_, attempting to install cryptography out of the box
-will result in an error. This can be resolved by setting the
-``DYLD_LIBRARY_PATH`` environment variable:
+will result in an error. This can be resolved by setting the library path
+environment variable for your platform.
+
+On OS X:
 
 .. code-block:: console
 
     $ env DYLD_LIBRARY_PATH="$HOME/anaconda/lib" pip install cryptography
 
+and on Linux:
+
+.. code-block:: console
+
+    $ env LD_LIBRARY_PATH="$HOME/anaconda/lib" pip install cryptography
+
 You will need to set this variable every time you start Python. For more
 information, consult `Greg Wilson's blog post`_ on the subject.
 
diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
index fd2a30c..088465a 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -98,6 +98,15 @@
                                     algorithm):
         pass
 
+    def mgf1_hash_supported(self, algorithm):
+        pass
+
+    def decrypt_rsa(self, private_key, ciphertext, padding):
+        pass
+
+    def encrypt_rsa(self, public_key, plaintext, padding):
+        pass
+
 
 @utils.register_interface(DSABackend)
 class DummyDSABackend(object):
@@ -211,6 +220,12 @@
         backend.create_rsa_verification_ctx("public_key", "sig",
                                             padding.PKCS1v15(), hashes.MD5())
 
+        backend.mgf1_hash_supported(hashes.MD5())
+
+        backend.encrypt_rsa("public_key", "encryptme", padding.PKCS1v15())
+
+        backend.decrypt_rsa("private_key", "encrypted", padding.PKCS1v15())
+
         backend = MultiBackend([])
         with raises_unsupported_algorithm(
             _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
@@ -229,6 +244,21 @@
             backend.create_rsa_verification_ctx(
                 "public_key", "sig", padding.PKCS1v15(), hashes.MD5())
 
+        with raises_unsupported_algorithm(
+            _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+        ):
+            backend.mgf1_hash_supported(hashes.MD5())
+
+        with raises_unsupported_algorithm(
+            _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+        ):
+            backend.encrypt_rsa("public_key", "encryptme", padding.PKCS1v15())
+
+        with raises_unsupported_algorithm(
+            _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+        ):
+            backend.decrypt_rsa("private_key", "encrypted", padding.PKCS1v15())
+
     def test_dsa(self):
         backend = MultiBackend([
             DummyDSABackend()