Загрузить файлы в «venv/Lib/site-packages/passlib/tests»
This commit is contained in:
688
venv/Lib/site-packages/passlib/tests/test_handlers_bcrypt.py
Normal file
688
venv/Lib/site-packages/passlib/tests/test_handlers_bcrypt.py
Normal file
@@ -0,0 +1,688 @@
|
||||
"""passlib.tests.test_handlers - tests for passlib hash algorithms"""
|
||||
#=============================================================================
|
||||
# imports
|
||||
#=============================================================================
|
||||
from __future__ import with_statement
|
||||
# core
|
||||
import logging; log = logging.getLogger(__name__)
|
||||
import os
|
||||
import warnings
|
||||
# site
|
||||
# pkg
|
||||
from passlib import hash
|
||||
from passlib.handlers.bcrypt import IDENT_2, IDENT_2X
|
||||
from passlib.utils import repeat_string, to_bytes, is_safe_crypt_input
|
||||
from passlib.utils.compat import irange, PY3
|
||||
from passlib.tests.utils import HandlerCase, TEST_MODE
|
||||
from passlib.tests.test_handlers import UPASS_TABLE
|
||||
# module
|
||||
|
||||
#=============================================================================
|
||||
# bcrypt
|
||||
#=============================================================================
|
||||
class _bcrypt_test(HandlerCase):
|
||||
"""base for BCrypt test cases"""
|
||||
handler = hash.bcrypt
|
||||
reduce_default_rounds = True
|
||||
fuzz_salts_need_bcrypt_repair = True
|
||||
|
||||
known_correct_hashes = [
|
||||
#
|
||||
# from JTR 1.7.9
|
||||
#
|
||||
('U*U*U*U*', '$2a$05$c92SVSfjeiCD6F2nAD6y0uBpJDjdRkt0EgeC4/31Rf2LUZbDRDE.O'),
|
||||
('U*U***U', '$2a$05$WY62Xk2TXZ7EvVDQ5fmjNu7b0GEzSzUXUh2cllxJwhtOeMtWV3Ujq'),
|
||||
('U*U***U*', '$2a$05$Fa0iKV3E2SYVUlMknirWU.CFYGvJ67UwVKI1E2FP6XeLiZGcH3MJi'),
|
||||
('*U*U*U*U', '$2a$05$.WRrXibc1zPgIdRXYfv.4uu6TD1KWf0VnHzq/0imhUhuxSxCyeBs2'),
|
||||
('', '$2a$05$Otz9agnajgrAe0.kFVF9V.tzaStZ2s1s4ZWi/LY4sw2k/MTVFj/IO'),
|
||||
|
||||
#
|
||||
# test vectors from http://www.openwall.com/crypt v1.2
|
||||
# note that this omits any hashes that depend on crypt_blowfish's
|
||||
# various CVE-2011-2483 workarounds (hash 2a and \xff\xff in password,
|
||||
# and any 2x hashes); and only contain hashes which are correct
|
||||
# under both crypt_blowfish 1.2 AND OpenBSD.
|
||||
#
|
||||
('U*U', '$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW'),
|
||||
('U*U*', '$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK'),
|
||||
('U*U*U', '$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a'),
|
||||
('', '$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy'),
|
||||
('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
'0123456789chars after 72 are ignored',
|
||||
'$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui'),
|
||||
(b'\xa3',
|
||||
'$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq'),
|
||||
(b'\xff\xa3345',
|
||||
'$2a$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e'),
|
||||
(b'\xa3ab',
|
||||
'$2a$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS'),
|
||||
(b'\xaa'*72 + b'chars after 72 are ignored as usual',
|
||||
'$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6'),
|
||||
(b'\xaa\x55'*36,
|
||||
'$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy'),
|
||||
(b'\x55\xaa\xff'*24,
|
||||
'$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe'),
|
||||
|
||||
# keeping one of their 2y tests, because we are supporting that.
|
||||
(b'\xa3',
|
||||
'$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq'),
|
||||
|
||||
#
|
||||
# 8bit bug (fixed in 2y/2b)
|
||||
#
|
||||
|
||||
# NOTE: see assert_lacks_8bit_bug() for origins of this test vector.
|
||||
(b"\xd1\x91", "$2y$05$6bNw2HLQYeqHYyBfLMsv/OUcZd0LKP39b87nBw3.S2tVZSqiQX6eu"),
|
||||
|
||||
#
|
||||
# bsd wraparound bug (fixed in 2b)
|
||||
#
|
||||
|
||||
# NOTE: if backend is vulnerable, password will hash the same as '0'*72
|
||||
# ("$2a$04$R1lJ2gkNaoPGdafE.H.16.nVyh2niHsGJhayOHLMiXlI45o8/DU.6"),
|
||||
# rather than same as ("0123456789"*8)[:72]
|
||||
# 255 should be sufficient, but checking
|
||||
(("0123456789"*26)[:254], '$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi'),
|
||||
(("0123456789"*26)[:255], '$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi'),
|
||||
(("0123456789"*26)[:256], '$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi'),
|
||||
(("0123456789"*26)[:257], '$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi'),
|
||||
|
||||
|
||||
#
|
||||
# from py-bcrypt tests
|
||||
#
|
||||
('', '$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s.'),
|
||||
('a', '$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u'),
|
||||
('abc', '$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi'),
|
||||
('abcdefghijklmnopqrstuvwxyz',
|
||||
'$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq'),
|
||||
('~!@#$%^&*() ~!@#$%^&*()PNBFRD',
|
||||
'$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS'),
|
||||
|
||||
#
|
||||
# custom test vectors
|
||||
#
|
||||
|
||||
# ensures utf-8 used for unicode
|
||||
(UPASS_TABLE,
|
||||
'$2a$05$Z17AXnnlpzddNUvnC6cZNOSwMA/8oNiKnHTHTwLlBijfucQQlHjaG'),
|
||||
|
||||
# ensure 2b support
|
||||
(UPASS_TABLE,
|
||||
'$2b$05$Z17AXnnlpzddNUvnC6cZNOSwMA/8oNiKnHTHTwLlBijfucQQlHjaG'),
|
||||
|
||||
]
|
||||
|
||||
if TEST_MODE("full"):
|
||||
#
|
||||
# add some extra tests related to 2/2a
|
||||
#
|
||||
CONFIG_2 = '$2$05$' + '.'*22
|
||||
CONFIG_A = '$2a$05$' + '.'*22
|
||||
known_correct_hashes.extend([
|
||||
("", CONFIG_2 + 'J2ihDv8vVf7QZ9BsaRrKyqs2tkn55Yq'),
|
||||
("", CONFIG_A + 'J2ihDv8vVf7QZ9BsaRrKyqs2tkn55Yq'),
|
||||
("abc", CONFIG_2 + 'XuQjdH.wPVNUZ/bOfstdW/FqB8QSjte'),
|
||||
("abc", CONFIG_A + 'ev6gDwpVye3oMCUpLY85aTpfBNHD0Ga'),
|
||||
("abc"*23, CONFIG_2 + 'XuQjdH.wPVNUZ/bOfstdW/FqB8QSjte'),
|
||||
("abc"*23, CONFIG_A + '2kIdfSj/4/R/Q6n847VTvc68BXiRYZC'),
|
||||
("abc"*24, CONFIG_2 + 'XuQjdH.wPVNUZ/bOfstdW/FqB8QSjte'),
|
||||
("abc"*24, CONFIG_A + 'XuQjdH.wPVNUZ/bOfstdW/FqB8QSjte'),
|
||||
("abc"*24+'x', CONFIG_2 + 'XuQjdH.wPVNUZ/bOfstdW/FqB8QSjte'),
|
||||
("abc"*24+'x', CONFIG_A + 'XuQjdH.wPVNUZ/bOfstdW/FqB8QSjte'),
|
||||
])
|
||||
|
||||
known_correct_configs = [
|
||||
('$2a$04$uM6csdM8R9SXTex/gbTaye', UPASS_TABLE,
|
||||
'$2a$04$uM6csdM8R9SXTex/gbTayezuvzFEufYGd2uB6of7qScLjQ4GwcD4G'),
|
||||
]
|
||||
|
||||
known_unidentified_hashes = [
|
||||
# invalid minor version
|
||||
"$2f$12$EXRkfkdmXnagzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q",
|
||||
"$2`$12$EXRkfkdmXnagzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q",
|
||||
]
|
||||
|
||||
known_malformed_hashes = [
|
||||
# bad char in otherwise correct hash
|
||||
# \/
|
||||
"$2a$12$EXRkfkdmXn!gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q",
|
||||
|
||||
# unsupported (but recognized) minor version
|
||||
"$2x$12$EXRkfkdmXnagzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q",
|
||||
|
||||
# rounds not zero-padded (py-bcrypt rejects this, therefore so do we)
|
||||
'$2a$6$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s.'
|
||||
|
||||
# NOTE: salts with padding bits set are technically malformed,
|
||||
# but we can reliably correct & issue a warning for that.
|
||||
]
|
||||
|
||||
platform_crypt_support = [
|
||||
("freedbsd|openbsd|netbsd", True),
|
||||
("darwin", False),
|
||||
("linux", None), # may be present via addon, e.g. debian's libpam-unix2
|
||||
("solaris", None), # depends on system policy
|
||||
]
|
||||
|
||||
#===================================================================
|
||||
# override some methods
|
||||
#===================================================================
|
||||
def setUp(self):
|
||||
# ensure builtin is enabled for duration of test.
|
||||
if TEST_MODE("full") and self.backend == "builtin":
|
||||
key = "PASSLIB_BUILTIN_BCRYPT"
|
||||
orig = os.environ.get(key)
|
||||
if orig:
|
||||
self.addCleanup(os.environ.__setitem__, key, orig)
|
||||
else:
|
||||
self.addCleanup(os.environ.__delitem__, key)
|
||||
os.environ[key] = "true"
|
||||
|
||||
super(_bcrypt_test, self).setUp()
|
||||
|
||||
# silence this warning, will come up a bunch during testing of old 2a hashes.
|
||||
warnings.filterwarnings("ignore", ".*backend is vulnerable to the bsd wraparound bug.*")
|
||||
|
||||
def populate_settings(self, kwds):
|
||||
# builtin is still just way too slow.
|
||||
if self.backend == "builtin":
|
||||
kwds.setdefault("rounds", 4)
|
||||
super(_bcrypt_test, self).populate_settings(kwds)
|
||||
|
||||
#===================================================================
|
||||
# fuzz testing
|
||||
#===================================================================
|
||||
def crypt_supports_variant(self, hash):
|
||||
"""check if OS crypt is expected to support given ident"""
|
||||
from passlib.handlers.bcrypt import bcrypt, IDENT_2X, IDENT_2Y
|
||||
from passlib.utils import safe_crypt
|
||||
ident = bcrypt.from_string(hash)
|
||||
return (safe_crypt("test", ident + "04$5BJqKfqMQvV7nS.yUguNcu") or "").startswith(ident)
|
||||
|
||||
fuzz_verifiers = HandlerCase.fuzz_verifiers + (
|
||||
"fuzz_verifier_bcrypt",
|
||||
"fuzz_verifier_pybcrypt",
|
||||
"fuzz_verifier_bcryptor",
|
||||
)
|
||||
|
||||
def fuzz_verifier_bcrypt(self):
|
||||
# test against bcrypt, if available
|
||||
from passlib.handlers.bcrypt import IDENT_2, IDENT_2A, IDENT_2B, IDENT_2X, IDENT_2Y, _detect_pybcrypt
|
||||
from passlib.utils import to_native_str, to_bytes
|
||||
try:
|
||||
import bcrypt
|
||||
except ImportError:
|
||||
return
|
||||
if _detect_pybcrypt():
|
||||
return
|
||||
def check_bcrypt(secret, hash):
|
||||
"""bcrypt"""
|
||||
secret = to_bytes(secret, self.FuzzHashGenerator.password_encoding)
|
||||
if hash.startswith(IDENT_2B):
|
||||
# bcrypt <1.1 lacks 2B support
|
||||
hash = IDENT_2A + hash[4:]
|
||||
elif hash.startswith(IDENT_2):
|
||||
# bcrypt doesn't support $2$ hashes; but we can fake it
|
||||
# using the $2a$ algorithm, by repeating the password until
|
||||
# it's 72 chars in length.
|
||||
hash = IDENT_2A + hash[3:]
|
||||
if secret:
|
||||
secret = repeat_string(secret, 72)
|
||||
elif hash.startswith(IDENT_2Y) and bcrypt.__version__ == "3.0.0":
|
||||
hash = IDENT_2B + hash[4:]
|
||||
hash = to_bytes(hash)
|
||||
try:
|
||||
return bcrypt.hashpw(secret, hash) == hash
|
||||
except ValueError:
|
||||
raise ValueError("bcrypt rejected hash: %r (secret=%r)" % (hash, secret))
|
||||
return check_bcrypt
|
||||
|
||||
def fuzz_verifier_pybcrypt(self):
|
||||
# test against py-bcrypt, if available
|
||||
from passlib.handlers.bcrypt import (
|
||||
IDENT_2, IDENT_2A, IDENT_2B, IDENT_2X, IDENT_2Y,
|
||||
_PyBcryptBackend,
|
||||
)
|
||||
from passlib.utils import to_native_str
|
||||
|
||||
loaded = _PyBcryptBackend._load_backend_mixin("pybcrypt", False)
|
||||
if not loaded:
|
||||
return
|
||||
|
||||
from passlib.handlers.bcrypt import _pybcrypt as bcrypt_mod
|
||||
|
||||
lock = _PyBcryptBackend._calc_lock # reuse threadlock workaround for pybcrypt 0.2
|
||||
|
||||
def check_pybcrypt(secret, hash):
|
||||
"""pybcrypt"""
|
||||
secret = to_native_str(secret, self.FuzzHashGenerator.password_encoding)
|
||||
if len(secret) > 200: # vulnerable to wraparound bug
|
||||
secret = secret[:200]
|
||||
if hash.startswith((IDENT_2B, IDENT_2Y)):
|
||||
hash = IDENT_2A + hash[4:]
|
||||
try:
|
||||
if lock:
|
||||
with lock:
|
||||
return bcrypt_mod.hashpw(secret, hash) == hash
|
||||
else:
|
||||
return bcrypt_mod.hashpw(secret, hash) == hash
|
||||
except ValueError:
|
||||
raise ValueError("py-bcrypt rejected hash: %r" % (hash,))
|
||||
return check_pybcrypt
|
||||
|
||||
def fuzz_verifier_bcryptor(self):
|
||||
# test against bcryptor if available
|
||||
from passlib.handlers.bcrypt import IDENT_2, IDENT_2A, IDENT_2Y, IDENT_2B
|
||||
from passlib.utils import to_native_str
|
||||
try:
|
||||
from bcryptor.engine import Engine
|
||||
except ImportError:
|
||||
return
|
||||
def check_bcryptor(secret, hash):
|
||||
"""bcryptor"""
|
||||
secret = to_native_str(secret, self.FuzzHashGenerator.password_encoding)
|
||||
if hash.startswith((IDENT_2B, IDENT_2Y)):
|
||||
hash = IDENT_2A + hash[4:]
|
||||
elif hash.startswith(IDENT_2):
|
||||
# bcryptor doesn't support $2$ hashes; but we can fake it
|
||||
# using the $2a$ algorithm, by repeating the password until
|
||||
# it's 72 chars in length.
|
||||
hash = IDENT_2A + hash[3:]
|
||||
if secret:
|
||||
secret = repeat_string(secret, 72)
|
||||
return Engine(False).hash_key(secret, hash) == hash
|
||||
return check_bcryptor
|
||||
|
||||
class FuzzHashGenerator(HandlerCase.FuzzHashGenerator):
|
||||
|
||||
def generate(self):
|
||||
opts = super(_bcrypt_test.FuzzHashGenerator, self).generate()
|
||||
|
||||
secret = opts['secret']
|
||||
other = opts['other']
|
||||
settings = opts['settings']
|
||||
ident = settings.get('ident')
|
||||
|
||||
if ident == IDENT_2X:
|
||||
# 2x is just recognized, not supported. don't test with it.
|
||||
del settings['ident']
|
||||
|
||||
elif ident == IDENT_2 and other and repeat_string(to_bytes(other), len(to_bytes(secret))) == to_bytes(secret):
|
||||
# avoid false failure due to flaw in 0-revision bcrypt:
|
||||
# repeated strings like 'abc' and 'abcabc' hash identically.
|
||||
opts['secret'], opts['other'] = self.random_password_pair()
|
||||
|
||||
return opts
|
||||
|
||||
def random_rounds(self):
|
||||
# decrease default rounds for fuzz testing to speed up volume.
|
||||
return self.randintgauss(5, 8, 6, 1)
|
||||
|
||||
#===================================================================
|
||||
# custom tests
|
||||
#===================================================================
|
||||
known_incorrect_padding = [
|
||||
# password, bad hash, good hash
|
||||
|
||||
# 2 bits of salt padding set
|
||||
# ("loppux", # \/
|
||||
# "$2a$12$oaQbBqq8JnSM1NHRPQGXORm4GCUMqp7meTnkft4zgSnrbhoKdDV0C",
|
||||
# "$2a$12$oaQbBqq8JnSM1NHRPQGXOOm4GCUMqp7meTnkft4zgSnrbhoKdDV0C"),
|
||||
("test", # \/
|
||||
'$2a$04$oaQbBqq8JnSM1NHRPQGXORY4Vw3bdHKLIXTecPDRAcJ98cz1ilveO',
|
||||
'$2a$04$oaQbBqq8JnSM1NHRPQGXOOY4Vw3bdHKLIXTecPDRAcJ98cz1ilveO'),
|
||||
|
||||
# all 4 bits of salt padding set
|
||||
# ("Passlib11", # \/
|
||||
# "$2a$12$M8mKpW9a2vZ7PYhq/8eJVcUtKxpo6j0zAezu0G/HAMYgMkhPu4fLK",
|
||||
# "$2a$12$M8mKpW9a2vZ7PYhq/8eJVOUtKxpo6j0zAezu0G/HAMYgMkhPu4fLK"),
|
||||
("test", # \/
|
||||
"$2a$04$yjDgE74RJkeqC0/1NheSScrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIS",
|
||||
"$2a$04$yjDgE74RJkeqC0/1NheSSOrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIS"),
|
||||
|
||||
# bad checksum padding
|
||||
("test", # \/
|
||||
"$2a$04$yjDgE74RJkeqC0/1NheSSOrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIV",
|
||||
"$2a$04$yjDgE74RJkeqC0/1NheSSOrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIS"),
|
||||
]
|
||||
|
||||
def test_90_bcrypt_padding(self):
|
||||
"""test passlib correctly handles bcrypt padding bits"""
|
||||
self.require_TEST_MODE("full")
|
||||
#
|
||||
# prevents reccurrence of issue 25 (https://code.google.com/p/passlib/issues/detail?id=25)
|
||||
# were some unused bits were incorrectly set in bcrypt salt strings.
|
||||
# (fixed since 1.5.3)
|
||||
#
|
||||
bcrypt = self.handler
|
||||
corr_desc = ".*incorrectly set padding bits"
|
||||
|
||||
#
|
||||
# test hash() / genconfig() don't generate invalid salts anymore
|
||||
#
|
||||
def check_padding(hash):
|
||||
assert hash.startswith(("$2a$", "$2b$")) and len(hash) >= 28, \
|
||||
"unexpectedly malformed hash: %r" % (hash,)
|
||||
self.assertTrue(hash[28] in '.Oeu',
|
||||
"unused bits incorrectly set in hash: %r" % (hash,))
|
||||
for i in irange(6):
|
||||
check_padding(bcrypt.genconfig())
|
||||
for i in irange(3):
|
||||
check_padding(bcrypt.using(rounds=bcrypt.min_rounds).hash("bob"))
|
||||
|
||||
#
|
||||
# test genconfig() corrects invalid salts & issues warning.
|
||||
#
|
||||
with self.assertWarningList(["salt too large", corr_desc]):
|
||||
hash = bcrypt.genconfig(salt="."*21 + "A.", rounds=5, relaxed=True)
|
||||
self.assertEqual(hash, "$2b$05$" + "." * (22 + 31))
|
||||
|
||||
#
|
||||
# test public methods against good & bad hashes
|
||||
#
|
||||
samples = self.known_incorrect_padding
|
||||
for pwd, bad, good in samples:
|
||||
|
||||
# make sure genhash() corrects bad configs, leaves good unchanged
|
||||
with self.assertWarningList([corr_desc]):
|
||||
self.assertEqual(bcrypt.genhash(pwd, bad), good)
|
||||
with self.assertWarningList([]):
|
||||
self.assertEqual(bcrypt.genhash(pwd, good), good)
|
||||
|
||||
# make sure verify() works correctly with good & bad hashes
|
||||
with self.assertWarningList([corr_desc]):
|
||||
self.assertTrue(bcrypt.verify(pwd, bad))
|
||||
with self.assertWarningList([]):
|
||||
self.assertTrue(bcrypt.verify(pwd, good))
|
||||
|
||||
# make sure normhash() corrects bad hashes, leaves good unchanged
|
||||
with self.assertWarningList([corr_desc]):
|
||||
self.assertEqual(bcrypt.normhash(bad), good)
|
||||
with self.assertWarningList([]):
|
||||
self.assertEqual(bcrypt.normhash(good), good)
|
||||
|
||||
# make sure normhash() leaves non-bcrypt hashes alone
|
||||
self.assertEqual(bcrypt.normhash("$md5$abc"), "$md5$abc")
|
||||
|
||||
def test_needs_update_w_padding(self):
|
||||
"""needs_update corrects bcrypt padding"""
|
||||
# NOTE: see padding test above for details about issue this detects
|
||||
bcrypt = self.handler.using(rounds=4)
|
||||
|
||||
# PASS1 = "test"
|
||||
# bad contains invalid 'c' char at end of salt:
|
||||
# \/
|
||||
BAD1 = "$2a$04$yjDgE74RJkeqC0/1NheSScrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIS"
|
||||
GOOD1 = "$2a$04$yjDgE74RJkeqC0/1NheSSOrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIS"
|
||||
|
||||
self.assertTrue(bcrypt.needs_update(BAD1))
|
||||
self.assertFalse(bcrypt.needs_update(GOOD1))
|
||||
|
||||
#===================================================================
|
||||
# eoc
|
||||
#===================================================================
|
||||
|
||||
# create test cases for specific backends
|
||||
bcrypt_bcrypt_test = _bcrypt_test.create_backend_case("bcrypt")
|
||||
bcrypt_pybcrypt_test = _bcrypt_test.create_backend_case("pybcrypt")
|
||||
bcrypt_bcryptor_test = _bcrypt_test.create_backend_case("bcryptor")
|
||||
|
||||
class bcrypt_os_crypt_test(_bcrypt_test.create_backend_case("os_crypt")):
|
||||
|
||||
# os crypt doesn't support non-utf8 secret bytes
|
||||
known_correct_hashes = [row for row in _bcrypt_test.known_correct_hashes
|
||||
if is_safe_crypt_input(row[0])]
|
||||
|
||||
# os crypt backend doesn't currently implement a per-call fallback if it fails
|
||||
has_os_crypt_fallback = False
|
||||
|
||||
bcrypt_builtin_test = _bcrypt_test.create_backend_case("builtin")
|
||||
|
||||
#=============================================================================
|
||||
# bcrypt
|
||||
#=============================================================================
|
||||
class _bcrypt_sha256_test(HandlerCase):
|
||||
"base for BCrypt-SHA256 test cases"
|
||||
handler = hash.bcrypt_sha256
|
||||
reduce_default_rounds = True
|
||||
forbidden_characters = None
|
||||
fuzz_salts_need_bcrypt_repair = True
|
||||
|
||||
known_correct_hashes = [
|
||||
#-------------------------------------------------------------------
|
||||
# custom test vectors for old v1 format
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
# empty
|
||||
("",
|
||||
'$bcrypt-sha256$2a,5$E/e/2AOhqM5W/KJTFQzLce$F6dYSxOdAEoJZO2eoHUZWZljW/e0TXO'),
|
||||
|
||||
# ascii
|
||||
("password",
|
||||
'$bcrypt-sha256$2a,5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu'),
|
||||
|
||||
# unicode / utf8
|
||||
(UPASS_TABLE,
|
||||
'$bcrypt-sha256$2a,5$.US1fQ4TQS.ZTz/uJ5Kyn.$QNdPDOTKKT5/sovNz1iWg26quOU4Pje'),
|
||||
(UPASS_TABLE.encode("utf-8"),
|
||||
'$bcrypt-sha256$2a,5$.US1fQ4TQS.ZTz/uJ5Kyn.$QNdPDOTKKT5/sovNz1iWg26quOU4Pje'),
|
||||
|
||||
# ensure 2b support
|
||||
("password",
|
||||
'$bcrypt-sha256$2b,5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu'),
|
||||
(UPASS_TABLE,
|
||||
'$bcrypt-sha256$2b,5$.US1fQ4TQS.ZTz/uJ5Kyn.$QNdPDOTKKT5/sovNz1iWg26quOU4Pje'),
|
||||
|
||||
# test >72 chars is hashed correctly -- under bcrypt these hash the same.
|
||||
# NOTE: test_60_truncate_size() handles this already, this is just for overkill :)
|
||||
(repeat_string("abc123", 72),
|
||||
'$bcrypt-sha256$2b,5$X1g1nh3g0v4h6970O68cxe$r/hyEtqJ0teqPEmfTLoZ83ciAI1Q74.'),
|
||||
(repeat_string("abc123", 72) + "qwr",
|
||||
'$bcrypt-sha256$2b,5$X1g1nh3g0v4h6970O68cxe$021KLEif6epjot5yoxk0m8I0929ohEa'),
|
||||
(repeat_string("abc123", 72) + "xyz",
|
||||
'$bcrypt-sha256$2b,5$X1g1nh3g0v4h6970O68cxe$7.1kgpHduMGEjvM3fX6e/QCvfn6OKja'),
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# custom test vectors for v2 format
|
||||
# TODO: convert to v2 format
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
# empty
|
||||
("",
|
||||
'$bcrypt-sha256$v=2,t=2b,r=5$E/e/2AOhqM5W/KJTFQzLce$WFPIZKtDDTriqWwlmRFfHiOTeheAZWe'),
|
||||
|
||||
# ascii
|
||||
("password",
|
||||
'$bcrypt-sha256$v=2,t=2b,r=5$5Hg1DKFqPE8C2aflZ5vVoe$wOK1VFFtS8IGTrGa7.h5fs0u84qyPbS'),
|
||||
|
||||
# unicode / utf8
|
||||
(UPASS_TABLE,
|
||||
'$bcrypt-sha256$v=2,t=2b,r=5$.US1fQ4TQS.ZTz/uJ5Kyn.$pzzgp40k8reM1CuQb03PvE0IDPQSdV6'),
|
||||
(UPASS_TABLE.encode("utf-8"),
|
||||
'$bcrypt-sha256$v=2,t=2b,r=5$.US1fQ4TQS.ZTz/uJ5Kyn.$pzzgp40k8reM1CuQb03PvE0IDPQSdV6'),
|
||||
|
||||
# test >72 chars is hashed correctly -- under bcrypt these hash the same.
|
||||
# NOTE: test_60_truncate_size() handles this already, this is just for overkill :)
|
||||
(repeat_string("abc123", 72),
|
||||
'$bcrypt-sha256$v=2,t=2b,r=5$X1g1nh3g0v4h6970O68cxe$zu1cloESVFIOsUIo7fCEgkdHaI9SSue'),
|
||||
(repeat_string("abc123", 72) + "qwr",
|
||||
'$bcrypt-sha256$v=2,t=2b,r=5$X1g1nh3g0v4h6970O68cxe$CBF9csfEdW68xv3DwE6xSULXMtqEFP.'),
|
||||
(repeat_string("abc123", 72) + "xyz",
|
||||
'$bcrypt-sha256$v=2,t=2b,r=5$X1g1nh3g0v4h6970O68cxe$zC/1UDUG2ofEXB6Onr2vvyFzfhEOS3S'),
|
||||
]
|
||||
|
||||
known_correct_configs =[
|
||||
# v1
|
||||
('$bcrypt-sha256$2a,5$5Hg1DKFqPE8C2aflZ5vVoe',
|
||||
"password", '$bcrypt-sha256$2a,5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu'),
|
||||
# v2
|
||||
('$bcrypt-sha256$v=2,t=2b,r=5$5Hg1DKFqPE8C2aflZ5vVoe',
|
||||
"password", '$bcrypt-sha256$v=2,t=2b,r=5$5Hg1DKFqPE8C2aflZ5vVoe$wOK1VFFtS8IGTrGa7.h5fs0u84qyPbS'),
|
||||
]
|
||||
|
||||
known_malformed_hashes = [
|
||||
#-------------------------------------------------------------------
|
||||
# v1 format
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
# bad char in otherwise correct hash
|
||||
# \/
|
||||
'$bcrypt-sha256$2a,5$5Hg1DKF!PE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu',
|
||||
|
||||
# unrecognized bcrypt variant
|
||||
'$bcrypt-sha256$2c,5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu',
|
||||
|
||||
# unsupported bcrypt variant
|
||||
'$bcrypt-sha256$2x,5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu',
|
||||
|
||||
# rounds zero-padded
|
||||
'$bcrypt-sha256$2a,05$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu',
|
||||
|
||||
# config string w/ $ added
|
||||
'$bcrypt-sha256$2a,5$5Hg1DKFqPE8C2aflZ5vVoe$',
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# v2 format
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
# bad char in otherwise correct hash
|
||||
# \/
|
||||
'$bcrypt-sha256$v=2,t=2b,r=5$5Hg1DKF!PE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu',
|
||||
|
||||
# unsupported version (for this format)
|
||||
'$bcrypt-sha256$v=1,t=2b,r=5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu',
|
||||
|
||||
# unrecognized version
|
||||
'$bcrypt-sha256$v=3,t=2b,r=5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu',
|
||||
|
||||
# unrecognized bcrypt variant
|
||||
'$bcrypt-sha256$v=2,t=2c,r=5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu',
|
||||
|
||||
# unsupported bcrypt variant
|
||||
'$bcrypt-sha256$v=2,t=2a,r=5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu',
|
||||
'$bcrypt-sha256$v=2,t=2x,r=5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu',
|
||||
|
||||
# rounds zero-padded
|
||||
'$bcrypt-sha256$v=2,t=2b,r=05$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu',
|
||||
|
||||
# config string w/ $ added
|
||||
'$bcrypt-sha256$v=2,t=2b,r=5$5Hg1DKFqPE8C2aflZ5vVoe$',
|
||||
]
|
||||
|
||||
#===================================================================
|
||||
# override some methods -- cloned from bcrypt
|
||||
#===================================================================
|
||||
def setUp(self):
|
||||
# ensure builtin is enabled for duration of test.
|
||||
if TEST_MODE("full") and self.backend == "builtin":
|
||||
key = "PASSLIB_BUILTIN_BCRYPT"
|
||||
orig = os.environ.get(key)
|
||||
if orig:
|
||||
self.addCleanup(os.environ.__setitem__, key, orig)
|
||||
else:
|
||||
self.addCleanup(os.environ.__delitem__, key)
|
||||
os.environ[key] = "enabled"
|
||||
super(_bcrypt_sha256_test, self).setUp()
|
||||
warnings.filterwarnings("ignore", ".*backend is vulnerable to the bsd wraparound bug.*")
|
||||
|
||||
def populate_settings(self, kwds):
|
||||
# builtin is still just way too slow.
|
||||
if self.backend == "builtin":
|
||||
kwds.setdefault("rounds", 4)
|
||||
super(_bcrypt_sha256_test, self).populate_settings(kwds)
|
||||
|
||||
#===================================================================
|
||||
# override ident tests for now
|
||||
#===================================================================
|
||||
|
||||
def require_many_idents(self):
|
||||
raise self.skipTest("multiple idents not supported")
|
||||
|
||||
def test_30_HasOneIdent(self):
|
||||
# forbidding ident keyword, we only support "2b" for now
|
||||
handler = self.handler
|
||||
handler(use_defaults=True)
|
||||
self.assertRaises(ValueError, handler, ident="$2y$", use_defaults=True)
|
||||
|
||||
#===================================================================
|
||||
# fuzz testing -- cloned from bcrypt
|
||||
#===================================================================
|
||||
|
||||
class FuzzHashGenerator(HandlerCase.FuzzHashGenerator):
|
||||
|
||||
def random_rounds(self):
|
||||
# decrease default rounds for fuzz testing to speed up volume.
|
||||
return self.randintgauss(5, 8, 6, 1)
|
||||
|
||||
def random_ident(self):
|
||||
return "2b"
|
||||
|
||||
#===================================================================
|
||||
# custom tests
|
||||
#===================================================================
|
||||
|
||||
def test_using_version(self):
|
||||
# default to v2
|
||||
handler = self.handler
|
||||
self.assertEqual(handler.version, 2)
|
||||
|
||||
# allow v1 explicitly
|
||||
subcls = handler.using(version=1)
|
||||
self.assertEqual(subcls.version, 1)
|
||||
|
||||
# forbid unknown ver
|
||||
self.assertRaises(ValueError, handler.using, version=999)
|
||||
|
||||
# allow '2a' only for v1
|
||||
subcls = handler.using(version=1, ident="2a")
|
||||
self.assertRaises(ValueError, handler.using, ident="2a")
|
||||
|
||||
def test_calc_digest_v2(self):
|
||||
"""
|
||||
test digest calc v2 matches bcrypt()
|
||||
"""
|
||||
from passlib.hash import bcrypt
|
||||
from passlib.crypto.digest import compile_hmac
|
||||
from passlib.utils.binary import b64encode
|
||||
|
||||
# manually calc intermediary digest
|
||||
salt = "nyKYxTAvjmy6lMDYMl11Uu"
|
||||
secret = "test"
|
||||
temp_digest = compile_hmac("sha256", salt.encode("ascii"))(secret.encode("ascii"))
|
||||
temp_digest = b64encode(temp_digest).decode("ascii")
|
||||
self.assertEqual(temp_digest, "J5TlyIDm+IcSWmKiDJm+MeICndBkFVPn4kKdJW8f+xY=")
|
||||
|
||||
# manually final hash from intermediary
|
||||
# XXX: genhash() could be useful here
|
||||
bcrypt_digest = bcrypt(ident="2b", salt=salt, rounds=12)._calc_checksum(temp_digest)
|
||||
self.assertEqual(bcrypt_digest, "M0wE0Ov/9LXoQFCe.jRHu3MSHPF54Ta")
|
||||
self.assertTrue(bcrypt.verify(temp_digest, "$2b$12$" + salt + bcrypt_digest))
|
||||
|
||||
# confirm handler outputs same thing.
|
||||
# XXX: genhash() could be useful here
|
||||
result = self.handler(ident="2b", salt=salt, rounds=12)._calc_checksum(secret)
|
||||
self.assertEqual(result, bcrypt_digest)
|
||||
|
||||
#===================================================================
|
||||
# eoc
|
||||
#===================================================================
|
||||
|
||||
# create test cases for specific backends
|
||||
bcrypt_sha256_bcrypt_test = _bcrypt_sha256_test.create_backend_case("bcrypt")
|
||||
bcrypt_sha256_pybcrypt_test = _bcrypt_sha256_test.create_backend_case("pybcrypt")
|
||||
bcrypt_sha256_bcryptor_test = _bcrypt_sha256_test.create_backend_case("bcryptor")
|
||||
|
||||
class bcrypt_sha256_os_crypt_test(_bcrypt_sha256_test.create_backend_case("os_crypt")):
|
||||
|
||||
@classmethod
|
||||
def _get_safe_crypt_handler_backend(cls):
|
||||
return bcrypt_os_crypt_test._get_safe_crypt_handler_backend()
|
||||
|
||||
has_os_crypt_fallback = False
|
||||
|
||||
bcrypt_sha256_builtin_test = _bcrypt_sha256_test.create_backend_case("builtin")
|
||||
|
||||
#=============================================================================
|
||||
# eof
|
||||
#=============================================================================
|
||||
457
venv/Lib/site-packages/passlib/tests/test_handlers_cisco.py
Normal file
457
venv/Lib/site-packages/passlib/tests/test_handlers_cisco.py
Normal file
@@ -0,0 +1,457 @@
|
||||
"""
|
||||
passlib.tests.test_handlers_cisco - tests for Cisco-specific algorithms
|
||||
"""
|
||||
#=============================================================================
|
||||
# imports
|
||||
#=============================================================================
|
||||
from __future__ import absolute_import, division, print_function
|
||||
# core
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
# site
|
||||
# pkg
|
||||
from passlib import hash, exc
|
||||
from passlib.utils.compat import u
|
||||
from .utils import UserHandlerMixin, HandlerCase, repeat_string
|
||||
from .test_handlers import UPASS_TABLE
|
||||
# module
|
||||
__all__ = [
|
||||
"cisco_pix_test",
|
||||
"cisco_asa_test",
|
||||
"cisco_type7_test",
|
||||
]
|
||||
#=============================================================================
|
||||
# shared code for cisco PIX & ASA
|
||||
#=============================================================================
|
||||
|
||||
class _PixAsaSharedTest(UserHandlerMixin, HandlerCase):
|
||||
"""
|
||||
class w/ shared info for PIX & ASA tests.
|
||||
"""
|
||||
__unittest_skip = True # for TestCase
|
||||
requires_user = False # for UserHandlerMixin
|
||||
|
||||
#: shared list of hashes which should be identical under pix & asa7
|
||||
#: (i.e. combined secret + user < 17 bytes)
|
||||
pix_asa_shared_hashes = [
|
||||
#
|
||||
# http://www.perlmonks.org/index.pl?node_id=797623
|
||||
#
|
||||
(("cisco", ""), "2KFQnbNIdI.2KYOU"), # confirmed ASA 9.6
|
||||
|
||||
#
|
||||
# http://www.hsc.fr/ressources/breves/pix_crack.html.en
|
||||
#
|
||||
(("hsc", ""), "YtT8/k6Np8F1yz2c"), # confirmed ASA 9.6
|
||||
|
||||
#
|
||||
# www.freerainbowtables.com/phpBB3/viewtopic.php?f=2&t=1441
|
||||
#
|
||||
(("", ""), "8Ry2YjIyt7RRXU24"), # confirmed ASA 9.6
|
||||
(("cisco", "john"), "hN7LzeyYjw12FSIU"),
|
||||
(("cisco", "jack"), "7DrfeZ7cyOj/PslD"),
|
||||
|
||||
#
|
||||
# http://comments.gmane.org/gmane.comp.security.openwall.john.user/2529
|
||||
#
|
||||
(("ripper", "alex"), "h3mJrcH0901pqX/m"),
|
||||
(("cisco", "cisco"), "3USUcOPFUiMCO4Jk"),
|
||||
(("cisco", "cisco1"), "3USUcOPFUiMCO4Jk"),
|
||||
(("CscFw-ITC!", "admcom"), "lZt7HSIXw3.QP7.R"),
|
||||
("cangetin", "TynyB./ftknE77QP"),
|
||||
(("cangetin", "rramsey"), "jgBZqYtsWfGcUKDi"),
|
||||
|
||||
#
|
||||
# http://openwall.info/wiki/john/sample-hashes
|
||||
#
|
||||
(("phonehome", "rharris"), "zyIIMSYjiPm0L7a6"),
|
||||
|
||||
#
|
||||
# http://www.openwall.com/lists/john-users/2010/08/08/3
|
||||
#
|
||||
(("cangetin", ""), "TynyB./ftknE77QP"),
|
||||
(("cangetin", "rramsey"), "jgBZqYtsWfGcUKDi"),
|
||||
|
||||
#
|
||||
# from JTR 1.7.9
|
||||
#
|
||||
("test1", "TRPEas6f/aa6JSPL"),
|
||||
("test2", "OMT6mXmAvGyzrCtp"),
|
||||
("test3", "gTC7RIy1XJzagmLm"),
|
||||
("test4", "oWC1WRwqlBlbpf/O"),
|
||||
("password", "NuLKvvWGg.x9HEKO"),
|
||||
("0123456789abcdef", ".7nfVBEIEu4KbF/1"),
|
||||
|
||||
#
|
||||
# http://www.cisco.com/en/US/docs/security/pix/pix50/configuration/guide/commands.html#wp5472
|
||||
#
|
||||
(("1234567890123456", ""), "feCkwUGktTCAgIbD"), # canonical source
|
||||
(("watag00s1am", ""), "jMorNbK0514fadBh"), # canonical source
|
||||
|
||||
#
|
||||
# custom
|
||||
#
|
||||
(("cisco1", "cisco1"), "jmINXNH6p1BxUppp"),
|
||||
|
||||
# ensures utf-8 used for unicode
|
||||
(UPASS_TABLE, 'CaiIvkLMu2TOHXGT'),
|
||||
|
||||
#
|
||||
# passlib reference vectors
|
||||
#
|
||||
# Some of these have been confirmed on various ASA firewalls,
|
||||
# and the exact version is noted next to each hash.
|
||||
# Would like to verify these under more PIX & ASA versions.
|
||||
#
|
||||
# Those without a note are generally an extrapolation,
|
||||
# to ensure the code stays consistent, but for various reasons,
|
||||
# hasn't been verified.
|
||||
#
|
||||
# * One such case is usernames w/ 1 & 2 digits --
|
||||
# ASA (9.6 at least) requires 3+ digits in username.
|
||||
#
|
||||
# The following hashes (below 13 chars) should be identical for PIX/ASA.
|
||||
# Ones which differ are listed separately in the known_correct_hashes
|
||||
# list for the two test classes.
|
||||
#
|
||||
|
||||
# 4 char password
|
||||
(('1234', ''), 'RLPMUQ26KL4blgFN'), # confirmed ASA 9.6
|
||||
|
||||
# 8 char password
|
||||
(('01234567', ''), '0T52THgnYdV1tlOF'), # confirmed ASA 9.6
|
||||
(('01234567', '3'), '.z0dT9Alkdc7EIGS'),
|
||||
(('01234567', '36'), 'CC3Lam53t/mHhoE7'),
|
||||
(('01234567', '365'), '8xPrWpNnBdD2DzdZ'), # confirmed ASA 9.6
|
||||
(('01234567', '3333'), '.z0dT9Alkdc7EIGS'), # confirmed ASA 9.6
|
||||
(('01234567', '3636'), 'CC3Lam53t/mHhoE7'), # confirmed ASA 9.6
|
||||
(('01234567', '3653'), '8xPrWpNnBdD2DzdZ'), # confirmed ASA 9.6
|
||||
(('01234567', 'adm'), 'dfWs2qiao6KD/P2L'), # confirmed ASA 9.6
|
||||
(('01234567', 'adma'), 'dfWs2qiao6KD/P2L'), # confirmed ASA 9.6
|
||||
(('01234567', 'admad'), 'dfWs2qiao6KD/P2L'), # confirmed ASA 9.6
|
||||
(('01234567', 'user'), 'PNZ4ycbbZ0jp1.j1'), # confirmed ASA 9.6
|
||||
(('01234567', 'user1234'), 'PNZ4ycbbZ0jp1.j1'), # confirmed ASA 9.6
|
||||
|
||||
# 12 char password
|
||||
(('0123456789ab', ''), 'S31BxZOGlAigndcJ'), # confirmed ASA 9.6
|
||||
(('0123456789ab', '36'), 'wFqSX91X5.YaRKsi'),
|
||||
(('0123456789ab', '365'), 'qjgo3kNgTVxExbno'), # confirmed ASA 9.6
|
||||
(('0123456789ab', '3333'), 'mcXPL/vIZcIxLUQs'), # confirmed ASA 9.6
|
||||
(('0123456789ab', '3636'), 'wFqSX91X5.YaRKsi'), # confirmed ASA 9.6
|
||||
(('0123456789ab', '3653'), 'qjgo3kNgTVxExbno'), # confirmed ASA 9.6
|
||||
(('0123456789ab', 'user'), 'f.T4BKdzdNkjxQl7'), # confirmed ASA 9.6
|
||||
(('0123456789ab', 'user1234'), 'f.T4BKdzdNkjxQl7'), # confirmed ASA 9.6
|
||||
|
||||
# NOTE: remaining reference vectors for 13+ char passwords
|
||||
# are split up between cisco_pix & cisco_asa tests.
|
||||
|
||||
# unicode passwords
|
||||
# ASA supposedly uses utf-8 encoding, but entering non-ascii
|
||||
# chars is error-prone, and while UTF-8 appears to be intended,
|
||||
# observed behaviors include:
|
||||
# * ssh cli stripping non-ascii chars entirely
|
||||
# * ASDM web iface double-encoding utf-8 strings
|
||||
((u("t\xe1ble").encode("utf-8"), 'user'), 'Og8fB4NyF0m5Ed9c'),
|
||||
((u("t\xe1ble").encode("utf-8").decode("latin-1").encode("utf-8"),
|
||||
'user'), 'cMvFC2XVBmK/68yB'), # confirmed ASA 9.6 when typed into ASDM
|
||||
]
|
||||
|
||||
def test_calc_digest_spoiler(self):
|
||||
"""
|
||||
_calc_checksum() -- spoil oversize passwords during verify
|
||||
|
||||
for details, see 'spoil_digest' flag instead that function.
|
||||
this helps cisco_pix/cisco_asa implement their policy of
|
||||
``.truncate_verify_reject=True``.
|
||||
"""
|
||||
def calc(secret, for_hash=False):
|
||||
return self.handler(use_defaults=for_hash)._calc_checksum(secret)
|
||||
|
||||
# short (non-truncated) password
|
||||
short_secret = repeat_string("1234", self.handler.truncate_size)
|
||||
short_hash = calc(short_secret)
|
||||
|
||||
# longer password should have totally different hash,
|
||||
# to prevent verify from matching (i.e. "spoiled").
|
||||
long_secret = short_secret + "X"
|
||||
long_hash = calc(long_secret)
|
||||
self.assertNotEqual(long_hash, short_hash)
|
||||
|
||||
# spoiled hash should depend on whole secret,
|
||||
# so that output isn't predictable
|
||||
alt_long_secret = short_secret + "Y"
|
||||
alt_long_hash = calc(alt_long_secret)
|
||||
self.assertNotEqual(alt_long_hash, short_hash)
|
||||
self.assertNotEqual(alt_long_hash, long_hash)
|
||||
|
||||
# for hash(), should throw error if password too large
|
||||
calc(short_secret, for_hash=True)
|
||||
self.assertRaises(exc.PasswordSizeError, calc, long_secret, for_hash=True)
|
||||
self.assertRaises(exc.PasswordSizeError, calc, alt_long_secret, for_hash=True)
|
||||
|
||||
#=============================================================================
|
||||
# cisco pix
|
||||
#=============================================================================
|
||||
class cisco_pix_test(_PixAsaSharedTest):
|
||||
handler = hash.cisco_pix
|
||||
|
||||
#: known correct pix hashes
|
||||
known_correct_hashes = _PixAsaSharedTest.pix_asa_shared_hashes + [
|
||||
#
|
||||
# passlib reference vectors (PIX-specific)
|
||||
#
|
||||
# NOTE: See 'pix_asa_shared_hashes' for general PIX+ASA vectors,
|
||||
# and general notes about the 'passlib reference vectors' test set.
|
||||
#
|
||||
# All of the following are PIX-specific, as ASA starts
|
||||
# to use a different padding size at 13 characters.
|
||||
#
|
||||
# TODO: these need confirming w/ an actual PIX system.
|
||||
#
|
||||
|
||||
# 13 char password
|
||||
(('0123456789abc', ''), 'eacOpB7vE7ZDukSF'),
|
||||
(('0123456789abc', '3'), 'ylJTd/qei66WZe3w'),
|
||||
(('0123456789abc', '36'), 'hDx8QRlUhwd6bU8N'),
|
||||
(('0123456789abc', '365'), 'vYOOtnkh1HXcMrM7'),
|
||||
(('0123456789abc', '3333'), 'ylJTd/qei66WZe3w'),
|
||||
(('0123456789abc', '3636'), 'hDx8QRlUhwd6bU8N'),
|
||||
(('0123456789abc', '3653'), 'vYOOtnkh1HXcMrM7'),
|
||||
(('0123456789abc', 'user'), 'f4/.SALxqDo59mfV'),
|
||||
(('0123456789abc', 'user1234'), 'f4/.SALxqDo59mfV'),
|
||||
|
||||
# 14 char password
|
||||
(('0123456789abcd', ''), '6r8888iMxEoPdLp4'),
|
||||
(('0123456789abcd', '3'), 'f5lvmqWYj9gJqkIH'),
|
||||
(('0123456789abcd', '36'), 'OJJ1Khg5HeAYBH1c'),
|
||||
(('0123456789abcd', '365'), 'OJJ1Khg5HeAYBH1c'),
|
||||
(('0123456789abcd', '3333'), 'f5lvmqWYj9gJqkIH'),
|
||||
(('0123456789abcd', '3636'), 'OJJ1Khg5HeAYBH1c'),
|
||||
(('0123456789abcd', '3653'), 'OJJ1Khg5HeAYBH1c'),
|
||||
(('0123456789abcd', 'adm'), 'DbPLCFIkHc2SiyDk'),
|
||||
(('0123456789abcd', 'adma'), 'DbPLCFIkHc2SiyDk'),
|
||||
(('0123456789abcd', 'user'), 'WfO2UiTapPkF/FSn'),
|
||||
(('0123456789abcd', 'user1234'), 'WfO2UiTapPkF/FSn'),
|
||||
|
||||
# 15 char password
|
||||
(('0123456789abcde', ''), 'al1e0XFIugTYLai3'),
|
||||
(('0123456789abcde', '3'), 'lYbwBu.f82OIApQB'),
|
||||
(('0123456789abcde', '36'), 'lYbwBu.f82OIApQB'),
|
||||
(('0123456789abcde', '365'), 'lYbwBu.f82OIApQB'),
|
||||
(('0123456789abcde', '3333'), 'lYbwBu.f82OIApQB'),
|
||||
(('0123456789abcde', '3636'), 'lYbwBu.f82OIApQB'),
|
||||
(('0123456789abcde', '3653'), 'lYbwBu.f82OIApQB'),
|
||||
(('0123456789abcde', 'adm'), 'KgKx1UQvdR/09i9u'),
|
||||
(('0123456789abcde', 'adma'), 'KgKx1UQvdR/09i9u'),
|
||||
(('0123456789abcde', 'user'), 'qLopkenJ4WBqxaZN'),
|
||||
(('0123456789abcde', 'user1234'), 'qLopkenJ4WBqxaZN'),
|
||||
|
||||
# 16 char password
|
||||
(('0123456789abcdef', ''), '.7nfVBEIEu4KbF/1'),
|
||||
(('0123456789abcdef', '36'), '.7nfVBEIEu4KbF/1'),
|
||||
(('0123456789abcdef', '365'), '.7nfVBEIEu4KbF/1'),
|
||||
(('0123456789abcdef', '3333'), '.7nfVBEIEu4KbF/1'),
|
||||
(('0123456789abcdef', '3636'), '.7nfVBEIEu4KbF/1'),
|
||||
(('0123456789abcdef', '3653'), '.7nfVBEIEu4KbF/1'),
|
||||
(('0123456789abcdef', 'user'), '.7nfVBEIEu4KbF/1'),
|
||||
(('0123456789abcdef', 'user1234'), '.7nfVBEIEu4KbF/1'),
|
||||
]
|
||||
|
||||
|
||||
#=============================================================================
|
||||
# cisco asa
|
||||
#=============================================================================
|
||||
class cisco_asa_test(_PixAsaSharedTest):
|
||||
handler = hash.cisco_asa
|
||||
|
||||
known_correct_hashes = _PixAsaSharedTest.pix_asa_shared_hashes + [
|
||||
#
|
||||
# passlib reference vectors (ASA-specific)
|
||||
#
|
||||
# NOTE: See 'pix_asa_shared_hashes' for general PIX+ASA vectors,
|
||||
# and general notes about the 'passlib reference vectors' test set.
|
||||
#
|
||||
|
||||
# 13 char password
|
||||
# NOTE: past this point, ASA pads to 32 bytes instead of 16
|
||||
# for all cases where user is set (secret + 4 bytes > 16),
|
||||
# but still uses 16 bytes for enable pwds (secret <= 16).
|
||||
# hashes w/ user WON'T match PIX, but "enable" passwords will.
|
||||
(('0123456789abc', ''), 'eacOpB7vE7ZDukSF'), # confirmed ASA 9.6
|
||||
(('0123456789abc', '36'), 'FRV9JG18UBEgX0.O'),
|
||||
(('0123456789abc', '365'), 'NIwkusG9hmmMy6ZQ'), # confirmed ASA 9.6
|
||||
(('0123456789abc', '3333'), 'NmrkP98nT7RAeKZz'), # confirmed ASA 9.6
|
||||
(('0123456789abc', '3636'), 'FRV9JG18UBEgX0.O'), # confirmed ASA 9.6
|
||||
(('0123456789abc', '3653'), 'NIwkusG9hmmMy6ZQ'), # confirmed ASA 9.6
|
||||
(('0123456789abc', 'user'), '8Q/FZeam5ai1A47p'), # confirmed ASA 9.6
|
||||
(('0123456789abc', 'user1234'), '8Q/FZeam5ai1A47p'), # confirmed ASA 9.6
|
||||
|
||||
# 14 char password
|
||||
(('0123456789abcd', ''), '6r8888iMxEoPdLp4'), # confirmed ASA 9.6
|
||||
(('0123456789abcd', '3'), 'yxGoujXKPduTVaYB'),
|
||||
(('0123456789abcd', '36'), 'W0jckhnhjnr/DiT/'),
|
||||
(('0123456789abcd', '365'), 'HuVOxfMQNahaoF8u'), # confirmed ASA 9.6
|
||||
(('0123456789abcd', '3333'), 'yxGoujXKPduTVaYB'), # confirmed ASA 9.6
|
||||
(('0123456789abcd', '3636'), 'W0jckhnhjnr/DiT/'), # confirmed ASA 9.6
|
||||
(('0123456789abcd', '3653'), 'HuVOxfMQNahaoF8u'), # confirmed ASA 9.6
|
||||
(('0123456789abcd', 'adm'), 'RtOmSeoCs4AUdZqZ'), # confirmed ASA 9.6
|
||||
(('0123456789abcd', 'adma'), 'RtOmSeoCs4AUdZqZ'), # confirmed ASA 9.6
|
||||
(('0123456789abcd', 'user'), 'rrucwrcM0h25pr.m'), # confirmed ASA 9.6
|
||||
(('0123456789abcd', 'user1234'), 'rrucwrcM0h25pr.m'), # confirmed ASA 9.6
|
||||
|
||||
# 15 char password
|
||||
(('0123456789abcde', ''), 'al1e0XFIugTYLai3'), # confirmed ASA 9.6
|
||||
(('0123456789abcde', '3'), 'nAZrQoHaL.fgrIqt'),
|
||||
(('0123456789abcde', '36'), '2GxIQ6ICE795587X'),
|
||||
(('0123456789abcde', '365'), 'QmDsGwCRBbtGEKqM'), # confirmed ASA 9.6
|
||||
(('0123456789abcde', '3333'), 'nAZrQoHaL.fgrIqt'), # confirmed ASA 9.6
|
||||
(('0123456789abcde', '3636'), '2GxIQ6ICE795587X'), # confirmed ASA 9.6
|
||||
(('0123456789abcde', '3653'), 'QmDsGwCRBbtGEKqM'), # confirmed ASA 9.6
|
||||
(('0123456789abcde', 'adm'), 'Aj2aP0d.nk62wl4m'), # confirmed ASA 9.6
|
||||
(('0123456789abcde', 'adma'), 'Aj2aP0d.nk62wl4m'), # confirmed ASA 9.6
|
||||
(('0123456789abcde', 'user'), 'etxiXfo.bINJcXI7'), # confirmed ASA 9.6
|
||||
(('0123456789abcde', 'user1234'), 'etxiXfo.bINJcXI7'), # confirmed ASA 9.6
|
||||
|
||||
# 16 char password
|
||||
(('0123456789abcdef', ''), '.7nfVBEIEu4KbF/1'), # confirmed ASA 9.6
|
||||
(('0123456789abcdef', '36'), 'GhI8.yFSC5lwoafg'),
|
||||
(('0123456789abcdef', '365'), 'KFBI6cNQauyY6h/G'), # confirmed ASA 9.6
|
||||
(('0123456789abcdef', '3333'), 'Ghdi1IlsswgYzzMH'), # confirmed ASA 9.6
|
||||
(('0123456789abcdef', '3636'), 'GhI8.yFSC5lwoafg'), # confirmed ASA 9.6
|
||||
(('0123456789abcdef', '3653'), 'KFBI6cNQauyY6h/G'), # confirmed ASA 9.6
|
||||
(('0123456789abcdef', 'user'), 'IneB.wc9sfRzLPoh'), # confirmed ASA 9.6
|
||||
(('0123456789abcdef', 'user1234'), 'IneB.wc9sfRzLPoh'), # confirmed ASA 9.6
|
||||
|
||||
# 17 char password
|
||||
# NOTE: past this point, ASA pads to 32 bytes instead of 16
|
||||
# for ALL cases, since secret > 16 bytes even for enable pwds;
|
||||
# and so none of these rest here should match PIX.
|
||||
(('0123456789abcdefq', ''), 'bKshl.EN.X3CVFRQ'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefq', '36'), 'JAeTXHs0n30svlaG'),
|
||||
(('0123456789abcdefq', '365'), '4fKSSUBHT1ChGqHp'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefq', '3333'), 'USEJbxI6.VY4ecBP'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefq', '3636'), 'JAeTXHs0n30svlaG'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefq', '3653'), '4fKSSUBHT1ChGqHp'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefq', 'user'), '/dwqyD7nGdwSrDwk'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefq', 'user1234'), '/dwqyD7nGdwSrDwk'), # confirmed ASA 9.6
|
||||
|
||||
# 27 char password
|
||||
(('0123456789abcdefqwertyuiopa', ''), '4wp19zS3OCe.2jt5'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefqwertyuiopa', '36'), 'PjUoGqWBKPyV9qOe'),
|
||||
(('0123456789abcdefqwertyuiopa', '365'), 'bfCy6xFAe5O/gzvM'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefqwertyuiopa', '3333'), 'rd/ZMuGTJFIb2BNG'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefqwertyuiopa', '3636'), 'PjUoGqWBKPyV9qOe'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefqwertyuiopa', '3653'), 'bfCy6xFAe5O/gzvM'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefqwertyuiopa', 'user'), 'zynfWw3UtszxLMgL'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefqwertyuiopa', 'user1234'), 'zynfWw3UtszxLMgL'), # confirmed ASA 9.6
|
||||
|
||||
# 28 char password
|
||||
# NOTE: past this point, ASA stops appending the username AT ALL,
|
||||
# even though there's still room for the first few chars.
|
||||
(('0123456789abcdefqwertyuiopas', ''), 'W6nbOddI0SutTK7m'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefqwertyuiopas', '36'), 'W6nbOddI0SutTK7m'),
|
||||
(('0123456789abcdefqwertyuiopas', '365'), 'W6nbOddI0SutTK7m'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefqwertyuiopas', 'user'), 'W6nbOddI0SutTK7m'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefqwertyuiopas', 'user1234'), 'W6nbOddI0SutTK7m'), # confirmed ASA 9.6
|
||||
|
||||
# 32 char password
|
||||
# NOTE: this is max size that ASA allows, and throws error for larger
|
||||
(('0123456789abcdefqwertyuiopasdfgh', ''), '5hPT/iC6DnoBxo6a'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefqwertyuiopasdfgh', '36'), '5hPT/iC6DnoBxo6a'),
|
||||
(('0123456789abcdefqwertyuiopasdfgh', '365'), '5hPT/iC6DnoBxo6a'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefqwertyuiopasdfgh', 'user'), '5hPT/iC6DnoBxo6a'), # confirmed ASA 9.6
|
||||
(('0123456789abcdefqwertyuiopasdfgh', 'user1234'), '5hPT/iC6DnoBxo6a'), # confirmed ASA 9.6
|
||||
]
|
||||
|
||||
|
||||
#=============================================================================
|
||||
# cisco type 7
|
||||
#=============================================================================
|
||||
class cisco_type7_test(HandlerCase):
|
||||
handler = hash.cisco_type7
|
||||
salt_bits = 4
|
||||
salt_type = int
|
||||
|
||||
known_correct_hashes = [
|
||||
#
|
||||
# http://mccltd.net/blog/?p=1034
|
||||
#
|
||||
("secure ", "04480E051A33490E"),
|
||||
|
||||
#
|
||||
# http://insecure.org/sploits/cisco.passwords.html
|
||||
#
|
||||
("Its time to go to lunch!",
|
||||
"153B1F1F443E22292D73212D5300194315591954465A0D0B59"),
|
||||
|
||||
#
|
||||
# http://blog.ioshints.info/2007/11/type-7-decryption-in-cisco-ios.html
|
||||
#
|
||||
("t35t:pa55w0rd", "08351F1B1D431516475E1B54382F"),
|
||||
|
||||
#
|
||||
# http://www.m00nie.com/2011/09/cisco-type-7-password-decryption-and-encryption-with-perl/
|
||||
#
|
||||
("hiImTesting:)", "020E0D7206320A325847071E5F5E"),
|
||||
|
||||
#
|
||||
# http://packetlife.net/forums/thread/54/
|
||||
#
|
||||
("cisco123", "060506324F41584B56"),
|
||||
("cisco123", "1511021F07257A767B"),
|
||||
|
||||
#
|
||||
# source ?
|
||||
#
|
||||
('Supe&8ZUbeRp4SS', "06351A3149085123301517391C501918"),
|
||||
|
||||
#
|
||||
# custom
|
||||
#
|
||||
|
||||
# ensures utf-8 used for unicode
|
||||
(UPASS_TABLE, '0958EDC8A9F495F6F8A5FD'),
|
||||
]
|
||||
|
||||
known_unidentified_hashes = [
|
||||
# salt with hex value
|
||||
"0A480E051A33490E",
|
||||
|
||||
# salt value > 52. this may in fact be valid, but we reject it for now
|
||||
# (see docs for more).
|
||||
'99400E4812',
|
||||
]
|
||||
|
||||
def test_90_decode(self):
|
||||
"""test cisco_type7.decode()"""
|
||||
from passlib.utils import to_unicode, to_bytes
|
||||
|
||||
handler = self.handler
|
||||
for secret, hash in self.known_correct_hashes:
|
||||
usecret = to_unicode(secret)
|
||||
bsecret = to_bytes(secret)
|
||||
self.assertEqual(handler.decode(hash), usecret)
|
||||
self.assertEqual(handler.decode(hash, None), bsecret)
|
||||
|
||||
self.assertRaises(UnicodeDecodeError, handler.decode,
|
||||
'0958EDC8A9F495F6F8A5FD', 'ascii')
|
||||
|
||||
def test_91_salt(self):
|
||||
"""test salt value border cases"""
|
||||
handler = self.handler
|
||||
self.assertRaises(TypeError, handler, salt=None)
|
||||
handler(salt=None, use_defaults=True)
|
||||
self.assertRaises(TypeError, handler, salt='abc')
|
||||
self.assertRaises(ValueError, handler, salt=-10)
|
||||
self.assertRaises(ValueError, handler, salt=100)
|
||||
|
||||
self.assertRaises(TypeError, handler.using, salt='abc')
|
||||
self.assertRaises(ValueError, handler.using, salt=-10)
|
||||
self.assertRaises(ValueError, handler.using, salt=100)
|
||||
with self.assertWarningList("salt/offset must be.*"):
|
||||
subcls = handler.using(salt=100, relaxed=True)
|
||||
self.assertEqual(subcls(use_defaults=True).salt, 52)
|
||||
|
||||
#=============================================================================
|
||||
# eof
|
||||
#=============================================================================
|
||||
413
venv/Lib/site-packages/passlib/tests/test_handlers_django.py
Normal file
413
venv/Lib/site-packages/passlib/tests/test_handlers_django.py
Normal file
@@ -0,0 +1,413 @@
|
||||
"""passlib.tests.test_handlers_django - tests for passlib hash algorithms"""
|
||||
#=============================================================================
|
||||
# imports
|
||||
#=============================================================================
|
||||
from __future__ import with_statement
|
||||
# core
|
||||
import logging; log = logging.getLogger(__name__)
|
||||
import re
|
||||
import warnings
|
||||
# site
|
||||
# pkg
|
||||
from passlib import hash
|
||||
from passlib.utils import repeat_string
|
||||
from passlib.utils.compat import u
|
||||
from passlib.tests.utils import TestCase, HandlerCase, skipUnless, SkipTest
|
||||
from passlib.tests.test_handlers import UPASS_USD, UPASS_TABLE
|
||||
from passlib.tests.test_ext_django import DJANGO_VERSION, MIN_DJANGO_VERSION, \
|
||||
check_django_hasher_has_backend
|
||||
# module
|
||||
|
||||
#=============================================================================
|
||||
# django
|
||||
#=============================================================================
|
||||
|
||||
# standard string django uses
|
||||
UPASS_LETMEIN = u('l\xe8tmein')
|
||||
|
||||
def vstr(version):
|
||||
return ".".join(str(e) for e in version)
|
||||
|
||||
class _DjangoHelper(TestCase):
|
||||
"""
|
||||
mixin for HandlerCase subclasses that are testing a hasher
|
||||
which is also present in django.
|
||||
"""
|
||||
__unittest_skip = True
|
||||
|
||||
#: minimum django version where hash alg is present / that we support testing against
|
||||
min_django_version = MIN_DJANGO_VERSION
|
||||
|
||||
#: max django version where hash alg is present
|
||||
#: TODO: for a bunch of the tests below, this is just max version where
|
||||
#: settings.PASSWORD_HASHERS includes it by default -- could add helper to patch
|
||||
#: desired django hasher back in for duration of test.
|
||||
#: XXX: change this to "disabled_in_django_version" instead?
|
||||
max_django_version = None
|
||||
|
||||
def _require_django_support(self):
|
||||
# make sure min django version
|
||||
if DJANGO_VERSION < self.min_django_version:
|
||||
raise self.skipTest("Django >= %s not installed" % vstr(self.min_django_version))
|
||||
if self.max_django_version and DJANGO_VERSION > self.max_django_version:
|
||||
raise self.skipTest("Django <= %s not installed" % vstr(self.max_django_version))
|
||||
|
||||
# make sure django has a backend for specified hasher
|
||||
name = self.handler.django_name
|
||||
if not check_django_hasher_has_backend(name):
|
||||
raise self.skipTest('django hasher %r not available' % name)
|
||||
|
||||
return True
|
||||
|
||||
extra_fuzz_verifiers = HandlerCase.fuzz_verifiers + (
|
||||
"fuzz_verifier_django",
|
||||
)
|
||||
|
||||
def fuzz_verifier_django(self):
|
||||
try:
|
||||
self._require_django_support()
|
||||
except SkipTest:
|
||||
return None
|
||||
from django.contrib.auth.hashers import check_password
|
||||
|
||||
def verify_django(secret, hash):
|
||||
"""django/check_password"""
|
||||
if self.handler.name == "django_bcrypt" and hash.startswith("bcrypt$$2y$"):
|
||||
hash = hash.replace("$$2y$", "$$2a$")
|
||||
if isinstance(secret, bytes):
|
||||
secret = secret.decode("utf-8")
|
||||
return check_password(secret, hash)
|
||||
return verify_django
|
||||
|
||||
def test_90_django_reference(self):
|
||||
"""run known correct hashes through Django's check_password()"""
|
||||
self._require_django_support()
|
||||
# XXX: esp. when it's no longer supported by django,
|
||||
# should verify it's *NOT* recognized
|
||||
from django.contrib.auth.hashers import check_password
|
||||
assert self.known_correct_hashes
|
||||
for secret, hash in self.iter_known_hashes():
|
||||
self.assertTrue(check_password(secret, hash),
|
||||
"secret=%r hash=%r failed to verify" %
|
||||
(secret, hash))
|
||||
self.assertFalse(check_password('x' + secret, hash),
|
||||
"mangled secret=%r hash=%r incorrect verified" %
|
||||
(secret, hash))
|
||||
|
||||
def test_91_django_generation(self):
|
||||
"""test against output of Django's make_password()"""
|
||||
self._require_django_support()
|
||||
# XXX: esp. when it's no longer supported by django,
|
||||
# should verify it's *NOT* recognized
|
||||
from passlib.utils import tick
|
||||
from django.contrib.auth.hashers import make_password
|
||||
name = self.handler.django_name # set for all the django_* handlers
|
||||
end = tick() + self.max_fuzz_time/2
|
||||
generator = self.FuzzHashGenerator(self, self.getRandom())
|
||||
while tick() < end:
|
||||
secret, other = generator.random_password_pair()
|
||||
if not secret: # django rejects empty passwords.
|
||||
continue
|
||||
if isinstance(secret, bytes):
|
||||
secret = secret.decode("utf-8")
|
||||
hash = make_password(secret, hasher=name)
|
||||
self.assertTrue(self.do_identify(hash))
|
||||
self.assertTrue(self.do_verify(secret, hash))
|
||||
self.assertFalse(self.do_verify(other, hash))
|
||||
|
||||
class django_disabled_test(HandlerCase):
|
||||
"""test django_disabled"""
|
||||
handler = hash.django_disabled
|
||||
disabled_contains_salt = True
|
||||
|
||||
known_correct_hashes = [
|
||||
# *everything* should hash to "!", and nothing should verify
|
||||
("password", "!"),
|
||||
("", "!"),
|
||||
(UPASS_TABLE, "!"),
|
||||
]
|
||||
|
||||
known_alternate_hashes = [
|
||||
# django 1.6 appends random alpnum string
|
||||
("!9wa845vn7098ythaehasldkfj", "password", "!"),
|
||||
]
|
||||
|
||||
class django_des_crypt_test(HandlerCase, _DjangoHelper):
|
||||
"""test django_des_crypt"""
|
||||
handler = hash.django_des_crypt
|
||||
max_django_version = (1,9)
|
||||
|
||||
known_correct_hashes = [
|
||||
# ensures only first two digits of salt count.
|
||||
("password", 'crypt$c2$c2M87q...WWcU'),
|
||||
("password", 'crypt$c2e86$c2M87q...WWcU'),
|
||||
("passwordignoreme", 'crypt$c2.AZ$c2M87q...WWcU'),
|
||||
|
||||
# ensures utf-8 used for unicode
|
||||
(UPASS_USD, 'crypt$c2e86$c2hN1Bxd6ZiWs'),
|
||||
(UPASS_TABLE, 'crypt$0.aQs$0.wB.TT0Czvlo'),
|
||||
(u("hell\u00D6"), "crypt$sa$saykDgk3BPZ9E"),
|
||||
|
||||
# prevent regression of issue 22
|
||||
("foo", 'crypt$MNVY.9ajgdvDQ$MNVY.9ajgdvDQ'),
|
||||
]
|
||||
|
||||
known_alternate_hashes = [
|
||||
# ensure django 1.4 empty salt field is accepted;
|
||||
# but that salt field is re-filled (for django 1.0 compatibility)
|
||||
('crypt$$c2M87q...WWcU', "password", 'crypt$c2$c2M87q...WWcU'),
|
||||
]
|
||||
|
||||
known_unidentified_hashes = [
|
||||
'sha1$aa$bb',
|
||||
]
|
||||
|
||||
known_malformed_hashes = [
|
||||
# checksum too short
|
||||
'crypt$c2$c2M87q',
|
||||
|
||||
# salt must be >2
|
||||
'crypt$f$c2M87q...WWcU',
|
||||
|
||||
# make sure first 2 chars of salt & chk field agree.
|
||||
'crypt$ffe86$c2M87q...WWcU',
|
||||
]
|
||||
|
||||
class django_salted_md5_test(HandlerCase, _DjangoHelper):
|
||||
"""test django_salted_md5"""
|
||||
handler = hash.django_salted_md5
|
||||
max_django_version = (1,9)
|
||||
|
||||
known_correct_hashes = [
|
||||
# test extra large salt
|
||||
("password", 'md5$123abcdef$c8272612932975ee80e8a35995708e80'),
|
||||
|
||||
# test django 1.4 alphanumeric salt
|
||||
("test", 'md5$3OpqnFAHW5CT$54b29300675271049a1ebae07b395e20'),
|
||||
|
||||
# ensures utf-8 used for unicode
|
||||
(UPASS_USD, 'md5$c2e86$92105508419a81a6babfaecf876a2fa0'),
|
||||
(UPASS_TABLE, 'md5$d9eb8$01495b32852bffb27cf5d4394fe7a54c'),
|
||||
]
|
||||
|
||||
known_unidentified_hashes = [
|
||||
'sha1$aa$bb',
|
||||
]
|
||||
|
||||
known_malformed_hashes = [
|
||||
# checksum too short
|
||||
'md5$aa$bb',
|
||||
]
|
||||
|
||||
class FuzzHashGenerator(HandlerCase.FuzzHashGenerator):
|
||||
|
||||
def random_salt_size(self):
|
||||
# workaround for django14 regression --
|
||||
# 1.4 won't accept hashes with empty salt strings, unlike 1.3 and earlier.
|
||||
# looks to be fixed in a future release -- https://code.djangoproject.com/ticket/18144
|
||||
# for now, we avoid salt_size==0 under 1.4
|
||||
handler = self.handler
|
||||
default = handler.default_salt_size
|
||||
assert handler.min_salt_size == 0
|
||||
lower = 1
|
||||
upper = handler.max_salt_size or default*4
|
||||
return self.randintgauss(lower, upper, default, default*.5)
|
||||
|
||||
class django_salted_sha1_test(HandlerCase, _DjangoHelper):
|
||||
"""test django_salted_sha1"""
|
||||
handler = hash.django_salted_sha1
|
||||
max_django_version = (1,9)
|
||||
|
||||
known_correct_hashes = [
|
||||
# test extra large salt
|
||||
("password",'sha1$123abcdef$e4a1877b0e35c47329e7ed7e58014276168a37ba'),
|
||||
|
||||
# test django 1.4 alphanumeric salt
|
||||
("test", 'sha1$bcwHF9Hy8lxS$6b4cfa0651b43161c6f1471ce9523acf1f751ba3'),
|
||||
|
||||
# ensures utf-8 used for unicode
|
||||
(UPASS_USD, 'sha1$c2e86$0f75c5d7fbd100d587c127ef0b693cde611b4ada'),
|
||||
(UPASS_TABLE, 'sha1$6d853$ef13a4d8fb57aed0cb573fe9c82e28dc7fd372d4'),
|
||||
|
||||
# generic password
|
||||
("MyPassword", 'sha1$54123$893cf12e134c3c215f3a76bd50d13f92404a54d3'),
|
||||
]
|
||||
|
||||
known_unidentified_hashes = [
|
||||
'md5$aa$bb',
|
||||
]
|
||||
|
||||
known_malformed_hashes = [
|
||||
# checksum too short
|
||||
'sha1$c2e86$0f75',
|
||||
]
|
||||
|
||||
# reuse custom random_salt_size() helper...
|
||||
FuzzHashGenerator = django_salted_md5_test.FuzzHashGenerator
|
||||
|
||||
class django_pbkdf2_sha256_test(HandlerCase, _DjangoHelper):
|
||||
"""test django_pbkdf2_sha256"""
|
||||
handler = hash.django_pbkdf2_sha256
|
||||
|
||||
known_correct_hashes = [
|
||||
#
|
||||
# custom - generated via django 1.4 hasher
|
||||
#
|
||||
('not a password',
|
||||
'pbkdf2_sha256$10000$kjVJaVz6qsnJ$5yPHw3rwJGECpUf70daLGhOrQ5+AMxIJdz1c3bqK1Rs='),
|
||||
(UPASS_TABLE,
|
||||
'pbkdf2_sha256$10000$bEwAfNrH1TlQ$OgYUblFNUX1B8GfMqaCYUK/iHyO0pa7STTDdaEJBuY0='),
|
||||
]
|
||||
|
||||
class django_pbkdf2_sha1_test(HandlerCase, _DjangoHelper):
|
||||
"""test django_pbkdf2_sha1"""
|
||||
handler = hash.django_pbkdf2_sha1
|
||||
|
||||
known_correct_hashes = [
|
||||
#
|
||||
# custom - generated via django 1.4 hashers
|
||||
#
|
||||
('not a password',
|
||||
'pbkdf2_sha1$10000$wz5B6WkasRoF$atJmJ1o+XfJxKq1+Nu1f1i57Z5I='),
|
||||
(UPASS_TABLE,
|
||||
'pbkdf2_sha1$10000$KZKWwvqb8BfL$rw5pWsxJEU4JrZAQhHTCO+u0f5Y='),
|
||||
]
|
||||
|
||||
@skipUnless(hash.bcrypt.has_backend(), "no bcrypt backends available")
|
||||
class django_bcrypt_test(HandlerCase, _DjangoHelper):
|
||||
"""test django_bcrypt"""
|
||||
handler = hash.django_bcrypt
|
||||
# XXX: not sure when this wasn't in default list anymore. somewhere in [2.0 - 2.2]
|
||||
max_django_version = (2, 0)
|
||||
fuzz_salts_need_bcrypt_repair = True
|
||||
|
||||
known_correct_hashes = [
|
||||
#
|
||||
# just copied and adapted a few test vectors from bcrypt (above),
|
||||
# since django_bcrypt is just a wrapper for the real bcrypt class.
|
||||
#
|
||||
('', 'bcrypt$$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s.'),
|
||||
('abcdefghijklmnopqrstuvwxyz',
|
||||
'bcrypt$$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq'),
|
||||
(UPASS_TABLE,
|
||||
'bcrypt$$2a$05$Z17AXnnlpzddNUvnC6cZNOSwMA/8oNiKnHTHTwLlBijfucQQlHjaG'),
|
||||
]
|
||||
|
||||
# NOTE: the following have been cloned from _bcrypt_test()
|
||||
|
||||
def populate_settings(self, kwds):
|
||||
# speed up test w/ lower rounds
|
||||
kwds.setdefault("rounds", 4)
|
||||
super(django_bcrypt_test, self).populate_settings(kwds)
|
||||
|
||||
class FuzzHashGenerator(HandlerCase.FuzzHashGenerator):
|
||||
|
||||
def random_rounds(self):
|
||||
# decrease default rounds for fuzz testing to speed up volume.
|
||||
return self.randintgauss(5, 8, 6, 1)
|
||||
|
||||
def random_ident(self):
|
||||
# omit multi-ident tests, only $2a$ counts for this class
|
||||
# XXX: enable this to check 2a / 2b?
|
||||
return None
|
||||
|
||||
@skipUnless(hash.bcrypt.has_backend(), "no bcrypt backends available")
|
||||
class django_bcrypt_sha256_test(HandlerCase, _DjangoHelper):
|
||||
"""test django_bcrypt_sha256"""
|
||||
handler = hash.django_bcrypt_sha256
|
||||
forbidden_characters = None
|
||||
fuzz_salts_need_bcrypt_repair = True
|
||||
|
||||
known_correct_hashes = [
|
||||
#
|
||||
# custom - generated via django 1.6 hasher
|
||||
#
|
||||
('',
|
||||
'bcrypt_sha256$$2a$06$/3OeRpbOf8/l6nPPRdZPp.nRiyYqPobEZGdNRBWihQhiFDh1ws1tu'),
|
||||
(UPASS_LETMEIN,
|
||||
'bcrypt_sha256$$2a$08$NDjSAIcas.EcoxCRiArvT.MkNiPYVhrsrnJsRkLueZOoV1bsQqlmC'),
|
||||
(UPASS_TABLE,
|
||||
'bcrypt_sha256$$2a$06$kCXUnRFQptGg491siDKNTu8RxjBGSjALHRuvhPYNFsa4Ea5d9M48u'),
|
||||
|
||||
# test >72 chars is hashed correctly -- under bcrypt these hash the same.
|
||||
(repeat_string("abc123",72),
|
||||
'bcrypt_sha256$$2a$06$Tg/oYyZTyAf.Nb3qSgN61OySmyXA8FoY4PjGizjE1QSDfuL5MXNni'),
|
||||
(repeat_string("abc123",72)+"qwr",
|
||||
'bcrypt_sha256$$2a$06$Tg/oYyZTyAf.Nb3qSgN61Ocy0BEz1RK6xslSNi8PlaLX2pe7x/KQG'),
|
||||
(repeat_string("abc123",72)+"xyz",
|
||||
'bcrypt_sha256$$2a$06$Tg/oYyZTyAf.Nb3qSgN61OvY2zoRVUa2Pugv2ExVOUT2YmhvxUFUa'),
|
||||
]
|
||||
|
||||
known_malformed_hashers = [
|
||||
# data in django salt field
|
||||
'bcrypt_sha256$xyz$2a$06$/3OeRpbOf8/l6nPPRdZPp.nRiyYqPobEZGdNRBWihQhiFDh1ws1tu',
|
||||
]
|
||||
|
||||
# NOTE: the following have been cloned from _bcrypt_test()
|
||||
|
||||
def populate_settings(self, kwds):
|
||||
# speed up test w/ lower rounds
|
||||
kwds.setdefault("rounds", 4)
|
||||
super(django_bcrypt_sha256_test, self).populate_settings(kwds)
|
||||
|
||||
class FuzzHashGenerator(HandlerCase.FuzzHashGenerator):
|
||||
|
||||
def random_rounds(self):
|
||||
# decrease default rounds for fuzz testing to speed up volume.
|
||||
return self.randintgauss(5, 8, 6, 1)
|
||||
|
||||
def random_ident(self):
|
||||
# omit multi-ident tests, only $2a$ counts for this class
|
||||
# XXX: enable this to check 2a / 2b?
|
||||
return None
|
||||
|
||||
from passlib.tests.test_handlers_argon2 import _base_argon2_test
|
||||
|
||||
@skipUnless(hash.argon2.has_backend(), "no argon2 backends available")
|
||||
class django_argon2_test(HandlerCase, _DjangoHelper):
|
||||
"""test django_bcrypt"""
|
||||
handler = hash.django_argon2
|
||||
|
||||
# NOTE: most of this adapted from _base_argon2_test & argon2pure test
|
||||
|
||||
known_correct_hashes = [
|
||||
# sample test
|
||||
("password", 'argon2$argon2i$v=19$m=256,t=1,p=1$c29tZXNhbHQ$AJFIsNZTMKTAewB4+ETN1A'),
|
||||
|
||||
# sample w/ all parameters different
|
||||
("password", 'argon2$argon2i$v=19$m=380,t=2,p=2$c29tZXNhbHQ$SrssP8n7m/12VWPM8dvNrw'),
|
||||
|
||||
# generated from django 1.10.3
|
||||
(UPASS_LETMEIN, 'argon2$argon2i$v=19$m=512,t=2,p=2$V25jN1l4UUJZWkR1$MxpA1BD2Gh7+D79gaAw6sQ'),
|
||||
]
|
||||
|
||||
def setUpWarnings(self):
|
||||
super(django_argon2_test, self).setUpWarnings()
|
||||
warnings.filterwarnings("ignore", ".*Using argon2pure backend.*")
|
||||
|
||||
def do_stub_encrypt(self, handler=None, **settings):
|
||||
# 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.wrapped(use_defaults=True)
|
||||
self.checksum = self._stub_checksum
|
||||
assert self.checksum
|
||||
return handler._wrap_hash(self.to_string())
|
||||
|
||||
def test_03_legacy_hash_workflow(self):
|
||||
# override base method
|
||||
raise self.skipTest("legacy 1.6 workflow not supported")
|
||||
|
||||
class FuzzHashGenerator(_base_argon2_test.FuzzHashGenerator):
|
||||
|
||||
def random_type(self):
|
||||
# override default since django only uses type I (see note in class)
|
||||
return "I"
|
||||
|
||||
def random_rounds(self):
|
||||
# decrease default rounds for fuzz testing to speed up volume.
|
||||
return self.randintgauss(1, 3, 2, 1)
|
||||
|
||||
#=============================================================================
|
||||
# eof
|
||||
#=============================================================================
|
||||
480
venv/Lib/site-packages/passlib/tests/test_handlers_pbkdf2.py
Normal file
480
venv/Lib/site-packages/passlib/tests/test_handlers_pbkdf2.py
Normal file
@@ -0,0 +1,480 @@
|
||||
"""passlib.tests.test_handlers - tests for passlib hash algorithms"""
|
||||
#=============================================================================
|
||||
# imports
|
||||
#=============================================================================
|
||||
# core
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
import warnings
|
||||
# site
|
||||
# pkg
|
||||
from passlib import hash
|
||||
from passlib.utils.compat import u
|
||||
from passlib.tests.utils import TestCase, HandlerCase
|
||||
from passlib.tests.test_handlers import UPASS_WAV
|
||||
# module
|
||||
|
||||
#=============================================================================
|
||||
# ldap_pbkdf2_{digest}
|
||||
#=============================================================================
|
||||
# NOTE: since these are all wrappers for the pbkdf2_{digest} hasehs,
|
||||
# they don't extensive separate testing.
|
||||
|
||||
class ldap_pbkdf2_test(TestCase):
|
||||
|
||||
def test_wrappers(self):
|
||||
"""test ldap pbkdf2 wrappers"""
|
||||
|
||||
self.assertTrue(
|
||||
hash.ldap_pbkdf2_sha1.verify(
|
||||
"password",
|
||||
'{PBKDF2}1212$OB.dtnSEXZK8U5cgxU/GYQ$y5LKPOplRmok7CZp/aqVDVg8zGI',
|
||||
)
|
||||
)
|
||||
|
||||
self.assertTrue(
|
||||
hash.ldap_pbkdf2_sha256.verify(
|
||||
"password",
|
||||
'{PBKDF2-SHA256}1212$4vjV83LKPjQzk31VI4E0Vw$hsYF68OiOUPdDZ1Fg'
|
||||
'.fJPeq1h/gXXY7acBp9/6c.tmQ'
|
||||
)
|
||||
)
|
||||
|
||||
self.assertTrue(
|
||||
hash.ldap_pbkdf2_sha512.verify(
|
||||
"password",
|
||||
'{PBKDF2-SHA512}1212$RHY0Fr3IDMSVO/RSZyb5ow$eNLfBK.eVozomMr.1gYa1'
|
||||
'7k9B7KIK25NOEshvhrSX.esqY3s.FvWZViXz4KoLlQI.BzY/YTNJOiKc5gBYFYGww'
|
||||
)
|
||||
)
|
||||
|
||||
#=============================================================================
|
||||
# pbkdf2 hashes
|
||||
#=============================================================================
|
||||
class atlassian_pbkdf2_sha1_test(HandlerCase):
|
||||
handler = hash.atlassian_pbkdf2_sha1
|
||||
|
||||
known_correct_hashes = [
|
||||
#
|
||||
# generated using Jira
|
||||
#
|
||||
("admin", '{PKCS5S2}c4xaeTQM0lUieMS3V5voiexyX9XhqC2dBd5ecVy60IPksHChwoTAVYFrhsgoq8/p'),
|
||||
(UPASS_WAV,
|
||||
"{PKCS5S2}cE9Yq6Am5tQGdHSHhky2XLeOnURwzaLBG2sur7FHKpvy2u0qDn6GcVGRjlmJoIUy"),
|
||||
]
|
||||
|
||||
known_malformed_hashes = [
|
||||
# bad char ---\/
|
||||
'{PKCS5S2}c4xaeTQM0lUieMS3V5voiexyX9XhqC2dBd5ecVy!0IPksHChwoTAVYFrhsgoq8/p'
|
||||
|
||||
# bad size, missing padding
|
||||
'{PKCS5S2}c4xaeTQM0lUieMS3V5voiexyX9XhqC2dBd5ecVy60IPksHChwoTAVYFrhsgoq8/'
|
||||
|
||||
# bad size, with correct padding
|
||||
'{PKCS5S2}c4xaeTQM0lUieMS3V5voiexyX9XhqC2dBd5ecVy60IPksHChwoTAVYFrhsgoq8/='
|
||||
]
|
||||
|
||||
class pbkdf2_sha1_test(HandlerCase):
|
||||
handler = hash.pbkdf2_sha1
|
||||
known_correct_hashes = [
|
||||
("password", '$pbkdf2$1212$OB.dtnSEXZK8U5cgxU/GYQ$y5LKPOplRmok7CZp/aqVDVg8zGI'),
|
||||
(UPASS_WAV,
|
||||
'$pbkdf2$1212$THDqatpidANpadlLeTeOEg$HV3oi1k5C5LQCgG1BMOL.BX4YZc'),
|
||||
]
|
||||
|
||||
known_malformed_hashes = [
|
||||
# zero padded rounds field
|
||||
'$pbkdf2$01212$THDqatpidANpadlLeTeOEg$HV3oi1k5C5LQCgG1BMOL.BX4YZc',
|
||||
|
||||
# empty rounds field
|
||||
'$pbkdf2$$THDqatpidANpadlLeTeOEg$HV3oi1k5C5LQCgG1BMOL.BX4YZc',
|
||||
|
||||
# too many field
|
||||
'$pbkdf2$1212$THDqatpidANpadlLeTeOEg$HV3oi1k5C5LQCgG1BMOL.BX4YZc$',
|
||||
]
|
||||
|
||||
class pbkdf2_sha256_test(HandlerCase):
|
||||
handler = hash.pbkdf2_sha256
|
||||
known_correct_hashes = [
|
||||
("password",
|
||||
'$pbkdf2-sha256$1212$4vjV83LKPjQzk31VI4E0Vw$hsYF68OiOUPdDZ1Fg.fJPeq1h/gXXY7acBp9/6c.tmQ'
|
||||
),
|
||||
(UPASS_WAV,
|
||||
'$pbkdf2-sha256$1212$3SABFJGDtyhrQMVt1uABPw$WyaUoqCLgvz97s523nF4iuOqZNbp5Nt8do/cuaa7AiI'
|
||||
),
|
||||
]
|
||||
|
||||
class pbkdf2_sha512_test(HandlerCase):
|
||||
handler = hash.pbkdf2_sha512
|
||||
known_correct_hashes = [
|
||||
("password",
|
||||
'$pbkdf2-sha512$1212$RHY0Fr3IDMSVO/RSZyb5ow$eNLfBK.eVozomMr.1gYa1'
|
||||
'7k9B7KIK25NOEshvhrSX.esqY3s.FvWZViXz4KoLlQI.BzY/YTNJOiKc5gBYFYGww'
|
||||
),
|
||||
(UPASS_WAV,
|
||||
'$pbkdf2-sha512$1212$KkbvoKGsAIcF8IslDR6skQ$8be/PRmd88Ps8fmPowCJt'
|
||||
'tH9G3vgxpG.Krjt3KT.NP6cKJ0V4Prarqf.HBwz0dCkJ6xgWnSj2ynXSV7MlvMa8Q'
|
||||
),
|
||||
]
|
||||
|
||||
class cta_pbkdf2_sha1_test(HandlerCase):
|
||||
handler = hash.cta_pbkdf2_sha1
|
||||
known_correct_hashes = [
|
||||
#
|
||||
# test vectors from original implementation
|
||||
#
|
||||
(u("hashy the \N{SNOWMAN}"), '$p5k2$1000$ZxK4ZBJCfQg=$jJZVscWtO--p1-xIZl6jhO2LKR0='),
|
||||
|
||||
#
|
||||
# custom
|
||||
#
|
||||
("password", "$p5k2$1$$h1TDLGSw9ST8UMAPeIE13i0t12c="),
|
||||
(UPASS_WAV,
|
||||
"$p5k2$4321$OTg3NjU0MzIx$jINJrSvZ3LXeIbUdrJkRpN62_WQ="),
|
||||
]
|
||||
|
||||
class dlitz_pbkdf2_sha1_test(HandlerCase):
|
||||
handler = hash.dlitz_pbkdf2_sha1
|
||||
known_correct_hashes = [
|
||||
#
|
||||
# test vectors from original implementation
|
||||
#
|
||||
('cloadm', '$p5k2$$exec$r1EWMCMk7Rlv3L/RNcFXviDefYa0hlql'),
|
||||
('gnu', '$p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g'),
|
||||
('dcl', '$p5k2$d$tUsch7fU$nqDkaxMDOFBeJsTSfABsyn.PYUXilHwL'),
|
||||
('spam', '$p5k2$3e8$H0NX9mT/$wk/sE8vv6OMKuMaqazCJYDSUhWY9YB2J'),
|
||||
(UPASS_WAV,
|
||||
'$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ'),
|
||||
]
|
||||
|
||||
class grub_pbkdf2_sha512_test(HandlerCase):
|
||||
handler = hash.grub_pbkdf2_sha512
|
||||
known_correct_hashes = [
|
||||
#
|
||||
# test vectors generated from cmd line tool
|
||||
#
|
||||
|
||||
# salt=32 bytes
|
||||
(UPASS_WAV,
|
||||
'grub.pbkdf2.sha512.10000.BCAC1CEC5E4341C8C511C529'
|
||||
'7FA877BE91C2817B32A35A3ECF5CA6B8B257F751.6968526A'
|
||||
'2A5B1AEEE0A29A9E057336B48D388FFB3F600233237223C21'
|
||||
'04DE1752CEC35B0DD1ED49563398A282C0F471099C2803FBA'
|
||||
'47C7919CABC43192C68F60'),
|
||||
|
||||
# salt=64 bytes
|
||||
('toomanysecrets',
|
||||
'grub.pbkdf2.sha512.10000.9B436BB6978682363D5C449B'
|
||||
'BEAB322676946C632208BC1294D51F47174A9A3B04A7E4785'
|
||||
'986CD4EA7470FAB8FE9F6BD522D1FC6C51109A8596FB7AD48'
|
||||
'7C4493.0FE5EF169AFFCB67D86E2581B1E251D88C777B98BA'
|
||||
'2D3256ECC9F765D84956FC5CA5C4B6FD711AA285F0A04DCF4'
|
||||
'634083F9A20F4B6F339A52FBD6BED618E527B'),
|
||||
|
||||
]
|
||||
|
||||
#=============================================================================
|
||||
# scram hash
|
||||
#=============================================================================
|
||||
class scram_test(HandlerCase):
|
||||
handler = hash.scram
|
||||
|
||||
# TODO: need a bunch more reference vectors from some real
|
||||
# SCRAM transactions.
|
||||
known_correct_hashes = [
|
||||
#
|
||||
# taken from example in SCRAM specification (rfc 5802)
|
||||
#
|
||||
('pencil', '$scram$4096$QSXCR.Q6sek8bf92$'
|
||||
'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30'),
|
||||
|
||||
#
|
||||
# custom
|
||||
#
|
||||
|
||||
# same as 5802 example hash, but with sha-256 & sha-512 added.
|
||||
('pencil', '$scram$4096$QSXCR.Q6sek8bf92$'
|
||||
'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30,'
|
||||
'sha-256=qXUXrlcvnaxxWG00DdRgVioR2gnUpuX5r.3EZ1rdhVY,'
|
||||
'sha-512=lzgniLFcvglRLS0gt.C4gy.NurS3OIOVRAU1zZOV4P.qFiVFO2/'
|
||||
'edGQSu/kD1LwdX0SNV/KsPdHSwEl5qRTuZQ'),
|
||||
|
||||
# test unicode passwords & saslprep (all the passwords below
|
||||
# should normalize to the same value: 'IX \xE0')
|
||||
(u('IX \xE0'), '$scram$6400$0BojBCBE6P2/N4bQ$'
|
||||
'sha-1=YniLes.b8WFMvBhtSACZyyvxeCc'),
|
||||
(u('\u2168\u3000a\u0300'), '$scram$6400$0BojBCBE6P2/N4bQ$'
|
||||
'sha-1=YniLes.b8WFMvBhtSACZyyvxeCc'),
|
||||
(u('\u00ADIX \xE0'), '$scram$6400$0BojBCBE6P2/N4bQ$'
|
||||
'sha-1=YniLes.b8WFMvBhtSACZyyvxeCc'),
|
||||
]
|
||||
|
||||
known_malformed_hashes = [
|
||||
# zero-padding in rounds
|
||||
'$scram$04096$QSXCR.Q6sek8bf92$sha-1=HZbuOlKbWl.eR8AfIposuKbhX30',
|
||||
|
||||
# non-digit in rounds
|
||||
'$scram$409A$QSXCR.Q6sek8bf92$sha-1=HZbuOlKbWl.eR8AfIposuKbhX30',
|
||||
|
||||
# bad char in salt ---\/
|
||||
'$scram$4096$QSXCR.Q6sek8bf9-$sha-1=HZbuOlKbWl.eR8AfIposuKbhX30',
|
||||
|
||||
# bad char in digest ---\/
|
||||
'$scram$4096$QSXCR.Q6sek8bf92$sha-1=HZbuOlKbWl.eR8AfIposuKbhX3-',
|
||||
|
||||
# missing sections
|
||||
'$scram$4096$QSXCR.Q6sek8bf92',
|
||||
'$scram$4096$QSXCR.Q6sek8bf92$',
|
||||
|
||||
# too many sections
|
||||
'$scram$4096$QSXCR.Q6sek8bf92$sha-1=HZbuOlKbWl.eR8AfIposuKbhX30$',
|
||||
|
||||
# missing separator
|
||||
'$scram$4096$QSXCR.Q6sek8bf92$sha-1=HZbuOlKbWl.eR8AfIposuKbhX30'
|
||||
'sha-256=qXUXrlcvnaxxWG00DdRgVioR2gnUpuX5r.3EZ1rdhVY',
|
||||
|
||||
# too many chars in alg name
|
||||
'$scram$4096$QSXCR.Q6sek8bf92$sha-1=HZbuOlKbWl.eR8AfIposuKbhX30,'
|
||||
'shaxxx-190=HZbuOlKbWl.eR8AfIposuKbhX30',
|
||||
|
||||
# missing sha-1 alg
|
||||
'$scram$4096$QSXCR.Q6sek8bf92$sha-256=HZbuOlKbWl.eR8AfIposuKbhX30',
|
||||
|
||||
# non-iana name
|
||||
'$scram$4096$QSXCR.Q6sek8bf92$sha1=HZbuOlKbWl.eR8AfIposuKbhX30',
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(scram_test, self).setUp()
|
||||
|
||||
# some platforms lack stringprep (e.g. Jython, IronPython)
|
||||
self.require_stringprep()
|
||||
|
||||
# silence norm_hash_name() warning
|
||||
warnings.filterwarnings("ignore", r"norm_hash_name\(\): unknown hash")
|
||||
|
||||
def test_90_algs(self):
|
||||
"""test parsing of 'algs' setting"""
|
||||
defaults = dict(salt=b'A'*10, rounds=1000)
|
||||
def parse(algs, **kwds):
|
||||
for k in defaults:
|
||||
kwds.setdefault(k, defaults[k])
|
||||
return self.handler(algs=algs, **kwds).algs
|
||||
|
||||
# None -> default list
|
||||
self.assertEqual(parse(None, use_defaults=True), hash.scram.default_algs)
|
||||
self.assertRaises(TypeError, parse, None)
|
||||
|
||||
# strings should be parsed
|
||||
self.assertEqual(parse("sha1"), ["sha-1"])
|
||||
self.assertEqual(parse("sha1, sha256, md5"), ["md5","sha-1","sha-256"])
|
||||
|
||||
# lists should be normalized
|
||||
self.assertEqual(parse(["sha-1","sha256"]), ["sha-1","sha-256"])
|
||||
|
||||
# sha-1 required
|
||||
self.assertRaises(ValueError, parse, ["sha-256"])
|
||||
self.assertRaises(ValueError, parse, algs=[], use_defaults=True)
|
||||
|
||||
# alg names must be < 10 chars
|
||||
self.assertRaises(ValueError, parse, ["sha-1","shaxxx-190"])
|
||||
|
||||
# alg & checksum mutually exclusive.
|
||||
self.assertRaises(RuntimeError, parse, ['sha-1'],
|
||||
checksum={"sha-1": b"\x00"*20})
|
||||
|
||||
def test_90_checksums(self):
|
||||
"""test internal parsing of 'checksum' keyword"""
|
||||
# check non-bytes checksum values are rejected
|
||||
self.assertRaises(TypeError, self.handler, use_defaults=True,
|
||||
checksum={'sha-1': u('X')*20})
|
||||
|
||||
# check sha-1 is required
|
||||
self.assertRaises(ValueError, self.handler, use_defaults=True,
|
||||
checksum={'sha-256': b'X'*32})
|
||||
|
||||
# XXX: anything else that's not tested by the other code already?
|
||||
|
||||
def test_91_extract_digest_info(self):
|
||||
"""test scram.extract_digest_info()"""
|
||||
edi = self.handler.extract_digest_info
|
||||
|
||||
# return appropriate value or throw KeyError
|
||||
h = "$scram$10$AAAAAA$sha-1=AQ,bbb=Ag,ccc=Aw"
|
||||
s = b'\x00'*4
|
||||
self.assertEqual(edi(h,"SHA1"), (s,10, b'\x01'))
|
||||
self.assertEqual(edi(h,"bbb"), (s,10, b'\x02'))
|
||||
self.assertEqual(edi(h,"ccc"), (s,10, b'\x03'))
|
||||
self.assertRaises(KeyError, edi, h, "ddd")
|
||||
|
||||
# config strings should cause value error.
|
||||
c = "$scram$10$....$sha-1,bbb,ccc"
|
||||
self.assertRaises(ValueError, edi, c, "sha-1")
|
||||
self.assertRaises(ValueError, edi, c, "bbb")
|
||||
self.assertRaises(ValueError, edi, c, "ddd")
|
||||
|
||||
def test_92_extract_digest_algs(self):
|
||||
"""test scram.extract_digest_algs()"""
|
||||
eda = self.handler.extract_digest_algs
|
||||
|
||||
self.assertEqual(eda('$scram$4096$QSXCR.Q6sek8bf92$'
|
||||
'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30'), ["sha-1"])
|
||||
|
||||
self.assertEqual(eda('$scram$4096$QSXCR.Q6sek8bf92$'
|
||||
'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30', format="hashlib"),
|
||||
["sha1"])
|
||||
|
||||
self.assertEqual(eda('$scram$4096$QSXCR.Q6sek8bf92$'
|
||||
'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30,'
|
||||
'sha-256=qXUXrlcvnaxxWG00DdRgVioR2gnUpuX5r.3EZ1rdhVY,'
|
||||
'sha-512=lzgniLFcvglRLS0gt.C4gy.NurS3OIOVRAU1zZOV4P.qFiVFO2/'
|
||||
'edGQSu/kD1LwdX0SNV/KsPdHSwEl5qRTuZQ'),
|
||||
["sha-1","sha-256","sha-512"])
|
||||
|
||||
def test_93_derive_digest(self):
|
||||
"""test scram.derive_digest()"""
|
||||
# NOTE: this just does a light test, since derive_digest
|
||||
# is used by hash / verify, and is tested pretty well via those.
|
||||
hash = self.handler.derive_digest
|
||||
|
||||
# check various encodings of password work.
|
||||
s1 = b'\x01\x02\x03'
|
||||
d1 = b'\xb2\xfb\xab\x82[tNuPnI\x8aZZ\x19\x87\xcen\xe9\xd3'
|
||||
self.assertEqual(hash(u("\u2168"), s1, 1000, 'sha-1'), d1)
|
||||
self.assertEqual(hash(b"\xe2\x85\xa8", s1, 1000, 'SHA-1'), d1)
|
||||
self.assertEqual(hash(u("IX"), s1, 1000, 'sha1'), d1)
|
||||
self.assertEqual(hash(b"IX", s1, 1000, 'SHA1'), d1)
|
||||
|
||||
# check algs
|
||||
self.assertEqual(hash("IX", s1, 1000, 'md5'),
|
||||
b'3\x19\x18\xc0\x1c/\xa8\xbf\xe4\xa3\xc2\x8eM\xe8od')
|
||||
self.assertRaises(ValueError, hash, "IX", s1, 1000, 'sha-666')
|
||||
|
||||
# check rounds
|
||||
self.assertRaises(ValueError, hash, "IX", s1, 0, 'sha-1')
|
||||
|
||||
# unicode salts accepted as of passlib 1.7 (previous caused TypeError)
|
||||
self.assertEqual(hash(u("IX"), s1.decode("latin-1"), 1000, 'sha1'), d1)
|
||||
|
||||
def test_94_saslprep(self):
|
||||
"""test hash/verify use saslprep"""
|
||||
# NOTE: this just does a light test that saslprep() is being
|
||||
# called in various places, relying in saslpreps()'s tests
|
||||
# to verify full normalization behavior.
|
||||
|
||||
# hash unnormalized
|
||||
h = self.do_encrypt(u("I\u00ADX"))
|
||||
self.assertTrue(self.do_verify(u("IX"), h))
|
||||
self.assertTrue(self.do_verify(u("\u2168"), h))
|
||||
|
||||
# hash normalized
|
||||
h = self.do_encrypt(u("\xF3"))
|
||||
self.assertTrue(self.do_verify(u("o\u0301"), h))
|
||||
self.assertTrue(self.do_verify(u("\u200Do\u0301"), h))
|
||||
|
||||
# throws error if forbidden char provided
|
||||
self.assertRaises(ValueError, self.do_encrypt, u("\uFDD0"))
|
||||
self.assertRaises(ValueError, self.do_verify, u("\uFDD0"), h)
|
||||
|
||||
def test_94_using_w_default_algs(self, param="default_algs"):
|
||||
"""using() -- 'default_algs' parameter"""
|
||||
# create subclass
|
||||
handler = self.handler
|
||||
orig = list(handler.default_algs) # in case it's modified in place
|
||||
subcls = handler.using(**{param: "sha1,md5"})
|
||||
|
||||
# shouldn't have changed handler
|
||||
self.assertEqual(handler.default_algs, orig)
|
||||
|
||||
# should have own set
|
||||
self.assertEqual(subcls.default_algs, ["md5", "sha-1"])
|
||||
|
||||
# test hash output
|
||||
h1 = subcls.hash("dummy")
|
||||
self.assertEqual(handler.extract_digest_algs(h1), ["md5", "sha-1"])
|
||||
|
||||
def test_94_using_w_algs(self):
|
||||
"""using() -- 'algs' parameter"""
|
||||
self.test_94_using_w_default_algs(param="algs")
|
||||
|
||||
def test_94_needs_update_algs(self):
|
||||
"""needs_update() -- algs setting"""
|
||||
handler1 = self.handler.using(algs="sha1,md5")
|
||||
|
||||
# shouldn't need update, has same algs
|
||||
h1 = handler1.hash("dummy")
|
||||
self.assertFalse(handler1.needs_update(h1))
|
||||
|
||||
# *currently* shouldn't need update, has superset of algs required by handler2
|
||||
# (may change this policy)
|
||||
handler2 = handler1.using(algs="sha1")
|
||||
self.assertFalse(handler2.needs_update(h1))
|
||||
|
||||
# should need update, doesn't have all algs required by handler3
|
||||
handler3 = handler1.using(algs="sha1,sha256")
|
||||
self.assertTrue(handler3.needs_update(h1))
|
||||
|
||||
def test_95_context_algs(self):
|
||||
"""test handling of 'algs' in context object"""
|
||||
handler = self.handler
|
||||
from passlib.context import CryptContext
|
||||
c1 = CryptContext(["scram"], scram__algs="sha1,md5")
|
||||
|
||||
h = c1.hash("dummy")
|
||||
self.assertEqual(handler.extract_digest_algs(h), ["md5", "sha-1"])
|
||||
self.assertFalse(c1.needs_update(h))
|
||||
|
||||
c2 = c1.copy(scram__algs="sha1")
|
||||
self.assertFalse(c2.needs_update(h))
|
||||
|
||||
c2 = c1.copy(scram__algs="sha1,sha256")
|
||||
self.assertTrue(c2.needs_update(h))
|
||||
|
||||
def test_96_full_verify(self):
|
||||
"""test verify(full=True) flag"""
|
||||
def vpart(s, h):
|
||||
return self.handler.verify(s, h)
|
||||
def vfull(s, h):
|
||||
return self.handler.verify(s, h, full=True)
|
||||
|
||||
# reference
|
||||
h = ('$scram$4096$QSXCR.Q6sek8bf92$'
|
||||
'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30,'
|
||||
'sha-256=qXUXrlcvnaxxWG00DdRgVioR2gnUpuX5r.3EZ1rdhVY,'
|
||||
'sha-512=lzgniLFcvglRLS0gt.C4gy.NurS3OIOVRAU1zZOV4P.qFiVFO2/'
|
||||
'edGQSu/kD1LwdX0SNV/KsPdHSwEl5qRTuZQ')
|
||||
self.assertTrue(vfull('pencil', h))
|
||||
self.assertFalse(vfull('tape', h))
|
||||
|
||||
# catch truncated digests.
|
||||
h = ('$scram$4096$QSXCR.Q6sek8bf92$'
|
||||
'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30,'
|
||||
'sha-256=qXUXrlcvnaxxWG00DdRgVioR2gnUpuX5r.3EZ1rdhV,' # -1 char
|
||||
'sha-512=lzgniLFcvglRLS0gt.C4gy.NurS3OIOVRAU1zZOV4P.qFiVFO2/'
|
||||
'edGQSu/kD1LwdX0SNV/KsPdHSwEl5qRTuZQ')
|
||||
self.assertRaises(ValueError, vfull, 'pencil', h)
|
||||
|
||||
# catch padded digests.
|
||||
h = ('$scram$4096$QSXCR.Q6sek8bf92$'
|
||||
'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30,'
|
||||
'sha-256=qXUXrlcvnaxxWG00DdRgVioR2gnUpuX5r.3EZ1rdhVYa,' # +1 char
|
||||
'sha-512=lzgniLFcvglRLS0gt.C4gy.NurS3OIOVRAU1zZOV4P.qFiVFO2/'
|
||||
'edGQSu/kD1LwdX0SNV/KsPdHSwEl5qRTuZQ')
|
||||
self.assertRaises(ValueError, vfull, 'pencil', h)
|
||||
|
||||
# catch hash containing digests belonging to diff passwords.
|
||||
# proper behavior for quick-verify (the default) is undefined,
|
||||
# but full-verify should throw error.
|
||||
h = ('$scram$4096$QSXCR.Q6sek8bf92$'
|
||||
'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30,' # 'pencil'
|
||||
'sha-256=R7RJDWIbeKRTFwhE9oxh04kab0CllrQ3kCcpZUcligc,' # 'tape'
|
||||
'sha-512=lzgniLFcvglRLS0gt.C4gy.NurS3OIOVRAU1zZOV4P.qFiVFO2/' # 'pencil'
|
||||
'edGQSu/kD1LwdX0SNV/KsPdHSwEl5qRTuZQ')
|
||||
self.assertTrue(vpart('tape', h))
|
||||
self.assertFalse(vpart('pencil', h))
|
||||
self.assertRaises(ValueError, vfull, 'pencil', h)
|
||||
self.assertRaises(ValueError, vfull, 'tape', h)
|
||||
|
||||
#=============================================================================
|
||||
# eof
|
||||
#=============================================================================
|
||||
111
venv/Lib/site-packages/passlib/tests/test_handlers_scrypt.py
Normal file
111
venv/Lib/site-packages/passlib/tests/test_handlers_scrypt.py
Normal file
@@ -0,0 +1,111 @@
|
||||
"""passlib.tests.test_handlers - tests for passlib hash algorithms"""
|
||||
#=============================================================================
|
||||
# imports
|
||||
#=============================================================================
|
||||
# core
|
||||
import logging; log = logging.getLogger(__name__)
|
||||
import warnings
|
||||
warnings.filterwarnings("ignore", ".*using builtin scrypt backend.*")
|
||||
# site
|
||||
# pkg
|
||||
from passlib import hash
|
||||
from passlib.tests.utils import HandlerCase, TEST_MODE
|
||||
from passlib.tests.test_handlers import UPASS_TABLE, PASS_TABLE_UTF8
|
||||
# module
|
||||
|
||||
#=============================================================================
|
||||
# scrypt hash
|
||||
#=============================================================================
|
||||
class _scrypt_test(HandlerCase):
|
||||
handler = hash.scrypt
|
||||
|
||||
known_correct_hashes = [
|
||||
#
|
||||
# excepted from test vectors from scrypt whitepaper
|
||||
# (http://www.tarsnap.com/scrypt/scrypt.pdf, appendix b),
|
||||
# and encoded using passlib's custom format
|
||||
#
|
||||
|
||||
# salt=b""
|
||||
("", "$scrypt$ln=4,r=1,p=1$$d9ZXYjhleyA7GcpCwYoEl/FrSETjB0ro39/6P+3iFEI"),
|
||||
|
||||
# salt=b"NaCl"
|
||||
("password", "$scrypt$ln=10,r=8,p=16$TmFDbA$/bq+HJ00cgB4VucZDQHp/nxq18vII3gw53N2Y0s3MWI"),
|
||||
|
||||
#
|
||||
# custom
|
||||
#
|
||||
|
||||
# simple test
|
||||
("test", '$scrypt$ln=8,r=8,p=1$wlhLyXmP8b53bm1NKYVQqg$mTpvG8lzuuDk+DWz8HZIB6Vum6erDuUm0As5yU+VxWA'),
|
||||
|
||||
# different block value
|
||||
("password", '$scrypt$ln=8,r=2,p=1$dO6d0xoDoLT2PofQGoNQag$g/Wf2A0vhHhaJM+addK61QPBthSmYB6uVTtQzh8CM3o'),
|
||||
|
||||
# different rounds
|
||||
(UPASS_TABLE, '$scrypt$ln=7,r=8,p=1$jjGmtDamdA4BQAjBeA9BSA$OiWRHhQtpDx7M/793x6UXK14AD512jg/qNm/hkWZG4M'),
|
||||
|
||||
# alt encoding
|
||||
(PASS_TABLE_UTF8, '$scrypt$ln=7,r=8,p=1$jjGmtDamdA4BQAjBeA9BSA$OiWRHhQtpDx7M/793x6UXK14AD512jg/qNm/hkWZG4M'),
|
||||
|
||||
# diff block & parallel counts as well
|
||||
("nacl", '$scrypt$ln=1,r=4,p=2$yhnD+J+Tci4lZCwFgHCuVQ$fAsEWmxSHuC0cHKMwKVFPzrQukgvK09Sj+NueTSxKds')
|
||||
]
|
||||
|
||||
if TEST_MODE("full"):
|
||||
# add some hashes with larger rounds value.
|
||||
known_correct_hashes.extend([
|
||||
#
|
||||
# from scrypt whitepaper
|
||||
#
|
||||
|
||||
# salt=b"SodiumChloride"
|
||||
("pleaseletmein", "$scrypt$ln=14,r=8,p=1$U29kaXVtQ2hsb3JpZGU"
|
||||
"$cCO9yzr9c0hGHAbNgf046/2o+7qQT44+qbVD9lRdofI"),
|
||||
|
||||
#
|
||||
# openwall format (https://gitlab.com/jas/scrypt-unix-crypt/blob/master/unix-scrypt.txt)
|
||||
#
|
||||
("pleaseletmein",
|
||||
"$7$C6..../....SodiumChloride$kBGj9fHznVYFQMEn/qDCfrDevf9YDtcDdKvEqHJLV8D"),
|
||||
|
||||
])
|
||||
|
||||
known_malformed_hashes = [
|
||||
# missing 'p' value
|
||||
'$scrypt$ln=10,r=1$wvif8/4fg1Cq9V7L2dv73w$bJcLia1lyfQ1X2x0xflehwVXPzWIUQWWdnlGwfVzBeQ',
|
||||
|
||||
# rounds too low
|
||||
'$scrypt$ln=0,r=1,p=1$wvif8/4fg1Cq9V7L2dv73w$bJcLia1lyfQ1X2x0xflehwVXPzWIUQWWdnlGwfVzBeQ',
|
||||
|
||||
# invalid block size
|
||||
'$scrypt$ln=10,r=A,p=1$wvif8/4fg1Cq9V7L2dv73w$bJcLia1lyfQ1X2x0xflehwVXPzWIUQWWdnlGwfVzBeQ',
|
||||
|
||||
# r*p too large
|
||||
'$scrypt$ln=10,r=134217728,p=8$wvif8/4fg1Cq9V7L2dv73w$bJcLia1lyfQ1X2x0xflehwVXPzWIUQWWdnlGwfVzBeQ',
|
||||
]
|
||||
|
||||
def setUpWarnings(self):
|
||||
super(_scrypt_test, self).setUpWarnings()
|
||||
warnings.filterwarnings("ignore", ".*using builtin scrypt backend.*")
|
||||
|
||||
def populate_settings(self, kwds):
|
||||
# builtin is still just way too slow.
|
||||
if self.backend == "builtin":
|
||||
kwds.setdefault("rounds", 6)
|
||||
super(_scrypt_test, self).populate_settings(kwds)
|
||||
|
||||
class FuzzHashGenerator(HandlerCase.FuzzHashGenerator):
|
||||
|
||||
def random_rounds(self):
|
||||
# decrease default rounds for fuzz testing to speed up volume.
|
||||
return self.randintgauss(4, 10, 6, 1)
|
||||
|
||||
# create test cases for specific backends
|
||||
scrypt_stdlib_test = _scrypt_test.create_backend_case("stdlib")
|
||||
scrypt_scrypt_test = _scrypt_test.create_backend_case("scrypt")
|
||||
scrypt_builtin_test = _scrypt_test.create_backend_case("builtin")
|
||||
|
||||
#=============================================================================
|
||||
# eof
|
||||
#=============================================================================
|
||||
Reference in New Issue
Block a user