Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/analyzers/aes_cbc_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def generer_cles_candidates(self, chemin_dictionnaire: str) -> list[bytes]:
chemin_dictionnaire(str): le chemin du dictionnaire de mots de passes pour l'attaque par dictionnaire.

Returns:
list[bytes]: liste des clés candidates.
list[bytes]: liste des clés candidates.
'''

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

except FileNotFoundError:
return b""
raise
4 changes: 1 addition & 3 deletions src/detecteur_crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,4 @@ class DetecteurCryptoOrchestrateur:
Initialisation de l'analyseur AES-CBC
"""
def __init__(self):
self.aes_cbc_analyzer = AesCbcAnalyzer()


self.aes_cbc_analyzer = AesCbcAnalyzer()
117 changes: 64 additions & 53 deletions src/utils.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,39 @@
import math
import string
import sys
import os
from typing import Any, Dict, List, TypedDict

class StatsDict(TypedDict):
imprimable: float
nombre_mots: int
p_mots_valide: float
non_mots: List[str]
ponctuation_valide: int

def calculer_entropie(bytes: bytes) -> float:
'''
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.

Args:
bytes(bytes): La donnée brute contenue dans le fichier crypté.

Returns:
float: l'entropie calculée.
'''
entropie = 0
proba_byte = 0
for specifique_byte in bytes:
i = 1
for chaque_byte in bytes:
if(chaque_byte == specifique_byte):
i += 1
if(chaque_byte == specifique_byte):
i += 1

proba_byte = 1 / i
entropie += (proba_byte) * math.log(1/proba_byte, 8)
return entropie



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

return True if pourcent > 70 else False




def verifier_texte_dechiffre(texte: str) -> dict[int, int, int, list, int]:


def verifier_texte_dechiffre(texte: str) -> Dict[str, Any]:
"""
Verifie que le dechiffrement d'un message a bien été effectué sur la base de certains critères.

Expand All @@ -71,51 +88,51 @@ def verifier_texte_dechiffre(texte: str) -> dict[int, int, int, list, int]:
'ponctuation_valide':0
}

if not texte:
return stats

#Verifier le pourcentage de caractères imprimables.

for lettre in texte:
if lettre.isprintable():
stats['imprimable']+= 100/len(texte)

stats['imprimable'] = int(sum(1 for char in texte if char.isprintable()) / len(texte) * 100)

# Traitement du texte brut pour obtenir une séquence distincte de pseudo-mot à cette étape séparé par des espaces

tab='./:!\\}{_%*$£&#;,~"()[]=§|`^@'
tab='./:!\\}{_%*$£&#;,~"()[]=§|`^@?'
copy=texte
for lettre in tab:
copy=copy.replace(lettre, ' ')
copy=copy.strip().split(' ')
stats['nombre_mots']=len(copy)
mots = [mot for mot in copy.strip().split(' ') if mot]
stats['nombre_mots']=len(mots)

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

try:
for mot in copy:
mots_valides = 0
for mot in mots:
trouve=False
if mot == '': continue
if not mot: continue

first_char = mot[0].lower()

for syl in ['Fr', 'En']:
chemin=f"{os.curdir}\\CryptoForensic-Python\\dico{syl}\\{mot[0]}.txt"

with open(chemin, 'r') as f:
ligne=f.readline()
ligne=ligne.removesuffix('\n')

while not trouve and ligne != "":

if ligne == mot:
stats['p_mots_valide']+=100/len(copy)
print(stats['p_mots_valide'], mot)
trouve=True
break

ligne=f.readline()
ligne=ligne.removesuffix('\n')

f.close()
chemin=f"dico{syl}/{first_char}.txt"
try:
with open(chemin, 'r', encoding='latin-1') as f:
for ligne in f:
if ligne.strip() == mot:
mots_valides += 1
trouve=True
break
except FileNotFoundError:
continue

if trouve : break

if not trouve :
stats['non_mots'].append(mot)
if mots:
stats['p_mots_valide'] = round((mots_valides / len(mots)) * 100, 2)
else:
stats['p_mots_valide'] = 0.0

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

points='.?!;,'
nbr_ponct=0
for point in points :
nbr_ponct+=texte.count(point)
for point in points :
partition= texte.partition(point)
if partition[2].startswith(' ') :
if (point in '?!.' and partition[2].lstrip()[0].isupper()) or (point in ';,' and partition[2].lstrip()[0].islower()):
stats['ponctuation_valide']+=100/nbr_ponct

for key in stats:
print(key)
if isinstance(stats[key], float):
stats[key]=round(stats[key], 2)
count = 0
for i, char in enumerate(texte):
if char in points:
if (i == len(texte) - 1) or (texte[i+1] == ' '):
count += 1
stats['ponctuation_valide'] = count

return stats


def rangerDico():
def rangerDico() -> None:
"""
Fonction utilitaire de rangement du dictionnaire anglais téléchargé
Pour effectuer des tests
Expand All @@ -151,11 +161,13 @@ def rangerDico():
compte = 0
# Ouverture du grand dictionnaire.
try :
with open(f"{os.path.abspath(os.curdir)}\\words_alpha.txt",'r') as f:
# Utilisation de Path pour un chemin portable
words_path = Path.cwd() / "words_alpha.txt"
with open(words_path,'r') as f:
while i<26:
# Définition du chemin vers le fichier de chaque mot en fonction de l'alphabet.
chemin=f"{os.curdir}\\dicoEn\\{string.ascii_lowercase[i]}.txt"
with open(chemin, 'a') as fichier:
dico_path = Path.cwd() / "dicoEn" / f"{string.ascii_lowercase[i]}.txt"
with open(dico_path, 'a') as fichier:
#Ecriture dans le fichier.
fichier.write(string.ascii_lowercase[i]+'\n')
while 1 :
Expand All @@ -173,4 +185,3 @@ def rangerDico():
print('Fichier non trouvé.')
# rangerDico()

print(verifier_texte_dechiffre('neither#nor avec, ded_caractère a'))
41 changes: 41 additions & 0 deletions tests/test_analyzers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from unittest import TestCase, main
import sys
sys.path.append('.')
sys.path.append('..')
from src.analyzers.aes_cbc_analyzer import Aes_Cbc_Analyzer

class AnalyzersTester(TestCase):

"""
Cette classe est principalement destinée à recueillir toutes les fonctions de test des analyseurs d'algorithme
de chiffrement.
"""

def setUp(self):
self.chemin_fichier_chiffre = "data/mission1.enc"
self.wordlist = "keys/wordlist.txt"
self.analyser = Aes_Cbc_Analyzer()


def test_aes_cbc_identifier_algo(self):
self.assertAlmostEqual(self.analyser.identifier_algo(self.chemin_fichier_chiffre), 1)

def test_aes_cbc_filtrage_dict(self):
self.assertIsInstance(self.analyser.filtrer_dictionnaire_par_indices(self.wordlist), list)

def test_generation_cles_candidate(self):
self.assertIsInstance(self.analyser.generer_cles_candidates(self.wordlist), list)

def test_exception_dechiffrer(self):
cles_candidates = self.analyser.generer_cles_candidates(self.wordlist)

if not cles_candidates:
self.fail("La liste des clés candidates ne devrait pas être vide.")

premiere_cle = cles_candidates[0]

with self.assertRaises(FileNotFoundError):
self.analyser.dechiffrer("no_file_dohi.txt", premiere_cle)

if __name__ == '__main__':
main()
41 changes: 29 additions & 12 deletions tests/test_global.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# import de la library pour les tests
from unittest import TestCase, main
import sys
sys.path.append('.')
sys.path.append('..')
from src.utils import verifier_texte_dechiffre, calculer_entropie
"""
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

Expand All @@ -11,23 +15,36 @@ def add(a,b):

class BetaTester(TestCase):
#Définition de la méthode de test
"""
# 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)

# En fonction du type de vérification que vous souhaitez effectué par rapport aux test les méthodes assert devront variés.
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
* assertIn() pour vérifier si une variable est dans une iterable
* assertIsInstance() pour vérifier le type de retour d'une variable ou fonction etc... (description des méthodes à l'appui)

NB : Pour tester sa fonction chacun devra faire un import pour éviter la redondance.
Chaque fonction à tester devra se retrouver dans la class BetaTester avec un nom clair et propre à sa fonctionnalité précédé du mot "test"

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

"""

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


"""
# 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)

# En fonction du type de vérification que vous souhaitez effectué par rapport aux test les méthodes assert devront variés.
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
* assertIn() pour vérifier si une variable est dans une iterable
* assertIsInstance() pour vérifier le type de retour d'une variable ou fonction etc... (description des méthodes à l'appui)
def test_verification_texte_dechiffre(self):
resultat = verifier_texte_dechiffre("je talk !a mamamia:?")
self.assertAlmostEqual(resultat['imprimable'], 100.0)
self.assertEqual(resultat['nombre_mots'], 4)
self.assertAlmostEqual(resultat['p_mots_valide'], 75.0)
self.assertEqual(resultat['non_mots'], ["mamamia"])
self.assertEqual(resultat['ponctuation_valide'], 1)

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

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

"""
main()
if __name__ == '__main__':
main()