ajout de tous les cours et TP préparés cet été

This commit is contained in:
2026-01-17 23:10:49 +01:00
parent ed9415bc81
commit 301cf5a98f
125 changed files with 21614 additions and 542 deletions

View File

@@ -0,0 +1,138 @@
# Corrigé des exercices — Sécurité
---
## Exercice : Algorithme ROT13
### Rappel
L'algorithme ROT13 décale chaque lettre de 13 positions dans l'alphabet. Comme l'alphabet a 26 lettres, appliquer ROT13 deux fois revient au message original.
### Solution
```python
def chiffrement_rot13_mot(mot):
"""
Chiffre un mot avec l'algorithme ROT13.
:param mot: (str) Le mot à chiffrer (en minuscules)
:return: (str) Le mot chiffré
"""
resultat = ""
for lettre in mot.lower():
if 'a' <= lettre <= 'z':
# Calculer la nouvelle position
code = ord(lettre) - ord('a') # Position 0-25
nouveau_code = (code + 13) % 26 # Décalage de 13
nouvelle_lettre = chr(nouveau_code + ord('a'))
resultat += nouvelle_lettre
else:
# Garder les caractères non alphabétiques
resultat += lettre
return resultat
def chiffrement_rot13(message):
"""
Chiffre un message complet avec l'algorithme ROT13.
:param message: (str) Le message à chiffrer
:return: (str) Le message chiffré
"""
mots = message.split()
mots_chiffres = [chiffrement_rot13_mot(mot) for mot in mots]
return " ".join(mots_chiffres)
```
### Version alternative (plus concise)
```python
def rot13(message):
"""Version concise du chiffrement ROT13."""
resultat = ""
for car in message.lower():
if 'a' <= car <= 'z':
resultat += chr((ord(car) - ord('a') + 13) % 26 + ord('a'))
else:
resultat += car
return resultat
```
### Tests
```python
>>> chiffrement_rot13("bonjour")
'obawbhe'
>>> chiffrement_rot13("python")
'clguba'
>>> chiffrement_rot13("Vive les numériques et sciences informatiques")
'ivir yrf ahzrevdhrf rg fpvraprf vasbezngvdhrf'
```
### Exemples de chiffrement de noms
| Prénom/Nom | ROT13 |
|------------|-------|
| Alice | nyvpr |
| Bob | obo |
| Florian | sybevna |
| Mathieu | znguvrh |
### Propriété remarquable
ROT13 est son propre inverse : appliquer ROT13 deux fois redonne le message original.
```python
>>> rot13(rot13("bonjour"))
'bonjour'
```
Cela s'explique car 13 + 13 = 26, soit un tour complet de l'alphabet.
### Généralisation : Chiffre de César
On peut généraliser avec un décalage variable (chiffre de César) :
```python
def cesar(message, decalage):
"""
Chiffre un message avec le chiffre de César.
:param message: (str) Le message à chiffrer
:param decalage: (int) Le décalage (clé)
:return: (str) Le message chiffré
"""
resultat = ""
for car in message.lower():
if 'a' <= car <= 'z':
resultat += chr((ord(car) - ord('a') + decalage) % 26 + ord('a'))
else:
resultat += car
return resultat
def decesar(message, decalage):
"""Déchiffre un message chiffré avec César."""
return cesar(message, -decalage)
```
```python
>>> cesar("bonjour", 3) # Décalage de 3 (César classique)
'erqmrxu'
>>> decesar("erqmrxu", 3)
'bonjour'
>>> cesar("bonjour", 13) # ROT13
'obawbhe'
```
---
Auteur : Florian Mathieu
Licence CC BY NC
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.

View File

