Skip to content

Commit 8bb648b

Browse files
authored
Merge pull request #25 from notypecheck/fix-bcrypt-tests
test: fix bcrypt tests
2 parents 10b515d + 68e5833 commit 8bb648b

File tree

4 files changed

+187
-112
lines changed

4 files changed

+187
-112
lines changed

tests/conftest.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from typing import Any
2+
3+
import pytest
4+
5+
from passlib.handlers.bcrypt import bcrypt
6+
7+
8+
@pytest.fixture(scope="session")
9+
def bcrypt_backend_raises_on_wraparound() -> bool:
10+
try:
11+
bcrypt.hash(secret="abc" * 100)
12+
except ValueError:
13+
return True
14+
return False
15+
16+
17+
@pytest.fixture(scope="class")
18+
def bcrypt_backend_raises_on_wraparound_unittest(
19+
request: Any,
20+
bcrypt_backend_raises_on_wraparound: bool,
21+
):
22+
request.cls.bcrypt_backend_raises_on_wraparound = (
23+
bcrypt_backend_raises_on_wraparound
24+
)

tests/test_handlers_bcrypt.py

Lines changed: 150 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,10 @@
66
import pytest
77

88
from passlib import hash
9-
from passlib.handlers.bcrypt import (
10-
IDENT_2,
11-
IDENT_2A,
12-
IDENT_2B,
13-
IDENT_2X,
14-
IDENT_2Y,
15-
)
9+
from passlib.handlers.bcrypt import IDENT_2, IDENT_2A, IDENT_2B, IDENT_2X, IDENT_2Y
10+
from passlib.handlers.bcrypt import bcrypt as bcrypt_handler
1611
from passlib.utils import repeat_string, to_bytes
12+
from passlib.utils.handlers import GenericHandler
1713
from tests.test_handlers import UPASS_TABLE
1814
from tests.utils import TEST_MODE, HandlerCase
1915
from tests.utils_ import no_warnings
@@ -26,101 +22,7 @@ class _bcrypt_test(HandlerCase):
2622
reduce_default_rounds = True
2723
fuzz_salts_need_bcrypt_repair = True
2824

