2025-12-25 upload

This commit is contained in:
“shengyudong”
2025-12-25 11:16:59 +08:00
commit 322ac74336
2241 changed files with 639966 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
# SPDX-License-Identifier: MIT
"""
Argon2 for Python
"""
from . import exceptions, low_level, profiles
from ._legacy import hash_password, hash_password_raw, verify_password
from ._password_hasher import (
DEFAULT_HASH_LENGTH,
DEFAULT_MEMORY_COST,
DEFAULT_PARALLELISM,
DEFAULT_RANDOM_SALT_LENGTH,
DEFAULT_TIME_COST,
PasswordHasher,
)
from ._utils import Parameters, extract_parameters
from .low_level import Type
__title__ = "argon2-cffi"
__author__ = "Hynek Schlawack"
__copyright__ = "Copyright (c) 2015 " + __author__
__license__ = "MIT"
__all__ = [
"DEFAULT_HASH_LENGTH",
"DEFAULT_MEMORY_COST",
"DEFAULT_PARALLELISM",
"DEFAULT_RANDOM_SALT_LENGTH",
"DEFAULT_TIME_COST",
"Parameters",
"PasswordHasher",
"Type",
"exceptions",
"extract_parameters",
"hash_password",
"hash_password_raw",
"low_level",
"profiles",
"verify_password",
]
def __getattr__(name: str) -> str:
dunder_to_metadata = {
"__version__": "version",
"__description__": "summary",
"__uri__": "",
"__url__": "",
"__email__": "",
}
if name not in dunder_to_metadata:
msg = f"module {__name__} has no attribute {name}"
raise AttributeError(msg)
import warnings
from importlib.metadata import metadata
warnings.warn(
f"Accessing argon2.{name} is deprecated and will be "
"removed in a future release. Use importlib.metadata directly "
"to query for argon2-cffi's packaging metadata.",
DeprecationWarning,
stacklevel=2,
)
meta = metadata("argon2-cffi")
if name in ("__uri__", "__url__"):
return meta["Project-URL"].split(" ", 1)[-1]
if name == "__email__":
return meta["Author-email"].split("<", 1)[1].rstrip(">")
return meta[dunder_to_metadata[name]]

View File

@@ -0,0 +1,91 @@
# SPDX-License-Identifier: MIT
from __future__ import annotations
import argparse
import sys
import timeit
from . import (
DEFAULT_HASH_LENGTH,
DEFAULT_MEMORY_COST,
DEFAULT_PARALLELISM,
DEFAULT_TIME_COST,
PasswordHasher,
profiles,
)
def main(argv: list[str]) -> None:
parser = argparse.ArgumentParser(
description="Benchmark Argon2.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument(
"-n", type=int, default=100, help="Number of iterations to measure."
)
parser.add_argument(
"-t", type=int, help="`time_cost`", default=DEFAULT_TIME_COST
)
parser.add_argument(
"-m", type=int, help="`memory_cost`", default=DEFAULT_MEMORY_COST
)
parser.add_argument(
"-p", type=int, help="`parallelism`", default=DEFAULT_PARALLELISM
)
parser.add_argument(
"-l", type=int, help="`hash_length`", default=DEFAULT_HASH_LENGTH
)
parser.add_argument(
"--profile",
type=str,
help="A profile from `argon2.profiles. Takes precedence.",
default=None,
)
args = parser.parse_args(argv[1:])
password = b"secret"
if args.profile:
ph = PasswordHasher.from_parameters(
getattr(profiles, args.profile.upper())
)
else:
ph = PasswordHasher(
time_cost=args.t,
memory_cost=args.m,
parallelism=args.p,
hash_len=args.l,
)
hash = ph.hash(password)
print(f"Running Argon2id {args.n} times with:")
for name, value, units in [
("hash_len", ph.hash_len, "bytes"),
("memory_cost", ph.memory_cost, "KiB"),
("parallelism", ph.parallelism, "threads"),
("time_cost", ph.time_cost, "iterations"),
]:
print(f"{name}: {value} {units}")
print("\nMeasuring...")
duration = timeit.timeit(
f"ph.verify({hash!r}, {password!r})",
setup=f"""\
from argon2 import PasswordHasher
ph = PasswordHasher(
time_cost={args.t!r},
memory_cost={args.m!r},
parallelism={args.p!r},
hash_len={args.l!r},
)
gc.enable()""",
number=args.n,
)
print(f"\n{duration / args.n * 1000:.1f}ms per password verification")
if __name__ == "__main__": # pragma: no cover
main(sys.argv)

