Uploaded Test files

This commit is contained in:
Batuhan Berk Başoğlu 2020-11-12 11:05:57 -05:00
parent f584ad9d97
commit 2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions

View file

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
from . import exceptions, low_level
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
__version__ = "20.1.0"
__title__ = "argon2-cffi"
__description__ = "The secure Argon2 password hashing algorithm."
__url__ = "https://argon2-cffi.readthedocs.io/"
__uri__ = __url__
__doc__ = __description__ + " <" + __url__ + ">"
__author__ = "Hynek Schlawack"
__email__ = "hs@ox.cx"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2015 " + __author__
__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",
"verify_password",
]

View file

@ -0,0 +1,89 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
import argparse
import sys
import timeit
import six
from . import (
DEFAULT_HASH_LENGTH,
DEFAULT_MEMORY_COST,
DEFAULT_PARALLELISM,
DEFAULT_TIME_COST,
PasswordHasher,
)
def main(argv):
parser = argparse.ArgumentParser(description="Benchmark Argon2.")
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="`parallellism`", default=DEFAULT_PARALLELISM
)
parser.add_argument(
"-l", type=int, help="`hash_length`", default=DEFAULT_HASH_LENGTH
)
args = parser.parse_args(argv[1:])
password = b"secret"
ph = PasswordHasher(
time_cost=args.t,
memory_cost=args.m,
parallelism=args.p,
hash_len=args.l,
)
hash = ph.hash(password)
params = {
"time_cost": (args.t, "iterations"),
"memory_cost": (args.m, "KiB"),
"parallelism": (args.p, "threads"),
"hash_len": (args.l, "bytes"),
}
print("Running Argon2id %d times with:" % (args.n,))
for k, v in sorted(six.iteritems(params)):
print("%s: %d %s" % (k, v[0], v[1]))
print("\nMeasuring...")
duration = timeit.timeit(
"ph.verify({hash!r}, {password!r})".format(
hash=hash, password=password
),
setup="""\
from argon2 import PasswordHasher, Type
ph = PasswordHasher(
time_cost={time_cost!r},
memory_cost={memory_cost!r},
parallelism={parallelism!r},
hash_len={hash_len!r},
)
gc.enable()""".format(
time_cost=args.t,
memory_cost=args.m,
parallelism=args.p,
hash_len=args.l,
),
number=args.n,
)
print(
"\n{0:.3}ms per password verification".format(duration / args.n * 1000)
)
if __name__ == "__main__": # pragma: nocover
main(sys.argv)

Binary file not shown.

View file

