Merge pull request #959 from public/openssl-loading-backend

OpenSSL loading backend
diff --git a/.travis.yml b/.travis.yml
index f4c4047..cacd1e2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -49,6 +49,7 @@
         - os: osx
           env: TOX_ENV=py3pep8
           compiler: clang
+
         - os: linux
           env: TOX_ENV=docs
           compiler: clang
diff --git a/.travis/install.sh b/.travis/install.sh
index 7979005..e028033 100755
--- a/.travis/install.sh
+++ b/.travis/install.sh
@@ -32,18 +32,18 @@
     if which pyenv > /dev/null; then eval "$(pyenv init -)"; fi
     case "${TOX_ENV}" in
         py26)
-            curl -O https://raw.githubusercontent.com/pypa/pip/master/contrib/get-pip.py
+            curl -O https://bootstrap.pypa.io/get-pip.py
             sudo python get-pip.py
             sudo pip install virtualenv
             ;;
         py27)
-            curl -O https://raw.githubusercontent.com/pypa/pip/master/contrib/get-pip.py
+            curl -O https://bootstrap.pypa.io/get-pip.py
             sudo python get-pip.py
             sudo pip install virtualenv
             ;;
         pypy)
-            pyenv install pypy-2.2.1
-            pyenv global pypy-2.2.1
+            pyenv install pypy-2.3
+            pyenv global pypy-2.3
             pip install virtualenv
             ;;
         py32)
@@ -62,7 +62,7 @@
             pip install virtualenv
             ;;
         docs)
-            curl -O https://raw.githubusercontent.com/pypa/pip/master/contrib/get-pip.py
+            curl -O https://bootstrap.pypa.io/get-pip.py
             sudo python get-pip.py
             sudo pip install virtualenv
             ;;
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 54d6229..9342398 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,14 @@
 
 .. note:: This version is not yet released and is under active development.
 
