Merge pull request #537 from public/pkcs1-vectors

PKCS#1 vectors
diff --git a/.coveragerc b/.coveragerc
index 20e3224..03fc621 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -3,5 +3,6 @@
 
 [report]
 exclude_lines =
+    pragma: no cover
     @abc.abstractmethod
     @abc.abstractproperty
diff --git a/.travis/install.sh b/.travis/install.sh
index 1e3b170..b6dd5ac 100755
--- a/.travis/install.sh
+++ b/.travis/install.sh
@@ -29,13 +29,11 @@
         py26)
             curl -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py
             sudo python get-pip.py
-            sudo pip install setuptools --no-use-wheel --upgrade
             sudo pip install virtualenv
             ;;
         py27)
             curl -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py
             sudo python get-pip.py
-            sudo pip install setuptools --no-use-wheel --upgrade
             sudo pip install virtualenv
             ;;
         pypy)
diff --git a/.travis/run.sh b/.travis/run.sh
index 4c01d67..6739c88 100755
--- a/.travis/run.sh
+++ b/.travis/run.sh
@@ -10,7 +10,12 @@
         export ARCHFLAGS="-arch x86_64"
         export LDFLAGS="-L/usr/local/opt/openssl/lib"
         export CFLAGS="-I/usr/local/opt/openssl/include"
+        # The Travis OS X jobs are run for two versions
+        # of OpenSSL, but we only need to run the
+        # CommonCrypto backend tests once. Exclude
+        # CommonCrypto when we test against brew OpenSSL
+        export TOX_FLAGS="--backend=openssl"
     fi
 fi
 source ~/.venv/bin/activate
-tox -e $TOX_ENV
+tox -e $TOX_ENV -- $TOX_FLAGS
diff --git a/AUTHORS.rst b/AUTHORS.rst
index ad27cec..0e3979a 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -12,3 +12,5 @@
 * Jarret Raim <jarito@gmail.com>
 * Alex Stapleton <alexs@prol.etari.at> (A1C7 E50B 66DE 39ED C847 9665 8E3C 20D1 9BD9 5C4C)
 * David Reid <dreid@dreid.org> (0F83 CC87 B32F 482B C726  B58A 9FBF D8F4 DA89 6D74)
+* Konstantinos Koukopoulos <koukopoulos@gmail.com> (D6BD 52B6 8C99 A91C E2C8  934D 3300 566B 3A46 726E)
+* Stephen Holsapple <sholsapp@gmail.com>
diff --git a/cryptography/exceptions.py b/cryptography/exceptions.py
index 44363c2..e2542a1 100644
--- a/cryptography/exceptions.py
+++ b/cryptography/exceptions.py
@@ -34,3 +34,11 @@
 
 class InvalidSignature(Exception):
     pass
+
+
+class InternalError(Exception):
+    pass
+
+
+class InvalidKey(Exception):
+    pass
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..e5d4ee0
--- /dev/null
+++ b/cryptography/hazmat/backends/commoncrypto/backend.py
@@ -0,0 +1,482 @@
+# 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, InvalidTag, InternalError
+)
+from cryptography.hazmat.backends.interfaces import (
+    HashBackend, HMACBackend, CipherBackend, PBKDF2HMACBackend
+)
+from cryptography.hazmat.bindings.commoncrypto.binding import Binding
+from cryptography.hazmat.primitives import interfaces, constant_time
+from cryptography.hazmat.primitives.ciphers.algorithms import (
+    AES, Blowfish, TripleDES, ARC4
+)
+from cryptography.hazmat.primitives.ciphers.modes import (
+    CBC, CTR, ECB, OFB, CFB, GCM
+)
+
+
+HashMethods = namedtuple(
+    "HashMethods", ["ctx", "hash_init", "hash_update", "hash_final"]
+)
+
+
+@utils.register_interface(CipherBackend)
+@utils.register_interface(HashBackend)
+@utils.register_interface(HMACBackend)
+@utils.register_interface(PBKDF2HMACBackend)
+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._cipher_registry = {}
+        self._register_default_ciphers()
+        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
+            ),
+        }
+
+        self._supported_hmac_algorithms = {
+            "md5": self._lib.kCCHmacAlgMD5,
+            "sha1": self._lib.kCCHmacAlgSHA1,
+            "sha224": self._lib.kCCHmacAlgSHA224,
+            "sha256": self._lib.kCCHmacAlgSHA256,
+            "sha384": self._lib.kCCHmacAlgSHA384,
+            "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
+
+    def hmac_supported(self, algorithm):
+        return algorithm.name in self._supported_hmac_algorithms
+
+    def create_hash_ctx(self, algorithm):
+        return _HashContext(self, algorithm)
+
+    def create_hmac_ctx(self, key, algorithm):
+        return _HMACContext(self, key, algorithm)
+
+    def cipher_supported(self, cipher, mode):
+        return (type(cipher), type(mode)) in self._cipher_registry
+
+    def create_symmetric_encryption_ctx(self, cipher, mode):
+        if isinstance(mode, GCM):
+            return _GCMCipherContext(
+                self, cipher, mode, self._lib.kCCEncrypt
+            )
+        else:
+            return _CipherContext(self, cipher, mode, self._lib.kCCEncrypt)
+
+    def create_symmetric_decryption_ctx(self, cipher, mode):
+        if isinstance(mode, GCM):
+            return _GCMCipherContext(
+                self, cipher, mode, self._lib.kCCDecrypt
+            )
+        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:
+            raise ValueError("Duplicate registration for: {0} {1}".format(
+                cipher_cls, mode_cls)
+            )
+        self._cipher_registry[cipher_cls, mode_cls] = (cipher_const,
+                                                       mode_const)
+
+    def _register_default_ciphers(self):
+        for mode_cls, mode_const in [
+            (CBC, self._lib.kCCModeCBC),
+            (ECB, self._lib.kCCModeECB),
+            (CFB, self._lib.kCCModeCFB),
+            (OFB, self._lib.kCCModeOFB),
+            (CTR, self._lib.kCCModeCTR),
+            (GCM, self._lib.kCCModeGCM),
+        ]:
+            self._register_cipher_adapter(
+                AES,
+                self._lib.kCCAlgorithmAES128,
+                mode_cls,
+                mode_const
+            )
+        for mode_cls, mode_const in [
+            (CBC, self._lib.kCCModeCBC),
+            (CFB, self._lib.kCCModeCFB),
+            (OFB, self._lib.kCCModeOFB),
+        ]:
+            self._register_cipher_adapter(
+                TripleDES,
+                self._lib.kCCAlgorithm3DES,
+                mode_cls,
+                mode_const
+            )
+        for mode_cls, mode_const in [
+            (CBC, self._lib.kCCModeCBC),
+            (ECB, self._lib.kCCModeECB),
+            (CFB, self._lib.kCCModeCFB),
+            (OFB, self._lib.kCCModeOFB)
+        ]:
+            self._register_cipher_adapter(
+                Blowfish,
+                self._lib.kCCAlgorithmBlowfish,
+                mode_cls,
+                mode_const
+            )
+        self._register_cipher_adapter(
+            ARC4,
+            self._lib.kCCAlgorithmRC4,
+            type(None),
+            self._lib.kCCModeRC4
+        )
+
+    def _check_response(self, response):
+        if response == self._lib.kCCSuccess:
+            return
+        elif response == self._lib.kCCAlignmentError:
+            # This error is not currently triggered due to a bug filed as
+            # rdar://15589470
+            raise ValueError(
+                "The length of the provided data is not a multiple of "
+                "the block length"
+            )
+        else:
+            raise InternalError(
+                "The backend returned an unknown error, consider filing a bug."
+                " Code: {0}.".format(response)
+            )
+
+
+def _release_cipher_ctx(ctx):
+    """
+    Called by the garbage collector and used to safely dereference and
+    release the context.
+    """
+    if ctx[0] != backend._ffi.NULL:
+        res = backend._lib.CCCryptorRelease(ctx[0])
+        backend._check_response(res)
+        ctx[0] = backend._ffi.NULL
+
+
+@utils.register_interface(interfaces.CipherContext)
+class _CipherContext(object):
+    def __init__(self, backend, cipher, mode, operation):
+        self._backend = backend
+        self._cipher = cipher
+        self._mode = mode
+        self._operation = operation
+        # There is a bug in CommonCrypto where block ciphers do not raise
+        # kCCAlignmentError when finalizing if you supply non-block aligned
+        # data. To work around this we need to keep track of the block
+        # alignment ourselves, but only for alg+mode combos that require
+        # block alignment. OFB, CFB, and CTR make a block cipher algorithm
+        # into a stream cipher so we don't need to track them (and thus their
+        # block size is effectively 1 byte just like OpenSSL/CommonCrypto
+        # treat RC4 and other stream cipher block sizes).
+        # This bug has been filed as rdar://15589470
+        self._bytes_processed = 0
+        if (isinstance(cipher, interfaces.BlockCipherAlgorithm) and not
+                isinstance(mode, (OFB, CFB, CTR))):
+            self._byte_block_size = cipher.block_size // 8
+        else:
+            self._byte_block_size = 1
+
+        registry = self._backend._cipher_registry
+        try:
+            cipher_enum, mode_enum = registry[type(cipher), type(mode)]
+        except KeyError:
+            raise UnsupportedAlgorithm(
+                "cipher {0} in {1} mode is not supported "
+                "by this backend".format(
+                    cipher.name, mode.name if mode else mode)
+            )
+
+        ctx = self._backend._ffi.new("CCCryptorRef *")
+        ctx = self._backend._ffi.gc(ctx, _release_cipher_ctx)
+
+        if isinstance(mode, interfaces.ModeWithInitializationVector):
+            iv_nonce = mode.initialization_vector
+        elif isinstance(mode, interfaces.ModeWithNonce):
+            iv_nonce = mode.nonce
+        else:
+            iv_nonce = self._backend._ffi.NULL
+
+        if isinstance(mode, CTR):
+            mode_option = self._backend._lib.kCCModeOptionCTR_BE
+        else:
+            mode_option = 0
+
+        res = self._backend._lib.CCCryptorCreateWithMode(
+            operation,
+            mode_enum, cipher_enum,
+            self._backend._lib.ccNoPadding, iv_nonce,
+            cipher.key, len(cipher.key),
+            self._backend._ffi.NULL, 0, 0, mode_option, ctx)
+        self._backend._check_response(res)
+
+        self._ctx = ctx
+
+    def update(self, data):
+        # Count bytes processed to handle block alignment.
+        self._bytes_processed += len(data)
+        buf = self._backend._ffi.new(
+            "unsigned char[]", len(data) + self._byte_block_size - 1)
+        outlen = self._backend._ffi.new("size_t *")
+        res = self._backend._lib.CCCryptorUpdate(
+            self._ctx[0], data, len(data), buf,
+            len(data) + self._byte_block_size - 1, outlen)
+        self._backend._check_response(res)
+        return self._backend._ffi.buffer(buf)[:outlen[0]]
+
+    def finalize(self):
+        # Raise error if block alignment is wrong.
+        if self._bytes_processed % self._byte_block_size:
+            raise ValueError(
+                "The length of the provided data is not a multiple of "
+                "the block length"
+            )
+        buf = self._backend._ffi.new("unsigned char[]", self._byte_block_size)
+        outlen = self._backend._ffi.new("size_t *")
+        res = self._backend._lib.CCCryptorFinal(
+            self._ctx[0], buf, len(buf), outlen)
+        self._backend._check_response(res)
+        _release_cipher_ctx(self._ctx)
+        return self._backend._ffi.buffer(buf)[:outlen[0]]
+
+
+@utils.register_interface(interfaces.AEADCipherContext)
+@utils.register_interface(interfaces.AEADEncryptionContext)
+class _GCMCipherContext(object):
+    def __init__(self, backend, cipher, mode, operation):
+        self._backend = backend
+        self._cipher = cipher
+        self._mode = mode
+        self._operation = operation
+        self._tag = None
+
+        registry = self._backend._cipher_registry
+        try:
+            cipher_enum, mode_enum = registry[type(cipher), type(mode)]
+        except KeyError:
+            raise UnsupportedAlgorithm(
+                "cipher {0} in {1} mode is not supported "
+                "by this backend".format(
+                    cipher.name, mode.name if mode else mode)
+            )
+
+        ctx = self._backend._ffi.new("CCCryptorRef *")
+        ctx = self._backend._ffi.gc(ctx, _release_cipher_ctx)
+
+        self._ctx = ctx
+
+        res = self._backend._lib.CCCryptorCreateWithMode(
+            operation,
+            mode_enum, cipher_enum,
+            self._backend._lib.ccNoPadding,
+            self._backend._ffi.NULL,
+            cipher.key, len(cipher.key),
+            self._backend._ffi.NULL, 0, 0, 0, self._ctx)
+        self._backend._check_response(res)
+
+        res = self._backend._lib.CCCryptorGCMAddIV(
+            self._ctx[0],
+            mode.initialization_vector,
+            len(mode.initialization_vector)
+        )
+        self._backend._check_response(res)
+
+    def update(self, data):
+        buf = self._backend._ffi.new("unsigned char[]", len(data))
+        args = (self._ctx[0], data, len(data), buf)
+        if self._operation == self._backend._lib.kCCEncrypt:
+            res = self._backend._lib.CCCryptorGCMEncrypt(*args)
+        else:
+            res = self._backend._lib.CCCryptorGCMDecrypt(*args)
+
+        self._backend._check_response(res)
+        return self._backend._ffi.buffer(buf)[:]
+
+    def finalize(self):
+        tag_size = self._cipher.block_size // 8
+        tag_buf = self._backend._ffi.new("unsigned char[]", tag_size)
+        tag_len = self._backend._ffi.new("size_t *", tag_size)
+        res = backend._lib.CCCryptorGCMFinal(self._ctx[0], tag_buf, tag_len)
+        self._backend._check_response(res)
+        _release_cipher_ctx(self._ctx)
+        self._tag = self._backend._ffi.buffer(tag_buf)[:]
+        if (self._operation == self._backend._lib.kCCDecrypt and
+                not constant_time.bytes_eq(
+                    self._tag[:len(self._mode.tag)], self._mode.tag
+                )):
+            raise InvalidTag
+        return b""
+
+    def authenticate_additional_data(self, data):
+        res = self._backend._lib.CCCryptorGCMAddAAD(
+            self._ctx[0], data, len(data)
+        )
+        self._backend._check_response(res)
+
+    @property
+    def tag(self):
+        return self._tag
+
+
+@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)[:]
+
+
+@utils.register_interface(interfaces.HashContext)
+class _HMACContext(object):
+    def __init__(self, backend, key, algorithm, ctx=None):
+        self.algorithm = algorithm
+        self._backend = backend
+        if ctx is None:
+            ctx = self._backend._ffi.new("CCHmacContext *")
+            try:
+                alg = self._backend._supported_hmac_algorithms[algorithm.name]
+            except KeyError:
+                raise UnsupportedAlgorithm(
+                    "{0} is not a supported HMAC hash on this backend".format(
+                        algorithm.name)
+                )
+
+            self._backend._lib.CCHmacInit(ctx, alg, key, len(key))
+
+        self._ctx = ctx
+        self._key = key
+
+    def copy(self):
+        copied_ctx = self._backend._ffi.new("CCHmacContext *")
+        # CommonCrypto has no APIs for copying HMACs, so we have to copy the
+        # underlying struct.
+        copied_ctx[0] = self._ctx[0]
+        return _HMACContext(
+            self._backend, self._key, self.algorithm, ctx=copied_ctx
+        )
+
+    def update(self, data):
+        self._backend._lib.CCHmacUpdate(self._ctx, data, len(data))
+
+    def finalize(self):
+        buf = self._backend._ffi.new("unsigned char[]",
+                                     self.algorithm.digest_size)
+        self._backend._lib.CCHmacFinal(self._ctx, buf)
+        return self._backend._ffi.buffer(buf)[:]
+
+
+backend = Backend()
diff --git a/cryptography/hazmat/backends/interfaces.py b/cryptography/hazmat/backends/interfaces.py
index 9a57096..53c7518 100644
--- a/cryptography/hazmat/backends/interfaces.py
+++ b/cryptography/hazmat/backends/interfaces.py
@@ -26,12 +26,6 @@
         """
 
     @abc.abstractmethod
-    def register_cipher_adapter(self, cipher, mode, adapter):
-        """
-        Register an adapter for a cipher and mode to a backend specific object.
-        """
-
-    @abc.abstractmethod
     def create_symmetric_encryption_ctx(self, cipher, mode):
         """
         Get a CipherContext that can be used for encryption.
