From 093f12a81272b07ea1e515c54a5e9cb13b7cec23 Mon Sep 17 00:00:00 2001 From: mouwaficbdr Date: Fri, 8 Aug 2025 09:18:26 +0100 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20Correction=20des=20imports=20non=20u?= =?UTF-8?q?tilis=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/analyzers/chacha20_analyzer.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/analyzers/chacha20_analyzer.py b/src/analyzers/chacha20_analyzer.py index 835a238..20989ae 100644 --- a/src/analyzers/chacha20_analyzer.py +++ b/src/analyzers/chacha20_analyzer.py @@ -1,9 +1,8 @@ # Import des modules import hashlib -from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms from rich import print -import os, struct -import math +import os import sys sys.path.append(os.path.join(os.path.dirname(__file__), '..')) from crypto_analyzer import CryptoAnalyzer From c15d2c2f555090ab1d17a2659972a99443189daf Mon Sep 17 00:00:00 2001 From: mouwaficbdr Date: Fri, 8 Aug 2025 09:21:35 +0100 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20Correction=20des=20probl=C3=A8mes=20?= =?UTF-8?q?de=20typage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/analyzers/chacha20_analyzer.py | 174 +++++++++++------------------ 1 file changed, 68 insertions(+), 106 deletions(-) diff --git a/src/analyzers/chacha20_analyzer.py b/src/analyzers/chacha20_analyzer.py index 20989ae..c980411 100644 --- a/src/analyzers/chacha20_analyzer.py +++ b/src/analyzers/chacha20_analyzer.py @@ -4,6 +4,8 @@ from rich import print import os import sys +from typing import List + sys.path.append(os.path.join(os.path.dirname(__file__), '..')) from crypto_analyzer import CryptoAnalyzer from utils import calculer_entropie @@ -25,12 +27,10 @@ class ChaCha20_Analyzer(CryptoAnalyzer): _CHACHA20_LONGUEUR_BLOC: la taille du bloc de chiffrement (64 bits) """ - _CHACHA20_LONGUEUR_CLE = 32 - _CHACHA20_LONGUEUR_NONCE = 12 #fourni - _CHACHA20_LONGUEUR_TAG = 16 - _CHACHA20_LONGUEUR_BLOC = 64 - - + _CHACHA20_LONGUEUR_CLE: int = 32 + _CHACHA20_LONGUEUR_NONCE: int = 12 + _CHACHA20_LONGUEUR_TAG: int = 16 + _CHACHA20_LONGUEUR_BLOC: int = 64 def identifier_algo(self, chemin_fichier_chiffre: str) -> float: """ @@ -40,142 +40,104 @@ def identifier_algo(self, chemin_fichier_chiffre: str) -> float: - vérifiant l'absence de padding (pas de contrainte de taille) - vérifiant que la taille du fichier est suffisante pour contenir un nonce - Retourne une probabilité entre 0 et 1(Pour connaitre la probabilité que l'algo de chiffrement utilisé soit l'ChaCha20). + Retourne une probabilité entre 0 et 1 (Pour connaitre la probabilité que l'algo de chiffrement utilisé soit l'ChaCha20). Args: - chemin_fichier_chiffre(str): le chemin du fichier chiffré à traiter . + chemin_fichier_chiffre(str): le chemin du fichier chiffré à traiter. Returns: - float: La probabilité que l'algo de chiffrement utilisé soit l'ChaCha20 apres le calcul. + float: La probabilité que l'algo de chiffrement utilisé soit l'ChaCha20 après le calcul. """ try: with open(chemin_fichier_chiffre, 'rb') as f: - donnees = f.read() + donnees: bytes = f.read() if len(donnees) < self._CHACHA20_LONGUEUR_NONCE: - return 0.0 # Fichier trop petit pour contenir un nonce + return 0.0 - # Extraire le nonce présumé (12 premiers bytes) - nonce = donnees[:self._CHACHA20_LONGUEUR_NONCE] - donnees_chiffrees = donnees[self._CHACHA20_LONGUEUR_NONCE:] + nonce: bytes = donnees[:self._CHACHA20_LONGUEUR_NONCE] + donnees_chiffrees: bytes = donnees[self._CHACHA20_LONGUEUR_NONCE:] if len(donnees_chiffrees) == 0: - return 0.0 # Pas de données chiffrées + return 0.0 - # Critère 1: Vérifier la taille minimale + taille_min: float = 0.0 if len(donnees) >= self._CHACHA20_LONGUEUR_NONCE + 16: taille_min = 1.0 - else: - taille_min = 0.0 - # Critère 2: Vérifier l'entropie des données chiffrées (doit être très élevée) - entropie = calculer_entropie(donnees_chiffrees) - # L'entropie d'un chiffrement ChaCha20 devrait être proche de 8 bits/octet - if entropie / 8.0 > 1.0: - entropie_max = 1.0 - else: - entropie_max = entropie / 8.0 + entropie: float = calculer_entropie(donnees_chiffrees) + entropie_max: float = min(entropie / 8.0, 1.0) - # Critère 3: Vérifier l'absence de padding (pas de contrainte de taille) - # ChaCha20 est un chiffrement de flux, donc pas de padding - # On vérifie que la taille des données chiffrées n'est pas un multiple d'une taille de bloc commune - taille_donnees = len(donnees_chiffrees) + padding_max: float = 1.0 + taille_donnees: int = len(donnees_chiffrees) if taille_donnees % 16 == 0 or taille_donnees % 8 == 0: padding_max = 0.5 - else: - padding_max = 1.0 - # Critère 4: Vérifier l'entropie du nonce (doit être élevée aussi) - entropie_nonce = calculer_entropie(nonce) - if entropie_nonce / 8.0 > 1.0: - nonce_max = 1.0 - else: - nonce_max = entropie_nonce / 8.0 + entropie_nonce: float = calculer_entropie(nonce) + nonce_max: float = min(entropie_nonce / 8.0, 1.0) - # Calcul de la probabilité finale (moyenne pondérée des scores) - probabilite = (taille_min * 0.1 + + probabilite: float = (taille_min * 0.1 + entropie_max * 0.4 + padding_max * 0.3 + nonce_max * 0.2) - return min(probabilite, 1.0) + return probabilite except Exception as e: print(f"Erreur lors de l'identification de l'algorithme: {e}") return 0.0 + def filtrer_dictionnaire_par_indices(self, chemin_fichier_chiffre: str) -> List[bytes]: + # En supposant qu'elle retourne une liste de bytes pour les clés. + return [] - def filtrer_dictionnaire_par_indices(self, chemin_fichier_chiffre): - pass - - def generer_cles_candidates(self, chemin_fichier_chiffre): - - ''' - Cette fonction se charge de générer les clés candidates pour le déchiffremment du fichier chiffré en utilisant - la dérivation sha256 pour renforcer les clées de chiffrement. + def generer_cles_candidates(self, chemin_dictionnaire: str) -> List[bytes]: + """ + Cette fonction se charge de générer les clés candidates pour le déchifremment du fichier chiffré en utilisant + la dérivation sha256 pour renforcer les clées de chiffrement. - - Args: - chemin_fichier_chiffre(str) : Le chemin vers le fichier chiffré - - Returns: - cles_candidates (list[bytes]) : Un tableau de clés, chaque clé étant une séquence d'octets - ''' + Args: + chemin_dictionnaire(str) : Le chemin vers le dictionnaire. - donnees_fichier_filtre = self.filtrer_dictionnaire_par_indices(chemin_fichier_chiffre) - - cle_candidates: list[bytes] = [] + Returns: + cles_candidates (List[bytes]) : Un tableau de clés, chaque clé étant une séquence d'octets. + """ + donnees_fichier_filtre: List[bytes] = self.filtrer_dictionnaire_par_indices(chemin_dictionnaire) + cles_candidates: List[bytes] = [] for cle in donnees_fichier_filtre: - cle_candidates.append(hashlib.sha256(cle).digest()) - - return cle_candidates + cles_candidates.append(hashlib.sha256(cle).digest()) + return cles_candidates def dechiffrer(self, chemin_fichier_chiffre: str, cle_donnee: bytes) -> bytes: if len(cle_donnee) != 32: raise ValueError("Erreur : La clé n'a pas la taille correcte") - else: - try: - # Utiliser le chemin complet si c'est un chemin absolu, sinon ajouter le préfixe data/ - if os.path.isabs(chemin_fichier_chiffre): - fichier_path = chemin_fichier_chiffre - else: - fichier_path = f"data/{chemin_fichier_chiffre}" - - with open(fichier_path, 'rb') as f: - nonce = f.read(self._CHACHA20_LONGUEUR_NONCE) - texte_chiffre = f.read() - - algorithm_chacha20 = algorithms.ChaCha20(cle_donnee, nonce) - cipher = Cipher(algorithm_chacha20, mode=None) - decrypteur = cipher.decryptor() - resultat = decrypteur.update(texte_chiffre) - - # Retourner les bytes bruts comme attendu par l'interface - return resultat - - except Exception as e: - print(f"Une erreur est survenue : {e}") - return b"" - cle_candidates.append(hashlib.sha256(cle).encode(encoding="utf-8")) - - return cle_candidates - - def dechiffrer(self,chemin_fichier_chiffer : str ,clef :bytes)->str: - if len(clef) != 32 : return ValueError("Erreur : La clé a pas la taille correcte ") - else: - try: - with open(f"data/{chemin_fichier_chiffer}",'rb') as f: - nonce = f.read(16) - texte_chiffrer = f.read() - - counter=0 - algorithm_chacha20 = algorithms.ChaCha20(clef,nonce) - cipher = Cipher(algorithm_chacha20,mode=None) - decrypteur = cipher.decryptor() - return decrypteur.update(texte_chiffrer) - except Exception as e: - print(f"Une erreur est survenu : {e}") - - + + try: + fichier_path: str = chemin_fichier_chiffre + if not os.path.isabs(chemin_fichier_chiffre): + fichier_path = f"data/{chemin_fichier_chiffre}" + + with open(fichier_path, 'rb') as f: + nonce: bytes = f.read(self._CHACHA20_LONGUEUR_NONCE) + texte_chiffre: bytes = f.read() + + algorithm_chacha20 = algorithms.ChaCha20(cle_donnee, nonce) + cipher = Cipher(algorithm_chacha20, mode=None) + decrypteur = cipher.decryptor() + resultat: bytes = decrypteur.update(texte_chiffre) + + return resultat -print(ChaCha20_Analyzer().dechiffrer("mission2.enc",os.urandom(32))) \ No newline at end of file + except Exception as e: + print(f"Une erreur est survenue : {e}") + return b"" + +# 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)) + 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 From 0f148270e631f4193becdc444891fb2f1adef173 Mon Sep 17 00:00:00 2001 From: mouwaficbdr Date: Fri, 8 Aug 2025 09:36:08 +0100 Subject: [PATCH 3/4] fix: Correction des imports dans Aes_Cbc_Analyzer --- src/analyzers/aes_cbc_analyzer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyzers/aes_cbc_analyzer.py b/src/analyzers/aes_cbc_analyzer.py index 08205a5..719381e 100644 --- a/src/analyzers/aes_cbc_analyzer.py +++ b/src/analyzers/aes_cbc_analyzer.py @@ -1,5 +1,5 @@ -from crypto_analyzer import CryptoAnalyzer -from utils import calculer_entropie +from ..crypto_analyzer import CryptoAnalyzer +from ..utils import calculer_entropie from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes From b40175b768c6536ab14605c5b4aa75553cd7dc52 Mon Sep 17 00:00:00 2001 From: mouwaficbdr Date: Fri, 8 Aug 2025 09:36:36 +0100 Subject: [PATCH 4/4] add: Tests unitaires pour ChaCha20_Analyzer --- tests/test_analyzers.py | 71 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/tests/test_analyzers.py b/tests/test_analyzers.py index 9987c75..dc007e5 100644 --- a/tests/test_analyzers.py +++ b/tests/test_analyzers.py @@ -1,14 +1,20 @@ from unittest import TestCase, main +import os import sys -sys.path.append('.') -sys.path.append('..') +import hashlib +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms + +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +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 -class AnalyzersTester(TestCase): +class AesCbcAnalyzerTester(TestCase): """ - Cette classe est principalement destinée à recueillir toutes les fonctions de test des analyseurs d'algorithme - de chiffrement. + Cette classe est principalement destinée à recueillir toutes les fonctions de test des analyseurs d'algorithme + de chiffrement. """ def setUp(self): @@ -40,6 +46,61 @@ def test_exception_dechiffrer(self): with self.assertRaises(FileNotFoundError): self.analyser.dechiffrer("no_file_dohi.txt", premiere_cle) + +class ChaCha20AnalyzerTester(TestCase): + + def setUp(self): + # Chemins pour ChaCha20_Analyzer + self.wordlist = "keys/wordlist.txt" + self.analyser_chacha = ChaCha20_Analyzer() + + # Données de test pour ChaCha20 + self.cle_test_chacha = hashlib.sha256(b"cle_test").digest() + self.nonce_test_chacha = b"\x00" * 12 + self.texte_clair_test_chacha = b"Bonjour le monde, ceci est un test de chiffrement ChaCha20" + self.chemin_fichier_chacha_valide = "tests/fichiers_pour_tests/chacha20_valide.enc" + self.chemin_fichier_chacha_invalide = "tests/fichiers_pour_tests/chacha20_invalide.enc" + # Générer un fichier chiffré valide pour les tests de ChaCha20 + cipher_chacha = Cipher(algorithms.ChaCha20(self.cle_test_chacha, self.nonce_test_chacha), mode=None) + encryptor = cipher_chacha.encryptor() + texte_chiffre_test = encryptor.update(self.texte_clair_test_chacha) + with open(self.chemin_fichier_chacha_valide, "wb") as f: + f.write(self.nonce_test_chacha) + f.write(texte_chiffre_test) + + # Ajout des tests pour ChaCha20_Analyzer + def test_chacha20_identifier_algo(self): + self.assertAlmostEqual(self.analyser_chacha.identifier_algo(self.chemin_fichier_chacha_valide), 1.0, 1) + self.assertAlmostEqual(self.analyser_chacha.identifier_algo(self.chemin_fichier_chacha_invalide), 0.0, 1) + + def test_chacha20_generer_cles_candidates(self): + # Comme la fonction filtrer_dictionnaire_par_indices retourne toujours une liste vide, + # generer_cles_candidates doit également retourner une liste vide. + self.assertEqual(self.analyser_chacha.generer_cles_candidates(self.wordlist), []) + + def test_chacha20_dechiffrer(self): + # Test de déchiffrement avec une clé et un nonce valides + resultat_dechiffrement = self.analyser_chacha.dechiffrer(self.chemin_fichier_chacha_valide, self.cle_test_chacha) + self.assertEqual(resultat_dechiffrement, self.texte_clair_test_chacha) + + # Test de déchiffrement avec une clé incorrecte + cle_incorrecte = hashlib.sha256(b"mauvaise_cle").digest() + resultat_incorrect = self.analyser_chacha.dechiffrer(self.chemin_fichier_chacha_valide, cle_incorrecte) + self.assertNotEqual(resultat_incorrect, self.texte_clair_test_chacha) + + def test_chacha20_dechiffrer_mauvaise_cle(self): + # Test de l'exception pour une clé de taille incorrecte + cle_mauvaise_taille = b"a" * 16 # La bonne taille est 32 + with self.assertRaises(ValueError): + self.analyser_chacha.dechiffrer(self.chemin_fichier_chacha_valide, cle_mauvaise_taille) + + def test_chacha20_dechiffrer_fichier_non_existant(self): + # Test de l'exception si le fichier n'existe pas + cle_valide = self.cle_test_chacha + with self.assertRaises(FileNotFoundError): + self.analyser_chacha.dechiffrer("chemin_invalide.enc", cle_valide) + + if __name__ == '__main__': main() \ No newline at end of file