+* Added :class:`~cryptography.hazmat.primitives.kdf.hkdf.HKDFExpand`.
+* Added :class:`~cryptography.hazmat.primitives.ciphers.modes.CFB8` support
+  for :class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES` and
+  :class:`~cryptography.hazmat.primitives.ciphers.algorithms.TripleDES` on
+  :doc:`/hazmat/backends/commoncrypto` and :doc:`/hazmat/backends/openssl`.
+* Added ``AES`` :class:`~cryptography.hazmat.primitives.ciphers.modes.CTR`
+  support to the OpenSSL backend when linked against 0.9.8.
+
 0.4 - 2014-05-03
 ~~~~~~~~~~~~~~~~
 
diff --git a/README.rst b/README.rst
index ac7ec61..5e1a82a 100644
--- a/README.rst
+++ b/README.rst
@@ -1,6 +1,10 @@
 Cryptography
 ============
 
+.. image:: https://pypip.in/version/cryptography/badge.svg
+    :target: https://pypi.python.org/pypi/cryptography/
+    :alt: Latest Version
+
 .. image:: https://travis-ci.org/pyca/cryptography.svg?branch=master
     :target: https://travis-ci.org/pyca/cryptography
 
diff --git a/cryptography/fernet.py b/cryptography/fernet.py
index 674ce8a..cdb9bdc 100644
--- a/cryptography/fernet.py
+++ b/cryptography/fernet.py
@@ -43,7 +43,7 @@
         key = base64.urlsafe_b64decode(key)
         if len(key) != 32:
             raise ValueError(
-                "Fernet key must be 32 url-safe base64-encoded bytes"
+                "Fernet key must be 32 url-safe base64-encoded bytes."
             )
 
         self._signing_key = key[:16]
@@ -60,10 +60,8 @@
         return self._encrypt_from_parts(data, current_time, iv)
 
     def _encrypt_from_parts(self, data, current_time, iv):
-        if isinstance(data, six.text_type):
-            raise TypeError(
-                "Unicode-objects must be encoded before encryption"
-            )
+        if not isinstance(data, bytes):
+            raise TypeError("data must be bytes.")
 
         padder = padding.PKCS7(algorithms.AES.block_size).padder()
         padded_data = padder.update(data) + padder.finalize()
@@ -82,10 +80,8 @@
         return base64.urlsafe_b64encode(basic_parts + hmac)
 
     def decrypt(self, token, ttl=None):
-        if isinstance(token, six.text_type):
-            raise TypeError(
-                "Unicode-objects must be encoded before decryption"
-            )
+        if not isinstance(token, bytes):
+            raise TypeError("token must be bytes.")
 
         current_time = int(time.time())
 
diff --git a/cryptography/hazmat/backends/commoncrypto/backend.py b/cryptography/hazmat/backends/commoncrypto/backend.py
index 4faca73..213cbd8 100644
--- a/cryptography/hazmat/backends/commoncrypto/backend.py
+++ b/cryptography/hazmat/backends/commoncrypto/backend.py
@@ -28,7 +28,7 @@
     AES, ARC4, Blowfish, CAST5, TripleDES
 )
 from cryptography.hazmat.primitives.ciphers.modes import (
-    CBC, CFB, CTR, ECB, GCM, OFB
+    CBC, CFB, CFB8, CTR, ECB, GCM, OFB
 )
 
 
@@ -154,7 +154,7 @@
     def _register_cipher_adapter(self, cipher_cls, cipher_const, mode_cls,
                                  mode_const):
         if (cipher_cls, mode_cls) in self._cipher_registry:
-            raise ValueError("Duplicate registration for: {0} {1}".format(
+            raise ValueError("Duplicate registration for: {0} {1}.".format(
                 cipher_cls, mode_cls)
             )
         self._cipher_registry[cipher_cls, mode_cls] = (cipher_const,
@@ -165,6 +165,7 @@
             (CBC, self._lib.kCCModeCBC),
             (ECB, self._lib.kCCModeECB),
             (CFB, self._lib.kCCModeCFB),
+            (CFB8, self._lib.kCCModeCFB8),
             (OFB, self._lib.kCCModeOFB),
             (CTR, self._lib.kCCModeCTR),
             (GCM, self._lib.kCCModeGCM),
@@ -178,6 +179,7 @@
         for mode_cls, mode_const in [
             (CBC, self._lib.kCCModeCBC),
             (CFB, self._lib.kCCModeCFB),
+            (CFB8, self._lib.kCCModeCFB8),
             (OFB, self._lib.kCCModeOFB),
         ]:
             self._register_cipher_adapter(
@@ -226,7 +228,7 @@
             # rdar://15589470
             raise ValueError(
                 "The length of the provided data is not a multiple of "
-                "the block length"
+                "the block length."
             )
         else:
             raise InternalError(
@@ -264,7 +266,7 @@
         # This bug has been filed as rdar://15589470
         self._bytes_processed = 0
         if (isinstance(cipher, interfaces.BlockCipherAlgorithm) and not
-                isinstance(mode, (OFB, CFB, CTR))):
+                isinstance(mode, (OFB, CFB, CFB8, CTR))):
             self._byte_block_size = cipher.block_size // 8
         else:
             self._byte_block_size = 1
@@ -275,7 +277,7 @@
         except KeyError:
             raise UnsupportedAlgorithm(
                 "cipher {0} in {1} mode is not supported "
-                "by this backend".format(
+                "by this backend.".format(
                     cipher.name, mode.name if mode else mode),
                 _Reasons.UNSUPPORTED_CIPHER
             )
@@ -322,7 +324,7 @@
         if self._bytes_processed % self._byte_block_size:
             raise ValueError(
                 "The length of the provided data is not a multiple of "
-                "the block length"
+                "the block length."
             )
         buf = self._backend._ffi.new("unsigned char[]", self._byte_block_size)
         outlen = self._backend._ffi.new("size_t *")
@@ -349,7 +351,7 @@
         except KeyError:
             raise UnsupportedAlgorithm(
                 "cipher {0} in {1} mode is not supported "
-                "by this backend".format(
+                "by this backend.".format(
                     cipher.name, mode.name if mode else mode),
                 _Reasons.UNSUPPORTED_CIPHER
             )
@@ -423,7 +425,7 @@
                 methods = self._backend._hash_mapping[self.algorithm.name]
             except KeyError:
                 raise UnsupportedAlgorithm(
-                    "{0} is not a supported hash on this backend".format(
+                    "{0} is not a supported hash on this backend.".format(
                         algorithm.name),
                     _Reasons.UNSUPPORTED_HASH
                 )
@@ -467,7 +469,7 @@
                 alg = self._backend._supported_hmac_algorithms[algorithm.name]
             except KeyError:
                 raise UnsupportedAlgorithm(
-                    "{0} is not a supported HMAC hash on this backend".format(
+                    "{0} is not a supported HMAC hash on this backend.".format(
                         algorithm.name),
                     _Reasons.UNSUPPORTED_HASH
                 )
diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py
index 753f4fc..21d307c 100644
--- a/cryptography/hazmat/backends/multibackend.py
+++ b/cryptography/hazmat/backends/multibackend.py
@@ -52,7 +52,7 @@
             except UnsupportedAlgorithm:
                 pass
         raise UnsupportedAlgorithm(
-            "cipher {0} in {1} mode is not supported by this backend".format(
+            "cipher {0} in {1} mode is not supported by this backend.".format(
                 algorithm.name, mode.name if mode else mode),
             _Reasons.UNSUPPORTED_CIPHER
         )
@@ -64,7 +64,7 @@
             except UnsupportedAlgorithm:
                 pass
         raise UnsupportedAlgorithm(
-            "cipher {0} in {1} mode is not supported by this backend".format(
+            "cipher {0} in {1} mode is not supported by this backend.".format(
                 algorithm.name, mode.name if mode else mode),
             _Reasons.UNSUPPORTED_CIPHER
         )
@@ -82,7 +82,7 @@
             except UnsupportedAlgorithm:
                 pass
         raise UnsupportedAlgorithm(
-            "{0} is not a supported hash on this backend".format(
+            "{0} is not a supported hash on this backend.".format(
                 algorithm.name),
             _Reasons.UNSUPPORTED_HASH
         )
@@ -100,7 +100,7 @@
             except UnsupportedAlgorithm:
                 pass
         raise UnsupportedAlgorithm(
-            "{0} is not a supported hash on this backend".format(
+            "{0} is not a supported hash on this backend.".format(
                 algorithm.name),
             _Reasons.UNSUPPORTED_HASH
         )
@@ -121,7 +121,7 @@
             except UnsupportedAlgorithm:
                 pass
         raise UnsupportedAlgorithm(
-            "{0} is not a supported hash on this backend".format(
+            "{0} is not a supported hash on this backend.".format(
                 algorithm.name),
             _Reasons.UNSUPPORTED_HASH
         )
@@ -129,13 +129,13 @@
     def generate_rsa_private_key(self, public_exponent, key_size):
         for b in self._filtered_backends(RSABackend):
             return b.generate_rsa_private_key(public_exponent, key_size)
-        raise UnsupportedAlgorithm("RSA is not supported by the backend",
+        raise UnsupportedAlgorithm("RSA is not supported by the backend.",
                                    _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
 
     def create_rsa_signature_ctx(self, private_key, padding, algorithm):
         for b in self._filtered_backends(RSABackend):
             return b.create_rsa_signature_ctx(private_key, padding, algorithm)
-        raise UnsupportedAlgorithm("RSA is not supported by the backend",
+        raise UnsupportedAlgorithm("RSA is not supported by the backend.",
                                    _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
 
     def create_rsa_verification_ctx(self, public_key, signature, padding,
@@ -143,44 +143,62 @@
         for b in self._filtered_backends(RSABackend):
             return b.create_rsa_verification_ctx(public_key, signature,
                                                  padding, algorithm)
-        raise UnsupportedAlgorithm("RSA is not supported by the backend",
+        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)
-        raise UnsupportedAlgorithm("DSA is not supported by the backend",
+        raise UnsupportedAlgorithm("DSA is not supported by the backend.",
                                    _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
 
     def generate_dsa_private_key(self, parameters):
         for b in self._filtered_backends(DSABackend):
             return b.generate_dsa_private_key(parameters)
-        raise UnsupportedAlgorithm("DSA is not supported by the backend",
+        raise UnsupportedAlgorithm("DSA is not supported by the backend.",
                                    _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
 
     def create_dsa_verification_ctx(self, public_key, signature, algorithm):
         for b in self._filtered_backends(DSABackend):
             return b.create_dsa_verification_ctx(public_key, signature,
                                                  algorithm)
-        raise UnsupportedAlgorithm("DSA is not supported by the backend",
+        raise UnsupportedAlgorithm("DSA is not supported by the backend.",
                                    _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
 
     def create_dsa_signature_ctx(self, private_key, algorithm):
         for b in self._filtered_backends(DSABackend):
             return b.create_dsa_signature_ctx(private_key, algorithm)
-        raise UnsupportedAlgorithm("DSA is not supported by the backend",
+        raise UnsupportedAlgorithm("DSA is not supported by the backend.",
                                    _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
 
     def dsa_hash_supported(self, algorithm):
         for b in self._filtered_backends(DSABackend):
             return b.dsa_hash_supported(algorithm)
-        raise UnsupportedAlgorithm("DSA is not supported by the backend",
+        raise UnsupportedAlgorithm("DSA is not supported by the backend.",
                                    _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
 
     def dsa_parameters_supported(self, p, q, g):
         for b in self._filtered_backends(DSABackend):
             return b.dsa_parameters_supported(p, q, g)
-        raise UnsupportedAlgorithm("DSA is not supported by the backend",
+        raise UnsupportedAlgorithm("DSA is not supported by the backend.",
                                    _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
 
     def cmac_algorithm_supported(self, algorithm):
@@ -195,5 +213,5 @@
                 return b.create_cmac_ctx(algorithm)
             except UnsupportedAlgorithm:
                 pass
-        raise UnsupportedAlgorithm("This backend does not support CMAC",
+        raise UnsupportedAlgorithm("This backend does not support CMAC.",
                                    _Reasons.UNSUPPORTED_CIPHER)
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index c8a93ef..5d9626d 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -38,7 +38,7 @@
     AES, ARC4, Blowfish, CAST5, Camellia, IDEA, SEED, TripleDES
 )
 from cryptography.hazmat.primitives.ciphers.modes import (
-    CBC, CFB, CTR, ECB, GCM, OFB
+    CBC, CFB, CFB8, CTR, ECB, GCM, OFB
 )
 
 
@@ -114,11 +114,14 @@
 
     def openssl_version_text(self):
         """
-        Friendly string name of linked OpenSSL.
+        Friendly string name of the loaded OpenSSL library. This is not
+        necessarily the same version as it was compiled against.
 
         Example: OpenSSL 1.0.1e 11 Feb 2013
         """
-        return self._ffi.string(self._lib.OPENSSL_VERSION_TEXT).decode("ascii")
+        return self._ffi.string(
+            self._lib.SSLeay_version(self._lib.SSLEAY_VERSION)
+        ).decode("ascii")
 
     def create_hmac_ctx(self, key, algorithm):
         return _HMACContext(self, key, algorithm)
@@ -134,6 +137,14 @@
         return _HashContext(self, algorithm)
 
     def cipher_supported(self, cipher, mode):
+        if self._evp_cipher_supported(cipher, mode):
+            return True
+        elif isinstance(mode, CTR) and isinstance(cipher, AES):
+            return True
+        else:
+            return False
+
+    def _evp_cipher_supported(self, cipher, mode):
         try:
             adapter = self._cipher_registry[type(cipher), type(mode)]
         except KeyError:
@@ -143,22 +154,25 @@
 
     def register_cipher_adapter(self, cipher_cls, mode_cls, adapter):
         if (cipher_cls, mode_cls) in self._cipher_registry:
-            raise ValueError("Duplicate registration for: {0} {1}".format(
+            raise ValueError("Duplicate registration for: {0} {1}.".format(
                 cipher_cls, mode_cls)
             )
         self._cipher_registry[cipher_cls, mode_cls] = adapter
 
     def _register_default_ciphers(self):
-        for cipher_cls, mode_cls in itertools.product(
-            [AES, Camellia],
-            [CBC, CTR, ECB, OFB, CFB],
-        ):
+        for mode_cls in [CBC, CTR, ECB, OFB, CFB, CFB8]:
             self.register_cipher_adapter(
-                cipher_cls,
+                AES,
                 mode_cls,
                 GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
             )
-        for mode_cls in [CBC, CFB, OFB]:
+        for mode_cls in [CBC, CTR, ECB, OFB, CFB]:
+            self.register_cipher_adapter(
+                Camellia,
+                mode_cls,
+                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
+            )
+        for mode_cls in [CBC, CFB, CFB8, OFB]:
             self.register_cipher_adapter(
                 TripleDES,
                 mode_cls,
@@ -197,10 +211,24 @@
         )
 
     def create_symmetric_encryption_ctx(self, cipher, mode):
-        return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT)
+        if (isinstance(mode, CTR) and isinstance(cipher, AES)
+                and not self._evp_cipher_supported(cipher, mode)):
+            # This is needed to provide support for AES CTR mode in OpenSSL
+            # 0.9.8. It can be removed when we drop 0.9.8 support (RHEL 5
+            # extended life ends 2020).
+            return _AESCTRCipherContext(self, cipher, mode)
+        else:
+            return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT)
 
     def create_symmetric_decryption_ctx(self, cipher, mode):
-        return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT)
+        if (isinstance(mode, CTR) and isinstance(cipher, AES)
+                and not self._evp_cipher_supported(cipher, mode)):
+            # This is needed to provide support for AES CTR mode in OpenSSL
+            # 0.9.8. It can be removed when we drop 0.9.8 support (RHEL 5
+            # extended life ends 2020).
+            return _AESCTRCipherContext(self, cipher, mode)
+        else:
+            return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT)
 
     def pbkdf2_hmac_supported(self, algorithm):
         if self._lib.Cryptography_HAS_PBKDF2_HMAC:
@@ -233,7 +261,7 @@
             if not isinstance(algorithm, hashes.SHA1):
                 raise UnsupportedAlgorithm(
                     "This version of OpenSSL only supports PBKDF2HMAC with "
-                    "SHA1",
+                    "SHA1.",
                     _Reasons.UNSUPPORTED_HASH
                 )
             res = self._lib.PKCS5_PBKDF2_HMAC_SHA1(
@@ -271,7 +299,7 @@
     def _unknown_error(self, error):
         return InternalError(
             "Unknown error code {0} from OpenSSL, "
-            "you should probably file a bug. {1}".format(
+            "you should probably file a bug. {1}.".format(
                 error.code, self._err_string(error.code)
             )
         )
@@ -328,13 +356,13 @@
 
     def generate_rsa_private_key(self, public_exponent, key_size):
         if public_exponent < 3:
-            raise ValueError("public_exponent must be >= 3")
+            raise ValueError("public_exponent must be >= 3.")
 
         if public_exponent & 1 == 0:
-            raise ValueError("public_exponent must be odd")
+            raise ValueError("public_exponent must be odd.")
 
         if key_size < 512:
-            raise ValueError("key_size must be at least 512-bits")
+            raise ValueError("key_size must be at least 512-bits.")
 
         ctx = self._lib.RSA_new()
         assert ctx != self._ffi.NULL
@@ -509,13 +537,13 @@
     def generate_dsa_parameters(self, key_size):
         if key_size not in (1024, 2048, 3072):
             raise ValueError(
-                "Key size must be 1024 or 2048 or 3072 bits")
+                "Key size must be 1024 or 2048 or 3072 bits.")
 
         if (self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f and
                 key_size > 1024):
             raise ValueError(
                 "Key size must be 1024 because OpenSSL < 1.0.0 doesn't "
-                "support larger key sizes")
+                "support larger key sizes.")
 
         ctx = self._lib.DSA_new()
         assert ctx != self._ffi.NULL
@@ -614,28 +642,28 @@
             padding_enum = self._lib.RSA_PKCS1_OAEP_PADDING
             if not isinstance(padding._mgf, MGF1):
                 raise UnsupportedAlgorithm(
-                    "Only MGF1 is supported by this backend",
+                    "Only MGF1 is supported by this backend.",
                     _Reasons.UNSUPPORTED_MGF
                 )
 
             if not isinstance(padding._mgf._algorithm, hashes.SHA1):
                 raise UnsupportedAlgorithm(
                     "This backend supports only SHA1 inside MGF1 when "
-                    "using OAEP",
+                    "using OAEP.",
                     _Reasons.UNSUPPORTED_HASH
                 )
 
             if padding._label is not None and padding._label != b"":
-                raise ValueError("This backend does not support OAEP labels")
+                raise ValueError("This backend does not support OAEP labels.")
 
             if not isinstance(padding._algorithm, hashes.SHA1):
                 raise UnsupportedAlgorithm(
-                    "This backend only supports SHA1 when using OAEP",
+                    "This backend only supports SHA1 when using OAEP.",
                     _Reasons.UNSUPPORTED_HASH
                 )
         else:
             raise UnsupportedAlgorithm(
-                "{0} is not supported by this backend".format(
+                "{0} is not supported by this backend.".format(
                     padding.name
                 ),
                 _Reasons.UNSUPPORTED_PADDING
@@ -715,14 +743,14 @@
                     self._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE)
             raise ValueError(
                 "Data too long for key size. Encrypt less data or use a "
-                "larger key size"
+                "larger key size."
             )
         else:
             assert (
                 errors[0].reason == self._lib.RSA_R_BLOCK_TYPE_IS_NOT_01 or
                 errors[0].reason == self._lib.RSA_R_BLOCK_TYPE_IS_NOT_02
             )
-            raise ValueError("Decryption failed")
+            raise ValueError("Decryption failed.")
 
     def cmac_algorithm_supported(self, algorithm):
         return (
@@ -838,7 +866,7 @@
         except KeyError:
             raise UnsupportedAlgorithm(
                 "cipher {0} in {1} mode is not supported "
-                "by this backend".format(
+                "by this backend.".format(
                     cipher.name, mode.name if mode else mode),
                 _Reasons.UNSUPPORTED_CIPHER
             )
@@ -847,7 +875,7 @@
         if evp_cipher == self._backend._ffi.NULL:
             raise UnsupportedAlgorithm(
                 "cipher {0} in {1} mode is not supported "
-                "by this backend".format(
+                "by this backend.".format(
                     cipher.name, mode.name if mode else mode),
                 _Reasons.UNSUPPORTED_CIPHER
             )
@@ -973,6 +1001,41 @@
         return self._tag
 
 
+@utils.register_interface(interfaces.CipherContext)
+class _AESCTRCipherContext(object):
+    """
+    This is needed to provide support for AES CTR mode in OpenSSL 0.9.8. It can
+    be removed when we drop 0.9.8 support (RHEL5 extended life ends 2020).
+    """
+    def __init__(self, backend, cipher, mode):
+        self._backend = backend
+
+        self._key = self._backend._ffi.new("AES_KEY *")
+        assert self._key != self._backend._ffi.NULL
+        res = self._backend._lib.AES_set_encrypt_key(
+            cipher.key, len(cipher.key) * 8, self._key
+        )
+        assert res == 0
+        self._ecount = self._backend._ffi.new("char[]", 16)
+        self._nonce = self._backend._ffi.new("char[16]", mode.nonce)
+        self._num = self._backend._ffi.new("unsigned int *", 0)
+
+    def update(self, data):
+        buf = self._backend._ffi.new("unsigned char[]", len(data))
+        self._backend._lib.AES_ctr128_encrypt(
+            data, buf, len(data), self._key, self._nonce,
+            self._ecount, self._num
+        )
+        return self._backend._ffi.buffer(buf)[:]
+
+    def finalize(self):
+        self._key = None
+        self._ecount = None
+        self._nonce = None
+        self._num = None
+        return b""
+
+
 @utils.register_interface(interfaces.HashContext)
 class _HashContext(object):
     def __init__(self, backend, algorithm, ctx=None):
@@ -988,7 +1051,7 @@
                 algorithm.name.encode("ascii"))
             if evp_md == self._backend._ffi.NULL:
                 raise UnsupportedAlgorithm(
-                    "{0} is not a supported hash on this backend".format(
+                    "{0} is not a supported hash on this backend.".format(
                         algorithm.name),
                     _Reasons.UNSUPPORTED_HASH
                 )
@@ -1039,7 +1102,7 @@
                 algorithm.name.encode('ascii'))
             if evp_md == self._backend._ffi.NULL:
                 raise UnsupportedAlgorithm(
-                    "{0} is not a supported hash on this backend".format(
+                    "{0} is not a supported hash on this backend.".format(
                         algorithm.name),
                     _Reasons.UNSUPPORTED_HASH
                 )
@@ -1108,7 +1171,7 @@
 
         if not isinstance(padding, interfaces.AsymmetricPadding):
             raise TypeError(
-                "Expected provider of interfaces.AsymmetricPadding")
+                "Expected provider of interfaces.AsymmetricPadding.")
 
         if isinstance(padding, PKCS1v15):
             if self._backend._lib.Cryptography_HAS_PKEY_CTX:
@@ -1119,7 +1182,7 @@
         elif isinstance(padding, PSS):
             if not isinstance(padding._mgf, MGF1):
                 raise UnsupportedAlgorithm(
-                    "Only MGF1 is supported by this backend",
+                    "Only MGF1 is supported by this backend.",
                     _Reasons.UNSUPPORTED_MGF
                 )
 
@@ -1144,7 +1207,7 @@
                 self._finalize_method = self._finalize_pss
         else:
             raise UnsupportedAlgorithm(
-                "{0} is not supported by this backend".format(padding.name),
+                "{0} is not supported by this backend.".format(padding.name),
                 _Reasons.UNSUPPORTED_PADDING
             )
 
@@ -1154,13 +1217,13 @@
 
     def update(self, data):
         if self._hash_ctx is None:
-            raise AlreadyFinalized("Context has already been finalized")
+            raise AlreadyFinalized("Context has already been finalized.")
 
         self._hash_ctx.update(data)
 
     def finalize(self):
         if self._hash_ctx is None:
-            raise AlreadyFinalized("Context has already been finalized")
+            raise AlreadyFinalized("Context has already been finalized.")
 
         evp_pkey = self._backend._rsa_private_key_to_evp_pkey(
             self._private_key)
@@ -1308,7 +1371,7 @@
 
         if not isinstance(padding, interfaces.AsymmetricPadding):
             raise TypeError(
-                "Expected provider of interfaces.AsymmetricPadding")
+                "Expected provider of interfaces.AsymmetricPadding.")
 
         if isinstance(padding, PKCS1v15):
             if self._backend._lib.Cryptography_HAS_PKEY_CTX:
@@ -1319,7 +1382,7 @@
         elif isinstance(padding, PSS):
             if not isinstance(padding._mgf, MGF1):
                 raise UnsupportedAlgorithm(
-                    "Only MGF1 is supported by this backend",
+                    "Only MGF1 is supported by this backend.",
                     _Reasons.UNSUPPORTED_MGF
                 )
 
@@ -1346,7 +1409,7 @@
                 self._verify_method = self._verify_pss
         else:
             raise UnsupportedAlgorithm(
-                "{0} is not supported by this backend".format(padding.name),
+                "{0} is not supported by this backend.".format(padding.name),
                 _Reasons.UNSUPPORTED_PADDING
             )
 
@@ -1356,13 +1419,13 @@
 
     def update(self, data):
         if self._hash_ctx is None:
-            raise AlreadyFinalized("Context has already been finalized")
+            raise AlreadyFinalized("Context has already been finalized.")
 
         self._hash_ctx.update(data)
 
     def verify(self):
         if self._hash_ctx is None:
-            raise AlreadyFinalized("Context has already been finalized")
+            raise AlreadyFinalized("Context has already been finalized.")
 
         evp_pkey = self._backend._rsa_public_key_to_evp_pkey(
             self._public_key)
@@ -1496,13 +1559,13 @@
 
     def update(self, data):
         if self._hash_ctx is None:
-            raise AlreadyFinalized("Context has already been finalized")
+            raise AlreadyFinalized("Context has already been finalized.")
 
         self._hash_ctx.update(data)
 
     def verify(self):
         if self._hash_ctx is None:
-            raise AlreadyFinalized("Context has already been finalized")
+            raise AlreadyFinalized("Context has already been finalized.")
 
         self._dsa_cdata = self._backend._dsa_cdata_from_public_key(
             self._public_key)
@@ -1541,13 +1604,13 @@
 
     def update(self, data):
         if self._hash_ctx is None:
-            raise AlreadyFinalized("Context has already been finalized")
+            raise AlreadyFinalized("Context has already been finalized.")
 
         self._hash_ctx.update(data)
 
     def finalize(self):
         if self._hash_ctx is None:
-            raise AlreadyFinalized("Context has already been finalized")
+            raise AlreadyFinalized("Context has already been finalized.")
 
         data_to_sign = self._hash_ctx.finalize()
         self._hash_ctx = None
@@ -1570,7 +1633,7 @@
 class _CMACContext(object):
     def __init__(self, backend, algorithm, ctx=None):
         if not backend.cmac_algorithm_supported(algorithm):
-            raise UnsupportedAlgorithm("This backend does not support CMAC",
+            raise UnsupportedAlgorithm("This backend does not support CMAC.",
                                        _Reasons.UNSUPPORTED_CIPHER)
 
         self._backend = backend
diff --git a/cryptography/hazmat/bindings/openssl/aes.py b/cryptography/hazmat/bindings/openssl/aes.py
index 17c154c..b0e0072 100644
--- a/cryptography/hazmat/bindings/openssl/aes.py
+++ b/cryptography/hazmat/bindings/openssl/aes.py
@@ -29,6 +29,12 @@
 FUNCTIONS = """
 int AES_set_encrypt_key(const unsigned char *, const int, AES_KEY *);
 int AES_set_decrypt_key(const unsigned char *, const int, AES_KEY *);
+/* The ctr128_encrypt function is only useful in 0.9.8. You should use EVP for
+   this in 1.0.0+. */
+void AES_ctr128_encrypt(const unsigned char *, unsigned char *,
+                        const unsigned long, const AES_KEY *,
+                        unsigned char[], unsigned char[], unsigned int *);
+
 """
 
 MACROS = """
diff --git a/cryptography/hazmat/bindings/openssl/binding.py b/cryptography/hazmat/bindings/openssl/binding.py
index aa0525f..464081b 100644
--- a/cryptography/hazmat/bindings/openssl/binding.py
+++ b/cryptography/hazmat/bindings/openssl/binding.py
@@ -149,7 +149,7 @@
             lock.release()
         else:
             raise RuntimeError(
-                "Unknown lock mode {0}: lock={1}, file={2}, line={3}".format(
+                "Unknown lock mode {0}: lock={1}, file={2}, line={3}.".format(
                     mode, n, file, line
                 )
             )
diff --git a/cryptography/hazmat/bindings/openssl/ec.py b/cryptography/hazmat/bindings/openssl/ec.py
index 45c17c2..26fc8ff 100644
--- a/cryptography/hazmat/bindings/openssl/ec.py
+++ b/cryptography/hazmat/bindings/openssl/ec.py
@@ -27,6 +27,8 @@
 static const int Cryptography_HAS_EC_NISTP_64_GCC_128;
 static const int Cryptography_HAS_EC2M;
 
+static const int OPENSSL_EC_NAMED_CURVE;
+
 typedef ... EC_KEY;
 typedef ... EC_GROUP;
 typedef ... EC_POINT;
@@ -61,6 +63,8 @@
 int EC_GROUP_get_curve_GF2m(
     const EC_GROUP *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *);
 
+int EC_GROUP_get_degree(const EC_GROUP *);
+
 const EC_METHOD *EC_GROUP_method_of(const EC_GROUP *);
 const EC_POINT *EC_GROUP_get0_generator(const EC_GROUP *);
 int EC_GROUP_get_curve_name(const EC_GROUP *);
@@ -198,6 +202,7 @@
 CUSTOMIZATIONS = """
 #ifdef OPENSSL_NO_EC
 static const long Cryptography_HAS_EC = 0;
+
 typedef void EC_KEY;
 typedef void EC_GROUP;
 typedef void EC_POINT;
@@ -208,6 +213,8 @@
 } EC_builtin_curve;
 typedef long point_conversion_form_t;
 
+static const int OPENSSL_EC_NAMED_CURVE = 0;
+
 void (*EC_KEY_free)(EC_KEY *) = NULL;
 size_t (*EC_get_builtin_curves)(EC_builtin_curve *, size_t) = NULL;
 EC_KEY *(*EC_KEY_new_by_curve_name)(int) = NULL;
@@ -250,6 +257,8 @@
 int (*EC_GROUP_get_curve_GFp)(
     const EC_GROUP *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *);
 
+int (*EC_GROUP_get_degree)(const EC_GROUP *) = NULL;
+
 const EC_METHOD *(*EC_GROUP_method_of)(const EC_GROUP *) = NULL;
 const EC_POINT *(*EC_GROUP_get0_generator)(const EC_GROUP *) = NULL;
 int (*EC_GROUP_get_curve_name)(const EC_GROUP *) = NULL;
@@ -389,6 +398,7 @@
 
 CONDITIONAL_NAMES = {
     "Cryptography_HAS_EC": [
+        "OPENSSL_EC_NAMED_CURVE",
         "EC_GROUP_new",
         "EC_GROUP_free",
         "EC_GROUP_clear_free",
@@ -399,6 +409,7 @@
         "EC_GROUP_method_of",
         "EC_GROUP_get0_generator",
         "EC_GROUP_get_curve_name",
+        "EC_GROUP_get_degree",
         "EC_KEY_free",
         "EC_get_builtin_curves",
         "EC_KEY_new_by_curve_name",
diff --git a/cryptography/hazmat/bindings/openssl/opensslv.py b/cryptography/hazmat/bindings/openssl/opensslv.py
index e4aa621..ef6e057 100644
--- a/cryptography/hazmat/bindings/openssl/opensslv.py
+++ b/cryptography/hazmat/bindings/openssl/opensslv.py
@@ -18,6 +18,8 @@
 """
 
 TYPES = """
+/* Note that these will be resolved when cryptography is compiled and are NOT
+   guaranteed to be the version that it actually loads. */
 static const int OPENSSL_VERSION_NUMBER;
 static const char *const OPENSSL_VERSION_TEXT;
 """
diff --git a/cryptography/hazmat/bindings/openssl/ssl.py b/cryptography/hazmat/bindings/openssl/ssl.py
index 0b15411..94b96d9 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,8 @@
 static const long TLSEXT_NAMETYPE_host_name;
 
 typedef ... SSL_CIPHER;
+typedef ... Cryptography_STACK_OF_SSL_CIPHER;
+typedef ... COMP_METHOD;
 """
 
 FUNCTIONS = """
@@ -190,6 +197,11 @@
 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 *);
+
+const COMP_METHOD *SSL_get_current_compression(SSL *);
+const COMP_METHOD *SSL_get_current_expansion(SSL *);
+const char *SSL_COMP_get_name(const COMP_METHOD *);
 
 /*  context */
 void SSL_CTX_free(SSL_CTX *);
@@ -248,6 +260,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 +364,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 +578,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/cryptography/hazmat/primitives/asymmetric/dsa.py b/cryptography/hazmat/primitives/asymmetric/dsa.py
index aa3cdc9..a9ae9ec 100644
--- a/cryptography/hazmat/primitives/asymmetric/dsa.py
+++ b/cryptography/hazmat/primitives/asymmetric/dsa.py
@@ -27,7 +27,7 @@
         not isinstance(subgroup_order, six.integer_types) or
         not isinstance(generator, six.integer_types)
     ):
-        raise TypeError("DSA parameters must be integers")
+        raise TypeError("DSA parameters must be integers.")
 
     if (utils.bit_length(modulus),
         utils.bit_length(subgroup_order)) not in (
@@ -36,10 +36,10 @@
             (3072, 256)):
         raise ValueError("modulus and subgroup_order lengths must be "
                          "one of these pairs (1024, 160) or (2048, 256) "
-                         "or (3072, 256)")
+                         "or (3072, 256).")
 
     if generator <= 1 or generator >= modulus:
-        raise ValueError("generator must be > 1 and < modulus")
+        raise ValueError("generator must be > 1 and < modulus.")
 
 
 @utils.register_interface(interfaces.DSAParameters)
@@ -55,7 +55,7 @@
     def generate(cls, key_size, backend):
         if not isinstance(backend, DSABackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement DSABackend",
+                "Backend object does not implement DSABackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
@@ -94,13 +94,13 @@
             not isinstance(x, six.integer_types) or
             not isinstance(y, six.integer_types)
         ):
-            raise TypeError("DSAPrivateKey arguments must be integers")
+            raise TypeError("DSAPrivateKey arguments must be integers.")
 
         if x <= 0 or x >= subgroup_order:
-            raise ValueError("x must be > 0 and < subgroup_order")
+            raise ValueError("x must be > 0 and < subgroup_order.")
 
         if y != pow(generator, x, modulus):
-            raise ValueError("y must be equal to (generator ** x % modulus)")
+            raise ValueError("y must be equal to (generator ** x % modulus).")
 
         self._modulus = modulus
         self._subgroup_order = subgroup_order
@@ -112,7 +112,7 @@
     def generate(cls, parameters, backend):
         if not isinstance(backend, DSABackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement DSABackend",
+                "Backend object does not implement DSABackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
@@ -121,7 +121,7 @@
     def signer(self, algorithm, backend):
         if not isinstance(backend, DSABackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement DSABackend",
+                "Backend object does not implement DSABackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
@@ -153,7 +153,7 @@
     def __init__(self, modulus, subgroup_order, generator, y):
         _check_dsa_parameters(modulus, subgroup_order, generator)
         if not isinstance(y, six.integer_types):
-            raise TypeError("y must be an integer")
+            raise TypeError("y must be an integer.")
 
         self._modulus = modulus
         self._subgroup_order = subgroup_order
@@ -163,7 +163,7 @@
     def verifier(self, signature, algorithm, backend):
         if not isinstance(backend, DSABackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement DSABackend",
+                "Backend object does not implement DSABackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
diff --git a/cryptography/hazmat/primitives/asymmetric/padding.py b/cryptography/hazmat/primitives/asymmetric/padding.py
index f7710c4..d44bbda 100644
--- a/cryptography/hazmat/primitives/asymmetric/padding.py
+++ b/cryptography/hazmat/primitives/asymmetric/padding.py
@@ -44,13 +44,13 @@
         else:
             if (not isinstance(salt_length, six.integer_types) and
                     salt_length is not self.MAX_LENGTH):
-                raise TypeError("salt_length must be an integer")
+                raise TypeError("salt_length must be an integer.")
 
             if salt_length is not self.MAX_LENGTH and salt_length < 0:
-                raise ValueError("salt_length must be zero or greater")
+                raise ValueError("salt_length must be zero or greater.")
 
         if salt_length is None and self._mgf._salt_length is None:
-            raise ValueError("You must supply salt_length")
+            raise ValueError("You must supply salt_length.")
 
         self._salt_length = salt_length
 
@@ -86,9 +86,9 @@
             )
             if (not isinstance(salt_length, six.integer_types) and
                     salt_length is not self.MAX_LENGTH):
-                raise TypeError("salt_length must be an integer")
+                raise TypeError("salt_length must be an integer.")
 
             if salt_length is not self.MAX_LENGTH and salt_length < 0:
-                raise ValueError("salt_length must be zero or greater")
+                raise ValueError("salt_length must be zero or greater.")
 
         self._salt_length = salt_length
diff --git a/cryptography/hazmat/primitives/asymmetric/rsa.py b/cryptography/hazmat/primitives/asymmetric/rsa.py
index 5d3bb36..a9f5783 100644
--- a/cryptography/hazmat/primitives/asymmetric/rsa.py
+++ b/cryptography/hazmat/primitives/asymmetric/rsa.py
@@ -28,16 +28,16 @@
             not isinstance(public_exponent, six.integer_types) or
             not isinstance(modulus, six.integer_types)
         ):
-            raise TypeError("RSAPublicKey arguments must be integers")
+            raise TypeError("RSAPublicKey arguments must be integers.")
 
         if modulus < 3:
-            raise ValueError("modulus must be >= 3")
+            raise ValueError("modulus must be >= 3.")
 
         if public_exponent < 3 or public_exponent >= modulus:
-            raise ValueError("public_exponent must be >= 3 and < modulus")
+            raise ValueError("public_exponent must be >= 3 and < modulus.")
 
         if public_exponent & 1 == 0:
-            raise ValueError("public_exponent must be odd")
+            raise ValueError("public_exponent must be odd.")
 
         self._public_exponent = public_exponent
         self._modulus = modulus
@@ -45,7 +45,7 @@
     def verifier(self, signature, padding, algorithm, backend):
         if not isinstance(backend, RSABackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement RSABackend",
+                "Backend object does not implement RSABackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
@@ -55,7 +55,7 @@
     def encrypt(self, plaintext, padding, backend):
         if not isinstance(backend, RSABackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement RSABackend",
+                "Backend object does not implement RSABackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
@@ -132,43 +132,43 @@
             not isinstance(public_exponent, six.integer_types) or
             not isinstance(modulus, six.integer_types)
         ):
-            raise TypeError("RSAPrivateKey arguments must be integers")
+            raise TypeError("RSAPrivateKey arguments must be integers.")
 
         if modulus < 3:
-            raise ValueError("modulus must be >= 3")
+            raise ValueError("modulus must be >= 3.")
 
         if p >= modulus:
-            raise ValueError("p must be < modulus")
+            raise ValueError("p must be < modulus.")
 
         if q >= modulus:
-            raise ValueError("q must be < modulus")
+            raise ValueError("q must be < modulus.")
 
         if dmp1 >= modulus:
-            raise ValueError("dmp1 must be < modulus")
+            raise ValueError("dmp1 must be < modulus.")
 
         if dmq1 >= modulus:
-            raise ValueError("dmq1 must be < modulus")
+            raise ValueError("dmq1 must be < modulus.")
 
         if iqmp >= modulus:
-            raise ValueError("iqmp must be < modulus")
+            raise ValueError("iqmp must be < modulus.")
 
         if private_exponent >= modulus:
-            raise ValueError("private_exponent must be < modulus")
+            raise ValueError("private_exponent must be < modulus.")
 
         if public_exponent < 3 or public_exponent >= modulus:
-            raise ValueError("public_exponent must be >= 3 and < modulus")
+            raise ValueError("public_exponent must be >= 3 and < modulus.")
 
         if public_exponent & 1 == 0:
-            raise ValueError("public_exponent must be odd")
+            raise ValueError("public_exponent must be odd.")
 
         if dmp1 & 1 == 0:
-            raise ValueError("dmp1 must be odd")
+            raise ValueError("dmp1 must be odd.")
 
         if dmq1 & 1 == 0:
-            raise ValueError("dmq1 must be odd")
+            raise ValueError("dmq1 must be odd.")
 
         if p * q != modulus:
-            raise ValueError("p*q must equal modulus")
+            raise ValueError("p*q must equal modulus.")
 
         self._p = p
         self._q = q
@@ -183,7 +183,7 @@
     def generate(cls, public_exponent, key_size, backend):
         if not isinstance(backend, RSABackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement RSABackend",
+                "Backend object does not implement RSABackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
@@ -192,7 +192,7 @@
     def signer(self, padding, algorithm, backend):
         if not isinstance(backend, RSABackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement RSABackend",
+                "Backend object does not implement RSABackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
@@ -201,7 +201,7 @@
     def decrypt(self, ciphertext, padding, backend):
         if not isinstance(backend, RSABackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement RSABackend",
+                "Backend object does not implement RSABackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
diff --git a/cryptography/hazmat/primitives/ciphers/algorithms.py b/cryptography/hazmat/primitives/ciphers/algorithms.py
index 52daf17..bd8437c 100644
--- a/cryptography/hazmat/primitives/ciphers/algorithms.py
+++ b/cryptography/hazmat/primitives/ciphers/algorithms.py
@@ -20,7 +20,7 @@
 def _verify_key_size(algorithm, key):
     # Verify that the key size matches the expected key size
     if len(key) * 8 not in algorithm.key_sizes:
-        raise ValueError("Invalid key size ({0}) for {1}".format(
+        raise ValueError("Invalid key size ({0}) for {1}.".format(
             len(key) * 8, algorithm.name
         ))
     return key
diff --git a/cryptography/hazmat/primitives/ciphers/base.py b/cryptography/hazmat/primitives/ciphers/base.py
index 2274e94..e3fe5ad 100644
--- a/cryptography/hazmat/primitives/ciphers/base.py
+++ b/cryptography/hazmat/primitives/ciphers/base.py
@@ -26,12 +26,14 @@
     def __init__(self, algorithm, mode, backend):
         if not isinstance(backend, CipherBackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement CipherBackend",
+                "Backend object does not implement CipherBackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
         if not isinstance(algorithm, interfaces.CipherAlgorithm):
-            raise TypeError("Expected interface of interfaces.CipherAlgorithm")
+            raise TypeError(
+                "Expected interface of interfaces.CipherAlgorithm."
+            )
 
         if mode is not None:
             mode.validate_for_algorithm(algorithm)
@@ -44,7 +46,7 @@
         if isinstance(self.mode, interfaces.ModeWithAuthenticationTag):
             if self.mode.tag is not None:
                 raise ValueError(
-                    "Authentication tag must be None when encrypting"
+                    "Authentication tag must be None when encrypting."
                 )
         ctx = self._backend.create_symmetric_encryption_ctx(
             self.algorithm, self.mode
@@ -55,7 +57,7 @@
         if isinstance(self.mode, interfaces.ModeWithAuthenticationTag):
             if self.mode.tag is None:
                 raise ValueError(
-                    "Authentication tag must be provided when decrypting"
+                    "Authentication tag must be provided when decrypting."
                 )
         ctx = self._backend.create_symmetric_decryption_ctx(
             self.algorithm, self.mode
@@ -79,12 +81,12 @@
 
     def update(self, data):
         if self._ctx is None:
-            raise AlreadyFinalized("Context was already finalized")
+            raise AlreadyFinalized("Context was already finalized.")
         return self._ctx.update(data)
 
     def finalize(self):
         if self._ctx is None:
-            raise AlreadyFinalized("Context was already finalized")
+            raise AlreadyFinalized("Context was already finalized.")
         data = self._ctx.finalize()
         self._ctx = None
         return data
@@ -100,13 +102,13 @@
 
     def update(self, data):
         if self._ctx is None:
-            raise AlreadyFinalized("Context was already finalized")
+            raise AlreadyFinalized("Context was already finalized.")
         self._updated = True
         return self._ctx.update(data)
 
     def finalize(self):
         if self._ctx is None:
-            raise AlreadyFinalized("Context was already finalized")
+            raise AlreadyFinalized("Context was already finalized.")
         data = self._ctx.finalize()
         self._tag = self._ctx.tag
         self._ctx = None
@@ -114,9 +116,9 @@
 
     def authenticate_additional_data(self, data):
         if self._ctx is None:
-            raise AlreadyFinalized("Context was already finalized")
+            raise AlreadyFinalized("Context was already finalized.")
         if self._updated:
-            raise AlreadyUpdated("Update has been called on this context")
+            raise AlreadyUpdated("Update has been called on this context.")
         self._ctx.authenticate_additional_data(data)
 
 
@@ -126,5 +128,5 @@
     def tag(self):
         if self._ctx is not None:
             raise NotYetFinalized("You must finalize encryption before "
-                                  "getting the tag")
+                                  "getting the tag.")
         return self._tag
diff --git a/cryptography/hazmat/primitives/ciphers/modes.py b/cryptography/hazmat/primitives/ciphers/modes.py
index 739f23d..e70a9db 100644
--- a/cryptography/hazmat/primitives/ciphers/modes.py
+++ b/cryptography/hazmat/primitives/ciphers/modes.py
@@ -17,6 +17,13 @@
 from cryptography.hazmat.primitives import interfaces
 
 
+def _check_iv_length(mode, algorithm):
+    if len(mode.initialization_vector) * 8 != algorithm.block_size:
+        raise ValueError("Invalid IV size ({0}) for {1}.".format(
+            len(mode.initialization_vector), mode.name
+        ))
+
+
 @utils.register_interface(interfaces.Mode)
 @utils.register_interface(interfaces.ModeWithInitializationVector)
 class CBC(object):
@@ -25,11 +32,7 @@
     def __init__(self, initialization_vector):
         self.initialization_vector = initialization_vector
 
-    def validate_for_algorithm(self, algorithm):
-        if len(self.initialization_vector) * 8 != algorithm.block_size:
-            raise ValueError("Invalid iv size ({0}) for {1}".format(
-                len(self.initialization_vector), self.name
-            ))
+    validate_for_algorithm = _check_iv_length
 
 
 @utils.register_interface(interfaces.Mode)
@@ -48,11 +51,7 @@
     def __init__(self, initialization_vector):
         self.initialization_vector = initialization_vector
 
-    def validate_for_algorithm(self, algorithm):
-        if len(self.initialization_vector) * 8 != algorithm.block_size:
-            raise ValueError("Invalid iv size ({0}) for {1}".format(
-                len(self.initialization_vector), self.name
-            ))
+    validate_for_algorithm = _check_iv_length
 
 
 @utils.register_interface(interfaces.Mode)
@@ -63,11 +62,18 @@
     def __init__(self, initialization_vector):
         self.initialization_vector = initialization_vector
 
-    def validate_for_algorithm(self, algorithm):
-        if len(self.initialization_vector) * 8 != algorithm.block_size:
-            raise ValueError("Invalid iv size ({0}) for {1}".format(
-                len(self.initialization_vector), self.name
-            ))
+    validate_for_algorithm = _check_iv_length
+
+
+@utils.register_interface(interfaces.Mode)
+@utils.register_interface(interfaces.ModeWithInitializationVector)
+class CFB8(object):
+    name = "CFB8"
+
+    def __init__(self, initialization_vector):
+        self.initialization_vector = initialization_vector
+
+    validate_for_algorithm = _check_iv_length
 
 
 @utils.register_interface(interfaces.Mode)
@@ -80,7 +86,7 @@
 
     def validate_for_algorithm(self, algorithm):
         if len(self.nonce) * 8 != algorithm.block_size:
-            raise ValueError("Invalid nonce size ({0}) for {1}".format(
+            raise ValueError("Invalid nonce size ({0}) for {1}.".format(
                 len(self.nonce), self.name
             ))
 
@@ -97,7 +103,7 @@
         # for it
         if tag is not None and len(tag) < 4:
             raise ValueError(
-                "Authentication tag must be 4 bytes or longer"
+                "Authentication tag must be 4 bytes or longer."
             )
 
         self.initialization_vector = initialization_vector
diff --git a/cryptography/hazmat/primitives/cmac.py b/cryptography/hazmat/primitives/cmac.py
index 7e7f65a..fa463ae 100644
--- a/cryptography/hazmat/primitives/cmac.py
+++ b/cryptography/hazmat/primitives/cmac.py
@@ -13,8 +13,6 @@
 
 from __future__ import absolute_import, division, print_function
 
-import six
-
 from cryptography import utils
 from cryptography.exceptions import (
     AlreadyFinalized, InvalidSignature, UnsupportedAlgorithm, _Reasons
@@ -28,13 +26,13 @@
     def __init__(self, algorithm, backend, ctx=None):
         if not isinstance(backend, CMACBackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement CMACBackend",
+                "Backend object does not implement CMACBackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
         if not isinstance(algorithm, interfaces.BlockCipherAlgorithm):
             raise TypeError(
-                "Expected instance of interfaces.BlockCipherAlgorithm"
+                "Expected instance of interfaces.BlockCipherAlgorithm."
             )
         self._algorithm = algorithm
 
@@ -46,28 +44,28 @@
 
     def update(self, data):
         if self._ctx is None:
-            raise AlreadyFinalized("Context was already finalized")
-        if isinstance(data, six.text_type):
-            raise TypeError("Unicode-objects must be encoded before hashing")
+            raise AlreadyFinalized("Context was already finalized.")
+        if not isinstance(data, bytes):
+            raise TypeError("data must be bytes.")
         self._ctx.update(data)
 
     def finalize(self):
         if self._ctx is None:
-            raise AlreadyFinalized("Context was already finalized")
+            raise AlreadyFinalized("Context was already finalized.")
         digest = self._ctx.finalize()
         self._ctx = None
         return digest
 
     def verify(self, signature):
-        if isinstance(signature, six.text_type):
-            raise TypeError("Unicode-objects must be encoded before verifying")
+        if not isinstance(signature, bytes):
+            raise TypeError("signature must be bytes.")
         digest = self.finalize()
         if not constant_time.bytes_eq(digest, signature):
             raise InvalidSignature("Signature did not match digest.")
 
     def copy(self):
         if self._ctx is None:
-            raise AlreadyFinalized("Context was already finalized")
+            raise AlreadyFinalized("Context was already finalized.")
         return CMAC(
             self._algorithm,
             backend=self._backend,
diff --git a/cryptography/hazmat/primitives/constant_time.py b/cryptography/hazmat/primitives/constant_time.py
index e0e9aa3..4547da1 100644
--- a/cryptography/hazmat/primitives/constant_time.py
+++ b/cryptography/hazmat/primitives/constant_time.py
@@ -17,8 +17,6 @@
 
 import cffi
 
-import six
-
 from cryptography.hazmat.bindings.utils import _create_modulename
 
 TYPES = """
@@ -57,7 +55,7 @@
 
 
 def bytes_eq(a, b):
-    if isinstance(a, six.text_type) or isinstance(b, six.text_type):
-        raise TypeError("Unicode-objects must be encoded before comparing")
+    if not isinstance(a, bytes) or not isinstance(b, bytes):
+            raise TypeError("a and b must be bytes.")
 
     return _lib.Cryptography_constant_time_bytes_eq(a, len(a), b, len(b)) == 1
diff --git a/cryptography/hazmat/primitives/hashes.py b/cryptography/hazmat/primitives/hashes.py
index 35b677b..04f7620 100644
--- a/cryptography/hazmat/primitives/hashes.py
+++ b/cryptography/hazmat/primitives/hashes.py
@@ -13,8 +13,6 @@
 
 from __future__ import absolute_import, division, print_function
 
-import six
-
 from cryptography import utils
 from cryptography.exceptions import (
     AlreadyFinalized, UnsupportedAlgorithm, _Reasons
@@ -28,7 +26,7 @@
     def __init__(self, algorithm, backend, ctx=None):
         if not isinstance(backend, HashBackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement HashBackend",
+                "Backend object does not implement HashBackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
@@ -45,21 +43,21 @@
 
     def update(self, data):
         if self._ctx is None:
-            raise AlreadyFinalized("Context was already finalized")
-        if isinstance(data, six.text_type):
-            raise TypeError("Unicode-objects must be encoded before hashing")
+            raise AlreadyFinalized("Context was already finalized.")
+        if not isinstance(data, bytes):
+            raise TypeError("data must be bytes.")
         self._ctx.update(data)
 
     def copy(self):
         if self._ctx is None:
-            raise AlreadyFinalized("Context was already finalized")
+            raise AlreadyFinalized("Context was already finalized.")
         return Hash(
             self.algorithm, backend=self._backend, ctx=self._ctx.copy()
         )
 
     def finalize(self):
         if self._ctx is None:
-            raise AlreadyFinalized("Context was already finalized")
+            raise AlreadyFinalized("Context was already finalized.")
         digest = self._ctx.finalize()
         self._ctx = None
         return digest
diff --git a/cryptography/hazmat/primitives/hmac.py b/cryptography/hazmat/primitives/hmac.py
index afbb2f7..026ad3b 100644
--- a/cryptography/hazmat/primitives/hmac.py
+++ b/cryptography/hazmat/primitives/hmac.py
@@ -13,8 +13,6 @@
 
 from __future__ import absolute_import, division, print_function
 
-import six
-
 from cryptography import utils
 from cryptography.exceptions import (
     AlreadyFinalized, InvalidSignature, UnsupportedAlgorithm, _Reasons
@@ -28,7 +26,7 @@
     def __init__(self, key, algorithm, backend, ctx=None):
         if not isinstance(backend, HMACBackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement HMACBackend",
+                "Backend object does not implement HMACBackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
@@ -45,14 +43,14 @@
 
     def update(self, msg):
         if self._ctx is None:
-            raise AlreadyFinalized("Context was already finalized")
-        if isinstance(msg, six.text_type):
-            raise TypeError("Unicode-objects must be encoded before hashing")
+            raise AlreadyFinalized("Context was already finalized.")
+        if not isinstance(msg, bytes):
+            raise TypeError("msg must be bytes.")
         self._ctx.update(msg)
 
     def copy(self):
         if self._ctx is None:
-            raise AlreadyFinalized("Context was already finalized")
+            raise AlreadyFinalized("Context was already finalized.")
         return HMAC(
             self._key,
             self.algorithm,
@@ -62,14 +60,14 @@
 
     def finalize(self):
         if self._ctx is None:
-            raise AlreadyFinalized("Context was already finalized")
+            raise AlreadyFinalized("Context was already finalized.")
         digest = self._ctx.finalize()
         self._ctx = None
         return digest
 
     def verify(self, signature):
-        if isinstance(signature, six.text_type):
-            raise TypeError("Unicode-objects must be encoded before verifying")
+        if not isinstance(signature, bytes):
+            raise TypeError("signature must be bytes.")
         digest = self.finalize()
         if not constant_time.bytes_eq(digest, signature):
             raise InvalidSignature("Signature did not match digest.")
diff --git a/cryptography/hazmat/primitives/kdf/hkdf.py b/cryptography/hazmat/primitives/kdf/hkdf.py
index 03500aa..04d02b2 100644
--- a/cryptography/hazmat/primitives/kdf/hkdf.py
+++ b/cryptography/hazmat/primitives/kdf/hkdf.py
@@ -28,12 +28,53 @@
     def __init__(self, algorithm, length, salt, info, backend):
         if not isinstance(backend, HMACBackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement HMACBackend",
+                "Backend object does not implement HMACBackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
         self._algorithm = algorithm
 
+        if not isinstance(salt, bytes) and salt is not None:
+            raise TypeError("salt must be bytes.")
+
+        if salt is None:
+            salt = b"\x00" * (self._algorithm.digest_size // 8)
+
+        self._salt = salt
+
+        self._backend = backend
+
+        self._hkdf_expand = HKDFExpand(self._algorithm, length, info, backend)
+
+    def _extract(self, key_material):
+        h = hmac.HMAC(self._salt, self._algorithm, backend=self._backend)
+        h.update(key_material)
+        return h.finalize()
+
+    def derive(self, key_material):
+        if not isinstance(key_material, bytes):
+            raise TypeError("key_material must be bytes.")
+
+        return self._hkdf_expand.derive(self._extract(key_material))
+
+    def verify(self, key_material, expected_key):
+        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
+            raise InvalidKey
+
+
+@utils.register_interface(interfaces.KeyDerivationFunction)
+class HKDFExpand(object):
+    def __init__(self, algorithm, length, info, backend):
+        if not isinstance(backend, HMACBackend):
+            raise UnsupportedAlgorithm(
+                "Backend object does not implement HMACBackend.",
+                _Reasons.BACKEND_MISSING_INTERFACE
+            )
+
+        self._algorithm = algorithm
+
+        self._backend = backend
+
         max_length = 255 * (algorithm.digest_size // 8)
 
         if length > max_length:
@@ -44,32 +85,16 @@
 
         self._length = length
 
-        if isinstance(salt, six.text_type):
-            raise TypeError(
-                "Unicode-objects must be encoded before using them as a salt.")
-
-        if salt is None:
-            salt = b"\x00" * (self._algorithm.digest_size // 8)
-
-        self._salt = salt
-
-        if isinstance(info, six.text_type):
-            raise TypeError(
-                "Unicode-objects must be encoded before using them as info.")
+        if not isinstance(info, bytes) and info is not None:
+            raise TypeError("info must be bytes.")
 
         if info is None:
             info = b""
 
         self._info = info
-        self._backend = backend
 
         self._used = False
 
-    def _extract(self, key_material):
-        h = hmac.HMAC(self._salt, self._algorithm, backend=self._backend)
-        h.update(key_material)
-        return h.finalize()
-
     def _expand(self, key_material):
         output = [b""]
         counter = 1
@@ -85,17 +110,14 @@
         return b"".join(output)[:self._length]
 
     def derive(self, key_material):
-        if isinstance(key_material, six.text_type):
-            raise TypeError(
-                "Unicode-objects must be encoded before using them as key "
-                "material."
-            )
+        if not isinstance(key_material, bytes):
+            raise TypeError("key_material must be bytes.")
 
         if self._used:
             raise AlreadyFinalized
 
         self._used = True
-        return self._expand(self._extract(key_material))
+        return self._expand(key_material)
 
     def verify(self, key_material, expected_key):
         if not constant_time.bytes_eq(self.derive(key_material), expected_key):
diff --git a/cryptography/hazmat/primitives/kdf/pbkdf2.py b/cryptography/hazmat/primitives/kdf/pbkdf2.py
index bec35bb..97b6408 100644
--- a/cryptography/hazmat/primitives/kdf/pbkdf2.py
+++ b/cryptography/hazmat/primitives/kdf/pbkdf2.py
@@ -13,8 +13,6 @@
 
 from __future__ import absolute_import, division, print_function
 
-import six
-
 from cryptography import utils
 from cryptography.exceptions import (
     AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
@@ -28,38 +26,32 @@
     def __init__(self, algorithm, length, salt, iterations, backend):
         if not isinstance(backend, PBKDF2HMACBackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement PBKDF2HMACBackend",
+                "Backend object does not implement PBKDF2HMACBackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
         if not backend.pbkdf2_hmac_supported(algorithm):
             raise UnsupportedAlgorithm(
-                "{0} is not supported for PBKDF2 by this backend".format(
+                "{0} is not supported for PBKDF2 by this backend.".format(
                     algorithm.name),
                 _Reasons.UNSUPPORTED_HASH
             )
         self._used = False
         self._algorithm = algorithm
         self._length = length
-        if isinstance(salt, six.text_type):
-            raise TypeError(
-                "Unicode-objects must be encoded before using them as key "
-                "material."
-            )
+        if not isinstance(salt, bytes):
+            raise TypeError("salt must be bytes.")
         self._salt = salt
         self._iterations = iterations
         self._backend = backend
 
     def derive(self, key_material):
         if self._used:
-            raise AlreadyFinalized("PBKDF2 instances can only be used once")
+            raise AlreadyFinalized("PBKDF2 instances can only be used once.")
         self._used = True
 
-        if isinstance(key_material, six.text_type):
-            raise TypeError(
-                "Unicode-objects must be encoded before using them as key "
-                "material."
-            )
+        if not isinstance(key_material, bytes):
+            raise TypeError("key_material must be bytes.")
         return self._backend.derive_pbkdf2_hmac(
             self._algorithm,
             self._length,
diff --git a/cryptography/hazmat/primitives/padding.py b/cryptography/hazmat/primitives/padding.py
index c1a763b..74f1ef2 100644
--- a/cryptography/hazmat/primitives/padding.py
+++ b/cryptography/hazmat/primitives/padding.py
@@ -79,10 +79,10 @@
 class PKCS7(object):
     def __init__(self, block_size):
         if not (0 <= block_size < 256):
-            raise ValueError("block_size must be in range(0, 256)")
+            raise ValueError("block_size must be in range(0, 256).")
 
         if block_size % 8 != 0:
-            raise ValueError("block_size must be a multiple of 8")
+            raise ValueError("block_size must be a multiple of 8.")
 
         self.block_size = block_size
 
@@ -102,10 +102,10 @@
 
     def update(self, data):
         if self._buffer is None:
-            raise AlreadyFinalized("Context was already finalized")
+            raise AlreadyFinalized("Context was already finalized.")
 
-        if isinstance(data, six.text_type):
-            raise TypeError("Unicode-objects must be encoded before padding")
+        if not isinstance(data, bytes):
+            raise TypeError("data must be bytes.")
 
         self._buffer += data
 
@@ -118,7 +118,7 @@
 
     def finalize(self):
         if self._buffer is None:
-            raise AlreadyFinalized("Context was already finalized")
+            raise AlreadyFinalized("Context was already finalized.")
 
         pad_size = self.block_size // 8 - len(self._buffer)
         result = self._buffer + six.int2byte(pad_size) * pad_size
@@ -135,10 +135,10 @@
 
     def update(self, data):
         if self._buffer is None:
-            raise AlreadyFinalized("Context was already finalized")
+            raise AlreadyFinalized("Context was already finalized.")
 
-        if isinstance(data, six.text_type):
-            raise TypeError("Unicode-objects must be encoded before unpadding")
+        if not isinstance(data, bytes):
+            raise TypeError("data must be bytes.")
 
         self._buffer += data
 
@@ -154,17 +154,17 @@
 
     def finalize(self):
         if self._buffer is None:
-            raise AlreadyFinalized("Context was already finalized")
+            raise AlreadyFinalized("Context was already finalized.")
 
         if len(self._buffer) != self.block_size // 8:
-            raise ValueError("Invalid padding bytes")
+            raise ValueError("Invalid padding bytes.")
 
         valid = _lib.Cryptography_check_pkcs7_padding(
             self._buffer, self.block_size // 8
         )
 
         if not valid:
-            raise ValueError("Invalid padding bytes")
+            raise ValueError("Invalid padding bytes.")
 
         pad_size = six.indexbytes(self._buffer, -1)
         res = self._buffer[:-pad_size]
diff --git a/cryptography/hazmat/primitives/twofactor/hotp.py b/cryptography/hazmat/primitives/twofactor/hotp.py
index 41c467c..d0b476a 100644
--- a/cryptography/hazmat/primitives/twofactor/hotp.py
+++ b/cryptography/hazmat/primitives/twofactor/hotp.py
@@ -29,7 +29,7 @@
     def __init__(self, key, length, algorithm, backend):
         if not isinstance(backend, HMACBackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement HMACBackend",
+                "Backend object does not implement HMACBackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
@@ -37,13 +37,13 @@
             raise ValueError("Key length has to be at least 128 bits.")
 
         if not isinstance(length, six.integer_types):
-            raise TypeError("Length parameter must be an integer type")
+            raise TypeError("Length parameter must be an integer type.")
 
         if length < 6 or length > 8:
             raise ValueError("Length of HOTP has to be between 6 to 8.")
 
         if not isinstance(algorithm, (SHA1, SHA256, SHA512)):
-            raise TypeError("Algorithm must be SHA1, SHA256 or SHA512")
+            raise TypeError("Algorithm must be SHA1, SHA256 or SHA512.")
 
         self._key = key
         self._length = length
@@ -57,15 +57,13 @@
 
     def verify(self, hotp, counter):
         if not constant_time.bytes_eq(self.generate(counter), hotp):
-            raise InvalidToken("Supplied HOTP value does not match")
+            raise InvalidToken("Supplied HOTP value does not match.")
 
     def _dynamic_truncate(self, counter):
         ctx = hmac.HMAC(self._key, self._algorithm, self._backend)
         ctx.update(struct.pack(">Q", counter))
         hmac_value = ctx.finalize()
 
-        offset_bits = six.indexbytes(hmac_value, len(hmac_value) - 1) & 0b1111
-
-        offset = int(offset_bits)
+        offset = six.indexbytes(hmac_value, len(hmac_value) - 1) & 0b1111
         p = hmac_value[offset:offset + 4]
         return struct.unpack(">I", p)[0] & 0x7fffffff
diff --git a/cryptography/hazmat/primitives/twofactor/totp.py b/cryptography/hazmat/primitives/twofactor/totp.py
index e55ba00..854c516 100644
--- a/cryptography/hazmat/primitives/twofactor/totp.py
+++ b/cryptography/hazmat/primitives/twofactor/totp.py
@@ -25,7 +25,7 @@
     def __init__(self, key, length, algorithm, time_step, backend):
         if not isinstance(backend, HMACBackend):
             raise UnsupportedAlgorithm(
-                "Backend object does not implement HMACBackend",
+                "Backend object does not implement HMACBackend.",
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
@@ -38,4 +38,4 @@
 
     def verify(self, totp, time):
         if not constant_time.bytes_eq(self.generate(time), totp):
-            raise InvalidToken("Supplied TOTP value does not match")
+            raise InvalidToken("Supplied TOTP value does not match.")
diff --git a/docs/development/custom-vectors/cast5.rst b/docs/development/custom-vectors/cast5.rst
index f045ec1..97de901 100644
--- a/docs/development/custom-vectors/cast5.rst
+++ b/docs/development/custom-vectors/cast5.rst
@@ -15,7 +15,8 @@
 
 .. literalinclude:: /development/custom-vectors/cast5/generate_cast5.py
 
-Download link: :download:`generate_cast5.py </development/custom-vectors/cast5/generate_cast5.py>`
+Download link: :download:`generate_cast5.py
+</development/custom-vectors/cast5/generate_cast5.py>`
 
 
 Verification
@@ -26,4 +27,5 @@
 .. literalinclude:: /development/custom-vectors/cast5/verify_cast5.go
     :language: go
 
-Download link: :download:`verify_cast5.go </development/custom-vectors/cast5/verify_cast5.go>`
+Download link: :download:`verify_cast5.go
+</development/custom-vectors/cast5/verify_cast5.go>`
diff --git a/docs/development/custom-vectors/idea.rst b/docs/development/custom-vectors/idea.rst
index c226863..336cdf0 100644
--- a/docs/development/custom-vectors/idea.rst
+++ b/docs/development/custom-vectors/idea.rst
@@ -14,7 +14,8 @@
 
 .. literalinclude:: /development/custom-vectors/idea/generate_idea.py
 
-Download link: :download:`generate_idea.py </development/custom-vectors/idea/generate_idea.py>`
+Download link: :download:`generate_idea.py
+</development/custom-vectors/idea/generate_idea.py>`
 
 
 Verification
@@ -25,6 +26,7 @@
 
 .. literalinclude:: /development/custom-vectors/idea/verify_idea.py
 
-Download link: :download:`verify_idea.py </development/custom-vectors/idea/verify_idea.py>`
+Download link: :download:`verify_idea.py
+</development/custom-vectors/idea/verify_idea.py>`
 
 .. _`Botan`: http://botan.randombit.net
diff --git a/docs/development/custom-vectors/seed.rst b/docs/development/custom-vectors/seed.rst
index 5ea4295..290fb77 100644
--- a/docs/development/custom-vectors/seed.rst
+++ b/docs/development/custom-vectors/seed.rst
@@ -14,7 +14,8 @@
 
 .. literalinclude:: /development/custom-vectors/seed/generate_seed.py
 
-Download link: :download:`generate_seed.py </development/custom-vectors/seed/generate_seed.py>`
+Download link: :download:`generate_seed.py
+</development/custom-vectors/seed/generate_seed.py>`
 
 
 Verification
@@ -25,6 +26,7 @@
 
 .. literalinclude:: /development/custom-vectors/seed/verify_seed.py
 
-Download link: :download:`verify_seed.py </development/custom-vectors/seed/verify_seed.py>`
+Download link: :download:`verify_seed.py
+</development/custom-vectors/seed/verify_seed.py>`
 
 .. _`Botan`: http://botan.randombit.net
diff --git a/docs/doing-a-release.rst b/docs/doing-a-release.rst
index ad3b479..dd62c79 100644
--- a/docs/doing-a-release.rst
+++ b/docs/doing-a-release.rst
@@ -52,3 +52,6 @@
 * Check for any outstanding code undergoing a deprecation cycle by looking in
   ``cryptography.utils`` for ``DeprecatedIn**`` definitions. If any exist open
   a ticket to increment them for the next release.
+* Send an email to the `mailing list`_ announcing the release.
+
+.. _`mailing list`: https://mail.python.org/mailman/listinfo/cryptography-dev
diff --git a/docs/faq.rst b/docs/faq.rst
index 0b7bdce..4e8efc1 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -14,5 +14,34 @@
 
 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 the issue. To select the right architecture
+(32-bit or 64-bit) open a command prompt and start your Python interpreter.
+
+If it is 32-bit it will say ``32 bit`` as well as ``Intel`` in the output:
+
+.. code-block:: console
+
+    Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
+
+If it is 64-bit you will see ``64 bit`` as well as ``AMD64``:
+
+.. code-block:: console
+
+    Python 2.7.6 (default, Nov 10 2013, 19:24:24) [MSC v.1500 64 bit (AMD64)] on win32
+
+Note that for both 32-bit and 64-bit it will say ``win32``, but other data
+in the string may vary based on your version of Python.
+
 .. _`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/fernet.rst b/docs/fernet.rst