@@ -0,0 +1,457 @@
"""
Corrigé du TP Gestionnaire de mots de passe
"""
import random
import string
import hashlib
# =============================================================================
# PARTIE 1 : Chiffrement XOR
# =============================================================================
def xor_chiffrement(message, cle):
"""
Chiffre ou déchiffre un message avec XOR.
:param message: (str) Le message à traiter
:param cle: (str) La clé de chiffrement
:return: (str) Le message chiffré/déchiffré
"""
resultat = ""
for i, caractere in enumerate(message):
# Récupérer le caractère de la clé (cyclique)
cle_char = cle[i % len(cle)]
# Appliquer XOR
nouveau_char = chr(ord(caractere) ^ ord(cle_char))
resultat += nouveau_char
return resultat
# =============================================================================
# PARTIE 2 : Classe MotDePasse
# =============================================================================
class MotDePasse:
"""Représente un identifiant stocké."""
def __init__(self, site, identifiant, mot_de_passe):
"""
Initialise une entrée.
:param site: (str) Nom du site/service
:param identifiant: (str) Nom d'utilisateur ou email
:param mot_de_passe: (str) Mot de passe en clair
"""
self.site = site
self.identifiant = identifiant
self.mot_de_passe = mot_de_passe
def __repr__(self):
return f"MotDePasse({self.site}, {self.identifiant}, ****)"
def to_string(self):
"""Convertit en chaîne pour le stockage."""
return f"{self.site}|{self.identifiant}|{self.mot_de_passe}"
@staticmethod
def from_string(chaine):
"""Crée un objet depuis une chaîne."""
parties = chaine.split("|")
if len(parties) == 3:
return MotDePasse(parties[0], parties[1], parties[2])
return None
# =============================================================================
# PARTIE 3 : Gestionnaire principal
# =============================================================================
class GestionnaireMDP:
"""Gestionnaire de mots de passe sécurisé."""
def __init__(self, mot_de_passe_maitre):
"""
Initialise le gestionnaire.
:param mot_de_passe_maitre: (str) Clé principale de chiffrement
"""
self.cle = mot_de_passe_maitre
self.coffre = []
def ajouter(self, site, identifiant, mot_de_passe):
"""
Ajoute un nouveau mot de passe au coffre.
:param site: (str) Nom du site
:param identifiant: (str) Identifiant
:param mot_de_passe: (str) Mot de passe
"""
# Vérifier si le site existe déjà
for mdp in self.coffre:
if mdp.site.lower() == site.lower():
# Mettre à jour l'entrée existante
mdp.identifiant = identifiant
mdp.mot_de_passe = mot_de_passe
return
# Sinon, créer une nouvelle entrée
nouvelle_entree = MotDePasse(site, identifiant, mot_de_passe)
self.coffre.append(nouvelle_entree)
def rechercher(self, site):
"""
Recherche un mot de passe par site.
:param site: (str) Nom du site à rechercher
:return: (MotDePasse ou None) L'entrée trouvée
"""
for mdp in self.coffre:
if mdp.site.lower() == site.lower():
return mdp
return None
def supprimer(self, site):
"""
Supprime une entrée du coffre.
:param site: (str) Nom du site à supprimer
:return: (bool) True si supprimé, False sinon
"""
for i, mdp in enumerate(self.coffre):
if mdp.site.lower() == site.lower():
self.coffre.pop(i)
return True
return False
def lister_sites(self):
"""
Liste tous les sites enregistrés.
:return: (list) Liste des noms de sites
"""
return [mdp.site for mdp in self.coffre]
def sauvegarder(self, fichier):
"""
Sauvegarde le coffre dans un fichier chiffré.
:param fichier: (str) Chemin du fichier
"""
if not self.coffre:
contenu = ""
else:
contenu = "\n".join([mdp.to_string() for mdp in self.coffre])
contenu_chiffre = xor_chiffrement(contenu, self.cle)
with open(fichier, 'w', encoding='utf-8') as f:
f.write(contenu_chiffre)
def charger(self, fichier):
"""
Charge le coffre depuis un fichier chiffré.
:param fichier: (str) Chemin du fichier
"""
with open(fichier, 'r', encoding='utf-8') as f:
contenu_chiffre = f.read()
if not contenu_chiffre:
self.coffre = []
return
contenu = xor_chiffrement(contenu_chiffre, self.cle)
lignes = contenu.split("\n")
self.coffre = []
for ligne in lignes:
if ligne:
mdp = MotDePasse.from_string(ligne)
if mdp:
self.coffre.append(mdp)
# =============================================================================
# PARTIE 4 : Génération de mots de passe
# =============================================================================
def generer_mot_de_passe(longueur=16, majuscules=True, chiffres=True, speciaux=True):
"""
Génère un mot de passe aléatoire robuste.
:param longueur: (int) Longueur du mot de passe
:param majuscules: (bool) Inclure des majuscules
:param chiffres: (bool) Inclure des chiffres
:param speciaux: (bool) Inclure des caractères spéciaux
:return: (str) Mot de passe généré
"""
caracteres = string.ascii_lowercase
if majuscules:
caracteres += string.ascii_uppercase
if chiffres:
caracteres += string.digits
if speciaux:
caracteres += "!@#$%^&*()_+-=[]{}|;:,.<>?"
# Générer le mot de passe
mot_de_passe = ''.join(random.choice(caracteres) for _ in range(longueur))
return mot_de_passe
def evaluer_robustesse(mot_de_passe):
"""
Évalue la robustesse d'un mot de passe.
:param mot_de_passe: (str) Le mot de passe à évaluer
:return: (str) Niveau de robustesse
"""
score = 0
# Longueur
if len(mot_de_passe) >= 8:
score += 1
if len(mot_de_passe) >= 12:
score += 1
if len(mot_de_passe) >= 16:
score += 1
# Variété de caractères
if any(c.islower() for c in mot_de_passe):
score += 1
if any(c.isupper() for c in mot_de_passe):
score += 1
if any(c.isdigit() for c in mot_de_passe):
score += 1
if any(c in "!@#$%^&*()_+-=[]{}|;:,.<>?" for c in mot_de_passe):
score += 1
# Retourner le niveau
if score <= 2:
return "Faible"
elif score <= 4:
return "Moyen"
elif score <= 5:
return "Fort"
else:
return "Très fort"
# =============================================================================
# PARTIE 5 : Interface utilisateur
# =============================================================================
def menu_principal():
"""Affiche le menu principal."""
print("\n" + "=" * 40)
print(" GESTIONNAIRE DE MOTS DE PASSE")
print("=" * 40)
print("1. Ajouter un mot de passe")
print("2. Rechercher un mot de passe")
print("3. Générer un mot de passe")
print("4. Lister les sites")
print("5. Supprimer une entrée")
print("6. Sauvegarder")
print("7. Quitter")
print("=" * 40)
return input("Votre choix : ")
def application():
"""Lance l'application."""
print("Bienvenue dans le gestionnaire de mots de passe !")
mdp_maitre = input("Entrez votre mot de passe maître : ")
gestionnaire = GestionnaireMDP(mdp_maitre)
try:
gestionnaire.charger("coffre.dat")
print("Coffre chargé avec succès.")
except FileNotFoundError:
print("Nouveau coffre créé.")
while True:
choix = menu_principal()
if choix == "1":
site = input("Site : ")
identifiant = input("Identifiant : ")
mdp = input("Mot de passe (laisser vide pour générer) : ")
if mdp == "":
mdp = generer_mot_de_passe()
print(f"Mot de passe généré : {mdp}")
gestionnaire.ajouter(site, identifiant, mdp)
print("Ajouté !")
elif choix == "2":
site = input("Site à rechercher : ")
resultat = gestionnaire.rechercher(site)
if resultat:
print(f"Site : {resultat.site}")
print(f"Identifiant : {resultat.identifiant}")
print(f"Mot de passe : {resultat.mot_de_passe}")
else:
print("Site non trouvé.")
elif choix == "3":
try:
longueur = int(input("Longueur (défaut 16) : ") or "16")
except ValueError:
longueur = 16
mdp = generer_mot_de_passe(longueur)
print(f"Mot de passe généré : {mdp}")
print(f"Robustesse : {evaluer_robustesse(mdp)}")
elif choix == "4":
sites = gestionnaire.lister_sites()
if sites:
print("Sites enregistrés :")
for s in sites:
print(f" - {s}")
else:
print("Aucun site enregistré.")
elif choix == "5":
site = input("Site à supprimer : ")
if gestionnaire.supprimer(site):
print("Supprimé !")
else:
print("Site non trouvé.")
elif choix == "6":
gestionnaire.sauvegarder("coffre.dat")
print("Coffre sauvegardé.")
elif choix == "7":
gestionnaire.sauvegarder("coffre.dat")
print("Au revoir !")
break
# =============================================================================
# PARTIE 6 : Améliorations (Bonus)
# =============================================================================
def hasher(mot_de_passe):
"""Retourne le hash SHA-256 du mot de passe."""
return hashlib.sha256(mot_de_passe.encode()).hexdigest()
class GestionnaireMDPSecurise(GestionnaireMDP):
"""Version améliorée avec hashage du mot de passe maître."""
def __init__(self, mot_de_passe_maitre):
# Utiliser le hash comme clé (plus long = plus sécurisé)
cle_derivee = hasher(mot_de_passe_maitre)
super().__init__(cle_derivee)
self.hash_maitre = hasher(mot_de_passe_maitre + "salt_verification")
def verifier_mot_de_passe(self, mot_de_passe):
"""Vérifie si le mot de passe maître est correct."""
return hasher(mot_de_passe + "salt_verification") == self.hash_maitre
# =============================================================================
# RÉPONSES AUX QUESTIONS
# =============================================================================
"""
RÉPONSES AUX QUESTIONS DE SYNTHÈSE
1. Pourquoi ne jamais stocker un mot de passe en clair ?
- Si un attaquant accède à la base de données, il obtient tous les mots de passe
- Les utilisateurs réutilisent souvent leurs mots de passe sur plusieurs sites
- C'est une violation des bonnes pratiques de sécurité (RGPD, etc.)
2. Différence entre chiffrement et hashage :
- Chiffrement : opération réversible avec une clé (on peut retrouver le message)
- Hashage : opération irréversible (on ne peut pas retrouver le message original)
- On chiffre les données confidentielles, on hashe les mots de passe
3. Pourquoi le mot de passe maître doit être robuste ?
- C'est la seule protection de tous vos mots de passe
- Une attaque par force brute pourrait le trouver si trop faible
- Compromis = tous les mots de passe compromis
4. Risques si quelqu'un obtient coffre.dat ?
- Il peut tenter une attaque par force brute sur le mot de passe maître
- XOR est vulnérable à certaines attaques (ex: known-plaintext attack)
- C'est pourquoi les vrais gestionnaires utilisent AES-256
5. Comment les vrais gestionnaires améliorent la sécurité ?
- Chiffrement AES-256 au lieu de XOR
- Dérivation de clé (PBKDF2, Argon2) pour ralentir les attaques
- Authentification à deux facteurs
- Chiffrement de bout en bout
- Audit de sécurité par des experts
"""
# =============================================================================
# TESTS
# =============================================================================
if __name__ == "__main__":
print("=" * 60)
print("TEST 1 : CHIFFREMENT XOR")
print("=" * 60)
message = "Bonjour le monde !"
cle = "secret"
chiffre = xor_chiffrement(message, cle)
print(f"Message original : {message}")
print(f"Clé : {cle}")
print(f"Message chiffré : {repr(chiffre)}")
dechiffre = xor_chiffrement(chiffre, cle)
print(f"Message déchiffré : {dechiffre}")
print(f"Vérification : {message == dechiffre}")
print("\n" + "=" * 60)
print("TEST 2 : GÉNÉRATION DE MOT DE PASSE")
print("=" * 60)
for _ in range(5):
mdp = generer_mot_de_passe(16)
robustesse = evaluer_robustesse(mdp)
print(f"{mdp} -> {robustesse}")
print("\n" + "=" * 60)
print("TEST 3 : GESTIONNAIRE")
print("=" * 60)
gestionnaire = GestionnaireMDP("mon_mot_de_passe_maitre")
gestionnaire.ajouter("Gmail", "alice@gmail.com", "mdp_gmail_123")
gestionnaire.ajouter("Facebook", "alice", "fb_password!")
gestionnaire.ajouter("Netflix", "alice@gmail.com", "netflix2024")
print("Sites enregistrés :", gestionnaire.lister_sites())
resultat = gestionnaire.rechercher("gmail")
if resultat:
print(f"Gmail -> {resultat.identifiant} : {resultat.mot_de_passe}")
print("\n" + "=" * 60)
print("TEST 4 : SAUVEGARDE/CHARGEMENT")
print("=" * 60)
gestionnaire.sauvegarder("test_coffre.dat")
print("Coffre sauvegardé.")
nouveau_gestionnaire = GestionnaireMDP("mon_mot_de_passe_maitre")
nouveau_gestionnaire.charger("test_coffre.dat")
print("Coffre chargé.")
print("Sites récupérés :", nouveau_gestionnaire.lister_sites())
# Nettoyage
import os
os.remove("test_coffre.dat")
print("\n" + "=" * 60)
print("Pour lancer l'application interactive :")
print(" application()")
print("=" * 60)

167
Sécurité/EVALUATION.md Normal file
View File

