2025-12-25 upload
This commit is contained in:
79
venv/Lib/site-packages/argon2/__init__.py
Normal file
79
venv/Lib/site-packages/argon2/__init__.py
Normal 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]]
|
||||
91
venv/Lib/site-packages/argon2/__main__.py
Normal file
91
venv/Lib/site-packages/argon2/__main__.py
Normal 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)
|
||||
92
venv/Lib/site-packages/argon2/_legacy.py
Normal file
92
venv/Lib/site-packages/argon2/_legacy.py
Normal 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)
|
||||
287
venv/Lib/site-packages/argon2/_password_hasher.py
Normal file
287
venv/Lib/site-packages/argon2/_password_hasher.py
Normal 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)
|
||||
174
venv/Lib/site-packages/argon2/_utils.py
Normal file
174
venv/Lib/site-packages/argon2/_utils.py
Normal 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)
|
||||
66
venv/Lib/site-packages/argon2/exceptions.py
Normal file
66
venv/Lib/site-packages/argon2/exceptions.py
Normal 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.
|
||||
"""
|
||||
253
venv/Lib/site-packages/argon2/low_level.py
Normal file
253
venv/Lib/site-packages/argon2/low_level.py
Normal 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]
|
||||
79
venv/Lib/site-packages/argon2/profiles.py
Normal file
79
venv/Lib/site-packages/argon2/profiles.py
Normal 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,
|
||||
)
|
||||
0
venv/Lib/site-packages/argon2/py.typed
Normal file
0
venv/Lib/site-packages/argon2/py.typed
Normal file
Reference in New Issue
Block a user