View File

@@ -0,0 +1,92 @@
# SPDX-License-Identifier: MIT
"""
Legacy mid-level functions.
"""
from __future__ import annotations
import os
import warnings
from typing import Literal
from ._password_hasher import (
DEFAULT_HASH_LENGTH,
DEFAULT_MEMORY_COST,
DEFAULT_PARALLELISM,
DEFAULT_RANDOM_SALT_LENGTH,
DEFAULT_TIME_COST,
)
from .low_level import Type, hash_secret, hash_secret_raw, verify_secret
_INSTEAD = " is deprecated, use argon2.PasswordHasher instead"
def hash_password(
password: bytes,
salt: bytes | None = None,
time_cost: int = DEFAULT_TIME_COST,
memory_cost: int = DEFAULT_MEMORY_COST,
parallelism: int = DEFAULT_PARALLELISM,
hash_len: int = DEFAULT_HASH_LENGTH,
type: Type = Type.I,
) -> bytes:
"""
Legacy alias for :func:`argon2.low_level.hash_secret` with default
parameters.
.. deprecated:: 16.0.0
Use :class:`argon2.PasswordHasher` for passwords.
"""
warnings.warn(
"argon2.hash_password" + _INSTEAD, DeprecationWarning, stacklevel=2
)
if salt is None:
salt = os.urandom(DEFAULT_RANDOM_SALT_LENGTH)
return hash_secret(
password, salt, time_cost, memory_cost, parallelism, hash_len, type
)
def hash_password_raw(
password: bytes,
salt: bytes | None = None,
time_cost: int = DEFAULT_TIME_COST,
memory_cost: int = DEFAULT_MEMORY_COST,
parallelism: int = DEFAULT_PARALLELISM,
hash_len: int = DEFAULT_HASH_LENGTH,
type: Type = Type.I,
) -> bytes:
"""
Legacy alias for :func:`argon2.low_level.hash_secret_raw` with default
parameters.
.. deprecated:: 16.0.0
Use :class:`argon2.PasswordHasher` for passwords.
"""
warnings.warn(
"argon2.hash_password_raw" + _INSTEAD, DeprecationWarning, stacklevel=2
)
if salt is None:
salt = os.urandom(DEFAULT_RANDOM_SALT_LENGTH)
return hash_secret_raw(
password, salt, time_cost, memory_cost, parallelism, hash_len, type
)
def verify_password(
hash: bytes, password: bytes, type: Type = Type.I
) -> Literal[True]:
"""
Legacy alias for :func:`argon2.low_level.verify_secret` with default
parameters.
.. deprecated:: 16.0.0
Use :class:`argon2.PasswordHasher` for passwords.
"""
warnings.warn(
"argon2.verify_password" + _INSTEAD, DeprecationWarning, stacklevel=2
)
return verify_secret(hash, password, type)

View File