@@ -0,0 +1,167 @@
## Évaluation
### Sujet
Le ROT47 est un cas particulier du chiffre de César, où la clé vaut 47. Les caractères d'un texte sont associés à leur valeur ASCII. On n'applique le décalage qu'aux caractères ayant une valeur comprise entre [33, 126], les autres caractères restent inchangés.
Par exemple, le caractère ! devient P :
```python
>>> chr(33)
'!'
>>> chr(33 +47)
'p'
```
Lorsque l'on dépasse 126, on retourne à 33. La lettre S, par exemple, qui a pour code ASCII 83. Avec la rotation 47, cela donne 83 + 47 = 130 = 127 + 3. Donc S est codé en chr(33 + 3) = $
1. Écrire une fonction python qui permet de chiffrer en ROT47. Déterminer alors le chiffrement de "Bonjour le monde ! "
2. Comment déchiffrer un message dont on sait qu'il a été chiffré avec ROT47 ? Quelle est sa particularité ?
3. On intercepte le fichier [rotXcrypte.txt](rotXcrypte.txt), codé en ROTx, avec x un nombre inconnu. On sait juste que cette fois, on utilise les points de code de ***0x21*** à ***0x1F0DF*** compris. Il ne s'agit donc plus d'ASCII mais le principe reste le même. Seule la limite maximum passe de 126 (***Ox7e***) à ***0x1f0df***
1. Comment procéder à une attaque de force brute ? Combien d'essais devrait-on effectuer au pire ?
2. Comment effectuer une attaque statistique sachant que le texte original est en français ?
3. Comparons donc avec un texte en français, les Trois Mousquetaires d'Alexandre Dumas:
```python
from urllib.request import urlretrieve
url_mousq = "https://www.gutenberg.org/cache/epub/13951/pg13951.txt"
urlretrieve(url_mousq, 'mousq.txt')
fic: str = open('mousq.txt', 'r').read()
```
Créer une fonction de signature:
```python
def stat(mes: str) -> list
```
Qui prend une chaîne de caractères en paramètre et renvoie la liste des caractères rangés par ordre décroissant de fréquence
4. On suppose disposer d'un tableau de fréquence pour le message à déchiffrer. Créer la fonction
```python
def remplace (origine: str, etalon: str) -> str
```
qui va remplacer chaque lettre dans le message chiffré par la lettre du message "etalon" (ici les Trois Mousquetaires) en suivant l'ordre d'apparition des lettres.
Obtient-on une bonne traduction ? Pourquoi ?
5. On découvre qu'il s'agit d'un chiffrement par décalage. Quelle autre stratégie peut-on employer pour découvrir ce décalage si l'on connaît le caractère le plus fréquent dans le message à déchiffrer et dans le texte étalon ?
6. Créer une fonction de même signature que la précédente mais qui utilise les nouveaux renseignements pour déchiffrer plus efficacement. Expliquer votre fonction.
7. Quelle pourrait être l'utilisation moderne de ROT47 dans le contexte d'Internet ?
--------
#### Corrigé
1. ```python
def rot47(clair: str) -> str:
chif =""
dec = 33
amp = 127 - dec
for car in clair:
new_code = dec + ((ord( car) - dec + 47) % amp)
chif += chr(new_code) if 33 <= ord(car) <= 126 else car
return chif
```
On obtient donc :
```python
>>> rot47("Bonjour le monde !")
'q@?;@FC =6 >@?56 P'
```
2. Le nombre de caractères décalables vaut 127 - 33 = 94. Il faut donc appliquer un décalage de -47 pour déchiffrer, sauf que... On remarque que si on applique un décalage de 47 après un premier décalage de 47, on effectue donc un décalage de 94 qui revient au point de départ. On peut imaginer le ROT47 comme un déplacement sur une roue ayant 94 graduations.
On n'a donc pas besoin de nouvelle fonction : la fonction de chiffrement est aussi la fonction de déchiffrement.
3. - 0x1f0df - 0x21 = 127166. Il y a donc autant de clés possible à essayer.
- Dans un chiffrement par décalage, chaque lettre est toujours chiffrée de la même manière donc la fréquence d'un caractère, par exemple le e, dans le texte clair, sera la même que la fréquence de son caractère associé dans le texte chiffré.
Il suffit donc de classer les caractères du texte chiffré selon leurs fréquences d'apparition et de comparer aux fréquences d'apparition usuelles des caractères de la langue française.
- On parcourt le texte. On regarde chaque caractère. S'il est dans le dictionnaire, on ajoute 1 à la valeur associée, sinon on crée une entrée avec pour valeur 1. On renvoie le dictionnaire trié par fréquence décroissante.
```python
def stat(mes: str) -> list:
dico = {}
for lettre in mes:
if lettre in dico:
dico[lettre] += 1
else:
dico[lettre] = 1
return sorted(dico, key = lambda x: dico[x], reverse=True)
```
On veut que le i-ième caractère du texte chiffré soit remplacé par le i-ième caractère du texte français de référence. Ces listes de fréquences n'étant pas a priori de même taille, on prend la précaution de récupérer la taille minimum.
4.
```python
def remplace (origine: str, etalon: str) -> str:
s_origine = stat(origine) #frequence du message
s_etalon = stat(etalon) # frequence du texte de référence
mini = min(len(s_origine), len(s_etalon)) # taille minimum
dic = {} # dictionnaire de correspondance chiffré -> référence
for i in range(mini):
dic[ s_origine[i] ] = s_etalon[i]
# on parcourt chaque caractère et on le remplace par sa traduction
trad = ""
for lettre in origine:
trad += dic[lettre]
return trad
```
Mais cela ne donne pas la meilleure traduction.
5. On repère le caractère le plus fréquent dans les deux textes ce qui nous donne le décalage probable en effectuant la différence des points de code de chacun.
```python
def guess(origine: str, etalon: str) -> str:
s_origine = stat(origine)
s_etalon = stat(etalon)
dec = ord(s_origine[0]) - ord(s_etalon[0])
return rotx(origine, -dec)
```
6. Sans oublier de créer la fonction rotx qui généralise rot47:
``` 
def rotx(clair: str, cle : int) -> str:
chif =""
dec = 0x20
amp = 0x1f0e0 - dec
for car in clair:
new_code = dec + ((ord(car) - dec + cle) % amp)
chif += chr(new_code) if 0x20 <= ord(car) <= 0x1f0df else car
return chif
```
Cette fois ci, c'est bon.
7. ROT47 et ROT13 sont souvent disponibles sur les navigateurs web car ils permettent de brouiller des textes pour les divers robots interceptant les données entrées par un utilisateur afin de faire de l'analyse de texte...
---
Auteur : Florian Mathieu
Licence CC BY NC
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.

View File

@@ -29,4 +29,12 @@ Quelques conseils :
Chiffrez vos noms, prénoms, ainsi que le message 'Vive les numeriques et sciences informatiques'
Chiffrez vos noms, prénoms, ainsi que le message 'Vive les numériques et sciences informatiques'
---
Auteur : Florian Mathieu
Licence CC BY NC
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.

View File

