-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathblowfish_analyzer.py
More file actions
206 lines (155 loc) · 7.75 KB
/
blowfish_analyzer.py
File metadata and controls
206 lines (155 loc) · 7.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
from src.crypto_analyzer import CryptoAnalyzer
from src.utils import calculer_entropie
import hashlib
import base64
import re
from cryptography.hazmat.primitives.ciphers import Cipher, modes
from cryptography.hazmat.primitives.padding import PKCS7
from cryptography.hazmat.decrepit.ciphers.algorithms import Blowfish
class Blowfish_Analyzer(CryptoAnalyzer):
'''Détermine si l'algo blowfish 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.
Cette classe a trois méthodes:
- identifier_algo: Détermine si l'algo de chiffrement utilsé sur le fichier chiffré qui lui est passé en paramètre est blowfish.
- generer_cles_candidates: Génère une liste de clés candidates pour le déchiffrement du fichier chiffré
- dechiffrer: fait le déchiffrement proprement dit sur la base de la liste des clés générées
Attributes:
__BLOWFISH_TAILLE_BLOC : taille à considérer pour les blocs de données chiffrés que le PKCS7 doit prendre en compte (8 bits)
__BLOWFISH_TAILLE_IV : taille du vecteur d'initialisation en début de fichier (8 bits)
'''
__BLOWFISH_TAILLE_BLOC = 64
__BLOWFISH_TAILLE_IV = 8
def identifier_algo(self, chemin_fichier_chiffre: str) -> float:
'''
Détermine la probabilité que l'algo de chiffrement utilisé soit blowfish en:
- vérifiant la présence d'un IV à l'en-tête (taille fichier > 8 octets) et que la taille du fichier est un multiple de 8 (blocs de 8 octets pour l'algo blowfish)
- calculant l'entropie des données chiffrées
- calculant l'entropie des sous blocs
Args:
chemin_fichier_chiffre(str): Le chemin du fichier chiffré à traiter (mission1.enc).
Returns:
float: probabilité calculée.
'''
score = 0.0
try:
with open(chemin_fichier_chiffre, "rb") as f:
contenu_fichier: bytes = f.read()
taille_totale = len(contenu_fichier)
TAILLE_IV = 8
# Heuristique 1 : Vérification de la taille (le critère le plus important)
if taille_totale > TAILLE_IV and taille_totale % 8 == 0:
score += 0.4
donnees_chiffrees = contenu_fichier[TAILLE_IV:]
# Heuristique 2 : Vérification de l'entropie globale
entropie_globale = calculer_entropie(donnees_chiffrees)
if entropie_globale > 7.5:
score += 0.3
# Heuristique 3 : Vérification du "pattern Blowfish" (entropie par sous-blocs)
taille_donnees = len(donnees_chiffrees)
moitie = taille_donnees // 2
entropie_moitie1 = calculer_entropie(donnees_chiffrees[:moitie])
entropie_moitie2 = calculer_entropie(donnees_chiffrees[moitie:])
if entropie_moitie1 > 7.5 and entropie_moitie2 > 7.5:
score += 0.3
except FileNotFoundError:
return 0.0
return score
def __filtrer_dictionnaire_par_indice(self, chemin_dictionnaire: str) -> list[str]:
"""
Filtre le dictionnaire en se basant sur les indices de la mission 3.
L'indice pointe vers un format de clé "sha + nombre + chiffres simples".
Args:
chemin_dictionnaire(str): Le chemin vers le fichier de dictionnaire.
Returns:
list[str]: Une liste de mots de passe filtrés.
"""
mots_filtres: list[str] = []
# Indices pour le préfixe et le suffixe
prefixes = ("sha256", "sha384", "sha512", "sha1")
suffixes = ("123", "456", "789")
try:
with open(chemin_dictionnaire, "r", encoding="utf-8") as f:
for ligne in f:
mot = ligne.strip()
# Vérifie si le mot commence par un préfixe et se termine par un suffixe
if mot.startswith(prefixes) and mot.endswith(suffixes):
mots_filtres.append(mot)
except FileNotFoundError:
print(f"Erreur : Le fichier de dictionnaire '{chemin_dictionnaire}' est introuvable.")
return []
return mots_filtres
def generer_cles_candidates(self, chemin_dictionnaire: str) -> list[bytes]:
"""
Génère une liste de clés candidates pour le déchiffrement.
Les candidats incluent les mots de passe directs, leur hash MD5 et leur hash SHA1.
Args:
chemin_dictionnaire(str): Le chemin vers le fichier de dictionnaire.
Returns:
list[bytes]: Une liste des clés candidates sous forme d'octets.
"""
cles_candidates: list[bytes] = []
# Utilisation de la méthode privée pour filtrer les mots
mots_de_passe_cible = self.__filtrer_dictionnaire_par_indice(chemin_dictionnaire)
for mot in mots_de_passe_cible:
mot_en_bytes = mot.encode("utf-8")
# 1. Ajouter le mot de passe direct comme clé candidate
cles_candidates.append(mot_en_bytes)
# 2. Hachage MD5 et ajout à la liste (en bytes)
hash_md5 = hashlib.md5(mot_en_bytes).digest()
cles_candidates.append(hash_md5)
# 3. Hachage SHA1 et ajout à la liste (en bytes)
hash_sha1 = hashlib.sha1(mot_en_bytes).digest()
cles_candidates.append(hash_sha1)
return cles_candidates
def decode_base64(self, encoded_bytes, altchars=b'+/'):
encoded_bytes = re.sub(
rb'[^a-zA-Z0-9%s]+' %
altchars, b'', encoded_bytes)
missing_padding_length = len(encoded_bytes) % 4
if missing_padding_length:
encoded_bytes += b'=' * (4 - missing_padding_length)
return base64.b64decode(encoded_bytes, altchars)
def dechiffrer(self, chemin_fichier_chiffre: str, cle_donnee: bytes) -> bytes:
"""
Déchiffre le fichier supposé crypté par l'algorithme blowfish avec la clé donnée en respectant les critères de
- récupération de l'IV
- suppression de padding
Args:
chemin_fichier_chiffre (str): le chemin vers le fichier chiffré
clee_donnee (bytes): La clé à utiliser pour le déchiffrement
Returns:
bytes: les données originales
"""
#La taille de clé est dans l'intervalle 32-448bits et est multiple de 8
print(cle_donnee)
if len(cle_donnee) not in range(4, 55, 8):
print(len(cle_donnee))
raise ValueError('Taille de clé invalide.')
try:
algorithm_blowfish = Blowfish(self.decode_base64(cle_donnee))
texte_chiffre = ''
#Récupération de l'IV et du texte chiffré dans le fichier
with open(chemin_fichier_chiffre, 'rb') as f:
donnees = f.read()
f.close()
initialization_vector = donnees[:self.__BLOWFISH_TAILLE_IV]
texte_chiffre = donnees[self.__BLOWFISH_TAILLE_IV:]
#Initialisation du cipher
cipher = Cipher(algorithm_blowfish, modes.CBC(initialization_vector))
decrypteur = cipher.decryptor()
#Suppresseur de padding
supresseur_padding = PKCS7(self.__BLOWFISH_TAILLE_BLOC).unpadder()
#Décriptage des données avec le padding(remplissage aléatoire)
donnees_chiffrees_avec_padding = decrypteur.update(texte_chiffre) + decrypteur.finalize()
#Suppression du padding et récupération de la donnée finale
donnees_originales = supresseur_padding.update(donnees_chiffrees_avec_padding) + supresseur_padding.finalize()
return donnees_originales
except FileNotFoundError:
raise
# if __name__ == "__main__":
# try:
# resultat_dechiffrement: bytes = Blowfish_Analyzer().dechiffrer("CryptoForensic-Python/data/mission3.enc", os.urandom(32))
# print(f"Résultat du déchiffrement : {resultat_dechiffrement.decode('utf-8')}")
# except ValueError as ve:
# print(ve)
# except FileNotFoundError:
# print("Erreur: Le fichier 'mission2.enc' est introuvable.")