%% nav-debut : généré par gen_navigation.py, ne pas modifier à la main %%
> [!nav]
> ↩ [[Code et Développement/Programmation/Cryptographie/Cryptographie|Accueil · Cryptographie]]
%% nav-fin %%
# Atelier - Cryptographie
## 🎯 Objectif
Programmer des chiffrements symétriques simples et mesurer leurs limites : coût d'une attaque par force brute, chiffrement faible, permutation alphabétique, chiffre de Vigenère.
Les cellules Python de cette page s'exécutent dans le navigateur. Le premier clic sur « Exécuter » charge Python, ce qui prend quelques secondes. Les cellules partagent les mêmes variables, dans l'ordre où vous les exécutez.
Les notions sont présentées dans les pages [[Code et Développement/Programmation/Cryptographie/02_Le chiffre de César|Le chiffre de César]], [[Code et Développement/Programmation/Cryptographie/03_Permutation alphabétique|Permutation alphabétique]] et [[Code et Développement/Programmation/Cryptographie/04_Le chiffre de Vigenère|Le chiffre de Vigenère]].
## Exercice 1 : temps de calcul d'une attaque par force brute
On connaît un texte clair, le même texte chiffré et l'algorithme utilisé. On cherche la clé en essayant toutes les clés possibles.
Les paramètres sont les suivants :
- la clé compte 128 bits, soit 2^128 clés possibles ;
- un essai de clé demande 1 000 opérations élémentaires ;
- le processeur effectue 110 000 millions d'instructions par seconde (Intel Core i7-8086K, 2018).
Calculez le nombre d'années nécessaires pour essayer toutes les clés. Rangez le résultat dans la variable `annees`.
```sq-python
operations = 0 # nombre total d'opérations, à calculer
secondes = 0 # durée en secondes, à calculer
annees = 0 # durée en années, à calculer
print(annees)
#--- correction
operations = 2**128 * 1000
secondes = operations / 110_000_000_000
annees = secondes / 60 / 60 / 24 / 365
print(annees)
#--- tests
assert 9.7e22 < annees < 9.9e22, "Le résultat attendu est d'environ 9,8 × 10^22 années."
#--- sortie
9.809348246187286e+22
```
Le résultat est d'environ 9,8 × 10^22 années. Le Big Bang date de 13,7 milliards d'années, soit 1,37 × 10^10 années.
Reprenez le calcul pour une clé de 56 bits. Calculez ensuite la durée en jours si le calcul est réparti sur 1 000 ordinateurs identiques. Rangez les résultats dans `annees` et `jours`.
```sq-python
annees = 0 # clé de 56 bits, un ordinateur
jours = 0 # clé de 56 bits, 1 000 ordinateurs
print(annees)
print(jours)
#--- correction
operations = 2**56 * 1000
secondes = operations / 110_000_000_000
annees = secondes / 60 / 60 / 24 / 365
jours = secondes / 1000 / 60 / 60 / 24
print(annees)
print(jours)
#--- tests
assert 20 < annees < 21, "Avec un seul ordinateur, la durée est d'un peu plus de 20 ans."
assert 7.5 < jours < 7.7, "Avec 1 000 ordinateurs, la durée est d'environ 7,6 jours."
#--- sortie
20.772102889029547
7.581817554495784
```
Avec une clé de 56 bits, la recherche prend un peu plus de 20 ans sur un ordinateur. Elle tombe à environ 7,6 jours sur 1 000 ordinateurs. La longueur de la clé décide de la résistance à la force brute.
## Exercice 2 : un chiffrement faible
Un système chiffre un texte en deux étapes :
1. une rotation du texte de r caractères vers la droite ;
2. l'ajout d'une valeur v à chaque lettre, modulo 26.
Exemple avec le texte `abcz`, r = 1 et v = 1 : la rotation donne `zabc`, l'ajout de 1 donne `abcd`.
Le texte clair `x` et le texte chiffré `y` sont donnés sous forme de listes de lettres minuscules non accentuées, de même longueur. Écrivez la fonction `crypt(x, y)` qui renvoie le couple `(r, v)` utilisé pour le chiffrement. La solution est unique.
Méthode proposée : essayer chaque rotation de 1 à la longueur du texte, puis chaque valeur de 1 à 26, et comparer au texte chiffré.
```sq-python
def rotation(x):
"""Renvoie la liste x après une rotation d'un caractère vers la droite."""
return x[-1:] + x[:-1]
def crypt(x, y):
"""Renvoie (r, v) tel que la rotation r puis l'ajout v transforment x en y."""
pass # à compléter
print(crypt(['a', 'b', 'c', 'd'], ['h', 'e', 'f', 'g']))
#--- correction
def rotation(x):
"""Renvoie la liste x après une rotation d'un caractère vers la droite."""
return x[-1:] + x[:-1]
def ajout(x):
"""Renvoie la liste x où chaque lettre avance d'un rang, z redevenant a."""
return [chr((ord(c) - ord('a') + 1) % 26 + ord('a')) for c in x]
def crypt(x, y):
"""Renvoie (r, v) tel que la rotation r puis l'ajout v transforment x en y."""
for r in range(1, len(x) + 1):
x = rotation(x)
for v in range(1, 27):
x = ajout(x)
if x == y:
return (r, v)
return None
print(crypt(['a', 'b', 'c', 'd'], ['h', 'e', 'f', 'g']))
#--- tests
assert crypt(['a', 'b', 'c', 'd'], ['h', 'e', 'f', 'g']) == (1, 4), "abcd chiffré en hefg : rotation 1, valeur 4."
assert crypt(list('abcz'), list('abcd')) == (1, 1), "abcz chiffré en abcd : rotation 1, valeur 1."
#--- sortie
(1, 4)
```
Vérification de l'exemple : la rotation de 1 transforme `abcd` en `dabc`, puis l'ajout de 4 donne `hefg`. Ce chiffrement n'a que 26 × n clés pour un texte de n lettres : une boucle les essaie toutes en une fraction de seconde.
## Exercice 3 : permutation alphabétique
Chiffrez le texte `vive la cryptographie` par permutation alphabétique avec la clé `yruhfwlkdxeajitsvznmcpbgqo` : a est remplacé par y, b par r, et ainsi de suite. Écrivez la fonction `code(x, cle)`. Les caractères qui ne sont pas des lettres minuscules restent inchangés.
```sq-python
def code(x, cle):
"""Renvoie x chiffré par la permutation alphabétique cle."""
pass # à compléter
print(code('vive la cryptographie', 'yruhfwlkdxeajitsvznmcpbgqo'))
#--- correction
def code(x, cle):
"""Renvoie x chiffré par la permutation alphabétique cle."""
alphabet = 'abcdefghijklmnopqrstuvwxyz'
resultat = ''
for c in x:
if c in alphabet:
resultat += cle[alphabet.index(c)]
else:
resultat += c
return resultat
print(code('vive la cryptographie', 'yruhfwlkdxeajitsvznmcpbgqo'))
#--- tests
assert code('vive la cryptographie', 'yruhfwlkdxeajitsvznmcpbgqo') == 'pdpf ay uzqsmtlzyskdf', "Le texte chiffré attendu est pdpf ay uzqsmtlzyskdf."
assert code('abc', 'yruhfwlkdxeajitsvznmcpbgqo') == 'yru', "a donne y, b donne r, c donne u."
#--- sortie
pdpf ay uzqsmtlzyskdf
```
## Exercice 4 : le chiffre de Vigenère
Dans cet exercice, la clé avance sur chaque caractère du texte, espaces et ponctuation compris. Le caractère d'indice i est donc chiffré avec la lettre de clé d'indice `i % len(cle)`. Seules les lettres minuscules sont transformées.
Écrivez la fonction `vigenere(x, cle)` et chiffrez `vive la cryptographie` avec la clé `python`.
```sq-python
alphabet = 'abcdefghijklmnopqrstuvwxyz'
def vigenere(x, cle):
"""Renvoie x chiffré par le chiffre de Vigenère avec la clé cle."""
pass # à compléter
print(vigenere('vive la cryptographie', 'python'))
#--- correction
alphabet = 'abcdefghijklmnopqrstuvwxyz'
def vigenere(x, cle):
"""Renvoie x chiffré par le chiffre de Vigenère avec la clé cle."""
resultat = ''
for i in range(len(x)):
if x[i] in alphabet:
decalage = alphabet.index(cle[i % len(cle)])
resultat += alphabet[(alphabet.index(x[i]) + decalage) % 26]
else:
resultat += x[i]
return resultat
print(vigenere('vive la cryptographie', 'python'))
#--- tests
assert vigenere('vive la cryptographie', 'python') == 'kgol yp vymcimzyocwgx', "Le texte chiffré attendu est kgol yp vymcimzyocwgx."
#--- sortie
kgol yp vymcimzyocwgx
```
Écrivez la fonction `decode_vigenere(x, cle)` et retrouvez le texte clair de `vn jsbgx puov zvjflirwhv`, chiffré avec la clé `baguette`.
```sq-python
def decode_vigenere(x, cle):
"""Renvoie x déchiffré par le chiffre de Vigenère avec la clé cle."""
pass # à compléter
print(decode_vigenere('vn jsbgx puov zvjflirwhv', 'baguette'))
#--- correction
def decode_vigenere(x, cle):
"""Renvoie x déchiffré par le chiffre de Vigenère avec la clé cle."""
resultat = ''
for i in range(len(x)):
if x[i] in alphabet:
decalage = alphabet.index(cle[i % len(cle)])
resultat += alphabet[(alphabet.index(x[i]) - decalage) % 26]
else:
resultat += x[i]
return resultat
print(decode_vigenere('vn jsbgx puov zvjflirwhv', 'baguette'))
#--- tests
assert decode_vigenere('vn jsbgx puov zvjflirwhv', 'baguette') == 'un point pour griffondor', "Le texte clair attendu est un point pour griffondor."
assert decode_vigenere(vigenere('bonjour tout le monde', 'cle'), 'cle') == 'bonjour tout le monde', "Déchiffrer un texte chiffré doit redonner le texte clair."
#--- sortie
un point pour griffondor
```
Le déchiffrement soustrait le décalage au lieu de l'ajouter. Le reste de la fonction est identique.
Retrouvez enfin la clé qui chiffre `un point pour griffondor` en `kh srqgv ficu okkmvivgrz`. Écrivez la fonction `cle_vigenere(x, y)` qui renvoie, pour chaque lettre, la lettre de clé utilisée. Lisez la clé dans le résultat, puis rangez-la dans la variable `cle`.
```sq-python
def cle_vigenere(x, y):
"""Renvoie, lettre par lettre, la clé qui chiffre x en y."""
pass # à compléter
print(cle_vigenere('un point pour griffondor', 'kh srqgv ficu okkmvivgrz'))
cle = '' # la clé lue dans le résultat
print(cle)
#--- correction
def cle_vigenere(x, y):
"""Renvoie, lettre par lettre, la clé qui chiffre x en y."""
resultat = ''
for i in range(len(x)):
if x[i] in alphabet:
resultat += alphabet[(alphabet.index(y[i]) - alphabet.index(x[i])) % 26]
else:
resultat += x[i]
return resultat
print(cle_vigenere('un point pour griffondor', 'kh srqgv ficu okkmvivgrz'))
cle = 'quidditch'
print(cle)
#--- tests
assert vigenere('un point pour griffondor', cle) == 'kh srqgv ficu okkmvivgrz', "Avec la bonne clé, le chiffrement du texte clair redonne le texte chiffré."
#--- sortie
qu dditc quid itchquiddi
quidditch
```
Le résultat `qu dditc quid itchquiddi` répète le mot `quidditch`. Les espaces du texte occupent une position de clé sans la montrer.
## 🧠 À retenir
- Ces algorithmes sont dits **symétriques** : la même clé sert à chiffrer et à déchiffrer.
- La clé doit rester confidentielle. Une personne qui la possède lit les messages et peut en fabriquer de faux.
- En pratique, on utilise une clé différente pour chaque correspondant.
- Le temps d'une attaque par force brute double à chaque bit ajouté à la clé.
- Le masque jetable reprend le principe de Vigenère avec une clé aléatoire, au moins aussi longue que le texte, et utilisée une seule fois.
- Le standard actuel de chiffrement symétrique est AES, avec une clé de 128, 192 ou 256 bits.
%% nav-debut : généré par gen_navigation.py, ne pas modifier à la main %%
> [!nav]
> ↩ [[Code et Développement/Programmation/Cryptographie/Cryptographie|Accueil · Cryptographie]]
%% nav-fin %%
---
🗓️ Historique
Dernière mise à jour : `05 février 2026`
Rédigé par : [[Julien DUQUENNOY]]