index f55a2d6..1c4918a 100644
--- a/docs/fernet.rst
+++ b/docs/fernet.rst
@@ -34,12 +34,13 @@
         they'll also be able forge arbitrary messages that will be
         authenticated and decrypted.
 
-    .. method:: encrypt(plaintext)
+    .. method:: encrypt(data)
 
-        :param bytes plaintext: The message you would like to encrypt.
+        :param bytes data: The message you would like to encrypt.
         :returns bytes: A secure message that cannot be read or altered
                         without the key. It is URL-safe base64-encoded. This is
                         referred to as a "Fernet token".
+        :raises TypeError: This exception is raised if ``data`` is not ``bytes``.
 
         .. note::
 
@@ -66,6 +67,7 @@
                                                   ``ttl``, it is malformed, or
                                                   it does not have a valid
                                                   signature.
+        :raises TypeError: This exception is raised if ``token`` is not ``bytes``.
 
 
 .. class:: InvalidToken
diff --git a/docs/hazmat/backends/commoncrypto.rst b/docs/hazmat/backends/commoncrypto.rst
index 77d6612..ddaf97e 100644
--- a/docs/hazmat/backends/commoncrypto.rst
+++ b/docs/hazmat/backends/commoncrypto.rst
@@ -3,8 +3,8 @@
 CommonCrypto backend
 ====================
 