@ -0,0 +1,191 @@
from __future__ import absolute_import, division, print_function
import os
import sys
from cffi import FFI
include_dirs = [os.path.join("extras", "libargon2", "include")]
use_system_argon2 = os.environ.get("ARGON2_CFFI_USE_SYSTEM", "0") == "1"
if use_system_argon2:
include_dirs = []
# Add vendored integer types headers.
if "win32" in str(sys.platform).lower():
int_base = os.path.join("extras", "msinttypes")
inttypes = os.path.join(int_base, "inttypes")
stdint = os.path.join(int_base, "stdint")
vi = sys.version_info[0:2]
if vi in [(2, 6), (2, 7)]:
# VS 2008 needs both.
include_dirs += [inttypes, stdint]
elif vi in [(3, 3), (3, 4)]:
# VS 2010 needs only inttypes.h
include_dirs += [inttypes]
ffi = FFI()
ffi.set_source(
"_ffi",
"#include <argon2.h>",
include_dirs=include_dirs,
libraries=["argon2"],
)
ffi.cdef(
"""\
typedef enum Argon2_type {
Argon2_d = ...,
Argon2_i = ...,
Argon2_id = ...,
} argon2_type;
typedef enum Argon2_version {
ARGON2_VERSION_10 = ...,
ARGON2_VERSION_13 = ...,
ARGON2_VERSION_NUMBER = ...
} argon2_version;
int argon2_hash(const uint32_t t_cost, const uint32_t m_cost,
const uint32_t parallelism, const void *pwd,
const size_t pwdlen, const void *salt,
const size_t saltlen, void *hash,
const size_t hashlen, char *encoded,
const size_t encodedlen, argon2_type type,
const uint32_t version);
int argon2_verify(const char *encoded, const void *pwd,
const size_t pwdlen, argon2_type type);
const char *argon2_error_message(int error_code);
typedef int (*allocate_fptr)(uint8_t **memory, size_t bytes_to_allocate);
typedef void (*deallocate_fptr)(uint8_t *memory, size_t bytes_to_allocate);
typedef struct Argon2_Context {
uint8_t *out; /* output array */
uint32_t outlen; /* digest length */
uint8_t *pwd; /* password array */
uint32_t pwdlen; /* password length */
uint8_t *salt; /* salt array */
uint32_t saltlen; /* salt length */
uint8_t *secret; /* key array */
uint32_t secretlen; /* key length */
uint8_t *ad; /* associated data array */
uint32_t adlen; /* associated data length */
uint32_t t_cost; /* number of passes */
uint32_t m_cost; /* amount of memory requested (KB) */
uint32_t lanes; /* number of lanes */
uint32_t threads; /* maximum number of threads */
uint32_t version; /* version number */
allocate_fptr allocate_cbk; /* pointer to memory allocator */
deallocate_fptr free_cbk; /* pointer to memory deallocator */
uint32_t flags; /* array of bool options */
} argon2_context;
int argon2_ctx(argon2_context *context, argon2_type type);
/* Error codes */
typedef enum Argon2_ErrorCodes {
ARGON2_OK = ...,
ARGON2_OUTPUT_PTR_NULL = ...,
ARGON2_OUTPUT_TOO_SHORT = ...,
ARGON2_OUTPUT_TOO_LONG = ...,
ARGON2_PWD_TOO_SHORT = ...,
ARGON2_PWD_TOO_LONG = ...,
ARGON2_SALT_TOO_SHORT = ...,
ARGON2_SALT_TOO_LONG = ...,
ARGON2_AD_TOO_SHORT = ...,
ARGON2_AD_TOO_LONG = ...,
ARGON2_SECRET_TOO_SHORT = ...,
ARGON2_SECRET_TOO_LONG = ...,
ARGON2_TIME_TOO_SMALL = ...,
ARGON2_TIME_TOO_LARGE = ...,
ARGON2_MEMORY_TOO_LITTLE = ...,
ARGON2_MEMORY_TOO_MUCH = ...,
ARGON2_LANES_TOO_FEW = ...,
ARGON2_LANES_TOO_MANY = ...,
ARGON2_PWD_PTR_MISMATCH = ..., /* NULL ptr with non-zero length */
ARGON2_SALT_PTR_MISMATCH = ..., /* NULL ptr with non-zero length */
ARGON2_SECRET_PTR_MISMATCH = ..., /* NULL ptr with non-zero length */
ARGON2_AD_PTR_MISMATCH = ..., /* NULL ptr with non-zero length */
ARGON2_MEMORY_ALLOCATION_ERROR = ...,
ARGON2_FREE_MEMORY_CBK_NULL = ...,
ARGON2_ALLOCATE_MEMORY_CBK_NULL = ...,
ARGON2_INCORRECT_PARAMETER = ...,
ARGON2_INCORRECT_TYPE = ...,
ARGON2_OUT_PTR_MISMATCH = ...,
ARGON2_THREADS_TOO_FEW = ...,
ARGON2_THREADS_TOO_MANY = ...,
ARGON2_MISSING_ARGS = ...,
ARGON2_ENCODING_FAIL = ...,
ARGON2_DECODING_FAIL = ...,
ARGON2_THREAD_FAIL = ...,
ARGON2_DECODING_LENGTH_FAIL= ...,
ARGON2_VERIFY_MISMATCH = ...,
} argon2_error_codes;
#define ARGON2_FLAG_CLEAR_PASSWORD ...
#define ARGON2_FLAG_CLEAR_SECRET ...
#define ARGON2_DEFAULT_FLAGS ...
#define ARGON2_MIN_LANES ...
#define ARGON2_MAX_LANES ...
#define ARGON2_MIN_THREADS ...
#define ARGON2_MAX_THREADS ...
#define ARGON2_SYNC_POINTS ...
#define ARGON2_MIN_OUTLEN ...
#define ARGON2_MAX_OUTLEN ...
#define ARGON2_MIN_MEMORY ...
#define ARGON2_MAX_MEMORY_BITS ...
#define ARGON2_MAX_MEMORY ...
#define ARGON2_MIN_TIME ...
#define ARGON2_MAX_TIME ...
#define ARGON2_MIN_PWD_LENGTH ...
#define ARGON2_MAX_PWD_LENGTH ...
#define ARGON2_MIN_AD_LENGTH ...
#define ARGON2_MAX_AD_LENGTH ...
#define ARGON2_MIN_SALT_LENGTH ...
#define ARGON2_MAX_SALT_LENGTH ...
#define ARGON2_MIN_SECRET ...
#define ARGON2_MAX_SECRET ...
uint32_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost,
uint32_t parallelism, uint32_t saltlen,
uint32_t hashlen, argon2_type type);
"""
)
if __name__ == "__main__":
ffi.compile()

