v1.0 - Version stable: multi-PC, détection UI-DETR-1, 3 modes exécution
- Frontend v4 accessible sur réseau local (192.168.1.40) - Ports ouverts: 3002 (frontend), 5001 (backend), 5004 (dashboard) - Ollama GPU fonctionnel - Self-healing interactif - Dashboard confiance Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
124
server/storage_encrypted.py
Normal file
124
server/storage_encrypted.py
Normal file
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Module de déchiffrement pour le serveur API.
|
||||
|
||||
Copie des fonctions de déchiffrement depuis agent_v0/storage_encrypted.py
|
||||
sans les dépendances sur config et raw_session.
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
||||
|
||||
logger = logging.getLogger("server.storage_encrypted")
|
||||
|
||||
|
||||
def decrypt_session_file(
|
||||
encrypted_path: str,
|
||||
password: str,
|
||||
output_path: str | None = None
|
||||
) -> str:
|
||||
"""
|
||||
Déchiffre un fichier .enc et restaure le ZIP original.
|
||||
|
||||
Args:
|
||||
encrypted_path: Chemin du fichier .enc
|
||||
password: Mot de passe de déchiffrement
|
||||
output_path: Chemin de sortie (défaut: même nom avec .zip)
|
||||
|
||||
Returns:
|
||||
Chemin du ZIP déchiffré
|
||||
|
||||
Note: Version serveur sans dépendances agent_v0
|
||||
"""
|
||||
if output_path is None:
|
||||
output_path = encrypted_path.replace('.enc', '.zip')
|
||||
|
||||
try:
|
||||
# Lire le fichier chiffré
|
||||
with open(encrypted_path, 'rb') as f:
|
||||
salt = f.read(16)
|
||||
iv = f.read(16)
|
||||
ciphertext = f.read()
|
||||
|
||||
if len(salt) != 16 or len(iv) != 16:
|
||||
raise ValueError("Fichier chiffré corrompu: salt ou IV manquant")
|
||||
|
||||
# Dériver la clé depuis le password
|
||||
kdf = PBKDF2HMAC(
|
||||
algorithm=hashes.SHA256(),
|
||||
length=32,
|
||||
salt=salt,
|
||||
iterations=100000,
|
||||
backend=default_backend()
|
||||
)
|
||||
key = kdf.derive(password.encode('utf-8'))
|
||||
|
||||
# Créer cipher pour déchiffrement
|
||||
cipher = Cipher(
|
||||
algorithms.AES(key),
|
||||
modes.CBC(iv),
|
||||
backend=default_backend()
|
||||
)
|
||||
decryptor = cipher.decryptor()
|
||||
|
||||
# Déchiffrer
|
||||
plaintext = decryptor.update(ciphertext) + decryptor.finalize()
|
||||
|
||||
# Retirer padding PKCS7
|
||||
if len(plaintext) == 0:
|
||||
raise ValueError("Données déchiffrées vides")
|
||||
|
||||
padding_length = plaintext[-1]
|
||||
if padding_length > 16 or padding_length == 0:
|
||||
raise ValueError(f"Padding invalide: {padding_length}")
|
||||
|
||||
# Vérifier que tous les bytes de padding sont identiques
|
||||
for i in range(padding_length):
|
||||
if plaintext[-(i+1)] != padding_length:
|
||||
raise ValueError("Padding PKCS7 invalide")
|
||||
|
||||
plaintext = plaintext[:-padding_length]
|
||||
|
||||
# Écrire le ZIP déchiffré
|
||||
with open(output_path, 'wb') as f:
|
||||
f.write(plaintext)
|
||||
|
||||
logger.info(f"Fichier déchiffré: {output_path}")
|
||||
return os.path.abspath(output_path)
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"Erreur lors du déchiffrement: {e}")
|
||||
raise
|
||||
|
||||
|
||||
def test_decryption_password(password: str) -> bool:
|
||||
"""
|
||||
Test rapide pour vérifier qu'un mot de passe de déchiffrement fonctionne.
|
||||
|
||||
Args:
|
||||
password: Le mot de passe à tester
|
||||
|
||||
Returns:
|
||||
True si le mot de passe semble valide
|
||||
"""
|
||||
if not password:
|
||||
return False
|
||||
|
||||
# Test basique: le mot de passe doit être une chaîne non vide
|
||||
if len(password.strip()) == 0:
|
||||
return False
|
||||
|
||||
# En production, on pourrait faire un test plus sophistiqué
|
||||
# avec un fichier de test connu
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Test rapide
|
||||
print("Module de déchiffrement serveur chargé")
|
||||
print(f"Test password 'test': {test_decryption_password('test')}")
|
||||
print(f"Test password '': {test_decryption_password('')}")
|
||||
Reference in New Issue
Block a user