Skip to content

Commit 8d0445c

Browse files
authored
Merge pull request #15 from Colby-Coffman/agnostic-hash
Add support for alternate hashing algorithms and minor bug fixes
2 parents 7b179ae + c14d31c commit 8d0445c

File tree

4 files changed

+89
-21
lines changed

4 files changed

+89
-21
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
usrlist
2-
__pycache__/usrcheck.cpython-310.pyc
2+
# Ignore the whole cache
3+
__pycache__/

backendtest.py

+13-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import usrcheck
2-
import hashlib
2+
import hash
33
def test_auth():
4-
usr_list=[['testusr',hashlib.sha256(str('testPSWD'+'1QgF35ws').encode()).hexdigest(),'1QgF35ws']]
5-
usrcheck.save_users(usr_list,True)
6-
assert usrcheck.load_users() == usr_list, "Userlist loading failed"
7-
assert usrcheck.usr_check('testusr', 'testPSWD',usr_list) == (True,True), "User check failed"
8-
#Wrong username
9-
assert usrcheck.usr_check('wrongusr', 'testPSWD',usr_list) == (False,False), "User check failed"
10-
#Wrong password
11-
assert usrcheck.usr_check('testusr', 'wrongPSWD',usr_list) == (True,False), "User check failed"
4+
# Updated test to support new implementation
5+
for hash_method in hash.SUPPORTED_HASHES:
6+
hasher = hash.Hasher(hash_method, ('testPSWD' + '1QgF35ws').encode())
7+
usr_list = [['testusr', hasher.hexdigest(), '1QgF35ws']]
8+
hasher.clear_hasher()
9+
usrcheck.save_users(usr_list, True)
10+
assert usrcheck.load_users() == usr_list, "UserList loading failed"
11+
assert usrcheck.usr_check('testusr', 'testPSWD', usr_list, test_hasher=hasher) == (True,True), "User check failed"
12+
#Wrong username
13+
assert usrcheck.usr_check('wrongusr', 'testPSWD', usr_list, test_hasher=hasher) == (False,False), "User check failed"
14+
#Wrong password
15+
assert usrcheck.usr_check('testusr', 'wrongPSWD', usr_list, test_hasher=hasher) == (True,False), "User check failed"
1216
test_auth()

hash.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import hashlib
2+
3+
SUPPORTED_HASHES = hashlib.algorithms_available
4+
5+
class Hasher():
6+
def __init__(self, hash_name: str, initial_data: bytes = b'', **kwargs):
7+
if not (hash_name in SUPPORTED_HASHES):
8+
raise ValueError(f"{hash_name} is not a supported hashing algorithm")
9+
self.__hash_implementation: hashlib._Hash = hashlib.new(hash_name, initial_data, **kwargs)
10+
self.__kwargs = kwargs
11+
12+
# Convenient but wont show up in lsp, might have to define each method later
13+
def __getattr__(self, name):
14+
return getattr(self.__hash_implementation, name)
15+
16+
def hexdigest(self, shake_output_size: int=None) -> bytes:
17+
if self.__hash_implementation.name in ('shake_128', 'shake_256'):
18+
if shake_output_size:
19+
return self.__hash_implementation.hexdigest(shake_output_size)
20+
# Good default output sizes for shake
21+
elif self.__hash_implementation.name == 'shake_128':
22+
return self.__hash_implementation.hexdigest(32)
23+
elif self.__hash_implementation.name == 'shake_256':
24+
return self.__hash_implementation.hexdigest(64)
25+
return self.__hash_implementation.hexdigest()
26+
27+
def clear_hasher(self) -> None:
28+
self.__hash_implementation = hashlib.new(self.__hash_implementation.name, b'', **self.__kwargs)

usrcheck.py