View file

@ -0,0 +1,70 @@
"""
Legacy mid-level functions.
"""
from __future__ import absolute_import, division, print_function
import os
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
def hash_password(
password,
salt=None,
time_cost=DEFAULT_TIME_COST,
memory_cost=DEFAULT_MEMORY_COST,
parallelism=DEFAULT_PARALLELISM,
hash_len=DEFAULT_HASH_LENGTH,
type=Type.I,
):
"""
Legacy alias for :func:`hash_secret` with default parameters.
.. deprecated:: 16.0.0
Use :class:`argon2.PasswordHasher` for passwords.
"""
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,
salt=None,
time_cost=DEFAULT_TIME_COST,
memory_cost=DEFAULT_MEMORY_COST,
parallelism=DEFAULT_PARALLELISM,
hash_len=DEFAULT_HASH_LENGTH,
type=Type.I,
):
"""
Legacy alias for :func:`hash_secret_raw` with default parameters.
.. deprecated:: 16.0.0
Use :class:`argon2.PasswordHasher` for passwords.
"""
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, password, type=Type.I):
"""
Legacy alias for :func:`verify_secret` with default parameters.
.. deprecated:: 16.0.0
Use :class:`argon2.PasswordHasher` for passwords.
"""
return verify_secret(hash, password, type)

View file

