We have code, that work for python 2
.
@password.setter
def password(self, value):
self.salt = bcrypt.gensalt()
self.passwd = bcrypt.hashpw(value.encode('utf-8'), self.salt)
def check_password(self, value):
return bcrypt.hashpw(value.encode('utf-8'), self.salt.encode('utf-8')) == self.passwd
However, when I try to convert it to python3, we meet following problems:
Error one happens on cassandra driver level:
cassandra.cqlengine.ValidationError: passwd <class 'bytes'> is not a string
Ok. Casting salt and passwd to string:
@password.setter
def password(self, value):
salt = bcrypt.gensalt()
self.salt = str(salt)
self.passwd = str(bcrypt.hashpw(value.encode('utf-8'), salt))
Now salt saves. But in check_password
we get ValueError: Invalid salt
.
If we change check password code to:
def check_password(self, value):
return bcrypt.hashpw(value, self.salt) == self.passwd
We get error TypeError: Unicode-objects must be encoded before hashing
.
Where to dig?
UPD Salt values in password and check password look same, for example:
b'$2b$12$cb03angGsu91KLj7xoh3Zu'
b'$2b$12$cb03angGsu91KLj7xoh3Zu'
Update
As of version 3.1.0 bcrypt
provides the convenience function
checkpw(password, hashed_password)
to perform password checking against a hashed password. This should be used instead of:
bcrypt.hashpw(passwd_to_check, hashed_passwd) == hashed_passwd
which is shown below. There is still no need to store the hash separately.
First of all, you don't need to store the salt because it is part of the hash produced by bcrypt.hashpw()
. You just need to store the hash. E.g.
>>> salt = bcrypt.gensalt()
>>> salt
b'$2b$12$ge7ZjwywBd5r5KG.tcznne'
>>> passwd = b'p@ssw0rd'
>>> hashed_passwd = bcrypt.hashpw(passwd, salt)
b'$2b$12$ge7ZjwywBd5r5KG.tcznnez8pEYcE1QvKshpqh3rrmwNTQIaDWWvO'
>>> hashed_passwd.startswith(salt)
True
So you can see that the salt is included in the hash.
You can also use bcrypt.hashpw()
to check that a password matches a hashed password:
>>> passwd_to_check = b'p@ssw0rd'
>>> matched = bcrypt.hashpw(passwd_to_check, hashed_passwd) == hashed_passwd
>>> matched
True
>>> bcrypt.hashpw(b'thewrongpassword', hashed_passwd) == hashed_passwd
False
No need to store the salt separately.
So you could write the setter like this (Python 3):
@password.setter
def password(self, passwd):
if isinstance(passwd, str):
passwd = bytes(passwd, 'utf-8')
self.passwd = str(bcrypt.hashpw(passwd, bcrypt.gensalt()), 'utf8')
And the checker like this:
def check_password(self, passwd_to_check):
if isinstance(passwd_to_check, str):
passwd_to_check = bytes(passwd_to_check, 'utf-8')
passwd = bytes(self.passwd, 'utf8')
return bcrypt.hashpw(passwd_to_check, passwd) == passwd
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With