• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# This file is dual licensed under the terms of the Apache License, Version
2# 2.0, and the BSD License. See the LICENSE file in the root of this repository
3# for complete details.
4
5from __future__ import absolute_import, division, print_function
6
7import sys
8
9from cryptography import utils
10from cryptography.exceptions import (
11    AlreadyFinalized,
12    InvalidKey,
13    UnsupportedAlgorithm,
14    _Reasons,
15)
16from cryptography.hazmat.backends import _get_backend
17from cryptography.hazmat.backends.interfaces import ScryptBackend
18from cryptography.hazmat.primitives import constant_time
19from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
20
21
22# This is used by the scrypt tests to skip tests that require more memory
23# than the MEM_LIMIT
24_MEM_LIMIT = sys.maxsize // 2
25
26
27@utils.register_interface(KeyDerivationFunction)
28class Scrypt(object):
29    def __init__(self, salt, length, n, r, p, backend=None):
30        backend = _get_backend(backend)
31        if not isinstance(backend, ScryptBackend):
32            raise UnsupportedAlgorithm(
33                "Backend object does not implement ScryptBackend.",
34                _Reasons.BACKEND_MISSING_INTERFACE,
35            )
36
37        self._length = length
38        utils._check_bytes("salt", salt)
39        if n < 2 or (n & (n - 1)) != 0:
40            raise ValueError("n must be greater than 1 and be a power of 2.")
41
42        if r < 1:
43            raise ValueError("r must be greater than or equal to 1.")
44
45        if p < 1:
46            raise ValueError("p must be greater than or equal to 1.")
47
48        self._used = False
49        self._salt = salt
50        self._n = n
51        self._r = r
52        self._p = p
53        self._backend = backend
54
55    def derive(self, key_material):
56        if self._used:
57            raise AlreadyFinalized("Scrypt instances can only be used once.")
58        self._used = True
59
60        utils._check_byteslike("key_material", key_material)
61        return self._backend.derive_scrypt(
62            key_material, self._salt, self._length, self._n, self._r, self._p
63        )
64
65    def verify(self, key_material, expected_key):
66        derived_key = self.derive(key_material)
67        if not constant_time.bytes_eq(derived_key, expected_key):
68            raise InvalidKey("Keys do not match.")
69