1+ # Import des modules
2+ import hashlib
3+ from cryptography .hazmat .primitives .ciphers import Cipher , algorithms , modes
4+ from rich import print
5+ import os , struct
6+ import math
7+ import sys
8+ sys .path .append (os .path .join (os .path .dirname (__file__ ), '..' ))
9+ from crypto_analyzer import CryptoAnalyzer
10+ from utils import calculer_entropie
11+
12+ # Définition de la classe ChaCha20_Analyzer
13+ class ChaCha20_Analyzer (CryptoAnalyzer ):
14+ """
15+ Détermine si l'algo ChaCha20 est utilisé, génère des clés et tente de de déchffrer un fichier chiffré en utilisant les clés générées.
16+
17+ Cette classe a trois méthodes:
18+ - identifier_algo: Détermine si l'algo de chiffrement utilisé sur le fichier chiffré qui lui est passé en paramètre est l'ChaCha20.
19+ - generer_cles_candidates: Génère une liste de clés candidates pour le déchiffrement du fichier chiffré
20+ - dechiffrer: fait le déchiffrement proprement dit sur la base de la liste des clés générées
21+
22+ Attributes:
23+ _CHACHA20_LONGUEUR_CLE: la taille de la clé de chiffrement (32 octets)
24+ _CHACHA20_LONGUEUR_NONCE: la taille du vecteur d'initialisation (12 octets)
25+ _CHACHA20_LONGUEUR_TAG: la taille de l'empreinte de chiffrement (16 octets)
26+ _CHACHA20_LONGUEUR_BLOC: la taille du bloc de chiffrement (64 bits)
27+ """
28+
29+ _CHACHA20_LONGUEUR_CLE = 32
30+ _CHACHA20_LONGUEUR_NONCE = 12 #fourni
31+ _CHACHA20_LONGUEUR_TAG = 16
32+ _CHACHA20_LONGUEUR_BLOC = 64
33+
34+
35+
36+ def identifier_algo (self , chemin_fichier_chiffre : str ) -> float :
37+ """
38+ Détermine la probabilité que l'algo de chiffrement utilisé soit l'ChaCha20 en:
39+ - vérifiant la présence d'un nonce de 12 bytes en début de fichier
40+ - vérifiant l'entropie très élevée sur l'ensemble des données
41+ - vérifiant l'absence de padding (pas de contrainte de taille)
42+ - vérifiant que la taille du fichier est suffisante pour contenir un nonce
43+
44+ Retourne une probabilité entre 0 et 1(Pour connaitre la probabilité que l'algo de chiffrement utilisé soit l'ChaCha20).
45+
46+ Args:
47+ chemin_fichier_chiffre(str): le chemin du fichier chiffré à traiter .
48+
49+ Returns:
50+ float: La probabilité que l'algo de chiffrement utilisé soit l'ChaCha20 apres le calcul.
51+ """
52+ try :
53+ with open (chemin_fichier_chiffre , 'rb' ) as f :
54+ donnees = f .read ()
55+
56+ if len (donnees ) < self ._CHACHA20_LONGUEUR_NONCE :
57+ return 0.0 # Fichier trop petit pour contenir un nonce
58+
59+ # Extraire le nonce présumé (12 premiers bytes)
60+ nonce = donnees [:self ._CHACHA20_LONGUEUR_NONCE ]
61+ donnees_chiffrees = donnees [self ._CHACHA20_LONGUEUR_NONCE :]
62+
63+ if len (donnees_chiffrees ) == 0 :
64+ return 0.0 # Pas de données chiffrées
65+
66+ # Critère 1: Vérifier la taille minimale
67+ if len (donnees ) >= self ._CHACHA20_LONGUEUR_NONCE + 16 :
68+ taille_min = 1.0
69+ else :
70+ taille_min = 0.0
71+
72+ # Critère 2: Vérifier l'entropie des données chiffrées (doit être très élevée)
73+ entropie = calculer_entropie (donnees_chiffrees )
74+ # L'entropie d'un chiffrement ChaCha20 devrait être proche de 8 bits/octet
75+ if entropie / 8.0 > 1.0 :
76+ entropie_max = 1.0
77+ else :
78+ entropie_max = entropie / 8.0
79+
80+ # Critère 3: Vérifier l'absence de padding (pas de contrainte de taille)
81+ # ChaCha20 est un chiffrement de flux, donc pas de padding
82+ # On vérifie que la taille des données chiffrées n'est pas un multiple d'une taille de bloc commune
83+ taille_donnees = len (donnees_chiffrees )
84+ if taille_donnees % 16 == 0 or taille_donnees % 8 == 0 :
85+ padding_max = 0.5
86+ else :
87+ padding_max = 1.0
88+
89+ # Critère 4: Vérifier l'entropie du nonce (doit être élevée aussi)
90+ entropie_nonce = calculer_entropie (nonce )
91+ if entropie_nonce / 8.0 > 1.0 :
92+ nonce_max = 1.0
93+ else :
94+ nonce_max = entropie_nonce / 8.0
95+
96+ # Calcul de la probabilité finale (moyenne pondérée des scores)
97+ probabilite = (taille_min * 0.1 +
98+ entropie_max * 0.4 +
99+ padding_max * 0.3 +
100+ nonce_max * 0.2 )
101+
102+ return min (probabilite , 1.0 )
103+
104+ except Exception as e :
105+ print (f"Erreur lors de l'identification de l'algorithme: { e } " )
106+ return 0.0
107+
108+
109+ def filtrer_dictionnaire_par_indices (self , chemin_fichier_chiffre ):
110+ pass
111+
112+ def generer_cles_candidates (self , chemin_fichier_chiffre ):
113+
114+ '''
115+ Cette fonction se charge de générer les clés candidates pour le déchiffremment du fichier chiffré en utilisant
116+ la dérivation sha256 pour renforcer les clées de chiffrement.
117+
118+
119+ Args:
120+ chemin_fichier_chiffre(str) : Le chemin vers le fichier chiffré
121+
122+ Returns:
123+ cles_candidates (list[bytes]) : Un tableau de clés, chaque clé étant une séquence d'octets
124+ '''
125+
126+ donnees_fichier_filtre = self .filtrer_dictionnaire_par_indices (chemin_fichier_chiffre )
127+
128+ cle_candidates : list [bytes ] = []
129+ for cle in donnees_fichier_filtre :
130+ cle_candidates .append (hashlib .sha256 (cle ).digest ())
131+
132+ return cle_candidates
133+
134+ def dechiffrer (self , chemin_fichier_chiffre : str , cle_donnee : bytes ) -> bytes :
135+ if len (cle_donnee ) != 32 :
136+ raise ValueError ("Erreur : La clé n'a pas la taille correcte" )
137+ else :
138+ try :
139+ # Utiliser le chemin complet si c'est un chemin absolu, sinon ajouter le préfixe data/
140+ if os .path .isabs (chemin_fichier_chiffre ):
141+ fichier_path = chemin_fichier_chiffre
142+ else :
143+ fichier_path = f"data/{ chemin_fichier_chiffre } "
144+
145+ with open (fichier_path , 'rb' ) as f :
146+ nonce = f .read (self ._CHACHA20_LONGUEUR_NONCE )
147+ texte_chiffre = f .read ()
148+
149+ algorithm_chacha20 = algorithms .ChaCha20 (cle_donnee , nonce )
150+ cipher = Cipher (algorithm_chacha20 , mode = None )
151+ decrypteur = cipher .decryptor ()
152+ resultat = decrypteur .update (texte_chiffre )
153+
154+ # Retourner les bytes bruts comme attendu par l'interface
155+ return resultat
156+
157+ except Exception as e :
158+ print (f"Une erreur est survenue : { e } " )
159+ return b""
160+ cle_candidates .append (hashlib .sha256 (cle ).encode (encoding = "utf-8" ))
161+
162+ return cle_candidates
163+
164+ def dechiffrer (self ,chemin_fichier_chiffer : str ,clef :bytes )-> str :
165+ if len (clef ) != 32 : return ValueError ("Erreur : La clé a pas la taille correcte " )
166+ else :
167+ try :
168+ with open (f"data/{ chemin_fichier_chiffer } " ,'rb' ) as f :
169+ nonce = f .read (16 )
170+ texte_chiffrer = f .read ()
171+
172+ counter = 0
173+ algorithm_chacha20 = algorithms .ChaCha20 (clef ,nonce )
174+ cipher = Cipher (algorithm_chacha20 ,mode = None )
175+ decrypteur = cipher .decryptor ()
176+ return decrypteur .update (texte_chiffrer )
177+
178+
179+
180+ except Exception as e :
181+ print (f"Une erreur est survenu : { e } " )
182+
183+
184+
185+ # print(ChaCha20_Analyzer().dechiffrer("mission2.enc",os.urandom(32)))
0 commit comments