core (memory): Modernize methods signature/docstrings of encrypt_value and decrypt_value:

- Use `client` instead of `profile`
- Add `decrypt_value_by_profile` which is notably useful on client startup, when the
  encrypted password needs to be retrieved, and the `SatXMPPEntity` instance is not yet
  created.
- Add type hints/update docstrings

rel 477
This commit is contained in:
Goffi 2026-06-06 11:23:51 +02:00
commit ed462a661b
3 changed files with 48 additions and 33 deletions

View file

@ -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).

View file

@ -16,6 +16,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
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")

View file

@ -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]