29-
known_correct_hashes = [
30-
#
31-
# from JTR 1.7.9
32-
#
33-
("U*U*U*U*", "$2a$05$c92SVSfjeiCD6F2nAD6y0uBpJDjdRkt0EgeC4/31Rf2LUZbDRDE.O"),
34-
("U*U***U", "$2a$05$WY62Xk2TXZ7EvVDQ5fmjNu7b0GEzSzUXUh2cllxJwhtOeMtWV3Ujq"),
35-
("U*U***U*", "$2a$05$Fa0iKV3E2SYVUlMknirWU.CFYGvJ67UwVKI1E2FP6XeLiZGcH3MJi"),
36-
("*U*U*U*U", "$2a$05$.WRrXibc1zPgIdRXYfv.4uu6TD1KWf0VnHzq/0imhUhuxSxCyeBs2"),
37-
("", "$2a$05$Otz9agnajgrAe0.kFVF9V.tzaStZ2s1s4ZWi/LY4sw2k/MTVFj/IO"),
38-
#
39-
# test vectors from http://www.openwall.com/crypt v1.2
40-
# note that this omits any hashes that depend on crypt_blowfish's
41-
# various CVE-2011-2483 workarounds (hash 2a and \xff\xff in password,
42-
# and any 2x hashes); and only contain hashes which are correct
43-
# under both crypt_blowfish 1.2 AND OpenBSD.
44-
#
45-
("U*U", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"),
46-
("U*U*", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK"),
47-
("U*U*U", "$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a"),
48-
("", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy"),
49-
(
50-
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
51-
"0123456789chars after 72 are ignored",
52-
"$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui",
53-
),
54-
(b"\xa3", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq"),
55-
(
56-
b"\xff\xa3345",
57-
"$2a$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e",
58-
),
59-
(b"\xa3ab", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS"),
60-
(
61-
b"\xaa" * 72 + b"chars after 72 are ignored as usual",
62-
"$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6",
63-
),
64-
(
65-
b"\xaa\x55" * 36,
66-
"$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy",
67-
),
68-
(
69-
b"\x55\xaa\xff" * 24,
70-
"$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe",
71-
),
72-
# keeping one of their 2y tests, because we are supporting that.
73-
(b"\xa3", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq"),
74-
#
75-
# 8bit bug (fixed in 2y/2b)
76-
#
77-
# NOTE: see assert_lacks_8bit_bug() for origins of this test vector.
78-
(b"\xd1\x91", "$2y$05$6bNw2HLQYeqHYyBfLMsv/OUcZd0LKP39b87nBw3.S2tVZSqiQX6eu"),
79-
#
80-
# bsd wraparound bug (fixed in 2b)
81-
#
82-
# NOTE: if backend is vulnerable, password will hash the same as '0'*72
83-
# ("$2a$04$R1lJ2gkNaoPGdafE.H.16.nVyh2niHsGJhayOHLMiXlI45o8/DU.6"),
84-
# rather than same as ("0123456789"*8)[:72]
85-
# 255 should be sufficient, but checking
86-
(
87-
("0123456789" * 26)[:254],
88-
"$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi",
89-
),
90-
(
91-
("0123456789" * 26)[:255],
92-
"$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi",
93-
),
94-
(
95-
("0123456789" * 26)[:256],
96-
"$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi",
97-
),
98-
(
99-
("0123456789" * 26)[:257],
100-
"$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi",
101-
),
102-
#
103-
# from py-bcrypt tests
104-
#
105-
("", "$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s."),
106-
("a", "$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u"),
107-
("abc", "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi"),
108-
(
109-
"abcdefghijklmnopqrstuvwxyz",
110-
"$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq",
111-
),
112-
(
113-
"~!@#$%^&*() ~!@#$%^&*()PNBFRD",
114-
"$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS",
115-
),
116-
#
117-
# custom test vectors
118-
#
119-
# ensures utf-8 used for unicode
120-
(UPASS_TABLE, "$2a$05$Z17AXnnlpzddNUvnC6cZNOSwMA/8oNiKnHTHTwLlBijfucQQlHjaG"),
121-
# ensure 2b support
122-
(UPASS_TABLE, "$2b$05$Z17AXnnlpzddNUvnC6cZNOSwMA/8oNiKnHTHTwLlBijfucQQlHjaG"),
123-
]
25+
known_correct_hashes = []
12426

12527
if TEST_MODE("full"):
12628
#
@@ -356,8 +258,153 @@ def test_needs_update_w_padding(self):
356258
assert not bcrypt.needs_update(GOOD1)
357259

358260

261+
@pytest.mark.usefixtures("bcrypt_backend_raises_on_wraparound_unittest")
262+
class bcrypt_test(_bcrypt_test):
263+
__test__ = False
264+
265+
def test_secret_with_truncate_size(self) -> None:
266+
if not self.bcrypt_backend_raises_on_wraparound: # type: ignore[attr-defined]
267+
super().test_secret_with_truncate_size()
268+
269+
def test_77_fuzz_input(self, threaded: bool = False) -> None:
270+
if not self.bcrypt_backend_raises_on_wraparound: # type: ignore[attr-defined]
271+
super().test_77_fuzz_input(threaded=threaded)
272+
273+
359274
# create test cases for specific backends
360-
bcrypt_bcrypt_test = _bcrypt_test.create_backend_case("bcrypt")
275+
bcrypt_bcrypt_test = bcrypt_test.create_backend_case("bcrypt")
276+
277+
278+
@pytest.fixture(scope="session")
279+
def handler() -> type[GenericHandler]:
280+
return bcrypt_handler
281+
282+
283+
@pytest.mark.parametrize(
284+
("secret", "hash"),
285+
[
286+
#
287+
# from JTR 1.7.9
288+
#
289+
("U*U*U*U*", "$2a$05$c92SVSfjeiCD6F2nAD6y0uBpJDjdRkt0EgeC4/31Rf2LUZbDRDE.O"),
290+
("U*U***U", "$2a$05$WY62Xk2TXZ7EvVDQ5fmjNu7b0GEzSzUXUh2cllxJwhtOeMtWV3Ujq"),
291+
("U*U***U*", "$2a$05$Fa0iKV3E2SYVUlMknirWU.CFYGvJ67UwVKI1E2FP6XeLiZGcH3MJi"),
292+
("*U*U*U*U", "$2a$05$.WRrXibc1zPgIdRXYfv.4uu6TD1KWf0VnHzq/0imhUhuxSxCyeBs2"),
293+
("", "$2a$05$Otz9agnajgrAe0.kFVF9V.tzaStZ2s1s4ZWi/LY4sw2k/MTVFj/IO"),
294+
#
295+
# test vectors from http://www.openwall.com/crypt v1.2
296+
# note that this omits any hashes that depend on crypt_blowfish's
297+
# various CVE-2011-2483 workarounds (hash 2a and \xff\xff in password,
298+
# and any 2x hashes); and only contain hashes which are correct
299+
# under both crypt_blowfish 1.2 AND OpenBSD.
300+
#
301+
("U*U", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"),
302+
("U*U*", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK"),
303+
("U*U*U", "$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a"),
304+
("", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy"),
305+
(
306+
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
307+
"0123456789chars after 72 are ignored",
308+
"$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui",
309+
),
310+
(b"\xa3", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq"),
311+
(
312+
b"\xff\xa3345",
313+
"$2a$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e",
314+
),
315+
(b"\xa3ab", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS"),
316+
(
317+
b"\xaa" * 72 + b"chars after 72 are ignored as usual",
318+
"$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6",
319+
),
320+
(
321+
b"\xaa\x55" * 36,
322+
"$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy",
323+
),
324+
(
325+
b"\x55\xaa\xff" * 24,
326+
"$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe",
327+
),
328+
# keeping one of their 2y tests, because we are supporting that.
329+
(b"\xa3", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq"),
330+
#
331+
# 8bit bug (fixed in 2y/2b)
332+
#
333+
# NOTE: see assert_lacks_8bit_bug() for origins of this test vector.
334+
(b"\xd1\x91", "$2y$05$6bNw2HLQYeqHYyBfLMsv/OUcZd0LKP39b87nBw3.S2tVZSqiQX6eu"),
335+
#
336+
# bsd wraparound bug (fixed in 2b)
337+
#
338+
# NOTE: if backend is vulnerable, password will hash the same as '0'*72
339+
# ("$2a$04$R1lJ2gkNaoPGdafE.H.16.nVyh2niHsGJhayOHLMiXlI45o8/DU.6"),
340+
# rather than same as ("0123456789"*8)[:72]
341+
# 255 should be sufficient, but checking
342+
(
343+
("0123456789" * 26)[:254],
344+
"$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi",
345+
),
346+
(
347+
("0123456789" * 26)[:255],
348+
"$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi",
349+
),
350+
(
351+
("0123456789" * 26)[:256],
352+
"$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi",
353+
),
354+
(
355+
("0123456789" * 26)[:257],
356+
"$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi",
357+
),
358+
#
359+
# from py-bcrypt tests
360+
#
361+
("", "$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s."),
362+
("a", "$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u"),
363+
("abc", "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi"),
364+
(
365+
"abcdefghijklmnopqrstuvwxyz",
366+
"$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq",
367+
),
368+
(
369+
"~!@#$%^&*() ~!@#$%^&*()PNBFRD",
370+
"$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS",
371+
),
372+
#
373+
# custom test vectors
374+
#
375+
# ensures utf-8 used for unicode
376+
(UPASS_TABLE, "$2a$05$Z17AXnnlpzddNUvnC6cZNOSwMA/8oNiKnHTHTwLlBijfucQQlHjaG"),
377+
# ensure 2b support
378+
(UPASS_TABLE, "$2b$05$Z17AXnnlpzddNUvnC6cZNOSwMA/8oNiKnHTHTwLlBijfucQQlHjaG"),
379+
],
380+
)
381+
def test_known_hashes(
382+
secret: str,
383+
hash: str,
384+
bcrypt_backend_raises_on_wraparound: bool,
385+
handler: GenericHandler,
386+
) -> None:
387+
assert handler.truncate_size
388+
389+
if bcrypt_backend_raises_on_wraparound and len(secret) > handler.truncate_size:
390+
return
391+
392+
assert handler.verify(secret=secret, hash=hash)
393+
394+
395+
def test_with_truncate_size(
396+
handler: GenericHandler,
397+
bcrypt_backend_raises_on_wraparound: bool,
398+
) -> None:
399+
if bcrypt_backend_raises_on_wraparound:
400+
return
401+
402+
assert handler.truncate_size
403+
404+
long_secret = "abc" * handler.truncate_size
405+
406+
hashed = handler.hash(secret=long_secret)
407+
assert handler.verify(secret=long_secret[: handler.truncate_size], hash=hashed)
361408

362409

363410
class _bcrypt_sha256_test(HandlerCase):
@@ -591,5 +638,4 @@ def test_calc_digest_v2(self):
591638
assert result == bcrypt_digest
592639

593640

594-
# create test cases for specific backends
595641
bcrypt_sha256_bcrypt_test = _bcrypt_sha256_test.create_backend_case("bcrypt")

tests/test_handlers_django.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import warnings
44
from unittest import SkipTest, skipUnless
55

6+
import pytest
7+
68
from passlib import hash
79
from passlib.utils import repeat_string
810
from tests.test_ext_django import (
@@ -14,9 +16,6 @@
1416
from tests.test_handlers_argon2 import _base_argon2_test
1517
from tests.utils import HandlerCase, TestCase
1618

17-
# module
18-
19-
2019
# standard string django uses
2120
UPASS_LETMEIN = "l\xe8tmein"
2221

@@ -280,6 +279,7 @@ class django_pbkdf2_sha1_test(HandlerCase, _DjangoHelper):
280279

281280

282281
@skipUnless(hash.bcrypt.has_backend(), "no bcrypt backends available")
282+
@pytest.mark.usefixtures("bcrypt_backend_raises_on_wraparound_unittest")
283283
class django_bcrypt_test(HandlerCase, _DjangoHelper):
284284
"""test django_bcrypt"""
285285

@@ -321,6 +321,14 @@ def random_ident(self):
321321
# XXX: enable this to check 2a / 2b?
322322
return None
323323

324+
def test_secret_with_truncate_size(self):
325+
if not self.bcrypt_backend_raises_on_wraparound:
326+
super().test_truncate_error_setting()
327+
328+
def test_77_fuzz_input(self, threaded: bool = False) -> None:
329+
if not self.bcrypt_backend_raises_on_wraparound: # type: ignore[attr-defined]
330+
super().test_77_fuzz_input(threaded=threaded)
331+
324332

325333
@skipUnless(hash.bcrypt.has_backend(), "no bcrypt backends available")
326334
class django_bcrypt_sha256_test(HandlerCase, _DjangoHelper):

tests/utils.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2059,13 +2059,10 @@ def test_secret_wo_truncate_size(self):
20592059
alt_secret = secret[:-1] + alt
20602060
assert not self.do_verify(alt_secret, hash), "full password not used in digest"
20612061

2062-
def test_secret_w_truncate_size(self):
2062+
def test_secret_with_truncate_size(self):
20632063
"""
20642064
test password size limits raise truncate_error (if appropriate)
20652065
"""
2066-
# --------------------------------------------------
2067-
# check if test is applicable
2068-
# --------------------------------------------------
20692066
handler = self.handler
20702067
truncate_size = handler.truncate_size
20712068
if not truncate_size:
@@ -2075,7 +2072,7 @@ def test_secret_w_truncate_size(self):
20752072
# setup vars
20762073
# --------------------------------------------------
20772074
# try to get versions w/ and w/o truncate_error set.
2078-
# set to None if policy isn't configruable
2075+
# set to None if policy isn't configurable
20792076
size_error_type = exc.PasswordSizeError
20802077
if "truncate_error" in handler.setting_kwds:
20812078
without_error = handler.using(truncate_error=False)

0 commit comments

Comments
 (0)