From 8d768bf9d5cc3df9bdc97f6c99e524b6811baca7 Mon Sep 17 00:00:00 2001 From: bruno Date: Tue, 27 Oct 2026 23:10:24 -0400 Subject: [PATCH] [fix] mypy Coffre + bump 2.80.2 : backend/coffre_crypto.py --- backend/coffre_crypto.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/backend/coffre_crypto.py b/backend/coffre_crypto.py index ff92dde..897c5dd 100644 --- a/backend/coffre_crypto.py +++ b/backend/coffre_crypto.py @@ -24,14 +24,19 @@ import logging import os import stat from pathlib import Path +from typing import Any logger = logging.getLogger("obsigate.coffre_crypto") +# Annotation explicite : sans elle, mypy refuse l'assignation de deux types +# (la classe AESGCM importée, puis None dans le repli sans cryptography) +# et le commentaire `type: ignore[assignment]` ne couvre pas ce code [misc]. +AESGCM: type[Any] | None try: # cryptography est une dépendance de la Phase 1a (requirements.txt) from cryptography.exceptions import InvalidTag from cryptography.hazmat.primitives.ciphers.aead import AESGCM except Exception: # pragma: no cover - environnement sans cryptography - AESGCM = None # type: ignore[assignment] + AESGCM = None InvalidTag = Exception # type: ignore[assignment,misc] @@ -55,11 +60,17 @@ class CryptoIndisponibleError(CoffreCryptoError): """La bibliothèque ``cryptography`` n'est pas installée.""" -def _require_aesgcm() -> None: +def _require_aesgcm() -> type[Any]: + """Retourne la classe AESGCM, ou lève si cryptography est absent. + + Retourner la classe (plutôt qu'un simple garde) permet à mypy de + savoir que l'appelant détient bien un constructible, et non None. + """ if AESGCM is None: raise CryptoIndisponibleError( "cryptography n'est pas installé (dépendance Phase 1a)" ) + return AESGCM def parse_kek(raw: str) -> bytes: @@ -146,17 +157,17 @@ def _unb64(value: str) -> bytes: def encrypt_with_key(key: bytes, plaintext: bytes, aad: bytes) -> dict: """Chiffre avec AES-256-GCM. Retourne {nonce, ciphertext} en base64.""" - _require_aesgcm() + aesgcm = _require_aesgcm() nonce = os.urandom(12) - ciphertext = AESGCM(key).encrypt(nonce, plaintext, aad) + ciphertext = aesgcm(key).encrypt(nonce, plaintext, aad) return {"nonce": _b64(nonce), "ciphertext": _b64(ciphertext)} def decrypt_with_key(key: bytes, nonce_b64: str, ciphertext_b64: str, aad: bytes) -> bytes: """Déchiffre avec AES-256-GCM. Tag invalide -> TagInvalideError.""" - _require_aesgcm() + aesgcm = _require_aesgcm() try: - return AESGCM(key).decrypt(_unb64(nonce_b64), _unb64(ciphertext_b64), aad) + return aesgcm(key).decrypt(_unb64(nonce_b64), _unb64(ciphertext_b64), aad) except InvalidTag as exc: raise TagInvalideError("Tag GCM invalide : données altérées ou mauvaise clé") from exc except (binascii.Error, ValueError) as exc: