scrypt.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. # Copyright 2013 Donald Stufft and individual contributors
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from typing import cast
  15. import nacl.bindings
  16. import nacl.encoding
  17. from nacl import exceptions as exc
  18. from nacl.exceptions import ensure
  19. _strbytes_plus_one = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_STRBYTES
  20. AVAILABLE = nacl.bindings.has_crypto_pwhash_scryptsalsa208sha256
  21. STRPREFIX = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_STRPREFIX
  22. SALTBYTES = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_SALTBYTES
  23. PASSWD_MIN = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN
  24. PASSWD_MAX = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX
  25. PWHASH_SIZE = _strbytes_plus_one - 1
  26. BYTES_MIN = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN
  27. BYTES_MAX = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX
  28. MEMLIMIT_MIN = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN
  29. MEMLIMIT_MAX = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX
  30. OPSLIMIT_MIN = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN
  31. OPSLIMIT_MAX = nacl.bindings.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX
  32. OPSLIMIT_INTERACTIVE = (
  33. nacl.bindings.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE
  34. )
  35. MEMLIMIT_INTERACTIVE = (
  36. nacl.bindings.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE
  37. )
  38. OPSLIMIT_SENSITIVE = (
  39. nacl.bindings.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE
  40. )
  41. MEMLIMIT_SENSITIVE = (
  42. nacl.bindings.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE
  43. )
  44. OPSLIMIT_MODERATE = 8 * OPSLIMIT_INTERACTIVE
  45. MEMLIMIT_MODERATE = 8 * MEMLIMIT_INTERACTIVE
  46. def kdf(
  47. size: int,
  48. password: bytes,
  49. salt: bytes,
  50. opslimit: int = OPSLIMIT_SENSITIVE,
  51. memlimit: int = MEMLIMIT_SENSITIVE,
  52. encoder: nacl.encoding.Encoder = nacl.encoding.RawEncoder,
  53. ) -> bytes:
  54. """
  55. Derive a ``size`` bytes long key from a caller-supplied
  56. ``password`` and ``salt`` pair using the scryptsalsa208sha256
  57. memory-hard construct.
  58. the enclosing module provides the constants
  59. - :py:const:`.OPSLIMIT_INTERACTIVE`
  60. - :py:const:`.MEMLIMIT_INTERACTIVE`
  61. - :py:const:`.OPSLIMIT_SENSITIVE`
  62. - :py:const:`.MEMLIMIT_SENSITIVE`
  63. - :py:const:`.OPSLIMIT_MODERATE`
  64. - :py:const:`.MEMLIMIT_MODERATE`
  65. as a guidance for correct settings respectively for the
  66. interactive login and the long term key protecting sensitive data
  67. use cases.
  68. :param size: derived key size, must be between
  69. :py:const:`.BYTES_MIN` and
  70. :py:const:`.BYTES_MAX`
  71. :type size: int
  72. :param password: password used to seed the key derivation procedure;
  73. it length must be between
  74. :py:const:`.PASSWD_MIN` and
  75. :py:const:`.PASSWD_MAX`
  76. :type password: bytes
  77. :param salt: **RANDOM** salt used in the key derivation procedure;
  78. its length must be exactly :py:const:`.SALTBYTES`
  79. :type salt: bytes
  80. :param opslimit: the time component (operation count)
  81. of the key derivation procedure's computational cost;
  82. it must be between
  83. :py:const:`.OPSLIMIT_MIN` and
  84. :py:const:`.OPSLIMIT_MAX`
  85. :type opslimit: int
  86. :param memlimit: the memory occupation component
  87. of the key derivation procedure's computational cost;
  88. it must be between
  89. :py:const:`.MEMLIMIT_MIN` and
  90. :py:const:`.MEMLIMIT_MAX`
  91. :type memlimit: int
  92. :rtype: bytes
  93. :raises nacl.exceptions.UnavailableError: If called when using a
  94. minimal build of libsodium.
  95. .. versionadded:: 1.2
  96. """
  97. ensure(
  98. AVAILABLE,
  99. "Not available in minimal build",
  100. raising=exc.UnavailableError,
  101. )
  102. ensure(
  103. len(salt) == SALTBYTES,
  104. "The salt must be exactly %s, not %s bytes long"
  105. % (SALTBYTES, len(salt)),
  106. raising=exc.ValueError,
  107. )
  108. n_log2, r, p = nacl.bindings.nacl_bindings_pick_scrypt_params(
  109. opslimit, memlimit
  110. )
  111. maxmem = memlimit + (2 ** 16)
  112. return encoder.encode(
  113. nacl.bindings.crypto_pwhash_scryptsalsa208sha256_ll(
  114. password,
  115. salt,
  116. # Cast safety: n_log2 is a positive integer, and so 2 ** n_log2 is also
  117. # a positive integer. Mypy+typeshed can't deduce this, because there's no
  118. # way to for them to know that n_log2: int is positive.
  119. cast(int, 2 ** n_log2),
  120. r,
  121. p,
  122. maxmem=maxmem,
  123. dklen=size,
  124. )
  125. )
  126. def str(
  127. password: bytes,
  128. opslimit: int = OPSLIMIT_INTERACTIVE,
  129. memlimit: int = MEMLIMIT_INTERACTIVE,
  130. ) -> bytes:
  131. """
  132. Hashes a password with a random salt, using the memory-hard
  133. scryptsalsa208sha256 construct and returning an ascii string
  134. that has all the needed info to check against a future password
  135. The default settings for opslimit and memlimit are those deemed
  136. correct for the interactive user login case.
  137. :param bytes password:
  138. :param int opslimit:
  139. :param int memlimit:
  140. :rtype: bytes
  141. :raises nacl.exceptions.UnavailableError: If called when using a
  142. minimal build of libsodium.
  143. .. versionadded:: 1.2
  144. """
  145. ensure(
  146. AVAILABLE,
  147. "Not available in minimal build",
  148. raising=exc.UnavailableError,
  149. )
  150. return nacl.bindings.crypto_pwhash_scryptsalsa208sha256_str(
  151. password, opslimit, memlimit
  152. )
  153. def verify(password_hash: bytes, password: bytes) -> bool:
  154. """
  155. Takes the output of scryptsalsa208sha256 and compares it against
  156. a user provided password to see if they are the same
  157. :param password_hash: bytes
  158. :param password: bytes
  159. :rtype: boolean
  160. :raises nacl.exceptions.UnavailableError: If called when using a
  161. minimal build of libsodium.
  162. .. versionadded:: 1.2
  163. """
  164. ensure(
  165. AVAILABLE,
  166. "Not available in minimal build",
  167. raising=exc.UnavailableError,
  168. )
  169. ensure(
  170. len(password_hash) == PWHASH_SIZE,
  171. "The password hash must be exactly %s bytes long"
  172. % nacl.bindings.crypto_pwhash_scryptsalsa208sha256_STRBYTES,
  173. raising=exc.ValueError,
  174. )
  175. return nacl.bindings.crypto_pwhash_scryptsalsa208sha256_str_verify(
  176. password_hash, password
  177. )