@@ -71,3 +65,19 @@
         """
         Create a HashContext for calculating a message authentication code.
         """
+
+
+class PBKDF2HMACBackend(six.with_metaclass(abc.ABCMeta)):
+    @abc.abstractmethod
+    def pbkdf2_hmac_supported(self, algorithm):
+        """
+        Return True if the hash algorithm is supported for PBKDF2 by this
+        backend.
+        """
+
+    @abc.abstractmethod
+    def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
+                           key_material):
+        """
+        Return length bytes derived from provided PBKDF2 parameters.
+        """
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index 07ee58c..cf931da 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -16,11 +16,13 @@
 import itertools
 
 from cryptography import utils
-from cryptography.exceptions import UnsupportedAlgorithm, InvalidTag
-from cryptography.hazmat.backends.interfaces import (
-    CipherBackend, HashBackend, HMACBackend
+from cryptography.exceptions import (
+    UnsupportedAlgorithm, InvalidTag, InternalError
 )
-from cryptography.hazmat.primitives import interfaces
+from cryptography.hazmat.backends.interfaces import (
+    CipherBackend, HashBackend, HMACBackend, PBKDF2HMACBackend
+)
+from cryptography.hazmat.primitives import interfaces, hashes
 from cryptography.hazmat.primitives.ciphers.algorithms import (
     AES, Blowfish, Camellia, TripleDES, ARC4,
 )
@@ -33,16 +35,20 @@
 @utils.register_interface(CipherBackend)
 @utils.register_interface(HashBackend)
 @utils.register_interface(HMACBackend)
+@utils.register_interface(PBKDF2HMACBackend)
 class Backend(object):
     """
     OpenSSL API binding interfaces.
     """
+    name = "openssl"
 
     def __init__(self):
         self._binding = Binding()
         self._ffi = self._binding.ffi
         self._lib = self._binding.lib
 
+        self._binding.init_static_locks()
+
         # adds all ciphers/digests for EVP
         self._lib.OpenSSL_add_all_algorithms()
         # registers available SSL/TLS ciphers and digests
@@ -128,6 +134,49 @@
     def create_symmetric_decryption_ctx(self, cipher, mode):
         return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT)
 
+    def pbkdf2_hmac_supported(self, algorithm):
+        if self._lib.Cryptography_HAS_PBKDF2_HMAC:
+            return self.hmac_supported(algorithm)
+        else:
+            # OpenSSL < 1.0.0 has an explicit PBKDF2-HMAC-SHA1 function,
+            # so if the PBKDF2_HMAC function is missing we only support
+            # SHA1 via PBKDF2_HMAC_SHA1.
+            return isinstance(algorithm, hashes.SHA1)
+
+    def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
+                           key_material):
+        buf = self._ffi.new("char[]", length)
+        if self._lib.Cryptography_HAS_PBKDF2_HMAC:
+            evp_md = self._lib.EVP_get_digestbyname(
+                algorithm.name.encode("ascii"))
+            assert evp_md != self._ffi.NULL
+            res = self._lib.PKCS5_PBKDF2_HMAC(
+                key_material,
+                len(key_material),
+                salt,
+                len(salt),
+                iterations,
+                evp_md,
+                length,
+                buf
+            )
+            assert res == 1
+        else:
+            # OpenSSL < 1.0.0
+            assert isinstance(algorithm, hashes.SHA1)
+            res = self._lib.PKCS5_PBKDF2_HMAC_SHA1(
+                key_material,
+                len(key_material),
+                salt,
+                len(salt),
+                iterations,
+                length,
+                buf
+            )
+            assert res == 1
+
+        return self._ffi.buffer(buf)[:]
+
     def _handle_error(self, mode):
         code = self._lib.ERR_get_error()
         if not code and isinstance(mode, GCM):
@@ -153,7 +202,7 @@
                         "the block length"
                     )
 