@@ -0,0 +1,287 @@
# SPDX-License-Identifier: MIT
from __future__ import annotations
import os
from typing import ClassVar, Literal
from ._utils import (
Parameters,
_check_types,
extract_parameters,
validate_params_for_platform,
)
from .exceptions import InvalidHashError
from .low_level import Type, hash_secret, verify_secret
from .profiles import get_default_parameters
default_params = get_default_parameters()
DEFAULT_RANDOM_SALT_LENGTH = default_params.salt_len
DEFAULT_HASH_LENGTH = default_params.hash_len
DEFAULT_TIME_COST = default_params.time_cost
DEFAULT_MEMORY_COST = default_params.memory_cost
DEFAULT_PARALLELISM = default_params.parallelism
def _ensure_bytes(s: bytes | str, encoding: str) -> bytes:
"""
Ensure *s* is a bytes string. Encode using *encoding* if it isn't.
"""
if isinstance(s, bytes):
return s
return s.encode(encoding)
class PasswordHasher:
r"""
High level class to hash passwords with sensible defaults.
Uses Argon2\ **id** by default and uses a random salt_ for hashing. But it
can verify any type of Argon2 as long as the hash is correctly encoded.
The reason for this being a class is both for convenience to carry
parameters and to verify the parameters only *once*. Any unnecessary
slowdown when hashing is a tangible advantage for a brute-force attacker.
Args:
time_cost:
Defines the amount of computation realized and therefore the
execution time, given in number of iterations.
memory_cost: Defines the memory usage, given in kibibytes_.
parallelism:
Defines the number of parallel threads (*changes* the resulting
hash value).
hash_len: Length of the hash in bytes.
salt_len: Length of random salt to be generated for each password.
encoding:
The Argon2 C library expects bytes. So if :meth:`hash` or
:meth:`verify` are passed a ``str``, it will be encoded using this
encoding.
type:
Argon2 type to use. Only change for interoperability with legacy
systems.
.. versionadded:: 16.0.0
.. versionchanged:: 18.2.0
Switch from Argon2i to Argon2id based on the recommendation by the
current RFC draft. See also :doc:`parameters`.
.. versionchanged:: 18.2.0
Changed default *memory_cost* to 100 MiB and default *parallelism* to 8.
.. versionchanged:: 18.2.0 ``verify`` now will determine the type of hash.
.. versionchanged:: 18.3.0 The Argon2 type is configurable now.
.. versionadded:: 21.2.0 :meth:`from_parameters`
.. versionchanged:: 21.2.0
Changed defaults to :data:`argon2.profiles.RFC_9106_LOW_MEMORY`.
.. _salt: https://en.wikipedia.org/wiki/Salt_(cryptography)
.. _kibibytes: https://en.wikipedia.org/wiki/Binary_prefix#kibi
"""
__slots__ = ["_parameters", "encoding"]
_parameters: Parameters
encoding: str
def __init__(
self,
time_cost: int = DEFAULT_TIME_COST,
memory_cost: int = DEFAULT_MEMORY_COST,
parallelism: int = DEFAULT_PARALLELISM,
hash_len: int = DEFAULT_HASH_LENGTH,
salt_len: int = DEFAULT_RANDOM_SALT_LENGTH,
encoding: str = "utf-8",
type: Type = Type.ID,
):
e = _check_types(
time_cost=(time_cost, int),
memory_cost=(memory_cost, int),
parallelism=(parallelism, int),
hash_len=(hash_len, int),
salt_len=(salt_len, int),
encoding=(encoding, str),
type=(type, Type),
)
if e:
raise TypeError(e)
params = Parameters(
type=type,
version=19,
salt_len=salt_len,
hash_len=hash_len,
time_cost=time_cost,
memory_cost=memory_cost,
parallelism=parallelism,
)
validate_params_for_platform(params)
# Cache a Parameters object for check_needs_rehash.
self._parameters = params
self.encoding = encoding
@classmethod
def from_parameters(cls, params: Parameters) -> PasswordHasher:
"""
Construct a `PasswordHasher` from *params*.
Returns:
A `PasswordHasher` instance with the parameters from *params*.
.. versionadded:: 21.2.0
"""
return cls(
time_cost=params.time_cost,
memory_cost=params.memory_cost,
parallelism=params.parallelism,
hash_len=params.hash_len,
salt_len=params.salt_len,
type=params.type,
)
@property
def time_cost(self) -> int:
return self._parameters.time_cost
@property
def memory_cost(self) -> int:
return self._parameters.memory_cost
@property
def parallelism(self) -> int:
return self._parameters.parallelism
@property
def hash_len(self) -> int:
return self._parameters.hash_len
@property
def salt_len(self) -> int:
return self._parameters.salt_len
@property
def type(self) -> Type:
return self._parameters.type
def hash(self, password: str | bytes, *, salt: bytes | None = None) -> str:
"""
Hash *password* and return an encoded hash.
Args:
password: Password to hash.
salt:
If None, a random salt is securely created.
.. danger::
You should **not** pass a salt unless you really know what
you are doing.
Raises:
argon2.exceptions.HashingError: If hashing fails.
Returns:
Hashed *password*.
.. versionadded:: 23.1.0 *salt* parameter
"""
return hash_secret(
secret=_ensure_bytes(password, self.encoding),
salt=salt or os.urandom(self.salt_len),
time_cost=self.time_cost,
memory_cost=self.memory_cost,
parallelism=self.parallelism,
hash_len=self.hash_len,
type=self.type,
).decode("ascii")
_header_to_type: ClassVar[dict[bytes, Type]] = {
b"$argon2i$": Type.I,
b"$argon2d$": Type.D,
b"$argon2id": Type.ID,
}
def verify(
self, hash: str | bytes, password: str | bytes
) -> Literal[True]:
"""
Verify that *password* matches *hash*.
.. warning::
It is assumed that the caller is in full control of the hash. No
other parsing than the determination of the hash type is done by
*argon2-cffi*.
Args:
hash: An encoded hash as returned from :meth:`PasswordHasher.hash`.
password: The password to verify.
Raises:
argon2.exceptions.VerifyMismatchError:
If verification fails because *hash* is not valid for
*password*.
argon2.exceptions.VerificationError:
If verification fails for other reasons.
argon2.exceptions.InvalidHashError:
If *hash* is so clearly invalid, that it couldn't be passed to
Argon2.
Returns:
``True`` on success, otherwise an exception is raised.
.. versionchanged:: 16.1.0
Raise :exc:`~argon2.exceptions.VerifyMismatchError` on mismatches
instead of its more generic superclass.
.. versionadded:: 18.2.0 Hash type agility.
"""
hash = _ensure_bytes(hash, "ascii")
try:
hash_type = self._header_to_type[hash[:9]]
except LookupError:
raise InvalidHashError from None
return verify_secret(
hash, _ensure_bytes(password, self.encoding), hash_type
)
def check_needs_rehash(self, hash: str | bytes) -> bool:
"""
Check whether *hash* was created using the instance's parameters.
Whenever your Argon2 parameters -- or *argon2-cffi*'s defaults! --
change, you should rehash your passwords at the next opportunity. The
common approach is to do that whenever a user logs in, since that
should be the only time when you have access to the cleartext
password.
Therefore it's best practice to check -- and if necessary rehash --
passwords after each successful authentication.
Args:
hash: An encoded Argon2 password hash.
Returns:
Whether *hash* was created using the instance's parameters.
.. versionadded:: 18.2.0
.. versionchanged:: 24.1.0 Accepts bytes for *hash*.
"""
if isinstance(hash, bytes):
hash = hash.decode("ascii")
return self._parameters != extract_parameters(hash)

