diff --git a/libervia/backend/memory/memory.py b/libervia/backend/memory/memory.py index 07014852..379eb587 100644 --- a/libervia/backend/memory/memory.py +++ b/libervia/backend/memory/memory.py @@ -1105,45 +1105,59 @@ class Memory: ## Encryption ## - def encrypt_value(self, value, profile): - """Encrypt a value for the given profile. The personal key must be loaded - already in the profile session, that should be the case if the profile is - already authenticated. + def encrypt_value(self, client: SatXMPPEntity, value: str) -> str: + """Encrypt a value for the given profile. - @param value (str): the value to encrypt - @param profile (str): %(doc_profile)s - @return: the deferred encrypted value + I uses a personal key derived from password. The personal key is stable on profile + password change, so there is no need to re-encrypt. + + The personal key must be loaded already in the profile session, that should be the + case if the profile is already authenticated (session started). + + @param client: client instance. + Its profile's personal key will be used as encryption key. + @param value: the value to encrypt + @return: base-64 encoded encrypted value """ - try: - personal_key = self.auth_sessions.profile_get_unique(profile)[ - C.MEMORY_CRYPTO_KEY - ] - except TypeError: + session = self.auth_sessions.profile_get_unique(client.profile) + if session is None or (personal_key := session.get(C.MEMORY_CRYPTO_KEY)) is None: raise exceptions.InternalError( - _("Trying to encrypt a value for %s while the personal key is undefined!") - % profile + f"Trying to encrypt a value for {client.profile} while the personal key " + "is undefined!" ) return BlockCipher.encrypt(personal_key, value) - def decrypt_value(self, value, profile): - """Decrypt a value for the given profile. The personal key must be loaded - already in the profile session, that should be the case if the profile is - already authenticated. + def decrypt_value(self, client: SatXMPPEntity, encrypted: str) -> str: + """Decrypt a value for the given profile. - @param value (str): the value to decrypt - @param profile (str): %(doc_profile)s - @return: the deferred decrypted value + See [encrypt_value]. + + @param client: client instance. + Its profile's personal key will be used as encryption key. + @param encrypted: base-64 encoded encrypted value + @return: decrypted value + @raise exceptions.InternalError: personal key is not loaded """ - try: - personal_key = self.auth_sessions.profile_get_unique(profile)[ - C.MEMORY_CRYPTO_KEY - ] - except TypeError: + return self.decrypt_value_by_profile(encrypted, client.profile) + + def decrypt_value_by_profile(self, encrypted: str, profile: str) -> str: + """Decrypt a value for the given profile by its name. + + Useful when a client is not yet available, but the profile session has been + started. + + @param encrypted: base-64 encoded encrypted value + @param profile: profile name + @return: decrypted value + @raise exceptions.InternalError: personal key is not loaded + """ + session = self.auth_sessions.profile_get_unique(profile) + if session is None or (personal_key := session.get(C.MEMORY_CRYPTO_KEY)) is None: raise exceptions.InternalError( - _("Trying to decrypt a value for %s while the personal key is undefined!") - % profile + f"Trying to decrypt a value for {profile} while the personal key " + "is undefined!" ) - return BlockCipher.decrypt(personal_key, value) + return BlockCipher.decrypt(personal_key, encrypted) def encrypt_personal_data(self, data_key, data_value, crypto_key, profile): """Re-encrypt a personal data (saved to a PersistentDict). diff --git a/libervia/backend/memory/params.py b/libervia/backend/memory/params.py index 2254e078..9ae7a5f5 100644 --- a/libervia/backend/memory/params.py +++ b/libervia/backend/memory/params.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +from libervia.backend import G from libervia.backend.core.i18n import _, D_ from libervia.backend.core import exceptions @@ -515,7 +516,7 @@ class Params(object): raise exceptions.ProfileNotSetError( "The profile is needed to decrypt a password" ) - password = self.host.memory.decrypt_value(value, profile) + password = self.host.memory.decrypt_value_by_profile(value, profile) if password is None: raise exceptions.InternalError("password should never be None") diff --git a/libervia/backend/plugins/plugin_sec_otr.py b/libervia/backend/plugins/plugin_sec_otr.py index 6c0a7cf3..ca288636 100644 --- a/libervia/backend/plugins/plugin_sec_otr.py +++ b/libervia/backend/plugins/plugin_sec_otr.py @@ -248,7 +248,7 @@ class Account(potr.context.Account): if self.privkey is None: raise exceptions.InternalError(_("Save is called but privkey is None !")) priv_key = hexlify(self.privkey.serializePrivateKey()) - encrypted_priv_key = self.host.memory.encrypt_value(priv_key, self.client.profile) + encrypted_priv_key = self.host.memory.encrypt_value(self.client, priv_key) self.client._otr_data[PRIVATE_KEY] = encrypted_priv_key def load_trusts(self): @@ -373,7 +373,7 @@ class OTR(object): yield client._otr_data.load() encrypted_priv_key = client._otr_data.get(PRIVATE_KEY, None) if encrypted_priv_key is not None: - priv_key = self.host.memory.decrypt_value(encrypted_priv_key, client.profile) + priv_key = self.host.memory.decrypt_value(client, encrypted_priv_key) ctxMng.account.privkey = potr.crypt.PK.parsePrivateKey( unhexlify(priv_key.encode("utf-8")) )[0]