Add checkpw (#76)
This commit is contained in:
parent
81e8efd0cf
commit
c9a9ec1e7a
14
README.rst
14
README.rst
@ -37,6 +37,10 @@ For Fedora and RHEL-derivatives, the following command will ensure that the requ
|
||||
Changelog
|
||||
=========
|
||||
|
||||
3.1.0
|
||||
-----
|
||||
* Added support for ``checkpw`` as another method of verifying a password.
|
||||
|
||||
3.0.0
|
||||
-----
|
||||
* Switched the C backend to code obtained from the OpenBSD project rather than
|
||||
@ -51,8 +55,8 @@ Changelog
|
||||
Usage
|
||||
-----
|
||||
|
||||
Hashing
|
||||
~~~~~~~
|
||||
Password Hashing
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Hashing and then later checking that a password matches the previous hashed
|
||||
password is very simple:
|
||||
@ -63,9 +67,9 @@ password is very simple:
|
||||
>>> password = b"super secret password"
|
||||
>>> # Hash a password for the first time, with a randomly-generated salt
|
||||
>>> hashed = bcrypt.hashpw(password, bcrypt.gensalt())
|
||||
>>> # Check that a unhashed password matches one that has previously been
|
||||
>>> # hashed
|
||||
>>> if bcrypt.hashpw(password, hashed) == hashed:
|
||||
>>> # Check that an unhashed password matches one that has previously been
|
||||
>>> # hashed
|
||||
>>> if bcrypt.checkpw(password, hashed):
|
||||
... print("It Matches!")
|
||||
... else:
|
||||
... print("It Does not Match :(")
|
||||
|
||||
@ -78,6 +78,24 @@ def hashpw(password, salt):
|
||||
return _bcrypt.ffi.string(hashed)
|
||||
|
||||
|
||||
def checkpw(password, hashed_password):
|
||||
if (isinstance(password, six.text_type) or
|
||||
isinstance(hashed_password, six.text_type)):
|
||||
raise TypeError("Unicode-objects 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"
|
||||
)
|
||||
|
||||
# If the user supplies a $2y$ prefix we normalize to $2b$
|
||||
hashed_password = _normalize_prefix(hashed_password)
|
||||
|
||||
ret = hashpw(password, hashed_password)
|
||||
|
||||
return _bcrypt.lib.timingsafe_bcmp(ret, hashed_password, len(ret)) == 0
|
||||
|
||||
|
||||
def kdf(password, salt, desired_key_bytes, rounds):
|
||||
if isinstance(password, six.text_type) or isinstance(salt, six.text_type):
|
||||
raise TypeError("Unicode-objects must be encoded before hashing")
|
||||
|
||||
@ -25,6 +25,7 @@ int bcrypt_hashpass(const char *, const char *, char *, size_t);
|
||||
int encode_base64(char *, const uint8_t *, size_t);
|
||||
int bcrypt_pbkdf(const char *, size_t, const uint8_t *, size_t,
|
||||
uint8_t *, size_t, unsigned int);
|
||||
int timingsafe_bcmp(const void *, const void *, size_t);
|
||||
""")
|
||||
|
||||
ffi.set_source(
|
||||
|
||||
@ -216,6 +216,11 @@ def test_hashpw_new(password, salt, hashed):
|
||||
assert bcrypt.hashpw(password, salt) == hashed
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("password", "salt", "hashed"), _test_vectors)
|
||||
def test_checkpw(password, salt, hashed):
|
||||
assert bcrypt.checkpw(password, hashed) is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("password", "salt", "hashed"), _test_vectors)
|
||||
def test_hashpw_existing(password, salt, hashed):
|
||||
assert bcrypt.hashpw(password, hashed) == hashed
|
||||
@ -226,11 +231,47 @@ def test_hashpw_2y_prefix(password, hashed, expected):
|
||||
assert bcrypt.hashpw(password, hashed) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("password", "hashed", "expected"), _2y_test_vectors)
|
||||
def test_checkpw_2y_prefix(password, hashed, expected):
|
||||
assert bcrypt.checkpw(password, hashed) is True
|
||||
|
||||
|
||||
def test_hashpw_invalid():
|
||||
with pytest.raises(ValueError):
|
||||
bcrypt.hashpw(b"password", b"$2z$04$cVWp4XaNU8a4v1uMRum2SO")
|
||||
|
||||
|
||||
def test_checkpw_wrong_password():
|
||||
assert bcrypt.checkpw(
|
||||
b"badpass",
|
||||
b"$2b$04$2Siw3Nv3Q/gTOIPetAyPr.GNj3aO0lb1E5E9UumYGKjP9BYqlNWJe"
|
||||
) is False
|
||||
|
||||
|
||||
def test_checkpw_bad_salt():
|
||||
with pytest.raises(ValueError):
|
||||
bcrypt.checkpw(
|
||||
b"badpass",
|
||||
b"$2b$04$?Siw3Nv3Q/gTOIPetAyPr.GNj3aO0lb1E5E9UumYGKjP9BYqlNWJe"
|
||||
)
|
||||
|
||||
|
||||
def test_checkpw_str_password():
|
||||
with pytest.raises(TypeError):
|
||||
bcrypt.checkpw(
|
||||
six.text_type("password"),
|
||||
b"$2b$04$cVWp4XaNU8a4v1uMRum2SO",
|
||||
)
|
||||
|
||||
|
||||
def test_checkpw_str_salt():
|
||||
with pytest.raises(TypeError):
|
||||
bcrypt.checkpw(
|
||||
b"password",
|
||||
six.text_type("$2b$04$cVWp4XaNU8a4v1uMRum2SO"),
|
||||
)
|
||||
|
||||
|
||||
def test_hashpw_str_password():
|
||||
with pytest.raises(TypeError):
|
||||
bcrypt.hashpw(
|
||||
@ -247,6 +288,20 @@ 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"
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
bcrypt.checkpw(
|
||||
b"abcdef",
|
||||
b"$2b$04$2S\0w3Nv3Q/gTOIPetAyPr.GNj3aO0lb1E5E9UumYGKjP9BYqlNWJe"
|
||||
)
|
||||
|
||||
|
||||
def test_hashpw_nul_byte():
|
||||
salt = bcrypt.gensalt(4)
|
||||
with pytest.raises(ValueError):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user