Merge pull request #253 from dreid/hash-raise-after-finalize

raise an exception if you try to use a HashContext after finalize is called.
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 9f63250..b47f77e 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -13,3 +13,11 @@
 ``docs/contributing.rst``, or online at:
 
 https://cryptography.io/en/latest/contributing/
+
+Security issues
+---------------
+
+To report a security issue, please follow the special `security reporting
+guidelines`_, do not report them in the public issue tracker.
+
+.. _`security reporting guidelines`: https://cryptography.io/en/latest/security/
diff --git a/cryptography/hazmat/bindings/openssl/backend.py b/cryptography/hazmat/bindings/openssl/backend.py
index 71b94ab..844e175 100644
--- a/cryptography/hazmat/bindings/openssl/backend.py
+++ b/cryptography/hazmat/bindings/openssl/backend.py
@@ -63,9 +63,8 @@
     def __init__(self):
         self._ensure_ffi_initialized()
 
-        self.ciphers = Ciphers(self)
-        self.hashes = Hashes(self)
-        self.hmacs = HMACs(self)
+        self._cipher_registry = {}
+        self._register_default_ciphers()
 
     @classmethod
     def _ensure_ffi_initialized(cls):
@@ -123,6 +122,70 @@
         """
         return self.ffi.string(self.lib.OPENSSL_VERSION_TEXT).decode("ascii")
 
+    def create_hmac_ctx(self, key, algorithm):
+        return _HMACContext(self, key, algorithm)
+
+    def hash_supported(self, algorithm):
+        digest = self.lib.EVP_get_digestbyname(algorithm.name.encode("ascii"))
+        return digest != self.ffi.NULL
+
+    def create_hash_ctx(self, algorithm):
+        return _HashContext(self, algorithm)
+
+    def cipher_supported(self, cipher, mode):
+        try:
+            adapter = self._cipher_registry[type(cipher), type(mode)]
+        except KeyError:
+            return False
+        evp_cipher = adapter(self, cipher, mode)
+        return self.ffi.NULL != evp_cipher
+
+    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(
+                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],
+        ):
+            self.register_cipher_adapter(
+                cipher_cls,
+                mode_cls,
+                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
+            )
+        for mode_cls in [CBC, CFB, OFB]:
+            self.register_cipher_adapter(
+                TripleDES,
+                mode_cls,
+                GetCipherByName("des-ede3-{mode.name}")
+            )
+        for mode_cls in [CBC, CFB, OFB, ECB]:
+            self.register_cipher_adapter(
+                Blowfish,
+                mode_cls,
+                GetCipherByName("bf-{mode.name}")
+            )
+        self.register_cipher_adapter(
+            CAST5,
+            ECB,
+            GetCipherByName("cast5-ecb")
+        )
+        self.register_cipher_adapter(
+            ARC4,
+            type(None),
+            GetCipherByName("rc4")
+        )
+
+    def create_symmetric_encryption_ctx(self, cipher, mode):
+        return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT)
+
+    def create_symmetric_decryption_ctx(self, cipher, mode):
+        return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT)
+
 
 class GetCipherByName(object):
     def __init__(self, fmt):
@@ -145,7 +208,7 @@
         ctx = self._backend.lib.EVP_CIPHER_CTX_new()
         ctx = self._backend.ffi.gc(ctx, self._backend.lib.EVP_CIPHER_CTX_free)
 
-        registry = self._backend.ciphers._cipher_registry
+        registry = self._backend._cipher_registry
         try:
             adapter = registry[type(cipher), type(mode)]
         except KeyError:
@@ -204,69 +267,6 @@
         return self._backend.ffi.buffer(buf)[:outlen[0]]
 
 
-class Ciphers(object):
-    def __init__(self, backend):
-        self._backend = backend
-        self._cipher_registry = {}
-        self._register_default_ciphers()
-
-    def supported(self, cipher, mode):
-        try:
-            adapter = self._cipher_registry[type(cipher), type(mode)]
-        except KeyError:
-            return False
-        evp_cipher = adapter(self._backend, cipher, mode)
-        return self._backend.ffi.NULL != evp_cipher
-
-    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(
-                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],
-        ):
-            self.register_cipher_adapter(
-                cipher_cls,
-                mode_cls,
-                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
-            )
-        for mode_cls in [CBC, CFB, OFB]:
-            self.register_cipher_adapter(
-                TripleDES,
-                mode_cls,
-                GetCipherByName("des-ede3-{mode.name}")
-            )
-        for mode_cls in [CBC, CFB, OFB, ECB]:
-            self.register_cipher_adapter(
-                Blowfish,
-                mode_cls,
-                GetCipherByName("bf-{mode.name}")
-            )
-        self.register_cipher_adapter(
-            CAST5,
-            ECB,
-            GetCipherByName("cast5-ecb")
-        )
-        self.register_cipher_adapter(
-            ARC4,
-            type(None),
-            GetCipherByName("rc4")
-        )
-
-    def create_encrypt_ctx(self, cipher, mode):
-        return _CipherContext(self._backend, cipher, mode,
-                              _CipherContext._ENCRYPT)
-
-    def create_decrypt_ctx(self, cipher, mode):
-        return _CipherContext(self._backend, cipher, mode,
-                              _CipherContext._DECRYPT)
-
-
 @interfaces.register(interfaces.HashContext)
 class _HashContext(object):
     def __init__(self, backend, algorithm, ctx=None):
@@ -307,60 +307,59 @@
         assert res != 0
         res = self._backend.lib.EVP_MD_CTX_cleanup(self._ctx)
         assert res == 1
-        return self._backend.ffi.buffer(buf)[:self.algorithm.digest_size]
+        return self._backend.ffi.buffer(buf)[:]
 
 
-class Hashes(object):
-    def __init__(self, backend):
+@interfaces.register(interfaces.HashContext)
+class _HMACContext(object):
+    def __init__(self, backend, key, algorithm, ctx=None):
+        self.algorithm = algorithm
         self._backend = backend
 
-    def supported(self, algorithm):
-        digest = self._backend.lib.EVP_get_digestbyname(
-            algorithm.name.encode("ascii")
-        )
-        return digest != self._backend.ffi.NULL
+        if ctx is None:
+            ctx = self._backend.ffi.new("HMAC_CTX *")
+            self._backend.lib.HMAC_CTX_init(ctx)
+            ctx = self._backend.ffi.gc(ctx, self._backend.lib.HMAC_CTX_cleanup)
+            evp_md = self._backend.lib.EVP_get_digestbyname(
+                algorithm.name.encode('ascii'))
+            assert evp_md != self._backend.ffi.NULL
+            res = self._backend.lib.Cryptography_HMAC_Init_ex(
+                ctx, key, len(key), evp_md, self._backend.ffi.NULL
+            )
+            assert res != 0
 
-    def create_ctx(self, algorithm):
-        return _HashContext(self._backend, algorithm)
+        self._ctx = ctx
+        self._key = key
 
-
-class HMACs(object):
-    def __init__(self, backend):
-        self._backend = backend
-
-    def create_ctx(self, key, hash_cls):
-        ctx = self._backend.ffi.new("HMAC_CTX *")
-        self._backend.lib.HMAC_CTX_init(ctx)
-        ctx = self._backend.ffi.gc(ctx, self._backend.lib.HMAC_CTX_cleanup)
-        evp_md = self._backend.lib.EVP_get_digestbyname(
-            hash_cls.name.encode('ascii'))
-        assert evp_md != self._backend.ffi.NULL
-        res = self._backend.lib.Cryptography_HMAC_Init_ex(
-            ctx, key, len(key), evp_md, self._backend.ffi.NULL
-        )
-        assert res != 0
-        return ctx
-
-    def update_ctx(self, ctx, data):
-        res = self._backend.lib.Cryptography_HMAC_Update(ctx, data, len(data))
-        assert res != 0
-
-    def finalize_ctx(self, ctx, digest_size):
-        buf = self._backend.ffi.new("unsigned char[]", digest_size)
-        buflen = self._backend.ffi.new("unsigned int *", digest_size)
-        res = self._backend.lib.Cryptography_HMAC_Final(ctx, buf, buflen)
-        assert res != 0
-        self._backend.lib.HMAC_CTX_cleanup(ctx)
-        return self._backend.ffi.buffer(buf)[:digest_size]
-
-    def copy_ctx(self, ctx):
+    def copy(self):
         copied_ctx = self._backend.ffi.new("HMAC_CTX *")
         self._backend.lib.HMAC_CTX_init(copied_ctx)
-        copied_ctx = self._backend.ffi.gc(copied_ctx,
-                                          self._backend.lib.HMAC_CTX_cleanup)
-        res = self._backend.lib.Cryptography_HMAC_CTX_copy(copied_ctx, ctx)
+        copied_ctx = self._backend.ffi.gc(
+            copied_ctx, self._backend.lib.HMAC_CTX_cleanup
+        )
+        res = self._backend.lib.Cryptography_HMAC_CTX_copy(
+            copied_ctx, self._ctx
+        )
         assert res != 0
-        return copied_ctx
+        return _HMACContext(
+            self._backend, self._key, self.algorithm, ctx=copied_ctx
+        )
+
+    def update(self, data):
+        res = self._backend.lib.Cryptography_HMAC_Update(
+            self._ctx, data, len(data)
+        )
+        assert res != 0
+
+    def finalize(self):
+        buf = self._backend.ffi.new("unsigned char[]",
+                                    self.algorithm.digest_size)
+        buflen = self._backend.ffi.new("unsigned int *",
+                                       self.algorithm.digest_size)
+        res = self._backend.lib.Cryptography_HMAC_Final(self._ctx, buf, buflen)
+        assert res != 0
+        self._backend.lib.HMAC_CTX_cleanup(self._ctx)
+        return self._backend.ffi.buffer(buf)[:]
 
 
 backend = Backend()
diff --git a/cryptography/hazmat/primitives/ciphers/base.py b/cryptography/hazmat/primitives/ciphers/base.py
index d48f9cc..794d019 100644
--- a/cryptography/hazmat/primitives/ciphers/base.py
+++ b/cryptography/hazmat/primitives/ciphers/base.py
@@ -28,14 +28,14 @@
         self._backend = backend
 
     def encryptor(self):
-        return _CipherContext(
-            self._backend.ciphers.create_encrypt_ctx(self.algorithm,
-                                                     self.mode))
+        return _CipherContext(self._backend.create_symmetric_encryption_ctx(
+            self.algorithm, self.mode
+        ))
 
     def decryptor(self):
-        return _CipherContext(
-            self._backend.ciphers.create_decrypt_ctx(self.algorithm,
-                                                     self.mode))
+        return _CipherContext(self._backend.create_symmetric_decryption_ctx(
+            self.algorithm, self.mode
+        ))
 
 
 @interfaces.register(interfaces.CipherContext)
diff --git a/cryptography/hazmat/primitives/hashes.py b/cryptography/hazmat/primitives/hashes.py
index adf64d3..b8de6c4 100644
--- a/cryptography/hazmat/primitives/hashes.py
+++ b/cryptography/hazmat/primitives/hashes.py
@@ -33,7 +33,7 @@
         self._backend = backend
 
         if ctx is None:
-            self._ctx = self._backend.hashes.create_ctx(self.algorithm)
+            self._ctx = self._backend.create_hash_ctx(self.algorithm)
         else:
             self._ctx = ctx
 
diff --git a/cryptography/hazmat/primitives/hmac.py b/cryptography/hazmat/primitives/hmac.py
index ed2dd54..1a67b33 100644
--- a/cryptography/hazmat/primitives/hmac.py
+++ b/cryptography/hazmat/primitives/hmac.py
@@ -32,23 +32,22 @@
         self._backend = backend
         self._key = key
         if ctx is None:
-            self._ctx = self._backend.hmacs.create_ctx(key, self.algorithm)
+            self._ctx = self._backend.create_hmac_ctx(key, self.algorithm)
         else:
             self._ctx = ctx
 
     def update(self, msg):
         if isinstance(msg, six.text_type):
             raise TypeError("Unicode-objects must be encoded before hashing")
-        self._backend.hmacs.update_ctx(self._ctx, msg)
+        self._ctx.update(msg)
 
     def copy(self):
         return HMAC(
             self._key,
             self.algorithm,
             backend=self._backend,
-            ctx=self._backend.hmacs.copy_ctx(self._ctx)
+            ctx=self._ctx.copy()
         )
 
     def finalize(self):
-        return self._backend.hmacs.finalize_ctx(self._ctx,
-                                                self.algorithm.digest_size)
+        return self._ctx.finalize()
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 3b30184..8e5b1ce 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -122,6 +122,18 @@
 * No blank line at the end.
 * Use Sphinx parameter/attribute documentation `syntax`_.
 
+Because of the inherent challenges in implementing correct cryptographic
+systems, we want to make our documentation point people in the right directions
+as much as possible. To that end:
+
+* When documenting a generic interface, use a strong algorithm in examples.
+  (e.g. when showing a hashing example, don't use
+  :class:`cryptography.hazmat.primitives.hashes.MD5`)
+* When giving prescriptive advice, always provide references and supporting
+  material.
+* When there is real disagreement between cryptographic experts, represent both
+  sides of the argument and describe the tradeoffs clearly.
+
 When documenting a new module in the ``hazmat`` package, its documentation
 should begin with the "Hazardous Materials" warning:
 
diff --git a/docs/hazmat/primitives/cryptographic-hashes.rst b/docs/hazmat/primitives/cryptographic-hashes.rst
index 7eff1b8..525fd88 100644
--- a/docs/hazmat/primitives/cryptographic-hashes.rst
+++ b/docs/hazmat/primitives/cryptographic-hashes.rst
@@ -27,6 +27,12 @@
         >>> digest.finalize()
         'l\xa1=R\xcap\xc8\x83\xe0\xf0\xbb\x10\x1eBZ\x89\xe8bM\xe5\x1d\xb2\xd29%\x93\xafj\x84\x11\x80\x90'
 
+    Keep in mind that attacks against cryptographic hashes only get stronger
+    with time, and that often algorithms that were once thought to be strong,
+    become broken. Because of this it's important to include a plan for
+    upgrading the hash algorithm you use over time. For more information, see
+    `Lifetimes of cryptographic hash functions`_.
+
     .. method:: update(data)
 
         :param bytes data: The bytes you wish to hash.
@@ -118,3 +124,6 @@
 
     MD5 is a deprecated cryptographic hash function. It has a 128-bit message
     digest and has practical known collision attacks.
+
+
+.. _`Lifetimes of cryptographic hash functions`: http://valerieaurora.org/hash.html
diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py
index f1493e8..241c641 100644
--- a/tests/hazmat/bindings/test_openssl.py
+++ b/tests/hazmat/bindings/test_openssl.py
@@ -44,11 +44,11 @@
         assert backend.openssl_version_text().startswith("OpenSSL")
 
     def test_supports_cipher(self):
-        assert backend.ciphers.supported(None, None) is False
+        assert backend.cipher_supported(None, None) is False
 
     def test_register_duplicate_cipher_adapter(self):
         with pytest.raises(ValueError):
-            backend.ciphers.register_cipher_adapter(AES, CBC, None)
+            backend.register_cipher_adapter(AES, CBC, None)
 
     def test_instances_share_ffi(self):
         b = Backend()
@@ -57,7 +57,7 @@
 
     def test_nonexistent_cipher(self):
         b = Backend()
-        b.ciphers.register_cipher_adapter(
+        b.register_cipher_adapter(
             FakeCipher,
             FakeMode,
             lambda backend, cipher, mode: backend.ffi.NULL
diff --git a/tests/hazmat/primitives/test_aes.py b/tests/hazmat/primitives/test_aes.py
index 7c8cab7..d178da7 100644
--- a/tests/hazmat/primitives/test_aes.py
+++ b/tests/hazmat/primitives/test_aes.py
@@ -127,7 +127,7 @@
         ["aes-128-ctr.txt", "aes-192-ctr.txt", "aes-256-ctr.txt"],
         lambda key, iv: algorithms.AES(binascii.unhexlify(key)),
         lambda key, iv: modes.CTR(binascii.unhexlify(iv)),
-        only_if=lambda backend: backend.ciphers.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_arc4.py b/tests/hazmat/primitives/test_arc4.py
index 302658f..d233bec 100644
--- a/tests/hazmat/primitives/test_arc4.py
+++ b/tests/hazmat/primitives/test_arc4.py
@@ -36,7 +36,7 @@
             "rfc-6229-256.txt",
         ],
         lambda key: algorithms.ARC4(binascii.unhexlify((key))),
-        only_if=lambda backend: backend.ciphers.supported(
+        only_if=lambda backend: backend.cipher_supported(
             algorithms.ARC4("\x00" * 16), None
         ),
         skip_message="Does not support ARC4",
diff --git a/tests/hazmat/primitives/test_blowfish.py b/tests/hazmat/primitives/test_blowfish.py
index eea0ac3..d5fbed6 100644
--- a/tests/hazmat/primitives/test_blowfish.py
+++ b/tests/hazmat/primitives/test_blowfish.py
@@ -29,7 +29,7 @@
         ["bf-ecb.txt"],
         lambda key: algorithms.Blowfish(binascii.unhexlify(key)),
         lambda key: modes.ECB(),
-        only_if=lambda backend: backend.ciphers.supported(
+        only_if=lambda backend: backend.cipher_supported(
             algorithms.Blowfish("\x00" * 56), modes.ECB()
         ),
         skip_message="Does not support Blowfish ECB",
@@ -41,7 +41,7 @@
         ["bf-cbc.txt"],
         lambda key, iv: algorithms.Blowfish(binascii.unhexlify(key)),
         lambda key, iv: modes.CBC(binascii.unhexlify(iv)),
-        only_if=lambda backend: backend.ciphers.supported(
+        only_if=lambda backend: backend.cipher_supported(
             algorithms.Blowfish("\x00" * 56), modes.CBC("\x00" * 8)
         ),
         skip_message="Does not support Blowfish CBC",
@@ -53,7 +53,7 @@
         ["bf-ofb.txt"],
         lambda key, iv: algorithms.Blowfish(binascii.unhexlify(key)),
         lambda key, iv: modes.OFB(binascii.unhexlify(iv)),
-        only_if=lambda backend: backend.ciphers.supported(
+        only_if=lambda backend: backend.cipher_supported(
             algorithms.Blowfish("\x00" * 56), modes.OFB("\x00" * 8)
         ),
         skip_message="Does not support Blowfish OFB",
@@ -65,7 +65,7 @@
         ["bf-cfb.txt"],
         lambda key, iv: algorithms.Blowfish(binascii.unhexlify(key)),
         lambda key, iv: modes.CFB(binascii.unhexlify(iv)),
-        only_if=lambda backend: backend.ciphers.supported(
+        only_if=lambda backend: backend.cipher_supported(
             algorithms.Blowfish("\x00" * 56), modes.CFB("\x00" * 8)
         ),
         skip_message="Does not support Blowfish CFB",
diff --git a/tests/hazmat/primitives/test_camellia.py b/tests/hazmat/primitives/test_camellia.py
index 223269a..a2c935d 100644
--- a/tests/hazmat/primitives/test_camellia.py
+++ b/tests/hazmat/primitives/test_camellia.py
@@ -35,7 +35,7 @@
         ],
         lambda key: algorithms.Camellia(binascii.unhexlify((key))),
         lambda key: modes.ECB(),
-        only_if=lambda backend: backend.ciphers.supported(
+        only_if=lambda backend: backend.cipher_supported(
             algorithms.Camellia("\x00" * 16), modes.ECB()
         ),
         skip_message="Does not support Camellia ECB",
@@ -47,7 +47,7 @@
         ["camellia-cbc.txt"],
         lambda key, iv: algorithms.Camellia(binascii.unhexlify(key)),
         lambda key, iv: modes.CBC(binascii.unhexlify(iv)),
-        only_if=lambda backend: backend.ciphers.supported(
+        only_if=lambda backend: backend.cipher_supported(
             algorithms.Camellia("\x00" * 16), modes.CBC("\x00" * 16)
         ),
         skip_message="Does not support Camellia CBC",
@@ -59,7 +59,7 @@
         ["camellia-ofb.txt"],
         lambda key, iv: algorithms.Camellia(binascii.unhexlify(key)),
         lambda key, iv: modes.OFB(binascii.unhexlify(iv)),
-        only_if=lambda backend: backend.ciphers.supported(
+        only_if=lambda backend: backend.cipher_supported(
             algorithms.Camellia("\x00" * 16), modes.OFB("\x00" * 16)
         ),
         skip_message="Does not support Camellia OFB",
@@ -71,7 +71,7 @@
         ["camellia-cfb.txt"],
         lambda key, iv: algorithms.Camellia(binascii.unhexlify(key)),
         lambda key, iv: modes.CFB(binascii.unhexlify(iv)),
-        only_if=lambda backend: backend.ciphers.supported(
+        only_if=lambda backend: backend.cipher_supported(
             algorithms.Camellia("\x00" * 16), modes.CFB("\x00" * 16)
         ),
         skip_message="Does not support Camellia CFB",
diff --git a/tests/hazmat/primitives/test_cast5.py b/tests/hazmat/primitives/test_cast5.py
index 486b5b5..a283daf 100644
--- a/tests/hazmat/primitives/test_cast5.py
+++ b/tests/hazmat/primitives/test_cast5.py
@@ -29,7 +29,7 @@
         ["cast5-ecb.txt"],
         lambda key: algorithms.CAST5(binascii.unhexlify((key))),
         lambda key: modes.ECB(),
-        only_if=lambda backend: backend.ciphers.supported(
+        only_if=lambda backend: backend.cipher_supported(
             algorithms.CAST5("\x00" * 16), modes.ECB()
         ),
         skip_message="Does not support CAST5 ECB",
diff --git a/tests/hazmat/primitives/test_hash_vectors.py b/tests/hazmat/primitives/test_hash_vectors.py
index b08beca..a865581 100644
--- a/tests/hazmat/primitives/test_hash_vectors.py
+++ b/tests/hazmat/primitives/test_hash_vectors.py
@@ -30,7 +30,7 @@
             "SHA1ShortMsg.rsp",
         ],
         hashes.SHA1(),
-        only_if=lambda backend: backend.hashes.supported(hashes.SHA1),
+        only_if=lambda backend: backend.hash_supported(hashes.SHA1),
         skip_message="Does not support SHA1",
     )
 
@@ -44,7 +44,7 @@
             "SHA224ShortMsg.rsp",
         ],
         hashes.SHA224(),
-        only_if=lambda backend: backend.hashes.supported(hashes.SHA224),
+        only_if=lambda backend: backend.hash_supported(hashes.SHA224),
         skip_message="Does not support SHA224",
     )
 
@@ -58,7 +58,7 @@
             "SHA256ShortMsg.rsp",
         ],
         hashes.SHA256(),
-        only_if=lambda backend: backend.hashes.supported(hashes.SHA256),
+        only_if=lambda backend: backend.hash_supported(hashes.SHA256),
         skip_message="Does not support SHA256",
     )
 
@@ -72,7 +72,7 @@
             "SHA384ShortMsg.rsp",
         ],
         hashes.SHA384(),
-        only_if=lambda backend: backend.hashes.supported(hashes.SHA384),
+        only_if=lambda backend: backend.hash_supported(hashes.SHA384),
         skip_message="Does not support SHA384",
     )
 
@@ -86,7 +86,7 @@
             "SHA512ShortMsg.rsp",
         ],
         hashes.SHA512(),
-        only_if=lambda backend: backend.hashes.supported(hashes.SHA512),
+        only_if=lambda backend: backend.hash_supported(hashes.SHA512),
         skip_message="Does not support SHA512",
     )
 
@@ -99,14 +99,14 @@
             "ripevectors.txt",
         ],
         hashes.RIPEMD160(),
-        only_if=lambda backend: backend.hashes.supported(hashes.RIPEMD160),
+        only_if=lambda backend: backend.hash_supported(hashes.RIPEMD160),
         skip_message="Does not support RIPEMD160",
     )
 
     test_RIPEMD160_long_string = generate_long_string_hash_test(
         hashes.RIPEMD160(),
         "52783243c1697bdbe16d37f97f68f08325dc1528",
-        only_if=lambda backend: backend.hashes.supported(hashes.RIPEMD160),
+        only_if=lambda backend: backend.hash_supported(hashes.RIPEMD160),
         skip_message="Does not support RIPEMD160",
     )
 
@@ -119,7 +119,7 @@
             "iso-test-vectors.txt",
         ],
         hashes.Whirlpool(),
-        only_if=lambda backend: backend.hashes.supported(hashes.Whirlpool),
+        only_if=lambda backend: backend.hash_supported(hashes.Whirlpool),
         skip_message="Does not support Whirlpool",
     )
 
@@ -128,7 +128,7 @@
         ("0c99005beb57eff50a7cf005560ddf5d29057fd86b2"
          "0bfd62deca0f1ccea4af51fc15490eddc47af32bb2b"
          "66c34ff9ad8c6008ad677f77126953b226e4ed8b01"),
-        only_if=lambda backend: backend.hashes.supported(hashes.Whirlpool),
+        only_if=lambda backend: backend.hash_supported(hashes.Whirlpool),
         skip_message="Does not support Whirlpool",
     )
 
@@ -141,6 +141,6 @@
             "rfc-1321.txt",
         ],
         hashes.MD5(),
-        only_if=lambda backend: backend.hashes.supported(hashes.MD5),
+        only_if=lambda backend: backend.hash_supported(hashes.MD5),
         skip_message="Does not support MD5",
     )
diff --git a/tests/hazmat/primitives/test_hashes.py b/tests/hazmat/primitives/test_hashes.py
index 4c64460..991caf1 100644
--- a/tests/hazmat/primitives/test_hashes.py
+++ b/tests/hazmat/primitives/test_hashes.py
@@ -68,7 +68,7 @@
         hashes.SHA1(),
         digest_size=20,
         block_size=64,
-        only_if=lambda backend: backend.hashes.supported(hashes.SHA1),
+        only_if=lambda backend: backend.hash_supported(hashes.SHA1),
         skip_message="Does not support SHA1",
     )
 
@@ -78,7 +78,7 @@
         hashes.SHA224(),
         digest_size=28,
         block_size=64,
-        only_if=lambda backend: backend.hashes.supported(hashes.SHA224),
+        only_if=lambda backend: backend.hash_supported(hashes.SHA224),
         skip_message="Does not support SHA224",
     )
 
@@ -88,7 +88,7 @@
         hashes.SHA256(),
         digest_size=32,
         block_size=64,
-        only_if=lambda backend: backend.hashes.supported(hashes.SHA256),
+        only_if=lambda backend: backend.hash_supported(hashes.SHA256),
         skip_message="Does not support SHA256",
     )
 
@@ -98,7 +98,7 @@
         hashes.SHA384(),
         digest_size=48,
         block_size=128,
-        only_if=lambda backend: backend.hashes.supported(hashes.SHA384),
+        only_if=lambda backend: backend.hash_supported(hashes.SHA384),
         skip_message="Does not support SHA384",
     )
 
@@ -108,7 +108,7 @@
         hashes.SHA512(),
         digest_size=64,
         block_size=128,
-        only_if=lambda backend: backend.hashes.supported(hashes.SHA512),
+        only_if=lambda backend: backend.hash_supported(hashes.SHA512),
         skip_message="Does not support SHA512",
     )
 
@@ -118,7 +118,7 @@
         hashes.RIPEMD160(),
         digest_size=20,
         block_size=64,
-        only_if=lambda backend: backend.hashes.supported(hashes.RIPEMD160),
+        only_if=lambda backend: backend.hash_supported(hashes.RIPEMD160),
         skip_message="Does not support RIPEMD160",
     )
 
@@ -128,7 +128,7 @@
         hashes.Whirlpool(),
         digest_size=64,
         block_size=64,
-        only_if=lambda backend: backend.hashes.supported(hashes.Whirlpool),
+        only_if=lambda backend: backend.hash_supported(hashes.Whirlpool),
         skip_message="Does not support Whirlpool",
     )
 
@@ -138,6 +138,6 @@
         hashes.MD5(),
         digest_size=16,
         block_size=64,
-        only_if=lambda backend: backend.hashes.supported(hashes.MD5),
+        only_if=lambda backend: backend.hash_supported(hashes.MD5),
         skip_message="Does not support MD5",
     )
diff --git a/tests/hazmat/primitives/test_hmac.py b/tests/hazmat/primitives/test_hmac.py
index a44838c..4186967 100644
--- a/tests/hazmat/primitives/test_hmac.py
+++ b/tests/hazmat/primitives/test_hmac.py
@@ -27,7 +27,7 @@
 class TestHMAC(object):
     test_copy = generate_base_hmac_test(
         hashes.MD5(),
-        only_if=lambda backend: backend.hashes.supported(hashes.MD5),
+        only_if=lambda backend: backend.hash_supported(hashes.MD5),
         skip_message="Does not support MD5",
     )
 
@@ -37,9 +37,10 @@
             h.update(six.u("\u00FC"))
 
     def test_copy_backend_object(self):
-        pretend_hmac = pretend.stub(copy_ctx=lambda a: True)
+        pretend_hmac = pretend.stub()
         pretend_backend = pretend.stub(hmacs=pretend_hmac)
-        pretend_ctx = pretend.stub()
+        copied_ctx = pretend.stub()
+        pretend_ctx = pretend.stub(copy=lambda: copied_ctx)
         h = hmac.HMAC(b"key", hashes.SHA1(), backend=pretend_backend,
                       ctx=pretend_ctx)
         assert h._backend is pretend_backend
diff --git a/tests/hazmat/primitives/test_hmac_vectors.py b/tests/hazmat/primitives/test_hmac_vectors.py
index 570c3d4..7d0f156 100644
--- a/tests/hazmat/primitives/test_hmac_vectors.py
+++ b/tests/hazmat/primitives/test_hmac_vectors.py
@@ -27,7 +27,7 @@
             "rfc-2202-md5.txt",
         ],
         hashes.MD5(),
-        only_if=lambda backend: backend.hashes.supported(hashes.MD5),
+        only_if=lambda backend: backend.hash_supported(hashes.MD5),
         skip_message="Does not support MD5",
     )
 
@@ -40,7 +40,7 @@
             "rfc-2202-sha1.txt",
         ],
         hashes.SHA1(),
-        only_if=lambda backend: backend.hashes.supported(hashes.SHA1),
+        only_if=lambda backend: backend.hash_supported(hashes.SHA1),
         skip_message="Does not support SHA1",
     )
 
@@ -53,7 +53,7 @@
             "rfc-4231-sha224.txt",
         ],
         hashes.SHA224(),
-        only_if=lambda backend: backend.hashes.supported(hashes.SHA224),
+        only_if=lambda backend: backend.hash_supported(hashes.SHA224),
         skip_message="Does not support SHA224",
     )
 
@@ -66,7 +66,7 @@
             "rfc-4231-sha256.txt",
         ],
         hashes.SHA256(),
-        only_if=lambda backend: backend.hashes.supported(hashes.SHA256),
+        only_if=lambda backend: backend.hash_supported(hashes.SHA256),
         skip_message="Does not support SHA256",
     )
 
@@ -79,7 +79,7 @@
             "rfc-4231-sha384.txt",
         ],
         hashes.SHA384(),
-        only_if=lambda backend: backend.hashes.supported(hashes.SHA384),
+        only_if=lambda backend: backend.hash_supported(hashes.SHA384),
         skip_message="Does not support SHA384",
     )
 
@@ -92,7 +92,7 @@
             "rfc-4231-sha512.txt",
         ],
         hashes.SHA512(),
-        only_if=lambda backend: backend.hashes.supported(hashes.SHA512),
+        only_if=lambda backend: backend.hash_supported(hashes.SHA512),
         skip_message="Does not support SHA512",
     )
 
@@ -105,6 +105,6 @@
             "rfc-2286-ripemd160.txt",
         ],
         hashes.RIPEMD160(),
-        only_if=lambda backend: backend.hashes.supported(hashes.RIPEMD160),
+        only_if=lambda backend: backend.hash_supported(hashes.RIPEMD160),
         skip_message="Does not support RIPEMD160",
     )