-        raise SystemError(
+        raise InternalError(
             "Unknown error code from OpenSSL, you should probably file a bug."
         )
 
diff --git a/cryptography/hazmat/bindings/commoncrypto/binding.py b/cryptography/hazmat/bindings/commoncrypto/binding.py
index 9c1af40..45c0eaa 100644
--- a/cryptography/hazmat/bindings/commoncrypto/binding.py
+++ b/cryptography/hazmat/bindings/commoncrypto/binding.py
@@ -26,6 +26,8 @@
     _modules = [
         "common_digest",
         "common_hmac",
+        "common_key_derivation",
+        "common_cryptor",
     ]
 
     ffi = None
diff --git a/cryptography/hazmat/bindings/commoncrypto/common_cryptor.py b/cryptography/hazmat/bindings/commoncrypto/common_cryptor.py
new file mode 100644
index 0000000..8f03bc3
--- /dev/null
+++ b/cryptography/hazmat/bindings/commoncrypto/common_cryptor.py
@@ -0,0 +1,108 @@
+# 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.
+
+INCLUDES = """
+#include <CommonCrypto/CommonCryptor.h>
+"""
+
+TYPES = """
+enum {
+    kCCAlgorithmAES128 = 0,
+    kCCAlgorithmDES,
+    kCCAlgorithm3DES,
+    kCCAlgorithmCAST,
+    kCCAlgorithmRC4,
+    kCCAlgorithmRC2,
+    kCCAlgorithmBlowfish
+};
+typedef uint32_t CCAlgorithm;
+enum {
+    kCCSuccess = 0,
+    kCCParamError = -4300,
+    kCCBufferTooSmall = -4301,
+    kCCMemoryFailure = -4302,
+    kCCAlignmentError = -4303,
+    kCCDecodeError = -4304,
+    kCCUnimplemented = -4305
+};
+typedef int32_t CCCryptorStatus;
+typedef uint32_t CCOptions;
+enum {
+    kCCEncrypt = 0,
+    kCCDecrypt,
+};
+typedef uint32_t CCOperation;
+typedef ... *CCCryptorRef;
+
+enum {
+    kCCModeOptionCTR_LE = 0x0001,
+    kCCModeOptionCTR_BE = 0x0002
+};
+
+typedef uint32_t CCModeOptions;
+
+enum {
+    kCCModeECB = 1,
+    kCCModeCBC = 2,
+    kCCModeCFB = 3,
+    kCCModeCTR = 4,
+    kCCModeF8 = 5,
+    kCCModeLRW = 6,
+    kCCModeOFB = 7,
+    kCCModeXTS = 8,
+    kCCModeRC4 = 9,
+    kCCModeCFB8 = 10,
+    kCCModeGCM = 11
+};
+typedef uint32_t CCMode;
+enum {
+    ccNoPadding = 0,
+    ccPKCS7Padding = 1,
+};
+typedef uint32_t CCPadding;
+"""
+
+FUNCTIONS = """
+CCCryptorStatus CCCryptorCreateWithMode(CCOperation, CCMode, CCAlgorithm,
+                                        CCPadding, const void *, const void *,
+                                        size_t, const void *, size_t, int,
+                                        CCModeOptions, CCCryptorRef *);
+CCCryptorStatus CCCryptorCreate(CCOperation, CCAlgorithm, CCOptions,
+                                const void *, size_t, const void *,
+                                CCCryptorRef *);
+CCCryptorStatus CCCryptorUpdate(CCCryptorRef, const void *, size_t, void *,
+                                size_t, size_t *);
+CCCryptorStatus CCCryptorFinal(CCCryptorRef, void *, size_t, size_t *);
+CCCryptorStatus CCCryptorRelease(CCCryptorRef);
+
+CCCryptorStatus CCCryptorGCMAddIV(CCCryptorRef, const void *, size_t);
+CCCryptorStatus CCCryptorGCMAddAAD(CCCryptorRef, const void *, size_t);
+CCCryptorStatus CCCryptorGCMEncrypt(CCCryptorRef, const void *, size_t,
+                                    void *);
+CCCryptorStatus CCCryptorGCMDecrypt(CCCryptorRef, const void *, size_t,
+                                    void *);
+CCCryptorStatus CCCryptorGCMFinal(CCCryptorRef, const void *, size_t *);
+CCCryptorStatus CCCryptorGCMReset(CCCryptorRef);
+"""
+
+MACROS = """
+"""
+
+CUSTOMIZATIONS = """
+// Not defined in the public header
+enum {
+    kCCModeGCM = 11
+};
+"""
+
+CONDITIONAL_NAMES = {}
diff --git a/cryptography/hazmat/bindings/commoncrypto/common_key_derivation.py b/cryptography/hazmat/bindings/commoncrypto/common_key_derivation.py
new file mode 100644
index 0000000..85def1e
--- /dev/null
+++ b/cryptography/hazmat/bindings/commoncrypto/common_key_derivation.py
@@ -0,0 +1,48 @@
+# 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.
+
+INCLUDES = """
+#include <CommonCrypto/CommonKeyDerivation.h>
+"""
+
+TYPES = """
+enum {
+    kCCPBKDF2 = 2,
+};
+typedef uint32_t CCPBKDFAlgorithm;
+enum {
+    kCCPRFHmacAlgSHA1 = 1,
+    kCCPRFHmacAlgSHA224 = 2,
+    kCCPRFHmacAlgSHA256 = 3,
+    kCCPRFHmacAlgSHA384 = 4,
+    kCCPRFHmacAlgSHA512 = 5,
+};
+typedef uint32_t CCPseudoRandomAlgorithm;
+typedef unsigned int uint;
+"""
+
+FUNCTIONS = """
+int CCKeyDerivationPBKDF(CCPBKDFAlgorithm, const char *, size_t,
+                         const uint8_t *, size_t, CCPseudoRandomAlgorithm,
+                         uint, uint8_t *, size_t);
+uint CCCalibratePBKDF(CCPBKDFAlgorithm, size_t, size_t,
+                      CCPseudoRandomAlgorithm, size_t, uint32_t);
+"""
+
+MACROS = """
+"""
+
+CUSTOMIZATIONS = """
+"""
+
+CONDITIONAL_NAMES = {}
diff --git a/cryptography/hazmat/bindings/openssl/aes.py b/cryptography/hazmat/bindings/openssl/aes.py
new file mode 100644
index 0000000..6cbcd57
--- /dev/null
+++ b/cryptography/hazmat/bindings/openssl/aes.py
@@ -0,0 +1,40 @@
+# 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.
+
+INCLUDES = """
+#include <openssl/aes.h>
+"""
+
+TYPES = """
+struct aes_key_st {
+    ...;
+};
+typedef struct aes_key_st AES_KEY;
+"""
+
+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 *);
+int AES_wrap_key(AES_KEY *, const unsigned char *, unsigned char *,
+                 const unsigned char *, unsigned int);
+int AES_unwrap_key(AES_KEY *, const unsigned char *, unsigned char *,
+                   const unsigned char *, unsigned int);
+"""
+
+MACROS = """
+"""
+
+CUSTOMIZATIONS = """
+"""
+
+CONDITIONAL_NAMES = {}
diff --git a/cryptography/hazmat/bindings/openssl/bignum.py b/cryptography/hazmat/bindings/openssl/bignum.py
index 59efd17..6545f32 100644
--- a/cryptography/hazmat/bindings/openssl/bignum.py
+++ b/cryptography/hazmat/bindings/openssl/bignum.py
@@ -47,6 +47,9 @@
 int BN_hex2bn(BIGNUM **, const char *);
 int BN_dec2bn(BIGNUM **, const char *);
 
+int BN_bn2bin(const BIGNUM *, unsigned char *);
+BIGNUM *BN_bin2bn(const unsigned char *, int, BIGNUM *);
+
 int BN_num_bits(const BIGNUM *);
 """
 
diff --git a/cryptography/hazmat/bindings/openssl/binding.py b/cryptography/hazmat/bindings/openssl/binding.py
index 88299d1..cde3bdb 100644
--- a/cryptography/hazmat/bindings/openssl/binding.py
+++ b/cryptography/hazmat/bindings/openssl/binding.py
@@ -13,6 +13,9 @@
 
 from __future__ import absolute_import, division, print_function
 
+import sys
+import threading
+
 from cryptography.hazmat.bindings.utils import build_ffi
 
 
@@ -41,6 +44,7 @@
     """
     _module_prefix = "cryptography.hazmat.bindings.openssl."
     _modules = [
+        "aes",
         "asn1",
         "bignum",
         "bio",
@@ -67,6 +71,10 @@
         "x509v3",
     ]
 
+    _locks = None
+    _lock_cb_handle = None
+    _lock_init_lock = threading.Lock()
+
     ffi = None
     lib = None
 
@@ -78,11 +86,57 @@
         if cls.ffi is not None and cls.lib is not None:
             return
 
+        # platform check to set the right library names
+        if sys.platform != "win32":
+            libraries = ["crypto", "ssl"]
+        else:  # pragma: no cover
+            libraries = ["libeay32", "ssleay32"]
+
         cls.ffi, cls.lib = build_ffi(cls._module_prefix, cls._modules,
                                      _OSX_PRE_INCLUDE, _OSX_POST_INCLUDE,
-                                     ["crypto", "ssl"])
+                                     libraries)
 
     @classmethod
     def is_available(cls):
         # OpenSSL is the only binding so for now it must always be available
         return True
+
+    @classmethod
+    def init_static_locks(cls):
+        with cls._lock_init_lock:
+            cls._ensure_ffi_initialized()
+
+            if not cls._lock_cb_handle:
+                cls._lock_cb_handle = cls.ffi.callback(
+                    "void(int, int, const char *, int)",
+                    cls._lock_cb
+                )
+
+            # use Python's implementation if available
+
+            __import__("_ssl")
+
+            if cls.lib.CRYPTO_get_locking_callback() != cls.ffi.NULL:
+                return
+
+            # otherwise setup our version
+
+            num_locks = cls.lib.CRYPTO_num_locks()
+            cls._locks = [threading.Lock() for n in range(num_locks)]
+
+            cls.lib.CRYPTO_set_locking_callback(cls._lock_cb_handle)
+
+    @classmethod
+    def _lock_cb(cls, mode, n, file, line):
+        lock = cls._locks[n]
+
+        if mode & cls.lib.CRYPTO_LOCK:
+            lock.acquire()
+        elif mode & cls.lib.CRYPTO_UNLOCK:
+            lock.release()
+        else:
+            raise RuntimeError(
+                "Unknown lock mode {0}: lock={1}, file={2}, line={3}".format(
+                    mode, n, file, line
+                )
+            )
diff --git a/cryptography/hazmat/bindings/openssl/crypto.py b/cryptography/hazmat/bindings/openssl/crypto.py
index 40d91bf..81d13b7 100644
--- a/cryptography/hazmat/bindings/openssl/crypto.py
+++ b/cryptography/hazmat/bindings/openssl/crypto.py
@@ -27,6 +27,11 @@
 static const int CRYPTO_MEM_CHECK_OFF;
 static const int CRYPTO_MEM_CHECK_ENABLE;
 static const int CRYPTO_MEM_CHECK_DISABLE;
+static const int CRYPTO_LOCK;
+static const int CRYPTO_UNLOCK;
+static const int CRYPTO_READ;
+static const int CRYPTO_WRITE;
+static const int CRYPTO_LOCK_SSL;
 """
 
 FUNCTIONS = """
@@ -43,6 +48,7 @@
 void CRYPTO_set_id_callback(unsigned long (*)(void));
 unsigned long (*CRYPTO_get_id_callback(void))(void);
 void (*CRYPTO_get_locking_callback(void))(int, int, const char *, int);
+void CRYPTO_lock(int, int, const char *, int);
 
 void OPENSSL_free(void *);
 """
@@ -51,7 +57,6 @@
 void CRYPTO_add(int *, int, int);
 void CRYPTO_malloc_init(void);
 void CRYPTO_malloc_debug_init(void);
-
 """
 
 CUSTOMIZATIONS = """
diff --git a/cryptography/hazmat/bindings/openssl/dh.py b/cryptography/hazmat/bindings/openssl/dh.py
index 3c12fbc..ecc62e9 100644
--- a/cryptography/hazmat/bindings/openssl/dh.py
+++ b/cryptography/hazmat/bindings/openssl/dh.py
@@ -16,7 +16,17 @@
 """
 
 TYPES = """
-typedef ... DH;
+typedef struct dh_st {
+    // prime number (shared)
+    BIGNUM *p;
+    // generator of Z_p (shared)
+    BIGNUM *g;
+    // private DH value x
+    BIGNUM *priv_key;
+    // public DH value g^x
+    BIGNUM *pub_key;
+    ...;
+} DH;
 """
 
 FUNCTIONS = """
diff --git a/cryptography/hazmat/bindings/openssl/dsa.py b/cryptography/hazmat/bindings/openssl/dsa.py
index 3b77d7a..609a33b 100644
--- a/cryptography/hazmat/bindings/openssl/dsa.py
+++ b/cryptography/hazmat/bindings/openssl/dsa.py
@@ -16,7 +16,19 @@
 """
 
 TYPES = """
-typedef ... DSA;
+typedef struct dsa_st {
+    // prime number (public)
+    BIGNUM *p;
+    // 160-bit subprime, q | p-1 (public)
+    BIGNUM *q;
+    // generator of subgroup (public)
+    BIGNUM *g;
+    // private key x
+    BIGNUM *priv_key;
+    // public key y = g^x
+    BIGNUM *pub_key;
+    ...;
+} DSA;
 """
 
 FUNCTIONS = """
diff --git a/cryptography/hazmat/bindings/openssl/ec.py b/cryptography/hazmat/bindings/openssl/ec.py
index 9f10365..39403ff 100644
--- a/cryptography/hazmat/bindings/openssl/ec.py
+++ b/cryptography/hazmat/bindings/openssl/ec.py
@@ -12,7 +12,10 @@
 # limitations under the License.
 
 INCLUDES = """
+#ifndef OPENSSL_NO_EC
 #include <openssl/ec.h>
+#endif
+
 #include <openssl/obj_mac.h>
 """
 
@@ -31,16 +34,17 @@
 """
 
 FUNCTIONS = """
-EC_KEY *EC_KEY_new_by_curve_name(int);
-void EC_KEY_free(EC_KEY *);
 """
 
 MACROS = """
+EC_KEY *EC_KEY_new_by_curve_name(int);
+void EC_KEY_free(EC_KEY *);
 """
 
 CUSTOMIZATIONS = """
 #ifdef OPENSSL_NO_EC
 static const long Cryptography_HAS_EC = 0;
+typedef void EC_KEY;
 EC_KEY* (*EC_KEY_new_by_curve_name)(int) = NULL;
 void (*EC_KEY_free)(EC_KEY *) = NULL;
 #else
diff --git a/cryptography/hazmat/bindings/openssl/evp.py b/cryptography/hazmat/bindings/openssl/evp.py
index c426e52..c7cc154 100644
--- a/cryptography/hazmat/bindings/openssl/evp.py
+++ b/cryptography/hazmat/bindings/openssl/evp.py
@@ -40,6 +40,7 @@
 static const int EVP_CTRL_GCM_SET_TAG;
 
 static const int Cryptography_HAS_GCM;
