chiffrement_affine.py
Note de lecture générée automatiquement
Le vrai fichier est
scripts/maths/chiffrement_affine.py(Obsidian n’affiche pas les scripts eux-mêmes). Ne modifie pas cette note : modifie le script, puis relancepython3 generer_vues_obsidian.pydans le dossierscripts.
- À quoi il sert : Chiffrer, déchiffrer, casser
- Lancer (dans le dossier
scripts/maths/) :python3 chiffrement_affine.py chiffrer 5 13 "BONJOUR" - Fiche : M01 - Arithmétique et chiffrement affine
- Retour : 00 - Tous les scripts
Code
#!/usr/bin/env python3
"""
chiffrement_affine.py — Chiffrement / déchiffrement affine y = (a·x + b) mod 26
Utilisation :
python3 chiffrement_affine.py chiffrer 5 13 "BONJOUR" -> SFAGFJU
python3 chiffrement_affine.py dechiffrer 5 13 "PDZ" -> QYS
python3 chiffrement_affine.py detail 5 13 "BONJOUR" (tableau étape par étape)
python3 chiffrement_affine.py casser "TEXTE CHIFFRE" (essaie les 312 clés)
"""
import math
import sys
ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def inverse_modulaire(a: int, n: int = 26) -> int:
ancien_r, r = a % n, n
ancien_u, u = 1, 0
while r != 0:
quotient = ancien_r // r
ancien_r, r = r, ancien_r - quotient * r
ancien_u, u = u, ancien_u - quotient * u
if ancien_r != 1:
raise ValueError(f"Clé invalide : pgcd({a}, {n}) = {ancien_r} ≠ 1")
return ancien_u % n
def verifier_cle(a: int) -> None:
if math.gcd(a, 26) != 1:
valides = [k for k in range(1, 26) if math.gcd(k, 26) == 1]
raise ValueError(f"a = {a} invalide (pgcd(a, 26) ≠ 1). Valeurs possibles : {valides}")
def chiffrer(texte: str, a: int, b: int) -> str:
verifier_cle(a)
resultat = ""
for lettre in texte.upper():
if lettre in ALPHABET:
x = ALPHABET.index(lettre)
resultat += ALPHABET[(a * x + b) % 26]
else:
resultat += lettre # espaces, ponctuation conservés
return resultat
def dechiffrer(texte: str, a: int, b: int) -> str:
verifier_cle(a)
a_inverse = inverse_modulaire(a)
resultat = ""
for lettre in texte.upper():
if lettre in ALPHABET:
y = ALPHABET.index(lettre)
resultat += ALPHABET[(a_inverse * (y - b)) % 26]
else:
resultat += lettre
return resultat
def detail(texte: str, a: int, b: int) -> None:
verifier_cle(a)
print(f"Clé (a={a}, b={b}) — inverse de a modulo 26 : {inverse_modulaire(a)}")
print(f"{'Lettre':<7}{'x':>4}{'a·x+b':>8}{'mod 26':>8} Chiffrée")
for lettre in texte.upper():
if lettre not in ALPHABET:
continue
x = ALPHABET.index(lettre)
brut = a * x + b
print(f"{lettre:<7}{x:>4}{brut:>8}{brut % 26:>8} {ALPHABET[brut % 26]}")
def casser(texte: str) -> None:
# Attaque par force brute : 12 valeurs de a × 26 valeurs de b = 312 clés
for a in range(1, 26):
if math.gcd(a, 26) != 1:
continue
for b in range(26):
print(f"a={a:>2} b={b:>2} : {dechiffrer(texte, a, b)}")
if __name__ == "__main__":
if len(sys.argv) < 3:
sys.exit(__doc__)
action = sys.argv[1]
if action == "casser":
casser(sys.argv[2])
else:
cle_a, cle_b, message = int(sys.argv[2]), int(sys.argv[3]), sys.argv[4]
if action == "chiffrer":
print(chiffrer(message, cle_a, cle_b))
elif action == "dechiffrer":
print(dechiffrer(message, cle_a, cle_b))
elif action == "detail":
detail(message, cle_a, cle_b)
else:
sys.exit(__doc__)