v1 milestone
This commit is contained in:
79
venv/lib/python3.12/site-packages/argon2/__init__.py
Normal file
79
venv/lib/python3.12/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/python3.12/site-packages/argon2/__main__.py
Normal file
91
venv/lib/python3.12/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)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
92
venv/lib/python3.12/site-packages/argon2/_legacy.py
Normal file
92
venv/lib/python3.12/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/python3.12/site-packages/argon2/_password_hasher.py
Normal file
287
venv/lib/python3.12/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/python3.12/site-packages/argon2/_utils.py
Normal file
174
venv/lib/python3.12/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/python3.12/site-packages/argon2/exceptions.py
Normal file
66
venv/lib/python3.12/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/python3.12/site-packages/argon2/low_level.py
Normal file
253
venv/lib/python3.12/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/python3.12/site-packages/argon2/profiles.py
Normal file
79
venv/lib/python3.12/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/python3.12/site-packages/argon2/py.typed
Normal file
0
venv/lib/python3.12/site-packages/argon2/py.typed
Normal file
Reference in New Issue
Block a user