PBKDF2 support for OpenSSL backend
diff --git a/tests/conftest.py b/tests/conftest.py
index a9acb54..7370294 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -2,7 +2,7 @@
 
 from cryptography.hazmat.backends import _ALL_BACKENDS
 from cryptography.hazmat.backends.interfaces import (
-    HMACBackend, CipherBackend, HashBackend
+    HMACBackend, CipherBackend, HashBackend, PBKDF2Backend
 )
 
 from .utils import check_for_iface, check_backend_support, select_backends
@@ -21,6 +21,7 @@
     check_for_iface("hmac", HMACBackend, item)
     check_for_iface("cipher", CipherBackend, item)
     check_for_iface("hash", HashBackend, item)
+    check_for_iface("pbkdf2", PBKDF2Backend, item)
     check_backend_support(item)
 
 
diff --git a/tests/hazmat/primitives/test_pbkdf2_vectors.py b/tests/hazmat/primitives/test_pbkdf2_vectors.py
new file mode 100644
index 0000000..e6e3935
--- /dev/null
+++ b/tests/hazmat/primitives/test_pbkdf2_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_hash_supported(hashes.SHA1()),
+    skip_message="Does not support SHA1 for PBKDF2",
+)
+@pytest.mark.pbkdf2
+class TestPBKDF2_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 f27afe4..3a1d6d8 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 PBKDF2
 from cryptography.hazmat.primitives.ciphers import Cipher
 from cryptography.exceptions import (
     AlreadyFinalized, NotYetFinalized, AlreadyUpdated, InvalidTag,
@@ -211,6 +212,30 @@
     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 = PBKDF2(
+        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/utils.py b/tests/utils.py
index 507bc42..5c0e524 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -89,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 = {}