View File

@@ -0,0 +1,174 @@
# SPDX-License-Identifier: MIT
from __future__ import annotations
import platform
import sys
from dataclasses import dataclass
from typing import Any
from .exceptions import InvalidHashError, UnsupportedParametersError
from .low_level import Type
NoneType = type(None)
def _check_types(**kw: Any) -> str | None:
"""
Check each ``name: (value, types)`` in *kw*.
Returns a human-readable string of all violations or `None``.
"""
errors = []
for name, (value, types) in kw.items():
if not isinstance(value, types):
if isinstance(types, tuple):
types = ", or ".join(t.__name__ for t in types)
else:
types = types.__name__
errors.append(
f"'{name}' must be a {types} (got {type(value).__name__})"
)
if errors != []:
return ", ".join(errors) + "."
return None
def _is_wasm() -> bool:
return sys.platform == "emscripten" or platform.machine() in [
"wasm32",
"wasm64",
]
def _decoded_str_len(length: int) -> int:
"""
Compute how long an encoded string of length *l* becomes.
"""
rem = length % 4
if rem == 3:
last_group_len = 2
elif rem == 2:
last_group_len = 1
else:
last_group_len = 0
return length // 4 * 3 + last_group_len
@dataclass
class Parameters:
"""
Argon2 hash parameters.
See :doc:`parameters` on how to pick them.
Attributes:
type: Hash type.
version: Argon2 version.
salt_len: Length of the salt in bytes.
hash_len: Length of the hash in bytes.
time_cost: Time cost in iterations.
memory_cost: Memory cost in kibibytes.
parallelism: Number of parallel threads.
.. versionadded:: 18.2.0
"""
type: Type
version: int
salt_len: int
hash_len: int
time_cost: int
memory_cost: int
parallelism: int
__slots__ = (
"hash_len",
"memory_cost",
"parallelism",
"salt_len",
"time_cost",
"type",
"version",
)
_NAME_TO_TYPE = {"argon2id": Type.ID, "argon2i": Type.I, "argon2d": Type.D}
_REQUIRED_KEYS = sorted(("v", "m", "t", "p"))
def extract_parameters(hash: str) -> Parameters:
"""
Extract parameters from an encoded *hash*.
Args:
hash: An encoded Argon2 hash string.
Returns:
The parameters used to create the hash.
.. versionadded:: 18.2.0
"""
parts = hash.split("$")
# Backwards compatibility for Argon v1.2 hashes
if len(parts) == 5:
parts.insert(2, "v=18")
if len(parts) != 6:
raise InvalidHashError
if parts[0]:
raise InvalidHashError
try:
type = _NAME_TO_TYPE[parts[1]]
kvs = {
k: int(v)
for k, v in (
s.split("=") for s in [parts[2], *parts[3].split(",")]
)
}
except Exception: # noqa: BLE001
raise InvalidHashError from None
if sorted(kvs.keys()) != _REQUIRED_KEYS:
raise InvalidHashError
return Parameters(
type=type,
salt_len=_decoded_str_len(len(parts[4])),
hash_len=_decoded_str_len(len(parts[5])),
version=kvs["v"],
time_cost=kvs["t"],
memory_cost=kvs["m"],
parallelism=kvs["p"],
)
def validate_params_for_platform(params: Parameters) -> None:
"""
Validate *params* against current platform.
Args:
params: Parameters to be validated
Returns:
None
"""
if _is_wasm() and params.parallelism != 1:
msg = "In WebAssembly environments `parallelism` must be 1."
raise UnsupportedParametersError(msg)