-The `CommonCrypto`_ C library provided by Apple on OS X and iOS. The CommonCrypto
-backend is only supported on OS X versions 10.8 and above.
+The `CommonCrypto`_ C library provided by Apple on OS X and iOS. The
+CommonCrypto backend is only supported on OS X versions 10.8 and above.
 
 .. currentmodule:: cryptography.hazmat.backends.commoncrypto.backend
 
diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst
index f363b54..ff389cb 100644
--- a/docs/hazmat/backends/interfaces.rst
+++ b/docs/hazmat/backends/interfaces.rst
@@ -6,8 +6,8 @@
 .. currentmodule:: cryptography.hazmat.backends.interfaces
 
 
-Backend implementations may provide a number of interfaces to support operations
-such as :doc:`/hazmat/primitives/symmetric-encryption`,
+Backend implementations may provide a number of interfaces to support
+operations such as :doc:`/hazmat/primitives/symmetric-encryption`,
 :doc:`/hazmat/primitives/cryptographic-hashes`, and
 :doc:`/hazmat/primitives/mac/hmac`.
 
@@ -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/backends/openssl.rst b/docs/hazmat/backends/openssl.rst
index f21116b..6ad0d04 100644
--- a/docs/hazmat/backends/openssl.rst
+++ b/docs/hazmat/backends/openssl.rst
@@ -46,9 +46,9 @@
 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
