crypto_pwhash.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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 __future__ import absolute_import, division, print_function
  15. import sys
  16. import nacl.exceptions as exc
  17. from nacl._sodium import ffi, lib
  18. from nacl.exceptions import ensure
  19. crypto_pwhash_scryptsalsa208sha256_SALTBYTES = \
  20. lib.crypto_pwhash_scryptsalsa208sha256_saltbytes()
  21. crypto_pwhash_scryptsalsa208sha256_STRBYTES = \
  22. lib.crypto_pwhash_scryptsalsa208sha256_strbytes()
  23. crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE = \
  24. lib.crypto_pwhash_scryptsalsa208sha256_opslimit_interactive()
  25. crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE = \
  26. lib.crypto_pwhash_scryptsalsa208sha256_memlimit_interactive()
  27. crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE = \
  28. lib.crypto_pwhash_scryptsalsa208sha256_opslimit_sensitive()
  29. crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE = \
  30. lib.crypto_pwhash_scryptsalsa208sha256_memlimit_sensitive()
  31. SCRYPT_OPSLIMIT_INTERACTIVE = \
  32. crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE
  33. SCRYPT_MEMLIMIT_INTERACTIVE = \
  34. crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE
  35. SCRYPT_OPSLIMIT_SENSITIVE = \
  36. crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE
  37. SCRYPT_MEMLIMIT_SENSITIVE = \
  38. crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE
  39. SCRYPT_SALTBYTES = \
  40. crypto_pwhash_scryptsalsa208sha256_SALTBYTES
  41. SCRYPT_STRBYTES = \
  42. crypto_pwhash_scryptsalsa208sha256_STRBYTES
  43. SCRYPT_PR_MAX = ((1 << 30) - 1)
  44. LOG2_UINT64_MAX = 63
  45. UINT64_MAX = (1 << 64) - 1
  46. SCRYPT_MAX_MEM = 32 * (1024 * 1024)
  47. def _check_memory_occupation(n, r, p, maxmem=SCRYPT_MAX_MEM):
  48. ensure(r != 0, 'Invalid block size',
  49. raising=exc.ValueError)
  50. ensure(p != 0, 'Invalid parallelization factor',
  51. raising=exc.ValueError)
  52. ensure((n & (n-1)) == 0, 'Cost factor must be a power of 2',
  53. raising=exc.ValueError)
  54. ensure(n > 1, 'Cost factor must be at least 2',
  55. raising=exc.ValueError)
  56. ensure(p <= SCRYPT_PR_MAX / r, 'p*r is greater than {0}'.format(
  57. SCRYPT_PR_MAX),
  58. raising=exc.ValueError)
  59. ensure(n < (1 << (16*r)),
  60. raising=exc.ValueError)
  61. Blen = p * 128 * r
  62. i = UINT64_MAX / 128
  63. ensure(n + 2 <= i / r,
  64. raising=exc.ValueError)
  65. Vlen = 32 * r * (n + 2) * 4
  66. ensure(Blen <= UINT64_MAX - Vlen,
  67. raising=exc.ValueError)
  68. ensure(Blen <= sys.maxsize - Vlen,
  69. raising=exc.ValueError)
  70. ensure(Blen + Vlen <= maxmem,
  71. 'Memory limit would be exceeded with the choosen n, r, p',
  72. raising=exc.ValueError)
  73. def nacl_bindings_pick_scrypt_params(opslimit, memlimit):
  74. """Python implementation of libsodium's pickparams"""
  75. if opslimit < 32768:
  76. opslimit = 32768
  77. r = 8
  78. if opslimit < (memlimit // 32):
  79. p = 1
  80. maxn = opslimit // (4 * r)
  81. for n_log2 in range(1, 63): # pragma: no branch
  82. if (2 ** n_log2) > (maxn // 2):
  83. break
  84. else:
  85. maxn = memlimit // (r * 128)
  86. for n_log2 in range(1, 63): # pragma: no branch
  87. if (2 ** n_log2) > maxn // 2:
  88. break
  89. maxrp = (opslimit // 4) // (2 ** n_log2)
  90. if maxrp > 0x3fffffff: # pragma: no cover
  91. maxrp = 0x3fffffff
  92. p = maxrp // r
  93. return n_log2, r, p
  94. def crypto_pwhash_scryptsalsa208sha256_ll(passwd, salt, n, r, p, dklen=64,
  95. maxmem=SCRYPT_MAX_MEM):
  96. """
  97. Derive a cryptographic key using the ``passwd`` and ``salt``
  98. given as input.
  99. The work factor can be tuned by by picking different
  100. values for the parameters
  101. :param bytes passwd:
  102. :param bytes salt:
  103. :param bytes salt: *must* be *exactly* :py:const:`.SALTBYTES` long
  104. :param int dklen:
  105. :param int opslimit:
  106. :param int n:
  107. :param int r: block size,
  108. :param int p: the parallelism factor
  109. :param int maxmem: the maximum available memory available for scrypt's
  110. operations
  111. :rtype: bytes
  112. """
  113. ensure(isinstance(n, int),
  114. raising=TypeError)
  115. ensure(isinstance(r, int),
  116. raising=TypeError)
  117. ensure(isinstance(p, int),
  118. raising=TypeError)
  119. ensure(isinstance(passwd, bytes),
  120. raising=TypeError)
  121. ensure(isinstance(salt, bytes),
  122. raising=TypeError)
  123. _check_memory_occupation(n, r, p, maxmem)
  124. buf = ffi.new("uint8_t[]", dklen)
  125. ret = lib.crypto_pwhash_scryptsalsa208sha256_ll(passwd, len(passwd),
  126. salt, len(salt),
  127. n, r, p,
  128. buf, dklen)
  129. ensure(ret == 0, 'Unexpected failure in key derivation',
  130. raising=exc.RuntimeError)
  131. return ffi.buffer(ffi.cast("char *", buf), dklen)[:]
  132. def crypto_pwhash_scryptsalsa208sha256_str(
  133. passwd, opslimit=SCRYPT_OPSLIMIT_INTERACTIVE,
  134. memlimit=SCRYPT_MEMLIMIT_INTERACTIVE):
  135. """
  136. Derive a cryptographic key using the ``passwd`` and ``salt``
  137. given as input, returning a string representation which includes
  138. the salt and the tuning parameters.
  139. The returned string can be directly stored as a password hash.
  140. See :py:func:`.crypto_pwhash_scryptsalsa208sha256` for a short
  141. discussion about ``opslimit`` and ``memlimit`` values.
  142. :param bytes passwd:
  143. :param int opslimit:
  144. :param int memlimit:
  145. :return: serialized key hash, including salt and tuning parameters
  146. :rtype: bytes
  147. """
  148. buf = ffi.new("char[]", SCRYPT_STRBYTES)
  149. ret = lib.crypto_pwhash_scryptsalsa208sha256_str(buf, passwd,
  150. len(passwd),
  151. opslimit,
  152. memlimit)
  153. ensure(ret == 0, 'Unexpected failure in password hashing',
  154. raising=exc.RuntimeError)
  155. return ffi.string(buf)
  156. def crypto_pwhash_scryptsalsa208sha256_str_verify(passwd_hash, passwd):
  157. """
  158. Verifies the ``passwd`` against the ``passwd_hash`` that was generated.
  159. Returns True or False depending on the success
  160. :param passwd_hash: bytes
  161. :param passwd: bytes
  162. :rtype: boolean
  163. """
  164. ensure(len(passwd_hash) == SCRYPT_STRBYTES - 1, 'Invalid password hash',
  165. raising=exc.ValueError)
  166. ret = lib.crypto_pwhash_scryptsalsa208sha256_str_verify(passwd_hash,
  167. passwd,
  168. len(passwd))
  169. ensure(ret == 0,
  170. "Wrong password",
  171. raising=exc.InvalidkeyError)
  172. # all went well, therefore:
  173. return True