69 lines
2.8 KiB
Python
69 lines
2.8 KiB
Python
|
# third party imports
|
||
|
import bcrypt
|
||
|
from cryptography.fernet import Fernet
|
||
|
|
||
|
|
||
|
class PasswordEncoder(Fernet):
|
||
|
"""This class is responsible for defining the encryption function for password encoding in the application and the
|
||
|
provision of a class method that can be used to define the static key attribute at the application's entry point.
|
||
|
|
||
|
:cvar key: a URL-safe base64-encoded 32-byte
|
||
|
:type key: bytes
|
||
|
"""
|
||
|
|
||
|
key = None
|
||
|
|
||
|
@classmethod
|
||
|
def set_key(cls, key: bytes):
|
||
|
"""This method sets the value of the static key attribute to make it accessible to all subsequent instances of
|
||
|
the class once defined.
|
||
|
:param key: key: a URL-safe base64-encoded 32-byte
|
||
|
:type key: bytes
|
||
|
"""
|
||
|
cls.key = key
|
||
|
|
||
|
def encrypt(self, ciphertext: bytes):
|
||
|
"""This overloads the encrypt function of the Fernet class
|
||
|
:param ciphertext: The data to be encrypted.
|
||
|
:type ciphertext: bytes
|
||
|
:return: A fernet token (A set of bytes representing the hashed value succeeding the encryption)
|
||
|
:rtype: bytes
|
||
|
"""
|
||
|
return super(PasswordEncoder, self).encrypt(ciphertext)
|
||
|
|
||
|
|
||
|
def create_password_hash(password):
|
||
|
"""This method encrypts a password value using a pre-set pepper and an appended salt. Documentation is brief since
|
||
|
symmetric encryption using a unique key (pepper) and salted passwords before hashing is well documented.
|
||
|
N/B: Fernet encryption requires the unique key to be a URL-safe base64-encoded 32-byte key.
|
||
|
https://cryptography.io/en/latest/fernet/
|
||
|
:param password: A password value
|
||
|
:type password: str
|
||
|
|
||
|
:raises ValueError: if a key whose length length is less than 32 bytes.
|
||
|
:raises binascii.Error: if base64 key is invalid or corrupted.
|
||
|
|
||
|
:return: A fernet token (A set of bytes representing the hashed value succeeding the encryption)
|
||
|
:rtype: str
|
||
|
"""
|
||
|
fernet = PasswordEncoder(PasswordEncoder.key)
|
||
|
return fernet.encrypt(bcrypt.hashpw(password.encode(), bcrypt.gensalt())).decode()
|
||
|
|
||
|
|
||
|
def check_password_hash(password, hashed_password):
|
||
|
"""This method ascertains a password's validity by hashing the provided password value using the original pepper and
|
||
|
compares the resultant fernet signature to the one persisted in the db for a given user.
|
||
|
:param password: A password value
|
||
|
:type password: str
|
||
|
:param hashed_password: A hash for a user's password value
|
||
|
:type hashed_password: str
|
||
|
|
||
|
:raises ValueError: if a key whose length length is less than 32 bytes.
|
||
|
:raises binascii.Error: if base64 key is invalid or corrupted.
|
||
|
|
||
|
:return: Password validity
|
||
|
:rtype: boolean
|
||
|
"""
|
||
|
fernet = PasswordEncoder(PasswordEncoder.key)
|
||
|
hashed_password = fernet.decrypt(hashed_password.encode())
|
||
|
return bcrypt.checkpw(password.encode(), hashed_password)
|