core (memory/crypto): replaced PyCrypto by cryptography:
`PyCrypto` is unmaintained for years but was used in SàT for password hashing. This patch fixes that by replacing `PyCrypto` by the reference `cryptography` module which is well maintained. The behaviour stays the same (except that previously async `hash`, `encrypt` and `decrypt` methods are now synchronous, as they are quick and using a deferToThread may actually be more resource intensive than using blocking methods). It is planed to improve `memory.crypto` by using more up-to-date cryptography/hashing algorithms in the future. PyCrypto is no more a dependency of SàT
This commit is contained in:
parent
5ef5f4aabf
commit
d0741de2ad
1 changed files with 108 additions and 141 deletions
|
|
@ -347,7 +347,7 @@ class Memory(object):
|
|||
finally:
|
||||
return session_d
|
||||
|
||||
auth_d = self.profileAuthenticate(password, profile)
|
||||
auth_d = defer.ensureDeferred(self.profileAuthenticate(password, profile))
|
||||
auth_d.addCallback(doStartSession)
|
||||
return auth_d
|
||||
|
||||
|
|
@ -381,37 +381,30 @@ class Memory(object):
|
|||
except KeyError:
|
||||
return False
|
||||
|
||||
def profileAuthenticate(self, password, profile):
|
||||
async def profileAuthenticate(self, password, profile):
|
||||
"""Authenticate the profile.
|
||||
|
||||
@param password (unicode): the SàT profile password
|
||||
@param profile: %(doc_profile)s
|
||||
@return (D): a deferred None in case of success, a failure otherwise.
|
||||
@return: None in case of success (an exception is raised otherwise)
|
||||
@raise exceptions.PasswordError: the password does not match
|
||||
"""
|
||||
if not password and self.auth_sessions.profileGetUnique(profile):
|
||||
# XXX: this allows any frontend to connect with the empty password as soon as
|
||||
# the profile has been authenticated at least once before. It is OK as long as
|
||||
# submitting a form with empty passwords is restricted to local frontends.
|
||||
return defer.succeed(None)
|
||||
return
|
||||
|
||||
def check_result(result):
|
||||
if not result:
|
||||
log.warning("Authentication failure of profile {}".format(profile))
|
||||
raise failure.Failure(
|
||||
exceptions.PasswordError(
|
||||
"The provided profile password doesn't match."
|
||||
)
|
||||
)
|
||||
return self.newAuthSession(password, profile)
|
||||
|
||||
d = self.asyncGetParamA(
|
||||
sat_cipher = await self.asyncGetParamA(
|
||||
C.PROFILE_PASS_PATH[1], C.PROFILE_PASS_PATH[0], profile_key=profile
|
||||
)
|
||||
d.addCallback(lambda sat_cipher: PasswordHasher.verify(password, sat_cipher))
|
||||
return d.addCallback(check_result)
|
||||
valid = PasswordHasher.verify(password, sat_cipher)
|
||||
if not valid:
|
||||
log.warning(_("Authentication failure of profile {profile}").format(
|
||||
profile=profile))
|
||||
raise exceptions.PasswordError("The provided profile password doesn't match.")
|
||||
return await self.newAuthSession(password, profile)
|
||||
|
||||
def newAuthSession(self, key, profile):
|
||||
async def newAuthSession(self, key, profile):
|
||||
"""Start a new session for the authenticated profile.
|
||||
|
||||
If there is already an existing session, no new one is created
|
||||
|
|
@ -419,21 +412,16 @@ class Memory(object):
|
|||
|
||||
@param key: the key to decrypt the personal key
|
||||
@param profile: %(doc_profile)s
|
||||
@return: a deferred None value
|
||||
"""
|
||||
|
||||
def gotPersonalKey(personal_key):
|
||||
"""Create the session for this profile and store the personal key"""
|
||||
session_data = self.auth_sessions.profileGetUnique(profile)
|
||||
if not session_data:
|
||||
self.auth_sessions.newSession(
|
||||
{C.MEMORY_CRYPTO_KEY: personal_key}, profile=profile
|
||||
)
|
||||
log.debug("auth session created for profile %s" % profile)
|
||||
|
||||
d = PersistentDict(C.MEMORY_CRYPTO_NAMESPACE, profile).load()
|
||||
d.addCallback(lambda data: BlockCipher.decrypt(key, data[C.MEMORY_CRYPTO_KEY]))
|
||||
return d.addCallback(gotPersonalKey)
|
||||
data = await PersistentDict(C.MEMORY_CRYPTO_NAMESPACE, profile).load()
|
||||
personal_key = BlockCipher.decrypt(key, data[C.MEMORY_CRYPTO_KEY])
|
||||
# Create the session for this profile and store the personal key
|
||||
session_data = self.auth_sessions.profileGetUnique(profile)
|
||||
if not session_data:
|
||||
self.auth_sessions.newSession(
|
||||
{C.MEMORY_CRYPTO_KEY: personal_key}, profile=profile
|
||||
)
|
||||
log.debug("auth session created for profile %s" % profile)
|
||||
|
||||
def purgeProfileSession(self, profile):
|
||||
"""Delete cache of data of profile
|
||||
|
|
@ -1064,13 +1052,8 @@ class Memory(object):
|
|||
"""
|
||||
|
||||
def gotIndMemory(data):
|
||||
d = BlockCipher.encrypt(crypto_key, data_value)
|
||||
|
||||
def cb(cipher):
|
||||
data[data_key] = cipher
|
||||
return data.force(data_key)
|
||||
|
||||
return d.addCallback(cb)
|
||||
data[data_key] = BlockCipher.encrypt(crypto_key, data_value)
|
||||
return data.force(data_key)
|
||||
|
||||
def done(__):
|
||||
log.debug(
|
||||
|
|
|
|||
Loading…
Reference in a new issue