- 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>
124 lines
3.7 KiB
Python
124 lines
3.7 KiB
Python
#!/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('')}") |