@@ -2,11 +2,11 @@
<img src="assets/bo.png" alt="bo" style="zoom:50%;" />
> Avec l'avancée technologique ainsi que les échanges sur Internet (**Environ 319,6 milliards d'e-mails étaient envoyés et reçus chaque jour en 2021**) il est devenu indispensable de sécuriser nos échanges. En effet, entre la santé, l'économie, l'administratif...de nombreux services passent aujourd'hui intégralement par le réseau mondial, sans parler des messages professionnels pouvant relever du confidentiel, de nos données qui valent des millions d'€, il devient donc impératif de savoir se protéger, ainsi que d'utiliser des services étant eux mêmes protégés.
> Avec l'avancée technologique ainsi que les échanges sur Internet (**Environ 319,6 milliards d'e-mails étaient envoyés et reçus chaque jour en 2021**) il est devenu indispensable de sécuriser nos échanges. En effet, entre la santé, l'économie, l'administratif...de nombreux services passent aujourd'hui intégralement par le réseau mondial, sans parler des messages professionnels pouvant relever du confidentiel, de nos données qui valent des millions d'€, il devient donc impératif de savoir se protéger, ainsi que d'utiliser des services étant eux-mêmes protégés.
### 1. Qu'est ce que le chiffrement ?
### 1. Qu'est-ce que le chiffrement ?
Contrairement aux idées reçues, le ***chiffrement*** *n'a pas pour but de rendre invisible vos données ou vos messages*. Cela est beaucoup trop compliqué dans une société hyper connectée comme la notre. Par contre, *il permet de rendre les données et échanges incompréhensibles aux yeux de tous et toutes* : seule la personne destinée à recevoir votre message pourra le comprendre.
Contrairement aux idées reçues, le ***chiffrement*** *n'a pas pour but de rendre invisible vos données ou vos messages*. Cela est beaucoup trop compliqué dans une société hyper connectée comme la nôtre. Par contre, *il permet de rendre les données et échanges incompréhensibles aux yeux de tous et toutes* : seule la personne destinée à recevoir votre message pourra le comprendre.
Un bon algorithme de chiffrement a besoin de certains pré-requis pour être efficace :
@@ -17,7 +17,7 @@ Un bon algorithme de chiffrement a besoin de certains pré-requis pour être eff
> Un algorithme de chiffrement est une suite d'opérations destinées à chiffrer un message.
>
> Un algorithme de déchiffrement est une suite d'opération destinées à déchiffrer le message.
> Un algorithme de déchiffrement est une suite d'opérations destinées à déchiffrer le message.
>
> Certains algorithmes font les deux.
@@ -33,9 +33,9 @@ L'Agence Nationale de la Sécurité des Services d'Informations (ANSSI) qualifi
### 2. Un peu d'histoire
Si le chiffrement est aujourd'hui associé aux échanges numériques, il ne faut pas croire que cette méthode est récente : l'humain cherche à sécuriser ses communications depuis l'Antiquité, notamment dans le domaine militaire. Un des premiers code est d'ailleurs celui inventé par Jules César : Le chiffre de César. Il s'agissait de décaler chaque lettre de l'aphabet de trois rangs (car la lettre C est la troisième). A devenant D, B devenant E, etc. Ce chiffrement a été utilisé par les égyptiens en -2000.
Si le chiffrement est aujourd'hui associé aux échanges numériques, il ne faut pas croire que cette méthode est récente : l'humain cherche à sécuriser ses communications depuis l'Antiquité, notamment dans le domaine militaire. Un des premiers codes est d'ailleurs celui inventé par Jules César : le chiffre de César. Il s'agissait de décaler chaque lettre de l'alphabet de trois rangs (car la lettre C est la troisième). A devenant D, B devenant E, etc. Ce chiffrement a été utilisé par les égyptiens en -2000.
Évidemment, une telle clé est facilement attaquable aujourd'hui (dans le cas d'un chiffrement par décalage, il existe donc vingt-cinq clés possible, ce qu'une attaque par **bruteforce** peut trouver en quelques secondes avec les machines actuelles.)
Évidemment, une telle clé est facilement attaquable aujourd'hui (dans le cas d'un chiffrement par décalage, il existe donc vingt-cinq clés possibles, ce qu'une attaque par **bruteforce** peut trouver en quelques secondes avec les machines actuelles.)
Plus sophistiqué, ***le chiffre de Vigénère*** utilise un décalage alphabétique également, mais avec une clé de plusieurs lettres répétée. Si la clé est NSI, la première et la quatrième lettre du message seront décalées de 14, la deuxième et la cinquième de 19, la troisième et la sixième de 9 etc. Ce chiffre a résisté longtemps au décryptage, et a été utilisé jusque pendant la guerre de Sécession.
@@ -47,17 +47,17 @@ Enfin, le fameux code ***Enigma*** utilisé par lAllemagne nazie pendant la
Un chiffrement est dit symétrique lorsque la clé utilisée pour chiffrer le message permet également de le déchiffrer.
On parle alors de ***clé secrète*** commune à l'ensemble des interlocuteurs d'une conversation, et va permettre de chiffrer les données lors de l'envoie, puis de les déchiffrer à leur réception.
On parle alors de ***clé secrète*** commune à l'ensemble des interlocuteurs d'une conversation, et va permettre de chiffrer les données lors de l'envoi, puis de les déchiffrer à leur réception.
<img src="assets/chiffrement_sym.png" alt="chiffrement_sym" style="zoom: 50%;" />
> Évidemment, il est necessaire de générer la clé et de la partager aux personnes / machines concernées par la communication, avant de commencer les échanges. Chaque machine pourra, dès lors, emettre ou recevoir des message chiffrés avec cette clé.
> Évidemment, il est nécessaire de générer la clé et de la partager aux personnes / machines concernées par la communication, avant de commencer les échanges. Chaque machine pourra, dès lors, émettre ou recevoir des messages chiffrés avec cette clé.
>
> Il est donc primordiale de passer par un canal de communication déjà sécurisé afin de transmettre la clé, le chiffrement symétrique étant donc impossible à utiliser pour initialiser une communication chiffré.
> Il est donc primordial de passer par un canal de communication déjà sécurisé afin de transmettre la clé, le chiffrement symétrique étant donc impossible à utiliser pour initialiser une communication chiffrée.
#### Pour la culture
Aujourd'hui, le standard utilisé en matière de chiffrement symétrique est l'[AES](https://fr.wikipedia.org/wiki/Advanced_Encryption_Standard) (Advanced Encryption Standard) qui utilise une clé pouvant aller jusqu'à 256 bits de longueur, ce qui laisse "juste" 2<sup>256</sup> possibilitées, soit 10<sup>77</sup> clés à tester.
Aujourd'hui, le standard utilisé en matière de chiffrement symétrique est l'[AES](https://fr.wikipedia.org/wiki/Advanced_Encryption_Standard) (Advanced Encryption Standard) qui utilise une clé pouvant aller jusqu'à 256 bits de longueur, ce qui laisse "juste" 2<sup>256</sup> possibilités, soit 10<sup>77</sup> clés à tester.
La meilleure méthode pour le casser restant, à ce jour, la force brute.
@@ -74,7 +74,7 @@ Créé très récemment (1976 !) par Whitfield Diffie et Martin Hellman, il port
- Partie privée (**clé privée** et notée *s* pour secret) que chaque personne conserve précieusement.
- Partie publique (**clé publique**, notée *p* pour publique) qui peut être révélée à tout le monde.
Pour faire simple, un message chiffré avec une partie d'une clé necessite l'utilisation de l'autre partie de la clé pour être déchiffré.
Pour faire simple, un message chiffré avec une partie d'une clé nécessite l'utilisation de l'autre partie de la clé pour être déchiffré.
Prenons Alice et Bob, deux personnes qui cherchent à communiquer de manière sécurisée. Cela se passera en trois étapes :
@@ -97,14 +97,15 @@ Avec ce type de chiffrement, la clé publique peut être partagée de manière o
Le chiffrement asymétrique utilise des fonctions mathématiques à sens unique.
Une fonction à sens unique est une fonction mathématique $f$ dont la réciproque $f^-1$ est très difficile à trouver (plusieurs milliers / millions d'années de calcul)
Cependant, une brèche secrète est toujours crée afin de pouvoir trouver cette réciproque.
Une fonction à sens unique est une fonction mathématique $f$ dont la réciproque $f^{-1}$ est très difficile à trouver (plusieurs milliers / millions d'années de calcul).
Cependant, une brèche secrète est toujours créée afin de pouvoir trouver cette réciproque.
Ici, la fonction est *p*, la clé publique. Et la brèche secrète, *s*, la clé privée.
Le chiffrement [RSA](https://fr.wikipedia.org/wiki/Chiffrement_RSA) est latechnologie la plus utilisée en matière de chiffrement asymétrique : crée en 1978 par Ronald Rivest, Adi Shamir et Leonard Adleman, ce protocole utilise des théorèmes arithmétiques connus (voir celui de Fermat en maths expertes)
Le chiffrement [RSA](https://fr.wikipedia.org/wiki/Chiffrement_RSA) est la technologie la plus utilisée en matière de chiffrement asymétrique : créé en 1978 par Ronald Rivest, Adi Shamir et Leonard Adleman, ce protocole utilise des théorèmes arithmétiques connus (voir celui de Fermat en maths expertes).
Ce chiffrement necessite une plus grand force de calcul ainsi que des clés de très grandes longueurs pour garantir la sécurité.
Ce chiffrement nécessite une plus grande force de calcul ainsi que des clés de très grandes longueurs pour garantir la sécurité.
#### Quels usages ?
@@ -114,16 +115,16 @@ Les signatures numériques, les échanges de clés secrètes afin de pouvoir ini
### 5. Le HTTPS
Le protocole HTTP ( Hyper Text Transfert Protocol) est celui utilisé pour le partage de pages web via Internet. Il transmet les requetes de pages et assure leur transport entre le client et le serveur.
Le protocole HTTP (Hyper Text Transfer Protocol) est celui utilisé pour le partage de pages web via Internet. Il transmet les requêtes de pages et assure leur transport entre le client et le serveur.
Mais ces échanges ne sont pas sécurisés. Il est donc necessaire de mettre en place des outils permettant de chiffrer les transmissions de requetes ainsi que les réponses, surtout en ce qui concerne les échanges monétaires et d'authentification.
Mais ces échanges ne sont pas sécurisés. Il est donc nécessaire de mettre en place des outils permettant de chiffrer les transmissions de requêtes ainsi que les réponses, surtout en ce qui concerne les échanges monétaires et d'authentification.
Deux principaux problèmes étaient pointés du doigt:
- Données qui transitent en clair et peuvent être lues par une tierce personne ([Man in the middle](https://fr.wikipedia.org/wiki/Attaque_de_l'homme_du_milieu))
- Une tierce personne peut se faire passer pour le serveur et recevoir vos données ([Fishing - Hameçonnage](https://fr.wikipedia.org/wiki/Hame%C3%A7onnage))
Deux protocoles ont emergé, **SSL** (Secure Sockets Layer) tout d'abord, puis son évolution, **TLS** (Transport Layer Security).
Deux protocoles ont émergé, **SSL** (Secure Sockets Layer) tout d'abord, puis son évolution, **TLS** (Transport Layer Security).
Couplé au HTTP, ils prennent le nom d'HTTPS ([Hyper Text Transfert Protocol Secure](https://fr.wikipedia.org/wiki/HyperText_Transfer_Protocol_Secure))
@@ -144,7 +145,7 @@ Ces protocoles agissent au niveau de la couche 5 du modèle OSI
--------
#### Souces
#### Sources
<a href="https://www.flaticon.com/free-icons/computer" title="computer icons">Computer icons created by Freepik - Flaticon</a>

View File

@@ -0,0 +1,427 @@
# TP : Gestionnaire de mots de passe
## Contexte
Vous développez un gestionnaire de mots de passe simplifié, similaire à **Bitwarden**, **1Password** ou **KeePass**. Ce type d'application permet de stocker de manière sécurisée tous ses identifiants en les chiffrant avec un mot de passe maître.
L'objectif est de comprendre les mécanismes de chiffrement symétrique et les bonnes pratiques de sécurité.
---
## Objectifs
- Implémenter un chiffrement symétrique simple
- Comprendre le rôle du mot de passe maître
- Stocker et récupérer des données chiffrées
- Générer des mots de passe robustes
---
## Partie 1 : Chiffrement XOR
### 1.1. Principe du XOR
L'opération XOR (ou exclusif) est fondamentale en cryptographie. Elle possède une propriété intéressante :
```
A XOR B XOR B = A
```
Autrement dit, appliquer deux fois XOR avec la même clé redonne le message original.
### 1.2. Implémentation
Implémenter la fonction de chiffrement XOR :
```python
def xor_chiffrement(message, cle):
"""
Chiffre ou déchiffre un message avec XOR.
:param message: (str) Le message à traiter
:param cle: (str) La clé de chiffrement
:return: (str) Le message chiffré/déchiffré
"""
resultat = ""
for i, caractere in enumerate(message):
# Récupérer le caractère de la clé (cyclique)
cle_char = cle[i % len(cle)]
# Appliquer XOR
nouveau_char = chr(ord(caractere) ^ ord(cle_char))
resultat += nouveau_char
return resultat
```
**Test** :
```python
>>> message = "Bonjour le monde"
>>> cle = "secret"
>>> chiffre = xor_chiffrement(message, cle)
>>> print(chiffre) # Caractères illisibles
>>> xor_chiffrement(chiffre, cle) # Déchiffrement
'Bonjour le monde'
```
### 1.3. Questions
1. Pourquoi la clé est-elle utilisée de manière cyclique ?
2. Que se passe-t-il si la clé est aussi longue que le message ?
3. Pourquoi XOR seul n'est-il pas suffisamment sécurisé ?
---
## Partie 2 : Classe MotDePasse
### 2.1. Structure d'une entrée
Créer une classe pour représenter un mot de passe stocké :
```python
class MotDePasse:
"""Représente un identifiant stocké."""
def __init__(self, site, identifiant, mot_de_passe):
"""
Initialise une entrée.
:param site: (str) Nom du site/service
:param identifiant: (str) Nom d'utilisateur ou email
:param mot_de_passe: (str) Mot de passe en clair
"""
self.site = site
self.identifiant = identifiant
self.mot_de_passe = mot_de_passe
def __repr__(self):
# Ne pas afficher le mot de passe en clair !
return f"MotDePasse({self.site}, {self.identifiant}, ****)"
def to_string(self):
"""Convertit en chaîne pour le stockage."""
return f"{self.site}|{self.identifiant}|{self.mot_de_passe}"
@staticmethod
def from_string(chaine):
"""Crée un objet depuis une chaîne."""
parties = chaine.split("|")
return MotDePasse(parties[0], parties[1], parties[2])
```
---
## Partie 3 : Gestionnaire principal
### 3.1. Classe GestionnaireMDP
Implémenter le gestionnaire complet :
```python
class GestionnaireMDP:
"""Gestionnaire de mots de passe sécurisé."""
def __init__(self, mot_de_passe_maitre):
"""
Initialise le gestionnaire.
:param mot_de_passe_maitre: (str) Clé principale de chiffrement
"""
self.cle = mot_de_passe_maitre
self.coffre = [] # Liste des MotDePasse
def ajouter(self, site, identifiant, mot_de_passe):
"""
Ajoute un nouveau mot de passe au coffre.
:param site: (str) Nom du site
:param identifiant: (str) Identifiant
:param mot_de_passe: (str) Mot de passe
"""
# À compléter
pass
def rechercher(self, site):
"""
Recherche un mot de passe par site.
:param site: (str) Nom du site à rechercher
:return: (MotDePasse ou None) L'entrée trouvée
"""
# À compléter
pass
def supprimer(self, site):
"""
Supprime une entrée du coffre.
:param site: (str) Nom du site à supprimer
:return: (bool) True si supprimé, False sinon
"""
# À compléter
pass
def lister_sites(self):
"""
Liste tous les sites enregistrés (sans les mots de passe).
:return: (list) Liste des noms de sites
"""
# À compléter
pass
```
### 3.2. Sauvegarde chiffrée
Ajouter les méthodes de sauvegarde et chargement :
```python
def sauvegarder(self, fichier):
"""
Sauvegarde le coffre dans un fichier chiffré.
:param fichier: (str) Chemin du fichier
"""
# Convertir toutes les entrées en une chaîne
contenu = "\n".join([mdp.to_string() for mdp in self.coffre])
# Chiffrer le contenu
contenu_chiffre = xor_chiffrement(contenu, self.cle)
# Écrire dans le fichier
with open(fichier, 'w', encoding='utf-8') as f:
f.write(contenu_chiffre)
def charger(self, fichier):
"""
Charge le coffre depuis un fichier chiffré.
:param fichier: (str) Chemin du fichier
"""
# À compléter
pass
```
---
## Partie 4 : Génération de mots de passe
### 4.1. Générateur simple
Créer une fonction de génération de mots de passe robustes :
```python
import random
import string
def generer_mot_de_passe(longueur=16, majuscules=True, chiffres=True, speciaux=True):
"""
Génère un mot de passe aléatoire robuste.
:param longueur: (int) Longueur du mot de passe
:param majuscules: (bool) Inclure des majuscules
:param chiffres: (bool) Inclure des chiffres
:param speciaux: (bool) Inclure des caractères spéciaux
:return: (str) Mot de passe généré
"""
caracteres = string.ascii_lowercase
if majuscules:
caracteres += string.ascii_uppercase
if chiffres:
caracteres += string.digits
if speciaux:
caracteres += "!@#$%^&*()_+-=[]{}|;:,.<>?"
# À compléter : générer le mot de passe
pass
```
### 4.2. Vérification de robustesse
Implémenter une fonction d'évaluation :
```python
def evaluer_robustesse(mot_de_passe):
"""
Évalue la robustesse d'un mot de passe.
:param mot_de_passe: (str) Le mot de passe à évaluer
:return: (str) Niveau de robustesse (Faible, Moyen, Fort, Très fort)
"""
score = 0
# Longueur
if len(mot_de_passe) >= 8:
score += 1
if len(mot_de_passe) >= 12:
score += 1
if len(mot_de_passe) >= 16:
score += 1
# Variété de caractères
if any(c.islower() for c in mot_de_passe):
score += 1
if any(c.isupper() for c in mot_de_passe):
score += 1
if any(c.isdigit() for c in mot_de_passe):
score += 1
if any(c in "!@#$%^&*()_+-=[]{}|;:,.<>?" for c in mot_de_passe):
score += 1
# À compléter : retourner le niveau selon le score
pass
```
---
## Partie 5 : Interface utilisateur
### 5.1. Menu interactif
Créer une interface en ligne de commande :
```python
def menu_principal():
"""Affiche le menu principal."""
print("\n" + "=" * 40)
print(" GESTIONNAIRE DE MOTS DE PASSE")
print("=" * 40)
print("1. Ajouter un mot de passe")
print("2. Rechercher un mot de passe")
print("3. Générer un mot de passe")
print("4. Lister les sites")
print("5. Supprimer une entrée")
print("6. Sauvegarder")
print("7. Quitter")
print("=" * 40)
return input("Votre choix : ")
def application():
"""Lance l'application."""
print("Bienvenue dans le gestionnaire de mots de passe !")
mdp_maitre = input("Entrez votre mot de passe maître : ")
gestionnaire = GestionnaireMDP(mdp_maitre)
# Tenter de charger un coffre existant
try:
gestionnaire.charger("coffre.dat")
print("Coffre chargé avec succès.")
except FileNotFoundError:
print("Nouveau coffre créé.")
while True:
choix = menu_principal()
if choix == "1":
# Ajouter
site = input("Site : ")
identifiant = input("Identifiant : ")
mdp = input("Mot de passe (laisser vide pour générer) : ")
if mdp == "":
mdp = generer_mot_de_passe()
print(f"Mot de passe généré : {mdp}")
gestionnaire.ajouter(site, identifiant, mdp)
print("Ajouté !")
elif choix == "2":
# Rechercher
site = input("Site à rechercher : ")
resultat = gestionnaire.rechercher(site)
if resultat:
print(f"Site : {resultat.site}")
print(f"Identifiant : {resultat.identifiant}")
print(f"Mot de passe : {resultat.mot_de_passe}")
else:
print("Site non trouvé.")
elif choix == "3":
# Générer
longueur = int(input("Longueur (défaut 16) : ") or "16")
mdp = generer_mot_de_passe(longueur)
print(f"Mot de passe généré : {mdp}")
print(f"Robustesse : {evaluer_robustesse(mdp)}")
elif choix == "4":
# Lister
sites = gestionnaire.lister_sites()
print("Sites enregistrés :")
for s in sites:
print(f" - {s}")
elif choix == "5":
# Supprimer
site = input("Site à supprimer : ")
if gestionnaire.supprimer(site):
print("Supprimé !")
else:
print("Site non trouvé.")
elif choix == "6":
# Sauvegarder
gestionnaire.sauvegarder("coffre.dat")
print("Coffre sauvegardé.")
elif choix == "7":
gestionnaire.sauvegarder("coffre.dat")
print("Au revoir !")
break
```
---
## Partie 6 : Améliorations (Bonus)
### 6.1. Hashage du mot de passe maître
Au lieu de stocker le mot de passe maître directement, utiliser un hash :
```python
import hashlib
def hasher(mot_de_passe):
"""Retourne le hash SHA-256 du mot de passe."""
return hashlib.sha256(mot_de_passe.encode()).hexdigest()
```
### 6.2. Dérivation de clé
Utiliser une fonction de dérivation de clé (PBKDF2) pour renforcer la sécurité.
### 6.3. Chiffrement AES
Remplacer XOR par AES pour un chiffrement professionnel (nécessite la bibliothèque `cryptography`).
---
## Questions de synthèse
1. Pourquoi ne faut-il **jamais** stocker un mot de passe en clair ?
2. Quelle est la différence entre **chiffrement** et **hashage** ?
3. Pourquoi le mot de passe maître doit-il être robuste ?
4. Quels sont les risques si quelqu'un obtient le fichier `coffre.dat` ?
5. Comment les vrais gestionnaires de mots de passe (Bitwarden, 1Password) améliorent-ils la sécurité ?
---
## Barème indicatif
| Partie | Points |
|--------|--------|
| Partie 1 : Chiffrement XOR | 3 |
| Partie 2 : Classe MotDePasse | 2 |
| Partie 3 : Gestionnaire | 5 |
| Partie 4 : Génération | 4 |
| Partie 5 : Interface | 4 |
| Partie 6 : Bonus | 2 |
| **Total** | **20** |
---
Auteur : Florian Mathieu
Licence CC BY NC
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.

0
Sécurité/Untitled-1 Normal file
View File

BIN
Sécurité/assets/TLS.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -0,0 +1,208 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Jan 8 15:27:05 2021
@author: pjoulaud
"""
from numpy.random import permutation
# Pour créer des permutations aléatoirement
class ECB:
"""
Classe dont les instances sont des cryptosystèmes dépendant de la donnée de leur clé
sous la forme d'une permutation
"""
def __init__(self, taille = None, images = None):
"""
Un cryptosystème ECB est défini par la donnée d'une permutation
Une permutation a pour attributs sa taille et la liste de ses images.
Un des deux doit être donné au moment de l'instanciation.
Une Permutation est si besoin créée aléatoirement en utilisant la fonction permutation
du module numpy.random
>>> ECB(images = [1,0,2]).images
[1, 0, 2]
"""
# si les images ne sont pas données, on les tire aléatoirement
if images :
liste_reference = [i for i in range(len(images))]
images_ordonnees = images[:]
images_ordonnees.sort()
#print(f"liste_reference {liste_reference}-images_ordonnees {images_ordonnees}")
if images_ordonnees==liste_reference:
self.images = images
else :
self.images = list(permutation(range(taille)))
def __repr__(self):
"""
On représente les images de la permutation :
>>> ECB(images=[0, 3, 2, 4, 1])
0 3 2 4 1
"""
return " ".join([str(image) for image in self.images])
def __invert__(self) :
"""
Permet d'obtenir la permutation réciproque de self avec ~
>>> mon_ecb = ECB(images = [1,3,0,2])
>>> mon_ecb
1 3 0 2
>>> ~mon_ecb
2 0 3 1
"""
ma_liste = []
for i in range(len(self.images)):
ma_liste.append(self.images.index(i))
mon_ecb = ECB(images=ma_liste)
return mon_ecb
@property
def taille(self):
"""
>>> ECB(6).taille
6
"""
# si la taille n'est pas donnée, c'est la longueur de la liste des images
return len(self.images)
def permute(self, une_liste):
"""
Permute les éléments d'une liste donnée en argument
Si xs = [xs[0], xs[1], xs[2],...] et si p est une permutation alors
mon_ecb.permute(xs) = [xs[p(0)], xs[p(1)], xs[p(2)],...]
>>> mon_ecb = ECB(images=[0, 3, 2, 4, 1])
>>> ma_liste = ['a', 'b', 'c', 'd', 'e']
>>> mon_ecb.permute(ma_liste)
['a', 'd', 'c', 'e', 'b']
"""
if len(une_liste)>=self.taille:
return [une_liste[i] for i in self.images]
def chaine2texte(self, une_chaine):
"""
Prend une chaine de caractères uniquement composée de 0 et de 1
Les cryptosystèmes manipulent des chaines de bits mais les humains veulent du texte
Cette méthode permet de transformer une trame de caractères codés sur un octet
en la chaîne de caractères correspondante.
>>> mon_ecb = ECB(images=[0, 3, 2, 4, 1])
>>> mon_ecb.chaine2texte('11101001')
'é'
>>> mon_ecb.chaine2texte('111010011110100111101001')
'ééé'
À comparer avec :
>>> chr(0b11101001)
'é'
"""
ma_chaine = ''
#car_a_completer = len(une_chaine)%8
#if car_a_completer!=0:
# une_chaine = '0'*(8-car_a_completer)+une_chaine
for i in range(len(une_chaine)//8):
ma_chaine += chr(int(une_chaine[8*i:8*(i+1)],2))
return ma_chaine
def texte2chaine(self, une_chaine):
"""
On transforme une instance de Texte en la chaîne caractères uniquement composée de 0 et de 1.
>>> mon_ecb = ECB(images=[0, 3, 2, 4, 1])
>>> mon_ecb.texte2chaine('ab')
'0110000101100010'
>>> mon_ecb.chaine2texte(mon_ecb.texte2chaine('azerty'))
'azerty'
"""
while len(une_chaine)%len(self.images) != 0:
une_chaine += ' '
ma_chaine = ''
for i in une_chaine :
for j in "{:08b}".format(ord(i)):
ma_chaine += j
return ma_chaine
def decoupe_en_blocs(self, les_caracteres):
"""
Prend une chaîne de caractères et la transforme en chaînes de caractères de
longueur celle de la permutation en complétant éventuellement par des '0'.
Méthode à usage interne facilitant l'écriture de la fonction chiffre
>>> mon_ecb = ECB(images=[1,0,3,2])
>>> mon_ecb.decoupe_en_blocs('123456789123456789')
['12345', '67891', '23456', '78900']
"""
ma_liste = []
car_a_completer = len(les_caracteres)%self.taille
if car_a_completer!=0:
les_caracteres = '0'*(self.taille-car_a_completer) + les_caracteres
car_a_completer = len(les_caracteres)%8
if car_a_completer!=0:
les_caracteres = '0'*(8-car_a_completer) + les_caracteres
nb_car_par_elem = len(les_caracteres)//self.taille
for i in range(self.taille):
ma_liste.append(les_caracteres[i*nb_car_par_elem:(i+1)*nb_car_par_elem])
return ma_liste
def permute(self, les_caracteres):
"""
Renvoie la liste des permutations des blocs de longueur celle de la permutation
construits à partir d'une chaîne de caracteres.
>>> mon_ecb = ECB(images=[1,0,3,2])
>>> mon_ecb.permute('123456789123456789')
'67891123457890023456'
"""
ma_liste_de_chaine = self.decoupe_en_blocs(les_caracteres)
#print(f"{ma_liste_de_chaine} toto")
ma_liste_de_chaine2 = []
for indice in self.images :
#print(f"{une_chaine}")
#ma_chaine = Chaine(self.cle.permute(une_chaine.entiers))
#print(f"{ma_chaine}")
ma_liste_de_chaine2.append(ma_liste_de_chaine[indice])
#print(f"{ma_liste_de_chaine} - {ma_liste_de_chaine2}")
return "".join(ma_liste_de_chaine2)
def chiffre(self, message):
"""
Retourne le message chiffré sous forme de chaîne de caracères.
Utilise les sous-méthodes précédentes.
>>> mon_ecb = ECB(images=[0, 1, 3, 4, 2])
>>> chaine_0_1 = mon_ecb.texte2chaine('hello world')
>>> chaine_0_1_code = mon_ecb.permute(chaine_0_1)
>>> message_code = mon_ecb.chaine2texte(chaine_0_1_code)
"""
chaine_0_1 = mon_ecb.texte2chaine(message)
#print(f"{chaine_0_1}")
chaine_0_1_code = mon_ecb.permute(chaine_0_1)
#print(f"{chaine_0_1_code}")
return mon_ecb.chaine2texte(chaine_0_1_code)
def dechiffre(self, message_code):
ecb_inverse = ~self
chaine_0_1 = ecb_inverse.texte2chaine(message_code)
print(f"0 : {chaine_0_1}")
chaine_0_1_code = ecb_inverse.permute(chaine_0_1)
print(f"1 : {chaine_0_1_code}")
return ecb_inverse.chaine2texte(chaine_0_1_code)
#mon_ecb = ECB(6)
#print(mon_ecb)
#print(mon_ecb.taille)
import doctest
doctest.testmod()
mon_ecb = ECB(25)
toto = mon_ecb.chiffre("Et si quelquefois, sur les marches d'un palais, sur l'herbe verte d'un fossé, vous vous réveillez, l'ivresse déjà diminuée ou disparue, demandez au vent, à la vague, à l'étoile, à l'oiseau, à l'horloge; à tout ce qui fuit, à tout ce qui gémit, à tout ce qui roule, à tout ce qui chante, à tout ce qui parle, demandez quelle heure il est. Et le vent, la vague, l'étoile, l'oiseau, l'horloge, vous répondront, il est l'heure de s'enivrer ; pour ne pas être les esclaves martyrisés du temps, enivrez-vous, enivrez-vous sans cesse de vin, de poésie, de vertu, à votre guise")
print(toto)
titi = mon_ecb.dechiffre(toto)
print(titi)

View File

@@ -0,0 +1,67 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Feb 3 12:41:14 2021
@author: pjoulaud
"""
CLE_PRIV_P = 53
CLE_PRIV_Q = 97
CLE_PUB_E = 7
CLE_PUB_N = CLE_PRIV_P * CLE_PRIV_Q
def conversion_message_en_nombre(chaine, bloc=3):
nombre = ""
for i in chaine :
nombre += str(ord(i)-ord('A')+1)
#print(nombre)
while len(nombre)%bloc != 0 :
nombre = '0' + nombre
return nombre
def chiffrement(chaine_num, bloc=3, e=CLE_PUB_E, n=CLE_PUB_N):
code = ""
for i in range(len(chaine_num)//bloc):
morceau_chaine_num = chaine_num[i*bloc:(i+1)*bloc]
#print(morceau_chaine_num)
morceau_chaine_num = int(morceau_chaine_num)
morceau_chaine_num = morceau_chaine_num**e
morceau_chaine_num = morceau_chaine_num%n
morceau_chaine_num = str(morceau_chaine_num)
code += morceau_chaine_num
code += ' '
return code[:-1]
def dechiffrement(chaine_num, bloc=2, e=CLE_PUB_E, n=CLE_PUB_N, p=CLE_PRIV_P, q=CLE_PRIV_Q):
nombre = 1
while nombre%e != 0 :
nombre += (p-1)*(q-1)
print(nombre)
d = nombre//e
liste_num = chaine_num.split(' ')
print(liste_num)
liste_decod = []
for i in liste_num :
decode = (int(i)**d) % n
liste_decod.append(str(decode))
print(liste_decod)
chaine_decode = ''.join(liste_decod)
print(chaine_decode)
message = ""
a_completer = len(chaine_decode)%bloc
for i in range(a_completer):
chaine_decode = '0' + chaine_decode
for i in range(len(chaine_decode)//bloc):
morceau_chaine_num = int(chaine_decode[i*bloc:(i+1)*bloc])
message += chr(morceau_chaine_num+ord('A')-1)
print(morceau_chaine_num, ' ',message)
return message
ma_chaine_num = conversion_message_en_nombre('BONJOUR')
print(ma_chaine_num)
mon_code = chiffrement(ma_chaine_num)
print(mon_code)
mon_message = dechiffrement(mon_code)
print('message : ', mon_message)

View File

@@ -0,0 +1,114 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Feb 3 12:41:14 2021
@author: pjoulaud
"""
BLOC_ASCII = 2
CLE_PRIV_P = 53
CLE_PRIV_Q = 97
CLE_PUB_E = 11
CLE_PUB_N = CLE_PRIV_P * CLE_PRIV_Q
def car_to_fig(chaine : str) -> str:
"""
Transforme une chaine de caractères en une chaine de caractères
correspondant à leurs numéros d' ordre.
Attention ! Chaque numéro d' ordre est codé sur BLOC_ASCII chiffres
Arguments : chaine, chaine à coder de type str
Retourne : nombre, une chaine de caractères avec tous les numéros d' ordre
>>> car_to_fig('A')
'01'
>>> car_to_fig('ABZ')
'010226'
"""
nombre = ""
liste = []
for i in chaine :
ordre = str(ord(i)-ord('A')+1)
while len(ordre)%BLOC_ASCII != 0:
ordre = '0'+ordre
liste.append(ordre)
#print(liste)
nombre = "".join(liste)
return nombre
def chiffrement(chaine, bloc=3, e=CLE_PUB_E, n=CLE_PUB_N):
"""
Transforme une chaine de caractères en une chaine de caractères
correspondant à leurs numéros d' ordre codés.
Chaque numéro est séparé par un espace
Arguments : chaine, chaine à coder de type str
Retourne : nombre, une chaine de caractères avec les numéros codés
>>> chiffrement('A')
'1689'
>>> chiffrement('ABZ')
'755 2705'
"""
chaine_num = car_to_fig(chaine)
while len(chaine_num)%bloc != 0 :
chaine_num = '0'+chaine_num
print("code du message initial :\n",chaine_num)
liste = []
for i in range(len(chaine_num)//bloc):
morceau_chaine_num = chaine_num[i*bloc:(i+1)*bloc]
morceau_chaine_num = int(morceau_chaine_num)
morceau_chaine_num = morceau_chaine_num**e
morceau_chaine_num = morceau_chaine_num%n
liste.append(str(morceau_chaine_num))
code = " ".join(liste)
print("code du message chiffre :\n", code)
return code
def dechiffrement(chaine_num, bloc=3, e=CLE_PUB_E, n=CLE_PUB_N, p=CLE_PRIV_P, q=CLE_PRIV_Q):
"""
Transforme une chaine de caractères correspondant à leurs numéros d' ordre
codés en message décodé.
Arguments : chaine_num, chaine de codes de type str
Retourne : message, une chaine de caractères avec le message décodé
>>> dechiffrement(chiffrement('A'))
'A'
>>> dechiffrement(chiffrement('ABZ'))
'ABZ'
"""
nombre = 1
# PARTIE LA PLUS DIFFICILE
# Il faut déterminer e tel que e.d = 1 mod [(p-1)(q-1)]
# Mon astuce : On incrémente de (p-1)(q-1) jusqu' à obtenir un nombre
# divisible par e
while nombre%e != 0 :
nombre += (p-1)*(q-1)
d = nombre//e
liste_num = chaine_num.split(' ')
liste_decod = []
for i in liste_num :
decode = (int(i)**d) % n
decode = str(decode)
while len(decode)%bloc != 0:
decode = '0'+decode
liste_decod.append(decode)
chaine_decode = ''.join(liste_decod)
message = ""
while len(chaine_decode)%BLOC_ASCII != 0:
chaine_decode = '0' + chaine_decode
print("code du message dechiffre : \n",chaine_decode)
for i in range(len(chaine_decode)//BLOC_ASCII, 0, -1):
morceau_chaine_num = chaine_decode[(i-1)*BLOC_ASCII:i*BLOC_ASCII]
morceau_chaine_num = int(morceau_chaine_num)
if morceau_chaine_num != 0:
message = chr(morceau_chaine_num+ord('A')-1) + message
print("Message dechiffre :\n", message)
return message
if __name__=='__main__':
if (CLE_PRIV_P-1)*(CLE_PRIV_Q-1)%CLE_PUB_E == 0:
raise Exception("e n' est pas premier avec (p-1)(q-1)")
chaine_depart = 'ABONJOURLESFRANCAISESETLESFRANCAIS'
print("chaine de depart : ",chaine_depart)
mon_code = chiffrement(chaine_depart, bloc=4)
#print("code : ", mon_code)
mon_message = dechiffrement(mon_code, bloc=4)
print('message : ', mon_message)

View File

@@ -0,0 +1,36 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Feb 3 10:55:48 2021
@author: pjoulaud
"""
def codage_binaire(message : str, code : str) -> str:
"""
Fonction qui code un message à l' aide d' une clé. Le message codé resulte
d' un OU exclusif bit à bit
Argument :
- message : Message à coder de type str
- code : Clé de codage de type str
Retourne :
- res : Message codé bit à bit
>>> codage_binaire(codage_binaire("Hello World!", "toto"), "toto")
'Hello World!'
"""
i = 0
res = ''
for car in message :
ord_car = ord(car)^ord(code[i%len(code)])
#print(f"car : {ord(car):08b}\ncode : {ord(code[i%len(code)]):08b}\n => {ord_car:08b} \n")
res += chr(ord_car)
i += 1
return res
#CORPS DE PROGRAMME
if __name__=='__main__':
import doctest
doctest.testmod()
#message_code = codage_binaire('Hello World', 'toto')
#input("Tapez Entree pour continuer")
#message_decode = codage_binaire(message_code, 'toto')

View File

@@ -0,0 +1,84 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Jan 7 20:03:19 2021
@author: pjoulaud
"""
def code_cesar(texte, decalage):
"""
Fonction qui code un texte suivant la méthode de césar (chaque lettre est
décalé).
Arguments ::
- texte : message à coder de type str
- decalage : indice de décalage de type int
Renvoie ::
- texte_chiffre : le message codé de type str
>>> code_cesar("VENI, VIDI, DICI", 3)
YHQL, YLGL, GLFL
"""
texte_chiffre = ""
for lettre in texte :
if lettre in ('A','B','C','D','E','F','G','H','I','J','K','L','M','N',
'O','P','Q','R','S','T','U','V','W','X','Y','Z'):
ordre_dans_alphabet = ord(lettre)+decalage-ord('A')
texte_chiffre += chr(ordre_dans_alphabet%26+ord('A'))
else:
texte_chiffre += lettre
return texte_chiffre
def code_cesar2(texte, decalage):
"""
Fonction qui code un texte suivant la méthode de césar (chaque lettre est
décalé).
Variante proposée par C. Veillon
Arguments ::
- texte : message à coder de type str
- decalage : indice de décalage de type int
Renvoie ::
- texte_chiffre : le message codé de type str
>>> code_cesar("VENI, VIDI, DICI", 3)
YHQL, YLGL, GLFL
"""
texte_chiffre = ""
liste_avec_lettres = []
for i in range(65, 91):
liste_avec_lettres.append(chr(i))
#print(liste_avec_lettres)
for lettre in texte :
if lettre in liste_avec_lettres:
for i in range(len(liste_avec_lettres)):
if lettre == liste_avec_lettres[i]:
texte_chiffre += liste_avec_lettres[(i+decalage)%26]
else :
texte_chiffre += lettre
return texte_chiffre
def decode_cesar(texte, decalage):
"""
Fonction qui décode un texte suivant la méthode de césar.
Arguments ::
- texte : message codé de type str
- decalage : indice de décalage de type int
Renvoie ::
- le message décodé de type str
>>> code_cesar("YHQL, YLGL, GLFL", 3)
VENI, VIDI, DICI
"""
return code_cesar(texte, -decalage)
if __name__ == '__main__':
#ACTION DE CODAGE
toto=code_cesar('VENI, VIDI, DICI',3)
print("message codé : ", toto)
#ACTION DE DECODAGE
toto=decode_cesar(toto,3)
print("message décodé : ", toto)
#ACTION DE CODAGE
toto=code_cesar2('VENI, VIDI, DICI',3)
print("message codé (méthode C. Veillon) : ", toto)
#ACTION DE DECODAGE
toto=code_cesar2(toto,-3)
print("message décodé (méthode C. Veillon) : ", toto)

View File

@@ -0,0 +1,38 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Jan 7 20:03:19 2021
@author: pjoulaud
"""
def codage_inverse(texte):
"""
Fonction qui code un texte suivant la méthode de codage inverse
(A <=> Z, B <=> Y, C <=> X, ...).
Arguments ::
- texte : message à coder de type str
Renvoie ::
- texte_chiffre : le message codé de type str
>>> codage_inverse('VENI, VIDI, DICI')
FWNS, FSXS, XSYS
>>> codage_inverse(codage_inverse('VENI, VIDI, DICI'))
VENI, VIDI, DICI
"""
texte_chiffre = ""
for lettre in texte :
if lettre in ('A','B','C','D','E','F','G','H','I','J','K','L','M','N',
'O','P','Q','R','S','T','U','V','W','X','Y','Z'):
ordre_dans_alphabet = 26-ord(lettre)-ord('A')
texte_chiffre += chr(ordre_dans_alphabet%26+ord('A'))
else:
texte_chiffre += lettre
return texte_chiffre
if __name__ == '__main__':
#ACTION DE CODAGE
toto=codage_inverse('VENI, VIDI, DICI')
print(toto)
#ACTION DE CODAGE
toto=codage_inverse(toto)
print(toto)

View File

@@ -0,0 +1,89 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Jan 7 20:03:19 2021
@author: pjoulaud
"""
from random import randint
def genere_table():
"""
Fonction qui génère aléatoirement une table de correspondance entre lettres
Les clés correspondent à chaque lettre de l' alphabet et les valeurs à une
autre lettre prise au hasard.
Arguments :
- Aucun
Retourne :
- dico_chiffrement de type dict
>>> toto = list(genere_table().keys())
>>> toto.sort()
>>> print(toto)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
>>> toto = list(genere_table().values())
>>> toto.sort()
>>> print(toto)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
"""
dico_chiffrement = {}
toutes_les_lettres = ('A','B','C','D','E','F','G','H','I','J','K','L','M','N',
'O','P','Q','R','S','T','U','V','W','X','Y','Z')
liste_lettres = list(toutes_les_lettres)
for lettre in toutes_les_lettres:
position = randint(0, len(liste_lettres)-1)
lettre_correspondante = liste_lettres[position]
dico_chiffrement[lettre] = lettre_correspondante
liste_lettres.remove(lettre_correspondante)
return dico_chiffrement
def code_par_substitution(texte, table):
"""
Fonction qui code un message grâce à une table de correspondance.
Arguments :
- texte : Message à coder de type str
- table : Table de correspondance de type dict
Retourne :
- texte_chiffre : Message codé de type str
>>> ma_table = {'A': 'H', 'B': 'A', 'C': 'F', 'D': 'M', 'E': 'D', 'F': 'T',\
'G': 'Z', 'H': 'W', 'I': 'L', 'J': 'X', 'K': 'G', 'L': 'S', 'M': 'I',\
'N': 'V', 'O': 'R', 'P': 'C', 'Q': 'O', 'R': 'E', 'S': 'N', 'T': 'Y',\
'U': 'U', 'V': 'K', 'W': 'B', 'X': 'J', 'Y': 'Q', 'Z': 'P'}
>>> code_par_substitution("BONJOUR TOUT LE MONDE", ma_table)
'ARVXRUE YRUY SD IRVMD'
"""
texte_chiffre = ""
for lettre in texte :
if lettre in ('A','B','C','D','E','F','G','H','I','J','K','L','M','N',
'O','P','Q','R','S','T','U','V','W','X','Y','Z'):
texte_chiffre += table[lettre]
else:
texte_chiffre += lettre
return texte_chiffre
def decode_par_substitution(texte, table):
"""
Fonction qui décode un message grâce à une table de correspondance.
Arguments :
- texte : Message codé de type str
- table : Table de correspondance de type dict
Retourne :
- texte_chiffre : Message décodé de type str
>>> ma_table = genere_table()
>>> decode_par_substitution(code_par_substitution("BONJOUR TOUT LE MONDE", ma_table), ma_table)
'BONJOUR TOUT LE MONDE'
"""
nouvelle_table = {}
for cle, valeur in table.items():
nouvelle_table[valeur]=cle
return code_par_substitution(texte, nouvelle_table)
if __name__=='__main__':
import doctest
doctest.testmod()
#ma_table_chiffrement = genere_table()
#print(ma_table_chiffrement)
#message_code = code_par_substitution("BONJOUR TOUT LE MONDE", ma_table_chiffrement)
#print(message_code)
#message_decode = decode_par_substitution(message_code, ma_table_chiffrement)
#print(message_decode)

View File

@@ -0,0 +1,115 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Feb 16 12:30:05 2022
@author: pjoulaud
"""
PREM_CARAC = ord('A') - 1
DER_CARAC = ord('Z')
#PREM_CARAC = 32
#DER_CARAC = 126
#PREM_CARAC = 0x21
#DER_CARAC = 0x1f0df
MODULO = DER_CARAC - PREM_CARAC
def codage_cle_chiffrement(chaine_a_coder, cle):
"""
Fonction de codage par substitution polyalphabetique
Arguments :
- chaine_a_coder : Message à coder de type str
- cle : clé de chiffrement de type str
Retourne :
- chaine_chiffree : Message codé de type str
>>> codage_cle_chiffrement("JE TIENS ENFIN LA GAULE", "SCIENCE")
'CH CNSQX XQONB OF ZDDQS'
"""
chaine_chiffree = ""
i = 0
for car in chaine_a_coder :
if car != " ":
ord_car = ord(car) - PREM_CARAC
ord_cle = ord(cle[i%len(cle)]) - PREM_CARAC
sum_ord = ord_car + ord_cle
#print(f"car : {car} - ord_car : {ord_car} - ord_cle : {ord_cle} - sum_ord : {sum_ord} - ")
if sum_ord <= MODULO :
car2 = chr(PREM_CARAC + sum_ord)
else :
car2 = chr(PREM_CARAC + sum_ord%MODULO)
i += 1
else :
car2 = car
chaine_chiffree += car2
return chaine_chiffree
def car_to_num(c):
return ord(c) - PREM_CARAC
def num_to_car(n):
if n <= MODULO :
return chr(PREM_CARAC + n)
else :
return chr(PREM_CARAC + n%MODULO)
def codage_cle_chiffrement2(chaine_a_coder, cle):
"""
Fonction de codage par substitution polyalphabetique - 2e version
Passe par deux fonctions (car_to_num et num_to_car) pour convertir
de caractères vers valeur numérique et vice-versa.
Arguments :
- chaine_a_coder : Message à coder de type str
- cle : clé de chiffrement de type str
Retourne :
- chaine_chiffree : Message codé de type str
>>> codage_cle_chiffrement2("JE TIENS ENFIN LA GAULE", "SCIENCE")
'CH CNSQX XQONB OF ZDDQS'
"""
chaine_chiffree = ""
i = 0
for car in chaine_a_coder :
if car != " ":
ord_car = car_to_num(car)
ord_cle = car_to_num(cle[i%len(cle)])
sum_ord = ord_car + ord_cle
#print(f"car : {car} - ord_car : {ord_car} - ord_cle : {ord_cle} - sum_ord : {sum_ord} - ")
car2 = num_to_car(sum_ord)
i += 1
else :
car2 = car
chaine_chiffree += car2
return chaine_chiffree
def decodage_cle_chiffrement(chaine_codee, cle):
"""
Fonction de décodage par substitution polyalphabetique
Arguments :
- chaine_codee : Message codé de type str
- cle : clé de chiffrement de type str
Retourne :
- chaine_clair : Message décodé de type str
>>> decodage_cle_chiffrement(codage_cle_chiffrement("JE TIENS ENFIN LA GAULE", "SCIENCE"), "SCIENCE")
'JE TIENS ENFIN LA GAULE'
"""
chaine_clair = ""
i = 0
for car in chaine_codee :
if car != " ":
ord_car = ord(car) - PREM_CARAC
ord_cle = ord(cle[i%len(cle)]) - PREM_CARAC
sum_ord = ord_car - ord_cle + MODULO
#print(f"car : {car} - ord_car : {ord_car} - ord_cle : {ord_cle} - sum_ord : {sum_ord} - ")
if sum_ord <= MODULO :
car2 = chr(PREM_CARAC + sum_ord)
else :
car2 = chr(PREM_CARAC + sum_ord%MODULO)
i += 1
else :
car2 = car
chaine_clair += car2
return chaine_clair
#CORPS DE PROGRAMME
if __name__=='__main__':
import doctest
doctest.testmod()

View File

@@ -0,0 +1,4 @@
ȹɜȐɕɣɤȐɤɟɥɚɟɥɢɣȐɑɣɣɕɪȐɔ˙ɜəɓɑɤȐɔɕȐɦɟɥɜɟəɢȐɥɤəɜəɣɕɢȐɥɞɕȐɑɤɤɑɡɥɕȐɣɤɑɤəɣɤəɡɥɕȐːȐɜȗɑəɔɕȐɔȗɥɞȐɤɕɨɤɕȐɤɢɟɠȐɠɕɤəɤȞ
ȽɑəɣȐɓɕɤȐɕɨɕɢɓəɓɕȐɞɕȐɣȗɑɔɢɕɣɣɕȐɠɑɣȐːȐɞȗəɝɠɟɢɤɕȐɡɥəȐȑȐȾɟɞȜȐɣəȐɦɟɥɣȐɓɘɕɢɓɘɕɪȐɓɕɤȐɕɨɕɢɓəɓɕȜȐɓȗɕɣɤȐɡɥɕȐɦɟɥɣȐɖɑəɤɕɣȐɠɑɢɤəɕȐɔɕȐɜȗ˙ɜəɤɕȞȐȿɥəȜȐɞɟɥɣȐɟɣɟɞɣȐɜɕȐɔəɢɕȐȪȐɦɟɥɣȐ˚ɤɕɣȐɜȗɕɣɠɟəɢȐɔɕȐɜȗɘɥɝɑɞəɤ˙ȐȑȐɆɟɥɣȐɑɦɕɪȐɓɘɟəɣəȐɔɕȐɠɟɥɢɣɥəɦɢɕȐɜȗ˙ɤɥɔɕȐɔɕȐɜȗəɞɖɟɢɝɑɤəɡɥɕȐɕɞȐɄɕɢɝəɞɑɜɕȞȐɁɥɕɜȐɞɟɒɜɕȐɓɘɟəɨȐȑȐȽɑəɞɤɕɞɑɞɤȐɚɕȐɔɟəɣȐ˙ɓɢəɢɕȐɥɞȐɠɕɥȐɓɕȐɡɥəȐɝɕȐɦəɕɞɣȐɠɑɢȐɜɑȐɤ˚ɤɕȐɠɟɥɢȐɢɕɝɠɜəɢȐɥɞȐɠɕɥȞȐɆɟɥɣȐɠɕɞɣəɕɪȐɣ˫ɢɕɝɕɞɤȐɤɢɟɥɦɕɢȐɥɞȐɤɕɨɤɕȐɠɜɥɣȐəɞɤ˙ɢɕɣɣɑɞɤȐːȐɜəɢɕȞȐ
ȺɕȐɜɕȐɔ˙ɠɜɟɢɕȐɑɥɤɑɞɤȐɡɥɕȐɦɟɥɣȞȐȺɕȐɞȗɑəɝɕȐɠɑɣȐɠɑɢɜɕɢȐɠɟɥɢȐɞɕȐɢəɕɞȐɔəɢɕȞȐȿɞȐɠɟɥɢɢɑəɤȐɣɕȐɔəɢɕȐȪȐ˗ɑȐɩȐɕɣɤȐȑȐȹɜȐɑȐɕɞɖəɞȐɑɣɣɕɪȐ˙ɓɢəɤȐɓɟɝɝɕȐ˗ɑȞȐȹɜȐɦɑȐɤɢɟɥɦɕɢȐɜɕȐɤɕɝɠɣȐɜɟɞɗȞ
ȽɑəɣȐɞɟɞȞȐȹɜȐɖɑəɤȐɠɟɥɢɤɑɞɤȐɒɕɑɥȐɔɕɘɟɢɣȐɕɞȐɓɕɤɤɕȐɠ˙ɢəɟɔɕȐɔɕȐɓɟɞɖəɞɕɝɕɞɤȞȐȽɑəɣȐɚɥɣɤɕɝɕɞɤȞȐɀɟɥɢɡɥɟəȐɦɟɥɜɟəɢȐɣɟɢɤəɢȞȐȴȗɑəɜɜɕɥɢɣȐɓȗɕɣɤȐəɝɠɟɣɣəɒɜɕȞȐȲɟɞȜȐɠɟɥɢȐɕɞȐɢɕɦɕɞəɢȐːȐɞɟɤɢɕȐɠɢɟɒɜ˘ɝɕȜȐəɜȐɞɕȐɖɑɥɤȐɠɑɣȐɞɟɞȐɠɜɥɣȐɣȗɑɤɤɕɞɔɢɕȐːȐɢɕɓɕɦɟəɢȐɔɕȐɜɟɞɗɣȐɝɕɣɣɑɗɕɣȐɔɟɞɓȐəɜȐɖɑɥɤȐɒəɕɞȐɤɢɟɥɦɕɢȐɥɞȐɝɟɩɕɞȐɠɟɥɢȐɑɖɖəɞɕɢȐɜȗɑɤɤɑɡɥɕȞȐȾȗɟɥɒɜəɟɞɣȐɠɑɣȐɡɥȗəɜȐɣȗɑɗəɤȐɔȗɥɞȐɓɘəɖɖɢɕɝɕɞɤȐːȐɔ˙ɓɑɜɑɗɕȞ

9
Sécurité/test.py Normal file
View File

@@ -0,0 +1,9 @@
def rot47(clair: str) -> str:
chif =""
dec = 33
amp = 127 - dec
for car in clair:
new_code = dec + ((ord( car) - dec + 47) % amp)
chif += chr(new_code) if 33 <= ord(car) <= 126 else car
return chif