split backend up (refs #170)

* Moves cipher methods into a Ciphers class and hash methods to a Hashes
  class and makes them available inside Backend as pluralized
  attributes.
* Shortened many of the methods since their purpose is now defined by
  their container class
diff --git a/cryptography/bindings/openssl/backend.py b/cryptography/bindings/openssl/backend.py
index 492f2e5..9c80be6 100644
--- a/cryptography/bindings/openssl/backend.py
+++ b/cryptography/bindings/openssl/backend.py
@@ -23,15 +23,6 @@
 from cryptography.primitives.block.modes import CBC, CTR, ECB, OFB, CFB
 
 
-class GetCipherByName(object):
-    def __init__(self, fmt):
-        self._fmt = fmt
-
-    def __call__(self, backend, cipher, mode):
-        cipher_name = self._fmt.format(cipher=cipher, mode=mode).lower()
-        return backend.lib.EVP_get_cipherbyname(cipher_name.encode("ascii"))
-
-
 class Backend(object):
     """
     OpenSSL API wrapper.
@@ -99,8 +90,8 @@
         self.lib.OpenSSL_add_all_algorithms()
         self.lib.SSL_load_error_strings()
 
-        self._cipher_registry = {}
-        self._register_default_ciphers()
+        self.ciphers = Ciphers(self.ffi, self.lib)
+        self.hashes = Hashes(self.ffi, self.lib)
 
     def openssl_version_text(self):
         """
@@ -110,7 +101,25 @@
         """
         return self.ffi.string(self.lib.OPENSSL_VERSION_TEXT).decode("ascii")
 
-    def supports_cipher(self, cipher, mode):
+
+class GetCipherByName(object):
+    def __init__(self, fmt):
+        self._fmt = fmt
+
+    def __call__(self, backend, cipher, mode):
+        cipher_name = self._fmt.format(cipher=cipher, mode=mode).lower()
+        return backend.lib.EVP_get_cipherbyname(cipher_name.encode("ascii"))
+
+
+class Ciphers(object):
+    def __init__(self, ffi, lib):
+        super(Ciphers, self).__init__()
+        self.ffi = ffi
+        self.lib = lib
+        self._cipher_registry = {}
+        self._register_default_ciphers()
+
+    def supported(self, cipher, mode):
         try:
             adapter = self._cipher_registry[type(cipher), type(mode)]
         except KeyError:
@@ -142,8 +151,8 @@
                 GetCipherByName("des-ede3-{mode.name}")
             )
 
-    def create_block_cipher_encrypt_context(self, cipher, mode):
-        ctx, evp, iv_nonce = self._create_block_cipher_context(cipher, mode)
+    def create_encrypt_ctx(self, cipher, mode):
+        ctx, evp, iv_nonce = self._create_ctx(cipher, mode)
         res = self.lib.EVP_EncryptInit_ex(ctx, evp, self.ffi.NULL, cipher.key,
                                           iv_nonce)
         assert res != 0
@@ -152,8 +161,8 @@
         self.lib.EVP_CIPHER_CTX_set_padding(ctx, 0)
         return ctx
 
-    def create_block_cipher_decrypt_context(self, cipher, mode):
-        ctx, evp, iv_nonce = self._create_block_cipher_context(cipher, mode)
+    def create_decrypt_ctx(self, cipher, mode):
+        ctx, evp, iv_nonce = self._create_ctx(cipher, mode)
         res = self.lib.EVP_DecryptInit_ex(ctx, evp, self.ffi.NULL, cipher.key,
                                           iv_nonce)
         assert res != 0
@@ -162,7 +171,7 @@
         self.lib.EVP_CIPHER_CTX_set_padding(ctx, 0)
         return ctx
 
-    def _create_block_cipher_context(self, cipher, mode):
+    def _create_ctx(self, cipher, mode):
         ctx = self.lib.EVP_CIPHER_CTX_new()
         ctx = self.ffi.gc(ctx, self.lib.EVP_CIPHER_CTX_free)
         evp_cipher = self._cipher_registry[type(cipher), type(mode)](
@@ -178,7 +187,7 @@
 
         return (ctx, evp_cipher, iv_nonce)
 
-    def update_encrypt_context(self, ctx, data):
+    def update_encrypt_ctx(self, ctx, data):
         block_size = self.lib.EVP_CIPHER_CTX_block_size(ctx)
         buf = self.ffi.new("unsigned char[]", len(data) + block_size - 1)
         outlen = self.ffi.new("int *")
@@ -186,7 +195,7 @@
         assert res != 0
         return self.ffi.buffer(buf)[:outlen[0]]
 
-    def update_decrypt_context(self, ctx, data):
+    def update_decrypt_ctx(self, ctx, data):
         block_size = self.lib.EVP_CIPHER_CTX_block_size(ctx)
         buf = self.ffi.new("unsigned char[]", len(data) + block_size - 1)
         outlen = self.ffi.new("int *")
@@ -194,7 +203,7 @@
         assert res != 0
         return self.ffi.buffer(buf)[:outlen[0]]
 
-    def finalize_encrypt_context(self, ctx):
+    def finalize_encrypt_ctx(self, ctx):
         block_size = self.lib.EVP_CIPHER_CTX_block_size(ctx)
         buf = self.ffi.new("unsigned char[]", block_size)
         outlen = self.ffi.new("int *")
@@ -204,7 +213,7 @@
         assert res == 1
         return self.ffi.buffer(buf)[:outlen[0]]
 
-    def finalize_decrypt_context(self, ctx):
+    def finalize_decrypt_ctx(self, ctx):
         block_size = self.lib.EVP_CIPHER_CTX_block_size(ctx)
         buf = self.ffi.new("unsigned char[]", block_size)
         outlen = self.ffi.new("int *")
@@ -214,11 +223,18 @@
         assert res == 1
         return self.ffi.buffer(buf)[:outlen[0]]
 
-    def supports_hash(self, hash_cls):
+
+class Hashes(object):
+    def __init__(self, ffi, lib):
+        super(Hashes, self).__init__()
+        self.ffi = ffi
+        self.lib = lib
+
+    def supported(self, hash_cls):
         return (self.ffi.NULL !=
                 self.lib.EVP_get_digestbyname(hash_cls.name.encode("ascii")))
 
-    def create_hash_context(self, hashobject):
+    def create_ctx(self, hashobject):
         ctx = self.lib.EVP_MD_CTX_create()
         ctx = self.ffi.gc(ctx, self.lib.EVP_MD_CTX_destroy)
         evp_md = self.lib.EVP_get_digestbyname(hashobject.name.encode("ascii"))
@@ -227,11 +243,11 @@
         assert res != 0
         return ctx
 
-    def update_hash_context(self, ctx, data):
+    def update_ctx(self, ctx, data):
         res = self.lib.EVP_DigestUpdate(ctx, data, len(data))
         assert res != 0
 
-    def finalize_hash_context(self, ctx, digest_size):
+    def finalize_ctx(self, ctx, digest_size):
         buf = self.ffi.new("unsigned char[]", digest_size)
         res = self.lib.EVP_DigestFinal_ex(ctx, buf, self.ffi.NULL)
         assert res != 0
@@ -239,7 +255,7 @@
         assert res == 1
         return self.ffi.buffer(buf)[:digest_size]
 
-    def copy_hash_context(self, ctx):
+    def copy_ctx(self, ctx):
         copied_ctx = self.lib.EVP_MD_CTX_create()
         copied_ctx = self.ffi.gc(copied_ctx, self.lib.EVP_MD_CTX_destroy)
         res = self.lib.EVP_MD_CTX_copy_ex(copied_ctx, ctx)
diff --git a/cryptography/primitives/block/base.py b/cryptography/primitives/block/base.py
index e9f5288..e9b949c 100644
--- a/cryptography/primitives/block/base.py
+++ b/cryptography/primitives/block/base.py
@@ -39,19 +39,17 @@
     def __init__(self, cipher, mode, backend):
         super(_CipherEncryptionContext, self).__init__()
         self._backend = backend
-        self._ctx = self._backend.create_block_cipher_encrypt_context(
-            cipher, mode
-        )
+        self._ctx = self._backend.ciphers.create_encrypt_ctx(cipher, mode)
 
     def update(self, data):
         if self._ctx is None:
             raise ValueError("Context was already finalized")
-        return self._backend.update_encrypt_context(self._ctx, data)
+        return self._backend.ciphers.update_encrypt_ctx(self._ctx, data)
 
     def finalize(self):
         if self._ctx is None:
             raise ValueError("Context was already finalized")
-        data = self._backend.finalize_encrypt_context(self._ctx)
+        data = self._backend.ciphers.finalize_encrypt_ctx(self._ctx)
         self._ctx = None
         return data
 
@@ -61,18 +59,16 @@
     def __init__(self, cipher, mode, backend):
         super(_CipherDecryptionContext, self).__init__()
         self._backend = backend
-        self._ctx = self._backend.create_block_cipher_decrypt_context(
-            cipher, mode
-        )
+        self._ctx = self._backend.ciphers.create_decrypt_ctx(cipher, mode)
 
     def update(self, data):
         if self._ctx is None:
             raise ValueError("Context was already finalized")
-        return self._backend.update_decrypt_context(self._ctx, data)
+        return self._backend.ciphers.update_decrypt_ctx(self._ctx, data)
 
     def finalize(self):
         if self._ctx is None:
             raise ValueError("Context was already finalized")
-        data = self._backend.finalize_decrypt_context(self._ctx)
+        data = self._backend.ciphers.finalize_decrypt_ctx(self._ctx)
         self._ctx = None
         return data
diff --git a/cryptography/primitives/hashes.py b/cryptography/primitives/hashes.py
index 4cd68ad..023041c 100644
--- a/cryptography/primitives/hashes.py
+++ b/cryptography/primitives/hashes.py
@@ -27,7 +27,7 @@
             backend = _default_backend
         self._backend = backend
         if ctx is None:
-            self._ctx = self._backend.create_hash_context(self)
+            self._ctx = self._backend.hashes.create_ctx(self)
         else:
             self._ctx = None
 
@@ -37,20 +37,20 @@
     def update(self, data):
         if isinstance(data, six.text_type):
             raise TypeError("Unicode-objects must be encoded before hashing")
-        self._backend.update_hash_context(self._ctx, data)
+        self._backend.hashes.update_ctx(self._ctx, data)
 
     def copy(self):
         return self.__class__(backend=self._backend, ctx=self._copy_ctx())
 
     def digest(self):
-        return self._backend.finalize_hash_context(self._copy_ctx(),
-                                                   self.digest_size)
+        return self._backend.hashes.finalize_ctx(self._copy_ctx(),
+                                                 self.digest_size)
 
     def hexdigest(self):
         return str(binascii.hexlify(self.digest()).decode("ascii"))
 
     def _copy_ctx(self):
-        return self._backend.copy_hash_context(self._ctx)
+        return self._backend.hashes.copy_ctx(self._ctx)
 
 
 class SHA1(BaseHash):
diff --git a/tests/bindings/test_openssl.py b/tests/bindings/test_openssl.py
index bdfbed3..6f9c9d2 100644
--- a/tests/bindings/test_openssl.py
+++ b/tests/bindings/test_openssl.py
@@ -34,8 +34,8 @@
         assert backend.openssl_version_text().startswith("OpenSSL")
 
     def test_supports_cipher(self):
-        assert backend.supports_cipher(None, None) is False
+        assert backend.ciphers.supported(None, None) is False
 
     def test_register_duplicate_cipher_adapter(self):
         with pytest.raises(ValueError):
-            backend.register_cipher_adapter(AES, CBC, None)
+            backend.ciphers.register_cipher_adapter(AES, CBC, None)
diff --git a/tests/primitives/test_cryptrec.py b/tests/primitives/test_cryptrec.py
index d8c9aaa..5e0b12d 100644
--- a/tests/primitives/test_cryptrec.py
+++ b/tests/primitives/test_cryptrec.py
@@ -37,7 +37,7 @@
         ],
         lambda key: ciphers.Camellia(binascii.unhexlify((key))),
         lambda key: modes.ECB(),
-        only_if=lambda backend: backend.supports_cipher(
+        only_if=lambda backend: backend.ciphers.supported(
             ciphers.Camellia("\x00" * 16), modes.ECB()
         ),
         skip_message="Does not support Camellia ECB",
diff --git a/tests/primitives/test_hash_vectors.py b/tests/primitives/test_hash_vectors.py
index b42021c..e385472 100644
--- a/tests/primitives/test_hash_vectors.py
+++ b/tests/primitives/test_hash_vectors.py
@@ -30,7 +30,7 @@
             "SHA1ShortMsg.rsp",
         ],
         hashes.SHA1,
-        only_if=lambda backend: backend.supports_hash(hashes.SHA1),
+        only_if=lambda backend: backend.hashes.supported(hashes.SHA1),
         skip_message="Does not support SHA1",
     )
 
@@ -44,7 +44,7 @@
             "SHA224ShortMsg.rsp",
         ],
         hashes.SHA224,
-        only_if=lambda backend: backend.supports_hash(hashes.SHA224),
+        only_if=lambda backend: backend.hashes.supported(hashes.SHA224),
         skip_message="Does not support SHA224",
     )
 
@@ -58,7 +58,7 @@
             "SHA256ShortMsg.rsp",
         ],
         hashes.SHA256,
-        only_if=lambda backend: backend.supports_hash(hashes.SHA256),
+        only_if=lambda backend: backend.hashes.supported(hashes.SHA256),
         skip_message="Does not support SHA256",
     )
 
@@ -72,7 +72,7 @@
             "SHA384ShortMsg.rsp",
         ],
         hashes.SHA384,
-        only_if=lambda backend: backend.supports_hash(hashes.SHA384),
+        only_if=lambda backend: backend.hashes.supported(hashes.SHA384),
         skip_message="Does not support SHA384",
     )
 
@@ -86,7 +86,7 @@
             "SHA512ShortMsg.rsp",
         ],
         hashes.SHA512,
-        only_if=lambda backend: backend.supports_hash(hashes.SHA512),
+        only_if=lambda backend: backend.hashes.supported(hashes.SHA512),
         skip_message="Does not support SHA512",
     )
 
@@ -99,14 +99,14 @@
             "ripevectors.txt",
         ],
         hashes.RIPEMD160,
-        only_if=lambda backend: backend.supports_hash(hashes.RIPEMD160),
+        only_if=lambda backend: backend.hashes.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.supports_hash(hashes.RIPEMD160),
+        only_if=lambda backend: backend.hashes.supported(hashes.RIPEMD160),
         skip_message="Does not support RIPEMD160",
     )
 
@@ -119,7 +119,7 @@
             "iso-test-vectors.txt",
         ],
         hashes.Whirlpool,
-        only_if=lambda backend: backend.supports_hash(hashes.Whirlpool),
+        only_if=lambda backend: backend.hashes.supported(hashes.Whirlpool),
         skip_message="Does not support Whirlpool",
     )
 
@@ -128,7 +128,7 @@
         ("0c99005beb57eff50a7cf005560ddf5d29057fd86b2"
          "0bfd62deca0f1ccea4af51fc15490eddc47af32bb2b"
          "66c34ff9ad8c6008ad677f77126953b226e4ed8b01"),
-        only_if=lambda backend: backend.supports_hash(hashes.Whirlpool),
+        only_if=lambda backend: backend.hashes.supported(hashes.Whirlpool),
         skip_message="Does not support Whirlpool",
     )
 
@@ -141,6 +141,6 @@
             "rfc-1321.txt",
         ],
         hashes.MD5,
-        only_if=lambda backend: backend.supports_hash(hashes.MD5),
+        only_if=lambda backend: backend.hashes.supported(hashes.MD5),
         skip_message="Does not support MD5",
     )
diff --git a/tests/primitives/test_hashes.py b/tests/primitives/test_hashes.py
index 2efda05..174fd5f 100644
--- a/tests/primitives/test_hashes.py
+++ b/tests/primitives/test_hashes.py
@@ -39,17 +39,18 @@
 
 class TestCopyHash(object):
     def test_copy_backend_object(self):
-        pretend_backend = pretend.stub(copy_hash_context=lambda a: "copiedctx")
+        pretend_hashes = pretend.stub(copy_ctx=lambda a: "copiedctx")
+        pretend_backend = pretend.stub(hashes=pretend_hashes)
         pretend_ctx = pretend.stub()
         h = hashes.SHA1(backend=pretend_backend, ctx=pretend_ctx)
         assert h._backend is pretend_backend
         assert h.copy()._backend is h._backend
 
 
-class TestDefaultAPISHA1(object):
+class TestDefaultBackendSHA1(object):
     def test_default_backend_creation(self):
         """
-        This test assumes the presence of SHA1 in the default API.
+        This test assumes the presence of SHA1 in the default backend.
         """
         h = hashes.SHA1()
         assert h._backend is _default_backend
@@ -60,7 +61,7 @@
         hashes.SHA1,
         digest_size=20,
         block_size=64,
-        only_if=lambda backend: backend.supports_hash(hashes.SHA1),
+        only_if=lambda backend: backend.hashes.supported(hashes.SHA1),
         skip_message="Does not support SHA1",
     )
 
@@ -70,7 +71,7 @@
         hashes.SHA224,
         digest_size=28,
         block_size=64,
-        only_if=lambda backend: backend.supports_hash(hashes.SHA224),
+        only_if=lambda backend: backend.hashes.supported(hashes.SHA224),
         skip_message="Does not support SHA224",
     )
 
@@ -80,7 +81,7 @@
         hashes.SHA256,
         digest_size=32,
         block_size=64,
-        only_if=lambda backend: backend.supports_hash(hashes.SHA256),
+        only_if=lambda backend: backend.hashes.supported(hashes.SHA256),
         skip_message="Does not support SHA256",
     )
 
@@ -90,7 +91,7 @@
         hashes.SHA384,
         digest_size=48,
         block_size=128,
-        only_if=lambda backend: backend.supports_hash(hashes.SHA384),
+        only_if=lambda backend: backend.hashes.supported(hashes.SHA384),
         skip_message="Does not support SHA384",
     )
 
@@ -100,7 +101,7 @@
         hashes.SHA512,
         digest_size=64,
         block_size=128,
-        only_if=lambda backend: backend.supports_hash(hashes.SHA512),
+        only_if=lambda backend: backend.hashes.supported(hashes.SHA512),
         skip_message="Does not support SHA512",
     )
 
@@ -110,7 +111,7 @@
         hashes.RIPEMD160,
         digest_size=20,
         block_size=64,
-        only_if=lambda backend: backend.supports_hash(hashes.RIPEMD160),
+        only_if=lambda backend: backend.hashes.supported(hashes.RIPEMD160),
         skip_message="Does not support RIPEMD160",
     )
 
@@ -120,7 +121,7 @@
         hashes.Whirlpool,
         digest_size=64,
         block_size=64,
-        only_if=lambda backend: backend.supports_hash(hashes.Whirlpool),
+        only_if=lambda backend: backend.hashes.supported(hashes.Whirlpool),
         skip_message="Does not support Whirlpool",
     )
 
@@ -130,6 +131,6 @@
         hashes.MD5,
         digest_size=16,
         block_size=64,
-        only_if=lambda backend: backend.supports_hash(hashes.MD5),
+        only_if=lambda backend: backend.hashes.supported(hashes.MD5),
         skip_message="Does not support MD5",
     )
diff --git a/tests/primitives/test_openssl_vectors.py b/tests/primitives/test_openssl_vectors.py
index ff42b16..0f242ef 100644
--- a/tests/primitives/test_openssl_vectors.py
+++ b/tests/primitives/test_openssl_vectors.py
@@ -32,7 +32,7 @@
         ["camellia-cbc.txt"],
         lambda key, iv: ciphers.Camellia(binascii.unhexlify(key)),
         lambda key, iv: modes.CBC(binascii.unhexlify(iv)),
-        only_if=lambda backend: backend.supports_cipher(
+        only_if=lambda backend: backend.ciphers.supported(
             ciphers.Camellia("\x00" * 16), modes.CBC("\x00" * 16)
         ),
         skip_message="Does not support Camellia CBC",
@@ -46,7 +46,7 @@
         ["camellia-ofb.txt"],
         lambda key, iv: ciphers.Camellia(binascii.unhexlify(key)),
         lambda key, iv: modes.OFB(binascii.unhexlify(iv)),
-        only_if=lambda backend: backend.supports_cipher(
+        only_if=lambda backend: backend.ciphers.supported(
             ciphers.Camellia("\x00" * 16), modes.OFB("\x00" * 16)
         ),
         skip_message="Does not support Camellia OFB",
@@ -60,7 +60,7 @@
         ["camellia-cfb.txt"],
         lambda key, iv: ciphers.Camellia(binascii.unhexlify(key)),
         lambda key, iv: modes.CFB(binascii.unhexlify(iv)),
-        only_if=lambda backend: backend.supports_cipher(
+        only_if=lambda backend: backend.ciphers.supported(
             ciphers.Camellia("\x00" * 16), modes.CFB("\x00" * 16)
         ),
         skip_message="Does not support Camellia CFB",
@@ -74,7 +74,7 @@
         ["aes-128-ctr.txt", "aes-192-ctr.txt", "aes-256-ctr.txt"],
         lambda key, iv: ciphers.AES(binascii.unhexlify(key)),
         lambda key, iv: modes.CTR(binascii.unhexlify(iv)),
-        only_if=lambda backend: backend.supports_cipher(
+        only_if=lambda backend: backend.ciphers.supported(
             ciphers.AES("\x00" * 16), modes.CTR("\x00" * 16)
         ),
         skip_message="Does not support AES CTR",