+46-11
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import hash
2+
3+
# TODO: Encapsulate this later
4+
hasher = None
5+
16
def configRead():
27
import configparser
38
config = configparser.ConfigParser()
@@ -8,11 +13,16 @@ def configRead():
813
except KeyError:
914
quiet = False
1015
try:
11-
alg = config['Config']['alg']
12-
alg = str(quiet).lower()
16+
alg = config['Config']['alg'].lower()
17+
# alg = str(quiet).lower() Not really sure the purpose of this line
18+
# Not the most elegant but trying to hit the KeyError on unsupported hash algs
19+
if not (alg in hash.SUPPORTED_HASHES):
20+
raise KeyError
1321
except KeyError:
1422
print("ERROR: Algorithm not defined. Reverting to SHA256")
1523
alg = 'sha256'
24+
global hasher
25+
hasher = hash.Hasher(alg)
1626
try:
1727
saltSize = config['Salt']['saltSize']
1828
saltSize = int(saltSize)
@@ -29,7 +39,6 @@ def salter(pswd,saltsize):
2939
return pswd+salt,salt
3040

3141
def init_usrs(nUsr, saltSize):
32-
import hashlib
3342
usrlist=[]
3443
for index in range(nUsr):
3544
usrname=''
@@ -39,7 +48,10 @@ def init_usrs(nUsr, saltSize):
3948
while pswd=='':
4049
pswd = input("Enter password: ")
4150
pswd,salt = salter(pswd,saltSize)
42-
pswd = hashlib.sha256(pswd.encode()).hexdigest()
51+
hasher.update(pswd.encode())
52+
pswd = hasher.hexdigest()
53+
# We have to clear the hasher every time with the new implementation, later we could define this behaviour automatically
54+
hasher.clear_hasher()
4355
usrlist.append([])
4456
usrlist[index].append(usrname)
4557
usrlist[index].append(pswd)
@@ -78,15 +90,35 @@ def save_users(usrlist,overWrite):
7890
saveFile.write('')
7991
saveFile.close()
8092

81-
def usr_check(usrn,pswd,usrlist):
82-
import hashlib
93+
def usr_check(usrn,pswd,usrlist, test_hasher: hash.Hasher=None):
94+
if test_hasher:
95+
hasher = test_hasher
96+
# TODO:
97+
# Supporting different hashes means we have to figure out what hash was used for the usrlist
98+
# This basic solution simply reads what hash was specified in the config file.
99+
# The config file will need additional information, and this logic will have to be changed, if variable
100+
# output for shake is allowed.
101+
else:
102+
import configparser
103+
config = configparser.ConfigParser()
104+
config.read('config.ini')
105+
try:
106+
alg = config['Config']['alg'].lower()
107+
if not (alg in hash.SUPPORTED_HASHES):
108+
raise KeyError
109+
except KeyError:
110+
print("No algorithm specified to read usrlist or unsuporrted algorithm\nDefaulting to sha256")
111+
alg = 'sha256'
112+
hasher = hash.Hasher(alg)
83113
index=0
84114
Ufound=False
85115
Pfound=False
86116
while Ufound==False and index<len(usrlist):
87117
if usrlist[index][0]==usrn:
88118
Ufound=True
89-
pswd=hashlib.sha256(str(pswd+str(usrlist[index][2])).encode()).hexdigest()
119+
hasher.update(str(pswd+str(usrlist[index][2])).encode())
120+
pswd = hasher.hexdigest()
121+
hasher.clear_hasher()
90122
if usrlist[index][1]==pswd:
91123
Pfound=True
92124
break
@@ -118,11 +150,14 @@ def login_init(usrname,pswd,overWrite,quiet,saltSize):
118150
print("Userfile does not exist! Execute with overWrite set to 'True'")
119151
sys.exit()
120152
elif overWrite==True:
121-
if input("Do you want to overwrite the existing user list[y/N]? ").upper()=='Y':
122-
newFile=True
123-
else:
124-
newFile=False
153+
# Bug fix for when no usrlist exists but the user asks not to overwrite user list
154+
newFile = True
155+
if os.path.isfile("usrlist") and (input("Do you want to overwrite the existing user list [Y/N]? ").upper() == 'N'):
156+
newFile = False
125157
while True:
158+
# TODO:
159+
# Seems to me redundant to ask if they want to clear the database when "overwriting" the existing database does
160+
# the same thing.
126161
nUsr=input("Enter the number of users to initialize ('0' Clears the database): ")
127162
try:
128163
nUsr=int(nUsr)

0 commit comments

Comments
 (0)