forked from indymedia/epicyon
Unit test for constant time string check
parent
7e87bbe2aa
commit
e584076aca
41
auth.py
41
auth.py
|
@ -34,6 +34,28 @@ def getPasswordHash(salt: str, providedPassword: str) -> str:
|
||||||
return binascii.hexlify(pwdhash).decode('ascii')
|
return binascii.hexlify(pwdhash).decode('ascii')
|
||||||
|
|
||||||
|
|
||||||
|
def constantTimeStringCheck(string1: str, string2: str) -> bool:
|
||||||
|
"""Compares two string and returns if they are the same
|
||||||
|
using a constant amount of time
|
||||||
|
See https://sqreen.github.io/DevelopersSecurityBestPractices/
|
||||||
|
timing-attack/python
|
||||||
|
"""
|
||||||
|
# strings must be of equal length
|
||||||
|
if len(string1) != len(string2):
|
||||||
|
return False
|
||||||
|
ctr = 0
|
||||||
|
matched = True
|
||||||
|
for ch in string1:
|
||||||
|
if ch != string2[ctr]:
|
||||||
|
matched = False
|
||||||
|
else:
|
||||||
|
# this is to make the timing more even
|
||||||
|
# and not provide clues
|
||||||
|
matched = matched
|
||||||
|
ctr += 1
|
||||||
|
return matched
|
||||||
|
|
||||||
|
|
||||||
def verifyPassword(storedPassword: str, providedPassword: str) -> bool:
|
def verifyPassword(storedPassword: str, providedPassword: str) -> bool:
|
||||||
"""Verify a stored password against one provided by user
|
"""Verify a stored password against one provided by user
|
||||||
"""
|
"""
|
||||||
|
@ -44,24 +66,7 @@ def verifyPassword(storedPassword: str, providedPassword: str) -> bool:
|
||||||
salt = storedPassword[:64]
|
salt = storedPassword[:64]
|
||||||
storedPassword = storedPassword[64:]
|
storedPassword = storedPassword[64:]
|
||||||
pwHash = getPasswordHash(salt, providedPassword)
|
pwHash = getPasswordHash(salt, providedPassword)
|
||||||
# check that hashes are of equal length
|
return constantTimeStringCheck(pwHash, storedPassword)
|
||||||
if len(pwHash) != len(storedPassword):
|
|
||||||
return False
|
|
||||||
# Compare all of the characters before returning true or false.
|
|
||||||
# Hence the match should take a constant amount of time.
|
|
||||||
# See https://sqreen.github.io/DevelopersSecurityBestPractices/
|
|
||||||
# timing-attack/python
|
|
||||||
ctr = 0
|
|
||||||
matched = True
|
|
||||||
for ch in pwHash:
|
|
||||||
if ch != storedPassword[ctr]:
|
|
||||||
matched = False
|
|
||||||
else:
|
|
||||||
# this is to make the timing more even
|
|
||||||
# and not provide clues
|
|
||||||
matched = matched
|
|
||||||
ctr += 1
|
|
||||||
return matched
|
|
||||||
|
|
||||||
|
|
||||||
def createBasicAuthHeader(nickname: str, password: str) -> str:
|
def createBasicAuthHeader(nickname: str, password: str) -> str:
|
||||||
|
|
29
tests.py
29
tests.py
|
@ -54,6 +54,7 @@ from person import setBio
|
||||||
from skills import setSkillLevel
|
from skills import setSkillLevel
|
||||||
from roles import setRole
|
from roles import setRole
|
||||||
from roles import outboxDelegate
|
from roles import outboxDelegate
|
||||||
|
from auth import constantTimeStringCheck
|
||||||
from auth import createBasicAuthHeader
|
from auth import createBasicAuthHeader
|
||||||
from auth import authorizeBasic
|
from auth import authorizeBasic
|
||||||
from auth import storeBasicCredentials
|
from auth import storeBasicCredentials
|
||||||
|
@ -2085,8 +2086,36 @@ def testTranslations():
|
||||||
assert langJson.get(englishStr)
|
assert langJson.get(englishStr)
|
||||||
|
|
||||||
|
|
||||||
|
def testConstantTimeStringCheck():
|
||||||
|
print('testConstantTimeStringCheck')
|
||||||
|
assert constantTimeStringCheck('testing', 'testing')
|
||||||
|
assert not constantTimeStringCheck('testing', '1234')
|
||||||
|
assert not constantTimeStringCheck('testing', '1234567')
|
||||||
|
|
||||||
|
itterations = 256
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
|
for timingTest in range(itterations):
|
||||||
|
constantTimeStringCheck('nnjfbefefbsnjsdnvbcueftqfeuqfbqefnjeniwufgy',
|
||||||
|
'nnjfbefefbsnjsdnvbcueftqfeuqfbqefnjeniwufgy')
|
||||||
|
end = time.time()
|
||||||
|
avTime1 = ((end - start) * 1000000 / itterations)
|
||||||
|
|
||||||
|
# change characters and observe timing difference
|
||||||
|
start = time.time()
|
||||||
|
for timingTest in range(itterations):
|
||||||
|
constantTimeStringCheck('nnjfbefefbsnjsdnvbcueftqfeuqfbqefnjeniwufgy',
|
||||||
|
'nnjfbefefbsnjsdnvbcueftqfeuqfbqeznjeniwufgy')
|
||||||
|
end = time.time()
|
||||||
|
avTime2 = ((end - start) * 1000000 / itterations)
|
||||||
|
timeDiffMicroseconds = abs(avTime2 - avTime1)
|
||||||
|
# time difference should be less than 10uS
|
||||||
|
assert timeDiffMicroseconds < 10
|
||||||
|
|
||||||
|
|
||||||
def runAllTests():
|
def runAllTests():
|
||||||
print('Running tests...')
|
print('Running tests...')
|
||||||
|
testConstantTimeStringCheck()
|
||||||
testTranslations()
|
testTranslations()
|
||||||
testValidContentWarning()
|
testValidContentWarning()
|
||||||
testRemoveIdEnding()
|
testRemoveIdEnding()
|
||||||
|
|
Loading…
Reference in New Issue