+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.
 
@@ -70,8 +70,8 @@
 the operation system you are using. See the `Microsoft documentation`_ for more
 details.
 
-Linux uses its own PRNG design. ``/dev/urandom`` is a non-blocking source seeded
-from the same pool as ``/dev/random``.
+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/
diff --git a/docs/hazmat/primitives/asymmetric/dsa.rst b/docs/hazmat/primitives/asymmetric/dsa.rst
index cc46029..6848d84 100644
--- a/docs/hazmat/primitives/asymmetric/dsa.rst
+++ b/docs/hazmat/primitives/asymmetric/dsa.rst
@@ -120,7 +120,7 @@
             ...     hashes.SHA256(),
             ...     default_backend()
             ... )
-            >>> data= b"this is some data I'd like to sign"
+            >>> data = b"this is some data I'd like to sign"
             >>> signer.update(data)
             >>> signature = signer.finalize()
 
diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst
index 68ad089..234a5c6 100644
--- a/docs/hazmat/primitives/asymmetric/rsa.rst
+++ b/docs/hazmat/primitives/asymmetric/rsa.rst
@@ -85,7 +85,10 @@
 
         :param padding: An instance of a
             :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding`
-            provider.
+            provider. Valid values are
+            :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS` and
+            :class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15`
+            (``PSS`` is recommended for all new applications).
 
         :param algorithm: An instance of a
             :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
@@ -154,21 +157,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)
@@ -216,7 +237,7 @@
             ...     hashes.SHA256(),
             ...     default_backend()
             ... )
-            >>> data= b"this is some data I'd like to sign"
+            >>> data = b"this is some data I'd like to sign"
             >>> signer.update(data)
             >>> signature = signer.finalize()
             >>> public_key = private_key.public_key()
@@ -236,7 +257,10 @@
 
         :param padding: An instance of a
             :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding`
