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
@@ -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
Expand Down
179 changes: 70 additions & 109 deletions src/analyzers/chacha20_analyzer.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# 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
from typing import List

sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
from crypto_analyzer import CryptoAnalyzer
from utils import calculer_entropie
Expand All @@ -26,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:
"""
Expand All @@ -41,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)))
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.")
71 changes: 66 additions & 5 deletions tests/test_analyzers.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down Expand Up @@ -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()