Scrypt Implementation (#3117)

* Scrypt implementation.

* Docs stuff.

* Make example just an example and not a doctest.

* Add changelog entry.

* Docs cleanup.

* Add more tests.

* Add multibackend tests.

* PEP8.

* Add docs about Scrypt parameters.

* Docs cleanup.

* Add AlreadyFinalized.
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 4ec7d72..7a03644 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -20,6 +20,9 @@
 * Added
   :attr:`~cryptography.x509.CertificateRevocationList.signature_algorithm_oid`
   support to :class:`~cryptography.x509.CertificateRevocationList`.
+* Added support for :class:`~cryptography.hazmat.primitives.kdf.scrypt.Scrypt`
+  when using OpenSSL 1.1.0.
+
 
 1.5 - 2016-08-26
 ~~~~~~~~~~~~~~~~
diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst
index 14f72cf..b79bb23 100644
--- a/docs/hazmat/backends/interfaces.rst
+++ b/docs/hazmat/backends/interfaces.rst
@@ -652,3 +652,33 @@
 
         :returns: ``True`` if the given values of ``p`` and ``g`` are supported
             by this backend, otherwise ``False``.
+
+
+.. class:: ScryptBackend
+
+    .. versionadded:: 1.6
+
+    A backend with methods for using Scrypt.
+
+    The following backends implement this interface:
+
+    * :doc:`/hazmat/backends/openssl`
+
+    .. method:: derive_scrypt(self, key_material, salt, length, n, r, p)
+
+        :param bytes key_material: The key material to use as a basis for
+            the derived key. This is typically a password.
+
+        :param bytes salt: A salt.
+
+        :param int length: The desired length of the derived key.
+
+        :param int n: CPU/Memory cost parameter. It must be larger than 1 and be a
+            power of 2.
+
+        :param int r: Block size parameter.
+
+        :param int p: Parallelization parameter.
+
+        :return bytes: Derived key.
+
diff --git a/docs/hazmat/backends/openssl.rst b/docs/hazmat/backends/openssl.rst
index 8bc7dac..791aab3 100644
--- a/docs/hazmat/backends/openssl.rst
+++ b/docs/hazmat/backends/openssl.rst
@@ -24,6 +24,11 @@
     * :class:`~cryptography.hazmat.backends.interfaces.PEMSerializationBackend`
     * :class:`~cryptography.hazmat.backends.interfaces.X509Backend`
 
+    It also implements the following interface for OpenSSL versions ``1.1.0``
+    and above.
+
+    * :class:`~cryptography.hazmat.backends.interfaces.ScryptBackend`
+
     It also exposes the following:
 
     .. attribute:: name
diff --git a/docs/hazmat/primitives/key-derivation-functions.rst b/docs/hazmat/primitives/key-derivation-functions.rst
index 873e0ff..03260c0 100644
--- a/docs/hazmat/primitives/key-derivation-functions.rst
+++ b/docs/hazmat/primitives/key-derivation-functions.rst
@@ -737,6 +737,111 @@
         The counter iteration variable will be concatenated after
         the fixed input data.
 
+.. currentmodule:: cryptography.hazmat.primitives.kdf.scrypt
+
+.. class:: Scrypt(salt, length, n, r, p, backend)
+
+    .. versionadded:: 1.6
+
+    Scrypt is a KDF designed for password storage by Colin Percival to be
+    resistant against hardware-assisted attackers by having a tunable memory
+    cost. It is described in :rfc:`7914`.
+
+    This class conforms to the
+    :class:`~cryptography.hazmat.primitives.kdf.KeyDerivationFunction`
+    interface.
+
+    .. code-block:: python
+
+        >>> import os
+        >>> from cryptography.hazmat.primitives import hashes
+        >>> from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
+        >>> from cryptography.hazmat.backends import default_backend
+        >>> backend = default_backend()
+        >>> salt = os.urandom(16)
+        >>> # derive
+        >>> kdf = Scrypt(
+        ...     salt=salt,
+        ...     length=64,
+        ...     n=1024,
+        ...     r=8,
+        ...     p=16,
+        ...     backend=backend
+        ... )
+        >>> key = kdf.derive(b"my great password")
+        >>> # verify
+        >>> kdf = Scrypt(
+        ...     salt=salt,
+        ...     length=64,
+        ...     n=1024,
+        ...     r=8,
+        ...     p=16,
+        ...     backend=backend
+        ... )
+        >>> kdf.verify(b"my great password", key)
+
+    :param bytes salt: A salt.
+    :param int length: The desired length of the derived key.
+    :param int n: CPU/Memory cost parameter. It must be larger than 1 and be a
+        power of 2.
+    :param int r: Block size parameter.
+    :param int p: Parallelization parameter.
+
+    The computational and memory cost of Scrypt can be adjusted by manipulating
+    the 3 parameters: n, r and p. In general, the memory cost of Scrypt is
+    affected by the values of both n and r while n also determines the number
+    of iterations performed. p increases the computational cost without
+    affecting memory usage. A more in-depth explanation of the 3 parameters can
+    be found `here`_.
+
+    :rfc:`7914` `recommends`_ values of r=8 and p=1 while scaling n to the
+    number appropriate for your system.
+
+    :param backend: An instance of
+        :class:`~cryptography.hazmat.backends.interfaces.ScryptBackend`.
+
+    :raises cryptography.exceptions.UnsupportedAlgorithm: This is raised if the
+        provided ``backend`` does not implement
+        :class:`~cryptography.hazmat.backends.interfaces.ScryptBackend`
+
+    :raises TypeError: This exception is raised if ``salt`` is not ``bytes``.
+
+    .. method:: derive(key_material)
+
+        :param bytes key_material: The input key material.
+        :return bytes: the derived key.
+        :raises TypeError: This exception is raised if ``key_material`` is not
+                           ``bytes``.
+        :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.
+
 Interface
 ~~~~~~~~~
 
@@ -795,3 +900,5 @@
 .. _`key stretching`: https://en.wikipedia.org/wiki/Key_stretching
 .. _`HKDF`: https://en.wikipedia.org/wiki/HKDF
 .. _`HKDF paper`: https://eprint.iacr.org/2010/264
+.. _`here`: https://stackoverflow.com/a/30308723/1170681
+.. _`recommends`: https://tools.ietf.org/html/rfc7914#section-2
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index f60943a..31bc995 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -55,6 +55,7 @@
 nonces
 online
 paddings
+Parallelization
 pickleable
 plaintext
 pre
@@ -73,6 +74,7 @@
 Solaris
 Tanja
 testability
+tunable
 Ubuntu
 unencrypted
 unpadded
diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py
index 5b9e6f3..9a1d704 100644
--- a/src/cryptography/hazmat/backends/interfaces.py
+++ b/src/cryptography/hazmat/backends/interfaces.py
@@ -357,3 +357,12 @@
         """
         Returns whether the backend supports DH with these parameter values.
         """
+
+
+@six.add_metaclass(abc.ABCMeta)
+class ScryptBackend(object):
+    @abc.abstractmethod
+    def derive_scrypt(self, key_material, salt, length, n, r, p):
+        """
+        Return bytes derived from provided Scrypt parameters.
+        """
diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py
index 48bc7d0..deca020 100644
--- a/src/cryptography/hazmat/backends/multibackend.py
+++ b/src/cryptography/hazmat/backends/multibackend.py
@@ -9,7 +9,7 @@
 from cryptography.hazmat.backends.interfaces import (
     CMACBackend, CipherBackend, DERSerializationBackend, DSABackend,
     EllipticCurveBackend, HMACBackend, HashBackend, PBKDF2HMACBackend,
-    PEMSerializationBackend, RSABackend, X509Backend
+    PEMSerializationBackend, RSABackend, ScryptBackend, X509Backend
 )
 
 
@@ -24,6 +24,7 @@
 @utils.register_interface(EllipticCurveBackend)
 @utils.register_interface(PEMSerializationBackend)
 @utils.register_interface(X509Backend)
+@utils.register_interface(ScryptBackend)
 class MultiBackend(object):
     name = "multibackend"
 
@@ -409,3 +410,8 @@
             "This backend does not support X.509.",
             _Reasons.UNSUPPORTED_X509
         )
