diff options
-rw-r--r-- | src/bcrypt/__init__.py | 8 | ||||
-rw-r--r-- | tests/reference/Makefile | 4 | ||||
-rw-r--r-- | tests/reference/bcrypt_reference.go | 35 | ||||
-rw-r--r-- | tests/reference/go.mod | 5 | ||||
-rw-r--r-- | tests/reference/go.sum | 2 | ||||
-rw-r--r-- | tests/test_bcrypt.py | 32 | ||||
-rw-r--r-- | tox.ini | 4 |
7 files changed, 75 insertions, 15 deletions
diff --git a/src/bcrypt/__init__.py b/src/bcrypt/__init__.py index be79417..1f2886f 100644 --- a/src/bcrypt/__init__.py +++ b/src/bcrypt/__init__.py @@ -73,9 +73,6 @@ def hashpw(password: bytes, salt: bytes) -> bytes: if isinstance(password, str) or isinstance(salt, str): raise TypeError("Strings must be encoded before hashing") - if b"\x00" in password: - raise ValueError("password may not contain NUL bytes") - # bcrypt originally suffered from a wraparound bug: # http://www.openwall.com/lists/oss-security/2012/01/02/4 # This bug was corrected in the OpenBSD source by truncating inputs to 72 @@ -91,11 +88,6 @@ def checkpw(password: bytes, hashed_password: bytes) -> bool: if isinstance(password, str) or isinstance(hashed_password, str): raise TypeError("Strings must be encoded before checking") - if b"\x00" in password or b"\x00" in hashed_password: - raise ValueError( - "password and hashed_password may not contain NUL bytes" - ) - ret = hashpw(password, hashed_password) return hmac.compare_digest(ret, hashed_password) diff --git a/tests/reference/Makefile b/tests/reference/Makefile new file mode 100644 index 0000000..faf967f --- /dev/null +++ b/tests/reference/Makefile @@ -0,0 +1,4 @@ +.PHONY: all + +all: + go build -v ./bcrypt_reference.go diff --git a/tests/reference/bcrypt_reference.go b/tests/reference/bcrypt_reference.go new file mode 100644 index 0000000..81417c8 --- /dev/null +++ b/tests/reference/bcrypt_reference.go @@ -0,0 +1,35 @@ +package main + +import ( + "io" + "os" + "strconv" + + "golang.org/x/crypto/bcrypt" +) + +// provides access to the golang implementation of bcrypt, for reference: +// "password" to hash is provided on stdin, cost parameter is an optional +// command-line parameter + +func main() { + cost := bcrypt.MinCost + if len(os.Args) > 1 { + if parsed, err := strconv.Atoi(os.Args[1]); err == nil { + cost = parsed + } + } + + buf, err := io.ReadAll(os.Stdin) + if err != nil { + panic(err) + } + + out, err := bcrypt.GenerateFromPassword(buf, cost) + if err != nil { + panic(err) + } + + os.Stdout.Write(out) + os.Stdout.Write([]byte("\n")) +} diff --git a/tests/reference/go.mod b/tests/reference/go.mod new file mode 100644 index 0000000..40d8c88 --- /dev/null +++ b/tests/reference/go.mod @@ -0,0 +1,5 @@ +module github.com/pyca/bcrypt + +go 1.17 + +require golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect diff --git a/tests/reference/go.sum b/tests/reference/go.sum new file mode 100644 index 0000000..8c260b8 --- /dev/null +++ b/tests/reference/go.sum @@ -0,0 +1,2 @@ +golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= diff --git a/tests/test_bcrypt.py b/tests/test_bcrypt.py index d3bf6a1..c6deb85 100644 --- a/tests/test_bcrypt.py +++ b/tests/test_bcrypt.py @@ -144,6 +144,20 @@ _test_vectors = [ b"$2a$05$/OK.fbVrR/bpIqNJ5ianF.", b"$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq", ), + ( + b"}>\xb3\xfe\xf1\x8b\xa0\xe6(\xa2Lzq\xc3P\x7f\xcc\xc8b{\xf9\x14\xf6" + b"\xf6`\x81G5\xec\x1d\x87\x10\xbf\xa7\xe1}I7 \x96\xdfc\xf2\xbf\xb3Vh" + b"\xdfM\x88q\xf7\xff\x1b\x82~z\x13\xdd\xe9\x84\x00\xdd4", + b"$2b$10$keO.ZZs22YtygVF6BLfhGO", + b"$2b$10$keO.ZZs22YtygVF6BLfhGOI/JjshJYPp8DZsUtym6mJV2Eha2Hdd.", + ), + ( + b"g7\r\x01\xf3\xd4\xd0\xa9JB^\x18\x007P\xb2N\xc7\x1c\xee\x87&\x83C" + b"\x8b\xe8\x18\xc5>\x86\x14/\xd6\xcc\x1cJ\xde\xd7ix\xeb\xdeO\xef" + b"\xe1i\xac\xcb\x03\x96v1' \xd6@.m\xa5!\xa0\xef\xc0(", + b"$2a$04$tecY.9ylRInW/rAAzXCXPO", + b"$2a$04$tecY.9ylRInW/rAAzXCXPOOlyYeCNzmNTzPDNSIFztFMKbvs/s5XG", + ), ] _2y_test_vectors = [ @@ -281,11 +295,10 @@ def test_hashpw_str_salt(): def test_checkpw_nul_byte(): - with pytest.raises(ValueError): - bcrypt.checkpw( - b"abc\0def", - b"$2b$04$2Siw3Nv3Q/gTOIPetAyPr.GNj3aO0lb1E5E9UumYGKjP9BYqlNWJe", - ) + bcrypt.checkpw( + b"abc\0def", + b"$2b$04$2Siw3Nv3Q/gTOIPetAyPr.GNj3aO0lb1E5E9UumYGKjP9BYqlNWJe", + ) with pytest.raises(ValueError): bcrypt.checkpw( @@ -296,8 +309,13 @@ def test_checkpw_nul_byte(): def test_hashpw_nul_byte(): salt = bcrypt.gensalt(4) - with pytest.raises(ValueError): - bcrypt.hashpw(b"abc\0def", salt) + hashed = bcrypt.hashpw(b"abc\0def", salt) + assert bcrypt.checkpw(b"abc\0def", hashed) + # assert that we are sensitive to changes in the password after the first + # null byte: + assert not bcrypt.checkpw(b"abc\0deg", hashed) + assert not bcrypt.checkpw(b"abc\0def\0", hashed) + assert not bcrypt.checkpw(b"abc\0def\0\0", hashed) def test_checkpw_extra_data(): @@ -42,3 +42,7 @@ ignore = E203,E211,E501,W503,W504 exclude = .tox,*.egg select = E,W,F,N,I application-import-names = bcrypt,tests + +[check-manifest] +ignore = + tests/reference/* |