View File

@@ -0,0 +1,66 @@
# SPDX-License-Identifier: MIT
from __future__ import annotations
class Argon2Error(Exception):
"""
Superclass of all argon2 exceptions.
Never thrown directly.
"""
class VerificationError(Argon2Error):
"""
Verification failed.
You can find the original error message from Argon2 in ``args[0]``.
"""
class VerifyMismatchError(VerificationError):
"""
The secret does not match the hash.
Subclass of :exc:`argon2.exceptions.VerificationError`.
.. versionadded:: 16.1.0
"""
class HashingError(Argon2Error):
"""
Raised if hashing failed.
You can find the original error message from Argon2 in ``args[0]``.
"""
class InvalidHashError(ValueError):
"""
Raised if the hash is invalid before passing it to Argon2.
.. versionadded:: 23.1.0
As a replacement for :exc:`argon2.exceptions.InvalidHash`.
"""
class UnsupportedParametersError(ValueError):
"""
Raised if the current platform does not support the parameters.
For example, in WebAssembly parallelism must be set to 1.
.. versionadded:: 25.1.0
"""
InvalidHash = InvalidHashError
"""
Deprecated alias for :class:`InvalidHashError`.
.. versionadded:: 18.2.0
.. deprecated:: 23.1.0
Use :exc:`argon2.exceptions.InvalidHashError` instead.
"""

