Skip to content

Commit 297d5f3

Browse files
mouwaficbdre-mandy
andauthored
Feature/tests unitaires (#16)
* Documentation de la fonction d'entropie * Essai de fusion (1/2) * Récupération du main (1/2) * Mise en place des tests unitaires (1/4) * Revert "Mise en place des tests unitaires (1/4)" This reverts commit e965801. * Mise en place des tests unitaires (1/4) * Mise en place des tests liés à l'analyzer aes cbc * Corrections de typage et de logique * Correction du comportement à la levée de l'exception * Correction de la lgoque de test de test_exception_déchiffrer * Corection de la logique de test_verification_texte_dechiffre --------- Co-authored-by: e-mandy <andymfrd02@gmail.com>
1 parent 8c0bd17 commit 297d5f3

5 files changed

Lines changed: 137 additions & 70 deletions

File tree

src/analyzers/aes_cbc_analyzer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def generer_cles_candidates(self, chemin_dictionnaire: str) -> list[bytes]:
9999
chemin_dictionnaire(str): le chemin du dictionnaire de mots de passes pour l'attaque par dictionnaire.
100100
101101
Returns:
102-
list[bytes]: liste des clés candidates.
102+
list[bytes]: liste des clés candidates.
103103
'''
104104

105105
mots_de_passe_cible = self.filtrer_dictionnaire_par_indices(chemin_dictionnaire)
@@ -155,4 +155,4 @@ def dechiffrer(self, chemin_fichier_chiffre: str, cle_donnee: bytes) -> bytes:
155155
return b""
156156

157157
except FileNotFoundError:
158-
return b""
158+
raise

src/detecteur_crypto.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,4 @@ class DetecteurCryptoOrchestrateur:
1212
Initialisation de l'analyseur AES-CBC
1313
"""
1414
def __init__(self):
15-
self.aes_cbc_analyzer = AesCbcAnalyzer()
16-
17-
15+
self.aes_cbc_analyzer = AesCbcAnalyzer()

src/utils.py

Lines changed: 64 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,39 @@
11
import math
22
import string
33
import sys
4-
import os
4+
from typing import Any, Dict, List, TypedDict
5+
6+
class StatsDict(TypedDict):
7+
imprimable: float
8+
nombre_mots: int
9+
p_mots_valide: float
10+
non_mots: List[str]
11+
ponctuation_valide: int
512

613
def calculer_entropie(bytes: bytes) -> float:
14+
'''
15+
Calcul l'entropie (le désordre dans une suite de données) afin de déterminer le degré d'improbabilité d'une chaine de données.
16+
17+
Args:
18+
bytes(bytes): La donnée brute contenue dans le fichier crypté.
19+
20+
Returns:
21+
float: l'entropie calculée.
22+
'''
723
entropie = 0
824
proba_byte = 0
925
for specifique_byte in bytes:
1026
i = 1
1127
for chaque_byte in bytes:
12-
if(chaque_byte == specifique_byte):
13-
i += 1
28+
if(chaque_byte == specifique_byte):
29+
i += 1
30+
1431
proba_byte = 1 / i
1532
entropie += (proba_byte) * math.log(1/proba_byte, 8)
1633
return entropie
1734

1835

36+
1937
def est_dechiffre(texte:str) -> bool:
2038
"""
2139
Détermine si oui ou non une chaine a été déchiffrée
@@ -41,11 +59,10 @@ def est_dechiffre(texte:str) -> bool:
4159
pourcent += 20
4260

4361
return True if pourcent > 70 else False
44-
45-
46-
4762

48-
def verifier_texte_dechiffre(texte: str) -> dict[int, int, int, list, int]:
63+
64+
65+
def verifier_texte_dechiffre(texte: str) -> Dict[str, Any]:
4966
"""
5067
Verifie que le dechiffrement d'un message a bien été effectué sur la base de certains critères.
5168
@@ -71,51 +88,51 @@ def verifier_texte_dechiffre(texte: str) -> dict[int, int, int, list, int]:
7188
'ponctuation_valide':0
7289
}
7390

91+
if not texte:
92+
return stats
93+
7494
#Verifier le pourcentage de caractères imprimables.
75-
76-
for lettre in texte:
77-
if lettre.isprintable():
78-
stats['imprimable']+= 100/len(texte)
79-
95+
stats['imprimable'] = int(sum(1 for char in texte if char.isprintable()) / len(texte) * 100)
96+
8097
# Traitement du texte brut pour obtenir une séquence distincte de pseudo-mot à cette étape séparé par des espaces
8198

82-
tab='./:!\\}{_%*$£&#;,~"()[]=§|`^@'
99+
tab='./:!\\}{_%*$£&#;,~"()[]=§|`^@?'
83100
copy=texte
84101
for lettre in tab:
85102
copy=copy.replace(lettre, ' ')
86-
copy=copy.strip().split(' ')
87-
stats['nombre_mots']=len(copy)
103+
mots = [mot for mot in copy.strip().split(' ') if mot]
104+
stats['nombre_mots']=len(mots)
88105

89106
# Verifier que le chaque mot du texte est un mot anglais/francais
90107

91108
try:
92-
for mot in copy:
109+
mots_valides = 0
110+
for mot in mots:
93111
trouve=False
94-
if mot == '': continue
112+
if not mot: continue
113+
114+
first_char = mot[0].lower()
115+
95116
for syl in ['Fr', 'En']:
96-
chemin=f"{os.curdir}\\CryptoForensic-Python\\dico{syl}\\{mot[0]}.txt"
97-
98-
with open(chemin, 'r') as f:
99-
ligne=f.readline()
100-
ligne=ligne.removesuffix('\n')
101-
102-
while not trouve and ligne != "":
103-
104-
if ligne == mot:
105-
stats['p_mots_valide']+=100/len(copy)
106-
print(stats['p_mots_valide'], mot)
107-
trouve=True
108-
break
109-
110-
ligne=f.readline()
111-
ligne=ligne.removesuffix('\n')
112-
113-
f.close()
117+
chemin=f"dico{syl}/{first_char}.txt"
118+
try:
119+
with open(chemin, 'r', encoding='latin-1') as f:
120+
for ligne in f:
121+
if ligne.strip() == mot:
122+
mots_valides += 1
123+
trouve=True
124+
break
125+
except FileNotFoundError:
126+
continue
114127

115128
if trouve : break
116129

117130
if not trouve :
118131
stats['non_mots'].append(mot)
132+
if mots:
133+
stats['p_mots_valide'] = round((mots_valides / len(mots)) * 100, 2)
134+
else:
135+
stats['p_mots_valide'] = 0.0
119136

120137
except Exception:
121138
tb=sys.exception().__traceback__
@@ -125,24 +142,17 @@ def verifier_texte_dechiffre(texte: str) -> dict[int, int, int, list, int]:
125142
#Verifier la structure de ponctuation.
126143

127144
points='.?!;,'
128-
nbr_ponct=0
129-
for point in points :
130-
nbr_ponct+=texte.count(point)
131-
for point in points :
132-
partition= texte.partition(point)
133-
if partition[2].startswith(' ') :
134-
if (point in '?!.' and partition[2].lstrip()[0].isupper()) or (point in ';,' and partition[2].lstrip()[0].islower()):
135-
stats['ponctuation_valide']+=100/nbr_ponct
136-
137-
for key in stats:
138-
print(key)
139-
if isinstance(stats[key], float):
140-
stats[key]=round(stats[key], 2)
145+
count = 0
146+
for i, char in enumerate(texte):
147+
if char in points:
148+
if (i == len(texte) - 1) or (texte[i+1] == ' '):
149+
count += 1
150+
stats['ponctuation_valide'] = count
141151

142152
return stats
143153

144154

145-
def rangerDico():
155+
def rangerDico() -> None:
146156
"""
147157
Fonction utilitaire de rangement du dictionnaire anglais téléchargé
148158
Pour effectuer des tests
@@ -151,11 +161,13 @@ def rangerDico():
151161
compte = 0
152162
# Ouverture du grand dictionnaire.
153163
try :
154-
with open(f"{os.path.abspath(os.curdir)}\\words_alpha.txt",'r') as f:
164+
# Utilisation de Path pour un chemin portable
165+
words_path = Path.cwd() / "words_alpha.txt"
166+
with open(words_path,'r') as f:
155167
while i<26:
156168
# Définition du chemin vers le fichier de chaque mot en fonction de l'alphabet.
157-
chemin=f"{os.curdir}\\dicoEn\\{string.ascii_lowercase[i]}.txt"
158-
with open(chemin, 'a') as fichier:
169+
dico_path = Path.cwd() / "dicoEn" / f"{string.ascii_lowercase[i]}.txt"
170+
with open(dico_path, 'a') as fichier:
159171
#Ecriture dans le fichier.
160172
fichier.write(string.ascii_lowercase[i]+'\n')
161173
while 1 :
@@ -173,4 +185,3 @@ def rangerDico():
173185
print('Fichier non trouvé.')
174186
# rangerDico()
175187

176-
print(verifier_texte_dechiffre('neither#nor avec, ded_caractère a'))

tests/test_analyzers.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from unittest import TestCase, main
2+
import sys
3+
sys.path.append('.')
4+
sys.path.append('..')
5+
from src.analyzers.aes_cbc_analyzer import Aes_Cbc_Analyzer
6+
7+
class AnalyzersTester(TestCase):
8+
9+
"""
10+
Cette classe est principalement destinée à recueillir toutes les fonctions de test des analyseurs d'algorithme
11+
de chiffrement.
12+
"""
13+
14+
def setUp(self):
15+
self.chemin_fichier_chiffre = "data/mission1.enc"
16+
self.wordlist = "keys/wordlist.txt"
17+
self.analyser = Aes_Cbc_Analyzer()
18+
19+
20+
def test_aes_cbc_identifier_algo(self):
21+
self.assertAlmostEqual(self.analyser.identifier_algo(self.chemin_fichier_chiffre), 1)
22+
23+
def test_aes_cbc_filtrage_dict(self):
24+
self.assertIsInstance(self.analyser.filtrer_dictionnaire_par_indices(self.wordlist), list)
25+
26+
def test_generation_cles_candidate(self):
27+
self.assertIsInstance(self.analyser.generer_cles_candidates(self.wordlist), list)
28+
29+
def test_exception_dechiffrer(self):
30+
cles_candidates = self.analyser.generer_cles_candidates(self.wordlist)
31+
32+
if not cles_candidates:
33+
self.fail("La liste des clés candidates ne devrait pas être vide.")
34+
35+
premiere_cle = cles_candidates[0]
36+
37+
with self.assertRaises(FileNotFoundError):
38+
self.analyser.dechiffrer("no_file_dohi.txt", premiere_cle)
39+
40+
if __name__ == '__main__':
41+
main()

tests/test_global.py

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# import de la library pour les tests
22
from unittest import TestCase, main
3+
import sys
4+
sys.path.append('.')
5+
sys.path.append('..')
6+
from src.utils import verifier_texte_dechiffre, calculer_entropie
37
"""
48
Ici le TestCase pour le regroupement des Cas de figures de Tests et le main pour l'exécution automatique des tests définis dans la classe ci-dessous
59
@@ -11,23 +15,36 @@ def add(a,b):
1115

1216
class BetaTester(TestCase):
1317
#Définition de la méthode de test
18+
"""
19+
# La fonction doit être préfixé du mot test pour que le TestCase puisse le l'identifier en tant que méthode à tester (le snake_case ici devra être appliqué ici)
20+
21+
# En fonction du type de vérification que vous souhaitez effectué par rapport aux test les méthodes assert devront variés.
22+
ex : * assertEqual() pour vérifier l'égalité. Dans le cas utilisé cette fonction vérifie si le retour de la fonction add correspond à la valeur 10
23+
* assertIn() pour vérifier si une variable est dans une iterable
24+
* assertIsInstance() pour vérifier le type de retour d'une variable ou fonction etc... (description des méthodes à l'appui)
25+
26+
NB : Pour tester sa fonction chacun devra faire un import pour éviter la redondance.
27+
Chaque fonction à tester devra se retrouver dans la class BetaTester avec un nom clair et propre à sa fonctionnalité précédé du mot "test"
28+
29+
command : pyhton test_global.py [-v (-- verbose)] (verbose pour un test avec plus de précision)
30+
31+
"""
1432

1533
def test_addition(self):
1634
self.assertEqual(add(5,5),10)
1735

1836

19-
"""
20-
# La fonction doit être préfixé du mot test pour que le TestCase puisse le l'identifier en tant que méthode à tester (le snake_case ici devra être appliqué ici)
21-
22-
# En fonction du type de vérification que vous souhaitez effectué par rapport aux test les méthodes assert devront variés.
23-
ex : * assertEqual() pour vérifier l'égalité. Dans le cas utilisé cette fonction vérifie si le retour de la fonction add correspond à la valeur 10
24-
* assertIn() pour vérifier si une variable est dans une iterable
25-
* assertIsInstance() pour vérifier le type de retour d'une variable ou fonction etc... (description des méthodes à l'appui)
37+
def test_verification_texte_dechiffre(self):
38+
resultat = verifier_texte_dechiffre("je talk !a mamamia:?")
39+
self.assertAlmostEqual(resultat['imprimable'], 100.0)
40+
self.assertEqual(resultat['nombre_mots'], 4)
41+
self.assertAlmostEqual(resultat['p_mots_valide'], 75.0)
42+
self.assertEqual(resultat['non_mots'], ["mamamia"])
43+
self.assertEqual(resultat['ponctuation_valide'], 1)
2644

27-
NB : Pour tester sa fonction chacun devra faire un import pour éviter la redondance.
28-
Chaque fonction à tester devra se retrouver dans la class BetaTester avec un nom clair et propre à sa fonctionnalité précédé du mot "test"
45+
def test_calcul_entropie(self) -> None:
46+
self.assertGreater(calculer_entropie("aaaaaaaa"), 0)
2947

30-
command : pyhton test_global.py [-v (-- verbose)] (verbose pour un test avec plus de précision)
3148

32-
"""
33-
main()
49+
if __name__ == '__main__':
50+
main()

0 commit comments

Comments
 (0)