Spaces:
Runtime error
Runtime error
| # SPDX-License-Identifier: MIT | |
| """ | |
| Low-level functions if you want to build your own higher level abstractions. | |
| .. warning:: | |
| This is a "Hazardous Materials" module. You should **ONLY** use it if | |
| you're 100% absolutely sure that you know what you're doing because this | |
| module is full of land mines, dragons, and dinosaurs with laser guns. | |
| """ | |
| from __future__ import annotations | |
| from enum import Enum | |
| from typing import Any | |
| from _argon2_cffi_bindings import ffi, lib | |
| from ._typing import Literal | |
| from .exceptions import HashingError, VerificationError, VerifyMismatchError | |
| __all__ = [ | |
| "ARGON2_VERSION", | |
| "Type", | |
| "ffi", | |
| "hash_secret", | |
| "hash_secret_raw", | |
| "verify_secret", | |
| ] | |
| ARGON2_VERSION = lib.ARGON2_VERSION_NUMBER | |
| """ | |
| The latest version of the Argon2 algorithm that is supported (and used by | |
| default). | |
| .. versionadded:: 16.1.0 | |
| """ | |
| class Type(Enum): | |
| """ | |
| Enum of Argon2 variants. | |
| Please see :doc:`parameters` on how to pick one. | |
| """ | |
| D = lib.Argon2_d | |
| I = lib.Argon2_i # noqa: E741 | |
| ID = lib.Argon2_id | |
| def hash_secret( | |
| secret: bytes, | |
| salt: bytes, | |
| time_cost: int, | |
| memory_cost: int, | |
| parallelism: int, | |
| hash_len: int, | |
| type: Type, | |
| version: int = ARGON2_VERSION, | |
| ) -> bytes: | |
| """ | |
| Hash *secret* and return an **encoded** hash. | |
| An encoded hash can be directly passed into :func:`verify_secret` as it | |
| contains all parameters and the salt. | |
| :param bytes secret: Secret to hash. | |
| :param bytes salt: A salt_. Should be random and different for each | |
| secret. | |
| :param Type type: Which Argon2 variant to use. | |
| :param int version: Which Argon2 version to use. | |
| For an explanation of the Argon2 parameters see | |
| :class:`argon2.PasswordHasher`. | |
| :rtype: bytes | |
| :raises argon2.exceptions.HashingError: If hashing fails. | |
| .. versionadded:: 16.0.0 | |
| .. _salt: https://en.wikipedia.org/wiki/Salt_(cryptography) | |
| .. _kibibytes: https://en.wikipedia.org/wiki/Binary_prefix#kibi | |
| """ | |
| size = ( | |
| lib.argon2_encodedlen( | |
| time_cost, | |
| memory_cost, | |
| parallelism, | |
| len(salt), | |
| hash_len, | |
| type.value, | |
| ) | |
| + 1 | |
| ) | |
| buf = ffi.new("char[]", size) | |
| rv = lib.argon2_hash( | |
| time_cost, | |
| memory_cost, | |
| parallelism, | |
| ffi.new("uint8_t[]", secret), | |
| len(secret), | |
| ffi.new("uint8_t[]", salt), | |
| len(salt), | |
| ffi.NULL, | |
| hash_len, | |
| buf, | |
| size, | |
| type.value, | |
| version, | |
| ) | |
| if rv != lib.ARGON2_OK: | |
| raise HashingError(error_to_str(rv)) | |
| return ffi.string(buf) # type: ignore[no-any-return] | |
| def hash_secret_raw( | |
| secret: bytes, | |
| salt: bytes, | |
| time_cost: int, | |
| memory_cost: int, | |
| parallelism: int, | |
| hash_len: int, | |
| type: Type, | |
| version: int = ARGON2_VERSION, | |
| ) -> bytes: | |
| """ | |
| Hash *password* and return a **raw** hash. | |
| This function takes the same parameters as :func:`hash_secret`. | |
| .. versionadded:: 16.0.0 | |
| """ | |
| buf = ffi.new("uint8_t[]", hash_len) | |
| rv = lib.argon2_hash( | |
| time_cost, | |
| memory_cost, | |
| parallelism, | |
| ffi.new("uint8_t[]", secret), | |
| len(secret), | |
| ffi.new("uint8_t[]", salt), | |
| len(salt), | |
| buf, | |
| hash_len, | |
| ffi.NULL, | |
| 0, | |
| type.value, | |
| version, | |
| ) | |
| if rv != lib.ARGON2_OK: | |
| raise HashingError(error_to_str(rv)) | |
| return bytes(ffi.buffer(buf, hash_len)) | |
| def verify_secret(hash: bytes, secret: bytes, type: Type) -> Literal[True]: | |
| """ | |
| Verify whether *secret* is correct for *hash* of *type*. | |
| :param bytes hash: An encoded Argon2 hash as returned by | |
| :func:`hash_secret`. | |
| :param bytes secret: The secret to verify whether it matches the one | |
| in *hash*. | |
| :param Type type: Type for *hash*. | |
| :raises argon2.exceptions.VerifyMismatchError: If verification fails | |
| because *hash* is not valid for *secret* of *type*. | |
| :raises argon2.exceptions.VerificationError: If verification fails for | |
| other reasons. | |
| :return: ``True`` on success, raise | |
| :exc:`~argon2.exceptions.VerificationError` otherwise. | |
| :rtype: bool | |
| .. versionadded:: 16.0.0 | |
| .. versionchanged:: 16.1.0 | |
| Raise :exc:`~argon2.exceptions.VerifyMismatchError` on mismatches | |
| instead of its more generic superclass. | |
| """ | |
| rv = lib.argon2_verify( | |
| ffi.new("char[]", hash), | |
| ffi.new("uint8_t[]", secret), | |
| len(secret), | |
| type.value, | |
| ) | |
| if rv == lib.ARGON2_OK: | |
| return True | |
| if rv == lib.ARGON2_VERIFY_MISMATCH: | |
| raise VerifyMismatchError(error_to_str(rv)) | |
| raise VerificationError(error_to_str(rv)) | |
| def core(context: Any, type: int) -> int: | |
| """ | |
| Direct binding to the ``argon2_ctx`` function. | |
| .. warning:: | |
| This is a strictly advanced function working on raw C data structures. | |
| Both Argon2's and *argon2-cffi*'s higher-level bindings do a lot of | |
| sanity checks and housekeeping work that *you* are now responsible for | |
| (e.g. clearing buffers). The structure of the *context* object can, | |
| has, and will change with *any* release! | |
| Use at your own peril; *argon2-cffi* does *not* use this binding | |
| itself. | |
| :param context: A CFFI Argon2 context object (i.e. an ``struct | |
| Argon2_Context`` / ``argon2_context``). | |
| :param int type: Which Argon2 variant to use. You can use the ``value`` | |
| field of :class:`Type`'s fields. | |
| :rtype: int | |
| :return: An Argon2 error code. Can be transformed into a string using | |
| :func:`error_to_str`. | |
| .. versionadded:: 16.0.0 | |
| """ | |
| return lib.argon2_ctx(context, type) # type: ignore[no-any-return] | |
| def error_to_str(error: int) -> str: | |
| """ | |
| Convert an Argon2 error code into a native string. | |
| :param int error: An Argon2 error code as returned by :func:`core`. | |
| :rtype: str | |
| .. versionadded:: 16.0.0 | |
| """ | |
| return ffi.string(lib.argon2_error_message(error)).decode("ascii") # type: ignore[no-any-return] | |