SHA3-512 User Class.

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