diff --git a/main.py b/main.py index e548258..0324145 100644 --- a/main.py +++ b/main.py @@ -1,2 +1,16 @@ from src.detecteur_crypto import DetecteurCryptoOrchestrateur -print(DetecteurCryptoOrchestrateur().analyser_fichier_specifique('data/mission1.enc')) \ No newline at end of file +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() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e688a26..07328da 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 \ No newline at end of file diff --git a/resultat.txt b/resultat.txt new file mode 100644 index 0000000..90e1ab6 Binary files /dev/null and b/resultat.txt differ diff --git a/src/analyzers/aes_gcm_analyzer.py b/src/analyzers/aes_gcm_analyzer.py index e74b1f7..22924d1 100644 --- a/src/analyzers/aes_gcm_analyzer.py +++ b/src/analyzers/aes_gcm_analyzer.py @@ -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 \ No newline at end of file + 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) \ No newline at end of file diff --git a/src/analyzers/blowfish_analyzer.py b/src/analyzers/blowfish_analyzer.py index 0a1973a..839faed 100644 --- a/src/analyzers/blowfish_analyzer.py +++ b/src/analyzers/blowfish_analyzer.py @@ -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. @@ -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: @@ -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 @@ -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)) @@ -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.") \ No newline at end of file diff --git a/src/analyzers/chacha20_analyzer.py b/src/analyzers/chacha20_analyzer.py index 283dcc9..bb60115 100644 --- a/src/analyzers/chacha20_analyzer.py +++ b/src/analyzers/chacha20_analyzer.py @@ -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]: """ @@ -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: @@ -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) diff --git a/src/detecteur_crypto.py b/src/detecteur_crypto.py index fb36344..d58dad6 100644 --- a/src/detecteur_crypto.py +++ b/src/detecteur_crypto.py @@ -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 @@ -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]] = { @@ -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) @@ -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: @@ -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({ @@ -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")) diff --git a/src/interface_console.py b/src/interface_console.py index 99446b7..dfd3be3 100644 --- a/src/interface_console.py +++ b/src/interface_console.py @@ -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() @@ -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() diff --git a/src/rapport_mission.py b/src/rapport_mission.py index af005b9..ba9b589 100644 --- a/src/rapport_mission.py +++ b/src/rapport_mission.py @@ -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]) diff --git a/src/utils.py b/src/utils.py index 0e481e6..e16dde6 100644 --- a/src/utils.py +++ b/src/utils.py @@ -1,4 +1,5 @@ import math +import re import string from pathlib import Path from typing import Any, Dict, List, TypedDict @@ -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 @@ -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 @@ -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 diff --git a/tests/fichiers_pour_tests/aes_gcm_invalide.enc b/tests/fichiers_pour_tests/aes_gcm_invalide.enc new file mode 100644 index 0000000..5e3c49c --- /dev/null +++ b/tests/fichiers_pour_tests/aes_gcm_invalide.enc @@ -0,0 +1 @@ +_‘À¿ò ÌŽºÏ~–lY{ô攳FÈÅÀyÔxÑãFNªÎfYž‰k]8]0”ß²Þˆ½Mƒì¥[DWå&ù†WùZÏ3©A€ºƒ p~ÏuVò \ No newline at end of file diff --git a/tests/test_analyzers.py b/tests/test_analyzers.py index 7205a78..7c2331a 100644 --- a/tests/test_analyzers.py +++ b/tests/test_analyzers.py @@ -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): @@ -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() \ No newline at end of file