Disclaimer: Although I am reasonably confident of the security of this implementation, I take absolutely no responsibility for its usage, nor any data-losses incurred as a result thereof. This is intended to be a proof-of-concept, and should under no circumstances be assumed to be secure, nor should it be used in production settings, or with real users’ data.
Introduction
Nearly two years ago, I wrote a simple Python login system using BCrypt (see here). After two more years of undergraduate study, and several security-focussed modules, this is my second attempt.
Note: this implementation is provided as a stand-alone class, and will not run on its own.
Code
user.py
import hashlib
import secrets
class User:
"""
A class to represent a user.
=========================
Attributes:
----------
first_name: str
first name of the user
last_name: str
last name of the user
email: str
email address of the user
username: str
username for the user
password: str
password for the user
Methods:
----------
get_user_details(password) -> dict:
returns a dictionary of the user's details if password is correct, otherwise None.
check_password(password: str) -> bool:
returns True if the password is correct for the user, otherwise False.
change_password(password: str, new_password: str) -> bool:
returns True if password changed, otherwise False.
"""
MINIMUM_PASSWORD_LENGTH = 12
__SALT_LENGTH = 256
def __init__(self, first_name: str, last_name: str, email: str, username: str, password: str) -> None:
self.__first_name = first_name
self.__last_name = last_name
self.__email = email
self.__username = username
if len(password) >= self.MINIMUM_PASSWORD_LENGTH:
self.__password = self.__hash_password(password)
else:
raise ValueError(f"The password must be at least {self.MINIMUM_PASSWORD_LENGTH} characters.")
def __hash_password(self, password: str) -> dict:
p = password.encode('UTF-8')
s = str(secrets.randbits(self.__SALT_LENGTH)).encode('UTF-8')
h = hashlib.sha3_512()
h.update(p)
h.update(s)
d = {
'hash': h.hexdigest(),
'salt': s
}
return d
def get_user_details(self, password: str) -> dict:
if self.check_password(password):
d = {
'first_name': self.__first_name,
'last_name': self.__last_name,
'email': self.__email,
'username': self.__username
}
return d
def check_password(self, password: str) -> bool:
p = password.encode('UTF-8')
s = self.__password['salt']
h = hashlib.sha3_512()
h.update(p)
h.update(s)
if h.hexdigest() == self.__password['hash']:
return True
else:
return False
def change_password(self, password: str, new_password: str) -> bool:
if self.check_password(password):
self.__password = self.__hash_password(new_password)
return True
else:
return False
This code is also available as a GitHub Gist here.
- George