Загрузить файлы в «venv/Lib/site-packages/passlib/tests»
This commit is contained in:
634
venv/Lib/site-packages/passlib/tests/test_crypto_scrypt.py
Normal file
634
venv/Lib/site-packages/passlib/tests/test_crypto_scrypt.py
Normal file
@@ -0,0 +1,634 @@
|
||||
"""tests for passlib.utils.scrypt"""
|
||||
#=============================================================================
|
||||
# imports
|
||||
#=============================================================================
|
||||
# core
|
||||
from binascii import hexlify
|
||||
import hashlib
|
||||
import logging; log = logging.getLogger(__name__)
|
||||
import struct
|
||||
import warnings
|
||||
warnings.filterwarnings("ignore", ".*using builtin scrypt backend.*")
|
||||
# site
|
||||
# pkg
|
||||
from passlib import exc
|
||||
from passlib.utils import getrandbytes
|
||||
from passlib.utils.compat import PYPY, u, bascii_to_str
|
||||
from passlib.utils.decor import classproperty
|
||||
from passlib.tests.utils import TestCase, skipUnless, TEST_MODE, hb
|
||||
# subject
|
||||
from passlib.crypto import scrypt as scrypt_mod
|
||||
# local
|
||||
__all__ = [
|
||||
"ScryptEngineTest",
|
||||
"BuiltinScryptTest",
|
||||
"FastScryptTest",
|
||||
]
|
||||
|
||||
#=============================================================================
|
||||
# support functions
|
||||
#=============================================================================
|
||||
def hexstr(data):
|
||||
"""return bytes as hex str"""
|
||||
return bascii_to_str(hexlify(data))
|
||||
|
||||
def unpack_uint32_list(data, check_count=None):
|
||||
"""unpack bytes as list of uint32 values"""
|
||||
count = len(data) // 4
|
||||
assert check_count is None or check_count == count
|
||||
return struct.unpack("<%dI" % count, data)
|
||||
|
||||
def seed_bytes(seed, count):
|
||||
"""
|
||||
generate random reference bytes from specified seed.
|
||||
used to generate some predictable test vectors.
|
||||
"""
|
||||
if hasattr(seed, "encode"):
|
||||
seed = seed.encode("ascii")
|
||||
buf = b''
|
||||
i = 0
|
||||
while len(buf) < count:
|
||||
buf += hashlib.sha256(seed + struct.pack("<I", i)).digest()
|
||||
i += 1
|
||||
return buf[:count]
|
||||
|
||||
#=============================================================================
|
||||
# test builtin engine's internals
|
||||
#=============================================================================
|
||||
class ScryptEngineTest(TestCase):
|
||||
descriptionPrefix = "passlib.crypto.scrypt._builtin"
|
||||
|
||||
def test_smix(self):
|
||||
"""smix()"""
|
||||
from passlib.crypto.scrypt._builtin import ScryptEngine
|
||||
rng = self.getRandom()
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# test vector from (expired) scrypt rfc draft
|
||||
# (https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01, section 9)
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
input = hb("""
|
||||
f7 ce 0b 65 3d 2d 72 a4 10 8c f5 ab e9 12 ff dd
|
||||
77 76 16 db bb 27 a7 0e 82 04 f3 ae 2d 0f 6f ad
|
||||
89 f6 8f 48 11 d1 e8 7b cc 3b d7 40 0a 9f fd 29
|
||||
09 4f 01 84 63 95 74 f3 9a e5 a1 31 52 17 bc d7
|
||||
89 49 91 44 72 13 bb 22 6c 25 b5 4d a8 63 70 fb
|
||||
cd 98 43 80 37 46 66 bb 8f fc b5 bf 40 c2 54 b0
|
||||
67 d2 7c 51 ce 4a d5 fe d8 29 c9 0b 50 5a 57 1b
|
||||
7f 4d 1c ad 6a 52 3c da 77 0e 67 bc ea af 7e 89
|
||||
""")
|
||||
|
||||
output = hb("""
|
||||
79 cc c1 93 62 9d eb ca 04 7f 0b 70 60 4b f6 b6
|
||||
2c e3 dd 4a 96 26 e3 55 fa fc 61 98 e6 ea 2b 46
|
||||
d5 84 13 67 3b 99 b0 29 d6 65 c3 57 60 1f b4 26
|
||||
a0 b2 f4 bb a2 00 ee 9f 0a 43 d1 9b 57 1a 9c 71
|
||||
ef 11 42 e6 5d 5a 26 6f dd ca 83 2c e5 9f aa 7c
|
||||
ac 0b 9c f1 be 2b ff ca 30 0d 01 ee 38 76 19 c4
|
||||
ae 12 fd 44 38 f2 03 a0 e4 e1 c4 7e c3 14 86 1f
|
||||
4e 90 87 cb 33 39 6a 68 73 e8 f9 d2 53 9a 4b 8e
|
||||
""")
|
||||
|
||||
# NOTE: p value should be ignored, so testing w/ random inputs.
|
||||
engine = ScryptEngine(n=16, r=1, p=rng.randint(1, 1023))
|
||||
self.assertEqual(engine.smix(input), output)
|
||||
|
||||
def test_bmix(self):
|
||||
"""bmix()"""
|
||||
from passlib.crypto.scrypt._builtin import ScryptEngine
|
||||
rng = self.getRandom()
|
||||
|
||||
# NOTE: bmix() call signature currently takes in list of 32*r uint32 elements,
|
||||
# and writes to target buffer of same size.
|
||||
|
||||
def check_bmix(r, input, output):
|
||||
"""helper to check bmix() output against reference"""
|
||||
# NOTE: * n & p values should be ignored, so testing w/ rng inputs.
|
||||
# * target buffer contents should be ignored, so testing w/ random inputs.
|
||||
engine = ScryptEngine(r=r, n=1 << rng.randint(1, 32), p=rng.randint(1, 1023))
|
||||
target = [rng.randint(0, 1 << 32) for _ in range((2 * r) * 16)]
|
||||
engine.bmix(input, target)
|
||||
self.assertEqual(target, list(output))
|
||||
|
||||
# ScryptEngine special-cases bmix() for r=1.
|
||||
# this removes the special case patching, so we also test original bmix function.
|
||||
if r == 1:
|
||||
del engine.bmix
|
||||
target = [rng.randint(0, 1 << 32) for _ in range((2 * r) * 16)]
|
||||
engine.bmix(input, target)
|
||||
self.assertEqual(target, list(output))
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# test vector from (expired) scrypt rfc draft
|
||||
# (https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01, section 8)
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
# NOTE: this pair corresponds to the first input & output pair
|
||||
# from the test vector in test_smix(), above.
|
||||
# NOTE: original reference lists input & output as two separate 64 byte blocks.
|
||||
# current internal representation used by bmix() uses single 2*r*16 array of uint32,
|
||||
# combining all the B blocks into a single flat array.
|
||||
input = unpack_uint32_list(hb("""
|
||||
f7 ce 0b 65 3d 2d 72 a4 10 8c f5 ab e9 12 ff dd
|
||||
77 76 16 db bb 27 a7 0e 82 04 f3 ae 2d 0f 6f ad
|
||||
89 f6 8f 48 11 d1 e8 7b cc 3b d7 40 0a 9f fd 29
|
||||
09 4f 01 84 63 95 74 f3 9a e5 a1 31 52 17 bc d7
|
||||
|
||||
89 49 91 44 72 13 bb 22 6c 25 b5 4d a8 63 70 fb
|
||||
cd 98 43 80 37 46 66 bb 8f fc b5 bf 40 c2 54 b0
|
||||
67 d2 7c 51 ce 4a d5 fe d8 29 c9 0b 50 5a 57 1b
|
||||
7f 4d 1c ad 6a 52 3c da 77 0e 67 bc ea af 7e 89
|
||||
"""), 32)
|
||||
|
||||
output = unpack_uint32_list(hb("""
|
||||
a4 1f 85 9c 66 08 cc 99 3b 81 ca cb 02 0c ef 05
|
||||
04 4b 21 81 a2 fd 33 7d fd 7b 1c 63 96 68 2f 29
|
||||
b4 39 31 68 e3 c9 e6 bc fe 6b c5 b7 a0 6d 96 ba
|
||||
e4 24 cc 10 2c 91 74 5c 24 ad 67 3d c7 61 8f 81
|
||||
|
||||
20 ed c9 75 32 38 81 a8 05 40 f6 4c 16 2d cd 3c
|
||||
21 07 7c fe 5f 8d 5f e2 b1 a4 16 8f 95 36 78 b7
|
||||
7d 3b 3d 80 3b 60 e4 ab 92 09 96 e5 9b 4d 53 b6
|
||||
5d 2a 22 58 77 d5 ed f5 84 2c b9 f1 4e ef e4 25
|
||||
"""), 32)
|
||||
|
||||
# check_bmix(1, input, output)
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# custom test vector for r=2
|
||||
# used to check for bmix() breakage while optimizing implementation.
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
r = 2
|
||||
input = unpack_uint32_list(seed_bytes("bmix with r=2", 128 * r))
|
||||
|
||||
output = unpack_uint32_list(hb("""
|
||||
ba240854954f4585f3d0573321f10beee96f12acdc1feb498131e40512934fd7
|
||||
43e8139c17d0743c89d09ac8c3582c273c60ab85db63e410d049a9e17a42c6a1
|
||||
|
||||
6c7831b11bf370266afdaff997ae1286920dea1dedf0f4a1795ba710ba9017f1
|
||||
a374400766f13ebd8969362de2d153965e9941bdde0768fa5b53e8522f116ce0
|
||||
|
||||
d14774afb88f46cd919cba4bc64af7fca0ecb8732d1fc2191e0d7d1b6475cb2e
|
||||
e3db789ee478d056c4eb6c6e28b99043602dbb8dfb60c6e048bf90719da8d57d
|
||||
|
||||
3c42250e40ab79a1ada6aae9299b9790f767f54f388d024a1465b30cbbe9eb89
|
||||
002d4f5c215c4259fac4d083bac5fb0b47463747d568f40bb7fa87c42f0a1dc1
|
||||
"""), 32 * r)
|
||||
|
||||
check_bmix(r, input, output)
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# custom test vector for r=3
|
||||
# used to check for bmix() breakage while optimizing implementation.
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
r = 3
|
||||
input = unpack_uint32_list(seed_bytes("bmix with r=3", 128 * r))
|
||||
|
||||
output = unpack_uint32_list(hb("""
|
||||
11ddd8cf60c61f59a6e5b128239bdc77b464101312c88bd1ccf6be6e75461b29
|
||||
7370d4770c904d0b09c402573cf409bf2db47b91ba87d5a3de469df8fb7a003c
|
||||
|
||||
95a66af96dbdd88beddc8df51a2f72a6f588d67e7926e9c2b676c875da13161e
|
||||
b6262adac39e6b3003e9a6fbc8c1a6ecf1e227c03bc0af3e5f8736c339b14f84
|
||||
|
||||
c7ae5b89f5e16d0faf8983551165f4bb712d97e4f81426e6b78eb63892d3ff54
|
||||
80bf406c98e479496d0f76d23d728e67d2a3d2cdbc4a932be6db36dc37c60209
|
||||
|
||||
a5ca76ca2d2979f995f73fe8182eefa1ce0ba0d4fc27d5b827cb8e67edd6552f
|
||||
00a5b3ab6b371bd985a158e728011314eb77f32ade619b3162d7b5078a19886c
|
||||
|
||||
06f12bc8ae8afa46489e5b0239954d5216967c928982984101e4a88bae1f60ae
|
||||
3f8a456e169a8a1c7450e7955b8a13a202382ae19d41ce8ef8b6a15eeef569a7
|
||||
|
||||
20f54c48e44cb5543dda032c1a50d5ddf2919030624978704eb8db0290052a1f
|
||||
5d88989b0ef931b6befcc09e9d5162320e71e80b89862de7e2f0b6c67229b93f
|
||||
"""), 32 * r)
|
||||
|
||||
check_bmix(r, input, output)
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# custom test vector for r=4
|
||||
# used to check for bmix() breakage while optimizing implementation.
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
r = 4
|
||||
input = unpack_uint32_list(seed_bytes("bmix with r=4", 128 * r))
|
||||
|
||||
output = unpack_uint32_list(hb("""
|
||||
803fcf7362702f30ef43250f20bc6b1b8925bf5c4a0f5a14bbfd90edce545997
|
||||
3047bd81655f72588ca93f5c2f4128adaea805e0705a35e14417101fdb1c498c
|
||||
|
||||
33bec6f4e5950d66098da8469f3fe633f9a17617c0ea21275185697c0e4608f7
|
||||
e6b38b7ec71704a810424637e2c296ca30d9cbf8172a71a266e0393deccf98eb
|
||||
|
||||
abc430d5f144eb0805308c38522f2973b7b6a48498851e4c762874497da76b88
|
||||
b769b471fbfc144c0e8e859b2b3f5a11f51604d268c8fd28db55dff79832741a
|
||||
|
||||
1ac0dfdaff10f0ada0d93d3b1f13062e4107c640c51df05f4110bdda15f51b53
|
||||
3a75bfe56489a6d8463440c78fb8c0794135e38591bdc5fa6cec96a124178a4a
|
||||
|
||||
d1a976e985bfe13d2b4af51bd0fc36dd4cfc3af08efe033b2323a235205dc43d
|
||||
e57778a492153f9527338b3f6f5493a03d8015cd69737ee5096ad4cbe660b10f
|
||||
|
||||
b75b1595ddc96e3748f5c9f61fba1ef1f0c51b6ceef8bbfcc34b46088652e6f7
|
||||
edab61521cbad6e69b77be30c9c97ea04a4af359dafc205c7878cc9a6c5d122f
|
||||
|
||||
8d77f3cbe65ab14c3c491ef94ecb3f5d2c2dd13027ea4c3606262bb3c9ce46e7
|
||||
dc424729dc75f6e8f06096c0ad8ad4d549c42f0cad9b33cb95d10fb3cadba27c
|
||||
|
||||
5f4bf0c1ac677c23ba23b64f56afc3546e62d96f96b58d7afc5029f8168cbab4
|
||||
533fd29fc83c8d2a32b81923992e4938281334e0c3694f0ee56f8ff7df7dc4ae
|
||||
"""), 32 * r)
|
||||
|
||||
check_bmix(r, input, output)
|
||||
|
||||
def test_salsa(self):
|
||||
"""salsa20()"""
|
||||
from passlib.crypto.scrypt._builtin import salsa20
|
||||
|
||||
# NOTE: salsa2() currently operates on lists of 16 uint32 elements,
|
||||
# which is what unpack_uint32_list(hb(() is for...
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# test vector from (expired) scrypt rfc draft
|
||||
# (https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01, section 7)
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
# NOTE: this pair corresponds to the first input & output pair
|
||||
# from the test vector in test_bmix(), above.
|
||||
|
||||
input = unpack_uint32_list(hb("""
|
||||
7e 87 9a 21 4f 3e c9 86 7c a9 40 e6 41 71 8f 26
|
||||
ba ee 55 5b 8c 61 c1 b5 0d f8 46 11 6d cd 3b 1d
|
||||
ee 24 f3 19 df 9b 3d 85 14 12 1e 4b 5a c5 aa 32
|
||||
76 02 1d 29 09 c7 48 29 ed eb c6 8d b8 b8 c2 5e
|
||||
"""))
|
||||
|
||||
output = unpack_uint32_list(hb("""
|
||||
a4 1f 85 9c 66 08 cc 99 3b 81 ca cb 02 0c ef 05
|
||||
04 4b 21 81 a2 fd 33 7d fd 7b 1c 63 96 68 2f 29
|
||||
b4 39 31 68 e3 c9 e6 bc fe 6b c5 b7 a0 6d 96 ba
|
||||
e4 24 cc 10 2c 91 74 5c 24 ad 67 3d c7 61 8f 81
|
||||
"""))
|
||||
self.assertEqual(salsa20(input), output)
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# custom test vector,
|
||||
# used to check for salsa20() breakage while optimizing _gen_files output.
|
||||
#-----------------------------------------------------------------------
|
||||
input = list(range(16))
|
||||
output = unpack_uint32_list(hb("""
|
||||
f518dd4fb98883e0a87954c05cab867083bb8808552810752285a05822f56c16
|
||||
9d4a2a0fd2142523d758c60b36411b682d53860514b871d27659042a5afa475d
|
||||
"""))
|
||||
self.assertEqual(salsa20(input), output)
|
||||
|
||||
#=============================================================================
|
||||
# eof
|
||||
#=============================================================================
|
||||
|
||||
#=============================================================================
|
||||
# test scrypt
|
||||
#=============================================================================
|
||||
class _CommonScryptTest(TestCase):
|
||||
"""
|
||||
base class for testing various scrypt backends against same set of reference vectors.
|
||||
"""
|
||||
#=============================================================================
|
||||
# class attrs
|
||||
#=============================================================================
|
||||
|
||||
@classproperty
|
||||
def descriptionPrefix(cls):
|
||||
return "passlib.utils.scrypt.scrypt() <%s backend>" % cls.backend
|
||||
backend = None
|
||||
|
||||
#=============================================================================
|
||||
# setup
|
||||
#=============================================================================
|
||||
def setUp(self):
|
||||
assert self.backend
|
||||
scrypt_mod._set_backend(self.backend)
|
||||
super(_CommonScryptTest, self).setUp()
|
||||
|
||||
#=============================================================================
|
||||
# reference vectors
|
||||
#=============================================================================
|
||||
|
||||
reference_vectors = [
|
||||
# entry format: (secret, salt, n, r, p, keylen, result)
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# test vectors from scrypt whitepaper --
|
||||
# http://www.tarsnap.com/scrypt/scrypt.pdf, appendix b
|
||||
#
|
||||
# also present in (expired) scrypt rfc draft --
|
||||
# https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01, section 11
|
||||
#------------------------------------------------------------------------
|
||||
("", "", 16, 1, 1, 64, hb("""
|
||||
77 d6 57 62 38 65 7b 20 3b 19 ca 42 c1 8a 04 97
|
||||
f1 6b 48 44 e3 07 4a e8 df df fa 3f ed e2 14 42
|
||||
fc d0 06 9d ed 09 48 f8 32 6a 75 3a 0f c8 1f 17
|
||||
e8 d3 e0 fb 2e 0d 36 28 cf 35 e2 0c 38 d1 89 06
|
||||
""")),
|
||||
|
||||
("password", "NaCl", 1024, 8, 16, 64, hb("""
|
||||
fd ba be 1c 9d 34 72 00 78 56 e7 19 0d 01 e9 fe
|
||||
7c 6a d7 cb c8 23 78 30 e7 73 76 63 4b 37 31 62
|
||||
2e af 30 d9 2e 22 a3 88 6f f1 09 27 9d 98 30 da
|
||||
c7 27 af b9 4a 83 ee 6d 83 60 cb df a2 cc 06 40
|
||||
""")),
|
||||
|
||||
# NOTE: the following are skipped for all backends unless TEST_MODE="full"
|
||||
|
||||
("pleaseletmein", "SodiumChloride", 16384, 8, 1, 64, hb("""
|
||||
70 23 bd cb 3a fd 73 48 46 1c 06 cd 81 fd 38 eb
|
||||
fd a8 fb ba 90 4f 8e 3e a9 b5 43 f6 54 5d a1 f2
|
||||
d5 43 29 55 61 3f 0f cf 62 d4 97 05 24 2a 9a f9
|
||||
e6 1e 85 dc 0d 65 1e 40 df cf 01 7b 45 57 58 87
|
||||
""")),
|
||||
|
||||
# NOTE: the following are always skipped for the builtin backend,
|
||||
# (just takes too long to be worth it)
|
||||
|
||||
("pleaseletmein", "SodiumChloride", 1048576, 8, 1, 64, hb("""
|
||||
21 01 cb 9b 6a 51 1a ae ad db be 09 cf 70 f8 81
|
||||
ec 56 8d 57 4a 2f fd 4d ab e5 ee 98 20 ad aa 47
|
||||
8e 56 fd 8f 4b a5 d0 9f fa 1c 6d 92 7c 40 f4 c3
|
||||
37 30 40 49 e8 a9 52 fb cb f4 5c 6f a7 7a 41 a4
|
||||
""")),
|
||||
]
|
||||
|
||||
def test_reference_vectors(self):
|
||||
"""reference vectors"""
|
||||
for secret, salt, n, r, p, keylen, result in self.reference_vectors:
|
||||
if n >= 1024 and TEST_MODE(max="default"):
|
||||
# skip large values unless we're running full test suite
|
||||
continue
|
||||
if n > 16384 and self.backend == "builtin":
|
||||
# skip largest vector for builtin, takes WAAY too long
|
||||
# (46s under pypy, ~5m under cpython)
|
||||
continue
|
||||
log.debug("scrypt reference vector: %r %r n=%r r=%r p=%r", secret, salt, n, r, p)
|
||||
self.assertEqual(scrypt_mod.scrypt(secret, salt, n, r, p, keylen), result)
|
||||
|
||||
#=============================================================================
|
||||
# fuzz testing
|
||||
#=============================================================================
|
||||
|
||||
_already_tested_others = None
|
||||
|
||||
def test_other_backends(self):
|
||||
"""compare output to other backends"""
|
||||
# only run once, since test is symetric.
|
||||
# maybe this means it should go somewhere else?
|
||||
if self._already_tested_others:
|
||||
raise self.skipTest("already run under %r backend test" % self._already_tested_others)
|
||||
self._already_tested_others = self.backend
|
||||
rng = self.getRandom()
|
||||
|
||||
# get available backends
|
||||
orig = scrypt_mod.backend
|
||||
available = set(name for name in scrypt_mod.backend_values
|
||||
if scrypt_mod._has_backend(name))
|
||||
scrypt_mod._set_backend(orig)
|
||||
available.discard(self.backend)
|
||||
if not available:
|
||||
raise self.skipTest("no other backends found")
|
||||
|
||||
warnings.filterwarnings("ignore", "(?i)using builtin scrypt backend",
|
||||
category=exc.PasslibSecurityWarning)
|
||||
|
||||
# generate some random options, and cross-check output
|
||||
for _ in range(10):
|
||||
# NOTE: keeping values low due to builtin test
|
||||
secret = getrandbytes(rng, rng.randint(0, 64))
|
||||
salt = getrandbytes(rng, rng.randint(0, 64))
|
||||
n = 1 << rng.randint(1, 10)
|
||||
r = rng.randint(1, 8)
|
||||
p = rng.randint(1, 3)
|
||||
ks = rng.randint(1, 64)
|
||||
previous = None
|
||||
backends = set()
|
||||
for name in available:
|
||||
scrypt_mod._set_backend(name)
|
||||
self.assertNotIn(scrypt_mod._scrypt, backends)
|
||||
backends.add(scrypt_mod._scrypt)
|
||||
result = hexstr(scrypt_mod.scrypt(secret, salt, n, r, p, ks))
|
||||
self.assertEqual(len(result), 2*ks)
|
||||
if previous is not None:
|
||||
self.assertEqual(result, previous,
|
||||
msg="%r output differs from others %r: %r" %
|
||||
(name, available, [secret, salt, n, r, p, ks]))
|
||||
|
||||
#=============================================================================
|
||||
# test input types
|
||||
#=============================================================================
|
||||
def test_backend(self):
|
||||
"""backend management"""
|
||||
# clobber backend
|
||||
scrypt_mod.backend = None
|
||||
scrypt_mod._scrypt = None
|
||||
self.assertRaises(TypeError, scrypt_mod.scrypt, 's', 's', 2, 2, 2, 16)
|
||||
|
||||
# reload backend
|
||||
scrypt_mod._set_backend(self.backend)
|
||||
self.assertEqual(scrypt_mod.backend, self.backend)
|
||||
scrypt_mod.scrypt('s', 's', 2, 2, 2, 16)
|
||||
|
||||
# throw error for unknown backend
|
||||
self.assertRaises(ValueError, scrypt_mod._set_backend, 'xxx')
|
||||
self.assertEqual(scrypt_mod.backend, self.backend)
|
||||
|
||||
def test_secret_param(self):
|
||||
"""'secret' parameter"""
|
||||
|
||||
def run_scrypt(secret):
|
||||
return hexstr(scrypt_mod.scrypt(secret, "salt", 2, 2, 2, 16))
|
||||
|
||||
# unicode
|
||||
TEXT = u("abc\u00defg")
|
||||
self.assertEqual(run_scrypt(TEXT), '05717106997bfe0da42cf4779a2f8bd8')
|
||||
|
||||
# utf8 bytes
|
||||
TEXT_UTF8 = b'abc\xc3\x9efg'
|
||||
self.assertEqual(run_scrypt(TEXT_UTF8), '05717106997bfe0da42cf4779a2f8bd8')
|
||||
|
||||
# latin1 bytes
|
||||
TEXT_LATIN1 = b'abc\xdefg'
|
||||
self.assertEqual(run_scrypt(TEXT_LATIN1), '770825d10eeaaeaf98e8a3c40f9f441d')
|
||||
|
||||
# accept empty string
|
||||
self.assertEqual(run_scrypt(""), 'ca1399e5fae5d3b9578dcd2b1faff6e2')
|
||||
|
||||
# reject other types
|
||||
self.assertRaises(TypeError, run_scrypt, None)
|
||||
self.assertRaises(TypeError, run_scrypt, 1)
|
||||
|
||||
def test_salt_param(self):
|
||||
"""'salt' parameter"""
|
||||
|
||||
def run_scrypt(salt):
|
||||
return hexstr(scrypt_mod.scrypt("secret", salt, 2, 2, 2, 16))
|
||||
|
||||
# unicode
|
||||
TEXT = u("abc\u00defg")
|
||||
self.assertEqual(run_scrypt(TEXT), 'a748ec0f4613929e9e5f03d1ab741d88')
|
||||
|
||||
# utf8 bytes
|
||||
TEXT_UTF8 = b'abc\xc3\x9efg'
|
||||
self.assertEqual(run_scrypt(TEXT_UTF8), 'a748ec0f4613929e9e5f03d1ab741d88')
|
||||
|
||||
# latin1 bytes
|
||||
TEXT_LATIN1 = b'abc\xdefg'
|
||||
self.assertEqual(run_scrypt(TEXT_LATIN1), '91d056fb76fb6e9a7d1cdfffc0a16cd1')
|
||||
|
||||
# reject other types
|
||||
self.assertRaises(TypeError, run_scrypt, None)
|
||||
self.assertRaises(TypeError, run_scrypt, 1)
|
||||
|
||||
def test_n_param(self):
|
||||
"""'n' (rounds) parameter"""
|
||||
|
||||
def run_scrypt(n):
|
||||
return hexstr(scrypt_mod.scrypt("secret", "salt", n, 2, 2, 16))
|
||||
|
||||
# must be > 1, and a power of 2
|
||||
self.assertRaises(ValueError, run_scrypt, -1)
|
||||
self.assertRaises(ValueError, run_scrypt, 0)
|
||||
self.assertRaises(ValueError, run_scrypt, 1)
|
||||
self.assertEqual(run_scrypt(2), 'dacf2bca255e2870e6636fa8c8957a66')
|
||||
self.assertRaises(ValueError, run_scrypt, 3)
|
||||
self.assertRaises(ValueError, run_scrypt, 15)
|
||||
self.assertEqual(run_scrypt(16), '0272b8fc72bc54b1159340ed99425233')
|
||||
|
||||
def test_r_param(self):
|
||||
"""'r' (block size) parameter"""
|
||||
def run_scrypt(r, n=2, p=2):
|
||||
return hexstr(scrypt_mod.scrypt("secret", "salt", n, r, p, 16))
|
||||
|
||||
# must be > 1
|
||||
self.assertRaises(ValueError, run_scrypt, -1)
|
||||
self.assertRaises(ValueError, run_scrypt, 0)
|
||||
self.assertEqual(run_scrypt(1), '3d630447d9f065363b8a79b0b3670251')
|
||||
self.assertEqual(run_scrypt(2), 'dacf2bca255e2870e6636fa8c8957a66')
|
||||
self.assertEqual(run_scrypt(5), '114f05e985a903c27237b5578e763736')
|
||||
|
||||
# reject r*p >= 2**30
|
||||
self.assertRaises(ValueError, run_scrypt, (1<<30), p=1)
|
||||
self.assertRaises(ValueError, run_scrypt, (1<<30) / 2, p=2)
|
||||
|
||||
def test_p_param(self):
|
||||
"""'p' (parallelism) parameter"""
|
||||
def run_scrypt(p, n=2, r=2):
|
||||
return hexstr(scrypt_mod.scrypt("secret", "salt", n, r, p, 16))
|
||||
|
||||
# must be > 1
|
||||
self.assertRaises(ValueError, run_scrypt, -1)
|
||||
self.assertRaises(ValueError, run_scrypt, 0)
|
||||
self.assertEqual(run_scrypt(1), 'f2960ea8b7d48231fcec1b89b784a6fa')
|
||||
self.assertEqual(run_scrypt(2), 'dacf2bca255e2870e6636fa8c8957a66')
|
||||
self.assertEqual(run_scrypt(5), '848a0eeb2b3543e7f543844d6ca79782')
|
||||
|
||||
# reject r*p >= 2**30
|
||||
self.assertRaises(ValueError, run_scrypt, (1<<30), r=1)
|
||||
self.assertRaises(ValueError, run_scrypt, (1<<30) / 2, r=2)
|
||||
|
||||
def test_keylen_param(self):
|
||||
"""'keylen' parameter"""
|
||||
rng = self.getRandom()
|
||||
|
||||
def run_scrypt(keylen):
|
||||
return hexstr(scrypt_mod.scrypt("secret", "salt", 2, 2, 2, keylen))
|
||||
|
||||
# must be > 0
|
||||
self.assertRaises(ValueError, run_scrypt, -1)
|
||||
self.assertRaises(ValueError, run_scrypt, 0)
|
||||
self.assertEqual(run_scrypt(1), 'da')
|
||||
|
||||
# pick random value
|
||||
ksize = rng.randint(1, 1 << 10)
|
||||
self.assertEqual(len(run_scrypt(ksize)), 2*ksize) # 2 hex chars per output
|
||||
|
||||
# one more than upper bound
|
||||
self.assertRaises(ValueError, run_scrypt, ((2**32) - 1) * 32 + 1)
|
||||
|
||||
#=============================================================================
|
||||
# eoc
|
||||
#=============================================================================
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# check what backends 'should' be available
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
def _can_import_cffi_scrypt():
|
||||
try:
|
||||
import scrypt
|
||||
except ImportError as err:
|
||||
if "scrypt" in str(err):
|
||||
return False
|
||||
raise
|
||||
return True
|
||||
|
||||
has_cffi_scrypt = _can_import_cffi_scrypt()
|
||||
|
||||
|
||||
def _can_import_stdlib_scrypt():
|
||||
try:
|
||||
from hashlib import scrypt
|
||||
return True
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
has_stdlib_scrypt = _can_import_stdlib_scrypt()
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# test individual backends
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
# NOTE: builtin version runs VERY slow (except under PyPy, where it's only 11x slower),
|
||||
# so skipping under quick test mode.
|
||||
@skipUnless(PYPY or TEST_MODE(min="default"), "skipped under current test mode")
|
||||
class BuiltinScryptTest(_CommonScryptTest):
|
||||
backend = "builtin"
|
||||
|
||||
def setUp(self):
|
||||
super(BuiltinScryptTest, self).setUp()
|
||||
warnings.filterwarnings("ignore", "(?i)using builtin scrypt backend",
|
||||
category=exc.PasslibSecurityWarning)
|
||||
|
||||
def test_missing_backend(self):
|
||||
"""backend management -- missing backend"""
|
||||
if has_stdlib_scrypt or has_cffi_scrypt:
|
||||
raise self.skipTest("non-builtin backend is present")
|
||||
self.assertRaises(exc.MissingBackendError, scrypt_mod._set_backend, 'scrypt')
|
||||
|
||||
|
||||
@skipUnless(has_cffi_scrypt, "'scrypt' package not found")
|
||||
class ScryptPackageTest(_CommonScryptTest):
|
||||
backend = "scrypt"
|
||||
|
||||
def test_default_backend(self):
|
||||
"""backend management -- default backend"""
|
||||
if has_stdlib_scrypt:
|
||||
raise self.skipTest("higher priority backend present")
|
||||
scrypt_mod._set_backend("default")
|
||||
self.assertEqual(scrypt_mod.backend, "scrypt")
|
||||
|
||||
|
||||
@skipUnless(has_stdlib_scrypt, "'hashlib.scrypt()' not found")
|
||||
class StdlibScryptTest(_CommonScryptTest):
|
||||
backend = "stdlib"
|
||||
|
||||
def test_default_backend(self):
|
||||
"""backend management -- default backend"""
|
||||
scrypt_mod._set_backend("default")
|
||||
self.assertEqual(scrypt_mod.backend, "stdlib")
|
||||
|
||||
#=============================================================================
|
||||
# eof
|
||||
#=============================================================================
|
||||
1080
venv/Lib/site-packages/passlib/tests/test_ext_django.py
Normal file
1080
venv/Lib/site-packages/passlib/tests/test_ext_django.py
Normal file
File diff suppressed because it is too large
Load Diff
250
venv/Lib/site-packages/passlib/tests/test_ext_django_source.py
Normal file
250
venv/Lib/site-packages/passlib/tests/test_ext_django_source.py
Normal file
@@ -0,0 +1,250 @@
|
||||
"""
|
||||
test passlib.ext.django against django source tests
|
||||
"""
|
||||
#=============================================================================
|
||||
# imports
|
||||
#=============================================================================
|
||||
from __future__ import absolute_import, division, print_function
|
||||
# core
|
||||
import logging; log = logging.getLogger(__name__)
|
||||
# site
|
||||
# pkg
|
||||
from passlib.utils.compat import suppress_cause
|
||||
from passlib.ext.django.utils import DJANGO_VERSION, DjangoTranslator, _PasslibHasherWrapper
|
||||
# tests
|
||||
from passlib.tests.utils import TestCase, TEST_MODE
|
||||
from .test_ext_django import (
|
||||
has_min_django, stock_config, _ExtensionSupport,
|
||||
)
|
||||
if has_min_django:
|
||||
from .test_ext_django import settings
|
||||
# local
|
||||
__all__ = [
|
||||
"HashersTest",
|
||||
]
|
||||
#=============================================================================
|
||||
# HashersTest --
|
||||
# hack up the some of the real django tests to run w/ extension loaded,
|
||||
# to ensure we mimic their behavior.
|
||||
# however, the django tests were moved out of the package, and into a source-only location
|
||||
# as of django 1.7. so we disable tests from that point on unless test-runner specifies
|
||||
#=============================================================================
|
||||
|
||||
#: ref to django unittest root module (if found)
|
||||
test_hashers_mod = None
|
||||
|
||||
#: message about why test module isn't present (if not found)
|
||||
hashers_skip_msg = None
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# try to load django's tests/auth_tests/test_hasher.py module,
|
||||
# or note why we failed.
|
||||
#----------------------------------------------------------------------
|
||||
if TEST_MODE(max="quick"):
|
||||
hashers_skip_msg = "requires >= 'default' test mode"
|
||||
|
||||
elif has_min_django:
|
||||
import os
|
||||
import sys
|
||||
source_path = os.environ.get("PASSLIB_TESTS_DJANGO_SOURCE_PATH")
|
||||
|
||||
if source_path:
|
||||
if not os.path.exists(source_path):
|
||||
raise EnvironmentError("django source path not found: %r" % source_path)
|
||||
if not all(os.path.exists(os.path.join(source_path, name))
|
||||
for name in ["django", "tests"]):
|
||||
raise EnvironmentError("invalid django source path: %r" % source_path)
|
||||
log.info("using django tests from source path: %r", source_path)
|
||||
tests_path = os.path.join(source_path, "tests")
|
||||
sys.path.insert(0, tests_path)
|
||||
try:
|
||||
from auth_tests import test_hashers as test_hashers_mod
|
||||
except ImportError as err:
|
||||
raise suppress_cause(
|
||||
EnvironmentError("error trying to import django tests "
|
||||
"from source path (%r): %r" %
|
||||
(source_path, err)))
|
||||
finally:
|
||||
sys.path.remove(tests_path)
|
||||
|
||||
else:
|
||||
hashers_skip_msg = "requires PASSLIB_TESTS_DJANGO_SOURCE_PATH to be set"
|
||||
|
||||
if TEST_MODE("full"):
|
||||
# print warning so user knows what's happening
|
||||
sys.stderr.write("\nWARNING: $PASSLIB_TESTS_DJANGO_SOURCE_PATH is not set; "
|
||||
"can't run Django's own unittests against passlib.ext.django\n")
|
||||
|
||||
elif DJANGO_VERSION:
|
||||
hashers_skip_msg = "django version too old"
|
||||
|
||||
else:
|
||||
hashers_skip_msg = "django not installed"
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# if found module, create wrapper to run django's own tests,
|
||||
# but with passlib monkeypatched in.
|
||||
#----------------------------------------------------------------------
|
||||
if test_hashers_mod:
|
||||
from django.core.signals import setting_changed
|
||||
from django.dispatch import receiver
|
||||
from django.utils.module_loading import import_string
|
||||
from passlib.utils.compat import get_unbound_method_function
|
||||
|
||||
class HashersTest(test_hashers_mod.TestUtilsHashPass, _ExtensionSupport):
|
||||
"""
|
||||
Run django's hasher unittests against passlib's extension
|
||||
and workalike implementations
|
||||
"""
|
||||
|
||||
#==================================================================
|
||||
# helpers
|
||||
#==================================================================
|
||||
|
||||
# port patchAttr() helper method from passlib.tests.utils.TestCase
|
||||
patchAttr = get_unbound_method_function(TestCase.patchAttr)
|
||||
|
||||
#==================================================================
|
||||
# custom setup
|
||||
#==================================================================
|
||||
def setUp(self):
|
||||
#---------------------------------------------------------
|
||||
# install passlib.ext.django adapter, and get context
|
||||
#---------------------------------------------------------
|
||||
self.load_extension(PASSLIB_CONTEXT=stock_config, check=False)
|
||||
from passlib.ext.django.models import adapter
|
||||
context = adapter.context
|
||||
|
||||
#---------------------------------------------------------
|
||||
# patch tests module to use our versions of patched funcs
|
||||
# (which should be installed in hashers module)
|
||||
#---------------------------------------------------------
|
||||
from django.contrib.auth import hashers
|
||||
for attr in ["make_password",
|
||||
"check_password",
|
||||
"identify_hasher",
|
||||
"is_password_usable",
|
||||
"get_hasher"]:
|
||||
self.patchAttr(test_hashers_mod, attr, getattr(hashers, attr))
|
||||
|
||||
#---------------------------------------------------------
|
||||
# django tests expect empty django_des_crypt salt field
|
||||
#---------------------------------------------------------
|
||||
from passlib.hash import django_des_crypt
|
||||
self.patchAttr(django_des_crypt, "use_duplicate_salt", False)
|
||||
|
||||
#---------------------------------------------------------
|
||||
# install receiver to update scheme list if test changes settings
|
||||
#---------------------------------------------------------
|
||||
django_to_passlib_name = DjangoTranslator().django_to_passlib_name
|
||||
|
||||
@receiver(setting_changed, weak=False)
|
||||
def update_schemes(**kwds):
|
||||
if kwds and kwds['setting'] != 'PASSWORD_HASHERS':
|
||||
return
|
||||
assert context is adapter.context
|
||||
schemes = [
|
||||
django_to_passlib_name(import_string(hash_path)())
|
||||
for hash_path in settings.PASSWORD_HASHERS
|
||||
]
|
||||
# workaround for a few tests that only specify hex_md5,
|
||||
# but test for django_salted_md5 format.
|
||||
if "hex_md5" in schemes and "django_salted_md5" not in schemes:
|
||||
schemes.append("django_salted_md5")
|
||||
schemes.append("django_disabled")
|
||||
context.update(schemes=schemes, deprecated="auto")
|
||||
adapter.reset_hashers()
|
||||
|
||||
self.addCleanup(setting_changed.disconnect, update_schemes)
|
||||
|
||||
update_schemes()
|
||||
|
||||
#---------------------------------------------------------
|
||||
# need password_context to keep up to date with django_hasher.iterations,
|
||||
# which is frequently patched by django tests.
|
||||
#
|
||||
# HACK: to fix this, inserting wrapper around a bunch of context
|
||||
# methods so that any time adapter calls them,
|
||||
# attrs are resynced first.
|
||||
#---------------------------------------------------------
|
||||
|
||||
def update_rounds():
|
||||
"""
|
||||
sync django hasher config -> passlib hashers
|
||||
"""
|
||||
for handler in context.schemes(resolve=True):
|
||||
if 'rounds' not in handler.setting_kwds:
|
||||
continue
|
||||
hasher = adapter.passlib_to_django(handler)
|
||||
if isinstance(hasher, _PasslibHasherWrapper):
|
||||
continue
|
||||
rounds = getattr(hasher, "rounds", None) or \
|
||||
getattr(hasher, "iterations", None)
|
||||
if rounds is None:
|
||||
continue
|
||||
# XXX: this doesn't modify the context, which would
|
||||
# cause other weirdness (since it would replace handler factories completely,
|
||||
# instead of just updating their state)
|
||||
handler.min_desired_rounds = handler.max_desired_rounds = handler.default_rounds = rounds
|
||||
|
||||
_in_update = [False]
|
||||
|
||||
def update_wrapper(wrapped, *args, **kwds):
|
||||
"""
|
||||
wrapper around arbitrary func, that first triggers sync
|
||||
"""
|
||||
if not _in_update[0]:
|
||||
_in_update[0] = True
|
||||
try:
|
||||
update_rounds()
|
||||
finally:
|
||||
_in_update[0] = False
|
||||
return wrapped(*args, **kwds)
|
||||
|
||||
# sync before any context call
|
||||
for attr in ["schemes", "handler", "default_scheme", "hash",
|
||||
"verify", "needs_update", "verify_and_update"]:
|
||||
self.patchAttr(context, attr, update_wrapper, wrap=True)
|
||||
|
||||
# sync whenever adapter tries to resolve passlib hasher
|
||||
self.patchAttr(adapter, "django_to_passlib", update_wrapper, wrap=True)
|
||||
|
||||
def tearDown(self):
|
||||
# NOTE: could rely on addCleanup() instead, but need py26 compat
|
||||
self.unload_extension()
|
||||
super(HashersTest, self).tearDown()
|
||||
|
||||
#==================================================================
|
||||
# skip a few methods that can't be replicated properly
|
||||
# *want to minimize these as much as possible*
|
||||
#==================================================================
|
||||
|
||||
_OMIT = lambda self: self.skipTest("omitted by passlib")
|
||||
|
||||
# XXX: this test registers two classes w/ same algorithm id,
|
||||
# something we don't support -- how does django sanely handle
|
||||
# that anyways? get_hashers_by_algorithm() should throw KeyError, right?
|
||||
test_pbkdf2_upgrade_new_hasher = _OMIT
|
||||
|
||||
# TODO: support wrapping django's harden-runtime feature?
|
||||
# would help pass their tests.
|
||||
test_check_password_calls_harden_runtime = _OMIT
|
||||
test_bcrypt_harden_runtime = _OMIT
|
||||
test_pbkdf2_harden_runtime = _OMIT
|
||||
|
||||
#==================================================================
|
||||
# eoc
|
||||
#==================================================================
|
||||
|
||||
else:
|
||||
# otherwise leave a stub so test log tells why test was skipped.
|
||||
|
||||
class HashersTest(TestCase):
|
||||
|
||||
def test_external_django_hasher_tests(self):
|
||||
"""external django hasher tests"""
|
||||
raise self.skipTest(hashers_skip_msg)
|
||||
|
||||
#=============================================================================
|
||||
# eof
|
||||
#=============================================================================
|
||||
1819
venv/Lib/site-packages/passlib/tests/test_handlers.py
Normal file
1819
venv/Lib/site-packages/passlib/tests/test_handlers.py
Normal file
File diff suppressed because it is too large
Load Diff
507
venv/Lib/site-packages/passlib/tests/test_handlers_argon2.py
Normal file
507
venv/Lib/site-packages/passlib/tests/test_handlers_argon2.py
Normal file
@@ -0,0 +1,507 @@
|
||||
"""passlib.tests.test_handlers_argon2 - tests for passlib hash algorithms"""
|
||||
#=============================================================================
|
||||
# imports
|
||||
#=============================================================================
|
||||
# core
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
import re
|
||||
import warnings
|
||||
# site
|
||||
# pkg
|
||||
from passlib import hash
|
||||
from passlib.utils.compat import unicode
|
||||
from passlib.tests.utils import HandlerCase, TEST_MODE
|
||||
from passlib.tests.test_handlers import UPASS_TABLE, PASS_TABLE_UTF8
|
||||
# module
|
||||
|
||||
#=============================================================================
|
||||
# a bunch of tests lifted nearlky verbatim from official argon2 UTs...
|
||||
# https://github.com/P-H-C/phc-winner-argon2/blob/master/src/test.c
|
||||
#=============================================================================
|
||||
def hashtest(version, t, logM, p, secret, salt, hex_digest, hash):
|
||||
return dict(version=version, rounds=t, logM=logM, memory_cost=1<<logM, parallelism=p,
|
||||
secret=secret, salt=salt, hex_digest=hex_digest, hash=hash)
|
||||
|
||||
# version 1.3 "I" tests
|
||||
version = 0x10
|
||||
reference_data = [
|
||||
hashtest(version, 2, 16, 1, "password", "somesalt",
|
||||
"f6c4db4a54e2a370627aff3db6176b94a2a209a62c8e36152711802f7b30c694",
|
||||
"$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ"
|
||||
"$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"),
|
||||
hashtest(version, 2, 20, 1, "password", "somesalt",
|
||||
"9690ec55d28d3ed32562f2e73ea62b02b018757643a2ae6e79528459de8106e9",
|
||||
"$argon2i$m=1048576,t=2,p=1$c29tZXNhbHQ"
|
||||
"$lpDsVdKNPtMlYvLnPqYrArAYdXZDoq5ueVKEWd6BBuk"),
|
||||
hashtest(version, 2, 18, 1, "password", "somesalt",
|
||||
"3e689aaa3d28a77cf2bc72a51ac53166761751182f1ee292e3f677a7da4c2467",
|
||||
"$argon2i$m=262144,t=2,p=1$c29tZXNhbHQ"
|
||||
"$Pmiaqj0op3zyvHKlGsUxZnYXURgvHuKS4/Z3p9pMJGc"),
|
||||
hashtest(version, 2, 8, 1, "password", "somesalt",
|
||||
"fd4dd83d762c49bdeaf57c47bdcd0c2f1babf863fdeb490df63ede9975fccf06",
|
||||
"$argon2i$m=256,t=2,p=1$c29tZXNhbHQ"
|
||||
"$/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY"),
|
||||
hashtest(version, 2, 8, 2, "password", "somesalt",
|
||||
"b6c11560a6a9d61eac706b79a2f97d68b4463aa3ad87e00c07e2b01e90c564fb",
|
||||
"$argon2i$m=256,t=2,p=2$c29tZXNhbHQ"
|
||||
"$tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs"),
|
||||
hashtest(version, 1, 16, 1, "password", "somesalt",
|
||||
"81630552b8f3b1f48cdb1992c4c678643d490b2b5eb4ff6c4b3438b5621724b2",
|
||||
"$argon2i$m=65536,t=1,p=1$c29tZXNhbHQ"
|
||||
"$gWMFUrjzsfSM2xmSxMZ4ZD1JCytetP9sSzQ4tWIXJLI"),
|
||||
hashtest(version, 4, 16, 1, "password", "somesalt",
|
||||
"f212f01615e6eb5d74734dc3ef40ade2d51d052468d8c69440a3a1f2c1c2847b",
|
||||
"$argon2i$m=65536,t=4,p=1$c29tZXNhbHQ"
|
||||
"$8hLwFhXm6110c03D70Ct4tUdBSRo2MaUQKOh8sHChHs"),
|
||||
hashtest(version, 2, 16, 1, "differentpassword", "somesalt",
|
||||
"e9c902074b6754531a3a0be519e5baf404b30ce69b3f01ac3bf21229960109a3",
|
||||
"$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ"
|
||||
"$6ckCB0tnVFMaOgvlGeW69ASzDOabPwGsO/ISKZYBCaM"),
|
||||
hashtest(version, 2, 16, 1, "password", "diffsalt",
|
||||
"79a103b90fe8aef8570cb31fc8b22259778916f8336b7bdac3892569d4f1c497",
|
||||
"$argon2i$m=65536,t=2,p=1$ZGlmZnNhbHQ"
|
||||
"$eaEDuQ/orvhXDLMfyLIiWXeJFvgza3vaw4kladTxxJc"),
|
||||
]
|
||||
|
||||
# version 1.9 "I" tests
|
||||
version = 0x13
|
||||
reference_data.extend([
|
||||
hashtest(version, 2, 16, 1, "password", "somesalt",
|
||||
"c1628832147d9720c5bd1cfd61367078729f6dfb6f8fea9ff98158e0d7816ed0",
|
||||
"$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ"
|
||||
"$wWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA"),
|
||||
hashtest(version, 2, 20, 1, "password", "somesalt",
|
||||
"d1587aca0922c3b5d6a83edab31bee3c4ebaef342ed6127a55d19b2351ad1f41",
|
||||
"$argon2i$v=19$m=1048576,t=2,p=1$c29tZXNhbHQ"
|
||||
"$0Vh6ygkiw7XWqD7asxvuPE667zQu1hJ6VdGbI1GtH0E"),
|
||||
hashtest(version, 2, 18, 1, "password", "somesalt",
|
||||
"296dbae80b807cdceaad44ae741b506f14db0959267b183b118f9b24229bc7cb",
|
||||
"$argon2i$v=19$m=262144,t=2,p=1$c29tZXNhbHQ"
|
||||
"$KW266AuAfNzqrUSudBtQbxTbCVkmexg7EY+bJCKbx8s"),
|
||||
hashtest(version, 2, 8, 1, "password", "somesalt",
|
||||
"89e9029f4637b295beb027056a7336c414fadd43f6b208645281cb214a56452f",
|
||||
"$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ"
|
||||
"$iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8"),
|
||||
hashtest(version, 2, 8, 2, "password", "somesalt",
|
||||
"4ff5ce2769a1d7f4c8a491df09d41a9fbe90e5eb02155a13e4c01e20cd4eab61",
|
||||
"$argon2i$v=19$m=256,t=2,p=2$c29tZXNhbHQ"
|
||||
"$T/XOJ2mh1/TIpJHfCdQan76Q5esCFVoT5MAeIM1Oq2E"),
|
||||
hashtest(version, 1, 16, 1, "password", "somesalt",
|
||||
"d168075c4d985e13ebeae560cf8b94c3b5d8a16c51916b6f4ac2da3ac11bbecf",
|
||||
"$argon2i$v=19$m=65536,t=1,p=1$c29tZXNhbHQ"
|
||||
"$0WgHXE2YXhPr6uVgz4uUw7XYoWxRkWtvSsLaOsEbvs8"),
|
||||
hashtest(version, 4, 16, 1, "password", "somesalt",
|
||||
"aaa953d58af3706ce3df1aefd4a64a84e31d7f54175231f1285259f88174ce5b",
|
||||
"$argon2i$v=19$m=65536,t=4,p=1$c29tZXNhbHQ"
|
||||
"$qqlT1YrzcGzj3xrv1KZKhOMdf1QXUjHxKFJZ+IF0zls"),
|
||||
hashtest(version, 2, 16, 1, "differentpassword", "somesalt",
|
||||
"14ae8da01afea8700c2358dcef7c5358d9021282bd88663a4562f59fb74d22ee",
|
||||
"$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ"
|
||||
"$FK6NoBr+qHAMI1jc73xTWNkCEoK9iGY6RWL1n7dNIu4"),
|
||||
hashtest(version, 2, 16, 1, "password", "diffsalt",
|
||||
"b0357cccfbef91f3860b0dba447b2348cbefecadaf990abfe9cc40726c521271",
|
||||
"$argon2i$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ"
|
||||
"$sDV8zPvvkfOGCw26RHsjSMvv7K2vmQq/6cxAcmxSEnE"),
|
||||
])
|
||||
|
||||
# version 1.9 "ID" tests
|
||||
version = 0x13
|
||||
reference_data.extend([
|
||||
hashtest(version, 2, 16, 1, "password", "somesalt",
|
||||
"09316115d5cf24ed5a15a31a3ba326e5cf32edc24702987c02b6566f61913cf7",
|
||||
"$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ"
|
||||
"$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc"),
|
||||
hashtest(version, 2, 18, 1, "password", "somesalt",
|
||||
"78fe1ec91fb3aa5657d72e710854e4c3d9b9198c742f9616c2f085bed95b2e8c",
|
||||
"$argon2id$v=19$m=262144,t=2,p=1$c29tZXNhbHQ"
|
||||
"$eP4eyR+zqlZX1y5xCFTkw9m5GYx0L5YWwvCFvtlbLow"),
|
||||
hashtest(version, 2, 8, 1, "password", "somesalt",
|
||||
"9dfeb910e80bad0311fee20f9c0e2b12c17987b4cac90c2ef54d5b3021c68bfe",
|
||||
"$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ"
|
||||
"$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"),
|
||||
hashtest(version, 2, 8, 2, "password", "somesalt",
|
||||
"6d093c501fd5999645e0ea3bf620d7b8be7fd2db59c20d9fff9539da2bf57037",
|
||||
"$argon2id$v=19$m=256,t=2,p=2$c29tZXNhbHQ"
|
||||
"$bQk8UB/VmZZF4Oo79iDXuL5/0ttZwg2f/5U52iv1cDc"),
|
||||
hashtest(version, 1, 16, 1, "password", "somesalt",
|
||||
"f6a5adc1ba723dddef9b5ac1d464e180fcd9dffc9d1cbf76cca2fed795d9ca98",
|
||||
"$argon2id$v=19$m=65536,t=1,p=1$c29tZXNhbHQ"
|
||||
"$9qWtwbpyPd3vm1rB1GThgPzZ3/ydHL92zKL+15XZypg"),
|
||||
hashtest(version, 4, 16, 1, "password", "somesalt",
|
||||
"9025d48e68ef7395cca9079da4c4ec3affb3c8911fe4f86d1a2520856f63172c",
|
||||
"$argon2id$v=19$m=65536,t=4,p=1$c29tZXNhbHQ"
|
||||
"$kCXUjmjvc5XMqQedpMTsOv+zyJEf5PhtGiUghW9jFyw"),
|
||||
hashtest(version, 2, 16, 1, "differentpassword", "somesalt",
|
||||
"0b84d652cf6b0c4beaef0dfe278ba6a80df6696281d7e0d2891b817d8c458fde",
|
||||
"$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ"
|
||||
"$C4TWUs9rDEvq7w3+J4umqA32aWKB1+DSiRuBfYxFj94"),
|
||||
hashtest(version, 2, 16, 1, "password", "diffsalt",
|
||||
"bdf32b05ccc42eb15d58fd19b1f856b113da1e9a5874fdcc544308565aa8141c",
|
||||
"$argon2id$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ"
|
||||
"$vfMrBczELrFdWP0ZsfhWsRPaHppYdP3MVEMIVlqoFBw"),
|
||||
])
|
||||
|
||||
#=============================================================================
|
||||
# argon2
|
||||
#=============================================================================
|
||||
class _base_argon2_test(HandlerCase):
|
||||
handler = hash.argon2
|
||||
|
||||
known_correct_hashes = [
|
||||
#
|
||||
# custom
|
||||
#
|
||||
|
||||
# sample test
|
||||
("password", '$argon2i$v=19$m=256,t=1,p=1$c29tZXNhbHQ$AJFIsNZTMKTAewB4+ETN1A'),
|
||||
|
||||
# sample w/ all parameters different
|
||||
("password", '$argon2i$v=19$m=380,t=2,p=2$c29tZXNhbHQ$SrssP8n7m/12VWPM8dvNrw'),
|
||||
|
||||
# ensures utf-8 used for unicode
|
||||
(UPASS_TABLE, '$argon2i$v=19$m=512,t=2,p=2$1sV0O4PWLtc12Ypv1f7oGw$'
|
||||
'z+yqzlKtrq3SaNfXDfIDnQ'),
|
||||
(PASS_TABLE_UTF8, '$argon2i$v=19$m=512,t=2,p=2$1sV0O4PWLtc12Ypv1f7oGw$'
|
||||
'z+yqzlKtrq3SaNfXDfIDnQ'),
|
||||
|
||||
# ensure trailing null bytes handled correctly
|
||||
('password\x00', '$argon2i$v=19$m=512,t=2,p=2$c29tZXNhbHQ$Fb5+nPuLzZvtqKRwqUEtUQ'),
|
||||
|
||||
# sample with type D (generated via argon_cffi2.PasswordHasher)
|
||||
("password", '$argon2d$v=19$m=102400,t=2,p=8$g2RodLh8j8WbSdCp+lUy/A$zzAJqL/HSjm809PYQu6qkA'),
|
||||
|
||||
]
|
||||
|
||||
known_malformed_hashes = [
|
||||
# unknown hash type
|
||||
"$argon2qq$v=19$t=2,p=4$c29tZXNhbHQAAAAAAAAAAA$QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY",
|
||||
|
||||
# missing 'm' param
|
||||
"$argon2i$v=19$t=2,p=4$c29tZXNhbHQAAAAAAAAAAA$QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY",
|
||||
|
||||
# 't' param > max uint32
|
||||
"$argon2i$v=19$m=65536,t=8589934592,p=4$c29tZXNhbHQAAAAAAAAAAA$QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY",
|
||||
|
||||
# unexpected param
|
||||
"$argon2i$v=19$m=65536,t=2,p=4,q=5$c29tZXNhbHQAAAAAAAAAAA$QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY",
|
||||
|
||||
# wrong param order
|
||||
"$argon2i$v=19$t=2,m=65536,p=4,q=5$c29tZXNhbHQAAAAAAAAAAA$QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY",
|
||||
|
||||
# constraint violation: m < 8 * p
|
||||
"$argon2i$v=19$m=127,t=2,p=16$c29tZXNhbHQ$IMit9qkFULCMA/ViizL57cnTLOa5DiVM9eMwpAvPwr4",
|
||||
]
|
||||
|
||||
known_parsehash_results = [
|
||||
('$argon2i$v=19$m=256,t=2,p=3$c29tZXNhbHQ$AJFIsNZTMKTAewB4+ETN1A',
|
||||
dict(type="i", memory_cost=256, rounds=2, parallelism=3, salt=b'somesalt',
|
||||
checksum=b'\x00\x91H\xb0\xd6S0\xa4\xc0{\x00x\xf8D\xcd\xd4')),
|
||||
]
|
||||
|
||||
def setUpWarnings(self):
|
||||
super(_base_argon2_test, self).setUpWarnings()
|
||||
warnings.filterwarnings("ignore", ".*Using argon2pure backend.*")
|
||||
|
||||
def do_stub_encrypt(self, handler=None, **settings):
|
||||
if self.backend == "argon2_cffi":
|
||||
# overriding default since no way to get stub config from argon2._calc_hash()
|
||||
# (otherwise test_21b_max_rounds blocks trying to do max rounds)
|
||||
handler = (handler or self.handler).using(**settings)
|
||||
self = handler(use_defaults=True)
|
||||
self.checksum = self._stub_checksum
|
||||
assert self.checksum
|
||||
return self.to_string()
|
||||
else:
|
||||
return super(_base_argon2_test, self).do_stub_encrypt(handler, **settings)
|
||||
|
||||
def test_03_legacy_hash_workflow(self):
|
||||
# override base method
|
||||
raise self.skipTest("legacy 1.6 workflow not supported")
|
||||
|
||||
def test_keyid_parameter(self):
|
||||
# NOTE: keyid parameter currently not supported by official argon2 hash parser,
|
||||
# even though it's mentioned in the format spec.
|
||||
# we're trying to be consistent w/ this, so hashes w/ keyid should
|
||||
# always through a NotImplementedError.
|
||||
self.assertRaises(NotImplementedError, self.handler.verify, 'password',
|
||||
"$argon2i$v=19$m=65536,t=2,p=4,keyid=ABCD$c29tZXNhbHQ$"
|
||||
"IMit9qkFULCMA/ViizL57cnTLOa5DiVM9eMwpAvPwr4")
|
||||
|
||||
def test_data_parameter(self):
|
||||
# NOTE: argon2 c library doesn't support passing in a data parameter to argon2_hash();
|
||||
# but argon2_verify() appears to parse that info... but then discards it (!?).
|
||||
# not sure what proper behavior is, filed issue -- https://github.com/P-H-C/phc-winner-argon2/issues/143
|
||||
# For now, replicating behavior we have for the two backends, to detect when things change.
|
||||
handler = self.handler
|
||||
|
||||
# ref hash of 'password' when 'data' is correctly passed into argon2()
|
||||
sample1 = '$argon2i$v=19$m=512,t=2,p=2,data=c29tZWRhdGE$c29tZXNhbHQ$KgHyCesFyyjkVkihZ5VNFw'
|
||||
|
||||
# ref hash of 'password' when 'data' is silently discarded (same digest as w/o data)
|
||||
sample2 = '$argon2i$v=19$m=512,t=2,p=2,data=c29tZWRhdGE$c29tZXNhbHQ$uEeXt1dxN1iFKGhklseW4w'
|
||||
|
||||
# hash of 'password' w/o the data field
|
||||
sample3 = '$argon2i$v=19$m=512,t=2,p=2$c29tZXNhbHQ$uEeXt1dxN1iFKGhklseW4w'
|
||||
|
||||
#
|
||||
# test sample 1
|
||||
#
|
||||
|
||||
if self.backend == "argon2_cffi":
|
||||
# argon2_cffi v16.1 would incorrectly return False here.
|
||||
# but v16.2 patches so it throws error on data parameter.
|
||||
# our code should detect that, and adapt it into a NotImplementedError
|
||||
self.assertRaises(NotImplementedError, handler.verify, "password", sample1)
|
||||
|
||||
# incorrectly returns sample3, dropping data parameter
|
||||
self.assertEqual(handler.genhash("password", sample1), sample3)
|
||||
|
||||
else:
|
||||
assert self.backend == "argon2pure"
|
||||
# should parse and verify
|
||||
self.assertTrue(handler.verify("password", sample1))
|
||||
|
||||
# should preserve sample1
|
||||
self.assertEqual(handler.genhash("password", sample1), sample1)
|
||||
|
||||
#
|
||||
# test sample 2
|
||||
#
|
||||
|
||||
if self.backend == "argon2_cffi":
|
||||
# argon2_cffi v16.1 would incorrectly return True here.
|
||||
# but v16.2 patches so it throws error on data parameter.
|
||||
# our code should detect that, and adapt it into a NotImplementedError
|
||||
self.assertRaises(NotImplementedError, handler.verify,"password", sample2)
|
||||
|
||||
# incorrectly returns sample3, dropping data parameter
|
||||
self.assertEqual(handler.genhash("password", sample1), sample3)
|
||||
|
||||
else:
|
||||
assert self.backend == "argon2pure"
|
||||
# should parse, but fail to verify
|
||||
self.assertFalse(self.handler.verify("password", sample2))
|
||||
|
||||
# should return sample1 (corrected digest)
|
||||
self.assertEqual(handler.genhash("password", sample2), sample1)
|
||||
|
||||
def test_keyid_and_data_parameters(self):
|
||||
# test combination of the two, just in case
|
||||
self.assertRaises(NotImplementedError, self.handler.verify, 'stub',
|
||||
"$argon2i$v=19$m=65536,t=2,p=4,keyid=ABCD,data=EFGH$c29tZXNhbHQ$"
|
||||
"IMit9qkFULCMA/ViizL57cnTLOa5DiVM9eMwpAvPwr4")
|
||||
|
||||
def test_type_kwd(self):
|
||||
cls = self.handler
|
||||
|
||||
# XXX: this mirrors test_30_HasManyIdents();
|
||||
# maybe switch argon2 class to use that mixin instead of "type" kwd?
|
||||
|
||||
# check settings
|
||||
self.assertTrue("type" in cls.setting_kwds)
|
||||
|
||||
# check supported type_values
|
||||
for value in cls.type_values:
|
||||
self.assertIsInstance(value, unicode)
|
||||
self.assertTrue("i" in cls.type_values)
|
||||
self.assertTrue("d" in cls.type_values)
|
||||
|
||||
# check default
|
||||
self.assertTrue(cls.type in cls.type_values)
|
||||
|
||||
# check constructor validates ident correctly.
|
||||
handler = cls
|
||||
hash = self.get_sample_hash()[1]
|
||||
kwds = handler.parsehash(hash)
|
||||
del kwds['type']
|
||||
|
||||
# ... accepts good type
|
||||
handler(type=cls.type, **kwds)
|
||||
|
||||
# XXX: this is policy "ident" uses, maybe switch to it?
|
||||
# # ... requires type w/o defaults
|
||||
# self.assertRaises(TypeError, handler, **kwds)
|
||||
handler(**kwds)
|
||||
|
||||
# ... supplies default type
|
||||
handler(use_defaults=True, **kwds)
|
||||
|
||||
# ... rejects bad type
|
||||
self.assertRaises(ValueError, handler, type='xXx', **kwds)
|
||||
|
||||
def test_type_using(self):
|
||||
handler = self.handler
|
||||
|
||||
# XXX: this mirrors test_has_many_idents_using();
|
||||
# maybe switch argon2 class to use that mixin instead of "type" kwd?
|
||||
|
||||
orig_type = handler.type
|
||||
for alt_type in handler.type_values:
|
||||
if alt_type != orig_type:
|
||||
break
|
||||
else:
|
||||
raise AssertionError("expected to find alternate type: default=%r values=%r" %
|
||||
(orig_type, handler.type_values))
|
||||
|
||||
def effective_type(cls):
|
||||
return cls(use_defaults=True).type
|
||||
|
||||
# keep default if nothing else specified
|
||||
subcls = handler.using()
|
||||
self.assertEqual(subcls.type, orig_type)
|
||||
|
||||
# accepts alt type
|
||||
subcls = handler.using(type=alt_type)
|
||||
self.assertEqual(subcls.type, alt_type)
|
||||
self.assertEqual(handler.type, orig_type)
|
||||
|
||||
# check subcls actually *generates* default type,
|
||||
# and that we didn't affect orig handler
|
||||
self.assertEqual(effective_type(subcls), alt_type)
|
||||
self.assertEqual(effective_type(handler), orig_type)
|
||||
|
||||
# rejects bad type
|
||||
self.assertRaises(ValueError, handler.using, type='xXx')
|
||||
|
||||
# honor 'type' alias
|
||||
subcls = handler.using(type=alt_type)
|
||||
self.assertEqual(subcls.type, alt_type)
|
||||
self.assertEqual(handler.type, orig_type)
|
||||
|
||||
# check type aliases are being honored
|
||||
self.assertEqual(effective_type(handler.using(type="I")), "i")
|
||||
|
||||
def test_needs_update_w_type(self):
|
||||
handler = self.handler
|
||||
|
||||
hash = handler.hash("stub")
|
||||
self.assertFalse(handler.needs_update(hash))
|
||||
|
||||
hash2 = re.sub(r"\$argon2\w+\$", "$argon2d$", hash)
|
||||
self.assertTrue(handler.needs_update(hash2))
|
||||
|
||||
def test_needs_update_w_version(self):
|
||||
handler = self.handler.using(memory_cost=65536, time_cost=2, parallelism=4,
|
||||
digest_size=32)
|
||||
hash = ("$argon2i$m=65536,t=2,p=4$c29tZXNhbHQAAAAAAAAAAA$"
|
||||
"QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY")
|
||||
if handler.max_version == 0x10:
|
||||
self.assertFalse(handler.needs_update(hash))
|
||||
else:
|
||||
self.assertTrue(handler.needs_update(hash))
|
||||
|
||||
def test_argon_byte_encoding(self):
|
||||
"""verify we're using right base64 encoding for argon2"""
|
||||
handler = self.handler
|
||||
if handler.version != 0x13:
|
||||
# TODO: make this fatal, and add refs for other version.
|
||||
raise self.skipTest("handler uses wrong version for sample hashes")
|
||||
|
||||
# 8 byte salt
|
||||
salt = b'somesalt'
|
||||
temp = handler.using(memory_cost=256, time_cost=2, parallelism=2, salt=salt,
|
||||
checksum_size=32, type="i")
|
||||
hash = temp.hash("password")
|
||||
self.assertEqual(hash, "$argon2i$v=19$m=256,t=2,p=2"
|
||||
"$c29tZXNhbHQ"
|
||||
"$T/XOJ2mh1/TIpJHfCdQan76Q5esCFVoT5MAeIM1Oq2E")
|
||||
|
||||
# 16 byte salt
|
||||
salt = b'somesalt\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
temp = handler.using(memory_cost=256, time_cost=2, parallelism=2, salt=salt,
|
||||
checksum_size=32, type="i")
|
||||
hash = temp.hash("password")
|
||||
self.assertEqual(hash, "$argon2i$v=19$m=256,t=2,p=2"
|
||||
"$c29tZXNhbHQAAAAAAAAAAA"
|
||||
"$rqnbEp1/jFDUEKZZmw+z14amDsFqMDC53dIe57ZHD38")
|
||||
|
||||
class FuzzHashGenerator(HandlerCase.FuzzHashGenerator):
|
||||
|
||||
settings_map = HandlerCase.FuzzHashGenerator.settings_map.copy()
|
||||
settings_map.update(memory_cost="random_memory_cost", type="random_type")
|
||||
|
||||
def random_type(self):
|
||||
return self.rng.choice(self.handler.type_values)
|
||||
|
||||
def random_memory_cost(self):
|
||||
if self.test.backend == "argon2pure":
|
||||
return self.randintgauss(128, 384, 256, 128)
|
||||
else:
|
||||
return self.randintgauss(128, 32767, 16384, 4096)
|
||||
|
||||
# TODO: fuzz parallelism, digest_size
|
||||
|
||||
#-----------------------------------------
|
||||
# test suites for specific backends
|
||||
#-----------------------------------------
|
||||
|
||||
class argon2_argon2_cffi_test(_base_argon2_test.create_backend_case("argon2_cffi")):
|
||||
|
||||
# add some more test vectors that take too long under argon2pure
|
||||
known_correct_hashes = _base_argon2_test.known_correct_hashes + [
|
||||
#
|
||||
# sample hashes from argon2 cffi package's unittests,
|
||||
# which in turn were generated by official argon2 cmdline tool.
|
||||
#
|
||||
|
||||
# v1.2, type I, w/o a version tag
|
||||
('password', "$argon2i$m=65536,t=2,p=4$c29tZXNhbHQAAAAAAAAAAA$"
|
||||
"QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY"),
|
||||
|
||||
# v1.3, type I
|
||||
('password', "$argon2i$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$"
|
||||
"IMit9qkFULCMA/ViizL57cnTLOa5DiVM9eMwpAvPwr4"),
|
||||
|
||||
# v1.3, type D
|
||||
('password', "$argon2d$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$"
|
||||
"cZn5d+rFh+ZfuRhm2iGUGgcrW5YLeM6q7L3vBsdmFA0"),
|
||||
|
||||
# v1.3, type ID
|
||||
('password', "$argon2id$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$"
|
||||
"GpZ3sK/oH9p7VIiV56G/64Zo/8GaUw434IimaPqxwCo"),
|
||||
|
||||
#
|
||||
# custom
|
||||
#
|
||||
|
||||
# ensure trailing null bytes handled correctly
|
||||
('password\x00', "$argon2i$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$"
|
||||
"Vpzuc0v0SrP88LcVvmg+z5RoOYpMDKH/lt6O+CZabIQ"),
|
||||
|
||||
]
|
||||
|
||||
# add reference hashes from argon2 clib tests
|
||||
known_correct_hashes.extend(
|
||||
(info['secret'], info['hash']) for info in reference_data
|
||||
if info['logM'] <= (18 if TEST_MODE("full") else 16)
|
||||
)
|
||||
|
||||
class argon2_argon2pure_test(_base_argon2_test.create_backend_case("argon2pure")):
|
||||
|
||||
# XXX: setting max_threads at 1 to prevent argon2pure from using multiprocessing,
|
||||
# which causes big problems when testing under pypy.
|
||||
# would like a "pure_use_threads" option instead, to make it use multiprocessing.dummy instead.
|
||||
handler = hash.argon2.using(memory_cost=32, parallelism=2)
|
||||
|
||||
# don't use multiprocessing for unittests, makes it a lot harder to ctrl-c
|
||||
# XXX: make this controlled by env var?
|
||||
handler.pure_use_threads = True
|
||||
|
||||
# add reference hashes from argon2 clib tests
|
||||
known_correct_hashes = _base_argon2_test.known_correct_hashes[:]
|
||||
|
||||
known_correct_hashes.extend(
|
||||
(info['secret'], info['hash']) for info in reference_data
|
||||
if info['logM'] < 16
|
||||
)
|
||||
|
||||
class FuzzHashGenerator(_base_argon2_test.FuzzHashGenerator):
|
||||
|
||||
def random_rounds(self):
|
||||
# decrease default rounds for fuzz testing to speed up volume.
|
||||
return self.randintgauss(1, 3, 2, 1)
|
||||
|
||||
#=============================================================================
|
||||
# eof
|
||||
#=============================================================================
|
||||
Reference in New Issue
Block a user