1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
# SPDX-License-Identifier: MIT
from base64 import b64encode
from dataclasses import replace
import pytest
from hypothesis import given
from hypothesis import strategies as st
from argon2 import Parameters, Type, extract_parameters
from argon2._utils import NoneType, _check_types, _decoded_str_len
from argon2.exceptions import InvalidHashError
class TestCheckTypes:
def test_success(self):
"""
Returns None if all types are okay.
"""
assert None is _check_types(
bytes=(b"bytes", bytes),
tuple=((1, 2), tuple),
str_or_None=(None, (str, NoneType)),
)
def test_fail(self):
"""
Returns summary of failures.
"""
rv = _check_types(
bytes=("not bytes", bytes), str_or_None=(42, (str, NoneType))
)
assert "." == rv[-1] # proper grammar FTW
assert "'str_or_None' must be a str, or NoneType (got int)" in rv
assert "'bytes' must be a bytes (got str)" in rv
@given(st.binary())
def test_decoded_str_len(bs):
"""
_decoded_str_len computes the resulting length.
"""
assert len(bs) == _decoded_str_len(len(b64encode(bs).rstrip(b"=")))
VALID_HASH = (
"$argon2id$v=19$m=65536,t=2,p=4$"
"c29tZXNhbHQ$GpZ3sK/oH9p7VIiV56G/64Zo/8GaUw434IimaPqxwCo"
)
VALID_PARAMETERS = Parameters(
type=Type.ID,
salt_len=8,
hash_len=32,
version=19,
memory_cost=65536,
time_cost=2,
parallelism=4,
)
VALID_HASH_V18 = (
"$argon2i$m=8,t=1,p=1$c29tZXNhbHQ$gwQOXSNhxiOxPOA0+PY10P9QFO"
"4NAYysnqRt1GSQLE55m+2GYDt9FEjPMHhP2Cuf0nOEXXMocVrsJAtNSsKyfg"
)
VALID_PARAMETERS_V18 = Parameters(
type=Type.I,
salt_len=8,
hash_len=64,
version=18,
memory_cost=8,
time_cost=1,
parallelism=1,
)
class TestExtractParameters:
def test_valid_hash(self):
"""
A valid hash is parsed.
"""
parsed = extract_parameters(VALID_HASH)
assert VALID_PARAMETERS == parsed
def test_valid_hash_v18(self):
"""
A valid Argon v1.2 hash is parsed.
"""
parsed = extract_parameters(VALID_HASH_V18)
assert VALID_PARAMETERS_V18 == parsed
@pytest.mark.parametrize(
"hash",
[
"",
"abc" + VALID_HASH,
VALID_HASH.replace("p=4", "p=four"),
VALID_HASH.replace(",p=4", ""),
],
)
def test_invalid_hash(self, hash):
"""
Invalid hashes of various types raise an InvalidHash error.
"""
with pytest.raises(InvalidHashError):
extract_parameters(hash)
class TestParameters:
def test_eq(self):
"""
Parameters are equal iff every attribute is equal.
"""
assert VALID_PARAMETERS == VALID_PARAMETERS # noqa: PLR0124
assert VALID_PARAMETERS != replace(VALID_PARAMETERS, salt_len=9)
def test_eq_wrong_type(self):
"""
Parameters are only compared if they have the same type.
"""
assert VALID_PARAMETERS != "foo"
assert VALID_PARAMETERS != object()
def test_repr(self):
"""
__repr__ returns s ensible string.
"""
assert repr(
Parameters(
type=Type.ID,
salt_len=8,
hash_len=32,
version=19,
memory_cost=65536,
time_cost=2,
parallelism=4,
)
) == (
"Parameters(type=<Type.ID: 2>, version=19, salt_len=8, "
"hash_len=32, time_cost=2, memory_cost=65536, parallelism=4)"
)
|