@ -0,0 +1,212 @@
from __future__ import absolute_import, division, print_function
import os
from ._utils import Parameters, _check_types, extract_parameters
from .exceptions import InvalidHash
from .low_level import Type, hash_secret, verify_secret
DEFAULT_RANDOM_SALT_LENGTH = 16
DEFAULT_HASH_LENGTH = 16
DEFAULT_TIME_COST = 2
DEFAULT_MEMORY_COST = 102400
DEFAULT_PARALLELISM = 8
def _ensure_bytes(s, encoding):
"""
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(object):
r"""
High level class to hash passwords with sensible defaults.
Uses Argon2\ **id** by default and always 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.
:param int time_cost: Defines the amount of computation realized and
therefore the execution time, given in number of iterations.
:param int memory_cost: Defines the memory usage, given in kibibytes_.
:param int parallelism: Defines the number of parallel threads (*changes*
the resulting hash value).
:param int hash_len: Length of the hash in bytes.
:param int salt_len: Length of random salt to be generated for each
password.
:param str encoding: The Argon2 C library expects bytes. So if
:meth:`hash` or :meth:`verify` are passed an unicode string, it will be
encoded using this encoding.
:param Type 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.
.. 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.
.. _salt: https://en.wikipedia.org/wiki/Salt_(cryptography)
.. _kibibytes: https://en.wikipedia.org/wiki/Binary_prefix#kibi
.. _RFC: https://tools.ietf.org/html/draft-irtf-cfrg-argon2-04#section-4
"""
__slots__ = ["_parameters", "encoding"]
def __init__(
self,
time_cost=DEFAULT_TIME_COST,
memory_cost=DEFAULT_MEMORY_COST,
parallelism=DEFAULT_PARALLELISM,
hash_len=DEFAULT_HASH_LENGTH,
salt_len=DEFAULT_RANDOM_SALT_LENGTH,
encoding="utf-8",
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)
# Cache a Parameters object for check_needs_rehash.
self._parameters = Parameters(
type=type,
version=19,
salt_len=salt_len,
hash_len=hash_len,
time_cost=time_cost,
memory_cost=memory_cost,
parallelism=parallelism,
)
self.encoding = encoding
@property
def time_cost(self):
return self._parameters.time_cost
@property
def memory_cost(self):
return self._parameters.memory_cost
@property
def parallelism(self):
return self._parameters.parallelism
@property
def hash_len(self):
return self._parameters.hash_len
@property
def salt_len(self):
return self._parameters.salt_len
@property
def type(self):
return self._parameters.type
def hash(self, password):
"""
Hash *password* and return an encoded hash.
:param password: Password to hash.
:type password: ``bytes`` or ``unicode``
:raises argon2.exceptions.HashingError: If hashing fails.
:rtype: unicode
"""
return hash_secret(
secret=_ensure_bytes(password, self.encoding),
salt=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 = {
b"$argon2i$": Type.I,
b"$argon2d$": Type.D,
b"$argon2id": Type.ID,
}
def verify(self, hash, password):
"""
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``.
:param hash: An encoded hash as returned from
:meth:`PasswordHasher.hash`.
:type hash: ``bytes`` or ``unicode``
:param password: The password to verify.
:type password: ``bytes`` or ``unicode``
:raises argon2.exceptions.VerifyMismatchError: If verification fails
because *hash* is not valid for *password*.
:raises argon2.exceptions.VerificationError: If verification fails for
other reasons.
:raises argon2.exceptions.InvalidHash: If *hash* is so clearly
invalid, that it couldn't be passed to Argon2.
:return: ``True`` on success, raise
:exc:`~argon2.exceptions.VerificationError` otherwise.
:rtype: bool
.. 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 (IndexError, KeyError, LookupError):
raise InvalidHash()
return verify_secret(
hash, _ensure_bytes(password, self.encoding), hash_type
)
def check_needs_rehash(self, hash):
"""
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.
:rtype: bool
.. versionadded:: 18.2.0
"""
return self._parameters != extract_parameters(hash)

View file