+static const int Cryptography_HAS_PBKDF2_HMAC;
 """
 
 FUNCTIONS = """
@@ -95,6 +96,9 @@
                     EVP_PKEY *);
 
 const EVP_MD *EVP_md5(void);
+
+int PKCS5_PBKDF2_HMAC_SHA1(const char *, int, const unsigned char *, int, int,
+                           int, unsigned char *);
 """
 
 MACROS = """
@@ -103,6 +107,9 @@
 int EVP_PKEY_assign_DSA(EVP_PKEY *, DSA *);
 int EVP_CIPHER_CTX_block_size(const EVP_CIPHER_CTX *);
 int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *, int, int, void *);
+
+int PKCS5_PBKDF2_HMAC(const char *, int, const unsigned char *, int, int,
+                      const EVP_MD *, int, unsigned char *);
 """
 
 CUSTOMIZATIONS = """
@@ -114,6 +121,13 @@
 const long EVP_CTRL_GCM_SET_TAG = -1;
 const long EVP_CTRL_GCM_SET_IVLEN = -1;
 #endif
+#if OPENSSL_VERSION_NUMBER >= 0x10000000
+const long Cryptography_HAS_PBKDF2_HMAC = 1;
+#else
+const long Cryptography_HAS_PBKDF2_HMAC = 0;
+int (*PKCS5_PBKDF2_HMAC)(const char *, int, const unsigned char *, int, int,
+                         const EVP_MD *, int, unsigned char *) = NULL;
+#endif
 """
 
 CONDITIONAL_NAMES = {
@@ -121,5 +135,8 @@
         "EVP_CTRL_GCM_GET_TAG",
         "EVP_CTRL_GCM_SET_TAG",
         "EVP_CTRL_GCM_SET_IVLEN",
+    ],
+    "Cryptography_HAS_PBKDF2_HMAC": [
+        "PKCS5_PBKDF2_HMAC"
     ]
 }
diff --git a/cryptography/hazmat/bindings/openssl/ssl.py b/cryptography/hazmat/bindings/openssl/ssl.py
index cd872d1..2b4e54f 100644
--- a/cryptography/hazmat/bindings/openssl/ssl.py
+++ b/cryptography/hazmat/bindings/openssl/ssl.py
@@ -393,6 +393,6 @@
     ],
 
     "Cryptography_HAS_EC": [
-        "EC_KEY_new_by_curve_name",
+        "SSL_CTX_set_tmp_ecdh",
     ]
 }
diff --git a/cryptography/hazmat/bindings/openssl/x509.py b/cryptography/hazmat/bindings/openssl/x509.py
index 840254a..e4021a1 100644
--- a/cryptography/hazmat/bindings/openssl/x509.py
+++ b/cryptography/hazmat/bindings/openssl/x509.py
@@ -119,6 +119,7 @@
 int X509_REQ_verify(X509_REQ *, EVP_PKEY *);
 EVP_PKEY *X509_REQ_get_pubkey(X509_REQ *);
 int X509_REQ_add_extensions(X509_REQ *, X509_EXTENSIONS *);
+X509_EXTENSIONS *X509_REQ_get_extensions(X509_REQ *);
 int X509_REQ_print_ex(BIO *, X509_REQ *, unsigned long, unsigned long);
 
 int X509V3_EXT_print(BIO *, X509_EXTENSION *, unsigned long, int);
@@ -165,6 +166,7 @@
 X509_STORE *X509_STORE_new(void);
 void X509_STORE_free(X509_STORE *);
 int X509_STORE_add_cert(X509_STORE *, X509 *);
+int X509_verify_cert(X509_STORE_CTX *);
 """
 
 MACROS = """
diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py
index 7a6bf3e..1a27644 100644
--- a/cryptography/hazmat/primitives/interfaces.py
+++ b/cryptography/hazmat/primitives/interfaces.py
@@ -169,3 +169,107 @@
         """
         Return a HashContext that is a copy of the current context.
         """
+
+
+class RSAPrivateKey(six.with_metaclass(abc.ABCMeta)):
+    @abc.abstractproperty
+    def modulus(self):
+        """
+        The public modulus of the RSA key.
+        """
+
+    @abc.abstractproperty
+    def public_exponent(self):
+        """
+        The public exponent of the RSA key.
+        """
+
+    @abc.abstractproperty
+    def key_length(self):
+        """
+        The bit length of the public modulus.
+        """
+
+    @abc.abstractmethod
+    def public_key(self):
+        """
+        The RSAPublicKey associated with this private key.
+        """
+
+    @abc.abstractproperty
+    def n(self):
+        """
+        The public modulus of the RSA key. Alias for modulus.
+        """
+
+    @abc.abstractproperty
+    def p(self):
+        """
+        One of the two primes used to generate d.
+        """
+
+    @abc.abstractproperty
+    def q(self):
+        """
+        One of the two primes used to generate d.
+        """
+
+    @abc.abstractproperty
+    def d(self):
+        """
+        The private exponent. This can be calculated using p and q.
+        """
+
+    @abc.abstractproperty
+    def e(self):
+        """
+        The public exponent of the RSA key. Alias for public_exponent.
+        """
+
+
+class RSAPublicKey(six.with_metaclass(abc.ABCMeta)):
+    @abc.abstractproperty
+    def modulus(self):
+        """
+        The public modulus of the RSA key.
+        """
+
+    @abc.abstractproperty
+    def public_exponent(self):
+        """
+        The public exponent of the RSA key.
+        """
+
+    @abc.abstractproperty
+    def key_length(self):
+        """
+        The bit length of the public modulus.
+        """
+
+    @abc.abstractproperty
+    def n(self):
+        """
+        The public modulus of the RSA key. Alias for modulus.
+        """
+
+    @abc.abstractproperty
+    def e(self):
+        """
+        The public exponent of the RSA key. Alias for public_exponent.
+        """
+
+
+class KeyDerivationFunction(six.with_metaclass(abc.ABCMeta)):
+    @abc.abstractmethod
+    def derive(self, key_material):
+        """
+        Deterministically generates and returns a new key based on the existing
+        key material.
+        """
+
+    @abc.abstractmethod
+    def verify(self, key_material, expected_key):
+        """
+        Checks whether the key generated by the key material matches the
+        expected derived key. Raises an exception if they do not match.
+        """
diff --git a/cryptography/hazmat/primitives/kdf/__init__.py b/cryptography/hazmat/primitives/kdf/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cryptography/hazmat/primitives/kdf/__init__.py
diff --git a/cryptography/hazmat/primitives/kdf/pbkdf2.py b/cryptography/hazmat/primitives/kdf/pbkdf2.py
new file mode 100644
index 0000000..71b8821
--- /dev/null
+++ b/cryptography/hazmat/primitives/kdf/pbkdf2.py
@@ -0,0 +1,66 @@
+# 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
+
+import six
+
+from cryptography import utils
+from cryptography.exceptions import (
+    InvalidKey, UnsupportedAlgorithm, AlreadyFinalized
+)
+from cryptography.hazmat.primitives import constant_time, interfaces
+
+
+@utils.register_interface(interfaces.KeyDerivationFunction)
+class PBKDF2HMAC(object):
+    def __init__(self, algorithm, length, salt, iterations, backend):
+        if not backend.pbkdf2_hmac_supported(algorithm):
+            raise UnsupportedAlgorithm(
+                "{0} is not supported for PBKDF2 by this backend".format(
+                    algorithm.name)
+            )
+        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."
+            )
+        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")
+        self._used = True
+
+        if isinstance(key_material, six.text_type):
+            raise TypeError(
+                "Unicode-objects must be encoded before using them as key "
+                "material."
+            )
+        return self._backend.derive_pbkdf2_hmac(
+            self._algorithm,
+            self._length,
+            self._salt,
+            self._iterations,
+            key_material
+        )
+
+    def verify(self, key_material, expected_key):
+        derived_key = self.derive(key_material)
+        if not constant_time.bytes_eq(derived_key, expected_key):
+            raise InvalidKey("Keys do not match.")
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 289992f..f401fe7 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -6,9 +6,18 @@
 
 **In development**
 
-* Added initial CommonCrypto bindings.
+* Added :doc:`/hazmat/backends/commoncrypto`.
+* Added initial :doc:`/hazmat/bindings/commoncrypto`.
+* Removed ``register_cipher_adapter`` method from
+  :class:`~cryptography.hazmat.backends.interfaces.CipherBackend`.
+* Added support for the OpenSSL backend under Windows.
+* Improved thread-safety for the OpenSSL backend.
+* Fixed compilation on systems where OpenSSL's ``ec.h`` header is not
+  available, such as CentOS.
+* Added :class:`~cryptography.hazmat.primitives.kdf.pbkdf2.PBKDF2HMAC`.
 
 0.1 - 2014-01-08
 ~~~~~~~~~~~~~~~~
 
 * Initial release.
+
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 8e32c36..184ba21 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -60,6 +60,12 @@
 philosophy: "make it hard to do insecure things". Here are a few strategies for
 API design which should be both followed, and should inspire other API choices:
 
+If it is necessary to compare a user provided value with a computed value (for
+example, verifying a signature), there should be an API provided which performs
+the verification in a secure way (for example, using a constant time
+comparison), rather than requiring the user to perform the comparison
+themselves.
+
 If it is incorrect to ignore the result of a method, it should raise an
 exception, and not return a boolean ``True``/``False`` flag. For example, a
 method to verify a signature should raise ``InvalidSignature``, and not return
@@ -250,6 +256,16 @@
 You may not have all the required Python versions installed, in which case you
 will see one or more ``InterpreterNotFound`` errors.
 
+
+Explicit Backend Selection
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+While testing you may want to run tests against a subset of the backends that
+cryptography supports. Explicit backend selection can be done via the
+``--backend`` flag. This flag should be passed to ``py.test`` with a comma
+delimited list of backend names. To use it with ``tox`` you must pass it as
+``tox -- --backend=openssl``.
+
 Building Documentation
 ~~~~~~~~~~~~~~~~~~~~~~
 
@@ -277,5 +293,5 @@
 .. _`tox`: https://pypi.python.org/pypi/tox
 .. _`virtualenv`: https://pypi.python.org/pypi/virtualenv
 .. _`pip`: https://pypi.python.org/pypi/pip
-.. _`sphinx`: https://pypi.python.org/pypi/sphinx
+.. _`sphinx`: https://pypi.python.org/pypi/Sphinx
 .. _`reStructured Text`: http://sphinx-doc.org/rest.html
diff --git a/docs/exceptions.rst b/docs/exceptions.rst
index 1fbd326..1e31e31 100644
--- a/docs/exceptions.rst
+++ b/docs/exceptions.rst
@@ -10,8 +10,8 @@
 
 .. class:: InvalidSignature
 
-    This is raised when the verify method of a hash context does not
-    compare equal.
+    This is raised when the verify method of a hash context's computed digest
+    does not match the expected digest.
 
 
 .. class:: NotYetFinalized
@@ -30,3 +30,9 @@
 
     This is raised when a backend doesn't support the requested algorithm (or
     combination of algorithms).
+
+
+.. class:: InvalidKey
+
+    This is raised when the verify method of a key derivation function's
+    computed key does not match the expected key.
diff --git a/docs/fernet.rst b/docs/fernet.rst
index 13295c0..b0215e3 100644
--- a/docs/fernet.rst
+++ b/docs/fernet.rst
@@ -72,5 +72,22 @@
 
     See :meth:`Fernet.decrypt` for more information.
 
+Implementation
+--------------
+
+Fernet is built on top of a number of standard cryptographic primitives.
+Specifically it uses:
+
+* :class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES` in
+  :class:`~cryptography.hazmat.primitives.ciphers.modes.CBC` mode with a
+  128-bit key for encryption; using
+  :class:`~cryptography.hazmat.primitives.ciphers.PKCS7` padding.
+* :class:`~cryptography.hazmat.primitives.hmac.HMAC` using
+  :class:`~cryptography.hazmat.primitives.hashes.SHA256` for authentication.
+* Initialization vectors are generated using ``os.urandom()``.
+
+For complete details consult the `specification`_.
+
 
 .. _`Fernet`: https://github.com/fernet/spec/
+.. _`specification`: https://github.com/fernet/spec/blob/master/Spec.md
diff --git a/docs/hazmat/backends/commoncrypto.rst b/docs/hazmat/backends/commoncrypto.rst
new file mode 100644
index 0000000..af2032b
--- /dev/null
+++ b/docs/hazmat/backends/commoncrypto.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..dbc0724 100644
--- a/docs/hazmat/backends/index.rst
+++ b/docs/hazmat/backends/index.rst
@@ -31,4 +31,5 @@
     :maxdepth: 1
 
     openssl
+    commoncrypto
     interfaces
diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst
index 5b6cd64..49e4c88 100644
--- a/docs/hazmat/backends/interfaces.rst
+++ b/docs/hazmat/backends/interfaces.rst
@@ -33,30 +33,11 @@
         :returns: ``True`` if the specified ``cipher`` and ``mode`` combination
             is supported by this backend, otherwise ``False``
 
-    .. method:: register_cipher_adapter(cipher_cls, mode_cls, adapter)
-
-        Register an adapter which can be used to create a backend specific
-        object from instances of the
-        :class:`~cryptography.hazmat.primitives.interfaces.CipherAlgorithm` and
-        the :class:`~cryptography.hazmat.primitives.interfaces.Mode` primitives.
-
-        :param cipher_cls: A class whose instances provide
-            :class:`~cryptography.hazmat.primitives.interfaces.CipherAlgorithm`
-        :param mode_cls: A class whose instances provide:
-            :class:`~cryptography.hazmat.primitives.interfaces.Mode`
-        :param adapter: A ``function`` that takes 3 arguments, ``backend`` (a
-            :class:`CipherBackend` provider), ``cipher`` (a
-            :class:`~cryptography.hazmat.primitives.interfaces.CipherAlgorithm`
-            provider ), and ``mode`` (a
-            :class:`~cryptography.hazmat.primitives.interfaces.Mode` provider).
-            It returns a backend specific object which may be used to construct
-            a :class:`~cryptogrpahy.hazmat.primitives.interfaces.CipherContext`.
-
 
     .. method:: create_symmetric_encryption_ctx(cipher, mode)
 
         Create a
-        :class:`~cryptogrpahy.hazmat.primitives.interfaces.CipherContext` that
+        :class:`~cryptography.hazmat.primitives.interfaces.CipherContext` that
         can be used for encrypting data with the symmetric ``cipher`` using
         the given ``mode``.
 
@@ -75,7 +56,7 @@
     .. method:: create_symmetric_decryption_ctx(cipher, mode)
 
         Create a
-        :class:`~cryptogrpahy.hazmat.primitives.interfaces.CipherContext` that
+        :class:`~cryptography.hazmat.primitives.interfaces.CipherContext` that
         can be used for decrypting data with the symmetric ``cipher`` using
         the given ``mode``.
 
@@ -110,7 +91,7 @@
     .. method:: create_hash_ctx(algorithm)
 
         Create a
-        :class:`~cryptogrpahy.hazmat.primitives.interfaces.HashContext` that
+        :class:`~cryptography.hazmat.primitives.interfaces.HashContext` that
         uses the specified ``algorithm`` to calculate a message digest.
 
         :param algorithm: An instance of a
@@ -140,7 +121,7 @@
     .. method:: create_hmac_ctx(algorithm)
 
         Create a
-        :class:`~cryptogrpahy.hazmat.primitives.interfaces.HashContext` that
+        :class:`~cryptography.hazmat.primitives.interfaces.HashContext` that
         uses the specified ``algorithm`` to calculate a hash-based message
         authentication code.
 
@@ -150,3 +131,44 @@
 
         :returns:
             :class:`~cryptography.hazmat.primitives.interfaces.HashContext`
+
+
+.. class:: PBKDF2HMACBackend
+
+    .. versionadded:: 0.2
+
+    A backend with methods for using PBKDF2 using HMAC as a PRF.
+
+    .. method:: pbkdf2_hmac_supported(algorithm)
+
+        Check if the specified ``algorithm`` is supported by this backend.
+
+        :param algorithm: An instance of a
+            :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+            provider.
+
+        :returns: ``True`` if the specified ``algorithm`` is supported for
+            PBKDF2 HMAC by this backend, otherwise ``False``.
+
+    .. method:: derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
+                                   key_material)
+
+        :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
+            (2\ :sup:`32` - 1) * ``algorithm.digest_size``
+
+        :param bytes salt: A salt.
+
+        :param int iterations: The number of iterations to perform of the hash
+            function. This can be used to control the length of time the
+            operation takes. Higher numbers help mitigate brute force attacks
+            against derived keys.
+
+        :param bytes key_material: The key material to use as a basis for
+            the derived key. This is typically a password.
+
+        :return bytes: Derived key.
+
diff --git a/docs/hazmat/backends/openssl.rst b/docs/hazmat/backends/openssl.rst
index 404573a..12d2d9f 100644
--- a/docs/hazmat/backends/openssl.rst
+++ b/docs/hazmat/backends/openssl.rst
@@ -7,33 +7,10 @@
 
 .. data:: cryptography.hazmat.backends.openssl.backend
 
-    This is the exposed API for the OpenSSL backend. It has no public attributes.    
+    This is the exposed API for the OpenSSL backend. It has one public attribute.
 
-Using your own OpenSSL on Linux
--------------------------------
+    .. attribute:: name
 
-Python links to OpenSSL for its own purposes and this can sometimes cause
-problems when you wish to use a different version of OpenSSL with cryptography.
-If you want to use cryptography with your own build of OpenSSL you will need to
-make sure that the build is configured correctly so that your version of
-OpenSSL doesn't conflict with Python's.
-
-The options you need to add allow the linker to identify every symbol correctly
-even when multiple versions of the library are linked into the same program. If
-you are using your distribution's source packages these will probably be
-patched in for you already, otherwise you'll need to use options something like
-this when configuring OpenSSL::
-
-    ./config -Wl,--version-script=openssl.ld -Wl,-Bsymbolic-functions -fPIC shared
-
-You'll also need to generate your own ``openssl.ld`` file. For example::
-
-    OPENSSL_1.0.1F_CUSTOM {
-        global:
-            *;
-    };
-
-You should replace the version string on the first line as appropriate for your
-build.
+        The string name of this backend: ``"openssl"``
 
 .. _`OpenSSL`: https://www.openssl.org/
diff --git a/docs/hazmat/bindings/openssl.rst b/docs/hazmat/bindings/openssl.rst
index 373fe47..557f8c4 100644
--- a/docs/hazmat/bindings/openssl.rst
+++ b/docs/hazmat/bindings/openssl.rst
@@ -22,6 +22,26 @@
         This is a ``cffi`` library. It can be used to call OpenSSL functions,
         and access constants.
 
+    .. classmethod:: init_static_locks
+
+        Enables the best available locking callback for OpenSSL.
+        See :ref:`openssl-threading`.
+
+.. _openssl-threading:
+
+Threading
+---------
+
+``cryptography`` enables OpenSSLs `thread safety facilities`_ in two different
+ways depending on the configuration of your system. Normally the locking
+callbacks provided by your Python implementation specifically for OpenSSL will
+be used. However if you have linked ``cryptography`` to a different version of
+OpenSSL than that used by your Python implementation we enable an alternative
+locking callback. This version is implemented in Python and so may result in
+lower performance in some situations. In particular parallelism is reduced
+because it has to acquire the GIL whenever any lock operations occur within
+OpenSSL.
 
 .. _`CFFI`: https://cffi.readthedocs.org/
 .. _`OpenSSL`: https://www.openssl.org/
+.. _`thread safety facilities`: http://www.openssl.org/docs/crypto/threads.html
diff --git a/docs/hazmat/primitives/index.rst b/docs/hazmat/primitives/index.rst
index b115fdb..bde0739 100644
--- a/docs/hazmat/primitives/index.rst
+++ b/docs/hazmat/primitives/index.rst
@@ -10,5 +10,6 @@
     hmac
     symmetric-encryption
     padding
+    key-derivation-functions
     constant-time
     interfaces
diff --git a/docs/hazmat/primitives/interfaces.rst b/docs/hazmat/primitives/interfaces.rst
index edb24cd..09a5a4c 100644
--- a/docs/hazmat/primitives/interfaces.rst
+++ b/docs/hazmat/primitives/interfaces.rst
@@ -102,3 +102,175 @@
 
         Exact requirements of the nonce are described by the documentation of
         individual modes.
+
+Asymmetric Interfaces
+~~~~~~~~~~~~~~~~~~~~~
+
+.. class:: RSAPrivateKey
+
+    .. versionadded:: 0.2
+
+    An `RSA`_ private key.
+
+    .. method:: public_key()
+
+        :return: :class:`~cryptography.hazmat.primitives.interfaces.RSAPublicKey`
+
+        An RSA public key object corresponding to the values of the private key.
+
+    .. attribute:: modulus
+
+        :type: int
+
+        The public modulus.
+
+    .. attribute:: public_exponent
+
+        :type: int
+
+        The public exponent.
+
+    .. attribute:: key_length
+
+        :type: int
+
+        The bit length of the modulus.
+
+    .. attribute:: p
+
+        :type: int
+
+        ``p``, one of the two primes composing the :attr:`modulus`.
+
+    .. attribute:: q
+
+        :type: int
+
+        ``q``, one of the two primes composing the :attr:`modulus`.
+
+    .. attribute:: d
+
+        :type: int
+
+        The private exponent.
+
+    .. attribute:: n
+
+        :type: int
+
+        The public modulus. Alias for :attr:`modulus`.
+
+    .. attribute:: e
+
+        :type: int
+
+        The public exponent. Alias for :attr:`public_exponent`.
+
+
+.. class:: RSAPublicKey
+
+    .. versionadded:: 0.2
+
+    An `RSA`_ public key.
+
+    .. attribute:: modulus
+
+        :type: int
+
+        The public modulus.
+
+    .. attribute:: key_length
+
+        :type: int
+
+        The bit length of the modulus.
+
+    .. attribute:: public_exponent
+
+        :type: int
+
+        The public exponent.
+
+    .. attribute:: n
+
+        :type: int
+
+        The public modulus. Alias for :attr:`modulus`.
+
+    .. attribute:: e
+
+        :type: int
+
+        The public exponent. Alias for :attr:`public_exponent`.
+
+
+Hash Algorithms
+~~~~~~~~~~~~~~~
+
+.. class:: HashAlgorithm
+
+    .. attribute:: name
+
+        :type: str
+
+        The standard name for the hash algorithm, for example: ``"sha256"`` or
+        ``"whirlpool"``.
+
+    .. attribute:: digest_size
+
+        :type: int
+
+        The size of the resulting digest in bytes.
+
+    .. attribute:: block_size
+
+        :type: int
+
+        The internal block size of the hash algorithm in bytes.
+
+
+Key Derivation Functions
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. class:: KeyDerivationFunction
+
+    .. versionadded:: 0.2
+
+    .. method:: derive(key_material)
+
+        :param key_material bytes: The input key material. Depending on what
+                                   key derivation function you are using this
+                                   could be either random material, or a user
+                                   supplied password.
+        :return: The new key.
+        :raises cryptography.exceptions.AlreadyFinalized: This is raised when
+                                                          :meth:`derive` or
+                                                          :meth:`verify` is
+                                                          called more than
+                                                          once.
+
+        This generates and returns a new key from the supplied key material.
+
+    .. 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.
+
+        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. This can be used for
+        something like checking whether a user's password attempt matches the
+        stored derived key.
+
+.. _`RSA`: http://en.wikipedia.org/wiki/RSA_(cryptosystem)
diff --git a/docs/hazmat/primitives/key-derivation-functions.rst b/docs/hazmat/primitives/key-derivation-functions.rst
new file mode 100644
index 0000000..f96eae0
--- /dev/null
+++ b/docs/hazmat/primitives/key-derivation-functions.rst
@@ -0,0 +1,125 @@
+.. hazmat::
+
+Key Derivation Functions
+========================
+
+.. currentmodule:: cryptography.hazmat.primitives.kdf
+
+Key derivation functions derive bytes suitable for cryptographic operations
+from passwords or other data sources using a pseudo-random function (PRF).
+Different KDFs are suitable for different tasks such as:
+
+* Cryptographic key derivation
+
+    Deriving a key suitable for use as input to an encryption algorithm.
+    Typically this means taking a password and running it through an algorithm
+    such as :class:`~cryptography.hazmat.primitives.kdf.pbkdf2.PBKDF2HMAC` or HKDF.
+    This process is typically known as `key stretching`_.
+
+* Password storage
+
+    When storing passwords you want to use an algorithm that is computationally
+    intensive. Legitimate users will only need to compute it once (for example,
+    taking the user's password, running it through the KDF, then comparing it
+    to the stored value), while attackers will need to do it billions of times.
+    Ideal password storage KDFs will be demanding on both computational and
+    memory resources.
+
+.. currentmodule:: cryptography.hazmat.primitives.kdf.pbkdf2
+
+.. class:: PBKDF2HMAC(algorithm, length, salt, iterations, backend)
+
+    .. versionadded:: 0.2
+
+    `PBKDF2`_ (Password Based Key Derivation Function 2) is typically used for
+    deriving a cryptographic key from a password. It may also be used for
+    key storage, but an alternate key storage KDF such as `scrypt`_ is generally
+    considered a better solution.
+
+    This class conforms to the
+    :class:`~cryptography.hazmat.primitives.interfaces.KeyDerivationFunction`
+    interface.
+
+    .. doctest::
+
+        >>> import os
+        >>> from cryptography.hazmat.primitives import hashes
+        >>> from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
+        >>> from cryptography.hazmat.backends import default_backend
+        >>> backend = default_backend()
+        >>> salt = os.urandom(16)
+        >>> # derive
+        >>> kdf = PBKDF2HMAC(
+        ...     algorithm=hashes.SHA256(),
+        ...     length=32,
+        ...     salt=salt,
+        ...     iterations=100000,
+        ...     backend=backend
+        ... )
+        >>> key = kdf.derive(b"my great password")
+        >>> # verify
+        >>> kdf = PBKDF2HMAC(
+        ...     algorithm=hashes.SHA256(),
+        ...     length=32,
+        ...     salt=salt,
+        ...     iterations=100000,
+        ...     backend=backend
+        ... )
+        >>> kdf.verify(b"my great password", 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
+        (2\ :sup:`32` - 1) * ``algorithm.digest_size``.
+    :param bytes salt: A salt. `NIST SP 800-132`_ recommends 128-bits or
+        longer.
+    :param int iterations: The number of iterations to perform of the hash
+        function. This can be used to control the length of time the operation
+        takes. Higher numbers help mitigate brute force attacks against derived
+        keys. See OWASP's `Password Storage Cheat Sheet`_ for more
+        detailed recommendations if you intend to use this for password storage.
+    :param backend: A
+        :class:`~cryptography.hazmat.backends.interfaces.PBKDF2HMACBackend`
+        provider.
+
+    .. method:: derive(key_material)
+
+        :param bytes key_material: The input key material. For PBKDF2 this
+            should be a password.
+        :return bytes: the derived key.
+        :raises cryptography.exceptions.AlreadyFinalized: This is raised when
+                                                          :meth:`derive` or
+                                                          :meth:`verify` is
+                                                          called more than
+                                                          once.
+
+        This generates and returns a new key from the supplied password.
+
+    .. method:: verify(key_material, expected_key)
+
+        :param bytes key_material: The input key material. This is the same as
+                                   ``key_material`` in :meth:`derive`.
+        :param bytes expected_key: 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.
+
+        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. This can be used for
+        checking whether the password a user provides matches the stored derived
+        key.
+
+.. _`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`: http://en.wikipedia.org/wiki/PBKDF2
+.. _`scrypt`: http://en.wikipedia.org/wiki/Scrypt
+.. _`key stretching`: http://en.wikipedia.org/wiki/Key_stretching
diff --git a/docs/index.rst b/docs/index.rst
index e17b4f9..86cd42c 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -5,15 +5,16 @@
 primitives. We hope it'll be your one-stop-shop for all your cryptographic
 needs in Python.
 
-Installing
-----------
-
+Installation
+------------
 You can install ``cryptography`` with ``pip``:
 
 .. code-block:: console
 
     $ pip install cryptography
 
+See :doc:`Installation <installation>` for more information.
+
 Why a new crypto library for Python?
 ------------------------------------
 
@@ -75,9 +76,13 @@
 .. toctree::
     :maxdepth: 2
 
+    installation
     contributing
     security
     api-stability
     doing-a-release
     changelog
     community
+
+
+.. _`pre-compiled binaries`: https://www.openssl.org/related/binaries.html
diff --git a/docs/installation.rst b/docs/installation.rst
new file mode 100644
index 0000000..2206107
--- /dev/null
+++ b/docs/installation.rst
@@ -0,0 +1,74 @@
+Installing
+==========
+
+You can install ``cryptography`` with ``pip``:
+
+.. code-block:: console
+
+    $ pip install cryptography
+
+Installation Notes
+==================
+On Windows
+----------
+If you're on Windows you'll need to make sure you have OpenSSL installed.
+There are `pre-compiled binaries`_ available. If your installation is in
+an unusual location set the ``LIB`` and ``INCLUDE`` environment variables
+to include the corresponding locations. For example:
+
+.. code-block:: console
+
+    C:\> \path\to\vcvarsall.bat x86_amd64
+    C:\> set LIB=C:\OpenSSL-1.0.1f-64bit\lib;%LIB%
+    C:\> set INCLUDE=C:\OpenSSL-1.0.1f-64bit\include;%INCLUDE%
+    C:\> pip install cryptography
+
+Using your own OpenSSL on Linux
+-------------------------------
+
+Python links to OpenSSL for its own purposes and this can sometimes cause
+problems when you wish to use a different version of OpenSSL with cryptography.
+If you want to use cryptography with your own build of OpenSSL you will need to
+make sure that the build is configured correctly so that your version of
+OpenSSL doesn't conflict with Python's.
+
+The options you need to add allow the linker to identify every symbol correctly
+even when multiple versions of the library are linked into the same program. If
+you are using your distribution's source packages these will probably be
+patched in for you already, otherwise you'll need to use options something like
+this when configuring OpenSSL:
+
+.. code-block:: console
+
+    $ ./config -Wl,--version-script=openssl.ld -Wl,-Bsymbolic-functions -fPIC shared
+
+You'll also need to generate your own ``openssl.ld`` file. For example::
+
+    OPENSSL_1.0.1F_CUSTOM {
+        global:
+            *;
+    };
+
+You should replace the version string on the first line as appropriate for your
+build.
+
+Using your own OpenSSL on OS X
+------------------------------
+
+To link cryptography against a custom version of OpenSSL you'll need to set
+``ARCHFLAGS``, ``LDFLAGS``, and ``CFLAGS``. OpenSSL can be installed via
+`Homebrew`_:
+
+.. code-block:: console
+
+    $ brew install openssl
+
+Then install cryptography linking against the brewed version:
+
+.. code-block:: console
+
+    $ env ARCHFLAGS="-arch x86_64" LDFLAGS="-L/usr/local/opt/openssl/lib" CFLAGS="-I/usr/local/opt/openssl/include" pip install cryptography
+
+
+.. _`Homebrew`: http://brew.sh
+.. _`pre-compiled binaries`: https://www.openssl.org/related/binaries.html
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
diff --git a/pytest.ini b/pytest.ini
index 36d4edc..2a1b6e9 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,7 +1,8 @@
 [pytest]
 addopts = -r s
 markers =
-    hmac: this test requires a backend providing HMACBackend
     cipher: this test requires a backend providing CipherBackend
     hash: this test requires a backend providing HashBackend
+    hmac: this test requires a backend providing HMACBackend
+    pbkdf2hmac: this test requires a backend providing PBKDF2HMACBackend
     supported: parametrized test requiring only_if and skip_message
diff --git a/setup.py b/setup.py
index e8bcc11..57a9575 100644
--- a/setup.py
+++ b/setup.py
@@ -43,14 +43,23 @@
     """
 
     def finalize_options(self):
