low_level.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. # SPDX-License-Identifier: MIT
  2. """
  3. Low-level functions if you want to build your own higher level abstractions.
  4. .. warning::
  5. This is a "Hazardous Materials" module. You should **ONLY** use it if
  6. you're 100% absolutely sure that you know what you're doing because this
  7. module is full of land mines, dragons, and dinosaurs with laser guns.
  8. """
  9. from __future__ import annotations
  10. from enum import Enum
  11. from typing import Any, Literal
  12. from _argon2_cffi_bindings import ffi, lib
  13. from .exceptions import HashingError, VerificationError, VerifyMismatchError
  14. __all__ = [
  15. "ARGON2_VERSION",
  16. "Type",
  17. "ffi",
  18. "hash_secret",
  19. "hash_secret_raw",
  20. "verify_secret",
  21. ]
  22. ARGON2_VERSION = lib.ARGON2_VERSION_NUMBER
  23. """
  24. The latest version of the Argon2 algorithm that is supported (and used by
  25. default).
  26. .. versionadded:: 16.1.0
  27. """
  28. class Type(Enum):
  29. """
  30. Enum of Argon2 variants.
  31. Please see :doc:`parameters` on how to pick one.
  32. """
  33. D = lib.Argon2_d
  34. I = lib.Argon2_i # noqa: E741
  35. ID = lib.Argon2_id
  36. def hash_secret(
  37. secret: bytes,
  38. salt: bytes,
  39. time_cost: int,
  40. memory_cost: int,
  41. parallelism: int,
  42. hash_len: int,
  43. type: Type,
  44. version: int = ARGON2_VERSION,
  45. ) -> bytes:
  46. """
  47. Hash *secret* and return an **encoded** hash.
  48. An encoded hash can be directly passed into :func:`verify_secret` as it
  49. contains all parameters and the salt.
  50. Args:
  51. secret: Secret to hash.
  52. salt: A salt_. Should be random and different for each secret.
  53. type: Which Argon2 variant to use.
  54. version: Which Argon2 version to use.
  55. For an explanation of the Argon2 parameters see
  56. :class:`argon2.PasswordHasher`.
  57. Returns:
  58. An encoded Argon2 hash.
  59. Raises:
  60. argon2.exceptions.HashingError: If hashing fails.
  61. .. versionadded:: 16.0.0
  62. .. _salt: https://en.wikipedia.org/wiki/Salt_(cryptography)
  63. """
  64. size = (
  65. lib.argon2_encodedlen(
  66. time_cost,
  67. memory_cost,
  68. parallelism,
  69. len(salt),
  70. hash_len,
  71. type.value,
  72. )
  73. + 1
  74. )
  75. buf = ffi.new("char[]", size)
  76. rv = lib.argon2_hash(
  77. time_cost,
  78. memory_cost,
  79. parallelism,
  80. ffi.new("uint8_t[]", secret),
  81. len(secret),
  82. ffi.new("uint8_t[]", salt),
  83. len(salt),
  84. ffi.NULL,
  85. hash_len,
  86. buf,
  87. size,
  88. type.value,
  89. version,
  90. )
  91. if rv != lib.ARGON2_OK:
  92. raise HashingError(error_to_str(rv))
  93. return ffi.string(buf) # type: ignore[no-any-return]
  94. def hash_secret_raw(
  95. secret: bytes,
  96. salt: bytes,
  97. time_cost: int,
  98. memory_cost: int,
  99. parallelism: int,
  100. hash_len: int,
  101. type: Type,
  102. version: int = ARGON2_VERSION,
  103. ) -> bytes:
  104. """
  105. Hash *password* and return a **raw** hash.
  106. This function takes the same parameters as :func:`hash_secret`.
  107. .. versionadded:: 16.0.0
  108. """
  109. buf = ffi.new("uint8_t[]", hash_len)
  110. rv = lib.argon2_hash(
  111. time_cost,
  112. memory_cost,
  113. parallelism,
  114. ffi.new("uint8_t[]", secret),
  115. len(secret),
  116. ffi.new("uint8_t[]", salt),
  117. len(salt),
  118. buf,
  119. hash_len,
  120. ffi.NULL,
  121. 0,
  122. type.value,
  123. version,
  124. )
  125. if rv != lib.ARGON2_OK:
  126. raise HashingError(error_to_str(rv))
  127. return bytes(ffi.buffer(buf, hash_len))
  128. def verify_secret(hash: bytes, secret: bytes, type: Type) -> Literal[True]:
  129. """
  130. Verify whether *secret* is correct for *hash* of *type*.
  131. Args:
  132. hash:
  133. An encoded Argon2 hash as returned by :func:`hash_secret`.
  134. secret:
  135. The secret to verify whether it matches the one in *hash*.
  136. type: Type for *hash*.
  137. Raises:
  138. argon2.exceptions.VerifyMismatchError:
  139. If verification fails because *hash* is not valid for *secret* of
  140. *type*.
  141. argon2.exceptions.VerificationError:
  142. If verification fails for other reasons.
  143. Returns:
  144. ``True`` on success, raise :exc:`~argon2.exceptions.VerificationError`
  145. otherwise.
  146. .. versionadded:: 16.0.0
  147. .. versionchanged:: 16.1.0
  148. Raise :exc:`~argon2.exceptions.VerifyMismatchError` on mismatches
  149. instead of its more generic superclass.
  150. """
  151. rv = lib.argon2_verify(
  152. ffi.new("char[]", hash),
  153. ffi.new("uint8_t[]", secret),
  154. len(secret),
  155. type.value,
  156. )
  157. if rv == lib.ARGON2_OK:
  158. return True
  159. if rv == lib.ARGON2_VERIFY_MISMATCH:
  160. raise VerifyMismatchError(error_to_str(rv))
  161. raise VerificationError(error_to_str(rv))
  162. def core(context: Any, type: int) -> int:
  163. """
  164. Direct binding to the ``argon2_ctx`` function.
  165. .. warning::
  166. This is a strictly advanced function working on raw C data structures.
  167. Both Argon2's and *argon2-cffi*'s higher-level bindings do a lot of
  168. sanity checks and housekeeping work that *you* are now responsible for
  169. (e.g. clearing buffers). The structure of the *context* object can,
  170. has, and will change with *any* release!
  171. Use at your own peril; *argon2-cffi* does *not* use this binding
  172. itself.
  173. Args:
  174. context:
  175. A CFFI Argon2 context object (i.e. an ``struct Argon2_Context`` /
  176. ``argon2_context``).
  177. type:
  178. Which Argon2 variant to use. You can use the ``value`` field of
  179. :class:`Type`'s fields.
  180. Returns:
  181. An Argon2 error code. Can be transformed into a string using
  182. :func:`error_to_str`.
  183. .. versionadded:: 16.0.0
  184. """
  185. return lib.argon2_ctx(context, type) # type: ignore[no-any-return]
  186. def error_to_str(error: int) -> str:
  187. """
  188. Convert an Argon2 error code into a native string.
  189. Args:
  190. error: An Argon2 error code as returned by :func:`core`.
  191. Returns:
  192. A human-readable string describing the error.
  193. .. versionadded:: 16.0.0
  194. """
  195. return ffi.string(lib.argon2_error_message(error)).decode("ascii") # type: ignore[no-any-return]