+
+    def derive_scrypt(self, key_material, salt, length, n, r, p):
+        for b in self._filtered_backends(ScryptBackend):
+            return b.derive_scrypt(key_material, salt, length, n, r, p)
+        raise UnsupportedAlgorithm("This backend does not support scrypt.")
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 7d16e05..955b197 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -8,6 +8,7 @@
 import calendar
 import collections
 import itertools
+import sys
 from contextlib import contextmanager
 
 import six
@@ -17,7 +18,7 @@
 from cryptography.hazmat.backends.interfaces import (
     CMACBackend, CipherBackend, DERSerializationBackend, DSABackend,
     EllipticCurveBackend, HMACBackend, HashBackend, PBKDF2HMACBackend,
-    PEMSerializationBackend, RSABackend, X509Backend
+    PEMSerializationBackend, RSABackend, ScryptBackend, X509Backend
 )
 from cryptography.hazmat.backends.openssl.ciphers import (
     _AESCTRCipherContext, _CipherContext
@@ -114,6 +115,9 @@
 @utils.register_interface(RSABackend)
 @utils.register_interface(PEMSerializationBackend)
 @utils.register_interface(X509Backend)
+@utils.register_interface_if(
+    binding.Binding().lib.Cryptography_HAS_SCRYPT, ScryptBackend
+)
 class Backend(object):
     """
     OpenSSL API binding interfaces.
@@ -1691,6 +1695,14 @@
                 serialization._ssh_write_string(public_numbers.encode_point())
             )
 
+    def derive_scrypt(self, key_material, salt, length, n, r, p):
+        buf = self._ffi.new("unsigned char[]", length)
+        res = self._lib.EVP_PBE_scrypt(key_material, len(key_material), salt,
+                                       len(salt), n, r, p, sys.maxsize // 2,
+                                       buf, length)
+        self.openssl_assert(res == 1)
+        return self._ffi.buffer(buf)[:]
+
 
 class GetCipherByName(object):
     def __init__(self, fmt):
diff --git a/src/cryptography/hazmat/primitives/kdf/scrypt.py b/src/cryptography/hazmat/primitives/kdf/scrypt.py
new file mode 100644
index 0000000..09181d9
--- /dev/null
+++ b/src/cryptography/hazmat/primitives/kdf/scrypt.py
@@ -0,0 +1,49 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import absolute_import, division, print_function
+
+from cryptography import utils
+from cryptography.exceptions import (
+    AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
+)
+from cryptography.hazmat.backends.interfaces import ScryptBackend
+from cryptography.hazmat.primitives import constant_time
+from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
+
+
+@utils.register_interface(KeyDerivationFunction)
+class Scrypt(object):
+    def __init__(self, salt, length, n, r, p, backend):
+        if not isinstance(backend, ScryptBackend):
+            raise UnsupportedAlgorithm(
+                "Backend object does not implement ScryptBackend.",
+                _Reasons.BACKEND_MISSING_INTERFACE
+            )
+
+        self._length = length
+        if not isinstance(salt, bytes):
+            raise TypeError("salt must be bytes.")
+        self._used = False
+        self._salt = salt
+        self._n = n
+        self._r = r
+        self._p = p
+        self._backend = backend
+
+    def derive(self, key_material):
+        if self._used:
+            raise AlreadyFinalized("Scrypt instances can only be used once.")
+        self._used = True
+
+        if not isinstance(key_material, bytes):
+            raise TypeError("key_material must be bytes.")
+        return self._backend.derive_scrypt(
+            key_material, self._salt, self._length, self._n, self._r, self._p
+        )
+
+    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/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
index bf54d5c..1cd8733 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -13,7 +13,7 @@
 from cryptography.hazmat.backends.interfaces import (
     CMACBackend, CipherBackend, DERSerializationBackend, DSABackend,
     EllipticCurveBackend, HMACBackend, HashBackend, PBKDF2HMACBackend,
-    PEMSerializationBackend, RSABackend, X509Backend
+    PEMSerializationBackend, RSABackend, ScryptBackend, X509Backend
 )
 from cryptography.hazmat.backends.multibackend import MultiBackend
 from cryptography.hazmat.primitives import cmac, hashes, hmac
@@ -231,6 +231,12 @@
         pass
 
 
+@utils.register_interface(ScryptBackend)
+class DummyScryptBackend(object):
+    def derive_scrypt(self, key_material, salt, length, n, r, p):
+        pass
+
+
 class TestMultiBackend(object):
     def test_raises_error_with_empty_list(self):
         with pytest.raises(ValueError):
@@ -558,3 +564,11 @@
             )
         with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
             backend.create_x509_revoked_certificate(object())
+
+    def test_scrypt(self):
+        backend = MultiBackend([DummyScryptBackend()])
+        backend.derive_scrypt(b"key", b"salt", 1, 1, 1, 1)
+
+        backend = MultiBackend([DummyBackend])
+        with pytest.raises(UnsupportedAlgorithm):
+            backend.derive_scrypt(b"key", b"salt", 1, 1, 1, 1)
diff --git a/tests/hazmat/primitives/test_scrypt.py b/tests/hazmat/primitives/test_scrypt.py
new file mode 100644
index 0000000..de4100e
--- /dev/null
+++ b/tests/hazmat/primitives/test_scrypt.py
@@ -0,0 +1,119 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import absolute_import, division, print_function
+
+import binascii
+
+import os
+
+import pytest
+
+from cryptography.exceptions import (
+    AlreadyFinalized, InvalidKey, UnsupportedAlgorithm
+)
+from cryptography.hazmat.backends.interfaces import ScryptBackend
+from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
+
+from tests.utils import load_nist_vectors, load_vectors_from_file
+
+vectors = load_vectors_from_file(
+    os.path.join("KDF", "scrypt.txt"), load_nist_vectors)
+
+
+@pytest.mark.requires_backend_interface(interface=ScryptBackend)
+class TestScrypt(object):
+    @pytest.mark.parametrize("params", vectors)
+    def test_derive(self, backend, params):
+        password = params["password"]
+        work_factor = int(params["n"])
+        block_size = int(params["r"])
+        parallelization_factor = int(params["p"])
+        length = int(params["length"])
+        salt = params["salt"]
+        derived_key = params["derived_key"]
+
+        scrypt = Scrypt(salt, length, work_factor, block_size,
+                        parallelization_factor, backend)
+        assert binascii.hexlify(scrypt.derive(password)) == derived_key
+
+    def test_unsupported_backend(self):
+        work_factor = 1024
+        block_size = 8
+        parallelization_factor = 16
+        length = 64
+        salt = b"NaCl"
+        backend = object()
+
+        with pytest.raises(UnsupportedAlgorithm):
+            Scrypt(salt, length, work_factor, block_size,
+                   parallelization_factor, backend)
+
+    def test_salt_not_bytes(self, backend):
+        work_factor = 1024
+        block_size = 8
+        parallelization_factor = 16
+        length = 64
+        salt = 1
+
+        with pytest.raises(TypeError):
+            Scrypt(salt, length, work_factor, block_size,
+                   parallelization_factor, backend)
+
+    def test_password_not_bytes(self, backend):
+        password = 1
+        work_factor = 1024
+        block_size = 8
+        parallelization_factor = 16
+        length = 64
+        salt = b"NaCl"
+
+        scrypt = Scrypt(salt, length, work_factor, block_size,
+                        parallelization_factor, backend)
+
+        with pytest.raises(TypeError):
+            scrypt.derive(password)
+
+    @pytest.mark.parametrize("params", vectors)
+    def test_verify(self, backend, params):
+        password = params["password"]
+        work_factor = int(params["n"])
+        block_size = int(params["r"])
+        parallelization_factor = int(params["p"])
+        length = int(params["length"])
+        salt = params["salt"]
+        derived_key = params["derived_key"]
+
+        scrypt = Scrypt(salt, length, work_factor, block_size,
+                        parallelization_factor, backend)
+        assert scrypt.verify(password, binascii.unhexlify(derived_key)) is None
+
+    def test_invalid_verify(self, backend):
+        password = b"password"
+        work_factor = 1024
+        block_size = 8
+        parallelization_factor = 16
+        length = 64
+        salt = b"NaCl"
+        derived_key = b"fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e773"
+
+        scrypt = Scrypt(salt, length, work_factor, block_size,
+                        parallelization_factor, backend)
+
+        with pytest.raises(InvalidKey):
+            scrypt.verify(password, binascii.unhexlify(derived_key))
+
+    def test_already_finalized(self, backend):
+        password = b"password"
+        work_factor = 1024
+        block_size = 8
+        parallelization_factor = 16
+        length = 64
+        salt = b"NaCl"
+
+        scrypt = Scrypt(salt, length, work_factor, block_size,
+                        parallelization_factor, backend)
+        scrypt.derive(password)
+        with pytest.raises(AlreadyFinalized):
+            scrypt.derive(password)