-        from cryptography.hazmat.bindings.openssl.binding import Binding
+        from cryptography.hazmat.bindings.commoncrypto.binding import (
+            Binding as CommonCryptoBinding
+        )
+        from cryptography.hazmat.bindings.openssl.binding import (
+            Binding as OpenSSLBinding
+        )
         from cryptography.hazmat.primitives import constant_time, padding
 
         self.distribution.ext_modules = [
-            Binding().ffi.verifier.get_extension(),
+            OpenSSLBinding().ffi.verifier.get_extension(),
             constant_time._ffi.verifier.get_extension(),
             padding._ffi.verifier.get_extension()
         ]
+        if CommonCryptoBinding.is_available():
+            self.distribution.ext_modules.append(
+                CommonCryptoBinding().ffi.verifier.get_extension()
+            )
 
         build.finalize_options(self)
 
diff --git a/tests/conftest.py b/tests/conftest.py
index 1d9f96e..ecad1b2 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -2,15 +2,18 @@
 
 from cryptography.hazmat.backends import _ALL_BACKENDS
 from cryptography.hazmat.backends.interfaces import (
-    HMACBackend, CipherBackend, HashBackend
+    HMACBackend, CipherBackend, HashBackend, PBKDF2HMACBackend
 )
 
-from .utils import check_for_iface, check_backend_support
+from .utils import check_for_iface, check_backend_support, select_backends
 
 
-@pytest.fixture(params=_ALL_BACKENDS)
-def backend(request):
-    return request.param
+def pytest_generate_tests(metafunc):
+    names = metafunc.config.getoption("--backend")
+    selected_backends = select_backends(names, _ALL_BACKENDS)
+
+    if "backend" in metafunc.fixturenames:
+        metafunc.parametrize("backend", selected_backends)
 
 
 @pytest.mark.trylast
