Merge pull request #449 from reaperhulk/common-crypto-backend

CommonCrypto Backend (Hash support)
diff --git a/cryptography/hazmat/backends/__init__.py b/cryptography/hazmat/backends/__init__.py
index 215aa4d..cb1fee9 100644
--- a/cryptography/hazmat/backends/__init__.py
+++ b/cryptography/hazmat/backends/__init__.py
@@ -12,11 +12,15 @@
 # limitations under the License.
 
 from cryptography.hazmat.backends import openssl
+from cryptography.hazmat.bindings.commoncrypto.binding import (
+    Binding as CCBinding
+)
 
+_ALL_BACKENDS = [openssl.backend]
 
-_ALL_BACKENDS = [
-    openssl.backend
-]
+if CCBinding.is_available():
+    from cryptography.hazmat.backends import commoncrypto
+    _ALL_BACKENDS.append(commoncrypto.backend)
 
 
 def default_backend():
diff --git a/cryptography/hazmat/backends/commoncrypto/__init__.py b/cryptography/hazmat/backends/commoncrypto/__init__.py
new file mode 100644
index 0000000..64a1c01
--- /dev/null
+++ b/cryptography/hazmat/backends/commoncrypto/__init__.py
@@ -0,0 +1,17 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from cryptography.hazmat.backends.commoncrypto.backend import backend
+
+
+__all__ = ["backend"]
diff --git a/cryptography/hazmat/backends/commoncrypto/backend.py b/cryptography/hazmat/backends/commoncrypto/backend.py
new file mode 100644
index 0000000..3d98bf6
--- /dev/null
+++ b/cryptography/hazmat/backends/commoncrypto/backend.py
@@ -0,0 +1,125 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import, division, print_function
+
+from collections import namedtuple
+
+from cryptography import utils
+from cryptography.exceptions import UnsupportedAlgorithm
+from cryptography.hazmat.backends.interfaces import (
+    HashBackend,
+)
+from cryptography.hazmat.bindings.commoncrypto.binding import Binding
+from cryptography.hazmat.primitives import interfaces
+
+
+HashMethods = namedtuple(
+    "HashMethods", ["ctx", "hash_init", "hash_update", "hash_final"]
+)
+
+
+@utils.register_interface(HashBackend)
+class Backend(object):
+    """
+    CommonCrypto API wrapper.
+    """
+    name = "commoncrypto"
+
+    def __init__(self):
+        self._binding = Binding()
+        self._ffi = self._binding.ffi
+        self._lib = self._binding.lib
+
+        self._hash_mapping = {
+            "md5": HashMethods(
+                "CC_MD5_CTX *", self._lib.CC_MD5_Init,
+                self._lib.CC_MD5_Update, self._lib.CC_MD5_Final
+            ),
+            "sha1": HashMethods(
+                "CC_SHA1_CTX *", self._lib.CC_SHA1_Init,
+                self._lib.CC_SHA1_Update, self._lib.CC_SHA1_Final
+            ),
+            "sha224": HashMethods(
+                "CC_SHA256_CTX *", self._lib.CC_SHA224_Init,
+                self._lib.CC_SHA224_Update, self._lib.CC_SHA224_Final
+            ),
+            "sha256": HashMethods(
+                "CC_SHA256_CTX *", self._lib.CC_SHA256_Init,
+                self._lib.CC_SHA256_Update, self._lib.CC_SHA256_Final
+            ),
+            "sha384": HashMethods(
+                "CC_SHA512_CTX *", self._lib.CC_SHA384_Init,
+                self._lib.CC_SHA384_Update, self._lib.CC_SHA384_Final
+            ),
+            "sha512": HashMethods(
+                "CC_SHA512_CTX *", self._lib.CC_SHA512_Init,
+                self._lib.CC_SHA512_Update, self._lib.CC_SHA512_Final
+            ),
+        }
+
+    def hash_supported(self, algorithm):
+        try:
+            self._hash_mapping[algorithm.name]
+            return True
+        except KeyError:
+            return False
+
+    def create_hash_ctx(self, algorithm):
+        return _HashContext(self, algorithm)
+
+
+@utils.register_interface(interfaces.HashContext)
+class _HashContext(object):
+    def __init__(self, backend, algorithm, ctx=None):
+        self.algorithm = algorithm
+        self._backend = backend
+
+        if ctx is None:
+            try:
+                methods = self._backend._hash_mapping[self.algorithm.name]
+            except KeyError:
+                raise UnsupportedAlgorithm(
+                    "{0} is not a supported hash on this backend".format(
+                        algorithm.name)
+                )
+            ctx = self._backend._ffi.new(methods.ctx)
+            res = methods.hash_init(ctx)
+            assert res == 1
+
+        self._ctx = ctx
+
+    def copy(self):
+        methods = self._backend._hash_mapping[self.algorithm.name]
+        new_ctx = self._backend._ffi.new(methods.ctx)
+        # CommonCrypto has no APIs for copying hashes, so we have to copy the
+        # underlying struct.
+        new_ctx[0] = self._ctx[0]
+
+        return _HashContext(self._backend, self.algorithm, ctx=new_ctx)
+
+    def update(self, data):
+        methods = self._backend._hash_mapping[self.algorithm.name]
+        res = methods.hash_update(self._ctx, data, len(data))
+        assert res == 1
+
+    def finalize(self):
+        methods = self._backend._hash_mapping[self.algorithm.name]
+        buf = self._backend._ffi.new("unsigned char[]",
+                                     self.algorithm.digest_size)
+        res = methods.hash_final(buf, self._ctx)
+        assert res == 1
+        return self._backend._ffi.buffer(buf)[:]
+
+
+backend = Backend()
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 289992f..b0baf91 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -6,6 +6,7 @@
 
 **In development**
 
+* Added CommonCrypto backend with hash support.
 * Added initial CommonCrypto bindings.
 
 0.1 - 2014-01-08
diff --git a/docs/hazmat/backends/common-crypto.rst b/docs/hazmat/backends/common-crypto.rst
new file mode 100644
index 0000000..af2032b
--- /dev/null
+++ b/docs/hazmat/backends/common-crypto.rst
@@ -0,0 +1,20 @@
+.. hazmat::
+
+CommonCrypto Backend
+====================
+
+The `CommonCrypto`_ C library provided by Apple on OS X and iOS.
+
+.. currentmodule:: cryptography.hazmat.backends.commoncrypto.backend
+
+.. versionadded:: 0.2
+
+.. data:: cryptography.hazmat.backends.commoncrypto.backend
+
+    This is the exposed API for the CommonCrypto backend. It has one public attribute.
+
+    .. attribute:: name
+
+        The string name of this backend: ``"commoncrypto"``
+
+.. _`CommonCrypto`: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/Common%20Crypto.3cc.html
diff --git a/docs/hazmat/backends/index.rst b/docs/hazmat/backends/index.rst
index 0695128..22354f6 100644
--- a/docs/hazmat/backends/index.rst
+++ b/docs/hazmat/backends/index.rst
@@ -31,4 +31,5 @@
     :maxdepth: 1
 
     openssl
+    common-crypto
     interfaces
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index 97356c2..75628ba 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -14,6 +14,7 @@
 indistinguishability
 introspectability
 invariants
+iOS
 pickleable
 plaintext
 testability