-            provider.
+            provider. Valid values are
+            :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS` and
+            :class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15`
+            (``PSS`` is recommended for all new applications).
 
         :param algorithm: An instance of a
             :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
@@ -306,27 +330,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/constant-time.rst b/docs/hazmat/primitives/constant-time.rst
index c6fcb3a..1394b6b 100644
--- a/docs/hazmat/primitives/constant-time.rst
+++ b/docs/hazmat/primitives/constant-time.rst
@@ -36,6 +36,8 @@
     :param bytes b: The right-hand side.
     :returns bool: ``True`` if ``a`` has the same bytes as ``b``, otherwise
                    ``False``.
+    :raises TypeError: This exception is raised if ``a`` or ``b`` is not
+                       ``bytes``.
 
 
 .. _`Coda Hale's blog post`: http://codahale.com/a-lesson-in-timing-attacks/
diff --git a/docs/hazmat/primitives/cryptographic-hashes.rst b/docs/hazmat/primitives/cryptographic-hashes.rst
index 773d97f..7e5295c 100644
--- a/docs/hazmat/primitives/cryptographic-hashes.rst
+++ b/docs/hazmat/primitives/cryptographic-hashes.rst
@@ -54,6 +54,7 @@
 
         :param bytes data: The bytes to be hashed.
         :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`.
+        :raises TypeError: This exception is raised if ``data`` is not ``bytes``.
 
     .. method:: copy()
 
diff --git a/docs/hazmat/primitives/key-derivation-functions.rst b/docs/hazmat/primitives/key-derivation-functions.rst
index 269f949..f68b12c 100644
--- a/docs/hazmat/primitives/key-derivation-functions.rst
+++ b/docs/hazmat/primitives/key-derivation-functions.rst
@@ -88,6 +88,8 @@
         provided ``backend`` does not implement
         :class:`~cryptography.hazmat.backends.interfaces.PBKDF2HMACBackend`
 
+    :raises TypeError: This exception is raised if ``salt`` is not ``bytes``.
+
     .. method:: derive(key_material)
 
         :param bytes key_material: The input key material. For PBKDF2 this
@@ -99,6 +101,9 @@
                                                           called more than
                                                           once.
 
+        :raises TypeError: This exception is raised if ``key_material`` is not
+                           ``bytes``.
+
         This generates and returns a new key from the supplied password.
 
     .. method:: verify(key_material, expected_key)
@@ -191,10 +196,15 @@
         provided ``backend`` does not implement
         :class:`~cryptography.hazmat.backends.interfaces.HMACBackend`
 
+    :raises TypeError: This exception is raised if ``salt`` or ``info`` is not
+                       ``bytes``.
+
     .. method:: derive(key_material)
 
         :param bytes key_material: The input key material.
-        :retunr bytes: The derived key.
+        :return bytes: The derived key.
+        :raises TypeError: This exception is raised if ``key_material`` is not
+                           ``bytes``.
 
         Derives a new key from the input key material by performing both the
         extract and expand operations.
@@ -219,6 +229,101 @@
         ``key_material`` generates the same key as the ``expected_key``, and
         raises an exception if they do not match.
 
+
+.. class:: HKDFExpand(algorithm, length, info, backend)
+
+    .. versionadded:: 0.5
+
+    HKDF consists of two stages, extract and expand. This class exposes an
+    expand only version of HKDF that is suitable when the key material is
+    already cryptographically strong.
+
+    .. warning::
+
+        HKDFExpand should only be used if the key material is
+        cryptographically strong. You should use
+        :class:`~cryptography.hazmat.primitives.kdf.hkdf.HKDF` if
+        you are unsure.
+
+    .. doctest::
+
+        >>> import os
+        >>> from cryptography.hazmat.primitives import hashes
+        >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDFExpand
+        >>> from cryptography.hazmat.backends import default_backend
+        >>> backend = default_backend()
+        >>> info = b"hkdf-example"
+        >>> key_material = os.urandom(16)
+        >>> hkdf = HKDFExpand(
+        ...     algorithm=hashes.SHA256(),
+        ...     length=32,
+        ...     info=info,
+        ...     backend=backend
+        ... )
+        >>> key = hkdf.derive(key_material)
+        >>> hkdf = HKDFExpand(
+        ...     algorithm=hashes.SHA256(),
+        ...     length=32,
+        ...     info=info,
+        ...     backend=backend
+        ... )
+        >>> hkdf.verify(key_material, key)
+
+    :param algorithm: An instance of a
+        :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+        provider.
+
+    :param int length: The desired length of the derived key. Maximum is
+        ``255 * (algorithm.digest_size // 8)``.
+
+    :param bytes info: Application specific context information.  If ``None``
+        is explicitly passed an empty byte string will be used.
+
+    :param backend: A
+        :class:`~cryptography.hazmat.backends.interfaces.HMACBackend`
+        provider.
+
+    :raises cryptography.exceptions.UnsupportedAlgorithm: This is raised if the
+        provided ``backend`` does not implement
+        :class:`~cryptography.hazmat.backends.interfaces.HMACBackend`
+    :raises TypeError: This is raised if the provided ``info`` is a unicode object
+    :raises TypeError: This exception is raised if ``info`` is not ``bytes``.
+
+    .. method:: derive(key_material)
+
+        :param bytes key_material: The input key material.
+        :return bytes: The derived key.
+
+        :raises TypeError: This is raised if the provided ``key_material`` is
+            a unicode object
+        :raises TypeError: This exception is raised if ``key_material`` is not
+                           ``bytes``.
+
+        Derives a new key from the input key material by performing both the
+        extract and expand operations.
+
+    .. method:: verify(key_material, expected_key)
+
+        :param key_material bytes: The input key material. This is the same as
+                                   ``key_material`` in :meth:`derive`.
+        :param expected_key bytes: The expected result of deriving a new key,
+                                   this is the same as the return value of
+                                   :meth:`derive`.
+        :raises cryptography.exceptions.InvalidKey: This is raised when the
+                                                    derived key does not match
+                                                    the expected key.
+        :raises cryptography.exceptions.AlreadyFinalized: This is raised when
+                                                          :meth:`derive` or
+                                                          :meth:`verify` is
+                                                          called more than
+                                                          once.
+        :raises TypeError: This is raised if the provided ``key_material`` is
+            a unicode object
+
+        This checks whether deriving a new key from the supplied
+        ``key_material`` generates the same key as the ``expected_key``, and
+        raises an exception if they do not match.
+
 .. _`NIST SP 800-132`: http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf
 .. _`Password Storage Cheat Sheet`: https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet
 .. _`PBKDF2`: https://en.wikipedia.org/wiki/PBKDF2
diff --git a/docs/hazmat/primitives/mac/cmac.rst b/docs/hazmat/primitives/mac/cmac.rst
index 1fde139..498b8b1 100644
--- a/docs/hazmat/primitives/mac/cmac.rst
+++ b/docs/hazmat/primitives/mac/cmac.rst
@@ -10,8 +10,8 @@
     import binascii
     key = binascii.unhexlify(b"0" * 32)
 
-`Cipher-based message authentication codes`_ (or CMACs) are a tool for calculating
-message authentication codes using a block cipher coupled with a
+`Cipher-based message authentication codes`_ (or CMACs) are a tool for
+calculating message authentication codes using a block cipher coupled with a
 secret key. You can use an CMAC to verify both the integrity and authenticity
 of a message.
 
@@ -68,6 +68,7 @@
 
         :param bytes data: The bytes to hash and authenticate.
         :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`
