Merge pull request #531 from reaperhulk/pbkdf2-commoncrypto

PBKDF2HMAC (CommonCrypto)
diff --git a/cryptography/hazmat/backends/commoncrypto/backend.py b/cryptography/hazmat/backends/commoncrypto/backend.py
index 6a98609..e5d4ee0 100644
--- a/cryptography/hazmat/backends/commoncrypto/backend.py
+++ b/cryptography/hazmat/backends/commoncrypto/backend.py
@@ -20,7 +20,7 @@
     UnsupportedAlgorithm, InvalidTag, InternalError
 )
 from cryptography.hazmat.backends.interfaces import (
-    HashBackend, HMACBackend, CipherBackend
+    HashBackend, HMACBackend, CipherBackend, PBKDF2HMACBackend
 )
 from cryptography.hazmat.bindings.commoncrypto.binding import Binding
 from cryptography.hazmat.primitives import interfaces, constant_time
@@ -40,6 +40,7 @@
 @utils.register_interface(CipherBackend)
 @utils.register_interface(HashBackend)
 @utils.register_interface(HMACBackend)
+@utils.register_interface(PBKDF2HMACBackend)
 class Backend(object):
     """
     CommonCrypto API wrapper.
@@ -89,6 +90,14 @@
             "sha512": self._lib.kCCHmacAlgSHA512,
         }
 
+        self._supported_pbkdf2_hmac_algorithms = {
+            "sha1": self._lib.kCCPRFHmacAlgSHA1,
+            "sha224": self._lib.kCCPRFHmacAlgSHA224,
+            "sha256": self._lib.kCCPRFHmacAlgSHA256,
+            "sha384": self._lib.kCCPRFHmacAlgSHA384,
+            "sha512": self._lib.kCCPRFHmacAlgSHA512,
+        }
+
     def hash_supported(self, algorithm):
         return algorithm.name in self._hash_mapping
 
@@ -120,6 +129,28 @@
         else:
             return _CipherContext(self, cipher, mode, self._lib.kCCDecrypt)
 
+    def pbkdf2_hmac_supported(self, algorithm):
+        return algorithm.name in self._supported_pbkdf2_hmac_algorithms
+
+    def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
+                           key_material):
+        alg_enum = self._supported_pbkdf2_hmac_algorithms[algorithm.name]
+        buf = self._ffi.new("char[]", length)
+        res = self._lib.CCKeyDerivationPBKDF(
+            self._lib.kCCPBKDF2,
+            key_material,
+            len(key_material),
+            salt,
+            len(salt),
+            alg_enum,
+            iterations,
+            buf,
+            length
+        )
+        self._check_response(res)
+
+        return self._ffi.buffer(buf)[:]
+
     def _register_cipher_adapter(self, cipher_cls, cipher_const, mode_cls,
                                  mode_const):
         if (cipher_cls, mode_cls) in self._cipher_registry: