Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
f40bb59
Documentation de la fonction d'entropie
e-mandy Aug 2, 2025
0474003
Merge branch 'feature/calculerEntropieShannon' of https://github.com/…
e-mandy Aug 2, 2025
8f0dbfc
Essai de fusion (1/2)
e-mandy Aug 2, 2025
eadb52d
Fusion (2/2)
e-mandy Aug 3, 2025
2a008e2
Implementation de la fonction de validation du texte déchiffrer
seathiel-12 Aug 3, 2025
b638a49
Récupération du main (1/2)
e-mandy Aug 3, 2025
ecf4eef
Récupération du main (2/2)
e-mandy Aug 3, 2025
e965801
Mise en place des tests unitaires (1/4)
e-mandy Aug 3, 2025
e119f5d
Revert "Mise en place des tests unitaires (1/4)"
e-mandy Aug 3, 2025
14e759d
Mise en place des tests unitaires (1/4)
e-mandy Aug 3, 2025
01800d8
Définition de la classe de gestion des rapports de ission et implémen…
seathiel-12 Aug 3, 2025
e3f5efa
Implémentation de la fonction de recherche d'anciens rapports
seathiel-12 Aug 4, 2025
e593c82
Merge branch 'main' into feature/rapport_mission
mouwaficbdr Aug 4, 2025
3d6332f
Merge branch 'main' of https://github.com/mouwaficbdr/CryptoForensic-…
e-mandy Aug 4, 2025
52433f7
Mise en place des tests liés à l'analyzer aes cbc
e-mandy Aug 4, 2025
af85ee2
Corrections de typage et de logique
mouwaficbdr Aug 4, 2025
896696a
Correction du comportement à la levée de l'exception
mouwaficbdr Aug 4, 2025
3391403
Correction de la lgoque de test de test_exception_déchiffrer
mouwaficbdr Aug 4, 2025
9b38d27
Corection de la logique de test_verification_texte_dechiffre
mouwaficbdr Aug 4, 2025
275b47a
fix: Utilisation de pathlib pour une gestion portable des chemins.
mouwaficbdr Aug 4, 2025
d662e06
Merge branch 'main' into feature/testsUnitaires
mouwaficbdr Aug 4, 2025
ae4cfb0
merge réussi
seathiel-12 Aug 4, 2025
a2c05c5
Validation des tests et corrections du rapport mission
seathiel-12 Aug 4, 2025
7e52274
Validation des tests et corrections du rapport mission
seathiel-12 Aug 4, 2025
ec4191c
merged
seathiel-12 Aug 5, 2025
f67a4a5
Merge branch 'main' of https://github.com/mouwaficbdr/CryptoForensic-…
seathiel-12 Aug 6, 2025
31569fb
Merge branch 'main' of https://github.com/mouwaficbdr/CryptoForensic-…
seathiel-12 Aug 7, 2025
7c1e4b4
chekpoint: Blowfish _Analyzer.identifier_algo()
mouwaficbdr Aug 7, 2025
6bb7780
add: Intiialisation de Blowfish_Analyzer et implémentation de Blowfis…
mouwaficbdr Aug 7, 2025
2a6a8fd
Merge branch 'main' into add/Initialisation_Blowfish_Analyzer
mouwaficbdr Aug 7, 2025
defc99b
Merge branch 'main' of github.com:mouwaficbdr/CryptoForensic-Python i…
mouwaficbdr Aug 7, 2025
da7935e
Implémentation de Blowfish_Analyzer.filtrer_dictionnaire_par_indice()
mouwaficbdr Aug 7, 2025
5053932
Merge branch 'add/Initialisation_Blowfish_Analyzer' of github.com:mou…
mouwaficbdr Aug 7, 2025
746d059
Correction de bug
seathiel-12 Aug 7, 2025
d6b3859
merged
seathiel-12 Aug 7, 2025
4eba401
Implementer de Blowfish déchiffrer
seathiel-12 Aug 7, 2025
7368195
merge sur la branche courante
seathiel-12 Aug 7, 2025
778632b
Integration de blowfish dans le detecteur crypto
seathiel-12 Aug 7, 2025
e4e7a46
merged
seathiel-12 Aug 8, 2025
5ab3e40
merged
seathiel-12 Aug 9, 2025
b5f9fe1
Merge branch 'main' of https://github.com/mouwaficbdr/CryptoForensic-…
seathiel-12 Aug 9, 2025
97432a6
Gestion des bugs de chemin et d'import
seathiel-12 Aug 10, 2025
eb8edb5
Merge branch 'main' of https://github.com/mouwaficbdr/CryptoForensic-…
seathiel-12 Aug 10, 2025
2513ebd
tests aes_gcm
seathiel-12 Aug 10, 2025
45b3656
pull et merge sur les tests
seathiel-12 Aug 10, 2025
dffa15f
pull et merge du main
seathiel-12 Aug 10, 2025
46accc8
Merge branch 'main' into feature/testsUnitaires
mouwaficbdr Aug 10, 2025
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
16 changes: 15 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
from src.detecteur_crypto import DetecteurCryptoOrchestrateur
print(DetecteurCryptoOrchestrateur().analyser_fichier_specifique('data/mission1.enc'))
from src.analyzers.blowfish_analyzer import Blowfish_Analyzer
from src.analyzers.aes_cbc_analyzer import Aes_Cbc_Analyzer
from src.interface_console import consoleInterface
import os
# print(DetecteurCryptoOrchestrateur().analyser_fichier_specifique('data/mission1.enc'))

# try:
# resultat_dechiffrement: bytes = Blowfish_Analyzer().dechiffrer("data/mission3.enc", Blowfish_Analyzer().generer_cles_candidates('keys/wordlist.txt')[2])
# print(f"Résultat du déchiffrement : {resultat_dechiffrement.decode('utf-8')}")
# except ValueError as ve:
# print(ve)
# except FileNotFoundError:
# print("Erreur: Le fichier 'mission3.enc' est introuvable.")

consoleInterface()
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cffi==1.17.1
cryptography==45.0.5
cryptography==43.0.0
pycparser==2.22
pycryptodome==3.23.0
rich==14.1.0
Binary file added resultat.txt
Binary file not shown.
8 changes: 7 additions & 1 deletion src/analyzers/aes_gcm_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,10 @@ def generer_cles_candidates(self, chemin_dictionnaire: str) -> list[bytes]:
cle_derivee: bytes = kdf.derive(mot_de_passe_en_octets)
clees_candidates.append(cle_derivee)

return clees_candidates
return clees_candidates

def identifier_algo(self, chemin_fichier_chiffre):
return super().identifier_algo(chemin_fichier_chiffre)

def dechiffrer(self, chemin_fichier_chiffre, cle_donnee):
return super().dechiffrer(chemin_fichier_chiffre, cle_donnee)
42 changes: 34 additions & 8 deletions src/analyzers/blowfish_analyzer.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from src.crypto_analyzer import CryptoAnalyzer
from src.utils import calculer_entropie
import hashlib
from cryptography.hazmat.primitives.ciphers import algorithms, Cipher, modes
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.

Expand All @@ -17,7 +20,7 @@ class Blowfish_Analyzer(CryptoAnalyzer):

'''

__BLOWFISH_TAILLE_BLOC = 8
__BLOWFISH_TAILLE_BLOC = 64
__BLOWFISH_TAILLE_IV = 8

def identifier_algo(self, chemin_fichier_chiffre: str) -> float:
Expand Down Expand Up @@ -132,6 +135,18 @@ def generer_cles_candidates(self, chemin_dictionnaire: str) -> list[bytes]:

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
Expand All @@ -146,19 +161,22 @@ def dechiffrer(self, chemin_fichier_chiffre: str, cle_donnee: bytes) -> bytes:
"""

#La taille de clé est dans l'intervalle 32-448bits et est multiple de 8
if len(cle_donnee) not in range(32, 448, 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 = algorithms.Blowfish(cle_donnee)

algorithm_blowfish = Blowfish(self.decode_base64(cle_donnee))
texte_chiffre = ''

#Récupération de l'IV et des texte chiffré das le fichier
#Récupération de l'IV et du texte chiffré dans le fichier
with open(chemin_fichier_chiffre, 'rb') as f:
initialization_vector = f.read(self.__BLOWFISH_TAILLE_IV)
texte_chiffre = f.read()
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))
Expand All @@ -178,3 +196,11 @@ def dechiffrer(self, chemin_fichier_chiffre: str, cle_donnee: bytes) -> bytes:
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.")
8 changes: 6 additions & 2 deletions src/analyzers/chacha20_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@ def filtrer_dictionnaire_par_indices(self, chemin_dictionnaire: str) -> List[byt
Returns:
list[bytes]: La liste de tous les mots susceptibles d'être des clés adéquates.
"""
return []
f = open('keys/wordlist.txt', 'rb')
cle = f.readlines()
f.close()
return cle

def generer_cles_candidates(self, chemin_dictionnaire: str) -> List[bytes]:
"""
Expand All @@ -117,6 +120,7 @@ def generer_cles_candidates(self, chemin_dictionnaire: str) -> List[bytes]:
cles_candidates: List[bytes] = []
for cle in donnees_fichier_filtre:
cles_candidates.append(hashlib.sha256(cle).digest())
print(cles_candidates)
return cles_candidates

def dechiffrer(self, chemin_fichier_chiffre: str, cle_donnee: bytes) -> bytes:
Expand Down Expand Up @@ -153,7 +157,7 @@ def dechiffrer(self, chemin_fichier_chiffre: str, cle_donnee: bytes) -> bytes:
# L'appel direct a été déplacé dans un bloc if __name__ == "__main__" pour de bonnes pratiques (Mouwafic)
if __name__ == "__main__":
try:
resultat_dechiffrement: bytes = ChaCha20_Analyzer().dechiffrer("mission2.enc", os.urandom(32))
resultat_dechiffrement: bytes = ChaCha20_Analyzer().dechiffrer("data/mission2.enc", os.urandom(32))
print(f"Résultat du déchiffrement : {resultat_dechiffrement.decode('utf-8')}")
except ValueError as ve:
print(ve)
Expand Down
16 changes: 10 additions & 6 deletions src/detecteur_crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
import os
import time
from typing import List, Union

from pathlib import Path
# Import des modules d'analyse
from src.analyzers.aes_cbc_analyzer import Aes_Cbc_Analyzer
from src.crypto_analyzer import CryptoAnalyzer
from src.analyzers.chacha20_analyzer import ChaCha20_Analyzer
from src.analyzers.blowfish_analyzer import Blowfish_Analyzer
from src.analyzers.aes_gcm_analyzer import Aes_Gcm_Analyzer
from src.analyzers.fernet_analyzer import FernetAnalyzer
from src.rapport_mission import generer_rapport_mission

# Import des modules utilitaries
from src.utils import est_dechiffre
Expand Down Expand Up @@ -41,7 +43,8 @@ def __init__(self):
"AES-CBC": Aes_Cbc_Analyzer(),
"ChaCha20": ChaCha20_Analyzer(),
"Blowfish": Blowfish_Analyzer(),
"AES-GCM": Aes_Gcm_Analyzer()
"AES-GCM": Aes_Gcm_Analyzer(),
"Fernet": FernetAnalyzer(),
}
self.missions_completees: list[dict[str, Union[str, list[ResultatAnalyse], float]]] = []
self.statistiques_globales: dict[str, Union[int, float]] = {
Expand All @@ -68,7 +71,7 @@ def analyser_fichier_specifique(self, chemin_fichier_chiffre: str) -> ResultatAn

try:
# Vérification de l'existence du fichier
if not os.path.exists(f"data/{chemin_fichier_chiffre}"):
if not os.path.isfile(Path('data')/f"{chemin_fichier_chiffre}"):
print("Erreur: Fichier non trouvé")
return ResultatAnalyse("", b"", 0.0, b"", 0.0, 0)

Expand Down Expand Up @@ -156,7 +159,7 @@ def mission_complete_automatique(self, dossier_chiffres: str, chemin_dictionnair
chemin_fichier = os.path.join(dossier_chiffres, fichier)

# Analyse du fichier
resultat = self.analyser_fichier_specifique(chemin_fichier)
resultat = self.analyser_fichier_specifique(fichier)

# Tentative de déchiffrement si algorithme détecté
if resultat.algo:
Expand All @@ -181,7 +184,7 @@ def mission_complete_automatique(self, dossier_chiffres: str, chemin_dictionnair
print(f"{fichier}: Aucun algorithme détecté")

# Rapport de synthèse final
self.generer_rapport_synthese(resultats, time.time() - debut_mission)
generer_rapport_mission().generer_rapport_synthese(resultats, time.time() - debut_mission)

# Mise à jour des statistiques globales
self.missions_completees.append({
Expand Down Expand Up @@ -251,4 +254,5 @@ def attaque_dictionnaire_manuelle(self, chemin_fichier: str, algorithme_choisi:
print(f"Erreur lors de l'attaque: {str(e)}")
temps_execution = time.time() - debut_attaque
return ResultatAnalyse("", b"", 0.0, b"", temps_execution, 0)
# print(DetecteurCryptoOrchestrateur().analyser_fichier_specifique('data/mission1.enc'))

# print(DetecteurCryptoOrchestrateur().analyser_fichier_specifique(f"{os.path.abspath(os.curdir)}\\CryptoForensic-Python\\data\\mission2.enc"))
20 changes: 15 additions & 5 deletions src/interface_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def menu_1(self):
print(f"\n[bold]Score de probabilité[/bold] : [green]{data.score_probabilite}[/green]")
# print(data.texte_dechiffre)
print(f"\n[bold]Temps d'éxécution[/bold] : [green]{round(data.temps_execution,4)}[/green] s")
esc=input("Veuillez appuyer sur la touche entrer pour retrouner au menu principal")
esc=input("Veuillez appuyer sur la touche entrer pour retourner au menu principal")
if esc=="":
self.default_menu()
else : self.default_menu()
Expand All @@ -107,14 +107,24 @@ def menu_2(self):
self.dynamiqueText("Mission complète automatique","green")
self.dynamiqueText("Veuillez entrer le chemin du dossier :","white")
time.sleep(0.02)
# chemin_dossier = self.prompt.ask("Veuillez entrer le chemin du dossier : ")
self.console.clear()

chemin_dossier = self.prompt.ask("Veuillez entrer le chemin du dossier : ")
resultat = DetecteurCryptoOrchestrateur().mission_complete_automatique(chemin_dossier, "keys/wordlist.txt")
print(line for line in resultat)
# self.console.clear()
self.dynamiqueText("Mission en cours...","green")
time.sleep(0.02)
self.console.clear()
# self.console.clear()
self.dynamiqueText("Mission terminée","green")

esc=input("Veuillez appuyer sur la touche entrer pour retourner au menu principal")
time.sleep(0.02)
self.default_menu()

if esc=="":
self.default_menu()
else : self.default_menu()

# self.default_menu()

def menu_3(self):
self.console.clear()
Expand Down
22 changes: 11 additions & 11 deletions src/rapport_mission.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ def recuperer_ancien_rapport(self, base_date:str)->list|str:
except FileNotFoundError:
print('Fichier non trouvé')

print(generer_rapport_mission().generer_rapport_synthese({
'algorithme':'CHACHA20',
'fichier': 'mission1.enc',
'cle':'PK7',
'tentatives':'127',
'temps_execution':"368s",
'taux_succes': "97%",
'statut_succes':'Succès',
'texte_dechiffre':'Je suis là!'
}))
# print(generer_rapport_mission().generer_rapport_synthese({
# 'algorithme':'CHACHA20',
# 'fichier': 'mission1.enc',
# 'cle':'PK7',
# 'tentatives':'127',
# 'temps_execution':"368s",
# 'taux_succes': "97%",
# 'statut_succes':'Succès',
# 'texte_dechiffre':'Je suis là!'
# }))

print(generer_rapport_mission().recuperer_ancien_rapport("05/08/25")[0])
# print(generer_rapport_mission().recuperer_ancien_rapport("05/08/25")[0])
9 changes: 5 additions & 4 deletions src/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import math
import re
import string
from pathlib import Path
from typing import Any, Dict, List, TypedDict
Expand Down Expand Up @@ -55,10 +56,10 @@ def est_dechiffre(texte:str) -> bool:
pourcent += 30

# Le respect de la ponctuation, les 20% restants
if stats['ponctuation'] > 50 :
if stats['ponctuation_valide'] > 50 :
pourcent += 20

return True if pourcent > 70 else False
return True if pourcent > 80 else False



Expand Down Expand Up @@ -100,7 +101,7 @@ def verifier_texte_dechiffre(texte: str) -> Dict[str, Any]:
copy=texte
for lettre in tab:
copy=copy.replace(lettre, ' ')
mots = [mot for mot in copy.strip().split(' ') if mot]
mots = [mot.removesuffix('\n').removeprefix('\n') for mot in copy.strip().split(' ') if mot != '\n']
stats['nombre_mots']=len(mots)

# Verifier que le chaque mot du texte est un mot anglais/francais
Expand All @@ -119,7 +120,7 @@ def verifier_texte_dechiffre(texte: str) -> Dict[str, Any]:
try:
with open(chemin, 'r', encoding='latin-1') as f:
for ligne in f:
if ligne.strip() == mot:
if re.match(ligne.strip().removesuffix('\n'), mot, re.I):
mots_valides += 1
trouve=True
break
Expand Down
1 change: 1 addition & 0 deletions tests/fichiers_pour_tests/aes_gcm_invalide.enc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
_���� ̎��~�lY{�攳F���y�x��FN��fY��k]8]0���ވ�M��[DW�&��W�Z�3�A��� p~�uV�
42 changes: 40 additions & 2 deletions tests/test_analyzers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
import sys
import hashlib
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305

from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from pathlib import Path
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))

from src.analyzers.aes_cbc_analyzer import Aes_Cbc_Analyzer
from src.analyzers.chacha20_analyzer import ChaCha20_Analyzer
from src.analyzers.aes_gcm_analyzer import Aes_Gcm_Analyzer


class AesCbcAnalyzerTester(TestCase):
Expand Down Expand Up @@ -103,6 +105,42 @@ def test_chacha20_dechiffrer_fichier_non_existant(self):
with self.assertRaises(FileNotFoundError):
self.analyser_chacha.dechiffrer("chemin_invalide.enc", cle_valide)


class AesGcmTester(TestCase) :
_wordlist = "keys/wordlist.txt"
_analyzer=Aes_Gcm_Analyzer()
_fichier="data/mission3.enc"
_fichier_test = Path('tests/fichiers_pour_tests') / 'aes_gcm_invalide.enc'
_texte_test = b"Test effectue pour AesGcm, encore. Nous en sommes a la.fin"


def setUp(self):
"""
Crée un fchier de test crypté en AESGCM pour les tests unitaires
"""
key = AESGCM.generate_key(128)
nonce = os.urandom(12)
aad = os.urandom(16)
texte_chiffre = AESGCM(key).encrypt(nonce, self._texte_test, aad)
with open(self._fichier_test, '+wb') as f:
f.write(nonce)
f.write(texte_chiffre)
f.close()

def test_aesgcm_generer_cles_candidates(self):
#Vérifie que les clés candidates générés par cet algorithme sont une liste de bytes
with self.assertRaises(ValueError):
self.assertIsInstance(self._analyzer.generer_cles_candidates(self._wordlist), list[bytes])

def test_aes_gcm_identifier_algo(self):
#Vérifie que la probabilité retournée pour le fichier mission3.enc est un float et élevée
with self.assertRaises(ValueError):
self.assertIsInstance(self._analyzer.identifier_algo(self._fichier_test), float)
self.assertAlmostEqual(self._analyzer.identifier_algo(self._fichier_test, 0))

def test_aes_gcm_dechiffrer(self):
self.assertIsInstance(self._analyzer.dechiffrer(self._fichier_test), bytes)



if __name__ == '__main__':
main()