+        :raises TypeError: This exception is raised if ``data`` is not ``bytes``.
 
     .. method:: copy()
 
@@ -89,6 +90,8 @@
         :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`
         :raises cryptography.exceptions.InvalidSignature: If signature does not
                                                                   match digest
+        :raises TypeError: This exception is raised if ``signature`` is not
+                           ``bytes``.
 
         .. method:: finalize()
 
diff --git a/docs/hazmat/primitives/mac/hmac.rst b/docs/hazmat/primitives/mac/hmac.rst
index e20a403..d56927b 100644
--- a/docs/hazmat/primitives/mac/hmac.rst
+++ b/docs/hazmat/primitives/mac/hmac.rst
@@ -69,6 +69,7 @@
 
         :param bytes msg: The bytes to hash and authenticate.
         :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`
+        :raises TypeError: This exception is raised if ``msg`` is not ``bytes``.
 
     .. method:: copy()
 
@@ -90,6 +91,8 @@
         :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`
         :raises cryptography.exceptions.InvalidSignature: If signature does not
                                                           match digest
+        :raises TypeError: This exception is raised if ``signature`` is not
+                           ``bytes``.
 
     .. method:: finalize()
 
diff --git a/docs/hazmat/primitives/mac/index.rst b/docs/hazmat/primitives/mac/index.rst
index 4f07541..acfe9be 100644
--- a/docs/hazmat/primitives/mac/index.rst
+++ b/docs/hazmat/primitives/mac/index.rst
@@ -6,7 +6,8 @@
 While cryptography supports both the CMAC and HMAC algorithms, we strongly
 recommend that HMAC should be used unless you have a good reason otherwise.
 
-For more information on why HMAC is preferred, see `Use cases for CMAC vs. HMAC?`_
+For more information on why HMAC is preferred, see `Use cases for CMAC vs.
+HMAC?`_
 
 .. _`Use cases for CMAC vs. HMAC?`: http://crypto.stackexchange.com/questions/15721/use-cases-for-cmac-vs-hmac
 
diff --git a/docs/hazmat/primitives/padding.rst b/docs/hazmat/primitives/padding.rst
index 4092ac0..0322f9d 100644
--- a/docs/hazmat/primitives/padding.rst
+++ b/docs/hazmat/primitives/padding.rst
@@ -70,6 +70,7 @@
         :return bytes: Returns the data that was padded or unpadded.
         :raises TypeError: Raised if data is not bytes.
         :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`.
+        :raises TypeError: This exception is raised if ``data`` is not ``bytes``.
 
     .. method:: finalize()
 
diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst
index 78bf663..bca7835 100644
--- a/docs/hazmat/primitives/symmetric-encryption.rst
+++ b/docs/hazmat/primitives/symmetric-encryption.rst
@@ -20,9 +20,9 @@
 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