View File

@@ -0,0 +1,253 @@
# 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, Literal
from _argon2_cffi_bindings import ffi, lib
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.
Args:
secret: Secret to hash.
salt: A salt_. Should be random and different for each secret.
type: Which Argon2 variant to use.
version: Which Argon2 version to use.
For an explanation of the Argon2 parameters see
:class:`argon2.PasswordHasher`.
Returns:
An encoded Argon2 hash.
Raises:
argon2.exceptions.HashingError: If hashing fails.
.. versionadded:: 16.0.0
.. _salt: https://en.wikipedia.org/wiki/Salt_(cryptography)
"""
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*.
Args:
hash:
An encoded Argon2 hash as returned by :func:`hash_secret`.
secret:
The secret to verify whether it matches the one in *hash*.
type: Type for *hash*.
Raises:
argon2.exceptions.VerifyMismatchError:
If verification fails because *hash* is not valid for *secret* of
*type*.
argon2.exceptions.VerificationError:
If verification fails for other reasons.
Returns:
``True`` on success, raise :exc:`~argon2.exceptions.VerificationError`
otherwise.
.. 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.
Args:
context:
A CFFI Argon2 context object (i.e. an ``struct Argon2_Context`` /
``argon2_context``).
type:
Which Argon2 variant to use. You can use the ``value`` field of
:class:`Type`'s fields.
Returns:
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.
Args:
error: An Argon2 error code as returned by :func:`core`.
Returns:
A human-readable string describing the error.
.. versionadded:: 16.0.0
"""
return ffi.string(lib.argon2_error_message(error)).decode("ascii") # type: ignore[no-any-return]

View File

@@ -0,0 +1,79 @@
# SPDX-License-Identifier: MIT
"""
This module offers access to standardized parameters that you can load using
:meth:`argon2.PasswordHasher.from_parameters()`. See the `source code
<https://github.com/hynek/argon2-cffi/blob/main/src/argon2/profiles.py>`_ for
concrete values and :doc:`parameters` for more information.
.. versionadded:: 21.2.0
"""
from __future__ import annotations
import dataclasses
from ._utils import Parameters, _is_wasm
from .low_level import Type
def get_default_parameters() -> Parameters:
"""
Create default parameters for current platform.
Returns:
Default, compatible, parameters for current platform.
.. versionadded:: 25.1.0
"""
params = RFC_9106_LOW_MEMORY
if _is_wasm():
params = dataclasses.replace(params, parallelism=1)
return params
# FIRST RECOMMENDED option per RFC 9106.
RFC_9106_HIGH_MEMORY = Parameters(
type=Type.ID,
version=19,
salt_len=16,
hash_len=32,
time_cost=1,
memory_cost=2097152, # 2 GiB
parallelism=4,
)
# SECOND RECOMMENDED option per RFC 9106.
RFC_9106_LOW_MEMORY = Parameters(
type=Type.ID,
version=19,
salt_len=16,
hash_len=32,
time_cost=3,
memory_cost=65536, # 64 MiB
parallelism=4,
)
# The pre-RFC defaults in argon2-cffi 18.2.0 - 21.1.0.
PRE_21_2 = Parameters(
type=Type.ID,
version=19,
salt_len=16,
hash_len=16,
time_cost=2,
memory_cost=102400, # 100 MiB
parallelism=8,
)
# Only for testing!
CHEAPEST = Parameters(
type=Type.ID,
version=19,
salt_len=8,
hash_len=4,
time_cost=1,
memory_cost=8,
parallelism=1,
)

View File