@@ -18,4 +21,12 @@
     check_for_iface("hmac", HMACBackend, item)
     check_for_iface("cipher", CipherBackend, item)
     check_for_iface("hash", HashBackend, item)
+    check_for_iface("pbkdf2hmac", PBKDF2HMACBackend, item)
     check_backend_support(item)
+
+
+def pytest_addoption(parser):
+    parser.addoption(
+        "--backend", action="store", metavar="NAME",
+        help="Only run tests matching the backend NAME."
+    )
diff --git a/tests/hazmat/backends/test_commoncrypto.py b/tests/hazmat/backends/test_commoncrypto.py
new file mode 100644
index 0000000..7cc0f72
--- /dev/null
+++ b/tests/hazmat/backends/test_commoncrypto.py
@@ -0,0 +1,65 @@
+# 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.
+
+import pytest
+
+from cryptography import utils
+from cryptography.exceptions import UnsupportedAlgorithm, InternalError
+from cryptography.hazmat.bindings.commoncrypto.binding import Binding
+from cryptography.hazmat.primitives import interfaces
+from cryptography.hazmat.primitives.ciphers.algorithms import AES
+from cryptography.hazmat.primitives.ciphers.base import Cipher
+from cryptography.hazmat.primitives.ciphers.modes import CBC, GCM
+
+
+@utils.register_interface(interfaces.CipherAlgorithm)
+class DummyCipher(object):
+    name = "dummy-cipher"
+    block_size = 128
+
+
+@pytest.mark.skipif(not Binding.is_available(),
+                    reason="CommonCrypto not available")
+class TestCommonCrypto(object):
+    def test_supports_cipher(self):
+        from cryptography.hazmat.backends.commoncrypto.backend import backend
+        assert backend.cipher_supported(None, None) is False
+
+    def test_register_duplicate_cipher_adapter(self):
+        from cryptography.hazmat.backends.commoncrypto.backend import backend
+        with pytest.raises(ValueError):
+            backend._register_cipher_adapter(
+                AES, backend._lib.kCCAlgorithmAES128,
+                CBC, backend._lib.kCCModeCBC
+            )
+
+    def test_handle_response(self):
+        from cryptography.hazmat.backends.commoncrypto.backend import backend
+
+        with pytest.raises(ValueError):
+            backend._check_response(backend._lib.kCCAlignmentError)
+
+        with pytest.raises(InternalError):
+            backend._check_response(backend._lib.kCCMemoryFailure)
+
+        with pytest.raises(InternalError):
+            backend._check_response(backend._lib.kCCDecodeError)
+
+    def test_nonexistent_aead_cipher(self):
+        from cryptography.hazmat.backends.commoncrypto.backend import Backend
+        b = Backend()
+        cipher = Cipher(
+            DummyCipher(), GCM(b"fake_iv_here"), backend=b,
+        )
+        with pytest.raises(UnsupportedAlgorithm):
+            cipher.encryptor()
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index 2a32992..f01c3f6 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -14,7 +14,7 @@
 import pytest
 
 from cryptography import utils