-message authentication code, such as :doc:`HMAC </hazmat/primitives/mac/hmac>`, in
-an "encrypt-then-MAC" formulation as `described by Colin Percival`_.
+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`_.
 
 .. class:: Cipher(algorithm, mode, backend)
 
@@ -275,6 +275,19 @@
         Must be the same number of bytes as the ``block_size`` of the cipher.
         Do not reuse an ``initialization_vector`` with a given ``key``.
 
+.. class:: CFB8(initialization_vector)
+
+    CFB (Cipher Feedback) is a mode of operation for block ciphers. It
+    transforms a block cipher into a stream cipher. The CFB8 variant uses an
+    8-bit shift register.
+
+    **This mode does not require padding.**
+
+    :param bytes initialization_vector: Must be random bytes. They do not need
+        to be kept secret and they can be included in a transmitted message.
+        Must be the same number of bytes as the ``block_size`` of the cipher.
+        Do not reuse an ``initialization_vector`` with a given ``key``.
+
 .. class:: GCM(initialization_vector, tag=None)
 
     .. danger::
diff --git a/docs/installation.rst b/docs/installation.rst
index 865e4cb..8fbbcb3 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -24,6 +24,7 @@
 OpenSSL releases:
 
 * ``OpenSSL 0.9.8e-fips-rhel5`` (``RHEL/CentOS 5``)
+* ``OpenSSL 0.9.8k``
 * ``OpenSSL 0.9.8y``
 * ``OpenSSL 1.0.0-fips`` (``RHEL/CentOS 6.4``)
 * ``OpenSSL 1.0.1``
@@ -107,7 +108,8 @@
 ------------------------------
 
 To link cryptography against a custom version of OpenSSL you'll need to set
-``ARCHFLAGS``, ``LDFLAGS``, and ``CFLAGS``. OpenSSL can be installed via `Homebrew`_ or `MacPorts`_:
+``ARCHFLAGS``, ``LDFLAGS``, and ``CFLAGS``. OpenSSL can be installed via
+`Homebrew`_ or `MacPorts`_:
 
 `Homebrew`_
 
@@ -127,13 +129,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/tasks.py b/tasks.py
index c205ac8..9ffdc8a 100644
--- a/tasks.py
+++ b/tasks.py
@@ -62,7 +62,7 @@
         response.raise_for_status()
         for artifact in response.json()["artifacts"]:
             response = requests.get(
-                "{0}artifacts/{1}".format(run["url"], artifact["relativePath"])
+                "{0}artifact/{1}".format(run["url"], artifact["relativePath"])
             )
             out_path = os.path.join(
                 os.path.dirname(__file__),
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()
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index 37347bc..19274bf 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -24,7 +24,7 @@
 from cryptography.hazmat.primitives.asymmetric import dsa, padding, rsa
 from cryptography.hazmat.primitives.ciphers import Cipher
 from cryptography.hazmat.primitives.ciphers.algorithms import AES
-from cryptography.hazmat.primitives.ciphers.modes import CBC
+from cryptography.hazmat.primitives.ciphers.modes import CBC, CTR
 from cryptography.hazmat.primitives.interfaces import BlockCipherAlgorithm
 
 from ...utils import raises_unsupported_algorithm
@@ -66,6 +66,11 @@
     def test_supports_cipher(self):
         assert backend.cipher_supported(None, None) is False
 
+    def test_aes_ctr_always_available(self):
+        # AES CTR should always be available in both 0.9.8 and 1.0.0+
+        assert backend.cipher_supported(AES(b"\x00" * 16),
+                                        CTR(b"\x00" * 16)) is True
+
     def test_register_duplicate_cipher_adapter(self):
         with pytest.raises(ValueError):
             backend.register_cipher_adapter(AES, CBC, None)
@@ -272,8 +277,8 @@
                 padding.PSS(
                     mgf=padding.MGF1(
                         algorithm=hashes.SHA256(),
-                        salt_length=padding.MGF1.MAX_LENGTH
-                    )
+                    ),
+                    salt_length=padding.PSS.MAX_LENGTH
                 ),
                 hashes.SHA1(),
                 backend
@@ -285,8 +290,8 @@
                 padding.PSS(
                     mgf=padding.MGF1(
                         algorithm=hashes.SHA256(),
-                        salt_length=padding.MGF1.MAX_LENGTH
-                    )
+                    ),
+                    salt_length=padding.PSS.MAX_LENGTH
                 ),
                 hashes.SHA1(),
                 backend
diff --git a/tests/hazmat/primitives/test_3des.py b/tests/hazmat/primitives/test_3des.py
index a4d696c..b9354f0 100644
--- a/tests/hazmat/primitives/test_3des.py
+++ b/tests/hazmat/primitives/test_3des.py
@@ -137,3 +137,40 @@
         ),
         lambda iv, **kwargs: modes.CFB(binascii.unhexlify(iv)),
     )
+
+
+@pytest.mark.supported(
+    only_if=lambda backend: backend.cipher_supported(
+        algorithms.TripleDES("\x00" * 8), modes.CFB8("\x00" * 8)
+    ),
+    skip_message="Does not support TripleDES CFB8",
+)
+@pytest.mark.cipher
+class TestTripleDESModeCFB8(object):
+    test_KAT = generate_encrypt_test(
+        load_nist_vectors,
+        os.path.join("ciphers", "3DES", "CFB"),
+        [
+            "TCFB8invperm.rsp",
+            "TCFB8permop.rsp",
+            "TCFB8subtab.rsp",
+            "TCFB8varkey.rsp",
+            "TCFB8vartext.rsp",
+        ],
+        lambda keys, **kwargs: algorithms.TripleDES(binascii.unhexlify(keys)),
+        lambda iv, **kwargs: modes.CFB8(binascii.unhexlify(iv)),
+    )
+
+    test_MMT = generate_encrypt_test(
+        load_nist_vectors,
+        os.path.join("ciphers", "3DES", "CFB"),
+        [
+            "TCFB8MMT1.rsp",
+            "TCFB8MMT2.rsp",
+            "TCFB8MMT3.rsp",
+        ],
+        lambda key1, key2, key3, **kwargs: algorithms.TripleDES(
+            binascii.unhexlify(key1 + key2 + key3)
+        ),
+        lambda iv, **kwargs: modes.CFB8(binascii.unhexlify(iv)),
+    )
diff --git a/tests/hazmat/primitives/test_aes.py b/tests/hazmat/primitives/test_aes.py
index 03be268..173075d 100644
--- a/tests/hazmat/primitives/test_aes.py
+++ b/tests/hazmat/primitives/test_aes.py
@@ -158,6 +158,39 @@
 
 @pytest.mark.supported(
     only_if=lambda backend: backend.cipher_supported(
+        algorithms.AES("\x00" * 16), modes.CFB8("\x00" * 16)
+    ),
+    skip_message="Does not support AES CFB8",
+)
+@pytest.mark.cipher
+class TestAESModeCFB8(object):
+    test_CFB8 = generate_encrypt_test(
+        load_nist_vectors,
+        os.path.join("ciphers", "AES", "CFB"),
+        [
+            "CFB8GFSbox128.rsp",
+            "CFB8GFSbox192.rsp",
+            "CFB8GFSbox256.rsp",
+            "CFB8KeySbox128.rsp",
+            "CFB8KeySbox192.rsp",
+            "CFB8KeySbox256.rsp",
+            "CFB8VarKey128.rsp",
+            "CFB8VarKey192.rsp",
+            "CFB8VarKey256.rsp",
+            "CFB8VarTxt128.rsp",
+            "CFB8VarTxt192.rsp",
+            "CFB8VarTxt256.rsp",
+            "CFB8MMT128.rsp",
+            "CFB8MMT192.rsp",
+            "CFB8MMT256.rsp",
+        ],
+        lambda key, **kwargs: algorithms.AES(binascii.unhexlify(key)),
+        lambda iv, **kwargs: modes.CFB8(binascii.unhexlify(iv)),
+    )
+
+
+@pytest.mark.supported(
+    only_if=lambda backend: backend.cipher_supported(
         algorithms.AES("\x00" * 16), modes.CTR("\x00" * 16)
     ),
     skip_message="Does not support AES CTR",
diff --git a/tests/hazmat/primitives/test_block.py b/tests/hazmat/primitives/test_block.py
index acfd947..022e3af 100644
--- a/tests/hazmat/primitives/test_block.py
+++ b/tests/hazmat/primitives/test_block.py
@@ -184,6 +184,14 @@
                 backend,
             )
 
+    def test_cfb8(self, backend):
+        with pytest.raises(ValueError):
+            Cipher(
+                algorithms.AES(b"\x00" * 16),
+                modes.CFB8(b"abc"),
+                backend,
+            )
+
     def test_ctr(self, backend):
         with pytest.raises(ValueError):
             Cipher(
diff --git a/tests/hazmat/primitives/test_hkdf.py b/tests/hazmat/primitives/test_hkdf.py
index 2e3c0c3..598f09f 100644
--- a/tests/hazmat/primitives/test_hkdf.py
+++ b/tests/hazmat/primitives/test_hkdf.py
@@ -13,6 +13,8 @@
 
 from __future__ import absolute_import, division, print_function
 
+import binascii
+
 import pytest
 
 import six
@@ -21,7 +23,7 @@
     AlreadyFinalized, InvalidKey, _Reasons
 )
 from cryptography.hazmat.primitives import hashes
-from cryptography.hazmat.primitives.kdf.hkdf import HKDF
+from cryptography.hazmat.primitives.kdf.hkdf import HKDF, HKDFExpand
 
 from ...utils import raises_unsupported_algorithm
 
@@ -151,8 +153,67 @@
             hkdf.verify(b"foo", six.u("bar"))
 
 
+@pytest.mark.hmac
+class TestHKDFExpand(object):
+    def test_derive(self, backend):
+        prk = binascii.unhexlify(
+            b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"
+        )
+
+        okm = (b"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c"
+               b"5bf34007208d5b887185865")
+
+        info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9")
+        hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend)
+
+        assert binascii.hexlify(hkdf.derive(prk)) == okm
+
+    def test_verify(self, backend):
+        prk = binascii.unhexlify(
+            b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"
+        )
+
+        okm = (b"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c"
+               b"5bf34007208d5b887185865")
+
+        info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9")
+        hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend)
+
+        assert hkdf.verify(prk, binascii.unhexlify(okm)) is None
+
+    def test_invalid_verify(self, backend):
+        prk = binascii.unhexlify(
+            b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"
+        )
+
+        info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9")
+        hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend)
+
+        with pytest.raises(InvalidKey):
+            hkdf.verify(prk, b"wrong key")
+
+    def test_already_finalized(self, backend):
+        info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9")
+        hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend)
+
+        hkdf.derive(b"first")
+
+        with pytest.raises(AlreadyFinalized):
+            hkdf.derive(b"second")
+
+    def test_unicode_error(self, backend):
+        info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9")
+        hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend)
+
+        with pytest.raises(TypeError):
+            hkdf.derive(six.u("first"))
+
+
 def test_invalid_backend():
     pretend_backend = object()
 
     with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
         HKDF(hashes.SHA256(), 16, None, None, pretend_backend)
+
+    with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
+        HKDFExpand(hashes.SHA256(), 16, None, pretend_backend)
diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py
index 63d6265..be5e8bf 100644
--- a/tests/hazmat/primitives/test_rsa.py
+++ b/tests/hazmat/primitives/test_rsa.py
@@ -458,8 +458,10 @@
             backend=backend
         )
         signer = private_key.signer(
-            padding.PSS(
-                mgf=padding.MGF1(
+            pytest.deprecated_call(
+                padding.PSS,
+                mgf=pytest.deprecated_call(
+                    padding.MGF1,
                     algorithm=hashes.SHA1(),
                     salt_length=padding.MGF1.MAX_LENGTH
                 )
@@ -472,8 +474,10 @@
         assert len(signature) == math.ceil(private_key.key_size / 8.0)
         verifier = private_key.public_key().verifier(
             signature,
-            padding.PSS(
-                mgf=padding.MGF1(
+            pytest.deprecated_call(
+                padding.PSS,
+                mgf=pytest.deprecated_call(
+                    padding.MGF1,
                     algorithm=hashes.SHA1(),
                     salt_length=padding.MGF1.MAX_LENGTH
                 )
@@ -944,8 +948,8 @@
             padding.PSS(
                 mgf=padding.MGF1(
                     algorithm=hashes.SHA1(),
-                    salt_length=1000000
-                )
+                ),
+                salt_length=1000000
             ),
             hashes.SHA1(),
             backend
@@ -972,8 +976,8 @@
         lambda params, hash_alg: padding.PSS(
             mgf=padding.MGF1(
                 algorithm=hash_alg,
-                salt_length=params["salt_length"]
-            )
+            ),
+            salt_length=params["salt_length"]
         )
     ))
 
@@ -992,8 +996,8 @@
         lambda params, hash_alg: padding.PSS(
             mgf=padding.MGF1(
                 algorithm=hash_alg,
-                salt_length=params["salt_length"]
-            )
+            ),
+            salt_length=params["salt_length"]
         )
     ))
 
@@ -1012,8 +1016,8 @@
         lambda params, hash_alg: padding.PSS(
             mgf=padding.MGF1(
                 algorithm=hash_alg,
-                salt_length=params["salt_length"]
-            )
+            ),
+            salt_length=params["salt_length"]
         )
     ))
 
@@ -1032,8 +1036,8 @@
         lambda params, hash_alg: padding.PSS(
             mgf=padding.MGF1(
                 algorithm=hash_alg,
-                salt_length=params["salt_length"]
-            )
+            ),
+            salt_length=params["salt_length"]
         )
     ))
 
@@ -1052,8 +1056,8 @@
         lambda params, hash_alg: padding.PSS(
             mgf=padding.MGF1(
                 algorithm=hash_alg,
-                salt_length=params["salt_length"]
-            )
+            ),
+            salt_length=params["salt_length"]
         )
     ))
 
diff --git a/tests/hazmat/primitives/utils.py b/tests/hazmat/primitives/utils.py
index 6c3f4c9..a496459 100644
--- a/tests/hazmat/primitives/utils.py
+++ b/tests/hazmat/primitives/utils.py
@@ -26,7 +26,7 @@
 from cryptography.hazmat.primitives import hashes, hmac
 from cryptography.hazmat.primitives.asymmetric import rsa
 from cryptography.hazmat.primitives.ciphers import Cipher
-from cryptography.hazmat.primitives.kdf.hkdf import HKDF
+from cryptography.hazmat.primitives.kdf.hkdf import HKDF, HKDFExpand
 from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
 
 from ...utils import load_vectors_from_file
@@ -347,15 +347,14 @@
 
 
 def hkdf_expand_test(backend, algorithm, params):
-    hkdf = HKDF(
+    hkdf = HKDFExpand(
         algorithm,
         int(params["l"]),
-        salt=binascii.unhexlify(params["salt"]) or None,
         info=binascii.unhexlify(params["info"]) or None,
         backend=backend
     )
 
-    okm = hkdf._expand(binascii.unhexlify(params["prk"]))
+    okm = hkdf.derive(binascii.unhexlify(params["prk"]))
 
     assert okm == binascii.unhexlify(params["okm"])
 
diff --git a/tox.ini b/tox.ini
index e7d168d..745fb37 100644
--- a/tox.ini
+++ b/tox.ini
@@ -16,6 +16,7 @@
 
 [testenv:docs]
 deps =
+    doc8
     pyenchant
     sphinx
     sphinx_rtd_theme
@@ -26,6 +27,7 @@
     sphinx-build -W -b latex -d {envtmpdir}/doctrees docs docs/_build/latex
     sphinx-build -W -b doctest -d {envtmpdir}/doctrees docs docs/_build/html
     sphinx-build -W -b spelling docs docs/_build/html
+    doc8 --allow-long-titles README.rst docs/
 
 [testenv:docs-linkcheck]
 deps =
@@ -45,7 +47,8 @@
     flake8
     flake8-import-order
     pep8-naming
-commands = flake8 .
+commands =
+    flake8 .
 
 [testenv:py3pep8]
 basepython = python3.3
@@ -53,7 +56,8 @@
     flake8
     flake8-import-order
     pep8-naming
-commands = flake8 .
+commands =
+    flake8 .
 
 [flake8]
 exclude = .tox,*.egg