@ -0,0 +1,198 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
from six import iteritems
from .exceptions import InvalidHash
from .low_level import Type
NoneType = type(None)
def _check_types(**kw):
"""
Check each ``name: (value, types)`` in *kw*.
Returns a human-readable string of all violations or `None``.
"""
errors = []
for name, (value, types) in iteritems(kw):
if not isinstance(value, types):
if isinstance(types, tuple):
types = ", or ".join(t.__name__ for t in types)
else:
types = types.__name__
errors.append(
"'{name}' must be a {type} (got {actual})".format(
name=name, type=types, actual=type(value).__name__
)
)
if errors != []:
return ", ".join(errors) + "."
def _encoded_str_len(l):
"""
Compute how long a byte string of length *l* becomes if encoded to hex.
"""
return (l << 2) / 3 + 2
def _decoded_str_len(l):
"""
Compute how long an encoded string of length *l* becomes.
"""
rem = l % 4
if rem == 3:
last_group_len = 2
elif rem == 2:
last_group_len = 1
else:
last_group_len = 0
return l // 4 * 3 + last_group_len
class Parameters(object):
"""
Argon2 hash parameters.
See :doc:`parameters` on how to pick them.
:ivar Type type: Hash type.
:ivar int version: Argon2 version.
:ivar int salt_len: Length of the salt in bytes.
:ivar int hash_len: Length of the hash in bytes.
:ivar int time_cost: Time cost in iterations.
:ivar int memory_cost: Memory cost in kibibytes.
:ivar int parallelism: Number of parallel threads.
.. versionadded:: 18.2.0
"""
__slots__ = [
"type",
"version",
"salt_len",
"hash_len",
"time_cost",
"memory_cost",
"parallelism",
]
def __init__(
self,
type,
version,
salt_len,
hash_len,
time_cost,
memory_cost,
parallelism,
):
self.type = type
self.version = version
self.salt_len = salt_len
self.hash_len = hash_len
self.time_cost = time_cost
self.memory_cost = memory_cost
self.parallelism = parallelism
def __repr__(self):
return (
"<Parameters(type=%r, version=%d, hash_len=%d, salt_len=%d, "
"time_cost=%d, memory_cost=%d, parallelelism=%d)>"
% (
self.type,
self.version,
self.hash_len,
self.salt_len,
self.time_cost,
self.memory_cost,
self.parallelism,
)
)
def __eq__(self, other):
if self.__class__ != other.__class__:
return NotImplemented
return (
self.type,
self.version,
self.salt_len,
self.hash_len,
self.time_cost,
self.memory_cost,
self.parallelism,
) == (
other.type,
other.version,
other.salt_len,
other.hash_len,
other.time_cost,
other.memory_cost,
other.parallelism,
)
def __ne__(self, other):
if self.__class__ != other.__class__:
return NotImplemented
return not self.__eq__(other)
_NAME_TO_TYPE = {"argon2id": Type.ID, "argon2i": Type.I, "argon2d": Type.D}
_REQUIRED_KEYS = sorted(("v", "m", "t", "p"))
def extract_parameters(hash):
"""
Extract parameters from an encoded *hash*.
:param str params: An encoded Argon2 hash string.
:rtype: Parameters
.. 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 InvalidHash
if parts[0] != "":
raise InvalidHash
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:
raise InvalidHash
if sorted(kvs.keys()) != _REQUIRED_KEYS:
raise InvalidHash
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"],
)

View file

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
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 InvalidHash(ValueError):
"""
Raised if the hash is invalid before passing it to Argon2.
.. versionadded:: 18.2.0
"""

View file

@ -0,0 +1,256 @@
# -*- coding: utf-8 -*-
"""
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 youre doing because this
module is full of land mines, dragons, and dinosaurs with laser guns.
"""
from __future__ import absolute_import, division, print_function
from enum import Enum
from six import PY3
from ._ffi 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
r"""
Argon2\ **d** is faster and uses data-depending memory access, which makes
it less suitable for hashing secrets and more suitable for cryptocurrencies
and applications with no threats from side-channel timing attacks.
"""
I = lib.Argon2_i
r"""
Argon2\ **i** uses data-independent memory access. Argon2i is slower as
it makes more passes over the memory to protect from tradeoff attacks.
"""
ID = lib.Argon2_id
r"""
Argon2\ **id** is a hybrid of Argon2i and Argon2d, using a combination of
data-depending and data-independent memory accesses, which gives some of
Argon2i's resistance to side-channel cache timing attacks and much of
Argon2d's resistance to GPU cracking attacks.
That makes it the preferred type for password hashing and password-based
key derivation.
.. versionadded:: 16.3.0
"""
def hash_secret(
secret,
salt,
time_cost,
memory_cost,
parallelism,
hash_len,
type,
version=ARGON2_VERSION,
):
"""
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:`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)
def hash_secret_raw(
secret,
salt,
time_cost,
memory_cost,
parallelism,
hash_len,
type,
version=ARGON2_VERSION,
):
"""
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, secret, type):
"""
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
elif rv == lib.ARGON2_VERIFY_MISMATCH:
raise VerifyMismatchError(error_to_str(rv))
else:
raise VerificationError(error_to_str(rv))
def core(context, type):
"""
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)
def error_to_str(error):
"""
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
"""
msg = ffi.string(lib.argon2_error_message(error))
if PY3:
msg = msg.decode("ascii")
return msg