-from cryptography.exceptions import UnsupportedAlgorithm
+from cryptography.exceptions import UnsupportedAlgorithm, InternalError
 from cryptography.hazmat.backends import default_backend
 from cryptography.hazmat.backends.openssl.backend import backend, Backend
 from cryptography.hazmat.primitives import interfaces
@@ -76,20 +76,20 @@
             cipher.encryptor()
 
     def test_handle_unknown_error(self):
-        with pytest.raises(SystemError):
+        with pytest.raises(InternalError):
             backend._handle_error_code(0, 0, 0)
 
-        with pytest.raises(SystemError):
+        with pytest.raises(InternalError):
             backend._handle_error_code(backend._lib.ERR_LIB_EVP, 0, 0)
 
-        with pytest.raises(SystemError):
+        with pytest.raises(InternalError):
             backend._handle_error_code(
                 backend._lib.ERR_LIB_EVP,
                 backend._lib.EVP_F_EVP_ENCRYPTFINAL_EX,
                 0
             )
 
-        with pytest.raises(SystemError):
+        with pytest.raises(InternalError):
             backend._handle_error_code(
                 backend._lib.ERR_LIB_EVP,
                 backend._lib.EVP_F_EVP_DECRYPTFINAL_EX,
diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py
index d1e8505..35eb7e8 100644
--- a/tests/hazmat/bindings/test_openssl.py
+++ b/tests/hazmat/bindings/test_openssl.py
@@ -11,6 +11,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import pytest
+
 from cryptography.hazmat.bindings.openssl.binding import Binding
 
 
@@ -23,3 +25,74 @@
 
     def test_is_available(self):
         assert Binding.is_available() is True
+
+    def test_crypto_lock_init(self):
+        b = Binding()
+        b.init_static_locks()
+        lock_cb = b.lib.CRYPTO_get_locking_callback()
+        assert lock_cb != b.ffi.NULL
+
+    def _skip_if_not_fallback_lock(self, b):
+        # only run this test if we are using our locking cb
+        original_cb = b.lib.CRYPTO_get_locking_callback()
+        if original_cb != b._lock_cb_handle:
+            pytest.skip(
+                "Not using the fallback Python locking callback "
+                "implementation. Probably because import _ssl set one"
+            )
+
+    def test_fallback_crypto_lock_via_openssl_api(self):
+        b = Binding()
+        b.init_static_locks()
+
+        self._skip_if_not_fallback_lock(b)
+
+        # check that the lock state changes appropriately
+        lock = b._locks[b.lib.CRYPTO_LOCK_SSL]
+
+        # starts out unlocked
+        assert lock.acquire(False)
+        lock.release()
+
+        b.lib.CRYPTO_lock(
+            b.lib.CRYPTO_LOCK | b.lib.CRYPTO_READ,
+            b.lib.CRYPTO_LOCK_SSL, b.ffi.NULL, 0
+        )
+
+        # becomes locked
+        assert not lock.acquire(False)
+
+        b.lib.CRYPTO_lock(
+            b.lib.CRYPTO_UNLOCK | b.lib.CRYPTO_READ,
+            b.lib.CRYPTO_LOCK_SSL, b.ffi.NULL, 0
+        )
+
+        # then unlocked
+        assert lock.acquire(False)
+        lock.release()
+
+    def test_fallback_crypto_lock_via_binding_api(self):
+        b = Binding()
+        b.init_static_locks()
+
+        self._skip_if_not_fallback_lock(b)
+
+        lock = b._locks[b.lib.CRYPTO_LOCK_SSL]
+
+        with pytest.raises(RuntimeError):
+            b._lock_cb(0, b.lib.CRYPTO_LOCK_SSL, "<test>", 1)
+
+        # errors shouldnt cause locking
+        assert lock.acquire(False)
+        lock.release()
+
+        b._lock_cb(b.lib.CRYPTO_LOCK | b.lib.CRYPTO_READ,
+                   b.lib.CRYPTO_LOCK_SSL, "<test>", 1)
+        # locked
+        assert not lock.acquire(False)
+
+        b._lock_cb(b.lib.CRYPTO_UNLOCK | b.lib.CRYPTO_READ,
+                   b.lib.CRYPTO_LOCK_SSL, "<test>", 1)
+        # unlocked
+        assert lock.acquire(False)
+        lock.release()
diff --git a/tests/hazmat/primitives/test_pbkdf2hmac.py b/tests/hazmat/primitives/test_pbkdf2hmac.py
new file mode 100644
index 0000000..6ad225a
--- /dev/null
+++ b/tests/hazmat/primitives/test_pbkdf2hmac.py
@@ -0,0 +1,69 @@
+# 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
+
+import pytest
+import six
+
+from cryptography import utils
+from cryptography.exceptions import (
+    InvalidKey, UnsupportedAlgorithm, AlreadyFinalized
+)
+from cryptography.hazmat.primitives import hashes, interfaces
+from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
+from cryptography.hazmat.backends import default_backend
+
+
+@utils.register_interface(interfaces.HashAlgorithm)
+class DummyHash(object):
+    name = "dummy-hash"
+
+
+class TestPBKDF2HMAC(object):
+    def test_already_finalized(self):
+        kdf = PBKDF2HMAC(hashes.SHA1(), 20, b"salt", 10, default_backend())
+        kdf.derive(b"password")
+        with pytest.raises(AlreadyFinalized):
+            kdf.derive(b"password2")
+
+        kdf = PBKDF2HMAC(hashes.SHA1(), 20, b"salt", 10, default_backend())
+        key = kdf.derive(b"password")
+        with pytest.raises(AlreadyFinalized):
+            kdf.verify(b"password", key)
+
+        kdf = PBKDF2HMAC(hashes.SHA1(), 20, b"salt", 10, default_backend())
+        kdf.verify(b"password", key)
+        with pytest.raises(AlreadyFinalized):
+            kdf.verify(b"password", key)
+
+    def test_unsupported_algorithm(self):
+        with pytest.raises(UnsupportedAlgorithm):
+            PBKDF2HMAC(DummyHash(), 20, b"salt", 10, default_backend())
+
+    def test_invalid_key(self):
+        kdf = PBKDF2HMAC(hashes.SHA1(), 20, b"salt", 10, default_backend())
+        key = kdf.derive(b"password")
+
+        kdf = PBKDF2HMAC(hashes.SHA1(), 20, b"salt", 10, default_backend())
+        with pytest.raises(InvalidKey):
+            kdf.verify(b"password2", key)
+
+    def test_unicode_error_with_salt(self):
+        with pytest.raises(TypeError):
+            PBKDF2HMAC(hashes.SHA1(), 20, six.u("salt"), 10, default_backend())
+
+    def test_unicode_error_with_key_material(self):
+        kdf = PBKDF2HMAC(hashes.SHA1(), 20, b"salt", 10, default_backend())
+        with pytest.raises(TypeError):
+            kdf.derive(six.u("unicode here"))
diff --git a/tests/hazmat/primitives/test_pbkdf2hmac_vectors.py b/tests/hazmat/primitives/test_pbkdf2hmac_vectors.py
new file mode 100644
index 0000000..cbd4cc9
--- /dev/null
+++ b/tests/hazmat/primitives/test_pbkdf2hmac_vectors.py
@@ -0,0 +1,37 @@
+# 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
+
+import pytest
+
+from cryptography.hazmat.primitives import hashes
+
+from .utils import generate_pbkdf2_test
+from ...utils import load_nist_vectors
+
+
+@pytest.mark.supported(
+    only_if=lambda backend: backend.pbkdf2_hmac_supported(hashes.SHA1()),
+    skip_message="Does not support SHA1 for PBKDF2HMAC",
+)
+@pytest.mark.pbkdf2hmac
+class TestPBKDF2HMAC_SHA1(object):
+    test_pbkdf2_sha1 = generate_pbkdf2_test(
+        load_nist_vectors,
+        "KDF",
+        [
+            "rfc-6070-PBKDF2-SHA1.txt",
+        ],
+        hashes.SHA1(),
+    )
diff --git a/tests/hazmat/primitives/utils.py b/tests/hazmat/primitives/utils.py
index 6ecc70f..6b1d055 100644
--- a/tests/hazmat/primitives/utils.py
+++ b/tests/hazmat/primitives/utils.py
@@ -4,6 +4,7 @@
 import pytest
 
 from cryptography.hazmat.primitives import hashes, hmac
+from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
 from cryptography.hazmat.primitives.ciphers import Cipher
 from cryptography.exceptions import (
     AlreadyFinalized, NotYetFinalized, AlreadyUpdated, InvalidTag,
@@ -141,8 +142,7 @@
 
 
 def hash_test(backend, algorithm, params):
-    msg = params[0]
-    md = params[1]
+    msg, md = params
     m = hashes.Hash(algorithm, backend=backend)
     m.update(binascii.unhexlify(msg))
     expected_md = md.replace(" ", "").lower().encode("ascii")
@@ -206,14 +206,36 @@
 
 
 def hmac_test(backend, algorithm, params):
-    msg = params[0]
-    md = params[1]
-    key = params[2]
+    msg, md, key = params
     h = hmac.HMAC(binascii.unhexlify(key), algorithm, backend=backend)
     h.update(binascii.unhexlify(msg))
     assert h.finalize() == binascii.unhexlify(md.encode("ascii"))
 
 
+def generate_pbkdf2_test(param_loader, path, file_names, algorithm):
+    all_params = _load_all_params(path, file_names, param_loader)
+
+    @pytest.mark.parametrize("params", all_params)
+    def test_pbkdf2(self, backend, params):
+        pbkdf2_test(backend, algorithm, params)
+    return test_pbkdf2
+
+
+def pbkdf2_test(backend, algorithm, params):
+    # Password and salt can contain \0, which should be loaded as a null char.
+    # The NIST loader loads them as literal strings so we replace with the
+    # proper value.
+    kdf = PBKDF2HMAC(
+        algorithm,
+        int(params["length"]),
+        params["salt"],
+        int(params["iterations"]),
+        backend
+    )
+    derived_key = kdf.derive(params["password"])
+    assert binascii.hexlify(derived_key) == params["derived_key"]
+
+
 def generate_aead_exception_test(cipher_factory, mode_factory):
     def test_aead_exception(self, backend):
         aead_exception_test(backend, cipher_factory, mode_factory)
diff --git a/tests/hazmat/primitives/vectors/KDF/rfc-5869-HKDF-SHA1.txt b/tests/hazmat/primitives/vectors/KDF/rfc-5869-HKDF-SHA1.txt
new file mode 100644
index 0000000..b3fd03a
--- /dev/null
+++ b/tests/hazmat/primitives/vectors/KDF/rfc-5869-HKDF-SHA1.txt
@@ -0,0 +1,56 @@
+# A.4.  Test Case 4
+# Basic test case with SHA-1
+
+COUNT = 4
+
+Hash = SHA-1
+IKM  = 0b0b0b0b0b0b0b0b0b0b0b
+salt = 000102030405060708090a0b0c
+info = f0f1f2f3f4f5f6f7f8f9
+L    = 42
+
+PRK  = 9b6c18c432a7bf8f0e71c8eb88f4b30baa2ba243
+OKM  = 085a01ea1b10f36933068b56efa5ad81a4f14b822f5b091568a9cdd4f155fda2c22e422478d305f3f896
+
+# A.5.  Test Case 5
+# Test with SHA-1 and longer inputs/outputs
+
+COUNT = 5
+
+Hash = SHA-1
+IKM  = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f
+salt = 606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf
+info = b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff
+L    = 82
+
+PRK  = 8adae09a2a307059478d309b26c4115a224cfaf6
+OKM  = 0bd770a74d1160f7c9f12cd5912a06ebff6adcae899d92191fe4305673ba2ffe8fa3f1a4e5ad79f3f334b3b202b2173c486ea37ce3d397ed034c7f9dfeb15c5e927336d0441f4c4300e2cff0d0900b52d3b4
+
+# A.6.  Test Case 6
+# Test with SHA-1 and zero-length salt/info
+
+COUNT = 6
+
+Hash = SHA-1
+IKM  = 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
+salt =
+info =
+L    = 42
+
+PRK  = da8c8a73c7fa77288ec6f5e7c297786aa0d32d01
+OKM  = 0ac1af7002b3d761d1e55298da9d0506b9ae52057220a306e07b6b87e8df21d0ea00033de03984d34918
+
+# A.7.  Test Case 7
+# Test with SHA-1, salt not provided (defaults to HashLen zero octets),
+# zero-length info
+
+COUNT = 7
+
+Hash = SHA-1
+IKM  = 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c
+salt =
+info =
+L    = 42
+
+PRK  = 2adccada18779e7c2077ad2eb19d3f3e731385dd
+OKM  = 2c91117204d745f3500d636a62f64f0ab3bae548aa53d423b0d1f27ebba6f5e5673a081d70cce7acfc48
diff --git a/tests/hazmat/primitives/vectors/KDF/rfc-5869-HKDF-SHA256.txt b/tests/hazmat/primitives/vectors/KDF/rfc-5869-HKDF-SHA256.txt
new file mode 100644
index 0000000..9068a73
--- /dev/null
+++ b/tests/hazmat/primitives/vectors/KDF/rfc-5869-HKDF-SHA256.txt
@@ -0,0 +1,40 @@
+# A.1.  Test Case 1
+# Basic test case with SHA-256
+
+COUNT = 1
+
+Hash = SHA-256
+IKM  = 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
+salt = 000102030405060708090a0b0c
+info = f0f1f2f3f4f5f6f7f8f9
+L    = 42
+PRK  = 077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5
+OKM  = 3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865
+
+# A.2.  Test Case 2
+# Test with SHA-256 and longer inputs/outputs
+
+COUNT = 2
+
+Hash = SHA-256
+IKM  = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f
+salt = 606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf
+info = b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff
+L    = 82
+
+PRK  = 06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244
+OKM  = b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87
+
+# A.3.  Test Case 3
+# Test with SHA-256 and zero-length salt/info
+
+COUNT = 3
+
+Hash = SHA-256
+IKM  = 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
+salt =
+info =
+L    = 42
+
+PRK  = 19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04
+OKM  = 8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8
diff --git a/tests/hazmat/primitives/vectors/KDF/rfc-6070-PBKDF2-SHA1.txt b/tests/hazmat/primitives/vectors/KDF/rfc-6070-PBKDF2-SHA1.txt
new file mode 100644
index 0000000..739f3f3
--- /dev/null
+++ b/tests/hazmat/primitives/vectors/KDF/rfc-6070-PBKDF2-SHA1.txt
@@ -0,0 +1,48 @@
+# PBKDF2 SHA1 vectors from http://www.ietf.org/rfc/rfc6070.txt
+
+COUNT = 0
+PASSWORD = password
+SALT = salt
+ITERATIONS = 1
+LENGTH = 20
+DERIVED_KEY = 0c60c80f961f0e71f3a9b524af6012062fe037a6
+
+
+COUNT = 1
+PASSWORD = password
+SALT = salt
+ITERATIONS = 2
+LENGTH = 20
+DERIVED_KEY = ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957
+
+
+COUNT = 2
+PASSWORD = password
+SALT = salt
+ITERATIONS = 4096
+LENGTH = 20
+DERIVED_KEY = 4b007901b765489abead49d926f721d065a429c1
+
+
+COUNT = 3
+PASSWORD = password
+SALT = salt
+ITERATIONS = 16777216
+LENGTH = 20
+DERIVED_KEY = eefe3d61cd4da4e4e9945b3d6ba2158c2634e984
+
+
+COUNT = 4
+PASSWORD = passwordPASSWORDpassword
+SALT = saltSALTsaltSALTsaltSALTsaltSALTsalt
+ITERATIONS = 4096
+LENGTH = 25
+DERIVED_KEY = 3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038
+
+
+COUNT = 5
+PASSWORD = pass\0word
+SALT = sa\0lt
+ITERATIONS = 4096
+LENGTH = 16
+DERIVED_KEY = 56fa6aa75548099dcc37d7f03425e0c3
diff --git a/tests/test_utils.py b/tests/test_utils.py
index e3e53d6..8ecb33f 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -21,7 +21,7 @@
 from .utils import (
     load_nist_vectors, load_vectors_from_file, load_cryptrec_vectors,
     load_openssl_vectors, load_hash_vectors, check_for_iface,
-    check_backend_support
+    check_backend_support, select_backends
 )
 
 
@@ -29,6 +29,48 @@
     pass
 
 
+def test_select_one_backend():
+    b1 = pretend.stub(name="b1")
+    b2 = pretend.stub(name="b2")
+    b3 = pretend.stub(name="b3")
+    backends = [b1, b2, b3]
+    name = "b2"
+    selected_backends = select_backends(name, backends)
+    assert len(selected_backends) == 1
+    assert selected_backends[0] == b2
+
+
+def test_select_no_backend():
+    b1 = pretend.stub(name="b1")
+    b2 = pretend.stub(name="b2")
+    b3 = pretend.stub(name="b3")
+    backends = [b1, b2, b3]
+    name = "back!"
+    with pytest.raises(ValueError):
+        select_backends(name, backends)
+
+
+def test_select_backends_none():
+    b1 = pretend.stub(name="b1")
+    b2 = pretend.stub(name="b2")
+    b3 = pretend.stub(name="b3")
+    backends = [b1, b2, b3]
+    name = None
+    selected_backends = select_backends(name, backends)
+    assert len(selected_backends) == 3
+
+
+def test_select_two_backends():
+    b1 = pretend.stub(name="b1")
+    b2 = pretend.stub(name="b2")
+    b3 = pretend.stub(name="b3")
+    backends = [b1, b2, b3]
+    name = "b2 ,b1 "
+    selected_backends = select_backends(name, backends)
+    assert len(selected_backends) == 2
+    assert selected_backends == [b1, b2]
+
+
 def test_check_for_iface():
     item = pretend.stub(keywords=["fake_name"], funcargs={"backend": True})
     with pytest.raises(pytest.skip.Exception) as exc_info:
@@ -138,6 +180,25 @@
     ]
 
 
+def test_load_nist_vectors_with_null_chars():
+    vector_data = textwrap.dedent("""
+    COUNT = 0
+    KEY = thing\\0withnulls
+
+    COUNT = 1
+    KEY = 00000000000000000000000000000000
+    """).splitlines()
+
+    assert load_nist_vectors(vector_data) == [
+        {
+            "key": b"thing\x00withnulls",
+        },
+        {
+            "key": b"00000000000000000000000000000000",
+        },
+    ]
+
+
 def test_load_cryptrec_vectors():
     vector_data = textwrap.dedent("""
     # Vectors taken from http://info.isl.ntt.co.jp/crypt/eng/camellia/
diff --git a/tests/utils.py b/tests/utils.py
index 693a0c8..5c0e524 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -11,11 +11,37 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import collections
 import os
 
 import pytest
 
 
+HashVector = collections.namedtuple("HashVector", ["message", "digest"])
+KeyedHashVector = collections.namedtuple(
+    "KeyedHashVector", ["message", "digest", "key"]
+)
+
+
+def select_backends(names, backend_list):
+    if names is None:
+        return backend_list
+    split_names = [x.strip() for x in names.split(',')]
+    # this must be duplicated and then removed to preserve the metadata
+    # pytest associates. Appending backends to a new list doesn't seem to work
+    selected_backends = []
+    for backend in backend_list:
+        if backend.name in split_names:
+            selected_backends.append(backend)
+
+    if len(selected_backends) > 0:
+        return selected_backends
+    else:
+        raise ValueError(
+            "No backend selected. Tried to select: {0}".format(split_names)
+        )
+
+
 def check_for_iface(name, iface, item):
     if name in item.keywords and "backend" in item.funcargs:
         if not isinstance(item.funcargs["backend"], iface):
@@ -63,6 +89,10 @@
         # Build our data using a simple Key = Value format
         name, value = [c.strip() for c in line.split("=")]
 
+        # Some tests (PBKDF2) contain \0, which should be interpreted as a
+        # null character rather than literal.
+        value = value.replace("\\0", "\0")
+
         # COUNT is a special token that indicates a new block of data
         if name.upper() == "COUNT":
             test_data = {}
@@ -139,27 +169,23 @@
         if line.startswith("Len"):
             length = int(line.split(" = ")[1])
         elif line.startswith("Key"):
-            """
-            HMAC vectors contain a key attribute. Hash vectors do not.
-            """
+            # HMAC vectors contain a key attribute. Hash vectors do not.
             key = line.split(" = ")[1].encode("ascii")
         elif line.startswith("Msg"):
-            """
-            In the NIST vectors they have chosen to represent an empty
-            string as hex 00, which is of course not actually an empty
-            string. So we parse the provided length and catch this edge case.
-            """
+            # In the NIST vectors they have chosen to represent an empty
+            # string as hex 00, which is of course not actually an empty
+            # string. So we parse the provided length and catch this edge case.
             msg = line.split(" = ")[1].encode("ascii") if length > 0 else b""
         elif line.startswith("MD"):
             md = line.split(" = ")[1]
             # after MD is found the Msg+MD (+ potential key) tuple is complete
             if key is not None:
-                vectors.append((msg, md, key))
+                vectors.append(KeyedHashVector(msg, md, key))
                 key = None
                 msg = None
                 md = None
             else:
-                vectors.append((msg, md))
+                vectors.append(HashVector(msg, md))
                 msg = None
                 md = None
         else:
diff --git a/tox.ini b/tox.ini
index ff5df36..5ff0877 100644
--- a/tox.ini
+++ b/tox.ini
@@ -8,7 +8,7 @@
     pretend
     pytest
 commands =
-    coverage run --source=cryptography/,tests/ -m pytest --capture=no --strict
+    coverage run --source=cryptography/,tests/ -m pytest --capture=no --strict {posargs}
     coverage report -m
 
 [testenv:docs]
@@ -28,7 +28,7 @@
 # Temporarily disable coverage on pypy because of performance problems with
 # coverage.py on pypy.
 [testenv:pypy]
-commands = py.test --capture=no --strict
+commands = py.test --capture=no --strict {posargs}
 
 [testenv:pep8]
 deps = flake8